@jackuait/blok 0.7.0-beta.3 → 0.7.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 (462) hide show
  1. package/dist/blok.mjs +3 -7
  2. package/dist/chunks/blok-Ufr5cPq-.mjs +12435 -0
  3. package/dist/chunks/constants-DT17zmu_.mjs +2934 -0
  4. package/dist/chunks/i18next-DymC16cN.mjs +1146 -0
  5. package/dist/chunks/i18next-loader-qjweOJ-t.mjs +35 -0
  6. package/dist/chunks/lightweight-i18n-vbtPx5C4.mjs +105 -0
  7. package/dist/chunks/messages-3bOAVT3X2.mjs +80 -0
  8. package/dist/chunks/messages-43N0Vfg42.mjs +80 -0
  9. package/dist/chunks/messages-B0cg-ThO2.mjs +80 -0
  10. package/dist/chunks/messages-B3StvafX.mjs +80 -0
  11. package/dist/chunks/messages-B7LU-b6n2.mjs +80 -0
  12. package/dist/chunks/messages-B87-89os.mjs +80 -0
  13. package/dist/chunks/messages-BFiMCfDX2.mjs +80 -0
  14. package/dist/chunks/messages-BLxyso1L.mjs +80 -0
  15. package/dist/chunks/messages-BQZtOYxr2.mjs +80 -0
  16. package/dist/chunks/messages-BRrtoRdw2.mjs +80 -0
  17. package/dist/chunks/messages-BU_YdaAf.mjs +80 -0
  18. package/dist/chunks/messages-BWbZYIs12.mjs +80 -0
  19. package/dist/chunks/messages-B_Qcy8kr2.mjs +80 -0
  20. package/dist/chunks/messages-B_uTiuQ-.mjs +80 -0
  21. package/dist/chunks/messages-BdWTM73p.mjs +80 -0
  22. package/dist/chunks/messages-BhZcNoIQ.mjs +80 -0
  23. package/dist/chunks/messages-Bn6LwI4B.mjs +80 -0
  24. package/dist/chunks/messages-BoTtYEct2.mjs +80 -0
  25. package/dist/chunks/messages-BrvAiuWT.mjs +80 -0
  26. package/dist/chunks/messages-Byp0YFMg.mjs +80 -0
  27. package/dist/chunks/messages-C0ZWDShx2.mjs +80 -0
  28. package/dist/chunks/messages-CA-jms9R.mjs +80 -0
  29. package/dist/chunks/messages-CFr0Ha6p2.mjs +80 -0
  30. package/dist/chunks/messages-CG2xl0IV.mjs +80 -0
  31. package/dist/chunks/messages-CIGX0FfW.mjs +80 -0
  32. package/dist/chunks/messages-CRMdL0jG.mjs +80 -0
  33. package/dist/chunks/messages-CRdl14uE.mjs +80 -0
  34. package/dist/chunks/messages-Cimsel4e.mjs +80 -0
  35. package/dist/chunks/messages-CjcSWeud.mjs +80 -0
  36. package/dist/chunks/messages-ClDJuy8K2.mjs +80 -0
  37. package/dist/chunks/messages-Cn1AC0Qk.mjs +80 -0
  38. package/dist/chunks/messages-CpnXbVOK2.mjs +80 -0
  39. package/dist/chunks/messages-CqsES1wk2.mjs +80 -0
  40. package/dist/chunks/messages-Csq7JatN.mjs +80 -0
  41. package/dist/chunks/messages-CtufKbaD.mjs +80 -0
  42. package/dist/chunks/messages-Cuk0QaLM.mjs +80 -0
  43. package/dist/chunks/messages-CvamFN6x.mjs +80 -0
  44. package/dist/chunks/messages-CwRhVVui.mjs +80 -0
  45. package/dist/chunks/messages-CzCezryo.mjs +80 -0
  46. package/dist/chunks/messages-D0v0Xa_i2.mjs +80 -0
  47. package/dist/chunks/messages-D3JVx_CH2.mjs +80 -0
  48. package/dist/chunks/messages-D4jR5Oc-.mjs +80 -0
  49. package/dist/chunks/messages-D7fI9Pj52.mjs +80 -0
  50. package/dist/chunks/messages-DGodJU2R.mjs +80 -0
  51. package/dist/chunks/messages-DLrmLkco2.mjs +80 -0
  52. package/dist/chunks/messages-DPe7kW6J.mjs +80 -0
  53. package/dist/chunks/messages-DRYKKPk8.mjs +80 -0
  54. package/dist/chunks/messages-DV5c_ZRQ.mjs +80 -0
  55. package/dist/chunks/messages-Dg6kSnxq.mjs +80 -0
  56. package/dist/chunks/messages-Dgfbmyf-.mjs +80 -0
  57. package/dist/chunks/messages-DihczS7L.mjs +80 -0
  58. package/dist/chunks/messages-DkSwQvmi2.mjs +80 -0
  59. package/dist/chunks/messages-Doxcj7Qy.mjs +80 -0
  60. package/dist/chunks/messages-DqGQvcXv2.mjs +80 -0
  61. package/dist/chunks/messages-Dr7yA3xM.mjs +80 -0
  62. package/dist/chunks/messages-DriB5lEF.mjs +80 -0
  63. package/dist/chunks/messages-FB_MePlt.mjs +80 -0
  64. package/dist/chunks/messages-JyZvGvrN.mjs +80 -0
  65. package/dist/chunks/messages-KdvbGwLH.mjs +80 -0
  66. package/dist/chunks/messages-M0HT-kBW.mjs +80 -0
  67. package/dist/chunks/messages-M8noQ6Kp2.mjs +80 -0
  68. package/dist/chunks/messages-elZUbCrN.mjs +80 -0
  69. package/dist/chunks/messages-iWMOMK822.mjs +80 -0
  70. package/dist/chunks/messages-kC92TJI72.mjs +80 -0
  71. package/dist/chunks/messages-tfyq1JIh2.mjs +80 -0
  72. package/dist/chunks/messages-v1HkA3kF2.mjs +80 -0
  73. package/dist/chunks/messages-yuqArCc6.mjs +80 -0
  74. package/dist/chunks/notifier-BqYxvxnV.mjs +96 -0
  75. package/dist/chunks/objectSpread2-CyPxu8-u.mjs +62 -0
  76. package/dist/chunks/tools-CJIETS-H.mjs +6004 -0
  77. package/dist/chunks/tw-DmW6-pCY.mjs +237 -0
  78. package/dist/cli.mjs +36 -49
  79. package/dist/full.mjs +26 -52
  80. package/dist/locales.mjs +181 -254
  81. package/dist/messages-2iHnlF0U.mjs +80 -0
  82. package/dist/messages-49ZJ_ISf.mjs +80 -0
  83. package/dist/messages-B8jjwMLK.mjs +80 -0
  84. package/dist/messages-BEDVb3ZX.mjs +80 -0
  85. package/dist/messages-BEEr6Vh82.mjs +80 -0
  86. package/dist/messages-BFT0F9pw.mjs +80 -0
  87. package/dist/messages-BHOI7R4K.mjs +80 -0
  88. package/dist/messages-BRPH_a6a.mjs +80 -0
  89. package/dist/messages-BSlQrYwp.mjs +80 -0
  90. package/dist/messages-BTNuOkhL.mjs +80 -0
  91. package/dist/messages-BX2KVzJp2.mjs +80 -0
  92. package/dist/messages-BaGwIHPb2.mjs +80 -0
  93. package/dist/messages-BdA_xvxj.mjs +80 -0
  94. package/dist/messages-BeJaje7e2.mjs +80 -0
  95. package/dist/messages-BfgHOkAy.mjs +80 -0
  96. package/dist/messages-BflWzIcP2.mjs +80 -0
  97. package/dist/messages-BigRnQS92.mjs +80 -0
  98. package/dist/messages-BjnJajTO2.mjs +80 -0
  99. package/dist/messages-BpA30dPf.mjs +80 -0
  100. package/dist/messages-BrPEPj382.mjs +80 -0
  101. package/dist/messages-Bt_9ptDu.mjs +80 -0
  102. package/dist/messages-C0cXOCHN2.mjs +80 -0
  103. package/dist/messages-C3tLCwJp2.mjs +80 -0
  104. package/dist/messages-C45IBZtA2.mjs +80 -0
  105. package/dist/messages-CA0hwajz.mjs +80 -0
  106. package/dist/messages-CCKZS2f4.mjs +80 -0
  107. package/dist/messages-CCm71gq3.mjs +80 -0
  108. package/dist/messages-CERs9LC9.mjs +80 -0
  109. package/dist/messages-CLQvtc_8.mjs +80 -0
  110. package/dist/messages-CPx1R-PH.mjs +80 -0
  111. package/dist/messages-CYFdbooL2.mjs +80 -0
  112. package/dist/messages-CYLYnOV82.mjs +80 -0
  113. package/dist/messages-CYZVFnaF.mjs +80 -0
  114. package/dist/messages-CaAdEXoh2.mjs +80 -0
  115. package/dist/messages-CicggErN2.mjs +80 -0
  116. package/dist/messages-CkAWTSc4.mjs +80 -0
  117. package/dist/messages-CkVfziK_2.mjs +80 -0
  118. package/dist/messages-CsM2iz1H2.mjs +80 -0
  119. package/dist/messages-D-12TeCM2.mjs +80 -0
  120. package/dist/messages-D0i5Vdyy2.mjs +80 -0
  121. package/dist/messages-D5KmRsUV2.mjs +80 -0
  122. package/dist/messages-DBwaWI0X.mjs +80 -0
  123. package/dist/messages-DDGzypb4.mjs +80 -0
  124. package/dist/messages-DQGzw4IC.mjs +80 -0
  125. package/dist/messages-DWZyaZNA.mjs +80 -0
  126. package/dist/messages-DYlxQEIv.mjs +80 -0
  127. package/dist/messages-DZo0x7Bd.mjs +80 -0
  128. package/dist/messages-Dc1yFFBM.mjs +80 -0
  129. package/dist/messages-DdUpYaJ1.mjs +80 -0
  130. package/dist/messages-DgstU8GH.mjs +80 -0
  131. package/dist/messages-DhdWq5oQ2.mjs +80 -0
  132. package/dist/messages-DmX52AQr.mjs +80 -0
  133. package/dist/messages-Dr-YJYIK2.mjs +80 -0
  134. package/dist/messages-DuubRyFf.mjs +80 -0
  135. package/dist/messages-DvTVsLOK2.mjs +80 -0
  136. package/dist/messages-DwPfgL_u.mjs +80 -0
  137. package/dist/messages-DxKIxLKw.mjs +80 -0
  138. package/dist/messages-DzhR8Klk.mjs +80 -0
  139. package/dist/messages-MBBSKGjJ2.mjs +80 -0
  140. package/dist/messages-RNusm48G2.mjs +80 -0
  141. package/dist/messages-XwPD18Kk.mjs +80 -0
  142. package/dist/messages-YfjdnhUF.mjs +80 -0
  143. package/dist/messages-aNMLsF8T2.mjs +80 -0
  144. package/dist/messages-cOqXp22e.mjs +80 -0
  145. package/dist/messages-g58itYPI.mjs +80 -0
  146. package/dist/messages-vfkwiKQo.mjs +80 -0
  147. package/dist/messages-vssmW7KO.mjs +80 -0
  148. package/dist/react.mjs +108 -0
  149. package/dist/tools.mjs +3 -7462
  150. package/dist/vendor.LICENSE.txt +86 -86
  151. package/package.json +56 -29
  152. package/src/blok.ts +52 -2
  153. package/src/components/block/api.ts +8 -0
  154. package/src/components/block/mutation-handler.ts +29 -4
  155. package/src/components/block/style-manager.ts +1 -1
  156. package/src/components/block-tunes/block-tune-width.ts +39 -0
  157. package/src/components/blocks.ts +56 -2
  158. package/src/components/core.ts +1 -0
  159. package/src/components/i18n/locales/am/messages.json +6 -1
  160. package/src/components/i18n/locales/ar/messages.json +6 -1
  161. package/src/components/i18n/locales/az/messages.json +6 -1
  162. package/src/components/i18n/locales/bg/messages.json +8 -3
  163. package/src/components/i18n/locales/bn/messages.json +6 -1
  164. package/src/components/i18n/locales/bs/messages.json +6 -1
  165. package/src/components/i18n/locales/cs/messages.json +6 -1
  166. package/src/components/i18n/locales/da/messages.json +6 -1
  167. package/src/components/i18n/locales/de/messages.json +8 -3
  168. package/src/components/i18n/locales/dv/messages.json +6 -1
  169. package/src/components/i18n/locales/el/messages.json +8 -3
  170. package/src/components/i18n/locales/en/messages.json +5 -0
  171. package/src/components/i18n/locales/es/messages.json +6 -1
  172. package/src/components/i18n/locales/et/messages.json +6 -1
  173. package/src/components/i18n/locales/fa/messages.json +8 -3
  174. package/src/components/i18n/locales/fi/messages.json +6 -1
  175. package/src/components/i18n/locales/fil/messages.json +21 -16
  176. package/src/components/i18n/locales/fr/messages.json +6 -1
  177. package/src/components/i18n/locales/gu/messages.json +6 -1
  178. package/src/components/i18n/locales/he/messages.json +6 -1
  179. package/src/components/i18n/locales/hi/messages.json +6 -1
  180. package/src/components/i18n/locales/hr/messages.json +6 -1
  181. package/src/components/i18n/locales/hu/messages.json +6 -1
  182. package/src/components/i18n/locales/hy/messages.json +8 -3
  183. package/src/components/i18n/locales/id/messages.json +12 -7
  184. package/src/components/i18n/locales/it/messages.json +6 -1
  185. package/src/components/i18n/locales/ja/messages.json +6 -1
  186. package/src/components/i18n/locales/ka/messages.json +6 -1
  187. package/src/components/i18n/locales/km/messages.json +6 -1
  188. package/src/components/i18n/locales/kn/messages.json +6 -1
  189. package/src/components/i18n/locales/ko/messages.json +6 -1
  190. package/src/components/i18n/locales/ku/messages.json +7 -2
  191. package/src/components/i18n/locales/lo/messages.json +7 -2
  192. package/src/components/i18n/locales/lt/messages.json +6 -1
  193. package/src/components/i18n/locales/lv/messages.json +6 -1
  194. package/src/components/i18n/locales/mk/messages.json +6 -1
  195. package/src/components/i18n/locales/ml/messages.json +6 -1
  196. package/src/components/i18n/locales/mn/messages.json +6 -1
  197. package/src/components/i18n/locales/mr/messages.json +6 -1
  198. package/src/components/i18n/locales/ms/messages.json +6 -1
  199. package/src/components/i18n/locales/my/messages.json +6 -1
  200. package/src/components/i18n/locales/ne/messages.json +9 -4
  201. package/src/components/i18n/locales/nl/messages.json +6 -1
  202. package/src/components/i18n/locales/no/messages.json +6 -1
  203. package/src/components/i18n/locales/pa/messages.json +6 -1
  204. package/src/components/i18n/locales/pl/messages.json +6 -1
  205. package/src/components/i18n/locales/ps/messages.json +8 -3
  206. package/src/components/i18n/locales/pt/messages.json +6 -1
  207. package/src/components/i18n/locales/ro/messages.json +6 -1
  208. package/src/components/i18n/locales/ru/messages.json +10 -5
  209. package/src/components/i18n/locales/sd/messages.json +6 -1
  210. package/src/components/i18n/locales/si/messages.json +6 -1
  211. package/src/components/i18n/locales/sk/messages.json +6 -1
  212. package/src/components/i18n/locales/sl/messages.json +6 -1
  213. package/src/components/i18n/locales/sq/messages.json +6 -1
  214. package/src/components/i18n/locales/sr/messages.json +6 -1
  215. package/src/components/i18n/locales/sv/messages.json +6 -1
  216. package/src/components/i18n/locales/sw/messages.json +6 -1
  217. package/src/components/i18n/locales/ta/messages.json +6 -1
  218. package/src/components/i18n/locales/te/messages.json +9 -4
  219. package/src/components/i18n/locales/th/messages.json +6 -1
  220. package/src/components/i18n/locales/tr/messages.json +6 -1
  221. package/src/components/i18n/locales/ug/messages.json +7 -2
  222. package/src/components/i18n/locales/uk/messages.json +6 -1
  223. package/src/components/i18n/locales/ur/messages.json +6 -1
  224. package/src/components/i18n/locales/vi/messages.json +6 -1
  225. package/src/components/i18n/locales/yi/messages.json +7 -2
  226. package/src/components/i18n/locales/zh/messages.json +6 -1
  227. package/src/components/icons/index.ts +16 -0
  228. package/src/components/inline-tools/inline-tool-link.ts +1 -1
  229. package/src/components/modules/api/blocks.ts +45 -2
  230. package/src/components/modules/api/index.ts +1 -0
  231. package/src/components/modules/api/width.ts +17 -0
  232. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +99 -0
  233. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +109 -2
  234. package/src/components/modules/blockEvents/constants.ts +7 -0
  235. package/src/components/modules/blockManager/blockManager.ts +113 -9
  236. package/src/components/modules/blockManager/hierarchy.ts +61 -0
  237. package/src/components/modules/blockManager/operations.ts +161 -16
  238. package/src/components/modules/blockManager/yjs-sync.ts +112 -4
  239. package/src/components/modules/blockSelection.ts +36 -2
  240. package/src/components/modules/crossBlockSelection.ts +22 -2
  241. package/src/components/modules/drag/DragController.ts +178 -4
  242. package/src/components/modules/drag/operations/DragOperations.ts +48 -9
  243. package/src/components/modules/drag/preview/DragPreview.ts +21 -1
  244. package/src/components/modules/drag/state/DragStateMachine.ts +6 -1
  245. package/src/components/modules/drag/target/DropTargetDetector.ts +80 -4
  246. package/src/components/modules/drag/utils/ToggleSpringLoader.ts +71 -0
  247. package/src/components/modules/index.ts +7 -1
  248. package/src/components/modules/modificationsObserver.ts +19 -0
  249. package/src/components/modules/paste/constants.ts +2 -0
  250. package/src/components/modules/paste/handlers/base.ts +33 -1
  251. package/src/components/modules/paste/handlers/html-handler.ts +121 -54
  252. package/src/components/modules/paste/index.ts +5 -0
  253. package/src/components/modules/paste/types.ts +5 -0
  254. package/src/components/modules/rectangleSelection.ts +74 -81
  255. package/src/components/modules/toolbar/blockSettings.ts +25 -7
  256. package/src/components/modules/toolbar/index.ts +9 -7
  257. package/src/components/modules/toolbar/inline/index.ts +6 -1
  258. package/src/components/modules/toolbar/plus-button.ts +2 -6
  259. package/src/components/modules/toolbar/positioning.ts +10 -1
  260. package/src/components/modules/toolbar/settings-toggler.ts +1 -1
  261. package/src/components/modules/toolbar/styles.ts +3 -7
  262. package/src/components/modules/ui.ts +59 -5
  263. package/src/components/modules/uiControllers/handlers/click.ts +3 -2
  264. package/src/components/modules/widthManager.ts +69 -0
  265. package/src/components/modules/yjs/document-store.ts +11 -0
  266. package/src/components/modules/yjs/index.ts +11 -0
  267. package/src/components/shared/color-picker.ts +3 -3
  268. package/src/components/tools/block.ts +1 -11
  269. package/src/components/ui/toolbox.ts +52 -8
  270. package/src/components/utils/blocks.ts +37 -7
  271. package/src/components/utils/mutations.ts +2 -2
  272. package/src/components/utils/notifier/draw.ts +1 -1
  273. package/src/components/utils/placeholder.ts +5 -6
  274. package/src/components/utils/popover/components/popover-header/popover-header.const.ts +1 -1
  275. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +4 -4
  276. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -6
  277. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
  278. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
  279. package/src/components/utils/popover/components/search-input/search-input.ts +7 -11
  280. package/src/components/utils/popover/components/search-input/search-input.types.ts +149 -10
  281. package/src/components/utils/popover/popover-abstract.ts +3 -2
  282. package/src/components/utils/popover/popover-desktop.ts +133 -11
  283. package/src/components/utils/popover/popover-inline.ts +1 -1
  284. package/src/components/utils/popover/popover.const.ts +3 -3
  285. package/src/components/utils/shortcut.ts +2 -0
  286. package/src/components/utils/tooltip.ts +11 -1
  287. package/src/react/BlokContent.tsx +46 -0
  288. package/src/react/holder-map.ts +17 -0
  289. package/src/react/index.ts +3 -0
  290. package/src/react/types.ts +16 -0
  291. package/src/react/useBlok.ts +173 -0
  292. package/src/stories/Placeholder.stories.ts +0 -59
  293. package/src/styles/main.css +663 -52
  294. package/src/tools/header/header-toggle-keyboard.ts +115 -0
  295. package/src/tools/header/index.ts +382 -187
  296. package/src/tools/list/block-operations.ts +1 -1
  297. package/src/tools/list/caret-manager.ts +9 -12
  298. package/src/tools/list/index.ts +2 -6
  299. package/src/tools/list/list-keyboard.ts +2 -2
  300. package/src/tools/paragraph/index.ts +1 -1
  301. package/src/tools/table/index.ts +75 -3
  302. package/src/tools/table/table-add-controls.ts +97 -8
  303. package/src/tools/table/table-cell-blocks.ts +17 -8
  304. package/src/tools/table/table-cell-clipboard.ts +1 -1
  305. package/src/tools/table/table-cell-selection.ts +27 -2
  306. package/src/tools/table/table-operations.ts +3 -2
  307. package/src/tools/toggle/block-operations.ts +4 -2
  308. package/src/tools/toggle/constants.ts +26 -2
  309. package/src/tools/toggle/dom-builder.ts +90 -25
  310. package/src/tools/toggle/index.ts +112 -9
  311. package/src/tools/toggle/toggle-keyboard.ts +5 -3
  312. package/src/tools/toggle/toggle-lifecycle.ts +79 -7
  313. package/src/tools/toggle/toggle-shortcuts.ts +214 -20
  314. package/src/tools/toggle/types.ts +2 -0
  315. package/src/types-internal/blok-modules.d.ts +4 -0
  316. package/types/api/block.d.ts +5 -0
  317. package/types/api/blocks.d.ts +29 -0
  318. package/types/api/index.d.ts +1 -0
  319. package/types/api/width.d.ts +19 -0
  320. package/types/configs/blok-config.d.ts +33 -0
  321. package/types/index.d.ts +4 -0
  322. package/types/react.d.ts +58 -0
  323. package/types/utils/popover/popover.d.ts +7 -0
  324. package/dist/chunks/blok-BlyYiZTm.mjs +0 -20098
  325. package/dist/chunks/constants-DEy4jBO5.mjs +0 -5123
  326. package/dist/chunks/i18next-B47TKgbU.mjs +0 -1303
  327. package/dist/chunks/i18next-loader-Cfbv-x6v.mjs +0 -43
  328. package/dist/chunks/index-Cu1w-sLZ.mjs +0 -130
  329. package/dist/chunks/messages-0Pxnqd4N.mjs +0 -75
  330. package/dist/chunks/messages-0ZXYUq7S.mjs +0 -75
  331. package/dist/chunks/messages-2OD2uUDS.mjs +0 -75
  332. package/dist/chunks/messages-7cEMfYzh.mjs +0 -75
  333. package/dist/chunks/messages-8mwfda1Q.mjs +0 -75
  334. package/dist/chunks/messages-B-FqWsBM.mjs +0 -75
  335. package/dist/chunks/messages-B1jzqWiQ.mjs +0 -75
  336. package/dist/chunks/messages-B5wk4Ezz.mjs +0 -75
  337. package/dist/chunks/messages-BAZ5Ld8x.mjs +0 -75
  338. package/dist/chunks/messages-BBhGp198.mjs +0 -75
  339. package/dist/chunks/messages-BC9IjIb7.mjs +0 -75
  340. package/dist/chunks/messages-BFEmpeV-.mjs +0 -75
  341. package/dist/chunks/messages-BGqzTZy0.mjs +0 -75
  342. package/dist/chunks/messages-BICs1abK.mjs +0 -75
  343. package/dist/chunks/messages-BJX6rOnd.mjs +0 -75
  344. package/dist/chunks/messages-BL2bXRhN.mjs +0 -75
  345. package/dist/chunks/messages-BMs5qdlH.mjs +0 -75
  346. package/dist/chunks/messages-BRsjUNwB.mjs +0 -75
  347. package/dist/chunks/messages-BSqV8OUR.mjs +0 -75
  348. package/dist/chunks/messages-BTqu3DfG.mjs +0 -75
  349. package/dist/chunks/messages-BXnDEsur.mjs +0 -75
  350. package/dist/chunks/messages-BYcre4-6.mjs +0 -75
  351. package/dist/chunks/messages-BZ9LRJf-.mjs +0 -75
  352. package/dist/chunks/messages-BgypBy7y.mjs +0 -75
  353. package/dist/chunks/messages-BsuGf70G.mjs +0 -75
  354. package/dist/chunks/messages-BwaoF4lQ.mjs +0 -75
  355. package/dist/chunks/messages-C1l8_7-y.mjs +0 -75
  356. package/dist/chunks/messages-C5NA_r9v.mjs +0 -75
  357. package/dist/chunks/messages-C6zgZ5pA.mjs +0 -75
  358. package/dist/chunks/messages-CAo5ghFI.mjs +0 -75
  359. package/dist/chunks/messages-CH9qlJ9I.mjs +0 -75
  360. package/dist/chunks/messages-CI0HqAeS.mjs +0 -75
  361. package/dist/chunks/messages-CJJtms9k.mjs +0 -75
  362. package/dist/chunks/messages-CM2hJqk6.mjs +0 -75
  363. package/dist/chunks/messages-CRMiDPIQ.mjs +0 -75
  364. package/dist/chunks/messages-CWsZuBj1.mjs +0 -75
  365. package/dist/chunks/messages-C_gLHo6A.mjs +0 -75
  366. package/dist/chunks/messages-Cbu-NUDn.mjs +0 -75
  367. package/dist/chunks/messages-Cjb_MCeh.mjs +0 -75
  368. package/dist/chunks/messages-ClXYO9Wn.mjs +0 -75
  369. package/dist/chunks/messages-CsH20vhP.mjs +0 -75
  370. package/dist/chunks/messages-CsjAGhzA.mjs +0 -75
  371. package/dist/chunks/messages-Cx7VKFOE.mjs +0 -75
  372. package/dist/chunks/messages-D3JeBwxo.mjs +0 -75
  373. package/dist/chunks/messages-D541fieJ.mjs +0 -75
  374. package/dist/chunks/messages-D7XPdglc.mjs +0 -75
  375. package/dist/chunks/messages-DBhylfvt.mjs +0 -75
  376. package/dist/chunks/messages-DCA120lW.mjs +0 -75
  377. package/dist/chunks/messages-DCf_xZMN.mjs +0 -75
  378. package/dist/chunks/messages-DDwXKCpe.mjs +0 -75
  379. package/dist/chunks/messages-DNKDlxcy.mjs +0 -75
  380. package/dist/chunks/messages-DPvEjrGK.mjs +0 -75
  381. package/dist/chunks/messages-DQ-AkNxA.mjs +0 -75
  382. package/dist/chunks/messages-DVuvkNap.mjs +0 -75
  383. package/dist/chunks/messages-DaglyqUT.mjs +0 -75
  384. package/dist/chunks/messages-Di0bAfwA.mjs +0 -75
  385. package/dist/chunks/messages-DuLct0Yr.mjs +0 -75
  386. package/dist/chunks/messages-DzEYYhZh.mjs +0 -75
  387. package/dist/chunks/messages-DznNGAB2.mjs +0 -75
  388. package/dist/chunks/messages-DzoIzyu8.mjs +0 -75
  389. package/dist/chunks/messages-QYOGmket.mjs +0 -75
  390. package/dist/chunks/messages-cEjGDAgI.mjs +0 -75
  391. package/dist/chunks/messages-ddhvrdpE.mjs +0 -75
  392. package/dist/chunks/messages-mwfNK5nZ.mjs +0 -75
  393. package/dist/chunks/messages-nG_vNDte.mjs +0 -75
  394. package/dist/chunks/messages-tDq3Owh7.mjs +0 -75
  395. package/dist/chunks/messages-x6VJVZKx.mjs +0 -75
  396. package/dist/messages-0Pxnqd4N.mjs +0 -75
  397. package/dist/messages-0ZXYUq7S.mjs +0 -75
  398. package/dist/messages-2OD2uUDS.mjs +0 -75
  399. package/dist/messages-7cEMfYzh.mjs +0 -75
  400. package/dist/messages-8mwfda1Q.mjs +0 -75
  401. package/dist/messages-B-FqWsBM.mjs +0 -75
  402. package/dist/messages-B1jzqWiQ.mjs +0 -75
  403. package/dist/messages-B5wk4Ezz.mjs +0 -75
  404. package/dist/messages-BAZ5Ld8x.mjs +0 -75
  405. package/dist/messages-BBhGp198.mjs +0 -75
  406. package/dist/messages-BC9IjIb7.mjs +0 -75
  407. package/dist/messages-BFEmpeV-.mjs +0 -75
  408. package/dist/messages-BGqzTZy0.mjs +0 -75
  409. package/dist/messages-BICs1abK.mjs +0 -75
  410. package/dist/messages-BJX6rOnd.mjs +0 -75
  411. package/dist/messages-BL2bXRhN.mjs +0 -75
  412. package/dist/messages-BMs5qdlH.mjs +0 -75
  413. package/dist/messages-BRsjUNwB.mjs +0 -75
  414. package/dist/messages-BSqV8OUR.mjs +0 -75
  415. package/dist/messages-BTqu3DfG.mjs +0 -75
  416. package/dist/messages-BXnDEsur.mjs +0 -75
  417. package/dist/messages-BYcre4-6.mjs +0 -75
  418. package/dist/messages-BZ9LRJf-.mjs +0 -75
  419. package/dist/messages-BgypBy7y.mjs +0 -75
  420. package/dist/messages-BsuGf70G.mjs +0 -75
  421. package/dist/messages-BwaoF4lQ.mjs +0 -75
  422. package/dist/messages-C1l8_7-y.mjs +0 -75
  423. package/dist/messages-C5NA_r9v.mjs +0 -75
  424. package/dist/messages-C6zgZ5pA.mjs +0 -75
  425. package/dist/messages-CAo5ghFI.mjs +0 -75
  426. package/dist/messages-CH9qlJ9I.mjs +0 -75
  427. package/dist/messages-CI0HqAeS.mjs +0 -75
  428. package/dist/messages-CJJtms9k.mjs +0 -75
  429. package/dist/messages-CM2hJqk6.mjs +0 -75
  430. package/dist/messages-CRMiDPIQ.mjs +0 -75
  431. package/dist/messages-CWsZuBj1.mjs +0 -75
  432. package/dist/messages-C_gLHo6A.mjs +0 -75
  433. package/dist/messages-Cbu-NUDn.mjs +0 -75
  434. package/dist/messages-Cjb_MCeh.mjs +0 -75
  435. package/dist/messages-ClXYO9Wn.mjs +0 -75
  436. package/dist/messages-CsH20vhP.mjs +0 -75
  437. package/dist/messages-CsjAGhzA.mjs +0 -75
  438. package/dist/messages-Cx7VKFOE.mjs +0 -75
  439. package/dist/messages-D3JeBwxo.mjs +0 -75
  440. package/dist/messages-D541fieJ.mjs +0 -75
  441. package/dist/messages-D7XPdglc.mjs +0 -75
  442. package/dist/messages-DBhylfvt.mjs +0 -75
  443. package/dist/messages-DCA120lW.mjs +0 -75
  444. package/dist/messages-DCf_xZMN.mjs +0 -75
  445. package/dist/messages-DDwXKCpe.mjs +0 -75
  446. package/dist/messages-DNKDlxcy.mjs +0 -75
  447. package/dist/messages-DPvEjrGK.mjs +0 -75
  448. package/dist/messages-DQ-AkNxA.mjs +0 -75
  449. package/dist/messages-DVuvkNap.mjs +0 -75
  450. package/dist/messages-DaglyqUT.mjs +0 -75
  451. package/dist/messages-Di0bAfwA.mjs +0 -75
  452. package/dist/messages-DuLct0Yr.mjs +0 -75
  453. package/dist/messages-DzEYYhZh.mjs +0 -75
  454. package/dist/messages-DznNGAB2.mjs +0 -75
  455. package/dist/messages-DzoIzyu8.mjs +0 -75
  456. package/dist/messages-QYOGmket.mjs +0 -75
  457. package/dist/messages-cEjGDAgI.mjs +0 -75
  458. package/dist/messages-ddhvrdpE.mjs +0 -75
  459. package/dist/messages-mwfNK5nZ.mjs +0 -75
  460. package/dist/messages-nG_vNDte.mjs +0 -75
  461. package/dist/messages-tDq3Owh7.mjs +0 -75
  462. package/dist/messages-x6VJVZKx.mjs +0 -75
@@ -6,9 +6,10 @@ import type { PopoverItem, PopoverItemRenderParamsMap } from './components/popov
6
6
  import { PopoverItemSeparator, css as popoverItemCls , PopoverItemDefault } from './components/popover-item';
7
7
  import { PopoverItemHtml } from './components/popover-item/popover-item-html/popover-item-html';
8
8
  import type { SearchableItem } from './components/search-input';
9
- import { SearchInput, SearchInputEvent, matchesSearchQuery } from './components/search-input';
9
+ import { SearchInput, SearchInputEvent, scoreSearchMatch } from './components/search-input';
10
10
  import { PopoverAbstract } from './popover-abstract';
11
- import { CSSVariables } from './popover.const';
11
+ import { CSSVariables, css as popoverCss } from './popover.const';
12
+ import { twMerge } from '../tw';
12
13
 
13
14
  import type { PopoverParams } from '@/types/utils/popover/popover';
14
15
  import { PopoverEvent } from '@/types/utils/popover/popover-event';
@@ -71,6 +72,12 @@ export class PopoverDesktop extends PopoverAbstract {
71
72
  */
72
73
  private _size: { height: number; width: number } | undefined;
73
74
 
75
+ /**
76
+ * Original order of item elements in the popover container.
77
+ * Cached on first search so we can restore order when query is cleared.
78
+ */
79
+ private originalItemOrder: Element[] | undefined;
80
+
74
81
  /**
75
82
  * Construct the instance
76
83
  * @param params - popover params
@@ -102,6 +109,7 @@ export class PopoverDesktop extends PopoverAbstract {
102
109
 
103
110
  if (this.nodes.popoverContainer !== null) {
104
111
  this.listeners.on(this.nodes.popoverContainer, 'mouseover', (event: Event) => this.handleHover(event));
112
+ this.listeners.on(this.nodes.popoverContainer, 'mouseleave', (event: Event) => this.handleMouseLeave(event));
105
113
  }
106
114
 
107
115
  if (params.searchable) {
@@ -225,15 +233,16 @@ export class PopoverDesktop extends PopoverAbstract {
225
233
  this.flipper?.activate(this.flippableElements);
226
234
 
227
235
  // Focus the first item: search field if present, otherwise first menu item
228
- requestAnimationFrame(() => {
236
+ queueMicrotask(() => {
229
237
  this.focusInitialElement();
230
238
  });
231
239
  }
232
240
 
233
241
  /**
234
242
  * Focuses the initial element when popover is shown.
235
- * Focuses search field if present, otherwise first menu item.
236
- * Skips the first Tab press so it just "enters" the menu rather than advancing.
243
+ * When a search field is present, it receives focus so the user can type immediately.
244
+ * When autoFocusFirstItem is false, no item is pre-focused focus only appears
245
+ * after the user begins keyboard navigation.
237
246
  */
238
247
  private focusInitialElement(): void {
239
248
  if (this.search) {
@@ -242,6 +251,10 @@ export class PopoverDesktop extends PopoverAbstract {
242
251
  return;
243
252
  }
244
253
 
254
+ if (this.params.autoFocusFirstItem === false) {
255
+ return;
256
+ }
257
+
245
258
  this.flipper?.focusItem(0, { skipNextTab: true });
246
259
  }
247
260
 
@@ -363,7 +376,20 @@ export class PopoverDesktop extends PopoverAbstract {
363
376
  return;
364
377
  }
365
378
 
366
- this.destroyNestedPopoverIfExists();
379
+ /**
380
+ * If the pointer moved into the nested popover (e.g. user is about to click
381
+ * an item in the sub-menu), do not destroy it.
382
+ */
383
+ if (
384
+ this.nestedPopover !== undefined &&
385
+ this.nestedPopover !== null &&
386
+ event.target instanceof Node &&
387
+ this.nestedPopover.hasNode(event.target)
388
+ ) {
389
+ return;
390
+ }
391
+
392
+ this.destroyNestedPopoverIfExists(false);
367
393
 
368
394
  this.previouslyHoveredItem = item;
369
395
 
@@ -374,6 +400,28 @@ export class PopoverDesktop extends PopoverAbstract {
374
400
  this.showNestedPopoverForItem(item);
375
401
  }
376
402
 
403
+ /**
404
+ * Handles mouse leaving the popover container.
405
+ * Destroys nested popover unless the mouse moved into it.
406
+ * @param event - mouseleave event
407
+ */
408
+ private handleMouseLeave(event: Event): void {
409
+ const mouseEvent = event as MouseEvent;
410
+ const relatedTarget = mouseEvent.relatedTarget;
411
+
412
+ if (
413
+ relatedTarget instanceof Node &&
414
+ this.nestedPopover !== undefined &&
415
+ this.nestedPopover !== null &&
416
+ this.nestedPopover.hasNode(relatedTarget)
417
+ ) {
418
+ return;
419
+ }
420
+
421
+ this.destroyNestedPopoverIfExists(false);
422
+ this.previouslyHoveredItem = null;
423
+ }
424
+
377
425
  /**
378
426
  * Sets CSS variable with position of item near which nested popover should be displayed.
379
427
  * Is used for correct positioning of the nested popover
@@ -393,8 +441,11 @@ export class PopoverDesktop extends PopoverAbstract {
393
441
 
394
442
  /**
395
443
  * Destroys existing nested popover
444
+ * @param restoreFocus - whether to restore keyboard focus to the trigger item after closing.
445
+ * Should be true for keyboard-driven closes (e.g. ArrowLeft/Escape), false for mouse-driven closes
446
+ * to avoid leaving a stale focus highlight on the trigger item.
396
447
  */
397
- protected destroyNestedPopoverIfExists(): void {
448
+ protected destroyNestedPopoverIfExists(restoreFocus = true): void {
398
449
  if (this.nestedPopover === undefined || this.nestedPopover === null) {
399
450
  return;
400
451
  }
@@ -408,8 +459,11 @@ export class PopoverDesktop extends PopoverAbstract {
408
459
  elementToRemove.remove();
409
460
  this.nestedPopover = null;
410
461
  this.flipper?.activate(this.flippableElements);
411
- // Focus the trigger item synchronously to ensure keyboard events work immediately
412
- this.focusAfterNestedPopoverClose(triggerItemElement);
462
+
463
+ if (restoreFocus) {
464
+ // Focus the trigger item synchronously to ensure keyboard events work immediately
465
+ this.focusAfterNestedPopoverClose(triggerItemElement);
466
+ }
413
467
 
414
468
  this.nestedPopoverTriggerItem?.onChildrenClose();
415
469
  // Reset trigger item so clicking the same item again will open the nested popover
@@ -457,6 +511,7 @@ export class PopoverDesktop extends PopoverAbstract {
457
511
  onNavigateBack: this.destroyNestedPopoverIfExists.bind(this),
458
512
  width: item.childrenWidth,
459
513
  handleContentEditableNavigation: handleContentEditable,
514
+ autoFocusFirstItem: this.params.autoFocusFirstItem,
460
515
  });
461
516
 
462
517
  item.onChildrenOpen();
@@ -608,10 +663,13 @@ export class PopoverDesktop extends PopoverAbstract {
608
663
 
609
664
  popoverClone.setAttribute(DATA_ATTR.popoverOpened, 'true');
610
665
  popoverClone.querySelector(`[${DATA_ATTR.nested}]`)?.remove();
611
- document.body.appendChild(popoverClone);
612
666
 
613
667
  const container = popoverClone.querySelector(`[${DATA_ATTR.popoverContainer}]`) as HTMLElement;
614
668
 
669
+ container.className = twMerge(container.className, popoverCss.popoverContainerOpened);
670
+
671
+ document.body.appendChild(popoverClone);
672
+
615
673
  size.height = container.offsetHeight;
616
674
  size.width = container.offsetWidth;
617
675
  popoverClone.remove();
@@ -690,7 +748,21 @@ export class PopoverDesktop extends PopoverAbstract {
690
748
  * @param query - search query text
691
749
  */
692
750
  public override filterItems(query: string): void {
693
- const matchingItems = this.itemsDefault.filter(item => matchesSearchQuery(item, query));
751
+ if (query === '') {
752
+ this.onSearch({
753
+ query,
754
+ items: this.itemsDefault as unknown as SearchableItem[],
755
+ });
756
+
757
+ return;
758
+ }
759
+
760
+ const scoredItems = this.itemsDefault
761
+ .map(item => ({ item, score: scoreSearchMatch(item, query) }))
762
+ .filter(({ score }) => score > 0)
763
+ .sort((a, b) => b.score - a.score);
764
+
765
+ const matchingItems = scoredItems.map(({ item }) => item);
694
766
 
695
767
  this.onSearch({
696
768
  query,
@@ -742,6 +814,13 @@ export class PopoverDesktop extends PopoverAbstract {
742
814
  });
743
815
  }
744
816
 
817
+ // Reorder DOM elements to reflect ranking
818
+ if (!isEmptyQuery && matchingItems.length > 0) {
819
+ this.reorderItemsByRank(matchingItems);
820
+ } else if (isEmptyQuery && this.originalItemOrder !== undefined) {
821
+ this.restoreOriginalItemOrder();
822
+ }
823
+
745
824
  this.toggleNothingFoundMessage(isNothingFound);
746
825
 
747
826
  /** List of elements available for keyboard navigation considering search query applied */
@@ -767,4 +846,47 @@ export class PopoverDesktop extends PopoverAbstract {
767
846
  this.flipper.focusItem(0, { skipNextTab: true });
768
847
  }
769
848
  };
849
+
850
+ /**
851
+ * Reorders DOM children of the items container to match the ranked order.
852
+ * Caches the original order on first call so it can be restored later.
853
+ * @param rankedItems - items sorted by search relevance (best first)
854
+ */
855
+ private reorderItemsByRank(rankedItems: PopoverItemDefault[]): void {
856
+ if (this.originalItemOrder === undefined && this.nodes.items !== null) {
857
+ this.originalItemOrder = Array.from(this.nodes.items.children);
858
+ }
859
+
860
+ const itemsContainer = this.nodes.items;
861
+
862
+ if (itemsContainer === null) {
863
+ return;
864
+ }
865
+
866
+ for (const item of rankedItems) {
867
+ const el = item.getElement();
868
+
869
+ if (el !== null) {
870
+ itemsContainer.appendChild(el);
871
+ }
872
+ }
873
+ }
874
+
875
+ /**
876
+ * Restores the original DOM order of items container children.
877
+ * Called when the search query is cleared.
878
+ */
879
+ private restoreOriginalItemOrder(): void {
880
+ const itemsContainer = this.nodes.items;
881
+
882
+ if (itemsContainer === null || this.originalItemOrder === undefined) {
883
+ return;
884
+ }
885
+
886
+ for (const el of this.originalItemOrder) {
887
+ itemsContainer.appendChild(el);
888
+ }
889
+
890
+ this.originalItemOrder = undefined;
891
+ }
770
892
  }
@@ -226,7 +226,7 @@ export class PopoverInline extends PopoverDesktop {
226
226
  this.nodes.popover.style.height = heightPx;
227
227
  }
228
228
 
229
- requestAnimationFrame(() => {
229
+ queueMicrotask(() => {
230
230
  this.flipper?.deactivate();
231
231
  this.flipper?.activate(this.flippableElements);
232
232
  });
@@ -5,19 +5,19 @@
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-(--width) w-(--width) rounded-xl shadow-[0_4px_20px_-4px_var(--color-popover-shadow),0_0_0_0.5px_rgba(0,0,0,0.06)] left-(--popover-left) top-(--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-xl text-sm 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.5 max-h-(--max-height) border border-popover-border animate-[panelShowing_100ms_ease]',
15
+ popoverContainerOpened: 'opacity-100 pointer-events-auto p-1.5 max-h-(--max-height) border-none animate-panel-showing',
16
16
 
17
17
  // Popover overlay
18
18
  popoverOverlay: 'hidden bg-dark',
19
19
 
20
- items: 'overflow-y-auto overscroll-contain [scrollbar-gutter:stable] pr-1',
20
+ items: 'overflow-y-auto overscroll-contain',
21
21
  };
22
22
 
23
23
  /**
@@ -57,6 +57,8 @@ const KEY_CODES: Record<string, string> = {
57
57
  'INSERT': 'Insert',
58
58
  'DELETE': 'Delete',
59
59
  '.': 'Period',
60
+ '[': 'BracketLeft',
61
+ ']': 'BracketRight',
60
62
  };
61
63
 
62
64
  type ModifierKey = keyof typeof SUPPORTED_COMMANDS;
@@ -360,7 +360,17 @@ class Tooltip {
360
360
  const shownClass = Array.isArray(this.CSS.tooltipShown) ? this.CSS.tooltipShown[0] : this.CSS.tooltipShown;
361
361
  const isShown = this.nodes.wrapper.classList.contains(shownClass);
362
362
 
363
- this.nodes.wrapper.style.setProperty(VISIBILITY_PROPERTY, isShown ? VISIBILITY_VISIBLE : VISIBILITY_HIDDEN);
363
+ /**
364
+ * Use `!important` priority when hiding so the inline style wins over the
365
+ * author stylesheet's `all: initial !important` rule (which sets visibility
366
+ * to `visible`). Inline `!important` > author `!important` in the cascade.
367
+ * When showing, normal priority is sufficient (no competing rule sets visible).
368
+ */
369
+ this.nodes.wrapper.style.setProperty(
370
+ VISIBILITY_PROPERTY,
371
+ isShown ? VISIBILITY_VISIBLE : VISIBILITY_HIDDEN,
372
+ isShown ? '' : 'important'
373
+ );
364
374
  this.nodes.wrapper.setAttribute(ARIA_HIDDEN_ATTRIBUTE, isShown ? ARIA_HIDDEN_FALSE : ARIA_HIDDEN_TRUE);
365
375
  this.nodes.wrapper.setAttribute('data-blok-shown', isShown ? 'true' : 'false');
366
376
  }
@@ -0,0 +1,46 @@
1
+ import { useEffect, useRef, forwardRef } from 'react';
2
+ import { getHolder } from './holder-map';
3
+ import type { BlokContentProps } from './types';
4
+
5
+ /**
6
+ * Component that provides the DOM mount point for a Blok editor.
7
+ * Renders a <div> and adopts the editor's detached holder DOM into it.
8
+ * When editor is null, renders an empty div (for layout stability).
9
+ * Passes through all standard HTML div attributes (className, style, etc.).
10
+ */
11
+ export const BlokContent = forwardRef<HTMLDivElement, BlokContentProps>(
12
+ function BlokContent({ editor, ...divProps }, ref) {
13
+ const containerRef = useRef<HTMLDivElement | null>(null);
14
+
15
+ const setRefs = (node: HTMLDivElement | null): void => {
16
+ containerRef.current = node;
17
+ if (typeof ref === 'function') {
18
+ ref(node);
19
+ } else if (ref !== null && ref !== undefined) {
20
+ const mutableRef = ref;
21
+
22
+ mutableRef.current = node;
23
+ }
24
+ };
25
+
26
+ useEffect(() => {
27
+ if (editor === null || containerRef.current === null) {
28
+ return;
29
+ }
30
+
31
+ const holder = getHolder(editor);
32
+
33
+ if (holder === undefined) {
34
+ return;
35
+ }
36
+
37
+ containerRef.current.appendChild(holder);
38
+
39
+ return (): void => {
40
+ holder.remove();
41
+ };
42
+ }, [editor]);
43
+
44
+ return <div ref={setRefs} {...divProps} />;
45
+ }
46
+ );
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Module-level WeakMap associating editor instances with their detached holder divs.
3
+ * WeakMap ensures no memory leaks — when the editor is garbage collected, the holder reference is released.
4
+ */
5
+ const holders = new WeakMap<WeakKey, HTMLDivElement>();
6
+
7
+ export function setHolder(editor: WeakKey, holder: HTMLDivElement): void {
8
+ holders.set(editor, holder);
9
+ }
10
+
11
+ export function getHolder(editor: WeakKey): HTMLDivElement | undefined {
12
+ return holders.get(editor);
13
+ }
14
+
15
+ export function removeHolder(editor: WeakKey): void {
16
+ holders.delete(editor);
17
+ }
@@ -0,0 +1,3 @@
1
+ export { useBlok } from './useBlok';
2
+ export { BlokContent } from './BlokContent';
3
+ export type { UseBlokConfig, BlokContentProps } from './types';
@@ -0,0 +1,16 @@
1
+ import type { BlokConfig, Blok } from '@/types';
2
+
3
+ /**
4
+ * Configuration for useBlok hook.
5
+ * Same as BlokConfig but without `holder` — the holder is managed by BlokContent.
6
+ */
7
+ export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {}
8
+
9
+ /**
10
+ * Props for the BlokContent component.
11
+ * Extends standard div HTML attributes so className, style, id, etc. pass through.
12
+ */
13
+ export interface BlokContentProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ /** The Blok editor instance returned by useBlok. Pass null if editor is not ready yet. */
15
+ editor: Blok | null;
16
+ }
@@ -0,0 +1,173 @@
1
+ import { useState, useEffect, useRef, useMemo, type DependencyList } from 'react';
2
+ import { Blok as BlokRuntime } from '../blok';
3
+ import { setHolder, removeHolder } from './holder-map';
4
+ import type { Blok } from '@/types';
5
+ import type { UseBlokConfig } from './types';
6
+
7
+ interface EditorInstanceState {
8
+ editor: Blok | null;
9
+ holder: HTMLDivElement | null;
10
+ destroyTimeout: ReturnType<typeof setTimeout> | null;
11
+ isDestroyed: boolean;
12
+ /** Opaque token identifying which deps cycle created this editor */
13
+ depsToken: Record<string, unknown> | null;
14
+ }
15
+
16
+ /**
17
+ * React hook that manages a Blok editor instance lifecycle.
18
+ *
19
+ * Creates a detached holder div, instantiates Blok, and returns the instance
20
+ * once `isReady` resolves. Handles StrictMode double-mount by deferring
21
+ * destroy via `setTimeout(0)` and cancelling on remount.
22
+ *
23
+ * @param config - Blok configuration without `holder` (managed internally)
24
+ * @param deps - Optional dependency array; when values change, the editor is recreated
25
+ * @returns The Blok instance once ready, or null during initialization / SSR
26
+ */
27
+ export function useBlok(config: UseBlokConfig, deps?: DependencyList): Blok | null {
28
+ const [editor, setEditor] = useState<Blok | null>(null);
29
+ const configRef = useRef(config);
30
+ configRef.current = config;
31
+
32
+ const stateRef = useRef<EditorInstanceState>({
33
+ editor: null,
34
+ holder: null,
35
+ destroyTimeout: null,
36
+ isDestroyed: false,
37
+ depsToken: null,
38
+ });
39
+
40
+ // A new token is created only when deps change (useMemo compares deps).
41
+ // StrictMode re-runs see the SAME token. Deps changes produce a NEW token.
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ const depsToken = useMemo(() => ({}), deps ?? []);
44
+
45
+ // Main lifecycle effect
46
+ useEffect(() => {
47
+ if (typeof window === 'undefined') {
48
+ return;
49
+ }
50
+
51
+ const state = stateRef.current;
52
+
53
+ // Cancel pending deferred destroy (StrictMode remount)
54
+ if (state.destroyTimeout !== null) {
55
+ clearTimeout(state.destroyTimeout);
56
+ state.destroyTimeout = null;
57
+ }
58
+
59
+ // Reuse editor on StrictMode remount (same deps cycle)
60
+ if (state.editor !== null && !state.isDestroyed && state.depsToken === depsToken) {
61
+ setEditor(state.editor);
62
+
63
+ return (): void => {
64
+ deferDestroy(state, setEditor);
65
+ };
66
+ }
67
+
68
+ // Destroy leftover editor from a previous deps cycle
69
+ if (state.editor !== null && !state.isDestroyed) {
70
+ removeHolder(state.editor);
71
+ try {
72
+ state.editor.destroy();
73
+ } catch {
74
+ // destroy may throw — still clean up state
75
+ }
76
+ state.editor = null;
77
+ state.holder = null;
78
+ state.isDestroyed = true;
79
+ setEditor(null);
80
+ }
81
+
82
+ // Create detached holder
83
+ const holder = document.createElement('div');
84
+ state.holder = holder;
85
+ state.isDestroyed = false;
86
+ state.depsToken = depsToken;
87
+
88
+ // Wrap callbacks via ref so they never go stale
89
+ const currentConfig = configRef.current;
90
+ const blokConfig = {
91
+ ...currentConfig,
92
+ holder,
93
+ onReady: (): void => {
94
+ configRef.current.onReady?.();
95
+ },
96
+ onChange: (...args: Parameters<NonNullable<UseBlokConfig['onChange']>>): void => {
97
+ configRef.current.onChange?.(...args);
98
+ },
99
+ };
100
+
101
+ const blok = new BlokRuntime(blokConfig) as unknown as Blok;
102
+ state.editor = blok;
103
+ setHolder(blok, holder);
104
+
105
+ void blok.isReady
106
+ .then(() => {
107
+ if (state.editor === blok && !state.isDestroyed) {
108
+ setEditor(blok);
109
+ }
110
+ })
111
+ .catch(() => {
112
+ if (state.editor === blok && !state.isDestroyed) {
113
+ removeHolder(blok);
114
+ try {
115
+ blok.destroy();
116
+ } catch {
117
+ // destroy may also throw — still clean up state
118
+ }
119
+ state.editor = null;
120
+ state.holder = null;
121
+ state.isDestroyed = true;
122
+ setEditor(null);
123
+ }
124
+ });
125
+
126
+ return (): void => {
127
+ deferDestroy(state, setEditor);
128
+ };
129
+ }, [depsToken]);
130
+
131
+ // Reactive: readOnly
132
+ const { readOnly } = config;
133
+ useEffect(() => {
134
+ if (editor === null) {
135
+ return;
136
+ }
137
+ void editor.readOnly.set(readOnly ?? false);
138
+ }, [editor, readOnly]);
139
+
140
+ // Reactive: autofocus
141
+ const { autofocus } = config;
142
+ useEffect(() => {
143
+ if (editor === null || !autofocus) {
144
+ return;
145
+ }
146
+ editor.focus();
147
+ }, [editor, autofocus]);
148
+
149
+ return editor;
150
+ }
151
+
152
+ function deferDestroy(
153
+ state: EditorInstanceState,
154
+ setEditorState: React.Dispatch<React.SetStateAction<Blok | null>>
155
+ ): void {
156
+ /* eslint-disable no-param-reassign -- intentional mutation of shared state ref */
157
+ state.destroyTimeout = setTimeout(() => {
158
+ if (state.editor !== null) {
159
+ removeHolder(state.editor);
160
+ try {
161
+ state.editor.destroy();
162
+ } catch {
163
+ // destroy may throw — still clean up state
164
+ }
165
+ state.editor = null;
166
+ state.holder = null;
167
+ state.isDestroyed = true;
168
+ state.destroyTimeout = null;
169
+ setEditorState(null);
170
+ }
171
+ }, 0);
172
+ /* eslint-enable no-param-reassign */
173
+ }
@@ -148,65 +148,6 @@ export const PlaceholderHiddenWithContent: Story = {
148
148
  },
149
149
  };
150
150
 
151
- /**
152
- * Typing hides placeholder, clearing restores it.
153
- */
154
- export const TypeAndClearPlaceholder: Story = {
155
- args: {
156
- placeholder: DEFAULT_PLACEHOLDER,
157
- data: undefined,
158
- },
159
- parameters: {
160
- chromatic: { delay: 500 },
161
- },
162
- play: async ({ canvasElement, step }) => {
163
- await step('Wait for editor to initialize', async () => {
164
- await waitFor(
165
- () => {
166
- const block = canvasElement.querySelector(BLOCK_TESTID);
167
-
168
- expect(block).toBeInTheDocument();
169
- },
170
- TIMEOUT_INIT
171
- );
172
- });
173
-
174
- await step('Focus and type to hide placeholder', async () => {
175
- const block = canvasElement.querySelector(BLOCK_TESTID);
176
- const contentEditable = block?.querySelector(CONTENTEDITABLE_SELECTOR);
177
-
178
- if (contentEditable) {
179
- await userEvent.click(contentEditable);
180
- await userEvent.keyboard('Hello world');
181
- }
182
-
183
- await waitFor(
184
- () => {
185
- expect(contentEditable?.textContent).toContain('Hello world');
186
- },
187
- TIMEOUT_ACTION
188
- );
189
- });
190
-
191
- await step('Clear content to restore placeholder', async () => {
192
- const block = canvasElement.querySelector(BLOCK_TESTID);
193
- const contentEditable = block?.querySelector(CONTENTEDITABLE_SELECTOR);
194
-
195
- if (contentEditable) {
196
- await userEvent.tripleClick(contentEditable);
197
- await userEvent.keyboard('{Backspace}');
198
- }
199
-
200
- await waitFor(
201
- () => {
202
- expect(contentEditable?.textContent?.trim()).toBe('');
203
- },
204
- TIMEOUT_ACTION
205
- );
206
- });
207
- },
208
- };
209
-
210
151
  /**
211
152
  * Multiple blocks - only first empty block shows placeholder.
212
153
  */