@jackuait/blok 0.10.3 → 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-3wc3aInM.mjs → blok-NcdNQ0I6.mjs} +2364 -2178
- package/dist/chunks/{constants-Bp622jic.mjs → constants-DtfShkXT.mjs} +318 -227
- 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-BC1jRfoS.mjs → tools-DMSi-3RW.mjs} +3445 -1302
- 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/inline/index.ts +0 -3
- package/src/components/modules/tools.ts +5 -0
- package/src/components/modules/uiControllers/controllers/keyboard.ts +29 -0
- 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/ui/toolbox.ts +0 -1
- 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 +4 -2
- 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/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
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"toolNames.numberedList": "נומעריקע ליסטע",
|
|
19
19
|
"toolNames.todoList": "צו-טאָן ליסטע",
|
|
20
20
|
"toolNames.toggleList": "אויפֿקלאַפּ ליסטע",
|
|
21
|
+
"toolNames.board": "טאָוול",
|
|
22
|
+
"toolNames.database": "דאַטאַבאַנק",
|
|
21
23
|
"tools.link.addLink": "צולייגן לינק",
|
|
22
24
|
"tools.link.invalidLink": "אומגילטיקער לינק",
|
|
23
25
|
"tools.marker.textColor": "טעקסט",
|
|
@@ -143,6 +145,22 @@
|
|
|
143
145
|
"tools.callout.emojiCategoryObjects": "זאַכן",
|
|
144
146
|
"tools.callout.emojiCategorySymbols": "סימבאָלן",
|
|
145
147
|
"tools.callout.emojiCategoryFlags": "פֿאָנען",
|
|
148
|
+
"tools.database.addCard": "צוגעבן קאַרטל",
|
|
149
|
+
"tools.database.newPage": "נייע בלאט",
|
|
150
|
+
"tools.database.addColumn": "צוגעבן שפּאַלט",
|
|
151
|
+
"tools.database.cardTitlePlaceholder": "ליידיק בלאַט",
|
|
152
|
+
"tools.database.columnTitlePlaceholder": "שפּאַלט",
|
|
153
|
+
"tools.database.deleteCard": "אויסמעקן קאַרטל",
|
|
154
|
+
"tools.database.editCardTitle": "Edit title",
|
|
155
|
+
"tools.database.cardMenuLabel": "Card options",
|
|
156
|
+
"tools.database.deleteColumn": "אויסמעקן שפּאַלט",
|
|
157
|
+
"tools.database.emptyColumn": "קיין קארטן",
|
|
158
|
+
"tools.database.rowTitlePlaceholder": "ליידיק בלאַט",
|
|
159
|
+
"tools.database.addRow": "צוגעבן שורה",
|
|
160
|
+
"tools.database.newRow": "נייע שורה",
|
|
161
|
+
"tools.database.openRow": "עפֿענען שורה",
|
|
162
|
+
"tools.database.deleteRow": "אויסמעקן שורה",
|
|
163
|
+
"tools.database.renameColumn": "באַנענען שפּאַלט",
|
|
146
164
|
"tools.callout.color": "קאָליר",
|
|
147
165
|
"tools.callout.colorDefault": "שטאַנדאַרד",
|
|
148
166
|
"tools.callout.colorGray": "גרוי",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"toolNames.numberedList": "有序列表",
|
|
19
19
|
"toolNames.todoList": "清单",
|
|
20
20
|
"toolNames.toggleList": "折叠列表",
|
|
21
|
+
"toolNames.board": "看板",
|
|
22
|
+
"toolNames.database": "数据库",
|
|
21
23
|
"tools.link.addLink": "添加链接",
|
|
22
24
|
"tools.link.invalidLink": "链接无效",
|
|
23
25
|
"tools.marker.textColor": "文字",
|
|
@@ -143,6 +145,22 @@
|
|
|
143
145
|
"tools.callout.emojiCategoryObjects": "物品",
|
|
144
146
|
"tools.callout.emojiCategorySymbols": "符号",
|
|
145
147
|
"tools.callout.emojiCategoryFlags": "旗帜",
|
|
148
|
+
"tools.database.addCard": "添加卡片",
|
|
149
|
+
"tools.database.newPage": "新页面",
|
|
150
|
+
"tools.database.addColumn": "添加列",
|
|
151
|
+
"tools.database.cardTitlePlaceholder": "空白页面",
|
|
152
|
+
"tools.database.columnTitlePlaceholder": "列",
|
|
153
|
+
"tools.database.deleteCard": "删除卡片",
|
|
154
|
+
"tools.database.editCardTitle": "Edit title",
|
|
155
|
+
"tools.database.cardMenuLabel": "Card options",
|
|
156
|
+
"tools.database.deleteColumn": "删除列",
|
|
157
|
+
"tools.database.emptyColumn": "没有卡片",
|
|
158
|
+
"tools.database.rowTitlePlaceholder": "空白页面",
|
|
159
|
+
"tools.database.addRow": "添加行",
|
|
160
|
+
"tools.database.newRow": "新行",
|
|
161
|
+
"tools.database.openRow": "打开行",
|
|
162
|
+
"tools.database.deleteRow": "删除行",
|
|
163
|
+
"tools.database.renameColumn": "重命名列",
|
|
146
164
|
"tools.callout.color": "颜色",
|
|
147
165
|
"tools.callout.colorDefault": "默认",
|
|
148
166
|
"tools.callout.colorGray": "灰色",
|
|
@@ -399,6 +399,13 @@ export const IconCopy = `
|
|
|
399
399
|
</svg>
|
|
400
400
|
`;
|
|
401
401
|
|
|
402
|
+
// Pencil/Rename icon
|
|
403
|
+
export const IconPencil = `
|
|
404
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
405
|
+
<path d="M12.5 5.5l2 2M4.5 13.5l8-8 2 2-8 8H4.5v-2z" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
|
406
|
+
</svg>
|
|
407
|
+
`;
|
|
408
|
+
|
|
402
409
|
// Trash/Delete icon
|
|
403
410
|
export const IconTrash = `
|
|
404
411
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -409,6 +416,15 @@ export const IconTrash = `
|
|
|
409
416
|
</svg>
|
|
410
417
|
`;
|
|
411
418
|
|
|
419
|
+
// Horizontal dots / more options icon
|
|
420
|
+
export const IconDotsHorizontal = `
|
|
421
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
422
|
+
<circle cx="5" cy="10" r="1.25" fill="currentColor"/>
|
|
423
|
+
<circle cx="10" cy="10" r="1.25" fill="currentColor"/>
|
|
424
|
+
<circle cx="15" cy="10" r="1.25" fill="currentColor"/>
|
|
425
|
+
</svg>
|
|
426
|
+
`;
|
|
427
|
+
|
|
412
428
|
// Toggle Heading 1 icon (H1 with toggle arrow)
|
|
413
429
|
export const IconToggleH1 = `
|
|
414
430
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -592,6 +608,55 @@ export const IconEmojiFlag = `
|
|
|
592
608
|
</svg>
|
|
593
609
|
`;
|
|
594
610
|
|
|
611
|
+
export const IconDatabase = `
|
|
612
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
613
|
+
<ellipse cx="10" cy="5.5" rx="6" ry="2.5" stroke="currentColor" stroke-width="1.25"/>
|
|
614
|
+
<path d="M4 5.5v4c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5v-4" stroke="currentColor" stroke-width="1.25"/>
|
|
615
|
+
<path d="M4 9.5v4c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5v-4" stroke="currentColor" stroke-width="1.25"/>
|
|
616
|
+
</svg>
|
|
617
|
+
`;
|
|
618
|
+
|
|
619
|
+
export const IconBoard = `
|
|
620
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
621
|
+
<rect x="3" y="4" width="14" height="12" rx="1.5" stroke="currentColor" stroke-width="1.25"/>
|
|
622
|
+
<path d="M7.5 4v12M12.5 4v12" stroke="currentColor" stroke-width="1.25"/>
|
|
623
|
+
<rect x="4.5" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
|
|
624
|
+
<rect x="4.5" y="9" width="1.5" height="2" rx="0.5" fill="currentColor"/>
|
|
625
|
+
<rect x="9" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
|
|
626
|
+
<rect x="14" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
|
|
627
|
+
</svg>
|
|
628
|
+
`;
|
|
629
|
+
|
|
630
|
+
export const IconGallery = `
|
|
631
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
632
|
+
<rect x="3" y="4" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
|
|
633
|
+
<rect x="11" y="4" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
|
|
634
|
+
<rect x="3" y="11" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
|
|
635
|
+
<rect x="11" y="11" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
|
|
636
|
+
</svg>
|
|
637
|
+
`;
|
|
638
|
+
|
|
639
|
+
export const IconList = `
|
|
640
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
641
|
+
<circle cx="4.5" cy="6.5" r="1" fill="currentColor"/>
|
|
642
|
+
<circle cx="4.5" cy="10" r="1" fill="currentColor"/>
|
|
643
|
+
<circle cx="4.5" cy="13.5" r="1" fill="currentColor"/>
|
|
644
|
+
<path d="M7.5 6.5h9M7.5 10h9M7.5 13.5h9" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
|
|
645
|
+
</svg>
|
|
646
|
+
`;
|
|
647
|
+
|
|
648
|
+
// Calendar icon (date property type)
|
|
649
|
+
export const IconCalendar = `
|
|
650
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
651
|
+
<rect x="3" y="4.5" width="14" height="12" rx="1.5" stroke="currentColor" stroke-width="1.25"/>
|
|
652
|
+
<path d="M3 8.5h14" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
|
|
653
|
+
<path d="M7 3v3M13 3v3" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
|
|
654
|
+
<circle cx="7" cy="12" r="1" fill="currentColor"/>
|
|
655
|
+
<circle cx="10" cy="12" r="1" fill="currentColor"/>
|
|
656
|
+
<circle cx="13" cy="12" r="1" fill="currentColor"/>
|
|
657
|
+
</svg>
|
|
658
|
+
`;
|
|
659
|
+
|
|
595
660
|
// Merge cells icon (two cells merging into one)
|
|
596
661
|
export const IconMergeCells = `
|
|
597
662
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
isRangeFormatted,
|
|
18
18
|
collectFormattingAncestors,
|
|
19
|
+
extendRangeToTrailingWhitespace,
|
|
19
20
|
} from './utils/formatting-range-utils';
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -212,6 +213,7 @@ export class BoldInlineTool implements InlineTool {
|
|
|
212
213
|
* @param range - The Range object containing the selection to wrap
|
|
213
214
|
*/
|
|
214
215
|
private wrapWithBold(range: Range): void {
|
|
216
|
+
extendRangeToTrailingWhitespace(range);
|
|
215
217
|
const html = this.getRangeHtmlWithoutBold(range);
|
|
216
218
|
const insertedRange = this.replaceRangeWithHtml(range, `<strong>${html}</strong>`);
|
|
217
219
|
const selection = window.getSelection();
|
|
@@ -308,8 +310,16 @@ export class BoldInlineTool implements InlineTool {
|
|
|
308
310
|
BoldNormalizationPass.normalizeAroundSelection(selection);
|
|
309
311
|
|
|
310
312
|
boldAncestors.forEach((element) => {
|
|
313
|
+
if (!element.isConnected) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
311
317
|
if (isElementEmpty(element)) {
|
|
312
318
|
element.remove();
|
|
319
|
+
} else if (element.textContent.trim().length === 0) {
|
|
320
|
+
// Element contains only whitespace — unwrap it to preserve the whitespace
|
|
321
|
+
// as plain text while removing the bold formatting
|
|
322
|
+
this.unwrapElement(element);
|
|
313
323
|
}
|
|
314
324
|
});
|
|
315
325
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isRangeFormatted,
|
|
7
7
|
findFormattingAncestor,
|
|
8
8
|
collectFormattingAncestors,
|
|
9
|
+
extendRangeToTrailingWhitespace,
|
|
9
10
|
} from './utils/formatting-range-utils';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -168,16 +169,68 @@ export class CodeInlineTool implements InlineTool {
|
|
|
168
169
|
* @param range - The Range object containing the selection to wrap
|
|
169
170
|
*/
|
|
170
171
|
private wrapWithCode(range: Range): void {
|
|
172
|
+
extendRangeToTrailingWhitespace(range);
|
|
171
173
|
const html = this.getRangeHtmlWithoutCode(range);
|
|
172
174
|
const insertedRange = this.replaceRangeWithHtml(range, `<code>${html}</code>`);
|
|
173
175
|
const selection = window.getSelection();
|
|
174
176
|
|
|
175
177
|
if (selection && insertedRange) {
|
|
178
|
+
const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
|
|
179
|
+
const newRange = document.createRange();
|
|
180
|
+
|
|
181
|
+
if (wrappedElement) {
|
|
182
|
+
this.normalizeNbspInElement(wrappedElement);
|
|
183
|
+
newRange.selectNodeContents(wrappedElement);
|
|
184
|
+
} else {
|
|
185
|
+
newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
|
|
186
|
+
newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
|
|
187
|
+
}
|
|
188
|
+
|
|
176
189
|
selection.removeAllRanges();
|
|
177
|
-
selection.addRange(
|
|
190
|
+
selection.addRange(newRange);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
|
|
196
|
+
* @param element - The element to normalize
|
|
197
|
+
*/
|
|
198
|
+
private normalizeNbspInElement(element: HTMLElement): void {
|
|
199
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
200
|
+
|
|
201
|
+
while (true) {
|
|
202
|
+
const node = walker.nextNode();
|
|
203
|
+
|
|
204
|
+
if (node === null) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
if (!node.textContent?.includes('\u00A0')) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const keepTrailing = node.textContent.endsWith('\u00A0') && this.isEffectivelyLastChild(node);
|
|
212
|
+
|
|
213
|
+
node.textContent = node.textContent.replace(/\u00A0/g, ' ');
|
|
214
|
+
|
|
215
|
+
if (keepTrailing) {
|
|
216
|
+
node.textContent = node.textContent.slice(0, -1) + '\u00A0';
|
|
217
|
+
}
|
|
178
218
|
}
|
|
179
219
|
}
|
|
180
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Check whether all siblings after a node are empty
|
|
223
|
+
*/
|
|
224
|
+
private isEffectivelyLastChild(node: Node): boolean {
|
|
225
|
+
const next = node.nextSibling;
|
|
226
|
+
|
|
227
|
+
if (!next) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return (next.textContent ?? '').length === 0 && this.isEffectivelyLastChild(next);
|
|
232
|
+
}
|
|
233
|
+
|
|
181
234
|
/**
|
|
182
235
|
* Remove code tags (<code>) while preserving content
|
|
183
236
|
* @param range - The Range object containing the selection to unwrap
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
findFormattingAncestor,
|
|
8
8
|
hasFormattingAncestor,
|
|
9
9
|
collectFormattingAncestors,
|
|
10
|
+
extendRangeToTrailingWhitespace,
|
|
10
11
|
} from './utils/formatting-range-utils';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -172,16 +173,68 @@ export class ItalicInlineTool implements InlineTool {
|
|
|
172
173
|
* @param range - The Range object containing the selection to wrap
|
|
173
174
|
*/
|
|
174
175
|
private wrapWithItalic(range: Range): void {
|
|
176
|
+
extendRangeToTrailingWhitespace(range);
|
|
175
177
|
const html = this.getRangeHtmlWithoutItalic(range);
|
|
176
178
|
const insertedRange = this.replaceRangeWithHtml(range, `<i>${html}</i>`);
|
|
177
179
|
const selection = window.getSelection();
|
|
178
180
|
|
|
179
181
|
if (selection && insertedRange) {
|
|
182
|
+
const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
|
|
183
|
+
const newRange = document.createRange();
|
|
184
|
+
|
|
185
|
+
if (wrappedElement) {
|
|
186
|
+
this.normalizeNbspInElement(wrappedElement);
|
|
187
|
+
newRange.selectNodeContents(wrappedElement);
|
|
188
|
+
} else {
|
|
189
|
+
newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
|
|
190
|
+
newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
|
|
191
|
+
}
|
|
192
|
+
|
|
180
193
|
selection.removeAllRanges();
|
|
181
|
-
selection.addRange(
|
|
194
|
+
selection.addRange(newRange);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
|
|
200
|
+
* @param element - The element to normalize
|
|
201
|
+
*/
|
|
202
|
+
private normalizeNbspInElement(element: HTMLElement): void {
|
|
203
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
204
|
+
|
|
205
|
+
while (true) {
|
|
206
|
+
const node = walker.nextNode();
|
|
207
|
+
|
|
208
|
+
if (node === null) {
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
if (!node.textContent?.includes('\u00A0')) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const keepTrailing = node.textContent.endsWith('\u00A0') && this.isEffectivelyLastChild(node);
|
|
216
|
+
|
|
217
|
+
node.textContent = node.textContent.replace(/\u00A0/g, ' ');
|
|
218
|
+
|
|
219
|
+
if (keepTrailing) {
|
|
220
|
+
node.textContent = node.textContent.slice(0, -1) + '\u00A0';
|
|
221
|
+
}
|
|
182
222
|
}
|
|
183
223
|
}
|
|
184
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Check whether all siblings after a node are empty
|
|
227
|
+
*/
|
|
228
|
+
private isEffectivelyLastChild(node: Node): boolean {
|
|
229
|
+
const next = node.nextSibling;
|
|
230
|
+
|
|
231
|
+
if (!next) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return (next.textContent ?? '').length === 0 && this.isEffectivelyLastChild(next);
|
|
236
|
+
}
|
|
237
|
+
|
|
185
238
|
/**
|
|
186
239
|
* Remove italic tags (<i>/<em>) while preserving content
|
|
187
240
|
* @param range - The Range object containing the selection to unwrap
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
findFormattingAncestor,
|
|
8
8
|
hasFormattingAncestor,
|
|
9
9
|
collectFormattingAncestors,
|
|
10
|
+
extendRangeToTrailingWhitespace,
|
|
10
11
|
} from './utils/formatting-range-utils';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -169,16 +170,68 @@ export class StrikethroughInlineTool implements InlineTool {
|
|
|
169
170
|
* @param range - The Range object containing the selection to wrap
|
|
170
171
|
*/
|
|
171
172
|
private wrapWithStrikethrough(range: Range): void {
|
|
173
|
+
extendRangeToTrailingWhitespace(range);
|
|
172
174
|
const html = this.getRangeHtmlWithoutStrikethrough(range);
|
|
173
175
|
const insertedRange = this.replaceRangeWithHtml(range, `<s>${html}</s>`);
|
|
174
176
|
const selection = window.getSelection();
|
|
175
177
|
|
|
176
178
|
if (selection && insertedRange) {
|
|
179
|
+
const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
|
|
180
|
+
const newRange = document.createRange();
|
|
181
|
+
|
|
182
|
+
if (wrappedElement) {
|
|
183
|
+
this.normalizeNbspInElement(wrappedElement);
|
|
184
|
+
newRange.selectNodeContents(wrappedElement);
|
|
185
|
+
} else {
|
|
186
|
+
newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
|
|
187
|
+
newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
selection.removeAllRanges();
|
|
178
|
-
selection.addRange(
|
|
191
|
+
selection.addRange(newRange);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
|
|
197
|
+
* @param element - The element to normalize
|
|
198
|
+
*/
|
|
199
|
+
private normalizeNbspInElement(element: HTMLElement): void {
|
|
200
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
201
|
+
|
|
202
|
+
while (true) {
|
|
203
|
+
const node = walker.nextNode();
|
|
204
|
+
|
|
205
|
+
if (node === null) {
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if (!node.textContent?.includes('\u00A0')) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const keepTrailing = node.textContent.endsWith('\u00A0') && this.isEffectivelyLastChild(node);
|
|
213
|
+
|
|
214
|
+
node.textContent = node.textContent.replace(/\u00A0/g, ' ');
|
|
215
|
+
|
|
216
|
+
if (keepTrailing) {
|
|
217
|
+
node.textContent = node.textContent.slice(0, -1) + '\u00A0';
|
|
218
|
+
}
|
|
179
219
|
}
|
|
180
220
|
}
|
|
181
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Check whether all siblings after a node are empty
|
|
224
|
+
*/
|
|
225
|
+
private isEffectivelyLastChild(node: Node): boolean {
|
|
226
|
+
const next = node.nextSibling;
|
|
227
|
+
|
|
228
|
+
if (!next) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return (next.textContent ?? '').length === 0 && this.isEffectivelyLastChild(next);
|
|
233
|
+
}
|
|
234
|
+
|
|
182
235
|
/**
|
|
183
236
|
* Remove strikethrough tags (<s>) while preserving content
|
|
184
237
|
* @param range - The Range object containing the selection to unwrap
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
findFormattingAncestor,
|
|
8
8
|
hasFormattingAncestor,
|
|
9
9
|
collectFormattingAncestors,
|
|
10
|
+
extendRangeToTrailingWhitespace,
|
|
10
11
|
} from './utils/formatting-range-utils';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -169,16 +170,68 @@ export class UnderlineInlineTool implements InlineTool {
|
|
|
169
170
|
* @param range - The Range object containing the selection to wrap
|
|
170
171
|
*/
|
|
171
172
|
private wrapWithUnderline(range: Range): void {
|
|
173
|
+
extendRangeToTrailingWhitespace(range);
|
|
172
174
|
const html = this.getRangeHtmlWithoutUnderline(range);
|
|
173
175
|
const insertedRange = this.replaceRangeWithHtml(range, `<u>${html}</u>`);
|
|
174
176
|
const selection = window.getSelection();
|
|
175
177
|
|
|
176
178
|
if (selection && insertedRange) {
|
|
179
|
+
const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
|
|
180
|
+
const newRange = document.createRange();
|
|
181
|
+
|
|
182
|
+
if (wrappedElement) {
|
|
183
|
+
this.normalizeNbspInElement(wrappedElement);
|
|
184
|
+
newRange.selectNodeContents(wrappedElement);
|
|
185
|
+
} else {
|
|
186
|
+
newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
|
|
187
|
+
newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
selection.removeAllRanges();
|
|
178
|
-
selection.addRange(
|
|
191
|
+
selection.addRange(newRange);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
|
|
197
|
+
* @param element - The element to normalize
|
|
198
|
+
*/
|
|
199
|
+
private normalizeNbspInElement(element: HTMLElement): void {
|
|
200
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
201
|
+
|
|
202
|
+
while (true) {
|
|
203
|
+
const node = walker.nextNode();
|
|
204
|
+
|
|
205
|
+
if (node === null) {
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if (!node.textContent?.includes('\u00A0')) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const keepTrailing = node.textContent.endsWith('\u00A0') && this.isEffectivelyLastChild(node);
|
|
213
|
+
|
|
214
|
+
node.textContent = node.textContent.replace(/\u00A0/g, ' ');
|
|
215
|
+
|
|
216
|
+
if (keepTrailing) {
|
|
217
|
+
node.textContent = node.textContent.slice(0, -1) + '\u00A0';
|
|
218
|
+
}
|
|
179
219
|
}
|
|
180
220
|
}
|
|
181
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Check whether all siblings after a node are empty
|
|
224
|
+
*/
|
|
225
|
+
private isEffectivelyLastChild(node: Node): boolean {
|
|
226
|
+
const next = node.nextSibling;
|
|
227
|
+
|
|
228
|
+
if (!next) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return (next.textContent ?? '').length === 0 && this.isEffectivelyLastChild(next);
|
|
233
|
+
}
|
|
234
|
+
|
|
182
235
|
/**
|
|
183
236
|
* Remove underline tags (<u>) while preserving content
|
|
184
237
|
* @param range - The Range object containing the selection to unwrap
|
|
@@ -201,7 +201,10 @@ export class BoldNormalizationPass {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
|
-
* Replace non-breaking spaces with regular spaces in a text node
|
|
204
|
+
* Replace non-breaking spaces with regular spaces in a text node.
|
|
205
|
+
* Preserves a trailing \u00A0 when the text node is the last child of its
|
|
206
|
+
* parent, because converting it to a regular space (char 32) would make it
|
|
207
|
+
* invisible under CSS white-space:normal.
|
|
205
208
|
* @param textNode - The text node to process
|
|
206
209
|
*/
|
|
207
210
|
private replaceNbspInTextNode(textNode: Text): void {
|
|
@@ -211,10 +214,13 @@ export class BoldNormalizationPass {
|
|
|
211
214
|
return;
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
const
|
|
217
|
+
const keepTrailing = text.endsWith('\u00A0') && BoldNormalizationPass.isEffectivelyLastChild(textNode);
|
|
218
|
+
const replaced = text.replace(/\u00A0/g, ' ');
|
|
215
219
|
|
|
216
220
|
// eslint-disable-next-line no-param-reassign
|
|
217
|
-
textNode.textContent =
|
|
221
|
+
textNode.textContent = keepTrailing
|
|
222
|
+
? replaced.slice(0, -1) + '\u00A0'
|
|
223
|
+
: replaced;
|
|
218
224
|
}
|
|
219
225
|
|
|
220
226
|
/**
|
|
@@ -279,6 +285,26 @@ export class BoldNormalizationPass {
|
|
|
279
285
|
rightStrong.remove();
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Check whether all siblings after a node are empty (zero-length text nodes
|
|
290
|
+
* or empty elements). The unbold process can leave behind empty text nodes
|
|
291
|
+
* which would cause `!node.nextSibling` to return false even though the node
|
|
292
|
+
* is effectively the last meaningful child.
|
|
293
|
+
*/
|
|
294
|
+
private static isEffectivelyLastChild(node: Node): boolean {
|
|
295
|
+
const next = node.nextSibling;
|
|
296
|
+
|
|
297
|
+
if (!next) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if ((next.textContent ?? '').length > 0) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return BoldNormalizationPass.isEffectivelyLastChild(next);
|
|
306
|
+
}
|
|
307
|
+
|
|
282
308
|
/**
|
|
283
309
|
* Find the appropriate scope element from a selection
|
|
284
310
|
* @param selection - The current selection
|
|
@@ -132,6 +132,89 @@ export const isRangeFormatted = (
|
|
|
132
132
|
return textNodes.every((textNode) => hasFormattingAncestor(textNode, predicate));
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Walk down the `lastChild` chain of `node` and return the deepest last
|
|
137
|
+
* descendant that is a text node, or `null` if none exists.
|
|
138
|
+
*/
|
|
139
|
+
const findDeepestLastTextNode = (node: Node): Text | null => {
|
|
140
|
+
const last = node.lastChild;
|
|
141
|
+
|
|
142
|
+
if (last === null) {
|
|
143
|
+
return node.nodeType === Node.TEXT_NODE ? (node as Text) : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return findDeepestLastTextNode(last);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Extend the range to include any trailing whitespace characters that browsers
|
|
151
|
+
* exclude from selections (e.g. Ctrl+A on loaded text stops before trailing spaces).
|
|
152
|
+
*
|
|
153
|
+
* When text is loaded via innerHTML (e.g. `"hello "`), browsers keep trailing
|
|
154
|
+
* regular spaces (char 32) as-is in the text node, but Ctrl+A / selectAll
|
|
155
|
+
* in Chromium and WebKit places the range end *before* those trailing spaces.
|
|
156
|
+
* This means `range.cloneContents()` omits them, so formatting operations
|
|
157
|
+
* (bold, italic, etc.) silently drop any trailing whitespace.
|
|
158
|
+
*
|
|
159
|
+
* By contrast, text typed by the user is stored internally with a non-breaking
|
|
160
|
+
* space (`\u00A0`) which is always included in the selection.
|
|
161
|
+
*
|
|
162
|
+
* This function mutates the range in place to cover those excluded trailing
|
|
163
|
+
* spaces, so downstream formatting can include them.
|
|
164
|
+
*
|
|
165
|
+
* Two browser behaviours are handled:
|
|
166
|
+
* 1. `endContainer` is a text node — the range ends mid-text, so check whether
|
|
167
|
+
* the remaining characters are all whitespace and extend if so.
|
|
168
|
+
* 2. `endContainer` is an element node — Ctrl+A / selectAll in Chromium and
|
|
169
|
+
* WebKit places the range end on the element itself with `endOffset` equal
|
|
170
|
+
* to the child count (i.e. after all children). In this case the last child
|
|
171
|
+
* text node may still have trailing whitespace that was excluded; walk to
|
|
172
|
+
* the deepest last text node and extend the range to its end if it ends with
|
|
173
|
+
* only whitespace after the current offset (or fully consists of whitespace).
|
|
174
|
+
*
|
|
175
|
+
* @param range - The range to extend (mutated in place)
|
|
176
|
+
*/
|
|
177
|
+
export const extendRangeToTrailingWhitespace = (range: Range): void => {
|
|
178
|
+
const endContainer = range.endContainer;
|
|
179
|
+
|
|
180
|
+
if (endContainer.nodeType === Node.TEXT_NODE) {
|
|
181
|
+
const text = endContainer.textContent ?? '';
|
|
182
|
+
const endOffset = range.endOffset;
|
|
183
|
+
|
|
184
|
+
if (endOffset >= text.length) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check whether all characters between endOffset and end of text node are whitespace
|
|
189
|
+
const trailingSlice = text.slice(endOffset);
|
|
190
|
+
|
|
191
|
+
if (trailingSlice.length > 0 && trailingSlice.trim().length === 0) {
|
|
192
|
+
range.setEnd(endContainer, text.length);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// endContainer is an element node (the common case for Ctrl+A on loaded content).
|
|
199
|
+
// Walk to the deepest last text node within it to find any trailing whitespace.
|
|
200
|
+
const lastTextNode = findDeepestLastTextNode(endContainer);
|
|
201
|
+
|
|
202
|
+
if (lastTextNode === null) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const text = lastTextNode.textContent ?? '';
|
|
207
|
+
|
|
208
|
+
if (text.length === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Extend range if the text ends with one or more whitespace characters.
|
|
213
|
+
if (/\s+$/.test(text)) {
|
|
214
|
+
range.setEnd(lastTextNode, text.length);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
135
218
|
/**
|
|
136
219
|
* Collect all unique formatting ancestors within a range
|
|
137
220
|
* @param range - The range to search within
|
|
@@ -11,6 +11,25 @@ export class ToolsAPI extends Module {
|
|
|
11
11
|
public get methods(): ToolsAPIInterface {
|
|
12
12
|
return {
|
|
13
13
|
getBlockTools: () => Array.from(this.Blok.Tools.blockTools.values()),
|
|
14
|
+
getToolsConfig: () => {
|
|
15
|
+
const result: ReturnType<ToolsAPIInterface['getToolsConfig']> = {
|
|
16
|
+
tools: this.config.tools,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (this.config.inlineToolbar !== undefined) {
|
|
20
|
+
result.inlineToolbar = this.config.inlineToolbar;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (this.config.tunes !== undefined) {
|
|
24
|
+
result.tunes = this.config.tunes;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.config.theme !== undefined) {
|
|
28
|
+
result.theme = this.config.theme;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result;
|
|
32
|
+
},
|
|
14
33
|
};
|
|
15
34
|
}
|
|
16
35
|
}
|