@jackuait/blok 0.4.1 → 0.4.3-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -17
- package/codemod/README.md +45 -7
- package/codemod/migrate-editorjs-to-blok.js +951 -92
- package/codemod/test.js +780 -77
- package/dist/blok.mjs +5 -2
- package/dist/chunks/blok-8ptWuVZC.mjs +12838 -0
- package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
- package/dist/chunks/i18next-loader-bLawSYRV.mjs +43 -0
- package/dist/{index-CBkApZKo.mjs → chunks/index-5nYtWZD2.mjs} +2 -2
- package/dist/chunks/inline-tool-convert-CvMDAIzb.mjs +1988 -0
- package/dist/chunks/messages-2434tVOK.mjs +47 -0
- package/dist/chunks/messages-3DcCwXMF.mjs +47 -0
- package/dist/chunks/messages-4kMwVAKY.mjs +47 -0
- package/dist/chunks/messages-57uL5htT.mjs +47 -0
- package/dist/chunks/messages-76-iJV9Q.mjs +47 -0
- package/dist/chunks/messages-8p86Eyf2.mjs +47 -0
- package/dist/chunks/messages-BBX0p0Pi.mjs +47 -0
- package/dist/chunks/messages-BCm2eudQ.mjs +47 -0
- package/dist/chunks/messages-BFiUomgG.mjs +47 -0
- package/dist/chunks/messages-BIPNHHAV.mjs +47 -0
- package/dist/chunks/messages-BUlwu9mo.mjs +47 -0
- package/dist/chunks/messages-BX-DPa-z.mjs +47 -0
- package/dist/chunks/messages-BextV3Qh.mjs +47 -0
- package/dist/chunks/messages-BiPSFlUG.mjs +47 -0
- package/dist/chunks/messages-BiXe9G-O.mjs +47 -0
- package/dist/chunks/messages-Bl5z_Igo.mjs +47 -0
- package/dist/chunks/messages-BnsE97ku.mjs +47 -0
- package/dist/chunks/messages-BoO8gsVD.mjs +47 -0
- package/dist/chunks/messages-BqWaOGMn.mjs +47 -0
- package/dist/chunks/messages-BqkL2_Ro.mjs +47 -0
- package/dist/chunks/messages-BvCkXKX-.mjs +47 -0
- package/dist/chunks/messages-C6tbPLoj.mjs +47 -0
- package/dist/chunks/messages-CA6T3-gQ.mjs +47 -0
- package/dist/chunks/messages-CFFPFdWP.mjs +47 -0
- package/dist/chunks/messages-CFrKE-TN.mjs +47 -0
- package/dist/chunks/messages-CHz8VlG-.mjs +47 -0
- package/dist/chunks/messages-CLixzySl.mjs +47 -0
- package/dist/chunks/messages-CV7OM_qk.mjs +47 -0
- package/dist/chunks/messages-CXHt3eCC.mjs +47 -0
- package/dist/chunks/messages-CbmsBrB0.mjs +47 -0
- package/dist/chunks/messages-Ceo1KtFx.mjs +47 -0
- package/dist/chunks/messages-Cm0LJLtB.mjs +47 -0
- package/dist/chunks/messages-CmymP_Ar.mjs +47 -0
- package/dist/chunks/messages-D0ohMB5H.mjs +47 -0
- package/dist/chunks/messages-D3GrDwXh.mjs +47 -0
- package/dist/chunks/messages-D3vTzIpL.mjs +47 -0
- package/dist/chunks/messages-D5WeksbV.mjs +47 -0
- package/dist/chunks/messages-DGaab4EP.mjs +47 -0
- package/dist/chunks/messages-DKha57ZU.mjs +47 -0
- package/dist/chunks/messages-DOaujgMW.mjs +47 -0
- package/dist/chunks/messages-DVbPLd_0.mjs +47 -0
- package/dist/chunks/messages-D_FCyfW6.mjs +47 -0
- package/dist/chunks/messages-Dd5iZN3c.mjs +47 -0
- package/dist/chunks/messages-DehM7135.mjs +47 -0
- package/dist/chunks/messages-Dg1OHftD.mjs +47 -0
- package/dist/chunks/messages-Di6Flq-b.mjs +47 -0
- package/dist/chunks/messages-Dqhhex6e.mjs +47 -0
- package/dist/chunks/messages-DueVe0F1.mjs +47 -0
- package/dist/chunks/messages-Dx3eFwI0.mjs +47 -0
- package/dist/chunks/messages-FOtiUoKl.mjs +47 -0
- package/dist/chunks/messages-FTOZNhRD.mjs +47 -0
- package/dist/chunks/messages-IQxGfQIV.mjs +47 -0
- package/dist/chunks/messages-JF2fzCkK.mjs +47 -0
- package/dist/chunks/messages-MOGl7I5v.mjs +47 -0
- package/dist/chunks/messages-QgYhPL-3.mjs +47 -0
- package/dist/chunks/messages-WYWIbQwo.mjs +47 -0
- package/dist/chunks/messages-a6A_LgDv.mjs +47 -0
- package/dist/chunks/messages-bSf31LJi.mjs +47 -0
- package/dist/chunks/messages-diGozhTn.mjs +47 -0
- package/dist/chunks/messages-er-kd-VO.mjs +47 -0
- package/dist/chunks/messages-ez3w5NBn.mjs +47 -0
- package/dist/chunks/messages-f3uXjegd.mjs +47 -0
- package/dist/chunks/messages-ohwI1UGv.mjs +47 -0
- package/dist/chunks/messages-p9BZJaFV.mjs +47 -0
- package/dist/chunks/messages-qIQ4L4rw.mjs +47 -0
- package/dist/chunks/messages-qWkXPggi.mjs +47 -0
- package/dist/chunks/messages-w5foGze_.mjs +47 -0
- package/dist/full.mjs +50 -0
- package/dist/locales.mjs +227 -0
- package/dist/messages-2434tVOK.mjs +47 -0
- package/dist/messages-3DcCwXMF.mjs +47 -0
- package/dist/messages-4kMwVAKY.mjs +47 -0
- package/dist/messages-57uL5htT.mjs +47 -0
- package/dist/messages-76-iJV9Q.mjs +47 -0
- package/dist/messages-8p86Eyf2.mjs +47 -0
- package/dist/messages-BBX0p0Pi.mjs +47 -0
- package/dist/messages-BCm2eudQ.mjs +47 -0
- package/dist/messages-BFiUomgG.mjs +47 -0
- package/dist/messages-BIPNHHAV.mjs +47 -0
- package/dist/messages-BUlwu9mo.mjs +47 -0
- package/dist/messages-BX-DPa-z.mjs +47 -0
- package/dist/messages-BextV3Qh.mjs +47 -0
- package/dist/messages-BiPSFlUG.mjs +47 -0
- package/dist/messages-BiXe9G-O.mjs +47 -0
- package/dist/messages-Bl5z_Igo.mjs +47 -0
- package/dist/messages-BnsE97ku.mjs +47 -0
- package/dist/messages-BoO8gsVD.mjs +47 -0
- package/dist/messages-BqWaOGMn.mjs +47 -0
- package/dist/messages-BqkL2_Ro.mjs +47 -0
- package/dist/messages-BvCkXKX-.mjs +47 -0
- package/dist/messages-C6tbPLoj.mjs +47 -0
- package/dist/messages-CA6T3-gQ.mjs +47 -0
- package/dist/messages-CFFPFdWP.mjs +47 -0
- package/dist/messages-CFrKE-TN.mjs +47 -0
- package/dist/messages-CHz8VlG-.mjs +47 -0
- package/dist/messages-CLixzySl.mjs +47 -0
- package/dist/messages-CV7OM_qk.mjs +47 -0
- package/dist/messages-CXHt3eCC.mjs +47 -0
- package/dist/messages-CbmsBrB0.mjs +47 -0
- package/dist/messages-Ceo1KtFx.mjs +47 -0
- package/dist/messages-Cm0LJLtB.mjs +47 -0
- package/dist/messages-CmymP_Ar.mjs +47 -0
- package/dist/messages-D0ohMB5H.mjs +47 -0
- package/dist/messages-D3GrDwXh.mjs +47 -0
- package/dist/messages-D3vTzIpL.mjs +47 -0
- package/dist/messages-D5WeksbV.mjs +47 -0
- package/dist/messages-DGaab4EP.mjs +47 -0
- package/dist/messages-DKha57ZU.mjs +47 -0
- package/dist/messages-DOaujgMW.mjs +47 -0
- package/dist/messages-DVbPLd_0.mjs +47 -0
- package/dist/messages-D_FCyfW6.mjs +47 -0
- package/dist/messages-Dd5iZN3c.mjs +47 -0
- package/dist/messages-DehM7135.mjs +47 -0
- package/dist/messages-Dg1OHftD.mjs +47 -0
- package/dist/messages-Di6Flq-b.mjs +47 -0
- package/dist/messages-Dqhhex6e.mjs +47 -0
- package/dist/messages-DueVe0F1.mjs +47 -0
- package/dist/messages-Dx3eFwI0.mjs +47 -0
- package/dist/messages-FOtiUoKl.mjs +47 -0
- package/dist/messages-FTOZNhRD.mjs +47 -0
- package/dist/messages-IQxGfQIV.mjs +47 -0
- package/dist/messages-JF2fzCkK.mjs +47 -0
- package/dist/messages-MOGl7I5v.mjs +47 -0
- package/dist/messages-QgYhPL-3.mjs +47 -0
- package/dist/messages-WYWIbQwo.mjs +47 -0
- package/dist/messages-a6A_LgDv.mjs +47 -0
- package/dist/messages-bSf31LJi.mjs +47 -0
- package/dist/messages-diGozhTn.mjs +47 -0
- package/dist/messages-er-kd-VO.mjs +47 -0
- package/dist/messages-ez3w5NBn.mjs +47 -0
- package/dist/messages-f3uXjegd.mjs +47 -0
- package/dist/messages-ohwI1UGv.mjs +47 -0
- package/dist/messages-p9BZJaFV.mjs +47 -0
- package/dist/messages-qIQ4L4rw.mjs +47 -0
- package/dist/messages-qWkXPggi.mjs +47 -0
- package/dist/messages-w5foGze_.mjs +47 -0
- package/dist/tools.mjs +3073 -0
- package/dist/vendor.LICENSE.txt +59 -156
- package/package.json +60 -15
- package/src/blok.ts +267 -0
- package/src/components/__module.ts +139 -0
- package/src/components/block/api.ts +155 -0
- package/src/components/block/index.ts +1427 -0
- package/src/components/block-tunes/block-tune-delete.ts +51 -0
- package/src/components/blocks.ts +338 -0
- package/src/components/constants/data-attributes.ts +342 -0
- package/src/components/constants.ts +76 -0
- package/src/components/core.ts +392 -0
- package/src/components/dom.ts +773 -0
- package/src/components/domIterator.ts +189 -0
- package/src/components/errors/critical.ts +5 -0
- package/src/components/events/BlockChanged.ts +16 -0
- package/src/components/events/BlockHovered.ts +21 -0
- package/src/components/events/BlockSettingsClosed.ts +12 -0
- package/src/components/events/BlockSettingsOpened.ts +12 -0
- package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
- package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
- package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
- package/src/components/events/HistoryStateChanged.ts +19 -0
- package/src/components/events/RedactorDomChanged.ts +14 -0
- package/src/components/events/index.ts +46 -0
- package/src/components/flipper.ts +481 -0
- package/src/components/i18n/i18next-loader.ts +84 -0
- package/src/components/i18n/lightweight-i18n.ts +86 -0
- package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
- package/src/components/i18n/locales/am/messages.json +44 -0
- package/src/components/i18n/locales/ar/messages.json +44 -0
- package/src/components/i18n/locales/az/messages.json +44 -0
- package/src/components/i18n/locales/bg/messages.json +44 -0
- package/src/components/i18n/locales/bn/messages.json +44 -0
- package/src/components/i18n/locales/bs/messages.json +44 -0
- package/src/components/i18n/locales/cs/messages.json +44 -0
- package/src/components/i18n/locales/da/messages.json +44 -0
- package/src/components/i18n/locales/de/messages.json +44 -0
- package/src/components/i18n/locales/dv/messages.json +44 -0
- package/src/components/i18n/locales/el/messages.json +44 -0
- package/src/components/i18n/locales/en/messages.json +44 -0
- package/src/components/i18n/locales/es/messages.json +44 -0
- package/src/components/i18n/locales/et/messages.json +44 -0
- package/src/components/i18n/locales/fa/messages.json +44 -0
- package/src/components/i18n/locales/fi/messages.json +44 -0
- package/src/components/i18n/locales/fil/messages.json +44 -0
- package/src/components/i18n/locales/fr/messages.json +44 -0
- package/src/components/i18n/locales/gu/messages.json +44 -0
- package/src/components/i18n/locales/he/messages.json +44 -0
- package/src/components/i18n/locales/hi/messages.json +44 -0
- package/src/components/i18n/locales/hr/messages.json +44 -0
- package/src/components/i18n/locales/hu/messages.json +44 -0
- package/src/components/i18n/locales/hy/messages.json +44 -0
- package/src/components/i18n/locales/id/messages.json +44 -0
- package/src/components/i18n/locales/index.ts +225 -0
- package/src/components/i18n/locales/it/messages.json +44 -0
- package/src/components/i18n/locales/ja/messages.json +44 -0
- package/src/components/i18n/locales/ka/messages.json +44 -0
- package/src/components/i18n/locales/km/messages.json +44 -0
- package/src/components/i18n/locales/kn/messages.json +44 -0
- package/src/components/i18n/locales/ko/messages.json +44 -0
- package/src/components/i18n/locales/ku/messages.json +44 -0
- package/src/components/i18n/locales/lo/messages.json +44 -0
- package/src/components/i18n/locales/lt/messages.json +44 -0
- package/src/components/i18n/locales/lv/messages.json +44 -0
- package/src/components/i18n/locales/mk/messages.json +44 -0
- package/src/components/i18n/locales/ml/messages.json +44 -0
- package/src/components/i18n/locales/mn/messages.json +44 -0
- package/src/components/i18n/locales/mr/messages.json +44 -0
- package/src/components/i18n/locales/ms/messages.json +44 -0
- package/src/components/i18n/locales/my/messages.json +44 -0
- package/src/components/i18n/locales/ne/messages.json +44 -0
- package/src/components/i18n/locales/nl/messages.json +44 -0
- package/src/components/i18n/locales/no/messages.json +44 -0
- package/src/components/i18n/locales/pa/messages.json +44 -0
- package/src/components/i18n/locales/pl/messages.json +44 -0
- package/src/components/i18n/locales/ps/messages.json +44 -0
- package/src/components/i18n/locales/pt/messages.json +44 -0
- package/src/components/i18n/locales/ro/messages.json +44 -0
- package/src/components/i18n/locales/ru/messages.json +44 -0
- package/src/components/i18n/locales/sd/messages.json +44 -0
- package/src/components/i18n/locales/si/messages.json +44 -0
- package/src/components/i18n/locales/sk/messages.json +44 -0
- package/src/components/i18n/locales/sl/messages.json +44 -0
- package/src/components/i18n/locales/sq/messages.json +44 -0
- package/src/components/i18n/locales/sr/messages.json +44 -0
- package/src/components/i18n/locales/sv/messages.json +44 -0
- package/src/components/i18n/locales/sw/messages.json +44 -0
- package/src/components/i18n/locales/ta/messages.json +44 -0
- package/src/components/i18n/locales/te/messages.json +44 -0
- package/src/components/i18n/locales/th/messages.json +44 -0
- package/src/components/i18n/locales/tr/messages.json +44 -0
- package/src/components/i18n/locales/ug/messages.json +44 -0
- package/src/components/i18n/locales/uk/messages.json +44 -0
- package/src/components/i18n/locales/ur/messages.json +44 -0
- package/src/components/i18n/locales/vi/messages.json +44 -0
- package/src/components/i18n/locales/yi/messages.json +44 -0
- package/src/components/i18n/locales/zh/messages.json +44 -0
- package/src/components/icons/index.ts +242 -0
- package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
- package/src/components/inline-tools/inline-tool-convert.ts +141 -0
- package/src/components/inline-tools/inline-tool-italic.ts +500 -0
- package/src/components/inline-tools/inline-tool-link.ts +539 -0
- package/src/components/modules/api/blocks.ts +363 -0
- package/src/components/modules/api/caret.ts +125 -0
- package/src/components/modules/api/events.ts +51 -0
- package/src/components/modules/api/history.ts +73 -0
- package/src/components/modules/api/i18n.ts +33 -0
- package/src/components/modules/api/index.ts +39 -0
- package/src/components/modules/api/inlineToolbar.ts +33 -0
- package/src/components/modules/api/listeners.ts +56 -0
- package/src/components/modules/api/notifier.ts +46 -0
- package/src/components/modules/api/readonly.ts +39 -0
- package/src/components/modules/api/sanitizer.ts +30 -0
- package/src/components/modules/api/saver.ts +52 -0
- package/src/components/modules/api/selection.ts +48 -0
- package/src/components/modules/api/styles.ts +72 -0
- package/src/components/modules/api/toolbar.ts +79 -0
- package/src/components/modules/api/tools.ts +16 -0
- package/src/components/modules/api/tooltip.ts +67 -0
- package/src/components/modules/api/ui.ts +36 -0
- package/src/components/modules/blockEvents.ts +1375 -0
- package/src/components/modules/blockManager.ts +1348 -0
- package/src/components/modules/blockSelection.ts +708 -0
- package/src/components/modules/caret.ts +853 -0
- package/src/components/modules/crossBlockSelection.ts +329 -0
- package/src/components/modules/dragManager.ts +1141 -0
- package/src/components/modules/history.ts +1098 -0
- package/src/components/modules/i18n.ts +325 -0
- package/src/components/modules/index.ts +139 -0
- package/src/components/modules/modificationsObserver.ts +147 -0
- package/src/components/modules/paste.ts +1092 -0
- package/src/components/modules/readonly.ts +136 -0
- package/src/components/modules/rectangleSelection.ts +668 -0
- package/src/components/modules/renderer.ts +155 -0
- package/src/components/modules/saver.ts +283 -0
- package/src/components/modules/toolbar/blockSettings.ts +776 -0
- package/src/components/modules/toolbar/index.ts +1311 -0
- package/src/components/modules/toolbar/inline.ts +956 -0
- package/src/components/modules/tools.ts +589 -0
- package/src/components/modules/ui.ts +1179 -0
- package/src/components/polyfills.ts +113 -0
- package/src/components/selection.ts +1189 -0
- package/src/components/tools/base.ts +274 -0
- package/src/components/tools/block.ts +291 -0
- package/src/components/tools/collection.ts +67 -0
- package/src/components/tools/factory.ts +85 -0
- package/src/components/tools/inline.ts +71 -0
- package/src/components/tools/tune.ts +33 -0
- package/src/components/ui/toolbox.ts +497 -0
- package/src/components/utils/announcer.ts +205 -0
- package/src/components/utils/api.ts +20 -0
- package/src/components/utils/bem.ts +26 -0
- package/src/components/utils/blocks.ts +284 -0
- package/src/components/utils/caret.ts +1067 -0
- package/src/components/utils/data-model-transform.ts +382 -0
- package/src/components/utils/events.ts +117 -0
- package/src/components/utils/keyboard.ts +60 -0
- package/src/components/utils/listeners.ts +296 -0
- package/src/components/utils/mutations.ts +39 -0
- package/src/components/utils/notifier/draw.ts +190 -0
- package/src/components/utils/notifier/index.ts +66 -0
- package/src/components/utils/notifier/types.ts +1 -0
- package/src/components/utils/notifier.ts +77 -0
- package/src/components/utils/placeholder.ts +140 -0
- package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
- package/src/components/utils/popover/components/hint/hint.ts +46 -0
- package/src/components/utils/popover/components/hint/index.ts +6 -0
- package/src/components/utils/popover/components/popover-header/index.ts +2 -0
- package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
- package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
- package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
- package/src/components/utils/popover/components/popover-item/index.ts +13 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +666 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
- package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
- package/src/components/utils/popover/components/popover-item/popover-item.ts +187 -0
- package/src/components/utils/popover/components/search-input/index.ts +2 -0
- package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
- package/src/components/utils/popover/components/search-input/search-input.ts +181 -0
- package/src/components/utils/popover/components/search-input/search-input.types.ts +30 -0
- package/src/components/utils/popover/index.ts +13 -0
- package/src/components/utils/popover/popover-abstract.ts +448 -0
- package/src/components/utils/popover/popover-desktop.ts +643 -0
- package/src/components/utils/popover/popover-inline.ts +338 -0
- package/src/components/utils/popover/popover-mobile.ts +201 -0
- package/src/components/utils/popover/popover.const.ts +81 -0
- package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
- package/src/components/utils/promise-queue.ts +43 -0
- package/src/components/utils/sanitizer.ts +537 -0
- package/src/components/utils/scroll-locker.ts +87 -0
- package/src/components/utils/shortcut.ts +231 -0
- package/src/components/utils/shortcuts.ts +113 -0
- package/src/components/utils/tools.ts +105 -0
- package/src/components/utils/tooltip.ts +642 -0
- package/src/components/utils/tw.ts +241 -0
- package/src/components/utils.ts +1081 -0
- package/src/env.d.ts +13 -0
- package/src/full.ts +69 -0
- package/src/locales.ts +51 -0
- package/src/stories/Block.stories.ts +498 -0
- package/src/stories/EditorModes.stories.ts +505 -0
- package/src/stories/Header.stories.ts +137 -0
- package/src/stories/InlineToolbar.stories.ts +498 -0
- package/src/stories/List.stories.ts +259 -0
- package/src/stories/Notifier.stories.ts +340 -0
- package/src/stories/Paragraph.stories.ts +112 -0
- package/src/stories/Placeholder.stories.ts +319 -0
- package/src/stories/Popover.stories.ts +844 -0
- package/src/stories/Selection.stories.ts +250 -0
- package/src/stories/StubBlock.stories.ts +156 -0
- package/src/stories/Toolbar.stories.ts +223 -0
- package/src/stories/Toolbox.stories.ts +166 -0
- package/src/stories/Tooltip.stories.ts +198 -0
- package/src/stories/helpers.ts +463 -0
- package/src/styles/main.css +123 -0
- package/src/tools/header/index.ts +570 -0
- package/src/tools/index.ts +38 -0
- package/src/tools/list/index.ts +1803 -0
- package/src/tools/paragraph/index.ts +411 -0
- package/src/tools/stub/index.ts +107 -0
- package/src/types-internal/blok-modules.d.ts +87 -0
- package/src/types-internal/html-janitor.d.ts +28 -0
- package/src/types-internal/module-config.d.ts +11 -0
- package/src/variants/all-locales.ts +155 -0
- package/src/variants/blok-maximum.ts +20 -0
- package/src/variants/blok-minimum.ts +243 -0
- package/types/api/blocks.d.ts +1 -1
- package/types/api/i18n.d.ts +5 -3
- package/types/api/selection.d.ts +6 -0
- package/types/api/styles.d.ts +0 -5
- package/types/configs/blok-config.d.ts +21 -0
- package/types/configs/i18n-config.d.ts +52 -2
- package/types/configs/i18n-dictionary.d.ts +16 -90
- package/types/data-attributes.d.ts +169 -0
- package/types/data-formats/output-data.d.ts +15 -0
- package/types/full.d.ts +80 -0
- package/types/index.d.ts +9 -24
- package/types/locales.d.ts +59 -0
- package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
- package/types/tools/block-tool.d.ts +9 -0
- package/types/tools/list.d.ts +25 -18
- package/types/tools/tool-settings.d.ts +8 -1
- package/types/tools/tool.d.ts +6 -0
- package/types/tools-entry.d.ts +49 -0
- package/types/utils/popover/popover-item.d.ts +0 -5
- package/dist/blok-BwPfU8ro.mjs +0 -21510
- package/dist/blok.umd.js +0 -198
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class Util
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Possible log levels
|
|
9
|
+
*/
|
|
10
|
+
export enum LogLevels {
|
|
11
|
+
VERBOSE = 'VERBOSE',
|
|
12
|
+
INFO = 'INFO',
|
|
13
|
+
WARN = 'WARN',
|
|
14
|
+
ERROR = 'ERROR',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Allow to use global VERSION, that will be overwritten by Webpack
|
|
19
|
+
*/
|
|
20
|
+
declare const VERSION: string;
|
|
21
|
+
|
|
22
|
+
const fallbackBlokVersion = 'dev';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns Blok version injected by bundler or a globally provided fallback.
|
|
26
|
+
*/
|
|
27
|
+
export const getBlokVersion = (): string => {
|
|
28
|
+
if (typeof VERSION !== 'undefined') {
|
|
29
|
+
return VERSION;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const globalVersion = (typeof globalThis === 'object' && globalThis !== null)
|
|
33
|
+
? (globalThis as { VERSION?: unknown }).VERSION
|
|
34
|
+
: undefined;
|
|
35
|
+
|
|
36
|
+
if (typeof globalVersion === 'string' && globalVersion.trim() !== '') {
|
|
37
|
+
return globalVersion;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return fallbackBlokVersion;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Blok utils
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns basic key codes as constants
|
|
50
|
+
* @returns {{}}
|
|
51
|
+
*/
|
|
52
|
+
export const keyCodes = {
|
|
53
|
+
BACKSPACE: 8,
|
|
54
|
+
TAB: 9,
|
|
55
|
+
ENTER: 13,
|
|
56
|
+
SHIFT: 16,
|
|
57
|
+
CTRL: 17,
|
|
58
|
+
ALT: 18,
|
|
59
|
+
ESC: 27,
|
|
60
|
+
SPACE: 32,
|
|
61
|
+
LEFT: 37,
|
|
62
|
+
UP: 38,
|
|
63
|
+
DOWN: 40,
|
|
64
|
+
RIGHT: 39,
|
|
65
|
+
DELETE: 46,
|
|
66
|
+
// Number keys range (0-9)
|
|
67
|
+
NUMBER_KEY_MIN: 47,
|
|
68
|
+
NUMBER_KEY_MAX: 58,
|
|
69
|
+
// Letter keys range (A-Z)
|
|
70
|
+
LETTER_KEY_MIN: 64,
|
|
71
|
+
LETTER_KEY_MAX: 91,
|
|
72
|
+
META: 91,
|
|
73
|
+
// Numpad keys range
|
|
74
|
+
NUMPAD_KEY_MIN: 95,
|
|
75
|
+
NUMPAD_KEY_MAX: 112,
|
|
76
|
+
// Punctuation keys range (;=,-./`)
|
|
77
|
+
PUNCTUATION_KEY_MIN: 185,
|
|
78
|
+
PUNCTUATION_KEY_MAX: 193,
|
|
79
|
+
// Bracket keys range ([\]')
|
|
80
|
+
BRACKET_KEY_MIN: 218,
|
|
81
|
+
BRACKET_KEY_MAX: 223,
|
|
82
|
+
// Processing key input for certain languages (Chinese, Japanese, etc.)
|
|
83
|
+
PROCESSING_KEY: 229,
|
|
84
|
+
SLASH: 191,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Return mouse buttons codes
|
|
89
|
+
*/
|
|
90
|
+
export const mouseButtons = {
|
|
91
|
+
LEFT: 0,
|
|
92
|
+
WHEEL: 1,
|
|
93
|
+
RIGHT: 2,
|
|
94
|
+
BACKWARD: 3,
|
|
95
|
+
FORWARD: 4,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Constants for ID generation
|
|
100
|
+
*/
|
|
101
|
+
const ID_RANDOM_MULTIPLIER = 100_000_000; // 1e8
|
|
102
|
+
const HEXADECIMAL_RADIX = 16;
|
|
103
|
+
|
|
104
|
+
type UniversalScope = {
|
|
105
|
+
window?: Window;
|
|
106
|
+
document?: Document;
|
|
107
|
+
navigator?: Navigator;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const globalScope: UniversalScope | undefined = (() => {
|
|
111
|
+
try {
|
|
112
|
+
return Function('return this')() as UniversalScope;
|
|
113
|
+
} catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
|
|
118
|
+
if (globalScope && typeof globalScope.window === 'undefined') {
|
|
119
|
+
(globalScope as Record<string, unknown>).window = globalScope;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns globally available window object if it exists.
|
|
124
|
+
*/
|
|
125
|
+
const getGlobalWindow = (): Window | undefined => {
|
|
126
|
+
if (globalScope?.window) {
|
|
127
|
+
return globalScope.window;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return undefined;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns globally available navigator object if it exists.
|
|
135
|
+
*/
|
|
136
|
+
const getGlobalNavigator = (): Navigator | undefined => {
|
|
137
|
+
if (globalScope?.navigator) {
|
|
138
|
+
return globalScope.navigator;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const win = getGlobalWindow();
|
|
142
|
+
|
|
143
|
+
return win?.navigator;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Type representing callable console methods
|
|
148
|
+
*/
|
|
149
|
+
type ConsoleMethod = {
|
|
150
|
+
[K in keyof Console]: Console[K] extends (...args: unknown[]) => unknown ? K : never;
|
|
151
|
+
}[keyof Console];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Custom logger
|
|
155
|
+
* @param {boolean} labeled — if true, Blok label is shown
|
|
156
|
+
* @param {string} msg - message
|
|
157
|
+
* @param {string} type - logging type 'log'|'warn'|'error'|'info'
|
|
158
|
+
* @param {*} [args] - argument to log with a message
|
|
159
|
+
* @param {string} style - additional styling to message
|
|
160
|
+
*/
|
|
161
|
+
const _log = (
|
|
162
|
+
labeled: boolean,
|
|
163
|
+
msg: string,
|
|
164
|
+
type: ConsoleMethod = 'log',
|
|
165
|
+
args?: unknown,
|
|
166
|
+
style = 'color: inherit'
|
|
167
|
+
): void => {
|
|
168
|
+
const consoleRef: Console | undefined = typeof console === 'undefined' ? undefined : console;
|
|
169
|
+
|
|
170
|
+
if (!consoleRef || typeof consoleRef[type] !== 'function') {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const isSimpleType = ['info', 'log', 'warn', 'error'].includes(type);
|
|
175
|
+
const argsToPass: unknown[] = [];
|
|
176
|
+
|
|
177
|
+
switch (_log.logLevel) {
|
|
178
|
+
case LogLevels.ERROR:
|
|
179
|
+
if (type !== 'error') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
case LogLevels.WARN:
|
|
185
|
+
if (!['error', 'warn'].includes(type)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
|
|
190
|
+
case LogLevels.INFO:
|
|
191
|
+
if (!isSimpleType || labeled) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (args) {
|
|
198
|
+
argsToPass.push(args);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const blokLabelText = `Blok ${getBlokVersion()}`;
|
|
202
|
+
const blokLabelStyle = `line-height: 1em;
|
|
203
|
+
color: #006FEA;
|
|
204
|
+
display: inline-block;
|
|
205
|
+
font-size: 11px;
|
|
206
|
+
line-height: 1em;
|
|
207
|
+
background-color: #fff;
|
|
208
|
+
padding: 4px 9px;
|
|
209
|
+
border-radius: 30px;
|
|
210
|
+
border: 1px solid rgba(56, 138, 229, 0.16);
|
|
211
|
+
margin: 4px 5px 4px 0;`;
|
|
212
|
+
|
|
213
|
+
const formattedMessage = (() => {
|
|
214
|
+
if (!labeled) {
|
|
215
|
+
return msg;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (isSimpleType) {
|
|
219
|
+
argsToPass.unshift(blokLabelStyle, style);
|
|
220
|
+
|
|
221
|
+
return `%c${blokLabelText}%c ${msg}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return `( ${blokLabelText} )${msg}`;
|
|
225
|
+
})();
|
|
226
|
+
|
|
227
|
+
const callArguments = (() => {
|
|
228
|
+
if (!isSimpleType) {
|
|
229
|
+
return [ formattedMessage ];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (args !== undefined) {
|
|
233
|
+
return [`${formattedMessage} %o`, ...argsToPass];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return [formattedMessage, ...argsToPass];
|
|
237
|
+
})();
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
consoleRef[type](...callArguments);
|
|
241
|
+
} catch (_ignored) {}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Current log level
|
|
246
|
+
*/
|
|
247
|
+
_log.logLevel = LogLevels.VERBOSE;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Set current log level
|
|
251
|
+
* @param {LogLevels} logLevel - log level to set
|
|
252
|
+
*/
|
|
253
|
+
export const setLogLevel = (logLevel: LogLevels): void => {
|
|
254
|
+
_log.logLevel = logLevel;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* _log method proxy without Blok label
|
|
259
|
+
* @param msg - message to log
|
|
260
|
+
* @param type - console method name
|
|
261
|
+
* @param args - optional payload to pass to console
|
|
262
|
+
* @param style - optional css style for the first argument
|
|
263
|
+
*/
|
|
264
|
+
export const log = (
|
|
265
|
+
msg: string,
|
|
266
|
+
type: ConsoleMethod = 'log',
|
|
267
|
+
args?: unknown,
|
|
268
|
+
style?: string
|
|
269
|
+
): void => {
|
|
270
|
+
_log(false, msg, type, args, style);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* _log method proxy with Blok label
|
|
275
|
+
* @param msg - message to log
|
|
276
|
+
* @param type - console method name
|
|
277
|
+
* @param args - optional payload to pass to console
|
|
278
|
+
* @param style - optional css style for the first argument
|
|
279
|
+
*/
|
|
280
|
+
export const logLabeled = (
|
|
281
|
+
msg: string,
|
|
282
|
+
type: ConsoleMethod = 'log',
|
|
283
|
+
args?: unknown,
|
|
284
|
+
style?: string
|
|
285
|
+
): void => {
|
|
286
|
+
_log(true, msg, type, args, style);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Check if passed variable is a function
|
|
291
|
+
* @param {*} fn - function to check
|
|
292
|
+
* @returns {boolean}
|
|
293
|
+
*/
|
|
294
|
+
export const isFunction = (fn: unknown): fn is (...args: unknown[]) => unknown => {
|
|
295
|
+
return typeof fn === 'function';
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Checks if passed argument is a plain object (created by {} or Object constructor)
|
|
300
|
+
* @param {*} v - object to check
|
|
301
|
+
* @returns {boolean}
|
|
302
|
+
*/
|
|
303
|
+
export const isObject = (v: unknown): v is object => {
|
|
304
|
+
if (v === null || typeof v !== 'object') {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
const proto = Object.getPrototypeOf(v);
|
|
308
|
+
|
|
309
|
+
return proto === null || proto === Object.prototype;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Checks if passed argument is a string
|
|
314
|
+
* @param {*} v - variable to check
|
|
315
|
+
* @returns {boolean}
|
|
316
|
+
*/
|
|
317
|
+
export const isString = (v: unknown): v is string => {
|
|
318
|
+
return typeof v === 'string';
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Checks if passed argument is boolean
|
|
323
|
+
* @param {*} v - variable to check
|
|
324
|
+
* @returns {boolean}
|
|
325
|
+
*/
|
|
326
|
+
export const isBoolean = (v: unknown): v is boolean => {
|
|
327
|
+
return typeof v === 'boolean';
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Checks if passed argument is number (including NaN, which has typeof 'number')
|
|
332
|
+
* @param {*} v - variable to check
|
|
333
|
+
* @returns {boolean}
|
|
334
|
+
*/
|
|
335
|
+
export const isNumber = (v: unknown): v is number => {
|
|
336
|
+
return typeof v === 'number';
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Checks if passed argument is undefined
|
|
341
|
+
* @param {*} v - variable to check
|
|
342
|
+
* @returns {boolean}
|
|
343
|
+
*/
|
|
344
|
+
export const isUndefined = function (v: unknown): v is undefined {
|
|
345
|
+
return v === undefined;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Checks if value is empty (null, undefined, empty string, empty array, empty object, empty Map/Set)
|
|
350
|
+
* @param {*} value - value to check
|
|
351
|
+
* @returns {boolean}
|
|
352
|
+
*/
|
|
353
|
+
export const isEmpty = (value: unknown): boolean => {
|
|
354
|
+
if (value === null || value === undefined) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
if (typeof value === 'string' || Array.isArray(value)) {
|
|
358
|
+
return value.length === 0;
|
|
359
|
+
}
|
|
360
|
+
if (value instanceof Map || value instanceof Set) {
|
|
361
|
+
return value.size === 0;
|
|
362
|
+
}
|
|
363
|
+
if (typeof value === 'object') {
|
|
364
|
+
return Object.keys(value).length === 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return false;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Returns true if passed key code is printable (a-Z, 0-9, etc) character.
|
|
372
|
+
* @param {number} keyCode - key code
|
|
373
|
+
* @returns {boolean}
|
|
374
|
+
*/
|
|
375
|
+
export const isPrintableKey = (keyCode: number): boolean => {
|
|
376
|
+
return (keyCode > keyCodes.NUMBER_KEY_MIN && keyCode < keyCodes.NUMBER_KEY_MAX) || // number keys
|
|
377
|
+
keyCode === keyCodes.SPACE || keyCode === keyCodes.ENTER || // Space bar & return key(s)
|
|
378
|
+
keyCode === keyCodes.PROCESSING_KEY || // processing key input for certain languages — Chinese, Japanese, etc.
|
|
379
|
+
(keyCode > keyCodes.LETTER_KEY_MIN && keyCode < keyCodes.LETTER_KEY_MAX) || // letter keys
|
|
380
|
+
(keyCode > keyCodes.NUMPAD_KEY_MIN && keyCode < keyCodes.NUMPAD_KEY_MAX) || // Numpad keys
|
|
381
|
+
(keyCode > keyCodes.PUNCTUATION_KEY_MIN && keyCode < keyCodes.PUNCTUATION_KEY_MAX) || // ;=,-./` (in order)
|
|
382
|
+
(keyCode > keyCodes.BRACKET_KEY_MIN && keyCode < keyCodes.BRACKET_KEY_MAX); // [\]' (in order)
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Make array from array-like collection
|
|
387
|
+
* @param {ArrayLike} collection - collection to convert to array
|
|
388
|
+
* @returns {Array}
|
|
389
|
+
*/
|
|
390
|
+
export const array = (collection: ArrayLike<any>): any[] => {
|
|
391
|
+
return Array.from(collection);
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Delays method execution
|
|
396
|
+
* @param {Function} method - method to execute
|
|
397
|
+
* @param {number} timeout - timeout in ms
|
|
398
|
+
*/
|
|
399
|
+
export const delay = (method: (...args: unknown[]) => unknown, timeout: number) => {
|
|
400
|
+
return function (this: unknown, ...args: unknown[]): void {
|
|
401
|
+
setTimeout(() => method.apply(this, args), timeout);
|
|
402
|
+
};
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get file extension
|
|
407
|
+
* @param {File} file - file
|
|
408
|
+
* @returns {string}
|
|
409
|
+
*/
|
|
410
|
+
export const getFileExtension = (file: File): string => {
|
|
411
|
+
return file.name.split('.').pop() ?? '';
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Check if string is MIME type
|
|
416
|
+
* @param {string} type - string to check
|
|
417
|
+
* @returns {boolean}
|
|
418
|
+
*/
|
|
419
|
+
export const isValidMimeType = (type: string): boolean => {
|
|
420
|
+
return /^[-\w]+\/([-+\w]+|\*)$/.test(type);
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Debouncing method
|
|
425
|
+
* Call method after passed time
|
|
426
|
+
*
|
|
427
|
+
* Note that this method returns Function and declared variable need to be called
|
|
428
|
+
* @param {Function} func - function that we're throttling
|
|
429
|
+
* @param {number} wait - time in milliseconds
|
|
430
|
+
* @param {boolean} immediate - call now
|
|
431
|
+
* @returns {Function}
|
|
432
|
+
*/
|
|
433
|
+
export const debounce = (func: (...args: unknown[]) => void, wait?: number, immediate?: boolean): (...args: unknown[]) => void => {
|
|
434
|
+
const state = {
|
|
435
|
+
timeoutId: null as ReturnType<typeof setTimeout> | null,
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
return function (this: unknown, ...args: unknown[]): void {
|
|
439
|
+
const later = (): void => {
|
|
440
|
+
state.timeoutId = null;
|
|
441
|
+
if (immediate !== true) {
|
|
442
|
+
func.apply(this, args);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const callNow = immediate === true && state.timeoutId === null;
|
|
447
|
+
|
|
448
|
+
if (state.timeoutId !== null) {
|
|
449
|
+
clearTimeout(state.timeoutId);
|
|
450
|
+
}
|
|
451
|
+
state.timeoutId = setTimeout(later, wait);
|
|
452
|
+
if (callNow) {
|
|
453
|
+
func.apply(this, args);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Returns a function, that, when invoked, will only be triggered at most once during a given window of time.
|
|
460
|
+
* @param func - function to throttle
|
|
461
|
+
* @param wait - function will be called only once for that period
|
|
462
|
+
* @param options - Normally, the throttled function will run as much as it can
|
|
463
|
+
* without ever going more than once per `wait` duration;
|
|
464
|
+
* but if you'd like to disable the execution on the leading edge, pass
|
|
465
|
+
* `{leading: false}`. To disable execution on the trailing edge, ditto.
|
|
466
|
+
*/
|
|
467
|
+
export const throttle = (
|
|
468
|
+
func: (...args: unknown[]) => unknown,
|
|
469
|
+
wait: number,
|
|
470
|
+
options?: {leading?: boolean; trailing?: boolean}
|
|
471
|
+
): ((...args: unknown[]) => unknown) => {
|
|
472
|
+
const leading = options?.leading !== false;
|
|
473
|
+
const trailing = options?.trailing !== false;
|
|
474
|
+
|
|
475
|
+
const state = {
|
|
476
|
+
lastCallTime: undefined as number | undefined,
|
|
477
|
+
lastInvokeTime: 0,
|
|
478
|
+
timerId: undefined as ReturnType<typeof setTimeout> | undefined,
|
|
479
|
+
lastArgs: undefined as unknown[] | undefined,
|
|
480
|
+
lastThis: undefined as unknown,
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const invokeFunc = (time: number): unknown => {
|
|
484
|
+
state.lastInvokeTime = time;
|
|
485
|
+
const args = state.lastArgs;
|
|
486
|
+
const thisArg = state.lastThis;
|
|
487
|
+
|
|
488
|
+
state.lastArgs = undefined;
|
|
489
|
+
state.lastThis = undefined;
|
|
490
|
+
|
|
491
|
+
return func.apply(thisArg, args ?? []);
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const remainingWait = (time: number): number => {
|
|
495
|
+
const timeSinceLastCall = time - (state.lastCallTime ?? 0);
|
|
496
|
+
const timeSinceLastInvoke = time - state.lastInvokeTime;
|
|
497
|
+
const timeWaiting = wait - timeSinceLastCall;
|
|
498
|
+
|
|
499
|
+
return trailing ? Math.min(timeWaiting, wait - timeSinceLastInvoke) : timeWaiting;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const shouldInvoke = (time: number): boolean => {
|
|
503
|
+
const timeSinceLastCall = time - (state.lastCallTime ?? 0);
|
|
504
|
+
const timeSinceLastInvoke = time - state.lastInvokeTime;
|
|
505
|
+
|
|
506
|
+
return (
|
|
507
|
+
state.lastCallTime === undefined ||
|
|
508
|
+
timeSinceLastCall >= wait ||
|
|
509
|
+
timeSinceLastCall < 0 ||
|
|
510
|
+
timeSinceLastInvoke >= wait
|
|
511
|
+
);
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const timerExpired = (): void => {
|
|
515
|
+
const time = Date.now();
|
|
516
|
+
|
|
517
|
+
if (!shouldInvoke(time)) {
|
|
518
|
+
state.timerId = setTimeout(timerExpired, remainingWait(time));
|
|
519
|
+
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
state.timerId = undefined;
|
|
524
|
+
const shouldInvokeTrailing = trailing && state.lastArgs !== undefined;
|
|
525
|
+
|
|
526
|
+
if (shouldInvokeTrailing) {
|
|
527
|
+
invokeFunc(time);
|
|
528
|
+
}
|
|
529
|
+
state.lastArgs = undefined;
|
|
530
|
+
state.lastThis = undefined;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const throttled = function (this: unknown, ...args: unknown[]): unknown {
|
|
534
|
+
const time = Date.now();
|
|
535
|
+
const isInvoking = shouldInvoke(time);
|
|
536
|
+
|
|
537
|
+
state.lastArgs = args;
|
|
538
|
+
state.lastThis = this;
|
|
539
|
+
state.lastCallTime = time;
|
|
540
|
+
|
|
541
|
+
const canStartTimer = isInvoking && state.timerId === undefined;
|
|
542
|
+
|
|
543
|
+
if (!canStartTimer) {
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
state.lastInvokeTime = time;
|
|
548
|
+
state.timerId = setTimeout(timerExpired, wait);
|
|
549
|
+
|
|
550
|
+
return leading ? invokeFunc(time) : undefined;
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
return throttled;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Returns object with os name as key and boolean as value. Shows current user OS
|
|
558
|
+
*/
|
|
559
|
+
export const getUserOS = (): {[key: string]: boolean} => {
|
|
560
|
+
const OS: {[key: string]: boolean} = {
|
|
561
|
+
win: false,
|
|
562
|
+
mac: false,
|
|
563
|
+
x11: false,
|
|
564
|
+
linux: false,
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
const navigatorRef = getGlobalNavigator();
|
|
568
|
+
const userAgent = navigatorRef?.userAgent?.toLowerCase() ?? '';
|
|
569
|
+
const userOS = userAgent ? Object.keys(OS).find((os: string) => userAgent.indexOf(os) !== -1) : undefined;
|
|
570
|
+
|
|
571
|
+
if (userOS !== undefined) {
|
|
572
|
+
OS[userOS] = true;
|
|
573
|
+
|
|
574
|
+
return OS;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return OS;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Capitalizes first letter of the string
|
|
582
|
+
* @param {string} text - text to capitalize
|
|
583
|
+
* @returns {string}
|
|
584
|
+
*/
|
|
585
|
+
export const capitalize = (text: string): string => {
|
|
586
|
+
if (!text) {
|
|
587
|
+
return text;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return text.slice(0, 1).toUpperCase() + text.slice(1);
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Deep merge two objects recursively. Arrays are overwritten (not merged).
|
|
595
|
+
* Undefined values in source are skipped (matching lodash.mergeWith behavior).
|
|
596
|
+
* @param target - target object
|
|
597
|
+
* @param source - source object
|
|
598
|
+
* @returns new merged object
|
|
599
|
+
*/
|
|
600
|
+
const deepMergeTwo = (target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> => {
|
|
601
|
+
const result = { ...target };
|
|
602
|
+
|
|
603
|
+
Object.keys(source).forEach((key) => {
|
|
604
|
+
const targetValue = result[key];
|
|
605
|
+
const sourceValue = source[key];
|
|
606
|
+
|
|
607
|
+
if (sourceValue === undefined) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const shouldRecurseMerge = isObject(sourceValue) && isObject(targetValue) && !Array.isArray(sourceValue);
|
|
612
|
+
|
|
613
|
+
if (shouldRecurseMerge) {
|
|
614
|
+
result[key] = deepMergeTwo(
|
|
615
|
+
targetValue as Record<string, unknown>,
|
|
616
|
+
sourceValue as Record<string, unknown>
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
result[key] = sourceValue;
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
return result;
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Deep merge objects. Arrays are overwritten (not merged).
|
|
630
|
+
* Mutates and returns the target object for compatibility with lodash.mergeWith.
|
|
631
|
+
* @param target - target object to merge into
|
|
632
|
+
* @param sources - source objects to merge from
|
|
633
|
+
* @returns merged object (same reference as target)
|
|
634
|
+
*/
|
|
635
|
+
export const deepMerge = <T extends object> (target: T, ...sources: Partial<T>[]): T => {
|
|
636
|
+
if (!isObject(target) || sources.length === 0) {
|
|
637
|
+
return target;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const merged = sources.reduce((acc, source) => {
|
|
641
|
+
if (!isObject(source)) {
|
|
642
|
+
return acc;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return deepMergeTwo(acc as Record<string, unknown>, source as Record<string, unknown>);
|
|
646
|
+
}, target as Record<string, unknown>);
|
|
647
|
+
|
|
648
|
+
Object.assign(target, merged);
|
|
649
|
+
|
|
650
|
+
return target;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Make shortcut command more human-readable
|
|
655
|
+
* @param {string} shortcut — string like 'CMD+B'
|
|
656
|
+
*/
|
|
657
|
+
export const beautifyShortcut = (shortcut: string): string => {
|
|
658
|
+
const OS = getUserOS();
|
|
659
|
+
const normalizedShortcut = shortcut
|
|
660
|
+
.replace(/shift/gi, '⇧')
|
|
661
|
+
.replace(/backspace/gi, '⌫')
|
|
662
|
+
.replace(/enter/gi, '⏎')
|
|
663
|
+
.replace(/up/gi, '↑')
|
|
664
|
+
.replace(/left/gi, '→')
|
|
665
|
+
.replace(/down/gi, '↓')
|
|
666
|
+
.replace(/right/gi, '←')
|
|
667
|
+
.replace(/escape/gi, '⎋')
|
|
668
|
+
.replace(/insert/gi, 'Ins')
|
|
669
|
+
.replace(/delete/gi, '␡')
|
|
670
|
+
.replace(/\+/gi, ' + ');
|
|
671
|
+
|
|
672
|
+
if (OS.mac) {
|
|
673
|
+
return normalizedShortcut.replace(/ctrl|cmd/gi, '⌘').replace(/alt/gi, '⌥');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return normalizedShortcut.replace(/cmd/gi, 'Ctrl').replace(/windows/gi, 'WIN');
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Returns valid URL. If it is going outside and valid, it returns itself
|
|
681
|
+
* If url has `one slash`, then it concatenates with window location origin
|
|
682
|
+
* or when url has `two lack` it appends only protocol
|
|
683
|
+
* @param {string} url - url to prettify
|
|
684
|
+
*/
|
|
685
|
+
export const getValidUrl = (url: string): string => {
|
|
686
|
+
try {
|
|
687
|
+
const urlObject = new URL(url);
|
|
688
|
+
|
|
689
|
+
return urlObject.href;
|
|
690
|
+
} catch (_e) {
|
|
691
|
+
// do nothing but handle below
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const win = getGlobalWindow();
|
|
695
|
+
|
|
696
|
+
if (url.substring(0, 2) === '//') {
|
|
697
|
+
return win ? `${win.location.protocol}${url}` : url;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return win ? `${win.location.origin}${url}` : url;
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Create a block id
|
|
705
|
+
* @returns {string}
|
|
706
|
+
*/
|
|
707
|
+
export const generateBlockId = (): string => {
|
|
708
|
+
const idLen = 10;
|
|
709
|
+
|
|
710
|
+
return nanoid(idLen);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Opens new Tab with passed URL
|
|
715
|
+
* @param {string} url - URL address to redirect
|
|
716
|
+
*/
|
|
717
|
+
export const openTab = (url: string): void => {
|
|
718
|
+
const win = getGlobalWindow();
|
|
719
|
+
|
|
720
|
+
if (!win) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
win.open(url, '_blank');
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Returns random generated identifier
|
|
729
|
+
* @param {string} prefix - identifier prefix
|
|
730
|
+
* @returns {string}
|
|
731
|
+
*/
|
|
732
|
+
export const generateId = (prefix = ''): string => {
|
|
733
|
+
return `${prefix}${(Math.floor(Math.random() * ID_RANDOM_MULTIPLIER)).toString(HEXADECIMAL_RADIX)}`;
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
type CacheableAccessor<Value> = {
|
|
738
|
+
get?: () => Value;
|
|
739
|
+
set?: (value: Value) => void;
|
|
740
|
+
init?: (value: Value) => Value;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
type Stage3DecoratorContext = {
|
|
744
|
+
kind: 'method' | 'getter' | 'setter' | 'accessor';
|
|
745
|
+
name: string | symbol;
|
|
746
|
+
static?: boolean;
|
|
747
|
+
private?: boolean;
|
|
748
|
+
access?: {
|
|
749
|
+
get?: () => unknown;
|
|
750
|
+
set?: (value: unknown) => void;
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
type CacheableDecorator = {
|
|
755
|
+
<Member>(
|
|
756
|
+
target: object,
|
|
757
|
+
propertyKey: string | symbol,
|
|
758
|
+
descriptor?: TypedPropertyDescriptor<Member>
|
|
759
|
+
): TypedPropertyDescriptor<Member> | void;
|
|
760
|
+
<Value = unknown, Arguments extends unknown[] = unknown[]>(
|
|
761
|
+
value: ((...args: Arguments) => Value) | CacheableAccessor<Value>,
|
|
762
|
+
context: Stage3DecoratorContext
|
|
763
|
+
):
|
|
764
|
+
| ((...args: Arguments) => Value)
|
|
765
|
+
| CacheableAccessor<Value>;
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const ensureCacheValue = <Value>(
|
|
769
|
+
holder: object,
|
|
770
|
+
cacheKey: string | symbol,
|
|
771
|
+
compute: () => Value
|
|
772
|
+
): Value => {
|
|
773
|
+
if (!Reflect.has(holder, cacheKey)) {
|
|
774
|
+
Object.defineProperty(holder, cacheKey, {
|
|
775
|
+
configurable: true,
|
|
776
|
+
writable: true,
|
|
777
|
+
value: compute(),
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return Reflect.get(holder, cacheKey) as Value;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const clearCacheValue = (holder: object, cacheKey: string | symbol): void => {
|
|
785
|
+
if (Reflect.has(holder, cacheKey)) {
|
|
786
|
+
Reflect.deleteProperty(holder, cacheKey);
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const isStage3DecoratorContext = (context: unknown): context is Stage3DecoratorContext => {
|
|
791
|
+
if (typeof context !== 'object' || context === null) {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return 'kind' in context && 'name' in context;
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
const buildLegacyCacheableDescriptor = (
|
|
799
|
+
target: Record<PropertyKey, unknown>,
|
|
800
|
+
propertyKey: string | symbol,
|
|
801
|
+
descriptor?: TypedPropertyDescriptor<unknown>
|
|
802
|
+
): TypedPropertyDescriptor<unknown> => {
|
|
803
|
+
const baseDescriptor =
|
|
804
|
+
descriptor ??
|
|
805
|
+
Object.getOwnPropertyDescriptor(target, propertyKey) ??
|
|
806
|
+
(typeof target === 'function'
|
|
807
|
+
? Object.getOwnPropertyDescriptor((target as unknown as { prototype?: object }).prototype ?? {}, propertyKey)
|
|
808
|
+
: undefined) ??
|
|
809
|
+
{
|
|
810
|
+
configurable: true,
|
|
811
|
+
enumerable: false,
|
|
812
|
+
writable: true,
|
|
813
|
+
value: Reflect.get(target, propertyKey),
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
const descriptorRef = { ...baseDescriptor } as TypedPropertyDescriptor<unknown>;
|
|
817
|
+
const cacheKey: string | symbol = typeof propertyKey === 'symbol' ? propertyKey : `#${propertyKey}Cache`;
|
|
818
|
+
const hasMethodValue = descriptorRef.value !== undefined && typeof descriptorRef.value === 'function';
|
|
819
|
+
const shouldWrapGetter = !hasMethodValue && descriptorRef.get !== undefined;
|
|
820
|
+
const shouldWrapSetter = shouldWrapGetter && descriptorRef.set !== undefined;
|
|
821
|
+
|
|
822
|
+
if (hasMethodValue) {
|
|
823
|
+
const originalMethod = descriptorRef.value as (...methodArgs: unknown[]) => unknown;
|
|
824
|
+
|
|
825
|
+
descriptorRef.value = function (this: object, ...methodArgs: unknown[]): unknown {
|
|
826
|
+
return ensureCacheValue(this, cacheKey, () => originalMethod.apply(this, methodArgs));
|
|
827
|
+
} as typeof originalMethod;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (shouldWrapGetter && descriptorRef.get !== undefined) {
|
|
831
|
+
const originalGetter = descriptorRef.get as () => unknown;
|
|
832
|
+
|
|
833
|
+
descriptorRef.get = function (this: object): unknown {
|
|
834
|
+
return ensureCacheValue(this, cacheKey, () => originalGetter.call(this));
|
|
835
|
+
} as typeof originalGetter;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (shouldWrapSetter && descriptorRef.set !== undefined) {
|
|
839
|
+
const originalSetter = descriptorRef.set;
|
|
840
|
+
|
|
841
|
+
descriptorRef.set = function (this: object, newValue: unknown): void {
|
|
842
|
+
clearCacheValue(this, cacheKey);
|
|
843
|
+
originalSetter.call(this, newValue);
|
|
844
|
+
} as typeof originalSetter;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (!descriptor) {
|
|
848
|
+
return descriptorRef;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
Object.keys(descriptor).forEach(propertyName => {
|
|
852
|
+
if (!(propertyName in descriptorRef)) {
|
|
853
|
+
Reflect.deleteProperty(descriptor, propertyName as keyof PropertyDescriptor);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
Object.assign(descriptor, descriptorRef);
|
|
858
|
+
|
|
859
|
+
return descriptor;
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
const applyStage3CacheableDecorator = (
|
|
863
|
+
value: ((...methodArgs: unknown[]) => unknown) | CacheableAccessor<unknown>,
|
|
864
|
+
context: Stage3DecoratorContext
|
|
865
|
+
): unknown => {
|
|
866
|
+
const cacheKey = Symbol(
|
|
867
|
+
typeof context.name === 'symbol'
|
|
868
|
+
? `cache:${context.name.description ?? 'symbol'}`
|
|
869
|
+
: `cache:${context.name}`
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
if (context.kind === 'method' && typeof value === 'function') {
|
|
873
|
+
const originalMethod = value as (...methodArgs: unknown[]) => unknown;
|
|
874
|
+
|
|
875
|
+
return function (this: object, ...methodArgs: unknown[]): unknown {
|
|
876
|
+
return ensureCacheValue(this, cacheKey, () => originalMethod.apply(this, methodArgs));
|
|
877
|
+
} as typeof originalMethod;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (context.kind === 'getter' && typeof value === 'function') {
|
|
881
|
+
const originalGetter = value as () => unknown;
|
|
882
|
+
|
|
883
|
+
return function (this: object): unknown {
|
|
884
|
+
return ensureCacheValue(this, cacheKey, () => originalGetter.call(this));
|
|
885
|
+
} as typeof originalGetter;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (context.kind === 'accessor' && typeof value === 'object' && value !== null) {
|
|
889
|
+
const accessor = value as CacheableAccessor<unknown>;
|
|
890
|
+
const fallbackGetter = accessor.get ?? context.access?.get;
|
|
891
|
+
const fallbackSetter = accessor.set ?? context.access?.set;
|
|
892
|
+
|
|
893
|
+
return {
|
|
894
|
+
get(this: object): unknown {
|
|
895
|
+
return fallbackGetter
|
|
896
|
+
? ensureCacheValue(this, cacheKey, () => fallbackGetter.call(this))
|
|
897
|
+
: undefined;
|
|
898
|
+
},
|
|
899
|
+
set(this: object, newValue: unknown): void {
|
|
900
|
+
clearCacheValue(this, cacheKey);
|
|
901
|
+
fallbackSetter?.call(this, newValue);
|
|
902
|
+
},
|
|
903
|
+
init(initialValue: unknown): unknown {
|
|
904
|
+
return accessor.init ? accessor.init(initialValue) : initialValue;
|
|
905
|
+
},
|
|
906
|
+
} satisfies CacheableAccessor<unknown>;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return value;
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Decorator which provides ability to cache method or accessor result.
|
|
914
|
+
* Supports both legacy and TC39 stage 3 decorator semantics.
|
|
915
|
+
* @param args - decorator arguments (legacy: target, propertyKey, descriptor. Stage 3: value, context)
|
|
916
|
+
*/
|
|
917
|
+
const cacheableImpl = (...args: unknown[]): unknown => {
|
|
918
|
+
if (args.length === 2 && isStage3DecoratorContext(args[1])) {
|
|
919
|
+
const [value, context] = args as [
|
|
920
|
+
((...methodArgs: unknown[]) => unknown) | CacheableAccessor<unknown>,
|
|
921
|
+
Stage3DecoratorContext
|
|
922
|
+
];
|
|
923
|
+
|
|
924
|
+
return applyStage3CacheableDecorator(value, context);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const [target, propertyKey, descriptor] = args as [
|
|
928
|
+
Record<PropertyKey, unknown>,
|
|
929
|
+
string | symbol,
|
|
930
|
+
TypedPropertyDescriptor<unknown> | undefined
|
|
931
|
+
];
|
|
932
|
+
|
|
933
|
+
return buildLegacyCacheableDescriptor(target, propertyKey, descriptor);
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
export const cacheable = cacheableImpl as CacheableDecorator;
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* All screens below this width will be treated as mobile;
|
|
940
|
+
*/
|
|
941
|
+
export const mobileScreenBreakpoint = 650;
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* True if screen has mobile size
|
|
945
|
+
*/
|
|
946
|
+
export const isMobileScreen = (): boolean => {
|
|
947
|
+
const win = getGlobalWindow();
|
|
948
|
+
|
|
949
|
+
if (!win || typeof win.matchMedia !== 'function') {
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
return win.matchMedia(`(max-width: ${mobileScreenBreakpoint}px)`).matches;
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* True if current device runs iOS
|
|
958
|
+
*/
|
|
959
|
+
export const isIosDevice = (() => {
|
|
960
|
+
const navigatorRef = getGlobalNavigator();
|
|
961
|
+
|
|
962
|
+
if (!navigatorRef) {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const userAgent = navigatorRef.userAgent || '';
|
|
967
|
+
|
|
968
|
+
// Use modern User-Agent Client Hints API if available
|
|
969
|
+
const userAgentData = (navigatorRef as Navigator & { userAgentData?: { platform?: string } }).userAgentData;
|
|
970
|
+
const platform = userAgentData?.platform;
|
|
971
|
+
|
|
972
|
+
// Check userAgent string first (most reliable method)
|
|
973
|
+
if (/iP(ad|hone|od)/.test(userAgent)) {
|
|
974
|
+
return true;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Check platform from User-Agent Client Hints API if available
|
|
978
|
+
if (platform !== undefined && platform !== '' && /iP(ad|hone|od)/.test(platform)) {
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Check for iPad on iOS 13+ (reports as MacIntel with touch support)
|
|
983
|
+
// Only access deprecated platform property when necessary
|
|
984
|
+
const hasTouchSupport = (navigatorRef.maxTouchPoints ?? 0) > 1;
|
|
985
|
+
const getLegacyPlatform = (): string | undefined =>
|
|
986
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- Fallback for older browsers that don't support User-Agent Client Hints
|
|
987
|
+
(navigatorRef as Navigator & { platform?: string })['platform'];
|
|
988
|
+
const platformHint = platform !== undefined && platform !== '' ? platform : undefined;
|
|
989
|
+
const platformValue = hasTouchSupport ? platformHint ?? getLegacyPlatform() : undefined;
|
|
990
|
+
|
|
991
|
+
if (platformValue === 'MacIntel') {
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return false;
|
|
996
|
+
})();
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Compares two arrays deeply for equality
|
|
1000
|
+
* @param arr1 - first array
|
|
1001
|
+
* @param arr2 - second array
|
|
1002
|
+
* @returns {boolean} true if arrays are equal
|
|
1003
|
+
*/
|
|
1004
|
+
const arraysEqual = (arr1: unknown[], arr2: unknown[]): boolean => {
|
|
1005
|
+
if (arr1.length !== arr2.length) {
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return arr1.every((item, index) => equals(item, arr2[index]));
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Compares two values deeply for equality
|
|
1014
|
+
* @param var1 - value to compare
|
|
1015
|
+
* @param var2 - value to compare with
|
|
1016
|
+
* @returns {boolean} true if they are equal
|
|
1017
|
+
*/
|
|
1018
|
+
export const equals = (var1: unknown, var2: unknown): boolean => {
|
|
1019
|
+
if (var1 === var2) {
|
|
1020
|
+
return true;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (var1 === null || var2 === null || typeof var1 !== 'object' || typeof var2 !== 'object') {
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (Array.isArray(var1) !== Array.isArray(var2)) {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (Array.isArray(var1) && Array.isArray(var2)) {
|
|
1032
|
+
return arraysEqual(var1, var2);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const keys1 = Object.keys(var1);
|
|
1036
|
+
const keys2 = Object.keys(var2);
|
|
1037
|
+
|
|
1038
|
+
if (keys1.length !== keys2.length) {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
return keys1.every((key) =>
|
|
1043
|
+
Object.prototype.hasOwnProperty.call(var2, key) &&
|
|
1044
|
+
equals((var1 as Record<string, unknown>)[key], (var2 as Record<string, unknown>)[key])
|
|
1045
|
+
);
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Strips fake background wrapper elements from HTML content.
|
|
1050
|
+
* These elements are used by the inline toolbar for visual selection highlighting
|
|
1051
|
+
* and should not be persisted in saved data.
|
|
1052
|
+
* @param html - HTML content that may contain fake background elements
|
|
1053
|
+
* @returns HTML content with fake background wrappers removed but their content preserved
|
|
1054
|
+
*/
|
|
1055
|
+
export const stripFakeBackgroundElements = (html: string): string => {
|
|
1056
|
+
if (!html || !html.includes('data-blok-fake-background')) {
|
|
1057
|
+
return html;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const tempDiv = document.createElement('div');
|
|
1061
|
+
|
|
1062
|
+
tempDiv.innerHTML = html;
|
|
1063
|
+
|
|
1064
|
+
const fakeBackgrounds = tempDiv.querySelectorAll('[data-blok-fake-background="true"]');
|
|
1065
|
+
|
|
1066
|
+
fakeBackgrounds.forEach((element) => {
|
|
1067
|
+
const parent = element.parentNode;
|
|
1068
|
+
|
|
1069
|
+
if (!parent) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
while (element.firstChild) {
|
|
1074
|
+
parent.insertBefore(element.firstChild, element);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
parent.removeChild(element);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
return tempDiv.innerHTML;
|
|
1081
|
+
};
|