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

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 (233) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-DK-97ZTf.mjs → blok-XA2L0_-K.mjs} +1216 -1178
  3. package/dist/chunks/{i18next-loader-CRollibS.mjs → i18next-loader-Ci2zhA-n.mjs} +1 -1
  4. package/dist/chunks/{index-jgHmMDND.mjs → index-R38OiQ_d.mjs} +1 -1
  5. package/dist/chunks/{inline-tool-convert-BIwvipPw.mjs → inline-tool-convert-e3PyuxB6.mjs} +88 -73
  6. package/dist/chunks/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
  7. package/dist/{messages-BPqWKx5Z.mjs → chunks/messages-49thXXGz.mjs} +16 -2
  8. package/dist/{messages-LvFKBBPa.mjs → chunks/messages-7W4d0DwD.mjs} +15 -1
  9. package/dist/{messages-Bn253WWC.mjs → chunks/messages-9SihnaXQ.mjs} +14 -0
  10. package/dist/{messages-Bf6Y3_GI.mjs → chunks/messages-B1Aww8q7.mjs} +16 -2
  11. package/dist/{messages-D00OjS2n.mjs → chunks/messages-B9fe4dQJ.mjs} +15 -1
  12. package/dist/{messages-pA5TvcAj.mjs → chunks/messages-BB5z9Uba.mjs} +14 -0
  13. package/dist/chunks/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
  14. package/dist/chunks/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
  15. package/dist/chunks/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
  16. package/dist/chunks/{messages-CQj2JU2j.mjs → messages-BGxiFoZf.mjs} +15 -1
  17. package/dist/{messages-B5puUm7R.mjs → chunks/messages-BL0tXcDf.mjs} +15 -1
  18. package/dist/{messages-CyDU5lz9.mjs → chunks/messages-BMv4xwIr.mjs} +16 -2
  19. package/dist/chunks/{messages-CkFT2gle.mjs → messages-BNe6LuHW.mjs} +14 -0
  20. package/dist/chunks/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
  21. package/dist/chunks/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
  22. package/dist/{messages-CXHd9SUK.mjs → chunks/messages-BYyy6Wqf.mjs} +14 -0
  23. package/dist/{messages-Cyi2AMmz.mjs → chunks/messages-Bfiw5w_W.mjs} +16 -2
  24. package/dist/chunks/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
  25. package/dist/chunks/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
  26. package/dist/chunks/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
  27. package/dist/chunks/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
  28. package/dist/chunks/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
  29. package/dist/chunks/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
  30. package/dist/{messages-CrsJ1TEJ.mjs → chunks/messages-CLhcMlTc.mjs} +15 -1
  31. package/dist/{messages-CnvW8Slp.mjs → chunks/messages-CMkNSDTo.mjs} +17 -3
  32. package/dist/chunks/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
  33. package/dist/chunks/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
  34. package/dist/chunks/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
  35. package/dist/chunks/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
  36. package/dist/{messages-Dg92dXZ5.mjs → chunks/messages-CvGLfqmV.mjs} +14 -0
  37. package/dist/{messages-AHESHJm_.mjs → chunks/messages-CzTufCHu.mjs} +14 -0
  38. package/dist/chunks/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
  39. package/dist/chunks/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
  40. package/dist/chunks/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
  41. package/dist/chunks/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
  42. package/dist/chunks/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
  43. package/dist/{messages-zSzDzXej.mjs → chunks/messages-DBRw-7Zc.mjs} +16 -2
  44. package/dist/chunks/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
  45. package/dist/{messages-DOlC_Tty.mjs → chunks/messages-DChXyvh2.mjs} +15 -1
  46. package/dist/chunks/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
  47. package/dist/{messages-DDLgIPDF.mjs → chunks/messages-DLfR5bMd.mjs} +16 -2
  48. package/dist/{messages-CbhuIWRJ.mjs → chunks/messages-DT4dP5uK.mjs} +15 -1
  49. package/dist/chunks/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
  50. package/dist/{messages-BlpqL8vG.mjs → chunks/messages-DjvaFRqx.mjs} +16 -2
  51. package/dist/{messages-BA0rcTCY.mjs → chunks/messages-DnIhyAJk.mjs} +18 -4
  52. package/dist/chunks/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
  53. package/dist/chunks/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
  54. package/dist/{messages-CJoBtXU6.mjs → chunks/messages-DqM1LFg5.mjs} +14 -0
  55. package/dist/chunks/{messages-CySyfkMU.mjs → messages-DsVNtdgM.mjs} +15 -1
  56. package/dist/chunks/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
  57. package/dist/chunks/{messages-Cu08aLS3.mjs → messages-EL5ARzmK.mjs} +16 -2
  58. package/dist/chunks/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
  59. package/dist/{messages-DY94ykcE.mjs → chunks/messages-LPVfA-8K.mjs} +14 -0
  60. package/dist/{messages-Cr-RJ7YB.mjs → chunks/messages-O5tQus_0.mjs} +14 -0
  61. package/dist/chunks/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
  62. package/dist/chunks/{messages-GC2PhgV3.mjs → messages-QtoE8uEv.mjs} +15 -1
  63. package/dist/chunks/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
  64. package/dist/chunks/{messages-zS1AXZ0y.mjs → messages-UX4gkere.mjs} +15 -1
  65. package/dist/{messages-CUy1vn-b.mjs → chunks/messages-Xq8UmkVs.mjs} +14 -0
  66. package/dist/chunks/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
  67. package/dist/chunks/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
  68. package/dist/chunks/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
  69. package/dist/chunks/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
  70. package/dist/chunks/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
  71. package/dist/chunks/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
  72. package/dist/{messages-BeUhMpsr.mjs → chunks/messages-wp_1b1hD.mjs} +15 -1
  73. package/dist/full.mjs +2 -2
  74. package/dist/locales.mjs +102 -88
  75. package/dist/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
  76. package/dist/{chunks/messages-BPqWKx5Z.mjs → messages-49thXXGz.mjs} +16 -2
  77. package/dist/{chunks/messages-LvFKBBPa.mjs → messages-7W4d0DwD.mjs} +15 -1
  78. package/dist/{chunks/messages-Bn253WWC.mjs → messages-9SihnaXQ.mjs} +14 -0
  79. package/dist/{chunks/messages-Bf6Y3_GI.mjs → messages-B1Aww8q7.mjs} +16 -2
  80. package/dist/{chunks/messages-D00OjS2n.mjs → messages-B9fe4dQJ.mjs} +15 -1
  81. package/dist/{chunks/messages-pA5TvcAj.mjs → messages-BB5z9Uba.mjs} +14 -0
  82. package/dist/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
  83. package/dist/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
  84. package/dist/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
  85. package/dist/{messages-CQj2JU2j.mjs → messages-BGxiFoZf.mjs} +15 -1
  86. package/dist/{chunks/messages-B5puUm7R.mjs → messages-BL0tXcDf.mjs} +15 -1
  87. package/dist/{chunks/messages-CyDU5lz9.mjs → messages-BMv4xwIr.mjs} +16 -2
  88. package/dist/{messages-CkFT2gle.mjs → messages-BNe6LuHW.mjs} +14 -0
  89. package/dist/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
  90. package/dist/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
  91. package/dist/{chunks/messages-CXHd9SUK.mjs → messages-BYyy6Wqf.mjs} +14 -0
  92. package/dist/{chunks/messages-Cyi2AMmz.mjs → messages-Bfiw5w_W.mjs} +16 -2
  93. package/dist/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
  94. package/dist/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
  95. package/dist/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
  96. package/dist/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
  97. package/dist/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
  98. package/dist/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
  99. package/dist/{chunks/messages-CrsJ1TEJ.mjs → messages-CLhcMlTc.mjs} +15 -1
  100. package/dist/{chunks/messages-CnvW8Slp.mjs → messages-CMkNSDTo.mjs} +17 -3
  101. package/dist/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
  102. package/dist/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
  103. package/dist/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
  104. package/dist/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
  105. package/dist/{chunks/messages-Dg92dXZ5.mjs → messages-CvGLfqmV.mjs} +14 -0
  106. package/dist/{chunks/messages-AHESHJm_.mjs → messages-CzTufCHu.mjs} +14 -0
  107. package/dist/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
  108. package/dist/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
  109. package/dist/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
  110. package/dist/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
  111. package/dist/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
  112. package/dist/{chunks/messages-zSzDzXej.mjs → messages-DBRw-7Zc.mjs} +16 -2
  113. package/dist/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
  114. package/dist/{chunks/messages-DOlC_Tty.mjs → messages-DChXyvh2.mjs} +15 -1
  115. package/dist/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
  116. package/dist/{chunks/messages-DDLgIPDF.mjs → messages-DLfR5bMd.mjs} +16 -2
  117. package/dist/{chunks/messages-CbhuIWRJ.mjs → messages-DT4dP5uK.mjs} +15 -1
  118. package/dist/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
  119. package/dist/{chunks/messages-BlpqL8vG.mjs → messages-DjvaFRqx.mjs} +16 -2
  120. package/dist/{chunks/messages-BA0rcTCY.mjs → messages-DnIhyAJk.mjs} +18 -4
  121. package/dist/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
  122. package/dist/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
  123. package/dist/{chunks/messages-CJoBtXU6.mjs → messages-DqM1LFg5.mjs} +14 -0
  124. package/dist/{messages-CySyfkMU.mjs → messages-DsVNtdgM.mjs} +15 -1
  125. package/dist/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
  126. package/dist/{messages-Cu08aLS3.mjs → messages-EL5ARzmK.mjs} +16 -2
  127. package/dist/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
  128. package/dist/{chunks/messages-DY94ykcE.mjs → messages-LPVfA-8K.mjs} +14 -0
  129. package/dist/{chunks/messages-Cr-RJ7YB.mjs → messages-O5tQus_0.mjs} +14 -0
  130. package/dist/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
  131. package/dist/{messages-GC2PhgV3.mjs → messages-QtoE8uEv.mjs} +15 -1
  132. package/dist/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
  133. package/dist/{messages-zS1AXZ0y.mjs → messages-UX4gkere.mjs} +15 -1
  134. package/dist/{chunks/messages-CUy1vn-b.mjs → messages-Xq8UmkVs.mjs} +14 -0
  135. package/dist/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
  136. package/dist/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
  137. package/dist/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
  138. package/dist/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
  139. package/dist/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
  140. package/dist/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
  141. package/dist/{chunks/messages-BeUhMpsr.mjs → messages-wp_1b1hD.mjs} +15 -1
  142. package/dist/tools.mjs +854 -719
  143. package/package.json +5 -9
  144. package/src/components/i18n/locales/am/messages.json +15 -1
  145. package/src/components/i18n/locales/ar/messages.json +14 -0
  146. package/src/components/i18n/locales/az/messages.json +14 -0
  147. package/src/components/i18n/locales/bg/messages.json +14 -0
  148. package/src/components/i18n/locales/bn/messages.json +15 -1
  149. package/src/components/i18n/locales/bs/messages.json +15 -1
  150. package/src/components/i18n/locales/cs/messages.json +14 -0
  151. package/src/components/i18n/locales/da/messages.json +14 -0
  152. package/src/components/i18n/locales/de/messages.json +14 -0
  153. package/src/components/i18n/locales/dv/messages.json +15 -1
  154. package/src/components/i18n/locales/el/messages.json +15 -1
  155. package/src/components/i18n/locales/en/messages.json +14 -0
  156. package/src/components/i18n/locales/es/messages.json +14 -0
  157. package/src/components/i18n/locales/et/messages.json +14 -0
  158. package/src/components/i18n/locales/fa/messages.json +15 -1
  159. package/src/components/i18n/locales/fi/messages.json +15 -1
  160. package/src/components/i18n/locales/fil/messages.json +14 -0
  161. package/src/components/i18n/locales/fr/messages.json +15 -1
  162. package/src/components/i18n/locales/gu/messages.json +15 -1
  163. package/src/components/i18n/locales/he/messages.json +14 -0
  164. package/src/components/i18n/locales/hi/messages.json +15 -1
  165. package/src/components/i18n/locales/hr/messages.json +14 -0
  166. package/src/components/i18n/locales/hu/messages.json +14 -0
  167. package/src/components/i18n/locales/hy/messages.json +16 -2
  168. package/src/components/i18n/locales/id/messages.json +16 -2
  169. package/src/components/i18n/locales/it/messages.json +14 -0
  170. package/src/components/i18n/locales/ja/messages.json +14 -0
  171. package/src/components/i18n/locales/ka/messages.json +15 -1
  172. package/src/components/i18n/locales/km/messages.json +16 -2
  173. package/src/components/i18n/locales/kn/messages.json +16 -2
  174. package/src/components/i18n/locales/ko/messages.json +14 -0
  175. package/src/components/i18n/locales/ku/messages.json +16 -2
  176. package/src/components/i18n/locales/lo/messages.json +15 -1
  177. package/src/components/i18n/locales/lt/messages.json +15 -1
  178. package/src/components/i18n/locales/lv/messages.json +15 -1
  179. package/src/components/i18n/locales/mk/messages.json +16 -2
  180. package/src/components/i18n/locales/ml/messages.json +16 -2
  181. package/src/components/i18n/locales/mn/messages.json +16 -2
  182. package/src/components/i18n/locales/mr/messages.json +15 -1
  183. package/src/components/i18n/locales/ms/messages.json +16 -2
  184. package/src/components/i18n/locales/my/messages.json +16 -2
  185. package/src/components/i18n/locales/ne/messages.json +15 -1
  186. package/src/components/i18n/locales/nl/messages.json +15 -1
  187. package/src/components/i18n/locales/no/messages.json +16 -2
  188. package/src/components/i18n/locales/pa/messages.json +15 -1
  189. package/src/components/i18n/locales/pl/messages.json +14 -0
  190. package/src/components/i18n/locales/ps/messages.json +17 -3
  191. package/src/components/i18n/locales/pt/messages.json +14 -0
  192. package/src/components/i18n/locales/ro/messages.json +15 -1
  193. package/src/components/i18n/locales/ru/messages.json +14 -0
  194. package/src/components/i18n/locales/sd/messages.json +16 -2
  195. package/src/components/i18n/locales/si/messages.json +15 -1
  196. package/src/components/i18n/locales/sk/messages.json +15 -1
  197. package/src/components/i18n/locales/sl/messages.json +16 -2
  198. package/src/components/i18n/locales/sq/messages.json +16 -2
  199. package/src/components/i18n/locales/sr/messages.json +16 -2
  200. package/src/components/i18n/locales/sv/messages.json +16 -2
  201. package/src/components/i18n/locales/sw/messages.json +16 -2
  202. package/src/components/i18n/locales/ta/messages.json +16 -2
  203. package/src/components/i18n/locales/te/messages.json +15 -1
  204. package/src/components/i18n/locales/th/messages.json +15 -1
  205. package/src/components/i18n/locales/tr/messages.json +15 -1
  206. package/src/components/i18n/locales/ug/messages.json +16 -2
  207. package/src/components/i18n/locales/uk/messages.json +15 -1
  208. package/src/components/i18n/locales/ur/messages.json +15 -1
  209. package/src/components/i18n/locales/vi/messages.json +16 -2
  210. package/src/components/i18n/locales/yi/messages.json +16 -2
  211. package/src/components/i18n/locales/zh/messages.json +15 -1
  212. package/src/components/modules/api/blocks.ts +17 -2
  213. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +12 -1
  214. package/src/components/modules/toolbar/index.ts +29 -7
  215. package/src/components/modules/ui.ts +46 -68
  216. package/src/components/modules/uiControllers/controllers/blockHover.ts +40 -61
  217. package/src/components/ui/toolbox.ts +41 -6
  218. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +3 -1
  219. package/src/components/utils/popover/popover-desktop.ts +27 -8
  220. package/src/tools/table/index.ts +67 -70
  221. package/src/tools/table/table-add-controls.ts +25 -7
  222. package/src/tools/table/table-cell-blocks.ts +62 -0
  223. package/src/tools/table/table-cell-selection.ts +70 -46
  224. package/src/tools/table/table-core.ts +20 -15
  225. package/src/tools/table/table-grip-visuals.ts +4 -4
  226. package/src/tools/table/table-operations.ts +22 -12
  227. package/src/tools/table/table-restrictions.ts +64 -0
  228. package/src/tools/table/table-row-col-action-handler.ts +190 -0
  229. package/src/tools/table/table-row-col-controls.ts +91 -182
  230. package/src/tools/table/table-row-col-drag.ts +4 -4
  231. package/src/tools/table/table-row-col-popover.ts +225 -0
  232. package/src/tools/table/types.ts +2 -0
  233. package/types/utils/popover/popover.d.ts +7 -0
@@ -18,7 +18,7 @@ import { TableGrid } from './table-core';
18
18
  import {
19
19
  applyPixelWidths,
20
20
  computeHalfAvgWidth,
21
- computeInsertColumnWidths,
21
+ computeInitialColWidth,
22
22
  deleteColumnWithBlockCleanup,
23
23
  deleteRowWithBlockCleanup,
24
24
  enableScrollOverflow,
@@ -30,14 +30,14 @@ import {
30
30
  normalizeTableData,
31
31
  populateNewCells,
32
32
  readPixelWidths,
33
- redistributePercentWidths,
34
33
  SCROLL_OVERFLOW_CLASSES,
35
34
  setupKeyboardNavigation,
36
- syncColWidthsAfterMove,
37
35
  updateHeadingColumnStyles,
38
36
  updateHeadingStyles,
39
37
  } from './table-operations';
40
38
  import { TableResize } from './table-resize';
39
+ import { executeRowColAction } from './table-row-col-action-handler';
40
+ import type { PendingHighlight } from './table-row-col-action-handler';
41
41
  import { TableRowColControls } from './table-row-col-controls';
42
42
  import type { RowColAction } from './table-row-col-controls';
43
43
  import type { TableData, TableConfig } from './types';
@@ -71,7 +71,7 @@ export class Table implements BlockTool {
71
71
  private cellSelection: TableCellSelection | null = null;
72
72
  private element: HTMLDivElement | null = null;
73
73
  private blockId: string | undefined;
74
- private pendingHighlight: { type: 'row' | 'col'; index: number } | null = null;
74
+ private pendingHighlight: PendingHighlight | null = null;
75
75
 
76
76
  constructor({ data, config, api, readOnly, block }: BlockToolConstructorOptions<TableData, TableConfig>) {
77
77
  this.api = api;
@@ -178,6 +178,14 @@ export class Table implements BlockTool {
178
178
 
179
179
  this.data.content = this.cellBlocks?.initializeCells(this.data.content) ?? this.data.content;
180
180
 
181
+ if (this.data.initialColWidth === undefined) {
182
+ const widths = this.data.colWidths ?? readPixelWidths(gridEl);
183
+
184
+ this.data.initialColWidth = widths.length > 0
185
+ ? computeInitialColWidth(widths)
186
+ : undefined;
187
+ }
188
+
181
189
  this.initResize(gridEl);
182
190
  this.initAddControls(gridEl);
183
191
  this.initRowColControls(gridEl);
@@ -197,6 +205,7 @@ export class Table implements BlockTool {
197
205
  stretched: this.data.stretched,
198
206
  content,
199
207
  ...(colWidths ? { colWidths } : {}),
208
+ ...(this.data.initialColWidth !== undefined ? { initialColWidth: this.data.initialColWidth } : {}),
200
209
  };
201
210
  }
202
211
 
@@ -252,6 +261,8 @@ export class Table implements BlockTool {
252
261
  }
253
262
 
254
263
  public destroy(): void {
264
+ this.cellBlocks?.deleteAllBlocks();
265
+
255
266
  this.resize?.destroy();
256
267
  this.resize = null;
257
268
  this.addControls?.destroy();
@@ -296,9 +307,12 @@ export class Table implements BlockTool {
296
307
  return;
297
308
  }
298
309
 
310
+ const dragState = { addedCols: 0 };
311
+
299
312
  this.addControls = new TableAddControls({
300
313
  wrapper: this.element,
301
314
  grid: gridEl,
315
+ i18n: this.api.i18n,
302
316
  onAddRow: () => {
303
317
  this.grid.addRow(gridEl);
304
318
  populateNewCells(gridEl, this.cellBlocks);
@@ -310,10 +324,12 @@ export class Table implements BlockTool {
310
324
  },
311
325
  onAddColumn: () => {
312
326
  const colWidths = this.data.colWidths ?? readPixelWidths(gridEl);
313
- const halfAvgWidth = computeHalfAvgWidth(colWidths);
327
+ const halfWidth = this.data.initialColWidth !== undefined
328
+ ? Math.round((this.data.initialColWidth / 2) * 100) / 100
329
+ : computeHalfAvgWidth(colWidths);
314
330
 
315
- this.grid.addColumn(gridEl, undefined, colWidths);
316
- this.data.colWidths = [...colWidths, halfAvgWidth];
331
+ this.grid.addColumn(gridEl, undefined, colWidths, halfWidth);
332
+ this.data.colWidths = [...colWidths, halfWidth];
317
333
  populateNewCells(gridEl, this.cellBlocks);
318
334
  updateHeadingColumnStyles(this.element, this.data.withHeadingColumn);
319
335
  this.initResize(gridEl);
@@ -342,14 +358,22 @@ export class Table implements BlockTool {
342
358
  },
343
359
  onDragAddCol: () => {
344
360
  const colWidths = this.data.colWidths ?? readPixelWidths(gridEl);
345
- const halfAvgWidth = computeHalfAvgWidth(colWidths);
361
+ const halfWidth = this.data.initialColWidth !== undefined
362
+ ? Math.round((this.data.initialColWidth / 2) * 100) / 100
363
+ : computeHalfAvgWidth(colWidths);
346
364
 
347
- this.grid.addColumn(gridEl, undefined, colWidths);
348
- this.data.colWidths = [...colWidths, halfAvgWidth];
365
+ this.grid.addColumn(gridEl, undefined, colWidths, halfWidth);
366
+ this.data.colWidths = [...colWidths, halfWidth];
349
367
  applyPixelWidths(gridEl, this.data.colWidths);
350
368
  populateNewCells(gridEl, this.cellBlocks);
351
369
  updateHeadingColumnStyles(this.element, this.data.withHeadingColumn);
352
370
  this.initResize(gridEl);
371
+
372
+ dragState.addedCols++;
373
+
374
+ if (this.element) {
375
+ this.element.scrollLeft = this.element.scrollWidth;
376
+ }
353
377
  },
354
378
  onDragRemoveCol: () => {
355
379
  const colCount = this.grid.getColumnCount(gridEl);
@@ -365,6 +389,8 @@ export class Table implements BlockTool {
365
389
  }
366
390
 
367
391
  this.initResize(gridEl);
392
+
393
+ dragState.addedCols--;
368
394
  },
369
395
  onDragEnd: () => {
370
396
  this.initResize(gridEl);
@@ -372,8 +398,10 @@ export class Table implements BlockTool {
372
398
  this.rowColControls?.refresh();
373
399
 
374
400
  if (this.element) {
375
- this.element.scrollLeft = 0;
401
+ this.element.scrollLeft = dragState.addedCols > 0 ? this.element.scrollWidth : 0;
376
402
  }
403
+
404
+ dragState.addedCols = 0;
377
405
  },
378
406
  });
379
407
  }
@@ -391,6 +419,7 @@ export class Table implements BlockTool {
391
419
  getRowCount: () => this.grid.getRowCount(gridEl),
392
420
  isHeadingRow: () => this.data.withHeadings,
393
421
  isHeadingColumn: () => this.data.withHeadingColumn,
422
+ i18n: this.api.i18n,
394
423
  onAction: (action: RowColAction) => this.handleRowColAction(gridEl, action),
395
424
  onDragStateChange: (isDragging: boolean) => {
396
425
  if (this.resize) {
@@ -434,72 +463,37 @@ export class Table implements BlockTool {
434
463
  }
435
464
 
436
465
  private handleRowColAction(gridEl: HTMLElement, action: RowColAction): void {
437
- switch (action.type) {
438
- case 'insert-row-above':
439
- this.grid.addRow(gridEl, action.index);
440
- populateNewCells(gridEl, this.cellBlocks);
441
- this.pendingHighlight = { type: 'row', index: action.index };
442
- break;
443
- case 'insert-row-below':
444
- this.grid.addRow(gridEl, action.index + 1);
445
- populateNewCells(gridEl, this.cellBlocks);
446
- this.pendingHighlight = { type: 'row', index: action.index + 1 };
447
- break;
448
- case 'insert-col-left':
449
- this.data.colWidths = computeInsertColumnWidths(gridEl, action.index, this.data, this.grid);
450
- populateNewCells(gridEl, this.cellBlocks);
451
- this.pendingHighlight = { type: 'col', index: action.index };
452
- break;
453
- case 'insert-col-right':
454
- this.data.colWidths = computeInsertColumnWidths(gridEl, action.index + 1, this.data, this.grid);
455
- populateNewCells(gridEl, this.cellBlocks);
456
- this.pendingHighlight = { type: 'col', index: action.index + 1 };
457
- break;
458
- case 'move-row':
459
- this.grid.moveRow(gridEl, action.fromIndex, action.toIndex);
460
- break;
461
- case 'move-col':
462
- this.grid.moveColumn(gridEl, action.fromIndex, action.toIndex);
463
- this.data.colWidths = syncColWidthsAfterMove(this.data.colWidths, action.fromIndex, action.toIndex);
464
- break;
465
- case 'delete-row': {
466
- deleteRowWithBlockCleanup(gridEl, action.index, this.grid, this.cellBlocks);
467
- const newRowCount = this.grid.getRowCount(gridEl);
468
- const neighborRow = action.index < newRowCount ? action.index : action.index - 1;
469
-
470
- this.pendingHighlight = { type: 'row', index: neighborRow };
471
- break;
472
- }
473
- case 'delete-col': {
474
- this.data.colWidths = deleteColumnWithBlockCleanup(gridEl, action.index, this.data.colWidths, this.grid, this.cellBlocks);
475
-
476
- if (this.data.colWidths) {
477
- applyPixelWidths(gridEl, this.data.colWidths);
478
- } else {
479
- redistributePercentWidths(gridEl);
480
- }
481
-
482
- const newColCount = this.grid.getColumnCount(gridEl);
483
- const neighborCol = action.index < newColCount ? action.index : action.index - 1;
466
+ const result = executeRowColAction(
467
+ gridEl,
468
+ action,
469
+ { grid: this.grid, data: this.data, cellBlocks: this.cellBlocks },
470
+ );
484
471
 
485
- this.pendingHighlight = { type: 'col', index: neighborCol };
486
- break;
487
- }
488
- case 'toggle-heading':
489
- this.data.withHeadings = !this.data.withHeadings;
490
- this.pendingHighlight = { type: 'row', index: 0 };
491
- break;
492
- case 'toggle-heading-column':
493
- this.data.withHeadingColumn = !this.data.withHeadingColumn;
494
- this.pendingHighlight = { type: 'col', index: 0 };
495
- break;
496
- }
472
+ this.data.colWidths = result.colWidths;
473
+ this.data.withHeadings = result.withHeadings;
474
+ this.data.withHeadingColumn = result.withHeadingColumn;
475
+ this.pendingHighlight = result.pendingHighlight;
497
476
 
498
477
  updateHeadingStyles(this.element, this.data.withHeadings);
499
478
  updateHeadingColumnStyles(this.element, this.data.withHeadingColumn);
500
479
  this.initResize(gridEl);
501
480
  this.addControls?.syncRowButtonWidth();
502
481
  this.rowColControls?.refresh();
482
+
483
+ if (!result.moveSelection) {
484
+ return;
485
+ }
486
+
487
+ // After move operations, select the moved row/column to show where it landed
488
+ const { type: moveType, index: moveIndex } = result.moveSelection;
489
+
490
+ if (moveType === 'row') {
491
+ this.cellSelection?.selectRow(moveIndex);
492
+ } else {
493
+ this.cellSelection?.selectColumn(moveIndex);
494
+ }
495
+
496
+ this.rowColControls?.setActiveGrip(moveType, moveIndex);
503
497
  }
504
498
 
505
499
  private initResize(gridEl: HTMLElement): void {
@@ -547,6 +541,7 @@ export class Table implements BlockTool {
547
541
  this.cellSelection = new TableCellSelection({
548
542
  grid: gridEl,
549
543
  rectangleSelection, // Pass reference
544
+ i18n: this.api.i18n,
550
545
  onSelectionActiveChange: (hasSelection) => {
551
546
  if (this.resize) {
552
547
  this.resize.enabled = !hasSelection;
@@ -567,3 +562,5 @@ export class Table implements BlockTool {
567
562
  });
568
563
  }
569
564
  }
565
+
566
+ export { isInsideTableCell, isRestrictedInTableCell, convertToParagraph } from './table-restrictions';
@@ -1,3 +1,4 @@
1
+ import type { I18n } from '../../../types/api';
1
2
  import { IconPlus } from '../../components/icons';
2
3
  import { createTooltipContent } from '../../components/modules/toolbar/tooltip';
3
4
  import { hide as hideTooltip, onHover } from '../../components/utils/tooltip';
@@ -46,6 +47,7 @@ interface DragState {
46
47
  interface TableAddControlsOptions {
47
48
  wrapper: HTMLElement;
48
49
  grid: HTMLElement;
50
+ i18n: I18n;
49
51
  onAddRow: () => void;
50
52
  onAddColumn: () => void;
51
53
  onDragStart: () => void;
@@ -64,6 +66,7 @@ interface TableAddControlsOptions {
64
66
  export class TableAddControls {
65
67
  private wrapper: HTMLElement;
66
68
  private grid: HTMLElement;
69
+ private i18n: I18n;
67
70
  private addRowBtn: HTMLElement;
68
71
  private addColBtn: HTMLElement;
69
72
  private rowHideTimeout: ReturnType<typeof setTimeout> | null = null;
@@ -90,6 +93,7 @@ export class TableAddControls {
90
93
  constructor(options: TableAddControlsOptions) {
91
94
  this.wrapper = options.wrapper;
92
95
  this.grid = options.grid;
96
+ this.i18n = options.i18n;
93
97
 
94
98
  this.boundAddRowClick = options.onAddRow;
95
99
  this.boundAddColClick = options.onAddColumn;
@@ -151,10 +155,15 @@ export class TableAddControls {
151
155
  * Disables pointer events and hover effects during cell selection.
152
156
  */
153
157
  public setInteractive(interactive: boolean): void {
154
- const pointerEvents = interactive ? '' : 'none';
158
+ if (!interactive) {
159
+ this.addRowBtn.style.pointerEvents = 'none';
160
+ this.addColBtn.style.pointerEvents = 'none';
155
161
 
156
- this.addRowBtn.style.pointerEvents = pointerEvents;
157
- this.addColBtn.style.pointerEvents = pointerEvents;
162
+ return;
163
+ }
164
+
165
+ this.addRowBtn.style.pointerEvents = this.rowVisible ? '' : 'none';
166
+ this.addColBtn.style.pointerEvents = this.colVisible ? '' : 'none';
158
167
  }
159
168
 
160
169
  public destroy(): void {
@@ -314,6 +323,7 @@ export class TableAddControls {
314
323
 
315
324
  if (!this.rowVisible) {
316
325
  this.addRowBtn.style.opacity = '1';
326
+ this.addRowBtn.style.pointerEvents = '';
317
327
  this.rowVisible = true;
318
328
  }
319
329
  }
@@ -323,6 +333,7 @@ export class TableAddControls {
323
333
 
324
334
  if (!this.colVisible) {
325
335
  this.addColBtn.style.opacity = '1';
336
+ this.addColBtn.style.pointerEvents = '';
326
337
  this.colVisible = true;
327
338
  }
328
339
  }
@@ -334,6 +345,7 @@ export class TableAddControls {
334
345
 
335
346
  this.rowHideTimeout = setTimeout(() => {
336
347
  this.addRowBtn.style.opacity = '0';
348
+ this.addRowBtn.style.pointerEvents = 'none';
337
349
  this.rowVisible = false;
338
350
  this.rowHideTimeout = null;
339
351
  }, HIDE_DELAY_MS);
@@ -346,6 +358,7 @@ export class TableAddControls {
346
358
 
347
359
  this.colHideTimeout = setTimeout(() => {
348
360
  this.addColBtn.style.opacity = '0';
361
+ this.addColBtn.style.pointerEvents = 'none';
349
362
  this.colVisible = false;
350
363
  this.colHideTimeout = null;
351
364
  }, HIDE_DELAY_MS);
@@ -372,6 +385,10 @@ export class TableAddControls {
372
385
  btn.setAttribute(ADD_ROW_ATTR, '');
373
386
  btn.setAttribute('contenteditable', 'false');
374
387
  btn.style.opacity = '0';
388
+ btn.style.pointerEvents = 'none';
389
+ btn.style.position = 'absolute';
390
+ btn.style.left = '0';
391
+ btn.style.bottom = '-36px';
375
392
  btn.style.boxSizing = 'content-box';
376
393
  btn.style.width = '100%';
377
394
  btn.style.height = '32px';
@@ -388,8 +405,8 @@ export class TableAddControls {
388
405
  btn.appendChild(visual);
389
406
 
390
407
  onHover(btn, createTooltipContent([
391
- 'Click to add a new row',
392
- 'Drag to add or remove rows',
408
+ this.i18n.t('tools.table.clickToAddRow'),
409
+ this.i18n.t('tools.table.dragToAddRemoveRows'),
393
410
  ]), { placement: 'bottom', marginTop: -16 });
394
411
 
395
412
  return btn;
@@ -402,6 +419,7 @@ export class TableAddControls {
402
419
  btn.setAttribute(ADD_COL_ATTR, '');
403
420
  btn.setAttribute('contenteditable', 'false');
404
421
  btn.style.opacity = '0';
422
+ btn.style.pointerEvents = 'none';
405
423
  btn.style.position = 'absolute';
406
424
  btn.style.right = '-36px';
407
425
  btn.style.top = '0px';
@@ -418,8 +436,8 @@ export class TableAddControls {
418
436
  btn.appendChild(visual);
419
437
 
420
438
  onHover(btn, createTooltipContent([
421
- 'Click to add a new column',
422
- 'Drag to add or remove columns',
439
+ this.i18n.t('tools.table.clickToAddColumn'),
440
+ this.i18n.t('tools.table.dragToAddRemoveColumns'),
423
441
  ]), { placement: 'bottom' });
424
442
 
425
443
  return btn;
@@ -71,6 +71,7 @@ export class TableCellBlocks {
71
71
  this.onNavigateToCell = options.onNavigateToCell;
72
72
 
73
73
  this.api.events.on('block changed', this.handleBlockMutation);
74
+ this.gridElement.addEventListener('click', this.handleCellBlankSpaceClick);
74
75
  }
75
76
 
76
77
  /**
@@ -575,10 +576,71 @@ export class TableCellBlocks {
575
576
  });
576
577
  }
577
578
 
579
+ /**
580
+ * Delete all blocks managed by this table from the BlockManager.
581
+ * Called before the table block itself is removed to prevent orphaned cell blocks.
582
+ */
583
+ public deleteAllBlocks(): void {
584
+ const allCells = this.gridElement.querySelectorAll(`[${CELL_ATTR}]`);
585
+ const blockIds = this.getBlockIdsFromCells(allCells);
586
+
587
+ this.deleteBlocks(blockIds);
588
+ }
589
+
590
+ /**
591
+ * Handle clicks on blank cell space.
592
+ * When a click lands on the cell or blocks container (not on block content),
593
+ * set the caret to the end of the last block in that cell.
594
+ */
595
+ private handleCellBlankSpaceClick = (event: Event): void => {
596
+ const target = event.target as HTMLElement | null;
597
+
598
+ if (!target) {
599
+ return;
600
+ }
601
+
602
+ const isCell = target.hasAttribute(CELL_ATTR);
603
+ const isBlocksContainer = target.hasAttribute(CELL_BLOCKS_ATTR);
604
+
605
+ if (!isCell && !isBlocksContainer) {
606
+ return;
607
+ }
608
+
609
+ const cell = isCell ? target : target.closest<HTMLElement>(`[${CELL_ATTR}]`);
610
+
611
+ if (!cell) {
612
+ return;
613
+ }
614
+
615
+ const container = isCell
616
+ ? cell.querySelector<HTMLElement>(`[${CELL_BLOCKS_ATTR}]`)
617
+ : target;
618
+
619
+ if (!container) {
620
+ return;
621
+ }
622
+
623
+ const blockHolders = container.querySelectorAll('[data-blok-id]');
624
+ const lastHolder = blockHolders[blockHolders.length - 1];
625
+
626
+ if (!lastHolder) {
627
+ return;
628
+ }
629
+
630
+ const blockId = lastHolder.getAttribute('data-blok-id');
631
+
632
+ if (!blockId) {
633
+ return;
634
+ }
635
+
636
+ this.api.caret.setToBlock(blockId, 'end');
637
+ };
638
+
578
639
  /**
579
640
  * Clean up event listeners
580
641
  */
581
642
  destroy(): void {
643
+ this.gridElement.removeEventListener('click', this.handleCellBlankSpaceClick);
582
644
  this.api.events.off('block changed', this.handleBlockMutation);
583
645
  this._activeCellWithBlocks = null;
584
646
  this.cellsPendingCheck.clear();
@@ -1,3 +1,4 @@
1
+ import type { I18n } from '../../../types/api';
1
2
  import { IconCross } from '../../components/icons';
2
3
  import { PopoverDesktop } from '../../components/utils/popover';
3
4
  import { twMerge } from '../../components/utils/tw';
@@ -56,6 +57,7 @@ interface CellSelectionOptions {
56
57
  rectangleSelection?: { cancelActiveSelection: () => void };
57
58
  onSelectionActiveChange?: (hasSelection: boolean) => void;
58
59
  onClearContent?: (cells: HTMLElement[]) => void;
60
+ i18n: I18n;
59
61
  }
60
62
 
61
63
  export class TableCellSelection {
@@ -63,6 +65,7 @@ export class TableCellSelection {
63
65
  private rectangleSelection?: { cancelActiveSelection: () => void };
64
66
  private onSelectionActiveChange: ((hasSelection: boolean) => void) | undefined;
65
67
  private onClearContent: ((cells: HTMLElement[]) => void) | undefined;
68
+ private i18n: I18n;
66
69
  private anchorCell: CellCoord | null = null;
67
70
  private extentCell: CellCoord | null = null;
68
71
  private isSelecting = false;
@@ -77,12 +80,14 @@ export class TableCellSelection {
77
80
  private boundPointerUp: () => void;
78
81
  private boundClearSelection: (e: PointerEvent) => void;
79
82
  private boundCancelRectangle: (e: MouseEvent) => void;
83
+ private boundKeyDown: (e: KeyboardEvent) => void;
80
84
 
81
85
  constructor(options: CellSelectionOptions) {
82
86
  this.grid = options.grid;
83
87
  this.rectangleSelection = options.rectangleSelection;
84
88
  this.onSelectionActiveChange = options.onSelectionActiveChange;
85
89
  this.onClearContent = options.onClearContent;
90
+ this.i18n = options.i18n;
86
91
  this.grid.style.position = 'relative';
87
92
 
88
93
  this.boundPointerDown = this.handlePointerDown.bind(this);
@@ -90,8 +95,10 @@ export class TableCellSelection {
90
95
  this.boundPointerUp = this.handlePointerUp.bind(this);
91
96
  this.boundClearSelection = this.handleClearSelection.bind(this);
92
97
  this.boundCancelRectangle = this.handleCancelRectangle.bind(this);
98
+ this.boundKeyDown = this.handleKeyDown.bind(this);
93
99
 
94
100
  this.grid.addEventListener('pointerdown', this.boundPointerDown);
101
+ document.addEventListener('keydown', this.boundKeyDown);
95
102
  }
96
103
 
97
104
  public destroy(): void {
@@ -102,6 +109,7 @@ export class TableCellSelection {
102
109
  document.removeEventListener('pointerup', this.boundPointerUp);
103
110
  document.removeEventListener('pointerdown', this.boundClearSelection);
104
111
  document.removeEventListener('mousemove', this.boundCancelRectangle, true);
112
+ document.removeEventListener('keydown', this.boundKeyDown);
105
113
  }
106
114
 
107
115
  /**
@@ -276,6 +284,25 @@ export class TableCellSelection {
276
284
  this.clearSelection();
277
285
  }
278
286
 
287
+ private handleKeyDown(e: KeyboardEvent): void {
288
+ // Only trigger when selection is active
289
+ if (!this.hasSelection) {
290
+ return;
291
+ }
292
+
293
+ // Check for Delete or Backspace
294
+ if (e.key !== 'Delete' && e.key !== 'Backspace') {
295
+ return;
296
+ }
297
+
298
+ // Prevent default behavior
299
+ e.preventDefault();
300
+
301
+ // Clear content and dismiss selection
302
+ this.onClearContent?.([...this.selectedCells]);
303
+ this.clearSelection();
304
+ }
305
+
279
306
  private clearSelection(): void {
280
307
  const hadSelection = this.hasSelection;
281
308
 
@@ -348,26 +375,10 @@ export class TableCellSelection {
348
375
  const rows = this.grid.querySelectorAll(`[${ROW_ATTR}]`);
349
376
 
350
377
  // Mark selected cells
351
- for (let r = minRow; r <= maxRow; r++) {
352
- const row = rows[r];
353
-
354
- if (!row) {
355
- continue;
356
- }
357
-
358
- const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
359
-
360
- for (let c = minCol; c <= maxCol; c++) {
361
- const cell = cells[c] as HTMLElement | undefined;
362
-
363
- if (!cell) {
364
- continue;
365
- }
366
-
367
- cell.setAttribute(SELECTED_ATTR, '');
368
- this.selectedCells.push(cell);
369
- }
370
- }
378
+ this.selectedCells = this.collectCellsInRange(rows, minRow, maxRow, minCol, maxCol);
379
+ this.selectedCells.forEach(cell => {
380
+ cell.setAttribute(SELECTED_ATTR, '');
381
+ });
371
382
 
372
383
  // Calculate overlay position from bounding rects of corner cells
373
384
  const firstCell = rows[minRow]?.querySelectorAll(`[${CELL_ATTR}]`)[minCol] as HTMLElement | undefined;
@@ -388,16 +399,14 @@ export class TableCellSelection {
388
399
  const borderTop = parseFloat(gridStyle.borderTopWidth) || 0;
389
400
  const borderLeft = parseFloat(gridStyle.borderLeftWidth) || 0;
390
401
 
391
- let top = firstRect.top - gridRect.top - borderTop;
392
- let left = firstRect.left - gridRect.left - borderLeft;
393
402
  const width = lastRect.right - firstRect.left + 1;
394
403
  const height = lastRect.bottom - firstRect.top + 1;
395
404
 
396
405
  // Extend overlay 1px outward to cover adjacent borders:
397
406
  // grid border-top/border-left at row 0/col 0, or the previous
398
407
  // row's border-bottom / previous column's border-right otherwise.
399
- top -= 1;
400
- left -= 1;
408
+ const top = firstRect.top - gridRect.top - borderTop - 1;
409
+ const left = firstRect.left - gridRect.left - borderLeft - 1;
401
410
 
402
411
  // Create overlay once, reuse on subsequent paints
403
412
  if (!this.overlay) {
@@ -476,7 +485,7 @@ export class TableCellSelection {
476
485
  const items: PopoverItemParams[] = [
477
486
  {
478
487
  icon: IconCross,
479
- title: 'Clear',
488
+ title: this.i18n.t('tools.table.clearSelection'),
480
489
  closeOnActivate: true,
481
490
  onActivate: (): void => {
482
491
  this.onClearContent?.([...this.selectedCells]);
@@ -592,27 +601,8 @@ export class TableCellSelection {
592
601
  return;
593
602
  }
594
603
 
595
- // Clamp row
596
- let row: number;
597
-
598
- if (e.clientY < gridRect.top) {
599
- row = 0;
600
- } else if (e.clientY > gridRect.bottom) {
601
- row = rowCount - 1;
602
- } else {
603
- row = this.extentCell?.row ?? this.anchorCell.row;
604
- }
605
-
606
- // Clamp col
607
- let col: number;
608
-
609
- if (e.clientX < gridRect.left) {
610
- col = 0;
611
- } else if (e.clientX > gridRect.right) {
612
- col = colCount - 1;
613
- } else {
614
- col = this.extentCell?.col ?? this.anchorCell.col;
615
- }
604
+ const row = this.clampAxis(e.clientY, gridRect.top, gridRect.bottom, rowCount, this.extentCell?.row ?? this.anchorCell.row);
605
+ const col = this.clampAxis(e.clientX, gridRect.left, gridRect.right, colCount, this.extentCell?.col ?? this.anchorCell.col);
616
606
 
617
607
  const clamped = { row, col };
618
608
 
@@ -621,4 +611,38 @@ export class TableCellSelection {
621
611
  this.paintSelection();
622
612
  }
623
613
  }
614
+
615
+ private collectCellsInRange(
616
+ rows: NodeListOf<Element>,
617
+ minRow: number,
618
+ maxRow: number,
619
+ minCol: number,
620
+ maxCol: number,
621
+ ): HTMLElement[] {
622
+ return Array.from(rows)
623
+ .slice(minRow, maxRow + 1)
624
+ .flatMap(row => {
625
+ const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
626
+
627
+ return Array.from(cells)
628
+ .slice(minCol, maxCol + 1)
629
+ .filter((cell): cell is HTMLElement => cell instanceof HTMLElement);
630
+ });
631
+ }
632
+
633
+ /**
634
+ * Clamp a pointer coordinate to an axis range, returning the edge index
635
+ * when outside or the fallback when inside.
636
+ */
637
+ private clampAxis(pointer: number, min: number, max: number, count: number, fallback: number): number {
638
+ if (pointer < min) {
639
+ return 0;
640
+ }
641
+
642
+ if (pointer > max) {
643
+ return count - 1;
644
+ }
645
+
646
+ return fallback;
647
+ }
624
648
  }