@jackuait/blok 0.6.0-beta.9 → 0.7.0-beta.1

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 (336) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-Bn6Q_o8h.mjs → blok-ob9Fwr1L.mjs} +3414 -2975
  3. package/dist/chunks/i18next-B47TKgbU.mjs +1303 -0
  4. package/dist/chunks/{i18next-loader-DjR4d8M7.mjs → i18next-loader-Bu3vFvye.mjs} +2 -2
  5. package/dist/chunks/{index-oe38cp86.mjs → index-CZmRzRIX.mjs} +12 -12
  6. package/dist/chunks/{inline-tool-convert-SRTkyaZn.mjs → inline-tool-convert-CvFW2iie.mjs} +1579 -961
  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 +30 -27
  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 +3537 -1710
  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/constants/data-attributes.ts +7 -0
  153. package/src/components/i18n/locales/am/messages.json +6 -0
  154. package/src/components/i18n/locales/ar/messages.json +6 -0
  155. package/src/components/i18n/locales/az/messages.json +6 -0
  156. package/src/components/i18n/locales/bg/messages.json +6 -0
  157. package/src/components/i18n/locales/bn/messages.json +6 -0
  158. package/src/components/i18n/locales/bs/messages.json +6 -0
  159. package/src/components/i18n/locales/cs/messages.json +6 -0
  160. package/src/components/i18n/locales/da/messages.json +6 -0
  161. package/src/components/i18n/locales/de/messages.json +6 -0
  162. package/src/components/i18n/locales/dv/messages.json +6 -0
  163. package/src/components/i18n/locales/el/messages.json +6 -0
  164. package/src/components/i18n/locales/en/messages.json +6 -0
  165. package/src/components/i18n/locales/es/messages.json +6 -0
  166. package/src/components/i18n/locales/et/messages.json +6 -0
  167. package/src/components/i18n/locales/fa/messages.json +6 -0
  168. package/src/components/i18n/locales/fi/messages.json +6 -0
  169. package/src/components/i18n/locales/fil/messages.json +6 -0
  170. package/src/components/i18n/locales/fr/messages.json +6 -0
  171. package/src/components/i18n/locales/gu/messages.json +6 -0
  172. package/src/components/i18n/locales/he/messages.json +6 -0
  173. package/src/components/i18n/locales/hi/messages.json +6 -0
  174. package/src/components/i18n/locales/hr/messages.json +6 -0
  175. package/src/components/i18n/locales/hu/messages.json +6 -0
  176. package/src/components/i18n/locales/hy/messages.json +6 -0
  177. package/src/components/i18n/locales/id/messages.json +6 -0
  178. package/src/components/i18n/locales/it/messages.json +6 -0
  179. package/src/components/i18n/locales/ja/messages.json +6 -0
  180. package/src/components/i18n/locales/ka/messages.json +6 -0
  181. package/src/components/i18n/locales/km/messages.json +6 -0
  182. package/src/components/i18n/locales/kn/messages.json +6 -0
  183. package/src/components/i18n/locales/ko/messages.json +6 -0
  184. package/src/components/i18n/locales/ku/messages.json +6 -0
  185. package/src/components/i18n/locales/lo/messages.json +6 -0
  186. package/src/components/i18n/locales/lt/messages.json +6 -0
  187. package/src/components/i18n/locales/lv/messages.json +6 -0
  188. package/src/components/i18n/locales/mk/messages.json +6 -0
  189. package/src/components/i18n/locales/ml/messages.json +6 -0
  190. package/src/components/i18n/locales/mn/messages.json +6 -0
  191. package/src/components/i18n/locales/mr/messages.json +6 -0
  192. package/src/components/i18n/locales/ms/messages.json +6 -0
  193. package/src/components/i18n/locales/my/messages.json +6 -0
  194. package/src/components/i18n/locales/ne/messages.json +6 -0
  195. package/src/components/i18n/locales/nl/messages.json +6 -0
  196. package/src/components/i18n/locales/no/messages.json +6 -0
  197. package/src/components/i18n/locales/pa/messages.json +6 -0
  198. package/src/components/i18n/locales/pl/messages.json +6 -0
  199. package/src/components/i18n/locales/ps/messages.json +6 -0
  200. package/src/components/i18n/locales/pt/messages.json +6 -0
  201. package/src/components/i18n/locales/ro/messages.json +6 -0
  202. package/src/components/i18n/locales/ru/messages.json +6 -0
  203. package/src/components/i18n/locales/sd/messages.json +6 -0
  204. package/src/components/i18n/locales/si/messages.json +6 -0
  205. package/src/components/i18n/locales/sk/messages.json +6 -0
  206. package/src/components/i18n/locales/sl/messages.json +6 -0
  207. package/src/components/i18n/locales/sq/messages.json +6 -0
  208. package/src/components/i18n/locales/sr/messages.json +6 -0
  209. package/src/components/i18n/locales/sv/messages.json +6 -0
  210. package/src/components/i18n/locales/sw/messages.json +6 -0
  211. package/src/components/i18n/locales/ta/messages.json +6 -0
  212. package/src/components/i18n/locales/te/messages.json +6 -0
  213. package/src/components/i18n/locales/th/messages.json +6 -0
  214. package/src/components/i18n/locales/tr/messages.json +6 -0
  215. package/src/components/i18n/locales/ug/messages.json +6 -0
  216. package/src/components/i18n/locales/uk/messages.json +6 -0
  217. package/src/components/i18n/locales/ur/messages.json +6 -0
  218. package/src/components/i18n/locales/vi/messages.json +6 -0
  219. package/src/components/i18n/locales/yi/messages.json +6 -0
  220. package/src/components/i18n/locales/zh/messages.json +6 -0
  221. package/src/components/icons/index.ts +61 -7
  222. package/src/components/inline-tools/inline-tool-link.ts +1 -1
  223. package/src/components/inline-tools/inline-tool-marker.ts +737 -0
  224. package/src/components/inline-tools/utils/formatting-range-utils.ts +6 -3
  225. package/src/components/inline-tools/utils/marker-dom-utils.ts +17 -0
  226. package/src/components/modules/api/blocks.ts +34 -9
  227. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +75 -29
  228. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +54 -2
  229. package/src/components/modules/blockEvents/constants.ts +12 -0
  230. package/src/components/modules/blockEvents/index.ts +13 -5
  231. package/src/components/modules/blockManager/blockManager.ts +81 -2
  232. package/src/components/modules/blockManager/hierarchy.ts +20 -2
  233. package/src/components/modules/blockManager/operations.ts +70 -35
  234. package/src/components/modules/blockManager/repository.ts +22 -0
  235. package/src/components/modules/blockManager/types.ts +3 -1
  236. package/src/components/modules/blockManager/yjs-sync.ts +173 -39
  237. package/src/components/modules/blockSelection.ts +3 -0
  238. package/src/components/modules/crossBlockSelection.ts +11 -3
  239. package/src/components/modules/drag/preview/DragPreview.ts +10 -2
  240. package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
  241. package/src/components/modules/drag/utils/drag.constants.ts +1 -1
  242. package/src/components/modules/normalizeInlineImages.ts +263 -0
  243. package/src/components/modules/paste/google-docs-preprocessor.ts +197 -0
  244. package/src/components/modules/paste/handlers/base.ts +43 -2
  245. package/src/components/modules/paste/handlers/html-handler.ts +1 -1
  246. package/src/components/modules/paste/handlers/index.ts +1 -0
  247. package/src/components/modules/paste/handlers/table-cells-handler.ts +104 -0
  248. package/src/components/modules/paste/index.ts +20 -3
  249. package/src/components/modules/readonly.ts +8 -2
  250. package/src/components/modules/rectangleSelection.ts +5 -2
  251. package/src/components/modules/renderer.ts +35 -0
  252. package/src/components/modules/saver.ts +52 -2
  253. package/src/components/modules/toolbar/blockSettings.ts +52 -44
  254. package/src/components/modules/toolbar/index.ts +124 -17
  255. package/src/components/modules/toolbar/inline/index.ts +4 -4
  256. package/src/components/modules/toolbar/plus-button.ts +3 -3
  257. package/src/components/modules/toolbar/settings-toggler.ts +3 -3
  258. package/src/components/modules/toolbar/styles.ts +7 -7
  259. package/src/components/modules/ui.ts +6 -6
  260. package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
  261. package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
  262. package/src/components/modules/yjs/block-observer.ts +9 -3
  263. package/src/components/modules/yjs/document-store.ts +10 -7
  264. package/src/components/modules/yjs/types.ts +8 -6
  265. package/src/components/modules/yjs/undo-history.ts +90 -11
  266. package/src/components/selection/fake-background/shadows.ts +1 -1
  267. package/src/components/shared/color-picker.ts +211 -0
  268. package/src/components/shared/color-presets.ts +25 -0
  269. package/src/components/ui/toolbox.ts +27 -11
  270. package/src/components/utils/color-mapping.ts +241 -0
  271. package/src/components/utils/notifier/draw.ts +9 -9
  272. package/src/components/utils/placeholder.ts +24 -8
  273. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +3 -3
  274. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +15 -12
  275. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
  276. package/src/components/utils/popover/popover-abstract.ts +30 -5
  277. package/src/components/utils/popover/popover-desktop.ts +26 -3
  278. package/src/components/utils/popover/popover-inline.ts +14 -1
  279. package/src/components/utils/popover/popover-mobile.ts +4 -4
  280. package/src/components/utils/popover/popover.const.ts +3 -3
  281. package/src/components/utils/sanitizer.ts +24 -3
  282. package/src/components/utils/tw.ts +17 -5
  283. package/src/full.ts +4 -0
  284. package/src/stories/Header.stories.ts +106 -0
  285. package/src/stories/MarkerColors.stories.ts +730 -0
  286. package/src/stories/Placeholder.stories.ts +7 -2
  287. package/src/stories/Popover.stories.ts +1 -3
  288. package/src/stories/Table.stories.ts +1662 -0
  289. package/src/stories/helpers.ts +2 -0
  290. package/src/styles/main.css +217 -39
  291. package/src/tools/header/index.ts +204 -26
  292. package/src/tools/index.ts +5 -1
  293. package/src/tools/list/caret-manager.ts +28 -10
  294. package/src/tools/list/constants.ts +2 -2
  295. package/src/tools/list/dom-builder.ts +3 -3
  296. package/src/tools/list/static-configs.ts +0 -1
  297. package/src/tools/paragraph/index.ts +9 -5
  298. package/src/tools/table/core/table-commands.ts +99 -0
  299. package/src/tools/table/core/table-controller.ts +231 -0
  300. package/src/tools/table/core/table-events.ts +102 -0
  301. package/src/tools/table/index.ts +1070 -174
  302. package/src/tools/table/ownership/table-event-broker.ts +74 -0
  303. package/src/tools/table/ownership/table-ownership-registry.ts +126 -0
  304. package/src/tools/table/table-add-controls.ts +85 -15
  305. package/src/tools/table/table-cell-blocks.ts +336 -38
  306. package/src/tools/table/table-cell-clipboard.ts +415 -0
  307. package/src/tools/table/table-cell-color-picker.ts +34 -0
  308. package/src/tools/table/table-cell-selection.ts +264 -15
  309. package/src/tools/table/table-core.ts +3 -42
  310. package/src/tools/table/table-heading-toggle.ts +2 -2
  311. package/src/tools/table/table-model.ts +623 -0
  312. package/src/tools/table/table-operations.ts +59 -78
  313. package/src/tools/table/table-resize.ts +15 -11
  314. package/src/tools/table/table-restrictions.ts +69 -3
  315. package/src/tools/table/table-row-col-action-handler.ts +22 -7
  316. package/src/tools/table/table-row-col-controls.ts +129 -12
  317. package/src/tools/table/table-row-col-drag.ts +14 -0
  318. package/src/tools/table/table-scroll-haze.ts +152 -0
  319. package/src/tools/table/types.ts +22 -1
  320. package/src/tools/table/view/table-cell-blocks-adapter.ts +47 -0
  321. package/src/tools/toggle/block-operations.ts +110 -0
  322. package/src/tools/toggle/constants.ts +49 -0
  323. package/src/tools/toggle/dom-builder.ts +125 -0
  324. package/src/tools/toggle/index.ts +280 -0
  325. package/src/tools/toggle/toggle-keyboard.ts +139 -0
  326. package/src/tools/toggle/toggle-lifecycle.ts +80 -0
  327. package/src/tools/toggle/toggle-shortcuts.ts +107 -0
  328. package/src/tools/toggle/types.ts +21 -0
  329. package/src/variants/blok-minimum.ts +13 -0
  330. package/types/api/block.d.ts +13 -0
  331. package/types/api/blocks.d.ts +16 -0
  332. package/types/full.d.ts +2 -0
  333. package/types/tools/table.d.ts +2 -0
  334. package/types/tools-entry.d.ts +2 -1
  335. package/dist/chunks/i18next-CugVlwWp.mjs +0 -1292
  336. package/src/tools/table/data-normalizer.ts +0 -32
@@ -8,6 +8,7 @@ import { BoldInlineTool } from '../components/inline-tools/inline-tool-bold';
8
8
  import { ConvertInlineTool } from '../components/inline-tools/inline-tool-convert';
9
9
  import { ItalicInlineTool } from '../components/inline-tools/inline-tool-italic';
10
10
  import { LinkInlineTool } from '../components/inline-tools/inline-tool-link';
11
+ import { MarkerInlineTool } from '../components/inline-tools/inline-tool-marker';
11
12
  import { Header } from '../tools/header';
12
13
  import { ListItem } from '../tools/list';
13
14
  import { Paragraph } from '../tools/paragraph';
@@ -35,6 +36,7 @@ export const defaultTools: { [toolName: string]: ToolConstructable | ToolSetting
35
36
  bold: BoldInlineTool,
36
37
  italic: ItalicInlineTool,
37
38
  link: LinkInlineTool,
39
+ marker: MarkerInlineTool,
38
40
  convertTo: ConvertInlineTool,
39
41
  };
40
42
 
@@ -1,61 +1,231 @@
1
- /* Tailwind CSS - must be first */
2
- @import 'tailwindcss/base';
3
- @import 'tailwindcss/components';
4
- @import 'tailwindcss/utilities';
1
+ /*
2
+ Import Tailwind CSS sub-modules individually (without native @layer wrappers).
3
+ The default `@import 'tailwindcss'` wraps all output in @layer declarations,
4
+ which causes styles to lose to ANY un-layered consumer CSS — per the CSS spec,
5
+ un-layered styles always beat layered styles regardless of specificity.
6
+ Individual imports avoid this, making the library's styles work in real apps.
7
+ See: https://github.com/tailwindlabs/tailwindcss/discussions/13188
8
+ */
9
+ @import 'tailwindcss/theme.css';
10
+ @import 'tailwindcss/preflight.css';
11
+ @import 'tailwindcss/utilities.css';
5
12
 
6
- /**
7
- * Component classes for the Styles API.
8
- * These single-class names can be safely used with classList.add() by external plugins.
9
- * Each class bundles multiple Tailwind utilities using @apply.
10
- */
11
- @layer components {
12
- /* Base Blok styles - applied to block tool wrappers */
13
- .blok-block {
14
- @apply py-[3px] px-[2px] [&::-webkit-input-placeholder]:!leading-normal;
15
- }
13
+ /*
14
+ Exclude non-source directories from Tailwind's automatic content detection.
15
+ Without this, Tailwind v4 scans all non-gitignored files (including docs/,
16
+ test/, .claude/) and picks up false class candidates from code examples,
17
+ test fixtures, and markdown — generating dead CSS in the production bundle.
18
+ */
19
+ @source not "../../docs";
20
+ @source not "../../test";
21
+ @source not "../../.claude";
22
+ @source not "../../scripts";
23
+ @source not "../../.storybook";
16
24
 
17
- /* Inline Tools styles */
18
- .blok-inline-tool-button {
19
- @apply flex justify-center items-center border-0 rounded h-full p-0 w-7 bg-transparent cursor-pointer leading-normal text-black;
25
+ @custom-variant mobile (@media (max-width: 650px));
26
+ @custom-variant not-mobile (@media (min-width: 651px));
27
+ @custom-variant can-hover (@media (hover: hover));
28
+
29
+ @custom-variant dark (&:where(.dark-mode, .dark-mode *));
30
+
31
+ @theme {
32
+ /* Z-index */
33
+ --z-index-overlay: 999;
34
+
35
+ /* Colors */
36
+ --color-selection: #e1f2ff;
37
+ --color-selection-inline: #d4ecff;
38
+ --color-selection-highlight: rgba(46, 170, 220, 0.2);
39
+ --color-bg-light: #eff2f5;
40
+ --color-gray-text: #707684;
41
+ --color-dark: #1d202b;
42
+ --color-active-icon: #388ae5;
43
+ --color-link: #388ae5;
44
+ --color-line-gray: #eff0f1;
45
+ --color-tooltip-bg: #1d202b;
46
+ --color-tooltip-font: #cdd1e0;
47
+ --color-icon-active-bg: rgba(56, 138, 229, 0.1);
48
+ --color-icon-active-text: #388ae5;
49
+ --color-text-primary: black;
50
+ --color-text-secondary: #707684;
51
+ --color-popover-border: #eff0f1;
52
+ --color-popover-shadow: rgba(13, 20, 33, 0.1);
53
+ --color-popover-bg: #ffffff;
54
+ --color-popover-border-icon: rgb(201 201 204 / 48%);
55
+ --color-popover-border-icon-disabled: #eff0f1;
56
+ --color-item-focus-bg: rgba(34, 186, 255, 0.08);
57
+ --color-item-focus-shadow: rgba(7, 161, 227, 0.08);
58
+ --color-item-hover-bg: #f8f8f8;
59
+ --color-icon-bg: rgba(0, 0, 0, 0.04);
60
+ --color-item-confirm-bg: #e24a4a;
61
+ --color-item-confirm-hover-bg: #ce4343;
62
+ --color-item-destructive-text: #e24a4a;
63
+ --color-item-destructive-hover-bg: rgba(226, 74, 74, 0.08);
64
+ --color-notify-success-bg: #fafffe;
65
+ --color-notify-success-bar: #41ffb1;
66
+ --color-notify-error-bg: #fffbfb;
67
+ --color-notify-error-bar: #fb5d5d;
68
+ --color-notify-btn-cancel-bg: #f2f5f7;
69
+ --color-notify-btn-cancel-text: #656b7c;
70
+ --color-notify-btn-confirm-bg: #34c992;
71
+
72
+ /* Spacing (also powers w-*, h-*, min-w-*, min-h-*, size-*) */
73
+ --spacing-content-width: 650px;
74
+ --spacing-narrow-mode-right-padding: 50px;
75
+ --spacing-toolbox-btn: 26px;
76
+ --spacing-toolbox-btn-mobile: 36px;
77
+ --spacing-icon: 20px;
78
+ --spacing-icon-mobile: 28px;
79
+ --spacing-block-padding-vertical: 0.4em;
80
+
81
+ /* Max-width */
82
+ --max-width-content: 650px;
83
+
84
+ /* Font family */
85
+ --font-mono: 'PT Mono', 'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace;
86
+
87
+ /* Box shadows */
88
+ --shadow-overlay-pane: 0 3px 15px -3px rgba(13, 20, 33, 0.13);
89
+ --shadow-tool-icon: 0 0 0 1px #eff0f1;
90
+ --shadow-tooltip: 0 8px 12px 0 rgba(29, 32, 43, 0.17), 0 4px 5px -3px rgba(5, 6, 12, 0.49);
91
+ --shadow-notify: 0 11px 17px 0 rgba(23, 32, 61, 0.13);
92
+ --shadow-button-focused: inset 0 0 0 1px rgba(7, 161, 227, 0.08);
93
+ --shadow-input: inset 0 1px 2px 0 rgba(35, 44, 72, 0.06);
94
+ --shadow-button-base: 0 2px 2px 0 rgba(18, 30, 57, 0.04);
95
+ --shadow-button-base-hover: 0 1px 3px 0 rgba(18, 30, 57, 0.08);
96
+ --shadow-demo-btn: 0 22px 18px -4px rgba(137, 207, 255, 0.77);
97
+ --shadow-demo-btn-hover: 0 20px 15px -4px rgba(137, 207, 255, 0.77);
98
+ --shadow-demo-btn-primary: 0 7px 8px -4px rgba(137, 207, 255, 0.77);
99
+ --shadow-demo-dark-btn: 0 24px 18px -14px rgba(4, 154, 255, 0.24);
100
+
101
+ /* Animations */
102
+ --animate-fade-in: fade-in 300ms ease forwards;
103
+ --animate-bounce-in: bounceIn 0.75s cubic-bezier(0.215, 0.61, 0.355, 1) forwards;
104
+ --animate-selection-bounce: selectionBounce 0.75s cubic-bezier(0.215, 0.61, 0.355, 1) forwards;
105
+ --animate-button-clicked: buttonClicked 0.75s cubic-bezier(0.215, 0.61, 0.355, 1) forwards;
106
+ --animate-panel-showing: panelShowing 100ms ease;
107
+ --animate-panel-showing-mobile: panelShowingMobile 250ms ease;
108
+ --animate-wobble: wobble 400ms;
109
+ --animate-rotation: rotation 1.2s infinite linear;
110
+ --animate-notify-bounce-in: notifyBounceIn 600ms 1;
111
+
112
+ /* Keyframes */
113
+ @keyframes fade-in {
114
+ from { opacity: 0; }
115
+ to { opacity: 1; }
20
116
  }
21
117
 
22
- .blok-inline-tool-button--active {
23
- @apply bg-icon-active-bg text-icon-active-text;
118
+ @keyframes bounceIn {
119
+ 0% { transform: scale3d(0.9, 0.9, 0.9); }
120
+ 20% { transform: scale3d(1.03, 1.03, 1.03); }
121
+ 60%, 100% { transform: scale3d(1, 1, 1); }
24
122
  }
25
123
 
26
- /* Input element styles */
27
- .blok-input {
28
- @apply w-full rounded-[3px] border border-line-gray px-3 py-2.5 outline-none shadow-input [&[data-blok-placeholder]]:before:!static [&[data-blok-placeholder]]:before:inline-block [&[data-blok-placeholder]]:before:w-0 [&[data-blok-placeholder]]:before:whitespace-nowrap [&[data-blok-placeholder]]:before:pointer-events-none;
124
+ @keyframes selectionBounce {
125
+ 0%, 100% { transform: scale3d(1, 1, 1); }
126
+ 50% { transform: scale3d(1.01, 1.01, 1.01); }
127
+ 70% { transform: scale3d(1, 1, 1); }
29
128
  }
30
129
 
31
- /* Loader styles with spinning animation */
32
- .blok-loader {
33
- @apply relative border border-line-gray before:absolute before:left-1/2 before:top-1/2 before:w-[18px] before:h-[18px] before:rounded-full before:content-[''] before:-ml-[11px] before:-mt-[11px] before:border-2 before:border-line-gray before:border-l-active-icon before:animate-rotation;
130
+ @keyframes buttonClicked {
131
+ 0% { transform: scale3d(0.95, 0.95, 0.95); }
132
+ 60% { transform: scale3d(1.02, 1.02, 1.02); }
133
+ 80%, 100% { transform: scale3d(1, 1, 1); }
34
134
  }
35
135
 
36
- /* Button styles */
37
- .blok-button {
38
- @apply p-[13px] rounded-[3px] border border-line-gray text-[14.9px] bg-white text-center cursor-pointer text-gray-text shadow-button-base hover:bg-[#fbfcfe] hover:shadow-button-base-hover [&_svg]:h-5 [&_svg]:mr-[0.2em] [&_svg]:-mt-0.5;
136
+ @keyframes panelShowing {
137
+ from { opacity: 0; transform: translateY(-8px) scale(0.9); }
138
+ 70% { opacity: 1; transform: translateY(2px); }
139
+ to { transform: translateY(0); }
39
140
  }
40
141
 
41
- /* Settings button styles */
42
- .blok-settings-button {
43
- @apply inline-flex items-center justify-center rounded-[3px] cursor-pointer border-0 outline-none bg-transparent align-bottom text-inherit m-0 min-w-toolbox-btn min-h-toolbox-btn [&_svg]:w-auto [&_svg]:h-auto mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile mobile:rounded-lg mobile:[&_svg]:w-icon-mobile mobile:[&_svg]:h-icon-mobile can-hover:hover:bg-bg-light;
142
+ @keyframes panelShowingMobile {
143
+ from { opacity: 0; transform: translateY(14px) scale(0.98); }
144
+ 70% { opacity: 1; transform: translateY(-4px); }
145
+ to { transform: translateY(0); }
44
146
  }
45
147
 
46
- .blok-settings-button--active {
47
- @apply text-active-icon;
148
+ @keyframes wobble {
149
+ from { transform: translate3d(0, 0, 0); }
150
+ 15% { transform: translate3d(-9%, 0, 0); }
151
+ 30% { transform: translate3d(9%, 0, 0); }
152
+ 45% { transform: translate3d(-4%, 0, 0); }
153
+ 60% { transform: translate3d(4%, 0, 0); }
154
+ 75% { transform: translate3d(-1%, 0, 0); }
155
+ to { transform: translate3d(0, 0, 0); }
48
156
  }
49
157
 
50
- .blok-settings-button--focused {
51
- @apply shadow-button-focused bg-item-focus-bg;
158
+ @keyframes rotation {
159
+ 0% { transform: rotate(0deg); }
160
+ 100% { transform: rotate(360deg); }
52
161
  }
53
162
 
54
- .blok-settings-button--focused-animated {
55
- @apply animate-button-clicked;
163
+ @keyframes notifyBounceIn {
164
+ 0% { opacity: 0; transform: scale(0.3); }
165
+ 50% { opacity: 1; transform: scale(1.05); }
166
+ 70% { transform: scale(0.9); }
167
+ 100% { transform: scale(1); }
56
168
  }
57
169
  }
58
170
 
171
+ /*
172
+ The default border color has changed to `currentcolor` in Tailwind CSS v4,
173
+ so we've added these compatibility styles to make sure everything still
174
+ looks the same as it did with Tailwind CSS v3.
175
+
176
+ If we ever want to remove these styles, we need to add an explicit border
177
+ color utility to any element that depends on these defaults.
178
+ */
179
+ *,
180
+ ::after,
181
+ ::before,
182
+ ::backdrop,
183
+ ::file-selector-button {
184
+ border-color: var(--color-gray-200, currentcolor);
185
+ }
186
+
187
+ /**
188
+ * Component classes for the Styles API.
189
+ * These single-class names can be safely used with classList.add() by external plugins.
190
+ * Each class bundles multiple Tailwind utilities using @apply.
191
+ */
192
+ @utility blok-block {
193
+ /* Base Blok styles - applied to block tool wrappers */
194
+ @apply py-[3px] px-[2px] [&::-webkit-input-placeholder]:leading-normal!;
195
+ }
196
+ @utility blok-inline-tool-button {
197
+ /* Inline Tools styles */
198
+ @apply flex justify-center items-center border-0 rounded-sm h-full p-0 w-7 bg-transparent cursor-pointer leading-normal text-black;
199
+ }
200
+ @utility blok-inline-tool-button--active {
201
+ @apply bg-icon-active-bg text-icon-active-text;
202
+ }
203
+ @utility blok-input {
204
+ /* Input element styles */
205
+ @apply w-full rounded-[3px] border border-line-gray px-3 py-2.5 outline-hidden shadow-input data-blok-placeholder:before:static! data-blok-placeholder:before:inline-block data-blok-placeholder:before:w-0 data-blok-placeholder:before:whitespace-nowrap data-blok-placeholder:before:pointer-events-none;
206
+ }
207
+ @utility blok-loader {
208
+ /* Loader styles with spinning animation */
209
+ @apply relative border border-line-gray before:absolute before:left-1/2 before:top-1/2 before:w-[18px] before:h-[18px] before:rounded-full before:content-[''] before:-ml-[11px] before:-mt-[11px] before:border-2 before:border-line-gray before:border-l-active-icon before:animate-rotation;
210
+ }
211
+ @utility blok-button {
212
+ /* Button styles */
213
+ @apply p-[13px] rounded-[3px] border border-line-gray text-[14.9px] bg-white text-center cursor-pointer text-gray-text shadow-button-base hover:bg-[#fbfcfe] hover:shadow-button-base-hover [&_svg]:h-5 [&_svg]:mr-[0.2em] [&_svg]:-mt-0.5;
214
+ }
215
+ @utility blok-settings-button {
216
+ /* Settings button styles */
217
+ @apply inline-flex items-center justify-center rounded-[3px] cursor-pointer border-0 outline-hidden bg-transparent align-bottom text-inherit m-0 min-w-toolbox-btn min-h-toolbox-btn [&_svg]:w-auto [&_svg]:h-auto mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile mobile:rounded-lg mobile:[&_svg]:w-icon-mobile mobile:[&_svg]:h-icon-mobile can-hover:hover:bg-bg-light;
218
+ }
219
+ @utility blok-settings-button--active {
220
+ @apply text-active-icon;
221
+ }
222
+ @utility blok-settings-button--focused {
223
+ @apply shadow-button-focused bg-item-focus-bg;
224
+ }
225
+ @utility blok-settings-button--focused-animated {
226
+ @apply animate-button-clicked;
227
+ }
228
+
59
229
  /**
60
230
  * Navigation mode styles
61
231
  * Applied when user enters block navigation mode via Escape key
@@ -84,7 +254,7 @@
84
254
  }
85
255
 
86
256
  [data-drop-indicator]::before {
87
- @apply content-[''] absolute w-full max-w-content h-1.5 rounded-sm bg-[#d4e3fc] pointer-events-none z-10;
257
+ @apply content-[''] absolute w-full max-w-content h-1.5 rounded-xs bg-[#d4e3fc] pointer-events-none z-10;
88
258
  left: 50%;
89
259
  margin-left: calc(var(--drop-indicator-depth, 0) * 12px);
90
260
  max-width: calc(650px - var(--drop-indicator-depth, 0) * 24px);
@@ -135,7 +305,7 @@
135
305
  }
136
306
 
137
307
  [data-blok-table-cell-blocks] .blok-block {
138
- @apply p-0 m-0;
308
+ @apply p-0 m-0 min-h-[1.6em];
139
309
  }
140
310
 
141
311
  /**
@@ -146,3 +316,11 @@
146
316
  [data-blok-table-heading-col] {
147
317
  @apply font-semibold bg-gray-50;
148
318
  }
319
+
320
+ /**
321
+ * Table scroll haze overlays
322
+ * Gradient indicators that show horizontal scroll availability
323
+ */
324
+ [data-blok-table-haze][data-blok-table-haze-visible] {
325
+ @apply opacity-100;
326
+ }
@@ -19,10 +19,12 @@ import type {
19
19
  } from '../../../types';
20
20
  import type { MenuConfig } from '../../../types/tools/menu-config';
21
21
  import { DATA_ATTR } from '../../components/constants';
22
- import { IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconHeading } from '../../components/icons';
22
+ import { IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconHeading, IconToggleH1, IconToggleH2, IconToggleH3 } from '../../components/icons';
23
23
  import { PLACEHOLDER_CLASSES, setupPlaceholder } from '../../components/utils/placeholder';
24
24
  import { translateToolTitle } from '../../components/utils/tools';
25
25
  import { twMerge } from '../../components/utils/tw';
26
+ import { ARROW_ICON, ARROW_STYLES, TOGGLE_ATTR, TOGGLE_WRAPPER_STYLES } from '../toggle/constants';
27
+ import { updateArrowState, updateChildrenVisibility } from '../toggle/toggle-lifecycle';
26
28
 
27
29
  /**
28
30
  * Tool's input and output data format
@@ -32,6 +34,8 @@ export interface HeaderData extends BlockToolData {
32
34
  text: string;
33
35
  /** Header's level from 1 to 6 */
34
36
  level: number;
37
+ /** Whether this header has toggle (collapse/expand) behavior */
38
+ isToggleable?: boolean;
35
39
  }
36
40
 
37
41
  /**
@@ -125,6 +129,21 @@ export class Header implements BlockTool {
125
129
  */
126
130
  private _element: HTMLHeadingElement;
127
131
 
132
+ /**
133
+ * Arrow element for toggle heading
134
+ */
135
+ private _arrowElement: HTMLElement | null = null;
136
+
137
+ /**
138
+ * Whether the toggle is currently open (expanded)
139
+ */
140
+ private _isOpen: boolean = false;
141
+
142
+ /**
143
+ * Block ID from the editor
144
+ */
145
+ private blockId?: string;
146
+
128
147
  /**
129
148
  * Render plugin's main Element and fill it with saved data
130
149
  *
@@ -133,20 +152,25 @@ export class Header implements BlockTool {
133
152
  * @param options.config - user config for Tool
134
153
  * @param options.api - Editor API
135
154
  * @param options.readOnly - read only mode flag
155
+ * @param options.block - block instance
136
156
  */
137
- constructor({ data, config, api, readOnly }: BlockToolConstructorOptions<HeaderData, HeaderConfig>) {
157
+ constructor({ data, config, api, readOnly, block }: BlockToolConstructorOptions<HeaderData, HeaderConfig>) {
138
158
  this.api = api;
139
159
  this.readOnly = readOnly;
140
160
 
141
161
  this._settings = config || {};
142
162
  this._data = this.normalizeData(data);
143
163
  this._element = this.getTag();
164
+
165
+ if (block) {
166
+ this.blockId = block.id;
167
+ }
144
168
  }
145
169
 
146
170
  /**
147
171
  * Base styles for all header levels
148
172
  */
149
- private static readonly BASE_STYLES = 'py-[3px] px-[2px] m-0 !leading-[1.3] outline-none [&_p]:!p-0 [&_p]:!m-0 [&_div]:!p-0 [&_div]:!m-0';
173
+ private static readonly BASE_STYLES = 'py-[3px] px-[2px] m-0 leading-[1.3]! outline-hidden [&_p]:p-0! [&_p]:m-0! [&_div]:p-0! [&_div]:m-0!';
150
174
 
151
175
  /**
152
176
  * Styles
@@ -183,10 +207,16 @@ export class Header implements BlockTool {
183
207
  const parsedLevel = parseInt(String(data.level));
184
208
  const isValidLevel = data.level !== undefined && !isNaN(parsedLevel);
185
209
 
186
- return {
210
+ const normalized: HeaderData = {
187
211
  text: data.text || '',
188
212
  level: isValidLevel ? parsedLevel : this.defaultLevel.number,
189
213
  };
214
+
215
+ if (data.isToggleable === true) {
216
+ normalized.isToggleable = true;
217
+ }
218
+
219
+ return normalized;
190
220
  }
191
221
 
192
222
  /**
@@ -198,6 +228,16 @@ export class Header implements BlockTool {
198
228
  return this._element;
199
229
  }
200
230
 
231
+ /**
232
+ * Called after the block is rendered in the DOM.
233
+ * Hides children if the toggle heading is collapsed.
234
+ */
235
+ public rendered(): void {
236
+ if (this._data.isToggleable) {
237
+ this.updateChildrenVisibility();
238
+ }
239
+ }
240
+
201
241
  /**
202
242
  * Returns header block tunes config
203
243
  *
@@ -219,28 +259,40 @@ export class Header implements BlockTool {
219
259
  toolboxEntries[0].data === undefined &&
220
260
  (toolboxEntries[0].title === undefined || toolboxEntries[0].title === 'Heading');
221
261
 
222
- if (toolboxEntries !== undefined && toolboxEntries.length > 0 && !isDefaultToolboxEntry) {
223
- return this.buildSettingsFromToolboxEntries(toolboxEntries);
224
- }
262
+ const levelSettings: MenuConfig = toolboxEntries !== undefined && toolboxEntries.length > 0 && !isDefaultToolboxEntry
263
+ ? this.buildSettingsFromToolboxEntries(toolboxEntries)
264
+ : this.levels.map(level => {
265
+ const translated = this.api.i18n.t(level.nameKey);
266
+ const title = translated !== level.nameKey ? translated : level.name;
267
+
268
+ return {
269
+ icon: level.icon,
270
+ title,
271
+ onActivate: (): void => this.setLevel(level.number),
272
+ closeOnActivate: true,
273
+ isActive: this.currentLevel.number === level.number,
274
+ dataset: {
275
+ 'blok-header-level': String(level.number),
276
+ },
277
+ };
278
+ });
279
+
280
+ const settingsArray = Array.isArray(levelSettings) ? levelSettings : [levelSettings];
225
281
 
226
282
  /**
227
- * Fall back to existing behavior using levels config
283
+ * Add toggle heading option
228
284
  */
229
- return this.levels.map(level => {
230
- const translated = this.api.i18n.t(level.nameKey);
231
- const title = translated !== level.nameKey ? translated : level.name;
232
-
233
- return {
234
- icon: level.icon,
235
- title,
236
- onActivate: (): void => this.setLevel(level.number),
237
- closeOnActivate: true,
238
- isActive: this.currentLevel.number === level.number,
239
- dataset: {
240
- 'blok-header-level': String(level.number),
241
- },
242
- };
285
+ const toggleHeadingTitle = this.api.i18n.t('tools.header.toggleHeading');
286
+
287
+ settingsArray.push({
288
+ icon: ARROW_ICON,
289
+ title: toggleHeadingTitle !== 'tools.header.toggleHeading' ? toggleHeadingTitle : 'Toggle heading',
290
+ onActivate: (): void => this.toggleIsToggleable(),
291
+ closeOnActivate: true,
292
+ isActive: this._data.isToggleable === true,
243
293
  });
294
+
295
+ return settingsArray;
244
296
  }
245
297
 
246
298
  /**
@@ -294,6 +346,7 @@ export class Header implements BlockTool {
294
346
  this.data = {
295
347
  level: level,
296
348
  text: this.data.text,
349
+ isToggleable: this._data.isToggleable,
297
350
  };
298
351
  }
299
352
 
@@ -325,10 +378,16 @@ export class Header implements BlockTool {
325
378
  * @returns saved data
326
379
  */
327
380
  public save(toolsContent: HTMLHeadingElement): HeaderData {
328
- return {
381
+ const data: HeaderData = {
329
382
  text: toolsContent.innerHTML,
330
383
  level: this.currentLevel.number,
331
384
  };
385
+
386
+ if (this._data.isToggleable === true) {
387
+ data.isToggleable = true;
388
+ }
389
+
390
+ return data;
332
391
  }
333
392
 
334
393
  /**
@@ -348,6 +407,7 @@ export class Header implements BlockTool {
348
407
  return {
349
408
  level: false,
350
409
  text: {},
410
+ isToggleable: false,
351
411
  };
352
412
  }
353
413
 
@@ -414,6 +474,27 @@ export class Header implements BlockTool {
414
474
  if (data.text !== undefined) {
415
475
  this._element.innerHTML = this._data.text || '';
416
476
  }
477
+
478
+ /**
479
+ * Re-add toggle arrow after innerHTML assignments (which destroy it).
480
+ * Also update toggle styles and attributes.
481
+ */
482
+ if (this._data.isToggleable) {
483
+ this._element.setAttribute(TOGGLE_ATTR.toggleOpen, String(this._isOpen));
484
+
485
+ if (!this._element.querySelector(`[${TOGGLE_ATTR.toggleArrow}]`)) {
486
+ const arrow = this.buildArrow();
487
+
488
+ this._arrowElement = arrow;
489
+ this._element.prepend(arrow);
490
+ }
491
+
492
+ this._element.className = twMerge(Header.BASE_STYLES, this.currentLevel.styles, PLACEHOLDER_CLASSES, TOGGLE_WRAPPER_STYLES);
493
+ } else {
494
+ this._element.removeAttribute(TOGGLE_ATTR.toggleOpen);
495
+ this._arrowElement = null;
496
+ this._element.className = twMerge(Header.BASE_STYLES, this.currentLevel.styles, PLACEHOLDER_CLASSES);
497
+ }
417
498
  }
418
499
 
419
500
  /**
@@ -434,9 +515,10 @@ export class Header implements BlockTool {
434
515
  tag.innerHTML = this._data.text || '';
435
516
 
436
517
  /**
437
- * Add styles class using twMerge to combine base and level-specific styles
518
+ * Add styles class using twMerge to combine base and level-specific styles.
519
+ * When isToggleable, add flex layout to align arrow and text.
438
520
  */
439
- tag.className = twMerge(Header.BASE_STYLES, this.currentLevel.styles, PLACEHOLDER_CLASSES);
521
+ tag.className = twMerge(Header.BASE_STYLES, this.currentLevel.styles, PLACEHOLDER_CLASSES, this._data.isToggleable ? TOGGLE_WRAPPER_STYLES : '');
440
522
 
441
523
  /**
442
524
  * Apply inline styles for custom overrides (dynamic values from config)
@@ -457,6 +539,17 @@ export class Header implements BlockTool {
457
539
  */
458
540
  tag.contentEditable = this.readOnly ? 'false' : 'true';
459
541
 
542
+ /**
543
+ * Add toggle arrow if isToggleable
544
+ */
545
+ if (this._data.isToggleable) {
546
+ tag.setAttribute(TOGGLE_ATTR.toggleOpen, String(this._isOpen));
547
+
548
+ const arrow = this.buildArrow();
549
+ this._arrowElement = arrow;
550
+ tag.prepend(arrow);
551
+ }
552
+
460
553
  /**
461
554
  * Add Placeholder with caret positioning support
462
555
  */
@@ -469,6 +562,71 @@ export class Header implements BlockTool {
469
562
  return tag;
470
563
  }
471
564
 
565
+ /**
566
+ * Build the arrow element for toggle heading.
567
+ *
568
+ * @returns The arrow element
569
+ */
570
+ private buildArrow(): HTMLElement {
571
+ const arrow = document.createElement('div');
572
+ arrow.className = ARROW_STYLES;
573
+ arrow.setAttribute(TOGGLE_ATTR.toggleArrow, '');
574
+ arrow.setAttribute('role', 'button');
575
+ arrow.setAttribute('tabindex', '-1');
576
+ arrow.setAttribute('aria-label', 'Expand');
577
+ arrow.contentEditable = 'false';
578
+ arrow.innerHTML = ARROW_ICON;
579
+
580
+ if (this._isOpen) {
581
+ arrow.style.transform = 'rotate(90deg)';
582
+ }
583
+
584
+ arrow.addEventListener('click', (event: MouseEvent) => {
585
+ event.stopPropagation();
586
+ this.toggleOpen();
587
+ });
588
+
589
+ return arrow;
590
+ }
591
+
592
+ /**
593
+ * Toggle the isToggleable state on/off.
594
+ * Called from the settings menu.
595
+ */
596
+ private toggleIsToggleable(): void {
597
+ const wasToggleable = this._data.isToggleable === true;
598
+
599
+ this.data = {
600
+ level: this._data.level,
601
+ text: this._data.text,
602
+ isToggleable: !wasToggleable || undefined,
603
+ };
604
+ }
605
+
606
+ /**
607
+ * Toggle the open/closed state of the toggle heading.
608
+ */
609
+ private toggleOpen(): void {
610
+ this._isOpen = !this._isOpen;
611
+
612
+ if (this._arrowElement && this._element) {
613
+ updateArrowState(this._arrowElement, this._element, this._isOpen);
614
+ }
615
+
616
+ this.updateChildrenVisibility();
617
+ }
618
+
619
+ /**
620
+ * Show or hide child blocks based on the toggle's open state.
621
+ */
622
+ private updateChildrenVisibility(): void {
623
+ if (this.blockId === undefined) {
624
+ return;
625
+ }
626
+
627
+ updateChildrenVisibility(this.api, this.blockId, this._isOpen);
628
+ }
629
+
472
630
  /**
473
631
  * Get current level
474
632
  *
@@ -636,7 +794,7 @@ export class Header implements BlockTool {
636
794
  * @returns ToolboxConfig array with entries for H1-H6
637
795
  */
638
796
  public static get toolbox(): ToolboxConfig {
639
- return Header.DEFAULT_LEVELS.map(level => ({
797
+ const headingEntries = Header.DEFAULT_LEVELS.map(level => ({
640
798
  icon: level.icon,
641
799
  title: level.name,
642
800
  titleKey: level.nameKey,
@@ -645,5 +803,25 @@ export class Header implements BlockTool {
645
803
  searchTerms: [`h${level.number}`, 'title', 'header', 'heading'],
646
804
  shortcut: '#'.repeat(level.number),
647
805
  }));
806
+
807
+ const toggleHeadingIcons: Record<number, string> = {
808
+ 1: IconToggleH1,
809
+ 2: IconToggleH2,
810
+ 3: IconToggleH3,
811
+ };
812
+
813
+ const toggleHeadingEntries = Header.DEFAULT_LEVELS
814
+ .filter(level => level.number <= 3)
815
+ .map(level => ({
816
+ icon: toggleHeadingIcons[level.number],
817
+ title: `Toggle heading ${level.number}`,
818
+ titleKey: `tools.header.toggleHeading${level.number}`,
819
+ name: `toggle-header-${level.number}`,
820
+ data: { level: level.number, isToggleable: true },
821
+ searchTerms: ['toggle', 'heading', `h${level.number}`, 'collapsible'],
822
+ }));
823
+
824
+ return [...headingEntries, ...toggleHeadingEntries];
648
825
  }
826
+
649
827
  }