@jackuait/blok 0.4.1-beta.1 → 0.4.1-beta.11
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 +138 -17
- package/codemod/README.md +45 -7
- package/codemod/migrate-editorjs-to-blok.js +960 -92
- package/codemod/test.js +780 -77
- package/dist/blok.mjs +5 -2
- package/dist/chunks/blok-oNSQ3HA6.mjs +13217 -0
- package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
- package/dist/chunks/i18next-loader-BdNRw4n4.mjs +43 -0
- package/dist/{index-CEXLTV6f.mjs → chunks/index-DHgXmfki.mjs} +2 -2
- package/dist/chunks/inline-tool-convert-CRqgjRim.mjs +1989 -0
- package/dist/chunks/messages-0tDXLuyH.mjs +48 -0
- package/dist/chunks/messages-2_xedlYw.mjs +48 -0
- package/dist/chunks/messages-AHESHJm_.mjs +48 -0
- package/dist/chunks/messages-B5hdXZwA.mjs +48 -0
- package/dist/chunks/messages-B5jGUnOy.mjs +48 -0
- package/dist/chunks/messages-B5puUm7R.mjs +48 -0
- package/dist/chunks/messages-B66ZSDCJ.mjs +48 -0
- package/dist/chunks/messages-B9Oba7sq.mjs +48 -0
- package/dist/chunks/messages-BA0rcTCY.mjs +48 -0
- package/dist/chunks/messages-BBJgd5jG.mjs +48 -0
- package/dist/chunks/messages-BPqWKx5Z.mjs +48 -0
- package/dist/chunks/messages-Bdv-IkfG.mjs +48 -0
- package/dist/chunks/messages-BeUhMpsr.mjs +48 -0
- package/dist/chunks/messages-Bf6Y3_GI.mjs +48 -0
- package/dist/chunks/messages-BiExzWJv.mjs +48 -0
- package/dist/chunks/messages-BlpqL8vG.mjs +48 -0
- package/dist/chunks/messages-BmKCChWZ.mjs +48 -0
- package/dist/chunks/messages-Bn253WWC.mjs +48 -0
- package/dist/chunks/messages-BrJHUxQL.mjs +48 -0
- package/dist/chunks/messages-C5b7hr_E.mjs +48 -0
- package/dist/chunks/messages-C7I_AVH2.mjs +48 -0
- package/dist/chunks/messages-CJoBtXU6.mjs +48 -0
- package/dist/chunks/messages-CQj2JU2j.mjs +48 -0
- package/dist/chunks/messages-CUZ1x1QD.mjs +48 -0
- package/dist/chunks/messages-CUy1vn-b.mjs +48 -0
- package/dist/chunks/messages-CVeWVKsV.mjs +48 -0
- package/dist/chunks/messages-CXHd9SUK.mjs +48 -0
- package/dist/chunks/messages-CbMyJSzS.mjs +48 -0
- package/dist/chunks/messages-CbhuIWRJ.mjs +48 -0
- package/dist/chunks/messages-CeCjVKMW.mjs +48 -0
- package/dist/chunks/messages-Cj-t1bdy.mjs +48 -0
- package/dist/chunks/messages-CkFT2gle.mjs +48 -0
- package/dist/chunks/messages-Cm9aLHeX.mjs +48 -0
- package/dist/chunks/messages-CnvW8Slp.mjs +48 -0
- package/dist/chunks/messages-Cr-RJ7YB.mjs +48 -0
- package/dist/chunks/messages-CrsJ1TEJ.mjs +48 -0
- package/dist/chunks/messages-Cu08aLS3.mjs +48 -0
- package/dist/chunks/messages-CvaqJFN-.mjs +48 -0
- package/dist/chunks/messages-CyDU5lz9.mjs +48 -0
- package/dist/chunks/messages-CySyfkMU.mjs +48 -0
- package/dist/chunks/messages-Cyi2AMmz.mjs +48 -0
- package/dist/chunks/messages-D00OjS2n.mjs +48 -0
- package/dist/chunks/messages-DDLgIPDF.mjs +48 -0
- package/dist/chunks/messages-DMQIHGRj.mjs +48 -0
- package/dist/chunks/messages-DOlC_Tty.mjs +48 -0
- package/dist/chunks/messages-DV6shA9b.mjs +48 -0
- package/dist/chunks/messages-DY94ykcE.mjs +48 -0
- package/dist/chunks/messages-DbVquYKN.mjs +48 -0
- package/dist/chunks/messages-DcKOuncK.mjs +48 -0
- package/dist/chunks/messages-Dg92dXZ5.mjs +48 -0
- package/dist/chunks/messages-DnbbyJT3.mjs +48 -0
- package/dist/chunks/messages-DteYq0rv.mjs +48 -0
- package/dist/chunks/messages-GC2PhgV3.mjs +48 -0
- package/dist/chunks/messages-JGsXAReJ.mjs +48 -0
- package/dist/chunks/messages-JZUhXTuV.mjs +48 -0
- package/dist/chunks/messages-LvFKBBPa.mjs +48 -0
- package/dist/chunks/messages-NP1myMGI.mjs +48 -0
- package/dist/chunks/messages-Q4kc_ZtL.mjs +48 -0
- package/dist/chunks/messages-RvMHb2Ht.mjs +48 -0
- package/dist/chunks/messages-ftMcCEuO.mjs +48 -0
- package/dist/chunks/messages-o24dK6CU.mjs +48 -0
- package/dist/chunks/messages-pA5TvcAj.mjs +48 -0
- package/dist/chunks/messages-rRSHQDCX.mjs +48 -0
- package/dist/chunks/messages-srxrv8Yh.mjs +48 -0
- package/dist/chunks/messages-wdqp4610.mjs +48 -0
- package/dist/chunks/messages-zS1AXZ0y.mjs +48 -0
- package/dist/chunks/messages-zSzDzXej.mjs +48 -0
- package/dist/full.mjs +50 -0
- package/dist/locales.mjs +228 -0
- package/dist/messages-0tDXLuyH.mjs +48 -0
- package/dist/messages-2_xedlYw.mjs +48 -0
- package/dist/messages-AHESHJm_.mjs +48 -0
- package/dist/messages-B5hdXZwA.mjs +48 -0
- package/dist/messages-B5jGUnOy.mjs +48 -0
- package/dist/messages-B5puUm7R.mjs +48 -0
- package/dist/messages-B66ZSDCJ.mjs +48 -0
- package/dist/messages-B9Oba7sq.mjs +48 -0
- package/dist/messages-BA0rcTCY.mjs +48 -0
- package/dist/messages-BBJgd5jG.mjs +48 -0
- package/dist/messages-BPqWKx5Z.mjs +48 -0
- package/dist/messages-Bdv-IkfG.mjs +48 -0
- package/dist/messages-BeUhMpsr.mjs +48 -0
- package/dist/messages-Bf6Y3_GI.mjs +48 -0
- package/dist/messages-BiExzWJv.mjs +48 -0
- package/dist/messages-BlpqL8vG.mjs +48 -0
- package/dist/messages-BmKCChWZ.mjs +48 -0
- package/dist/messages-Bn253WWC.mjs +48 -0
- package/dist/messages-BrJHUxQL.mjs +48 -0
- package/dist/messages-C5b7hr_E.mjs +48 -0
- package/dist/messages-C7I_AVH2.mjs +48 -0
- package/dist/messages-CJoBtXU6.mjs +48 -0
- package/dist/messages-CQj2JU2j.mjs +48 -0
- package/dist/messages-CUZ1x1QD.mjs +48 -0
- package/dist/messages-CUy1vn-b.mjs +48 -0
- package/dist/messages-CVeWVKsV.mjs +48 -0
- package/dist/messages-CXHd9SUK.mjs +48 -0
- package/dist/messages-CbMyJSzS.mjs +48 -0
- package/dist/messages-CbhuIWRJ.mjs +48 -0
- package/dist/messages-CeCjVKMW.mjs +48 -0
- package/dist/messages-Cj-t1bdy.mjs +48 -0
- package/dist/messages-CkFT2gle.mjs +48 -0
- package/dist/messages-Cm9aLHeX.mjs +48 -0
- package/dist/messages-CnvW8Slp.mjs +48 -0
- package/dist/messages-Cr-RJ7YB.mjs +48 -0
- package/dist/messages-CrsJ1TEJ.mjs +48 -0
- package/dist/messages-Cu08aLS3.mjs +48 -0
- package/dist/messages-CvaqJFN-.mjs +48 -0
- package/dist/messages-CyDU5lz9.mjs +48 -0
- package/dist/messages-CySyfkMU.mjs +48 -0
- package/dist/messages-Cyi2AMmz.mjs +48 -0
- package/dist/messages-D00OjS2n.mjs +48 -0
- package/dist/messages-DDLgIPDF.mjs +48 -0
- package/dist/messages-DMQIHGRj.mjs +48 -0
- package/dist/messages-DOlC_Tty.mjs +48 -0
- package/dist/messages-DV6shA9b.mjs +48 -0
- package/dist/messages-DY94ykcE.mjs +48 -0
- package/dist/messages-DbVquYKN.mjs +48 -0
- package/dist/messages-DcKOuncK.mjs +48 -0
- package/dist/messages-Dg92dXZ5.mjs +48 -0
- package/dist/messages-DnbbyJT3.mjs +48 -0
- package/dist/messages-DteYq0rv.mjs +48 -0
- package/dist/messages-GC2PhgV3.mjs +48 -0
- package/dist/messages-JGsXAReJ.mjs +48 -0
- package/dist/messages-JZUhXTuV.mjs +48 -0
- package/dist/messages-LvFKBBPa.mjs +48 -0
- package/dist/messages-NP1myMGI.mjs +48 -0
- package/dist/messages-Q4kc_ZtL.mjs +48 -0
- package/dist/messages-RvMHb2Ht.mjs +48 -0
- package/dist/messages-ftMcCEuO.mjs +48 -0
- package/dist/messages-o24dK6CU.mjs +48 -0
- package/dist/messages-pA5TvcAj.mjs +48 -0
- package/dist/messages-rRSHQDCX.mjs +48 -0
- package/dist/messages-srxrv8Yh.mjs +48 -0
- package/dist/messages-wdqp4610.mjs +48 -0
- package/dist/messages-zS1AXZ0y.mjs +48 -0
- package/dist/messages-zSzDzXej.mjs +48 -0
- package/dist/tools.mjs +3117 -0
- package/dist/vendor.LICENSE.txt +26 -225
- package/package.json +63 -24
- package/src/blok.ts +267 -0
- package/src/components/__module.ts +139 -0
- package/src/components/block/api.ts +155 -0
- package/src/components/block/index.ts +1428 -0
- package/src/components/block-tunes/block-tune-delete.ts +51 -0
- package/src/components/blocks.ts +352 -0
- package/src/components/constants/data-attributes.ts +344 -0
- package/src/components/constants.ts +76 -0
- package/src/components/core.ts +392 -0
- package/src/components/dom.ts +773 -0
- package/src/components/domIterator.ts +189 -0
- package/src/components/errors/critical.ts +5 -0
- package/src/components/events/BlockChanged.ts +16 -0
- package/src/components/events/BlockHovered.ts +21 -0
- package/src/components/events/BlockSettingsClosed.ts +12 -0
- package/src/components/events/BlockSettingsOpened.ts +12 -0
- package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
- package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
- package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
- package/src/components/events/HistoryStateChanged.ts +19 -0
- package/src/components/events/RedactorDomChanged.ts +14 -0
- package/src/components/events/index.ts +46 -0
- package/src/components/flipper.ts +497 -0
- package/src/components/i18n/i18next-loader.ts +84 -0
- package/src/components/i18n/lightweight-i18n.ts +86 -0
- package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
- package/src/components/i18n/locales/am/messages.json +45 -0
- package/src/components/i18n/locales/ar/messages.json +45 -0
- package/src/components/i18n/locales/az/messages.json +45 -0
- package/src/components/i18n/locales/bg/messages.json +45 -0
- package/src/components/i18n/locales/bn/messages.json +45 -0
- package/src/components/i18n/locales/bs/messages.json +45 -0
- package/src/components/i18n/locales/cs/messages.json +45 -0
- package/src/components/i18n/locales/da/messages.json +45 -0
- package/src/components/i18n/locales/de/messages.json +45 -0
- package/src/components/i18n/locales/dv/messages.json +45 -0
- package/src/components/i18n/locales/el/messages.json +45 -0
- package/src/components/i18n/locales/en/messages.json +45 -0
- package/src/components/i18n/locales/es/messages.json +45 -0
- package/src/components/i18n/locales/et/messages.json +45 -0
- package/src/components/i18n/locales/fa/messages.json +45 -0
- package/src/components/i18n/locales/fi/messages.json +45 -0
- package/src/components/i18n/locales/fil/messages.json +45 -0
- package/src/components/i18n/locales/fr/messages.json +45 -0
- package/src/components/i18n/locales/gu/messages.json +45 -0
- package/src/components/i18n/locales/he/messages.json +45 -0
- package/src/components/i18n/locales/hi/messages.json +45 -0
- package/src/components/i18n/locales/hr/messages.json +45 -0
- package/src/components/i18n/locales/hu/messages.json +45 -0
- package/src/components/i18n/locales/hy/messages.json +45 -0
- package/src/components/i18n/locales/id/messages.json +45 -0
- package/src/components/i18n/locales/index.ts +231 -0
- package/src/components/i18n/locales/it/messages.json +45 -0
- package/src/components/i18n/locales/ja/messages.json +45 -0
- package/src/components/i18n/locales/ka/messages.json +45 -0
- package/src/components/i18n/locales/km/messages.json +45 -0
- package/src/components/i18n/locales/kn/messages.json +45 -0
- package/src/components/i18n/locales/ko/messages.json +45 -0
- package/src/components/i18n/locales/ku/messages.json +45 -0
- package/src/components/i18n/locales/lo/messages.json +45 -0
- package/src/components/i18n/locales/lt/messages.json +45 -0
- package/src/components/i18n/locales/lv/messages.json +45 -0
- package/src/components/i18n/locales/mk/messages.json +45 -0
- package/src/components/i18n/locales/ml/messages.json +45 -0
- package/src/components/i18n/locales/mn/messages.json +45 -0
- package/src/components/i18n/locales/mr/messages.json +45 -0
- package/src/components/i18n/locales/ms/messages.json +45 -0
- package/src/components/i18n/locales/my/messages.json +45 -0
- package/src/components/i18n/locales/ne/messages.json +45 -0
- package/src/components/i18n/locales/nl/messages.json +45 -0
- package/src/components/i18n/locales/no/messages.json +45 -0
- package/src/components/i18n/locales/pa/messages.json +45 -0
- package/src/components/i18n/locales/pl/messages.json +45 -0
- package/src/components/i18n/locales/ps/messages.json +45 -0
- package/src/components/i18n/locales/pt/messages.json +45 -0
- package/src/components/i18n/locales/ro/messages.json +45 -0
- package/src/components/i18n/locales/ru/messages.json +45 -0
- package/src/components/i18n/locales/sd/messages.json +45 -0
- package/src/components/i18n/locales/si/messages.json +45 -0
- package/src/components/i18n/locales/sk/messages.json +45 -0
- package/src/components/i18n/locales/sl/messages.json +45 -0
- package/src/components/i18n/locales/sq/messages.json +45 -0
- package/src/components/i18n/locales/sr/messages.json +45 -0
- package/src/components/i18n/locales/sv/messages.json +45 -0
- package/src/components/i18n/locales/sw/messages.json +45 -0
- package/src/components/i18n/locales/ta/messages.json +45 -0
- package/src/components/i18n/locales/te/messages.json +45 -0
- package/src/components/i18n/locales/th/messages.json +45 -0
- package/src/components/i18n/locales/tr/messages.json +45 -0
- package/src/components/i18n/locales/ug/messages.json +45 -0
- package/src/components/i18n/locales/uk/messages.json +45 -0
- package/src/components/i18n/locales/ur/messages.json +45 -0
- package/src/components/i18n/locales/vi/messages.json +45 -0
- package/src/components/i18n/locales/yi/messages.json +45 -0
- package/src/components/i18n/locales/zh/messages.json +45 -0
- package/src/components/icons/index.ts +242 -0
- package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
- package/src/components/inline-tools/inline-tool-convert.ts +141 -0
- package/src/components/inline-tools/inline-tool-italic.ts +500 -0
- package/src/components/inline-tools/inline-tool-link.ts +539 -0
- package/src/components/modules/api/blocks.ts +377 -0
- package/src/components/modules/api/caret.ts +125 -0
- package/src/components/modules/api/events.ts +51 -0
- package/src/components/modules/api/history.ts +73 -0
- package/src/components/modules/api/i18n.ts +35 -0
- package/src/components/modules/api/index.ts +39 -0
- package/src/components/modules/api/inlineToolbar.ts +33 -0
- package/src/components/modules/api/listeners.ts +56 -0
- package/src/components/modules/api/notifier.ts +46 -0
- package/src/components/modules/api/readonly.ts +39 -0
- package/src/components/modules/api/sanitizer.ts +30 -0
- package/src/components/modules/api/saver.ts +52 -0
- package/src/components/modules/api/selection.ts +48 -0
- package/src/components/modules/api/styles.ts +72 -0
- package/src/components/modules/api/toolbar.ts +79 -0
- package/src/components/modules/api/tools.ts +16 -0
- package/src/components/modules/api/tooltip.ts +67 -0
- package/src/components/modules/api/ui.ts +36 -0
- package/src/components/modules/blockEvents.ts +1591 -0
- package/src/components/modules/blockManager.ts +1356 -0
- package/src/components/modules/blockSelection.ts +708 -0
- package/src/components/modules/caret.ts +853 -0
- package/src/components/modules/crossBlockSelection.ts +329 -0
- package/src/components/modules/dragManager.ts +1204 -0
- package/src/components/modules/history.ts +1098 -0
- package/src/components/modules/i18n.ts +332 -0
- package/src/components/modules/index.ts +139 -0
- package/src/components/modules/modificationsObserver.ts +147 -0
- package/src/components/modules/paste.ts +1092 -0
- package/src/components/modules/readonly.ts +136 -0
- package/src/components/modules/rectangleSelection.ts +711 -0
- package/src/components/modules/renderer.ts +155 -0
- package/src/components/modules/saver.ts +283 -0
- package/src/components/modules/toolbar/blockSettings.ts +781 -0
- package/src/components/modules/toolbar/index.ts +1315 -0
- package/src/components/modules/toolbar/inline.ts +956 -0
- package/src/components/modules/tools.ts +625 -0
- package/src/components/modules/ui.ts +1283 -0
- package/src/components/polyfills.ts +113 -0
- package/src/components/selection.ts +1179 -0
- package/src/components/tools/base.ts +301 -0
- package/src/components/tools/block.ts +339 -0
- package/src/components/tools/collection.ts +67 -0
- package/src/components/tools/factory.ts +138 -0
- package/src/components/tools/inline.ts +71 -0
- package/src/components/tools/tune.ts +33 -0
- package/src/components/ui/toolbox.ts +601 -0
- package/src/components/utils/announcer.ts +205 -0
- package/src/components/utils/api.ts +20 -0
- package/src/components/utils/bem.ts +26 -0
- package/src/components/utils/blocks.ts +284 -0
- package/src/components/utils/caret.ts +1067 -0
- package/src/components/utils/data-model-transform.ts +382 -0
- package/src/components/utils/events.ts +117 -0
- package/src/components/utils/keyboard.ts +60 -0
- package/src/components/utils/listeners.ts +296 -0
- package/src/components/utils/mutations.ts +39 -0
- package/src/components/utils/notifier/draw.ts +190 -0
- package/src/components/utils/notifier/index.ts +66 -0
- package/src/components/utils/notifier/types.ts +1 -0
- package/src/components/utils/notifier.ts +77 -0
- package/src/components/utils/placeholder.ts +140 -0
- package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
- package/src/components/utils/popover/components/hint/hint.ts +46 -0
- package/src/components/utils/popover/components/hint/index.ts +6 -0
- package/src/components/utils/popover/components/popover-header/index.ts +2 -0
- package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
- package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
- package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
- package/src/components/utils/popover/components/popover-item/index.ts +13 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +680 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
- package/src/components/utils/popover/components/popover-item/popover-item.ts +186 -0
- package/src/components/utils/popover/components/search-input/index.ts +2 -0
- package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
- package/src/components/utils/popover/components/search-input/search-input.ts +178 -0
- package/src/components/utils/popover/components/search-input/search-input.types.ts +59 -0
- package/src/components/utils/popover/index.ts +13 -0
- package/src/components/utils/popover/popover-abstract.ts +457 -0
- package/src/components/utils/popover/popover-desktop.ts +676 -0
- package/src/components/utils/popover/popover-inline.ts +338 -0
- package/src/components/utils/popover/popover-mobile.ts +201 -0
- package/src/components/utils/popover/popover.const.ts +81 -0
- package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
- package/src/components/utils/promise-queue.ts +43 -0
- package/src/components/utils/sanitizer.ts +537 -0
- package/src/components/utils/scroll-locker.ts +87 -0
- package/src/components/utils/shortcut.ts +231 -0
- package/src/components/utils/shortcuts.ts +113 -0
- package/src/components/utils/tools.ts +110 -0
- package/src/components/utils/tooltip.ts +591 -0
- package/src/components/utils/tw.ts +241 -0
- package/src/components/utils.ts +1081 -0
- package/src/env.d.ts +13 -0
- package/src/full.ts +69 -0
- package/src/locales.ts +51 -0
- package/src/stories/Block.stories.ts +498 -0
- package/src/stories/EditorModes.stories.ts +505 -0
- package/src/stories/Header.stories.ts +137 -0
- package/src/stories/InlineToolbar.stories.ts +498 -0
- package/src/stories/List.stories.ts +259 -0
- package/src/stories/Notifier.stories.ts +340 -0
- package/src/stories/Paragraph.stories.ts +112 -0
- package/src/stories/Placeholder.stories.ts +319 -0
- package/src/stories/Popover.stories.ts +844 -0
- package/src/stories/Selection.stories.ts +250 -0
- package/src/stories/StubBlock.stories.ts +156 -0
- package/src/stories/Toolbar.stories.ts +223 -0
- package/src/stories/Toolbox.stories.ts +166 -0
- package/src/stories/Tooltip.stories.ts +198 -0
- package/src/stories/helpers.ts +463 -0
- package/src/styles/main.css +123 -0
- package/src/tools/header/index.ts +646 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/list/index.ts +1819 -0
- package/src/tools/paragraph/index.ts +412 -0
- package/src/tools/stub/index.ts +107 -0
- package/src/types-internal/blok-modules.d.ts +87 -0
- package/src/types-internal/html-janitor.d.ts +28 -0
- package/src/types-internal/module-config.d.ts +11 -0
- package/src/variants/all-locales.ts +155 -0
- package/src/variants/blok-maximum.ts +20 -0
- package/src/variants/blok-minimum.ts +243 -0
- package/types/api/blocks.d.ts +9 -1
- package/types/api/history.d.ts +7 -0
- package/types/api/i18n.d.ts +22 -3
- package/types/api/selection.d.ts +6 -0
- package/types/api/styles.d.ts +23 -10
- package/types/configs/blok-config.d.ts +29 -0
- package/types/configs/i18n-config.d.ts +52 -2
- package/types/configs/i18n-dictionary.d.ts +16 -90
- package/types/data-attributes.d.ts +170 -0
- package/types/data-formats/output-data.d.ts +15 -0
- package/types/full.d.ts +80 -0
- package/types/index.d.ts +29 -13
- package/types/locales.d.ts +59 -0
- package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
- package/types/tools/block-tool.d.ts +9 -0
- package/types/tools/header.d.ts +18 -0
- package/types/tools/index.d.ts +1 -0
- package/types/tools/list.d.ts +91 -0
- package/types/tools/paragraph.d.ts +71 -0
- package/types/tools/tool-settings.d.ts +92 -6
- package/types/tools/tool.d.ts +6 -0
- package/types/tools-entry.d.ts +49 -0
- package/types/utils/popover/popover-item.d.ts +18 -5
- package/types/utils/popover/popover.d.ts +7 -0
- package/dist/blok-C8XbyLHh.mjs +0 -25795
- package/dist/blok.umd.js +0 -181
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
import { Module } from '../../__module';
|
|
2
|
+
import { Dom as $ } from '../../dom';
|
|
3
|
+
import { SelectionUtils } from '../../selection';
|
|
4
|
+
import type { Block } from '../../block';
|
|
5
|
+
import { Flipper } from '../../flipper';
|
|
6
|
+
import type { MenuConfigItem } from '../../../../types/tools';
|
|
7
|
+
import type { PopoverItemParams } from '../../utils/popover';
|
|
8
|
+
import { type Popover, PopoverDesktop, PopoverMobile, PopoverItemType } from '../../utils/popover';
|
|
9
|
+
import type { PopoverParams } from '@/types/utils/popover/popover';
|
|
10
|
+
import { PopoverEvent } from '@/types/utils/popover/popover-event';
|
|
11
|
+
import { isMobileScreen, keyCodes } from '../../utils';
|
|
12
|
+
import { css as popoverItemCls } from '../../utils/popover/components/popover-item';
|
|
13
|
+
import { BlockSettingsClosed, BlockSettingsOpened, BlokMobileLayoutToggled } from '../../events';
|
|
14
|
+
import { IconReplace, IconCross } from '../../icons';
|
|
15
|
+
import { getConvertibleToolsForBlock, getConvertibleToolsForBlocks } from '../../utils/blocks';
|
|
16
|
+
import { translateToolTitle } from '../../utils/tools';
|
|
17
|
+
import { BlockAPI } from '../../block/api';
|
|
18
|
+
import type { BlockToolAdapter } from '../../tools/block';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* HTML Elements that used for BlockSettings
|
|
22
|
+
*/
|
|
23
|
+
interface BlockSettingsNodes {
|
|
24
|
+
/**
|
|
25
|
+
* Block Settings wrapper. Undefined when before "make" method called
|
|
26
|
+
*/
|
|
27
|
+
wrapper: HTMLElement | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Block Settings
|
|
32
|
+
* @todo Make Block Settings no-module but a standalone class, like Toolbox
|
|
33
|
+
*/
|
|
34
|
+
export class BlockSettings extends Module<BlockSettingsNodes> {
|
|
35
|
+
/**
|
|
36
|
+
* Module Events
|
|
37
|
+
*/
|
|
38
|
+
public get events(): { opened: typeof BlockSettingsOpened; closed: typeof BlockSettingsClosed } {
|
|
39
|
+
return {
|
|
40
|
+
opened: BlockSettingsOpened,
|
|
41
|
+
closed: BlockSettingsClosed,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Block Settings CSS
|
|
47
|
+
* @deprecated Use data attributes for identification instead
|
|
48
|
+
*/
|
|
49
|
+
public get CSS(): { [name: string]: string } {
|
|
50
|
+
return {
|
|
51
|
+
settings: '',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Opened state
|
|
57
|
+
*/
|
|
58
|
+
public opened = false;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Getter for inner popover's flipper instance
|
|
62
|
+
* @todo remove once BlockSettings becomes standalone non-module class
|
|
63
|
+
*/
|
|
64
|
+
public get flipper(): Flipper {
|
|
65
|
+
return this.flipperInstance;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Page selection utils
|
|
70
|
+
*/
|
|
71
|
+
private selection: SelectionUtils = new SelectionUtils();
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Popover instance. There is a util for vertical lists.
|
|
75
|
+
* Null until popover is not initialized
|
|
76
|
+
*/
|
|
77
|
+
private popover: Popover | null = null;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Shared flipper instance used for keyboard navigation in block settings popover
|
|
81
|
+
*/
|
|
82
|
+
private readonly flipperInstance: Flipper = new Flipper({
|
|
83
|
+
focusedItemClass: popoverItemCls.focused,
|
|
84
|
+
allowedKeys: [
|
|
85
|
+
keyCodes.TAB,
|
|
86
|
+
keyCodes.UP,
|
|
87
|
+
keyCodes.DOWN,
|
|
88
|
+
keyCodes.ENTER,
|
|
89
|
+
keyCodes.RIGHT,
|
|
90
|
+
keyCodes.LEFT,
|
|
91
|
+
],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Stored keydown handler reference to detach when block tunes are closed
|
|
96
|
+
*/
|
|
97
|
+
private flipperKeydownHandler: ((event: KeyboardEvent) => void) | null = null;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Element that listens for keydown events while block tunes are opened
|
|
101
|
+
*/
|
|
102
|
+
private flipperKeydownSource: HTMLElement | null = null;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Panel with block settings with 2 sections:
|
|
106
|
+
* - Tool's Settings
|
|
107
|
+
* - Default Settings [Move, Remove, etc]
|
|
108
|
+
*/
|
|
109
|
+
public make(): void {
|
|
110
|
+
this.nodes.wrapper = $.make('div');
|
|
111
|
+
this.nodes.wrapper.setAttribute('data-blok-testid', 'block-tunes-wrapper');
|
|
112
|
+
|
|
113
|
+
this.eventsDispatcher.on(BlokMobileLayoutToggled, this.close);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Destroys module
|
|
118
|
+
*/
|
|
119
|
+
public destroy(): void {
|
|
120
|
+
this.detachFlipperKeydownListener();
|
|
121
|
+
this.removeAllNodes();
|
|
122
|
+
this.listeners.destroy();
|
|
123
|
+
this.eventsDispatcher.off(BlokMobileLayoutToggled, this.close);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Open Block Settings pane
|
|
128
|
+
* @param targetBlock - near which Block we should open BlockSettings
|
|
129
|
+
* @param trigger - element to position the popover relative to
|
|
130
|
+
*/
|
|
131
|
+
public async open(targetBlock?: Block, trigger?: HTMLElement): Promise<void> {
|
|
132
|
+
const selectedBlocks = this.Blok.BlockSelection.selectedBlocks;
|
|
133
|
+
const hasMultipleBlocksSelected = selectedBlocks.length > 1;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* When multiple blocks are selected, use the first selected block as the anchor
|
|
137
|
+
* Otherwise, use the target block or current block
|
|
138
|
+
*/
|
|
139
|
+
const block = hasMultipleBlocksSelected
|
|
140
|
+
? selectedBlocks[0]
|
|
141
|
+
: (targetBlock ?? this.Blok.BlockManager.currentBlock);
|
|
142
|
+
|
|
143
|
+
if (block === undefined) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* If block settings contains any inputs, focus will be set there,
|
|
149
|
+
* so we need to save current selection to restore it after block settings is closed
|
|
150
|
+
*/
|
|
151
|
+
this.selection.save();
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Highlight content of a Block we are working with
|
|
155
|
+
* For multiple blocks, they should already be selected
|
|
156
|
+
*/
|
|
157
|
+
if (!hasMultipleBlocksSelected) {
|
|
158
|
+
this.Blok.BlockSelection.selectBlock(block);
|
|
159
|
+
this.Blok.BlockSelection.clearCache();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Get tool's settings data - only relevant for single block selection */
|
|
163
|
+
const { toolTunes, commonTunes } = block.getTunes();
|
|
164
|
+
|
|
165
|
+
const PopoverClass = isMobileScreen() ? PopoverMobile : PopoverDesktop;
|
|
166
|
+
const popoverParams: PopoverParams & { flipper?: Flipper } = {
|
|
167
|
+
searchable: true,
|
|
168
|
+
trigger: trigger || this.nodes.wrapper,
|
|
169
|
+
items: await this.getTunesItems(block, commonTunes, toolTunes),
|
|
170
|
+
scopeElement: this.Blok.API.methods.ui.nodes.redactor,
|
|
171
|
+
messages: {
|
|
172
|
+
nothingFound: this.Blok.I18n.t('popover.nothingFound'),
|
|
173
|
+
search: this.Blok.I18n.t('popover.search'),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (PopoverClass === PopoverDesktop) {
|
|
178
|
+
popoverParams.flipper = this.flipperInstance;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.popover = new PopoverClass(popoverParams);
|
|
182
|
+
this.popover.getElement().setAttribute('data-blok-testid', 'block-tunes-popover');
|
|
183
|
+
|
|
184
|
+
this.popover.on(PopoverEvent.Closed, this.onPopoverClose);
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Set opened flag AFTER popover is created to prevent race conditions
|
|
188
|
+
* where close() is called during the async getTunesItems() call
|
|
189
|
+
* when opened=true but popover is still null
|
|
190
|
+
*/
|
|
191
|
+
this.opened = true;
|
|
192
|
+
|
|
193
|
+
/** Tell to subscribers that block settings is opened */
|
|
194
|
+
this.eventsDispatcher.emit(this.events.opened);
|
|
195
|
+
|
|
196
|
+
this.popover.show();
|
|
197
|
+
this.attachFlipperKeydownListener(block);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Returns root block settings element
|
|
202
|
+
*/
|
|
203
|
+
public getElement(): HTMLElement | undefined {
|
|
204
|
+
return this.nodes.wrapper;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Checks if the element is contained in the BlockSettings or its Popover
|
|
209
|
+
* @param element - element to check
|
|
210
|
+
*/
|
|
211
|
+
public contains(element: HTMLElement): boolean {
|
|
212
|
+
if (this.nodes.wrapper?.contains(element)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.popover?.hasNode(element)) {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Close Block Settings pane
|
|
225
|
+
*/
|
|
226
|
+
public close = (): void => {
|
|
227
|
+
if (!this.opened) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.opened = false;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* If selection is at blok on Block Settings closing,
|
|
235
|
+
* it means that caret placed at some editable element inside the Block Settings.
|
|
236
|
+
* Previously we have saved the selection, then open the Block Settings and set caret to the input
|
|
237
|
+
*
|
|
238
|
+
* So, we need to restore selection back to Block after closing the Block Settings
|
|
239
|
+
*/
|
|
240
|
+
if (!SelectionUtils.isAtBlok) {
|
|
241
|
+
this.selection.restore();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.selection.clearSaved();
|
|
245
|
+
this.detachFlipperKeydownListener();
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Remove highlighted content of Blocks we are working with
|
|
249
|
+
* Handle both single and multiple block selection
|
|
250
|
+
*/
|
|
251
|
+
this.clearBlockSelectionOnClose();
|
|
252
|
+
|
|
253
|
+
/** Tell to subscribers that block settings is closed */
|
|
254
|
+
this.eventsDispatcher.emit(this.events.closed);
|
|
255
|
+
|
|
256
|
+
if (this.popover) {
|
|
257
|
+
this.popover.off(PopoverEvent.Closed, this.onPopoverClose);
|
|
258
|
+
this.popover.destroy();
|
|
259
|
+
this.popover.getElement().remove();
|
|
260
|
+
this.popover = null;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Returns list of items to be displayed in block tunes menu.
|
|
266
|
+
* Merges tool specific tunes, conversion menu and common tunes in one list in predefined order
|
|
267
|
+
* @param currentBlock – block we are about to open block tunes for
|
|
268
|
+
* @param commonTunes – common tunes
|
|
269
|
+
* @param toolTunes - tool specific tunes
|
|
270
|
+
*/
|
|
271
|
+
private async getTunesItems(currentBlock: Block, commonTunes: MenuConfigItem[], toolTunes?: MenuConfigItem[]): Promise<PopoverItemParams[]> {
|
|
272
|
+
const items = [] as MenuConfigItem[];
|
|
273
|
+
const selectedBlocks = this.Blok.BlockSelection.selectedBlocks;
|
|
274
|
+
const hasMultipleBlocksSelected = selectedBlocks.length > 1;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Only show tool-specific tunes when a single block is selected
|
|
278
|
+
*/
|
|
279
|
+
if (!hasMultipleBlocksSelected && toolTunes !== undefined && toolTunes.length > 0) {
|
|
280
|
+
items.push(...toolTunes);
|
|
281
|
+
items.push({
|
|
282
|
+
type: PopoverItemType.Separator,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const allBlockTools = Array.from(this.Blok.Tools.blockTools.values());
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get convertible tools based on selection:
|
|
290
|
+
* - For single block: use existing single-block conversion logic
|
|
291
|
+
* - For multiple blocks: find tools that ALL selected blocks can convert to
|
|
292
|
+
*/
|
|
293
|
+
const convertibleTools = hasMultipleBlocksSelected
|
|
294
|
+
? await getConvertibleToolsForBlocks(
|
|
295
|
+
selectedBlocks.map((block) => new BlockAPI(block)),
|
|
296
|
+
allBlockTools
|
|
297
|
+
)
|
|
298
|
+
: await getConvertibleToolsForBlock(currentBlock, allBlockTools);
|
|
299
|
+
|
|
300
|
+
const convertToItems = convertibleTools.reduce<PopoverItemParams[]>((result, tool) => {
|
|
301
|
+
if (tool.toolbox === undefined) {
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
tool.toolbox.forEach((toolboxItem) => {
|
|
306
|
+
result.push({
|
|
307
|
+
icon: toolboxItem.icon,
|
|
308
|
+
title: translateToolTitle(this.Blok.I18n, toolboxItem, tool.name),
|
|
309
|
+
name: toolboxItem.name ?? tool.name,
|
|
310
|
+
closeOnActivate: true,
|
|
311
|
+
onActivate: async () => {
|
|
312
|
+
const { Caret, Toolbar } = this.Blok;
|
|
313
|
+
|
|
314
|
+
const newBlock = await this.convertBlock(
|
|
315
|
+
currentBlock,
|
|
316
|
+
selectedBlocks,
|
|
317
|
+
hasMultipleBlocksSelected,
|
|
318
|
+
tool,
|
|
319
|
+
toolboxItem.data
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
Toolbar.close();
|
|
323
|
+
|
|
324
|
+
if (newBlock) {
|
|
325
|
+
Caret.setToBlock(newBlock, Caret.positions.END);
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return result;
|
|
332
|
+
}, []);
|
|
333
|
+
|
|
334
|
+
if (convertToItems.length > 0) {
|
|
335
|
+
items.push({
|
|
336
|
+
icon: IconReplace,
|
|
337
|
+
name: 'convert-to',
|
|
338
|
+
title: this.Blok.I18n.t('popover.convertTo'),
|
|
339
|
+
children: {
|
|
340
|
+
items: convertToItems,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
items.push({
|
|
344
|
+
type: PopoverItemType.Separator,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* For single block selection, show all common tunes (delete, move, etc.)
|
|
350
|
+
* For multiple blocks, only show delete option with multi-block delete behavior
|
|
351
|
+
*/
|
|
352
|
+
if (!hasMultipleBlocksSelected) {
|
|
353
|
+
items.push(...commonTunes);
|
|
354
|
+
} else {
|
|
355
|
+
items.push({
|
|
356
|
+
icon: IconCross,
|
|
357
|
+
title: this.Blok.I18n.t('blockSettings.delete'),
|
|
358
|
+
name: 'delete',
|
|
359
|
+
closeOnActivate: true,
|
|
360
|
+
onActivate: () => {
|
|
361
|
+
const { BlockManager, Caret, Toolbar } = this.Blok;
|
|
362
|
+
const indexToInsert = BlockManager.removeSelectedBlocks();
|
|
363
|
+
|
|
364
|
+
if (indexToInsert !== undefined && BlockManager.blocks.length === 0) {
|
|
365
|
+
BlockManager.insert();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const currentBlock = BlockManager.currentBlock;
|
|
369
|
+
|
|
370
|
+
if (currentBlock) {
|
|
371
|
+
Caret.setToBlock(currentBlock, Caret.positions.END);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
Toolbar.close();
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return items;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Handles popover close event
|
|
384
|
+
*/
|
|
385
|
+
private onPopoverClose = (): void => {
|
|
386
|
+
this.close();
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Clears block selection when block settings is closed
|
|
391
|
+
* Handles both single and multiple block selection scenarios
|
|
392
|
+
*/
|
|
393
|
+
private clearBlockSelectionOnClose(): void {
|
|
394
|
+
if (this.Blok.CrossBlockSelection.isCrossBlockSelectionStarted) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const selectedBlocks = this.Blok.BlockSelection.selectedBlocks;
|
|
399
|
+
const hasMultipleBlocksSelected = selectedBlocks.length > 1;
|
|
400
|
+
|
|
401
|
+
if (hasMultipleBlocksSelected) {
|
|
402
|
+
this.Blok.BlockSelection.allBlocksSelected = false;
|
|
403
|
+
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const currentBlock = this.Blok.BlockManager.currentBlock;
|
|
408
|
+
|
|
409
|
+
if (currentBlock) {
|
|
410
|
+
this.Blok.BlockSelection.unselectBlock(currentBlock);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Converts multiple selected blocks to a target tool type.
|
|
416
|
+
* For tools that support multi-item data (like lists), all blocks are combined into a single block.
|
|
417
|
+
* Otherwise, each block is converted individually and remains as a separate block.
|
|
418
|
+
* @param blocks - array of blocks to convert
|
|
419
|
+
* @param targetToolName - name of the tool to convert to
|
|
420
|
+
* @param toolboxData - optional data overrides for the new blocks
|
|
421
|
+
* @returns the resulting block (merged or last converted) or null if all conversions failed
|
|
422
|
+
*/
|
|
423
|
+
private async convertBlock(
|
|
424
|
+
currentBlock: Block,
|
|
425
|
+
selectedBlocks: Block[],
|
|
426
|
+
hasMultipleBlocksSelected: boolean,
|
|
427
|
+
tool: BlockToolAdapter,
|
|
428
|
+
toolboxData?: Record<string, unknown>
|
|
429
|
+
): Promise<Block | null> {
|
|
430
|
+
const { BlockManager } = this.Blok;
|
|
431
|
+
|
|
432
|
+
if (hasMultipleBlocksSelected) {
|
|
433
|
+
return this.convertMultipleBlocks(selectedBlocks, tool.name, toolboxData);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Check if we should explode a multi-item block (like List) into separate blocks
|
|
438
|
+
* This happens when converting to a tool that doesn't support multiple items
|
|
439
|
+
*/
|
|
440
|
+
const explodableItems = await this.getExplodableItems(currentBlock);
|
|
441
|
+
const shouldExplode = !this.canToolMergeMultipleItems(tool) && explodableItems !== null;
|
|
442
|
+
|
|
443
|
+
if (shouldExplode) {
|
|
444
|
+
return this.convertMultiItemBlockToSeparateBlocks(currentBlock, tool.name, toolboxData);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return BlockManager.convert(currentBlock, tool.name, toolboxData);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Converts multiple selected blocks to a target tool type.
|
|
452
|
+
* For tools that support multi-item data (like lists), all blocks are combined into a single block.
|
|
453
|
+
* Otherwise, each block is converted individually and remains as a separate block.
|
|
454
|
+
* @param blocks - array of blocks to convert
|
|
455
|
+
* @param targetToolName - name of the tool to convert to
|
|
456
|
+
* @param toolboxData - optional data overrides for the new blocks
|
|
457
|
+
* @returns the resulting block (merged or last converted) or null if all conversions failed
|
|
458
|
+
*/
|
|
459
|
+
private async convertMultipleBlocks(
|
|
460
|
+
blocks: Block[],
|
|
461
|
+
targetToolName: string,
|
|
462
|
+
toolboxData?: Record<string, unknown>
|
|
463
|
+
): Promise<Block | null> {
|
|
464
|
+
const { Tools } = this.Blok;
|
|
465
|
+
|
|
466
|
+
if (blocks.length === 0) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Check if the target tool's conversion config import function can handle
|
|
472
|
+
* newline-separated content to create multiple items (like lists do).
|
|
473
|
+
* We detect this by checking if the import function returns data with an 'items' array.
|
|
474
|
+
*/
|
|
475
|
+
const targetTool = Tools.blockTools.get(targetToolName);
|
|
476
|
+
const shouldMergeIntoSingleBlock = targetTool && this.canToolMergeMultipleItems(targetTool);
|
|
477
|
+
|
|
478
|
+
if (shouldMergeIntoSingleBlock) {
|
|
479
|
+
return this.convertBlocksToSingleMergedBlock(blocks, targetToolName, toolboxData);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Convert each block individually, maintaining them as separate blocks
|
|
484
|
+
*/
|
|
485
|
+
return this.convertBlocksIndividually(blocks, targetToolName, toolboxData);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Checks if a tool can merge multiple items into a single block.
|
|
490
|
+
* This is determined by testing if the tool's import function creates an 'items' array.
|
|
491
|
+
* @param tool - the target tool adapter
|
|
492
|
+
* @returns true if the tool supports merging multiple items
|
|
493
|
+
*/
|
|
494
|
+
private canToolMergeMultipleItems(tool: BlockToolAdapter): boolean {
|
|
495
|
+
const conversionConfig = tool.conversionConfig;
|
|
496
|
+
|
|
497
|
+
if (!conversionConfig?.import) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Test the import function with a sample multi-line string
|
|
503
|
+
* to see if it creates multiple items
|
|
504
|
+
*/
|
|
505
|
+
try {
|
|
506
|
+
const testResult = typeof conversionConfig.import === 'function'
|
|
507
|
+
? conversionConfig.import('line1\nline2', tool.settings)
|
|
508
|
+
: { [conversionConfig.import]: 'line1\nline2' };
|
|
509
|
+
|
|
510
|
+
return Array.isArray(testResult?.items) && testResult.items.length > 1;
|
|
511
|
+
} catch {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Converts multiple blocks into a single merged block by combining their exported content.
|
|
518
|
+
* Used for tools like lists that can hold multiple items.
|
|
519
|
+
* @param blocks - blocks to convert and merge
|
|
520
|
+
* @param targetToolName - name of the tool to convert to
|
|
521
|
+
* @param toolboxData - optional data overrides
|
|
522
|
+
* @returns the merged block or null if conversion failed
|
|
523
|
+
*/
|
|
524
|
+
private async convertBlocksToSingleMergedBlock(
|
|
525
|
+
blocks: Block[],
|
|
526
|
+
targetToolName: string,
|
|
527
|
+
toolboxData?: Record<string, unknown>
|
|
528
|
+
): Promise<Block | null> {
|
|
529
|
+
const { BlockManager } = this.Blok;
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Export all blocks' content and combine with newlines
|
|
533
|
+
*/
|
|
534
|
+
const exportedContents: string[] = [];
|
|
535
|
+
|
|
536
|
+
for (const block of blocks) {
|
|
537
|
+
try {
|
|
538
|
+
const content = await block.exportDataAsString();
|
|
539
|
+
|
|
540
|
+
exportedContents.push(content);
|
|
541
|
+
} catch {
|
|
542
|
+
// Skip blocks that fail to export
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (exportedContents.length === 0) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Convert the first block with combined content
|
|
552
|
+
*/
|
|
553
|
+
const firstBlock = blocks[0];
|
|
554
|
+
const combinedContent = exportedContents.join('\n');
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Get the target tool to use its conversion config
|
|
558
|
+
*/
|
|
559
|
+
const targetTool = this.Blok.Tools.blockTools.get(targetToolName);
|
|
560
|
+
|
|
561
|
+
if (!targetTool) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Import the combined content using the target tool's conversion config
|
|
567
|
+
*/
|
|
568
|
+
const importedData = typeof targetTool.conversionConfig?.import === 'function'
|
|
569
|
+
? targetTool.conversionConfig.import(combinedContent, targetTool.settings)
|
|
570
|
+
: { [targetTool.conversionConfig?.import as string]: combinedContent };
|
|
571
|
+
|
|
572
|
+
const newBlockData = toolboxData
|
|
573
|
+
? Object.assign(importedData, toolboxData)
|
|
574
|
+
: importedData;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Replace the first block with the new merged block
|
|
578
|
+
*/
|
|
579
|
+
const newBlock = BlockManager.replace(firstBlock, targetToolName, newBlockData);
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Remove the remaining blocks (they've been merged into the first one)
|
|
583
|
+
*/
|
|
584
|
+
const remainingBlocks = blocks.slice(1);
|
|
585
|
+
|
|
586
|
+
for (const block of remainingBlocks) {
|
|
587
|
+
await BlockManager.removeBlock(block, false);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return newBlock;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Converts blocks individually, keeping them as separate blocks.
|
|
595
|
+
* @param blocks - blocks to convert
|
|
596
|
+
* @param targetToolName - name of the tool to convert to
|
|
597
|
+
* @param toolboxData - optional data overrides
|
|
598
|
+
* @returns the last converted block or null if all conversions failed
|
|
599
|
+
*/
|
|
600
|
+
private async convertBlocksIndividually(
|
|
601
|
+
blocks: Block[],
|
|
602
|
+
targetToolName: string,
|
|
603
|
+
toolboxData?: Record<string, unknown>
|
|
604
|
+
): Promise<Block | null> {
|
|
605
|
+
const { BlockManager } = this.Blok;
|
|
606
|
+
const convertedBlocks: Block[] = [];
|
|
607
|
+
|
|
608
|
+
for (const block of blocks) {
|
|
609
|
+
const convertedBlock = await this.convertBlockSafely(BlockManager, block, targetToolName, toolboxData);
|
|
610
|
+
|
|
611
|
+
if (convertedBlock) {
|
|
612
|
+
convertedBlocks.push(convertedBlock);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return convertedBlocks.length > 0
|
|
617
|
+
? convertedBlocks[convertedBlocks.length - 1]
|
|
618
|
+
: null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Safely converts a single block, catching any errors
|
|
623
|
+
* @param blockManager - the block manager instance
|
|
624
|
+
* @param block - block to convert
|
|
625
|
+
* @param targetToolName - name of the tool to convert to
|
|
626
|
+
* @param toolboxData - optional data overrides
|
|
627
|
+
* @returns the converted block or null if conversion failed
|
|
628
|
+
*/
|
|
629
|
+
private async convertBlockSafely(
|
|
630
|
+
blockManager: typeof this.Blok.BlockManager,
|
|
631
|
+
block: Block,
|
|
632
|
+
targetToolName: string,
|
|
633
|
+
toolboxData?: Record<string, unknown>
|
|
634
|
+
): Promise<Block | null> {
|
|
635
|
+
try {
|
|
636
|
+
return await blockManager.convert(block, targetToolName, toolboxData);
|
|
637
|
+
} catch (e) {
|
|
638
|
+
console.warn(`Failed to convert block ${block.id}:`, e);
|
|
639
|
+
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Checks if a block contains multiple items that should be exploded into separate blocks
|
|
646
|
+
* when converting to a single-item tool.
|
|
647
|
+
* @param block - block to check
|
|
648
|
+
* @returns array of content strings if block should be exploded, null otherwise
|
|
649
|
+
*/
|
|
650
|
+
private async getExplodableItems(block: Block): Promise<string[] | null> {
|
|
651
|
+
try {
|
|
652
|
+
const blockData = await block.data;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Check if block has an 'items' array with multiple items (like List tool)
|
|
656
|
+
*/
|
|
657
|
+
if (!Array.isArray(blockData?.items) || blockData.items.length <= 1) {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Extract content from each item, handling nested items recursively
|
|
663
|
+
*/
|
|
664
|
+
const extractContent = (items: Array<{ content?: string; items?: unknown[] }>): string[] => {
|
|
665
|
+
const contents: string[] = [];
|
|
666
|
+
|
|
667
|
+
for (const item of items) {
|
|
668
|
+
if (item.content !== undefined && item.content !== '') {
|
|
669
|
+
contents.push(item.content);
|
|
670
|
+
}
|
|
671
|
+
if (Array.isArray(item.items) && item.items.length > 0) {
|
|
672
|
+
contents.push(...extractContent(item.items as Array<{ content?: string; items?: unknown[] }>));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return contents;
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
return extractContent(blockData.items);
|
|
680
|
+
} catch {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Converts a multi-item block (like List) into multiple single-item blocks.
|
|
687
|
+
* Each item becomes a separate block of the target type.
|
|
688
|
+
* @param block - block to convert
|
|
689
|
+
* @param targetToolName - name of the tool to convert to
|
|
690
|
+
* @param toolboxData - optional data overrides
|
|
691
|
+
* @returns the last created block or null if conversion failed
|
|
692
|
+
*/
|
|
693
|
+
private async convertMultiItemBlockToSeparateBlocks(
|
|
694
|
+
block: Block,
|
|
695
|
+
targetToolName: string,
|
|
696
|
+
toolboxData?: Record<string, unknown>
|
|
697
|
+
): Promise<Block | null> {
|
|
698
|
+
const { BlockManager, Tools } = this.Blok;
|
|
699
|
+
const items = await this.getExplodableItems(block);
|
|
700
|
+
|
|
701
|
+
if (!items || items.length === 0) {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const targetTool = Tools.blockTools.get(targetToolName);
|
|
706
|
+
const conversionImport = targetTool?.conversionConfig?.import;
|
|
707
|
+
|
|
708
|
+
if (!conversionImport) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const blockIndex = BlockManager.getBlockIndex(block);
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Remove the original block first
|
|
716
|
+
*/
|
|
717
|
+
await BlockManager.removeBlock(block, false);
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Create a new block for each item
|
|
721
|
+
*/
|
|
722
|
+
const createdBlocks = items.map((content, index) => {
|
|
723
|
+
/**
|
|
724
|
+
* Import the content using the target tool's conversion config
|
|
725
|
+
*/
|
|
726
|
+
const importedData = typeof conversionImport === 'function'
|
|
727
|
+
? conversionImport(content, targetTool?.settings)
|
|
728
|
+
: { [conversionImport as string]: content };
|
|
729
|
+
|
|
730
|
+
const newBlockData = toolboxData
|
|
731
|
+
? Object.assign(importedData, toolboxData)
|
|
732
|
+
: importedData;
|
|
733
|
+
|
|
734
|
+
return BlockManager.insert({
|
|
735
|
+
tool: targetToolName,
|
|
736
|
+
data: newBlockData,
|
|
737
|
+
index: blockIndex + index,
|
|
738
|
+
needToFocus: false,
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
return createdBlocks.length > 0 ? createdBlocks[createdBlocks.length - 1] : null;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Attaches keydown listener to delegate navigation events to the shared flipper
|
|
747
|
+
* @param block - block that owns the currently focused content
|
|
748
|
+
*/
|
|
749
|
+
private attachFlipperKeydownListener(block: Block): void {
|
|
750
|
+
this.detachFlipperKeydownListener();
|
|
751
|
+
|
|
752
|
+
const pluginsContent = block?.pluginsContent;
|
|
753
|
+
|
|
754
|
+
if (!(pluginsContent instanceof HTMLElement)) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
this.flipperInstance.setHandleContentEditableTargets(true);
|
|
759
|
+
|
|
760
|
+
this.flipperKeydownHandler = (event: KeyboardEvent) => {
|
|
761
|
+
this.flipperInstance.handleExternalKeydown(event);
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
pluginsContent.addEventListener('keydown', this.flipperKeydownHandler, true);
|
|
765
|
+
this.flipperKeydownSource = pluginsContent;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Removes keydown listener from the previously active block
|
|
770
|
+
*/
|
|
771
|
+
private detachFlipperKeydownListener(): void {
|
|
772
|
+
if (this.flipperKeydownSource !== null && this.flipperKeydownHandler !== null) {
|
|
773
|
+
this.flipperKeydownSource.removeEventListener('keydown', this.flipperKeydownHandler, true);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
this.flipperInstance.setHandleContentEditableTargets(false);
|
|
777
|
+
|
|
778
|
+
this.flipperKeydownSource = null;
|
|
779
|
+
this.flipperKeydownHandler = null;
|
|
780
|
+
}
|
|
781
|
+
}
|