@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,676 @@
|
|
|
1
|
+
import { Flipper } from '../../flipper';
|
|
2
|
+
import { PopoverAbstract } from './popover-abstract';
|
|
3
|
+
import type { PopoverItem, PopoverItemRenderParamsMap } from './components/popover-item';
|
|
4
|
+
import { PopoverItemSeparator, css as popoverItemCls } from './components/popover-item';
|
|
5
|
+
import type { PopoverParams } from '@/types/utils/popover/popover';
|
|
6
|
+
import { PopoverEvent } from '@/types/utils/popover/popover-event';
|
|
7
|
+
import { keyCodes } from '../../utils';
|
|
8
|
+
import { CSSVariables } from './popover.const';
|
|
9
|
+
import { DATA_ATTR } from '../../constants/data-attributes';
|
|
10
|
+
import type { SearchableItem } from './components/search-input';
|
|
11
|
+
import { SearchInput, SearchInputEvent, matchesSearchQuery } from './components/search-input';
|
|
12
|
+
import { PopoverItemDefault } from './components/popover-item';
|
|
13
|
+
import { PopoverItemHtml } from './components/popover-item/popover-item-html/popover-item-html';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Desktop popover.
|
|
17
|
+
* On desktop devices popover behaves like a floating element. Nested popover appears at right or left side.
|
|
18
|
+
* @internal
|
|
19
|
+
* @todo support rtl for nested popovers and search
|
|
20
|
+
*/
|
|
21
|
+
export class PopoverDesktop extends PopoverAbstract {
|
|
22
|
+
/**
|
|
23
|
+
* Flipper - module for keyboard iteration between elements
|
|
24
|
+
*/
|
|
25
|
+
public flipper: Flipper | undefined;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Popover nesting level. 0 value means that it is a root popover
|
|
29
|
+
*/
|
|
30
|
+
public nestingLevel = 0;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reference to nested popover if exists.
|
|
34
|
+
* Undefined by default, PopoverDesktop when exists and null after destroyed.
|
|
35
|
+
*/
|
|
36
|
+
protected nestedPopover: PopoverDesktop | undefined | null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Item nested popover is displayed for
|
|
40
|
+
*/
|
|
41
|
+
protected nestedPopoverTriggerItem: PopoverItem | null = null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Last hovered item inside popover.
|
|
45
|
+
* Is used to determine if cursor is moving inside one item or already moved away to another one.
|
|
46
|
+
* Helps prevent reopening nested popover while cursor is moving inside one item area.
|
|
47
|
+
*/
|
|
48
|
+
private previouslyHoveredItem: PopoverItem | null = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Element of the page that creates 'scope' of the popover.
|
|
52
|
+
* If possible, popover will not cross specified element's borders when opening.
|
|
53
|
+
*/
|
|
54
|
+
private scopeElement: HTMLElement = document.body;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Element relative to which the popover should be positioned
|
|
58
|
+
*/
|
|
59
|
+
private trigger: HTMLElement | undefined;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Popover size cache
|
|
63
|
+
*/
|
|
64
|
+
private _size: { height: number; width: number } | undefined;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Construct the instance
|
|
68
|
+
* @param params - popover params
|
|
69
|
+
* @param itemsRenderParams – popover item render params.
|
|
70
|
+
* The parameters that are not set by user via popover api but rather depend on technical implementation
|
|
71
|
+
*/
|
|
72
|
+
constructor(params: PopoverParams, itemsRenderParams?: PopoverItemRenderParamsMap) {
|
|
73
|
+
super(params, itemsRenderParams);
|
|
74
|
+
|
|
75
|
+
if (params.trigger) {
|
|
76
|
+
this.trigger = params.trigger;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (params.nestingLevel !== undefined) {
|
|
80
|
+
this.nestingLevel = params.nestingLevel;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.nestingLevel > 0) {
|
|
84
|
+
this.nodes.popover.setAttribute(DATA_ATTR.nested, 'true');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (params.scopeElement !== undefined) {
|
|
88
|
+
this.scopeElement = params.scopeElement;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (this.nodes.popoverContainer !== null) {
|
|
92
|
+
this.listeners.on(this.nodes.popoverContainer, 'mouseover', (event: Event) => this.handleHover(event));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (params.searchable) {
|
|
96
|
+
this.addSearch();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (params.flippable === false) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (params.flipper !== undefined) {
|
|
104
|
+
params.flipper.deactivate();
|
|
105
|
+
params.flipper.removeOnFlip(this.onFlip);
|
|
106
|
+
this.flipper = params.flipper;
|
|
107
|
+
} else {
|
|
108
|
+
this.flipper = new Flipper({
|
|
109
|
+
items: this.flippableElements,
|
|
110
|
+
focusedItemClass: popoverItemCls.focused,
|
|
111
|
+
allowedKeys: [
|
|
112
|
+
keyCodes.TAB,
|
|
113
|
+
keyCodes.UP,
|
|
114
|
+
keyCodes.DOWN,
|
|
115
|
+
keyCodes.ENTER,
|
|
116
|
+
keyCodes.RIGHT,
|
|
117
|
+
keyCodes.LEFT,
|
|
118
|
+
],
|
|
119
|
+
onArrowLeft: params.onNavigateBack,
|
|
120
|
+
handleContentEditableTargets: params.handleContentEditableNavigation,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.flipper?.onFlip(this.onFlip);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns true if some item inside popover is focused
|
|
129
|
+
*/
|
|
130
|
+
public hasFocus(): boolean {
|
|
131
|
+
if (this.flipper === undefined) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.flipper.hasFocus();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Scroll position inside items container of the popover
|
|
140
|
+
*/
|
|
141
|
+
public get scrollTop(): number {
|
|
142
|
+
if (this.nodes.items === null) {
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.nodes.items.scrollTop;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns visible element offset top
|
|
151
|
+
*/
|
|
152
|
+
public get offsetTop(): number {
|
|
153
|
+
if (this.nodes.popoverContainer === null) {
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.nodes.popoverContainer.offsetTop;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Open popover
|
|
162
|
+
*/
|
|
163
|
+
public show(): void {
|
|
164
|
+
const mountTarget = this.getMountElement();
|
|
165
|
+
|
|
166
|
+
if (this.trigger && mountTarget) {
|
|
167
|
+
document.body.appendChild(mountTarget);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (this.trigger) {
|
|
171
|
+
const { top, left } = this.calculatePosition();
|
|
172
|
+
this.nodes.popover.style.position = 'absolute';
|
|
173
|
+
this.nodes.popover.style.top = `${top}px`;
|
|
174
|
+
this.nodes.popover.style.left = `${left}px`;
|
|
175
|
+
this.nodes.popover.style.setProperty(CSSVariables.PopoverTop, '0px');
|
|
176
|
+
this.nodes.popover.style.setProperty(CSSVariables.PopoverLeft, '0px');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.nodes.popover.style.setProperty(CSSVariables.PopoverHeight, this.size.height + 'px');
|
|
180
|
+
|
|
181
|
+
if (!this.trigger && !this.shouldOpenBottom) {
|
|
182
|
+
this.setOpenTop(true);
|
|
183
|
+
// Apply open-top positioning (moved from popover.css)
|
|
184
|
+
this.nodes.popover.style.setProperty(CSSVariables.PopoverTop, 'calc(-1 * (0.5rem + var(--popover-height)))');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!this.trigger && !this.shouldOpenRight) {
|
|
188
|
+
this.setOpenLeft(true);
|
|
189
|
+
// Apply open-left positioning (moved from popover.css)
|
|
190
|
+
this.nodes.popover.style.setProperty(CSSVariables.PopoverLeft, 'calc(-1 * var(--width) + 100%)');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
super.show();
|
|
194
|
+
this.flipper?.activate(this.flippableElements);
|
|
195
|
+
|
|
196
|
+
// Focus the first item: search field if present, otherwise first menu item
|
|
197
|
+
requestAnimationFrame(() => {
|
|
198
|
+
this.focusInitialElement();
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Focuses the initial element when popover is shown.
|
|
204
|
+
* Focuses search field if present, otherwise first menu item.
|
|
205
|
+
* Skips the first Tab press so it just "enters" the menu rather than advancing.
|
|
206
|
+
*/
|
|
207
|
+
private focusInitialElement(): void {
|
|
208
|
+
if (this.search) {
|
|
209
|
+
this.search.focus();
|
|
210
|
+
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.flipper?.focusItem(0, { skipNextTab: true });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Calculates position for the popover
|
|
219
|
+
*/
|
|
220
|
+
private calculatePosition(): { top: number; left: number } {
|
|
221
|
+
if (!this.trigger) {
|
|
222
|
+
return {
|
|
223
|
+
top: 0,
|
|
224
|
+
left: 0,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const triggerRect = this.trigger.getBoundingClientRect();
|
|
229
|
+
const popoverRect = this.size;
|
|
230
|
+
const windowWidth = window.innerWidth;
|
|
231
|
+
const windowHeight = window.innerHeight;
|
|
232
|
+
const offset = 8;
|
|
233
|
+
|
|
234
|
+
const initialTop = triggerRect.bottom + offset + window.scrollY;
|
|
235
|
+
const shouldFlipTop = (triggerRect.bottom + offset + popoverRect.height > windowHeight + window.scrollY) &&
|
|
236
|
+
(triggerRect.top - offset - popoverRect.height > window.scrollY);
|
|
237
|
+
const top = shouldFlipTop ? triggerRect.top - offset - popoverRect.height + window.scrollY : initialTop;
|
|
238
|
+
|
|
239
|
+
const initialLeft = triggerRect.left + window.scrollX;
|
|
240
|
+
const shouldFlipLeft = initialLeft + popoverRect.width > windowWidth + window.scrollX;
|
|
241
|
+
const left = shouldFlipLeft ? Math.max(0, triggerRect.right - popoverRect.width + window.scrollX) : initialLeft;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
top,
|
|
245
|
+
left,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Closes popover
|
|
251
|
+
*/
|
|
252
|
+
public hide = (): void => {
|
|
253
|
+
super.hide();
|
|
254
|
+
|
|
255
|
+
this.destroyNestedPopoverIfExists();
|
|
256
|
+
|
|
257
|
+
this.flipper?.deactivate();
|
|
258
|
+
|
|
259
|
+
this.previouslyHoveredItem = null;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Clears memory
|
|
264
|
+
*/
|
|
265
|
+
public destroy(): void {
|
|
266
|
+
this.hide();
|
|
267
|
+
super.destroy();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Checks if popover contains the node.
|
|
272
|
+
* Overridden to check nested popover as well.
|
|
273
|
+
* @param node - node to check
|
|
274
|
+
*/
|
|
275
|
+
public override hasNode(node: Node): boolean {
|
|
276
|
+
if (super.hasNode(node)) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (this.nestedPopover !== undefined && this.nestedPopover !== null) {
|
|
281
|
+
return this.nestedPopover.hasNode(node);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Handles displaying nested items for the item.
|
|
289
|
+
* @param item – item to show nested popover for
|
|
290
|
+
*/
|
|
291
|
+
protected override showNestedItems(item: PopoverItem): void {
|
|
292
|
+
if (this.nestedPopover !== null && this.nestedPopover !== undefined) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.nestedPopoverTriggerItem = item;
|
|
297
|
+
|
|
298
|
+
this.showNestedPopoverForItem(item);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Handles hover events inside popover items container
|
|
303
|
+
* @param event - hover event data
|
|
304
|
+
*/
|
|
305
|
+
protected handleHover(event: Event): void {
|
|
306
|
+
const item = this.getTargetItem(event);
|
|
307
|
+
|
|
308
|
+
if (item === undefined) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (this.previouslyHoveredItem === item) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
this.destroyNestedPopoverIfExists();
|
|
317
|
+
|
|
318
|
+
this.previouslyHoveredItem = item;
|
|
319
|
+
|
|
320
|
+
if (!item.hasChildren) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.showNestedPopoverForItem(item);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Sets CSS variable with position of item near which nested popover should be displayed.
|
|
329
|
+
* Is used for correct positioning of the nested popover
|
|
330
|
+
* @param nestedPopoverEl - nested popover element
|
|
331
|
+
* @param item – item near which nested popover should be displayed
|
|
332
|
+
*/
|
|
333
|
+
protected setTriggerItemPosition(nestedPopoverEl: HTMLElement, item: PopoverItem): void {
|
|
334
|
+
const itemEl = item.getElement();
|
|
335
|
+
const itemOffsetTop = (itemEl ? itemEl.offsetTop : 0) - this.scrollTop;
|
|
336
|
+
const topOffset = this.offsetTop + itemOffsetTop;
|
|
337
|
+
|
|
338
|
+
const actualPopoverEl = nestedPopoverEl.querySelector(`[${DATA_ATTR.popover}]`) as HTMLElement | null ?? nestedPopoverEl;
|
|
339
|
+
|
|
340
|
+
actualPopoverEl.style.setProperty(CSSVariables.TriggerItemTop, topOffset + 'px');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Destroys existing nested popover
|
|
345
|
+
*/
|
|
346
|
+
protected destroyNestedPopoverIfExists(): void {
|
|
347
|
+
if (this.nestedPopover === undefined || this.nestedPopover === null) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const triggerItemElement = this.nestedPopoverTriggerItem?.getElement();
|
|
352
|
+
|
|
353
|
+
this.nestedPopover.off(PopoverEvent.ClosedOnActivate, this.hide);
|
|
354
|
+
this.nestedPopover.hide();
|
|
355
|
+
this.nestedPopover.destroy();
|
|
356
|
+
this.nestedPopover.getElement().remove();
|
|
357
|
+
this.nestedPopover = null;
|
|
358
|
+
this.flipper?.activate(this.flippableElements);
|
|
359
|
+
// Focus the trigger item synchronously to ensure keyboard events work immediately
|
|
360
|
+
this.focusAfterNestedPopoverClose(triggerItemElement);
|
|
361
|
+
|
|
362
|
+
this.nestedPopoverTriggerItem?.onChildrenClose();
|
|
363
|
+
// Reset trigger item so clicking the same item again will open the nested popover
|
|
364
|
+
this.nestedPopoverTriggerItem = null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Focuses the appropriate item after nested popover closes.
|
|
369
|
+
* Focuses the item that opened the nested popover, or falls back to first item.
|
|
370
|
+
* @param triggerItemElement - element that triggered the nested popover
|
|
371
|
+
*/
|
|
372
|
+
private focusAfterNestedPopoverClose(triggerItemElement: HTMLElement | null | undefined): void {
|
|
373
|
+
if (!triggerItemElement || !this.flipper) {
|
|
374
|
+
this.flipper?.focusFirst();
|
|
375
|
+
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const triggerIndex = this.flippableElements.indexOf(triggerItemElement);
|
|
380
|
+
|
|
381
|
+
if (triggerIndex !== -1) {
|
|
382
|
+
// Don't skip next Tab - user expects Tab to move to next item after closing nested popover
|
|
383
|
+
this.flipper.focusItem(triggerIndex, { skipNextTab: false });
|
|
384
|
+
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this.flipper.focusFirst();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Creates and displays nested popover for specified item.
|
|
393
|
+
* Is used only on desktop
|
|
394
|
+
* @param item - item to display nested popover by
|
|
395
|
+
*/
|
|
396
|
+
protected showNestedPopoverForItem(item: PopoverItem): PopoverDesktop {
|
|
397
|
+
this.nestedPopover = new PopoverDesktop({
|
|
398
|
+
searchable: item.isChildrenSearchable,
|
|
399
|
+
items: item.children,
|
|
400
|
+
nestingLevel: this.nestingLevel + 1,
|
|
401
|
+
flippable: item.isChildrenFlippable,
|
|
402
|
+
messages: this.messages,
|
|
403
|
+
onNavigateBack: this.destroyNestedPopoverIfExists.bind(this),
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
item.onChildrenOpen();
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Close nested popover when item with 'closeOnActivate' property set was clicked
|
|
410
|
+
* parent popover should also be closed
|
|
411
|
+
*/
|
|
412
|
+
this.nestedPopover.on(PopoverEvent.ClosedOnActivate, this.hide);
|
|
413
|
+
|
|
414
|
+
const nestedPopoverEl = this.nestedPopover.getMountElement();
|
|
415
|
+
const actualNestedPopoverEl = this.nestedPopover.getElement();
|
|
416
|
+
|
|
417
|
+
this.nodes.popover.appendChild(nestedPopoverEl);
|
|
418
|
+
|
|
419
|
+
this.setTriggerItemPosition(nestedPopoverEl, item);
|
|
420
|
+
|
|
421
|
+
/* We need nesting level value in CSS to calculate offset left for nested popover */
|
|
422
|
+
/* Set on the actual popover element so it's available for --popover-left calculation */
|
|
423
|
+
actualNestedPopoverEl.style.setProperty(CSSVariables.NestingLevel, this.nestedPopover.nestingLevel.toString());
|
|
424
|
+
|
|
425
|
+
// Apply nested popover positioning (moved from popover.css)
|
|
426
|
+
this.applyNestedPopoverPositioning(nestedPopoverEl);
|
|
427
|
+
|
|
428
|
+
this.nestedPopover.show();
|
|
429
|
+
this.flipper?.deactivate();
|
|
430
|
+
|
|
431
|
+
return this.nestedPopover;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Applies positioning styles to nested popover container.
|
|
436
|
+
* This replaces CSS selectors like [data-blok-nested] [data-blok-popover-container]
|
|
437
|
+
* @param nestedPopoverEl - the nested popover element (mount element)
|
|
438
|
+
*/
|
|
439
|
+
private applyNestedPopoverPositioning(nestedPopoverEl: HTMLElement): void {
|
|
440
|
+
const nestedContainer = nestedPopoverEl.querySelector(`[${DATA_ATTR.popoverContainer}]`) as HTMLElement | null;
|
|
441
|
+
|
|
442
|
+
if (!nestedContainer) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const actualPopoverEl = nestedPopoverEl.querySelector(`[${DATA_ATTR.popover}]`) as HTMLElement | null ?? nestedPopoverEl;
|
|
447
|
+
|
|
448
|
+
// Check if parent popover has openTop or openLeft state
|
|
449
|
+
const isParentOpenTop = this.nodes.popover.hasAttribute(DATA_ATTR.popoverOpenTop);
|
|
450
|
+
const isParentOpenLeft = this.nodes.popover.hasAttribute(DATA_ATTR.popoverOpenLeft);
|
|
451
|
+
|
|
452
|
+
// Apply position: absolute for nested container
|
|
453
|
+
nestedContainer.style.position = 'absolute';
|
|
454
|
+
|
|
455
|
+
// Calculate --popover-left based on nesting level and parent open direction
|
|
456
|
+
// Set on the actual popover element to override its default value
|
|
457
|
+
if (isParentOpenLeft) {
|
|
458
|
+
// Position to the left
|
|
459
|
+
actualPopoverEl.style.setProperty(CSSVariables.PopoverLeft, 'calc(-1 * (var(--nesting-level) + 1) * var(--width) + 100%)');
|
|
460
|
+
} else {
|
|
461
|
+
// Position to the right
|
|
462
|
+
actualPopoverEl.style.setProperty(CSSVariables.PopoverLeft, 'calc(var(--nesting-level) * (var(--width) - var(--nested-popover-overlap)))');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Calculate top position based on parent open direction
|
|
466
|
+
if (isParentOpenTop) {
|
|
467
|
+
// Open upward
|
|
468
|
+
nestedContainer.style.top = 'calc(var(--trigger-item-top) - var(--popover-height) + var(--item-height) + 0.5rem + var(--nested-popover-overlap))';
|
|
469
|
+
} else {
|
|
470
|
+
// Open downward
|
|
471
|
+
nestedContainer.style.top = 'calc(var(--trigger-item-top) - var(--nested-popover-overlap))';
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Checks if popover should be opened bottom.
|
|
477
|
+
* It should happen when there is enough space below or not enough space above
|
|
478
|
+
*/
|
|
479
|
+
private get shouldOpenBottom(): boolean {
|
|
480
|
+
if (this.nodes.popover === undefined || this.nodes.popover === null) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
const popoverRect = this.nodes.popoverContainer.getBoundingClientRect();
|
|
484
|
+
const scopeElementRect = this.scopeElement.getBoundingClientRect();
|
|
485
|
+
const popoverHeight = this.size.height;
|
|
486
|
+
const popoverPotentialBottomEdge = popoverRect.top + popoverHeight;
|
|
487
|
+
const popoverPotentialTopEdge = popoverRect.top - popoverHeight;
|
|
488
|
+
const bottomEdgeForComparison = Math.min(window.innerHeight, scopeElementRect.bottom);
|
|
489
|
+
|
|
490
|
+
return popoverPotentialTopEdge < scopeElementRect.top || popoverPotentialBottomEdge <= bottomEdgeForComparison;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Checks if popover should be opened left.
|
|
495
|
+
* It should happen when there is enough space in the right or not enough space in the left
|
|
496
|
+
*/
|
|
497
|
+
private get shouldOpenRight(): boolean {
|
|
498
|
+
if (this.nodes.popover === undefined || this.nodes.popover === null) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const popoverRect = this.nodes.popover.getBoundingClientRect();
|
|
503
|
+
const scopeElementRect = this.scopeElement.getBoundingClientRect();
|
|
504
|
+
const popoverWidth = this.size.width;
|
|
505
|
+
const popoverPotentialRightEdge = popoverRect.right + popoverWidth;
|
|
506
|
+
const popoverPotentialLeftEdge = popoverRect.left - popoverWidth;
|
|
507
|
+
const rightEdgeForComparison = Math.min(window.innerWidth, scopeElementRect.right);
|
|
508
|
+
|
|
509
|
+
return popoverPotentialLeftEdge < scopeElementRect.left || popoverPotentialRightEdge <= rightEdgeForComparison;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Helps to calculate size of popover that is only resolved when popover is displayed on screen.
|
|
514
|
+
* Renders invisible clone of popover to get actual values.
|
|
515
|
+
*/
|
|
516
|
+
public get size(): { height: number; width: number } {
|
|
517
|
+
if (this._size) {
|
|
518
|
+
return this._size;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const size = {
|
|
522
|
+
height: 0,
|
|
523
|
+
width: 0,
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
if (this.nodes.popover === null) {
|
|
527
|
+
return size;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const popoverClone = this.nodes.popover.cloneNode(true) as HTMLElement;
|
|
531
|
+
|
|
532
|
+
popoverClone.style.visibility = 'hidden';
|
|
533
|
+
popoverClone.style.position = 'absolute';
|
|
534
|
+
popoverClone.style.top = '-1000px';
|
|
535
|
+
|
|
536
|
+
popoverClone.setAttribute(DATA_ATTR.popoverOpened, 'true');
|
|
537
|
+
popoverClone.querySelector(`[${DATA_ATTR.nested}]`)?.remove();
|
|
538
|
+
document.body.appendChild(popoverClone);
|
|
539
|
+
|
|
540
|
+
const container = popoverClone.querySelector(`[${DATA_ATTR.popoverContainer}]`) as HTMLElement;
|
|
541
|
+
|
|
542
|
+
size.height = container.offsetHeight;
|
|
543
|
+
size.width = container.offsetWidth;
|
|
544
|
+
popoverClone.remove();
|
|
545
|
+
|
|
546
|
+
this._size = size;
|
|
547
|
+
|
|
548
|
+
return size;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Returns list of elements available for keyboard navigation.
|
|
553
|
+
*/
|
|
554
|
+
protected get flippableElements(): HTMLElement[] {
|
|
555
|
+
const result = this.items.flatMap(item => {
|
|
556
|
+
return this.getFlippableElementsForItem(item);
|
|
557
|
+
}).filter((item): item is HTMLElement => item !== undefined && item !== null);
|
|
558
|
+
|
|
559
|
+
return result;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Gets flippable elements for a single item.
|
|
564
|
+
* @param item - popover item to get elements from
|
|
565
|
+
* @returns array of HTML elements for keyboard navigation
|
|
566
|
+
*/
|
|
567
|
+
private getFlippableElementsForItem(item: PopoverItem): HTMLElement[] {
|
|
568
|
+
if (item instanceof PopoverItemHtml) {
|
|
569
|
+
const element = item.getElement();
|
|
570
|
+
|
|
571
|
+
return element ? [element] : [];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (!(item instanceof PopoverItemDefault)) {
|
|
575
|
+
return [];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (item.isDisabled) {
|
|
579
|
+
return [];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const element = item.getElement();
|
|
583
|
+
|
|
584
|
+
return element ? [ element ] : [];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Called on flipper navigation
|
|
589
|
+
*/
|
|
590
|
+
private onFlip = (): void => {
|
|
591
|
+
const focusedItem = this.itemsDefault.find(item => item.isFocused);
|
|
592
|
+
|
|
593
|
+
focusedItem?.onFocus();
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Adds search to the popover
|
|
598
|
+
*/
|
|
599
|
+
private addSearch(): void {
|
|
600
|
+
this.search = new SearchInput({
|
|
601
|
+
items: this.itemsDefault,
|
|
602
|
+
placeholder: this.messages.search,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
this.search.on(SearchInputEvent.Search, this.onSearch);
|
|
606
|
+
|
|
607
|
+
const searchElement = this.search.getElement();
|
|
608
|
+
|
|
609
|
+
searchElement.classList.add('mb-1.5');
|
|
610
|
+
|
|
611
|
+
this.nodes.popoverContainer.insertBefore(searchElement, this.nodes.popoverContainer.firstChild);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Filters popover items by query string.
|
|
616
|
+
* Used for inline slash search where typing happens in the block, not in a search input.
|
|
617
|
+
* @param query - search query text
|
|
618
|
+
*/
|
|
619
|
+
public override filterItems(query: string): void {
|
|
620
|
+
const matchingItems = this.itemsDefault.filter(item => matchesSearchQuery(item, query));
|
|
621
|
+
|
|
622
|
+
this.onSearch({
|
|
623
|
+
query,
|
|
624
|
+
items: matchingItems as unknown as SearchableItem[],
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Handles input inside search field
|
|
630
|
+
* @param data - search input event data
|
|
631
|
+
* @param data.query - search query text
|
|
632
|
+
* @param data.items - search results
|
|
633
|
+
*/
|
|
634
|
+
private onSearch = (data: { query: string, items: SearchableItem[] }): void => {
|
|
635
|
+
const isEmptyQuery = data.query === '';
|
|
636
|
+
const isNothingFound = data.items.length === 0;
|
|
637
|
+
|
|
638
|
+
// Cast data.items to PopoverItemDefault[] since we know that's what filterItems passes
|
|
639
|
+
const matchingItems = data.items as unknown as PopoverItemDefault[];
|
|
640
|
+
|
|
641
|
+
this.items
|
|
642
|
+
.forEach((item) => {
|
|
643
|
+
const isDefaultItem = item instanceof PopoverItemDefault;
|
|
644
|
+
const isSeparatorOrHtml = item instanceof PopoverItemSeparator || item instanceof PopoverItemHtml;
|
|
645
|
+
const isHidden = isDefaultItem
|
|
646
|
+
? !matchingItems.includes(item as PopoverItemDefault)
|
|
647
|
+
: isSeparatorOrHtml && (isNothingFound || !isEmptyQuery);
|
|
648
|
+
|
|
649
|
+
item.toggleHidden(isHidden);
|
|
650
|
+
});
|
|
651
|
+
this.toggleNothingFoundMessage(isNothingFound);
|
|
652
|
+
|
|
653
|
+
/** List of elements available for keyboard navigation considering search query applied */
|
|
654
|
+
const flippableElements = isEmptyQuery ? this.flippableElements : data.items.map(item => (item as PopoverItem).getElement());
|
|
655
|
+
|
|
656
|
+
if (!this.flipper?.isActivated) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Update flipper items with only visible */
|
|
661
|
+
this.flipper.deactivate();
|
|
662
|
+
this.flipper.activate(flippableElements as HTMLElement[]);
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Focus first item after filtering.
|
|
666
|
+
* Always skip the first Tab press so it just "enters" the menu rather than
|
|
667
|
+
* advancing to second item. This applies regardless of whether the query is
|
|
668
|
+
* empty (initial "/" open) or non-empty (user is typing to filter), because
|
|
669
|
+
* the user's keyboard focus is still in the search input - pressing Tab
|
|
670
|
+
* should enter the list at item 0, not advance from 0 to 1.
|
|
671
|
+
*/
|
|
672
|
+
if (flippableElements.length > 0) {
|
|
673
|
+
this.flipper.focusItem(0, { skipNextTab: true });
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
}
|