@jackuait/blok 0.10.2 → 0.10.4
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-D-T1XZ92.mjs → blok-NcdNQ0I6.mjs} +2304 -2101
- package/dist/chunks/{constants-CaB-mlB5.mjs → constants-DtfShkXT.mjs} +414 -317
- package/dist/chunks/{i18next-loader-CDnSPae_.mjs → i18next-loader-D32EUWLr.mjs} +1 -1
- package/dist/chunks/{lightweight-i18n-DZmo8dAI.mjs → lightweight-i18n-DpkvRXEd.mjs} +19 -0
- package/dist/{messages-Ddq3Ce3E2.mjs → chunks/messages-AD17iDBx.mjs} +18 -0
- package/dist/{messages-neGD3WGq.mjs → chunks/messages-B03yUEra2.mjs} +18 -0
- package/dist/chunks/{messages-CIfUm1Oa.mjs → messages-B2dU00Z3.mjs} +18 -0
- package/dist/chunks/{messages-BKN3YVIj.mjs → messages-B8hICx3L.mjs} +18 -0
- package/dist/{messages-Dnd5YSWv.mjs → chunks/messages-BBe45sPH.mjs} +18 -0
- package/dist/{messages-C7lJg8fy2.mjs → chunks/messages-BCifzMVO2.mjs} +18 -0
- package/dist/chunks/{messages-D7dx_6k8.mjs → messages-BGmvvtg_.mjs} +18 -0
- package/dist/{messages-Q5sQeVap2.mjs → chunks/messages-BJNFCDv42.mjs} +18 -0
- package/dist/chunks/{messages-BlxwW7M6.mjs → messages-BNy4e7Xl.mjs} +18 -0
- package/dist/chunks/{messages-C15z2r5U.mjs → messages-BcHZf9o-.mjs} +18 -0
- package/dist/{messages-A96tMxeU.mjs → chunks/messages-BjyYZeBm2.mjs} +18 -0
- package/dist/{messages-BbJ7ZXY8.mjs → chunks/messages-Bop7vrhU.mjs} +18 -0
- package/dist/{messages-BiTMwiKH.mjs → chunks/messages-BouFtpfO.mjs} +18 -0
- package/dist/chunks/{messages-ElIGUi0O2.mjs → messages-Br6bE1FD2.mjs} +18 -0
- package/dist/chunks/{messages-BHMiK51R.mjs → messages-C-EBhOHE.mjs} +18 -0
- package/dist/chunks/{messages-kGmxkeFH.mjs → messages-C3X7dv3f.mjs} +18 -0
- package/dist/chunks/{messages-4Ck88DYZ2.mjs → messages-C7Pjof0d2.mjs} +18 -0
- package/dist/{messages-D0lLw9KM.mjs → chunks/messages-C7sBaZOO2.mjs} +18 -0
- package/dist/chunks/{messages-QMOmwcZb.mjs → messages-C85zv_7x.mjs} +18 -0
- package/dist/chunks/{messages-DSrdy9Nw2.mjs → messages-CCEgR9GN2.mjs} +18 -0
- package/dist/chunks/{messages-DUr9WAkD.mjs → messages-CDSyoUft.mjs} +18 -0
- package/dist/{messages-bkGniiaz.mjs → chunks/messages-CGFlOwst.mjs} +18 -0
- package/dist/chunks/{messages-DBMaLL8b2.mjs → messages-CGLTjtRv2.mjs} +18 -0
- package/dist/{messages-2ZWBTerL.mjs → chunks/messages-CGPxUESo.mjs} +18 -0
- package/dist/chunks/{messages-BfAcUavP.mjs → messages-CNaaqQVz.mjs} +18 -0
- package/dist/{messages-DBhvm8NK.mjs → chunks/messages-CPFB2_m-2.mjs} +18 -0
- package/dist/chunks/{messages-zt6zdYWh.mjs → messages-CTdSIOAc.mjs} +18 -0
- package/dist/chunks/{messages-1_6UkKLS.mjs → messages-CXVWb9js.mjs} +18 -0
- package/dist/{messages-CdEASHDp2.mjs → chunks/messages-Cfbmwdep2.mjs} +18 -0
- package/dist/chunks/{messages-DxHh0O8j2.mjs → messages-ChayV9WY2.mjs} +18 -0
- package/dist/chunks/{messages-BgM91Lxm2.mjs → messages-Ci7UXRVI2.mjs} +18 -0
- package/dist/chunks/{messages-Clku7Cf-2.mjs → messages-CpXvyGWv2.mjs} +18 -0
- package/dist/chunks/{messages-DjvaiALg2.mjs → messages-Cql2ozf_2.mjs} +18 -0
- package/dist/{messages-DODrhcop.mjs → chunks/messages-Cxy_E2IS.mjs} +18 -0
- package/dist/chunks/{messages-CZSlfnkO2.mjs → messages-D9syZVGi2.mjs} +18 -0
- package/dist/chunks/{messages-BRAoJpOu.mjs → messages-D9uWAWjW.mjs} +18 -0
- package/dist/chunks/{messages-BK8Cp2d0.mjs → messages-DRJxSTqs.mjs} +18 -0
- package/dist/{messages-C_Qn9SbQ.mjs → chunks/messages-DSbI0vJf.mjs} +18 -0
- package/dist/chunks/{messages-CD_MnBln.mjs → messages-DVvrZRyZ.mjs} +18 -0
- package/dist/{messages-BE_z-zrb.mjs → chunks/messages-DY8zPIZW.mjs} +18 -0
- package/dist/chunks/{messages-Bz0-KNEB.mjs → messages-D_kZN9rB.mjs} +18 -0
- package/dist/{messages-C1vc5584.mjs → chunks/messages-DjSuq0-y2.mjs} +18 -0
- package/dist/chunks/{messages-DPzHD51Y.mjs → messages-DkP3Jf4F.mjs} +18 -0
- package/dist/{messages-_PLyRfVw.mjs → chunks/messages-DoPdy75l.mjs} +18 -0
- package/dist/chunks/{messages-JSQjKQ8I.mjs → messages-DpydMd36.mjs} +18 -0
- package/dist/{messages-BckDk9aq2.mjs → chunks/messages-DtZ9U9g72.mjs} +18 -0
- package/dist/{messages-JNrYldAa2.mjs → chunks/messages-H6vLy8wJ.mjs} +18 -0
- package/dist/chunks/{messages-DTN1XGll.mjs → messages-HzH9_QH8.mjs} +18 -0
- package/dist/chunks/{messages-C0IFfhnp.mjs → messages-O6FOfUgF.mjs} +18 -0
- package/dist/{messages-Be_2RHZD.mjs → chunks/messages-OSP4Hj5o.mjs} +18 -0
- package/dist/chunks/{messages-DMoERagV2.mjs → messages-RiqdVwuN2.mjs} +18 -0
- package/dist/chunks/{messages-BJ-vT1SU2.mjs → messages-SP659Sal2.mjs} +18 -0
- package/dist/{messages-Che99vKP.mjs → chunks/messages-THR8q8bJ.mjs} +18 -0
- package/dist/chunks/{messages-CvANwuht2.mjs → messages-VlEyFUxF2.mjs} +18 -0
- package/dist/{messages-apA6BStA.mjs → chunks/messages-VtfKWZ2S.mjs} +18 -0
- package/dist/{messages-DpJGbx3q.mjs → chunks/messages-YbckahVx2.mjs} +18 -0
- package/dist/{messages-DYuD5-rO.mjs → chunks/messages-ZhHLC6dk.mjs} +18 -0
- package/dist/{messages-C0GSBBCo2.mjs → chunks/messages-bFEdH3lv.mjs} +18 -0
- package/dist/chunks/{messages-euM2m3wQ.mjs → messages-dpXwA3Sz.mjs} +18 -0
- package/dist/chunks/{messages-CQBo3lmL2.mjs → messages-fbL5y58u2.mjs} +18 -0
- package/dist/chunks/{messages-CxiURE2X.mjs → messages-oPV2oMxM.mjs} +18 -0
- package/dist/{messages-DM4Gjc9h.mjs → chunks/messages-oXBbHW9A.mjs} +18 -0
- package/dist/chunks/{messages-QilfinOn2.mjs → messages-vDgsEqQW2.mjs} +18 -0
- package/dist/{messages-ClGvlFcH2.mjs → chunks/messages-wYQksm10.mjs} +18 -0
- package/dist/{messages-CnuH-BZK2.mjs → chunks/messages-yGedmr61.mjs} +18 -0
- package/dist/chunks/{messages-sDdNf8O9.mjs → messages-zQOpKjl3.mjs} +18 -0
- package/dist/chunks/{messages-eFd4YYzt.mjs → messages-zWqsggJh.mjs} +18 -0
- package/dist/chunks/{tools-BFK2MvVI.mjs → tools-DMSi-3RW.mjs} +3434 -1240
- package/dist/full.mjs +10 -10
- package/dist/locales.mjs +86 -67
- package/dist/{messages-BK_LsgY4.mjs → messages-0lOPMv8u.mjs} +18 -0
- package/dist/{messages-LYJbLq_F.mjs → messages-5wuR90qS.mjs} +18 -0
- package/dist/{messages-98nQiC7t2.mjs → messages-6eX0fWGR2.mjs} +18 -0
- package/dist/{chunks/messages-DUeiPraX.mjs → messages-9L4qqCKh2.mjs} +18 -0
- package/dist/{chunks/messages-Q7-4ZJLB2.mjs → messages-B4zPxKl62.mjs} +18 -0
- package/dist/{messages-D0005ti32.mjs → messages-BCMFYqKc2.mjs} +18 -0
- package/dist/{chunks/messages-CC_noR8y.mjs → messages-BKXjO3NH.mjs} +18 -0
- package/dist/{messages-CRNogopy2.mjs → messages-BLW2GX7J2.mjs} +18 -0
- package/dist/{messages-D81w6AmW.mjs → messages-BLfK27kX.mjs} +18 -0
- package/dist/{chunks/messages-CPBN4zWc.mjs → messages-BM2kx9Td.mjs} +18 -0
- package/dist/{messages-E8NjqzWq2.mjs → messages-BORkMoil2.mjs} +18 -0
- package/dist/{messages-Dqu4aX9s.mjs → messages-BPw_x-6H.mjs} +18 -0
- package/dist/{messages-DSmxJWju2.mjs → messages-BRY51SEw2.mjs} +18 -0
- package/dist/{chunks/messages-BONyZroH.mjs → messages-BSNsrZVN.mjs} +18 -0
- package/dist/{chunks/messages-BAlZjPcl.mjs → messages-B_UKuqrH.mjs} +18 -0
- package/dist/{chunks/messages-DB_-5Xln.mjs → messages-BrYeJsSE2.mjs} +18 -0
- package/dist/{messages-Brd5R-da2.mjs → messages-BwttyHDI2.mjs} +18 -0
- package/dist/{messages-qfvXgPpu2.mjs → messages-C-8qb9sf2.mjs} +18 -0
- package/dist/{chunks/messages-BbEW9bQz.mjs → messages-C34dTwF72.mjs} +18 -0
- package/dist/{messages-BmH2cQHQ.mjs → messages-C67YUZ9-.mjs} +18 -0
- package/dist/{messages-DpwMKDV0.mjs → messages-C6yKu_PJ.mjs} +18 -0
- package/dist/{messages-Do7Xjy0n.mjs → messages-CA6J_QoC.mjs} +18 -0
- package/dist/{messages-DVL0KZE5.mjs → messages-CFUBJfnf.mjs} +18 -0
- package/dist/{chunks/messages-DVr1sqfI2.mjs → messages-CLUBh7O_.mjs} +18 -0
- package/dist/{chunks/messages-wl8YrvGG.mjs → messages-CLZoy5fQ.mjs} +18 -0
- package/dist/{messages-CisR4PNV.mjs → messages-CNGwdIEz.mjs} +18 -0
- package/dist/{messages-DopaMHC42.mjs → messages-CR4gHjd82.mjs} +18 -0
- package/dist/{messages-DK6dA0O2.mjs → messages-CVMngZNA.mjs} +18 -0
- package/dist/{messages-Xc0KUbYl.mjs → messages-Cd5CW5Tt.mjs} +18 -0
- package/dist/{chunks/messages-ChK7v1PV.mjs → messages-CrjQ2Op0.mjs} +18 -0
- package/dist/{chunks/messages-CRF7nNrO.mjs → messages-Cv1PSaNk.mjs} +18 -0
- package/dist/{messages-DOGbHYv-2.mjs → messages-CxZarWTm2.mjs} +18 -0
- package/dist/{messages-D3rwCtKn.mjs → messages-D0eT_eWA.mjs} +18 -0
- package/dist/{messages-C6ONf71u2.mjs → messages-D6RYu9JW2.mjs} +18 -0
- package/dist/{messages-DQORja0D.mjs → messages-D8U5D391.mjs} +18 -0
- package/dist/{chunks/messages-EDMC5ukV.mjs → messages-D8dO6OMN.mjs} +18 -0
- package/dist/{messages-DfFZ6Yj5.mjs → messages-DA4T9WBe.mjs} +18 -0
- package/dist/{chunks/messages-D22e9h7V2.mjs → messages-DB4UKN8D.mjs} +18 -0
- package/dist/{chunks/messages-DEBy3nuJ2.mjs → messages-DCdP2ujL.mjs} +18 -0
- package/dist/{messages-D05jqBIa2.mjs → messages-DPFuzIdF2.mjs} +18 -0
- package/dist/{chunks/messages-DrfRYiM32.mjs → messages-DQ1icG7L.mjs} +18 -0
- package/dist/{chunks/messages-a07QVz8U.mjs → messages-DT7dwzEe.mjs} +18 -0
- package/dist/{chunks/messages-CszmHAvQ.mjs → messages-DUYxMxrQ2.mjs} +18 -0
- package/dist/{chunks/messages-DtoId_bw2.mjs → messages-D_V0kHD7.mjs} +18 -0
- package/dist/{messages-BesJaI6A.mjs → messages-DfqM_XvD.mjs} +18 -0
- package/dist/{messages-CT-Kdas6.mjs → messages-Di3-WVzq.mjs} +18 -0
- package/dist/{messages-BcVB3osF.mjs → messages-Dl0bfeA-.mjs} +18 -0
- package/dist/{chunks/messages-C1S9ztpF.mjs → messages-Do3mHd9U.mjs} +18 -0
- package/dist/{chunks/messages-BeGZqQwz.mjs → messages-DqDlcEPn.mjs} +18 -0
- package/dist/{chunks/messages-CTCe595D2.mjs → messages-DwiykEgr2.mjs} +18 -0
- package/dist/{chunks/messages-8Ld7P_9j2.mjs → messages-Dx5n6MLQ2.mjs} +18 -0
- package/dist/{messages-LMaR2_bE.mjs → messages-DxEiqa-B.mjs} +18 -0
- package/dist/{chunks/messages-CxxyR4vY.mjs → messages-Dxr1BBvo.mjs} +18 -0
- package/dist/{messages-D6VIFnSW.mjs → messages-DzknMM7W.mjs} +18 -0
- package/dist/{chunks/messages-oMc7qugU2.mjs → messages-ELvF3qMl2.mjs} +18 -0
- package/dist/{messages-53w0fPZS2.mjs → messages-JVJdC0Er2.mjs} +18 -0
- package/dist/{chunks/messages-BMD37y3q2.mjs → messages-KVerxvZC.mjs} +18 -0
- package/dist/{chunks/messages-Du2BffA7.mjs → messages-OOiDDmVw.mjs} +18 -0
- package/dist/{messages-uwK7ktqk.mjs → messages-PyOr_YgV.mjs} +18 -0
- package/dist/{messages-qbKjjvgd2.mjs → messages-VrQw3tQ62.mjs} +18 -0
- package/dist/{messages-CTTmWn4Y2.mjs → messages-WsUHzXMu2.mjs} +18 -0
- package/dist/{messages-CZbcxlZt2.mjs → messages-ZHgPRUj02.mjs} +18 -0
- package/dist/{messages-DKHbt-7l2.mjs → messages-aoO_TtoE2.mjs} +18 -0
- package/dist/{chunks/messages-CW35K1pq.mjs → messages-bh8BiOee2.mjs} +18 -0
- package/dist/{messages-BrOWqNCu2.mjs → messages-gZEhkRrR2.mjs} +18 -0
- package/dist/{chunks/messages-BRoa9tGl.mjs → messages-hya8YLMj.mjs} +18 -0
- package/dist/{messages-CdduYw-q.mjs → messages-tb1FD_ge.mjs} +18 -0
- package/dist/react.mjs +2 -2
- package/dist/tools.mjs +3 -3
- package/dist/vendor.LICENSE.txt +135 -0
- package/package.json +2 -1
- package/src/blok.ts +48 -0
- package/src/components/block/index.ts +21 -2
- package/src/components/block-tunes/block-tune-copy-link.ts +82 -0
- package/src/components/i18n/locales/am/messages.json +18 -0
- package/src/components/i18n/locales/ar/messages.json +18 -0
- package/src/components/i18n/locales/az/messages.json +18 -0
- package/src/components/i18n/locales/bg/messages.json +18 -0
- package/src/components/i18n/locales/bn/messages.json +18 -0
- package/src/components/i18n/locales/bs/messages.json +18 -0
- package/src/components/i18n/locales/cs/messages.json +18 -0
- package/src/components/i18n/locales/da/messages.json +18 -0
- package/src/components/i18n/locales/de/messages.json +18 -0
- package/src/components/i18n/locales/dv/messages.json +18 -0
- package/src/components/i18n/locales/el/messages.json +18 -0
- package/src/components/i18n/locales/en/messages.json +19 -0
- package/src/components/i18n/locales/es/messages.json +18 -0
- package/src/components/i18n/locales/et/messages.json +18 -0
- package/src/components/i18n/locales/fa/messages.json +18 -0
- package/src/components/i18n/locales/fi/messages.json +18 -0
- package/src/components/i18n/locales/fil/messages.json +18 -0
- package/src/components/i18n/locales/fr/messages.json +18 -0
- package/src/components/i18n/locales/gu/messages.json +18 -0
- package/src/components/i18n/locales/he/messages.json +18 -0
- package/src/components/i18n/locales/hi/messages.json +18 -0
- package/src/components/i18n/locales/hr/messages.json +18 -0
- package/src/components/i18n/locales/hu/messages.json +18 -0
- package/src/components/i18n/locales/hy/messages.json +18 -0
- package/src/components/i18n/locales/id/messages.json +18 -0
- package/src/components/i18n/locales/it/messages.json +18 -0
- package/src/components/i18n/locales/ja/messages.json +18 -0
- package/src/components/i18n/locales/ka/messages.json +18 -0
- package/src/components/i18n/locales/km/messages.json +18 -0
- package/src/components/i18n/locales/kn/messages.json +18 -0
- package/src/components/i18n/locales/ko/messages.json +18 -0
- package/src/components/i18n/locales/ku/messages.json +18 -0
- package/src/components/i18n/locales/lo/messages.json +18 -0
- package/src/components/i18n/locales/lt/messages.json +18 -0
- package/src/components/i18n/locales/lv/messages.json +18 -0
- package/src/components/i18n/locales/mk/messages.json +18 -0
- package/src/components/i18n/locales/ml/messages.json +18 -0
- package/src/components/i18n/locales/mn/messages.json +18 -0
- package/src/components/i18n/locales/mr/messages.json +18 -0
- package/src/components/i18n/locales/ms/messages.json +18 -0
- package/src/components/i18n/locales/my/messages.json +18 -0
- package/src/components/i18n/locales/ne/messages.json +18 -0
- package/src/components/i18n/locales/nl/messages.json +18 -0
- package/src/components/i18n/locales/no/messages.json +18 -0
- package/src/components/i18n/locales/pa/messages.json +18 -0
- package/src/components/i18n/locales/pl/messages.json +18 -0
- package/src/components/i18n/locales/ps/messages.json +18 -0
- package/src/components/i18n/locales/pt/messages.json +18 -0
- package/src/components/i18n/locales/ro/messages.json +18 -0
- package/src/components/i18n/locales/ru/messages.json +18 -0
- package/src/components/i18n/locales/sd/messages.json +18 -0
- package/src/components/i18n/locales/si/messages.json +18 -0
- package/src/components/i18n/locales/sk/messages.json +18 -0
- package/src/components/i18n/locales/sl/messages.json +18 -0
- package/src/components/i18n/locales/sq/messages.json +18 -0
- package/src/components/i18n/locales/sr/messages.json +18 -0
- package/src/components/i18n/locales/sv/messages.json +18 -0
- package/src/components/i18n/locales/sw/messages.json +18 -0
- package/src/components/i18n/locales/ta/messages.json +18 -0
- package/src/components/i18n/locales/te/messages.json +18 -0
- package/src/components/i18n/locales/th/messages.json +18 -0
- package/src/components/i18n/locales/tr/messages.json +18 -0
- package/src/components/i18n/locales/ug/messages.json +18 -0
- package/src/components/i18n/locales/uk/messages.json +18 -0
- package/src/components/i18n/locales/ur/messages.json +18 -0
- package/src/components/i18n/locales/vi/messages.json +18 -0
- package/src/components/i18n/locales/yi/messages.json +18 -0
- package/src/components/i18n/locales/zh/messages.json +18 -0
- package/src/components/icons/index.ts +65 -0
- package/src/components/inline-tools/inline-tool-bold.ts +10 -0
- package/src/components/inline-tools/inline-tool-code.ts +54 -1
- package/src/components/inline-tools/inline-tool-italic.ts +54 -1
- package/src/components/inline-tools/inline-tool-strikethrough.ts +54 -1
- package/src/components/inline-tools/inline-tool-underline.ts +54 -1
- package/src/components/inline-tools/services/bold-normalization-pass.ts +29 -3
- package/src/components/inline-tools/utils/formatting-range-utils.ts +83 -0
- package/src/components/modules/api/tools.ts +19 -0
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +5 -5
- package/src/components/modules/blockManager/blockManager.ts +22 -0
- package/src/components/modules/blockManager/event-binder.ts +12 -1
- package/src/components/modules/blockManager/factory.ts +4 -0
- package/src/components/modules/blockManager/types.ts +4 -0
- package/src/components/modules/blockManager/yjs-sync.ts +16 -2
- package/src/components/modules/paste/google-docs-preprocessor.ts +49 -3
- package/src/components/modules/paste/handlers/table-cells-handler.ts +12 -2
- package/src/components/modules/paste/index.ts +8 -4
- package/src/components/modules/paste/types.ts +2 -0
- package/src/components/modules/renderer.ts +22 -2
- package/src/components/modules/saver.ts +19 -1
- package/src/components/modules/themeManager.ts +3 -1
- package/src/components/modules/toolbar/blockSettings.ts +51 -1
- package/src/components/modules/toolbar/index.ts +95 -3
- package/src/components/modules/toolbar/inline/index.ts +0 -3
- package/src/components/modules/toolbar/plus-button.ts +37 -0
- package/src/components/modules/toolbar/settings-toggler.ts +6 -0
- package/src/components/modules/tools.ts +5 -0
- package/src/components/modules/uiControllers/controllers/keyboard.ts +85 -22
- package/src/components/modules/uiControllers/controllers/selection.ts +14 -2
- package/src/components/modules/yjs/document-store.ts +22 -0
- package/src/components/modules/yjs/index.ts +10 -0
- package/src/components/modules/yjs/serializer.ts +20 -0
- package/src/components/selection/cursor.ts +12 -2
- package/src/components/ui/toolbox.ts +31 -5
- package/src/components/utils/id-generator.ts +11 -0
- package/src/components/utils/key-icon.ts +187 -0
- package/src/components/utils/popover/components/hint/hint.ts +3 -1
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +18 -5
- package/src/components/utils/popover/popover-abstract.ts +45 -0
- package/src/components/utils/popover/popover-desktop.ts +1 -0
- package/src/components/utils/popover/popover-position.ts +12 -5
- package/src/components/utils/popover/popover.const.ts +2 -0
- package/src/components/utils.ts +1 -0
- package/src/styles/main.css +1269 -0
- package/src/tools/code/index.ts +4 -0
- package/src/tools/database/database-backend-sync.ts +132 -0
- package/src/tools/database/database-board-view.ts +410 -0
- package/src/tools/database/database-card-drag.ts +306 -0
- package/src/tools/database/database-card-drawer.ts +546 -0
- package/src/tools/database/database-column-controls.ts +141 -0
- package/src/tools/database/database-column-drag.ts +262 -0
- package/src/tools/database/database-keyboard.ts +35 -0
- package/src/tools/database/database-list-row-drag.ts +245 -0
- package/src/tools/database/database-list-view.ts +333 -0
- package/src/tools/database/database-model.ts +214 -0
- package/src/tools/database/database-property-type-popover.ts +108 -0
- package/src/tools/database/database-tab-bar.ts +558 -0
- package/src/tools/database/database-view-popover.ts +129 -0
- package/src/tools/database/database-view-renderer.ts +25 -0
- package/src/tools/database/index.ts +1223 -0
- package/src/tools/database/types.ts +152 -0
- package/src/tools/database-row/index.ts +74 -0
- package/src/tools/index.ts +4 -0
- package/src/tools/table/index.ts +10 -19
- package/src/tools/table/table-cell-selection.ts +126 -7
- package/src/tools/table/table-core.ts +59 -5
- package/src/tools/table/table-model.ts +8 -0
- package/src/tools/table/table-row-col-controls.ts +40 -18
- package/types/api/tools.d.ts +18 -0
- package/types/configs/blok-config.d.ts +27 -0
- package/types/data-formats/output-data.d.ts +13 -0
- package/types/index.d.ts +17 -0
- package/types/tools/database.d.ts +152 -0
- package/types/tools-entry.d.ts +7 -4
- package/types/utils/popover/popover.d.ts +6 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { BlockToolData, OutputData } from '../../../types';
|
|
2
|
+
|
|
3
|
+
// ─── Property types ───
|
|
4
|
+
|
|
5
|
+
export type PropertyType = 'title' | 'text' | 'number' | 'select' | 'multiSelect' | 'date' | 'checkbox' | 'url' | 'richText';
|
|
6
|
+
|
|
7
|
+
export interface SelectOption {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
color?: string;
|
|
11
|
+
position: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SelectPropertyConfig {
|
|
15
|
+
options: SelectOption[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PropertyConfig = SelectPropertyConfig;
|
|
19
|
+
|
|
20
|
+
export interface PropertyDefinition {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
type: PropertyType;
|
|
24
|
+
position: string;
|
|
25
|
+
config?: PropertyConfig;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type PropertyValue = string | number | boolean | string[] | OutputData | null;
|
|
29
|
+
|
|
30
|
+
// ─── Rows ───
|
|
31
|
+
|
|
32
|
+
export interface DatabaseRow {
|
|
33
|
+
id: string;
|
|
34
|
+
position: string;
|
|
35
|
+
properties: Record<string, PropertyValue>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DatabaseRowData extends BlockToolData {
|
|
39
|
+
properties: Record<string, PropertyValue>;
|
|
40
|
+
position: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── View config ───
|
|
44
|
+
|
|
45
|
+
export type ViewType = 'board' | 'table' | 'gallery' | 'list';
|
|
46
|
+
|
|
47
|
+
export interface SortConfig {
|
|
48
|
+
propertyId: string;
|
|
49
|
+
direction: 'asc' | 'desc';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface FilterConfig {
|
|
53
|
+
propertyId: string;
|
|
54
|
+
operator: string;
|
|
55
|
+
value: PropertyValue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface DatabaseViewConfig {
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
type: ViewType;
|
|
62
|
+
position: string;
|
|
63
|
+
groupBy?: string;
|
|
64
|
+
sorts: SortConfig[];
|
|
65
|
+
filters: FilterConfig[];
|
|
66
|
+
visibleProperties: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Top-level saved data ───
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Data saved by the database block.
|
|
73
|
+
* Schema and views only — rows are child blocks (database-row type).
|
|
74
|
+
*/
|
|
75
|
+
export interface DatabaseData extends BlockToolData {
|
|
76
|
+
title?: string;
|
|
77
|
+
schema: PropertyDefinition[];
|
|
78
|
+
views: DatabaseViewConfig[];
|
|
79
|
+
activeViewId: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Adapter ───
|
|
83
|
+
|
|
84
|
+
export interface DatabaseAdapter {
|
|
85
|
+
loadDatabase(): Promise<{
|
|
86
|
+
schema: PropertyDefinition[];
|
|
87
|
+
views: DatabaseViewConfig[];
|
|
88
|
+
}>;
|
|
89
|
+
|
|
90
|
+
createRow(params: {
|
|
91
|
+
id: string;
|
|
92
|
+
properties: Record<string, PropertyValue>;
|
|
93
|
+
position: string;
|
|
94
|
+
}): Promise<DatabaseRow>;
|
|
95
|
+
|
|
96
|
+
updateRow(params: {
|
|
97
|
+
rowId: string;
|
|
98
|
+
properties: Record<string, PropertyValue>;
|
|
99
|
+
}): Promise<DatabaseRow>;
|
|
100
|
+
|
|
101
|
+
moveRow(params: {
|
|
102
|
+
rowId: string;
|
|
103
|
+
position: string;
|
|
104
|
+
}): Promise<DatabaseRow>;
|
|
105
|
+
|
|
106
|
+
deleteRow(params: {
|
|
107
|
+
rowId: string;
|
|
108
|
+
}): Promise<void>;
|
|
109
|
+
|
|
110
|
+
createProperty(params: {
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
type: PropertyType;
|
|
114
|
+
position: string;
|
|
115
|
+
config?: PropertyConfig;
|
|
116
|
+
}): Promise<PropertyDefinition>;
|
|
117
|
+
|
|
118
|
+
updateProperty(params: {
|
|
119
|
+
propertyId: string;
|
|
120
|
+
changes: Partial<Pick<PropertyDefinition, 'name' | 'config'>>;
|
|
121
|
+
}): Promise<PropertyDefinition>;
|
|
122
|
+
|
|
123
|
+
deleteProperty(params: {
|
|
124
|
+
propertyId: string;
|
|
125
|
+
}): Promise<void>;
|
|
126
|
+
|
|
127
|
+
createView(params: {
|
|
128
|
+
id: string;
|
|
129
|
+
name: string;
|
|
130
|
+
type: ViewType;
|
|
131
|
+
position: string;
|
|
132
|
+
groupBy?: string;
|
|
133
|
+
sorts?: SortConfig[];
|
|
134
|
+
filters?: FilterConfig[];
|
|
135
|
+
visibleProperties?: string[];
|
|
136
|
+
}): Promise<DatabaseViewConfig>;
|
|
137
|
+
|
|
138
|
+
updateView(params: {
|
|
139
|
+
viewId: string;
|
|
140
|
+
changes: Partial<Pick<DatabaseViewConfig,
|
|
141
|
+
'name' | 'type' | 'position' | 'groupBy' | 'sorts' | 'filters' | 'visibleProperties'
|
|
142
|
+
>>;
|
|
143
|
+
}): Promise<DatabaseViewConfig>;
|
|
144
|
+
|
|
145
|
+
deleteView(params: {
|
|
146
|
+
viewId: string;
|
|
147
|
+
}): Promise<void>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface DatabaseConfig {
|
|
151
|
+
adapter?: DatabaseAdapter;
|
|
152
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { BlockTool, BlockToolConstructorOptions } from '../../../types/tools/block-tool';
|
|
2
|
+
import type { DatabaseRowData, PropertyValue } from '../database/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DatabaseRowTool — lightweight block that stores a single database row.
|
|
6
|
+
*
|
|
7
|
+
* Not user-insertable (no toolbox entry). The parent DatabaseTool creates
|
|
8
|
+
* and manages row blocks; this tool's job is to hold properties and position
|
|
9
|
+
* as block data so rows participate in the block tree.
|
|
10
|
+
*
|
|
11
|
+
* Custom methods (updateProperties, updatePosition, getProperties, getPosition)
|
|
12
|
+
* are accessed by the parent DatabaseTool via block.call('methodName', params),
|
|
13
|
+
* which invokes tool instance methods by name through the Block adapter.
|
|
14
|
+
*/
|
|
15
|
+
export class DatabaseRowTool implements BlockTool {
|
|
16
|
+
private _data: DatabaseRowData;
|
|
17
|
+
|
|
18
|
+
constructor({ data }: BlockToolConstructorOptions<DatabaseRowData>) {
|
|
19
|
+
this._data = {
|
|
20
|
+
properties: data.properties ?? {},
|
|
21
|
+
position: data.position ?? 'a0',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public render(): HTMLDivElement {
|
|
26
|
+
const el = document.createElement('div');
|
|
27
|
+
|
|
28
|
+
el.setAttribute('data-blok-tool', 'database-row');
|
|
29
|
+
|
|
30
|
+
return el;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public save(_block: HTMLElement): DatabaseRowData {
|
|
34
|
+
return {
|
|
35
|
+
properties: this._data.properties,
|
|
36
|
+
position: this._data.position,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public validate(data: DatabaseRowData): boolean {
|
|
41
|
+
return data.properties !== null && data.properties !== undefined && typeof data.properties === 'object';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public updateProperties(changes: Record<string, PropertyValue>): void {
|
|
45
|
+
Object.assign(this._data.properties, changes);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public updatePosition(param: { position: string }): void {
|
|
49
|
+
this._data.position = param.position;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public getProperties(): Record<string, PropertyValue> {
|
|
53
|
+
return this._data.properties;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public getPosition(): string {
|
|
57
|
+
return this._data.position;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public static get isReadOnlySupported(): boolean {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* No-op: DatabaseRowTool renders an invisible div with no interactive elements.
|
|
66
|
+
* Implementing this method enables the fast-path in-place read-only toggle in
|
|
67
|
+
* the ReadOnly module (which requires ALL tools to have setReadOnly()).
|
|
68
|
+
*/
|
|
69
|
+
public setReadOnly(_state: boolean): void {
|
|
70
|
+
// intentionally empty
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type { DatabaseRowData };
|
package/src/tools/index.ts
CHANGED
|
@@ -26,6 +26,8 @@ export { ListItem as List } from './list';
|
|
|
26
26
|
export { Table } from './table';
|
|
27
27
|
export { ToggleItem as Toggle } from './toggle';
|
|
28
28
|
export { CalloutTool as Callout } from './callout';
|
|
29
|
+
export { DatabaseTool as Database } from './database';
|
|
30
|
+
export { DatabaseRowTool as DatabaseRow } from './database-row';
|
|
29
31
|
export { DividerTool as Divider } from './divider';
|
|
30
32
|
export { Quote } from './quote';
|
|
31
33
|
export { CodeTool as Code } from './code';
|
|
@@ -49,6 +51,8 @@ export const defaultBlockTools = {
|
|
|
49
51
|
table: {},
|
|
50
52
|
toggle: {},
|
|
51
53
|
callout: {},
|
|
54
|
+
database: {},
|
|
55
|
+
'database-row': {},
|
|
52
56
|
divider: {},
|
|
53
57
|
quote: {},
|
|
54
58
|
code: { inlineToolbar: false },
|
package/src/tools/table/index.ts
CHANGED
|
@@ -1575,18 +1575,9 @@ export class Table implements BlockTool {
|
|
|
1575
1575
|
return [];
|
|
1576
1576
|
}
|
|
1577
1577
|
|
|
1578
|
-
const allRows = Array.from(gridEl.querySelectorAll(`[${ROW_ATTR}]`));
|
|
1579
|
-
|
|
1580
1578
|
return cells.map(cell => {
|
|
1581
|
-
const
|
|
1582
|
-
|
|
1583
|
-
if (!row) {
|
|
1584
|
-
return null;
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
const rowIndex = allRows.indexOf(row);
|
|
1588
|
-
const cellsInRow = Array.from(row.querySelectorAll(`[${CELL_ATTR}]`));
|
|
1589
|
-
const colIndex = cellsInRow.indexOf(cell);
|
|
1579
|
+
const rowIndex = parseInt(cell.getAttribute(CELL_ROW_ATTR) ?? '0', 10);
|
|
1580
|
+
const colIndex = parseInt(cell.getAttribute(CELL_COL_ATTR) ?? '0', 10);
|
|
1590
1581
|
|
|
1591
1582
|
const container = cell.querySelector(`[${CELL_BLOCKS_ATTR}]`);
|
|
1592
1583
|
const blocks: ClipboardBlockData[] = [];
|
|
@@ -1643,7 +1634,7 @@ export class Table implements BlockTool {
|
|
|
1643
1634
|
...(color !== undefined ? { color } : {}),
|
|
1644
1635
|
...(textColor !== undefined ? { textColor } : {}),
|
|
1645
1636
|
};
|
|
1646
|
-
})
|
|
1637
|
+
});
|
|
1647
1638
|
}
|
|
1648
1639
|
|
|
1649
1640
|
private initCellSelection(gridEl: HTMLElement): void {
|
|
@@ -1741,6 +1732,9 @@ export class Table implements BlockTool {
|
|
|
1741
1732
|
this.rebuildTableBody();
|
|
1742
1733
|
});
|
|
1743
1734
|
},
|
|
1735
|
+
getCellSpan: (row, col) => {
|
|
1736
|
+
return this.model.getCellSpan(row, col);
|
|
1737
|
+
},
|
|
1744
1738
|
});
|
|
1745
1739
|
}
|
|
1746
1740
|
|
|
@@ -1804,9 +1798,7 @@ export class Table implements BlockTool {
|
|
|
1804
1798
|
return;
|
|
1805
1799
|
}
|
|
1806
1800
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
if (!targetRow) {
|
|
1801
|
+
if (!targetCell.closest(`[${ROW_ATTR}]`)) {
|
|
1810
1802
|
return;
|
|
1811
1803
|
}
|
|
1812
1804
|
|
|
@@ -1827,10 +1819,9 @@ export class Table implements BlockTool {
|
|
|
1827
1819
|
e.preventDefault();
|
|
1828
1820
|
e.stopPropagation();
|
|
1829
1821
|
|
|
1830
|
-
|
|
1831
|
-
const targetRowIndex =
|
|
1832
|
-
const
|
|
1833
|
-
const targetColIndex = cellsInRow.indexOf(targetCell);
|
|
1822
|
+
// Read true model coordinates from stamped data attributes
|
|
1823
|
+
const targetRowIndex = parseInt(targetCell.getAttribute(CELL_ROW_ATTR) ?? '0', 10);
|
|
1824
|
+
const targetColIndex = parseInt(targetCell.getAttribute(CELL_COL_ATTR) ?? '0', 10);
|
|
1834
1825
|
|
|
1835
1826
|
this.pastePayloadIntoCells(gridEl, payload, targetRowIndex, targetColIndex);
|
|
1836
1827
|
}
|
|
@@ -86,6 +86,8 @@ interface CellSelectionOptions {
|
|
|
86
86
|
isMergedCell?: (row: number, col: number) => boolean;
|
|
87
87
|
/** Called when user requests to split a merged cell. */
|
|
88
88
|
onSplitCell?: (row: number, col: number) => void;
|
|
89
|
+
/** Returns the colspan and rowspan of the cell at (row, col). Used to expand the selection rect to full merged-cell spans. */
|
|
90
|
+
getCellSpan?: (row: number, col: number) => { colspan: number; rowspan: number };
|
|
89
91
|
i18n: I18n;
|
|
90
92
|
}
|
|
91
93
|
|
|
@@ -118,7 +120,9 @@ export class TableCellSelection {
|
|
|
118
120
|
private onMergeCells: ((range: SelectionRange) => void) | undefined;
|
|
119
121
|
private isMergedCell: ((row: number, col: number) => boolean) | undefined;
|
|
120
122
|
private onSplitCell: ((row: number, col: number) => void) | undefined;
|
|
123
|
+
private getCellSpan: ((row: number, col: number) => { colspan: number; rowspan: number }) | undefined;
|
|
121
124
|
private lastPaintedRange: SelectionRange | null = null;
|
|
125
|
+
private preExpansionWasSingleCell = false;
|
|
122
126
|
|
|
123
127
|
private boundPointerDown: (e: PointerEvent) => void;
|
|
124
128
|
private boundPointerMove: (e: PointerEvent) => void;
|
|
@@ -148,6 +152,7 @@ export class TableCellSelection {
|
|
|
148
152
|
this.onMergeCells = options.onMergeCells;
|
|
149
153
|
this.isMergedCell = options.isMergedCell;
|
|
150
154
|
this.onSplitCell = options.onSplitCell;
|
|
155
|
+
this.getCellSpan = options.getCellSpan;
|
|
151
156
|
this.i18n = options.i18n;
|
|
152
157
|
this.grid.style.position = 'relative';
|
|
153
158
|
|
|
@@ -466,6 +471,14 @@ export class TableCellSelection {
|
|
|
466
471
|
return;
|
|
467
472
|
}
|
|
468
473
|
|
|
474
|
+
// For single-cell selections, if the user has a non-collapsed native text
|
|
475
|
+
// selection within the cell's contenteditable (i.e., they selected specific
|
|
476
|
+
// text characters), defer to the browser's native copy so their text
|
|
477
|
+
// selection is copied rather than the whole cell block structure.
|
|
478
|
+
if (this.selectedCells.length <= 1 && this.hasNativeTextSelection()) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
469
482
|
e.preventDefault();
|
|
470
483
|
this.onCopy?.([...this.selectedCells], e.clipboardData);
|
|
471
484
|
}
|
|
@@ -475,18 +488,37 @@ export class TableCellSelection {
|
|
|
475
488
|
return;
|
|
476
489
|
}
|
|
477
490
|
|
|
491
|
+
// For single-cell selections, if the user has a non-collapsed native text
|
|
492
|
+
// selection within the cell's contenteditable, defer to the browser's native
|
|
493
|
+
// cut so their text selection is cut rather than clearing the entire cell.
|
|
494
|
+
if (this.selectedCells.length <= 1 && this.hasNativeTextSelection()) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
478
498
|
e.preventDefault();
|
|
479
499
|
this.onCut?.([...this.selectedCells], e.clipboardData);
|
|
480
500
|
this.onClearContent?.([...this.selectedCells]);
|
|
481
501
|
this.clearSelection();
|
|
482
502
|
}
|
|
483
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Returns true if the browser has a non-collapsed text selection (i.e. the
|
|
506
|
+
* user has selected one or more characters inside a contenteditable), as
|
|
507
|
+
* opposed to a mere caret position or no selection at all.
|
|
508
|
+
*/
|
|
509
|
+
private hasNativeTextSelection(): boolean {
|
|
510
|
+
const selection = window.getSelection();
|
|
511
|
+
|
|
512
|
+
return selection !== null && !selection.isCollapsed;
|
|
513
|
+
}
|
|
514
|
+
|
|
484
515
|
private clearSelection(): void {
|
|
485
516
|
const hadSelection = this.hasSelection;
|
|
486
517
|
|
|
487
518
|
this.restoreModifiedCells();
|
|
488
519
|
this.hasSelection = false;
|
|
489
520
|
this.lastPaintedRange = null;
|
|
521
|
+
this.preExpansionWasSingleCell = false;
|
|
490
522
|
|
|
491
523
|
if (hadSelection) {
|
|
492
524
|
this.onSelectionActiveChange?.(false);
|
|
@@ -540,6 +572,50 @@ export class TableCellSelection {
|
|
|
540
572
|
document.addEventListener('pointerdown', this.boundClearSelection);
|
|
541
573
|
}
|
|
542
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Expand a selection rect to fully include the spans of any merged cells
|
|
577
|
+
* whose origins fall within the rect. Iterates until the rect is stable,
|
|
578
|
+
* since pulling in a new merged cell may expose further cells that extend
|
|
579
|
+
* beyond the current boundary.
|
|
580
|
+
*/
|
|
581
|
+
private expandRectToMergedSpans(rect: SelectionRange): SelectionRange {
|
|
582
|
+
if (!this.getCellSpan) {
|
|
583
|
+
return rect;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return this.expandRectStep(rect);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private expandRectStep(rect: SelectionRange): SelectionRange {
|
|
590
|
+
const getCellSpan = this.getCellSpan;
|
|
591
|
+
|
|
592
|
+
if (!getCellSpan) {
|
|
593
|
+
return rect;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const rows = Array.from({ length: rect.maxRow - rect.minRow + 1 }, (_, i) => rect.minRow + i);
|
|
597
|
+
const cols = Array.from({ length: rect.maxCol - rect.minCol + 1 }, (_, i) => rect.minCol + i);
|
|
598
|
+
|
|
599
|
+
const expanded = rows.reduce<SelectionRange>((acc, r) => {
|
|
600
|
+
return cols.reduce<SelectionRange>((inner, c) => {
|
|
601
|
+
const { colspan, rowspan } = getCellSpan(r, c);
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
minRow: inner.minRow,
|
|
605
|
+
maxRow: Math.max(inner.maxRow, r + rowspan - 1),
|
|
606
|
+
minCol: inner.minCol,
|
|
607
|
+
maxCol: Math.max(inner.maxCol, c + colspan - 1),
|
|
608
|
+
};
|
|
609
|
+
}, acc);
|
|
610
|
+
}, rect);
|
|
611
|
+
|
|
612
|
+
const changed =
|
|
613
|
+
expanded.maxRow !== rect.maxRow ||
|
|
614
|
+
expanded.maxCol !== rect.maxCol;
|
|
615
|
+
|
|
616
|
+
return changed ? this.expandRectStep(expanded) : expanded;
|
|
617
|
+
}
|
|
618
|
+
|
|
543
619
|
private paintSelection(): void {
|
|
544
620
|
if (!this.anchorCell || !this.extentCell) {
|
|
545
621
|
return;
|
|
@@ -557,12 +633,15 @@ export class TableCellSelection {
|
|
|
557
633
|
const minCol = Math.min(this.anchorCell.col, this.extentCell.col);
|
|
558
634
|
const maxCol = Math.max(this.anchorCell.col, this.extentCell.col);
|
|
559
635
|
|
|
560
|
-
this.
|
|
636
|
+
this.preExpansionWasSingleCell = minRow === maxRow && minCol === maxCol;
|
|
637
|
+
this.lastPaintedRange = this.expandRectToMergedSpans({ minRow, maxRow, minCol, maxCol });
|
|
638
|
+
|
|
639
|
+
const { minRow: expandedMinRow, maxRow: expandedMaxRow, minCol: expandedMinCol, maxCol: expandedMaxCol } = this.lastPaintedRange;
|
|
561
640
|
|
|
562
641
|
const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
|
|
563
642
|
|
|
564
643
|
// Mark selected cells
|
|
565
|
-
this.selectedCells = this.collectCellsInRange(rows,
|
|
644
|
+
this.selectedCells = this.collectCellsInRange(rows, expandedMinRow, expandedMaxRow, expandedMinCol, expandedMaxCol);
|
|
566
645
|
this.selectedCells.forEach(cell => {
|
|
567
646
|
cell.setAttribute(SELECTED_ATTR, '');
|
|
568
647
|
});
|
|
@@ -570,8 +649,8 @@ export class TableCellSelection {
|
|
|
570
649
|
// Calculate overlay position from bounding rects of corner cells.
|
|
571
650
|
// Try coordinate-based lookup first (works with merged cells),
|
|
572
651
|
// then fall back to index-based lookup for backwards compatibility.
|
|
573
|
-
const firstCell = this.findCellByCoordOrIndex(rows,
|
|
574
|
-
const lastCell = this.findCellByCoordOrIndex(rows,
|
|
652
|
+
const firstCell = this.findCellByCoordOrIndex(rows, expandedMinRow, expandedMinCol);
|
|
653
|
+
const lastCell = this.findCellByCoordOrIndex(rows, expandedMaxRow, expandedMaxCol);
|
|
575
654
|
|
|
576
655
|
if (!firstCell || !lastCell) {
|
|
577
656
|
return;
|
|
@@ -803,7 +882,7 @@ export class TableCellSelection {
|
|
|
803
882
|
if (this.lastPaintedRange && this.onMergeCells) {
|
|
804
883
|
const range = this.lastPaintedRange;
|
|
805
884
|
const isMultiCell = range.minRow !== range.maxRow || range.minCol !== range.maxCol;
|
|
806
|
-
const canMerge = isMultiCell && this.canMergeCells?.(range);
|
|
885
|
+
const canMerge = isMultiCell && !this.preExpansionWasSingleCell && this.canMergeCells?.(range);
|
|
807
886
|
|
|
808
887
|
if (canMerge) {
|
|
809
888
|
mergeItems.push({
|
|
@@ -821,8 +900,9 @@ export class TableCellSelection {
|
|
|
821
900
|
if (this.lastPaintedRange && this.onSplitCell) {
|
|
822
901
|
const range = this.lastPaintedRange;
|
|
823
902
|
const isSingleCell = range.minRow === range.maxRow && range.minCol === range.maxCol;
|
|
903
|
+
const isSingleOriginExpanded = this.preExpansionWasSingleCell && this.isMergedCell?.(range.minRow, range.minCol);
|
|
824
904
|
|
|
825
|
-
if (isSingleCell && this.isMergedCell?.(range.minRow, range.minCol)) {
|
|
905
|
+
if ((isSingleCell || isSingleOriginExpanded) && this.isMergedCell?.(range.minRow, range.minCol)) {
|
|
826
906
|
mergeItems.push({
|
|
827
907
|
icon: IconSplitCell,
|
|
828
908
|
title: this.i18n.t('tools.table.splitCell'),
|
|
@@ -936,6 +1016,23 @@ export class TableCellSelection {
|
|
|
936
1016
|
return null;
|
|
937
1017
|
}
|
|
938
1018
|
|
|
1019
|
+
// Prefer logical coordinate attributes stamped by reindexCoordinates() —
|
|
1020
|
+
// these are correct even when rows have fewer physical <td> elements than
|
|
1021
|
+
// logical columns (e.g. after a colspan/rowspan merge).
|
|
1022
|
+
const cellRowAttr = cell.getAttribute(CELL_ROW_ATTR);
|
|
1023
|
+
const cellColAttr = cell.getAttribute(CELL_COL_ATTR);
|
|
1024
|
+
|
|
1025
|
+
if (cellRowAttr !== null && cellColAttr !== null) {
|
|
1026
|
+
const rowIndex = parseInt(cellRowAttr, 10);
|
|
1027
|
+
const colIndex = parseInt(cellColAttr, 10);
|
|
1028
|
+
|
|
1029
|
+
if (!isNaN(rowIndex) && !isNaN(colIndex)) {
|
|
1030
|
+
return { row: rowIndex, col: colIndex };
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Fallback: physical DOM index — only used for grids without coordinate
|
|
1035
|
+
// attributes (e.g. legacy non-table grid elements).
|
|
939
1036
|
const rows = Array.from(this.grid.querySelectorAll(`[${ROW_ATTR}]`));
|
|
940
1037
|
const rowIndex = rows.indexOf(row);
|
|
941
1038
|
|
|
@@ -1025,6 +1122,12 @@ export class TableCellSelection {
|
|
|
1025
1122
|
/**
|
|
1026
1123
|
* Find a cell by coordinate attributes first, falling back to index-based
|
|
1027
1124
|
* lookup when coordinate attributes are not present.
|
|
1125
|
+
*
|
|
1126
|
+
* When both primary lookups fail (e.g. `col` points to a covered logical
|
|
1127
|
+
* column that has no physical <td> of its own), scan all cells in the row
|
|
1128
|
+
* and return the one whose colspan range covers `col`. This handles the
|
|
1129
|
+
* case where `expandRectToMergedSpans` has expanded the selection corner to
|
|
1130
|
+
* a column that is spanned by an origin cell at a lower column index.
|
|
1028
1131
|
*/
|
|
1029
1132
|
private findCellByCoordOrIndex(
|
|
1030
1133
|
rows: NodeListOf<Element>,
|
|
@@ -1039,7 +1142,23 @@ export class TableCellSelection {
|
|
|
1039
1142
|
return coordCell;
|
|
1040
1143
|
}
|
|
1041
1144
|
|
|
1042
|
-
|
|
1145
|
+
const indexCell = rows[row]?.querySelectorAll(`[${CELL_ATTR}]`)[col] as HTMLElement | undefined;
|
|
1146
|
+
|
|
1147
|
+
if (indexCell) {
|
|
1148
|
+
return indexCell;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// Neither coord-based nor index-based lookup found a cell. Walk all
|
|
1152
|
+
// physical cells in the row to find one whose logical column range covers
|
|
1153
|
+
// the requested column (origin cellCol <= col <= cellCol + colSpan - 1).
|
|
1154
|
+
const rowCells = Array.from(rows[row]?.querySelectorAll<HTMLElement>(`[${CELL_ATTR}]`) ?? []);
|
|
1155
|
+
|
|
1156
|
+
return rowCells.find(cell => {
|
|
1157
|
+
const cellCol = Number(cell.getAttribute(CELL_COL_ATTR));
|
|
1158
|
+
const cellColSpan = (cell as HTMLTableCellElement).colSpan || 1;
|
|
1159
|
+
|
|
1160
|
+
return cellCol <= col && cellCol + cellColSpan - 1 >= col;
|
|
1161
|
+
});
|
|
1043
1162
|
}
|
|
1044
1163
|
|
|
1045
1164
|
/**
|
|
@@ -413,17 +413,71 @@ export class TableGrid {
|
|
|
413
413
|
/**
|
|
414
414
|
* Reindex coordinate attributes on all cells after structural changes.
|
|
415
415
|
* Sets data-blok-table-cell-row and data-blok-table-cell-col to match
|
|
416
|
-
* each cell's
|
|
416
|
+
* each cell's model (logical) position, accounting for colspan and rowspan.
|
|
417
|
+
*
|
|
418
|
+
* Uses sparse table reconstruction: tracks columns blocked by rowspan cells
|
|
419
|
+
* from previous rows so that each DOM cell gets the correct model column index
|
|
420
|
+
* rather than its physical DOM index.
|
|
417
421
|
*/
|
|
418
422
|
public reindexCoordinates(table: HTMLElement): void {
|
|
419
|
-
const rows = table.querySelectorAll(`[${ROW_ATTR}]`);
|
|
423
|
+
const rows = Array.from(table.querySelectorAll(`[${ROW_ATTR}]`));
|
|
424
|
+
|
|
425
|
+
// Map from rowIndex -> Set of columnIndices occupied by rowspan cells from earlier rows
|
|
426
|
+
const occupiedCols: Map<number, Set<number>> = new Map();
|
|
420
427
|
|
|
421
428
|
rows.forEach((row, r) => {
|
|
422
|
-
const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
|
|
429
|
+
const cells = Array.from(row.querySelectorAll(`[${CELL_ATTR}]`));
|
|
430
|
+
const blockedCols = occupiedCols.get(r) ?? new Set<number>();
|
|
431
|
+
|
|
432
|
+
cells.reduce((modelCol, cell) => {
|
|
433
|
+
const tdCell = cell as HTMLTableCellElement;
|
|
434
|
+
|
|
435
|
+
// Skip columns that are occupied by rowspan cells from previous rows
|
|
436
|
+
const skipBlocked = (c: number): number => (blockedCols.has(c) ? skipBlocked(c + 1) : c);
|
|
437
|
+
const col = skipBlocked(modelCol);
|
|
423
438
|
|
|
424
|
-
cells.forEach((cell, c) => {
|
|
425
439
|
cell.setAttribute(CELL_ROW_ATTR, String(r));
|
|
426
|
-
cell.setAttribute(CELL_COL_ATTR, String(
|
|
440
|
+
cell.setAttribute(CELL_COL_ATTR, String(col));
|
|
441
|
+
|
|
442
|
+
const colSpan = tdCell.colSpan || 1;
|
|
443
|
+
const rowSpan = tdCell.rowSpan || 1;
|
|
444
|
+
|
|
445
|
+
// If this cell has rowspan > 1, mark those columns as blocked in subsequent rows
|
|
446
|
+
if (rowSpan > 1) {
|
|
447
|
+
this.blockRowspanCols(occupiedCols, r, col, rowSpan, colSpan);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Advance by colspan
|
|
451
|
+
return col + colSpan;
|
|
452
|
+
}, 0);
|
|
453
|
+
|
|
454
|
+
occupiedCols.delete(r);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Register blocked columns in occupiedCols for a cell with rowspan > 1.
|
|
460
|
+
* All columns in [startCol, startCol + colSpan) are blocked for rows
|
|
461
|
+
* [startRow + 1, startRow + rowSpan).
|
|
462
|
+
*/
|
|
463
|
+
private blockRowspanCols(
|
|
464
|
+
occupiedCols: Map<number, Set<number>>,
|
|
465
|
+
startRow: number,
|
|
466
|
+
startCol: number,
|
|
467
|
+
rowSpan: number,
|
|
468
|
+
colSpan: number
|
|
469
|
+
): void {
|
|
470
|
+
Array.from({ length: rowSpan - 1 }, (_, i) => i + 1).forEach((dr) => {
|
|
471
|
+
const futureRow = startRow + dr;
|
|
472
|
+
|
|
473
|
+
if (!occupiedCols.has(futureRow)) {
|
|
474
|
+
occupiedCols.set(futureRow, new Set());
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const blocked = occupiedCols.get(futureRow) as Set<number>;
|
|
478
|
+
|
|
479
|
+
Array.from({ length: colSpan }, (_, dc) => dc).forEach((dc) => {
|
|
480
|
+
blocked.add(startCol + dc);
|
|
427
481
|
});
|
|
428
482
|
});
|
|
429
483
|
}
|
|
@@ -181,6 +181,10 @@ export class TableModel {
|
|
|
181
181
|
return;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
if (this.isSpannedCell(row, col)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
184
188
|
// Enforce invariant 5: no block in more than one cell
|
|
185
189
|
const existing = this.blockCellMap.get(blockId);
|
|
186
190
|
|
|
@@ -221,6 +225,10 @@ export class TableModel {
|
|
|
221
225
|
return;
|
|
222
226
|
}
|
|
223
227
|
|
|
228
|
+
if (this.isSpannedCell(row, col)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
224
232
|
// Remove old entries from map
|
|
225
233
|
for (const oldId of this.contentGrid[row][col].blocks) {
|
|
226
234
|
this.blockCellMap.delete(oldId);
|