@jackuait/blok 0.6.0 → 0.7.0-beta.2
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-BAh1rvUC.mjs → blok-D9Rs29Wo.mjs} +1928 -1793
- package/dist/chunks/{inline-tool-convert-DduRc0fF.mjs → constants-DmNIR3I8.mjs} +596 -475
- package/dist/chunks/{i18next-loader-CHtGO6IK.mjs → i18next-loader-C2-jYpLi.mjs} +1 -1
- package/dist/chunks/index-D7V1g7Oq.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 +30 -27
- 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 +1233 -801
- package/package.json +1 -1
- package/src/components/constants/data-attributes.ts +7 -0
- 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/icons/index.ts +44 -7
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +44 -2
- package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +54 -2
- package/src/components/modules/blockEvents/constants.ts +12 -0
- 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 +12 -0
- package/src/components/ui/toolbox.ts +26 -2
- 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 +7 -7
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
- package/src/components/utils/popover/components/search-input/search-input.const.ts +1 -1
- package/src/components/utils/popover/popover-abstract.ts +5 -2
- package/src/components/utils/popover/popover-desktop.ts +39 -2
- package/src/components/utils/popover/popover.const.ts +3 -3
- package/src/full.ts +4 -0
- package/src/stories/Placeholder.stories.ts +7 -2
- package/src/stories/helpers.ts +2 -0
- package/src/styles/main.css +64 -10
- package/src/tools/header/index.ts +307 -26
- package/src/tools/index.ts +3 -1
- 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/block-operations.ts +110 -0
- package/src/tools/toggle/constants.ts +49 -0
- package/src/tools/toggle/dom-builder.ts +152 -0
- package/src/tools/toggle/index.ts +302 -0
- package/src/tools/toggle/toggle-keyboard.ts +156 -0
- package/src/tools/toggle/toggle-lifecycle.ts +81 -0
- package/src/tools/toggle/toggle-shortcuts.ts +113 -0
- package/src/tools/toggle/types.ts +21 -0
- package/types/full.d.ts +2 -0
- package/types/tools-entry.d.ts +2 -1
- package/types/utils/popover/popover.d.ts +8 -0
- package/dist/chunks/index-DBWWKrDe.mjs +0 -78
|
@@ -175,6 +175,35 @@ export class BlockOperations {
|
|
|
175
175
|
return previousBlock ?? null;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Get next visible block (skips blocks whose holder has 'hidden' class)
|
|
180
|
+
* Returns null when no visible block is found after the current one
|
|
181
|
+
*/
|
|
182
|
+
public get nextVisibleBlock(): Block | null {
|
|
183
|
+
if (this.currentBlockIndex === -1) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return this.repository.blocks
|
|
188
|
+
.slice(this.currentBlockIndex + 1)
|
|
189
|
+
.find(block => !block.holder.classList.contains('hidden')) ?? null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get previous visible block (skips blocks whose holder has 'hidden' class)
|
|
194
|
+
* Returns null when no visible block is found before the current one
|
|
195
|
+
*/
|
|
196
|
+
public get previousVisibleBlock(): Block | null {
|
|
197
|
+
if (this.currentBlockIndex === -1) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return this.repository.blocks
|
|
202
|
+
.slice(0, this.currentBlockIndex)
|
|
203
|
+
.reverse()
|
|
204
|
+
.find(block => !block.holder.classList.contains('hidden')) ?? null;
|
|
205
|
+
}
|
|
206
|
+
|
|
178
207
|
/**
|
|
179
208
|
* Insert new block
|
|
180
209
|
* @param options - Insert options
|
|
@@ -256,6 +285,20 @@ export class BlockOperations {
|
|
|
256
285
|
|
|
257
286
|
blocksStore.insert(targetIndex, block, replace, appendToWorkingArea);
|
|
258
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Update the raw currentBlockIndex BEFORE firing the mutation event so
|
|
290
|
+
* that listeners (e.g. TableCellBlocks.handleBlockMutation) see the
|
|
291
|
+
* index of the newly inserted block. We bypass the setter to avoid
|
|
292
|
+
* triggering stopCapturing prematurely — that happens after Yjs sync.
|
|
293
|
+
*/
|
|
294
|
+
const prevIndex = this.currentBlockIndex;
|
|
295
|
+
|
|
296
|
+
if (needToFocus) {
|
|
297
|
+
this.currentBlockIndex = targetIndex;
|
|
298
|
+
} else if (targetIndex <= this.currentBlockIndex) {
|
|
299
|
+
this.currentBlockIndex++;
|
|
300
|
+
}
|
|
301
|
+
|
|
259
302
|
/**
|
|
260
303
|
* Force call of didMutated event on Block insertion
|
|
261
304
|
*/
|
|
@@ -277,12 +320,12 @@ export class BlockOperations {
|
|
|
277
320
|
}, targetIndex);
|
|
278
321
|
}
|
|
279
322
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
this.
|
|
323
|
+
/**
|
|
324
|
+
* Trigger stopCapturing for the index change now that Yjs sync is done.
|
|
325
|
+
* This preserves undo group boundaries at the original timing.
|
|
326
|
+
*/
|
|
327
|
+
if (this.currentBlockIndex !== prevIndex && !this.suppressStopCapturing) {
|
|
328
|
+
this.dependencies.YjsManager?.stopCapturing();
|
|
286
329
|
}
|
|
287
330
|
|
|
288
331
|
return block;
|
|
@@ -349,6 +392,18 @@ export class BlockOperations {
|
|
|
349
392
|
parentBlock.contentIds = parentBlock.contentIds.filter(id => id !== block.id);
|
|
350
393
|
}
|
|
351
394
|
|
|
395
|
+
// Promote children to root level when a parent block is removed
|
|
396
|
+
for (const childId of block.contentIds) {
|
|
397
|
+
const childBlock = this.repository.getBlockById(childId);
|
|
398
|
+
|
|
399
|
+
if (childBlock === undefined) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
childBlock.parentId = null;
|
|
404
|
+
childBlock.holder.classList.remove('hidden');
|
|
405
|
+
}
|
|
406
|
+
|
|
352
407
|
blocksStore.remove(index);
|
|
353
408
|
|
|
354
409
|
/**
|
|
@@ -416,6 +471,8 @@ export class BlockOperations {
|
|
|
416
471
|
tool: block.name,
|
|
417
472
|
data: Object.assign({}, existingData, data ?? {}),
|
|
418
473
|
tunes: tunes ?? block.preservedTunes,
|
|
474
|
+
parentId: block.parentId ?? undefined,
|
|
475
|
+
contentIds: block.contentIds.length > 0 ? [...block.contentIds] : undefined,
|
|
419
476
|
bindEventsImmediately: true,
|
|
420
477
|
});
|
|
421
478
|
|
|
@@ -455,6 +512,10 @@ export class BlockOperations {
|
|
|
455
512
|
const blockIndex = this.repository.getBlockIndex(block);
|
|
456
513
|
const newBlockId = generateBlockId();
|
|
457
514
|
|
|
515
|
+
// Capture hierarchy before replacement
|
|
516
|
+
const oldParentId = block.parentId;
|
|
517
|
+
const oldContentIds = [...block.contentIds];
|
|
518
|
+
|
|
458
519
|
// Atomic transaction: remove old block + add new block as single undo entry
|
|
459
520
|
this.dependencies.YjsManager.transact(() => {
|
|
460
521
|
this.dependencies.YjsManager.removeBlock(block.id);
|
|
@@ -466,7 +527,7 @@ export class BlockOperations {
|
|
|
466
527
|
});
|
|
467
528
|
|
|
468
529
|
// DOM update (skip Yjs sync — already done above)
|
|
469
|
-
|
|
530
|
+
const newBlock = this.insert({
|
|
470
531
|
id: newBlockId,
|
|
471
532
|
tool: newTool,
|
|
472
533
|
data,
|
|
@@ -474,6 +535,28 @@ export class BlockOperations {
|
|
|
474
535
|
replace: true,
|
|
475
536
|
skipYjsSync: true,
|
|
476
537
|
}, blocksStore);
|
|
538
|
+
|
|
539
|
+
// Transfer hierarchy to new block
|
|
540
|
+
if (oldParentId !== null) {
|
|
541
|
+
newBlock.parentId = oldParentId;
|
|
542
|
+
|
|
543
|
+
const parentBlock = this.repository.getBlockById(oldParentId);
|
|
544
|
+
|
|
545
|
+
if (parentBlock !== undefined) {
|
|
546
|
+
parentBlock.contentIds = parentBlock.contentIds.map(id => id === block.id ? newBlock.id : id);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
for (const childId of oldContentIds) {
|
|
551
|
+
const childBlock = this.repository.getBlockById(childId);
|
|
552
|
+
|
|
553
|
+
if (childBlock !== undefined) {
|
|
554
|
+
childBlock.parentId = newBlock.id;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
newBlock.contentIds = oldContentIds;
|
|
558
|
+
|
|
559
|
+
return newBlock;
|
|
477
560
|
}
|
|
478
561
|
|
|
479
562
|
/**
|
|
@@ -663,12 +746,19 @@ export class BlockOperations {
|
|
|
663
746
|
});
|
|
664
747
|
|
|
665
748
|
// Insert DOM block (skip Yjs sync - already done above)
|
|
666
|
-
|
|
749
|
+
const newBlock = this.insert({
|
|
667
750
|
id: newBlockId,
|
|
668
751
|
tool: currentBlock.name,
|
|
669
752
|
data: { text: extractedText },
|
|
670
753
|
skipYjsSync: true,
|
|
671
754
|
}, blocksStore);
|
|
755
|
+
|
|
756
|
+
// Inherit parentId from the split block so nested blocks stay nested
|
|
757
|
+
if (currentBlock.parentId !== null) {
|
|
758
|
+
this.hierarchy.setBlockParent(newBlock, currentBlock.parentId);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return newBlock;
|
|
672
762
|
});
|
|
673
763
|
}
|
|
674
764
|
|
|
@@ -722,7 +812,7 @@ export class BlockOperations {
|
|
|
722
812
|
}
|
|
723
813
|
|
|
724
814
|
// Insert DOM block (skip Yjs sync - already done above)
|
|
725
|
-
|
|
815
|
+
const newBlock = this.insert({
|
|
726
816
|
id: newBlockId,
|
|
727
817
|
tool: newBlockType,
|
|
728
818
|
data: newBlockData,
|
|
@@ -730,6 +820,13 @@ export class BlockOperations {
|
|
|
730
820
|
needToFocus: true,
|
|
731
821
|
skipYjsSync: true,
|
|
732
822
|
}, blocksStore);
|
|
823
|
+
|
|
824
|
+
// Inherit parentId from the split block so nested blocks stay nested
|
|
825
|
+
if (currentBlock.parentId !== null) {
|
|
826
|
+
this.hierarchy.setBlockParent(newBlock, currentBlock.parentId);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return newBlock;
|
|
733
830
|
});
|
|
734
831
|
}
|
|
735
832
|
|
|
@@ -336,6 +336,8 @@ export class BlockSelection extends Module {
|
|
|
336
336
|
tool: block.name,
|
|
337
337
|
data: block.preservedData,
|
|
338
338
|
tunes: block.preservedTunes,
|
|
339
|
+
parentId: block.parentId,
|
|
340
|
+
contentIds: block.contentIds,
|
|
339
341
|
}));
|
|
340
342
|
|
|
341
343
|
this.selectedBlocks.forEach((block) => {
|
|
@@ -499,7 +499,7 @@ export class Caret extends Module {
|
|
|
499
499
|
*/
|
|
500
500
|
public navigateNext(force = false): boolean {
|
|
501
501
|
const { BlockManager } = this.Blok;
|
|
502
|
-
const { currentBlock, nextBlock } = BlockManager;
|
|
502
|
+
const { currentBlock, nextVisibleBlock: nextBlock } = BlockManager;
|
|
503
503
|
|
|
504
504
|
if (currentBlock === undefined) {
|
|
505
505
|
return false;
|
|
@@ -567,7 +567,7 @@ export class Caret extends Module {
|
|
|
567
567
|
* @param {boolean} force - pass true to skip check for caret position
|
|
568
568
|
*/
|
|
569
569
|
public navigatePrevious(force = false): boolean {
|
|
570
|
-
const { currentBlock, previousBlock } = this.Blok.BlockManager;
|
|
570
|
+
const { currentBlock, previousVisibleBlock: previousBlock } = this.Blok.BlockManager;
|
|
571
571
|
|
|
572
572
|
if (!currentBlock) {
|
|
573
573
|
return false;
|
|
@@ -634,7 +634,7 @@ export class Caret extends Module {
|
|
|
634
634
|
*/
|
|
635
635
|
public navigateVerticalNext(): boolean {
|
|
636
636
|
const { BlockManager } = this.Blok;
|
|
637
|
-
const { currentBlock, nextBlock } = BlockManager;
|
|
637
|
+
const { currentBlock, nextVisibleBlock: nextBlock } = BlockManager;
|
|
638
638
|
|
|
639
639
|
if (currentBlock === undefined) {
|
|
640
640
|
return false;
|
|
@@ -681,6 +681,35 @@ export class Caret extends Module {
|
|
|
681
681
|
return true;
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
/**
|
|
685
|
+
* If current block is inside a table cell (has parentId), check if we should
|
|
686
|
+
* exit the table. This handles two cases:
|
|
687
|
+
* 1. nextBlock is still in the same table → skip all cell paragraphs
|
|
688
|
+
* 2. nextBlock is null (last block in flat list) → table is at the end
|
|
689
|
+
*/
|
|
690
|
+
const shouldExitParent = currentBlock.parentId !== null && (
|
|
691
|
+
nextBlock === null ||
|
|
692
|
+
nextBlock.parentId === currentBlock.parentId ||
|
|
693
|
+
nextBlock.id === currentBlock.parentId
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
if (shouldExitParent && currentBlock.parentId !== null) {
|
|
697
|
+
const blockAfterTable = this.findFirstBlockAfterParent(currentBlock.parentId);
|
|
698
|
+
|
|
699
|
+
if (blockAfterTable !== null) {
|
|
700
|
+
this.setToBlockAtXPosition(blockAfterTable, caretX, true);
|
|
701
|
+
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// No block after table — create one
|
|
706
|
+
const newBlock = BlockManager.insertAtEnd();
|
|
707
|
+
|
|
708
|
+
this.setToBlock(newBlock, this.positions.START);
|
|
709
|
+
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
|
|
684
713
|
/**
|
|
685
714
|
* Navigate to next block, preserving horizontal position
|
|
686
715
|
*/
|
|
@@ -714,7 +743,7 @@ export class Caret extends Module {
|
|
|
714
743
|
*/
|
|
715
744
|
public navigateVerticalPrevious(): boolean {
|
|
716
745
|
const { BlockManager } = this.Blok;
|
|
717
|
-
const { currentBlock, previousBlock } = BlockManager;
|
|
746
|
+
const { currentBlock, previousVisibleBlock: previousBlock } = BlockManager;
|
|
718
747
|
|
|
719
748
|
if (currentBlock === undefined) {
|
|
720
749
|
return false;
|
|
@@ -773,6 +802,22 @@ export class Caret extends Module {
|
|
|
773
802
|
return false;
|
|
774
803
|
}
|
|
775
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Find the first block after a parent block (e.g., a table) by scanning the flat
|
|
807
|
+
* block array and skipping blocks whose parentId matches.
|
|
808
|
+
*/
|
|
809
|
+
private findFirstBlockAfterParent(parentBlockId: string): Block | null {
|
|
810
|
+
const { BlockManager } = this.Blok;
|
|
811
|
+
const blocks = BlockManager.blocks;
|
|
812
|
+
const parentIndex = blocks.findIndex(b => b.id === parentBlockId);
|
|
813
|
+
|
|
814
|
+
if (parentIndex === -1) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return blocks.slice(parentIndex + 1).find(b => b.parentId !== parentBlockId) ?? null;
|
|
819
|
+
}
|
|
820
|
+
|
|
776
821
|
/**
|
|
777
822
|
* Inserts shadow element after passed element where caret can be placed
|
|
778
823
|
* @param {Element} element - element after which shadow caret should be inserted
|
|
@@ -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
|
|
@@ -158,6 +160,15 @@ export class UI extends Module<UINodes> {
|
|
|
158
160
|
*/
|
|
159
161
|
this.initializeControllers();
|
|
160
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Register toggle shortcuts (CMD+ALT+T) for collapsing/expanding all toggle blocks
|
|
165
|
+
*/
|
|
166
|
+
this.toggleShortcuts = new ToggleShortcuts(
|
|
167
|
+
this.Blok.API.methods,
|
|
168
|
+
this.nodes.wrapper
|
|
169
|
+
);
|
|
170
|
+
this.toggleShortcuts.register();
|
|
171
|
+
|
|
161
172
|
/**
|
|
162
173
|
* Enable selection controller after initialization.
|
|
163
174
|
* This is needed because bindReadOnlyInsensitiveListeners() is called in make()
|
|
@@ -343,6 +354,7 @@ export class UI extends Module<UINodes> {
|
|
|
343
354
|
* Clean blok`s UI
|
|
344
355
|
*/
|
|
345
356
|
public destroy(): void {
|
|
357
|
+
this.toggleShortcuts?.unregister();
|
|
346
358
|
this.nodes.holder.innerHTML = '';
|
|
347
359
|
|
|
348
360
|
this.unbindReadOnlyInsensitiveListeners();
|
|
@@ -14,6 +14,7 @@ import { translateToolTitle, type I18nInstance } from '../utils/tools';
|
|
|
14
14
|
|
|
15
15
|
import type { API, BlockToolData, ToolboxConfigEntry, PopoverItemParams, BlockAPI } from '@/types';
|
|
16
16
|
import { PopoverEvent } from '@/types/utils/popover/popover-event';
|
|
17
|
+
import { DATA_ATTR } from '../constants';
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -56,7 +57,7 @@ export interface ToolboxEventMap {
|
|
|
56
57
|
/**
|
|
57
58
|
* Available i18n dict keys that should be passed to the constructor
|
|
58
59
|
*/
|
|
59
|
-
type ToolboxTextLabelsKeys = 'filter' | 'nothingFound';
|
|
60
|
+
type ToolboxTextLabelsKeys = 'filter' | 'nothingFound' | 'slashSearchPlaceholder';
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
63
|
* Toolbox
|
|
@@ -144,6 +145,11 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
144
145
|
*/
|
|
145
146
|
private triggerElement?: HTMLElement;
|
|
146
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Optional element whose left edge is used for horizontal popover alignment.
|
|
150
|
+
*/
|
|
151
|
+
private leftAlignElement?: HTMLElement;
|
|
152
|
+
|
|
147
153
|
/**
|
|
148
154
|
* The block element currently being listened to for inline slash search
|
|
149
155
|
*/
|
|
@@ -168,13 +174,15 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
168
174
|
* @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not
|
|
169
175
|
* @param options.i18n - I18n instance for translations
|
|
170
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
|
|
171
178
|
*/
|
|
172
|
-
constructor({ api, tools, i18nLabels, i18n, triggerElement }: {
|
|
179
|
+
constructor({ api, tools, i18nLabels, i18n, triggerElement, leftAlignElement }: {
|
|
173
180
|
api: API;
|
|
174
181
|
tools: ToolsCollection<BlockToolAdapter>;
|
|
175
182
|
i18nLabels: Record<ToolboxTextLabelsKeys, string>;
|
|
176
183
|
i18n: I18nInstance;
|
|
177
184
|
triggerElement?: HTMLElement;
|
|
185
|
+
leftAlignElement?: HTMLElement;
|
|
178
186
|
}) {
|
|
179
187
|
super();
|
|
180
188
|
|
|
@@ -183,6 +191,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
183
191
|
this.i18nLabels = i18nLabels;
|
|
184
192
|
this.i18n = i18n;
|
|
185
193
|
this.triggerElement = triggerElement;
|
|
194
|
+
this.leftAlignElement = leftAlignElement;
|
|
186
195
|
|
|
187
196
|
this.enableShortcuts();
|
|
188
197
|
|
|
@@ -311,6 +320,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
311
320
|
this.isInsideTableCell = false;
|
|
312
321
|
}
|
|
313
322
|
|
|
323
|
+
this.stopListeningToBlockInput();
|
|
314
324
|
this.popover?.hide();
|
|
315
325
|
this.opened = false;
|
|
316
326
|
this.emit(ToolboxEvent.Closed);
|
|
@@ -344,6 +354,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
344
354
|
this.popover = new PopoverClass({
|
|
345
355
|
scopeElement: this.api.ui.nodes.redactor,
|
|
346
356
|
trigger: this.triggerElement || this.nodes.toolbox,
|
|
357
|
+
leftAlignElement: this.leftAlignElement,
|
|
347
358
|
messages: {
|
|
348
359
|
nothingFound: this.i18nLabels.nothingFound,
|
|
349
360
|
search: this.i18nLabels.filter,
|
|
@@ -630,6 +641,9 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
630
641
|
|
|
631
642
|
this.currentBlockForSearch = currentBlock.holder;
|
|
632
643
|
this.currentContentEditable = this.currentBlockForSearch.querySelector('[contenteditable="true"]');
|
|
644
|
+
if (this.currentContentEditable instanceof HTMLElement) {
|
|
645
|
+
this.currentContentEditable.setAttribute(DATA_ATTR.slashSearch, this.i18nLabels.slashSearchPlaceholder);
|
|
646
|
+
}
|
|
633
647
|
this.listeners.on(this.currentBlockForSearch, 'input', this.handleBlockInput);
|
|
634
648
|
}
|
|
635
649
|
|
|
@@ -639,6 +653,9 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
639
653
|
private stopListeningToBlockInput(): void {
|
|
640
654
|
if (this.currentBlockForSearch !== null) {
|
|
641
655
|
this.listeners.off(this.currentBlockForSearch, 'input', this.handleBlockInput);
|
|
656
|
+
if (this.currentContentEditable instanceof HTMLElement) {
|
|
657
|
+
this.currentContentEditable.removeAttribute(DATA_ATTR.slashSearch);
|
|
658
|
+
}
|
|
642
659
|
this.currentBlockForSearch = null;
|
|
643
660
|
this.currentContentEditable = null;
|
|
644
661
|
}
|
|
@@ -666,6 +683,13 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
666
683
|
|
|
667
684
|
const query = text.slice(slashIndex + 1);
|
|
668
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
|
+
|
|
669
693
|
this.popover?.filterItems(query);
|
|
670
694
|
};
|
|
671
695
|
|