@jackuait/blok 0.7.0-beta.1 → 0.7.0-beta.3
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 +20 -4
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-ob9Fwr1L.mjs → blok-BlyYiZTm.mjs} +1840 -1654
- package/dist/chunks/{inline-tool-convert-CvFW2iie.mjs → constants-DEy4jBO5.mjs} +316 -297
- package/dist/chunks/{i18next-loader-Bu3vFvye.mjs → i18next-loader-Cfbv-x6v.mjs} +1 -1
- package/dist/chunks/index-Cu1w-sLZ.mjs +130 -0
- package/dist/chunks/{messages-D2NOpHn9.mjs → messages-0Pxnqd4N.mjs} +7 -0
- package/dist/chunks/{messages-GSByFygY.mjs → messages-0ZXYUq7S.mjs} +7 -0
- package/dist/{messages-BUl_Rcnj.mjs → chunks/messages-2OD2uUDS.mjs} +9 -2
- package/dist/{messages-CgTq3QhU.mjs → chunks/messages-7cEMfYzh.mjs} +7 -0
- package/dist/{messages-DlJbPF2T.mjs → chunks/messages-8mwfda1Q.mjs} +7 -0
- package/dist/{messages-D9ndgBnU.mjs → chunks/messages-B-FqWsBM.mjs} +7 -0
- package/dist/{messages-B217znr-.mjs → chunks/messages-B1jzqWiQ.mjs} +7 -0
- package/dist/{messages-BcpCubnC.mjs → chunks/messages-B5wk4Ezz.mjs} +7 -0
- package/dist/chunks/{messages-DRXWF0PV.mjs → messages-BAZ5Ld8x.mjs} +7 -0
- package/dist/{messages-CRJ_mchV.mjs → chunks/messages-BBhGp198.mjs} +7 -0
- package/dist/chunks/{messages-yHcs38yI.mjs → messages-BC9IjIb7.mjs} +7 -0
- package/dist/chunks/{messages-Cr94GzbX.mjs → messages-BFEmpeV-.mjs} +7 -0
- package/dist/chunks/{messages-ucTVgS5G.mjs → messages-BGqzTZy0.mjs} +7 -0
- package/dist/{messages-begYOTgC.mjs → chunks/messages-BICs1abK.mjs} +7 -0
- package/dist/chunks/{messages-DVQvl8Qj.mjs → messages-BJX6rOnd.mjs} +7 -0
- package/dist/chunks/{messages-Chb7k3Rg.mjs → messages-BL2bXRhN.mjs} +7 -0
- package/dist/{messages-Phkd7XmE.mjs → chunks/messages-BMs5qdlH.mjs} +7 -0
- package/dist/chunks/{messages-Cjjo7yHR.mjs → messages-BRsjUNwB.mjs} +7 -0
- package/dist/chunks/{messages-D4qqwVgQ.mjs → messages-BSqV8OUR.mjs} +7 -0
- package/dist/chunks/{messages-DviiFSv2.mjs → messages-BTqu3DfG.mjs} +7 -0
- package/dist/chunks/{messages-0AbcLMLm.mjs → messages-BXnDEsur.mjs} +7 -0
- package/dist/{messages-CmR9ftc_.mjs → chunks/messages-BYcre4-6.mjs} +7 -0
- package/dist/{messages-wmi-iFkH.mjs → chunks/messages-BZ9LRJf-.mjs} +7 -0
- package/dist/chunks/{messages-D00x4S8o.mjs → messages-BgypBy7y.mjs} +7 -0
- package/dist/{messages-96kNZDll.mjs → chunks/messages-BsuGf70G.mjs} +7 -0
- package/dist/chunks/{messages-v3GipbFl.mjs → messages-BwaoF4lQ.mjs} +7 -0
- package/dist/{messages-DDTQgImT.mjs → chunks/messages-C1l8_7-y.mjs} +7 -0
- package/dist/{messages-B1FZ8lxU.mjs → chunks/messages-C5NA_r9v.mjs} +7 -0
- package/dist/{messages-Cs8zmZ3L.mjs → chunks/messages-C6zgZ5pA.mjs} +7 -0
- package/dist/chunks/{messages-ZjUAIWb1.mjs → messages-CAo5ghFI.mjs} +7 -0
- package/dist/{messages-D5S1Dnpm.mjs → chunks/messages-CH9qlJ9I.mjs} +7 -0
- package/dist/{messages-D7u2bmP2.mjs → chunks/messages-CI0HqAeS.mjs} +7 -0
- package/dist/{messages-DH_jBeED.mjs → chunks/messages-CJJtms9k.mjs} +7 -0
- package/dist/{messages-CDBLbUOQ.mjs → chunks/messages-CM2hJqk6.mjs} +7 -0
- package/dist/chunks/{messages-8IPXkrDl.mjs → messages-CRMiDPIQ.mjs} +7 -0
- package/dist/chunks/{messages-Dzzn6XoD.mjs → messages-CWsZuBj1.mjs} +7 -0
- package/dist/chunks/{messages-CW4c4cRk.mjs → messages-C_gLHo6A.mjs} +7 -0
- package/dist/{messages-CH4hrauY.mjs → chunks/messages-Cbu-NUDn.mjs} +7 -0
- package/dist/{messages-RonBBCnh.mjs → chunks/messages-Cjb_MCeh.mjs} +7 -0
- package/dist/chunks/{messages-BJ6zrz2j.mjs → messages-ClXYO9Wn.mjs} +7 -0
- package/dist/chunks/{messages-CrCYPCk3.mjs → messages-CsH20vhP.mjs} +7 -0
- package/dist/{messages-CzK0LEhb.mjs → chunks/messages-CsjAGhzA.mjs} +7 -0
- package/dist/chunks/{messages-BZlmVRwn.mjs → messages-Cx7VKFOE.mjs} +7 -0
- package/dist/chunks/{messages-0E0AkrNu.mjs → messages-D3JeBwxo.mjs} +7 -0
- package/dist/chunks/{messages-D85FqxgY.mjs → messages-D541fieJ.mjs} +7 -0
- package/dist/{messages-4v4MuVEc.mjs → chunks/messages-D7XPdglc.mjs} +7 -0
- package/dist/{messages-BC8IN4Bf.mjs → chunks/messages-DBhylfvt.mjs} +7 -0
- package/dist/chunks/{messages-B8WNljW3.mjs → messages-DCA120lW.mjs} +7 -0
- package/dist/chunks/{messages-Cr49Nt3U.mjs → messages-DCf_xZMN.mjs} +7 -0
- package/dist/chunks/{messages-VDriF5Qy.mjs → messages-DDwXKCpe.mjs} +7 -0
- package/dist/{messages-b1EdvUm0.mjs → chunks/messages-DNKDlxcy.mjs} +7 -0
- package/dist/{messages-L_kl2Qvh.mjs → chunks/messages-DPvEjrGK.mjs} +7 -0
- package/dist/chunks/{messages-62v-CLC-.mjs → messages-DQ-AkNxA.mjs} +7 -0
- package/dist/chunks/{messages-DdK-nFGm.mjs → messages-DVuvkNap.mjs} +7 -0
- package/dist/{messages-DnVlmiNT.mjs → chunks/messages-DaglyqUT.mjs} +7 -0
- package/dist/{messages-Bm-E4iRC.mjs → chunks/messages-Di0bAfwA.mjs} +7 -0
- package/dist/{messages-D1mn7Zd5.mjs → chunks/messages-DuLct0Yr.mjs} +7 -0
- package/dist/{messages-8DeO60Oo.mjs → chunks/messages-DzEYYhZh.mjs} +7 -0
- package/dist/chunks/{messages-CfiyT2Wi.mjs → messages-DznNGAB2.mjs} +7 -0
- package/dist/chunks/{messages-DXktiao_.mjs → messages-DzoIzyu8.mjs} +7 -0
- package/dist/{messages-C_4otP7U.mjs → chunks/messages-QYOGmket.mjs} +7 -0
- package/dist/chunks/{messages-nefz1S71.mjs → messages-cEjGDAgI.mjs} +7 -0
- package/dist/chunks/{messages-jrncnb-H.mjs → messages-ddhvrdpE.mjs} +7 -0
- package/dist/chunks/{messages-DzqM3Fel.mjs → messages-mwfNK5nZ.mjs} +7 -0
- package/dist/chunks/{messages-Cl6ayUaq.mjs → messages-nG_vNDte.mjs} +7 -0
- package/dist/{messages-C4jL-90N.mjs → chunks/messages-tDq3Owh7.mjs} +7 -0
- package/dist/{messages-BI43k_BD.mjs → chunks/messages-x6VJVZKx.mjs} +7 -0
- package/dist/full.mjs +2 -2
- package/dist/locales.mjs +87 -80
- package/dist/{messages-D2NOpHn9.mjs → messages-0Pxnqd4N.mjs} +7 -0
- package/dist/{messages-GSByFygY.mjs → messages-0ZXYUq7S.mjs} +7 -0
- package/dist/{chunks/messages-BUl_Rcnj.mjs → messages-2OD2uUDS.mjs} +9 -2
- package/dist/{chunks/messages-CgTq3QhU.mjs → messages-7cEMfYzh.mjs} +7 -0
- package/dist/{chunks/messages-DlJbPF2T.mjs → messages-8mwfda1Q.mjs} +7 -0
- package/dist/{chunks/messages-D9ndgBnU.mjs → messages-B-FqWsBM.mjs} +7 -0
- package/dist/{chunks/messages-B217znr-.mjs → messages-B1jzqWiQ.mjs} +7 -0
- package/dist/{chunks/messages-BcpCubnC.mjs → messages-B5wk4Ezz.mjs} +7 -0
- package/dist/{messages-DRXWF0PV.mjs → messages-BAZ5Ld8x.mjs} +7 -0
- package/dist/{chunks/messages-CRJ_mchV.mjs → messages-BBhGp198.mjs} +7 -0
- package/dist/{messages-yHcs38yI.mjs → messages-BC9IjIb7.mjs} +7 -0
- package/dist/{messages-Cr94GzbX.mjs → messages-BFEmpeV-.mjs} +7 -0
- package/dist/{messages-ucTVgS5G.mjs → messages-BGqzTZy0.mjs} +7 -0
- package/dist/{chunks/messages-begYOTgC.mjs → messages-BICs1abK.mjs} +7 -0
- package/dist/{messages-DVQvl8Qj.mjs → messages-BJX6rOnd.mjs} +7 -0
- package/dist/{messages-Chb7k3Rg.mjs → messages-BL2bXRhN.mjs} +7 -0
- package/dist/{chunks/messages-Phkd7XmE.mjs → messages-BMs5qdlH.mjs} +7 -0
- package/dist/{messages-Cjjo7yHR.mjs → messages-BRsjUNwB.mjs} +7 -0
- package/dist/{messages-D4qqwVgQ.mjs → messages-BSqV8OUR.mjs} +7 -0
- package/dist/{messages-DviiFSv2.mjs → messages-BTqu3DfG.mjs} +7 -0
- package/dist/{messages-0AbcLMLm.mjs → messages-BXnDEsur.mjs} +7 -0
- package/dist/{chunks/messages-CmR9ftc_.mjs → messages-BYcre4-6.mjs} +7 -0
- package/dist/{chunks/messages-wmi-iFkH.mjs → messages-BZ9LRJf-.mjs} +7 -0
- package/dist/{messages-D00x4S8o.mjs → messages-BgypBy7y.mjs} +7 -0
- package/dist/{chunks/messages-96kNZDll.mjs → messages-BsuGf70G.mjs} +7 -0
- package/dist/{messages-v3GipbFl.mjs → messages-BwaoF4lQ.mjs} +7 -0
- package/dist/{chunks/messages-DDTQgImT.mjs → messages-C1l8_7-y.mjs} +7 -0
- package/dist/{chunks/messages-B1FZ8lxU.mjs → messages-C5NA_r9v.mjs} +7 -0
- package/dist/{chunks/messages-Cs8zmZ3L.mjs → messages-C6zgZ5pA.mjs} +7 -0
- package/dist/{messages-ZjUAIWb1.mjs → messages-CAo5ghFI.mjs} +7 -0
- package/dist/{chunks/messages-D5S1Dnpm.mjs → messages-CH9qlJ9I.mjs} +7 -0
- package/dist/{chunks/messages-D7u2bmP2.mjs → messages-CI0HqAeS.mjs} +7 -0
- package/dist/{chunks/messages-DH_jBeED.mjs → messages-CJJtms9k.mjs} +7 -0
- package/dist/{chunks/messages-CDBLbUOQ.mjs → messages-CM2hJqk6.mjs} +7 -0
- package/dist/{messages-8IPXkrDl.mjs → messages-CRMiDPIQ.mjs} +7 -0
- package/dist/{messages-Dzzn6XoD.mjs → messages-CWsZuBj1.mjs} +7 -0
- package/dist/{messages-CW4c4cRk.mjs → messages-C_gLHo6A.mjs} +7 -0
- package/dist/{chunks/messages-CH4hrauY.mjs → messages-Cbu-NUDn.mjs} +7 -0
- package/dist/{chunks/messages-RonBBCnh.mjs → messages-Cjb_MCeh.mjs} +7 -0
- package/dist/{messages-BJ6zrz2j.mjs → messages-ClXYO9Wn.mjs} +7 -0
- package/dist/{messages-CrCYPCk3.mjs → messages-CsH20vhP.mjs} +7 -0
- package/dist/{chunks/messages-CzK0LEhb.mjs → messages-CsjAGhzA.mjs} +7 -0
- package/dist/{messages-BZlmVRwn.mjs → messages-Cx7VKFOE.mjs} +7 -0
- package/dist/{messages-0E0AkrNu.mjs → messages-D3JeBwxo.mjs} +7 -0
- package/dist/{messages-D85FqxgY.mjs → messages-D541fieJ.mjs} +7 -0
- package/dist/{chunks/messages-4v4MuVEc.mjs → messages-D7XPdglc.mjs} +7 -0
- package/dist/{chunks/messages-BC8IN4Bf.mjs → messages-DBhylfvt.mjs} +7 -0
- package/dist/{messages-B8WNljW3.mjs → messages-DCA120lW.mjs} +7 -0
- package/dist/{messages-Cr49Nt3U.mjs → messages-DCf_xZMN.mjs} +7 -0
- package/dist/{messages-VDriF5Qy.mjs → messages-DDwXKCpe.mjs} +7 -0
- package/dist/{chunks/messages-b1EdvUm0.mjs → messages-DNKDlxcy.mjs} +7 -0
- package/dist/{chunks/messages-L_kl2Qvh.mjs → messages-DPvEjrGK.mjs} +7 -0
- package/dist/{messages-62v-CLC-.mjs → messages-DQ-AkNxA.mjs} +7 -0
- package/dist/{messages-DdK-nFGm.mjs → messages-DVuvkNap.mjs} +7 -0
- package/dist/{chunks/messages-DnVlmiNT.mjs → messages-DaglyqUT.mjs} +7 -0
- package/dist/{chunks/messages-Bm-E4iRC.mjs → messages-Di0bAfwA.mjs} +7 -0
- package/dist/{chunks/messages-D1mn7Zd5.mjs → messages-DuLct0Yr.mjs} +7 -0
- package/dist/{chunks/messages-8DeO60Oo.mjs → messages-DzEYYhZh.mjs} +7 -0
- package/dist/{messages-CfiyT2Wi.mjs → messages-DznNGAB2.mjs} +7 -0
- package/dist/{messages-DXktiao_.mjs → messages-DzoIzyu8.mjs} +7 -0
- package/dist/{chunks/messages-C_4otP7U.mjs → messages-QYOGmket.mjs} +7 -0
- package/dist/{messages-nefz1S71.mjs → messages-cEjGDAgI.mjs} +7 -0
- package/dist/{messages-jrncnb-H.mjs → messages-ddhvrdpE.mjs} +7 -0
- package/dist/{messages-DzqM3Fel.mjs → messages-mwfNK5nZ.mjs} +7 -0
- package/dist/{messages-Cl6ayUaq.mjs → messages-nG_vNDte.mjs} +7 -0
- package/dist/{chunks/messages-C4jL-90N.mjs → messages-tDq3Owh7.mjs} +7 -0
- package/dist/{chunks/messages-BI43k_BD.mjs → messages-x6VJVZKx.mjs} +7 -0
- package/dist/tools.mjs +443 -338
- package/package.json +1 -1
- package/src/components/i18n/locales/am/messages.json +7 -0
- package/src/components/i18n/locales/ar/messages.json +7 -0
- package/src/components/i18n/locales/az/messages.json +7 -0
- package/src/components/i18n/locales/bg/messages.json +7 -0
- package/src/components/i18n/locales/bn/messages.json +7 -0
- package/src/components/i18n/locales/bs/messages.json +7 -0
- package/src/components/i18n/locales/cs/messages.json +7 -0
- package/src/components/i18n/locales/da/messages.json +7 -0
- package/src/components/i18n/locales/de/messages.json +7 -0
- package/src/components/i18n/locales/dv/messages.json +7 -0
- package/src/components/i18n/locales/el/messages.json +7 -0
- package/src/components/i18n/locales/en/messages.json +7 -0
- package/src/components/i18n/locales/es/messages.json +7 -0
- package/src/components/i18n/locales/et/messages.json +7 -0
- package/src/components/i18n/locales/fa/messages.json +7 -0
- package/src/components/i18n/locales/fi/messages.json +7 -0
- package/src/components/i18n/locales/fil/messages.json +7 -0
- package/src/components/i18n/locales/fr/messages.json +7 -0
- package/src/components/i18n/locales/gu/messages.json +7 -0
- package/src/components/i18n/locales/he/messages.json +7 -0
- package/src/components/i18n/locales/hi/messages.json +7 -0
- package/src/components/i18n/locales/hr/messages.json +7 -0
- package/src/components/i18n/locales/hu/messages.json +7 -0
- package/src/components/i18n/locales/hy/messages.json +7 -0
- package/src/components/i18n/locales/id/messages.json +7 -0
- package/src/components/i18n/locales/it/messages.json +7 -0
- package/src/components/i18n/locales/ja/messages.json +7 -0
- package/src/components/i18n/locales/ka/messages.json +7 -0
- package/src/components/i18n/locales/km/messages.json +7 -0
- package/src/components/i18n/locales/kn/messages.json +7 -0
- package/src/components/i18n/locales/ko/messages.json +7 -0
- package/src/components/i18n/locales/ku/messages.json +7 -0
- package/src/components/i18n/locales/lo/messages.json +7 -0
- package/src/components/i18n/locales/lt/messages.json +7 -0
- package/src/components/i18n/locales/lv/messages.json +7 -0
- package/src/components/i18n/locales/mk/messages.json +7 -0
- package/src/components/i18n/locales/ml/messages.json +7 -0
- package/src/components/i18n/locales/mn/messages.json +7 -0
- package/src/components/i18n/locales/mr/messages.json +7 -0
- package/src/components/i18n/locales/ms/messages.json +7 -0
- package/src/components/i18n/locales/my/messages.json +7 -0
- package/src/components/i18n/locales/ne/messages.json +7 -0
- package/src/components/i18n/locales/nl/messages.json +7 -0
- package/src/components/i18n/locales/no/messages.json +7 -0
- package/src/components/i18n/locales/pa/messages.json +7 -0
- package/src/components/i18n/locales/pl/messages.json +7 -0
- package/src/components/i18n/locales/ps/messages.json +7 -0
- package/src/components/i18n/locales/pt/messages.json +7 -0
- package/src/components/i18n/locales/ro/messages.json +7 -0
- package/src/components/i18n/locales/ru/messages.json +7 -0
- package/src/components/i18n/locales/sd/messages.json +7 -0
- package/src/components/i18n/locales/si/messages.json +7 -0
- package/src/components/i18n/locales/sk/messages.json +7 -0
- package/src/components/i18n/locales/sl/messages.json +7 -0
- package/src/components/i18n/locales/sq/messages.json +7 -0
- package/src/components/i18n/locales/sr/messages.json +7 -0
- package/src/components/i18n/locales/sv/messages.json +7 -0
- package/src/components/i18n/locales/sw/messages.json +7 -0
- package/src/components/i18n/locales/ta/messages.json +7 -0
- package/src/components/i18n/locales/te/messages.json +7 -0
- package/src/components/i18n/locales/th/messages.json +7 -0
- package/src/components/i18n/locales/tr/messages.json +7 -0
- package/src/components/i18n/locales/ug/messages.json +7 -0
- package/src/components/i18n/locales/uk/messages.json +7 -0
- package/src/components/i18n/locales/ur/messages.json +7 -0
- package/src/components/i18n/locales/vi/messages.json +7 -0
- package/src/components/i18n/locales/yi/messages.json +7 -0
- package/src/components/i18n/locales/zh/messages.json +7 -0
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +44 -2
- package/src/components/modules/blockEvents/index.ts +1 -3
- package/src/components/modules/blockManager/blockManager.ts +16 -0
- package/src/components/modules/blockManager/operations.ts +106 -9
- package/src/components/modules/blockSelection.ts +2 -0
- package/src/components/modules/caret.ts +49 -4
- package/src/components/modules/drag/DragController.ts +34 -2
- package/src/components/modules/paste/handlers/blok-data-handler.ts +50 -3
- package/src/components/modules/toolbar/index.ts +11 -16
- package/src/components/modules/ui.ts +22 -0
- package/src/components/ui/toolbox.ts +19 -3
- package/src/components/utils/notifier/draw.ts +116 -14
- package/src/components/utils/notifier/index.ts +31 -4
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -2
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -7
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
- package/src/components/utils/popover/popover-abstract.ts +2 -0
- package/src/components/utils/popover/popover-desktop.ts +39 -2
- package/src/stories/Block.stories.ts +21 -0
- package/src/stories/EditorModes.stories.ts +19 -0
- package/src/stories/InlineToolbar.stories.ts +41 -9
- package/src/stories/MarkerColors.stories.ts +48 -52
- package/src/stories/Notifier.stories.ts +19 -1
- package/src/stories/Placeholder.stories.ts +12 -0
- package/src/stories/Popover.stories.ts +26 -0
- package/src/stories/Selection.stories.ts +6 -0
- package/src/stories/Table.stories.ts +12 -0
- package/src/stories/Toolbar.stories.ts +9 -0
- package/src/stories/Toolbox.stories.ts +4 -0
- package/src/stories/Tooltip.stories.ts +6 -0
- package/src/stories/helpers.ts +63 -8
- package/src/styles/main.css +46 -0
- package/src/tools/header/index.ts +121 -22
- package/src/tools/table/index.ts +2 -3
- package/src/tools/table/table-add-controls.ts +29 -1
- package/src/tools/table/table-cell-blocks.ts +93 -0
- package/src/tools/toggle/constants.ts +2 -2
- package/src/tools/toggle/dom-builder.ts +32 -4
- package/src/tools/toggle/index.ts +26 -4
- package/src/tools/toggle/toggle-keyboard.ts +19 -2
- package/src/tools/toggle/toggle-lifecycle.ts +1 -0
- package/src/tools/toggle/toggle-shortcuts.ts +18 -8
- package/types/utils/popover/popover.d.ts +8 -0
- package/dist/chunks/index-CZmRzRIX.mjs +0 -78
|
@@ -110,11 +110,18 @@ export class DragController extends Module {
|
|
|
110
110
|
? this.Blok.BlockSelection.selectedBlocks
|
|
111
111
|
: [block];
|
|
112
112
|
|
|
113
|
-
// For single-block
|
|
114
|
-
const
|
|
113
|
+
// For single-block drags, include all descendants (list items via depth, toggles via contentIds)
|
|
114
|
+
const listDescendants = !isBlockSelected && this.listItemDescendants
|
|
115
115
|
? this.listItemDescendants.getDescendants(block)
|
|
116
116
|
: [];
|
|
117
117
|
|
|
118
|
+
// If no list descendants found, check for hierarchy children (toggle blocks)
|
|
119
|
+
const hasHierarchyChildren = !isBlockSelected && block.contentIds?.length > 0;
|
|
120
|
+
const hierarchyDescendants = listDescendants.length === 0 && hasHierarchyChildren
|
|
121
|
+
? this.getHierarchyDescendants(block)
|
|
122
|
+
: [];
|
|
123
|
+
const descendants = listDescendants.length > 0 ? listDescendants : hierarchyDescendants;
|
|
124
|
+
|
|
118
125
|
const blocksToMove = descendants.length > 0
|
|
119
126
|
? [block, ...descendants]
|
|
120
127
|
: initialBlocks;
|
|
@@ -458,6 +465,31 @@ export class DragController extends Module {
|
|
|
458
465
|
this.Blok.Toolbar.moveAndOpen(blockToShow);
|
|
459
466
|
}
|
|
460
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Recursively collects all descendants of a block via the parentId/contentIds hierarchy.
|
|
470
|
+
* Used for toggle blocks and other parent-child structures that don't use data-list-depth.
|
|
471
|
+
* @param block - Parent block to collect descendants for
|
|
472
|
+
* @returns Array of descendant blocks
|
|
473
|
+
*/
|
|
474
|
+
private getHierarchyDescendants(block: Block): Block[] {
|
|
475
|
+
const descendants: Block[] = [];
|
|
476
|
+
|
|
477
|
+
const collectChildren = (parentBlock: Block): void => {
|
|
478
|
+
for (const childId of parentBlock.contentIds) {
|
|
479
|
+
const child = this.Blok.BlockManager.getBlockById(childId);
|
|
480
|
+
|
|
481
|
+
if (child !== undefined) {
|
|
482
|
+
descendants.push(child);
|
|
483
|
+
collectChildren(child);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
collectChildren(block);
|
|
489
|
+
|
|
490
|
+
return descendants;
|
|
491
|
+
}
|
|
492
|
+
|
|
461
493
|
public destroy(): void {
|
|
462
494
|
this.cleanup();
|
|
463
495
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SanitizerConfig } from '../../../../../types/configs/sanitizer-config';
|
|
2
2
|
import type { SavedData } from '../../../../../types/data-formats';
|
|
3
3
|
import type { BlokModules } from '../../../../types-internal/blok-modules';
|
|
4
|
+
import type { Block } from '../../../block';
|
|
4
5
|
import { sanitizeBlocks } from '../../../utils/sanitizer';
|
|
5
6
|
import type { SanitizerConfigBuilder } from '../sanitizer-config';
|
|
6
7
|
import type { ToolRegistry } from '../tool-registry';
|
|
@@ -10,6 +11,15 @@ import type { PasteHandler } from './base';
|
|
|
10
11
|
import { BasePasteHandler } from './base';
|
|
11
12
|
import { PatternHandler } from './pattern-handler';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Shape of a block in the Blok clipboard data.
|
|
16
|
+
* Extends the basic SavedData fields with optional hierarchy information.
|
|
17
|
+
*/
|
|
18
|
+
interface BlokClipboardBlock extends Pick<SavedData, 'id' | 'data' | 'tool'> {
|
|
19
|
+
parentId?: string | null;
|
|
20
|
+
contentIds?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
|
|
14
24
|
/**
|
|
15
25
|
* Blok Data Handler Priority.
|
|
@@ -45,7 +55,7 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
45
55
|
return false;
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
const parsedBlokData = JSON.parse(data) as
|
|
58
|
+
const parsedBlokData = JSON.parse(data) as BlokClipboardBlock[];
|
|
49
59
|
|
|
50
60
|
// Check if we should try pattern matching first (for URL pasting within editor)
|
|
51
61
|
const hasPatterns = this.toolRegistry.toolsPatterns.length > 0;
|
|
@@ -118,9 +128,11 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
118
128
|
|
|
119
129
|
/**
|
|
120
130
|
* Insert Blok JSON blocks.
|
|
131
|
+
* After inserting all blocks, restores parent-child hierarchy using an ID mapping
|
|
132
|
+
* (pasted blocks receive new IDs, so original IDs are mapped to new block instances).
|
|
121
133
|
*/
|
|
122
134
|
private insertBlokBlocks(
|
|
123
|
-
blocks:
|
|
135
|
+
blocks: BlokClipboardBlock[],
|
|
124
136
|
canReplace: boolean
|
|
125
137
|
): void {
|
|
126
138
|
const { BlockManager, Caret, Tools } = this.Blok;
|
|
@@ -130,7 +142,14 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
130
142
|
this.config.sanitizer
|
|
131
143
|
);
|
|
132
144
|
|
|
133
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Map from original (old) block ID to the newly inserted Block instance.
|
|
147
|
+
* Used after insertion to re-establish parent-child relationships.
|
|
148
|
+
*/
|
|
149
|
+
const oldIdToEntry = new Map<string, { newBlock: Block; original: BlokClipboardBlock }>();
|
|
150
|
+
|
|
151
|
+
sanitizedBlocks.forEach((sanitizedBlock, i) => {
|
|
152
|
+
const { tool, data } = sanitizedBlock;
|
|
134
153
|
const needToReplaceCurrentBlock = i === 0 &&
|
|
135
154
|
canReplace &&
|
|
136
155
|
Boolean(BlockManager.currentBlock?.tool.isDefault) &&
|
|
@@ -142,7 +161,35 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
142
161
|
replace: needToReplaceCurrentBlock,
|
|
143
162
|
});
|
|
144
163
|
|
|
164
|
+
const originalBlock = blocks[i];
|
|
165
|
+
|
|
166
|
+
if (originalBlock !== undefined) {
|
|
167
|
+
oldIdToEntry.set(originalBlock.id, { newBlock: block, original: originalBlock });
|
|
168
|
+
}
|
|
169
|
+
|
|
145
170
|
Caret.setToBlock(block, Caret.positions.END);
|
|
146
171
|
});
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Restore parent-child hierarchy using the old-to-new ID mapping.
|
|
175
|
+
* Only restores relationships where both parent and child are in the pasted set.
|
|
176
|
+
*/
|
|
177
|
+
for (const [, { newBlock, original }] of oldIdToEntry) {
|
|
178
|
+
if (original.parentId === undefined || original.parentId === null) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const parentEntry = oldIdToEntry.get(original.parentId);
|
|
183
|
+
|
|
184
|
+
if (parentEntry === undefined) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
newBlock.parentId = parentEntry.newBlock.id;
|
|
189
|
+
|
|
190
|
+
if (!parentEntry.newBlock.contentIds.includes(newBlock.id)) {
|
|
191
|
+
parentEntry.newBlock.contentIds = [...parentEntry.newBlock.contentIds, newBlock.id];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
147
194
|
}
|
|
148
195
|
}
|
|
@@ -393,22 +393,16 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
/**
|
|
396
|
-
*
|
|
397
|
-
* The
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
* Note: we check focus (activeElement) not hover. On hover alone, the plus
|
|
401
|
-
* button should remain visible so the user can click it to add blocks
|
|
402
|
-
* below the table. Only when the user actually clicks/focuses inside a cell
|
|
403
|
-
* should the buttons be suppressed.
|
|
396
|
+
* Adjust toolbar button visibility when focus is inside a table cell.
|
|
397
|
+
* The plus button always stays visible so users can add blocks below the table.
|
|
398
|
+
* The settings toggler is hidden because drag/settings don't apply to individual cells.
|
|
404
399
|
*/
|
|
405
400
|
const focusIsInsideCell = this.isFocusInsideTableCell();
|
|
406
|
-
const displayValue = focusIsInsideCell ? 'none' : '';
|
|
407
401
|
|
|
408
|
-
plusButton.style.display =
|
|
402
|
+
plusButton.style.display = '';
|
|
409
403
|
|
|
410
404
|
if (settingsToggler) {
|
|
411
|
-
settingsToggler.style.display =
|
|
405
|
+
settingsToggler.style.display = focusIsInsideCell ? 'none' : '';
|
|
412
406
|
}
|
|
413
407
|
|
|
414
408
|
const targetBlockHolder = targetBlock.holder;
|
|
@@ -662,8 +656,8 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
662
656
|
|
|
663
657
|
/**
|
|
664
658
|
* Updates toolbar button visibility based on whether a table cell has focus.
|
|
665
|
-
*
|
|
666
|
-
*
|
|
659
|
+
* The plus button always stays visible; the settings toggler is hidden when
|
|
660
|
+
* focus is inside a cell and restored when focus moves to a regular block.
|
|
667
661
|
*
|
|
668
662
|
* Called from the focusin listener so button state is updated immediately
|
|
669
663
|
* on click, without waiting for the next hover/moveAndOpen cycle.
|
|
@@ -676,12 +670,11 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
676
670
|
}
|
|
677
671
|
|
|
678
672
|
const focusIsInsideCell = this.isFocusInsideTableCell();
|
|
679
|
-
const displayValue = focusIsInsideCell ? 'none' : '';
|
|
680
673
|
|
|
681
|
-
plusButton.style.display =
|
|
674
|
+
plusButton.style.display = '';
|
|
682
675
|
|
|
683
676
|
if (settingsToggler) {
|
|
684
|
-
settingsToggler.style.display =
|
|
677
|
+
settingsToggler.style.display = focusIsInsideCell ? 'none' : '';
|
|
685
678
|
}
|
|
686
679
|
}
|
|
687
680
|
|
|
@@ -837,9 +830,11 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
837
830
|
i18nLabels: {
|
|
838
831
|
filter: this.Blok.I18n.t('popover.search'),
|
|
839
832
|
nothingFound: this.Blok.I18n.t('popover.nothingFound'),
|
|
833
|
+
slashSearchPlaceholder: this.Blok.I18n.t('toolbox.typeToSearch'),
|
|
840
834
|
},
|
|
841
835
|
i18n: this.Blok.I18n,
|
|
842
836
|
triggerElement: this.nodes.plusButton,
|
|
837
|
+
leftAlignElement: this.nodes.content,
|
|
843
838
|
});
|
|
844
839
|
|
|
845
840
|
this.toolboxInstance.on(ToolboxEvent.Opened, () => {
|
|
@@ -22,6 +22,7 @@ import { KeyboardController } from './uiControllers/controllers/keyboard';
|
|
|
22
22
|
import { SelectionController } from './uiControllers/controllers/selection';
|
|
23
23
|
import { createDocumentClickedHandler } from './uiControllers/handlers/click';
|
|
24
24
|
import { createRedactorTouchHandler } from './uiControllers/handlers/touch';
|
|
25
|
+
import { ToggleShortcuts } from '../../tools/toggle/toggle-shortcuts';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* HTML Elements used for UI
|
|
@@ -56,6 +57,7 @@ export class UI extends Module<UINodes> {
|
|
|
56
57
|
private keyboardController: KeyboardController | null = null;
|
|
57
58
|
private selectionController: SelectionController | null = null;
|
|
58
59
|
private blockHoverController: BlockHoverController | null = null;
|
|
60
|
+
private toggleShortcuts: ToggleShortcuts | null = null;
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* Handlers for simple event behaviors
|
|
@@ -162,8 +164,27 @@ export class UI extends Module<UINodes> {
|
|
|
162
164
|
* Enable selection controller after initialization.
|
|
163
165
|
* This is needed because bindReadOnlyInsensitiveListeners() is called in make()
|
|
164
166
|
* before initializeControllers(), so the selectionController doesn't exist yet.
|
|
167
|
+
* Must happen before toggleShortcuts.register() so that a shortcut registration
|
|
168
|
+
* error cannot prevent the selectionchange listener from being set up.
|
|
165
169
|
*/
|
|
166
170
|
this.selectionController?.enable();
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Register toggle shortcuts (CMD+ALT+T) for collapsing/expanding all toggle blocks.
|
|
174
|
+
* Wrapped in try-catch because the Shortcuts singleton may throw if shortcuts are
|
|
175
|
+
* already registered (e.g. race condition with multiple editor instances in CI).
|
|
176
|
+
* This is non-critical — the editor works fine without toggle shortcuts.
|
|
177
|
+
*/
|
|
178
|
+
this.toggleShortcuts = new ToggleShortcuts(
|
|
179
|
+
this.Blok.API.methods,
|
|
180
|
+
this.nodes.wrapper
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
this.toggleShortcuts.register();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.warn('Blok: Failed to register toggle shortcuts:', error);
|
|
187
|
+
}
|
|
167
188
|
}
|
|
168
189
|
|
|
169
190
|
/**
|
|
@@ -343,6 +364,7 @@ export class UI extends Module<UINodes> {
|
|
|
343
364
|
* Clean blok`s UI
|
|
344
365
|
*/
|
|
345
366
|
public destroy(): void {
|
|
367
|
+
this.toggleShortcuts?.unregister();
|
|
346
368
|
this.nodes.holder.innerHTML = '';
|
|
347
369
|
|
|
348
370
|
this.unbindReadOnlyInsensitiveListeners();
|
|
@@ -57,7 +57,7 @@ export interface ToolboxEventMap {
|
|
|
57
57
|
/**
|
|
58
58
|
* Available i18n dict keys that should be passed to the constructor
|
|
59
59
|
*/
|
|
60
|
-
type ToolboxTextLabelsKeys = 'filter' | 'nothingFound';
|
|
60
|
+
type ToolboxTextLabelsKeys = 'filter' | 'nothingFound' | 'slashSearchPlaceholder';
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Toolbox
|
|
@@ -145,6 +145,11 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
145
145
|
*/
|
|
146
146
|
private triggerElement?: HTMLElement;
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Optional element whose left edge is used for horizontal popover alignment.
|
|
150
|
+
*/
|
|
151
|
+
private leftAlignElement?: HTMLElement;
|
|
152
|
+
|
|
148
153
|
/**
|
|
149
154
|
* The block element currently being listened to for inline slash search
|
|
150
155
|
*/
|
|
@@ -169,13 +174,15 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
169
174
|
* @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not
|
|
170
175
|
* @param options.i18n - I18n instance for translations
|
|
171
176
|
* @param options.triggerElement - Element relative to which the popover should be positioned
|
|
177
|
+
* @param options.leftAlignElement - Element whose left edge is used for horizontal popover alignment
|
|
172
178
|
*/
|
|
173
|
-
constructor({ api, tools, i18nLabels, i18n, triggerElement }: {
|
|
179
|
+
constructor({ api, tools, i18nLabels, i18n, triggerElement, leftAlignElement }: {
|
|
174
180
|
api: API;
|
|
175
181
|
tools: ToolsCollection<BlockToolAdapter>;
|
|
176
182
|
i18nLabels: Record<ToolboxTextLabelsKeys, string>;
|
|
177
183
|
i18n: I18nInstance;
|
|
178
184
|
triggerElement?: HTMLElement;
|
|
185
|
+
leftAlignElement?: HTMLElement;
|
|
179
186
|
}) {
|
|
180
187
|
super();
|
|
181
188
|
|
|
@@ -184,6 +191,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
184
191
|
this.i18nLabels = i18nLabels;
|
|
185
192
|
this.i18n = i18n;
|
|
186
193
|
this.triggerElement = triggerElement;
|
|
194
|
+
this.leftAlignElement = leftAlignElement;
|
|
187
195
|
|
|
188
196
|
this.enableShortcuts();
|
|
189
197
|
|
|
@@ -346,6 +354,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
346
354
|
this.popover = new PopoverClass({
|
|
347
355
|
scopeElement: this.api.ui.nodes.redactor,
|
|
348
356
|
trigger: this.triggerElement || this.nodes.toolbox,
|
|
357
|
+
leftAlignElement: this.leftAlignElement,
|
|
349
358
|
messages: {
|
|
350
359
|
nothingFound: this.i18nLabels.nothingFound,
|
|
351
360
|
search: this.i18nLabels.filter,
|
|
@@ -633,7 +642,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
633
642
|
this.currentBlockForSearch = currentBlock.holder;
|
|
634
643
|
this.currentContentEditable = this.currentBlockForSearch.querySelector('[contenteditable="true"]');
|
|
635
644
|
if (this.currentContentEditable instanceof HTMLElement) {
|
|
636
|
-
this.currentContentEditable.setAttribute(DATA_ATTR.slashSearch,
|
|
645
|
+
this.currentContentEditable.setAttribute(DATA_ATTR.slashSearch, this.i18nLabels.slashSearchPlaceholder);
|
|
637
646
|
}
|
|
638
647
|
this.listeners.on(this.currentBlockForSearch, 'input', this.handleBlockInput);
|
|
639
648
|
}
|
|
@@ -674,6 +683,13 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
674
683
|
|
|
675
684
|
const query = text.slice(slashIndex + 1);
|
|
676
685
|
|
|
686
|
+
if (this.currentContentEditable instanceof HTMLElement) {
|
|
687
|
+
this.currentContentEditable.setAttribute(
|
|
688
|
+
DATA_ATTR.slashSearch,
|
|
689
|
+
query.length === 0 ? this.i18nLabels.slashSearchPlaceholder : ''
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
677
693
|
this.popover?.filterItems(query);
|
|
678
694
|
};
|
|
679
695
|
|
|
@@ -2,22 +2,39 @@ import { twMerge, twJoin } from '../tw';
|
|
|
2
2
|
|
|
3
3
|
import type { NotifierOptions, ConfirmNotifierOptions, PromptNotifierOptions } from './types';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* SVG icons for notification styles.
|
|
7
|
+
* Each icon is 16x16, stroke-based for consistency.
|
|
8
|
+
*/
|
|
9
|
+
const ICONS = {
|
|
10
|
+
success: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M5.5 8.25l1.75 1.75 3.25-3.5"/></svg>`,
|
|
11
|
+
error: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M8 5.25v3"/><circle cx="8" cy="10.75" r="0.5" fill="currentColor" stroke="none"/></svg>`,
|
|
12
|
+
default: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M8 7.25v3.25"/><circle cx="8" cy="5.25" r="0.5" fill="currentColor" stroke="none"/></svg>`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const CLOSE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 2l6 6M8 2l-6 6"/></svg>`;
|
|
16
|
+
|
|
5
17
|
export const CSS = {
|
|
6
18
|
wrapper: twJoin(
|
|
7
19
|
'fixed z-2 bottom-5 left-5',
|
|
8
20
|
'font-[-apple-system,BlinkMacSystemFont,"Segoe_UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira_Sans","Droid_Sans","Helvetica_Neue",sans-serif]'
|
|
9
21
|
),
|
|
10
22
|
notification: twJoin(
|
|
11
|
-
'relative w-[230px] mt-[15px] py-[13px] px-4',
|
|
12
|
-
'bg-white shadow-notify rounded-[
|
|
13
|
-
'text-sm leading-[1.4em] wrap-break-word',
|
|
23
|
+
'relative flex items-start gap-2.5 w-[230px] mt-[15px] py-[13px] px-4',
|
|
24
|
+
'bg-white shadow-notify rounded-[6px]',
|
|
25
|
+
'text-sm leading-[1.4em] wrap-break-word overflow-hidden',
|
|
14
26
|
'before:content-[""] before:absolute before:block before:top-0 before:left-0',
|
|
15
27
|
'before:w-[3px] before:h-[calc(100%-6px)] before:m-[3px] before:rounded-[5px] before:bg-transparent'
|
|
16
28
|
),
|
|
29
|
+
icon: 'shrink-0 mt-px',
|
|
30
|
+
iconSuccess: 'text-[#34c992]',
|
|
31
|
+
iconError: 'text-[#fb5d5d]',
|
|
32
|
+
iconDefault: 'text-[#9ca3af]',
|
|
33
|
+
messageWrapper: 'flex-1 min-w-0',
|
|
17
34
|
crossBtn: twJoin(
|
|
18
|
-
'absolute top-[7px] right-[
|
|
19
|
-
'
|
|
20
|
-
'
|
|
35
|
+
'absolute top-[7px] right-[7px] flex items-center justify-center',
|
|
36
|
+
'w-6 h-6 rounded opacity-40 cursor-pointer',
|
|
37
|
+
'transition-opacity duration-150',
|
|
21
38
|
'hover:opacity-100'
|
|
22
39
|
),
|
|
23
40
|
btnsWrapper: 'flex flex-row flex-nowrap mt-[5px]',
|
|
@@ -37,6 +54,70 @@ export const CSS = {
|
|
|
37
54
|
'bg-[#fffbfb]!',
|
|
38
55
|
'before:bg-[#fb5d5d]!'
|
|
39
56
|
),
|
|
57
|
+
progressBar: twJoin(
|
|
58
|
+
'absolute bottom-0 left-0 h-[2px] rounded-b-[6px]',
|
|
59
|
+
'animate-notify-progress'
|
|
60
|
+
),
|
|
61
|
+
progressDefault: 'bg-[#d1d5db]',
|
|
62
|
+
progressSuccess: 'bg-[#41ffb1]',
|
|
63
|
+
progressError: 'bg-[#fb5d5d]',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates an icon element for the notification.
|
|
68
|
+
*/
|
|
69
|
+
const createIcon = (style?: string): HTMLElement => {
|
|
70
|
+
const iconWrapper = document.createElement('span');
|
|
71
|
+
const resolvedStyle = style === 'success' || style === 'error' ? style : 'default';
|
|
72
|
+
|
|
73
|
+
iconWrapper.innerHTML = ICONS[resolvedStyle];
|
|
74
|
+
iconWrapper.setAttribute('data-blok-testid', 'notification-icon');
|
|
75
|
+
iconWrapper.setAttribute('data-blok-style', resolvedStyle);
|
|
76
|
+
|
|
77
|
+
const iconColorMap: Record<string, string> = {
|
|
78
|
+
success: CSS.iconSuccess,
|
|
79
|
+
error: CSS.iconError,
|
|
80
|
+
default: CSS.iconDefault,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const colorClass = iconColorMap[resolvedStyle] ?? CSS.iconDefault;
|
|
84
|
+
|
|
85
|
+
iconWrapper.className = twJoin(CSS.icon, colorClass);
|
|
86
|
+
|
|
87
|
+
return iconWrapper;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates a close (cross) button with an SVG icon.
|
|
92
|
+
*/
|
|
93
|
+
const createCloseButton = (): HTMLElement => {
|
|
94
|
+
const cross = document.createElement('div');
|
|
95
|
+
|
|
96
|
+
cross.className = CSS.crossBtn;
|
|
97
|
+
cross.setAttribute('data-blok-testid', 'notification-cross');
|
|
98
|
+
cross.innerHTML = CLOSE_ICON;
|
|
99
|
+
|
|
100
|
+
return cross;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a progress bar element for auto-dismissing alerts.
|
|
105
|
+
*/
|
|
106
|
+
export const createProgressBar = (style?: string, time?: number): HTMLElement => {
|
|
107
|
+
const bar = document.createElement('div');
|
|
108
|
+
|
|
109
|
+
const progressColorMap: Record<string, string> = {
|
|
110
|
+
success: CSS.progressSuccess,
|
|
111
|
+
error: CSS.progressError,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const colorClass = progressColorMap[style ?? ''] ?? CSS.progressDefault;
|
|
115
|
+
|
|
116
|
+
bar.className = twJoin(CSS.progressBar, colorClass);
|
|
117
|
+
bar.setAttribute('data-blok-testid', 'notification-progress');
|
|
118
|
+
bar.style.animationDuration = `${time ?? 8000}ms`;
|
|
119
|
+
|
|
120
|
+
return bar;
|
|
40
121
|
};
|
|
41
122
|
|
|
42
123
|
/**
|
|
@@ -45,8 +126,6 @@ export const CSS = {
|
|
|
45
126
|
*/
|
|
46
127
|
export const alert = (options: NotifierOptions): HTMLElement => {
|
|
47
128
|
const notify = document.createElement('DIV');
|
|
48
|
-
const cross = document.createElement('DIV');
|
|
49
|
-
const message = options.message;
|
|
50
129
|
const style = options.style;
|
|
51
130
|
|
|
52
131
|
const getStyleClasses = (): string => {
|
|
@@ -69,12 +148,23 @@ export const alert = (options: NotifierOptions): HTMLElement => {
|
|
|
69
148
|
notify.setAttribute('data-blok-testid', 'notification');
|
|
70
149
|
}
|
|
71
150
|
|
|
72
|
-
|
|
151
|
+
// Icon
|
|
152
|
+
const icon = createIcon(style);
|
|
73
153
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
154
|
+
notify.appendChild(icon);
|
|
155
|
+
|
|
156
|
+
// Message wrapper (flex child that holds message + buttons)
|
|
157
|
+
const messageWrapper = document.createElement('div');
|
|
158
|
+
|
|
159
|
+
messageWrapper.className = CSS.messageWrapper;
|
|
160
|
+
messageWrapper.setAttribute('data-blok-testid', 'notification-message');
|
|
161
|
+
messageWrapper.innerHTML = options.message;
|
|
162
|
+
notify.appendChild(messageWrapper);
|
|
163
|
+
|
|
164
|
+
// Close button
|
|
165
|
+
const cross = createCloseButton();
|
|
77
166
|
|
|
167
|
+
cross.addEventListener('click', () => notify.remove());
|
|
78
168
|
notify.appendChild(cross);
|
|
79
169
|
|
|
80
170
|
return notify;
|
|
@@ -86,6 +176,7 @@ export const alert = (options: NotifierOptions): HTMLElement => {
|
|
|
86
176
|
*/
|
|
87
177
|
export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
|
|
88
178
|
const notify = alert(options);
|
|
179
|
+
const messageWrapper = notify.querySelector('[data-blok-testid="notification-message"]') as HTMLElement;
|
|
89
180
|
const btnsWrapper = document.createElement('div');
|
|
90
181
|
const okBtn = document.createElement('button');
|
|
91
182
|
const cancelBtn = document.createElement('button');
|
|
@@ -123,7 +214,12 @@ export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
|
|
|
123
214
|
btnsWrapper.appendChild(okBtn);
|
|
124
215
|
btnsWrapper.appendChild(cancelBtn);
|
|
125
216
|
|
|
126
|
-
|
|
217
|
+
// Append buttons to the message wrapper so they flow under the message text
|
|
218
|
+
if (messageWrapper) {
|
|
219
|
+
messageWrapper.appendChild(btnsWrapper);
|
|
220
|
+
} else {
|
|
221
|
+
notify.appendChild(btnsWrapper);
|
|
222
|
+
}
|
|
127
223
|
|
|
128
224
|
return notify;
|
|
129
225
|
};
|
|
@@ -134,6 +230,7 @@ export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
|
|
|
134
230
|
*/
|
|
135
231
|
export const prompt = (options: PromptNotifierOptions): HTMLElement => {
|
|
136
232
|
const notify = alert(options);
|
|
233
|
+
const messageWrapper = notify.querySelector('[data-blok-testid="notification-message"]') as HTMLElement;
|
|
137
234
|
const btnsWrapper = document.createElement('div');
|
|
138
235
|
const okBtn = document.createElement('button');
|
|
139
236
|
const input = document.createElement('input');
|
|
@@ -176,7 +273,12 @@ export const prompt = (options: PromptNotifierOptions): HTMLElement => {
|
|
|
176
273
|
btnsWrapper.appendChild(input);
|
|
177
274
|
btnsWrapper.appendChild(okBtn);
|
|
178
275
|
|
|
179
|
-
|
|
276
|
+
// Append to message wrapper for proper flex layout
|
|
277
|
+
if (messageWrapper) {
|
|
278
|
+
messageWrapper.appendChild(btnsWrapper);
|
|
279
|
+
} else {
|
|
280
|
+
notify.appendChild(btnsWrapper);
|
|
281
|
+
}
|
|
180
282
|
|
|
181
283
|
return notify;
|
|
182
284
|
};
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { alert, confirm, getWrapper, prompt } from './draw';
|
|
1
|
+
import { alert, confirm, createProgressBar, getWrapper, prompt } from './draw';
|
|
2
2
|
import type { NotifierOptions, ConfirmNotifierOptions, PromptNotifierOptions } from './types';
|
|
3
3
|
|
|
4
4
|
const DEFAULT_TIME = 8000;
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Applies the exit animation and removes the element after it finishes.
|
|
8
|
+
*/
|
|
9
|
+
const dismissWithAnimation = (element: HTMLElement): void => {
|
|
10
|
+
element.classList.add('animate-notify-slide-out');
|
|
11
|
+
|
|
12
|
+
element.addEventListener('animationend', () => {
|
|
13
|
+
element.remove();
|
|
14
|
+
}, { once: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
6
17
|
/**
|
|
7
18
|
* Prepare wrapper for notifications
|
|
8
19
|
* @returns {HTMLElement}
|
|
@@ -48,8 +59,24 @@ export const show = (options: NotifierOptions | ConfirmNotifierOptions | PromptN
|
|
|
48
59
|
// type is 'alert' or undefined
|
|
49
60
|
const alertElement = alert(options);
|
|
50
61
|
|
|
62
|
+
// Add progress bar for auto-dismissing alerts
|
|
63
|
+
const progressBar = createProgressBar(options.style, time);
|
|
64
|
+
|
|
65
|
+
alertElement.appendChild(progressBar);
|
|
66
|
+
|
|
67
|
+
// Wire up the close button to use animated dismissal
|
|
68
|
+
const crossBtn = alertElement.querySelector('[data-blok-testid="notification-cross"]');
|
|
69
|
+
|
|
70
|
+
if (crossBtn) {
|
|
71
|
+
// Replace the basic remove handler with animated dismissal
|
|
72
|
+
const newCross = crossBtn.cloneNode(true) as HTMLElement;
|
|
73
|
+
|
|
74
|
+
crossBtn.replaceWith(newCross);
|
|
75
|
+
newCross.addEventListener('click', () => dismissWithAnimation(alertElement));
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
window.setTimeout(() => {
|
|
52
|
-
alertElement
|
|
79
|
+
dismissWithAnimation(alertElement);
|
|
53
80
|
}, time);
|
|
54
81
|
|
|
55
82
|
return alertElement;
|
|
@@ -57,11 +84,11 @@ export const show = (options: NotifierOptions | ConfirmNotifierOptions | PromptN
|
|
|
57
84
|
|
|
58
85
|
if (wrapper && notify) {
|
|
59
86
|
wrapper.appendChild(notify);
|
|
60
|
-
notify.className = `${notify.className} animate-notify-
|
|
87
|
+
notify.className = `${notify.className} animate-notify-slide-in`;
|
|
61
88
|
notify.setAttribute('data-blok-bounce-in', 'true');
|
|
62
89
|
}
|
|
63
90
|
};
|
|
64
91
|
|
|
65
92
|
export const Notifier = {
|
|
66
93
|
show,
|
|
67
|
-
};
|
|
94
|
+
};
|
|
@@ -10,7 +10,7 @@ export const css = {
|
|
|
10
10
|
* Note: noHover state is handled via [data-blok-popover-item-no-hover] which disables hover
|
|
11
11
|
* Priority order: active < hover < focus (focus wins when navigating with keyboard)
|
|
12
12
|
*/
|
|
13
|
-
item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-(--item-padding) text-text-primary mb-0.5 transition-
|
|
13
|
+
item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-(--item-padding) text-text-primary mb-0.5 transition-[color,background-color,border-color,opacity,max-height,padding,margin] duration-150 max-h-10 overflow-hidden data-blok-popover-item-active:bg-icon-active-bg data-blok-popover-item-active:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg data-blok-force-hover:cursor-pointer data-blok-force-hover:bg-item-hover-bg data-[blok-focused="true"]:bg-item-focus-bg data-blok-popover-item-no-hover:hover:bg-transparent data-blok-popover-item-no-hover:cursor-default can-hover:data-blok-popover-item-destructive:hover:text-item-destructive-text can-hover:data-blok-popover-item-destructive:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Item disabled state
|
|
@@ -21,7 +21,7 @@ export const css = {
|
|
|
21
21
|
/**
|
|
22
22
|
* Icon container styles
|
|
23
23
|
*/
|
|
24
|
-
icon: 'flex items-center justify-center w-[26px] h-[26px] rounded-lg [&_svg]:w-icon [&_svg]:h-icon',
|
|
24
|
+
icon: 'flex items-center justify-center w-[26px] h-[26px] shrink-0 rounded-lg [&_svg]:w-icon [&_svg]:h-icon',
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Focused state class for DomIterator/Flipper keyboard navigation.
|
|
@@ -258,8 +258,8 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
258
258
|
const titleEl = document.createElement('div');
|
|
259
259
|
|
|
260
260
|
titleEl.className = params.secondaryLabel
|
|
261
|
-
? 'grow
|
|
262
|
-
: 'mr-auto
|
|
261
|
+
? 'grow whitespace-nowrap text-sm font-medium leading-5'
|
|
262
|
+
: 'mr-auto whitespace-nowrap text-sm font-medium leading-5';
|
|
263
263
|
titleEl.setAttribute(DATA_ATTR.popoverItemTitle, '');
|
|
264
264
|
titleEl.setAttribute('data-blok-testid', 'popover-item-title');
|
|
265
265
|
titleEl.textContent = title;
|
|
@@ -272,7 +272,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
272
272
|
if (params.secondaryLabel) {
|
|
273
273
|
const secondaryEl = document.createElement('div');
|
|
274
274
|
|
|
275
|
-
secondaryEl.className = 'ml-auto flex items-center whitespace-nowrap pl-
|
|
275
|
+
secondaryEl.className = 'ml-auto shrink-0 flex items-center whitespace-nowrap pl-20 text-[11px] font-medium tracking-wide text-text-secondary/50';
|
|
276
276
|
secondaryEl.setAttribute(DATA_ATTR.popoverItemSecondaryTitle, '');
|
|
277
277
|
secondaryEl.setAttribute('data-blok-testid', 'popover-item-secondary-title');
|
|
278
278
|
secondaryEl.textContent = params.secondaryLabel;
|
|
@@ -326,8 +326,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
326
326
|
|
|
327
327
|
return twMerge(
|
|
328
328
|
css.item,
|
|
329
|
-
|
|
330
|
-
!isInline && !isNestedInline && (this.params.secondaryLabel || (this.hasChildren && !this.isChevronHidden) ? 'pl-2 pr-4' : 'pl-2 pr-8'),
|
|
329
|
+
!isInline && !isNestedInline && 'pl-2 pr-4',
|
|
331
330
|
isInline && cssInline.item,
|
|
332
331
|
isNestedInline && cssNestedInline.item,
|
|
333
332
|
this.params.isDisabled && css.itemDisabled
|
|
@@ -385,10 +384,10 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
385
384
|
|
|
386
385
|
if (isHidden) {
|
|
387
386
|
this.nodes.root.setAttribute(DATA_ATTR.hidden, 'true');
|
|
388
|
-
this.nodes.root.classList.add('
|
|
387
|
+
this.nodes.root.classList.add('opacity-0', 'max-h-0!', 'py-0!', 'mb-0!');
|
|
389
388
|
} else {
|
|
390
389
|
this.nodes.root.removeAttribute(DATA_ATTR.hidden);
|
|
391
|
-
this.nodes.root.classList.remove('
|
|
390
|
+
this.nodes.root.classList.remove('opacity-0', 'max-h-0!', 'py-0!', 'mb-0!');
|
|
392
391
|
}
|
|
393
392
|
}
|
|
394
393
|
|