@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
@@ -0,0 +1,241 @@
1
+ import { COLOR_PRESETS } from '../shared/color-presets';
2
+
3
+ /**
4
+ * Convert an HSL color (H in degrees, S and L as 0-100 percentages) to an RGB tuple.
5
+ */
6
+ function hslToRgb(h: number, s: number, l: number): [number, number, number] {
7
+ const sNorm = s / 100;
8
+ const lNorm = l / 100;
9
+
10
+ if (sNorm === 0) {
11
+ const gray = Math.round(lNorm * 255);
12
+
13
+ return [gray, gray, gray];
14
+ }
15
+
16
+ const wrapHue = (t: number): number => {
17
+ if (t < 0) {
18
+ return t + 1;
19
+ }
20
+
21
+ if (t > 1) {
22
+ return t - 1;
23
+ }
24
+
25
+ return t;
26
+ };
27
+
28
+ const hueToChannel = (p: number, q: number, t: number): number => {
29
+ const wrapped = wrapHue(t);
30
+
31
+ if (wrapped < 1 / 6) {
32
+ return p + (q - p) * 6 * wrapped;
33
+ }
34
+
35
+ if (wrapped < 1 / 2) {
36
+ return q;
37
+ }
38
+
39
+ if (wrapped < 2 / 3) {
40
+ return p + (q - p) * (2 / 3 - wrapped) * 6;
41
+ }
42
+
43
+ return p;
44
+ };
45
+
46
+ const q = lNorm < 0.5 ? lNorm * (1 + sNorm) : lNorm + sNorm - lNorm * sNorm;
47
+ const p = 2 * lNorm - q;
48
+ const hNorm = h / 360;
49
+
50
+ return [
51
+ Math.round(hueToChannel(p, q, hNorm + 1 / 3) * 255),
52
+ Math.round(hueToChannel(p, q, hNorm) * 255),
53
+ Math.round(hueToChannel(p, q, hNorm - 1 / 3) * 255),
54
+ ];
55
+ }
56
+
57
+ /**
58
+ * Parse a CSS color string to an [R, G, B] tuple.
59
+ *
60
+ * Supported formats:
61
+ * - `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`
62
+ * - `rgb(r, g, b)`, `rgba(r, g, b, a)`
63
+ * - `hsl(h, s%, l%)`, `hsla(h, s%, l%, a)`
64
+ *
65
+ * Alpha values are ignored since only RGB is needed for distance calculation.
66
+ * Returns null if the string cannot be parsed.
67
+ */
68
+ export function parseColor(cssColor: string): [number, number, number] | null {
69
+ /* 6-digit hex: #rrggbb */
70
+ const hex6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(cssColor);
71
+
72
+ if (hex6) {
73
+ return [parseInt(hex6[1], 16), parseInt(hex6[2], 16), parseInt(hex6[3], 16)];
74
+ }
75
+
76
+ /* 8-digit hex: #rrggbbaa (alpha ignored) */
77
+ const hex8 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})[0-9a-f]{2}$/i.exec(cssColor);
78
+
79
+ if (hex8) {
80
+ return [parseInt(hex8[1], 16), parseInt(hex8[2], 16), parseInt(hex8[3], 16)];
81
+ }
82
+
83
+ /* 3-digit hex: #rgb */
84
+ const hex3 = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(cssColor);
85
+
86
+ if (hex3) {
87
+ return [
88
+ parseInt(hex3[1] + hex3[1], 16),
89
+ parseInt(hex3[2] + hex3[2], 16),
90
+ parseInt(hex3[3] + hex3[3], 16),
91
+ ];
92
+ }
93
+
94
+ /* 4-digit hex: #rgba (alpha ignored) */
95
+ const hex4 = /^#([0-9a-f])([0-9a-f])([0-9a-f])[0-9a-f]$/i.exec(cssColor);
96
+
97
+ if (hex4) {
98
+ return [
99
+ parseInt(hex4[1] + hex4[1], 16),
100
+ parseInt(hex4[2] + hex4[2], 16),
101
+ parseInt(hex4[3] + hex4[3], 16),
102
+ ];
103
+ }
104
+
105
+ /* rgb() and rgba() — alpha component is optional and ignored */
106
+ const rgba = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*[\d.]+\s*)?\)$/i.exec(cssColor);
107
+
108
+ if (rgba) {
109
+ return [Number(rgba[1]), Number(rgba[2]), Number(rgba[3])];
110
+ }
111
+
112
+ /* hsl() and hsla() — alpha component is optional and ignored */
113
+ const hsla = /^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*(?:,\s*[\d.]+\s*)?\)$/i.exec(cssColor);
114
+
115
+ if (hsla) {
116
+ return hslToRgb(Number(hsla[1]), Number(hsla[2]), Number(hsla[3]));
117
+ }
118
+
119
+ return null;
120
+ }
121
+
122
+ /** Saturation threshold below which a color is considered achromatic (gray/black/white) */
123
+ const ACHROMATIC_THRESHOLD = 0.10;
124
+
125
+ /**
126
+ * Convert an RGB tuple (0-255 per channel) to HSL.
127
+ *
128
+ * @returns [H, S, L] where H is 0-360 degrees, S and L are 0-1
129
+ */
130
+ function rgbToHsl(rgb: [number, number, number]): [number, number, number] {
131
+ const r = rgb[0] / 255;
132
+ const g = rgb[1] / 255;
133
+ const b = rgb[2] / 255;
134
+
135
+ const max = Math.max(r, g, b);
136
+ const min = Math.min(r, g, b);
137
+ const l = (max + min) / 2;
138
+
139
+ if (max === min) {
140
+ return [0, 0, l];
141
+ }
142
+
143
+ const d = max - min;
144
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
145
+
146
+ const hueFromMax = (channel: number): number => {
147
+ if (channel === r) {
148
+ return ((g - b) / d + (g < b ? 6 : 0)) / 6;
149
+ }
150
+
151
+ if (channel === g) {
152
+ return ((b - r) / d + 2) / 6;
153
+ }
154
+
155
+ return ((r - g) / d + 4) / 6;
156
+ };
157
+
158
+ return [hueFromMax(max) * 360, s, l];
159
+ }
160
+
161
+ /**
162
+ * Compute the perceptual distance between two colors using HSL with hue weighting.
163
+ *
164
+ * Achromatic colors (saturation below threshold) are compared by lightness only,
165
+ * and are penalized heavily against chromatic colors to prevent gray mapping to
166
+ * a saturated preset.
167
+ *
168
+ * Chromatic colors use weighted distance: hue (most important), saturation, lightness.
169
+ * Hue distance is circular (wraps at 360 degrees).
170
+ */
171
+ function hslDistance(
172
+ hsl1: [number, number, number],
173
+ hsl2: [number, number, number]
174
+ ): number {
175
+ const HUE_WEIGHT = 8;
176
+ const SAT_WEIGHT = 1;
177
+ const LIGHT_WEIGHT = 1;
178
+
179
+ const isAchromatic1 = hsl1[1] < ACHROMATIC_THRESHOLD;
180
+ const isAchromatic2 = hsl2[1] < ACHROMATIC_THRESHOLD;
181
+
182
+ /* Both achromatic: compare lightness only */
183
+ if (isAchromatic1 && isAchromatic2) {
184
+ const lightDiff = Math.abs(hsl1[2] - hsl2[2]);
185
+
186
+ return lightDiff * lightDiff;
187
+ }
188
+
189
+ /* One achromatic, one chromatic: large penalty so grays never match saturated presets */
190
+ if (isAchromatic1 !== isAchromatic2) {
191
+ return 1000;
192
+ }
193
+
194
+ /* Both chromatic: weighted HSL distance */
195
+ const hueDiff = Math.abs(hsl1[0] - hsl2[0]);
196
+ const circularHue = Math.min(hueDiff, 360 - hueDiff) / 180; // normalized 0-1
197
+ const satDiff = Math.abs(hsl1[1] - hsl2[1]);
198
+ const lightDiff = Math.abs(hsl1[2] - hsl2[2]);
199
+
200
+ return (
201
+ HUE_WEIGHT * circularHue * circularHue +
202
+ SAT_WEIGHT * satDiff * satDiff +
203
+ LIGHT_WEIGHT * lightDiff * lightDiff
204
+ );
205
+ }
206
+
207
+ /**
208
+ * Map an arbitrary CSS color to the nearest Blok preset color.
209
+ *
210
+ * Uses HSL-weighted distance with hue priority for perceptually intuitive results.
211
+ *
212
+ * @param cssColor - CSS color string (hex or rgb)
213
+ * @param mode - 'text' for text color presets, 'bg' for background presets
214
+ * @returns the nearest preset hex color, or the input unchanged if unparseable
215
+ */
216
+ export function mapToNearestPresetColor(cssColor: string, mode: 'text' | 'bg'): string {
217
+ const rgb = parseColor(cssColor);
218
+
219
+ if (rgb === null) {
220
+ return cssColor;
221
+ }
222
+
223
+ const hsl = rgbToHsl(rgb);
224
+
225
+ const best = COLOR_PRESETS.reduce<{ color: string; distance: number }>(
226
+ (acc, preset) => {
227
+ const presetRgb = parseColor(preset[mode]);
228
+
229
+ if (presetRgb === null) {
230
+ return acc;
231
+ }
232
+
233
+ const distance = hslDistance(hsl, rgbToHsl(presetRgb));
234
+
235
+ return distance < acc.distance ? { color: preset[mode], distance } : acc;
236
+ },
237
+ { color: cssColor, distance: Infinity }
238
+ );
239
+
240
+ return best.color;
241
+ }
@@ -4,19 +4,19 @@ import type { NotifierOptions, ConfirmNotifierOptions, PromptNotifierOptions } f
4
4
 
5
5
  export const CSS = {
6
6
  wrapper: twJoin(
7
- 'fixed z-[2] bottom-5 left-5',
7
+ 'fixed z-2 bottom-5 left-5',
8
8
  'font-[-apple-system,BlinkMacSystemFont,"Segoe_UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira_Sans","Droid_Sans","Helvetica_Neue",sans-serif]'
9
9
  ),
10
10
  notification: twJoin(
11
11
  'relative w-[230px] mt-[15px] py-[13px] px-4',
12
- 'bg-white shadow-[0_11px_17px_0_rgba(23,32,61,0.13)] rounded-[5px]',
13
- 'text-sm leading-[1.4em] break-words',
12
+ 'bg-white shadow-notify rounded-[5px]',
13
+ 'text-sm leading-[1.4em] wrap-break-word',
14
14
  'before:content-[""] before:absolute before:block before:top-0 before:left-0',
15
15
  'before:w-[3px] before:h-[calc(100%-6px)] before:m-[3px] before:rounded-[5px] before:bg-transparent'
16
16
  ),
17
17
  crossBtn: twJoin(
18
18
  'absolute top-[7px] right-[15px] w-2.5 h-2.5 p-[5px] opacity-55 cursor-pointer',
19
- 'before:content-[""] before:absolute before:left-[9px] before:top-[5px] before:h-3 before:w-0.5 before:bg-[#575d67] before:rotate-[-45deg]',
19
+ 'before:content-[""] before:absolute before:left-[9px] before:top-[5px] before:h-3 before:w-0.5 before:bg-[#575d67] before:-rotate-45',
20
20
  'after:content-[""] after:absolute after:left-[9px] after:top-[5px] after:h-3 after:w-0.5 after:bg-[#575d67] after:rotate-45',
21
21
  'hover:opacity-100'
22
22
  ),
@@ -26,16 +26,16 @@ export const CSS = {
26
26
  cancelBtn: 'bg-[#f2f5f7] shadow-[0_2px_1px_0_rgba(16,19,29,0)] text-[#656b7c] hover:bg-[#e9ecee]',
27
27
  input: twJoin(
28
28
  'max-w-[130px] py-[5px] px-2.5 bg-[#f7f7f7] border-0 rounded-[3px]',
29
- 'text-[13px] text-[#656b7c] outline-none',
29
+ 'text-[13px] text-[#656b7c] outline-hidden',
30
30
  'placeholder:text-[#656b7c] focus:placeholder:text-[rgba(101,107,124,0.3)]'
31
31
  ),
32
32
  successNotification: twJoin(
33
- '!bg-[#fafffe]',
34
- 'before:!bg-[#41ffb1]'
33
+ 'bg-[#fafffe]!',
34
+ 'before:bg-[#41ffb1]!'
35
35
  ),
36
36
  errorNotification: twJoin(
37
- '!bg-[#fffbfb]',
38
- 'before:!bg-[#fb5d5d]'
37
+ 'bg-[#fffbfb]!',
38
+ 'before:bg-[#fb5d5d]!'
39
39
  ),
40
40
  };
41
41
 
@@ -19,10 +19,10 @@ export const PLACEHOLDER_CLASSES: string[] = [
19
19
  'empty:before:text-gray-text',
20
20
  'empty:before:cursor-text',
21
21
  'empty:before:content-[attr(data-placeholder)]',
22
- '[&[data-blok-empty=true]]:before:pointer-events-none',
23
- '[&[data-blok-empty=true]]:before:text-gray-text',
24
- '[&[data-blok-empty=true]]:before:cursor-text',
25
- '[&[data-blok-empty=true]]:before:content-[attr(data-placeholder)]',
22
+ 'data-[blok-empty=true]:before:pointer-events-none',
23
+ 'data-[blok-empty=true]:before:text-gray-text',
24
+ 'data-[blok-empty=true]:before:cursor-text',
25
+ 'data-[blok-empty=true]:before:content-[attr(data-placeholder)]',
26
26
  ];
27
27
 
28
28
  /**
@@ -34,10 +34,10 @@ export const PLACEHOLDER_ACTIVE_CLASSES: string[] = [
34
34
  'empty:before:text-gray-text',
35
35
  'empty:before:cursor-text',
36
36
  'empty:before:content-[attr(data-blok-placeholder-active)]',
37
- '[&[data-empty=true]]:before:pointer-events-none',
38
- '[&[data-empty=true]]:before:text-gray-text',
39
- '[&[data-empty=true]]:before:cursor-text',
40
- '[&[data-empty=true]]:before:content-[attr(data-blok-placeholder-active)]',
37
+ 'data-[empty=true]:before:pointer-events-none',
38
+ 'data-[empty=true]:before:text-gray-text',
39
+ 'data-[empty=true]:before:cursor-text',
40
+ 'data-[empty=true]:before:content-[attr(data-blok-placeholder-active)]',
41
41
  ];
42
42
 
43
43
  /**
@@ -56,6 +56,22 @@ export const PLACEHOLDER_FOCUS_ONLY_CLASSES: string[] = [
56
56
  '[&[data-empty=true]:focus]:before:content-[attr(data-blok-placeholder-active)]',
57
57
  ];
58
58
 
59
+ /**
60
+ * Placeholder classes that show when the editor is empty (ancestor has data-blok-empty="true").
61
+ * Shows placeholder without requiring focus — used for initial empty-state hint.
62
+ * Uses data-blok-placeholder-active attribute for the placeholder text.
63
+ */
64
+ export const PLACEHOLDER_EMPTY_EDITOR_CLASSES: string[] = [
65
+ 'empty:in-data-[blok-empty=true]:before:pointer-events-none',
66
+ 'empty:in-data-[blok-empty=true]:before:text-gray-text',
67
+ 'empty:in-data-[blok-empty=true]:before:cursor-text',
68
+ 'empty:in-data-[blok-empty=true]:before:content-[attr(data-blok-placeholder-active)]',
69
+ '[[data-blok-empty=true]_&[data-empty=true]]:before:pointer-events-none',
70
+ '[[data-blok-empty=true]_&[data-empty=true]]:before:text-gray-text',
71
+ '[[data-blok-empty=true]_&[data-empty=true]]:before:cursor-text',
72
+ '[[data-blok-empty=true]_&[data-empty=true]]:before:content-[attr(data-blok-placeholder-active)]',
73
+ ];
74
+
59
75
  /**
60
76
  * Check if an element's content is empty
61
77
  *
@@ -10,7 +10,7 @@ export const css = {
10
10
  * Note: noHover state is handled via [data-blok-popover-item-no-hover] which disables hover
11
11
  * Priority order: active < hover < focus (focus wins when navigating with keyboard)
12
12
  */
13
- item: 'flex items-center select-none border-none bg-transparent rounded-md px-2 py-[var(--item-padding)] text-text-primary mb-px [&[data-blok-popover-item-active]]:bg-icon-active-bg [&[data-blok-popover-item-active]]:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg [&[data-blok-force-hover]]:cursor-pointer [&[data-blok-force-hover]]:bg-item-hover-bg [&[data-blok-focused="true"]]:bg-item-focus-bg [&[data-blok-popover-item-no-hover]]:hover:bg-transparent [&[data-blok-popover-item-no-hover]]:cursor-default can-hover:[&[data-blok-popover-item-destructive]]:hover:text-item-destructive-text can-hover:[&[data-blok-popover-item-destructive]]:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
13
+ item: 'flex items-center select-none border-none bg-transparent rounded-md px-2 py-(--item-padding) text-text-primary mb-px data-blok-popover-item-active:bg-icon-active-bg data-blok-popover-item-active:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg data-blok-force-hover:cursor-pointer data-blok-force-hover:bg-item-hover-bg data-[blok-focused="true"]:bg-item-focus-bg data-blok-popover-item-no-hover:hover:bg-transparent data-blok-popover-item-no-hover:cursor-default can-hover:data-blok-popover-item-destructive:hover:text-item-destructive-text can-hover:data-blok-popover-item-destructive:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
14
14
 
15
15
  /**
16
16
  * Item disabled state
@@ -37,7 +37,7 @@ export const cssInline = {
37
37
  /**
38
38
  * Item in inline context - more compact styling
39
39
  */
40
- item: 'rounded p-1',
40
+ item: 'rounded-sm p-1',
41
41
  };
42
42
 
43
43
  /**
@@ -257,7 +257,9 @@ export class PopoverItemDefault extends PopoverItem {
257
257
  if (title !== undefined) {
258
258
  const titleEl = document.createElement('div');
259
259
 
260
- titleEl.className = 'mr-auto truncate text-sm font-medium leading-5';
260
+ titleEl.className = params.secondaryLabel
261
+ ? 'grow truncate text-sm font-medium leading-5'
262
+ : 'mr-auto truncate text-sm font-medium leading-5';
261
263
  titleEl.setAttribute(DATA_ATTR.popoverItemTitle, '');
262
264
  titleEl.setAttribute('data-blok-testid', 'popover-item-title');
263
265
  titleEl.textContent = title;
@@ -270,7 +272,7 @@ export class PopoverItemDefault extends PopoverItem {
270
272
  if (params.secondaryLabel) {
271
273
  const secondaryEl = document.createElement('div');
272
274
 
273
- secondaryEl.className = 'whitespace-nowrap pr-1.5 text-xs font-light tracking-[0.25px] text-text-secondary opacity-60';
275
+ secondaryEl.className = 'min-w-14 text-right whitespace-nowrap pl-3 pr-1.5 text-xs font-light tracking-[0.25px] text-text-secondary opacity-60';
274
276
  secondaryEl.setAttribute(DATA_ATTR.popoverItemSecondaryTitle, '');
275
277
  secondaryEl.setAttribute('data-blok-testid', 'popover-item-secondary-title');
276
278
  secondaryEl.textContent = params.secondaryLabel;
@@ -324,8 +326,8 @@ export class PopoverItemDefault extends PopoverItem {
324
326
 
325
327
  return twMerge(
326
328
  css.item,
327
- // Asymmetric padding for vertical popovers (better visual balance)
328
- !isInline && !isNestedInline && 'pl-2 pr-8',
329
+ // Asymmetric padding: pr-8 for visual balance, pr-2 when secondary label or chevron occupies right edge
330
+ !isInline && !isNestedInline && (this.params.secondaryLabel || (this.hasChildren && !this.isChevronHidden) ? 'pl-2 pr-2' : 'pl-2 pr-8'),
329
331
  isInline && cssInline.item,
330
332
  isNestedInline && cssNestedInline.item,
331
333
  this.params.isDisabled && css.itemDisabled
@@ -341,8 +343,8 @@ export class PopoverItemDefault extends PopoverItem {
341
343
  isInline && 'w-auto h-auto [&_svg]:w-icon [&_svg]:h-icon mobile:[&_svg]:w-icon-mobile mobile:[&_svg]:h-icon-mobile',
342
344
  isNestedInline && 'w-toolbox-btn h-toolbox-btn',
343
345
  iconWithGap && 'mr-3',
344
- iconWithGap && isInline && 'shadow-none bg-transparent !mr-0',
345
- iconWithGap && isNestedInline && '!mr-2',
346
+ iconWithGap && isInline && 'shadow-none bg-transparent mr-0!',
347
+ iconWithGap && isNestedInline && 'mr-2!',
346
348
  isWobbling && 'animate-wobble'
347
349
  );
348
350
  }
@@ -382,10 +384,10 @@ export class PopoverItemDefault extends PopoverItem {
382
384
 
383
385
  if (isHidden) {
384
386
  this.nodes.root.setAttribute(DATA_ATTR.hidden, 'true');
385
- this.nodes.root.classList.add('!hidden');
387
+ this.nodes.root.classList.add('hidden!');
386
388
  } else {
387
389
  this.nodes.root.removeAttribute(DATA_ATTR.hidden);
388
- this.nodes.root.classList.remove('!hidden');
390
+ this.nodes.root.classList.remove('hidden!');
389
391
  }
390
392
  }
391
393
 
@@ -400,10 +402,10 @@ export class PopoverItemDefault extends PopoverItem {
400
402
 
401
403
  if (isFocused) {
402
404
  this.nodes.root.setAttribute(DATA_ATTR.focused, 'true');
403
- this.nodes.root.classList.add('!bg-item-focus-bg');
405
+ this.nodes.root.classList.add('bg-item-focus-bg!');
404
406
  } else {
405
407
  this.nodes.root.removeAttribute(DATA_ATTR.focused);
406
- this.nodes.root.classList.remove('!bg-item-focus-bg');
408
+ this.nodes.root.classList.remove('bg-item-focus-bg!');
407
409
  }
408
410
  }
409
411
 
@@ -486,7 +488,7 @@ export class PopoverItemDefault extends PopoverItem {
486
488
  }
487
489
 
488
490
  this.nodes.root.removeAttribute(DATA_ATTR.popoverItemConfirmation);
489
- this.nodes.root.classList.remove('!bg-item-confirm-bg', '!text-white');
491
+ this.nodes.root.classList.remove('bg-item-confirm-bg!', 'text-white!');
490
492
 
491
493
  this.restoreOriginalIcon();
492
494
  this.restoreOriginalTitle();
@@ -502,7 +504,7 @@ export class PopoverItemDefault extends PopoverItem {
502
504
  }
503
505
 
504
506
  this.nodes.root.setAttribute(DATA_ATTR.popoverItemConfirmation, 'true');
505
- this.nodes.root.classList.add('!bg-item-confirm-bg', '!text-white');
507
+ this.nodes.root.classList.add('bg-item-confirm-bg!', 'text-white!');
506
508
 
507
509
  this.updateIcon(params.icon);
508
510
  this.updateTitle(params);
@@ -2,7 +2,7 @@
2
2
  * CSS class names to be used in popover search input class
3
3
  */
4
4
  export const css = {
5
- wrapper: 'bg-[#F8F8F8] border border-[rgba(226,226,229,0.20)] rounded p-0.5 grid grid-cols-[auto_auto_1fr] grid-rows-[auto]',
5
+ wrapper: 'bg-[#F8F8F8] border border-[rgba(226,226,229,0.20)] rounded-sm p-0.5 grid grid-cols-[auto_auto_1fr] grid-rows-[auto]',
6
6
  icon: 'w-toolbox-btn h-toolbox-btn flex items-center justify-center mr-2 [&_svg]:w-icon [&_svg]:h-icon [&_svg]:text-gray-text',
7
- input: "text-sm outline-none font-medium font-inherit border-0 bg-transparent m-0 p-0 leading-[22px] min-w-[calc(100%-theme('spacing.6')-10px)] placeholder:text-gray-text placeholder:font-medium",
7
+ input: "text-sm outline-hidden font-medium font-inherit border-0 bg-transparent m-0 p-0 leading-[22px] min-w-[calc(100%-(--spacing(6))-10px)] placeholder:text-gray-text placeholder:font-medium",
8
8
  };
@@ -114,7 +114,10 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
114
114
 
115
115
  // Update DOM state
116
116
  this.nodes.popover.setAttribute(DATA_ATTR.popoverOpened, 'true');
117
- this.nodes.popoverContainer.classList.add(...css.popoverContainerOpened.split(' '));
117
+ this.nodes.popoverContainer.className = twMerge(
118
+ this.nodes.popoverContainer.className,
119
+ css.popoverContainerOpened
120
+ );
118
121
 
119
122
  /**
120
123
  * Refresh active states for all items.
@@ -142,7 +145,7 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
142
145
  this.nodes.popover.removeAttribute(DATA_ATTR.popoverOpened);
143
146
  this.nodes.popover.removeAttribute(DATA_ATTR.popoverOpenTop);
144
147
  this.nodes.popover.removeAttribute(DATA_ATTR.popoverOpenLeft);
145
- this.nodes.popoverContainer.classList.remove(...css.popoverContainerOpened.split(' '));
148
+ this.nodes.popoverContainer.className = css.popoverContainer;
146
149
 
147
150
  this.itemsDefault.forEach(item => item.reset());
148
151
 
@@ -174,6 +177,12 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
174
177
  // No-op in base class. PopoverDesktop overrides this.
175
178
  }
176
179
 
180
+ /**
181
+ * Names of items that have been explicitly hidden via toggleItemHiddenByName.
182
+ * These items must stay hidden even when filterItems would normally show them.
183
+ */
184
+ private readonly permanentlyHiddenNames = new Set<string>();
185
+
177
186
  /**
178
187
  * Toggles hidden state of all items matching the given name
179
188
  * @param name - name of the items to toggle
@@ -183,6 +192,21 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
183
192
  this.items
184
193
  .filter(item => item.name === name)
185
194
  .forEach(item => item.toggleHidden(isHidden));
195
+
196
+ if (isHidden) {
197
+ this.permanentlyHiddenNames.add(name);
198
+ } else {
199
+ this.permanentlyHiddenNames.delete(name);
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Returns true if the given item name was explicitly hidden via toggleItemHiddenByName.
205
+ * Used by subclasses to prevent filter logic from un-hiding restricted items.
206
+ * @param name - item name to check
207
+ */
208
+ protected isNamePermanentlyHidden(name: string): boolean {
209
+ return this.permanentlyHiddenNames.has(name);
186
210
  }
187
211
 
188
212
  /**
@@ -326,7 +350,7 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
326
350
  * This is useful for items that determine their active state dynamically (e.g., inline tools).
327
351
  * @param item - popover item to refresh
328
352
  */
329
- private refreshItemActiveState(item: PopoverItem): void {
353
+ protected refreshItemActiveState(item: PopoverItem): void {
330
354
  if (!(item instanceof PopoverItemDefault)) {
331
355
  return;
332
356
  }
@@ -138,6 +138,18 @@ export class PopoverDesktop extends PopoverAbstract {
138
138
  return this.flipper.hasFocus();
139
139
  }
140
140
 
141
+ /**
142
+ * Toggles hidden state of all items matching the given name.
143
+ * Invalidates the cached size so the next access re-measures the popover.
144
+ * @param name - name of the items to toggle
145
+ * @param isHidden - true to hide, false to show
146
+ */
147
+ public override toggleItemHiddenByName(name: string, isHidden: boolean): void {
148
+ super.toggleItemHiddenByName(name, isHidden);
149
+
150
+ this._size = undefined;
151
+ }
152
+
141
153
  /**
142
154
  * Scroll position inside items container of the popover
143
155
  */
@@ -453,6 +465,17 @@ export class PopoverDesktop extends PopoverAbstract {
453
465
  // Apply nested popover positioning (moved from popover.css)
454
466
  this.applyNestedPopoverPositioning(nestedPopoverEl);
455
467
 
468
+ /**
469
+ * Refresh trigger item's active state after any click inside the nested popover.
470
+ * This handles the case where a child action (e.g. color swatch click) changes
471
+ * the trigger tool's active state but the parent popover is not aware of it.
472
+ */
473
+ this.listeners.on(nestedPopoverEl, 'click', () => {
474
+ if (this.nestedPopoverTriggerItem !== null) {
475
+ this.refreshItemActiveState(this.nestedPopoverTriggerItem);
476
+ }
477
+ });
478
+
456
479
  this.nestedPopover.show();
457
480
  this.flipper?.deactivate();
458
481
 
@@ -481,8 +504,8 @@ export class PopoverDesktop extends PopoverAbstract {
481
504
  // Apply position: absolute for nested container
482
505
  nestedContainer.style.position = 'absolute';
483
506
 
484
- // Get parent width - use computed width if --width is 'auto'
485
- const parentWidth = this.params.width === 'auto'
507
+ // Get parent width - use computed width if --width resolves to 'auto'
508
+ const parentWidth = this.params.width === undefined || this.params.width === 'auto'
486
509
  ? `${this.nodes.popoverContainer.offsetWidth}px`
487
510
  : 'var(--width)';
488
511
 
@@ -677,7 +700,7 @@ export class PopoverDesktop extends PopoverAbstract {
677
700
  const isDefaultItem = item instanceof PopoverItemDefault;
678
701
  const isSeparatorOrHtml = item instanceof PopoverItemSeparator || item instanceof PopoverItemHtml;
679
702
  const isHidden = isDefaultItem
680
- ? !matchingItems.includes(item)
703
+ ? !matchingItems.includes(item) || (item.name !== undefined && this.isNamePermanentlyHidden(item.name))
681
704
  : isSeparatorOrHtml && (isNothingFound || !isEmptyQuery);
682
705
 
683
706
  item.toggleHidden(isHidden);
@@ -7,6 +7,7 @@ import type { PopoverItem } from './components/popover-item';
7
7
  import { PopoverItemDefault, PopoverItemType , css as popoverItemCls } from './components/popover-item';
8
8
  import { PopoverItemHtml } from './components/popover-item/popover-item-html/popover-item-html';
9
9
  import { PopoverDesktop } from './popover-desktop';
10
+ import { PopoverRegistry } from './popover-registry';
10
11
  import { css, cssInline, CSSVariables, getNestedLevelAttrValue } from './popover.const';
11
12
 
12
13
  import type { PopoverParams } from '@/types/utils/popover/popover';
@@ -66,6 +67,9 @@ export class PopoverInline extends PopoverDesktop {
66
67
  this.nodes.popoverContainer.style.height = '';
67
68
  }
68
69
 
70
+ // Unregister from PopoverRegistry (from abstract)
71
+ PopoverRegistry.instance.unregister(this);
72
+
69
73
  // Emit closed event (from abstract)
70
74
  this.emit(PopoverEvent.Closed);
71
75
  };
@@ -307,7 +311,16 @@ export class PopoverInline extends PopoverDesktop {
307
311
 
308
312
  // Apply level-1 specific positioning styles
309
313
  if (nestedPopover.nestingLevel === 1 && nestedContainer instanceof HTMLElement) {
310
- nestedContainer.className = twMerge(nestedContainer.className, 'left-0');
314
+ // Position near the trigger item, clamped to stay within the toolbar bounds
315
+ const itemEl = item.getElement();
316
+ const triggerLeft = itemEl ? itemEl.offsetLeft + this.offsetLeft : 0;
317
+ const nestedWidth = nestedPopover.size.width;
318
+ const toolbarWidth = this.nodes.popoverContainer.offsetWidth;
319
+ const maxLeft = Math.max(0, toolbarWidth - nestedWidth);
320
+ const left = Math.max(0, Math.min(triggerLeft, maxLeft));
321
+
322
+ nestedContainer.style.left = `${left}px`;
323
+
311
324
  // Set top position based on height
312
325
  const topOffset = isMobileScreen() ? 'calc(var(--height-mobile) + 3px)' : 'calc(var(--height) + 3px)';
313
326
  nestedContainer.style.top = topOffset;
@@ -90,19 +90,19 @@ export class PopoverMobile extends PopoverAbstract<PopoverMobileNodes> {
90
90
  */
91
91
  public show(): void {
92
92
  this.nodes.overlay.removeAttribute(DATA_ATTR.overlayHidden);
93
- this.nodes.overlay.className = twMerge(css.popoverOverlay, 'fixed inset-0 block visible z-[3] opacity-50 transition-opacity duration-[120ms] ease-in will-change-[opacity]');
93
+ this.nodes.overlay.className = twMerge(css.popoverOverlay, 'fixed inset-0 block visible z-3 opacity-50 transition-opacity duration-120 ease-in will-change-[opacity]');
94
94
 
95
95
  super.show();
96
96
 
97
97
  // Apply mobile opened state classes AFTER super.show() to override base class styles
98
- // For mobile, we use max-h-none instead of max-h-[var(--max-height)] since mobile popovers
98
+ // For mobile, we use max-h-none instead of max-h-(--max-height) since mobile popovers
99
99
  // should expand to fit their content
100
- // Use z-[4] to ensure container is above the overlay (z-[3])
100
+ // Use z-4 to ensure container is above the overlay (z-[3])
101
101
  this.nodes.popoverContainer.className = twMerge(
102
102
  css.popoverContainer,
103
103
  css.popoverContainerMobile,
104
104
  css.popoverContainerOpened,
105
- 'max-h-none z-[4] animate-[panelShowingMobile_250ms_ease]'
105
+ 'max-h-none z-4 animate-[panelShowingMobile_250ms_ease]'
106
106
  );
107
107
 
108
108
  this.scrollLocker.lock();
@@ -5,14 +5,14 @@
5
5
  */
6
6
  export const css = {
7
7
  // Popover container - base styles
8
- popoverContainer: 'absolute flex flex-col overflow-hidden box-border opacity-0 pointer-events-none p-0 border-none z-[4] max-h-0 min-w-[var(--width)] w-[var(--width)] rounded-lg shadow-[0_3px_15px_-3px_theme(colors.popover-shadow)] left-[var(--popover-left)] top-[var(--popover-top)] bg-popover-bg',
8
+ popoverContainer: 'absolute flex flex-col overflow-hidden box-border opacity-0 pointer-events-none p-0 border-none z-4 max-h-0 min-w-(--width) w-(--width) rounded-lg shadow-[0_3px_15px_-3px_var(--color-popover-shadow)] left-(--popover-left) top-(--popover-top) bg-popover-bg',
9
9
 
10
10
  // Popover container - mobile styles (applied conditionally)
11
11
  // Reset left/top from base class since inset shorthand may not properly override them in twMerge
12
12
  popoverContainerMobile: 'fixed max-w-none rounded-[10px] min-w-[calc(100%-var(--offset)*2)] left-auto top-auto inset-[auto_var(--offset)_calc(var(--offset)+env(safe-area-inset-bottom))_var(--offset)]',
13
13
 
14
14
  // Popover container - opened state
15
- popoverContainerOpened: 'opacity-100 pointer-events-auto p-1 max-h-[var(--max-height)] border border-popover-border animate-[panelShowing_100ms_ease]',
15
+ popoverContainerOpened: 'opacity-100 pointer-events-auto p-1 max-h-(--max-height) border border-popover-border animate-[panelShowing_100ms_ease]',
16
16
 
17
17
  // Popover overlay
18
18
  popoverOverlay: 'hidden bg-dark',