@jackuait/blok 0.6.0-beta.9 → 0.7.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-Bn6Q_o8h.mjs → blok-ob9Fwr1L.mjs} +3414 -2975
- package/dist/chunks/i18next-B47TKgbU.mjs +1303 -0
- package/dist/chunks/{i18next-loader-DjR4d8M7.mjs → i18next-loader-Bu3vFvye.mjs} +2 -2
- package/dist/chunks/{index-oe38cp86.mjs → index-CZmRzRIX.mjs} +12 -12
- package/dist/chunks/{inline-tool-convert-SRTkyaZn.mjs → inline-tool-convert-CvFW2iie.mjs} +1579 -961
- package/dist/chunks/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
- package/dist/chunks/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
- package/dist/{messages-DnXLrlHh.mjs → chunks/messages-4v4MuVEc.mjs} +6 -0
- package/dist/chunks/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
- package/dist/chunks/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
- package/dist/chunks/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
- package/dist/{messages-uKX8WBaD.mjs → chunks/messages-96kNZDll.mjs} +6 -0
- package/dist/chunks/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
- package/dist/{messages-DBn76jVV.mjs → chunks/messages-B217znr-.mjs} +8 -2
- package/dist/{messages-DT4dP5uK.mjs → chunks/messages-B8WNljW3.mjs} +6 -0
- package/dist/chunks/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
- package/dist/{messages-CZygwLwM.mjs → chunks/messages-BI43k_BD.mjs} +6 -0
- package/dist/{messages-CzTufCHu.mjs → chunks/messages-BJ6zrz2j.mjs} +6 -0
- package/dist/{messages-BoJc_p1r.mjs → chunks/messages-BUl_Rcnj.mjs} +6 -0
- package/dist/chunks/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
- package/dist/{messages-C2htQ_3F.mjs → chunks/messages-BcpCubnC.mjs} +6 -0
- package/dist/{messages-D5C3J9qr.mjs → chunks/messages-Bm-E4iRC.mjs} +6 -0
- package/dist/chunks/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
- package/dist/chunks/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
- package/dist/chunks/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
- package/dist/{messages-Dz9L52ol.mjs → chunks/messages-CRJ_mchV.mjs} +6 -0
- package/dist/chunks/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
- package/dist/chunks/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
- package/dist/{messages-R3hUSvr3.mjs → chunks/messages-CfiyT2Wi.mjs} +6 -0
- package/dist/{messages-CJdUsQ-c.mjs → chunks/messages-CgTq3QhU.mjs} +6 -0
- package/dist/chunks/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
- package/dist/{messages-Q7AO_FLv.mjs → chunks/messages-Cjjo7yHR.mjs} +6 -0
- package/dist/{messages-C99mq906.mjs → chunks/messages-Cl6ayUaq.mjs} +6 -0
- package/dist/chunks/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
- package/dist/chunks/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
- package/dist/chunks/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
- package/dist/{messages-BWF-zUpY.mjs → chunks/messages-CrCYPCk3.mjs} +6 -0
- package/dist/{messages-D-ZtY5v0.mjs → chunks/messages-Cs8zmZ3L.mjs} +6 -0
- package/dist/{messages-DprmQg6V.mjs → chunks/messages-CzK0LEhb.mjs} +6 -0
- package/dist/chunks/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
- package/dist/chunks/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
- package/dist/chunks/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
- package/dist/{messages-kep5wtm4.mjs → chunks/messages-D4qqwVgQ.mjs} +6 -0
- package/dist/chunks/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
- package/dist/{messages-CY8_RyFE.mjs → chunks/messages-D7u2bmP2.mjs} +6 -0
- package/dist/chunks/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
- package/dist/{messages-DLfR5bMd.mjs → chunks/messages-D9ndgBnU.mjs} +6 -0
- package/dist/{messages-CVw84KdI.mjs → chunks/messages-DDTQgImT.mjs} +6 -0
- package/dist/{messages-_ErNTNhk.mjs → chunks/messages-DH_jBeED.mjs} +6 -0
- package/dist/chunks/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
- package/dist/chunks/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
- package/dist/chunks/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
- package/dist/chunks/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
- package/dist/chunks/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
- package/dist/chunks/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
- package/dist/{messages-Z9nEU2xK.mjs → chunks/messages-DviiFSv2.mjs} +6 -0
- package/dist/chunks/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
- package/dist/{messages-w7v1GNaE.mjs → chunks/messages-Dzzn6XoD.mjs} +6 -0
- package/dist/{messages-CqWJcCbY.mjs → chunks/messages-GSByFygY.mjs} +6 -0
- package/dist/chunks/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
- package/dist/chunks/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
- package/dist/{messages-BU2nlrLK.mjs → chunks/messages-RonBBCnh.mjs} +6 -0
- package/dist/{messages-Bmu_S7GM.mjs → chunks/messages-VDriF5Qy.mjs} +6 -0
- package/dist/{messages-CLhcMlTc.mjs → chunks/messages-ZjUAIWb1.mjs} +6 -0
- package/dist/{messages-9SihnaXQ.mjs → chunks/messages-b1EdvUm0.mjs} +6 -0
- package/dist/{messages-DvFLX36Q.mjs → chunks/messages-begYOTgC.mjs} +6 -0
- package/dist/{messages-BMv4xwIr.mjs → chunks/messages-jrncnb-H.mjs} +6 -0
- package/dist/{messages-D5iv1Kox.mjs → chunks/messages-nefz1S71.mjs} +6 -0
- package/dist/{messages-CQwpzUFp.mjs → chunks/messages-ucTVgS5G.mjs} +6 -0
- package/dist/chunks/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
- package/dist/{messages-C9eaarcK.mjs → chunks/messages-wmi-iFkH.mjs} +6 -0
- package/dist/chunks/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
- package/dist/full.mjs +30 -27
- package/dist/locales.mjs +90 -84
- package/dist/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
- package/dist/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
- package/dist/{chunks/messages-DnXLrlHh.mjs → messages-4v4MuVEc.mjs} +6 -0
- package/dist/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
- package/dist/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
- package/dist/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
- package/dist/{chunks/messages-uKX8WBaD.mjs → messages-96kNZDll.mjs} +6 -0
- package/dist/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
- package/dist/{chunks/messages-DBn76jVV.mjs → messages-B217znr-.mjs} +8 -2
- package/dist/{chunks/messages-DT4dP5uK.mjs → messages-B8WNljW3.mjs} +6 -0
- package/dist/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
- package/dist/{chunks/messages-CZygwLwM.mjs → messages-BI43k_BD.mjs} +6 -0
- package/dist/{chunks/messages-CzTufCHu.mjs → messages-BJ6zrz2j.mjs} +6 -0
- package/dist/{chunks/messages-BoJc_p1r.mjs → messages-BUl_Rcnj.mjs} +6 -0
- package/dist/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
- package/dist/{chunks/messages-C2htQ_3F.mjs → messages-BcpCubnC.mjs} +6 -0
- package/dist/{chunks/messages-D5C3J9qr.mjs → messages-Bm-E4iRC.mjs} +6 -0
- package/dist/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
- package/dist/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
- package/dist/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
- package/dist/{chunks/messages-Dz9L52ol.mjs → messages-CRJ_mchV.mjs} +6 -0
- package/dist/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
- package/dist/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
- package/dist/{chunks/messages-R3hUSvr3.mjs → messages-CfiyT2Wi.mjs} +6 -0
- package/dist/{chunks/messages-CJdUsQ-c.mjs → messages-CgTq3QhU.mjs} +6 -0
- package/dist/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
- package/dist/{chunks/messages-Q7AO_FLv.mjs → messages-Cjjo7yHR.mjs} +6 -0
- package/dist/{chunks/messages-C99mq906.mjs → messages-Cl6ayUaq.mjs} +6 -0
- package/dist/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
- package/dist/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
- package/dist/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
- package/dist/{chunks/messages-BWF-zUpY.mjs → messages-CrCYPCk3.mjs} +6 -0
- package/dist/{chunks/messages-D-ZtY5v0.mjs → messages-Cs8zmZ3L.mjs} +6 -0
- package/dist/{chunks/messages-DprmQg6V.mjs → messages-CzK0LEhb.mjs} +6 -0
- package/dist/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
- package/dist/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
- package/dist/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
- package/dist/{chunks/messages-kep5wtm4.mjs → messages-D4qqwVgQ.mjs} +6 -0
- package/dist/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
- package/dist/{chunks/messages-CY8_RyFE.mjs → messages-D7u2bmP2.mjs} +6 -0
- package/dist/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
- package/dist/{chunks/messages-DLfR5bMd.mjs → messages-D9ndgBnU.mjs} +6 -0
- package/dist/{chunks/messages-CVw84KdI.mjs → messages-DDTQgImT.mjs} +6 -0
- package/dist/{chunks/messages-_ErNTNhk.mjs → messages-DH_jBeED.mjs} +6 -0
- package/dist/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
- package/dist/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
- package/dist/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
- package/dist/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
- package/dist/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
- package/dist/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
- package/dist/{chunks/messages-Z9nEU2xK.mjs → messages-DviiFSv2.mjs} +6 -0
- package/dist/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
- package/dist/{chunks/messages-w7v1GNaE.mjs → messages-Dzzn6XoD.mjs} +6 -0
- package/dist/{chunks/messages-CqWJcCbY.mjs → messages-GSByFygY.mjs} +6 -0
- package/dist/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
- package/dist/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
- package/dist/{chunks/messages-BU2nlrLK.mjs → messages-RonBBCnh.mjs} +6 -0
- package/dist/{chunks/messages-Bmu_S7GM.mjs → messages-VDriF5Qy.mjs} +6 -0
- package/dist/{chunks/messages-CLhcMlTc.mjs → messages-ZjUAIWb1.mjs} +6 -0
- package/dist/{chunks/messages-9SihnaXQ.mjs → messages-b1EdvUm0.mjs} +6 -0
- package/dist/{chunks/messages-DvFLX36Q.mjs → messages-begYOTgC.mjs} +6 -0
- package/dist/{chunks/messages-BMv4xwIr.mjs → messages-jrncnb-H.mjs} +6 -0
- package/dist/{chunks/messages-D5iv1Kox.mjs → messages-nefz1S71.mjs} +6 -0
- package/dist/{chunks/messages-CQwpzUFp.mjs → messages-ucTVgS5G.mjs} +6 -0
- package/dist/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
- package/dist/{chunks/messages-C9eaarcK.mjs → messages-wmi-iFkH.mjs} +6 -0
- package/dist/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
- package/dist/tools.mjs +3537 -1710
- package/dist/vendor.LICENSE.txt +109 -109
- package/package.json +43 -57
- package/src/blok.ts +12 -0
- package/src/components/__module.ts +21 -0
- package/src/components/block/api.ts +17 -0
- package/src/components/block/style-manager.ts +6 -2
- package/src/components/block/tool-renderer.ts +33 -30
- package/src/components/blocks.ts +132 -15
- package/src/components/constants/data-attributes.ts +7 -0
- package/src/components/i18n/locales/am/messages.json +6 -0
- package/src/components/i18n/locales/ar/messages.json +6 -0
- package/src/components/i18n/locales/az/messages.json +6 -0
- package/src/components/i18n/locales/bg/messages.json +6 -0
- package/src/components/i18n/locales/bn/messages.json +6 -0
- package/src/components/i18n/locales/bs/messages.json +6 -0
- package/src/components/i18n/locales/cs/messages.json +6 -0
- package/src/components/i18n/locales/da/messages.json +6 -0
- package/src/components/i18n/locales/de/messages.json +6 -0
- package/src/components/i18n/locales/dv/messages.json +6 -0
- package/src/components/i18n/locales/el/messages.json +6 -0
- package/src/components/i18n/locales/en/messages.json +6 -0
- package/src/components/i18n/locales/es/messages.json +6 -0
- package/src/components/i18n/locales/et/messages.json +6 -0
- package/src/components/i18n/locales/fa/messages.json +6 -0
- package/src/components/i18n/locales/fi/messages.json +6 -0
- package/src/components/i18n/locales/fil/messages.json +6 -0
- package/src/components/i18n/locales/fr/messages.json +6 -0
- package/src/components/i18n/locales/gu/messages.json +6 -0
- package/src/components/i18n/locales/he/messages.json +6 -0
- package/src/components/i18n/locales/hi/messages.json +6 -0
- package/src/components/i18n/locales/hr/messages.json +6 -0
- package/src/components/i18n/locales/hu/messages.json +6 -0
- package/src/components/i18n/locales/hy/messages.json +6 -0
- package/src/components/i18n/locales/id/messages.json +6 -0
- package/src/components/i18n/locales/it/messages.json +6 -0
- package/src/components/i18n/locales/ja/messages.json +6 -0
- package/src/components/i18n/locales/ka/messages.json +6 -0
- package/src/components/i18n/locales/km/messages.json +6 -0
- package/src/components/i18n/locales/kn/messages.json +6 -0
- package/src/components/i18n/locales/ko/messages.json +6 -0
- package/src/components/i18n/locales/ku/messages.json +6 -0
- package/src/components/i18n/locales/lo/messages.json +6 -0
- package/src/components/i18n/locales/lt/messages.json +6 -0
- package/src/components/i18n/locales/lv/messages.json +6 -0
- package/src/components/i18n/locales/mk/messages.json +6 -0
- package/src/components/i18n/locales/ml/messages.json +6 -0
- package/src/components/i18n/locales/mn/messages.json +6 -0
- package/src/components/i18n/locales/mr/messages.json +6 -0
- package/src/components/i18n/locales/ms/messages.json +6 -0
- package/src/components/i18n/locales/my/messages.json +6 -0
- package/src/components/i18n/locales/ne/messages.json +6 -0
- package/src/components/i18n/locales/nl/messages.json +6 -0
- package/src/components/i18n/locales/no/messages.json +6 -0
- package/src/components/i18n/locales/pa/messages.json +6 -0
- package/src/components/i18n/locales/pl/messages.json +6 -0
- package/src/components/i18n/locales/ps/messages.json +6 -0
- package/src/components/i18n/locales/pt/messages.json +6 -0
- package/src/components/i18n/locales/ro/messages.json +6 -0
- package/src/components/i18n/locales/ru/messages.json +6 -0
- package/src/components/i18n/locales/sd/messages.json +6 -0
- package/src/components/i18n/locales/si/messages.json +6 -0
- package/src/components/i18n/locales/sk/messages.json +6 -0
- package/src/components/i18n/locales/sl/messages.json +6 -0
- package/src/components/i18n/locales/sq/messages.json +6 -0
- package/src/components/i18n/locales/sr/messages.json +6 -0
- package/src/components/i18n/locales/sv/messages.json +6 -0
- package/src/components/i18n/locales/sw/messages.json +6 -0
- package/src/components/i18n/locales/ta/messages.json +6 -0
- package/src/components/i18n/locales/te/messages.json +6 -0
- package/src/components/i18n/locales/th/messages.json +6 -0
- package/src/components/i18n/locales/tr/messages.json +6 -0
- package/src/components/i18n/locales/ug/messages.json +6 -0
- package/src/components/i18n/locales/uk/messages.json +6 -0
- package/src/components/i18n/locales/ur/messages.json +6 -0
- package/src/components/i18n/locales/vi/messages.json +6 -0
- package/src/components/i18n/locales/yi/messages.json +6 -0
- package/src/components/i18n/locales/zh/messages.json +6 -0
- package/src/components/icons/index.ts +61 -7
- package/src/components/inline-tools/inline-tool-link.ts +1 -1
- package/src/components/inline-tools/inline-tool-marker.ts +737 -0
- package/src/components/inline-tools/utils/formatting-range-utils.ts +6 -3
- package/src/components/inline-tools/utils/marker-dom-utils.ts +17 -0
- package/src/components/modules/api/blocks.ts +34 -9
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +75 -29
- 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 +13 -5
- package/src/components/modules/blockManager/blockManager.ts +81 -2
- package/src/components/modules/blockManager/hierarchy.ts +20 -2
- package/src/components/modules/blockManager/operations.ts +70 -35
- package/src/components/modules/blockManager/repository.ts +22 -0
- package/src/components/modules/blockManager/types.ts +3 -1
- package/src/components/modules/blockManager/yjs-sync.ts +173 -39
- package/src/components/modules/blockSelection.ts +3 -0
- package/src/components/modules/crossBlockSelection.ts +11 -3
- package/src/components/modules/drag/preview/DragPreview.ts +10 -2
- package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
- package/src/components/modules/drag/utils/drag.constants.ts +1 -1
- package/src/components/modules/normalizeInlineImages.ts +263 -0
- package/src/components/modules/paste/google-docs-preprocessor.ts +197 -0
- package/src/components/modules/paste/handlers/base.ts +43 -2
- package/src/components/modules/paste/handlers/html-handler.ts +1 -1
- package/src/components/modules/paste/handlers/index.ts +1 -0
- package/src/components/modules/paste/handlers/table-cells-handler.ts +104 -0
- package/src/components/modules/paste/index.ts +20 -3
- package/src/components/modules/readonly.ts +8 -2
- package/src/components/modules/rectangleSelection.ts +5 -2
- package/src/components/modules/renderer.ts +35 -0
- package/src/components/modules/saver.ts +52 -2
- package/src/components/modules/toolbar/blockSettings.ts +52 -44
- package/src/components/modules/toolbar/index.ts +124 -17
- package/src/components/modules/toolbar/inline/index.ts +4 -4
- package/src/components/modules/toolbar/plus-button.ts +3 -3
- package/src/components/modules/toolbar/settings-toggler.ts +3 -3
- package/src/components/modules/toolbar/styles.ts +7 -7
- package/src/components/modules/ui.ts +6 -6
- package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
- package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
- package/src/components/modules/yjs/block-observer.ts +9 -3
- package/src/components/modules/yjs/document-store.ts +10 -7
- package/src/components/modules/yjs/types.ts +8 -6
- package/src/components/modules/yjs/undo-history.ts +90 -11
- package/src/components/selection/fake-background/shadows.ts +1 -1
- package/src/components/shared/color-picker.ts +211 -0
- package/src/components/shared/color-presets.ts +25 -0
- package/src/components/ui/toolbox.ts +27 -11
- package/src/components/utils/color-mapping.ts +241 -0
- package/src/components/utils/notifier/draw.ts +9 -9
- package/src/components/utils/placeholder.ts +24 -8
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +3 -3
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +15 -12
- package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
- package/src/components/utils/popover/popover-abstract.ts +30 -5
- package/src/components/utils/popover/popover-desktop.ts +26 -3
- package/src/components/utils/popover/popover-inline.ts +14 -1
- package/src/components/utils/popover/popover-mobile.ts +4 -4
- package/src/components/utils/popover/popover.const.ts +3 -3
- package/src/components/utils/sanitizer.ts +24 -3
- package/src/components/utils/tw.ts +17 -5
- package/src/full.ts +4 -0
- package/src/stories/Header.stories.ts +106 -0
- package/src/stories/MarkerColors.stories.ts +730 -0
- package/src/stories/Placeholder.stories.ts +7 -2
- package/src/stories/Popover.stories.ts +1 -3
- package/src/stories/Table.stories.ts +1662 -0
- package/src/stories/helpers.ts +2 -0
- package/src/styles/main.css +217 -39
- package/src/tools/header/index.ts +204 -26
- package/src/tools/index.ts +5 -1
- package/src/tools/list/caret-manager.ts +28 -10
- package/src/tools/list/constants.ts +2 -2
- package/src/tools/list/dom-builder.ts +3 -3
- package/src/tools/list/static-configs.ts +0 -1
- package/src/tools/paragraph/index.ts +9 -5
- package/src/tools/table/core/table-commands.ts +99 -0
- package/src/tools/table/core/table-controller.ts +231 -0
- package/src/tools/table/core/table-events.ts +102 -0
- package/src/tools/table/index.ts +1070 -174
- package/src/tools/table/ownership/table-event-broker.ts +74 -0
- package/src/tools/table/ownership/table-ownership-registry.ts +126 -0
- package/src/tools/table/table-add-controls.ts +85 -15
- package/src/tools/table/table-cell-blocks.ts +336 -38
- package/src/tools/table/table-cell-clipboard.ts +415 -0
- package/src/tools/table/table-cell-color-picker.ts +34 -0
- package/src/tools/table/table-cell-selection.ts +264 -15
- package/src/tools/table/table-core.ts +3 -42
- package/src/tools/table/table-heading-toggle.ts +2 -2
- package/src/tools/table/table-model.ts +623 -0
- package/src/tools/table/table-operations.ts +59 -78
- package/src/tools/table/table-resize.ts +15 -11
- package/src/tools/table/table-restrictions.ts +69 -3
- package/src/tools/table/table-row-col-action-handler.ts +22 -7
- package/src/tools/table/table-row-col-controls.ts +129 -12
- package/src/tools/table/table-row-col-drag.ts +14 -0
- package/src/tools/table/table-scroll-haze.ts +152 -0
- package/src/tools/table/types.ts +22 -1
- package/src/tools/table/view/table-cell-blocks-adapter.ts +47 -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 +125 -0
- package/src/tools/toggle/index.ts +280 -0
- package/src/tools/toggle/toggle-keyboard.ts +139 -0
- package/src/tools/toggle/toggle-lifecycle.ts +80 -0
- package/src/tools/toggle/toggle-shortcuts.ts +107 -0
- package/src/tools/toggle/types.ts +21 -0
- package/src/variants/blok-minimum.ts +13 -0
- package/types/api/block.d.ts +13 -0
- package/types/api/blocks.d.ts +16 -0
- package/types/full.d.ts +2 -0
- package/types/tools/table.d.ts +2 -0
- package/types/tools-entry.d.ts +2 -1
- package/dist/chunks/i18next-CugVlwWp.mjs +0 -1292
- package/src/tools/table/data-normalizer.ts +0 -32
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import type { SanitizerConfig } from '../../../types/configs/sanitizer-config';
|
|
2
|
+
import type { ClipboardBlockData, TableCellsClipboard } from './types';
|
|
3
|
+
import { mapToNearestPresetColor } from '../../components/utils/color-mapping';
|
|
4
|
+
import { clean } from '../../components/utils/sanitizer';
|
|
5
|
+
|
|
6
|
+
/** Attribute name used to embed clipboard data on the HTML table element. */
|
|
7
|
+
const DATA_ATTR = 'data-blok-table-cells';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the background-color style value for clipboard markup.
|
|
11
|
+
* When the cell has an explicit background, use it; when it only has a text
|
|
12
|
+
* color, force a transparent background so the mark element doesn't inherit
|
|
13
|
+
* an unwanted default; otherwise return an empty string (no style needed).
|
|
14
|
+
*/
|
|
15
|
+
function resolveBackgroundStyle(hasBgColor: boolean, hasColor: boolean, mappedBg: string): string {
|
|
16
|
+
if (hasBgColor) {
|
|
17
|
+
return `background-color: ${mappedBg}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (hasColor) {
|
|
21
|
+
return 'background-color: transparent';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Entry describing one cell to be serialized for the clipboard.
|
|
29
|
+
*/
|
|
30
|
+
interface CellEntry {
|
|
31
|
+
row: number;
|
|
32
|
+
col: number;
|
|
33
|
+
blocks: ClipboardBlockData[];
|
|
34
|
+
color?: string;
|
|
35
|
+
textColor?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build a {@link TableCellsClipboard} payload from a flat list of cell entries.
|
|
40
|
+
*
|
|
41
|
+
* Row/col indices are normalized so the minimum becomes 0.
|
|
42
|
+
* Empty input produces `{ rows: 0, cols: 0, cells: [] }`.
|
|
43
|
+
*/
|
|
44
|
+
export function serializeCellsToClipboard(entries: CellEntry[]): TableCellsClipboard {
|
|
45
|
+
if (entries.length === 0) {
|
|
46
|
+
return { rows: 0, cols: 0, cells: [] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const minRow = Math.min(...entries.map((e) => e.row));
|
|
50
|
+
const minCol = Math.min(...entries.map((e) => e.col));
|
|
51
|
+
|
|
52
|
+
const maxRow = Math.max(...entries.map((e) => e.row));
|
|
53
|
+
const maxCol = Math.max(...entries.map((e) => e.col));
|
|
54
|
+
|
|
55
|
+
const rows = maxRow - minRow + 1;
|
|
56
|
+
const cols = maxCol - minCol + 1;
|
|
57
|
+
|
|
58
|
+
// Pre-fill with empty cells
|
|
59
|
+
const cells: TableCellsClipboard['cells'] = Array.from({ length: rows }, () =>
|
|
60
|
+
Array.from({ length: cols }, () => ({ blocks: [] as ClipboardBlockData[] }))
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const r = entry.row - minRow;
|
|
65
|
+
const c = entry.col - minCol;
|
|
66
|
+
|
|
67
|
+
cells[r][c] = { blocks: entry.blocks };
|
|
68
|
+
|
|
69
|
+
if (entry.color !== undefined) {
|
|
70
|
+
cells[r][c].color = entry.color;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (entry.textColor !== undefined) {
|
|
74
|
+
cells[r][c].textColor = entry.textColor;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { rows, cols, cells };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Extract a plain-text representation from a single block's data.
|
|
83
|
+
*
|
|
84
|
+
* Looks for `data.text` (string), then `data.items` (array of strings),
|
|
85
|
+
* and falls back to an empty string.
|
|
86
|
+
*/
|
|
87
|
+
function extractBlockText(block: ClipboardBlockData): string {
|
|
88
|
+
const { data } = block;
|
|
89
|
+
|
|
90
|
+
if (typeof data.text === 'string') {
|
|
91
|
+
return data.text;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(data.items)) {
|
|
95
|
+
return (data.items as unknown[])
|
|
96
|
+
.filter((item): item is string => typeof item === 'string')
|
|
97
|
+
.join(' ');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build an HTML `<table>` string that carries the clipboard payload in a
|
|
105
|
+
* `data-blok-table-cells` attribute.
|
|
106
|
+
*
|
|
107
|
+
* External apps receive a clean HTML table, while Blok's paste handler can
|
|
108
|
+
* detect and extract the structured JSON.
|
|
109
|
+
*/
|
|
110
|
+
export function buildClipboardHtml(payload: TableCellsClipboard): string {
|
|
111
|
+
// Use single-quoted attribute so JSON double quotes don't break the HTML.
|
|
112
|
+
// Escape any literal single quotes inside the JSON with '.
|
|
113
|
+
const json = JSON.stringify(payload).replace(/'/g, ''');
|
|
114
|
+
|
|
115
|
+
const rowsHtml = payload.cells
|
|
116
|
+
.map((row) => {
|
|
117
|
+
const cellsHtml = row
|
|
118
|
+
.map((cell) => {
|
|
119
|
+
const text = cell.blocks.map(extractBlockText).join(' ');
|
|
120
|
+
|
|
121
|
+
const styles = [
|
|
122
|
+
cell.color ? `background-color: ${cell.color}` : '',
|
|
123
|
+
cell.textColor ? `color: ${cell.textColor}` : '',
|
|
124
|
+
].filter(Boolean).join('; ');
|
|
125
|
+
|
|
126
|
+
const styleAttr = styles ? ` style="${styles}"` : '';
|
|
127
|
+
|
|
128
|
+
return `<td${styleAttr}>${text}</td>`;
|
|
129
|
+
})
|
|
130
|
+
.join('');
|
|
131
|
+
|
|
132
|
+
return `<tr>${cellsHtml}</tr>`;
|
|
133
|
+
})
|
|
134
|
+
.join('');
|
|
135
|
+
|
|
136
|
+
return `<table ${DATA_ATTR}='${json}'>${rowsHtml}</table>`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build a plain-text representation of the clipboard payload.
|
|
141
|
+
*
|
|
142
|
+
* Cells are separated by tabs (`\t`), rows by newlines (`\n`).
|
|
143
|
+
* Multiple blocks within a cell are joined with spaces.
|
|
144
|
+
*/
|
|
145
|
+
export function buildClipboardPlainText(payload: TableCellsClipboard): string {
|
|
146
|
+
return payload.cells
|
|
147
|
+
.map((row) =>
|
|
148
|
+
row.map((cell) => cell.blocks.map(extractBlockText).join(' ')).join('\t')
|
|
149
|
+
)
|
|
150
|
+
.join('\n');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* CSS properties allowed on <mark> elements inside table cells.
|
|
155
|
+
* Matches MarkerInlineTool.ALLOWED_STYLE_PROPS.
|
|
156
|
+
*/
|
|
157
|
+
const ALLOWED_MARK_STYLE_PROPS = new Set(['color', 'background-color']);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Sanitizer config for cell content: allows bold, italic, line breaks, links,
|
|
161
|
+
* and color markers (<mark> with color/background-color styles only).
|
|
162
|
+
*/
|
|
163
|
+
const CELL_SANITIZE_CONFIG: SanitizerConfig = {
|
|
164
|
+
b: true,
|
|
165
|
+
strong: true,
|
|
166
|
+
i: true,
|
|
167
|
+
em: true,
|
|
168
|
+
br: true,
|
|
169
|
+
a: { href: true },
|
|
170
|
+
mark: (node: Element): { [attr: string]: boolean | string } => {
|
|
171
|
+
const el = node as HTMLElement;
|
|
172
|
+
const style = el.style;
|
|
173
|
+
|
|
174
|
+
const props = Array.from({ length: style.length }, (_, i) => style.item(i));
|
|
175
|
+
|
|
176
|
+
for (const prop of props) {
|
|
177
|
+
if (!ALLOWED_MARK_STYLE_PROPS.has(prop)) {
|
|
178
|
+
style.removeProperty(prop);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return style.length > 0 ? { style: true } : {};
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check whether a CSS color value is the default black text color.
|
|
188
|
+
* Google Docs uses different formats: `rgb(0, 0, 0)`, `rgb(0,0,0)`, or `#000000`.
|
|
189
|
+
* Spans with only this color should not be converted to `<mark>`.
|
|
190
|
+
*/
|
|
191
|
+
export function isDefaultBlack(color: string): boolean {
|
|
192
|
+
const normalized = color.replace(/\s/g, '');
|
|
193
|
+
|
|
194
|
+
return normalized === 'rgb(0,0,0)' || normalized === '#000000';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Extract HTML content from a `<td>`/`<th>` element, converting Google Docs
|
|
199
|
+
* style-based spans to semantic tags and sanitizing to allowed formatting only.
|
|
200
|
+
*
|
|
201
|
+
* - `<span style="font-weight:700">` or `font-weight:bold` → `<b>`
|
|
202
|
+
* - `<span style="font-style:italic">` → `<i>`
|
|
203
|
+
* - `<span style="color:...">` → `<mark style="color: ...">`
|
|
204
|
+
* - `<span style="background-color:...">` → `<mark style="background-color: ...">`
|
|
205
|
+
* - `<p>` boundaries → `<br>` line breaks
|
|
206
|
+
* - Everything else stripped except `<b>`, `<strong>`, `<i>`, `<em>`, `<br>`, `<a href>`, `<mark style>`
|
|
207
|
+
*/
|
|
208
|
+
function sanitizeCellHtml(td: Element): string {
|
|
209
|
+
const clone = td.cloneNode(true) as HTMLElement;
|
|
210
|
+
|
|
211
|
+
// Convert style-based spans to semantic tags
|
|
212
|
+
for (const span of Array.from(clone.querySelectorAll('span'))) {
|
|
213
|
+
const style = span.getAttribute('style') ?? '';
|
|
214
|
+
const isBold = /font-weight\s*:\s*(700|bold)/i.test(style);
|
|
215
|
+
const isItalic = /font-style\s*:\s*italic/i.test(style);
|
|
216
|
+
|
|
217
|
+
const colorMatch = /(?<![a-z-])color\s*:\s*([^;]+)/i.exec(style);
|
|
218
|
+
const bgMatch = /background-color\s*:\s*([^;]+)/i.exec(style);
|
|
219
|
+
|
|
220
|
+
const color = colorMatch?.[1]?.trim();
|
|
221
|
+
const bgColor = bgMatch?.[1]?.trim();
|
|
222
|
+
|
|
223
|
+
const hasColor = color !== undefined && !isDefaultBlack(color);
|
|
224
|
+
const hasBgColor = bgColor !== undefined && bgColor !== 'transparent';
|
|
225
|
+
|
|
226
|
+
if (!isBold && !isItalic && !hasColor && !hasBgColor) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const mappedColor = hasColor ? mapToNearestPresetColor(color, 'text') : '';
|
|
231
|
+
const mappedBg = hasBgColor ? mapToNearestPresetColor(bgColor, 'bg') : '';
|
|
232
|
+
|
|
233
|
+
const colorStyles = [
|
|
234
|
+
hasColor ? `color: ${mappedColor}` : '',
|
|
235
|
+
resolveBackgroundStyle(hasBgColor, hasColor, mappedBg),
|
|
236
|
+
].filter(Boolean).join('; ');
|
|
237
|
+
|
|
238
|
+
const inner = span.innerHTML;
|
|
239
|
+
const marked = colorStyles
|
|
240
|
+
? `<mark style="${colorStyles};">${inner}</mark>`
|
|
241
|
+
: inner;
|
|
242
|
+
|
|
243
|
+
const italic = isItalic ? `<i>${marked}</i>` : marked;
|
|
244
|
+
const wrapped = isBold ? `<b>${italic}</b>` : italic;
|
|
245
|
+
|
|
246
|
+
span.replaceWith(document.createRange().createContextualFragment(wrapped));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Move color/background-color from <a> tags into <mark> wrappers.
|
|
250
|
+
// The sanitizer only allows href on <a>, so inline color styles would be lost.
|
|
251
|
+
for (const anchor of Array.from(clone.querySelectorAll('a[style]'))) {
|
|
252
|
+
const style = anchor.getAttribute('style') ?? '';
|
|
253
|
+
|
|
254
|
+
const colorMatch = /(?<![a-z-])color\s*:\s*([^;]+)/i.exec(style);
|
|
255
|
+
const bgMatch = /background-color\s*:\s*([^;]+)/i.exec(style);
|
|
256
|
+
|
|
257
|
+
const color = colorMatch?.[1]?.trim();
|
|
258
|
+
const bgColor = bgMatch?.[1]?.trim();
|
|
259
|
+
|
|
260
|
+
const hasColor = color !== undefined && !isDefaultBlack(color) && color !== 'inherit';
|
|
261
|
+
const hasBgColor = bgColor !== undefined && bgColor !== 'transparent' && bgColor !== 'inherit';
|
|
262
|
+
|
|
263
|
+
if (!hasColor && !hasBgColor) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const mappedColor = hasColor ? mapToNearestPresetColor(color, 'text') : '';
|
|
268
|
+
const mappedBg = hasBgColor ? mapToNearestPresetColor(bgColor, 'bg') : '';
|
|
269
|
+
|
|
270
|
+
const colorStyles = [
|
|
271
|
+
hasColor ? `color: ${mappedColor}` : '',
|
|
272
|
+
resolveBackgroundStyle(hasBgColor, hasColor, mappedBg),
|
|
273
|
+
].filter(Boolean).join('; ');
|
|
274
|
+
|
|
275
|
+
const el = anchor as HTMLElement;
|
|
276
|
+
|
|
277
|
+
el.innerHTML = `<mark style="${colorStyles};">${el.innerHTML}</mark>`;
|
|
278
|
+
el.style.removeProperty('color');
|
|
279
|
+
el.style.removeProperty('background-color');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Convert <p> boundaries to <br> line breaks
|
|
283
|
+
for (const p of Array.from(clone.querySelectorAll('p'))) {
|
|
284
|
+
const fragment = document.createRange().createContextualFragment(p.innerHTML + '<br>');
|
|
285
|
+
|
|
286
|
+
p.replaceWith(fragment);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const html = clean(clone.innerHTML, CELL_SANITIZE_CONFIG)
|
|
290
|
+
.replace(/(<br\s*\/?>|\s)+$/i, '')
|
|
291
|
+
.trim();
|
|
292
|
+
|
|
293
|
+
return html;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Parse a generic HTML table (e.g. from Google Docs, Word, Excel) into a
|
|
298
|
+
* {@link TableCellsClipboard} payload.
|
|
299
|
+
*
|
|
300
|
+
* Each `<td>`/`<th>` becomes one or more paragraph blocks (split by line breaks).
|
|
301
|
+
* content. Returns `null` when the HTML does not contain a `<table>`, when
|
|
302
|
+
* the table has no rows, or when the table already carries our custom
|
|
303
|
+
* `data-blok-table-cells` attribute (those should be handled by
|
|
304
|
+
* {@link parseClipboardHtml} instead).
|
|
305
|
+
*/
|
|
306
|
+
export function parseGenericHtmlTable(html: string): TableCellsClipboard | null {
|
|
307
|
+
if (!html) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
312
|
+
const table = doc.querySelector('table');
|
|
313
|
+
|
|
314
|
+
if (!table) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Defer to parseClipboardHtml for our own format
|
|
319
|
+
if (table.hasAttribute(DATA_ATTR)) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const rows = table.querySelectorAll('tr');
|
|
324
|
+
|
|
325
|
+
if (rows.length === 0) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
type CellPayload = TableCellsClipboard['cells'][number][number];
|
|
330
|
+
|
|
331
|
+
const cellGrid: CellPayload[][] = [];
|
|
332
|
+
|
|
333
|
+
rows.forEach((row) => {
|
|
334
|
+
const tds = row.querySelectorAll('td, th');
|
|
335
|
+
const rowCells: CellPayload[] = [];
|
|
336
|
+
|
|
337
|
+
tds.forEach((td) => {
|
|
338
|
+
const text = sanitizeCellHtml(td);
|
|
339
|
+
const segments = text.split(/<br\s*\/?>/i).map(s => s.trim()).filter(Boolean);
|
|
340
|
+
const blocks = segments.length > 0
|
|
341
|
+
? segments.map(s => ({ tool: 'paragraph' as const, data: { text: s } }))
|
|
342
|
+
: [{ tool: 'paragraph' as const, data: { text: '' } }];
|
|
343
|
+
|
|
344
|
+
const cell: CellPayload = { blocks };
|
|
345
|
+
|
|
346
|
+
// Extract cell-level colors from td/th style attribute
|
|
347
|
+
const tdStyle = td.getAttribute('style') ?? '';
|
|
348
|
+
const cellBgMatch = /background-color\s*:\s*([^;]+)/i.exec(tdStyle);
|
|
349
|
+
|
|
350
|
+
if (cellBgMatch?.[1]) {
|
|
351
|
+
cell.color = mapToNearestPresetColor(cellBgMatch[1].trim(), 'bg');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const cellTextColorMatch = /(?<![a-z-])color\s*:\s*([^;]+)/i.exec(tdStyle);
|
|
355
|
+
|
|
356
|
+
if (cellTextColorMatch?.[1] && !isDefaultBlack(cellTextColorMatch[1].trim())) {
|
|
357
|
+
cell.textColor = mapToNearestPresetColor(cellTextColorMatch[1].trim(), 'text');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
rowCells.push(cell);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
cellGrid.push(rowCells);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const maxCols = Math.max(0, ...cellGrid.map((row) => row.length));
|
|
367
|
+
|
|
368
|
+
if (cellGrid.length === 0 || maxCols === 0) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Normalize: ensure all rows have the same number of columns
|
|
373
|
+
for (const row of cellGrid) {
|
|
374
|
+
while (row.length < maxCols) {
|
|
375
|
+
row.push({ blocks: [{ tool: 'paragraph', data: { text: '' } }] });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
rows: cellGrid.length,
|
|
381
|
+
cols: maxCols,
|
|
382
|
+
cells: cellGrid,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Attempt to parse a {@link TableCellsClipboard} from an HTML string.
|
|
388
|
+
*
|
|
389
|
+
* Returns `null` if the HTML does not contain a `<table>` with the expected
|
|
390
|
+
* data attribute, or if the JSON within is invalid.
|
|
391
|
+
*/
|
|
392
|
+
export function parseClipboardHtml(html: string): TableCellsClipboard | null {
|
|
393
|
+
// Match both single-quoted and double-quoted attribute values
|
|
394
|
+
const pattern = new RegExp(`${DATA_ATTR}='([^']*)'|${DATA_ATTR}="([^"]*)"`, 's');
|
|
395
|
+
const match = pattern.exec(html);
|
|
396
|
+
|
|
397
|
+
if (!match) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
// Group 1 = single-quoted value, group 2 = double-quoted value
|
|
403
|
+
const raw = match[1] ?? match[2];
|
|
404
|
+
|
|
405
|
+
if (raw === undefined) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const jsonStr = raw.replace(/'/g, "'").replace(/"/g, '"');
|
|
410
|
+
|
|
411
|
+
return JSON.parse(jsonStr) as TableCellsClipboard;
|
|
412
|
+
} catch {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { I18n } from '../../../types/api';
|
|
2
|
+
import { createColorPicker } from '../../components/shared/color-picker';
|
|
3
|
+
|
|
4
|
+
export type CellColorMode = 'textColor' | 'backgroundColor';
|
|
5
|
+
|
|
6
|
+
interface CellColorPickerOptions {
|
|
7
|
+
i18n: I18n;
|
|
8
|
+
onColorSelect: (color: string | null, mode: CellColorMode) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface CellColorPickerResult {
|
|
12
|
+
element: HTMLDivElement;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a cell color picker for the table cell popover.
|
|
17
|
+
* Thin wrapper around the shared color picker component.
|
|
18
|
+
*/
|
|
19
|
+
export const createCellColorPicker = (options: CellColorPickerOptions): CellColorPickerResult => {
|
|
20
|
+
const handle = createColorPicker({
|
|
21
|
+
i18n: options.i18n,
|
|
22
|
+
testIdPrefix: 'cell-color',
|
|
23
|
+
defaultModeIndex: 1,
|
|
24
|
+
modes: [
|
|
25
|
+
{ key: 'textColor', labelKey: 'tools.marker.textColor', presetField: 'text' },
|
|
26
|
+
{ key: 'backgroundColor', labelKey: 'tools.marker.background', presetField: 'bg' },
|
|
27
|
+
],
|
|
28
|
+
onColorSelect: (color, modeKey) => {
|
|
29
|
+
options.onColorSelect(color, modeKey as CellColorMode);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return { element: handle.element };
|
|
34
|
+
};
|