@jackuait/blok 0.6.0-beta.9 → 0.6.0

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 (320) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-Bn6Q_o8h.mjs → blok-BAh1rvUC.mjs} +3410 -2927
  3. package/dist/chunks/i18next-B47TKgbU.mjs +1303 -0
  4. package/dist/chunks/{i18next-loader-DjR4d8M7.mjs → i18next-loader-CHtGO6IK.mjs} +2 -2
  5. package/dist/chunks/{index-oe38cp86.mjs → index-DBWWKrDe.mjs} +12 -12
  6. package/dist/chunks/{inline-tool-convert-SRTkyaZn.mjs → inline-tool-convert-DduRc0fF.mjs} +1467 -951
  7. package/dist/chunks/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
  8. package/dist/chunks/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
  9. package/dist/{messages-DnXLrlHh.mjs → chunks/messages-4v4MuVEc.mjs} +6 -0
  10. package/dist/chunks/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
  11. package/dist/chunks/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
  12. package/dist/chunks/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
  13. package/dist/{messages-uKX8WBaD.mjs → chunks/messages-96kNZDll.mjs} +6 -0
  14. package/dist/chunks/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
  15. package/dist/{messages-DBn76jVV.mjs → chunks/messages-B217znr-.mjs} +8 -2
  16. package/dist/{messages-DT4dP5uK.mjs → chunks/messages-B8WNljW3.mjs} +6 -0
  17. package/dist/chunks/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
  18. package/dist/{messages-CZygwLwM.mjs → chunks/messages-BI43k_BD.mjs} +6 -0
  19. package/dist/{messages-CzTufCHu.mjs → chunks/messages-BJ6zrz2j.mjs} +6 -0
  20. package/dist/{messages-BoJc_p1r.mjs → chunks/messages-BUl_Rcnj.mjs} +6 -0
  21. package/dist/chunks/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
  22. package/dist/{messages-C2htQ_3F.mjs → chunks/messages-BcpCubnC.mjs} +6 -0
  23. package/dist/{messages-D5C3J9qr.mjs → chunks/messages-Bm-E4iRC.mjs} +6 -0
  24. package/dist/chunks/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
  25. package/dist/chunks/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
  26. package/dist/chunks/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
  27. package/dist/{messages-Dz9L52ol.mjs → chunks/messages-CRJ_mchV.mjs} +6 -0
  28. package/dist/chunks/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
  29. package/dist/chunks/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
  30. package/dist/{messages-R3hUSvr3.mjs → chunks/messages-CfiyT2Wi.mjs} +6 -0
  31. package/dist/{messages-CJdUsQ-c.mjs → chunks/messages-CgTq3QhU.mjs} +6 -0
  32. package/dist/chunks/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
  33. package/dist/{messages-Q7AO_FLv.mjs → chunks/messages-Cjjo7yHR.mjs} +6 -0
  34. package/dist/{messages-C99mq906.mjs → chunks/messages-Cl6ayUaq.mjs} +6 -0
  35. package/dist/chunks/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
  36. package/dist/chunks/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
  37. package/dist/chunks/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
  38. package/dist/{messages-BWF-zUpY.mjs → chunks/messages-CrCYPCk3.mjs} +6 -0
  39. package/dist/{messages-D-ZtY5v0.mjs → chunks/messages-Cs8zmZ3L.mjs} +6 -0
  40. package/dist/{messages-DprmQg6V.mjs → chunks/messages-CzK0LEhb.mjs} +6 -0
  41. package/dist/chunks/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
  42. package/dist/chunks/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
  43. package/dist/chunks/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
  44. package/dist/{messages-kep5wtm4.mjs → chunks/messages-D4qqwVgQ.mjs} +6 -0
  45. package/dist/chunks/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
  46. package/dist/{messages-CY8_RyFE.mjs → chunks/messages-D7u2bmP2.mjs} +6 -0
  47. package/dist/chunks/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
  48. package/dist/{messages-DLfR5bMd.mjs → chunks/messages-D9ndgBnU.mjs} +6 -0
  49. package/dist/{messages-CVw84KdI.mjs → chunks/messages-DDTQgImT.mjs} +6 -0
  50. package/dist/{messages-_ErNTNhk.mjs → chunks/messages-DH_jBeED.mjs} +6 -0
  51. package/dist/chunks/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
  52. package/dist/chunks/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
  53. package/dist/chunks/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
  54. package/dist/chunks/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
  55. package/dist/chunks/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
  56. package/dist/chunks/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
  57. package/dist/{messages-Z9nEU2xK.mjs → chunks/messages-DviiFSv2.mjs} +6 -0
  58. package/dist/chunks/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
  59. package/dist/{messages-w7v1GNaE.mjs → chunks/messages-Dzzn6XoD.mjs} +6 -0
  60. package/dist/{messages-CqWJcCbY.mjs → chunks/messages-GSByFygY.mjs} +6 -0
  61. package/dist/chunks/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
  62. package/dist/chunks/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
  63. package/dist/{messages-BU2nlrLK.mjs → chunks/messages-RonBBCnh.mjs} +6 -0
  64. package/dist/{messages-Bmu_S7GM.mjs → chunks/messages-VDriF5Qy.mjs} +6 -0
  65. package/dist/{messages-CLhcMlTc.mjs → chunks/messages-ZjUAIWb1.mjs} +6 -0
  66. package/dist/{messages-9SihnaXQ.mjs → chunks/messages-b1EdvUm0.mjs} +6 -0
  67. package/dist/{messages-DvFLX36Q.mjs → chunks/messages-begYOTgC.mjs} +6 -0
  68. package/dist/{messages-BMv4xwIr.mjs → chunks/messages-jrncnb-H.mjs} +6 -0
  69. package/dist/{messages-D5iv1Kox.mjs → chunks/messages-nefz1S71.mjs} +6 -0
  70. package/dist/{messages-CQwpzUFp.mjs → chunks/messages-ucTVgS5G.mjs} +6 -0
  71. package/dist/chunks/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
  72. package/dist/{messages-C9eaarcK.mjs → chunks/messages-wmi-iFkH.mjs} +6 -0
  73. package/dist/chunks/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
  74. package/dist/full.mjs +2 -2
  75. package/dist/locales.mjs +90 -84
  76. package/dist/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
  77. package/dist/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
  78. package/dist/{chunks/messages-DnXLrlHh.mjs → messages-4v4MuVEc.mjs} +6 -0
  79. package/dist/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
  80. package/dist/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
  81. package/dist/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
  82. package/dist/{chunks/messages-uKX8WBaD.mjs → messages-96kNZDll.mjs} +6 -0
  83. package/dist/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
  84. package/dist/{chunks/messages-DBn76jVV.mjs → messages-B217znr-.mjs} +8 -2
  85. package/dist/{chunks/messages-DT4dP5uK.mjs → messages-B8WNljW3.mjs} +6 -0
  86. package/dist/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
  87. package/dist/{chunks/messages-CZygwLwM.mjs → messages-BI43k_BD.mjs} +6 -0
  88. package/dist/{chunks/messages-CzTufCHu.mjs → messages-BJ6zrz2j.mjs} +6 -0
  89. package/dist/{chunks/messages-BoJc_p1r.mjs → messages-BUl_Rcnj.mjs} +6 -0
  90. package/dist/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
  91. package/dist/{chunks/messages-C2htQ_3F.mjs → messages-BcpCubnC.mjs} +6 -0
  92. package/dist/{chunks/messages-D5C3J9qr.mjs → messages-Bm-E4iRC.mjs} +6 -0
  93. package/dist/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
  94. package/dist/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
  95. package/dist/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
  96. package/dist/{chunks/messages-Dz9L52ol.mjs → messages-CRJ_mchV.mjs} +6 -0
  97. package/dist/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
  98. package/dist/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
  99. package/dist/{chunks/messages-R3hUSvr3.mjs → messages-CfiyT2Wi.mjs} +6 -0
  100. package/dist/{chunks/messages-CJdUsQ-c.mjs → messages-CgTq3QhU.mjs} +6 -0
  101. package/dist/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
  102. package/dist/{chunks/messages-Q7AO_FLv.mjs → messages-Cjjo7yHR.mjs} +6 -0
  103. package/dist/{chunks/messages-C99mq906.mjs → messages-Cl6ayUaq.mjs} +6 -0
  104. package/dist/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
  105. package/dist/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
  106. package/dist/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
  107. package/dist/{chunks/messages-BWF-zUpY.mjs → messages-CrCYPCk3.mjs} +6 -0
  108. package/dist/{chunks/messages-D-ZtY5v0.mjs → messages-Cs8zmZ3L.mjs} +6 -0
  109. package/dist/{chunks/messages-DprmQg6V.mjs → messages-CzK0LEhb.mjs} +6 -0
  110. package/dist/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
  111. package/dist/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
  112. package/dist/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
  113. package/dist/{chunks/messages-kep5wtm4.mjs → messages-D4qqwVgQ.mjs} +6 -0
  114. package/dist/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
  115. package/dist/{chunks/messages-CY8_RyFE.mjs → messages-D7u2bmP2.mjs} +6 -0
  116. package/dist/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
  117. package/dist/{chunks/messages-DLfR5bMd.mjs → messages-D9ndgBnU.mjs} +6 -0
  118. package/dist/{chunks/messages-CVw84KdI.mjs → messages-DDTQgImT.mjs} +6 -0
  119. package/dist/{chunks/messages-_ErNTNhk.mjs → messages-DH_jBeED.mjs} +6 -0
  120. package/dist/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
  121. package/dist/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
  122. package/dist/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
  123. package/dist/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
  124. package/dist/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
  125. package/dist/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
  126. package/dist/{chunks/messages-Z9nEU2xK.mjs → messages-DviiFSv2.mjs} +6 -0
  127. package/dist/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
  128. package/dist/{chunks/messages-w7v1GNaE.mjs → messages-Dzzn6XoD.mjs} +6 -0
  129. package/dist/{chunks/messages-CqWJcCbY.mjs → messages-GSByFygY.mjs} +6 -0
  130. package/dist/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
  131. package/dist/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
  132. package/dist/{chunks/messages-BU2nlrLK.mjs → messages-RonBBCnh.mjs} +6 -0
  133. package/dist/{chunks/messages-Bmu_S7GM.mjs → messages-VDriF5Qy.mjs} +6 -0
  134. package/dist/{chunks/messages-CLhcMlTc.mjs → messages-ZjUAIWb1.mjs} +6 -0
  135. package/dist/{chunks/messages-9SihnaXQ.mjs → messages-b1EdvUm0.mjs} +6 -0
  136. package/dist/{chunks/messages-DvFLX36Q.mjs → messages-begYOTgC.mjs} +6 -0
  137. package/dist/{chunks/messages-BMv4xwIr.mjs → messages-jrncnb-H.mjs} +6 -0
  138. package/dist/{chunks/messages-D5iv1Kox.mjs → messages-nefz1S71.mjs} +6 -0
  139. package/dist/{chunks/messages-CQwpzUFp.mjs → messages-ucTVgS5G.mjs} +6 -0
  140. package/dist/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
  141. package/dist/{chunks/messages-C9eaarcK.mjs → messages-wmi-iFkH.mjs} +6 -0
  142. package/dist/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
  143. package/dist/tools.mjs +3190 -1690
  144. package/dist/vendor.LICENSE.txt +109 -109
  145. package/package.json +43 -57
  146. package/src/blok.ts +12 -0
  147. package/src/components/__module.ts +21 -0
  148. package/src/components/block/api.ts +17 -0
  149. package/src/components/block/style-manager.ts +6 -2
  150. package/src/components/block/tool-renderer.ts +33 -30
  151. package/src/components/blocks.ts +132 -15
  152. package/src/components/i18n/locales/am/messages.json +6 -0
  153. package/src/components/i18n/locales/ar/messages.json +6 -0
  154. package/src/components/i18n/locales/az/messages.json +6 -0
  155. package/src/components/i18n/locales/bg/messages.json +6 -0
  156. package/src/components/i18n/locales/bn/messages.json +6 -0
  157. package/src/components/i18n/locales/bs/messages.json +6 -0
  158. package/src/components/i18n/locales/cs/messages.json +6 -0
  159. package/src/components/i18n/locales/da/messages.json +6 -0
  160. package/src/components/i18n/locales/de/messages.json +6 -0
  161. package/src/components/i18n/locales/dv/messages.json +6 -0
  162. package/src/components/i18n/locales/el/messages.json +6 -0
  163. package/src/components/i18n/locales/en/messages.json +6 -0
  164. package/src/components/i18n/locales/es/messages.json +6 -0
  165. package/src/components/i18n/locales/et/messages.json +6 -0
  166. package/src/components/i18n/locales/fa/messages.json +6 -0
  167. package/src/components/i18n/locales/fi/messages.json +6 -0
  168. package/src/components/i18n/locales/fil/messages.json +6 -0
  169. package/src/components/i18n/locales/fr/messages.json +6 -0
  170. package/src/components/i18n/locales/gu/messages.json +6 -0
  171. package/src/components/i18n/locales/he/messages.json +6 -0
  172. package/src/components/i18n/locales/hi/messages.json +6 -0
  173. package/src/components/i18n/locales/hr/messages.json +6 -0
  174. package/src/components/i18n/locales/hu/messages.json +6 -0
  175. package/src/components/i18n/locales/hy/messages.json +6 -0
  176. package/src/components/i18n/locales/id/messages.json +6 -0
  177. package/src/components/i18n/locales/it/messages.json +6 -0
  178. package/src/components/i18n/locales/ja/messages.json +6 -0
  179. package/src/components/i18n/locales/ka/messages.json +6 -0
  180. package/src/components/i18n/locales/km/messages.json +6 -0
  181. package/src/components/i18n/locales/kn/messages.json +6 -0
  182. package/src/components/i18n/locales/ko/messages.json +6 -0
  183. package/src/components/i18n/locales/ku/messages.json +6 -0
  184. package/src/components/i18n/locales/lo/messages.json +6 -0
  185. package/src/components/i18n/locales/lt/messages.json +6 -0
  186. package/src/components/i18n/locales/lv/messages.json +6 -0
  187. package/src/components/i18n/locales/mk/messages.json +6 -0
  188. package/src/components/i18n/locales/ml/messages.json +6 -0
  189. package/src/components/i18n/locales/mn/messages.json +6 -0
  190. package/src/components/i18n/locales/mr/messages.json +6 -0
  191. package/src/components/i18n/locales/ms/messages.json +6 -0
  192. package/src/components/i18n/locales/my/messages.json +6 -0
  193. package/src/components/i18n/locales/ne/messages.json +6 -0
  194. package/src/components/i18n/locales/nl/messages.json +6 -0
  195. package/src/components/i18n/locales/no/messages.json +6 -0
  196. package/src/components/i18n/locales/pa/messages.json +6 -0
  197. package/src/components/i18n/locales/pl/messages.json +6 -0
  198. package/src/components/i18n/locales/ps/messages.json +6 -0
  199. package/src/components/i18n/locales/pt/messages.json +6 -0
  200. package/src/components/i18n/locales/ro/messages.json +6 -0
  201. package/src/components/i18n/locales/ru/messages.json +6 -0
  202. package/src/components/i18n/locales/sd/messages.json +6 -0
  203. package/src/components/i18n/locales/si/messages.json +6 -0
  204. package/src/components/i18n/locales/sk/messages.json +6 -0
  205. package/src/components/i18n/locales/sl/messages.json +6 -0
  206. package/src/components/i18n/locales/sq/messages.json +6 -0
  207. package/src/components/i18n/locales/sr/messages.json +6 -0
  208. package/src/components/i18n/locales/sv/messages.json +6 -0
  209. package/src/components/i18n/locales/sw/messages.json +6 -0
  210. package/src/components/i18n/locales/ta/messages.json +6 -0
  211. package/src/components/i18n/locales/te/messages.json +6 -0
  212. package/src/components/i18n/locales/th/messages.json +6 -0
  213. package/src/components/i18n/locales/tr/messages.json +6 -0
  214. package/src/components/i18n/locales/ug/messages.json +6 -0
  215. package/src/components/i18n/locales/uk/messages.json +6 -0
  216. package/src/components/i18n/locales/ur/messages.json +6 -0
  217. package/src/components/i18n/locales/vi/messages.json +6 -0
  218. package/src/components/i18n/locales/yi/messages.json +6 -0
  219. package/src/components/i18n/locales/zh/messages.json +6 -0
  220. package/src/components/icons/index.ts +17 -0
  221. package/src/components/inline-tools/inline-tool-link.ts +1 -1
  222. package/src/components/inline-tools/inline-tool-marker.ts +737 -0
  223. package/src/components/inline-tools/utils/formatting-range-utils.ts +6 -3
  224. package/src/components/inline-tools/utils/marker-dom-utils.ts +17 -0
  225. package/src/components/modules/api/blocks.ts +34 -9
  226. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +75 -29
  227. package/src/components/modules/blockEvents/index.ts +13 -5
  228. package/src/components/modules/blockManager/blockManager.ts +81 -2
  229. package/src/components/modules/blockManager/hierarchy.ts +20 -2
  230. package/src/components/modules/blockManager/operations.ts +70 -35
  231. package/src/components/modules/blockManager/repository.ts +22 -0
  232. package/src/components/modules/blockManager/types.ts +3 -1
  233. package/src/components/modules/blockManager/yjs-sync.ts +173 -39
  234. package/src/components/modules/blockSelection.ts +3 -0
  235. package/src/components/modules/crossBlockSelection.ts +11 -3
  236. package/src/components/modules/drag/preview/DragPreview.ts +10 -2
  237. package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
  238. package/src/components/modules/drag/utils/drag.constants.ts +1 -1
  239. package/src/components/modules/normalizeInlineImages.ts +263 -0
  240. package/src/components/modules/paste/google-docs-preprocessor.ts +197 -0
  241. package/src/components/modules/paste/handlers/base.ts +43 -2
  242. package/src/components/modules/paste/handlers/html-handler.ts +1 -1
  243. package/src/components/modules/paste/handlers/index.ts +1 -0
  244. package/src/components/modules/paste/handlers/table-cells-handler.ts +104 -0
  245. package/src/components/modules/paste/index.ts +20 -3
  246. package/src/components/modules/readonly.ts +8 -2
  247. package/src/components/modules/rectangleSelection.ts +5 -2
  248. package/src/components/modules/renderer.ts +35 -0
  249. package/src/components/modules/saver.ts +52 -2
  250. package/src/components/modules/toolbar/blockSettings.ts +52 -44
  251. package/src/components/modules/toolbar/index.ts +124 -17
  252. package/src/components/modules/toolbar/inline/index.ts +4 -4
  253. package/src/components/modules/toolbar/plus-button.ts +3 -3
  254. package/src/components/modules/toolbar/settings-toggler.ts +3 -3
  255. package/src/components/modules/toolbar/styles.ts +7 -7
  256. package/src/components/modules/ui.ts +6 -6
  257. package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
  258. package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
  259. package/src/components/modules/yjs/block-observer.ts +9 -3
  260. package/src/components/modules/yjs/document-store.ts +10 -7
  261. package/src/components/modules/yjs/types.ts +8 -6
  262. package/src/components/modules/yjs/undo-history.ts +90 -11
  263. package/src/components/selection/fake-background/shadows.ts +1 -1
  264. package/src/components/shared/color-picker.ts +211 -0
  265. package/src/components/shared/color-presets.ts +25 -0
  266. package/src/components/ui/toolbox.ts +19 -11
  267. package/src/components/utils/color-mapping.ts +241 -0
  268. package/src/components/utils/notifier/draw.ts +9 -9
  269. package/src/components/utils/placeholder.ts +24 -8
  270. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -2
  271. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +14 -12
  272. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
  273. package/src/components/utils/popover/popover-abstract.ts +27 -3
  274. package/src/components/utils/popover/popover-desktop.ts +26 -3
  275. package/src/components/utils/popover/popover-inline.ts +14 -1
  276. package/src/components/utils/popover/popover-mobile.ts +4 -4
  277. package/src/components/utils/popover/popover.const.ts +2 -2
  278. package/src/components/utils/sanitizer.ts +24 -3
  279. package/src/components/utils/tw.ts +17 -5
  280. package/src/stories/Header.stories.ts +106 -0
  281. package/src/stories/MarkerColors.stories.ts +730 -0
  282. package/src/stories/Popover.stories.ts +1 -3
  283. package/src/stories/Table.stories.ts +1662 -0
  284. package/src/styles/main.css +207 -37
  285. package/src/tools/header/index.ts +1 -1
  286. package/src/tools/index.ts +3 -1
  287. package/src/tools/list/caret-manager.ts +28 -10
  288. package/src/tools/list/constants.ts +2 -2
  289. package/src/tools/list/dom-builder.ts +3 -3
  290. package/src/tools/list/static-configs.ts +0 -1
  291. package/src/tools/paragraph/index.ts +9 -5
  292. package/src/tools/table/core/table-commands.ts +99 -0
  293. package/src/tools/table/core/table-controller.ts +231 -0
  294. package/src/tools/table/core/table-events.ts +102 -0
  295. package/src/tools/table/index.ts +1070 -174
  296. package/src/tools/table/ownership/table-event-broker.ts +74 -0
  297. package/src/tools/table/ownership/table-ownership-registry.ts +126 -0
  298. package/src/tools/table/table-add-controls.ts +85 -15
  299. package/src/tools/table/table-cell-blocks.ts +336 -38
  300. package/src/tools/table/table-cell-clipboard.ts +415 -0
  301. package/src/tools/table/table-cell-color-picker.ts +34 -0
  302. package/src/tools/table/table-cell-selection.ts +264 -15
  303. package/src/tools/table/table-core.ts +3 -42
  304. package/src/tools/table/table-heading-toggle.ts +2 -2
  305. package/src/tools/table/table-model.ts +623 -0
  306. package/src/tools/table/table-operations.ts +59 -78
  307. package/src/tools/table/table-resize.ts +15 -11
  308. package/src/tools/table/table-restrictions.ts +69 -3
  309. package/src/tools/table/table-row-col-action-handler.ts +22 -7
  310. package/src/tools/table/table-row-col-controls.ts +129 -12
  311. package/src/tools/table/table-row-col-drag.ts +14 -0
  312. package/src/tools/table/table-scroll-haze.ts +152 -0
  313. package/src/tools/table/types.ts +22 -1
  314. package/src/tools/table/view/table-cell-blocks-adapter.ts +47 -0
  315. package/src/variants/blok-minimum.ts +13 -0
  316. package/types/api/block.d.ts +13 -0
  317. package/types/api/blocks.d.ts +16 -0
  318. package/types/tools/table.d.ts +2 -0
  319. package/dist/chunks/i18next-CugVlwWp.mjs +0 -1292
  320. package/src/tools/table/data-normalizer.ts +0 -32
@@ -163,18 +163,19 @@ export const syncColWidthsAfterDeleteColumn = (colWidths: number[] | undefined,
163
163
  export const computeInsertColumnWidths = (
164
164
  gridEl: HTMLElement,
165
165
  index: number,
166
- data: TableData,
166
+ colWidths: number[] | undefined,
167
+ initialColWidth: number | undefined,
167
168
  grid: TableGrid,
168
169
  ): number[] => {
169
- const colWidths = data.colWidths ?? readPixelWidths(gridEl);
170
+ const widths = colWidths ?? readPixelWidths(gridEl);
170
171
 
171
- const halfWidth = data.initialColWidth !== undefined
172
- ? Math.round((data.initialColWidth / 2) * 100) / 100
173
- : computeHalfAvgWidth(colWidths);
172
+ const halfWidth = initialColWidth !== undefined
173
+ ? Math.round((initialColWidth / 2) * 100) / 100
174
+ : computeHalfAvgWidth(widths);
174
175
 
175
- grid.addColumn(gridEl, index, colWidths, halfWidth);
176
+ grid.addColumn(gridEl, index, widths, halfWidth);
176
177
 
177
- const newWidths = [...colWidths];
178
+ const newWidths = [...widths];
178
179
 
179
180
  newWidths.splice(index, 0, halfWidth);
180
181
 
@@ -230,53 +231,6 @@ export const getBlockIdsInColumn = (element: HTMLElement | null, cellBlocks: Tab
230
231
  return cellBlocks?.getBlockIdsFromCells(cellsInColumn) ?? [];
231
232
  };
232
233
 
233
- // ─── Row/column deletion with block cleanup ─────────────────────────
234
-
235
- export const deleteRowWithBlockCleanup = (
236
- gridEl: HTMLElement,
237
- rowIndex: number,
238
- grid: TableGrid,
239
- cellBlocks: TableCellBlocks | null,
240
- ): void => {
241
- const rows = gridEl.querySelectorAll(`[${ROW_ATTR}]`);
242
- const row = rows[rowIndex];
243
-
244
- if (row && cellBlocks) {
245
- const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
246
-
247
- cellBlocks.deleteBlocks(cellBlocks.getBlockIdsFromCells(cells));
248
- }
249
-
250
- grid.deleteRow(gridEl, rowIndex);
251
- };
252
-
253
- export const deleteColumnWithBlockCleanup = (
254
- gridEl: HTMLElement,
255
- colIndex: number,
256
- colWidths: number[] | undefined,
257
- grid: TableGrid,
258
- cellBlocks: TableCellBlocks | null,
259
- ): number[] | undefined => {
260
- if (cellBlocks) {
261
- const rows = gridEl.querySelectorAll(`[${ROW_ATTR}]`);
262
- const cellsInColumn: Element[] = [];
263
-
264
- rows.forEach(row => {
265
- const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
266
-
267
- if (colIndex < cells.length) {
268
- cellsInColumn.push(cells[colIndex]);
269
- }
270
- });
271
-
272
- cellBlocks.deleteBlocks(cellBlocks.getBlockIdsFromCells(cellsInColumn));
273
- }
274
-
275
- grid.deleteColumn(gridEl, colIndex);
276
-
277
- return syncColWidthsAfterDeleteColumn(colWidths, colIndex);
278
- };
279
-
280
234
  // ─── Populate new cells ─────────────────────────────────────────────
281
235
 
282
236
  export const populateNewCells = (gridEl: HTMLElement, cellBlocks: TableCellBlocks | null): void => {
@@ -293,6 +247,7 @@ export const mountCellBlocksReadOnly = (
293
247
  gridEl: HTMLElement,
294
248
  content: LegacyCellContent[][],
295
249
  api: API,
250
+ _tableBlockId: string,
296
251
  ): void => {
297
252
  const rowElements = gridEl.querySelectorAll(`[${ROW_ATTR}]`);
298
253
 
@@ -326,21 +281,18 @@ export const mountCellBlocksReadOnly = (
326
281
  }
327
282
 
328
283
  if (!isCellWithBlocks(cellContent)) {
329
- // Handle legacy string content by creating a paragraph block
330
- const legacyText = typeof cellContent === 'string' ? cellContent : '';
331
- const insertedBlock = api.blocks.insert(
332
- 'paragraph',
333
- { text: legacyText },
334
- {},
335
- undefined,
336
- true
337
- );
338
-
339
- insertedBlock?.holder && container.appendChild(insertedBlock.holder);
284
+ // Read-only render path must not mutate block state.
285
+ // Render legacy content (plain string) in-place.
286
+ container.textContent = cellContent;
340
287
 
341
288
  return;
342
289
  }
343
290
 
291
+ // If this container previously rendered legacy text, clear it before mounting holders.
292
+ if (!hasExistingBlocks && (container.textContent ?? '').length > 0) {
293
+ container.textContent = '';
294
+ }
295
+
344
296
  for (const blockId of cellContent.blocks) {
345
297
  const index = api.blocks.getBlockIndex(blockId);
346
298
 
@@ -356,6 +308,15 @@ export const mountCellBlocksReadOnly = (
356
308
 
357
309
  container.appendChild(block.holder);
358
310
  }
311
+
312
+ // Strip placeholder attributes so paragraphs inside table cells
313
+ // don't show standalone-paragraph placeholders in readonly mode.
314
+ container.querySelectorAll<HTMLElement>('[data-blok-placeholder-active]').forEach(el => {
315
+ el.removeAttribute('data-blok-placeholder-active');
316
+ });
317
+ container.querySelectorAll<HTMLElement>('[data-placeholder]').forEach(el => {
318
+ el.removeAttribute('data-placeholder');
319
+ });
359
320
  });
360
321
  });
361
322
  };
@@ -414,7 +375,7 @@ export const setupKeyboardNavigation = (
414
375
  });
415
376
  };
416
377
 
417
- export const SCROLL_OVERFLOW_CLASSES = ['overflow-x-auto', 'pt-[9px]', 'pl-[9px]'];
378
+ export const SCROLL_OVERFLOW_CLASSES = ['overflow-x-auto', 'overflow-y-hidden'];
418
379
 
419
380
  export const enableScrollOverflow = (element: HTMLDivElement | null): void => {
420
381
  element?.classList.add(...SCROLL_OVERFLOW_CLASSES);
@@ -422,13 +383,7 @@ export const enableScrollOverflow = (element: HTMLDivElement | null): void => {
422
383
 
423
384
  // ─── Heading styles ─────────────────────────────────────────────────
424
385
 
425
- export const updateHeadingStyles = (element: HTMLDivElement | null, withHeadings: boolean): void => {
426
- if (!element) {
427
- return;
428
- }
429
-
430
- const gridEl = element.firstElementChild as HTMLElement;
431
-
386
+ export const updateHeadingStyles = (gridEl: HTMLElement | null, withHeadings: boolean): void => {
432
387
  if (!gridEl) {
433
388
  return;
434
389
  }
@@ -444,13 +399,39 @@ export const updateHeadingStyles = (element: HTMLDivElement | null, withHeadings
444
399
  }
445
400
  };
446
401
 
447
- export const updateHeadingColumnStyles = (element: HTMLDivElement | null, withHeadingColumn: boolean): void => {
448
- if (!element) {
449
- return;
450
- }
402
+ export const applyCellColors = (gridEl: HTMLElement, content: LegacyCellContent[][]): void => {
403
+ const rows = gridEl.querySelectorAll(`[${ROW_ATTR}]`);
451
404
 
452
- const gridEl = element.firstElementChild as HTMLElement;
405
+ content.forEach((rowContent, r) => {
406
+ if (r >= rows.length) {
407
+ return;
408
+ }
409
+
410
+ const cells = rows[r].querySelectorAll(`[${CELL_ATTR}]`);
411
+
412
+ rowContent.forEach((cellContent, c) => {
413
+ if (c >= cells.length) {
414
+ return;
415
+ }
416
+
417
+ const el = cells[c] as HTMLElement;
418
+
419
+ if (isCellWithBlocks(cellContent) && cellContent.color) {
420
+ el.style.backgroundColor = cellContent.color;
421
+ } else {
422
+ el.style.backgroundColor = '';
423
+ }
424
+
425
+ if (isCellWithBlocks(cellContent) && cellContent.textColor) {
426
+ el.style.color = cellContent.textColor;
427
+ } else {
428
+ el.style.color = '';
429
+ }
430
+ });
431
+ });
432
+ };
453
433
 
434
+ export const updateHeadingColumnStyles = (gridEl: HTMLElement | null, withHeadingColumn: boolean): void => {
454
435
  if (!gridEl) {
455
436
  return;
456
437
  }
@@ -23,6 +23,7 @@ export class TableResize {
23
23
  private dragStartX = 0;
24
24
  private dragColIndex = -1;
25
25
  private startColWidth = 0;
26
+ private dragRowCells: HTMLElement[][] | null = null;
26
27
  private handles: HTMLElement[] = [];
27
28
  private needsInitialApply: boolean;
28
29
 
@@ -73,6 +74,7 @@ export class TableResize {
73
74
  this.gridEl.removeEventListener('pointerdown', this.boundPointerDown);
74
75
  document.removeEventListener('pointermove', this.boundPointerMove);
75
76
  document.removeEventListener('pointerup', this.boundPointerUp);
77
+ this.dragRowCells = null;
76
78
 
77
79
  this.handles.forEach(handle => handle.remove());
78
80
  this.handles = [];
@@ -168,6 +170,7 @@ export class TableResize {
168
170
  this.isDragging = true;
169
171
  this.dragStartX = e.clientX;
170
172
  this.startColWidth = this.colWidths[this.dragColIndex];
173
+ this.dragRowCells = this.resolveRowCells();
171
174
 
172
175
  this.onDragStart?.();
173
176
  this.gridEl.style.userSelect = 'none';
@@ -192,7 +195,7 @@ export class TableResize {
192
195
  const newWidth = Math.max(MIN_COL_WIDTH, rawWidth);
193
196
 
194
197
  this.colWidths[this.dragColIndex] = newWidth;
195
- this.applyWidths();
198
+ this.applyWidths(this.dragRowCells ?? undefined);
196
199
  this.updateHandlePositions();
197
200
  this.onDrag?.();
198
201
  }
@@ -213,25 +216,26 @@ export class TableResize {
213
216
 
214
217
  document.removeEventListener('pointermove', this.boundPointerMove);
215
218
  document.removeEventListener('pointerup', this.boundPointerUp);
219
+ this.dragRowCells = null;
216
220
 
217
221
  this.onChange([...this.colWidths]);
218
222
  }
219
223
 
220
- private applyWidths(): void {
221
- const totalWidth = this.colWidths.reduce((sum, w) => sum + w, 0);
224
+ private resolveRowCells(): HTMLElement[][] {
225
+ const rows = this.gridEl.querySelectorAll<HTMLElement>(`[${ROW_ATTR}]`);
222
226
 
223
- this.gridEl.style.width = `${totalWidth + BORDER_WIDTH}px`;
227
+ return Array.from(rows, row => Array.from(row.querySelectorAll<HTMLElement>(`[${CELL_ATTR}]`)));
228
+ }
224
229
 
225
- const rows = this.gridEl.querySelectorAll(`[${ROW_ATTR}]`);
230
+ private applyWidths(rowCells: HTMLElement[][] = this.resolveRowCells()): void {
231
+ const totalWidth = this.colWidths.reduce((sum, w) => sum + w, 0);
226
232
 
227
- rows.forEach(row => {
228
- const cells = row.querySelectorAll(`[${CELL_ATTR}]`);
233
+ this.gridEl.style.width = `${totalWidth + BORDER_WIDTH}px`;
229
234
 
230
- cells.forEach((node, i) => {
235
+ rowCells.forEach(cells => {
236
+ cells.forEach((cellEl, i) => {
231
237
  if (i < this.colWidths.length) {
232
- const cellEl = node as HTMLElement;
233
-
234
- cellEl.style.width = `${this.colWidths[i]}px`;
238
+ cellEl.style.setProperty('width', `${this.colWidths[i]}px`);
235
239
  }
236
240
  });
237
241
  });
@@ -2,10 +2,75 @@ import type { API } from '../../../types';
2
2
  import type { Block } from '../../components/block';
3
3
 
4
4
  /**
5
- * List of block tools that are restricted from being inserted into table cells.
5
+ * Default block tools that are always restricted from being inserted into table cells.
6
6
  * These tools create semantic or structural issues when nested in table cells.
7
7
  */
8
- export const RESTRICTED_TOOLS = ['header', 'table'];
8
+ const DEFAULT_RESTRICTED_TOOLS = ['header', 'table'];
9
+
10
+ /**
11
+ * Additional restricted tools registered via table tool config.
12
+ * Users can extend the default list by setting `restrictedTools` in the table tool config.
13
+ */
14
+ const additionalRestrictedTools = new Map<string, number>();
15
+
16
+ /**
17
+ * Register additional tools as restricted in table cells.
18
+ * Called by the Table tool constructor when `restrictedTools` is set in the config.
19
+ *
20
+ * @param tools - Tool names to add to the restricted list
21
+ * @returns Cleanup function to unregister these tools when the owner is destroyed
22
+ */
23
+ export const registerAdditionalRestrictedTools = (tools: string[]): (() => void) => {
24
+ const uniqueTools = [...new Set(tools)];
25
+
26
+ for (const tool of uniqueTools) {
27
+ const currentCount = additionalRestrictedTools.get(tool) ?? 0;
28
+
29
+ additionalRestrictedTools.set(tool, currentCount + 1);
30
+ }
31
+
32
+ const state = { cleanedUp: false };
33
+
34
+ return (): void => {
35
+ if (state.cleanedUp) {
36
+ return;
37
+ }
38
+
39
+ state.cleanedUp = true;
40
+
41
+ for (const tool of uniqueTools) {
42
+ const currentCount = additionalRestrictedTools.get(tool);
43
+
44
+ if (currentCount === undefined) {
45
+ continue;
46
+ }
47
+
48
+ if (currentCount <= 1) {
49
+ additionalRestrictedTools.delete(tool);
50
+ } else {
51
+ additionalRestrictedTools.set(tool, currentCount - 1);
52
+ }
53
+ }
54
+ };
55
+ };
56
+
57
+ /**
58
+ * Clear all additional restricted tools.
59
+ * Useful for cleanup when the editor is destroyed or in tests.
60
+ */
61
+ export const clearAdditionalRestrictedTools = (): void => {
62
+ additionalRestrictedTools.clear();
63
+ };
64
+
65
+ /**
66
+ * Returns all restricted tool names (default + additional registered via config).
67
+ * Used by the toolbox to hide restricted tools when inside table cells.
68
+ *
69
+ * @returns Array of all restricted tool names
70
+ */
71
+ export const getRestrictedTools = (): string[] => {
72
+ return [...DEFAULT_RESTRICTED_TOOLS, ...additionalRestrictedTools.keys()];
73
+ };
9
74
 
10
75
  /**
11
76
  * Check if a block or element is inside a table cell.
@@ -26,12 +91,13 @@ export const isInsideTableCell = (block: Block | HTMLElement | null | undefined)
26
91
 
27
92
  /**
28
93
  * Check if a tool name is restricted inside table cells.
94
+ * Checks both the default restricted list and any additional tools registered via config.
29
95
  *
30
96
  * @param toolName - Name of the block tool to check
31
97
  * @returns true if the tool is restricted in table cells, false otherwise
32
98
  */
33
99
  export const isRestrictedInTableCell = (toolName: string): boolean => {
34
- return RESTRICTED_TOOLS.includes(toolName);
100
+ return DEFAULT_RESTRICTED_TOOLS.includes(toolName) || additionalRestrictedTools.has(toolName);
35
101
  };
36
102
 
37
103
  /**
@@ -3,24 +3,34 @@ import type { TableGrid } from './table-core';
3
3
  import {
4
4
  applyPixelWidths,
5
5
  computeInsertColumnWidths,
6
- deleteColumnWithBlockCleanup,
7
- deleteRowWithBlockCleanup,
8
6
  populateNewCells,
9
7
  redistributePercentWidths,
8
+ syncColWidthsAfterDeleteColumn,
10
9
  syncColWidthsAfterMove,
11
10
  } from './table-operations';
12
11
  import type { RowColAction } from './table-row-col-controls';
13
- import type { TableData } from './types';
14
12
 
15
13
  /**
16
14
  * Describes which row or column to highlight after an action completes.
17
15
  */
18
16
  export type PendingHighlight = { type: 'row' | 'col'; index: number };
19
17
 
18
+ /**
19
+ * Minimal metadata the action handler needs.
20
+ * Decoupled from TableData so callers can pass model-derived primitives.
21
+ */
22
+ export interface ActionData {
23
+ colWidths?: number[];
24
+ withHeadings: boolean;
25
+ withHeadingColumn: boolean;
26
+ initialColWidth?: number;
27
+ }
28
+
20
29
  interface ActionContext {
21
30
  grid: TableGrid;
22
- data: TableData;
31
+ data: ActionData;
23
32
  cellBlocks: TableCellBlocks | null;
33
+ blocksToDelete?: string[];
24
34
  }
25
35
 
26
36
  interface ActionResult {
@@ -53,7 +63,7 @@ const handleInsertCol = (
53
63
  index: number,
54
64
  ctx: ActionContext,
55
65
  ): ActionResult => {
56
- const colWidths = computeInsertColumnWidths(gridEl, index, ctx.data, ctx.grid);
66
+ const colWidths = computeInsertColumnWidths(gridEl, index, ctx.data.colWidths, ctx.data.initialColWidth, ctx.grid);
57
67
 
58
68
  populateNewCells(gridEl, ctx.cellBlocks);
59
69
 
@@ -105,7 +115,9 @@ const handleDeleteRow = (
105
115
  index: number,
106
116
  ctx: ActionContext,
107
117
  ): ActionResult => {
108
- deleteRowWithBlockCleanup(gridEl, index, ctx.grid, ctx.cellBlocks);
118
+ ctx.cellBlocks?.deleteBlocks(ctx.blocksToDelete ?? []);
119
+ ctx.grid.deleteRow(gridEl, index);
120
+
109
121
  const newRowCount = ctx.grid.getRowCount(gridEl);
110
122
  const neighborRow = index < newRowCount ? index : index - 1;
111
123
 
@@ -123,7 +135,10 @@ const handleDeleteCol = (
123
135
  index: number,
124
136
  ctx: ActionContext,
125
137
  ): ActionResult => {
126
- const colWidths = deleteColumnWithBlockCleanup(gridEl, index, ctx.data.colWidths, ctx.grid, ctx.cellBlocks);
138
+ ctx.cellBlocks?.deleteBlocks(ctx.blocksToDelete ?? []);
139
+ ctx.grid.deleteColumn(gridEl, index);
140
+
141
+ const colWidths = syncColWidthsAfterDeleteColumn(ctx.data.colWidths, index);
127
142
 
128
143
  if (colWidths) {
129
144
  applyPixelWidths(gridEl, colWidths);
@@ -33,6 +33,8 @@ export type RowColAction =
33
33
 
34
34
  export interface TableRowColControlsOptions {
35
35
  grid: HTMLElement;
36
+ overlay?: HTMLElement;
37
+ scrollContainer?: HTMLElement;
36
38
  getColumnCount: () => number;
37
39
  getRowCount: () => number;
38
40
  isHeadingRow: () => boolean;
@@ -46,8 +48,8 @@ export interface TableRowColControlsOptions {
46
48
 
47
49
  const GRIP_CAPSULE_CLASSES = [
48
50
  'absolute',
49
- 'z-[3]',
50
- 'rounded',
51
+ 'z-3',
52
+ 'rounded-sm',
51
53
  'cursor-grab',
52
54
  'select-none',
53
55
  'transition-[opacity,background-color,width,height]',
@@ -83,6 +85,8 @@ const GRIP_ACTIVE_CLASSES = [
83
85
  */
84
86
  export class TableRowColControls {
85
87
  private grid: HTMLElement;
88
+ private overlay: HTMLElement | undefined;
89
+ private scrollContainer: HTMLElement | undefined;
86
90
  private getColumnCount: () => number;
87
91
  private getRowCount: () => number;
88
92
  private isHeadingRow: () => boolean;
@@ -108,9 +112,12 @@ export class TableRowColControls {
108
112
  private boundMouseOver: (e: MouseEvent) => void;
109
113
  private boundMouseLeave: (e: MouseEvent) => void;
110
114
  private boundPointerDown: (e: PointerEvent) => void;
115
+ private boundScrollHandler: (() => void) | null = null;
111
116
 
112
117
  constructor(options: TableRowColControlsOptions) {
113
118
  this.grid = options.grid;
119
+ this.overlay = options.overlay;
120
+ this.scrollContainer = options.scrollContainer;
114
121
  this.getColumnCount = options.getColumnCount;
115
122
  this.getRowCount = options.getRowCount;
116
123
  this.isHeadingRow = options.isHeadingRow;
@@ -141,11 +148,39 @@ export class TableRowColControls {
141
148
  }
142
149
 
143
150
  /**
144
- * Recreate grips after structural changes (row/column add/delete/move)
151
+ * Recreate grips after structural changes (row/column add/delete/move).
152
+ * Preserves the active grip state when a popover is open so the grip
153
+ * remains visible after being recreated.
145
154
  */
146
155
  public refresh(): void {
156
+ const popoverGripInfo = this.popoverState.grip
157
+ ? this.detectGripType(this.popoverState.grip)
158
+ : null;
159
+
147
160
  this.destroyGrips();
148
161
  this.createGrips();
162
+
163
+ if (!popoverGripInfo) {
164
+ return;
165
+ }
166
+
167
+ const newGrip = popoverGripInfo.type === 'col'
168
+ ? this.colGrips[popoverGripInfo.index]
169
+ : this.rowGrips[popoverGripInfo.index];
170
+
171
+ if (!newGrip) {
172
+ return;
173
+ }
174
+
175
+ this.popoverState.grip = newGrip;
176
+ this.hideAllGripsExcept(newGrip);
177
+ this.applyActiveClasses(newGrip);
178
+
179
+ if (popoverGripInfo.type === 'col') {
180
+ newGrip.style.height = `${GRIP_HOVER_SIZE}px`;
181
+ } else {
182
+ newGrip.style.width = `${GRIP_HOVER_SIZE}px`;
183
+ }
149
184
  }
150
185
 
151
186
  /**
@@ -162,6 +197,7 @@ export class TableRowColControls {
162
197
  }
163
198
 
164
199
  this.unlockGrip();
200
+ this.clearHideTimeout();
165
201
  this.hideAllGripsExcept(grip);
166
202
  this.applyActiveClasses(grip);
167
203
 
@@ -173,18 +209,32 @@ export class TableRowColControls {
173
209
 
174
210
  this.lockedGrip = grip;
175
211
 
176
- requestAnimationFrame(() => {
177
- document.addEventListener('pointerdown', this.boundUnlockGrip);
178
- });
212
+ document.addEventListener('pointerdown', this.boundUnlockGrip);
179
213
  }
180
214
 
181
- private handleUnlockGrip(): void {
215
+ private handleUnlockGrip(e: PointerEvent): void {
182
216
  document.removeEventListener('pointerdown', this.boundUnlockGrip);
183
217
 
184
218
  if (this.lockedGrip) {
185
219
  this.applyIdleClasses(this.lockedGrip);
186
220
  this.lockedGrip = null;
187
221
  }
222
+
223
+ // Re-evaluate grip visibility: the preceding mouseover was blocked
224
+ // by isGripInteractionLocked(). Check if pointer is over a table cell.
225
+ const target = e.target instanceof HTMLElement ? e.target : null;
226
+ const cell = target?.closest<HTMLElement>(`[${CELL_ATTR}]`);
227
+
228
+ if (cell) {
229
+ const position = this.getCellPosition(cell);
230
+
231
+ if (position) {
232
+ this.clearHideTimeout();
233
+ this.showColGrip(position.col);
234
+ this.showRowGrip(position.row);
235
+ this.isInsideTable = true;
236
+ }
237
+ }
188
238
  }
189
239
 
190
240
  private unlockGrip(): void {
@@ -192,6 +242,37 @@ export class TableRowColControls {
192
242
  this.lockedGrip = null;
193
243
  }
194
244
 
245
+ /**
246
+ * Return the indices of the currently visible grips, or null if none are active.
247
+ */
248
+ public getVisibleGripIndices(): { col: number; row: number } | null {
249
+ if (this.activeColGripIndex < 0 && this.activeRowGripIndex < 0) {
250
+ return null;
251
+ }
252
+
253
+ return { col: this.activeColGripIndex, row: this.activeRowGripIndex };
254
+ }
255
+
256
+ /**
257
+ * Programmatically restore grip visibility (e.g. after a DOM rebuild during undo).
258
+ */
259
+ public restoreVisibleGrips(col: number, row: number): void {
260
+ // Set isInsideTable BEFORE showing grips so applyVisibleClasses()
261
+ // skips the CSS opacity transition (no flash).
262
+ this.isInsideTable = col >= 0 || row >= 0;
263
+
264
+ if (col >= 0) {
265
+ this.showColGrip(col);
266
+ }
267
+ if (row >= 0) {
268
+ this.showRowGrip(row);
269
+ }
270
+ }
271
+
272
+ public get isPopoverOpen(): boolean {
273
+ return this.popoverState.popover !== null;
274
+ }
275
+
195
276
  public destroy(): void {
196
277
  this.destroyPopover();
197
278
  this.unlockGrip();
@@ -205,28 +286,45 @@ export class TableRowColControls {
205
286
  private createGrips(): void {
206
287
  const colCount = this.getColumnCount();
207
288
  const rowCount = this.getRowCount();
289
+ const gripContainer = this.overlay ?? this.grid;
208
290
 
209
291
  Array.from({ length: colCount }).forEach((_, i) => {
210
292
  const grip = this.createGripElement('col', i);
211
293
 
212
294
  this.colGrips.push(grip);
213
- this.grid.appendChild(grip);
295
+ gripContainer.appendChild(grip);
214
296
  });
215
297
 
216
298
  Array.from({ length: rowCount }).forEach((_, i) => {
217
299
  const grip = this.createGripElement('row', i);
218
300
 
219
301
  this.rowGrips.push(grip);
220
- this.grid.appendChild(grip);
302
+ gripContainer.appendChild(grip);
221
303
  });
222
304
 
223
305
  this.positionGrips();
224
306
  this.observeRowHeights();
307
+ this.attachScrollListener();
308
+ }
309
+
310
+ private attachScrollListener(): void {
311
+ if (this.overlay && this.scrollContainer) {
312
+ this.boundScrollHandler = () => this.positionGrips();
313
+ this.scrollContainer.addEventListener('scroll', this.boundScrollHandler);
314
+ }
315
+ }
316
+
317
+ private detachScrollListener(): void {
318
+ if (this.boundScrollHandler && this.scrollContainer) {
319
+ this.scrollContainer.removeEventListener('scroll', this.boundScrollHandler);
320
+ this.boundScrollHandler = null;
321
+ }
225
322
  }
226
323
 
227
324
  private destroyGrips(): void {
228
325
  this.rowResizeObserver?.disconnect();
229
326
  this.rowResizeObserver = null;
327
+ this.detachScrollListener();
230
328
  this.colGrips.forEach(g => g.remove());
231
329
  this.rowGrips.forEach(g => g.remove());
232
330
  this.colGrips = [];
@@ -257,13 +355,20 @@ export class TableRowColControls {
257
355
 
258
356
  grip.addEventListener('pointerdown', this.boundPointerDown);
259
357
  grip.addEventListener('mouseenter', () => {
358
+ if (this.overlay) {
359
+ this.clearHideTimeout();
360
+ }
260
361
  if (!this.isGripInteractionLocked()) {
261
362
  expandGrip(grip, type);
262
363
  }
263
364
  });
264
365
  grip.addEventListener('mouseleave', () => {
265
- if (!this.isGripInteractionLocked()) {
266
- collapseGrip(grip, type, pillSize);
366
+ if (this.isGripInteractionLocked()) {
367
+ return;
368
+ }
369
+ collapseGrip(grip, type, pillSize);
370
+ if (this.overlay) {
371
+ this.scheduleHideAll();
267
372
  }
268
373
  });
269
374
 
@@ -283,6 +388,12 @@ export class TableRowColControls {
283
388
  }
284
389
 
285
390
  const edges = getCumulativeColEdges(this.grid);
391
+ const scrollLeft = this.overlay && this.scrollContainer
392
+ ? this.scrollContainer.scrollLeft
393
+ : 0;
394
+ const containerWidth = this.overlay && this.scrollContainer
395
+ ? this.scrollContainer.clientWidth
396
+ : Infinity;
286
397
 
287
398
  this.colGrips.forEach((grip, i) => {
288
399
  if (i + 1 >= edges.length) {
@@ -290,10 +401,16 @@ export class TableRowColControls {
290
401
  }
291
402
 
292
403
  const centerX = (edges[i] + edges[i + 1]) / 2;
404
+ const adjustedX = centerX - scrollLeft;
293
405
  const style = grip.style;
294
406
 
295
407
  style.top = `${-BORDER_WIDTH / 2}px`;
296
- style.left = `${centerX}px`;
408
+ style.left = `${adjustedX}px`;
409
+
410
+ // Hide grips scrolled out of the visible area
411
+ if (this.overlay) {
412
+ style.visibility = (adjustedX < 0 || adjustedX > containerWidth) ? 'hidden' : '';
413
+ }
297
414
  });
298
415
 
299
416
  this.rowGrips.forEach((grip, i) => {