@jackuait/blok 0.4.1-beta.0 → 0.4.1-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -17
- package/codemod/README.md +45 -7
- package/codemod/migrate-editorjs-to-blok.js +960 -92
- package/codemod/test.js +780 -77
- package/dist/blok.mjs +5 -2
- package/dist/chunks/blok-oNSQ3HA6.mjs +13217 -0
- package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
- package/dist/chunks/i18next-loader-BdNRw4n4.mjs +43 -0
- package/dist/{index-OwEtDFlk.mjs → chunks/index-DHgXmfki.mjs} +2 -2
- package/dist/chunks/inline-tool-convert-CRqgjRim.mjs +1989 -0
- package/dist/chunks/messages-0tDXLuyH.mjs +48 -0
- package/dist/chunks/messages-2_xedlYw.mjs +48 -0
- package/dist/chunks/messages-AHESHJm_.mjs +48 -0
- package/dist/chunks/messages-B5hdXZwA.mjs +48 -0
- package/dist/chunks/messages-B5jGUnOy.mjs +48 -0
- package/dist/chunks/messages-B5puUm7R.mjs +48 -0
- package/dist/chunks/messages-B66ZSDCJ.mjs +48 -0
- package/dist/chunks/messages-B9Oba7sq.mjs +48 -0
- package/dist/chunks/messages-BA0rcTCY.mjs +48 -0
- package/dist/chunks/messages-BBJgd5jG.mjs +48 -0
- package/dist/chunks/messages-BPqWKx5Z.mjs +48 -0
- package/dist/chunks/messages-Bdv-IkfG.mjs +48 -0
- package/dist/chunks/messages-BeUhMpsr.mjs +48 -0
- package/dist/chunks/messages-Bf6Y3_GI.mjs +48 -0
- package/dist/chunks/messages-BiExzWJv.mjs +48 -0
- package/dist/chunks/messages-BlpqL8vG.mjs +48 -0
- package/dist/chunks/messages-BmKCChWZ.mjs +48 -0
- package/dist/chunks/messages-Bn253WWC.mjs +48 -0
- package/dist/chunks/messages-BrJHUxQL.mjs +48 -0
- package/dist/chunks/messages-C5b7hr_E.mjs +48 -0
- package/dist/chunks/messages-C7I_AVH2.mjs +48 -0
- package/dist/chunks/messages-CJoBtXU6.mjs +48 -0
- package/dist/chunks/messages-CQj2JU2j.mjs +48 -0
- package/dist/chunks/messages-CUZ1x1QD.mjs +48 -0
- package/dist/chunks/messages-CUy1vn-b.mjs +48 -0
- package/dist/chunks/messages-CVeWVKsV.mjs +48 -0
- package/dist/chunks/messages-CXHd9SUK.mjs +48 -0
- package/dist/chunks/messages-CbMyJSzS.mjs +48 -0
- package/dist/chunks/messages-CbhuIWRJ.mjs +48 -0
- package/dist/chunks/messages-CeCjVKMW.mjs +48 -0
- package/dist/chunks/messages-Cj-t1bdy.mjs +48 -0
- package/dist/chunks/messages-CkFT2gle.mjs +48 -0
- package/dist/chunks/messages-Cm9aLHeX.mjs +48 -0
- package/dist/chunks/messages-CnvW8Slp.mjs +48 -0
- package/dist/chunks/messages-Cr-RJ7YB.mjs +48 -0
- package/dist/chunks/messages-CrsJ1TEJ.mjs +48 -0
- package/dist/chunks/messages-Cu08aLS3.mjs +48 -0
- package/dist/chunks/messages-CvaqJFN-.mjs +48 -0
- package/dist/chunks/messages-CyDU5lz9.mjs +48 -0
- package/dist/chunks/messages-CySyfkMU.mjs +48 -0
- package/dist/chunks/messages-Cyi2AMmz.mjs +48 -0
- package/dist/chunks/messages-D00OjS2n.mjs +48 -0
- package/dist/chunks/messages-DDLgIPDF.mjs +48 -0
- package/dist/chunks/messages-DMQIHGRj.mjs +48 -0
- package/dist/chunks/messages-DOlC_Tty.mjs +48 -0
- package/dist/chunks/messages-DV6shA9b.mjs +48 -0
- package/dist/chunks/messages-DY94ykcE.mjs +48 -0
- package/dist/chunks/messages-DbVquYKN.mjs +48 -0
- package/dist/chunks/messages-DcKOuncK.mjs +48 -0
- package/dist/chunks/messages-Dg92dXZ5.mjs +48 -0
- package/dist/chunks/messages-DnbbyJT3.mjs +48 -0
- package/dist/chunks/messages-DteYq0rv.mjs +48 -0
- package/dist/chunks/messages-GC2PhgV3.mjs +48 -0
- package/dist/chunks/messages-JGsXAReJ.mjs +48 -0
- package/dist/chunks/messages-JZUhXTuV.mjs +48 -0
- package/dist/chunks/messages-LvFKBBPa.mjs +48 -0
- package/dist/chunks/messages-NP1myMGI.mjs +48 -0
- package/dist/chunks/messages-Q4kc_ZtL.mjs +48 -0
- package/dist/chunks/messages-RvMHb2Ht.mjs +48 -0
- package/dist/chunks/messages-ftMcCEuO.mjs +48 -0
- package/dist/chunks/messages-o24dK6CU.mjs +48 -0
- package/dist/chunks/messages-pA5TvcAj.mjs +48 -0
- package/dist/chunks/messages-rRSHQDCX.mjs +48 -0
- package/dist/chunks/messages-srxrv8Yh.mjs +48 -0
- package/dist/chunks/messages-wdqp4610.mjs +48 -0
- package/dist/chunks/messages-zS1AXZ0y.mjs +48 -0
- package/dist/chunks/messages-zSzDzXej.mjs +48 -0
- package/dist/full.mjs +50 -0
- package/dist/locales.mjs +228 -0
- package/dist/messages-0tDXLuyH.mjs +48 -0
- package/dist/messages-2_xedlYw.mjs +48 -0
- package/dist/messages-AHESHJm_.mjs +48 -0
- package/dist/messages-B5hdXZwA.mjs +48 -0
- package/dist/messages-B5jGUnOy.mjs +48 -0
- package/dist/messages-B5puUm7R.mjs +48 -0
- package/dist/messages-B66ZSDCJ.mjs +48 -0
- package/dist/messages-B9Oba7sq.mjs +48 -0
- package/dist/messages-BA0rcTCY.mjs +48 -0
- package/dist/messages-BBJgd5jG.mjs +48 -0
- package/dist/messages-BPqWKx5Z.mjs +48 -0
- package/dist/messages-Bdv-IkfG.mjs +48 -0
- package/dist/messages-BeUhMpsr.mjs +48 -0
- package/dist/messages-Bf6Y3_GI.mjs +48 -0
- package/dist/messages-BiExzWJv.mjs +48 -0
- package/dist/messages-BlpqL8vG.mjs +48 -0
- package/dist/messages-BmKCChWZ.mjs +48 -0
- package/dist/messages-Bn253WWC.mjs +48 -0
- package/dist/messages-BrJHUxQL.mjs +48 -0
- package/dist/messages-C5b7hr_E.mjs +48 -0
- package/dist/messages-C7I_AVH2.mjs +48 -0
- package/dist/messages-CJoBtXU6.mjs +48 -0
- package/dist/messages-CQj2JU2j.mjs +48 -0
- package/dist/messages-CUZ1x1QD.mjs +48 -0
- package/dist/messages-CUy1vn-b.mjs +48 -0
- package/dist/messages-CVeWVKsV.mjs +48 -0
- package/dist/messages-CXHd9SUK.mjs +48 -0
- package/dist/messages-CbMyJSzS.mjs +48 -0
- package/dist/messages-CbhuIWRJ.mjs +48 -0
- package/dist/messages-CeCjVKMW.mjs +48 -0
- package/dist/messages-Cj-t1bdy.mjs +48 -0
- package/dist/messages-CkFT2gle.mjs +48 -0
- package/dist/messages-Cm9aLHeX.mjs +48 -0
- package/dist/messages-CnvW8Slp.mjs +48 -0
- package/dist/messages-Cr-RJ7YB.mjs +48 -0
- package/dist/messages-CrsJ1TEJ.mjs +48 -0
- package/dist/messages-Cu08aLS3.mjs +48 -0
- package/dist/messages-CvaqJFN-.mjs +48 -0
- package/dist/messages-CyDU5lz9.mjs +48 -0
- package/dist/messages-CySyfkMU.mjs +48 -0
- package/dist/messages-Cyi2AMmz.mjs +48 -0
- package/dist/messages-D00OjS2n.mjs +48 -0
- package/dist/messages-DDLgIPDF.mjs +48 -0
- package/dist/messages-DMQIHGRj.mjs +48 -0
- package/dist/messages-DOlC_Tty.mjs +48 -0
- package/dist/messages-DV6shA9b.mjs +48 -0
- package/dist/messages-DY94ykcE.mjs +48 -0
- package/dist/messages-DbVquYKN.mjs +48 -0
- package/dist/messages-DcKOuncK.mjs +48 -0
- package/dist/messages-Dg92dXZ5.mjs +48 -0
- package/dist/messages-DnbbyJT3.mjs +48 -0
- package/dist/messages-DteYq0rv.mjs +48 -0
- package/dist/messages-GC2PhgV3.mjs +48 -0
- package/dist/messages-JGsXAReJ.mjs +48 -0
- package/dist/messages-JZUhXTuV.mjs +48 -0
- package/dist/messages-LvFKBBPa.mjs +48 -0
- package/dist/messages-NP1myMGI.mjs +48 -0
- package/dist/messages-Q4kc_ZtL.mjs +48 -0
- package/dist/messages-RvMHb2Ht.mjs +48 -0
- package/dist/messages-ftMcCEuO.mjs +48 -0
- package/dist/messages-o24dK6CU.mjs +48 -0
- package/dist/messages-pA5TvcAj.mjs +48 -0
- package/dist/messages-rRSHQDCX.mjs +48 -0
- package/dist/messages-srxrv8Yh.mjs +48 -0
- package/dist/messages-wdqp4610.mjs +48 -0
- package/dist/messages-zS1AXZ0y.mjs +48 -0
- package/dist/messages-zSzDzXej.mjs +48 -0
- package/dist/tools.mjs +3117 -0
- package/dist/vendor.LICENSE.txt +26 -225
- package/package.json +63 -24
- package/src/blok.ts +267 -0
- package/src/components/__module.ts +139 -0
- package/src/components/block/api.ts +155 -0
- package/src/components/block/index.ts +1428 -0
- package/src/components/block-tunes/block-tune-delete.ts +51 -0
- package/src/components/blocks.ts +352 -0
- package/src/components/constants/data-attributes.ts +344 -0
- package/src/components/constants.ts +76 -0
- package/src/components/core.ts +392 -0
- package/src/components/dom.ts +773 -0
- package/src/components/domIterator.ts +189 -0
- package/src/components/errors/critical.ts +5 -0
- package/src/components/events/BlockChanged.ts +16 -0
- package/src/components/events/BlockHovered.ts +21 -0
- package/src/components/events/BlockSettingsClosed.ts +12 -0
- package/src/components/events/BlockSettingsOpened.ts +12 -0
- package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
- package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
- package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
- package/src/components/events/HistoryStateChanged.ts +19 -0
- package/src/components/events/RedactorDomChanged.ts +14 -0
- package/src/components/events/index.ts +46 -0
- package/src/components/flipper.ts +497 -0
- package/src/components/i18n/i18next-loader.ts +84 -0
- package/src/components/i18n/lightweight-i18n.ts +86 -0
- package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
- package/src/components/i18n/locales/am/messages.json +45 -0
- package/src/components/i18n/locales/ar/messages.json +45 -0
- package/src/components/i18n/locales/az/messages.json +45 -0
- package/src/components/i18n/locales/bg/messages.json +45 -0
- package/src/components/i18n/locales/bn/messages.json +45 -0
- package/src/components/i18n/locales/bs/messages.json +45 -0
- package/src/components/i18n/locales/cs/messages.json +45 -0
- package/src/components/i18n/locales/da/messages.json +45 -0
- package/src/components/i18n/locales/de/messages.json +45 -0
- package/src/components/i18n/locales/dv/messages.json +45 -0
- package/src/components/i18n/locales/el/messages.json +45 -0
- package/src/components/i18n/locales/en/messages.json +45 -0
- package/src/components/i18n/locales/es/messages.json +45 -0
- package/src/components/i18n/locales/et/messages.json +45 -0
- package/src/components/i18n/locales/fa/messages.json +45 -0
- package/src/components/i18n/locales/fi/messages.json +45 -0
- package/src/components/i18n/locales/fil/messages.json +45 -0
- package/src/components/i18n/locales/fr/messages.json +45 -0
- package/src/components/i18n/locales/gu/messages.json +45 -0
- package/src/components/i18n/locales/he/messages.json +45 -0
- package/src/components/i18n/locales/hi/messages.json +45 -0
- package/src/components/i18n/locales/hr/messages.json +45 -0
- package/src/components/i18n/locales/hu/messages.json +45 -0
- package/src/components/i18n/locales/hy/messages.json +45 -0
- package/src/components/i18n/locales/id/messages.json +45 -0
- package/src/components/i18n/locales/index.ts +231 -0
- package/src/components/i18n/locales/it/messages.json +45 -0
- package/src/components/i18n/locales/ja/messages.json +45 -0
- package/src/components/i18n/locales/ka/messages.json +45 -0
- package/src/components/i18n/locales/km/messages.json +45 -0
- package/src/components/i18n/locales/kn/messages.json +45 -0
- package/src/components/i18n/locales/ko/messages.json +45 -0
- package/src/components/i18n/locales/ku/messages.json +45 -0
- package/src/components/i18n/locales/lo/messages.json +45 -0
- package/src/components/i18n/locales/lt/messages.json +45 -0
- package/src/components/i18n/locales/lv/messages.json +45 -0
- package/src/components/i18n/locales/mk/messages.json +45 -0
- package/src/components/i18n/locales/ml/messages.json +45 -0
- package/src/components/i18n/locales/mn/messages.json +45 -0
- package/src/components/i18n/locales/mr/messages.json +45 -0
- package/src/components/i18n/locales/ms/messages.json +45 -0
- package/src/components/i18n/locales/my/messages.json +45 -0
- package/src/components/i18n/locales/ne/messages.json +45 -0
- package/src/components/i18n/locales/nl/messages.json +45 -0
- package/src/components/i18n/locales/no/messages.json +45 -0
- package/src/components/i18n/locales/pa/messages.json +45 -0
- package/src/components/i18n/locales/pl/messages.json +45 -0
- package/src/components/i18n/locales/ps/messages.json +45 -0
- package/src/components/i18n/locales/pt/messages.json +45 -0
- package/src/components/i18n/locales/ro/messages.json +45 -0
- package/src/components/i18n/locales/ru/messages.json +45 -0
- package/src/components/i18n/locales/sd/messages.json +45 -0
- package/src/components/i18n/locales/si/messages.json +45 -0
- package/src/components/i18n/locales/sk/messages.json +45 -0
- package/src/components/i18n/locales/sl/messages.json +45 -0
- package/src/components/i18n/locales/sq/messages.json +45 -0
- package/src/components/i18n/locales/sr/messages.json +45 -0
- package/src/components/i18n/locales/sv/messages.json +45 -0
- package/src/components/i18n/locales/sw/messages.json +45 -0
- package/src/components/i18n/locales/ta/messages.json +45 -0
- package/src/components/i18n/locales/te/messages.json +45 -0
- package/src/components/i18n/locales/th/messages.json +45 -0
- package/src/components/i18n/locales/tr/messages.json +45 -0
- package/src/components/i18n/locales/ug/messages.json +45 -0
- package/src/components/i18n/locales/uk/messages.json +45 -0
- package/src/components/i18n/locales/ur/messages.json +45 -0
- package/src/components/i18n/locales/vi/messages.json +45 -0
- package/src/components/i18n/locales/yi/messages.json +45 -0
- package/src/components/i18n/locales/zh/messages.json +45 -0
- package/src/components/icons/index.ts +242 -0
- package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
- package/src/components/inline-tools/inline-tool-convert.ts +141 -0
- package/src/components/inline-tools/inline-tool-italic.ts +500 -0
- package/src/components/inline-tools/inline-tool-link.ts +539 -0
- package/src/components/modules/api/blocks.ts +377 -0
- package/src/components/modules/api/caret.ts +125 -0
- package/src/components/modules/api/events.ts +51 -0
- package/src/components/modules/api/history.ts +73 -0
- package/src/components/modules/api/i18n.ts +35 -0
- package/src/components/modules/api/index.ts +39 -0
- package/src/components/modules/api/inlineToolbar.ts +33 -0
- package/src/components/modules/api/listeners.ts +56 -0
- package/src/components/modules/api/notifier.ts +46 -0
- package/src/components/modules/api/readonly.ts +39 -0
- package/src/components/modules/api/sanitizer.ts +30 -0
- package/src/components/modules/api/saver.ts +52 -0
- package/src/components/modules/api/selection.ts +48 -0
- package/src/components/modules/api/styles.ts +72 -0
- package/src/components/modules/api/toolbar.ts +79 -0
- package/src/components/modules/api/tools.ts +16 -0
- package/src/components/modules/api/tooltip.ts +67 -0
- package/src/components/modules/api/ui.ts +36 -0
- package/src/components/modules/blockEvents.ts +1591 -0
- package/src/components/modules/blockManager.ts +1356 -0
- package/src/components/modules/blockSelection.ts +708 -0
- package/src/components/modules/caret.ts +853 -0
- package/src/components/modules/crossBlockSelection.ts +329 -0
- package/src/components/modules/dragManager.ts +1204 -0
- package/src/components/modules/history.ts +1098 -0
- package/src/components/modules/i18n.ts +332 -0
- package/src/components/modules/index.ts +139 -0
- package/src/components/modules/modificationsObserver.ts +147 -0
- package/src/components/modules/paste.ts +1092 -0
- package/src/components/modules/readonly.ts +136 -0
- package/src/components/modules/rectangleSelection.ts +711 -0
- package/src/components/modules/renderer.ts +155 -0
- package/src/components/modules/saver.ts +283 -0
- package/src/components/modules/toolbar/blockSettings.ts +781 -0
- package/src/components/modules/toolbar/index.ts +1315 -0
- package/src/components/modules/toolbar/inline.ts +956 -0
- package/src/components/modules/tools.ts +625 -0
- package/src/components/modules/ui.ts +1283 -0
- package/src/components/polyfills.ts +113 -0
- package/src/components/selection.ts +1179 -0
- package/src/components/tools/base.ts +301 -0
- package/src/components/tools/block.ts +339 -0
- package/src/components/tools/collection.ts +67 -0
- package/src/components/tools/factory.ts +138 -0
- package/src/components/tools/inline.ts +71 -0
- package/src/components/tools/tune.ts +33 -0
- package/src/components/ui/toolbox.ts +601 -0
- package/src/components/utils/announcer.ts +205 -0
- package/src/components/utils/api.ts +20 -0
- package/src/components/utils/bem.ts +26 -0
- package/src/components/utils/blocks.ts +284 -0
- package/src/components/utils/caret.ts +1067 -0
- package/src/components/utils/data-model-transform.ts +382 -0
- package/src/components/utils/events.ts +117 -0
- package/src/components/utils/keyboard.ts +60 -0
- package/src/components/utils/listeners.ts +296 -0
- package/src/components/utils/mutations.ts +39 -0
- package/src/components/utils/notifier/draw.ts +190 -0
- package/src/components/utils/notifier/index.ts +66 -0
- package/src/components/utils/notifier/types.ts +1 -0
- package/src/components/utils/notifier.ts +77 -0
- package/src/components/utils/placeholder.ts +140 -0
- package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
- package/src/components/utils/popover/components/hint/hint.ts +46 -0
- package/src/components/utils/popover/components/hint/index.ts +6 -0
- package/src/components/utils/popover/components/popover-header/index.ts +2 -0
- package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
- package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
- package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
- package/src/components/utils/popover/components/popover-item/index.ts +13 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +680 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
- package/src/components/utils/popover/components/popover-item/popover-item.ts +186 -0
- package/src/components/utils/popover/components/search-input/index.ts +2 -0
- package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
- package/src/components/utils/popover/components/search-input/search-input.ts +178 -0
- package/src/components/utils/popover/components/search-input/search-input.types.ts +59 -0
- package/src/components/utils/popover/index.ts +13 -0
- package/src/components/utils/popover/popover-abstract.ts +457 -0
- package/src/components/utils/popover/popover-desktop.ts +676 -0
- package/src/components/utils/popover/popover-inline.ts +338 -0
- package/src/components/utils/popover/popover-mobile.ts +201 -0
- package/src/components/utils/popover/popover.const.ts +81 -0
- package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
- package/src/components/utils/promise-queue.ts +43 -0
- package/src/components/utils/sanitizer.ts +537 -0
- package/src/components/utils/scroll-locker.ts +87 -0
- package/src/components/utils/shortcut.ts +231 -0
- package/src/components/utils/shortcuts.ts +113 -0
- package/src/components/utils/tools.ts +110 -0
- package/src/components/utils/tooltip.ts +591 -0
- package/src/components/utils/tw.ts +241 -0
- package/src/components/utils.ts +1081 -0
- package/src/env.d.ts +13 -0
- package/src/full.ts +69 -0
- package/src/locales.ts +51 -0
- package/src/stories/Block.stories.ts +498 -0
- package/src/stories/EditorModes.stories.ts +505 -0
- package/src/stories/Header.stories.ts +137 -0
- package/src/stories/InlineToolbar.stories.ts +498 -0
- package/src/stories/List.stories.ts +259 -0
- package/src/stories/Notifier.stories.ts +340 -0
- package/src/stories/Paragraph.stories.ts +112 -0
- package/src/stories/Placeholder.stories.ts +319 -0
- package/src/stories/Popover.stories.ts +844 -0
- package/src/stories/Selection.stories.ts +250 -0
- package/src/stories/StubBlock.stories.ts +156 -0
- package/src/stories/Toolbar.stories.ts +223 -0
- package/src/stories/Toolbox.stories.ts +166 -0
- package/src/stories/Tooltip.stories.ts +198 -0
- package/src/stories/helpers.ts +463 -0
- package/src/styles/main.css +123 -0
- package/src/tools/header/index.ts +646 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/list/index.ts +1819 -0
- package/src/tools/paragraph/index.ts +412 -0
- package/src/tools/stub/index.ts +107 -0
- package/src/types-internal/blok-modules.d.ts +87 -0
- package/src/types-internal/html-janitor.d.ts +28 -0
- package/src/types-internal/module-config.d.ts +11 -0
- package/src/variants/all-locales.ts +155 -0
- package/src/variants/blok-maximum.ts +20 -0
- package/src/variants/blok-minimum.ts +243 -0
- package/types/api/blocks.d.ts +9 -1
- package/types/api/history.d.ts +7 -0
- package/types/api/i18n.d.ts +22 -3
- package/types/api/selection.d.ts +6 -0
- package/types/api/styles.d.ts +23 -10
- package/types/configs/blok-config.d.ts +29 -0
- package/types/configs/i18n-config.d.ts +52 -2
- package/types/configs/i18n-dictionary.d.ts +16 -90
- package/types/data-attributes.d.ts +170 -0
- package/types/data-formats/output-data.d.ts +15 -0
- package/types/full.d.ts +80 -0
- package/types/index.d.ts +30 -13
- package/types/locales.d.ts +59 -0
- package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
- package/types/tools/block-tool.d.ts +9 -0
- package/types/tools/header.d.ts +18 -0
- package/types/tools/index.d.ts +1 -0
- package/types/tools/list.d.ts +91 -0
- package/types/tools/paragraph.d.ts +71 -0
- package/types/tools/tool-settings.d.ts +92 -6
- package/types/tools/tool.d.ts +6 -0
- package/types/tools-entry.d.ts +49 -0
- package/types/utils/popover/popover-item.d.ts +18 -5
- package/types/utils/popover/popover.d.ts +7 -0
- package/dist/blok-D_baBvTG.mjs +0 -25795
- package/dist/blok.umd.js +0 -181
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
import { array, isNumber, isString } from './utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DOM manipulations helper
|
|
5
|
+
* @todo get rid of class and make separate utility functions
|
|
6
|
+
*/
|
|
7
|
+
export class Dom {
|
|
8
|
+
/**
|
|
9
|
+
* Check if passed tag has no closed tag
|
|
10
|
+
* @param {HTMLElement} tag - element to check
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
public static isSingleTag(tag: HTMLElement): boolean {
|
|
14
|
+
return Boolean(tag.tagName) && [
|
|
15
|
+
'AREA',
|
|
16
|
+
'BASE',
|
|
17
|
+
'BR',
|
|
18
|
+
'COL',
|
|
19
|
+
'COMMAND',
|
|
20
|
+
'EMBED',
|
|
21
|
+
'HR',
|
|
22
|
+
'IMG',
|
|
23
|
+
'INPUT',
|
|
24
|
+
'KEYGEN',
|
|
25
|
+
'LINK',
|
|
26
|
+
'META',
|
|
27
|
+
'PARAM',
|
|
28
|
+
'SOURCE',
|
|
29
|
+
'TRACK',
|
|
30
|
+
'WBR',
|
|
31
|
+
].includes(tag.tagName);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if element is BR or WBR
|
|
36
|
+
* @param {HTMLElement} element - element to check
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
public static isLineBreakTag(element: HTMLElement): element is HTMLBRElement {
|
|
40
|
+
return !!element && ['BR', 'WBR'].includes(element.tagName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Checks if a class name is valid for use with classList.add()
|
|
45
|
+
* classList.add() throws if class contains whitespace, is empty, or contains invalid characters
|
|
46
|
+
* @param className - class name to validate
|
|
47
|
+
* @returns {boolean} - true if valid for classList.add()
|
|
48
|
+
*/
|
|
49
|
+
private static isValidClassName(className: string): boolean {
|
|
50
|
+
if (className === '' || /\s/.test(className)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Try to validate by creating a temporary element and using classList.add
|
|
56
|
+
* This is more reliable than regex because it follows the actual browser implementation
|
|
57
|
+
*/
|
|
58
|
+
try {
|
|
59
|
+
const testEl = document.createElement('div');
|
|
60
|
+
|
|
61
|
+
testEl.classList.add(className);
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Safely adds class names to an element, filtering out invalid ones
|
|
71
|
+
* @param element - element to add classes to
|
|
72
|
+
* @param classNames - array of class names to add
|
|
73
|
+
*/
|
|
74
|
+
private static safelyAddClasses(element: HTMLElement, classNames: string[]): void {
|
|
75
|
+
const validClasses: string[] = [];
|
|
76
|
+
const invalidClasses: string[] = [];
|
|
77
|
+
|
|
78
|
+
for (const className of classNames) {
|
|
79
|
+
if (Dom.isValidClassName(className)) {
|
|
80
|
+
validClasses.push(className);
|
|
81
|
+
} else {
|
|
82
|
+
invalidClasses.push(className);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (validClasses.length > 0) {
|
|
87
|
+
element.classList.add(...validClasses);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* For invalid class names (e.g. Tailwind arbitrary values with brackets/parentheses),
|
|
92
|
+
* we need to set them via className attribute directly
|
|
93
|
+
*/
|
|
94
|
+
if (invalidClasses.length > 0) {
|
|
95
|
+
const existingClasses = element.className;
|
|
96
|
+
const allClasses = existingClasses
|
|
97
|
+
? `${existingClasses} ${invalidClasses.join(' ')}`
|
|
98
|
+
: invalidClasses.join(' ');
|
|
99
|
+
|
|
100
|
+
element.setAttribute('class', allClasses);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Helper for making Elements with class name and attributes
|
|
106
|
+
* @param {string} tagName - new Element tag name
|
|
107
|
+
* @param {string[]|string} [classNames] - list or name of CSS class name(s)
|
|
108
|
+
* @param {object} [attributes] - any attributes
|
|
109
|
+
* @returns {HTMLElement}
|
|
110
|
+
*/
|
|
111
|
+
public static make(tagName: string, classNames: string | (string | undefined)[] | null = null, attributes: Record<string, string | number | boolean | null | undefined> = {}): HTMLElement {
|
|
112
|
+
const el = document.createElement(tagName);
|
|
113
|
+
|
|
114
|
+
if (Array.isArray(classNames)) {
|
|
115
|
+
const validClassnames = classNames
|
|
116
|
+
.filter((className): className is string => className !== undefined && className !== '')
|
|
117
|
+
.flatMap((className) => className.split(' '))
|
|
118
|
+
.filter((className) => className !== '');
|
|
119
|
+
|
|
120
|
+
Dom.safelyAddClasses(el, validClassnames);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof classNames === 'string' && classNames !== '') {
|
|
124
|
+
const splitClassNames = classNames.split(' ').filter((className) => className !== '');
|
|
125
|
+
|
|
126
|
+
Dom.safelyAddClasses(el, splitClassNames);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const attrName in attributes) {
|
|
130
|
+
if (!Object.prototype.hasOwnProperty.call(attributes, attrName)) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const value = attributes[attrName];
|
|
135
|
+
|
|
136
|
+
if (value === undefined || value === null) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (attrName in el) {
|
|
141
|
+
(el as unknown as Record<string, unknown>)[attrName] = value;
|
|
142
|
+
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
el.setAttribute(attrName, String(value));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return el;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Creates Text Node with the passed content
|
|
154
|
+
* @param {string} content - text content
|
|
155
|
+
* @returns {Text}
|
|
156
|
+
*/
|
|
157
|
+
public static text(content: string): Text {
|
|
158
|
+
return document.createTextNode(content);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Append one or several elements to the parent
|
|
163
|
+
* @param {Element|DocumentFragment} parent - where to append
|
|
164
|
+
* @param {Element|Element[]|DocumentFragment|Text|Text[]} elements - element or elements list
|
|
165
|
+
*/
|
|
166
|
+
public static append(
|
|
167
|
+
parent: Element | DocumentFragment,
|
|
168
|
+
elements: Element | Element[] | DocumentFragment | Text | Text[]
|
|
169
|
+
): void {
|
|
170
|
+
if (Array.isArray(elements)) {
|
|
171
|
+
elements.forEach((el) => parent.appendChild(el));
|
|
172
|
+
} else {
|
|
173
|
+
parent.appendChild(elements);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Append element or a couple to the beginning of the parent elements
|
|
179
|
+
* @param {Element} parent - where to append
|
|
180
|
+
* @param {Element|Element[]} elements - element or elements list
|
|
181
|
+
*/
|
|
182
|
+
public static prepend(parent: Element, elements: Element | Element[]): void {
|
|
183
|
+
if (Array.isArray(elements)) {
|
|
184
|
+
const reversedElements = [ ...elements ].reverse();
|
|
185
|
+
|
|
186
|
+
reversedElements.forEach((el) => parent.prepend(el));
|
|
187
|
+
} else {
|
|
188
|
+
parent.prepend(elements);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Selector Decorator
|
|
194
|
+
*
|
|
195
|
+
* Returns first match
|
|
196
|
+
* @param {Element} el - element we searching inside. Default - DOM Document
|
|
197
|
+
* @param {string} selector - searching string
|
|
198
|
+
* @returns {Element}
|
|
199
|
+
*/
|
|
200
|
+
public static find(el: Element | Document = document, selector: string): Element | null {
|
|
201
|
+
return el.querySelector(selector);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get Element by Id
|
|
206
|
+
* @param {string} id - id to find
|
|
207
|
+
* @returns {HTMLElement | null}
|
|
208
|
+
*/
|
|
209
|
+
public static get(id: string): HTMLElement | null {
|
|
210
|
+
return document.getElementById(id);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Selector Decorator.
|
|
215
|
+
*
|
|
216
|
+
* Returns all matches
|
|
217
|
+
* @param {Element|Document} el - element we searching inside. Default - DOM Document
|
|
218
|
+
* @param {string} selector - searching string
|
|
219
|
+
* @returns {NodeList}
|
|
220
|
+
*/
|
|
221
|
+
public static findAll(el: Element | Document = document, selector: string): NodeList {
|
|
222
|
+
return el.querySelectorAll(selector);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Returns CSS selector for all text inputs
|
|
227
|
+
*/
|
|
228
|
+
public static get allInputsSelector(): string {
|
|
229
|
+
const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url'];
|
|
230
|
+
|
|
231
|
+
return '[contenteditable=true], textarea, input:not([type]), ' +
|
|
232
|
+
allowedInputTypes.map((type) => `input[type="${type}"]`).join(', ');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Find all contenteditable, textarea and editable input elements passed holder contains
|
|
237
|
+
* @param holder - element where to find inputs
|
|
238
|
+
*/
|
|
239
|
+
public static findAllInputs(holder: Element): HTMLElement[] {
|
|
240
|
+
return array(holder.querySelectorAll(Dom.allInputsSelector))
|
|
241
|
+
/**
|
|
242
|
+
* If contenteditable element contains block elements, treat them as inputs.
|
|
243
|
+
*/
|
|
244
|
+
.reduce((result, input) => {
|
|
245
|
+
if (Dom.isNativeInput(input) || Dom.containsOnlyInlineElements(input)) {
|
|
246
|
+
return [...result, input];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return [...result, ...Dom.getDeepestBlockElements(input)];
|
|
250
|
+
}, []);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Search for deepest node which is Leaf.
|
|
255
|
+
* Leaf is the vertex that doesn't have any child nodes
|
|
256
|
+
* @description Method recursively goes throw the all Node until it finds the Leaf
|
|
257
|
+
* @param {Node} node - root Node. From this vertex we start Deep-first search
|
|
258
|
+
* {@link https://en.wikipedia.org/wiki/Depth-first_search}
|
|
259
|
+
* @param {boolean} [atLast] - find last text node
|
|
260
|
+
* @returns - it can be text Node or Element Node, so that caret will able to work with it
|
|
261
|
+
* Can return null if node is Document or DocumentFragment, or node is not attached to the DOM
|
|
262
|
+
*/
|
|
263
|
+
public static getDeepestNode(node: Node | null, atLast = false): Node | null {
|
|
264
|
+
/**
|
|
265
|
+
* Current function have two directions:
|
|
266
|
+
* - starts from first child and every time gets first or nextSibling in special cases
|
|
267
|
+
* - starts from last child and gets last or previousSibling
|
|
268
|
+
* @type {string}
|
|
269
|
+
*/
|
|
270
|
+
const child: 'lastChild' | 'firstChild' = atLast ? 'lastChild' : 'firstChild';
|
|
271
|
+
const sibling: 'previousSibling' | 'nextSibling' = atLast ? 'previousSibling' : 'nextSibling';
|
|
272
|
+
|
|
273
|
+
if (node === null || node.nodeType !== Node.ELEMENT_NODE) {
|
|
274
|
+
return node;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const nodeChildProperty = node[child];
|
|
278
|
+
|
|
279
|
+
if (nodeChildProperty === null) {
|
|
280
|
+
return node;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const nodeChild = nodeChildProperty as Node;
|
|
284
|
+
const shouldSkipChild = Dom.isSingleTag(nodeChild as HTMLElement) &&
|
|
285
|
+
!Dom.isNativeInput(nodeChild) &&
|
|
286
|
+
!Dom.isLineBreakTag(nodeChild as HTMLElement);
|
|
287
|
+
|
|
288
|
+
if (!shouldSkipChild) {
|
|
289
|
+
return this.getDeepestNode(nodeChild, atLast);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const siblingNode = nodeChild[sibling];
|
|
293
|
+
|
|
294
|
+
if (siblingNode) {
|
|
295
|
+
return this.getDeepestNode(siblingNode, atLast);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const parentSiblingNode = nodeChild.parentNode?.[sibling];
|
|
299
|
+
|
|
300
|
+
if (parentSiblingNode) {
|
|
301
|
+
return this.getDeepestNode(parentSiblingNode, atLast);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return nodeChild.parentNode;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Check if object is DOM node
|
|
309
|
+
* @param {*} node - object to check
|
|
310
|
+
* @returns {boolean}
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
public static isElement(node: any): node is Element {
|
|
314
|
+
if (isNumber(node)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return node != null && node.nodeType != null && node.nodeType === Node.ELEMENT_NODE;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Check if object is DocumentFragment node
|
|
323
|
+
* @param {object} node - object to check
|
|
324
|
+
* @returns {boolean}
|
|
325
|
+
*/
|
|
326
|
+
|
|
327
|
+
public static isFragment(node: any): node is DocumentFragment {
|
|
328
|
+
if (isNumber(node)) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return node != null && node.nodeType != null && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Check if passed element is contenteditable
|
|
337
|
+
* @param {HTMLElement} element - html element to check
|
|
338
|
+
* @returns {boolean}
|
|
339
|
+
*/
|
|
340
|
+
public static isContentEditable(element: HTMLElement): boolean {
|
|
341
|
+
return element.contentEditable === 'true';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Checks target if it is native input
|
|
346
|
+
* @param {*} target - HTML element or string
|
|
347
|
+
* @returns {boolean}
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
public static isNativeInput(target: any): target is HTMLInputElement | HTMLTextAreaElement {
|
|
351
|
+
const nativeInputs = [
|
|
352
|
+
'INPUT',
|
|
353
|
+
'TEXTAREA',
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
return target != null && typeof target.tagName === 'string' ? nativeInputs.includes(target.tagName) : false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Checks if we can set caret
|
|
361
|
+
* @param {HTMLElement} target - target to check
|
|
362
|
+
* @returns {boolean}
|
|
363
|
+
*/
|
|
364
|
+
public static canSetCaret(target: HTMLElement): boolean {
|
|
365
|
+
if (Dom.isNativeInput(target)) {
|
|
366
|
+
const disallowedTypes = new Set([
|
|
367
|
+
'file',
|
|
368
|
+
'checkbox',
|
|
369
|
+
'radio',
|
|
370
|
+
'hidden',
|
|
371
|
+
'submit',
|
|
372
|
+
'button',
|
|
373
|
+
'image',
|
|
374
|
+
'reset',
|
|
375
|
+
]);
|
|
376
|
+
|
|
377
|
+
return !disallowedTypes.has(target.type);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return Dom.isContentEditable(target);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Checks node if it is empty
|
|
385
|
+
* @description Method checks simple Node without any childs for emptiness
|
|
386
|
+
* If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method
|
|
387
|
+
* @param {Node} node - node to check
|
|
388
|
+
* @param {string} [ignoreChars] - char or substring to treat as empty
|
|
389
|
+
* @returns {boolean} true if it is empty
|
|
390
|
+
*/
|
|
391
|
+
public static isNodeEmpty(node: Node, ignoreChars?: string): boolean {
|
|
392
|
+
if (this.isSingleTag(node as HTMLElement) && !this.isLineBreakTag(node as HTMLElement)) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const baseText = this.isElement(node) && this.isNativeInput(node)
|
|
397
|
+
? (node as HTMLInputElement).value
|
|
398
|
+
: node.textContent?.replace('\u200B', '');
|
|
399
|
+
const normalizedText = ignoreChars
|
|
400
|
+
? baseText?.replace(new RegExp(ignoreChars, 'g'), '')
|
|
401
|
+
: baseText;
|
|
402
|
+
|
|
403
|
+
return (normalizedText?.length ?? 0) === 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* checks node if it is doesn't have any child nodes
|
|
408
|
+
* @param {Node} node - node to check
|
|
409
|
+
* @returns {boolean}
|
|
410
|
+
*/
|
|
411
|
+
public static isLeaf(node: Node): boolean {
|
|
412
|
+
if (!node) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return node.childNodes.length === 0;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* breadth-first search (BFS)
|
|
421
|
+
* {@link https://en.wikipedia.org/wiki/Breadth-first_search}
|
|
422
|
+
* @description Pushes to stack all DOM leafs and checks for emptiness
|
|
423
|
+
* @param {Node} node - node to check
|
|
424
|
+
* @param {string} [ignoreChars] - char or substring to treat as empty
|
|
425
|
+
* @returns {boolean}
|
|
426
|
+
*/
|
|
427
|
+
public static isEmpty(node: Node, ignoreChars?: string): boolean {
|
|
428
|
+
const treeWalker = [ node ];
|
|
429
|
+
|
|
430
|
+
while (treeWalker.length > 0) {
|
|
431
|
+
const currentNode = treeWalker.shift();
|
|
432
|
+
|
|
433
|
+
if (!currentNode) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (this.isLeaf(currentNode) && !this.isNodeEmpty(currentNode, ignoreChars)) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (currentNode.childNodes) {
|
|
442
|
+
treeWalker.push(...Array.from(currentNode.childNodes));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Check if string contains html elements
|
|
451
|
+
* @param {string} str - string to check
|
|
452
|
+
* @returns {boolean}
|
|
453
|
+
*/
|
|
454
|
+
public static isHTMLString(str: string): boolean {
|
|
455
|
+
const wrapper = Dom.make('div');
|
|
456
|
+
|
|
457
|
+
wrapper.innerHTML = str;
|
|
458
|
+
|
|
459
|
+
return wrapper.childElementCount > 0;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Return length of node`s text content
|
|
464
|
+
* @param {Node} node - node with content
|
|
465
|
+
* @returns {number}
|
|
466
|
+
*/
|
|
467
|
+
public static getContentLength(node: Node): number {
|
|
468
|
+
if (Dom.isNativeInput(node)) {
|
|
469
|
+
return (node as HTMLInputElement).value.length;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
473
|
+
return (node as Text).length;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return node.textContent?.length ?? 0;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Return array of names of block html elements
|
|
481
|
+
* @returns {string[]}
|
|
482
|
+
*/
|
|
483
|
+
public static get blockElements(): string[] {
|
|
484
|
+
return [
|
|
485
|
+
'address',
|
|
486
|
+
'article',
|
|
487
|
+
'aside',
|
|
488
|
+
'blockquote',
|
|
489
|
+
'canvas',
|
|
490
|
+
'div',
|
|
491
|
+
'dl',
|
|
492
|
+
'dt',
|
|
493
|
+
'fieldset',
|
|
494
|
+
'figcaption',
|
|
495
|
+
'figure',
|
|
496
|
+
'footer',
|
|
497
|
+
'form',
|
|
498
|
+
'h1',
|
|
499
|
+
'h2',
|
|
500
|
+
'h3',
|
|
501
|
+
'h4',
|
|
502
|
+
'h5',
|
|
503
|
+
'h6',
|
|
504
|
+
'header',
|
|
505
|
+
'hgroup',
|
|
506
|
+
'hr',
|
|
507
|
+
'li',
|
|
508
|
+
'main',
|
|
509
|
+
'nav',
|
|
510
|
+
'noscript',
|
|
511
|
+
'ol',
|
|
512
|
+
'output',
|
|
513
|
+
'p',
|
|
514
|
+
'pre',
|
|
515
|
+
'ruby',
|
|
516
|
+
'section',
|
|
517
|
+
'table',
|
|
518
|
+
'tbody',
|
|
519
|
+
'thead',
|
|
520
|
+
'tr',
|
|
521
|
+
'tfoot',
|
|
522
|
+
'ul',
|
|
523
|
+
'video',
|
|
524
|
+
];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Check if passed content includes only inline elements
|
|
529
|
+
* @param {string|HTMLElement} data - element or html string
|
|
530
|
+
* @returns {boolean}
|
|
531
|
+
*/
|
|
532
|
+
public static containsOnlyInlineElements(data: string | HTMLElement): boolean {
|
|
533
|
+
const wrapper = isString(data)
|
|
534
|
+
? (() => {
|
|
535
|
+
const container = document.createElement('div');
|
|
536
|
+
|
|
537
|
+
container.innerHTML = data;
|
|
538
|
+
|
|
539
|
+
return container;
|
|
540
|
+
})()
|
|
541
|
+
: data;
|
|
542
|
+
|
|
543
|
+
const check = (element: Element): boolean => {
|
|
544
|
+
return !Dom.blockElements.includes(element.tagName.toLowerCase()) &&
|
|
545
|
+
Array.from(element.children).every(check);
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
return Array.from(wrapper.children).every(check);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Find and return all block elements in the passed parent (including subtree)
|
|
553
|
+
* @param {HTMLElement} parent - root element
|
|
554
|
+
* @returns {HTMLElement[]}
|
|
555
|
+
*/
|
|
556
|
+
public static getDeepestBlockElements(parent: HTMLElement): HTMLElement[] {
|
|
557
|
+
if (Dom.containsOnlyInlineElements(parent)) {
|
|
558
|
+
return [ parent ];
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return Array.from(parent.children).reduce((result, element) => {
|
|
562
|
+
return [...result, ...Dom.getDeepestBlockElements(element as HTMLElement)];
|
|
563
|
+
}, [] as HTMLElement[]);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Helper for get holder from {string} or return HTMLElement
|
|
568
|
+
* @param {string | HTMLElement} element - holder's id or holder's HTML Element
|
|
569
|
+
* @returns {HTMLElement}
|
|
570
|
+
*/
|
|
571
|
+
public static getHolder(element: string | HTMLElement): HTMLElement {
|
|
572
|
+
if (!isString(element)) {
|
|
573
|
+
return element;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const holder = document.getElementById(element);
|
|
577
|
+
|
|
578
|
+
if (holder !== null) {
|
|
579
|
+
return holder;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
throw new Error(`Element with id "${element}" not found`);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Returns true if element is anchor (is A tag)
|
|
587
|
+
* @param {Element} element - element to check
|
|
588
|
+
* @returns {boolean}
|
|
589
|
+
*/
|
|
590
|
+
public static isAnchor(element: Element): element is HTMLAnchorElement {
|
|
591
|
+
return element.tagName.toLowerCase() === 'a';
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Return element's offset related to the document
|
|
596
|
+
* @todo handle case when blok initialized in scrollable popup
|
|
597
|
+
* @param el - element to compute offset
|
|
598
|
+
*/
|
|
599
|
+
public static offset(el: Element): { top: number; left: number; right: number; bottom: number } {
|
|
600
|
+
const rect = el.getBoundingClientRect();
|
|
601
|
+
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
|
|
602
|
+
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
603
|
+
|
|
604
|
+
const top = rect.top + scrollTop;
|
|
605
|
+
const left = rect.left + scrollLeft;
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
top,
|
|
609
|
+
left,
|
|
610
|
+
bottom: top + rect.height,
|
|
611
|
+
right: left + rect.width,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Find text node and offset by total content offset
|
|
617
|
+
* @param {Node} root - root node to start search from
|
|
618
|
+
* @param {number} totalOffset - offset relative to the root node content
|
|
619
|
+
* @returns {{node: Node | null, offset: number}} - node and offset inside node
|
|
620
|
+
*/
|
|
621
|
+
public static getNodeByOffset(root: Node, totalOffset: number): { node: Node | null; offset: number } {
|
|
622
|
+
const walker = document.createTreeWalker(
|
|
623
|
+
root,
|
|
624
|
+
NodeFilter.SHOW_TEXT,
|
|
625
|
+
null
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
const findNode = (
|
|
629
|
+
nextNode: Node | null,
|
|
630
|
+
accumulatedOffset: number,
|
|
631
|
+
previousNode: Node | null,
|
|
632
|
+
previousNodeLength: number
|
|
633
|
+
): { node: Node | null; offset: number } => {
|
|
634
|
+
if (!nextNode && previousNode) {
|
|
635
|
+
const baseOffset = accumulatedOffset - previousNodeLength;
|
|
636
|
+
const safeTotalOffset = Math.max(totalOffset - baseOffset, 0);
|
|
637
|
+
const offsetInsidePrevious = Math.min(safeTotalOffset, previousNodeLength);
|
|
638
|
+
|
|
639
|
+
return {
|
|
640
|
+
node: previousNode,
|
|
641
|
+
offset: offsetInsidePrevious,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (!nextNode) {
|
|
646
|
+
return {
|
|
647
|
+
node: null,
|
|
648
|
+
offset: 0,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const textContent = nextNode.textContent ?? '';
|
|
653
|
+
const nodeLength = textContent.length;
|
|
654
|
+
const hasReachedOffset = accumulatedOffset + nodeLength >= totalOffset;
|
|
655
|
+
|
|
656
|
+
if (hasReachedOffset) {
|
|
657
|
+
return {
|
|
658
|
+
node: nextNode,
|
|
659
|
+
offset: Math.min(totalOffset - accumulatedOffset, nodeLength),
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return findNode(
|
|
664
|
+
walker.nextNode(),
|
|
665
|
+
accumulatedOffset + nodeLength,
|
|
666
|
+
nextNode,
|
|
667
|
+
nodeLength
|
|
668
|
+
);
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const initialNode = walker.nextNode();
|
|
672
|
+
const { node, offset } = findNode(initialNode, 0, null, 0);
|
|
673
|
+
|
|
674
|
+
if (!node) {
|
|
675
|
+
return {
|
|
676
|
+
node: null,
|
|
677
|
+
offset: 0,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const textContent = node.textContent;
|
|
682
|
+
|
|
683
|
+
if (!textContent || textContent.length === 0) {
|
|
684
|
+
return {
|
|
685
|
+
node: null,
|
|
686
|
+
offset: 0,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return {
|
|
691
|
+
node,
|
|
692
|
+
offset,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Determine whether a passed text content is a collapsed whitespace.
|
|
699
|
+
*
|
|
700
|
+
* In HTML, whitespaces at the start and end of elements and outside elements are ignored.
|
|
701
|
+
* There are two types of whitespaces in HTML:
|
|
702
|
+
* - Visible ( )
|
|
703
|
+
* - Invisible (regular trailing spaces, tabs, etc)
|
|
704
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
|
|
705
|
+
* @see https://www.w3.org/TR/css-text-3/#white-space-processing
|
|
706
|
+
* @param textContent — any string, for ex a textContent of a node
|
|
707
|
+
* @returns True if passed text content is whitespace which is collapsed (invisible) in browser
|
|
708
|
+
*/
|
|
709
|
+
export const isCollapsedWhitespaces = (textContent: string): boolean => {
|
|
710
|
+
/**
|
|
711
|
+
* Throughout, whitespace is defined as one of the characters
|
|
712
|
+
* "\t" TAB \u0009
|
|
713
|
+
* "\n" LF \u000A
|
|
714
|
+
* "\r" CR \u000D
|
|
715
|
+
* " " SPC \u0020
|
|
716
|
+
*
|
|
717
|
+
* Also \u200B (Zero Width Space) is considered as collapsed whitespace
|
|
718
|
+
*/
|
|
719
|
+
return !/[^\t\n\r \u200B]/.test(textContent);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Calculates the Y coordinate of the text baseline from the top of the element's margin box,
|
|
724
|
+
*
|
|
725
|
+
* The calculation formula is as follows:
|
|
726
|
+
*
|
|
727
|
+
* 1. Calculate the baseline offset:
|
|
728
|
+
* - Typically, the baseline is about 80% of the `fontSize` from the top of the text, as this is a common average for many fonts.
|
|
729
|
+
*
|
|
730
|
+
* 2. Calculate the additional space due to `lineHeight`:
|
|
731
|
+
* - If the `lineHeight` is greater than the `fontSize`, the extra space is evenly distributed above and below the text. This extra space is `(lineHeight - fontSize) / 2`.
|
|
732
|
+
*
|
|
733
|
+
* 3. Calculate the total baseline Y coordinate:
|
|
734
|
+
* - Sum of `marginTop`, `borderTopWidth`, `paddingTop`, the extra space due to `lineHeight`, and the baseline offset.
|
|
735
|
+
* @param element - The element to calculate the baseline for.
|
|
736
|
+
* @returns {number} - The Y coordinate of the text baseline from the top of the element's margin box.
|
|
737
|
+
*/
|
|
738
|
+
export const calculateBaseline = (element: Element): number => {
|
|
739
|
+
const style = window.getComputedStyle(element);
|
|
740
|
+
const fontSize = parseFloat(style.fontSize);
|
|
741
|
+
|
|
742
|
+
const lineHeight = parseFloat(style.lineHeight) || fontSize * 1.2; // default line-height if not set
|
|
743
|
+
const paddingTop = parseFloat(style.paddingTop);
|
|
744
|
+
const borderTopWidth = parseFloat(style.borderTopWidth);
|
|
745
|
+
const marginTop = parseFloat(style.marginTop);
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Typically, the baseline is about 80% of the `fontSize` from the top of the text, as this is a common average for many fonts.
|
|
749
|
+
*/
|
|
750
|
+
|
|
751
|
+
const baselineOffset = fontSize * 0.8;
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* If the `lineHeight` is greater than the `fontSize`, the extra space is evenly distributed above and below the text. This extra space is `(lineHeight - fontSize) / 2`.
|
|
755
|
+
*/
|
|
756
|
+
const extraLineHeight = (lineHeight - fontSize) / 2;
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Calculate the total baseline Y coordinate from the top of the margin box
|
|
760
|
+
*/
|
|
761
|
+
const baselineY = marginTop + borderTopWidth + paddingTop + extraLineHeight + baselineOffset;
|
|
762
|
+
|
|
763
|
+
return baselineY;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Toggles the [data-blok-empty] attribute on element depending on its emptiness
|
|
768
|
+
* Used to mark empty inputs with a special attribute for placeholders feature
|
|
769
|
+
* @param element - The element to toggle the [data-blok-empty] attribute on
|
|
770
|
+
*/
|
|
771
|
+
export const toggleEmptyMark = (element: HTMLElement): void => {
|
|
772
|
+
element.setAttribute('data-blok-empty', Dom.isEmpty(element) ? 'true' : 'false');
|
|
773
|
+
};
|