@jackuait/blok 0.6.0-beta.5 → 0.6.0-beta.7

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.
Files changed (75) hide show
  1. package/README.md +16 -169
  2. package/bin/blok.mjs +10 -0
  3. package/dist/blok.mjs +2 -2
  4. package/dist/chunks/{blok-XA2L0_-K.mjs → blok-Bjkxis7j.mjs} +1029 -934
  5. package/dist/chunks/{i18next-loader-Ci2zhA-n.mjs → i18next-loader-D-sjerXt.mjs} +1 -1
  6. package/dist/chunks/{index-R38OiQ_d.mjs → index-CJfN4Vuv.mjs} +1 -1
  7. package/dist/chunks/{inline-tool-convert-e3PyuxB6.mjs → inline-tool-convert-5DE7c2fi.mjs} +1 -1
  8. package/dist/{messages-BGxiFoZf.mjs → chunks/messages-7QoX8DkW.mjs} +14 -14
  9. package/dist/{messages-UX4gkere.mjs → chunks/messages-BMXCuEKO.mjs} +5 -5
  10. package/dist/{messages-wp_1b1hD.mjs → chunks/messages-BSbjsyHY.mjs} +16 -16
  11. package/dist/chunks/{messages-DChXyvh2.mjs → messages-BdeLo0N9.mjs} +14 -14
  12. package/dist/chunks/{messages-B9fe4dQJ.mjs → messages-C2htQ_3F.mjs} +12 -12
  13. package/dist/{messages-BNe6LuHW.mjs → chunks/messages-C9eaarcK.mjs} +7 -7
  14. package/dist/chunks/messages-CKI54h6O.mjs +62 -0
  15. package/dist/{messages-DjvaFRqx.mjs → chunks/messages-CQwpzUFp.mjs} +6 -6
  16. package/dist/{messages-EL5ARzmK.mjs → chunks/messages-CVw84KdI.mjs} +7 -7
  17. package/dist/chunks/{messages-49thXXGz.mjs → messages-Diu6jAaR.mjs} +6 -6
  18. package/dist/{messages-Bfiw5w_W.mjs → chunks/messages-DvFLX36Q.mjs} +10 -10
  19. package/dist/chunks/{messages-QtoE8uEv.mjs → messages-Dzwxv9v1.mjs} +8 -8
  20. package/dist/cli.mjs +50 -0
  21. package/dist/full.mjs +15 -15
  22. package/dist/locales.mjs +12 -12
  23. package/dist/{chunks/messages-BGxiFoZf.mjs → messages-7QoX8DkW.mjs} +14 -14
  24. package/dist/{chunks/messages-UX4gkere.mjs → messages-BMXCuEKO.mjs} +5 -5
  25. package/dist/{chunks/messages-wp_1b1hD.mjs → messages-BSbjsyHY.mjs} +16 -16
  26. package/dist/{messages-DChXyvh2.mjs → messages-BdeLo0N9.mjs} +14 -14
  27. package/dist/{messages-B9fe4dQJ.mjs → messages-C2htQ_3F.mjs} +12 -12
  28. package/dist/{chunks/messages-BNe6LuHW.mjs → messages-C9eaarcK.mjs} +7 -7
  29. package/dist/messages-CKI54h6O.mjs +62 -0
  30. package/dist/{chunks/messages-DjvaFRqx.mjs → messages-CQwpzUFp.mjs} +6 -6
  31. package/dist/{chunks/messages-EL5ARzmK.mjs → messages-CVw84KdI.mjs} +7 -7
  32. package/dist/{messages-49thXXGz.mjs → messages-Diu6jAaR.mjs} +6 -6
  33. package/dist/{chunks/messages-Bfiw5w_W.mjs → messages-DvFLX36Q.mjs} +10 -10
  34. package/dist/{messages-QtoE8uEv.mjs → messages-Dzwxv9v1.mjs} +8 -8
  35. package/dist/tools.mjs +222 -207
  36. package/dist/vendor.LICENSE.txt +1 -1
  37. package/package.json +13 -8
  38. package/src/cli/commands/migration.ts +16 -0
  39. package/src/cli/commands/migrationContent.ts +6 -0
  40. package/src/cli/index.ts +47 -0
  41. package/src/cli/utils/output.ts +10 -0
  42. package/src/components/i18n/locales/bn/messages.json +16 -16
  43. package/src/components/i18n/locales/fil/messages.json +7 -7
  44. package/src/components/i18n/locales/hi/messages.json +14 -14
  45. package/src/components/i18n/locales/id/messages.json +6 -6
  46. package/src/components/i18n/locales/mr/messages.json +14 -14
  47. package/src/components/i18n/locales/ms/messages.json +6 -6
  48. package/src/components/i18n/locales/ne/messages.json +12 -12
  49. package/src/components/i18n/locales/si/messages.json +8 -8
  50. package/src/components/i18n/locales/ta/messages.json +7 -7
  51. package/src/components/i18n/locales/te/messages.json +35 -35
  52. package/src/components/i18n/locales/th/messages.json +5 -5
  53. package/src/components/i18n/locales/vi/messages.json +10 -10
  54. package/src/components/modules/api/history.ts +64 -0
  55. package/src/components/modules/api/index.ts +1 -0
  56. package/src/components/modules/api/readonly.ts +11 -1
  57. package/src/components/modules/blockManager/blockManager.ts +7 -0
  58. package/src/components/modules/blockManager/yjs-sync.ts +12 -2
  59. package/src/components/modules/index.ts +3 -0
  60. package/src/components/modules/readonly.ts +11 -0
  61. package/src/components/modules/rectangleSelection.ts +7 -3
  62. package/src/components/modules/saver.ts +3 -2
  63. package/src/components/modules/toolbar/index.ts +46 -11
  64. package/src/components/modules/toolbar/plus-button.ts +22 -1
  65. package/src/components/modules/yjs/index.ts +23 -0
  66. package/src/tools/table/index.ts +20 -0
  67. package/src/tools/table/table-add-controls.ts +8 -0
  68. package/src/tools/table/table-cell-blocks.ts +15 -5
  69. package/src/types-internal/blok-modules.d.ts +2 -0
  70. package/types/api/history.d.ts +33 -0
  71. package/types/api/index.d.ts +1 -0
  72. package/types/api/readonly.d.ts +12 -2
  73. package/types/index.d.ts +3 -0
  74. package/dist/chunks/messages-DsVNtdgM.mjs +0 -62
  75. package/dist/messages-DsVNtdgM.mjs +0 -62
@@ -1,24 +1,24 @@
1
1
  {
2
- "blockSettings.dragToMove": "తరలించడానికి డ్రాగ్ చేయండి",
3
- "blockSettings.clickToOpenMenu": "మెనూ తెరవడానికి క్లిక్ చేయండి",
4
- "toolbox.addBelow": "క్రింద జోడించడానికి క్లిక్ చేయండి",
5
- "toolbox.optionAddAbove": "పైన జోడించడానికి Option-క్లిక్ చేయండి",
6
- "toolbox.ctrlAddAbove": "పైన జోడించడానికి Ctrl-క్లిక్ చేయండి",
2
+ "blockSettings.dragToMove": "తరలించడానికి లాగండి",
3
+ "blockSettings.clickToOpenMenu": "జాబితా తెరవడానికి నొక్కండి",
4
+ "toolbox.addBelow": "క్రింద జోడించడానికి నొక్కండి",
5
+ "toolbox.optionAddAbove": "పైన జోడించడానికి Option-నొక్కండి",
6
+ "toolbox.ctrlAddAbove": "పైన జోడించడానికి Ctrl-నొక్కండి",
7
7
  "popover.search": "వెతకండి",
8
8
  "popover.nothingFound": "ఏమీ కనుగొనబడలేదు",
9
9
  "popover.convertTo": "మార్చు",
10
- "toolNames.text": "టెక్స్ట్",
10
+ "toolNames.text": "వచనం",
11
11
  "toolNames.heading": "శీర్షిక",
12
12
  "toolNames.link": "లింక్",
13
- "toolNames.bold": "బోల్డ్",
14
- "toolNames.italic": "ఇటాలిక్",
15
- "toolNames.bulletedList": "బుల్లెట్ జాబితా",
16
- "toolNames.numberedList": "నంబర్ జాబితా",
13
+ "toolNames.bold": "గాఢం",
14
+ "toolNames.italic": "వాలు",
15
+ "toolNames.bulletedList": "చుక్కల జాబితా",
16
+ "toolNames.numberedList": "అంకెల జాబితా",
17
17
  "toolNames.todoList": "చేయవలసిన పనుల జాబితా",
18
18
  "tools.link.addLink": "లింక్ జోడించండి",
19
19
  "tools.link.invalidLink": "చెల్లని లింక్",
20
20
  "tools.stub.error": "లోపం",
21
- "tools.stub.blockCannotBeDisplayed": "ఈ బ్లాక్‌ను ప్రదర్శించలేము",
21
+ "tools.stub.blockCannotBeDisplayed": "ఈ విభాగాన్ని ప్రదర్శించలేము",
22
22
  "tools.header.heading1": "శీర్షిక 1",
23
23
  "tools.header.heading2": "శీర్షిక 2",
24
24
  "tools.header.heading3": "శీర్షిక 3",
@@ -27,33 +27,33 @@
27
27
  "tools.header.heading6": "శీర్షిక 6",
28
28
  "tools.list.placeholder": "జాబితా",
29
29
  "blockSettings.delete": "తొలగించు",
30
- "a11y.dragHandle": "బ్లాక్ తరలించడానికి డ్రాగ్ చేయండి లేదా మెనూ కోసం క్లిక్ చేయండి",
31
- "a11y.dragHandleRole": "డ్రాగ్ హ్యాండిల్",
32
- "a11y.dragStarted": "బ్లాక్ డ్రాగ్ అవుతోంది",
33
- "a11y.dragStartedMultiple": "{count} బ్లాక్‌లు డ్రాగ్ అవుతున్నాయి",
34
- "a11y.dropPosition": "{total} లో {position} స్థానంలో డ్రాప్ అవుతుంది",
35
- "a11y.dropCancelled": "డ్రాగ్ రద్దు చేయబడింది",
36
- "a11y.blockMoved": "బ్లాక్ {total} లో {position} స్థానానికి తరలించబడింది",
37
- "a11y.blocksMoved": "{count} బ్లాక్‌లు {position} స్థానానికి తరలించబడ్డాయి",
38
- "a11y.blockDuplicated": "బ్లాక్ {total} లో {position} స్థానంలో నకలు చేయబడింది",
39
- "a11y.blocksDuplicated": "{count} బ్లాక్‌లు {position} స్థానం నుండి నకలు చేయబడ్డాయి",
40
- "a11y.movedUp": "బ్లాక్ {total} లో {position} స్థానానికి పైకి తరలించబడింది",
41
- "a11y.movedDown": "బ్లాక్ {total} లో {position} స్థానానికి క్రిందికి తరలించబడింది",
42
- "a11y.atTop": "బ్లాక్ పైన ఉంది, పైకి తరలించలేము",
43
- "a11y.atBottom": "బ్లాక్ క్రింద ఉంది, క్రిందికి తరలించలేము",
44
- "tools.paragraph.placeholder": "ఏదైనా రాయండి లేదా ఎంచుకోడానికి / నొక్కండి",
30
+ "a11y.dragHandle": "విభాగం తరలించడానికి లాగండి లేదా జాబితా కోసం నొక్కండి",
31
+ "a11y.dragHandleRole": "లాగు పట్టిక",
32
+ "a11y.dragStarted": "విభాగం తరలుతోంది",
33
+ "a11y.dragStartedMultiple": "{count} విభాగాలు తరలుతున్నాయి",
34
+ "a11y.dropPosition": "{total} లో {position} స్థానంలో వదులుతుంది",
35
+ "a11y.dropCancelled": "లాగడం రద్దు చేయబడింది",
36
+ "a11y.blockMoved": "విభాగం {total} లో {position} స్థానానికి తరలించబడింది",
37
+ "a11y.blocksMoved": "{count} విభాగాలు {position} స్థానానికి తరలించబడ్డాయి",
38
+ "a11y.blockDuplicated": "విభాగం {total} లో {position} స్థానంలో నకలు చేయబడింది",
39
+ "a11y.blocksDuplicated": "{count} విభాగాలు {position} స్థానం నుండి నకలు చేయబడ్డాయి",
40
+ "a11y.movedUp": "విభాగం {total} లో {position} స్థానానికి పైకి తరలించబడింది",
41
+ "a11y.movedDown": "విభాగం {total} లో {position} స్థానానికి క్రిందికి తరలించబడింది",
42
+ "a11y.atTop": "విభాగం పైన ఉంది, పైకి తరలించలేము",
43
+ "a11y.atBottom": "విభాగం క్రింద ఉంది, క్రిందికి తరలించలేము",
44
+ "tools.paragraph.placeholder": "ఏదైనా రాయండి లేదా సాధనం ఎంచుకోడానికి / నొక్కండి",
45
45
  "tools.table.title": "పట్టిక",
46
- "tools.table.clearSelection": "క్లియర్ చేయండి",
47
- "tools.table.headerColumn": "హెడర్ కాలమ్",
48
- "tools.table.insertColumnLeft": "ఎడమవైపు కాలమ్ చొప్పించు",
49
- "tools.table.insertColumnRight": "కుడివైపు కాలమ్ చొప్పించు",
46
+ "tools.table.clearSelection": "తుడుచు",
47
+ "tools.table.headerColumn": "శీర్ష స్తంభం",
48
+ "tools.table.insertColumnLeft": "ఎడమవైపు స్తంభం చొప్పించు",
49
+ "tools.table.insertColumnRight": "కుడివైపు స్తంభం చొప్పించు",
50
50
  "tools.table.deleteColumn": "తొలగించు",
51
- "tools.table.headerRow": "హెడర్ వరుస",
51
+ "tools.table.headerRow": "శీర్ష వరుస",
52
52
  "tools.table.insertRowAbove": "పైన వరుస చొప్పించు",
53
53
  "tools.table.insertRowBelow": "క్రింద వరుస చొప్పించు",
54
54
  "tools.table.deleteRow": "తొలగించు",
55
- "tools.table.clickToAddRow": "కొత్త వరుస జోడించడానికి క్లిక్ చేయండి",
56
- "tools.table.dragToAddRemoveRows": "వరుసలు జోడించడానికి లేదా తొలగించడానికి డ్రాగ్ చేయండి",
57
- "tools.table.clickToAddColumn": "కొత్త కాలమ్ జోడించడానికి క్లిక్ చేయండి",
58
- "tools.table.dragToAddRemoveColumns": "కాలమ్‌లు జోడించడానికి లేదా తొలగించడానికి డ్రాగ్ చేయండి"
55
+ "tools.table.clickToAddRow": "కొత్త వరుస జోడించడానికి నొక్కండి",
56
+ "tools.table.dragToAddRemoveRows": "వరుసలు జోడించడానికి లేదా తొలగించడానికి లాగండి",
57
+ "tools.table.clickToAddColumn": "కొత్త స్తంభం జోడించడానికి నొక్కండి",
58
+ "tools.table.dragToAddRemoveColumns": "స్తంభాలు జోడించడానికి లేదా తొలగించడానికి లాగండి"
59
59
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "blockSettings.dragToMove": "ลากเพื่อย้าย",
3
3
  "blockSettings.clickToOpenMenu": "คลิกเพื่อเปิดเมนู",
4
- "toolbox.addBelow": "เพิ่มบล็อก",
5
- "toolbox.optionAddAbove": "⌥ — เพิ่มด้านบน",
6
- "toolbox.ctrlAddAbove": "Ctrl — เพิ่มด้านบน",
4
+ "toolbox.addBelow": "คลิกเพื่อเพิ่มด้านล่าง",
5
+ "toolbox.optionAddAbove": "⌥ คลิกเพื่อเพิ่มด้านบน",
6
+ "toolbox.ctrlAddAbove": "Ctrl คลิกเพื่อเพิ่มด้านบน",
7
7
  "popover.search": "ค้นหา",
8
8
  "popover.nothingFound": "ไม่พบผลลัพธ์",
9
9
  "popover.convertTo": "แปลงเป็น",
@@ -35,13 +35,13 @@
35
35
  "a11y.dropCancelled": "ยกเลิกการลาก",
36
36
  "a11y.blockMoved": "ย้ายบล็อกไปตำแหน่ง {position} จาก {total}",
37
37
  "a11y.blocksMoved": "ย้าย {count} บล็อกไปตำแหน่ง {position}",
38
- "a11y.blockDuplicated": "คัดลอกบล็อกไปตำแหน่ง {position} จาก {total}",
38
+ "a11y.blockDuplicated": "คัดลอกบล็อกที่ตำแหน่ง {position} จาก {total}",
39
39
  "a11y.blocksDuplicated": "คัดลอก {count} บล็อก เริ่มจากตำแหน่ง {position}",
40
40
  "a11y.movedUp": "ย้ายบล็อกขึ้น ตำแหน่ง {position} จาก {total}",
41
41
  "a11y.movedDown": "ย้ายบล็อกลง ตำแหน่ง {position} จาก {total}",
42
42
  "a11y.atTop": "บล็อกอยู่บนสุดแล้ว ไม่สามารถย้ายขึ้นได้",
43
43
  "a11y.atBottom": "บล็อกอยู่ล่างสุดแล้ว ไม่สามารถย้ายลงได้",
44
- "tools.paragraph.placeholder": "เขียนอะไรสักอย่าง หรือกด / เพื่อเลือก",
44
+ "tools.paragraph.placeholder": "เขียนอะไรสักอย่าง หรือกด / เพื่อเลือกเครื่องมือ",
45
45
  "tools.table.title": "ตาราง",
46
46
  "tools.table.clearSelection": "ล้าง",
47
47
  "tools.table.headerColumn": "คอลัมน์หัวข้อ",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "blockSettings.dragToMove": "Kéo để di chuyển",
3
3
  "blockSettings.clickToOpenMenu": "Nhấp để mở menu",
4
- "toolbox.addBelow": "Thêm khối",
5
- "toolbox.optionAddAbove": "⌥ Thêm trên",
6
- "toolbox.ctrlAddAbove": "Ctrl Thêm trên",
4
+ "toolbox.addBelow": "Nhấp để thêm bên dưới",
5
+ "toolbox.optionAddAbove": "⌥ Nhấp để thêm bên trên",
6
+ "toolbox.ctrlAddAbove": "Ctrl+Nhấp để thêm bên trên",
7
7
  "popover.search": "Tìm kiếm",
8
8
  "popover.nothingFound": "Không tìm thấy",
9
- "popover.convertTo": "Chuyển đổi",
9
+ "popover.convertTo": "Chuyển sang",
10
10
  "toolNames.text": "Văn bản",
11
11
  "toolNames.heading": "Tiêu đề",
12
12
  "toolNames.link": "Liên kết",
@@ -26,7 +26,7 @@
26
26
  "tools.header.heading5": "Tiêu đề 5",
27
27
  "tools.header.heading6": "Tiêu đề 6",
28
28
  "tools.list.placeholder": "Danh sách",
29
- "tools.paragraph.placeholder": "Viết gì đó hoặc nhấn / để chọn",
29
+ "tools.paragraph.placeholder": "Viết gì đó hoặc nhấn / để chọn công cụ",
30
30
  "tools.table.title": "Bảng",
31
31
  "tools.table.clearSelection": "Xóa",
32
32
  "tools.table.headerColumn": "Cột tiêu đề",
@@ -46,14 +46,14 @@
46
46
  "a11y.dragHandleRole": "tay nắm kéo",
47
47
  "a11y.dragStarted": "Đang kéo khối",
48
48
  "a11y.dragStartedMultiple": "Đang kéo {count} khối",
49
- "a11y.dropPosition": "Sẽ thả vào vị trí {position} trên {total}",
49
+ "a11y.dropPosition": "Sẽ thả vào vị trí {position} trong {total}",
50
50
  "a11y.dropCancelled": "Đã hủy kéo",
51
- "a11y.blockMoved": "Khối đã được di chuyển đến vị trí {position} trên {total}",
51
+ "a11y.blockMoved": "Khối đã được di chuyển đến vị trí {position} trong {total}",
52
52
  "a11y.blocksMoved": "{count} khối đã được di chuyển đến vị trí {position}",
53
- "a11y.blockDuplicated": "Khối đã được sao chép tại vị trí {position} trên {total}",
53
+ "a11y.blockDuplicated": "Khối đã được sao chép tại vị trí {position} trong {total}",
54
54
  "a11y.blocksDuplicated": "{count} khối đã được sao chép từ vị trí {position}",
55
- "a11y.movedUp": "Khối đã di chuyển lên, vị trí {position} trên {total}",
56
- "a11y.movedDown": "Khối đã di chuyển xuống, vị trí {position} trên {total}",
55
+ "a11y.movedUp": "Khối đã di chuyển lên đến vị trí {position} trong {total}",
56
+ "a11y.movedDown": "Khối đã di chuyển xuống đến vị trí {position} trong {total}",
57
57
  "a11y.atTop": "Khối đang ở trên cùng, không thể di chuyển lên",
58
58
  "a11y.atBottom": "Khối đang ở dưới cùng, không thể di chuyển xuống"
59
59
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @module HistoryAPI
3
+ * @copyright <CodeX> 2018
4
+ *
5
+ * Provides public methods for undo/redo operations
6
+ */
7
+ import type { History } from '../../../../types/api';
8
+ import { Module } from '../../__module';
9
+
10
+ /**
11
+ * @class HistoryAPI
12
+ */
13
+ export class HistoryAPI extends Module {
14
+ /**
15
+ * Available methods
16
+ * @returns {History}
17
+ */
18
+ public get methods(): History {
19
+ return {
20
+ undo: (): void => this.undo(),
21
+ redo: (): void => this.redo(),
22
+ canUndo: (): boolean => this.canUndo(),
23
+ canRedo: (): boolean => this.canRedo(),
24
+ clear: (): void => this.clear(),
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Undo the last operation
30
+ */
31
+ public undo(): void {
32
+ this.Blok.YjsManager.undo();
33
+ }
34
+
35
+ /**
36
+ * Redo the last undone operation
37
+ */
38
+ public redo(): void {
39
+ this.Blok.YjsManager.redo();
40
+ }
41
+
42
+ /**
43
+ * Check if undo is available
44
+ * @returns {boolean} true if undo is available
45
+ */
46
+ public canUndo(): boolean {
47
+ return this.Blok.YjsManager.canUndo();
48
+ }
49
+
50
+ /**
51
+ * Check if redo is available
52
+ * @returns {boolean} true if redo is available
53
+ */
54
+ public canRedo(): boolean {
55
+ return this.Blok.YjsManager.canRedo();
56
+ }
57
+
58
+ /**
59
+ * Clear all history
60
+ */
61
+ public clear(): void {
62
+ this.Blok.YjsManager.clear();
63
+ }
64
+ }
@@ -21,6 +21,7 @@ export class API extends Module {
21
21
  caret: this.Blok.CaretAPI.methods,
22
22
  tools: this.Blok.ToolsAPI.methods,
23
23
  events: this.Blok.EventsAPI.methods,
24
+ history: this.Blok.HistoryAPI.methods,
24
25
  listeners: this.Blok.ListenersAPI.methods,
25
26
  notifier: this.Blok.NotifierAPI.methods,
26
27
  sanitizer: this.Blok.SanitizerAPI.methods,
@@ -12,9 +12,10 @@ export class ReadOnlyAPI extends Module {
12
12
  public get methods(): ReadOnly {
13
13
  const getIsEnabled = (): boolean => this.isEnabled;
14
14
 
15
-
15
+
16
16
  return {
17
17
  toggle: (state): Promise<boolean> => this.toggle(state),
18
+ set: (state): Promise<boolean> => this.set(state),
18
19
  get isEnabled(): boolean {
19
20
  return getIsEnabled();
20
21
  },
@@ -30,6 +31,15 @@ export class ReadOnlyAPI extends Module {
30
31
  return this.Blok.ReadOnly.toggle(state);
31
32
  }
32
33
 
34
+ /**
35
+ * Set read-only mode to the specified boolean state
36
+ * @param {boolean} state - read-only state to set
37
+ * @returns {Promise<boolean>} the new read-only state
38
+ */
39
+ public set(state: boolean): Promise<boolean> {
40
+ return this.Blok.ReadOnly.set(state);
41
+ }
42
+
33
43
  /**
34
44
  * Returns current read-only state
35
45
  */
@@ -7,6 +7,7 @@
7
7
  import type { BlockToolData, OutputBlockData, PasteEvent } from '../../../../types';
8
8
  import type { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
9
9
  import type { BlockMutationEventMap, BlockMutationType } from '../../../../types/events/block';
10
+ import { BlockAddedMutationType } from '../../../../types/events/block/BlockAdded';
10
11
  import { BlockChangedMutationType } from '../../../../types/events/block/BlockChanged';
11
12
  import { BlockRemovedMutationType } from '../../../../types/events/block/BlockRemoved';
12
13
  import { Module } from '../../__module';
@@ -307,6 +308,12 @@ export class BlockManager extends Module {
307
308
  replaceBlock: (index, newBlock) => {
308
309
  this.blocksStore.replace(index, newBlock);
309
310
  },
311
+ onBlockRemoved: (block, index) => {
312
+ this.blockDidMutated(BlockRemovedMutationType, block, { index });
313
+ },
314
+ onBlockAdded: (block, index) => {
315
+ this.blockDidMutated(BlockAddedMutationType, block, { index });
316
+ },
310
317
  },
311
318
  this.blocksStore
312
319
  );
@@ -43,6 +43,10 @@ export interface SyncHandlers {
43
43
  updateIndentation: (block: Block) => void;
44
44
  /** Called to replace a block at a specific index with a new block instance */
45
45
  replaceBlock: (index: number, newBlock: Block) => void;
46
+ /** Called when a block is removed during undo/redo (before DOM removal) */
47
+ onBlockRemoved: (block: Block, index: number) => void;
48
+ /** Called when a block is added during undo/redo (after insertion) */
49
+ onBlockAdded: (block: Block, index: number) => void;
46
50
  }
47
51
 
48
52
  /**
@@ -273,10 +277,12 @@ export class BlockYjsSync {
273
277
  bindEventsImmediately: true,
274
278
  });
275
279
 
276
- // Insert into blocks store at correct position - caller must handle this
277
- // This is a limitation - we need the blocksStore.insert method
278
280
  this.blocksStore.insert(targetIndex, block);
279
281
 
282
+ // Emit block-added event so listeners (e.g., TableCellBlocks) can
283
+ // claim the block for the correct cell during undo/redo
284
+ this.handlers.onBlockAdded(block, targetIndex);
285
+
280
286
  // Apply indentation if needed
281
287
  if (parentId !== undefined) {
282
288
  this.handlers.updateIndentation(block);
@@ -300,6 +306,10 @@ export class BlockYjsSync {
300
306
  return;
301
307
  }
302
308
 
309
+ // Emit block-removed event BEFORE removal so listeners can inspect
310
+ // the block's DOM position (e.g., which table cell it's in)
311
+ this.handlers.onBlockRemoved(block, index);
312
+
303
313
  // Remove from DOM
304
314
  this.blocksStore.remove(index);
305
315
 
@@ -1,6 +1,7 @@
1
1
  import { BlocksAPI } from './api/blocks';
2
2
  import { CaretAPI } from './api/caret';
3
3
  import { EventsAPI } from './api/events';
4
+ import { HistoryAPI } from './api/history';
4
5
  import { I18nAPI } from './api/i18n';
5
6
  import { API } from './api/index';
6
7
  import { InlineToolbarAPI } from './api/inlineToolbar';
@@ -44,6 +45,7 @@ export {
44
45
  BlocksAPI,
45
46
  CaretAPI,
46
47
  EventsAPI,
48
+ HistoryAPI,
47
49
  I18nAPI,
48
50
  API,
49
51
  InlineToolbarAPI,
@@ -91,6 +93,7 @@ export const Modules = {
91
93
  BlocksAPI,
92
94
  CaretAPI,
93
95
  EventsAPI,
96
+ HistoryAPI,
94
97
  I18nAPI,
95
98
  API,
96
99
  InlineToolbarAPI,
@@ -125,6 +125,17 @@ export class ReadOnly extends Module {
125
125
  return this.readOnlyEnabled;
126
126
  }
127
127
 
128
+ /**
129
+ * Set read-only mode to the specified boolean state
130
+ * Unlike toggle(), this method requires a parameter and does not have default toggle behavior
131
+ * Call all Modules `toggleReadOnly` method and re-render Blok
132
+ * @param state - read-only state to set (required)
133
+ * @returns the new read-only state
134
+ */
135
+ public async set(state: boolean): Promise<boolean> {
136
+ return this.toggle(state);
137
+ }
138
+
128
139
  /**
129
140
  * Throws an error about tools which don't support read-only mode
130
141
  */
@@ -140,11 +140,15 @@ export class RectangleSelection extends Module {
140
140
  const pointerX = pageX - scrollLeft;
141
141
 
142
142
  /**
143
- * Check if pointer is within editor's horizontal bounds.
143
+ * Check if pointer is within the content area's horizontal bounds.
144
144
  * This determines whether we should close the toolbar when starting selection.
145
- * Clicks outside the horizontal bounds (to the left or right) should NOT close the toolbar.
145
+ * Clicks outside the content area (to the left or right, in the margin/toolbar zone)
146
+ * should NOT close the toolbar. Use the first block's content element for bounds,
147
+ * falling back to the redactor bounds if no content element exists.
146
148
  */
147
- const withinEditorHorizontally = pointerX >= editorRect.left && pointerX <= editorRect.right;
149
+ const contentElement = redactor.querySelector('[data-blok-testid="block-content"]');
150
+ const contentRect = contentElement ? contentElement.getBoundingClientRect() : editorRect;
151
+ const withinEditorHorizontally = pointerX >= contentRect.left && pointerX <= contentRect.right;
148
152
 
149
153
  const elemWhereSelectionStart = document.elementFromPoint(pageX - scrollLeft, pointerY);
150
154
 
@@ -125,7 +125,9 @@ export class Saver extends Module {
125
125
  const extractedBlocks: OutputData['blocks'] = [];
126
126
 
127
127
  allExtractedData.forEach(({ id, tool, data, tunes, isValid, parentId, contentIds }) => {
128
- if (!isValid) {
128
+ const hasParent = parentId !== undefined && parentId !== null;
129
+
130
+ if (!isValid && !hasParent) {
129
131
  log(`Block «${tool}» skipped because saved data is invalid`);
130
132
 
131
133
  return;
@@ -151,7 +153,6 @@ export class Saver extends Module {
151
153
  }
152
154
 
153
155
  const isTunesEmpty = tunes === undefined || isEmpty(tunes);
154
- const hasParent = parentId !== undefined && parentId !== null;
155
156
  const hasContent = contentIds !== undefined && contentIds.length > 0;
156
157
 
157
158
  const output: OutputData['blocks'][number] = {
@@ -85,6 +85,13 @@ export class Toolbar extends Module<ToolbarNodes> {
85
85
  */
86
86
  private explicitlyClosed: boolean = false;
87
87
 
88
+ /**
89
+ * Flag to track if the current hovered block was resolved from a table cell block.
90
+ * When true, the toolbar suppresses plus button, settings toggler, and
91
+ * prevents overriding the current block when the toolbox opens.
92
+ */
93
+ private hoveredBlockIsFromTableCell: boolean = false;
94
+
88
95
  /**
89
96
  * Toolbox class instance
90
97
  * It will be created in requestIdleCallback so it can be null in some period of time
@@ -215,8 +222,10 @@ export class Toolbar extends Module<ToolbarNodes> {
215
222
 
216
223
  /**
217
224
  * Set current block to cover the case when the Toolbar showed near hovered Block but caret is set to another Block.
225
+ * Skip this when the hovered block was resolved from a table cell, so the toolbox
226
+ * can detect the original cell block and hide restricted tools (e.g., table, header).
218
227
  */
219
- if (this.hoveredBlock) {
228
+ if (this.hoveredBlock && !this.hoveredBlockIsFromTableCell) {
220
229
  this.Blok.BlockManager.currentBlock = this.hoveredBlock;
221
230
  }
222
231
 
@@ -328,11 +337,12 @@ export class Toolbar extends Module<ToolbarNodes> {
328
337
 
329
338
  const targetBlock = this.resolveTableCellBlock(unresolvedBlock);
330
339
 
331
- if (!targetBlock) {
332
- this.close();
333
-
334
- return;
335
- }
340
+ /**
341
+ * Track whether the original block was inside a table cell.
342
+ * When true, the toolbar suppresses plus button and settings toggler,
343
+ * and the toolbox uses the original current block for cell detection.
344
+ */
345
+ this.hoveredBlockIsFromTableCell = targetBlock !== unresolvedBlock;
336
346
 
337
347
  /** Clean up draggable on previous block if any */
338
348
  if (this.hoveredBlock && this.hoveredBlock !== targetBlock) {
@@ -351,6 +361,25 @@ export class Toolbar extends Module<ToolbarNodes> {
351
361
  return;
352
362
  }
353
363
 
364
+ /**
365
+ * Suppress toolbar buttons when the block is inside a table cell.
366
+ * The toolbar still positions itself for toolbox/slash-search purposes,
367
+ * but plus button and settings toggler remain hidden.
368
+ */
369
+ if (this.hoveredBlockIsFromTableCell) {
370
+ plusButton.style.display = 'none';
371
+
372
+ if (settingsToggler) {
373
+ settingsToggler.style.display = 'none';
374
+ }
375
+ } else {
376
+ plusButton.style.display = '';
377
+
378
+ if (settingsToggler) {
379
+ settingsToggler.style.display = '';
380
+ }
381
+ }
382
+
354
383
  const targetBlockHolder = targetBlock.holder;
355
384
  const { isMobile } = this.Blok.UI;
356
385
 
@@ -521,18 +550,24 @@ export class Toolbar extends Module<ToolbarNodes> {
521
550
  * to prevent toolbar from reopening on subsequent block-hovered events
522
551
  */
523
552
  this.hoveredBlock = null;
553
+ this.hoveredBlockIsFromTableCell = false;
524
554
  // Only set explicitlyClosed if not explicitly disabled (e.g., when called from toolbox after block insertion)
525
555
  if (options?.setExplicitlyClosed !== false) {
526
556
  this.explicitlyClosed = true;
527
557
  }
528
558
 
529
559
  /**
530
- * Restore plus button visibility in case it was hidden by other interactions
560
+ * Restore plus button and settings toggler visibility
561
+ * in case they were hidden for table cell blocks
531
562
  */
532
563
  if (this.nodes.plusButton) {
533
564
  this.nodes.plusButton.style.display = '';
534
565
  }
535
566
 
567
+ if (this.nodes.settingsToggler) {
568
+ this.nodes.settingsToggler.style.display = '';
569
+ }
570
+
536
571
  /**
537
572
  * Reset the content offset transform
538
573
  */
@@ -566,9 +601,9 @@ export class Toolbar extends Module<ToolbarNodes> {
566
601
  * Uses the DOM attribute directly to avoid cross-module dependency on the table tool.
567
602
  *
568
603
  * @param block - the block to resolve
569
- * @returns the parent table block if inside a cell, the original block otherwise, or null if resolution fails
604
+ * @returns the parent table block if inside a cell, the original block otherwise
570
605
  */
571
- private resolveTableCellBlock(block: Block): Block | null {
606
+ private resolveTableCellBlock(block: Block): Block {
572
607
  const cellBlocksContainer = block.holder.closest('[data-blok-table-cell-blocks]');
573
608
 
574
609
  if (!cellBlocksContainer) {
@@ -578,10 +613,10 @@ export class Toolbar extends Module<ToolbarNodes> {
578
613
  const tableBlockHolder = cellBlocksContainer.closest('[data-blok-testid="block-wrapper"]');
579
614
 
580
615
  if (!tableBlockHolder) {
581
- return null;
616
+ return block;
582
617
  }
583
618
 
584
- return this.Blok.BlockManager.getBlockByChildNode(tableBlockHolder) ?? null;
619
+ return this.Blok.BlockManager.getBlockByChildNode(tableBlockHolder) ?? block;
585
620
  }
586
621
 
587
622
  /**
@@ -173,12 +173,33 @@ export class PlusButtonHandler {
173
173
  const hoveredBlockIndex = hoveredBlock !== null
174
174
  ? BlockManager.getBlockIndex(hoveredBlock)
175
175
  : BlockManager.currentBlockIndex;
176
- const insertIndex = insertAbove ? hoveredBlockIndex : hoveredBlockIndex + 1;
176
+ const baseInsertIndex = insertAbove ? hoveredBlockIndex : hoveredBlockIndex + 1;
177
+
178
+ // When inserting below, skip past any blocks nested inside another block's
179
+ // DOM (e.g. paragraph blocks inside table cells). The block array may
180
+ // interleave nested blocks from multiple parents, so check whether each
181
+ // block's holder lives inside any block-wrapper ancestor rather than only
182
+ // the hovered block's holder.
183
+ const blocksAfterInsert = BlockManager.blocks.slice(baseInsertIndex);
184
+ const isNested = (block: Block): boolean =>
185
+ block.holder.parentElement?.closest('[data-blok-testid="block-wrapper"]') !== null;
186
+ const firstNonNestedOffset = !insertAbove && hoveredBlock && blocksAfterInsert.length > 0
187
+ ? blocksAfterInsert.findIndex((block) => !isNested(block))
188
+ : 0;
189
+ const insertIndex = baseInsertIndex + (firstNonNestedOffset === -1 ? blocksAfterInsert.length : firstNonNestedOffset);
177
190
 
178
191
  const targetBlock = isEmptyParagraph || startsWithSlash
179
192
  ? hoveredBlock
180
193
  : BlockManager.insertDefaultBlockAtIndex(insertIndex, true);
181
194
 
195
+ // The DOM insertion may place the new block's holder inside a nested
196
+ // container (e.g. a table cell) because the previous block in the array
197
+ // is inside another block's DOM. Move the holder to be a sibling after
198
+ // the hovered block so it becomes a top-level block.
199
+ if (targetBlock !== hoveredBlock && isNested(targetBlock)) {
200
+ hoveredBlock?.holder.after(targetBlock.holder);
201
+ }
202
+
182
203
  // Insert "/" or position caret after existing one
183
204
  if (startsWithSlash) {
184
205
  Caret.setToBlock(targetBlock, Caret.positions.DEFAULT, 1);
@@ -189,6 +189,29 @@ export class YjsManager extends Module {
189
189
  this.undoHistory.redo();
190
190
  }
191
191
 
192
+ /**
193
+ * Check if undo is available.
194
+ * @returns true if undo is available
195
+ */
196
+ public canUndo(): boolean {
197
+ return this.undoHistory.canUndo();
198
+ }
199
+
200
+ /**
201
+ * Check if redo is available.
202
+ * @returns true if redo is available
203
+ */
204
+ public canRedo(): boolean {
205
+ return this.undoHistory.canRedo();
206
+ }
207
+
208
+ /**
209
+ * Clear all history.
210
+ */
211
+ public clear(): void {
212
+ this.undoHistory.clear();
213
+ }
214
+
192
215
  /**
193
216
  * Stop capturing changes into current undo group.
194
217
  * Call this to force next change into a new undo entry.