@jackuait/blok 0.4.2 → 0.4.3-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/codemod/README.md +32 -10
- package/codemod/migrate-editorjs-to-blok.js +92 -0
- package/codemod/test.js +98 -0
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-BjgH1REI.mjs → blok-D85C_iyk.mjs} +1863 -1736
- package/dist/chunks/{i18next-loader-DfiUa_gd.mjs → i18next-loader-exc8oN3J.mjs} +1 -1
- package/dist/chunks/{index-5m5JWNey.mjs → index-fqgWb6Ue.mjs} +1 -1
- package/dist/chunks/{inline-tool-convert-Bx5BVd8I.mjs → inline-tool-convert-CYkcoF1r.mjs} +201 -203
- package/dist/chunks/{messages-BFiUomgG.mjs → messages--yMo5vYp.mjs} +1 -1
- package/dist/chunks/{messages-2434tVOK.mjs → messages-82j7Sb1f.mjs} +1 -1
- package/dist/chunks/{messages-p9BZJaFV.mjs → messages-9-pnvSWZ.mjs} +1 -1
- package/dist/{messages-D5WeksbV.mjs → chunks/messages-B-b_IFZh.mjs} +1 -1
- package/dist/chunks/{messages-ez3w5NBn.mjs → messages-B5OIvtYB.mjs} +1 -1
- package/dist/{messages-FTOZNhRD.mjs → chunks/messages-B7zIiLTq.mjs} +1 -1
- package/dist/{messages-JF2fzCkK.mjs → chunks/messages-BCidD-VB.mjs} +1 -1
- package/dist/chunks/{messages-WYWIbQwo.mjs → messages-BWDnoNq0.mjs} +1 -1
- package/dist/chunks/{messages-BIPNHHAV.mjs → messages-BXsoyz5-.mjs} +1 -1
- package/dist/{messages-CXHt3eCC.mjs → chunks/messages-BYeiscyc.mjs} +1 -1
- package/dist/chunks/{messages-er-kd-VO.mjs → messages-BZ-Ia4tk.mjs} +1 -1
- package/dist/chunks/{messages-CmymP_Ar.mjs → messages-BaaIMBL8.mjs} +1 -1
- package/dist/chunks/{messages-Dd5iZN3c.mjs → messages-BgbVziWf.mjs} +1 -1
- package/dist/chunks/{messages-76-iJV9Q.mjs → messages-BgmPDL9Z.mjs} +1 -1
- package/dist/chunks/{messages-BextV3Qh.mjs → messages-Bi0X1QMU.mjs} +1 -1
- package/dist/chunks/{messages-QgYhPL-3.mjs → messages-BjZll4R7.mjs} +1 -1
- package/dist/{messages-D_FCyfW6.mjs → chunks/messages-BnmcUH66.mjs} +1 -1
- package/dist/chunks/{messages-DueVe0F1.mjs → messages-Bo9JrN9m.mjs} +1 -1
- package/dist/{messages-f3uXjegd.mjs → chunks/messages-BuKgyEeU.mjs} +1 -1
- package/dist/chunks/{messages-ohwI1UGv.mjs → messages-CJQt8nzO.mjs} +1 -1
- package/dist/chunks/{messages-D3vTzIpL.mjs → messages-CNRsIuSS.mjs} +1 -1
- package/dist/chunks/{messages-qWkXPggi.mjs → messages-CPdlHIEi.mjs} +1 -1
- package/dist/{messages-CbmsBrB0.mjs → chunks/messages-CTeYgNo1.mjs} +1 -1
- package/dist/{messages-BCm2eudQ.mjs → chunks/messages-CVvHbBlx.mjs} +1 -1
- package/dist/{messages-D3GrDwXh.mjs → chunks/messages-CbP4KoKV.mjs} +1 -1
- package/dist/chunks/{messages-Dg1OHftD.mjs → messages-CbepBt3K.mjs} +1 -1
- package/dist/{messages-BqWaOGMn.mjs → chunks/messages-CbtBGtJT.mjs} +1 -1
- package/dist/chunks/{messages-BqkL2_Ro.mjs → messages-CcYGSGRh.mjs} +1 -1
- package/dist/{messages-57uL5htT.mjs → chunks/messages-CjksZqgf.mjs} +1 -1
- package/dist/chunks/{messages-CFrKE-TN.mjs → messages-Cm4WhO4g.mjs} +1 -1
- package/dist/{messages-BiXe9G-O.mjs → chunks/messages-CoY-hJgp.mjs} +1 -1
- package/dist/chunks/{messages-qIQ4L4rw.mjs → messages-CpkO-V0C.mjs} +1 -1
- package/dist/{messages-DKha57ZU.mjs → chunks/messages-CwYueRo5.mjs} +1 -1
- package/dist/chunks/{messages-3DcCwXMF.mjs → messages-D-tDNAzW.mjs} +1 -1
- package/dist/{messages-DehM7135.mjs → chunks/messages-D2h_bTrn.mjs} +1 -1
- package/dist/chunks/{messages-IQxGfQIV.mjs → messages-D5J2Xlpx.mjs} +1 -1
- package/dist/chunks/{messages-Cm0LJLtB.mjs → messages-D8VsV_Ro.mjs} +1 -1
- package/dist/chunks/{messages-CHz8VlG-.mjs → messages-D8sk1mxH.mjs} +1 -1
- package/dist/chunks/{messages-MOGl7I5v.mjs → messages-D8tlQmPj.mjs} +1 -1
- package/dist/chunks/{messages-BiPSFlUG.mjs → messages-D9xzl8Wn.mjs} +1 -1
- package/dist/{messages-BoO8gsVD.mjs → chunks/messages-DG2QXu_f.mjs} +1 -1
- package/dist/{messages-DGaab4EP.mjs → chunks/messages-DHdkT1vy.mjs} +1 -1
- package/dist/{messages-4kMwVAKY.mjs → chunks/messages-DIbeBo-6.mjs} +1 -1
- package/dist/{messages-DVbPLd_0.mjs → chunks/messages-DR0sRlko.mjs} +1 -1
- package/dist/{messages-BX-DPa-z.mjs → chunks/messages-DRTt_xBc.mjs} +1 -1
- package/dist/{messages-CV7OM_qk.mjs → chunks/messages-DT43ZUKr.mjs} +1 -1
- package/dist/{messages-Di6Flq-b.mjs → chunks/messages-DT5djAjs.mjs} +1 -1
- package/dist/{messages-C6tbPLoj.mjs → chunks/messages-Df6LYOt5.mjs} +1 -1
- package/dist/chunks/{messages-BUlwu9mo.mjs → messages-DhdzbzrS.mjs} +1 -1
- package/dist/chunks/{messages-Ceo1KtFx.mjs → messages-DiyoZ_Oa.mjs} +1 -1
- package/dist/chunks/{messages-8p86Eyf2.mjs → messages-DkJ5aV3V.mjs} +1 -1
- package/dist/{messages-Dx3eFwI0.mjs → chunks/messages-DvwZEFTL.mjs} +1 -1
- package/dist/{messages-Bl5z_Igo.mjs → chunks/messages-Dw5GZ4YT.mjs} +1 -1
- package/dist/chunks/{messages-CLixzySl.mjs → messages-HSxl9MoN.mjs} +1 -1
- package/dist/chunks/{messages-DOaujgMW.mjs → messages-JztkG3tZ.mjs} +1 -1
- package/dist/{messages-BnsE97ku.mjs → chunks/messages-KLzTvDED.mjs} +1 -1
- package/dist/{messages-BBX0p0Pi.mjs → chunks/messages-TulYnz1f.mjs} +1 -1
- package/dist/chunks/{messages-D0ohMB5H.mjs → messages-Uexhy7C9.mjs} +1 -1
- package/dist/{messages-Dqhhex6e.mjs → chunks/messages-ctRNTfv5.mjs} +1 -1
- package/dist/chunks/{messages-CA6T3-gQ.mjs → messages-dIzxSHR2.mjs} +1 -1
- package/dist/chunks/{messages-bSf31LJi.mjs → messages-g7Njot5w.mjs} +1 -1
- package/dist/chunks/{messages-w5foGze_.mjs → messages-iNIhRBkQ.mjs} +1 -1
- package/dist/chunks/{messages-FOtiUoKl.mjs → messages-jmh1Gbfd.mjs} +1 -1
- package/dist/chunks/{messages-a6A_LgDv.mjs → messages-oO_lJ-_I.mjs} +1 -1
- package/dist/chunks/{messages-BvCkXKX-.mjs → messages-qFesO1Lu.mjs} +1 -1
- package/dist/{messages-diGozhTn.mjs → chunks/messages-sSYZ0Mz9.mjs} +1 -1
- package/dist/chunks/{messages-CFFPFdWP.mjs → messages-zs9AgeBF.mjs} +1 -1
- package/dist/full.mjs +2 -2
- package/dist/locales.mjs +68 -68
- package/dist/{messages-BFiUomgG.mjs → messages--yMo5vYp.mjs} +1 -1
- package/dist/{messages-2434tVOK.mjs → messages-82j7Sb1f.mjs} +1 -1
- package/dist/{messages-p9BZJaFV.mjs → messages-9-pnvSWZ.mjs} +1 -1
- package/dist/{chunks/messages-D5WeksbV.mjs → messages-B-b_IFZh.mjs} +1 -1
- package/dist/{messages-ez3w5NBn.mjs → messages-B5OIvtYB.mjs} +1 -1
- package/dist/{chunks/messages-FTOZNhRD.mjs → messages-B7zIiLTq.mjs} +1 -1
- package/dist/{chunks/messages-JF2fzCkK.mjs → messages-BCidD-VB.mjs} +1 -1
- package/dist/{messages-WYWIbQwo.mjs → messages-BWDnoNq0.mjs} +1 -1
- package/dist/{messages-BIPNHHAV.mjs → messages-BXsoyz5-.mjs} +1 -1
- package/dist/{chunks/messages-CXHt3eCC.mjs → messages-BYeiscyc.mjs} +1 -1
- package/dist/{messages-er-kd-VO.mjs → messages-BZ-Ia4tk.mjs} +1 -1
- package/dist/{messages-CmymP_Ar.mjs → messages-BaaIMBL8.mjs} +1 -1
- package/dist/{messages-Dd5iZN3c.mjs → messages-BgbVziWf.mjs} +1 -1
- package/dist/{messages-76-iJV9Q.mjs → messages-BgmPDL9Z.mjs} +1 -1
- package/dist/{messages-BextV3Qh.mjs → messages-Bi0X1QMU.mjs} +1 -1
- package/dist/{messages-QgYhPL-3.mjs → messages-BjZll4R7.mjs} +1 -1
- package/dist/{chunks/messages-D_FCyfW6.mjs → messages-BnmcUH66.mjs} +1 -1
- package/dist/{messages-DueVe0F1.mjs → messages-Bo9JrN9m.mjs} +1 -1
- package/dist/{chunks/messages-f3uXjegd.mjs → messages-BuKgyEeU.mjs} +1 -1
- package/dist/{messages-ohwI1UGv.mjs → messages-CJQt8nzO.mjs} +1 -1
- package/dist/{messages-D3vTzIpL.mjs → messages-CNRsIuSS.mjs} +1 -1
- package/dist/{messages-qWkXPggi.mjs → messages-CPdlHIEi.mjs} +1 -1
- package/dist/{chunks/messages-CbmsBrB0.mjs → messages-CTeYgNo1.mjs} +1 -1
- package/dist/{chunks/messages-BCm2eudQ.mjs → messages-CVvHbBlx.mjs} +1 -1
- package/dist/{chunks/messages-D3GrDwXh.mjs → messages-CbP4KoKV.mjs} +1 -1
- package/dist/{messages-Dg1OHftD.mjs → messages-CbepBt3K.mjs} +1 -1
- package/dist/{chunks/messages-BqWaOGMn.mjs → messages-CbtBGtJT.mjs} +1 -1
- package/dist/{messages-BqkL2_Ro.mjs → messages-CcYGSGRh.mjs} +1 -1
- package/dist/{chunks/messages-57uL5htT.mjs → messages-CjksZqgf.mjs} +1 -1
- package/dist/{messages-CFrKE-TN.mjs → messages-Cm4WhO4g.mjs} +1 -1
- package/dist/{chunks/messages-BiXe9G-O.mjs → messages-CoY-hJgp.mjs} +1 -1
- package/dist/{messages-qIQ4L4rw.mjs → messages-CpkO-V0C.mjs} +1 -1
- package/dist/{chunks/messages-DKha57ZU.mjs → messages-CwYueRo5.mjs} +1 -1
- package/dist/{messages-3DcCwXMF.mjs → messages-D-tDNAzW.mjs} +1 -1
- package/dist/{chunks/messages-DehM7135.mjs → messages-D2h_bTrn.mjs} +1 -1
- package/dist/{messages-IQxGfQIV.mjs → messages-D5J2Xlpx.mjs} +1 -1
- package/dist/{messages-Cm0LJLtB.mjs → messages-D8VsV_Ro.mjs} +1 -1
- package/dist/{messages-CHz8VlG-.mjs → messages-D8sk1mxH.mjs} +1 -1
- package/dist/{messages-MOGl7I5v.mjs → messages-D8tlQmPj.mjs} +1 -1
- package/dist/{messages-BiPSFlUG.mjs → messages-D9xzl8Wn.mjs} +1 -1
- package/dist/{chunks/messages-BoO8gsVD.mjs → messages-DG2QXu_f.mjs} +1 -1
- package/dist/{chunks/messages-DGaab4EP.mjs → messages-DHdkT1vy.mjs} +1 -1
- package/dist/{chunks/messages-4kMwVAKY.mjs → messages-DIbeBo-6.mjs} +1 -1
- package/dist/{chunks/messages-DVbPLd_0.mjs → messages-DR0sRlko.mjs} +1 -1
- package/dist/{chunks/messages-BX-DPa-z.mjs → messages-DRTt_xBc.mjs} +1 -1
- package/dist/{chunks/messages-CV7OM_qk.mjs → messages-DT43ZUKr.mjs} +1 -1
- package/dist/{chunks/messages-Di6Flq-b.mjs → messages-DT5djAjs.mjs} +1 -1
- package/dist/{chunks/messages-C6tbPLoj.mjs → messages-Df6LYOt5.mjs} +1 -1
- package/dist/{messages-BUlwu9mo.mjs → messages-DhdzbzrS.mjs} +1 -1
- package/dist/{messages-Ceo1KtFx.mjs → messages-DiyoZ_Oa.mjs} +1 -1
- package/dist/{messages-8p86Eyf2.mjs → messages-DkJ5aV3V.mjs} +1 -1
- package/dist/{chunks/messages-Dx3eFwI0.mjs → messages-DvwZEFTL.mjs} +1 -1
- package/dist/{chunks/messages-Bl5z_Igo.mjs → messages-Dw5GZ4YT.mjs} +1 -1
- package/dist/{messages-CLixzySl.mjs → messages-HSxl9MoN.mjs} +1 -1
- package/dist/{messages-DOaujgMW.mjs → messages-JztkG3tZ.mjs} +1 -1
- package/dist/{chunks/messages-BnsE97ku.mjs → messages-KLzTvDED.mjs} +1 -1
- package/dist/{chunks/messages-BBX0p0Pi.mjs → messages-TulYnz1f.mjs} +1 -1
- package/dist/{messages-D0ohMB5H.mjs → messages-Uexhy7C9.mjs} +1 -1
- package/dist/{chunks/messages-Dqhhex6e.mjs → messages-ctRNTfv5.mjs} +1 -1
- package/dist/{messages-CA6T3-gQ.mjs → messages-dIzxSHR2.mjs} +1 -1
- package/dist/{messages-bSf31LJi.mjs → messages-g7Njot5w.mjs} +1 -1
- package/dist/{messages-w5foGze_.mjs → messages-iNIhRBkQ.mjs} +1 -1
- package/dist/{messages-FOtiUoKl.mjs → messages-jmh1Gbfd.mjs} +1 -1
- package/dist/{messages-a6A_LgDv.mjs → messages-oO_lJ-_I.mjs} +1 -1
- package/dist/{messages-BvCkXKX-.mjs → messages-qFesO1Lu.mjs} +1 -1
- package/dist/{chunks/messages-diGozhTn.mjs → messages-sSYZ0Mz9.mjs} +1 -1
- package/dist/{messages-CFFPFdWP.mjs → messages-zs9AgeBF.mjs} +1 -1
- package/dist/tools.mjs +131 -90
- package/package.json +16 -3
- package/src/components/flipper.ts +17 -1
- package/src/components/i18n/locales/am/messages.json +1 -1
- package/src/components/i18n/locales/ar/messages.json +1 -1
- package/src/components/i18n/locales/az/messages.json +1 -1
- package/src/components/i18n/locales/bg/messages.json +1 -1
- package/src/components/i18n/locales/bn/messages.json +1 -1
- package/src/components/i18n/locales/bs/messages.json +1 -1
- package/src/components/i18n/locales/cs/messages.json +1 -1
- package/src/components/i18n/locales/da/messages.json +1 -1
- package/src/components/i18n/locales/de/messages.json +1 -1
- package/src/components/i18n/locales/dv/messages.json +1 -1
- package/src/components/i18n/locales/el/messages.json +1 -1
- package/src/components/i18n/locales/en/messages.json +1 -1
- package/src/components/i18n/locales/es/messages.json +1 -1
- package/src/components/i18n/locales/et/messages.json +1 -1
- package/src/components/i18n/locales/fa/messages.json +1 -1
- package/src/components/i18n/locales/fi/messages.json +1 -1
- package/src/components/i18n/locales/fil/messages.json +1 -1
- package/src/components/i18n/locales/fr/messages.json +1 -1
- package/src/components/i18n/locales/gu/messages.json +1 -1
- package/src/components/i18n/locales/he/messages.json +1 -1
- package/src/components/i18n/locales/hi/messages.json +1 -1
- package/src/components/i18n/locales/hr/messages.json +1 -1
- package/src/components/i18n/locales/hu/messages.json +1 -1
- package/src/components/i18n/locales/hy/messages.json +1 -1
- package/src/components/i18n/locales/id/messages.json +1 -1
- package/src/components/i18n/locales/it/messages.json +1 -1
- package/src/components/i18n/locales/ja/messages.json +1 -1
- package/src/components/i18n/locales/ka/messages.json +1 -1
- package/src/components/i18n/locales/km/messages.json +1 -1
- package/src/components/i18n/locales/kn/messages.json +1 -1
- package/src/components/i18n/locales/ko/messages.json +1 -1
- package/src/components/i18n/locales/ku/messages.json +1 -1
- package/src/components/i18n/locales/lo/messages.json +1 -1
- package/src/components/i18n/locales/lt/messages.json +1 -1
- package/src/components/i18n/locales/lv/messages.json +1 -1
- package/src/components/i18n/locales/mk/messages.json +1 -1
- package/src/components/i18n/locales/ml/messages.json +1 -1
- package/src/components/i18n/locales/mn/messages.json +1 -1
- package/src/components/i18n/locales/mr/messages.json +1 -1
- package/src/components/i18n/locales/ms/messages.json +1 -1
- package/src/components/i18n/locales/my/messages.json +1 -1
- package/src/components/i18n/locales/ne/messages.json +1 -1
- package/src/components/i18n/locales/nl/messages.json +1 -1
- package/src/components/i18n/locales/no/messages.json +1 -1
- package/src/components/i18n/locales/pa/messages.json +1 -1
- package/src/components/i18n/locales/pl/messages.json +1 -1
- package/src/components/i18n/locales/ps/messages.json +1 -1
- package/src/components/i18n/locales/pt/messages.json +1 -1
- package/src/components/i18n/locales/ro/messages.json +1 -1
- package/src/components/i18n/locales/ru/messages.json +1 -1
- package/src/components/i18n/locales/sd/messages.json +1 -1
- package/src/components/i18n/locales/si/messages.json +1 -1
- package/src/components/i18n/locales/sk/messages.json +1 -1
- package/src/components/i18n/locales/sl/messages.json +1 -1
- package/src/components/i18n/locales/sq/messages.json +1 -1
- package/src/components/i18n/locales/sr/messages.json +1 -1
- package/src/components/i18n/locales/sv/messages.json +1 -1
- package/src/components/i18n/locales/sw/messages.json +1 -1
- package/src/components/i18n/locales/ta/messages.json +1 -1
- package/src/components/i18n/locales/te/messages.json +1 -1
- package/src/components/i18n/locales/th/messages.json +1 -1
- package/src/components/i18n/locales/tr/messages.json +1 -1
- package/src/components/i18n/locales/ug/messages.json +1 -1
- package/src/components/i18n/locales/uk/messages.json +1 -1
- package/src/components/i18n/locales/ur/messages.json +1 -1
- package/src/components/i18n/locales/vi/messages.json +1 -1
- package/src/components/i18n/locales/yi/messages.json +1 -1
- package/src/components/i18n/locales/zh/messages.json +1 -1
- package/src/components/modules/api/i18n.ts +1 -0
- package/src/components/modules/dragManager.ts +71 -13
- package/src/components/modules/i18n.ts +6 -11
- package/src/components/modules/rectangleSelection.ts +62 -19
- package/src/components/modules/toolbar/index.ts +2 -2
- package/src/components/modules/tools.ts +39 -3
- package/src/components/modules/ui.ts +83 -18
- package/src/components/selection.ts +2 -12
- package/src/components/tools/base.ts +29 -2
- package/src/components/tools/block.ts +20 -3
- package/src/components/tools/factory.ts +50 -1
- package/src/components/utils/tooltip.ts +0 -14
- package/src/tools/header/index.ts +82 -0
- package/src/tools/index.ts +13 -6
- package/src/tools/list/index.ts +2 -2
- package/types/api/i18n.d.ts +8 -0
- package/types/tools/tool-settings.d.ts +62 -4
|
@@ -35,6 +35,8 @@ const DRAG_CONFIG = {
|
|
|
35
35
|
autoScrollSpeed: 10,
|
|
36
36
|
/** Throttle delay for drop position announcements (ms) */
|
|
37
37
|
announcementThrottleMs: 300,
|
|
38
|
+
/** Horizontal distance to the left of blocks where drop is still valid */
|
|
39
|
+
leftDropZone: 50,
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -424,20 +426,10 @@ export class DragManager extends Module {
|
|
|
424
426
|
return;
|
|
425
427
|
}
|
|
426
428
|
|
|
427
|
-
// Find block holder
|
|
428
|
-
const blockHolder =
|
|
429
|
+
// Find block holder - first try direct hit, then left drop zone fallback
|
|
430
|
+
const { block: targetBlock, holder: blockHolder } = this.findDropTargetBlock(elementUnderCursor, clientX, clientY);
|
|
429
431
|
|
|
430
|
-
if (!blockHolder) {
|
|
431
|
-
this.dragState.targetBlock = null;
|
|
432
|
-
this.dragState.targetEdge = null;
|
|
433
|
-
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Find the block instance
|
|
438
|
-
const targetBlock = this.Blok.BlockManager.blocks.find(b => b.holder === blockHolder);
|
|
439
|
-
|
|
440
|
-
if (!targetBlock || targetBlock === this.dragState.sourceBlock) {
|
|
432
|
+
if (!blockHolder || !targetBlock || targetBlock === this.dragState.sourceBlock) {
|
|
441
433
|
this.dragState.targetBlock = null;
|
|
442
434
|
this.dragState.targetEdge = null;
|
|
443
435
|
|
|
@@ -1132,6 +1124,72 @@ export class DragManager extends Module {
|
|
|
1132
1124
|
return collectDescendants(blockIndex + 1, []);
|
|
1133
1125
|
}
|
|
1134
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* Finds the drop target block from an element or by checking the left drop zone.
|
|
1129
|
+
* @param elementUnderCursor - Element directly under the cursor
|
|
1130
|
+
* @param clientX - Cursor X position
|
|
1131
|
+
* @param clientY - Cursor Y position
|
|
1132
|
+
* @returns Object with block and holder, or nulls if no valid target found
|
|
1133
|
+
*/
|
|
1134
|
+
private findDropTargetBlock(
|
|
1135
|
+
elementUnderCursor: Element,
|
|
1136
|
+
clientX: number,
|
|
1137
|
+
clientY: number
|
|
1138
|
+
): { block: Block | undefined; holder: HTMLElement | null } {
|
|
1139
|
+
// First try: find block holder directly under cursor
|
|
1140
|
+
const directHolder = elementUnderCursor.closest(createSelector(DATA_ATTR.element)) as HTMLElement | null;
|
|
1141
|
+
|
|
1142
|
+
if (directHolder) {
|
|
1143
|
+
const block = this.Blok.BlockManager.blocks.find(b => b.holder === directHolder);
|
|
1144
|
+
|
|
1145
|
+
return { block, holder: directHolder };
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// Fallback: check if cursor is in the left drop zone
|
|
1149
|
+
const leftZoneBlock = this.findBlockInLeftDropZone(clientX, clientY);
|
|
1150
|
+
|
|
1151
|
+
if (leftZoneBlock) {
|
|
1152
|
+
return { block: leftZoneBlock, holder: leftZoneBlock.holder };
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
return { block: undefined, holder: null };
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Finds a block by vertical position when cursor is in the left drop zone.
|
|
1160
|
+
* Used as a fallback when elementFromPoint doesn't find a block directly.
|
|
1161
|
+
* @param clientX - Cursor X position
|
|
1162
|
+
* @param clientY - Cursor Y position
|
|
1163
|
+
* @returns Block at the vertical position, or null if not in left zone or no block found
|
|
1164
|
+
*/
|
|
1165
|
+
private findBlockInLeftDropZone(clientX: number, clientY: number): Block | null {
|
|
1166
|
+
const contentRect = this.Blok.UI.contentRect;
|
|
1167
|
+
const leftEdge = contentRect.left;
|
|
1168
|
+
|
|
1169
|
+
// Check if cursor is within left drop zone (between leftEdge - leftDropZone and leftEdge)
|
|
1170
|
+
const distanceFromEdge = leftEdge - clientX;
|
|
1171
|
+
|
|
1172
|
+
if (distanceFromEdge < 0 || distanceFromEdge > DRAG_CONFIG.leftDropZone) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Find block by Y position
|
|
1177
|
+
for (const block of this.Blok.BlockManager.blocks) {
|
|
1178
|
+
// Skip source blocks
|
|
1179
|
+
if (this.dragState?.sourceBlocks.includes(block)) {
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const rect = block.holder.getBoundingClientRect();
|
|
1184
|
+
|
|
1185
|
+
if (clientY >= rect.top && clientY <= rect.bottom) {
|
|
1186
|
+
return block;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1135
1193
|
/**
|
|
1136
1194
|
* Module destruction
|
|
1137
1195
|
*/
|
|
@@ -204,17 +204,7 @@ export class I18n extends Module {
|
|
|
204
204
|
// Set default locale if configured
|
|
205
205
|
this.applyDefaultLocale(i18nConfig?.defaultLocale);
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
if (i18nConfig?.messages !== undefined) {
|
|
209
|
-
// For custom messages, use lightweight implementation with overrides
|
|
210
|
-
this.lightweightI18n.setDictionary(i18nConfig.messages);
|
|
211
|
-
this.usingI18next = false;
|
|
212
|
-
// Set direction from config or default to 'ltr' for custom messages
|
|
213
|
-
this.updateConfigDirection(i18nConfig.direction ?? 'ltr');
|
|
214
|
-
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
207
|
+
// Load base translations first
|
|
218
208
|
const requestedLocale = i18nConfig?.locale;
|
|
219
209
|
|
|
220
210
|
if (requestedLocale === undefined || requestedLocale === 'auto') {
|
|
@@ -223,6 +213,11 @@ export class I18n extends Module {
|
|
|
223
213
|
await this.setLocale(requestedLocale);
|
|
224
214
|
}
|
|
225
215
|
|
|
216
|
+
// Merge custom messages on top of base translations (if provided)
|
|
217
|
+
if (i18nConfig?.messages !== undefined) {
|
|
218
|
+
this.setDictionary(i18nConfig.messages);
|
|
219
|
+
}
|
|
220
|
+
|
|
226
221
|
// Update config.i18n.direction so other modules can access it via isRtl getter
|
|
227
222
|
this.updateConfigDirection(i18nConfig?.direction ?? this.getDirection());
|
|
228
223
|
}
|
|
@@ -116,11 +116,29 @@ export class RectangleSelection extends Module {
|
|
|
116
116
|
* Init rect params
|
|
117
117
|
* @param {number} pageX - X coord of mouse
|
|
118
118
|
* @param {number} pageY - Y coord of mouse
|
|
119
|
+
* @param {boolean} shiftKey - whether Shift key is held for additive selection
|
|
119
120
|
*/
|
|
120
|
-
public startSelection(pageX: number, pageY: number): void {
|
|
121
|
-
const
|
|
121
|
+
public startSelection(pageX: number, pageY: number, shiftKey = false): void {
|
|
122
|
+
const { UI } = this.Blok;
|
|
123
|
+
const redactor = UI.nodes.redactor;
|
|
124
|
+
|
|
125
|
+
if (!redactor) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const editorRect = redactor.getBoundingClientRect();
|
|
122
130
|
const scrollTop = this.getScrollTop();
|
|
123
|
-
const
|
|
131
|
+
const pointerY = pageY - scrollTop;
|
|
132
|
+
|
|
133
|
+
// Check if pointer is within editor's vertical bounds
|
|
134
|
+
const withinEditorVertically = pointerY >= editorRect.top && pointerY <= editorRect.bottom;
|
|
135
|
+
|
|
136
|
+
if (!withinEditorVertically) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const scrollLeft = this.getScrollLeft();
|
|
141
|
+
const elemWhereSelectionStart = document.elementFromPoint(pageX - scrollLeft, pointerY);
|
|
124
142
|
|
|
125
143
|
if (!elemWhereSelectionStart) {
|
|
126
144
|
return;
|
|
@@ -132,7 +150,11 @@ export class RectangleSelection extends Module {
|
|
|
132
150
|
*/
|
|
133
151
|
const startsInsideToolbar = elemWhereSelectionStart.closest(createSelector(DATA_ATTR.toolbar));
|
|
134
152
|
|
|
135
|
-
|
|
153
|
+
/**
|
|
154
|
+
* When Shift is held, preserve existing selection for additive behavior.
|
|
155
|
+
* Otherwise, clear selection state.
|
|
156
|
+
*/
|
|
157
|
+
if (!startsInsideToolbar && !shiftKey) {
|
|
136
158
|
this.Blok.BlockSelection.allBlocksSelected = false;
|
|
137
159
|
this.clearSelection();
|
|
138
160
|
this.stackOfSelected = [];
|
|
@@ -141,16 +163,16 @@ export class RectangleSelection extends Module {
|
|
|
141
163
|
const selectorsToAvoid = [
|
|
142
164
|
createSelector(DATA_ATTR.elementContent),
|
|
143
165
|
createSelector(DATA_ATTR.toolbar),
|
|
166
|
+
createSelector(DATA_ATTR.popover),
|
|
144
167
|
INLINE_TOOLBAR_INTERFACE_SELECTOR,
|
|
145
168
|
];
|
|
146
169
|
|
|
147
|
-
const startsInsideBlok = elemWhereSelectionStart.closest(createSelector(DATA_ATTR.editor));
|
|
148
170
|
const startsInSelectorToAvoid = selectorsToAvoid.some((selector) => !!elemWhereSelectionStart.closest(selector));
|
|
149
171
|
|
|
150
172
|
/**
|
|
151
|
-
* If selection starts
|
|
173
|
+
* If selection starts inside the blocks content or on Blok UI elements, do not handle it
|
|
152
174
|
*/
|
|
153
|
-
if (
|
|
175
|
+
if (startsInSelectorToAvoid) {
|
|
154
176
|
return;
|
|
155
177
|
}
|
|
156
178
|
|
|
@@ -194,9 +216,9 @@ export class RectangleSelection extends Module {
|
|
|
194
216
|
* Sets Module necessary event handlers
|
|
195
217
|
*/
|
|
196
218
|
private enableModuleBindings(): void {
|
|
197
|
-
|
|
219
|
+
this.genHTML();
|
|
198
220
|
|
|
199
|
-
this.listeners.on(
|
|
221
|
+
this.listeners.on(document.body, 'mousedown', (event: Event) => {
|
|
200
222
|
this.processMouseDown(event as MouseEvent);
|
|
201
223
|
}, false);
|
|
202
224
|
|
|
@@ -245,7 +267,7 @@ export class RectangleSelection extends Module {
|
|
|
245
267
|
const startedFromContentEditable = (mouseEvent.target as Element).closest($.allInputsSelector) !== null;
|
|
246
268
|
|
|
247
269
|
if (!startedFromContentEditable) {
|
|
248
|
-
this.startSelection(mouseEvent.pageX, mouseEvent.pageY);
|
|
270
|
+
this.startSelection(mouseEvent.pageX, mouseEvent.pageY, mouseEvent.shiftKey);
|
|
249
271
|
}
|
|
250
272
|
}
|
|
251
273
|
|
|
@@ -402,21 +424,38 @@ export class RectangleSelection extends Module {
|
|
|
402
424
|
this.mouseY = event.pageY;
|
|
403
425
|
}
|
|
404
426
|
|
|
405
|
-
const { rightPos, leftPos, index } = this.genInfoForMouseSelection();
|
|
406
|
-
// There is not new block in selection
|
|
407
|
-
|
|
408
|
-
const rectIsOnRighSideOfredactor = this.startX > rightPos && this.mouseX > rightPos;
|
|
409
|
-
const rectISOnLeftSideOfRedactor = this.startX < leftPos && this.mouseX < leftPos;
|
|
410
|
-
|
|
411
|
-
this.rectCrossesBlocks = !(rectIsOnRighSideOfredactor || rectISOnLeftSideOfRedactor);
|
|
412
|
-
|
|
413
427
|
if (!this.isRectSelectionActivated) {
|
|
414
|
-
this.rectCrossesBlocks = false;
|
|
415
428
|
this.isRectSelectionActivated = true;
|
|
416
429
|
this.shrinkRectangleToPoint();
|
|
417
430
|
overlayRectangle.style.display = 'block';
|
|
418
431
|
}
|
|
419
432
|
|
|
433
|
+
const { index } = this.genInfoForMouseSelection();
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Check if the selection rectangle intersects the block holder horizontally.
|
|
437
|
+
* For page-wide selection, we need to verify the rectangle actually reaches the block,
|
|
438
|
+
* not just that the mouse Y position is at the same height as a block.
|
|
439
|
+
*/
|
|
440
|
+
this.rectCrossesBlocks = false;
|
|
441
|
+
const block = index !== undefined ? this.Blok.BlockManager.getBlockByIndex(index) : undefined;
|
|
442
|
+
|
|
443
|
+
if (block) {
|
|
444
|
+
const holderRect = block.holder.getBoundingClientRect();
|
|
445
|
+
const scrollLeft = this.getScrollLeft();
|
|
446
|
+
|
|
447
|
+
// Selection rectangle horizontal bounds (in page coordinates)
|
|
448
|
+
const rectLeft = Math.min(this.startX, this.mouseX);
|
|
449
|
+
const rectRight = Math.max(this.startX, this.mouseX);
|
|
450
|
+
|
|
451
|
+
// Block holder horizontal bounds (convert from viewport to page coordinates)
|
|
452
|
+
const holderLeft = holderRect.left + scrollLeft;
|
|
453
|
+
const holderRight = holderRect.right + scrollLeft;
|
|
454
|
+
|
|
455
|
+
// Check for horizontal intersection
|
|
456
|
+
this.rectCrossesBlocks = rectRight >= holderLeft && rectLeft <= holderRight;
|
|
457
|
+
}
|
|
458
|
+
|
|
420
459
|
this.updateRectangleSize();
|
|
421
460
|
|
|
422
461
|
/**
|
|
@@ -517,6 +556,7 @@ export class RectangleSelection extends Module {
|
|
|
517
556
|
|
|
518
557
|
/**
|
|
519
558
|
* Collects information needed to determine the behavior of the rectangle
|
|
559
|
+
* For page-wide selection, we check blocks at the center X position but at the actual mouse Y position
|
|
520
560
|
* @returns {object} index - index next Block, leftPos - start of left border of Block, rightPos - right border
|
|
521
561
|
*/
|
|
522
562
|
private genInfoForMouseSelection(): {index: number | undefined; leftPos: number; rightPos: number} {
|
|
@@ -524,6 +564,9 @@ export class RectangleSelection extends Module {
|
|
|
524
564
|
const centerOfRedactor = widthOfRedactor / 2;
|
|
525
565
|
const scrollTop = this.getScrollTop();
|
|
526
566
|
const y = this.mouseY - scrollTop;
|
|
567
|
+
|
|
568
|
+
// For page-wide selection: check what block is at the center X, but at the mouse's Y position
|
|
569
|
+
// This allows selection to work even when mouse is in the left/right margins
|
|
527
570
|
const elementUnderMouse = document.elementFromPoint(centerOfRedactor, y);
|
|
528
571
|
const lastBlockHolder = this.Blok.BlockManager.lastBlock?.holder;
|
|
529
572
|
const contentElement = lastBlockHolder?.querySelector(createSelector(DATA_ATTR.elementContent));
|
|
@@ -998,9 +998,9 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
998
998
|
*/
|
|
999
999
|
this.eventsDispatcher.on(BlockHovered, (data) => {
|
|
1000
1000
|
/**
|
|
1001
|
-
* Do not move toolbar during drag operations
|
|
1001
|
+
* Do not move toolbar during drag or rectangle selection operations
|
|
1002
1002
|
*/
|
|
1003
|
-
if (this.Blok.DragManager.isDragging) {
|
|
1003
|
+
if (this.Blok.DragManager.isDragging || this.Blok.RectangleSelection.isRectActivated()) {
|
|
1004
1004
|
return;
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
@@ -334,18 +334,18 @@ export class Tools extends Module {
|
|
|
334
334
|
.map(([toolName, settings]): ChainData => {
|
|
335
335
|
const toolData: ToolPrepareData = {
|
|
336
336
|
toolName,
|
|
337
|
-
config: (settings
|
|
337
|
+
config: this.extractToolConfig(settings),
|
|
338
338
|
};
|
|
339
339
|
|
|
340
340
|
const prepareFunction: ChainData['function'] = async (payload?: unknown) => {
|
|
341
|
-
const constructable = settings.class;
|
|
341
|
+
const constructable = settings.class as ToolConstructable | undefined;
|
|
342
342
|
|
|
343
343
|
if (!constructable || !isFunction(constructable.prepare)) {
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
const data = (payload ?? toolData) as ToolPrepareData;
|
|
348
|
-
const prepareMethod = constructable.prepare as
|
|
348
|
+
const prepareMethod = constructable.prepare as ToolPrepareFunction;
|
|
349
349
|
|
|
350
350
|
return prepareMethod.call(constructable, data);
|
|
351
351
|
};
|
|
@@ -357,6 +357,42 @@ export class Tools extends Module {
|
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Keys that are Blok-level settings (not passed to tool constructor)
|
|
362
|
+
*/
|
|
363
|
+
private static readonly BLOK_SETTINGS_KEYS = new Set([
|
|
364
|
+
'class',
|
|
365
|
+
'inlineToolbar',
|
|
366
|
+
'tunes',
|
|
367
|
+
'shortcut',
|
|
368
|
+
'toolbox',
|
|
369
|
+
'config',
|
|
370
|
+
'isInternal',
|
|
371
|
+
]);
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Extracts tool configuration from settings.
|
|
375
|
+
* Merges nested config with flat tool-specific options (flat takes precedence).
|
|
376
|
+
* @param settings - Tool settings from user config
|
|
377
|
+
* @returns Merged tool configuration
|
|
378
|
+
*/
|
|
379
|
+
private extractToolConfig(settings: ToolSettings): ToolConfig {
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
|
|
381
|
+
const nestedConfig = (settings.config ?? {}) as ToolConfig;
|
|
382
|
+
|
|
383
|
+
// Extract non-Blok keys as tool-specific config
|
|
384
|
+
const flatConfig: Record<string, unknown> = {};
|
|
385
|
+
|
|
386
|
+
for (const key of Object.keys(settings)) {
|
|
387
|
+
if (!Tools.BLOK_SETTINGS_KEYS.has(key)) {
|
|
388
|
+
flatConfig[key] = settings[key as keyof typeof settings];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Merge: nested config first, flat config overrides
|
|
393
|
+
return { ...nestedConfig, ...flatConfig } as ToolConfig;
|
|
394
|
+
}
|
|
395
|
+
|
|
360
396
|
/**
|
|
361
397
|
* Assign enabled Inline Tools and Block Tunes for Block Tool
|
|
362
398
|
*/
|
|
@@ -9,8 +9,15 @@ import { debounce, getValidUrl, isEmpty, openTab, throttle } from '../utils';
|
|
|
9
9
|
|
|
10
10
|
import { SelectionUtils as Selection } from '../selection';
|
|
11
11
|
import { Flipper } from '../flipper';
|
|
12
|
+
import type { Block } from '../block';
|
|
12
13
|
import { mobileScreenBreakpoint } from '../utils';
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Horizontal distance from the content edge where block hover is still detected.
|
|
17
|
+
* Extends to the left for LTR layouts, to the right for RTL.
|
|
18
|
+
*/
|
|
19
|
+
const HOVER_ZONE_SIZE = 100;
|
|
20
|
+
|
|
14
21
|
import styles from '../../styles/main.css?inline';
|
|
15
22
|
import { BlockHovered } from '../events/BlockHovered';
|
|
16
23
|
import {
|
|
@@ -453,43 +460,60 @@ export class UI extends Module<UINodes> {
|
|
|
453
460
|
*/
|
|
454
461
|
private watchBlockHoveredEvents(): void {
|
|
455
462
|
/**
|
|
456
|
-
* Used to not emit the same block multiple times to the 'block-hovered' event on every mousemove
|
|
463
|
+
* Used to not emit the same block multiple times to the 'block-hovered' event on every mousemove.
|
|
464
|
+
* Stores block ID to ensure consistent comparison regardless of how the block was detected.
|
|
457
465
|
*/
|
|
458
|
-
const blockHoveredState: {
|
|
459
|
-
|
|
466
|
+
const blockHoveredState: { lastHoveredBlockId: string | null } = {
|
|
467
|
+
lastHoveredBlockId: null,
|
|
460
468
|
};
|
|
461
469
|
|
|
462
470
|
const handleBlockHovered = (event: Event): void => {
|
|
463
|
-
|
|
464
|
-
const isTouchEvent = typeof TouchEvent !== 'undefined' && event instanceof TouchEvent;
|
|
465
|
-
|
|
466
|
-
if (!isMouseEvent && !isTouchEvent) {
|
|
471
|
+
if (!(event instanceof MouseEvent)) {
|
|
467
472
|
return;
|
|
468
473
|
}
|
|
469
474
|
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
if (!hoveredBlock) {
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
+
const hoveredBlockElement = (event.target as Element | null)?.closest('[data-blok-testid="block-wrapper"]');
|
|
475
476
|
|
|
476
477
|
/**
|
|
477
|
-
*
|
|
478
|
-
* The toolbar module will handle the logic of whether to move or not.
|
|
478
|
+
* If no block element found directly, try the extended hover zone
|
|
479
479
|
*/
|
|
480
|
+
const zoneBlock = !hoveredBlockElement
|
|
481
|
+
? this.findBlockInHoverZone(event.clientX, event.clientY)
|
|
482
|
+
: null;
|
|
483
|
+
|
|
484
|
+
if (zoneBlock !== null && blockHoveredState.lastHoveredBlockId !== zoneBlock.id) {
|
|
485
|
+
blockHoveredState.lastHoveredBlockId = zoneBlock.id;
|
|
480
486
|
|
|
481
|
-
|
|
487
|
+
this.eventsDispatcher.emit(BlockHovered, {
|
|
488
|
+
block: zoneBlock,
|
|
489
|
+
target: zoneBlock.holder,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (zoneBlock !== null) {
|
|
482
494
|
return;
|
|
483
495
|
}
|
|
484
496
|
|
|
485
|
-
|
|
497
|
+
if (!hoveredBlockElement) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
486
500
|
|
|
487
|
-
const block = this.Blok.BlockManager.getBlockByChildNode(
|
|
501
|
+
const block = this.Blok.BlockManager.getBlockByChildNode(hoveredBlockElement);
|
|
488
502
|
|
|
489
503
|
if (!block) {
|
|
490
504
|
return;
|
|
491
505
|
}
|
|
492
506
|
|
|
507
|
+
/**
|
|
508
|
+
* For multi-block selection, still emit 'block-hovered' event so toolbar can follow the hovered block.
|
|
509
|
+
* The toolbar module will handle the logic of whether to move or not.
|
|
510
|
+
*/
|
|
511
|
+
if (blockHoveredState.lastHoveredBlockId === block.id) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
blockHoveredState.lastHoveredBlockId = block.id;
|
|
516
|
+
|
|
493
517
|
this.eventsDispatcher.emit(BlockHovered, {
|
|
494
518
|
block,
|
|
495
519
|
target: event.target as Element,
|
|
@@ -502,13 +526,54 @@ export class UI extends Module<UINodes> {
|
|
|
502
526
|
20
|
|
503
527
|
);
|
|
504
528
|
|
|
505
|
-
|
|
529
|
+
/**
|
|
530
|
+
* Listen on document to detect hover in the extended zone
|
|
531
|
+
* which is outside the wrapper's bounds.
|
|
532
|
+
* We filter events to only process those over the editor or in the hover zone.
|
|
533
|
+
*/
|
|
534
|
+
this.readOnlyMutableListeners.on(document, 'mousemove', (event: Event) => {
|
|
506
535
|
throttledHandleBlockHovered(event);
|
|
507
536
|
}, {
|
|
508
537
|
passive: true,
|
|
509
538
|
});
|
|
510
539
|
}
|
|
511
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Finds a block by vertical position when cursor is in the extended hover zone.
|
|
543
|
+
* The zone extends HOVER_ZONE_SIZE pixels from the content edge (left for LTR, right for RTL).
|
|
544
|
+
* @param clientX - Cursor X position
|
|
545
|
+
* @param clientY - Cursor Y position
|
|
546
|
+
* @returns Block at the vertical position, or null if not in hover zone or no block found
|
|
547
|
+
*/
|
|
548
|
+
private findBlockInHoverZone(clientX: number, clientY: number): Block | null {
|
|
549
|
+
const contentRect = this.contentRect;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* For LTR: check if cursor is within hover zone to the left of content
|
|
553
|
+
* For RTL: check if cursor is within hover zone to the right of content
|
|
554
|
+
*/
|
|
555
|
+
const isInHoverZone = this.isRtl
|
|
556
|
+
? clientX > contentRect.right && clientX <= contentRect.right + HOVER_ZONE_SIZE
|
|
557
|
+
: clientX < contentRect.left && clientX >= contentRect.left - HOVER_ZONE_SIZE;
|
|
558
|
+
|
|
559
|
+
if (!isInHoverZone) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Find block by Y position
|
|
565
|
+
*/
|
|
566
|
+
for (const block of this.Blok.BlockManager.blocks) {
|
|
567
|
+
const rect = block.holder.getBoundingClientRect();
|
|
568
|
+
|
|
569
|
+
if (clientY >= rect.top && clientY <= rect.bottom) {
|
|
570
|
+
return block;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
512
577
|
/**
|
|
513
578
|
* Unbind events that should work only in read-only mode
|
|
514
579
|
*/
|
|
@@ -53,11 +53,6 @@ export class SelectionUtils {
|
|
|
53
53
|
*/
|
|
54
54
|
public isFakeBackgroundEnabled = false;
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* The contenteditable element that had the selection when fake background was enabled
|
|
58
|
-
* Used to restore focus and selection when fake background is removed
|
|
59
|
-
*/
|
|
60
|
-
private selectionContainer: HTMLElement | null = null;
|
|
61
56
|
|
|
62
57
|
/**
|
|
63
58
|
* Returns selected anchor
|
|
@@ -401,7 +396,6 @@ export class SelectionUtils {
|
|
|
401
396
|
this.removeHighlightSpans();
|
|
402
397
|
|
|
403
398
|
this.isFakeBackgroundEnabled = false;
|
|
404
|
-
this.selectionContainer = null;
|
|
405
399
|
}
|
|
406
400
|
|
|
407
401
|
/**
|
|
@@ -454,7 +448,6 @@ export class SelectionUtils {
|
|
|
454
448
|
public clearFakeBackground(): void {
|
|
455
449
|
this.removeOrphanedFakeBackgroundElements();
|
|
456
450
|
this.isFakeBackgroundEnabled = false;
|
|
457
|
-
this.selectionContainer = null;
|
|
458
451
|
}
|
|
459
452
|
|
|
460
453
|
/**
|
|
@@ -478,12 +471,9 @@ export class SelectionUtils {
|
|
|
478
471
|
}
|
|
479
472
|
|
|
480
473
|
// Find the contenteditable container that holds the selection
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
? container as HTMLElement
|
|
484
|
-
: container.parentElement;
|
|
474
|
+
|
|
475
|
+
|
|
485
476
|
|
|
486
|
-
this.selectionContainer = element?.closest('[contenteditable="true"]') as HTMLElement | null;
|
|
487
477
|
|
|
488
478
|
// Collect text nodes and wrap them with highlight spans
|
|
489
479
|
const textNodes = this.collectTextNodes(range);
|
|
@@ -7,6 +7,19 @@ import type { InlineToolAdapter as InlineToolAdapterInterface } from '@/types/to
|
|
|
7
7
|
import type { BlockToolAdapter as BlockToolAdapterInterface } from '@/types/tools/adapters/block-tool-adapter';
|
|
8
8
|
import type { BlockTuneAdapter as BlockTuneAdapterInterface } from '@/types/tools/adapters/block-tune-adapter';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Keys that are Blok-level settings (not passed to tool constructor)
|
|
12
|
+
*/
|
|
13
|
+
const BLOK_SETTINGS_KEYS = new Set([
|
|
14
|
+
'class',
|
|
15
|
+
'inlineToolbar',
|
|
16
|
+
'tunes',
|
|
17
|
+
'shortcut',
|
|
18
|
+
'toolbox',
|
|
19
|
+
'config',
|
|
20
|
+
'isInternal',
|
|
21
|
+
]);
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* Enum of Tool options provided by user
|
|
12
25
|
*/
|
|
@@ -188,10 +201,24 @@ export abstract class BaseToolAdapter<Type extends ToolType = ToolType, ToolClas
|
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
/**
|
|
191
|
-
* Returns Tool user configuration
|
|
204
|
+
* Returns Tool user configuration.
|
|
205
|
+
* Extracts tool-specific options from flat config and merges with nested config.
|
|
192
206
|
*/
|
|
193
207
|
public get settings(): ToolConfig {
|
|
194
|
-
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
|
|
209
|
+
const nestedConfig = (this.config[UserSettings.Config] ?? {}) as ToolConfig;
|
|
210
|
+
|
|
211
|
+
// Extract non-Blok keys as tool-specific config
|
|
212
|
+
const flatConfig: Record<string, unknown> = {};
|
|
213
|
+
|
|
214
|
+
for (const key of Object.keys(this.config)) {
|
|
215
|
+
if (!BLOK_SETTINGS_KEYS.has(key)) {
|
|
216
|
+
flatConfig[key] = this.config[key as keyof typeof this.config];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Merge: nested config first, flat config overrides
|
|
221
|
+
const config = { ...nestedConfig, ...flatConfig } as ToolConfig;
|
|
195
222
|
|
|
196
223
|
if (this.isDefault && !('placeholder' in config) && this.defaultPlaceholder) {
|
|
197
224
|
config.placeholder = this.defaultPlaceholder;
|
|
@@ -51,13 +51,22 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
|
|
|
51
51
|
* @param readOnly - True if Blok is in read-only mode
|
|
52
52
|
*/
|
|
53
53
|
public create(data: BlockToolData, block: BlockAPI, readOnly: boolean): IBlockTool {
|
|
54
|
+
const toolboxEntries = this.toolbox;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Inject merged toolbox entries into config so tools can use them in renderSettings().
|
|
58
|
+
* This allows tools like Header to show the same options in block settings as in the toolbox.
|
|
59
|
+
*/
|
|
60
|
+
const configWithToolbox = toolboxEntries !== undefined
|
|
61
|
+
? { ...this.settings, _toolboxEntries: toolboxEntries }
|
|
62
|
+
: this.settings;
|
|
54
63
|
|
|
55
64
|
return new this.constructable({
|
|
56
65
|
data,
|
|
57
66
|
block,
|
|
58
67
|
readOnly,
|
|
59
68
|
api: this.api,
|
|
60
|
-
config:
|
|
69
|
+
config: configWithToolbox,
|
|
61
70
|
}) as IBlockTool;
|
|
62
71
|
}
|
|
63
72
|
|
|
@@ -194,10 +203,18 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
|
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
/**
|
|
197
|
-
* Returns enabled inline tools for Tool
|
|
206
|
+
* Returns enabled inline tools for Tool.
|
|
207
|
+
* Defaults to true (all inline tools) unless explicitly set to false or array.
|
|
198
208
|
*/
|
|
199
209
|
public get enabledInlineTools(): boolean | string[] {
|
|
200
|
-
|
|
210
|
+
const setting = this.config[UserSettings.EnabledInlineTools];
|
|
211
|
+
|
|
212
|
+
// Default to true if not specified
|
|
213
|
+
if (setting === undefined) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return setting;
|
|
201
218
|
}
|
|
202
219
|
|
|
203
220
|
/**
|