@jackuait/blok 0.6.0-beta.0 → 0.6.0-beta.10
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 +16 -169
- package/bin/blok.mjs +10 -0
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-Bu9S3SsR.mjs → blok-Buf0btS7.mjs} +2267 -2024
- package/dist/chunks/{i18next-loader-CKuXJ0Av.mjs → i18next-loader-CVf_ZfwA.mjs} +1 -1
- package/dist/chunks/{index-jtZaryNw.mjs → index-C6jsfLLp.mjs} +1 -1
- package/dist/chunks/{inline-tool-convert-CFjyrH30.mjs → inline-tool-convert-BKKEoOqB.mjs} +710 -570
- package/dist/chunks/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
- package/dist/chunks/{messages-CQj2JU2j.mjs → messages-7QoX8DkW.mjs} +23 -9
- package/dist/{messages-LvFKBBPa.mjs → chunks/messages-7W4d0DwD.mjs} +15 -1
- package/dist/{messages-Bn253WWC.mjs → chunks/messages-9SihnaXQ.mjs} +14 -0
- package/dist/{messages-Bf6Y3_GI.mjs → chunks/messages-B1Aww8q7.mjs} +16 -2
- package/dist/{messages-pA5TvcAj.mjs → chunks/messages-BB5z9Uba.mjs} +14 -0
- package/dist/chunks/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
- package/dist/chunks/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
- package/dist/chunks/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
- package/dist/{messages-B5puUm7R.mjs → chunks/messages-BL0tXcDf.mjs} +15 -1
- package/dist/chunks/{messages-zS1AXZ0y.mjs → messages-BMXCuEKO.mjs} +19 -5
- package/dist/{messages-CyDU5lz9.mjs → chunks/messages-BMv4xwIr.mjs} +16 -2
- package/dist/chunks/{messages-BeUhMpsr.mjs → messages-BSbjsyHY.mjs} +25 -11
- package/dist/chunks/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
- package/dist/chunks/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
- package/dist/{messages-CXHd9SUK.mjs → chunks/messages-BYyy6Wqf.mjs} +14 -0
- package/dist/chunks/{messages-DOlC_Tty.mjs → messages-BdeLo0N9.mjs} +24 -10
- package/dist/chunks/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
- package/dist/chunks/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
- package/dist/chunks/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
- package/dist/chunks/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
- package/dist/chunks/{messages-D00OjS2n.mjs → messages-C2htQ_3F.mjs} +24 -10
- package/dist/chunks/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
- package/dist/chunks/{messages-CkFT2gle.mjs → messages-C9eaarcK.mjs} +20 -6
- package/dist/chunks/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
- package/dist/chunks/messages-CKI54h6O.mjs +62 -0
- package/dist/{messages-CrsJ1TEJ.mjs → chunks/messages-CLhcMlTc.mjs} +15 -1
- package/dist/{messages-CnvW8Slp.mjs → chunks/messages-CMkNSDTo.mjs} +17 -3
- package/dist/{messages-BlpqL8vG.mjs → chunks/messages-CQwpzUFp.mjs} +19 -5
- package/dist/chunks/{messages-Cu08aLS3.mjs → messages-CVw84KdI.mjs} +21 -7
- package/dist/chunks/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
- package/dist/chunks/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
- package/dist/chunks/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
- package/dist/chunks/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
- package/dist/{messages-Dg92dXZ5.mjs → chunks/messages-CvGLfqmV.mjs} +14 -0
- package/dist/{messages-AHESHJm_.mjs → chunks/messages-CzTufCHu.mjs} +14 -0
- package/dist/chunks/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
- package/dist/chunks/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
- package/dist/chunks/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
- package/dist/chunks/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
- package/dist/chunks/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
- package/dist/{messages-zSzDzXej.mjs → chunks/messages-DBRw-7Zc.mjs} +16 -2
- package/dist/chunks/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
- package/dist/chunks/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
- package/dist/{messages-DDLgIPDF.mjs → chunks/messages-DLfR5bMd.mjs} +16 -2
- package/dist/{messages-CbhuIWRJ.mjs → chunks/messages-DT4dP5uK.mjs} +15 -1
- package/dist/chunks/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
- package/dist/{messages-BPqWKx5Z.mjs → chunks/messages-Diu6jAaR.mjs} +17 -3
- package/dist/{messages-BA0rcTCY.mjs → chunks/messages-DnIhyAJk.mjs} +18 -4
- package/dist/chunks/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
- package/dist/chunks/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
- package/dist/{messages-CJoBtXU6.mjs → chunks/messages-DqM1LFg5.mjs} +14 -0
- package/dist/chunks/{messages-Cyi2AMmz.mjs → messages-DvFLX36Q.mjs} +25 -11
- package/dist/chunks/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
- package/dist/chunks/{messages-GC2PhgV3.mjs → messages-Dzwxv9v1.mjs} +23 -9
- package/dist/chunks/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
- package/dist/{messages-DY94ykcE.mjs → chunks/messages-LPVfA-8K.mjs} +14 -0
- package/dist/{messages-Cr-RJ7YB.mjs → chunks/messages-O5tQus_0.mjs} +14 -0
- package/dist/chunks/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
- package/dist/chunks/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
- package/dist/{messages-CUy1vn-b.mjs → chunks/messages-Xq8UmkVs.mjs} +14 -0
- package/dist/chunks/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
- package/dist/chunks/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
- package/dist/chunks/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
- package/dist/chunks/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
- package/dist/chunks/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
- package/dist/chunks/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
- package/dist/cli.mjs +50 -0
- package/dist/full.mjs +15 -15
- package/dist/locales.mjs +102 -88
- package/dist/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
- package/dist/{messages-CQj2JU2j.mjs → messages-7QoX8DkW.mjs} +23 -9
- package/dist/{chunks/messages-LvFKBBPa.mjs → messages-7W4d0DwD.mjs} +15 -1
- package/dist/{chunks/messages-Bn253WWC.mjs → messages-9SihnaXQ.mjs} +14 -0
- package/dist/{chunks/messages-Bf6Y3_GI.mjs → messages-B1Aww8q7.mjs} +16 -2
- package/dist/{chunks/messages-pA5TvcAj.mjs → messages-BB5z9Uba.mjs} +14 -0
- package/dist/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
- package/dist/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
- package/dist/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
- package/dist/{chunks/messages-B5puUm7R.mjs → messages-BL0tXcDf.mjs} +15 -1
- package/dist/{messages-zS1AXZ0y.mjs → messages-BMXCuEKO.mjs} +19 -5
- package/dist/{chunks/messages-CyDU5lz9.mjs → messages-BMv4xwIr.mjs} +16 -2
- package/dist/{messages-BeUhMpsr.mjs → messages-BSbjsyHY.mjs} +25 -11
- package/dist/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
- package/dist/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
- package/dist/{chunks/messages-CXHd9SUK.mjs → messages-BYyy6Wqf.mjs} +14 -0
- package/dist/{messages-DOlC_Tty.mjs → messages-BdeLo0N9.mjs} +24 -10
- package/dist/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
- package/dist/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
- package/dist/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
- package/dist/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
- package/dist/{messages-D00OjS2n.mjs → messages-C2htQ_3F.mjs} +24 -10
- package/dist/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
- package/dist/{messages-CkFT2gle.mjs → messages-C9eaarcK.mjs} +20 -6
- package/dist/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
- package/dist/messages-CKI54h6O.mjs +62 -0
- package/dist/{chunks/messages-CrsJ1TEJ.mjs → messages-CLhcMlTc.mjs} +15 -1
- package/dist/{chunks/messages-CnvW8Slp.mjs → messages-CMkNSDTo.mjs} +17 -3
- package/dist/{chunks/messages-BlpqL8vG.mjs → messages-CQwpzUFp.mjs} +19 -5
- package/dist/{messages-Cu08aLS3.mjs → messages-CVw84KdI.mjs} +21 -7
- package/dist/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
- package/dist/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
- package/dist/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
- package/dist/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
- package/dist/{chunks/messages-Dg92dXZ5.mjs → messages-CvGLfqmV.mjs} +14 -0
- package/dist/{chunks/messages-AHESHJm_.mjs → messages-CzTufCHu.mjs} +14 -0
- package/dist/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
- package/dist/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
- package/dist/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
- package/dist/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
- package/dist/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
- package/dist/{chunks/messages-zSzDzXej.mjs → messages-DBRw-7Zc.mjs} +16 -2
- package/dist/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
- package/dist/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
- package/dist/{chunks/messages-DDLgIPDF.mjs → messages-DLfR5bMd.mjs} +16 -2
- package/dist/{chunks/messages-CbhuIWRJ.mjs → messages-DT4dP5uK.mjs} +15 -1
- package/dist/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
- package/dist/{chunks/messages-BPqWKx5Z.mjs → messages-Diu6jAaR.mjs} +17 -3
- package/dist/{chunks/messages-BA0rcTCY.mjs → messages-DnIhyAJk.mjs} +18 -4
- package/dist/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
- package/dist/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
- package/dist/{chunks/messages-CJoBtXU6.mjs → messages-DqM1LFg5.mjs} +14 -0
- package/dist/{messages-Cyi2AMmz.mjs → messages-DvFLX36Q.mjs} +25 -11
- package/dist/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
- package/dist/{messages-GC2PhgV3.mjs → messages-Dzwxv9v1.mjs} +23 -9
- package/dist/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
- package/dist/{chunks/messages-DY94ykcE.mjs → messages-LPVfA-8K.mjs} +14 -0
- package/dist/{chunks/messages-Cr-RJ7YB.mjs → messages-O5tQus_0.mjs} +14 -0
- package/dist/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
- package/dist/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
- package/dist/{chunks/messages-CUy1vn-b.mjs → messages-Xq8UmkVs.mjs} +14 -0
- package/dist/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
- package/dist/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
- package/dist/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
- package/dist/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
- package/dist/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
- package/dist/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
- package/dist/tools.mjs +2005 -1267
- package/dist/vendor.LICENSE.txt +1 -1
- package/package.json +15 -14
- package/src/cli/commands/migration.ts +16 -0
- package/src/cli/commands/migrationContent.ts +6 -0
- package/src/cli/index.ts +47 -0
- package/src/cli/utils/output.ts +10 -0
- package/src/components/block-tunes/block-tune-delete.ts +3 -2
- package/src/components/blocks.ts +23 -6
- package/src/components/constants/data-attributes.ts +2 -0
- package/src/components/i18n/locales/am/messages.json +15 -1
- package/src/components/i18n/locales/ar/messages.json +14 -0
- package/src/components/i18n/locales/az/messages.json +14 -0
- package/src/components/i18n/locales/bg/messages.json +14 -0
- package/src/components/i18n/locales/bn/messages.json +25 -11
- package/src/components/i18n/locales/bs/messages.json +15 -1
- package/src/components/i18n/locales/cs/messages.json +14 -0
- package/src/components/i18n/locales/da/messages.json +14 -0
- package/src/components/i18n/locales/de/messages.json +14 -0
- package/src/components/i18n/locales/dv/messages.json +15 -1
- package/src/components/i18n/locales/el/messages.json +15 -1
- package/src/components/i18n/locales/en/messages.json +14 -0
- package/src/components/i18n/locales/es/messages.json +14 -0
- package/src/components/i18n/locales/et/messages.json +14 -0
- package/src/components/i18n/locales/fa/messages.json +15 -1
- package/src/components/i18n/locales/fi/messages.json +15 -1
- package/src/components/i18n/locales/fil/messages.json +20 -6
- package/src/components/i18n/locales/fr/messages.json +15 -1
- package/src/components/i18n/locales/gu/messages.json +15 -1
- package/src/components/i18n/locales/he/messages.json +14 -0
- package/src/components/i18n/locales/hi/messages.json +23 -9
- package/src/components/i18n/locales/hr/messages.json +14 -0
- package/src/components/i18n/locales/hu/messages.json +14 -0
- package/src/components/i18n/locales/hy/messages.json +16 -2
- package/src/components/i18n/locales/id/messages.json +19 -5
- package/src/components/i18n/locales/it/messages.json +14 -0
- package/src/components/i18n/locales/ja/messages.json +14 -0
- package/src/components/i18n/locales/ka/messages.json +15 -1
- package/src/components/i18n/locales/km/messages.json +16 -2
- package/src/components/i18n/locales/kn/messages.json +16 -2
- package/src/components/i18n/locales/ko/messages.json +14 -0
- package/src/components/i18n/locales/ku/messages.json +16 -2
- package/src/components/i18n/locales/lo/messages.json +15 -1
- package/src/components/i18n/locales/lt/messages.json +15 -1
- package/src/components/i18n/locales/lv/messages.json +15 -1
- package/src/components/i18n/locales/mk/messages.json +16 -2
- package/src/components/i18n/locales/ml/messages.json +16 -2
- package/src/components/i18n/locales/mn/messages.json +16 -2
- package/src/components/i18n/locales/mr/messages.json +24 -10
- package/src/components/i18n/locales/ms/messages.json +17 -3
- package/src/components/i18n/locales/my/messages.json +16 -2
- package/src/components/i18n/locales/ne/messages.json +24 -10
- package/src/components/i18n/locales/nl/messages.json +15 -1
- package/src/components/i18n/locales/no/messages.json +16 -2
- package/src/components/i18n/locales/pa/messages.json +15 -1
- package/src/components/i18n/locales/pl/messages.json +14 -0
- package/src/components/i18n/locales/ps/messages.json +17 -3
- package/src/components/i18n/locales/pt/messages.json +14 -0
- package/src/components/i18n/locales/ro/messages.json +15 -1
- package/src/components/i18n/locales/ru/messages.json +14 -0
- package/src/components/i18n/locales/sd/messages.json +16 -2
- package/src/components/i18n/locales/si/messages.json +23 -9
- package/src/components/i18n/locales/sk/messages.json +15 -1
- package/src/components/i18n/locales/sl/messages.json +16 -2
- package/src/components/i18n/locales/sq/messages.json +16 -2
- package/src/components/i18n/locales/sr/messages.json +16 -2
- package/src/components/i18n/locales/sv/messages.json +16 -2
- package/src/components/i18n/locales/sw/messages.json +16 -2
- package/src/components/i18n/locales/ta/messages.json +21 -7
- package/src/components/i18n/locales/te/messages.json +40 -26
- package/src/components/i18n/locales/th/messages.json +19 -5
- package/src/components/i18n/locales/tr/messages.json +15 -1
- package/src/components/i18n/locales/ug/messages.json +16 -2
- package/src/components/i18n/locales/uk/messages.json +15 -1
- package/src/components/i18n/locales/ur/messages.json +15 -1
- package/src/components/i18n/locales/vi/messages.json +25 -11
- package/src/components/i18n/locales/yi/messages.json +16 -2
- package/src/components/i18n/locales/zh/messages.json +15 -1
- package/src/components/icons/index.ts +104 -83
- package/src/components/modules/api/blocks.ts +35 -2
- package/src/components/modules/api/history.ts +64 -0
- package/src/components/modules/api/index.ts +2 -0
- package/src/components/modules/api/readonly.ts +11 -1
- package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +12 -1
- package/src/components/modules/blockManager/blockManager.ts +7 -0
- package/src/components/modules/blockManager/operations.ts +3 -2
- package/src/components/modules/blockManager/types.ts +3 -1
- package/src/components/modules/blockManager/yjs-sync.ts +12 -2
- package/src/components/modules/index.ts +3 -0
- package/src/components/modules/normalizeInlineImages.ts +263 -0
- package/src/components/modules/readonly.ts +11 -0
- package/src/components/modules/rectangleSelection.ts +19 -3
- package/src/components/modules/saver.ts +7 -3
- package/src/components/modules/toolbar/blockSettings.ts +3 -3
- package/src/components/modules/toolbar/index.ts +72 -14
- package/src/components/modules/toolbar/plus-button.ts +24 -3
- package/src/components/modules/toolbar/settings-toggler.ts +3 -5
- package/src/components/modules/ui.ts +46 -68
- package/src/components/modules/uiControllers/controllers/blockHover.ts +49 -61
- package/src/components/modules/uiControllers/controllers/keyboard.ts +17 -11
- package/src/components/modules/uiControllers/handlers/click.ts +0 -12
- package/src/components/modules/yjs/index.ts +23 -0
- package/src/components/ui/toolbox.ts +41 -6
- package/src/components/utils/placeholder.ts +16 -0
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -1
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -1
- package/src/components/utils/popover/index.ts +1 -0
- package/src/components/utils/popover/popover-abstract.ts +11 -1
- package/src/components/utils/popover/popover-desktop.ts +27 -8
- package/src/components/utils/popover/popover-registry.ts +188 -0
- package/src/components/utils/sanitizer.ts +23 -2
- package/src/components/utils/tooltip.ts +2 -24
- package/src/styles/main.css +22 -0
- package/src/tools/paragraph/index.ts +12 -4
- package/src/tools/table/data-normalizer.ts +1 -0
- package/src/tools/table/index.ts +283 -346
- package/src/tools/table/table-add-controls.ts +353 -47
- package/src/tools/table/table-cell-blocks.ts +95 -7
- package/src/tools/table/table-cell-selection.ts +648 -0
- package/src/tools/table/table-core.ts +21 -32
- package/src/tools/table/table-grip-visuals.ts +96 -0
- package/src/tools/table/table-heading-toggle.ts +127 -0
- package/src/tools/table/table-operations.ts +475 -0
- package/src/tools/table/table-resize.ts +27 -6
- package/src/tools/table/table-restrictions.ts +64 -0
- package/src/tools/table/table-row-col-action-handler.ts +190 -0
- package/src/tools/table/table-row-col-controls.ts +265 -211
- package/src/tools/table/table-row-col-drag.ts +4 -4
- package/src/tools/table/table-row-col-popover.ts +225 -0
- package/src/tools/table/types.ts +4 -0
- package/src/types-internal/blok-modules.d.ts +2 -0
- package/types/api/blocks.d.ts +8 -0
- package/types/api/history.d.ts +33 -0
- package/types/api/index.d.ts +1 -0
- package/types/api/readonly.d.ts +12 -2
- package/types/index.d.ts +10 -0
- package/types/tools/table.d.ts +67 -0
- package/types/tools-entry.d.ts +4 -0
- package/types/utils/popover/popover-item.d.ts +6 -0
- package/types/utils/popover/popover.d.ts +7 -0
- package/dist/chunks/messages-CySyfkMU.mjs +0 -48
- package/dist/messages-CySyfkMU.mjs +0 -48
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import type { I18n } from '../../../types/api';
|
|
2
|
+
import { IconCross } from '../../components/icons';
|
|
3
|
+
import { PopoverDesktop } from '../../components/utils/popover';
|
|
4
|
+
import { twMerge } from '../../components/utils/tw';
|
|
5
|
+
|
|
6
|
+
import { CELL_ATTR, ROW_ATTR } from './table-core';
|
|
7
|
+
import { createGripDotsSvg } from './table-grip-visuals';
|
|
8
|
+
|
|
9
|
+
import { PopoverEvent } from '@/types/utils/popover/popover-event';
|
|
10
|
+
import type { PopoverItemParams } from '@/types/utils/popover/popover-item';
|
|
11
|
+
|
|
12
|
+
const SELECTED_ATTR = 'data-blok-table-cell-selected';
|
|
13
|
+
|
|
14
|
+
const SELECTION_BORDER = '2px solid #3b82f6';
|
|
15
|
+
|
|
16
|
+
const PILL_ATTR = 'data-blok-table-selection-pill';
|
|
17
|
+
const PILL_WIDTH = 16;
|
|
18
|
+
const PILL_HEIGHT = 20;
|
|
19
|
+
const PILL_IDLE_SIZE = 4;
|
|
20
|
+
|
|
21
|
+
const PILL_CLASSES = [
|
|
22
|
+
'absolute',
|
|
23
|
+
'z-[3]',
|
|
24
|
+
'rounded',
|
|
25
|
+
'select-none',
|
|
26
|
+
'transition-[opacity,background-color,width]',
|
|
27
|
+
'duration-150',
|
|
28
|
+
'flex',
|
|
29
|
+
'items-center',
|
|
30
|
+
'justify-center',
|
|
31
|
+
'overflow-hidden',
|
|
32
|
+
'cursor-pointer',
|
|
33
|
+
'bg-blue-500',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
interface CellCoord {
|
|
37
|
+
row: number;
|
|
38
|
+
col: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if a grip drag or resize is in progress by testing for known drag indicators.
|
|
43
|
+
* Returns true if the grid has an active drag ghost or user-select is disabled.
|
|
44
|
+
*/
|
|
45
|
+
const isOtherInteractionActive = (grid: HTMLElement): boolean => {
|
|
46
|
+
return grid.style.userSelect === 'none';
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handles rectangular cell selection via click-and-drag.
|
|
51
|
+
* Selection starts when a pointer drag crosses from one cell into another.
|
|
52
|
+
* Selected cells are highlighted with a blue outer border around the selection rectangle
|
|
53
|
+
* using an absolutely-positioned overlay div.
|
|
54
|
+
*/
|
|
55
|
+
interface CellSelectionOptions {
|
|
56
|
+
grid: HTMLElement;
|
|
57
|
+
rectangleSelection?: { cancelActiveSelection: () => void };
|
|
58
|
+
onSelectionActiveChange?: (hasSelection: boolean) => void;
|
|
59
|
+
onClearContent?: (cells: HTMLElement[]) => void;
|
|
60
|
+
i18n: I18n;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class TableCellSelection {
|
|
64
|
+
private grid: HTMLElement;
|
|
65
|
+
private rectangleSelection?: { cancelActiveSelection: () => void };
|
|
66
|
+
private onSelectionActiveChange: ((hasSelection: boolean) => void) | undefined;
|
|
67
|
+
private onClearContent: ((cells: HTMLElement[]) => void) | undefined;
|
|
68
|
+
private i18n: I18n;
|
|
69
|
+
private anchorCell: CellCoord | null = null;
|
|
70
|
+
private extentCell: CellCoord | null = null;
|
|
71
|
+
private isSelecting = false;
|
|
72
|
+
private hasSelection = false;
|
|
73
|
+
private selectedCells: HTMLElement[] = [];
|
|
74
|
+
private overlay: HTMLElement | null = null;
|
|
75
|
+
private pill: HTMLElement | null = null;
|
|
76
|
+
private pillPopover: PopoverDesktop | null = null;
|
|
77
|
+
|
|
78
|
+
private boundPointerDown: (e: PointerEvent) => void;
|
|
79
|
+
private boundPointerMove: (e: PointerEvent) => void;
|
|
80
|
+
private boundPointerUp: () => void;
|
|
81
|
+
private boundClearSelection: (e: PointerEvent) => void;
|
|
82
|
+
private boundCancelRectangle: (e: MouseEvent) => void;
|
|
83
|
+
private boundKeyDown: (e: KeyboardEvent) => void;
|
|
84
|
+
|
|
85
|
+
constructor(options: CellSelectionOptions) {
|
|
86
|
+
this.grid = options.grid;
|
|
87
|
+
this.rectangleSelection = options.rectangleSelection;
|
|
88
|
+
this.onSelectionActiveChange = options.onSelectionActiveChange;
|
|
89
|
+
this.onClearContent = options.onClearContent;
|
|
90
|
+
this.i18n = options.i18n;
|
|
91
|
+
this.grid.style.position = 'relative';
|
|
92
|
+
|
|
93
|
+
this.boundPointerDown = this.handlePointerDown.bind(this);
|
|
94
|
+
this.boundPointerMove = this.handlePointerMove.bind(this);
|
|
95
|
+
this.boundPointerUp = this.handlePointerUp.bind(this);
|
|
96
|
+
this.boundClearSelection = this.handleClearSelection.bind(this);
|
|
97
|
+
this.boundCancelRectangle = this.handleCancelRectangle.bind(this);
|
|
98
|
+
this.boundKeyDown = this.handleKeyDown.bind(this);
|
|
99
|
+
|
|
100
|
+
this.grid.addEventListener('pointerdown', this.boundPointerDown);
|
|
101
|
+
document.addEventListener('keydown', this.boundKeyDown);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public destroy(): void {
|
|
105
|
+
this.destroyPillPopover();
|
|
106
|
+
this.clearSelection();
|
|
107
|
+
this.grid.removeEventListener('pointerdown', this.boundPointerDown);
|
|
108
|
+
document.removeEventListener('pointermove', this.boundPointerMove);
|
|
109
|
+
document.removeEventListener('pointerup', this.boundPointerUp);
|
|
110
|
+
document.removeEventListener('pointerdown', this.boundClearSelection);
|
|
111
|
+
document.removeEventListener('mousemove', this.boundCancelRectangle, true);
|
|
112
|
+
document.removeEventListener('keydown', this.boundKeyDown);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Programmatically select an entire row.
|
|
117
|
+
*/
|
|
118
|
+
public selectRow(rowIndex: number): void {
|
|
119
|
+
const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
|
|
120
|
+
const colCount = rows[0]?.querySelectorAll(`[${CELL_ATTR}]`).length ?? 0;
|
|
121
|
+
|
|
122
|
+
if (colCount === 0) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.showProgrammaticSelection(rowIndex, 0, rowIndex, colCount - 1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Programmatically select an entire column.
|
|
131
|
+
*/
|
|
132
|
+
public selectColumn(colIndex: number): void {
|
|
133
|
+
const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
|
|
134
|
+
const rowCount = rows.length;
|
|
135
|
+
|
|
136
|
+
if (rowCount === 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.showProgrammaticSelection(0, colIndex, rowCount - 1, colIndex);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Clear any active programmatic or drag selection.
|
|
145
|
+
*/
|
|
146
|
+
public clearActiveSelection(): void {
|
|
147
|
+
this.clearSelection();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private handlePointerDown(e: PointerEvent): void {
|
|
151
|
+
// Don't interfere with grip drags, resize, or add-button drags
|
|
152
|
+
if (isOtherInteractionActive(this.grid)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Only respond to primary button
|
|
157
|
+
if (e.button !== 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Don't start selection from grip elements
|
|
162
|
+
const target = e.target as HTMLElement;
|
|
163
|
+
|
|
164
|
+
if (target.closest('[data-blok-table-grip]') || target.closest('[data-blok-table-resize]') || target.closest(`[${PILL_ATTR}]`)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const cell = this.resolveCellCoord(target);
|
|
169
|
+
|
|
170
|
+
if (!cell) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// If there's an existing selection, clear it first
|
|
175
|
+
if (this.hasSelection) {
|
|
176
|
+
this.clearSelection();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.anchorCell = cell;
|
|
180
|
+
this.isSelecting = false;
|
|
181
|
+
|
|
182
|
+
// Listen to mousemove in capture phase to cancel RectangleSelection before it runs
|
|
183
|
+
document.addEventListener('mousemove', this.boundCancelRectangle, true);
|
|
184
|
+
document.addEventListener('pointermove', this.boundPointerMove);
|
|
185
|
+
document.addEventListener('pointerup', this.boundPointerUp);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private handlePointerMove(e: PointerEvent): void {
|
|
189
|
+
if (!this.anchorCell) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const target = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;
|
|
194
|
+
|
|
195
|
+
if (!target) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const cell = this.resolveCellCoord(target);
|
|
200
|
+
|
|
201
|
+
if (!cell) {
|
|
202
|
+
// Pointer left the grid — clamp to edge
|
|
203
|
+
this.clampExtentToEdge(e);
|
|
204
|
+
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Still in the same cell as anchor — don't start selection yet
|
|
209
|
+
if (!this.isSelecting && cell.row === this.anchorCell.row && cell.col === this.anchorCell.col) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Crossed into a different cell — start selection
|
|
214
|
+
if (!this.isSelecting) {
|
|
215
|
+
this.isSelecting = true;
|
|
216
|
+
this.onSelectionActiveChange?.(true);
|
|
217
|
+
|
|
218
|
+
// Clear native text selection
|
|
219
|
+
window.getSelection()?.removeAllRanges();
|
|
220
|
+
this.grid.style.userSelect = 'none';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Update extent and repaint
|
|
224
|
+
if (!this.extentCell || this.extentCell.row !== cell.row || this.extentCell.col !== cell.col) {
|
|
225
|
+
this.extentCell = cell;
|
|
226
|
+
this.paintSelection();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private handleCancelRectangle(_e: MouseEvent): void {
|
|
231
|
+
// Cancel RectangleSelection in capture phase, before it processes the event
|
|
232
|
+
if (!this.rectangleSelection) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.rectangleSelection.cancelActiveSelection();
|
|
237
|
+
|
|
238
|
+
// Also directly hide the overlay since cancelActiveSelection() doesn't work when called repeatedly
|
|
239
|
+
const overlay = document.querySelector('[data-blok-overlay-rectangle]');
|
|
240
|
+
|
|
241
|
+
if (!overlay) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const overlayElement = overlay as HTMLElement;
|
|
246
|
+
|
|
247
|
+
overlayElement.style.display = 'none';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private handlePointerUp(): void {
|
|
251
|
+
document.removeEventListener('mousemove', this.boundCancelRectangle, true);
|
|
252
|
+
document.removeEventListener('pointermove', this.boundPointerMove);
|
|
253
|
+
document.removeEventListener('pointerup', this.boundPointerUp);
|
|
254
|
+
|
|
255
|
+
if (this.isSelecting) {
|
|
256
|
+
this.grid.style.userSelect = '';
|
|
257
|
+
this.hasSelection = true;
|
|
258
|
+
|
|
259
|
+
// Listen for next pointerdown anywhere to clear selection
|
|
260
|
+
requestAnimationFrame(() => {
|
|
261
|
+
document.addEventListener('pointerdown', this.boundClearSelection);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.isSelecting = false;
|
|
266
|
+
this.anchorCell = null;
|
|
267
|
+
this.extentCell = null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private handleClearSelection(e: PointerEvent): void {
|
|
271
|
+
const target = e.target;
|
|
272
|
+
|
|
273
|
+
if (target instanceof HTMLElement && target.closest(`[${PILL_ATTR}]`)) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Don't clear while the pill popover is open — the user may be
|
|
278
|
+
// clicking a popover item whose pointerdown bubbles to the document.
|
|
279
|
+
if (this.pillPopover !== null) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
document.removeEventListener('pointerdown', this.boundClearSelection);
|
|
284
|
+
this.clearSelection();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private handleKeyDown(e: KeyboardEvent): void {
|
|
288
|
+
// Only trigger when selection is active
|
|
289
|
+
if (!this.hasSelection) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check for Delete or Backspace
|
|
294
|
+
if (e.key !== 'Delete' && e.key !== 'Backspace') {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Prevent default behavior
|
|
299
|
+
e.preventDefault();
|
|
300
|
+
|
|
301
|
+
// Clear content and dismiss selection
|
|
302
|
+
this.onClearContent?.([...this.selectedCells]);
|
|
303
|
+
this.clearSelection();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private clearSelection(): void {
|
|
307
|
+
const hadSelection = this.hasSelection;
|
|
308
|
+
|
|
309
|
+
this.restoreModifiedCells();
|
|
310
|
+
this.hasSelection = false;
|
|
311
|
+
|
|
312
|
+
if (hadSelection) {
|
|
313
|
+
this.onSelectionActiveChange?.(false);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private restoreModifiedCells(): void {
|
|
318
|
+
this.destroyPillPopover();
|
|
319
|
+
|
|
320
|
+
this.selectedCells.forEach(cell => {
|
|
321
|
+
cell.removeAttribute(SELECTED_ATTR);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (this.pill) {
|
|
325
|
+
this.pill.remove();
|
|
326
|
+
this.pill = null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (this.overlay) {
|
|
330
|
+
this.overlay.remove();
|
|
331
|
+
this.overlay = null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.selectedCells = [];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private showProgrammaticSelection(
|
|
338
|
+
fromRow: number,
|
|
339
|
+
fromCol: number,
|
|
340
|
+
toRow: number,
|
|
341
|
+
toCol: number,
|
|
342
|
+
): void {
|
|
343
|
+
this.clearSelection();
|
|
344
|
+
this.anchorCell = { row: fromRow, col: fromCol };
|
|
345
|
+
this.extentCell = { row: toRow, col: toCol };
|
|
346
|
+
this.paintSelection();
|
|
347
|
+
this.hasSelection = true;
|
|
348
|
+
this.onSelectionActiveChange?.(true);
|
|
349
|
+
this.anchorCell = null;
|
|
350
|
+
this.extentCell = null;
|
|
351
|
+
|
|
352
|
+
// Listen for next pointerdown anywhere to clear the programmatic selection
|
|
353
|
+
requestAnimationFrame(() => {
|
|
354
|
+
document.addEventListener('pointerdown', this.boundClearSelection);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private paintSelection(): void {
|
|
359
|
+
if (!this.anchorCell || !this.extentCell) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Clear previous cell markers
|
|
364
|
+
this.selectedCells.forEach(cell => {
|
|
365
|
+
cell.removeAttribute(SELECTED_ATTR);
|
|
366
|
+
});
|
|
367
|
+
this.selectedCells = [];
|
|
368
|
+
|
|
369
|
+
// Compute rectangle bounds
|
|
370
|
+
const minRow = Math.min(this.anchorCell.row, this.extentCell.row);
|
|
371
|
+
const maxRow = Math.max(this.anchorCell.row, this.extentCell.row);
|
|
372
|
+
const minCol = Math.min(this.anchorCell.col, this.extentCell.col);
|
|
373
|
+
const maxCol = Math.max(this.anchorCell.col, this.extentCell.col);
|
|
374
|
+
|
|
375
|
+
const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
|
|
376
|
+
|
|
377
|
+
// Mark selected cells
|
|
378
|
+
this.selectedCells = this.collectCellsInRange(rows, minRow, maxRow, minCol, maxCol);
|
|
379
|
+
this.selectedCells.forEach(cell => {
|
|
380
|
+
cell.setAttribute(SELECTED_ATTR, '');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Calculate overlay position from bounding rects of corner cells
|
|
384
|
+
const firstCell = rows[minRow]?.querySelectorAll(`[${CELL_ATTR}]`)[minCol] as HTMLElement | undefined;
|
|
385
|
+
const lastCell = rows[maxRow]?.querySelectorAll(`[${CELL_ATTR}]`)[maxCol] as HTMLElement | undefined;
|
|
386
|
+
|
|
387
|
+
if (!firstCell || !lastCell) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const gridRect = this.grid.getBoundingClientRect();
|
|
392
|
+
const firstRect = firstCell.getBoundingClientRect();
|
|
393
|
+
const lastRect = lastCell.getBoundingClientRect();
|
|
394
|
+
|
|
395
|
+
// getBoundingClientRect() measures from the border-box edge, but
|
|
396
|
+
// position:absolute offsets from the padding-box edge. Subtract
|
|
397
|
+
// grid border widths to align with cell edges.
|
|
398
|
+
const gridStyle = getComputedStyle(this.grid);
|
|
399
|
+
const borderTop = parseFloat(gridStyle.borderTopWidth) || 0;
|
|
400
|
+
const borderLeft = parseFloat(gridStyle.borderLeftWidth) || 0;
|
|
401
|
+
|
|
402
|
+
const width = lastRect.right - firstRect.left + 1;
|
|
403
|
+
const height = lastRect.bottom - firstRect.top + 1;
|
|
404
|
+
|
|
405
|
+
// Extend overlay 1px outward to cover adjacent borders:
|
|
406
|
+
// grid border-top/border-left at row 0/col 0, or the previous
|
|
407
|
+
// row's border-bottom / previous column's border-right otherwise.
|
|
408
|
+
const top = firstRect.top - gridRect.top - borderTop - 1;
|
|
409
|
+
const left = firstRect.left - gridRect.left - borderLeft - 1;
|
|
410
|
+
|
|
411
|
+
// Create overlay once, reuse on subsequent paints
|
|
412
|
+
if (!this.overlay) {
|
|
413
|
+
this.overlay = document.createElement('div');
|
|
414
|
+
this.overlay.setAttribute('data-blok-table-selection-overlay', '');
|
|
415
|
+
this.overlay.style.position = 'absolute';
|
|
416
|
+
this.overlay.style.border = SELECTION_BORDER;
|
|
417
|
+
this.overlay.style.pointerEvents = 'none';
|
|
418
|
+
this.overlay.style.boxSizing = 'border-box';
|
|
419
|
+
this.overlay.style.borderRadius = '2px';
|
|
420
|
+
this.grid.appendChild(this.overlay);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
this.overlay.style.top = `${top}px`;
|
|
424
|
+
this.overlay.style.left = `${left}px`;
|
|
425
|
+
this.overlay.style.width = `${width}px`;
|
|
426
|
+
this.overlay.style.height = `${height}px`;
|
|
427
|
+
|
|
428
|
+
// Create pill once, reuse on subsequent paints
|
|
429
|
+
if (!this.pill) {
|
|
430
|
+
this.pill = this.createPill();
|
|
431
|
+
this.grid.appendChild(this.pill);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Position at center of the 2px right border; translate(-50%,-50%) handles centering
|
|
435
|
+
this.pill.style.left = `${left + width - 1}px`;
|
|
436
|
+
this.pill.style.top = `${top + height / 2}px`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private createPill(): HTMLElement {
|
|
440
|
+
const pill = document.createElement('div');
|
|
441
|
+
|
|
442
|
+
pill.setAttribute(PILL_ATTR, '');
|
|
443
|
+
pill.setAttribute('contenteditable', 'false');
|
|
444
|
+
pill.className = twMerge(PILL_CLASSES);
|
|
445
|
+
pill.style.width = `${PILL_IDLE_SIZE}px`;
|
|
446
|
+
pill.style.height = `${PILL_HEIGHT}px`;
|
|
447
|
+
pill.style.pointerEvents = 'auto';
|
|
448
|
+
pill.style.transform = 'translate(-50%, -50%)';
|
|
449
|
+
pill.style.outline = '2px solid white';
|
|
450
|
+
|
|
451
|
+
const svg = createGripDotsSvg('vertical');
|
|
452
|
+
|
|
453
|
+
svg.classList.remove('text-gray-400');
|
|
454
|
+
svg.classList.add('text-white');
|
|
455
|
+
pill.appendChild(svg);
|
|
456
|
+
|
|
457
|
+
pill.addEventListener('mouseenter', () => {
|
|
458
|
+
if (this.pillPopover === null) {
|
|
459
|
+
this.expandPill();
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
pill.addEventListener('mouseleave', () => {
|
|
463
|
+
if (this.pillPopover === null) {
|
|
464
|
+
this.collapsePill();
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
pill.addEventListener('pointerdown', (e) => {
|
|
468
|
+
e.stopPropagation();
|
|
469
|
+
e.preventDefault();
|
|
470
|
+
this.openPillPopover();
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
return pill;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private openPillPopover(): void {
|
|
477
|
+
this.destroyPillPopover();
|
|
478
|
+
|
|
479
|
+
if (!this.pill) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this.expandPill();
|
|
484
|
+
|
|
485
|
+
const items: PopoverItemParams[] = [
|
|
486
|
+
{
|
|
487
|
+
icon: IconCross,
|
|
488
|
+
title: this.i18n.t('tools.table.clearSelection'),
|
|
489
|
+
closeOnActivate: true,
|
|
490
|
+
onActivate: (): void => {
|
|
491
|
+
this.onClearContent?.([...this.selectedCells]);
|
|
492
|
+
this.clearSelection();
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
];
|
|
496
|
+
|
|
497
|
+
this.pillPopover = new PopoverDesktop({
|
|
498
|
+
items,
|
|
499
|
+
trigger: this.pill,
|
|
500
|
+
flippable: true,
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
this.pillPopover.on(PopoverEvent.Closed, () => {
|
|
504
|
+
if (this.pillPopover === null) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
this.destroyPillPopover();
|
|
509
|
+
|
|
510
|
+
this.collapsePill();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
this.pillPopover.show();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private expandPill(): void {
|
|
517
|
+
if (!this.pill) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
this.pill.style.width = `${PILL_WIDTH}px`;
|
|
522
|
+
|
|
523
|
+
const svg = this.pill.querySelector('svg');
|
|
524
|
+
|
|
525
|
+
if (svg) {
|
|
526
|
+
svg.classList.remove('opacity-0');
|
|
527
|
+
svg.classList.add('opacity-100');
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private collapsePill(): void {
|
|
532
|
+
if (!this.pill) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
this.pill.style.width = `${PILL_IDLE_SIZE}px`;
|
|
537
|
+
|
|
538
|
+
const svg = this.pill.querySelector('svg');
|
|
539
|
+
|
|
540
|
+
if (svg) {
|
|
541
|
+
svg.classList.add('opacity-0');
|
|
542
|
+
svg.classList.remove('opacity-100');
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private destroyPillPopover(): void {
|
|
547
|
+
if (this.pillPopover !== null) {
|
|
548
|
+
const popover = this.pillPopover;
|
|
549
|
+
|
|
550
|
+
this.pillPopover = null;
|
|
551
|
+
popover.destroy();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private resolveCellCoord(target: HTMLElement): CellCoord | null {
|
|
556
|
+
const cell = target.closest<HTMLElement>(`[${CELL_ATTR}]`);
|
|
557
|
+
|
|
558
|
+
if (!cell) {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const row = cell.closest<HTMLElement>(`[${ROW_ATTR}]`);
|
|
563
|
+
|
|
564
|
+
if (!row) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Verify cell is within our grid
|
|
569
|
+
if (!this.grid.contains(row)) {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const rows = Array.from(this.grid.querySelectorAll(`[${ROW_ATTR}]`));
|
|
574
|
+
const rowIndex = rows.indexOf(row);
|
|
575
|
+
|
|
576
|
+
if (rowIndex < 0) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const cells = Array.from(row.querySelectorAll(`[${CELL_ATTR}]`));
|
|
581
|
+
const colIndex = cells.indexOf(cell);
|
|
582
|
+
|
|
583
|
+
if (colIndex < 0) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return { row: rowIndex, col: colIndex };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private clampExtentToEdge(e: PointerEvent): void {
|
|
591
|
+
if (!this.anchorCell || !this.isSelecting) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const gridRect = this.grid.getBoundingClientRect();
|
|
596
|
+
const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
|
|
597
|
+
const rowCount = rows.length;
|
|
598
|
+
const colCount = rows[0]?.querySelectorAll(`[${CELL_ATTR}]`).length ?? 0;
|
|
599
|
+
|
|
600
|
+
if (rowCount === 0 || colCount === 0) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const row = this.clampAxis(e.clientY, gridRect.top, gridRect.bottom, rowCount, this.extentCell?.row ?? this.anchorCell.row);
|
|
605
|
+
const col = this.clampAxis(e.clientX, gridRect.left, gridRect.right, colCount, this.extentCell?.col ?? this.anchorCell.col);
|
|
606
|
+
|
|
607
|
+
const clamped = { row, col };
|
|
608
|
+
|
|
609
|
+
if (!this.extentCell || this.extentCell.row !== clamped.row || this.extentCell.col !== clamped.col) {
|
|
610
|
+
this.extentCell = clamped;
|
|
611
|
+
this.paintSelection();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private collectCellsInRange(
|
|
616
|
+
rows: NodeListOf<Element>,
|
|
617
|
+
minRow: number,
|
|
618
|
+
maxRow: number,
|
|
619
|
+
minCol: number,
|
|
620
|
+
maxCol: number,
|
|
621
|
+
): HTMLElement[] {
|
|
622
|
+
return Array.from(rows)
|
|
623
|
+
.slice(minRow, maxRow + 1)
|
|
624
|
+
.flatMap(row => {
|
|
625
|
+
const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
|
|
626
|
+
|
|
627
|
+
return Array.from(cells)
|
|
628
|
+
.slice(minCol, maxCol + 1)
|
|
629
|
+
.filter((cell): cell is HTMLElement => cell instanceof HTMLElement);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Clamp a pointer coordinate to an axis range, returning the edge index
|
|
635
|
+
* when outside or the fallback when inside.
|
|
636
|
+
*/
|
|
637
|
+
private clampAxis(pointer: number, min: number, max: number, count: number, fallback: number): number {
|
|
638
|
+
if (pointer < min) {
|
|
639
|
+
return 0;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (pointer > max) {
|
|
643
|
+
return count - 1;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return fallback;
|
|
647
|
+
}
|
|
648
|
+
}
|