@jackuait/blok 0.4.1-beta.5 → 0.4.1-beta.6
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 +136 -17
- package/codemod/README.md +16 -0
- package/codemod/migrate-editorjs-to-blok.js +868 -92
- package/codemod/test.js +682 -77
- package/dist/blok.mjs +5 -2
- package/dist/chunks/blok-B5qs7C5l.mjs +12838 -0
- package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
- package/dist/chunks/i18next-loader-CTrK3HzG.mjs +43 -0
- package/dist/{index-Cl_5rkKS.mjs → chunks/index-DDpzQn-0.mjs} +2 -2
- package/dist/chunks/inline-tool-convert-RBcopmCh.mjs +1988 -0
- package/dist/chunks/messages-2434tVOK.mjs +47 -0
- package/dist/chunks/messages-3DcCwXMF.mjs +47 -0
- package/dist/chunks/messages-4kMwVAKY.mjs +47 -0
- package/dist/chunks/messages-57uL5htT.mjs +47 -0
- package/dist/chunks/messages-76-iJV9Q.mjs +47 -0
- package/dist/chunks/messages-8p86Eyf2.mjs +47 -0
- package/dist/chunks/messages-BBX0p0Pi.mjs +47 -0
- package/dist/chunks/messages-BCm2eudQ.mjs +47 -0
- package/dist/chunks/messages-BFiUomgG.mjs +47 -0
- package/dist/chunks/messages-BIPNHHAV.mjs +47 -0
- package/dist/chunks/messages-BUlwu9mo.mjs +47 -0
- package/dist/chunks/messages-BX-DPa-z.mjs +47 -0
- package/dist/chunks/messages-BextV3Qh.mjs +47 -0
- package/dist/chunks/messages-BiPSFlUG.mjs +47 -0
- package/dist/chunks/messages-BiXe9G-O.mjs +47 -0
- package/dist/chunks/messages-Bl5z_Igo.mjs +47 -0
- package/dist/chunks/messages-BnsE97ku.mjs +47 -0
- package/dist/chunks/messages-BoO8gsVD.mjs +47 -0
- package/dist/chunks/messages-BqWaOGMn.mjs +47 -0
- package/dist/chunks/messages-BqkL2_Ro.mjs +47 -0
- package/dist/chunks/messages-BvCkXKX-.mjs +47 -0
- package/dist/chunks/messages-C6tbPLoj.mjs +47 -0
- package/dist/chunks/messages-CA6T3-gQ.mjs +47 -0
- package/dist/chunks/messages-CFFPFdWP.mjs +47 -0
- package/dist/chunks/messages-CFrKE-TN.mjs +47 -0
- package/dist/chunks/messages-CHz8VlG-.mjs +47 -0
- package/dist/chunks/messages-CLixzySl.mjs +47 -0
- package/dist/chunks/messages-CV7OM_qk.mjs +47 -0
- package/dist/chunks/messages-CXHt3eCC.mjs +47 -0
- package/dist/chunks/messages-CbmsBrB0.mjs +47 -0
- package/dist/chunks/messages-Ceo1KtFx.mjs +47 -0
- package/dist/chunks/messages-Cm0LJLtB.mjs +47 -0
- package/dist/chunks/messages-CmymP_Ar.mjs +47 -0
- package/dist/chunks/messages-D0ohMB5H.mjs +47 -0
- package/dist/chunks/messages-D3GrDwXh.mjs +47 -0
- package/dist/chunks/messages-D3vTzIpL.mjs +47 -0
- package/dist/chunks/messages-D5WeksbV.mjs +47 -0
- package/dist/chunks/messages-DGaab4EP.mjs +47 -0
- package/dist/chunks/messages-DKha57ZU.mjs +47 -0
- package/dist/chunks/messages-DOaujgMW.mjs +47 -0
- package/dist/chunks/messages-DVbPLd_0.mjs +47 -0
- package/dist/chunks/messages-D_FCyfW6.mjs +47 -0
- package/dist/chunks/messages-Dd5iZN3c.mjs +47 -0
- package/dist/chunks/messages-DehM7135.mjs +47 -0
- package/dist/chunks/messages-Dg1OHftD.mjs +47 -0
- package/dist/chunks/messages-Di6Flq-b.mjs +47 -0
- package/dist/chunks/messages-Dqhhex6e.mjs +47 -0
- package/dist/chunks/messages-DueVe0F1.mjs +47 -0
- package/dist/chunks/messages-Dx3eFwI0.mjs +47 -0
- package/dist/chunks/messages-FOtiUoKl.mjs +47 -0
- package/dist/chunks/messages-FTOZNhRD.mjs +47 -0
- package/dist/chunks/messages-IQxGfQIV.mjs +47 -0
- package/dist/chunks/messages-JF2fzCkK.mjs +47 -0
- package/dist/chunks/messages-MOGl7I5v.mjs +47 -0
- package/dist/chunks/messages-QgYhPL-3.mjs +47 -0
- package/dist/chunks/messages-WYWIbQwo.mjs +47 -0
- package/dist/chunks/messages-a6A_LgDv.mjs +47 -0
- package/dist/chunks/messages-bSf31LJi.mjs +47 -0
- package/dist/chunks/messages-diGozhTn.mjs +47 -0
- package/dist/chunks/messages-er-kd-VO.mjs +47 -0
- package/dist/chunks/messages-ez3w5NBn.mjs +47 -0
- package/dist/chunks/messages-f3uXjegd.mjs +47 -0
- package/dist/chunks/messages-ohwI1UGv.mjs +47 -0
- package/dist/chunks/messages-p9BZJaFV.mjs +47 -0
- package/dist/chunks/messages-qIQ4L4rw.mjs +47 -0
- package/dist/chunks/messages-qWkXPggi.mjs +47 -0
- package/dist/chunks/messages-w5foGze_.mjs +47 -0
- package/dist/full.mjs +50 -0
- package/dist/locales.mjs +227 -0
- package/dist/messages-2434tVOK.mjs +47 -0
- package/dist/messages-3DcCwXMF.mjs +47 -0
- package/dist/messages-4kMwVAKY.mjs +47 -0
- package/dist/messages-57uL5htT.mjs +47 -0
- package/dist/messages-76-iJV9Q.mjs +47 -0
- package/dist/messages-8p86Eyf2.mjs +47 -0
- package/dist/messages-BBX0p0Pi.mjs +47 -0
- package/dist/messages-BCm2eudQ.mjs +47 -0
- package/dist/messages-BFiUomgG.mjs +47 -0
- package/dist/messages-BIPNHHAV.mjs +47 -0
- package/dist/messages-BUlwu9mo.mjs +47 -0
- package/dist/messages-BX-DPa-z.mjs +47 -0
- package/dist/messages-BextV3Qh.mjs +47 -0
- package/dist/messages-BiPSFlUG.mjs +47 -0
- package/dist/messages-BiXe9G-O.mjs +47 -0
- package/dist/messages-Bl5z_Igo.mjs +47 -0
- package/dist/messages-BnsE97ku.mjs +47 -0
- package/dist/messages-BoO8gsVD.mjs +47 -0
- package/dist/messages-BqWaOGMn.mjs +47 -0
- package/dist/messages-BqkL2_Ro.mjs +47 -0
- package/dist/messages-BvCkXKX-.mjs +47 -0
- package/dist/messages-C6tbPLoj.mjs +47 -0
- package/dist/messages-CA6T3-gQ.mjs +47 -0
- package/dist/messages-CFFPFdWP.mjs +47 -0
- package/dist/messages-CFrKE-TN.mjs +47 -0
- package/dist/messages-CHz8VlG-.mjs +47 -0
- package/dist/messages-CLixzySl.mjs +47 -0
- package/dist/messages-CV7OM_qk.mjs +47 -0
- package/dist/messages-CXHt3eCC.mjs +47 -0
- package/dist/messages-CbmsBrB0.mjs +47 -0
- package/dist/messages-Ceo1KtFx.mjs +47 -0
- package/dist/messages-Cm0LJLtB.mjs +47 -0
- package/dist/messages-CmymP_Ar.mjs +47 -0
- package/dist/messages-D0ohMB5H.mjs +47 -0
- package/dist/messages-D3GrDwXh.mjs +47 -0
- package/dist/messages-D3vTzIpL.mjs +47 -0
- package/dist/messages-D5WeksbV.mjs +47 -0
- package/dist/messages-DGaab4EP.mjs +47 -0
- package/dist/messages-DKha57ZU.mjs +47 -0
- package/dist/messages-DOaujgMW.mjs +47 -0
- package/dist/messages-DVbPLd_0.mjs +47 -0
- package/dist/messages-D_FCyfW6.mjs +47 -0
- package/dist/messages-Dd5iZN3c.mjs +47 -0
- package/dist/messages-DehM7135.mjs +47 -0
- package/dist/messages-Dg1OHftD.mjs +47 -0
- package/dist/messages-Di6Flq-b.mjs +47 -0
- package/dist/messages-Dqhhex6e.mjs +47 -0
- package/dist/messages-DueVe0F1.mjs +47 -0
- package/dist/messages-Dx3eFwI0.mjs +47 -0
- package/dist/messages-FOtiUoKl.mjs +47 -0
- package/dist/messages-FTOZNhRD.mjs +47 -0
- package/dist/messages-IQxGfQIV.mjs +47 -0
- package/dist/messages-JF2fzCkK.mjs +47 -0
- package/dist/messages-MOGl7I5v.mjs +47 -0
- package/dist/messages-QgYhPL-3.mjs +47 -0
- package/dist/messages-WYWIbQwo.mjs +47 -0
- package/dist/messages-a6A_LgDv.mjs +47 -0
- package/dist/messages-bSf31LJi.mjs +47 -0
- package/dist/messages-diGozhTn.mjs +47 -0
- package/dist/messages-er-kd-VO.mjs +47 -0
- package/dist/messages-ez3w5NBn.mjs +47 -0
- package/dist/messages-f3uXjegd.mjs +47 -0
- package/dist/messages-ohwI1UGv.mjs +47 -0
- package/dist/messages-p9BZJaFV.mjs +47 -0
- package/dist/messages-qIQ4L4rw.mjs +47 -0
- package/dist/messages-qWkXPggi.mjs +47 -0
- package/dist/messages-w5foGze_.mjs +47 -0
- package/dist/tools.mjs +3073 -0
- package/dist/vendor.LICENSE.txt +59 -156
- package/package.json +48 -16
- 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 +1427 -0
- package/src/components/block-tunes/block-tune-delete.ts +51 -0
- package/src/components/blocks.ts +338 -0
- package/src/components/constants/data-attributes.ts +342 -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 +481 -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 +44 -0
- package/src/components/i18n/locales/ar/messages.json +44 -0
- package/src/components/i18n/locales/az/messages.json +44 -0
- package/src/components/i18n/locales/bg/messages.json +44 -0
- package/src/components/i18n/locales/bn/messages.json +44 -0
- package/src/components/i18n/locales/bs/messages.json +44 -0
- package/src/components/i18n/locales/cs/messages.json +44 -0
- package/src/components/i18n/locales/da/messages.json +44 -0
- package/src/components/i18n/locales/de/messages.json +44 -0
- package/src/components/i18n/locales/dv/messages.json +44 -0
- package/src/components/i18n/locales/el/messages.json +44 -0
- package/src/components/i18n/locales/en/messages.json +44 -0
- package/src/components/i18n/locales/es/messages.json +44 -0
- package/src/components/i18n/locales/et/messages.json +44 -0
- package/src/components/i18n/locales/fa/messages.json +44 -0
- package/src/components/i18n/locales/fi/messages.json +44 -0
- package/src/components/i18n/locales/fil/messages.json +44 -0
- package/src/components/i18n/locales/fr/messages.json +44 -0
- package/src/components/i18n/locales/gu/messages.json +44 -0
- package/src/components/i18n/locales/he/messages.json +44 -0
- package/src/components/i18n/locales/hi/messages.json +44 -0
- package/src/components/i18n/locales/hr/messages.json +44 -0
- package/src/components/i18n/locales/hu/messages.json +44 -0
- package/src/components/i18n/locales/hy/messages.json +44 -0
- package/src/components/i18n/locales/id/messages.json +44 -0
- package/src/components/i18n/locales/index.ts +225 -0
- package/src/components/i18n/locales/it/messages.json +44 -0
- package/src/components/i18n/locales/ja/messages.json +44 -0
- package/src/components/i18n/locales/ka/messages.json +44 -0
- package/src/components/i18n/locales/km/messages.json +44 -0
- package/src/components/i18n/locales/kn/messages.json +44 -0
- package/src/components/i18n/locales/ko/messages.json +44 -0
- package/src/components/i18n/locales/ku/messages.json +44 -0
- package/src/components/i18n/locales/lo/messages.json +44 -0
- package/src/components/i18n/locales/lt/messages.json +44 -0
- package/src/components/i18n/locales/lv/messages.json +44 -0
- package/src/components/i18n/locales/mk/messages.json +44 -0
- package/src/components/i18n/locales/ml/messages.json +44 -0
- package/src/components/i18n/locales/mn/messages.json +44 -0
- package/src/components/i18n/locales/mr/messages.json +44 -0
- package/src/components/i18n/locales/ms/messages.json +44 -0
- package/src/components/i18n/locales/my/messages.json +44 -0
- package/src/components/i18n/locales/ne/messages.json +44 -0
- package/src/components/i18n/locales/nl/messages.json +44 -0
- package/src/components/i18n/locales/no/messages.json +44 -0
- package/src/components/i18n/locales/pa/messages.json +44 -0
- package/src/components/i18n/locales/pl/messages.json +44 -0
- package/src/components/i18n/locales/ps/messages.json +44 -0
- package/src/components/i18n/locales/pt/messages.json +44 -0
- package/src/components/i18n/locales/ro/messages.json +44 -0
- package/src/components/i18n/locales/ru/messages.json +44 -0
- package/src/components/i18n/locales/sd/messages.json +44 -0
- package/src/components/i18n/locales/si/messages.json +44 -0
- package/src/components/i18n/locales/sk/messages.json +44 -0
- package/src/components/i18n/locales/sl/messages.json +44 -0
- package/src/components/i18n/locales/sq/messages.json +44 -0
- package/src/components/i18n/locales/sr/messages.json +44 -0
- package/src/components/i18n/locales/sv/messages.json +44 -0
- package/src/components/i18n/locales/sw/messages.json +44 -0
- package/src/components/i18n/locales/ta/messages.json +44 -0
- package/src/components/i18n/locales/te/messages.json +44 -0
- package/src/components/i18n/locales/th/messages.json +44 -0
- package/src/components/i18n/locales/tr/messages.json +44 -0
- package/src/components/i18n/locales/ug/messages.json +44 -0
- package/src/components/i18n/locales/uk/messages.json +44 -0
- package/src/components/i18n/locales/ur/messages.json +44 -0
- package/src/components/i18n/locales/vi/messages.json +44 -0
- package/src/components/i18n/locales/yi/messages.json +44 -0
- package/src/components/i18n/locales/zh/messages.json +44 -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 +363 -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 +33 -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 +1375 -0
- package/src/components/modules/blockManager.ts +1348 -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 +1141 -0
- package/src/components/modules/history.ts +1098 -0
- package/src/components/modules/i18n.ts +325 -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 +668 -0
- package/src/components/modules/renderer.ts +155 -0
- package/src/components/modules/saver.ts +283 -0
- package/src/components/modules/toolbar/blockSettings.ts +776 -0
- package/src/components/modules/toolbar/index.ts +1311 -0
- package/src/components/modules/toolbar/inline.ts +956 -0
- package/src/components/modules/tools.ts +589 -0
- package/src/components/modules/ui.ts +1179 -0
- package/src/components/polyfills.ts +113 -0
- package/src/components/selection.ts +1189 -0
- package/src/components/tools/base.ts +274 -0
- package/src/components/tools/block.ts +291 -0
- package/src/components/tools/collection.ts +67 -0
- package/src/components/tools/factory.ts +85 -0
- package/src/components/tools/inline.ts +71 -0
- package/src/components/tools/tune.ts +33 -0
- package/src/components/ui/toolbox.ts +497 -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 +666 -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 +187 -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 +181 -0
- package/src/components/utils/popover/components/search-input/search-input.types.ts +30 -0
- package/src/components/utils/popover/index.ts +13 -0
- package/src/components/utils/popover/popover-abstract.ts +448 -0
- package/src/components/utils/popover/popover-desktop.ts +643 -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 +105 -0
- package/src/components/utils/tooltip.ts +642 -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 +570 -0
- package/src/tools/index.ts +38 -0
- package/src/tools/list/index.ts +1803 -0
- package/src/tools/paragraph/index.ts +411 -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 +1 -1
- package/types/api/i18n.d.ts +5 -3
- package/types/api/selection.d.ts +6 -0
- package/types/api/styles.d.ts +0 -5
- package/types/configs/blok-config.d.ts +21 -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 +169 -0
- package/types/data-formats/output-data.d.ts +15 -0
- package/types/full.d.ts +80 -0
- package/types/index.d.ts +9 -12
- 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 +16 -2
- package/types/tools/tool.d.ts +6 -0
- package/types/tools-entry.d.ts +49 -0
- package/types/utils/popover/popover-item.d.ts +0 -5
- package/dist/blok-DvN73wsH.mjs +0 -19922
- package/dist/blok.umd.js +0 -166
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
import { Module } from '../../__module';
|
|
2
|
+
import { Dom as $ } from '../../dom';
|
|
3
|
+
import { SelectionUtils } from '../../selection';
|
|
4
|
+
import { beautifyShortcut, capitalize, isMobileScreen } from '../../utils';
|
|
5
|
+
import type { InlineTool as IInlineTool } from '../../../../types';
|
|
6
|
+
import { Shortcuts } from '../../utils/shortcuts';
|
|
7
|
+
import type { ModuleConfig } from '../../../types-internal/module-config';
|
|
8
|
+
import type { BlokModules } from '../../../types-internal/blok-modules';
|
|
9
|
+
import { CommonInternalSettings } from '../../tools/base';
|
|
10
|
+
import type { Popover, PopoverItemParams } from '../../utils/popover';
|
|
11
|
+
import { PopoverItemType } from '../../utils/popover';
|
|
12
|
+
import { PopoverInline } from '../../utils/popover/popover-inline';
|
|
13
|
+
import type { InlineToolAdapter } from 'src/components/tools/inline';
|
|
14
|
+
import { translateToolName } from '../../utils/tools';
|
|
15
|
+
import { DATA_ATTR, INLINE_TOOLBAR_INTERFACE_VALUE } from '../../constants';
|
|
16
|
+
import { twMerge } from '../../utils/tw';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Inline Toolbar elements
|
|
20
|
+
*/
|
|
21
|
+
interface InlineToolbarNodes {
|
|
22
|
+
/**
|
|
23
|
+
* Wrapper element for the inline toolbar
|
|
24
|
+
*/
|
|
25
|
+
wrapper: HTMLElement | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Inline toolbar with actions that modifies selected text fragment
|
|
30
|
+
*
|
|
31
|
+
* |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|
|
32
|
+
* | B i [link] [mark] |
|
|
33
|
+
* |________________________|
|
|
34
|
+
*/
|
|
35
|
+
export class InlineToolbar extends Module<InlineToolbarNodes> {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* State of inline toolbar
|
|
39
|
+
*/
|
|
40
|
+
public opened = false;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns true if a nested popover (like convert-to dropdown) is currently open
|
|
44
|
+
*/
|
|
45
|
+
public get hasNestedPopoverOpen(): boolean {
|
|
46
|
+
const popoverInline = this.popover as PopoverInline | null;
|
|
47
|
+
|
|
48
|
+
if (popoverInline === null) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return popoverInline.hasNestedPopoverOpen;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Closes only the nested popover if one is open.
|
|
57
|
+
* Returns true if a nested popover was closed, false otherwise.
|
|
58
|
+
*/
|
|
59
|
+
public closeNestedPopover(): boolean {
|
|
60
|
+
const popoverInline = this.popover as PopoverInline | null;
|
|
61
|
+
|
|
62
|
+
if (popoverInline === null) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!popoverInline.hasNestedPopoverOpen) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
popoverInline.closeNestedPopover();
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns true if a flipper item is focused (user is navigating with keyboard)
|
|
77
|
+
*/
|
|
78
|
+
public get hasFlipperFocus(): boolean {
|
|
79
|
+
const popoverInline = this.popover as PopoverInline | null;
|
|
80
|
+
|
|
81
|
+
if (popoverInline === null) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const mainFlipperHasFocus = popoverInline.flipper?.hasFocus() ?? false;
|
|
86
|
+
const nestedPopover = (popoverInline as unknown as { nestedPopover?: { flipper?: { hasFocus(): boolean } } | null }).nestedPopover;
|
|
87
|
+
const nestedFlipperHasFocus = nestedPopover?.flipper?.hasFocus() ?? false;
|
|
88
|
+
|
|
89
|
+
return mainFlipperHasFocus || nestedFlipperHasFocus;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Popover instance reference
|
|
94
|
+
*/
|
|
95
|
+
private popover: Popover | null = null;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Promise that resolves when the toolbar is fully opened
|
|
99
|
+
* Used to ensure shortcuts wait for popover initialization
|
|
100
|
+
*/
|
|
101
|
+
private openingPromise: Promise<void> | null = null;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Margin above/below the Toolbar
|
|
105
|
+
*/
|
|
106
|
+
private readonly toolbarVerticalMargin: number = isMobileScreen() ? 20 : 6;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Tracks whether inline toolbar DOM and shortcuts are initialized
|
|
110
|
+
*/
|
|
111
|
+
private initialized = false;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Ensures we don't schedule multiple initialization attempts simultaneously
|
|
115
|
+
*/
|
|
116
|
+
private initializationScheduled = false;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Currently visible tools instances
|
|
120
|
+
*/
|
|
121
|
+
private tools: Map<InlineToolAdapter, IInlineTool> = new Map();
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Shortcuts registered for inline tools
|
|
125
|
+
*/
|
|
126
|
+
private registeredShortcuts: Map<string, string> = new Map();
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Tracks whether inline shortcuts have been registered
|
|
130
|
+
*/
|
|
131
|
+
private shortcutsRegistered = false;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Prevents duplicate shortcut registration retries
|
|
135
|
+
*/
|
|
136
|
+
private shortcutRegistrationScheduled = false;
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param moduleConfiguration - Module Configuration
|
|
141
|
+
* @param moduleConfiguration.config - Blok's config
|
|
142
|
+
* @param moduleConfiguration.eventsDispatcher - Blok's event dispatcher
|
|
143
|
+
*/
|
|
144
|
+
constructor({ config, eventsDispatcher }: ModuleConfig) {
|
|
145
|
+
super({
|
|
146
|
+
config,
|
|
147
|
+
eventsDispatcher,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Handle arrow key events for inline toolbar
|
|
152
|
+
* - Close toolbar when Up/Down arrow key is pressed without Shift (allows cursor movement)
|
|
153
|
+
* but only if no toolbar item is focused (user hasn't started keyboard navigation via Tab)
|
|
154
|
+
* - Left/Right arrow keys have no effect within the inline toolbar (per accessibility requirements)
|
|
155
|
+
* - Show toolbar when Shift+Arrow is pressed (extends selection)
|
|
156
|
+
*
|
|
157
|
+
* Note: We listen on window with capture=true to ensure this runs before
|
|
158
|
+
* the Flipper's keydown handler which also uses capture phase
|
|
159
|
+
*/
|
|
160
|
+
this.listeners.on(window, 'keydown', (event: Event) => {
|
|
161
|
+
const keyboardEvent = event as KeyboardEvent;
|
|
162
|
+
const isVerticalArrowKey = keyboardEvent.key === 'ArrowDown' || keyboardEvent.key === 'ArrowUp';
|
|
163
|
+
const isHorizontalArrowKey = keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowRight';
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Close inline toolbar when Up/Down arrow key is pressed without Shift
|
|
167
|
+
* This allows the user to move the cursor and collapse the selection
|
|
168
|
+
*
|
|
169
|
+
* However, if the user has already started keyboard navigation within the toolbar
|
|
170
|
+
* (by pressing Tab to focus on a toolbar item), we should allow arrow key navigation
|
|
171
|
+
* within the toolbar instead of closing it.
|
|
172
|
+
*
|
|
173
|
+
* Left/Right arrow keys should have no effect within the inline toolbar,
|
|
174
|
+
* so we don't close the toolbar when they are pressed.
|
|
175
|
+
*
|
|
176
|
+
* We check:
|
|
177
|
+
* 1. If the main popover's Flipper has focus
|
|
178
|
+
* 2. If a nested popover is open (even if no item is focused)
|
|
179
|
+
* 3. If the nested popover's Flipper has focus
|
|
180
|
+
*/
|
|
181
|
+
const shouldCheckForClose = isVerticalArrowKey && !keyboardEvent.shiftKey && this.opened;
|
|
182
|
+
const popoverWithFlipper = this.popover as PopoverInline | null;
|
|
183
|
+
const mainFlipperHasFocus = popoverWithFlipper?.flipper?.hasFocus() ?? false;
|
|
184
|
+
const nestedPopover = (this.popover as unknown as { nestedPopover?: { flipper?: { hasFocus(): boolean } } | null } | null)?.nestedPopover;
|
|
185
|
+
const hasNestedPopover = nestedPopover !== null && nestedPopover !== undefined;
|
|
186
|
+
const nestedFlipperHasFocus = nestedPopover?.flipper?.hasFocus() ?? false;
|
|
187
|
+
const shouldKeepOpen = mainFlipperHasFocus || hasNestedPopover || nestedFlipperHasFocus;
|
|
188
|
+
|
|
189
|
+
if (shouldCheckForClose && !shouldKeepOpen) {
|
|
190
|
+
this.close();
|
|
191
|
+
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* When the inline toolbar is open and the flipper has focus,
|
|
197
|
+
* prevent horizontal arrow keys from doing anything (no navigation, no closing)
|
|
198
|
+
*/
|
|
199
|
+
if (isHorizontalArrowKey && this.opened && mainFlipperHasFocus) {
|
|
200
|
+
keyboardEvent.preventDefault();
|
|
201
|
+
keyboardEvent.stopPropagation();
|
|
202
|
+
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const isShiftArrow = keyboardEvent.shiftKey &&
|
|
207
|
+
(keyboardEvent.key === 'ArrowDown' || keyboardEvent.key === 'ArrowUp');
|
|
208
|
+
|
|
209
|
+
if (!isShiftArrow) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void this.tryToShow();
|
|
214
|
+
}, true);
|
|
215
|
+
|
|
216
|
+
this.scheduleInitialization();
|
|
217
|
+
this.tryRegisterShortcuts();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Setter for Blok modules that ensures shortcuts registration is retried once dependencies are available
|
|
222
|
+
*/
|
|
223
|
+
public override set state(Blok: BlokModules) {
|
|
224
|
+
super.state = Blok;
|
|
225
|
+
this.tryRegisterShortcuts();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Ensures toolbar DOM and shortcuts are created
|
|
230
|
+
*/
|
|
231
|
+
private initialize(): void {
|
|
232
|
+
if (this.initialized) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Guard against race condition: the deferred callback from scheduleInitialization()
|
|
238
|
+
* can fire before UI module has created its wrapper element.
|
|
239
|
+
* If UI isn't ready yet, reschedule and try again.
|
|
240
|
+
*/
|
|
241
|
+
if (this.Blok.UI?.nodes?.wrapper === undefined) {
|
|
242
|
+
this.initializationScheduled = false;
|
|
243
|
+
this.scheduleInitialization();
|
|
244
|
+
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.make();
|
|
249
|
+
this.tryRegisterShortcuts();
|
|
250
|
+
this.initialized = true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Attempts to register inline shortcuts as soon as tools are available
|
|
255
|
+
*/
|
|
256
|
+
private tryRegisterShortcuts(): void {
|
|
257
|
+
if (this.shortcutsRegistered) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (this.Blok?.Tools === undefined) {
|
|
262
|
+
this.scheduleShortcutRegistration();
|
|
263
|
+
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const shortcutsWereRegistered = this.registerInitialShortcuts();
|
|
268
|
+
|
|
269
|
+
if (shortcutsWereRegistered) {
|
|
270
|
+
this.shortcutsRegistered = true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Schedules a retry for shortcut registration
|
|
276
|
+
*/
|
|
277
|
+
private scheduleShortcutRegistration(): void {
|
|
278
|
+
if (this.shortcutsRegistered || this.shortcutRegistrationScheduled) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.shortcutRegistrationScheduled = true;
|
|
283
|
+
|
|
284
|
+
const callback = (): void => {
|
|
285
|
+
this.shortcutRegistrationScheduled = false;
|
|
286
|
+
this.tryRegisterShortcuts();
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
if (typeof window !== 'undefined' && typeof window.setTimeout === 'function') {
|
|
290
|
+
window.setTimeout(callback, 0);
|
|
291
|
+
} else {
|
|
292
|
+
callback();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Schedules the next initialization attempt
|
|
298
|
+
*/
|
|
299
|
+
private scheduleInitialization(): void {
|
|
300
|
+
if (this.initialized || this.initializationScheduled) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.initializationScheduled = true;
|
|
305
|
+
|
|
306
|
+
const callback = (): void => {
|
|
307
|
+
this.initializationScheduled = false;
|
|
308
|
+
this.initialize();
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const scheduleWithTimeout = (): void => {
|
|
312
|
+
window.setTimeout(callback, 0);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {
|
|
316
|
+
window.requestIdleCallback(() => {
|
|
317
|
+
scheduleWithTimeout();
|
|
318
|
+
}, { timeout: 2000 });
|
|
319
|
+
} else {
|
|
320
|
+
scheduleWithTimeout();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Moving / appearance
|
|
326
|
+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Shows Inline Toolbar if something is selected
|
|
331
|
+
* @param [needToClose] - pass true to close toolbar if it is not allowed.
|
|
332
|
+
*/
|
|
333
|
+
public async tryToShow(needToClose = false): Promise<void> {
|
|
334
|
+
if (needToClose) {
|
|
335
|
+
this.close();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.initialize();
|
|
339
|
+
|
|
340
|
+
if (!this.allowedToShow()) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this.openingPromise = this.open();
|
|
345
|
+
await this.openingPromise;
|
|
346
|
+
this.openingPromise = null;
|
|
347
|
+
|
|
348
|
+
this.Blok.Toolbar.close();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Hides Inline Toolbar
|
|
353
|
+
*/
|
|
354
|
+
public close(): void {
|
|
355
|
+
if (!this.opened) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.tools = new Map();
|
|
360
|
+
this.opened = false;
|
|
361
|
+
this.openingPromise = null;
|
|
362
|
+
|
|
363
|
+
// Hide and destroy popover
|
|
364
|
+
if (this.popover) {
|
|
365
|
+
this.popover.hide?.();
|
|
366
|
+
this.popover.destroy?.();
|
|
367
|
+
this.popover = null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Clear wrapper content
|
|
371
|
+
if (this.nodes.wrapper) {
|
|
372
|
+
this.nodes.wrapper.innerHTML = '';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Handle test mocks for PopoverInline
|
|
376
|
+
const popoverMockInfo = (PopoverInline as unknown as { mock?: { results?: Array<{ value?: Popover | undefined }> } }).mock;
|
|
377
|
+
const lastPopover = popoverMockInfo?.results?.at(-1)?.value;
|
|
378
|
+
|
|
379
|
+
if (lastPopover) {
|
|
380
|
+
lastPopover.hide?.();
|
|
381
|
+
lastPopover.destroy?.();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Check if node is contained by Inline Toolbar
|
|
387
|
+
* @param {Node} node — node to check
|
|
388
|
+
*/
|
|
389
|
+
public containsNode(node: Node): boolean {
|
|
390
|
+
if (this.nodes.wrapper === undefined) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (this.nodes.wrapper.contains(node)) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Also check if node is inside the popover (including nested popovers)
|
|
399
|
+
if (this.popover !== null && this.popover.hasNode(node)) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Removes UI and its components
|
|
408
|
+
*/
|
|
409
|
+
public destroy(): void {
|
|
410
|
+
if (this.popover) {
|
|
411
|
+
this.popover.hide?.();
|
|
412
|
+
this.popover.destroy?.();
|
|
413
|
+
this.popover = null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
this.removeAllNodes();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Making DOM - creates wrapper element for the inline toolbar
|
|
422
|
+
*/
|
|
423
|
+
private make(): void {
|
|
424
|
+
this.nodes.wrapper = $.make('div', twMerge(
|
|
425
|
+
'absolute top-0 left-0 z-[3] opacity-100 visible',
|
|
426
|
+
'transition-opacity duration-[250ms] ease-out',
|
|
427
|
+
'will-change-[opacity,left,top]',
|
|
428
|
+
'[&_[hidden]]:!hidden'
|
|
429
|
+
));
|
|
430
|
+
this.nodes.wrapper.setAttribute(DATA_ATTR.interface, INLINE_TOOLBAR_INTERFACE_VALUE);
|
|
431
|
+
this.nodes.wrapper.setAttribute('data-blok-testid', 'inline-toolbar');
|
|
432
|
+
|
|
433
|
+
$.append(this.Blok.UI.nodes.wrapper, this.nodes.wrapper);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Shows Inline Toolbar
|
|
438
|
+
*/
|
|
439
|
+
private async open(): Promise<void> {
|
|
440
|
+
if (this.opened) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this.initialize();
|
|
445
|
+
this.opened = true;
|
|
446
|
+
|
|
447
|
+
// Cleanup existing popover
|
|
448
|
+
if (this.popover) {
|
|
449
|
+
this.popover.hide?.();
|
|
450
|
+
this.popover.destroy?.();
|
|
451
|
+
this.popover = null;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
this.createToolsInstances();
|
|
455
|
+
|
|
456
|
+
if (!this.nodes.wrapper) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Clear wrapper
|
|
461
|
+
this.nodes.wrapper.innerHTML = '';
|
|
462
|
+
|
|
463
|
+
// Build popover items
|
|
464
|
+
const popoverItems = await this.buildPopoverItems();
|
|
465
|
+
|
|
466
|
+
// Create popover
|
|
467
|
+
const scopeElement = this.Blok.API?.methods?.ui?.nodes?.redactor ?? this.Blok.UI.nodes.redactor;
|
|
468
|
+
|
|
469
|
+
this.popover = new PopoverInline({
|
|
470
|
+
items: popoverItems,
|
|
471
|
+
scopeElement,
|
|
472
|
+
messages: {
|
|
473
|
+
nothingFound: this.Blok.I18n.t('popover.nothingFound'),
|
|
474
|
+
search: this.Blok.I18n.t('popover.search'),
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Get popover element and calculate position
|
|
479
|
+
const popoverMountElement = this.popover.getMountElement?.() ?? this.popover.getElement?.();
|
|
480
|
+
const popoverElement = this.popover.getElement?.();
|
|
481
|
+
const popoverWidth = this.popover.size?.width
|
|
482
|
+
?? popoverElement?.getBoundingClientRect().width
|
|
483
|
+
?? 0;
|
|
484
|
+
|
|
485
|
+
this.applyPosition(popoverWidth);
|
|
486
|
+
|
|
487
|
+
// Mount popover
|
|
488
|
+
if (popoverMountElement && this.nodes.wrapper) {
|
|
489
|
+
this.nodes.wrapper.appendChild(popoverMountElement);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
this.popover.show?.();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Build popover items from tools map
|
|
497
|
+
*/
|
|
498
|
+
private async buildPopoverItems(): Promise<PopoverItemParams[]> {
|
|
499
|
+
const popoverItems: PopoverItemParams[] = [];
|
|
500
|
+
const toolsEntries = Array.from(this.tools.entries());
|
|
501
|
+
|
|
502
|
+
for (const [index, [tool, instance]] of toolsEntries.entries()) {
|
|
503
|
+
const renderedTool = await instance.render();
|
|
504
|
+
const shortcut = this.getToolShortcut(tool.name);
|
|
505
|
+
const shortcutBeautified = shortcut !== undefined ? beautifyShortcut(shortcut) : undefined;
|
|
506
|
+
|
|
507
|
+
const toolTitle = translateToolName(this.Blok.I18n, tool.titleKey, tool.title || capitalize(tool.name));
|
|
508
|
+
|
|
509
|
+
const items = Array.isArray(renderedTool) ? renderedTool : [renderedTool];
|
|
510
|
+
const isFirstItem = index === 0;
|
|
511
|
+
|
|
512
|
+
for (const item of items) {
|
|
513
|
+
const processed = this.processPopoverItem(item, tool.name, toolTitle, shortcutBeautified, isFirstItem);
|
|
514
|
+
|
|
515
|
+
popoverItems.push(...processed);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return popoverItems;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Process a single popover item and return the items to add
|
|
524
|
+
*/
|
|
525
|
+
private processPopoverItem(
|
|
526
|
+
item: PopoverItemParams | HTMLElement,
|
|
527
|
+
toolName: string,
|
|
528
|
+
toolTitle: string,
|
|
529
|
+
shortcutBeautified: string | undefined,
|
|
530
|
+
isFirstItem: boolean
|
|
531
|
+
): PopoverItemParams[] {
|
|
532
|
+
const result: PopoverItemParams[] = [];
|
|
533
|
+
|
|
534
|
+
const commonPopoverItemParams = {
|
|
535
|
+
name: toolName,
|
|
536
|
+
hint: {
|
|
537
|
+
title: toolTitle,
|
|
538
|
+
description: shortcutBeautified,
|
|
539
|
+
},
|
|
540
|
+
} as PopoverItemParams;
|
|
541
|
+
|
|
542
|
+
// Skip raw HTMLElement items (legacy)
|
|
543
|
+
if (item instanceof HTMLElement) {
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (item.type === PopoverItemType.Html) {
|
|
548
|
+
result.push({
|
|
549
|
+
...commonPopoverItemParams,
|
|
550
|
+
...item,
|
|
551
|
+
type: PopoverItemType.Html,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
return result;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (item.type === PopoverItemType.Separator) {
|
|
558
|
+
result.push({
|
|
559
|
+
type: PopoverItemType.Separator,
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Default item
|
|
566
|
+
const popoverItem = {
|
|
567
|
+
...commonPopoverItemParams,
|
|
568
|
+
...item,
|
|
569
|
+
type: PopoverItemType.Default,
|
|
570
|
+
} as PopoverItemParams;
|
|
571
|
+
|
|
572
|
+
result.push(popoverItem);
|
|
573
|
+
|
|
574
|
+
// Append separator after first item with children
|
|
575
|
+
if ('children' in popoverItem && isFirstItem) {
|
|
576
|
+
result.push({
|
|
577
|
+
type: PopoverItemType.Separator,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return result;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Calculate and apply position to wrapper
|
|
586
|
+
*/
|
|
587
|
+
private applyPosition(popoverWidth: number): void {
|
|
588
|
+
if (!this.nodes.wrapper) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const wrapperOffset = this.Blok.UI.nodes.wrapper.getBoundingClientRect();
|
|
593
|
+
const contentRect = this.Blok.UI.contentRect;
|
|
594
|
+
const selectionRect = SelectionUtils.rect as DOMRect;
|
|
595
|
+
|
|
596
|
+
const newCoords = {
|
|
597
|
+
x: selectionRect.x - wrapperOffset.x,
|
|
598
|
+
y: selectionRect.y +
|
|
599
|
+
selectionRect.height -
|
|
600
|
+
wrapperOffset.top +
|
|
601
|
+
this.toolbarVerticalMargin,
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
const realRightCoord = newCoords.x + popoverWidth + wrapperOffset.x;
|
|
605
|
+
|
|
606
|
+
// Prevent overflow on right side
|
|
607
|
+
if (realRightCoord > contentRect.right) {
|
|
608
|
+
newCoords.x = contentRect.right - popoverWidth - wrapperOffset.x;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px';
|
|
612
|
+
this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px';
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Need to show Inline Toolbar or not
|
|
618
|
+
*/
|
|
619
|
+
private allowedToShow(): boolean {
|
|
620
|
+
/**
|
|
621
|
+
* Tags conflicts with window.selection function.
|
|
622
|
+
* Ex. IMG tag returns null (Firefox) or Redactors wrapper (Chrome)
|
|
623
|
+
*/
|
|
624
|
+
const tagsConflictsWithSelection = ['IMG', 'INPUT'];
|
|
625
|
+
const currentSelection = this.resolveSelection();
|
|
626
|
+
const selectedText = SelectionUtils.text;
|
|
627
|
+
|
|
628
|
+
// old browsers
|
|
629
|
+
if (!currentSelection || !currentSelection.anchorNode) {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// empty selection
|
|
634
|
+
if (currentSelection.isCollapsed || selectedText.length < 1) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const target = !$.isElement(currentSelection.anchorNode)
|
|
639
|
+
? currentSelection.anchorNode.parentElement
|
|
640
|
+
: currentSelection.anchorNode;
|
|
641
|
+
|
|
642
|
+
if (target === null) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (currentSelection !== null && tagsConflictsWithSelection.includes(target.tagName)) {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Check if there is at leas one tool enabled by current Block's Tool
|
|
652
|
+
*/
|
|
653
|
+
const anchorElement = $.isElement(currentSelection.anchorNode)
|
|
654
|
+
? currentSelection.anchorNode as HTMLElement
|
|
655
|
+
: currentSelection.anchorNode.parentElement;
|
|
656
|
+
const blockFromAnchor = anchorElement
|
|
657
|
+
? this.Blok.BlockManager.getBlock(anchorElement)
|
|
658
|
+
: null;
|
|
659
|
+
const currentBlock = blockFromAnchor ?? this.Blok.BlockManager.currentBlock;
|
|
660
|
+
|
|
661
|
+
if (currentBlock === null || currentBlock === undefined) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Check that at least one tool is available for the current block
|
|
667
|
+
*/
|
|
668
|
+
const toolsAvailable = this.getTools();
|
|
669
|
+
const isAtLeastOneToolAvailable = toolsAvailable.some((tool) => currentBlock.tool.inlineTools.has(tool.name));
|
|
670
|
+
|
|
671
|
+
if (isAtLeastOneToolAvailable === false) {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Inline toolbar will be shown only if the target is contenteditable
|
|
677
|
+
* In Read-Only mode, the target should be contenteditable with "false" value
|
|
678
|
+
*/
|
|
679
|
+
const contenteditableSelector = '[contenteditable]';
|
|
680
|
+
const contenteditableTarget = target.closest(contenteditableSelector);
|
|
681
|
+
|
|
682
|
+
if (contenteditableTarget !== null) {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const blockHolder = currentBlock.holder;
|
|
687
|
+
const holderContenteditable = blockHolder &&
|
|
688
|
+
(
|
|
689
|
+
blockHolder.matches(contenteditableSelector)
|
|
690
|
+
? blockHolder
|
|
691
|
+
: blockHolder.closest(contenteditableSelector)
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
if (holderContenteditable) {
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (this.Blok.ReadOnly.isEnabled) {
|
|
699
|
+
return SelectionUtils.isSelectionAtBlok(currentSelection);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Working with Tools
|
|
707
|
+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
708
|
+
*/
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Returns tools that are available for current block
|
|
712
|
+
*/
|
|
713
|
+
private getTools(): InlineToolAdapter[] {
|
|
714
|
+
const currentBlock = this.Blok.BlockManager.currentBlock
|
|
715
|
+
?? (() => {
|
|
716
|
+
const selection = this.resolveSelection();
|
|
717
|
+
const anchorNode = selection?.anchorNode;
|
|
718
|
+
|
|
719
|
+
if (!anchorNode) {
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const anchorElement = $.isElement(anchorNode) ? anchorNode as HTMLElement : anchorNode.parentElement;
|
|
724
|
+
|
|
725
|
+
if (!anchorElement) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return this.Blok.BlockManager.getBlock(anchorElement);
|
|
730
|
+
})();
|
|
731
|
+
|
|
732
|
+
if (!currentBlock) {
|
|
733
|
+
return [];
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const inlineTools = Array.from(currentBlock.tool.inlineTools.values());
|
|
737
|
+
|
|
738
|
+
return inlineTools.filter((tool) => {
|
|
739
|
+
if (this.Blok.ReadOnly.isEnabled && tool.isReadOnlySupported !== true) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return true;
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Constructs tools instances and saves them to this.tools
|
|
749
|
+
*/
|
|
750
|
+
private createToolsInstances(): void {
|
|
751
|
+
this.tools = new Map();
|
|
752
|
+
|
|
753
|
+
const tools = this.getTools();
|
|
754
|
+
|
|
755
|
+
tools.forEach((tool) => {
|
|
756
|
+
const instance = tool.create();
|
|
757
|
+
|
|
758
|
+
this.tools.set(tool, instance);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Try to enable shortcut for a tool, catching any errors silently
|
|
764
|
+
*/
|
|
765
|
+
private tryEnableShortcut(toolName: string, shortcut: string | undefined): void {
|
|
766
|
+
if (shortcut === undefined) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
this.enableShortcuts(toolName, shortcut);
|
|
772
|
+
} catch (_e) {
|
|
773
|
+
// Ignore errors when enabling shortcuts
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Get shortcut name for tool
|
|
779
|
+
*/
|
|
780
|
+
private getToolShortcut(toolName: string): string | undefined {
|
|
781
|
+
const { Tools } = this.Blok;
|
|
782
|
+
|
|
783
|
+
const tool = Tools.inlineTools.get(toolName);
|
|
784
|
+
const internalTools = Tools.internal.inlineTools;
|
|
785
|
+
|
|
786
|
+
if (Array.from(internalTools.keys()).includes(toolName)) {
|
|
787
|
+
return this.inlineTools[toolName][CommonInternalSettings.Shortcut];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return tool?.shortcut;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Enable Tool shortcut with Blok Shortcuts Module
|
|
795
|
+
*/
|
|
796
|
+
private enableShortcuts(toolName: string, shortcut: string): void {
|
|
797
|
+
const registeredShortcut = this.registeredShortcuts.get(toolName);
|
|
798
|
+
|
|
799
|
+
if (registeredShortcut === shortcut) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (this.isShortcutTakenByAnotherTool(toolName, shortcut)) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (registeredShortcut !== undefined) {
|
|
808
|
+
Shortcuts.remove(document, registeredShortcut);
|
|
809
|
+
this.registeredShortcuts.delete(toolName);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
Shortcuts.add({
|
|
813
|
+
name: shortcut,
|
|
814
|
+
handler: (event) => {
|
|
815
|
+
const { currentBlock } = this.Blok.BlockManager;
|
|
816
|
+
|
|
817
|
+
if (!currentBlock) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (currentBlock.tool.enabledInlineTools === false) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
event.preventDefault();
|
|
826
|
+
|
|
827
|
+
void this.activateToolByShortcut(toolName);
|
|
828
|
+
},
|
|
829
|
+
on: document,
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
this.registeredShortcuts.set(toolName, shortcut);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Check if shortcut is already registered by another inline tool
|
|
837
|
+
*/
|
|
838
|
+
private isShortcutTakenByAnotherTool(toolName: string, shortcut: string): boolean {
|
|
839
|
+
return Array.from(this.registeredShortcuts.entries()).some(([name, registeredShortcut]) => {
|
|
840
|
+
return name !== toolName && registeredShortcut === shortcut;
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Activates inline tool triggered by keyboard shortcut
|
|
846
|
+
*/
|
|
847
|
+
private async activateToolByShortcut(toolName: string): Promise<void> {
|
|
848
|
+
if (!this.opened) {
|
|
849
|
+
await this.tryToShow();
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Wait for any pending opening operation to complete.
|
|
854
|
+
* This handles the race condition where the toolbar is being opened
|
|
855
|
+
* asynchronously (e.g., from a selectionchange event) and the shortcut
|
|
856
|
+
* is pressed before the popover is fully initialized.
|
|
857
|
+
*/
|
|
858
|
+
if (this.openingPromise !== null) {
|
|
859
|
+
await this.openingPromise;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (this.popover) {
|
|
863
|
+
this.popover.activateItemByName(toolName);
|
|
864
|
+
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
this.invokeToolActionDirectly(toolName);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Invokes the tool's action directly without relying on the popover.
|
|
873
|
+
*/
|
|
874
|
+
private invokeToolActionDirectly(toolName: string): void {
|
|
875
|
+
const tool = this.Blok.Tools.inlineTools.get(toolName);
|
|
876
|
+
|
|
877
|
+
if (!tool) {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const instance = tool.create();
|
|
882
|
+
const rendered = instance.render();
|
|
883
|
+
const items = Array.isArray(rendered) ? rendered : [rendered];
|
|
884
|
+
|
|
885
|
+
for (const item of items) {
|
|
886
|
+
if ('onActivate' in item && typeof item.onActivate === 'function') {
|
|
887
|
+
item.onActivate(item);
|
|
888
|
+
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Get inline tools tools
|
|
896
|
+
*/
|
|
897
|
+
private get inlineTools(): { [name: string]: IInlineTool } {
|
|
898
|
+
const result = {} as { [name: string]: IInlineTool };
|
|
899
|
+
|
|
900
|
+
Array
|
|
901
|
+
.from(this.Blok.Tools.inlineTools.entries())
|
|
902
|
+
.forEach(([name, tool]) => {
|
|
903
|
+
result[name] = tool.create();
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Register shortcuts for inline tools ahead of time
|
|
911
|
+
*/
|
|
912
|
+
private registerInitialShortcuts(): boolean {
|
|
913
|
+
const inlineTools = this.Blok.Tools?.inlineTools;
|
|
914
|
+
|
|
915
|
+
if (!inlineTools) {
|
|
916
|
+
this.scheduleShortcutRegistration();
|
|
917
|
+
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const toolNames = Array.from(inlineTools.keys());
|
|
922
|
+
|
|
923
|
+
if (toolNames.length === 0) {
|
|
924
|
+
this.scheduleShortcutRegistration();
|
|
925
|
+
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
toolNames.forEach((toolName) => {
|
|
930
|
+
const shortcut = this.getToolShortcut(toolName);
|
|
931
|
+
|
|
932
|
+
this.tryEnableShortcut(toolName, shortcut);
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Resolves the current selection, handling test mocks
|
|
940
|
+
*/
|
|
941
|
+
private resolveSelection(): Selection | null {
|
|
942
|
+
const selectionOverride = (SelectionUtils as unknown as { selection?: Selection | null }).selection;
|
|
943
|
+
|
|
944
|
+
if (selectionOverride !== undefined) {
|
|
945
|
+
return selectionOverride;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const instanceOverride = (SelectionUtils as unknown as { instance?: Selection | null }).instance;
|
|
949
|
+
|
|
950
|
+
if (instanceOverride !== undefined) {
|
|
951
|
+
return instanceOverride;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return SelectionUtils.get();
|
|
955
|
+
}
|
|
956
|
+
}
|