@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,141 @@
|
|
|
1
|
+
import type { InlineTool, API } from '../../../types';
|
|
2
|
+
import type { MenuConfig, MenuConfigItem } from '../../../types/tools';
|
|
3
|
+
import { capitalize, isMobileScreen } from '../utils';
|
|
4
|
+
import type { Blocks, Selection, Tools, Caret, I18n } from '../../../types/api';
|
|
5
|
+
import { SelectionUtils } from '../selection';
|
|
6
|
+
import { getConvertibleToolsForBlock } from '../utils/blocks';
|
|
7
|
+
import { translateToolTitle, translateToolName, type I18nInstance } from '../utils/tools';
|
|
8
|
+
import type { BlockToolAdapter } from '../tools/block';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Inline tools for converting blocks
|
|
12
|
+
*/
|
|
13
|
+
export class ConvertInlineTool implements InlineTool {
|
|
14
|
+
/**
|
|
15
|
+
* Specifies Tool as Inline Toolbar Tool
|
|
16
|
+
*/
|
|
17
|
+
public static isInline = true;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* API for working with blok blocks
|
|
21
|
+
*/
|
|
22
|
+
private readonly blocksAPI: Blocks;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* API for working with Selection
|
|
26
|
+
*/
|
|
27
|
+
private readonly selectionAPI: Selection;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* API for working with Tools
|
|
31
|
+
*/
|
|
32
|
+
private readonly toolsAPI: Tools;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* I18n API
|
|
36
|
+
*/
|
|
37
|
+
private readonly i18nAPI: I18n;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* I18n instance wrapper for tool utilities
|
|
41
|
+
*/
|
|
42
|
+
private readonly i18nInstance: I18nInstance;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* API for working with Caret
|
|
46
|
+
*/
|
|
47
|
+
private readonly caretAPI: Caret;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param api - Blok API
|
|
51
|
+
*/
|
|
52
|
+
constructor({ api }: { api: API }) {
|
|
53
|
+
this.i18nAPI = api.i18n;
|
|
54
|
+
this.blocksAPI = api.blocks;
|
|
55
|
+
this.selectionAPI = api.selection;
|
|
56
|
+
this.toolsAPI = api.tools;
|
|
57
|
+
this.caretAPI = api.caret;
|
|
58
|
+
|
|
59
|
+
// Create wrapper that provides has() method for tool utilities
|
|
60
|
+
// Public API's t() returns the key itself when translation doesn't exist
|
|
61
|
+
this.i18nInstance = {
|
|
62
|
+
t: (key, _vars) => this.i18nAPI.t(key),
|
|
63
|
+
has: (key) => this.i18nAPI.t(key) !== key,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Returns tool's UI config
|
|
69
|
+
*/
|
|
70
|
+
public async render(): Promise<MenuConfig> {
|
|
71
|
+
const currentSelection = SelectionUtils.get();
|
|
72
|
+
|
|
73
|
+
if (currentSelection === null) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const currentBlock = this.blocksAPI.getBlockByElement(currentSelection.anchorNode as HTMLElement);
|
|
78
|
+
|
|
79
|
+
if (currentBlock === undefined) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const allBlockTools = this.toolsAPI.getBlockTools() as BlockToolAdapter[];
|
|
84
|
+
const convertibleTools = await getConvertibleToolsForBlock(currentBlock, allBlockTools);
|
|
85
|
+
|
|
86
|
+
if (convertibleTools.length === 0) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const convertToItems = convertibleTools.reduce<MenuConfigItem[]>((result, tool) => {
|
|
91
|
+
tool.toolbox?.forEach((toolboxItem) => {
|
|
92
|
+
if (toolboxItem.title === undefined) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
result.push({
|
|
97
|
+
icon: toolboxItem.icon,
|
|
98
|
+
title: translateToolTitle(this.i18nInstance, toolboxItem, tool.name),
|
|
99
|
+
name: tool.name,
|
|
100
|
+
closeOnActivate: true,
|
|
101
|
+
onActivate: async () => {
|
|
102
|
+
const newBlock = await this.blocksAPI.convert(currentBlock.id, tool.name, toolboxItem.data);
|
|
103
|
+
|
|
104
|
+
this.caretAPI.setToBlock(newBlock, 'end');
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}, []);
|
|
111
|
+
|
|
112
|
+
const currentBlockToolboxItem = await currentBlock.getActiveToolboxEntry();
|
|
113
|
+
const currentBlockTitle = currentBlockToolboxItem
|
|
114
|
+
? translateToolTitle(this.i18nInstance, currentBlockToolboxItem, currentBlock.name)
|
|
115
|
+
: translateToolName(this.i18nInstance, currentBlock.name, capitalize(currentBlock.name));
|
|
116
|
+
const isDesktop = !isMobileScreen();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
name: 'convert-to',
|
|
120
|
+
title: currentBlockTitle,
|
|
121
|
+
hint: {
|
|
122
|
+
title: this.i18nAPI.t('popover.convertTo'),
|
|
123
|
+
},
|
|
124
|
+
children: {
|
|
125
|
+
items: convertToItems,
|
|
126
|
+
onOpen: () => {
|
|
127
|
+
if (isDesktop) {
|
|
128
|
+
this.selectionAPI.setFakeBackground();
|
|
129
|
+
this.selectionAPI.save();
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
onClose: () => {
|
|
133
|
+
if (isDesktop) {
|
|
134
|
+
this.selectionAPI.restore();
|
|
135
|
+
this.selectionAPI.removeFakeBackground();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import type { InlineTool, SanitizerConfig } from '../../../types';
|
|
2
|
+
import { IconItalic } from '../icons';
|
|
3
|
+
import type { MenuConfig } from '../../../types/tools';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Italic Tool
|
|
7
|
+
*
|
|
8
|
+
* Inline Toolbar Tool
|
|
9
|
+
*
|
|
10
|
+
* Style selected text with italic
|
|
11
|
+
*/
|
|
12
|
+
export class ItalicInlineTool implements InlineTool {
|
|
13
|
+
/**
|
|
14
|
+
* Specifies Tool as Inline Toolbar Tool
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
public static isInline = true;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Title for the Inline Tool
|
|
21
|
+
*/
|
|
22
|
+
public static title = 'Italic';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Translation key for i18n
|
|
26
|
+
*/
|
|
27
|
+
public static titleKey = 'italic';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sanitizer Rule
|
|
31
|
+
* Leave <i> and <em> tags
|
|
32
|
+
* @returns {object}
|
|
33
|
+
*/
|
|
34
|
+
public static get sanitize(): SanitizerConfig {
|
|
35
|
+
return {
|
|
36
|
+
i: {},
|
|
37
|
+
em: {},
|
|
38
|
+
} as SanitizerConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create button for Inline Toolbar
|
|
43
|
+
*/
|
|
44
|
+
public render(): MenuConfig {
|
|
45
|
+
return {
|
|
46
|
+
icon: IconItalic,
|
|
47
|
+
name: 'italic',
|
|
48
|
+
onActivate: () => {
|
|
49
|
+
this.toggleItalic();
|
|
50
|
+
},
|
|
51
|
+
isActive: () => {
|
|
52
|
+
const selection = window.getSelection();
|
|
53
|
+
|
|
54
|
+
return selection ? this.isSelectionVisuallyItalic(selection) : false;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Shortcut for italic tool
|
|
61
|
+
*/
|
|
62
|
+
public static shortcut = 'CMD+I';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Apply or remove italic formatting using modern Selection API
|
|
66
|
+
*/
|
|
67
|
+
private toggleItalic(): void {
|
|
68
|
+
const selection = window.getSelection();
|
|
69
|
+
|
|
70
|
+
if (!selection || selection.rangeCount === 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const range = selection.getRangeAt(0);
|
|
75
|
+
|
|
76
|
+
if (range.collapsed) {
|
|
77
|
+
this.toggleCollapsedItalic(range, selection);
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const shouldUnwrap = this.isRangeItalic(range, { ignoreWhitespace: true });
|
|
83
|
+
|
|
84
|
+
if (shouldUnwrap) {
|
|
85
|
+
this.unwrapItalicTags(range);
|
|
86
|
+
} else {
|
|
87
|
+
this.wrapWithItalic(range);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Handle toggle for collapsed selection (caret)
|
|
93
|
+
* @param range - Current range
|
|
94
|
+
* @param selection - Current selection
|
|
95
|
+
*/
|
|
96
|
+
private toggleCollapsedItalic(range: Range, selection: Selection): void {
|
|
97
|
+
const isItalic = this.isRangeItalic(range, { ignoreWhitespace: true });
|
|
98
|
+
|
|
99
|
+
if (isItalic) {
|
|
100
|
+
const textNode = document.createTextNode('\u200B');
|
|
101
|
+
|
|
102
|
+
range.insertNode(textNode);
|
|
103
|
+
range.selectNode(textNode);
|
|
104
|
+
this.unwrapItalicTags(range);
|
|
105
|
+
|
|
106
|
+
const newRange = document.createRange();
|
|
107
|
+
|
|
108
|
+
newRange.setStart(textNode, 1);
|
|
109
|
+
newRange.setEnd(textNode, 1);
|
|
110
|
+
|
|
111
|
+
selection.removeAllRanges();
|
|
112
|
+
selection.addRange(newRange);
|
|
113
|
+
} else {
|
|
114
|
+
const i = document.createElement('i');
|
|
115
|
+
const textNode = document.createTextNode('\u200B');
|
|
116
|
+
|
|
117
|
+
i.appendChild(textNode);
|
|
118
|
+
range.insertNode(i);
|
|
119
|
+
|
|
120
|
+
const newRange = document.createRange();
|
|
121
|
+
|
|
122
|
+
newRange.setStart(textNode, 1);
|
|
123
|
+
newRange.setEnd(textNode, 1);
|
|
124
|
+
|
|
125
|
+
selection.removeAllRanges();
|
|
126
|
+
selection.addRange(newRange);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if current selection is within an italic tag
|
|
132
|
+
* @param selection - The Selection object to check
|
|
133
|
+
*/
|
|
134
|
+
private isSelectionVisuallyItalic(selection: Selection): boolean {
|
|
135
|
+
if (!selection || selection.rangeCount === 0) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const range = selection.getRangeAt(0);
|
|
140
|
+
|
|
141
|
+
return this.isRangeItalic(range, { ignoreWhitespace: true });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if a range contains italic text
|
|
146
|
+
* @param range - The range to check
|
|
147
|
+
* @param options - Options for checking italic status
|
|
148
|
+
*/
|
|
149
|
+
private isRangeItalic(range: Range, options: { ignoreWhitespace: boolean }): boolean {
|
|
150
|
+
if (range.collapsed) {
|
|
151
|
+
return Boolean(this.findItalicElement(range.startContainer));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const walker = document.createTreeWalker(
|
|
155
|
+
range.commonAncestorContainer,
|
|
156
|
+
NodeFilter.SHOW_TEXT,
|
|
157
|
+
{
|
|
158
|
+
acceptNode: (node) => {
|
|
159
|
+
try {
|
|
160
|
+
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
161
|
+
} catch (_error) {
|
|
162
|
+
const nodeRange = document.createRange();
|
|
163
|
+
|
|
164
|
+
nodeRange.selectNodeContents(node);
|
|
165
|
+
|
|
166
|
+
const startsBeforeEnd = range.compareBoundaryPoints(Range.END_TO_START, nodeRange) > 0;
|
|
167
|
+
const endsAfterStart = range.compareBoundaryPoints(Range.START_TO_END, nodeRange) < 0;
|
|
168
|
+
|
|
169
|
+
return (startsBeforeEnd && endsAfterStart) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const textNodes: Text[] = [];
|
|
176
|
+
|
|
177
|
+
while (walker.nextNode()) {
|
|
178
|
+
const textNode = walker.currentNode as Text;
|
|
179
|
+
const value = textNode.textContent ?? '';
|
|
180
|
+
|
|
181
|
+
if (options.ignoreWhitespace && value.trim().length === 0) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (value.length === 0) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
textNodes.push(textNode);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (textNodes.length === 0) {
|
|
193
|
+
return Boolean(this.findItalicElement(range.startContainer));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return textNodes.every((textNode) => this.hasItalicParent(textNode));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Wrap selection with <i> tag
|
|
201
|
+
* @param range - The Range object containing the selection to wrap
|
|
202
|
+
*/
|
|
203
|
+
private wrapWithItalic(range: Range): void {
|
|
204
|
+
const html = this.getRangeHtmlWithoutItalic(range);
|
|
205
|
+
const insertedRange = this.replaceRangeWithHtml(range, `<i>${html}</i>`);
|
|
206
|
+
const selection = window.getSelection();
|
|
207
|
+
|
|
208
|
+
if (selection && insertedRange) {
|
|
209
|
+
selection.removeAllRanges();
|
|
210
|
+
selection.addRange(insertedRange);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Remove italic tags (<i>/<em>) while preserving content
|
|
216
|
+
* @param range - The Range object containing the selection to unwrap
|
|
217
|
+
*/
|
|
218
|
+
private unwrapItalicTags(range: Range): void {
|
|
219
|
+
const italicAncestors = this.collectItalicAncestors(range);
|
|
220
|
+
const selection = window.getSelection();
|
|
221
|
+
|
|
222
|
+
if (!selection) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const marker = document.createElement('span');
|
|
227
|
+
const fragment = range.extractContents();
|
|
228
|
+
|
|
229
|
+
marker.appendChild(fragment);
|
|
230
|
+
this.removeNestedItalic(marker);
|
|
231
|
+
|
|
232
|
+
range.insertNode(marker);
|
|
233
|
+
|
|
234
|
+
const markerRange = document.createRange();
|
|
235
|
+
|
|
236
|
+
markerRange.selectNodeContents(marker);
|
|
237
|
+
selection.removeAllRanges();
|
|
238
|
+
selection.addRange(markerRange);
|
|
239
|
+
|
|
240
|
+
for (; ;) {
|
|
241
|
+
const currentItalic = this.findItalicElement(marker);
|
|
242
|
+
|
|
243
|
+
if (!currentItalic) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.moveMarkerOutOfItalic(marker, currentItalic);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const firstChild = marker.firstChild;
|
|
251
|
+
const lastChild = marker.lastChild;
|
|
252
|
+
|
|
253
|
+
this.unwrapElement(marker);
|
|
254
|
+
|
|
255
|
+
const finalRange = firstChild && lastChild ? (() => {
|
|
256
|
+
const newRange = document.createRange();
|
|
257
|
+
|
|
258
|
+
newRange.setStartBefore(firstChild);
|
|
259
|
+
newRange.setEndAfter(lastChild);
|
|
260
|
+
|
|
261
|
+
selection.removeAllRanges();
|
|
262
|
+
selection.addRange(newRange);
|
|
263
|
+
|
|
264
|
+
return newRange;
|
|
265
|
+
})() : undefined;
|
|
266
|
+
|
|
267
|
+
if (!finalRange) {
|
|
268
|
+
selection.removeAllRanges();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
italicAncestors.forEach((element) => {
|
|
272
|
+
if ((element.textContent ?? '').length === 0) {
|
|
273
|
+
element.remove();
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check if a node or any of its parents is an italic tag
|
|
280
|
+
* @param node - The node to check
|
|
281
|
+
*/
|
|
282
|
+
private hasItalicParent(node: Node | null): boolean {
|
|
283
|
+
if (!node) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (node.nodeType === Node.ELEMENT_NODE && this.isItalicTag(node as Element)) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return this.hasItalicParent(node.parentNode);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Find an italic element in the parent chain
|
|
296
|
+
* @param node - The node to start searching from
|
|
297
|
+
*/
|
|
298
|
+
private findItalicElement(node: Node | null): HTMLElement | null {
|
|
299
|
+
if (!node) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (node.nodeType === Node.ELEMENT_NODE && this.isItalicTag(node as Element)) {
|
|
304
|
+
return node as HTMLElement;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return this.findItalicElement(node.parentNode);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Check if an element is an italic tag (<i> or <em>)
|
|
312
|
+
* @param node - The element to check
|
|
313
|
+
*/
|
|
314
|
+
private isItalicTag(node: Element): boolean {
|
|
315
|
+
const tag = node.tagName;
|
|
316
|
+
|
|
317
|
+
return tag === 'I' || tag === 'EM';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Collect all italic ancestor elements within a range
|
|
322
|
+
* @param range - The range to search for italic ancestors
|
|
323
|
+
*/
|
|
324
|
+
private collectItalicAncestors(range: Range): HTMLElement[] {
|
|
325
|
+
const ancestors = new Set<HTMLElement>();
|
|
326
|
+
const walker = document.createTreeWalker(
|
|
327
|
+
range.commonAncestorContainer,
|
|
328
|
+
NodeFilter.SHOW_TEXT,
|
|
329
|
+
{
|
|
330
|
+
acceptNode: (node) => {
|
|
331
|
+
try {
|
|
332
|
+
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
333
|
+
} catch (_error) {
|
|
334
|
+
const nodeRange = document.createRange();
|
|
335
|
+
|
|
336
|
+
nodeRange.selectNodeContents(node);
|
|
337
|
+
|
|
338
|
+
const startsBeforeEnd = range.compareBoundaryPoints(Range.END_TO_START, nodeRange) > 0;
|
|
339
|
+
const endsAfterStart = range.compareBoundaryPoints(Range.START_TO_END, nodeRange) < 0;
|
|
340
|
+
|
|
341
|
+
return (startsBeforeEnd && endsAfterStart) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
while (walker.nextNode()) {
|
|
348
|
+
const italicElement = this.findItalicElement(walker.currentNode);
|
|
349
|
+
|
|
350
|
+
if (italicElement) {
|
|
351
|
+
ancestors.add(italicElement);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return Array.from(ancestors);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get HTML content of a range with italic tags removed
|
|
360
|
+
* @param range - The range to extract HTML from
|
|
361
|
+
*/
|
|
362
|
+
private getRangeHtmlWithoutItalic(range: Range): string {
|
|
363
|
+
const contents = range.cloneContents();
|
|
364
|
+
|
|
365
|
+
this.removeNestedItalic(contents);
|
|
366
|
+
|
|
367
|
+
const container = document.createElement('div');
|
|
368
|
+
|
|
369
|
+
container.appendChild(contents);
|
|
370
|
+
|
|
371
|
+
return container.innerHTML;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Remove nested italic tags from a root node
|
|
376
|
+
* @param root - The root node to process
|
|
377
|
+
*/
|
|
378
|
+
private removeNestedItalic(root: ParentNode): void {
|
|
379
|
+
const italicNodes = root.querySelectorAll?.('i,em');
|
|
380
|
+
|
|
381
|
+
if (!italicNodes) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
italicNodes.forEach((node) => {
|
|
386
|
+
this.unwrapElement(node);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Unwrap an element by moving its children to the parent
|
|
392
|
+
* @param element - The element to unwrap
|
|
393
|
+
*/
|
|
394
|
+
private unwrapElement(element: Element): void {
|
|
395
|
+
const parent = element.parentNode;
|
|
396
|
+
|
|
397
|
+
if (!parent) {
|
|
398
|
+
element.remove();
|
|
399
|
+
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
while (element.firstChild) {
|
|
404
|
+
parent.insertBefore(element.firstChild, element);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
parent.removeChild(element);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Replace the current range contents with provided HTML snippet
|
|
412
|
+
* @param range - Range to replace
|
|
413
|
+
* @param html - HTML string to insert
|
|
414
|
+
*/
|
|
415
|
+
private replaceRangeWithHtml(range: Range, html: string): Range | undefined {
|
|
416
|
+
const fragment = this.createFragmentFromHtml(html);
|
|
417
|
+
const firstInserted = fragment.firstChild ?? null;
|
|
418
|
+
const lastInserted = fragment.lastChild ?? null;
|
|
419
|
+
|
|
420
|
+
range.deleteContents();
|
|
421
|
+
|
|
422
|
+
if (!firstInserted || !lastInserted) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
range.insertNode(fragment);
|
|
427
|
+
|
|
428
|
+
const newRange = document.createRange();
|
|
429
|
+
|
|
430
|
+
newRange.setStartBefore(firstInserted);
|
|
431
|
+
newRange.setEndAfter(lastInserted);
|
|
432
|
+
|
|
433
|
+
return newRange;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Convert an HTML snippet to a document fragment
|
|
438
|
+
* @param html - HTML string to convert
|
|
439
|
+
*/
|
|
440
|
+
private createFragmentFromHtml(html: string): DocumentFragment {
|
|
441
|
+
const template = document.createElement('template');
|
|
442
|
+
|
|
443
|
+
template.innerHTML = html;
|
|
444
|
+
|
|
445
|
+
return template.content;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Move a temporary marker element outside of an italic ancestor while preserving content order
|
|
450
|
+
* @param marker - Marker element wrapping the selection contents
|
|
451
|
+
* @param italicElement - Italic ancestor containing the marker
|
|
452
|
+
*/
|
|
453
|
+
private moveMarkerOutOfItalic(marker: HTMLElement, italicElement: HTMLElement): void {
|
|
454
|
+
const parent = italicElement.parentNode;
|
|
455
|
+
|
|
456
|
+
if (!parent) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Remove empty text nodes to ensure accurate child count
|
|
461
|
+
Array.from(italicElement.childNodes).forEach((node) => {
|
|
462
|
+
if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? '').length === 0) {
|
|
463
|
+
node.remove();
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const isOnlyChild = italicElement.childNodes.length === 1 && italicElement.firstChild === marker;
|
|
468
|
+
|
|
469
|
+
if (isOnlyChild) {
|
|
470
|
+
italicElement.replaceWith(marker);
|
|
471
|
+
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const isFirstChild = italicElement.firstChild === marker;
|
|
476
|
+
|
|
477
|
+
if (isFirstChild) {
|
|
478
|
+
parent.insertBefore(marker, italicElement);
|
|
479
|
+
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const isLastChild = italicElement.lastChild === marker;
|
|
484
|
+
|
|
485
|
+
if (isLastChild) {
|
|
486
|
+
parent.insertBefore(marker, italicElement.nextSibling);
|
|
487
|
+
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const trailingClone = italicElement.cloneNode(false) as HTMLElement;
|
|
492
|
+
|
|
493
|
+
while (marker.nextSibling) {
|
|
494
|
+
trailingClone.appendChild(marker.nextSibling);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
parent.insertBefore(trailingClone, italicElement.nextSibling);
|
|
498
|
+
parent.insertBefore(marker, trailingClone);
|
|
499
|
+
}
|
|
500
|
+
}
|