@jackuait/blok 0.7.0-beta.1 → 0.7.0-beta.3

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 (256) hide show
  1. package/README.md +20 -4
  2. package/dist/blok.mjs +2 -2
  3. package/dist/chunks/{blok-ob9Fwr1L.mjs → blok-BlyYiZTm.mjs} +1840 -1654
  4. package/dist/chunks/{inline-tool-convert-CvFW2iie.mjs → constants-DEy4jBO5.mjs} +316 -297
  5. package/dist/chunks/{i18next-loader-Bu3vFvye.mjs → i18next-loader-Cfbv-x6v.mjs} +1 -1
  6. package/dist/chunks/index-Cu1w-sLZ.mjs +130 -0
  7. package/dist/chunks/{messages-D2NOpHn9.mjs → messages-0Pxnqd4N.mjs} +7 -0
  8. package/dist/chunks/{messages-GSByFygY.mjs → messages-0ZXYUq7S.mjs} +7 -0
  9. package/dist/{messages-BUl_Rcnj.mjs → chunks/messages-2OD2uUDS.mjs} +9 -2
  10. package/dist/{messages-CgTq3QhU.mjs → chunks/messages-7cEMfYzh.mjs} +7 -0
  11. package/dist/{messages-DlJbPF2T.mjs → chunks/messages-8mwfda1Q.mjs} +7 -0
  12. package/dist/{messages-D9ndgBnU.mjs → chunks/messages-B-FqWsBM.mjs} +7 -0
  13. package/dist/{messages-B217znr-.mjs → chunks/messages-B1jzqWiQ.mjs} +7 -0
  14. package/dist/{messages-BcpCubnC.mjs → chunks/messages-B5wk4Ezz.mjs} +7 -0
  15. package/dist/chunks/{messages-DRXWF0PV.mjs → messages-BAZ5Ld8x.mjs} +7 -0
  16. package/dist/{messages-CRJ_mchV.mjs → chunks/messages-BBhGp198.mjs} +7 -0
  17. package/dist/chunks/{messages-yHcs38yI.mjs → messages-BC9IjIb7.mjs} +7 -0
  18. package/dist/chunks/{messages-Cr94GzbX.mjs → messages-BFEmpeV-.mjs} +7 -0
  19. package/dist/chunks/{messages-ucTVgS5G.mjs → messages-BGqzTZy0.mjs} +7 -0
  20. package/dist/{messages-begYOTgC.mjs → chunks/messages-BICs1abK.mjs} +7 -0
  21. package/dist/chunks/{messages-DVQvl8Qj.mjs → messages-BJX6rOnd.mjs} +7 -0
  22. package/dist/chunks/{messages-Chb7k3Rg.mjs → messages-BL2bXRhN.mjs} +7 -0
  23. package/dist/{messages-Phkd7XmE.mjs → chunks/messages-BMs5qdlH.mjs} +7 -0
  24. package/dist/chunks/{messages-Cjjo7yHR.mjs → messages-BRsjUNwB.mjs} +7 -0
  25. package/dist/chunks/{messages-D4qqwVgQ.mjs → messages-BSqV8OUR.mjs} +7 -0
  26. package/dist/chunks/{messages-DviiFSv2.mjs → messages-BTqu3DfG.mjs} +7 -0
  27. package/dist/chunks/{messages-0AbcLMLm.mjs → messages-BXnDEsur.mjs} +7 -0
  28. package/dist/{messages-CmR9ftc_.mjs → chunks/messages-BYcre4-6.mjs} +7 -0
  29. package/dist/{messages-wmi-iFkH.mjs → chunks/messages-BZ9LRJf-.mjs} +7 -0
  30. package/dist/chunks/{messages-D00x4S8o.mjs → messages-BgypBy7y.mjs} +7 -0
  31. package/dist/{messages-96kNZDll.mjs → chunks/messages-BsuGf70G.mjs} +7 -0
  32. package/dist/chunks/{messages-v3GipbFl.mjs → messages-BwaoF4lQ.mjs} +7 -0
  33. package/dist/{messages-DDTQgImT.mjs → chunks/messages-C1l8_7-y.mjs} +7 -0
  34. package/dist/{messages-B1FZ8lxU.mjs → chunks/messages-C5NA_r9v.mjs} +7 -0
  35. package/dist/{messages-Cs8zmZ3L.mjs → chunks/messages-C6zgZ5pA.mjs} +7 -0
  36. package/dist/chunks/{messages-ZjUAIWb1.mjs → messages-CAo5ghFI.mjs} +7 -0
  37. package/dist/{messages-D5S1Dnpm.mjs → chunks/messages-CH9qlJ9I.mjs} +7 -0
  38. package/dist/{messages-D7u2bmP2.mjs → chunks/messages-CI0HqAeS.mjs} +7 -0
  39. package/dist/{messages-DH_jBeED.mjs → chunks/messages-CJJtms9k.mjs} +7 -0
  40. package/dist/{messages-CDBLbUOQ.mjs → chunks/messages-CM2hJqk6.mjs} +7 -0
  41. package/dist/chunks/{messages-8IPXkrDl.mjs → messages-CRMiDPIQ.mjs} +7 -0
  42. package/dist/chunks/{messages-Dzzn6XoD.mjs → messages-CWsZuBj1.mjs} +7 -0
  43. package/dist/chunks/{messages-CW4c4cRk.mjs → messages-C_gLHo6A.mjs} +7 -0
  44. package/dist/{messages-CH4hrauY.mjs → chunks/messages-Cbu-NUDn.mjs} +7 -0
  45. package/dist/{messages-RonBBCnh.mjs → chunks/messages-Cjb_MCeh.mjs} +7 -0
  46. package/dist/chunks/{messages-BJ6zrz2j.mjs → messages-ClXYO9Wn.mjs} +7 -0
  47. package/dist/chunks/{messages-CrCYPCk3.mjs → messages-CsH20vhP.mjs} +7 -0
  48. package/dist/{messages-CzK0LEhb.mjs → chunks/messages-CsjAGhzA.mjs} +7 -0
  49. package/dist/chunks/{messages-BZlmVRwn.mjs → messages-Cx7VKFOE.mjs} +7 -0
  50. package/dist/chunks/{messages-0E0AkrNu.mjs → messages-D3JeBwxo.mjs} +7 -0
  51. package/dist/chunks/{messages-D85FqxgY.mjs → messages-D541fieJ.mjs} +7 -0
  52. package/dist/{messages-4v4MuVEc.mjs → chunks/messages-D7XPdglc.mjs} +7 -0
  53. package/dist/{messages-BC8IN4Bf.mjs → chunks/messages-DBhylfvt.mjs} +7 -0
  54. package/dist/chunks/{messages-B8WNljW3.mjs → messages-DCA120lW.mjs} +7 -0
  55. package/dist/chunks/{messages-Cr49Nt3U.mjs → messages-DCf_xZMN.mjs} +7 -0
  56. package/dist/chunks/{messages-VDriF5Qy.mjs → messages-DDwXKCpe.mjs} +7 -0
  57. package/dist/{messages-b1EdvUm0.mjs → chunks/messages-DNKDlxcy.mjs} +7 -0
  58. package/dist/{messages-L_kl2Qvh.mjs → chunks/messages-DPvEjrGK.mjs} +7 -0
  59. package/dist/chunks/{messages-62v-CLC-.mjs → messages-DQ-AkNxA.mjs} +7 -0
  60. package/dist/chunks/{messages-DdK-nFGm.mjs → messages-DVuvkNap.mjs} +7 -0
  61. package/dist/{messages-DnVlmiNT.mjs → chunks/messages-DaglyqUT.mjs} +7 -0
  62. package/dist/{messages-Bm-E4iRC.mjs → chunks/messages-Di0bAfwA.mjs} +7 -0
  63. package/dist/{messages-D1mn7Zd5.mjs → chunks/messages-DuLct0Yr.mjs} +7 -0
  64. package/dist/{messages-8DeO60Oo.mjs → chunks/messages-DzEYYhZh.mjs} +7 -0
  65. package/dist/chunks/{messages-CfiyT2Wi.mjs → messages-DznNGAB2.mjs} +7 -0
  66. package/dist/chunks/{messages-DXktiao_.mjs → messages-DzoIzyu8.mjs} +7 -0
  67. package/dist/{messages-C_4otP7U.mjs → chunks/messages-QYOGmket.mjs} +7 -0
  68. package/dist/chunks/{messages-nefz1S71.mjs → messages-cEjGDAgI.mjs} +7 -0
  69. package/dist/chunks/{messages-jrncnb-H.mjs → messages-ddhvrdpE.mjs} +7 -0
  70. package/dist/chunks/{messages-DzqM3Fel.mjs → messages-mwfNK5nZ.mjs} +7 -0
  71. package/dist/chunks/{messages-Cl6ayUaq.mjs → messages-nG_vNDte.mjs} +7 -0
  72. package/dist/{messages-C4jL-90N.mjs → chunks/messages-tDq3Owh7.mjs} +7 -0
  73. package/dist/{messages-BI43k_BD.mjs → chunks/messages-x6VJVZKx.mjs} +7 -0
  74. package/dist/full.mjs +2 -2
  75. package/dist/locales.mjs +87 -80
  76. package/dist/{messages-D2NOpHn9.mjs → messages-0Pxnqd4N.mjs} +7 -0
  77. package/dist/{messages-GSByFygY.mjs → messages-0ZXYUq7S.mjs} +7 -0
  78. package/dist/{chunks/messages-BUl_Rcnj.mjs → messages-2OD2uUDS.mjs} +9 -2
  79. package/dist/{chunks/messages-CgTq3QhU.mjs → messages-7cEMfYzh.mjs} +7 -0
  80. package/dist/{chunks/messages-DlJbPF2T.mjs → messages-8mwfda1Q.mjs} +7 -0
  81. package/dist/{chunks/messages-D9ndgBnU.mjs → messages-B-FqWsBM.mjs} +7 -0
  82. package/dist/{chunks/messages-B217znr-.mjs → messages-B1jzqWiQ.mjs} +7 -0
  83. package/dist/{chunks/messages-BcpCubnC.mjs → messages-B5wk4Ezz.mjs} +7 -0
  84. package/dist/{messages-DRXWF0PV.mjs → messages-BAZ5Ld8x.mjs} +7 -0
  85. package/dist/{chunks/messages-CRJ_mchV.mjs → messages-BBhGp198.mjs} +7 -0
  86. package/dist/{messages-yHcs38yI.mjs → messages-BC9IjIb7.mjs} +7 -0
  87. package/dist/{messages-Cr94GzbX.mjs → messages-BFEmpeV-.mjs} +7 -0
  88. package/dist/{messages-ucTVgS5G.mjs → messages-BGqzTZy0.mjs} +7 -0
  89. package/dist/{chunks/messages-begYOTgC.mjs → messages-BICs1abK.mjs} +7 -0
  90. package/dist/{messages-DVQvl8Qj.mjs → messages-BJX6rOnd.mjs} +7 -0
  91. package/dist/{messages-Chb7k3Rg.mjs → messages-BL2bXRhN.mjs} +7 -0
  92. package/dist/{chunks/messages-Phkd7XmE.mjs → messages-BMs5qdlH.mjs} +7 -0
  93. package/dist/{messages-Cjjo7yHR.mjs → messages-BRsjUNwB.mjs} +7 -0
  94. package/dist/{messages-D4qqwVgQ.mjs → messages-BSqV8OUR.mjs} +7 -0
  95. package/dist/{messages-DviiFSv2.mjs → messages-BTqu3DfG.mjs} +7 -0
  96. package/dist/{messages-0AbcLMLm.mjs → messages-BXnDEsur.mjs} +7 -0
  97. package/dist/{chunks/messages-CmR9ftc_.mjs → messages-BYcre4-6.mjs} +7 -0
  98. package/dist/{chunks/messages-wmi-iFkH.mjs → messages-BZ9LRJf-.mjs} +7 -0
  99. package/dist/{messages-D00x4S8o.mjs → messages-BgypBy7y.mjs} +7 -0
  100. package/dist/{chunks/messages-96kNZDll.mjs → messages-BsuGf70G.mjs} +7 -0
  101. package/dist/{messages-v3GipbFl.mjs → messages-BwaoF4lQ.mjs} +7 -0
  102. package/dist/{chunks/messages-DDTQgImT.mjs → messages-C1l8_7-y.mjs} +7 -0
  103. package/dist/{chunks/messages-B1FZ8lxU.mjs → messages-C5NA_r9v.mjs} +7 -0
  104. package/dist/{chunks/messages-Cs8zmZ3L.mjs → messages-C6zgZ5pA.mjs} +7 -0
  105. package/dist/{messages-ZjUAIWb1.mjs → messages-CAo5ghFI.mjs} +7 -0
  106. package/dist/{chunks/messages-D5S1Dnpm.mjs → messages-CH9qlJ9I.mjs} +7 -0
  107. package/dist/{chunks/messages-D7u2bmP2.mjs → messages-CI0HqAeS.mjs} +7 -0
  108. package/dist/{chunks/messages-DH_jBeED.mjs → messages-CJJtms9k.mjs} +7 -0
  109. package/dist/{chunks/messages-CDBLbUOQ.mjs → messages-CM2hJqk6.mjs} +7 -0
  110. package/dist/{messages-8IPXkrDl.mjs → messages-CRMiDPIQ.mjs} +7 -0
  111. package/dist/{messages-Dzzn6XoD.mjs → messages-CWsZuBj1.mjs} +7 -0
  112. package/dist/{messages-CW4c4cRk.mjs → messages-C_gLHo6A.mjs} +7 -0
  113. package/dist/{chunks/messages-CH4hrauY.mjs → messages-Cbu-NUDn.mjs} +7 -0
  114. package/dist/{chunks/messages-RonBBCnh.mjs → messages-Cjb_MCeh.mjs} +7 -0
  115. package/dist/{messages-BJ6zrz2j.mjs → messages-ClXYO9Wn.mjs} +7 -0
  116. package/dist/{messages-CrCYPCk3.mjs → messages-CsH20vhP.mjs} +7 -0
  117. package/dist/{chunks/messages-CzK0LEhb.mjs → messages-CsjAGhzA.mjs} +7 -0
  118. package/dist/{messages-BZlmVRwn.mjs → messages-Cx7VKFOE.mjs} +7 -0
  119. package/dist/{messages-0E0AkrNu.mjs → messages-D3JeBwxo.mjs} +7 -0
  120. package/dist/{messages-D85FqxgY.mjs → messages-D541fieJ.mjs} +7 -0
  121. package/dist/{chunks/messages-4v4MuVEc.mjs → messages-D7XPdglc.mjs} +7 -0
  122. package/dist/{chunks/messages-BC8IN4Bf.mjs → messages-DBhylfvt.mjs} +7 -0
  123. package/dist/{messages-B8WNljW3.mjs → messages-DCA120lW.mjs} +7 -0
  124. package/dist/{messages-Cr49Nt3U.mjs → messages-DCf_xZMN.mjs} +7 -0
  125. package/dist/{messages-VDriF5Qy.mjs → messages-DDwXKCpe.mjs} +7 -0
  126. package/dist/{chunks/messages-b1EdvUm0.mjs → messages-DNKDlxcy.mjs} +7 -0
  127. package/dist/{chunks/messages-L_kl2Qvh.mjs → messages-DPvEjrGK.mjs} +7 -0
  128. package/dist/{messages-62v-CLC-.mjs → messages-DQ-AkNxA.mjs} +7 -0
  129. package/dist/{messages-DdK-nFGm.mjs → messages-DVuvkNap.mjs} +7 -0
  130. package/dist/{chunks/messages-DnVlmiNT.mjs → messages-DaglyqUT.mjs} +7 -0
  131. package/dist/{chunks/messages-Bm-E4iRC.mjs → messages-Di0bAfwA.mjs} +7 -0
  132. package/dist/{chunks/messages-D1mn7Zd5.mjs → messages-DuLct0Yr.mjs} +7 -0
  133. package/dist/{chunks/messages-8DeO60Oo.mjs → messages-DzEYYhZh.mjs} +7 -0
  134. package/dist/{messages-CfiyT2Wi.mjs → messages-DznNGAB2.mjs} +7 -0
  135. package/dist/{messages-DXktiao_.mjs → messages-DzoIzyu8.mjs} +7 -0
  136. package/dist/{chunks/messages-C_4otP7U.mjs → messages-QYOGmket.mjs} +7 -0
  137. package/dist/{messages-nefz1S71.mjs → messages-cEjGDAgI.mjs} +7 -0
  138. package/dist/{messages-jrncnb-H.mjs → messages-ddhvrdpE.mjs} +7 -0
  139. package/dist/{messages-DzqM3Fel.mjs → messages-mwfNK5nZ.mjs} +7 -0
  140. package/dist/{messages-Cl6ayUaq.mjs → messages-nG_vNDte.mjs} +7 -0
  141. package/dist/{chunks/messages-C4jL-90N.mjs → messages-tDq3Owh7.mjs} +7 -0
  142. package/dist/{chunks/messages-BI43k_BD.mjs → messages-x6VJVZKx.mjs} +7 -0
  143. package/dist/tools.mjs +443 -338
  144. package/package.json +1 -1
  145. package/src/components/i18n/locales/am/messages.json +7 -0
  146. package/src/components/i18n/locales/ar/messages.json +7 -0
  147. package/src/components/i18n/locales/az/messages.json +7 -0
  148. package/src/components/i18n/locales/bg/messages.json +7 -0
  149. package/src/components/i18n/locales/bn/messages.json +7 -0
  150. package/src/components/i18n/locales/bs/messages.json +7 -0
  151. package/src/components/i18n/locales/cs/messages.json +7 -0
  152. package/src/components/i18n/locales/da/messages.json +7 -0
  153. package/src/components/i18n/locales/de/messages.json +7 -0
  154. package/src/components/i18n/locales/dv/messages.json +7 -0
  155. package/src/components/i18n/locales/el/messages.json +7 -0
  156. package/src/components/i18n/locales/en/messages.json +7 -0
  157. package/src/components/i18n/locales/es/messages.json +7 -0
  158. package/src/components/i18n/locales/et/messages.json +7 -0
  159. package/src/components/i18n/locales/fa/messages.json +7 -0
  160. package/src/components/i18n/locales/fi/messages.json +7 -0
  161. package/src/components/i18n/locales/fil/messages.json +7 -0
  162. package/src/components/i18n/locales/fr/messages.json +7 -0
  163. package/src/components/i18n/locales/gu/messages.json +7 -0
  164. package/src/components/i18n/locales/he/messages.json +7 -0
  165. package/src/components/i18n/locales/hi/messages.json +7 -0
  166. package/src/components/i18n/locales/hr/messages.json +7 -0
  167. package/src/components/i18n/locales/hu/messages.json +7 -0
  168. package/src/components/i18n/locales/hy/messages.json +7 -0
  169. package/src/components/i18n/locales/id/messages.json +7 -0
  170. package/src/components/i18n/locales/it/messages.json +7 -0
  171. package/src/components/i18n/locales/ja/messages.json +7 -0
  172. package/src/components/i18n/locales/ka/messages.json +7 -0
  173. package/src/components/i18n/locales/km/messages.json +7 -0
  174. package/src/components/i18n/locales/kn/messages.json +7 -0
  175. package/src/components/i18n/locales/ko/messages.json +7 -0
  176. package/src/components/i18n/locales/ku/messages.json +7 -0
  177. package/src/components/i18n/locales/lo/messages.json +7 -0
  178. package/src/components/i18n/locales/lt/messages.json +7 -0
  179. package/src/components/i18n/locales/lv/messages.json +7 -0
  180. package/src/components/i18n/locales/mk/messages.json +7 -0
  181. package/src/components/i18n/locales/ml/messages.json +7 -0
  182. package/src/components/i18n/locales/mn/messages.json +7 -0
  183. package/src/components/i18n/locales/mr/messages.json +7 -0
  184. package/src/components/i18n/locales/ms/messages.json +7 -0
  185. package/src/components/i18n/locales/my/messages.json +7 -0
  186. package/src/components/i18n/locales/ne/messages.json +7 -0
  187. package/src/components/i18n/locales/nl/messages.json +7 -0
  188. package/src/components/i18n/locales/no/messages.json +7 -0
  189. package/src/components/i18n/locales/pa/messages.json +7 -0
  190. package/src/components/i18n/locales/pl/messages.json +7 -0
  191. package/src/components/i18n/locales/ps/messages.json +7 -0
  192. package/src/components/i18n/locales/pt/messages.json +7 -0
  193. package/src/components/i18n/locales/ro/messages.json +7 -0
  194. package/src/components/i18n/locales/ru/messages.json +7 -0
  195. package/src/components/i18n/locales/sd/messages.json +7 -0
  196. package/src/components/i18n/locales/si/messages.json +7 -0
  197. package/src/components/i18n/locales/sk/messages.json +7 -0
  198. package/src/components/i18n/locales/sl/messages.json +7 -0
  199. package/src/components/i18n/locales/sq/messages.json +7 -0
  200. package/src/components/i18n/locales/sr/messages.json +7 -0
  201. package/src/components/i18n/locales/sv/messages.json +7 -0
  202. package/src/components/i18n/locales/sw/messages.json +7 -0
  203. package/src/components/i18n/locales/ta/messages.json +7 -0
  204. package/src/components/i18n/locales/te/messages.json +7 -0
  205. package/src/components/i18n/locales/th/messages.json +7 -0
  206. package/src/components/i18n/locales/tr/messages.json +7 -0
  207. package/src/components/i18n/locales/ug/messages.json +7 -0
  208. package/src/components/i18n/locales/uk/messages.json +7 -0
  209. package/src/components/i18n/locales/ur/messages.json +7 -0
  210. package/src/components/i18n/locales/vi/messages.json +7 -0
  211. package/src/components/i18n/locales/yi/messages.json +7 -0
  212. package/src/components/i18n/locales/zh/messages.json +7 -0
  213. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +44 -2
  214. package/src/components/modules/blockEvents/index.ts +1 -3
  215. package/src/components/modules/blockManager/blockManager.ts +16 -0
  216. package/src/components/modules/blockManager/operations.ts +106 -9
  217. package/src/components/modules/blockSelection.ts +2 -0
  218. package/src/components/modules/caret.ts +49 -4
  219. package/src/components/modules/drag/DragController.ts +34 -2
  220. package/src/components/modules/paste/handlers/blok-data-handler.ts +50 -3
  221. package/src/components/modules/toolbar/index.ts +11 -16
  222. package/src/components/modules/ui.ts +22 -0
  223. package/src/components/ui/toolbox.ts +19 -3
  224. package/src/components/utils/notifier/draw.ts +116 -14
  225. package/src/components/utils/notifier/index.ts +31 -4
  226. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -2
  227. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -7
  228. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
  229. package/src/components/utils/popover/popover-abstract.ts +2 -0
  230. package/src/components/utils/popover/popover-desktop.ts +39 -2
  231. package/src/stories/Block.stories.ts +21 -0
  232. package/src/stories/EditorModes.stories.ts +19 -0
  233. package/src/stories/InlineToolbar.stories.ts +41 -9
  234. package/src/stories/MarkerColors.stories.ts +48 -52
  235. package/src/stories/Notifier.stories.ts +19 -1
  236. package/src/stories/Placeholder.stories.ts +12 -0
  237. package/src/stories/Popover.stories.ts +26 -0
  238. package/src/stories/Selection.stories.ts +6 -0
  239. package/src/stories/Table.stories.ts +12 -0
  240. package/src/stories/Toolbar.stories.ts +9 -0
  241. package/src/stories/Toolbox.stories.ts +4 -0
  242. package/src/stories/Tooltip.stories.ts +6 -0
  243. package/src/stories/helpers.ts +63 -8
  244. package/src/styles/main.css +46 -0
  245. package/src/tools/header/index.ts +121 -22
  246. package/src/tools/table/index.ts +2 -3
  247. package/src/tools/table/table-add-controls.ts +29 -1
  248. package/src/tools/table/table-cell-blocks.ts +93 -0
  249. package/src/tools/toggle/constants.ts +2 -2
  250. package/src/tools/toggle/dom-builder.ts +32 -4
  251. package/src/tools/toggle/index.ts +26 -4
  252. package/src/tools/toggle/toggle-keyboard.ts +19 -2
  253. package/src/tools/toggle/toggle-lifecycle.ts +1 -0
  254. package/src/tools/toggle/toggle-shortcuts.ts +18 -8
  255. package/types/utils/popover/popover.d.ts +8 -0
  256. package/dist/chunks/index-CZmRzRIX.mjs +0 -78
@@ -110,11 +110,18 @@ export class DragController extends Module {
110
110
  ? this.Blok.BlockSelection.selectedBlocks
111
111
  : [block];
112
112
 
113
- // For single-block list item drags, include all descendants
114
- const descendants = !isBlockSelected && this.listItemDescendants
113
+ // For single-block drags, include all descendants (list items via depth, toggles via contentIds)
114
+ const listDescendants = !isBlockSelected && this.listItemDescendants
115
115
  ? this.listItemDescendants.getDescendants(block)
116
116
  : [];
117
117
 
118
+ // If no list descendants found, check for hierarchy children (toggle blocks)
119
+ const hasHierarchyChildren = !isBlockSelected && block.contentIds?.length > 0;
120
+ const hierarchyDescendants = listDescendants.length === 0 && hasHierarchyChildren
121
+ ? this.getHierarchyDescendants(block)
122
+ : [];
123
+ const descendants = listDescendants.length > 0 ? listDescendants : hierarchyDescendants;
124
+
118
125
  const blocksToMove = descendants.length > 0
119
126
  ? [block, ...descendants]
120
127
  : initialBlocks;
@@ -458,6 +465,31 @@ export class DragController extends Module {
458
465
  this.Blok.Toolbar.moveAndOpen(blockToShow);
459
466
  }
460
467
 
468
+ /**
469
+ * Recursively collects all descendants of a block via the parentId/contentIds hierarchy.
470
+ * Used for toggle blocks and other parent-child structures that don't use data-list-depth.
471
+ * @param block - Parent block to collect descendants for
472
+ * @returns Array of descendant blocks
473
+ */
474
+ private getHierarchyDescendants(block: Block): Block[] {
475
+ const descendants: Block[] = [];
476
+
477
+ const collectChildren = (parentBlock: Block): void => {
478
+ for (const childId of parentBlock.contentIds) {
479
+ const child = this.Blok.BlockManager.getBlockById(childId);
480
+
481
+ if (child !== undefined) {
482
+ descendants.push(child);
483
+ collectChildren(child);
484
+ }
485
+ }
486
+ };
487
+
488
+ collectChildren(block);
489
+
490
+ return descendants;
491
+ }
492
+
461
493
  public destroy(): void {
462
494
  this.cleanup();
463
495
  }
@@ -1,6 +1,7 @@
1
1
  import type { SanitizerConfig } from '../../../../../types/configs/sanitizer-config';
2
2
  import type { SavedData } from '../../../../../types/data-formats';
3
3
  import type { BlokModules } from '../../../../types-internal/blok-modules';
4
+ import type { Block } from '../../../block';
4
5
  import { sanitizeBlocks } from '../../../utils/sanitizer';
5
6
  import type { SanitizerConfigBuilder } from '../sanitizer-config';
6
7
  import type { ToolRegistry } from '../tool-registry';
@@ -10,6 +11,15 @@ import type { PasteHandler } from './base';
10
11
  import { BasePasteHandler } from './base';
11
12
  import { PatternHandler } from './pattern-handler';
12
13
 
14
+ /**
15
+ * Shape of a block in the Blok clipboard data.
16
+ * Extends the basic SavedData fields with optional hierarchy information.
17
+ */
18
+ interface BlokClipboardBlock extends Pick<SavedData, 'id' | 'data' | 'tool'> {
19
+ parentId?: string | null;
20
+ contentIds?: string[];
21
+ }
22
+
13
23
 
14
24
  /**
15
25
  * Blok Data Handler Priority.
@@ -45,7 +55,7 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
45
55
  return false;
46
56
  }
47
57
 
48
- const parsedBlokData = JSON.parse(data) as Pick<SavedData, 'id' | 'data' | 'tool'>[];
58
+ const parsedBlokData = JSON.parse(data) as BlokClipboardBlock[];
49
59
 
50
60
  // Check if we should try pattern matching first (for URL pasting within editor)
51
61
  const hasPatterns = this.toolRegistry.toolsPatterns.length > 0;
@@ -118,9 +128,11 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
118
128
 
119
129
  /**
120
130
  * Insert Blok JSON blocks.
131
+ * After inserting all blocks, restores parent-child hierarchy using an ID mapping
132
+ * (pasted blocks receive new IDs, so original IDs are mapped to new block instances).
121
133
  */
122
134
  private insertBlokBlocks(
123
- blocks: Pick<SavedData, 'id' | 'data' | 'tool'>[],
135
+ blocks: BlokClipboardBlock[],
124
136
  canReplace: boolean
125
137
  ): void {
126
138
  const { BlockManager, Caret, Tools } = this.Blok;
@@ -130,7 +142,14 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
130
142
  this.config.sanitizer
131
143
  );
132
144
 
133
- sanitizedBlocks.forEach(({ tool, data }, i) => {
145
+ /**
146
+ * Map from original (old) block ID to the newly inserted Block instance.
147
+ * Used after insertion to re-establish parent-child relationships.
148
+ */
149
+ const oldIdToEntry = new Map<string, { newBlock: Block; original: BlokClipboardBlock }>();
150
+
151
+ sanitizedBlocks.forEach((sanitizedBlock, i) => {
152
+ const { tool, data } = sanitizedBlock;
134
153
  const needToReplaceCurrentBlock = i === 0 &&
135
154
  canReplace &&
136
155
  Boolean(BlockManager.currentBlock?.tool.isDefault) &&
@@ -142,7 +161,35 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
142
161
  replace: needToReplaceCurrentBlock,
143
162
  });
144
163
 
164
+ const originalBlock = blocks[i];
165
+
166
+ if (originalBlock !== undefined) {
167
+ oldIdToEntry.set(originalBlock.id, { newBlock: block, original: originalBlock });
168
+ }
169
+
145
170
  Caret.setToBlock(block, Caret.positions.END);
146
171
  });
172
+
173
+ /**
174
+ * Restore parent-child hierarchy using the old-to-new ID mapping.
175
+ * Only restores relationships where both parent and child are in the pasted set.
176
+ */
177
+ for (const [, { newBlock, original }] of oldIdToEntry) {
178
+ if (original.parentId === undefined || original.parentId === null) {
179
+ continue;
180
+ }
181
+
182
+ const parentEntry = oldIdToEntry.get(original.parentId);
183
+
184
+ if (parentEntry === undefined) {
185
+ continue;
186
+ }
187
+
188
+ newBlock.parentId = parentEntry.newBlock.id;
189
+
190
+ if (!parentEntry.newBlock.contentIds.includes(newBlock.id)) {
191
+ parentEntry.newBlock.contentIds = [...parentEntry.newBlock.contentIds, newBlock.id];
192
+ }
193
+ }
147
194
  }
148
195
  }
@@ -393,22 +393,16 @@ export class Toolbar extends Module<ToolbarNodes> {
393
393
  }
394
394
 
395
395
  /**
396
- * Suppress toolbar buttons when the focused element is inside a table cell.
397
- * The toolbar still positions itself for toolbox/slash-search purposes,
398
- * but plus button and settings toggler remain hidden while a cell has focus.
399
- *
400
- * Note: we check focus (activeElement) not hover. On hover alone, the plus
401
- * button should remain visible so the user can click it to add blocks
402
- * below the table. Only when the user actually clicks/focuses inside a cell
403
- * should the buttons be suppressed.
396
+ * Adjust toolbar button visibility when focus is inside a table cell.
397
+ * The plus button always stays visible so users can add blocks below the table.
398
+ * The settings toggler is hidden because drag/settings don't apply to individual cells.
404
399
  */
405
400
  const focusIsInsideCell = this.isFocusInsideTableCell();
406
- const displayValue = focusIsInsideCell ? 'none' : '';
407
401
 
408
- plusButton.style.display = displayValue;
402
+ plusButton.style.display = '';
409
403
 
410
404
  if (settingsToggler) {
411
- settingsToggler.style.display = displayValue;
405
+ settingsToggler.style.display = focusIsInsideCell ? 'none' : '';
412
406
  }
413
407
 
414
408
  const targetBlockHolder = targetBlock.holder;
@@ -662,8 +656,8 @@ export class Toolbar extends Module<ToolbarNodes> {
662
656
 
663
657
  /**
664
658
  * Updates toolbar button visibility based on whether a table cell has focus.
665
- * Hides plus button and settings toggler when focus is inside a cell;
666
- * restores them when focus moves to a regular block.
659
+ * The plus button always stays visible; the settings toggler is hidden when
660
+ * focus is inside a cell and restored when focus moves to a regular block.
667
661
  *
668
662
  * Called from the focusin listener so button state is updated immediately
669
663
  * on click, without waiting for the next hover/moveAndOpen cycle.
@@ -676,12 +670,11 @@ export class Toolbar extends Module<ToolbarNodes> {
676
670
  }
677
671
 
678
672
  const focusIsInsideCell = this.isFocusInsideTableCell();
679
- const displayValue = focusIsInsideCell ? 'none' : '';
680
673
 
681
- plusButton.style.display = displayValue;
674
+ plusButton.style.display = '';
682
675
 
683
676
  if (settingsToggler) {
684
- settingsToggler.style.display = displayValue;
677
+ settingsToggler.style.display = focusIsInsideCell ? 'none' : '';
685
678
  }
686
679
  }
687
680
 
@@ -837,9 +830,11 @@ export class Toolbar extends Module<ToolbarNodes> {
837
830
  i18nLabels: {
838
831
  filter: this.Blok.I18n.t('popover.search'),
839
832
  nothingFound: this.Blok.I18n.t('popover.nothingFound'),
833
+ slashSearchPlaceholder: this.Blok.I18n.t('toolbox.typeToSearch'),
840
834
  },
841
835
  i18n: this.Blok.I18n,
842
836
  triggerElement: this.nodes.plusButton,
837
+ leftAlignElement: this.nodes.content,
843
838
  });
844
839
 
845
840
  this.toolboxInstance.on(ToolboxEvent.Opened, () => {
@@ -22,6 +22,7 @@ import { KeyboardController } from './uiControllers/controllers/keyboard';
22
22
  import { SelectionController } from './uiControllers/controllers/selection';
23
23
  import { createDocumentClickedHandler } from './uiControllers/handlers/click';
24
24
  import { createRedactorTouchHandler } from './uiControllers/handlers/touch';
25
+ import { ToggleShortcuts } from '../../tools/toggle/toggle-shortcuts';
25
26
 
26
27
  /**
27
28
  * HTML Elements used for UI
@@ -56,6 +57,7 @@ export class UI extends Module<UINodes> {
56
57
  private keyboardController: KeyboardController | null = null;
57
58
  private selectionController: SelectionController | null = null;
58
59
  private blockHoverController: BlockHoverController | null = null;
60
+ private toggleShortcuts: ToggleShortcuts | null = null;
59
61
 
60
62
  /**
61
63
  * Handlers for simple event behaviors
@@ -162,8 +164,27 @@ export class UI extends Module<UINodes> {
162
164
  * Enable selection controller after initialization.
163
165
  * This is needed because bindReadOnlyInsensitiveListeners() is called in make()
164
166
  * before initializeControllers(), so the selectionController doesn't exist yet.
167
+ * Must happen before toggleShortcuts.register() so that a shortcut registration
168
+ * error cannot prevent the selectionchange listener from being set up.
165
169
  */
166
170
  this.selectionController?.enable();
171
+
172
+ /**
173
+ * Register toggle shortcuts (CMD+ALT+T) for collapsing/expanding all toggle blocks.
174
+ * Wrapped in try-catch because the Shortcuts singleton may throw if shortcuts are
175
+ * already registered (e.g. race condition with multiple editor instances in CI).
176
+ * This is non-critical — the editor works fine without toggle shortcuts.
177
+ */
178
+ this.toggleShortcuts = new ToggleShortcuts(
179
+ this.Blok.API.methods,
180
+ this.nodes.wrapper
181
+ );
182
+
183
+ try {
184
+ this.toggleShortcuts.register();
185
+ } catch (error) {
186
+ console.warn('Blok: Failed to register toggle shortcuts:', error);
187
+ }
167
188
  }
168
189
 
169
190
  /**
@@ -343,6 +364,7 @@ export class UI extends Module<UINodes> {
343
364
  * Clean blok`s UI
344
365
  */
345
366
  public destroy(): void {
367
+ this.toggleShortcuts?.unregister();
346
368
  this.nodes.holder.innerHTML = '';
347
369
 
348
370
  this.unbindReadOnlyInsensitiveListeners();
@@ -57,7 +57,7 @@ export interface ToolboxEventMap {
57
57
  /**
58
58
  * Available i18n dict keys that should be passed to the constructor
59
59
  */
60
- type ToolboxTextLabelsKeys = 'filter' | 'nothingFound';
60
+ type ToolboxTextLabelsKeys = 'filter' | 'nothingFound' | 'slashSearchPlaceholder';
61
61
 
62
62
  /**
63
63
  * Toolbox
@@ -145,6 +145,11 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
145
145
  */
146
146
  private triggerElement?: HTMLElement;
147
147
 
148
+ /**
149
+ * Optional element whose left edge is used for horizontal popover alignment.
150
+ */
151
+ private leftAlignElement?: HTMLElement;
152
+
148
153
  /**
149
154
  * The block element currently being listened to for inline slash search
150
155
  */
@@ -169,13 +174,15 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
169
174
  * @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not
170
175
  * @param options.i18n - I18n instance for translations
171
176
  * @param options.triggerElement - Element relative to which the popover should be positioned
177
+ * @param options.leftAlignElement - Element whose left edge is used for horizontal popover alignment
172
178
  */
173
- constructor({ api, tools, i18nLabels, i18n, triggerElement }: {
179
+ constructor({ api, tools, i18nLabels, i18n, triggerElement, leftAlignElement }: {
174
180
  api: API;
175
181
  tools: ToolsCollection<BlockToolAdapter>;
176
182
  i18nLabels: Record<ToolboxTextLabelsKeys, string>;
177
183
  i18n: I18nInstance;
178
184
  triggerElement?: HTMLElement;
185
+ leftAlignElement?: HTMLElement;
179
186
  }) {
180
187
  super();
181
188
 
@@ -184,6 +191,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
184
191
  this.i18nLabels = i18nLabels;
185
192
  this.i18n = i18n;
186
193
  this.triggerElement = triggerElement;
194
+ this.leftAlignElement = leftAlignElement;
187
195
 
188
196
  this.enableShortcuts();
189
197
 
@@ -346,6 +354,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
346
354
  this.popover = new PopoverClass({
347
355
  scopeElement: this.api.ui.nodes.redactor,
348
356
  trigger: this.triggerElement || this.nodes.toolbox,
357
+ leftAlignElement: this.leftAlignElement,
349
358
  messages: {
350
359
  nothingFound: this.i18nLabels.nothingFound,
351
360
  search: this.i18nLabels.filter,
@@ -633,7 +642,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
633
642
  this.currentBlockForSearch = currentBlock.holder;
634
643
  this.currentContentEditable = this.currentBlockForSearch.querySelector('[contenteditable="true"]');
635
644
  if (this.currentContentEditable instanceof HTMLElement) {
636
- this.currentContentEditable.setAttribute(DATA_ATTR.slashSearch, '');
645
+ this.currentContentEditable.setAttribute(DATA_ATTR.slashSearch, this.i18nLabels.slashSearchPlaceholder);
637
646
  }
638
647
  this.listeners.on(this.currentBlockForSearch, 'input', this.handleBlockInput);
639
648
  }
@@ -674,6 +683,13 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
674
683
 
675
684
  const query = text.slice(slashIndex + 1);
676
685
 
686
+ if (this.currentContentEditable instanceof HTMLElement) {
687
+ this.currentContentEditable.setAttribute(
688
+ DATA_ATTR.slashSearch,
689
+ query.length === 0 ? this.i18nLabels.slashSearchPlaceholder : ''
690
+ );
691
+ }
692
+
677
693
  this.popover?.filterItems(query);
678
694
  };
679
695
 
@@ -2,22 +2,39 @@ import { twMerge, twJoin } from '../tw';
2
2
 
3
3
  import type { NotifierOptions, ConfirmNotifierOptions, PromptNotifierOptions } from './types';
4
4
 
5
+ /**
6
+ * SVG icons for notification styles.
7
+ * Each icon is 16x16, stroke-based for consistency.
8
+ */
9
+ const ICONS = {
10
+ success: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M5.5 8.25l1.75 1.75 3.25-3.5"/></svg>`,
11
+ error: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M8 5.25v3"/><circle cx="8" cy="10.75" r="0.5" fill="currentColor" stroke="none"/></svg>`,
12
+ default: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6.25"/><path d="M8 7.25v3.25"/><circle cx="8" cy="5.25" r="0.5" fill="currentColor" stroke="none"/></svg>`,
13
+ };
14
+
15
+ const CLOSE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 2l6 6M8 2l-6 6"/></svg>`;
16
+
5
17
  export const CSS = {
6
18
  wrapper: twJoin(
7
19
  'fixed z-2 bottom-5 left-5',
8
20
  'font-[-apple-system,BlinkMacSystemFont,"Segoe_UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira_Sans","Droid_Sans","Helvetica_Neue",sans-serif]'
9
21
  ),
10
22
  notification: twJoin(
11
- 'relative w-[230px] mt-[15px] py-[13px] px-4',
12
- 'bg-white shadow-notify rounded-[5px]',
13
- 'text-sm leading-[1.4em] wrap-break-word',
23
+ 'relative flex items-start gap-2.5 w-[230px] mt-[15px] py-[13px] px-4',
24
+ 'bg-white shadow-notify rounded-[6px]',
25
+ 'text-sm leading-[1.4em] wrap-break-word overflow-hidden',
14
26
  'before:content-[""] before:absolute before:block before:top-0 before:left-0',
15
27
  'before:w-[3px] before:h-[calc(100%-6px)] before:m-[3px] before:rounded-[5px] before:bg-transparent'
16
28
  ),
29
+ icon: 'shrink-0 mt-px',
30
+ iconSuccess: 'text-[#34c992]',
31
+ iconError: 'text-[#fb5d5d]',
32
+ iconDefault: 'text-[#9ca3af]',
33
+ messageWrapper: 'flex-1 min-w-0',
17
34
  crossBtn: twJoin(
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-45',
20
- 'after:content-[""] after:absolute after:left-[9px] after:top-[5px] after:h-3 after:w-0.5 after:bg-[#575d67] after:rotate-45',
35
+ 'absolute top-[7px] right-[7px] flex items-center justify-center',
36
+ 'w-6 h-6 rounded opacity-40 cursor-pointer',
37
+ 'transition-opacity duration-150',
21
38
  'hover:opacity-100'
22
39
  ),
23
40
  btnsWrapper: 'flex flex-row flex-nowrap mt-[5px]',
@@ -37,6 +54,70 @@ export const CSS = {
37
54
  'bg-[#fffbfb]!',
38
55
  'before:bg-[#fb5d5d]!'
39
56
  ),
57
+ progressBar: twJoin(
58
+ 'absolute bottom-0 left-0 h-[2px] rounded-b-[6px]',
59
+ 'animate-notify-progress'
60
+ ),
61
+ progressDefault: 'bg-[#d1d5db]',
62
+ progressSuccess: 'bg-[#41ffb1]',
63
+ progressError: 'bg-[#fb5d5d]',
64
+ };
65
+
66
+ /**
67
+ * Creates an icon element for the notification.
68
+ */
69
+ const createIcon = (style?: string): HTMLElement => {
70
+ const iconWrapper = document.createElement('span');
71
+ const resolvedStyle = style === 'success' || style === 'error' ? style : 'default';
72
+
73
+ iconWrapper.innerHTML = ICONS[resolvedStyle];
74
+ iconWrapper.setAttribute('data-blok-testid', 'notification-icon');
75
+ iconWrapper.setAttribute('data-blok-style', resolvedStyle);
76
+
77
+ const iconColorMap: Record<string, string> = {
78
+ success: CSS.iconSuccess,
79
+ error: CSS.iconError,
80
+ default: CSS.iconDefault,
81
+ };
82
+
83
+ const colorClass = iconColorMap[resolvedStyle] ?? CSS.iconDefault;
84
+
85
+ iconWrapper.className = twJoin(CSS.icon, colorClass);
86
+
87
+ return iconWrapper;
88
+ };
89
+
90
+ /**
91
+ * Creates a close (cross) button with an SVG icon.
92
+ */
93
+ const createCloseButton = (): HTMLElement => {
94
+ const cross = document.createElement('div');
95
+
96
+ cross.className = CSS.crossBtn;
97
+ cross.setAttribute('data-blok-testid', 'notification-cross');
98
+ cross.innerHTML = CLOSE_ICON;
99
+
100
+ return cross;
101
+ };
102
+
103
+ /**
104
+ * Creates a progress bar element for auto-dismissing alerts.
105
+ */
106
+ export const createProgressBar = (style?: string, time?: number): HTMLElement => {
107
+ const bar = document.createElement('div');
108
+
109
+ const progressColorMap: Record<string, string> = {
110
+ success: CSS.progressSuccess,
111
+ error: CSS.progressError,
112
+ };
113
+
114
+ const colorClass = progressColorMap[style ?? ''] ?? CSS.progressDefault;
115
+
116
+ bar.className = twJoin(CSS.progressBar, colorClass);
117
+ bar.setAttribute('data-blok-testid', 'notification-progress');
118
+ bar.style.animationDuration = `${time ?? 8000}ms`;
119
+
120
+ return bar;
40
121
  };
41
122
 
42
123
  /**
@@ -45,8 +126,6 @@ export const CSS = {
45
126
  */
46
127
  export const alert = (options: NotifierOptions): HTMLElement => {
47
128
  const notify = document.createElement('DIV');
48
- const cross = document.createElement('DIV');
49
- const message = options.message;
50
129
  const style = options.style;
51
130
 
52
131
  const getStyleClasses = (): string => {
@@ -69,12 +148,23 @@ export const alert = (options: NotifierOptions): HTMLElement => {
69
148
  notify.setAttribute('data-blok-testid', 'notification');
70
149
  }
71
150
 
72
- notify.innerHTML = message;
151
+ // Icon
152
+ const icon = createIcon(style);
73
153
 
74
- cross.className = CSS.crossBtn;
75
- cross.setAttribute('data-blok-testid', 'notification-cross');
76
- cross.addEventListener('click', () => notify.remove());
154
+ notify.appendChild(icon);
155
+
156
+ // Message wrapper (flex child that holds message + buttons)
157
+ const messageWrapper = document.createElement('div');
158
+
159
+ messageWrapper.className = CSS.messageWrapper;
160
+ messageWrapper.setAttribute('data-blok-testid', 'notification-message');
161
+ messageWrapper.innerHTML = options.message;
162
+ notify.appendChild(messageWrapper);
163
+
164
+ // Close button
165
+ const cross = createCloseButton();
77
166
 
167
+ cross.addEventListener('click', () => notify.remove());
78
168
  notify.appendChild(cross);
79
169
 
80
170
  return notify;
@@ -86,6 +176,7 @@ export const alert = (options: NotifierOptions): HTMLElement => {
86
176
  */
87
177
  export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
88
178
  const notify = alert(options);
179
+ const messageWrapper = notify.querySelector('[data-blok-testid="notification-message"]') as HTMLElement;
89
180
  const btnsWrapper = document.createElement('div');
90
181
  const okBtn = document.createElement('button');
91
182
  const cancelBtn = document.createElement('button');
@@ -123,7 +214,12 @@ export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
123
214
  btnsWrapper.appendChild(okBtn);
124
215
  btnsWrapper.appendChild(cancelBtn);
125
216
 
126
- notify.appendChild(btnsWrapper);
217
+ // Append buttons to the message wrapper so they flow under the message text
218
+ if (messageWrapper) {
219
+ messageWrapper.appendChild(btnsWrapper);
220
+ } else {
221
+ notify.appendChild(btnsWrapper);
222
+ }
127
223
 
128
224
  return notify;
129
225
  };
@@ -134,6 +230,7 @@ export const confirm = (options: ConfirmNotifierOptions): HTMLElement => {
134
230
  */
135
231
  export const prompt = (options: PromptNotifierOptions): HTMLElement => {
136
232
  const notify = alert(options);
233
+ const messageWrapper = notify.querySelector('[data-blok-testid="notification-message"]') as HTMLElement;
137
234
  const btnsWrapper = document.createElement('div');
138
235
  const okBtn = document.createElement('button');
139
236
  const input = document.createElement('input');
@@ -176,7 +273,12 @@ export const prompt = (options: PromptNotifierOptions): HTMLElement => {
176
273
  btnsWrapper.appendChild(input);
177
274
  btnsWrapper.appendChild(okBtn);
178
275
 
179
- notify.appendChild(btnsWrapper);
276
+ // Append to message wrapper for proper flex layout
277
+ if (messageWrapper) {
278
+ messageWrapper.appendChild(btnsWrapper);
279
+ } else {
280
+ notify.appendChild(btnsWrapper);
281
+ }
180
282
 
181
283
  return notify;
182
284
  };
@@ -1,8 +1,19 @@
1
- import { alert, confirm, getWrapper, prompt } from './draw';
1
+ import { alert, confirm, createProgressBar, getWrapper, prompt } from './draw';
2
2
  import type { NotifierOptions, ConfirmNotifierOptions, PromptNotifierOptions } from './types';
3
3
 
4
4
  const DEFAULT_TIME = 8000;
5
5
 
6
+ /**
7
+ * Applies the exit animation and removes the element after it finishes.
8
+ */
9
+ const dismissWithAnimation = (element: HTMLElement): void => {
10
+ element.classList.add('animate-notify-slide-out');
11
+
12
+ element.addEventListener('animationend', () => {
13
+ element.remove();
14
+ }, { once: true });
15
+ };
16
+
6
17
  /**
7
18
  * Prepare wrapper for notifications
8
19
  * @returns {HTMLElement}
@@ -48,8 +59,24 @@ export const show = (options: NotifierOptions | ConfirmNotifierOptions | PromptN
48
59
  // type is 'alert' or undefined
49
60
  const alertElement = alert(options);
50
61
 
62
+ // Add progress bar for auto-dismissing alerts
63
+ const progressBar = createProgressBar(options.style, time);
64
+
65
+ alertElement.appendChild(progressBar);
66
+
67
+ // Wire up the close button to use animated dismissal
68
+ const crossBtn = alertElement.querySelector('[data-blok-testid="notification-cross"]');
69
+
70
+ if (crossBtn) {
71
+ // Replace the basic remove handler with animated dismissal
72
+ const newCross = crossBtn.cloneNode(true) as HTMLElement;
73
+
74
+ crossBtn.replaceWith(newCross);
75
+ newCross.addEventListener('click', () => dismissWithAnimation(alertElement));
76
+ }
77
+
51
78
  window.setTimeout(() => {
52
- alertElement.remove();
79
+ dismissWithAnimation(alertElement);
53
80
  }, time);
54
81
 
55
82
  return alertElement;
@@ -57,11 +84,11 @@ export const show = (options: NotifierOptions | ConfirmNotifierOptions | PromptN
57
84
 
58
85
  if (wrapper && notify) {
59
86
  wrapper.appendChild(notify);
60
- notify.className = `${notify.className} animate-notify-bounce-in`;
87
+ notify.className = `${notify.className} animate-notify-slide-in`;
61
88
  notify.setAttribute('data-blok-bounce-in', 'true');
62
89
  }
63
90
  };
64
91
 
65
92
  export const Notifier = {
66
93
  show,
67
- };
94
+ };
@@ -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-lg px-2 py-(--item-padding) text-text-primary mb-0.5 transition-colors duration-150 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-lg px-2 py-(--item-padding) text-text-primary mb-0.5 transition-[color,background-color,border-color,opacity,max-height,padding,margin] duration-150 max-h-10 overflow-hidden 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
@@ -21,7 +21,7 @@ export const css = {
21
21
  /**
22
22
  * Icon container styles
23
23
  */
24
- icon: 'flex items-center justify-center w-[26px] h-[26px] rounded-lg [&_svg]:w-icon [&_svg]:h-icon',
24
+ icon: 'flex items-center justify-center w-[26px] h-[26px] shrink-0 rounded-lg [&_svg]:w-icon [&_svg]:h-icon',
25
25
 
26
26
  /**
27
27
  * Focused state class for DomIterator/Flipper keyboard navigation.
@@ -258,8 +258,8 @@ export class PopoverItemDefault extends PopoverItem {
258
258
  const titleEl = document.createElement('div');
259
259
 
260
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
+ ? 'grow whitespace-nowrap text-sm font-medium leading-5'
262
+ : 'mr-auto whitespace-nowrap text-sm font-medium leading-5';
263
263
  titleEl.setAttribute(DATA_ATTR.popoverItemTitle, '');
264
264
  titleEl.setAttribute('data-blok-testid', 'popover-item-title');
265
265
  titleEl.textContent = title;
@@ -272,7 +272,7 @@ export class PopoverItemDefault extends PopoverItem {
272
272
  if (params.secondaryLabel) {
273
273
  const secondaryEl = document.createElement('div');
274
274
 
275
- secondaryEl.className = 'ml-auto flex items-center whitespace-nowrap pl-3 text-[11px] font-medium tracking-wide text-text-secondary/50';
275
+ secondaryEl.className = 'ml-auto shrink-0 flex items-center whitespace-nowrap pl-20 text-[11px] font-medium tracking-wide text-text-secondary/50';
276
276
  secondaryEl.setAttribute(DATA_ATTR.popoverItemSecondaryTitle, '');
277
277
  secondaryEl.setAttribute('data-blok-testid', 'popover-item-secondary-title');
278
278
  secondaryEl.textContent = params.secondaryLabel;
@@ -326,8 +326,7 @@ export class PopoverItemDefault extends PopoverItem {
326
326
 
327
327
  return twMerge(
328
328
  css.item,
329
- // Asymmetric padding: pr-8 for visual balance, pr-4 when secondary label or chevron occupies right edge
330
- !isInline && !isNestedInline && (this.params.secondaryLabel || (this.hasChildren && !this.isChevronHidden) ? 'pl-2 pr-4' : 'pl-2 pr-8'),
329
+ !isInline && !isNestedInline && 'pl-2 pr-4',
331
330
  isInline && cssInline.item,
332
331
  isNestedInline && cssNestedInline.item,
333
332
  this.params.isDisabled && css.itemDisabled
@@ -385,10 +384,10 @@ export class PopoverItemDefault extends PopoverItem {
385
384
 
386
385
  if (isHidden) {
387
386
  this.nodes.root.setAttribute(DATA_ATTR.hidden, 'true');
388
- this.nodes.root.classList.add('hidden!');
387
+ this.nodes.root.classList.add('opacity-0', 'max-h-0!', 'py-0!', 'mb-0!');
389
388
  } else {
390
389
  this.nodes.root.removeAttribute(DATA_ATTR.hidden);
391
- this.nodes.root.classList.remove('hidden!');
390
+ this.nodes.root.classList.remove('opacity-0', 'max-h-0!', 'py-0!', 'mb-0!');
392
391
  }
393
392
  }
394
393