@jackuait/blok 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -17
- package/codemod/README.md +16 -0
- package/codemod/migrate-editorjs-to-blok.js +859 -92
- package/codemod/test.js +682 -77
- package/dist/blok.mjs +5 -2
- package/dist/chunks/blok-BjgH1REI.mjs +12838 -0
- package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
- package/dist/chunks/i18next-loader-DfiUa_gd.mjs +43 -0
- package/dist/{index-CBkApZKo.mjs → chunks/index-5m5JWNey.mjs} +2 -2
- package/dist/chunks/inline-tool-convert-Bx5BVd8I.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 +47 -15
- 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 -24
- 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/list.d.ts +25 -18
- package/types/tools/tool-settings.d.ts +8 -1
- 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-BwPfU8ro.mjs +0 -21510
- package/dist/blok.umd.js +0 -198
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { DomIterator } from './domIterator';
|
|
2
|
+
import { isFunction, keyCodes } from './utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Flipper construction options
|
|
6
|
+
* @interface FlipperOptions
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export interface FlipperOptions {
|
|
10
|
+
/**
|
|
11
|
+
* CSS-modifier for focused item
|
|
12
|
+
*/
|
|
13
|
+
focusedItemClass?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Allow handling keyboard events dispatched from contenteditable elements
|
|
17
|
+
*/
|
|
18
|
+
handleContentEditableTargets?: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* If flipping items are the same for all Block (for ex. Toolbox), ypu can pass it on constructing
|
|
22
|
+
*/
|
|
23
|
+
items?: HTMLElement[];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional callback for button click
|
|
27
|
+
*/
|
|
28
|
+
activateCallback?: (item: HTMLElement) => void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* List of keys allowed for handling.
|
|
32
|
+
* Can include codes of the following keys:
|
|
33
|
+
* - Tab
|
|
34
|
+
* - Enter
|
|
35
|
+
* - Arrow up
|
|
36
|
+
* - Arrow down
|
|
37
|
+
* - Arrow right
|
|
38
|
+
* - Arrow left
|
|
39
|
+
* If not specified all keys are enabled
|
|
40
|
+
*/
|
|
41
|
+
allowedKeys?: number[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Callback fired when ArrowLeft is pressed.
|
|
45
|
+
* Used by nested popovers to close and return focus to parent.
|
|
46
|
+
*/
|
|
47
|
+
onArrowLeft?: () => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Flipper is a component that iterates passed items array by TAB or Arrows and clicks it by ENTER
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
export class Flipper {
|
|
55
|
+
/**
|
|
56
|
+
* True if flipper is currently activated
|
|
57
|
+
*/
|
|
58
|
+
public get isActivated(): boolean {
|
|
59
|
+
return this.activated;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Instance of flipper iterator
|
|
64
|
+
*/
|
|
65
|
+
private readonly iterator: DomIterator | null = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Flag that defines activation status
|
|
69
|
+
*/
|
|
70
|
+
private activated = false;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Skip moving focus on the next Tab press when initial focus was pre-set
|
|
74
|
+
*/
|
|
75
|
+
private skipNextTabFocus = false;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* True if flipper should handle events coming from contenteditable elements
|
|
79
|
+
*/
|
|
80
|
+
private handleContentEditableTargets: boolean;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* List codes of the keys allowed for handling
|
|
84
|
+
*/
|
|
85
|
+
private readonly allowedKeys: number[];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Call back for button click/enter
|
|
89
|
+
*/
|
|
90
|
+
private readonly activateCallback?: (item: HTMLElement) => void;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Contains list of callbacks to be executed on each flip
|
|
94
|
+
*/
|
|
95
|
+
private flipCallbacks: Array<() => void> = [];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Callback fired when ArrowLeft is pressed.
|
|
99
|
+
* Used by nested popovers to close and return focus to parent.
|
|
100
|
+
*/
|
|
101
|
+
private readonly onArrowLeftCallback?: () => void;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param options - different constructing settings
|
|
105
|
+
*/
|
|
106
|
+
constructor(options: FlipperOptions) {
|
|
107
|
+
this.iterator = new DomIterator(options.items || [], options.focusedItemClass ?? '');
|
|
108
|
+
this.activateCallback = options.activateCallback;
|
|
109
|
+
this.allowedKeys = options.allowedKeys || Flipper.usedKeys;
|
|
110
|
+
this.handleContentEditableTargets = options.handleContentEditableTargets ?? false;
|
|
111
|
+
this.onArrowLeftCallback = options.onArrowLeft;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Array of keys (codes) that is handled by Flipper
|
|
116
|
+
* Used to:
|
|
117
|
+
* - preventDefault only for this keys, not all keydowns (@see constructor)
|
|
118
|
+
* - to skip external behaviours only for these keys, when filler is activated (@see BlockEvents@arrowRightAndDown)
|
|
119
|
+
*/
|
|
120
|
+
public static get usedKeys(): number[] {
|
|
121
|
+
return [
|
|
122
|
+
keyCodes.TAB,
|
|
123
|
+
keyCodes.LEFT,
|
|
124
|
+
keyCodes.RIGHT,
|
|
125
|
+
keyCodes.ENTER,
|
|
126
|
+
keyCodes.UP,
|
|
127
|
+
keyCodes.DOWN,
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Active tab/arrows handling by flipper
|
|
133
|
+
* @param items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically
|
|
134
|
+
* @param cursorPosition - index of the item that should be focused once flipper is activated
|
|
135
|
+
*/
|
|
136
|
+
public activate(items?: HTMLElement[], cursorPosition?: number): void {
|
|
137
|
+
this.activated = true;
|
|
138
|
+
if (items) {
|
|
139
|
+
this.iterator?.setItems(items);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (cursorPosition !== undefined) {
|
|
143
|
+
this.iterator?.setCursor(cursorPosition);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Listening all keydowns on document and react on TAB/Enter press
|
|
148
|
+
* TAB will leaf iterator items
|
|
149
|
+
* ENTER will click the focused item
|
|
150
|
+
*
|
|
151
|
+
* Note: the event should be handled in capturing mode on following reasons:
|
|
152
|
+
* - prevents plugins inner keydown handlers from being called while keyboard navigation
|
|
153
|
+
* - otherwise this handler will be called at the moment it is attached which causes false flipper firing (see https://techread.me/js-addeventlistener-fires-for-past-events/)
|
|
154
|
+
*/
|
|
155
|
+
document.addEventListener('keydown', this.onKeyDown, true);
|
|
156
|
+
window.addEventListener('keydown', this.onKeyDown, true);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Disable tab/arrows handling by flipper
|
|
161
|
+
*/
|
|
162
|
+
public deactivate(): void {
|
|
163
|
+
this.activated = false;
|
|
164
|
+
this.dropCursor();
|
|
165
|
+
this.skipNextTabFocus = false;
|
|
166
|
+
|
|
167
|
+
document.removeEventListener('keydown', this.onKeyDown, true);
|
|
168
|
+
window.removeEventListener('keydown', this.onKeyDown, true);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Focus first item
|
|
173
|
+
*/
|
|
174
|
+
public focusFirst(): void {
|
|
175
|
+
this.dropCursor();
|
|
176
|
+
this.flipRight();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Focus item at specified position without triggering flip callbacks
|
|
181
|
+
* @param position - index of item to focus. Negative value clears focus.
|
|
182
|
+
* @param options - optional settings for focus behavior
|
|
183
|
+
* @param options.skipNextTab - if true, skip the next Tab press (default: true for position 0 when no current item)
|
|
184
|
+
*/
|
|
185
|
+
public focusItem(position: number, options?: { skipNextTab?: boolean }): void {
|
|
186
|
+
const iterator = this.iterator;
|
|
187
|
+
|
|
188
|
+
if (!iterator) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!iterator.hasItems()) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (position < 0) {
|
|
197
|
+
iterator.dropCursor();
|
|
198
|
+
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const shouldSkipNextTab = options?.skipNextTab ?? (!iterator.currentItem && position === 0);
|
|
203
|
+
|
|
204
|
+
if (shouldSkipNextTab) {
|
|
205
|
+
this.skipNextTabFocus = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
iterator.setCursor(position);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Focuses previous flipper iterator item
|
|
213
|
+
*/
|
|
214
|
+
public flipLeft(): void {
|
|
215
|
+
this.iterator?.previous();
|
|
216
|
+
this.flipCallback();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Focuses next flipper iterator item
|
|
221
|
+
*/
|
|
222
|
+
public flipRight(): void {
|
|
223
|
+
this.iterator?.next();
|
|
224
|
+
this.flipCallback();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Return true if some button is focused
|
|
229
|
+
*/
|
|
230
|
+
public hasFocus(): boolean {
|
|
231
|
+
return !!this.iterator?.currentItem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Checks if current focused item has children (nested menu)
|
|
236
|
+
* Looks for data-blok-has-children attribute on the current item
|
|
237
|
+
*/
|
|
238
|
+
private currentItemHasChildren(): boolean {
|
|
239
|
+
const currentItem = this.iterator?.currentItem;
|
|
240
|
+
|
|
241
|
+
if (!currentItem) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return currentItem.hasAttribute('data-blok-has-children');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Registers a function that should be executed on each navigation action
|
|
250
|
+
* @param cb - function to execute
|
|
251
|
+
*/
|
|
252
|
+
public onFlip(cb: () => void): void {
|
|
253
|
+
this.flipCallbacks.push(cb);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Unregisters a function that is executed on each navigation action
|
|
258
|
+
* @param cb - function to stop executing
|
|
259
|
+
*/
|
|
260
|
+
public removeOnFlip(cb: () => void): void {
|
|
261
|
+
this.flipCallbacks = this.flipCallbacks.filter(fn => fn !== cb);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Drops flipper's iterator cursor
|
|
266
|
+
* @see DomIterator#dropCursor
|
|
267
|
+
*/
|
|
268
|
+
private dropCursor(): void {
|
|
269
|
+
this.iterator?.dropCursor();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get numeric keyCode from KeyboardEvent.key for backward compatibility
|
|
274
|
+
* @param event - keyboard event
|
|
275
|
+
* @returns numeric keyCode or null if not recognized
|
|
276
|
+
*/
|
|
277
|
+
private getKeyCode(event: KeyboardEvent): number | null {
|
|
278
|
+
const keyToCodeMap: Record<string, number> = {
|
|
279
|
+
'Tab': keyCodes.TAB,
|
|
280
|
+
'Enter': keyCodes.ENTER,
|
|
281
|
+
'ArrowLeft': keyCodes.LEFT,
|
|
282
|
+
'ArrowRight': keyCodes.RIGHT,
|
|
283
|
+
'ArrowUp': keyCodes.UP,
|
|
284
|
+
'ArrowDown': keyCodes.DOWN,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return keyToCodeMap[event.key] ?? null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* KeyDown event handler
|
|
292
|
+
* @param event - keydown event
|
|
293
|
+
*/
|
|
294
|
+
private onKeyDown = (event: KeyboardEvent): void => {
|
|
295
|
+
const target = event.target as HTMLElement | null;
|
|
296
|
+
|
|
297
|
+
if (this.shouldSkipTarget(target, event)) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const keyCode = this.getKeyCode(event);
|
|
302
|
+
const isDirectionalArrow = keyCode === keyCodes.LEFT
|
|
303
|
+
|| keyCode === keyCodes.RIGHT
|
|
304
|
+
|| keyCode === keyCodes.UP
|
|
305
|
+
|| keyCode === keyCodes.DOWN;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Allow selecting text with Shift combined with arrow keys by delegating handling to the browser.
|
|
309
|
+
* Other Shift-based combinations (for example Shift+Tab) are still handled by Flipper.
|
|
310
|
+
*/
|
|
311
|
+
if (event.shiftKey && isDirectionalArrow) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const isReady = this.isEventReadyForHandling(event);
|
|
316
|
+
|
|
317
|
+
if (!isReady) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* For Enter key, only handle it if there's a focused item.
|
|
323
|
+
* Otherwise, let the event propagate to allow block splitting etc.
|
|
324
|
+
*/
|
|
325
|
+
if (keyCode === keyCodes.ENTER && !this.iterator?.currentItem) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Stop propagation to prevent plugin-level handlers from being called
|
|
331
|
+
* while Flipper manages keyboard navigation.
|
|
332
|
+
*/
|
|
333
|
+
event.stopPropagation();
|
|
334
|
+
event.stopImmediatePropagation();
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Prevent only used keys default behaviour
|
|
339
|
+
* (allows to navigate by ARROW DOWN, for example)
|
|
340
|
+
*/
|
|
341
|
+
if (keyCode !== null && Flipper.usedKeys.includes(keyCode)) {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
switch (keyCode) {
|
|
346
|
+
case keyCodes.TAB:
|
|
347
|
+
this.handleTabPress(event);
|
|
348
|
+
break;
|
|
349
|
+
case keyCodes.LEFT:
|
|
350
|
+
// ArrowLeft triggers callback only if callback is set (for nested popovers)
|
|
351
|
+
if (this.onArrowLeftCallback) {
|
|
352
|
+
this.onArrowLeftCallback();
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
case keyCodes.UP:
|
|
356
|
+
this.flipLeft();
|
|
357
|
+
break;
|
|
358
|
+
case keyCodes.RIGHT:
|
|
359
|
+
// ArrowRight clicks the focused item to open nested popover, but only if item has children
|
|
360
|
+
// Otherwise, do nothing (don't activate items without nested menu)
|
|
361
|
+
if (this.iterator?.currentItem && this.currentItemHasChildren()) {
|
|
362
|
+
this.handleEnterPress(event);
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
case keyCodes.DOWN:
|
|
366
|
+
this.flipRight();
|
|
367
|
+
break;
|
|
368
|
+
case keyCodes.ENTER:
|
|
369
|
+
this.handleEnterPress(event);
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* This function is fired before handling flipper keycodes
|
|
376
|
+
* The result of this function defines if it is need to be handled or not
|
|
377
|
+
* @param {KeyboardEvent} event - keydown keyboard event
|
|
378
|
+
* @returns {boolean}
|
|
379
|
+
*/
|
|
380
|
+
private isEventReadyForHandling(event: KeyboardEvent): boolean {
|
|
381
|
+
const keyCode = this.getKeyCode(event);
|
|
382
|
+
|
|
383
|
+
return this.activated && keyCode !== null && this.allowedKeys.includes(keyCode);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Enables or disables handling events dispatched from contenteditable elements
|
|
388
|
+
* @param value - true if events from contenteditable elements should be handled
|
|
389
|
+
*/
|
|
390
|
+
public setHandleContentEditableTargets(value: boolean): void {
|
|
391
|
+
this.handleContentEditableTargets = value;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* When flipper is activated tab press will leaf the items
|
|
396
|
+
* @param {KeyboardEvent} event - tab keydown event
|
|
397
|
+
*/
|
|
398
|
+
private handleTabPress(event: KeyboardEvent): void {
|
|
399
|
+
/** this property defines leaf direction */
|
|
400
|
+
const shiftKey = event.shiftKey;
|
|
401
|
+
const direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT;
|
|
402
|
+
|
|
403
|
+
if (this.skipNextTabFocus) {
|
|
404
|
+
this.skipNextTabFocus = false;
|
|
405
|
+
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
switch (direction) {
|
|
410
|
+
case DomIterator.directions.RIGHT:
|
|
411
|
+
this.flipRight();
|
|
412
|
+
break;
|
|
413
|
+
case DomIterator.directions.LEFT:
|
|
414
|
+
this.flipLeft();
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Delegates external keyboard events to the flipper handler.
|
|
421
|
+
* @param event - keydown event captured outside the flipper
|
|
422
|
+
*/
|
|
423
|
+
public handleExternalKeydown(event: KeyboardEvent): void {
|
|
424
|
+
this.onKeyDown(event);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Enter press will click current item if flipper is activated
|
|
429
|
+
* @param {KeyboardEvent} event - enter keydown event
|
|
430
|
+
*/
|
|
431
|
+
private handleEnterPress(event: KeyboardEvent): void {
|
|
432
|
+
if (!this.activated) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (this.iterator?.currentItem) {
|
|
437
|
+
/**
|
|
438
|
+
* Stop Enter propagation only if flipper is ready to select focused item
|
|
439
|
+
*/
|
|
440
|
+
event.stopPropagation();
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
this.iterator.currentItem.click();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (isFunction(this.activateCallback) && this.iterator?.currentItem) {
|
|
446
|
+
this.activateCallback(this.iterator.currentItem);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Fired after flipping in any direction
|
|
452
|
+
*/
|
|
453
|
+
private flipCallback(): void {
|
|
454
|
+
if (this.iterator?.currentItem) {
|
|
455
|
+
this.iterator.currentItem.scrollIntoViewIfNeeded?.();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
this.flipCallbacks.forEach(cb => cb());
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Determine if keyboard events coming from a target should be skipped
|
|
463
|
+
* @param target - event target element
|
|
464
|
+
* @param event - keyboard event being handled
|
|
465
|
+
*/
|
|
466
|
+
private shouldSkipTarget(target: HTMLElement | null, event: KeyboardEvent): boolean {
|
|
467
|
+
if (!target) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const isNativeInput = target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement;
|
|
472
|
+
const isNavigationKey = event.key === 'Tab' || event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Enter' || event.key === 'ArrowRight' || event.key === 'ArrowLeft';
|
|
473
|
+
const shouldHandleNativeInput = target.getAttribute('data-blok-flipper-navigation-target') === 'true' && isNavigationKey;
|
|
474
|
+
const isContentEditable = target.isContentEditable;
|
|
475
|
+
const isInlineToolInput = target.closest('[data-blok-link-tool-input-opened="true"]') !== null;
|
|
476
|
+
|
|
477
|
+
const shouldSkipContentEditable = isContentEditable && !this.handleContentEditableTargets;
|
|
478
|
+
|
|
479
|
+
return (isNativeInput && !shouldHandleNativeInput) || shouldSkipContentEditable || isInlineToolInput;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { i18n as I18nextInstance, InitOptions } from 'i18next';
|
|
2
|
+
import type { I18nDictionary } from '../../../types/configs/i18n-dictionary';
|
|
3
|
+
import type { SupportedLocale } from '../../../types/configs/i18n-config';
|
|
4
|
+
import { englishDictionary } from './lightweight-i18n';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* i18next instance type after initialization
|
|
8
|
+
*/
|
|
9
|
+
export type InitializedI18next = I18nextInstance;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Result of initializing i18next
|
|
13
|
+
*/
|
|
14
|
+
export interface I18nextInitResult {
|
|
15
|
+
instance: InitializedI18next;
|
|
16
|
+
t: (key: string, vars?: Record<string, string | number>) => string;
|
|
17
|
+
has: (key: string) => boolean;
|
|
18
|
+
setDictionary: (dictionary: I18nDictionary) => void;
|
|
19
|
+
changeLanguage: (locale: SupportedLocale) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Dynamically load and initialize i18next.
|
|
24
|
+
* This is only called when a non-English locale is needed.
|
|
25
|
+
*
|
|
26
|
+
* @param initialLocale - The locale to initialize with
|
|
27
|
+
* @param localeConfig - The loaded locale configuration
|
|
28
|
+
* @returns Initialized i18next wrapper
|
|
29
|
+
*/
|
|
30
|
+
export const loadI18next = async (
|
|
31
|
+
initialLocale: SupportedLocale,
|
|
32
|
+
localeConfig: { dictionary: I18nDictionary }
|
|
33
|
+
): Promise<I18nextInitResult> => {
|
|
34
|
+
// Dynamic import of i18next - this is the key to tree-shaking it out
|
|
35
|
+
const i18next = await import('i18next');
|
|
36
|
+
|
|
37
|
+
// Create a new instance (not the global one) for isolation
|
|
38
|
+
const instance = i18next.default.createInstance();
|
|
39
|
+
|
|
40
|
+
const initOptions: InitOptions = {
|
|
41
|
+
lng: initialLocale,
|
|
42
|
+
fallbackLng: 'en',
|
|
43
|
+
resources: {
|
|
44
|
+
en: {
|
|
45
|
+
translation: englishDictionary,
|
|
46
|
+
},
|
|
47
|
+
[initialLocale]: {
|
|
48
|
+
translation: localeConfig.dictionary,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
interpolation: {
|
|
52
|
+
// Use single braces {var} to match existing format
|
|
53
|
+
prefix: '{',
|
|
54
|
+
suffix: '}',
|
|
55
|
+
escapeValue: false, // React/DOM handles escaping
|
|
56
|
+
},
|
|
57
|
+
// Return key if translation is missing (consistent behavior)
|
|
58
|
+
returnNull: false,
|
|
59
|
+
returnEmptyString: false,
|
|
60
|
+
// Don't parse keys as nested objects - we use flat keys with dots
|
|
61
|
+
keySeparator: false,
|
|
62
|
+
nsSeparator: false,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
await instance.init(initOptions);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
instance,
|
|
69
|
+
t: (key: string, vars?: Record<string, string | number>): string => {
|
|
70
|
+
return instance.t(key, vars);
|
|
71
|
+
},
|
|
72
|
+
has: (key: string): boolean => {
|
|
73
|
+
return instance.exists(key);
|
|
74
|
+
},
|
|
75
|
+
setDictionary: (dictionary: I18nDictionary): void => {
|
|
76
|
+
const currentLang = instance.language;
|
|
77
|
+
|
|
78
|
+
instance.addResourceBundle(currentLang, 'translation', dictionary, true, true);
|
|
79
|
+
},
|
|
80
|
+
changeLanguage: async (locale: SupportedLocale): Promise<void> => {
|
|
81
|
+
await instance.changeLanguage(locale);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { I18nDictionary } from '../../../types/configs/i18n-dictionary';
|
|
2
|
+
import enMessages from './locales/en/messages.json';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lightweight i18n implementation for English-only usage.
|
|
6
|
+
* Provides simple string interpolation without the overhead of i18next.
|
|
7
|
+
*
|
|
8
|
+
* For non-English locales, the full i18next-based implementation is loaded dynamically.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export class LightweightI18n {
|
|
13
|
+
/**
|
|
14
|
+
* Current dictionary (defaults to English)
|
|
15
|
+
*/
|
|
16
|
+
private dictionary: I18nDictionary;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Custom overrides set via setDictionary
|
|
20
|
+
*/
|
|
21
|
+
private overrides: I18nDictionary | null = null;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.dictionary = enMessages;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Translate a key with optional interpolation.
|
|
29
|
+
*
|
|
30
|
+
* @param key - Translation key
|
|
31
|
+
* @param vars - Optional variables for interpolation
|
|
32
|
+
* @returns Translated string, or the key if not found
|
|
33
|
+
*/
|
|
34
|
+
public t(key: string, vars?: Record<string, string | number>): string {
|
|
35
|
+
// Check overrides first, then fall back to base dictionary
|
|
36
|
+
const value = this.overrides?.[key] ?? this.dictionary[key];
|
|
37
|
+
|
|
38
|
+
if (value === undefined) {
|
|
39
|
+
return key;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (vars === undefined) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Simple interpolation: replace {varName} with value
|
|
47
|
+
return value.replace(/\{(\w+)\}/g, (match, varName: string) => {
|
|
48
|
+
const replacement = vars[varName];
|
|
49
|
+
|
|
50
|
+
return replacement !== undefined ? String(replacement) : match;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a translation exists
|
|
56
|
+
*/
|
|
57
|
+
public has(key: string): boolean {
|
|
58
|
+
return (this.overrides?.[key] ?? this.dictionary[key]) !== undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Add custom dictionary overrides
|
|
63
|
+
*/
|
|
64
|
+
public setDictionary(dictionary: I18nDictionary): void {
|
|
65
|
+
this.overrides = { ...this.overrides, ...dictionary };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the current locale
|
|
70
|
+
*/
|
|
71
|
+
public getLocale(): 'en' {
|
|
72
|
+
return 'en';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get text direction (always LTR for English)
|
|
77
|
+
*/
|
|
78
|
+
public getDirection(): 'ltr' {
|
|
79
|
+
return 'ltr';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* English dictionary for synchronous access
|
|
85
|
+
*/
|
|
86
|
+
export const englishDictionary = enMessages;
|