suneditor 3.0.0-beta.9 → 3.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -57
- package/dist/suneditor-contents.min.css +1 -0
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +110 -61
- package/src/assets/design/color.css +36 -17
- package/src/assets/design/size.css +2 -0
- package/src/assets/icons/defaultIcons.js +17 -2
- package/src/assets/suneditor-contents.css +51 -16
- package/src/assets/suneditor.css +116 -43
- package/src/core/config/contextProvider.js +288 -0
- package/src/core/config/eventManager.js +188 -0
- package/src/core/config/instanceCheck.js +59 -0
- package/src/core/config/optionProvider.js +452 -0
- package/src/core/editor.js +166 -1637
- package/src/core/event/actions/index.js +229 -0
- package/src/core/event/effects/common.registry.js +74 -0
- package/src/core/event/effects/keydown.registry.js +573 -0
- package/src/core/event/effects/ruleHelpers.js +148 -0
- package/src/core/event/eventOrchestrator.js +944 -0
- package/src/core/event/executor.js +27 -0
- package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +27 -28
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +10 -8
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +22 -23
- package/src/core/event/handlers/handler_ww_input.js +75 -0
- package/src/core/event/handlers/handler_ww_key.js +228 -0
- package/src/core/event/handlers/handler_ww_mouse.js +166 -0
- package/src/core/event/ports.js +211 -0
- package/src/core/event/reducers/keydown.reducer.js +97 -0
- package/src/core/event/rules/keydown.rule.arrow.js +63 -0
- package/src/core/event/rules/keydown.rule.backspace.js +208 -0
- package/src/core/event/rules/keydown.rule.delete.js +132 -0
- package/src/core/event/rules/keydown.rule.enter.js +150 -0
- package/src/core/event/rules/keydown.rule.tab.js +35 -0
- package/src/core/event/support/defaultLineManager.js +136 -0
- package/src/core/event/support/selectionState.js +204 -0
- package/src/core/kernel/coreKernel.js +320 -0
- package/src/core/kernel/kernelInjector.js +19 -0
- package/src/core/kernel/store.js +173 -0
- package/src/core/{class → logic/dom}/char.js +42 -45
- package/src/core/logic/dom/format.js +1075 -0
- package/src/core/{class → logic/dom}/html.js +743 -624
- package/src/core/logic/dom/inline.js +1847 -0
- package/src/core/logic/dom/listFormat.js +601 -0
- package/src/core/{class → logic/dom}/nodeTransform.js +92 -72
- package/src/core/{class → logic/dom}/offset.js +254 -317
- package/src/core/logic/dom/selection.js +754 -0
- package/src/core/logic/panel/menu.js +389 -0
- package/src/core/logic/panel/toolbar.js +449 -0
- package/src/core/logic/panel/viewer.js +761 -0
- package/src/core/logic/shell/_commandExecutor.js +380 -0
- package/src/core/logic/shell/commandDispatcher.js +241 -0
- package/src/core/logic/shell/component.js +970 -0
- package/src/core/logic/shell/focusManager.js +110 -0
- package/src/core/{base → logic/shell}/history.js +110 -60
- package/src/core/logic/shell/pluginManager.js +363 -0
- package/src/core/logic/shell/shortcuts.js +130 -0
- package/src/core/logic/shell/ui.js +904 -0
- package/src/core/schema/context.js +66 -0
- package/src/core/schema/frameContext.js +160 -0
- package/src/core/schema/options.js +628 -0
- package/src/core/section/constructor.js +194 -500
- package/src/core/section/documentType.js +297 -222
- package/src/events.js +808 -543
- package/src/helper/clipboard.js +27 -16
- package/src/helper/converter.js +100 -78
- package/src/helper/dom/domCheck.js +56 -30
- package/src/helper/dom/domQuery.js +159 -89
- package/src/helper/dom/domUtils.js +114 -49
- package/src/helper/dom/index.js +5 -1
- package/src/helper/env.js +26 -26
- package/src/helper/index.js +1 -1
- package/src/helper/keyCodeMap.js +25 -28
- package/src/helper/numbers.js +4 -8
- package/src/helper/unicode.js +4 -8
- package/src/hooks/base.js +307 -0
- package/src/hooks/params.js +130 -0
- package/src/interfaces/contracts.js +227 -0
- package/src/interfaces/index.js +7 -0
- package/src/interfaces/plugins.js +239 -0
- package/src/langs/ckb.js +4 -4
- package/src/langs/cs.js +4 -4
- package/src/langs/da.js +4 -4
- package/src/langs/de.js +4 -4
- package/src/langs/en.js +4 -4
- package/src/langs/es.js +4 -4
- package/src/langs/fa.js +4 -4
- package/src/langs/fr.js +4 -4
- package/src/langs/he.js +4 -4
- package/src/langs/hu.js +4 -4
- package/src/langs/it.js +4 -4
- package/src/langs/ja.js +4 -4
- package/src/langs/km.js +4 -4
- package/src/langs/ko.js +4 -4
- package/src/langs/lv.js +4 -4
- package/src/langs/nl.js +4 -4
- package/src/langs/pl.js +4 -4
- package/src/langs/pt_br.js +13 -13
- package/src/langs/ro.js +4 -4
- package/src/langs/ru.js +4 -4
- package/src/langs/se.js +4 -4
- package/src/langs/tr.js +4 -4
- package/src/langs/uk.js +4 -4
- package/src/langs/ur.js +4 -4
- package/src/langs/zh_cn.js +4 -4
- package/src/modules/{Browser.js → contract/Browser.js} +119 -128
- package/src/modules/{ColorPicker.js → contract/ColorPicker.js} +132 -142
- package/src/modules/contract/Controller.js +589 -0
- package/src/modules/{Figure.js → contract/Figure.js} +591 -411
- package/src/modules/{HueSlider.js → contract/HueSlider.js} +125 -86
- package/src/modules/contract/Modal.js +357 -0
- package/src/modules/contract/index.js +9 -0
- package/src/modules/manager/ApiManager.js +197 -0
- package/src/modules/{FileManager.js → manager/FileManager.js} +128 -160
- package/src/modules/manager/index.js +5 -0
- package/src/modules/{ModalAnchorEditor.js → ui/ModalAnchorEditor.js} +108 -138
- package/src/modules/{SelectMenu.js → ui/SelectMenu.js} +119 -120
- package/src/modules/{_DragHandle.js → ui/_DragHandle.js} +1 -1
- package/src/modules/ui/index.js +6 -0
- package/src/plugins/browser/audioGallery.js +23 -26
- package/src/plugins/browser/fileBrowser.js +25 -28
- package/src/plugins/browser/fileGallery.js +20 -23
- package/src/plugins/browser/imageGallery.js +24 -23
- package/src/plugins/browser/videoGallery.js +27 -29
- package/src/plugins/command/blockquote.js +11 -17
- package/src/plugins/command/exportPDF.js +26 -26
- package/src/plugins/command/fileUpload.js +138 -133
- package/src/plugins/command/list_bulleted.js +48 -44
- package/src/plugins/command/list_numbered.js +48 -44
- package/src/plugins/dropdown/align.js +64 -50
- package/src/plugins/dropdown/backgroundColor.js +34 -35
- package/src/plugins/dropdown/{formatBlock.js → blockStyle.js} +43 -37
- package/src/plugins/dropdown/font.js +50 -36
- package/src/plugins/dropdown/fontColor.js +34 -35
- package/src/plugins/dropdown/hr.js +55 -50
- package/src/plugins/dropdown/layout.js +20 -15
- package/src/plugins/dropdown/lineHeight.js +46 -30
- package/src/plugins/dropdown/list.js +32 -33
- package/src/plugins/dropdown/paragraphStyle.js +40 -34
- package/src/plugins/dropdown/table/index.js +915 -0
- package/src/plugins/dropdown/table/render/table.html.js +308 -0
- package/src/plugins/dropdown/table/render/table.menu.js +121 -0
- package/src/plugins/dropdown/table/services/table.cell.js +465 -0
- package/src/plugins/dropdown/table/services/table.clipboard.js +414 -0
- package/src/plugins/dropdown/table/services/table.grid.js +504 -0
- package/src/plugins/dropdown/table/services/table.resize.js +463 -0
- package/src/plugins/dropdown/table/services/table.selection.js +466 -0
- package/src/plugins/dropdown/table/services/table.style.js +844 -0
- package/src/plugins/dropdown/table/shared/table.constants.js +109 -0
- package/src/plugins/dropdown/table/shared/table.utils.js +219 -0
- package/src/plugins/dropdown/template.js +20 -15
- package/src/plugins/dropdown/textStyle.js +28 -22
- package/src/plugins/field/mention.js +54 -49
- package/src/plugins/index.js +5 -5
- package/src/plugins/input/fontSize.js +100 -97
- package/src/plugins/input/pageNavigator.js +13 -10
- package/src/plugins/modal/audio.js +208 -219
- package/src/plugins/modal/drawing.js +99 -104
- package/src/plugins/modal/embed.js +323 -312
- package/src/plugins/modal/image/index.js +942 -0
- package/src/plugins/modal/image/render/image.html.js +150 -0
- package/src/plugins/modal/image/services/image.size.js +198 -0
- package/src/plugins/modal/image/services/image.upload.js +216 -0
- package/src/plugins/modal/image/shared/image.constants.js +20 -0
- package/src/plugins/modal/link.js +74 -54
- package/src/plugins/modal/math.js +126 -119
- package/src/plugins/modal/video/index.js +858 -0
- package/src/plugins/modal/video/render/video.html.js +131 -0
- package/src/plugins/modal/video/services/video.size.js +281 -0
- package/src/plugins/modal/video/services/video.upload.js +92 -0
- package/src/plugins/popup/anchor.js +57 -49
- package/src/suneditor.js +73 -61
- package/src/themes/cobalt.css +155 -0
- package/src/themes/dark.css +143 -120
- package/src/typedef.js +214 -63
- package/types/assets/icons/defaultIcons.d.ts +8 -0
- package/types/assets/suneditor-contents.css.d.ts +1 -0
- package/types/assets/suneditor.css.d.ts +1 -0
- package/types/core/config/contextProvider.d.ts +148 -0
- package/types/core/config/eventManager.d.ts +68 -0
- package/types/core/config/instanceCheck.d.ts +33 -0
- package/types/core/config/optionProvider.d.ts +147 -0
- package/types/core/editor.d.ts +27 -586
- package/types/core/event/actions/index.d.ts +50 -0
- package/types/core/event/effects/common.registry.d.ts +56 -0
- package/types/core/event/effects/keydown.registry.d.ts +80 -0
- package/types/core/event/effects/ruleHelpers.d.ts +36 -0
- package/types/core/event/eventOrchestrator.d.ts +191 -0
- package/types/core/event/executor.d.ts +13 -0
- package/types/core/event/handlers/handler_toolbar.d.ts +38 -0
- package/types/core/event/handlers/handler_ww_clipboard.d.ts +36 -0
- package/types/core/event/handlers/handler_ww_dragDrop.d.ts +26 -0
- package/types/core/event/handlers/handler_ww_input.d.ts +38 -0
- package/types/core/event/handlers/handler_ww_key.d.ts +40 -0
- package/types/core/event/handlers/handler_ww_mouse.d.ts +47 -0
- package/types/core/event/ports.d.ts +256 -0
- package/types/core/event/reducers/keydown.reducer.d.ts +84 -0
- package/types/core/event/rules/keydown.rule.arrow.d.ts +19 -0
- package/types/core/event/rules/keydown.rule.backspace.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.delete.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.enter.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.tab.d.ts +18 -0
- package/types/core/event/support/defaultLineManager.d.ts +22 -0
- package/types/core/event/support/selectionState.d.ts +29 -0
- package/types/core/kernel/coreKernel.d.ts +219 -0
- package/types/core/kernel/kernelInjector.d.ts +16 -0
- package/types/core/kernel/store.d.ts +170 -0
- package/types/core/logic/dom/char.d.ts +46 -0
- package/types/core/logic/dom/format.d.ts +234 -0
- package/types/core/logic/dom/html.d.ts +290 -0
- package/types/core/logic/dom/inline.d.ts +93 -0
- package/types/core/logic/dom/listFormat.d.ts +101 -0
- package/types/core/logic/dom/nodeTransform.d.ts +110 -0
- package/types/core/logic/dom/offset.d.ts +335 -0
- package/types/core/logic/dom/selection.d.ts +165 -0
- package/types/core/logic/panel/menu.d.ts +93 -0
- package/types/core/logic/panel/toolbar.d.ts +128 -0
- package/types/core/logic/panel/viewer.d.ts +89 -0
- package/types/core/logic/shell/_commandExecutor.d.ts +18 -0
- package/types/core/logic/shell/commandDispatcher.d.ts +65 -0
- package/types/core/logic/shell/component.d.ts +182 -0
- package/types/core/logic/shell/focusManager.d.ts +31 -0
- package/types/core/{base → logic/shell}/history.d.ts +13 -12
- package/types/core/logic/shell/pluginManager.d.ts +115 -0
- package/types/core/logic/shell/shortcuts.d.ts +131 -0
- package/types/core/logic/shell/ui.d.ts +261 -0
- package/types/core/schema/context.d.ts +104 -0
- package/types/core/schema/frameContext.d.ts +320 -0
- package/types/core/schema/options.d.ts +1241 -0
- package/types/core/section/constructor.d.ts +117 -652
- package/types/core/section/documentType.d.ts +43 -61
- package/types/events.d.ts +796 -65
- package/types/helper/clipboard.d.ts +5 -4
- package/types/helper/converter.d.ts +55 -43
- package/types/helper/dom/domCheck.d.ts +27 -19
- package/types/helper/dom/domQuery.d.ts +76 -57
- package/types/helper/dom/domUtils.d.ts +62 -39
- package/types/helper/dom/index.d.ts +87 -1
- package/types/helper/env.d.ts +16 -13
- package/types/helper/index.d.ts +8 -2
- package/types/helper/keyCodeMap.d.ts +24 -23
- package/types/helper/numbers.d.ts +4 -6
- package/types/helper/unicode.d.ts +4 -3
- package/types/hooks/base.d.ts +239 -0
- package/types/hooks/params.d.ts +65 -0
- package/types/index.d.ts +20 -117
- package/types/interfaces/contracts.d.ts +183 -0
- package/types/interfaces/index.d.ts +3 -0
- package/types/interfaces/plugins.d.ts +168 -0
- package/types/langs/_Lang.d.ts +2 -2
- package/types/langs/index.d.ts +2 -2
- package/types/modules/contract/Browser.d.ts +262 -0
- package/types/modules/contract/ColorPicker.d.ts +99 -0
- package/types/modules/contract/Controller.d.ts +204 -0
- package/types/modules/contract/Figure.d.ts +529 -0
- package/types/modules/{HueSlider.d.ts → contract/HueSlider.d.ts} +39 -28
- package/types/modules/contract/Modal.d.ts +62 -0
- package/types/modules/contract/index.d.ts +7 -0
- package/types/modules/manager/ApiManager.d.ts +106 -0
- package/types/modules/manager/FileManager.d.ts +124 -0
- package/types/modules/manager/index.d.ts +3 -0
- package/types/modules/ui/ModalAnchorEditor.d.ts +152 -0
- package/types/modules/ui/SelectMenu.d.ts +107 -0
- package/types/modules/{_DragHandle.d.ts → ui/_DragHandle.d.ts} +1 -0
- package/types/modules/ui/index.d.ts +4 -0
- package/types/plugins/browser/audioGallery.d.ts +33 -41
- package/types/plugins/browser/fileBrowser.d.ts +42 -50
- package/types/plugins/browser/fileGallery.d.ts +33 -41
- package/types/plugins/browser/imageGallery.d.ts +30 -37
- package/types/plugins/browser/videoGallery.d.ts +33 -41
- package/types/plugins/command/blockquote.d.ts +4 -21
- package/types/plugins/command/exportPDF.d.ts +23 -33
- package/types/plugins/command/fileUpload.d.ts +80 -100
- package/types/plugins/command/list_bulleted.d.ts +9 -35
- package/types/plugins/command/list_numbered.d.ts +9 -35
- package/types/plugins/dropdown/align.d.ts +23 -46
- package/types/plugins/dropdown/backgroundColor.d.ts +35 -53
- package/types/plugins/dropdown/blockStyle.d.ts +45 -0
- package/types/plugins/dropdown/font.d.ts +18 -41
- package/types/plugins/dropdown/fontColor.d.ts +35 -53
- package/types/plugins/dropdown/hr.d.ts +26 -52
- package/types/plugins/dropdown/layout.d.ts +19 -25
- package/types/plugins/dropdown/lineHeight.d.ts +21 -39
- package/types/plugins/dropdown/list.d.ts +6 -34
- package/types/plugins/dropdown/paragraphStyle.d.ts +34 -45
- package/types/plugins/dropdown/table/index.d.ts +158 -0
- package/types/plugins/dropdown/table/render/table.html.d.ts +71 -0
- package/types/plugins/dropdown/table/render/table.menu.d.ts +59 -0
- package/types/plugins/dropdown/table/services/table.cell.d.ts +76 -0
- package/types/plugins/dropdown/table/services/table.clipboard.d.ts +26 -0
- package/types/plugins/dropdown/table/services/table.grid.d.ts +77 -0
- package/types/plugins/dropdown/table/services/table.resize.d.ts +72 -0
- package/types/plugins/dropdown/table/services/table.selection.d.ts +59 -0
- package/types/plugins/dropdown/table/services/table.style.d.ts +162 -0
- package/types/plugins/dropdown/table/shared/table.constants.d.ts +134 -0
- package/types/plugins/dropdown/table/shared/table.utils.d.ts +91 -0
- package/types/plugins/dropdown/template.d.ts +19 -25
- package/types/plugins/dropdown/textStyle.d.ts +23 -30
- package/types/plugins/field/mention.d.ts +66 -72
- package/types/plugins/index.d.ts +41 -40
- package/types/plugins/input/fontSize.d.ts +57 -96
- package/types/plugins/input/pageNavigator.d.ts +5 -8
- package/types/plugins/modal/audio.d.ts +60 -153
- package/types/plugins/modal/drawing.d.ts +16 -118
- package/types/plugins/modal/embed.d.ts +46 -166
- package/types/plugins/modal/image/index.d.ts +281 -0
- package/types/plugins/modal/image/render/image.html.d.ts +45 -0
- package/types/plugins/modal/image/services/image.size.d.ts +55 -0
- package/types/plugins/modal/image/services/image.upload.d.ts +24 -0
- package/types/plugins/modal/image/shared/image.constants.d.ts +17 -0
- package/types/plugins/modal/link.d.ts +46 -66
- package/types/plugins/modal/math.d.ts +17 -86
- package/types/plugins/modal/{video.d.ts → video/index.d.ts} +89 -221
- package/types/plugins/modal/video/render/video.html.d.ts +37 -0
- package/types/plugins/modal/video/services/video.size.d.ts +74 -0
- package/types/plugins/modal/video/services/video.upload.d.ts +19 -0
- package/types/plugins/popup/anchor.d.ts +8 -38
- package/types/suneditor.d.ts +55 -24
- package/types/typedef.d.ts +344 -228
- package/CONTRIBUTING.md +0 -186
- package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
- package/src/core/base/eventHandlers/handler_ww_mouse.js +0 -194
- package/src/core/base/eventManager.js +0 -1523
- package/src/core/class/component.js +0 -856
- package/src/core/class/format.js +0 -3433
- package/src/core/class/menu.js +0 -346
- package/src/core/class/selection.js +0 -610
- package/src/core/class/shortcuts.js +0 -98
- package/src/core/class/toolbar.js +0 -431
- package/src/core/class/ui.js +0 -424
- package/src/core/class/viewer.js +0 -750
- package/src/core/section/actives.js +0 -266
- package/src/core/section/context.js +0 -102
- package/src/editorInjector/_classes.js +0 -36
- package/src/editorInjector/_core.js +0 -87
- package/src/editorInjector/index.js +0 -73
- package/src/modules/ApiManager.js +0 -191
- package/src/modules/Controller.js +0 -474
- package/src/modules/Modal.js +0 -346
- package/src/modules/index.js +0 -14
- package/src/plugins/dropdown/table.js +0 -4034
- package/src/plugins/modal/image.js +0 -1376
- package/src/plugins/modal/video.js +0 -1226
- package/types/core/base/eventHandlers/handler_toolbar.d.ts +0 -41
- package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +0 -40
- package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +0 -35
- package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +0 -45
- package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +0 -39
- package/types/core/base/eventManager.d.ts +0 -401
- package/types/core/class/char.d.ts +0 -61
- package/types/core/class/component.d.ts +0 -213
- package/types/core/class/format.d.ts +0 -623
- package/types/core/class/html.d.ts +0 -430
- package/types/core/class/menu.d.ts +0 -126
- package/types/core/class/nodeTransform.d.ts +0 -93
- package/types/core/class/offset.d.ts +0 -522
- package/types/core/class/selection.d.ts +0 -188
- package/types/core/class/shortcuts.d.ts +0 -142
- package/types/core/class/toolbar.d.ts +0 -189
- package/types/core/class/ui.d.ts +0 -164
- package/types/core/class/viewer.d.ts +0 -140
- package/types/core/section/actives.d.ts +0 -46
- package/types/core/section/context.d.ts +0 -45
- package/types/editorInjector/_classes.d.ts +0 -41
- package/types/editorInjector/_core.d.ts +0 -87
- package/types/editorInjector/index.d.ts +0 -69
- package/types/modules/ApiManager.d.ts +0 -125
- package/types/modules/Browser.d.ts +0 -326
- package/types/modules/ColorPicker.d.ts +0 -135
- package/types/modules/Controller.d.ts +0 -251
- package/types/modules/Figure.d.ts +0 -517
- package/types/modules/FileManager.d.ts +0 -202
- package/types/modules/Modal.d.ts +0 -111
- package/types/modules/ModalAnchorEditor.d.ts +0 -236
- package/types/modules/SelectMenu.d.ts +0 -194
- package/types/modules/index.d.ts +0 -26
- package/types/plugins/dropdown/formatBlock.d.ts +0 -55
- package/types/plugins/dropdown/table.d.ts +0 -627
- package/types/plugins/modal/image.d.ts +0 -451
- /package/{LICENSE → LICENSE.txt} +0 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { dom } from '../../../helper';
|
|
2
|
+
import { UpdateButton } from '../../section/constructor';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} PluginEventParam
|
|
6
|
+
* @property {SunEditor.FrameContext} frameContext
|
|
7
|
+
* @property {Event} event
|
|
8
|
+
* @property {string} [data]
|
|
9
|
+
* @property {Node} [line]
|
|
10
|
+
* @property {Range} [range]
|
|
11
|
+
* @property {File} [file]
|
|
12
|
+
* @property {Document} [doc]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {(element: Node | null) => * } ComponentChecker
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @description Manages plugin registration and state.
|
|
21
|
+
* Extracts `plugin` related responsibilities from the monolithic `Editor` class.
|
|
22
|
+
*/
|
|
23
|
+
class PluginManager {
|
|
24
|
+
#kernel;
|
|
25
|
+
#$;
|
|
26
|
+
|
|
27
|
+
#plugins;
|
|
28
|
+
#contextProvider;
|
|
29
|
+
#options;
|
|
30
|
+
|
|
31
|
+
#pluginCallButtons;
|
|
32
|
+
#pluginCallButtons_sub;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @description Properties for managing files in the `FileManager` module
|
|
36
|
+
* @type {Array<*>}
|
|
37
|
+
*/
|
|
38
|
+
#fileInfoPluginsCheck = [];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @description Properties for managing files in the `FileManager` module
|
|
42
|
+
* @type {Array<*>}
|
|
43
|
+
*/
|
|
44
|
+
#fileInfoPluginsReset = [];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @description Variables for file component management
|
|
48
|
+
* @type {Object<string, *>}
|
|
49
|
+
*/
|
|
50
|
+
#fileInfo = {
|
|
51
|
+
tags: null,
|
|
52
|
+
regExp: null,
|
|
53
|
+
pluginRegExp: null,
|
|
54
|
+
pluginMap: null,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @description Variables for managing the components
|
|
59
|
+
* @type {Array<ComponentChecker>}
|
|
60
|
+
*/
|
|
61
|
+
#componentCheckers = [];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @internal
|
|
65
|
+
* @description Plugin `retainFormat` info `Map`
|
|
66
|
+
* @type {Map<string, { key: string, method: (...args: *) => * }>}
|
|
67
|
+
*/
|
|
68
|
+
#retainFormatCheckers = new Map();
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @description Plugin call event map
|
|
72
|
+
* @type {Map<string, Array<((...args: *) => *) & { index: number }>>}
|
|
73
|
+
*/
|
|
74
|
+
#onPluginEvents = new Map([
|
|
75
|
+
['onMouseMove', []],
|
|
76
|
+
['onMouseLeave', []],
|
|
77
|
+
['onMouseDown', []],
|
|
78
|
+
['onMouseUp', []],
|
|
79
|
+
['onScroll', []],
|
|
80
|
+
['onClick', []],
|
|
81
|
+
['onBeforeInput', []],
|
|
82
|
+
['onInput', []],
|
|
83
|
+
['onKeyDown', []],
|
|
84
|
+
['onKeyUp', []],
|
|
85
|
+
['onFocus', []],
|
|
86
|
+
['onBlur', []],
|
|
87
|
+
['onPaste', []],
|
|
88
|
+
['onFilePasteAndDrop', []],
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @constructor
|
|
93
|
+
* @param {SunEditor.Kernel} kernel
|
|
94
|
+
* @param {import('../../section/constructor').ConstructorReturnType} [product] - Initial editor context
|
|
95
|
+
*/
|
|
96
|
+
constructor(kernel, product) {
|
|
97
|
+
this.#kernel = kernel;
|
|
98
|
+
this.#$ = kernel.$;
|
|
99
|
+
|
|
100
|
+
this.#plugins = product.plugins || {};
|
|
101
|
+
this.#contextProvider = this.#$.contextProvider;
|
|
102
|
+
this.#options = this.#$.options;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @description List of buttons to run plugins in the toolbar
|
|
106
|
+
* @type {Object<string, Array<HTMLElement>>}
|
|
107
|
+
*/
|
|
108
|
+
this.#pluginCallButtons = product.pluginCallButtons;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @description List of buttons to run plugins in the Sub-Toolbar
|
|
112
|
+
* @type {Object<string, Array<HTMLElement>>|[]}
|
|
113
|
+
*/
|
|
114
|
+
this.#pluginCallButtons_sub = product.pluginCallButtons_sub;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @description Returns the plugins object.
|
|
119
|
+
* @returns {Object<string, *>}
|
|
120
|
+
*/
|
|
121
|
+
get plugins() {
|
|
122
|
+
return this.#plugins;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @description Returns the file component management object.
|
|
127
|
+
* @returns {Object<string, *>}
|
|
128
|
+
*/
|
|
129
|
+
get fileInfo() {
|
|
130
|
+
return this.#fileInfo;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @description Finds component information for the given element.
|
|
135
|
+
* @param {Node} element The DOM element to check.
|
|
136
|
+
* @returns {SunEditor.ComponentInfo|null}
|
|
137
|
+
*/
|
|
138
|
+
findComponentInfo(element) {
|
|
139
|
+
for (const checker of this.#componentCheckers) {
|
|
140
|
+
const result = checker(element);
|
|
141
|
+
if (result) return result;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @description Apply retain format rules from plugins to the parsed DOM
|
|
148
|
+
* @param {DocumentFragment|Document} domParser
|
|
149
|
+
*/
|
|
150
|
+
applyRetainFormat(domParser) {
|
|
151
|
+
let retainFilter;
|
|
152
|
+
if ((retainFilter = this.#options.get('__pluginRetainFilter'))) {
|
|
153
|
+
this.#retainFormatCheckers.forEach((plugin, query) => {
|
|
154
|
+
const infoLst = domParser.querySelectorAll(query);
|
|
155
|
+
for (let i = 0, len = infoLst.length; i < len; i++) {
|
|
156
|
+
if (retainFilter === true || retainFilter[plugin.key] !== false) plugin.method(infoLst[i]);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @description Dispatches an event to all registered plugin handlers synchronously.
|
|
164
|
+
* @param {string} name The event name (e.g., 'onMouseMove', 'onFocus')
|
|
165
|
+
* @param {PluginEventParam} e The event payload
|
|
166
|
+
* @returns {boolean|undefined} Returns false if any handler cancels the event
|
|
167
|
+
*/
|
|
168
|
+
emitEvent(name, e) {
|
|
169
|
+
const eventPlugins = this.#onPluginEvents.get(name);
|
|
170
|
+
for (let i = 0, r; i < eventPlugins.length; i++) {
|
|
171
|
+
r = eventPlugins[i](e);
|
|
172
|
+
if (typeof r === 'boolean') return r;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @description Dispatches an event to all registered plugin handlers asynchronously.
|
|
178
|
+
* @param {string} name The event name (e.g., 'onKeyDown', 'onPaste')
|
|
179
|
+
* @param {PluginEventParam} e The event payload
|
|
180
|
+
* @returns {Promise<boolean|undefined>} Returns false if any handler cancels the event
|
|
181
|
+
*/
|
|
182
|
+
async emitEventAsync(name, e) {
|
|
183
|
+
const eventPlugins = this.#onPluginEvents.get(name);
|
|
184
|
+
for (let i = 0, r; i < eventPlugins.length; i++) {
|
|
185
|
+
r = await eventPlugins[i](e);
|
|
186
|
+
if (typeof r === 'boolean') return r;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @description Check the components such as image and video and modify them according to the format.
|
|
192
|
+
* @param {boolean} loaded If `true`, the component is loaded.
|
|
193
|
+
*/
|
|
194
|
+
checkFileInfo(loaded) {
|
|
195
|
+
for (let i = 0, len = this.#fileInfoPluginsCheck.length; i < len; i++) {
|
|
196
|
+
this.#fileInfoPluginsCheck[i](loaded);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @description Initialize the information of the components.
|
|
202
|
+
*/
|
|
203
|
+
resetFileInfo() {
|
|
204
|
+
for (let i = 0, len = this.#fileInfoPluginsReset.length; i < len; i++) {
|
|
205
|
+
this.#fileInfoPluginsReset[i]();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @description If the plugin is not added, add the plugin and call the `add` function.
|
|
211
|
+
* - If the plugin is added call `callBack` function.
|
|
212
|
+
* @param {string} pluginName The name of the plugin to call
|
|
213
|
+
* @param {?Array<HTMLElement>} targets Plugin target button
|
|
214
|
+
* @param {?Object<string, *>} pluginOptions Plugin's options
|
|
215
|
+
*/
|
|
216
|
+
register(pluginName, targets, pluginOptions) {
|
|
217
|
+
let plugin = this.#plugins[pluginName];
|
|
218
|
+
if (!plugin) {
|
|
219
|
+
throw Error(`[SUNEDITOR.registerPlugin.fail] The called plugin does not exist or is in an invalid format. (pluginName: "${pluginName}")`);
|
|
220
|
+
} else if (typeof this.#plugins[pluginName] === 'function') {
|
|
221
|
+
plugin = this.#plugins[pluginName] = new this.#plugins[pluginName](this.#kernel, pluginOptions || {});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (targets) {
|
|
225
|
+
const { icons, lang } = this.#contextProvider;
|
|
226
|
+
for (let i = 0, len = targets.length; i < len; i++) {
|
|
227
|
+
UpdateButton(targets[i], plugin, icons, lang);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!this.#$.commandDispatcher.activeCommands.includes(pluginName) && typeof this.#plugins[pluginName].active === 'function') {
|
|
231
|
+
this.#$.commandDispatcher.activeCommands.push(pluginName);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @description Initialize the plugin manager and register plugins.
|
|
238
|
+
* @param {SunEditor.InitOptions} options
|
|
239
|
+
*/
|
|
240
|
+
init(options) {
|
|
241
|
+
this.#fileInfo.tags = [];
|
|
242
|
+
this.#fileInfo.pluginMap = {};
|
|
243
|
+
this.#fileInfo.tagAttrs = {};
|
|
244
|
+
|
|
245
|
+
const plugins = this.#plugins;
|
|
246
|
+
const filePluginRegExp = [];
|
|
247
|
+
let plugin;
|
|
248
|
+
for (const key in plugins) {
|
|
249
|
+
this.register(key, this.#pluginCallButtons[key], options[key]);
|
|
250
|
+
this.register(key, this.#pluginCallButtons_sub[key], options[key]);
|
|
251
|
+
plugin = this.#plugins[key];
|
|
252
|
+
|
|
253
|
+
// Filemanager
|
|
254
|
+
if (typeof plugin.__fileManagement === 'object') {
|
|
255
|
+
const fm = plugin.__fileManagement;
|
|
256
|
+
this.#fileInfoPluginsCheck.push(fm._checkInfo.bind(fm));
|
|
257
|
+
this.#fileInfoPluginsReset.push(fm._resetInfo.bind(fm));
|
|
258
|
+
if (Array.isArray(fm.tagNames)) {
|
|
259
|
+
const tagNames = fm.tagNames;
|
|
260
|
+
this.#fileInfo.tags = this.#fileInfo.tags.concat(tagNames);
|
|
261
|
+
filePluginRegExp.push(key);
|
|
262
|
+
for (let tag = 0, tLen = tagNames.length, t; tag < tLen; tag++) {
|
|
263
|
+
t = tagNames[tag].toLowerCase();
|
|
264
|
+
this.#fileInfo.pluginMap[t] = key;
|
|
265
|
+
if (fm.tagAttrs) {
|
|
266
|
+
this.#fileInfo.tagAttrs[t] = fm.tagAttrs;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Not file component
|
|
273
|
+
if (typeof plugin.constructor.component === 'function') {
|
|
274
|
+
this.#componentCheckers.push(
|
|
275
|
+
function (launcher, element) {
|
|
276
|
+
if (!element || !(element = launcher.component?.(element))) return null;
|
|
277
|
+
return {
|
|
278
|
+
target: element,
|
|
279
|
+
pluginName: launcher.key,
|
|
280
|
+
options: launcher.options,
|
|
281
|
+
};
|
|
282
|
+
}.bind(null, plugin.constructor),
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// plugin event
|
|
287
|
+
const pluginOptions = plugin.constructor.options || {};
|
|
288
|
+
this.#onPluginEvents.forEach((v, k) => {
|
|
289
|
+
if (typeof plugin[k] === 'function') {
|
|
290
|
+
const f = plugin[k].bind(plugin);
|
|
291
|
+
f.index = pluginOptions[`eventIndex_${k}`] || pluginOptions.eventIndex || 0;
|
|
292
|
+
v.push(f);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// plugin maintain
|
|
297
|
+
if (plugin.retainFormat) {
|
|
298
|
+
const info = plugin.retainFormat();
|
|
299
|
+
this.#retainFormatCheckers.set(info.query, { key: plugin.constructor.key, method: info.method });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (const v of this.#onPluginEvents.values()) {
|
|
304
|
+
v.sort((a, b) => a.index - b.index);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (this.#options.get('buttons').has('pageBreak') || this.#options.get('buttons_sub')?.has('pageBreak')) {
|
|
308
|
+
this.#componentCheckers.push(
|
|
309
|
+
function (focusManager, history, element) {
|
|
310
|
+
if (!element || !dom.utils.hasClass(element, 'se-page-break')) return null;
|
|
311
|
+
return {
|
|
312
|
+
target: element,
|
|
313
|
+
launcher: {
|
|
314
|
+
destroy: (target) => {
|
|
315
|
+
const focusEl = target.previousElementSibling || target.nextElementSibling;
|
|
316
|
+
dom.utils.removeItem(target);
|
|
317
|
+
// focus
|
|
318
|
+
focusManager.focusEdge(focusEl);
|
|
319
|
+
history.push(false);
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}.bind(null, this.#$.focusManager, this.#$.history),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.#fileInfo.regExp = new RegExp(`^(${this.#fileInfo.tags.join('|') || '\\^'})$`, 'i');
|
|
328
|
+
this.#fileInfo.pluginRegExp = new RegExp(`^(${filePluginRegExp.length === 0 ? '\\^' : filePluginRegExp.join('|')})$`, 'i');
|
|
329
|
+
|
|
330
|
+
this.#pluginCallButtons = null;
|
|
331
|
+
this.#pluginCallButtons_sub = null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @description Get a specific plugin instance
|
|
336
|
+
* @param {string} name
|
|
337
|
+
* @returns {*}
|
|
338
|
+
*/
|
|
339
|
+
get(name) {
|
|
340
|
+
return this.#plugins[name];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* @description Destroy the plugin manager
|
|
345
|
+
*/
|
|
346
|
+
_destroy() {
|
|
347
|
+
/** destroy plugins first (they may use editor references) */
|
|
348
|
+
for (const k in this.#plugins) {
|
|
349
|
+
const p = this.#plugins[k];
|
|
350
|
+
p._destroy?.();
|
|
351
|
+
// break circular reference: plugin.editor
|
|
352
|
+
p.editor = null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
this.#plugins = null;
|
|
356
|
+
this.#fileInfo = null;
|
|
357
|
+
this.#componentCheckers = null;
|
|
358
|
+
this.#onPluginEvents?.clear();
|
|
359
|
+
this.#retainFormatCheckers?.clear();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export default PluginManager;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { keyCodeMap } from '../../../helper';
|
|
2
|
+
import { CreateShortcuts } from '../../section/constructor';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} ShortcutInfo
|
|
6
|
+
* @property {boolean} c - Whether the [Ctrl, Command] key is pressed.
|
|
7
|
+
* @property {boolean} s - Whether the [Shift] key is pressed.
|
|
8
|
+
* @property {boolean} space - Whether the [Space] key is pressed.
|
|
9
|
+
* @property {boolean} enter - Whether the Enter key is pressed.
|
|
10
|
+
* @property {string} command - The command key. (e.g. `bold`)
|
|
11
|
+
* @property {boolean} edge - Whether the cursor is at the end of the line.
|
|
12
|
+
* @property {string} [key] - The key pressed (e.g., `1.`).
|
|
13
|
+
* @property {string} [keyCode] - The keyEvent.code.
|
|
14
|
+
* @property {string|((...args: *) => *)} [method] - A plugin's `shortcut` method that is called instead of the default `editor.run` method.
|
|
15
|
+
* @property {string} plugin - The plugin name.
|
|
16
|
+
* @property {string} type - Plugin's type. (`command`, `dropdown`, `modal`, `browser`, `input`, `field`, `popup`).
|
|
17
|
+
* @property {Node} button - The plugin command button.
|
|
18
|
+
* @property {Array<string>} r - An array of key codes generated with the reverseButtons option, used to reverse the action for a specific key combination.
|
|
19
|
+
* @property {string} textTrigger - Whether the event was triggered by a text input (e.g., mention like @ab).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @description Shortcuts class
|
|
24
|
+
*/
|
|
25
|
+
class Shortcuts {
|
|
26
|
+
#$;
|
|
27
|
+
#options;
|
|
28
|
+
|
|
29
|
+
#isDisabled = false;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @constructor
|
|
33
|
+
* @param {SunEditor.Kernel} kernel
|
|
34
|
+
*/
|
|
35
|
+
constructor(kernel) {
|
|
36
|
+
this.#$ = kernel.$;
|
|
37
|
+
this.#options = this.#$.options;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description Shortcuts key map
|
|
41
|
+
* @type {Map<string, *>}
|
|
42
|
+
*/
|
|
43
|
+
this.keyMap = new Map();
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @description Shortcuts reverse key array
|
|
47
|
+
* - An array of key codes generated with the reverseButtons option, used to reverse the action for a specific key combination.
|
|
48
|
+
* @type {Set<string>}
|
|
49
|
+
*/
|
|
50
|
+
this.reverseKeys = new Set();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @description If there is a shortcut function, run it.
|
|
55
|
+
* @param {KeyboardEvent} event Keyboard event object
|
|
56
|
+
* @param {boolean} ctrl Whether the Ctrl key is pressed
|
|
57
|
+
* @param {boolean} shift Whether the Shift key is pressed
|
|
58
|
+
* @param {string} keyCode The keyEvent.code.
|
|
59
|
+
* @param {string} text The text content of the key
|
|
60
|
+
* @param {boolean} edge Whether the cursor is at the end of the line
|
|
61
|
+
* @param {HTMLElement} line The current line node
|
|
62
|
+
* @param {Range} range The current range object
|
|
63
|
+
* @returns {boolean} Whether to execute shortcuts
|
|
64
|
+
*/
|
|
65
|
+
command(event, ctrl, shift, keyCode, text, edge, line, range) {
|
|
66
|
+
if (this.#isDisabled) return false;
|
|
67
|
+
|
|
68
|
+
/** @type {ShortcutInfo} */
|
|
69
|
+
let info = null;
|
|
70
|
+
|
|
71
|
+
if (ctrl) {
|
|
72
|
+
info = this.keyMap.get(keyCode + (shift ? '1000' : ''));
|
|
73
|
+
} else {
|
|
74
|
+
info = this.keyMap.get(text) || this.keyMap.get(text + event.key);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!info || (!shift && info.s) || (info.space && !keyCodeMap.isSpace(keyCode)) || (info.enter && !keyCodeMap.isEnter(keyCode)) || (info.textTrigger && !event.key.trim()) || (info.edge && !edge)) return false;
|
|
78
|
+
|
|
79
|
+
if (info.plugin && typeof info.method === 'string') {
|
|
80
|
+
this.#$.plugins[info.plugin][info.method]?.({ range, line, info, event, keyCode });
|
|
81
|
+
} else if (typeof info.method === 'function') {
|
|
82
|
+
info.method({ range, line, info, event, keyCode });
|
|
83
|
+
} else {
|
|
84
|
+
this.#$.commandDispatcher.run(info.command, info.type, info.button);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @description Disable the shortcut activation.
|
|
92
|
+
*/
|
|
93
|
+
disable() {
|
|
94
|
+
this.#isDisabled = true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @description Enable the shortcut activation.
|
|
99
|
+
*/
|
|
100
|
+
enable() {
|
|
101
|
+
this.#isDisabled = false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @internal
|
|
106
|
+
* @description Registers custom shortcut keys (keys starting with `_`) into the shortcut map.
|
|
107
|
+
* Called during initialization and when toolbar is reset.
|
|
108
|
+
*/
|
|
109
|
+
_registerCustomShortcuts() {
|
|
110
|
+
const shortcuts = this.#options.get('shortcuts');
|
|
111
|
+
const reverseCommandArray = this.#options.get('_reverseCommandArray');
|
|
112
|
+
const keyMap = (this.keyMap = new Map());
|
|
113
|
+
const reverseKeys = this.reverseKeys;
|
|
114
|
+
for (const key of Object.keys(shortcuts)) {
|
|
115
|
+
if (!key.startsWith('_')) continue;
|
|
116
|
+
CreateShortcuts('', null, shortcuts[key], keyMap, reverseCommandArray, reverseKeys);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @internal
|
|
122
|
+
* @description Destroy the Shortcuts instance and release memory
|
|
123
|
+
*/
|
|
124
|
+
_destroy() {
|
|
125
|
+
this.keyMap.clear();
|
|
126
|
+
this.reverseKeys.clear();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default Shortcuts;
|