@jackuait/blok 0.7.0-beta.4 → 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 -7485
  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 +133 -15
  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 +37 -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-B0pAWdVk.mjs +0 -20102
  325. package/dist/chunks/constants-DmDwNSTM.mjs +0 -5123
  326. package/dist/chunks/i18next-B47TKgbU.mjs +0 -1303
  327. package/dist/chunks/i18next-loader-v9SlYZ0i.mjs +0 -43
  328. package/dist/chunks/index-DHLWzZaA.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,14 +6,17 @@
6
6
  */
7
7
 
8
8
  import { DATA_ATTR } from '../../components/constants';
9
+ import { PLACEHOLDER_ACTIVE_CLASSES, PLACEHOLDER_EMPTY_EDITOR_CLASSES } from '../../components/utils/placeholder';
9
10
  import { twMerge } from '../../components/utils/tw';
10
11
 
11
12
  import {
12
13
  ARROW_ICON,
13
14
  ARROW_STYLES,
14
15
  BASE_STYLES,
16
+ BODY_PLACEHOLDER_STYLES,
15
17
  CONTENT_STYLES,
16
18
  TOGGLE_ATTR,
19
+ TOGGLE_CHILDREN_STYLES,
17
20
  TOGGLE_WRAPPER_STYLES,
18
21
  TOOL_NAME,
19
22
  } from './constants';
@@ -31,8 +34,14 @@ export interface ToggleDOMBuilderContext {
31
34
  isOpen: boolean;
32
35
  /** Optional keydown event handler */
33
36
  keydownHandler: ((event: KeyboardEvent) => void) | null;
34
- /** Callback when the arrow is clicked */
35
- onArrowClick: () => void;
37
+ /** Callback when the arrow is clicked. Null/undefined disables interaction (read-only mode). */
38
+ onArrowClick: (() => void) | null | undefined;
39
+ /** Callback when the body placeholder is clicked */
40
+ onBodyPlaceholderClick: (() => void) | null;
41
+ /** Translated text for the body placeholder */
42
+ bodyPlaceholderText: string;
43
+ /** Translated aria labels for the toggle arrow */
44
+ ariaLabels: { collapse: string; expand: string };
36
45
  }
37
46
 
38
47
  /**
@@ -45,6 +54,10 @@ export interface ToggleBuildResult {
45
54
  arrowElement: HTMLElement;
46
55
  /** The content element for text input */
47
56
  contentElement: HTMLElement;
57
+ /** The body placeholder element */
58
+ bodyPlaceholderElement: HTMLElement;
59
+ /** The child container element */
60
+ childContainerElement: HTMLElement;
48
61
  }
49
62
 
50
63
  /**
@@ -54,20 +67,40 @@ export interface ToggleBuildResult {
54
67
  * @returns Object containing the created elements
55
68
  */
56
69
  export const buildToggleItem = (context: ToggleDOMBuilderContext): ToggleBuildResult => {
57
- const { data, readOnly, isOpen, keydownHandler, onArrowClick } = context;
70
+ const { data, readOnly, isOpen, keydownHandler, onArrowClick, onBodyPlaceholderClick } = context;
58
71
 
59
72
  const wrapper = document.createElement('div');
60
- wrapper.className = twMerge(BASE_STYLES, TOGGLE_WRAPPER_STYLES);
73
+ wrapper.className = BASE_STYLES;
61
74
  wrapper.setAttribute(DATA_ATTR.tool, TOOL_NAME);
62
75
  wrapper.setAttribute(TOGGLE_ATTR.toggleOpen, String(isOpen));
63
76
 
64
- const arrowElement = buildArrow(isOpen, onArrowClick);
77
+ const headerRow = document.createElement('div');
78
+ headerRow.className = TOGGLE_WRAPPER_STYLES;
79
+
80
+ const arrowElement = buildArrow(isOpen, onArrowClick, {}, context.ariaLabels);
65
81
  const contentElement = buildContent(data, readOnly, keydownHandler);
66
82
 
67
- wrapper.appendChild(arrowElement);
68
- wrapper.appendChild(contentElement);
83
+ headerRow.appendChild(arrowElement);
84
+ headerRow.appendChild(contentElement);
85
+
86
+ const bodyPlaceholderElement = buildBodyPlaceholder(onBodyPlaceholderClick, context.bodyPlaceholderText);
87
+
88
+ const childContainerElement = document.createElement('div');
89
+ childContainerElement.className = TOGGLE_CHILDREN_STYLES;
90
+ childContainerElement.setAttribute(TOGGLE_ATTR.toggleChildren, '');
91
+ // Block DOM mutations inside the children container from triggering the toggle tool's
92
+ // didMutated → syncBlockDataToYjs path. Child block insertions/removals are tracked
93
+ // via the block hierarchy (parentId / contentIds) and must not create spurious Yjs
94
+ // undo entries that split "insert child" into two CMD+Z steps.
95
+ childContainerElement.setAttribute('data-blok-mutation-free', 'true');
96
+ childContainerElement.id = `toggle-children-${Date.now()}-${Math.random().toString(36).slice(2)}`;
97
+ arrowElement.setAttribute('aria-controls', childContainerElement.id);
69
98
 
70
- return { wrapper, arrowElement, contentElement };
99
+ wrapper.appendChild(headerRow);
100
+ wrapper.appendChild(bodyPlaceholderElement);
101
+ wrapper.appendChild(childContainerElement);
102
+
103
+ return { wrapper, arrowElement, contentElement, bodyPlaceholderElement, childContainerElement };
71
104
  };
72
105
 
73
106
  /**
@@ -82,14 +115,15 @@ export interface BuildArrowOptions {
82
115
  * Build the arrow element for toggling open/closed state.
83
116
  *
84
117
  * @param isOpen - Whether the toggle is currently open
85
- * @param onArrowClick - Callback when arrow is clicked
118
+ * @param onArrowClick - Callback when arrow is clicked. Pass null/undefined to disable click interaction (e.g. read-only mode).
86
119
  * @param options - Optional configuration
87
120
  * @returns The arrow element
88
121
  */
89
122
  export const buildArrow = (
90
123
  isOpen: boolean,
91
- onArrowClick: () => void,
92
- options: BuildArrowOptions = {}
124
+ onArrowClick: (() => void) | null | undefined,
125
+ options: BuildArrowOptions = {},
126
+ ariaLabels: { collapse: string; expand: string } = { collapse: 'Collapse', expand: 'Expand' }
93
127
  ): HTMLElement => {
94
128
  const arrow = document.createElement('span');
95
129
  arrow.className = ARROW_STYLES;
@@ -97,7 +131,7 @@ export const buildArrow = (
97
131
  arrow.setAttribute(DATA_ATTR.mutationFree, 'true');
98
132
  arrow.setAttribute('role', 'button');
99
133
  arrow.setAttribute('tabindex', '0');
100
- arrow.setAttribute('aria-label', isOpen ? 'Collapse' : 'Expand');
134
+ arrow.setAttribute('aria-label', isOpen ? ariaLabels.collapse : ariaLabels.expand);
101
135
  arrow.setAttribute('aria-expanded', String(isOpen));
102
136
 
103
137
  if (options.contentEditableFalse === true) {
@@ -106,26 +140,57 @@ export const buildArrow = (
106
140
 
107
141
  arrow.innerHTML = ARROW_ICON;
108
142
 
109
- if (isOpen) {
110
- arrow.style.transform = 'rotate(90deg)';
111
- }
143
+ const svg = arrow.querySelector('svg');
112
144
 
113
- arrow.addEventListener('click', (event: MouseEvent) => {
114
- event.stopPropagation();
115
- onArrowClick();
116
- });
145
+ if (svg) {
146
+ svg.style.transition = 'transform 200ms ease-in-out';
117
147
 
118
- arrow.addEventListener('keydown', (event: KeyboardEvent) => {
119
- if (event.key === 'Enter' || event.key === ' ') {
120
- event.preventDefault();
148
+ if (isOpen) {
149
+ svg.style.transform = 'rotate(90deg)';
150
+ }
151
+ }
152
+
153
+ if (onArrowClick) {
154
+ arrow.addEventListener('click', (event: MouseEvent) => {
121
155
  event.stopPropagation();
122
156
  onArrowClick();
123
- }
124
- });
157
+ });
158
+
159
+ arrow.addEventListener('keydown', (event: KeyboardEvent) => {
160
+ if (event.key === 'Enter' || event.key === ' ') {
161
+ event.preventDefault();
162
+ event.stopPropagation();
163
+ onArrowClick();
164
+ }
165
+ });
166
+ }
125
167
 
126
168
  return arrow;
127
169
  };
128
170
 
171
+ /**
172
+ * Build the body placeholder element shown when the toggle has no children.
173
+ *
174
+ * @param onClick - Optional click handler (creates a child block)
175
+ * @returns The body placeholder element
176
+ */
177
+ const buildBodyPlaceholder = (onClick: (() => void) | null, text: string): HTMLElement => {
178
+ const placeholder = document.createElement('div');
179
+ placeholder.className = BODY_PLACEHOLDER_STYLES;
180
+ placeholder.setAttribute(TOGGLE_ATTR.toggleBodyPlaceholder, '');
181
+ // Class changes on the body placeholder (show/hide) must not trigger didMutated →
182
+ // syncBlockDataToYjs, which would create a spurious Yjs undo entry when a child
183
+ // block is inserted via Enter. The placeholder holds no user-editable content.
184
+ placeholder.setAttribute('data-blok-mutation-free', 'true');
185
+ placeholder.textContent = text;
186
+
187
+ if (onClick) {
188
+ placeholder.addEventListener('click', onClick);
189
+ }
190
+
191
+ return placeholder;
192
+ };
193
+
129
194
  /**
130
195
  * Build the content element for text input.
131
196
  *
@@ -140,7 +205,7 @@ const buildContent = (
140
205
  keydownHandler: ((event: KeyboardEvent) => void) | null
141
206
  ): HTMLElement => {
142
207
  const content = document.createElement('div');
143
- content.className = CONTENT_STYLES;
208
+ content.className = twMerge(CONTENT_STYLES, PLACEHOLDER_ACTIVE_CLASSES, PLACEHOLDER_EMPTY_EDITOR_CLASSES);
144
209
  content.setAttribute(TOGGLE_ATTR.toggleContent, '');
145
210
  content.contentEditable = readOnly ? 'false' : 'true';
146
211
  content.innerHTML = data.text;
@@ -11,6 +11,7 @@ import type {
11
11
  BlockToolConstructorOptions,
12
12
  ToolboxConfig,
13
13
  ConversionConfig,
14
+ SanitizerConfig,
14
15
  ToolSanitizerConfig,
15
16
  PasteConfig,
16
17
  PasteEvent,
@@ -23,9 +24,10 @@ import {
23
24
  setToggleItemData,
24
25
  parseHTML,
25
26
  } from './block-operations';
26
- import { PLACEHOLDER_KEY, TOOL_NAME } from './constants';
27
+ import { clean } from '../../components/utils/sanitizer';
28
+ import { ARIA_LABEL_COLLAPSE_KEY, ARIA_LABEL_EXPAND_KEY, BODY_PLACEHOLDER_KEY, PLACEHOLDER_KEY, TOOL_NAME } from './constants';
27
29
  import { IconToggleList } from '../../components/icons';
28
- import { renderToggleItem, updateArrowState, updateChildrenVisibility } from './toggle-lifecycle';
30
+ import { renderToggleItem, updateArrowState, updateChildrenVisibility, updateBodyPlaceholderVisibility } from './toggle-lifecycle';
29
31
  import { handleToggleEnter, handleToggleBackspace } from './toggle-keyboard';
30
32
  import type { ToggleItemData, ToggleItemConfig } from './types';
31
33
 
@@ -37,7 +39,9 @@ export class ToggleItem implements BlockTool {
37
39
  private _element: HTMLElement | null = null;
38
40
  private _contentElement: HTMLElement | null = null;
39
41
  private _arrowElement: HTMLElement | null = null;
40
- private _isOpen: boolean = false;
42
+ private _bodyPlaceholderElement: HTMLElement | null = null;
43
+ private _childContainerElement: HTMLElement | null = null;
44
+ private _isOpen: boolean;
41
45
 
42
46
  private blockId?: string;
43
47
 
@@ -46,17 +50,28 @@ export class ToggleItem implements BlockTool {
46
50
  this.readOnly = readOnly;
47
51
  this._settings = config || {};
48
52
  this._data = this.normalizeData(data);
53
+ this._isOpen = this._data.isOpen ?? !readOnly;
49
54
 
50
55
  if (block) {
51
56
  this.blockId = block.id;
52
57
  }
58
+
59
+ if (!readOnly) {
60
+ this.api.events.on('block changed', this.handleBlockChanged);
61
+ }
53
62
  }
54
63
 
55
64
  private normalizeData(data: ToggleItemData | Record<string, never>): ToggleItemData {
56
65
  if (typeof data === 'object' && data !== null && 'text' in data) {
57
- return {
66
+ const normalized: ToggleItemData = {
58
67
  text: typeof data.text === 'string' ? data.text : '',
59
68
  };
69
+
70
+ if (typeof (data as ToggleItemData).isOpen === 'boolean') {
71
+ normalized.isOpen = (data as ToggleItemData).isOpen;
72
+ }
73
+
74
+ return normalized;
60
75
  }
61
76
 
62
77
  return { text: '' };
@@ -83,22 +98,31 @@ export class ToggleItem implements BlockTool {
83
98
  isOpen: this._isOpen,
84
99
  placeholder: this.placeholder,
85
100
  keydownHandler: this.readOnly ? null : this.handleKeyDown.bind(this),
86
- onArrowClick: () => this.toggleOpen(),
101
+ onArrowClick: this.readOnly ? null : () => this.toggleOpen(),
102
+ onBodyPlaceholderClick: this.readOnly ? null : () => this.handleBodyPlaceholderClick(),
103
+ bodyPlaceholderText: this.api.i18n.t(BODY_PLACEHOLDER_KEY),
104
+ ariaLabels: {
105
+ collapse: this.api.i18n.t(ARIA_LABEL_COLLAPSE_KEY),
106
+ expand: this.api.i18n.t(ARIA_LABEL_EXPAND_KEY),
107
+ },
87
108
  });
88
109
 
89
110
  this._element = result.wrapper;
90
111
  this._contentElement = result.contentElement;
91
112
  this._arrowElement = result.arrowElement;
113
+ this._bodyPlaceholderElement = result.bodyPlaceholderElement;
114
+ this._childContainerElement = result.childContainerElement;
92
115
 
93
116
  return this._element;
94
117
  }
95
118
 
96
119
  public rendered(): void {
97
120
  this.updateChildrenVisibility();
121
+ this.updateBodyPlaceholderVisibility();
98
122
  }
99
123
 
100
124
  public save(): ToggleItemData {
101
- return saveToggleItem(this._data, this._element, this.getContentElement.bind(this));
125
+ return saveToggleItem(this._data, this._element, this.getContentElement.bind(this), this._isOpen);
102
126
  }
103
127
 
104
128
  public validate(_blockData: ToggleItemData): boolean {
@@ -125,7 +149,8 @@ export class ToggleItem implements BlockTool {
125
149
 
126
150
  const content = detail.data as HTMLElement;
127
151
  const summary = content.querySelector('summary');
128
- const text = summary !== null ? summary.innerHTML : content.innerHTML;
152
+ const rawText = summary !== null ? summary.innerHTML : content.innerHTML;
153
+ const text = clean(rawText, ToggleItem.sanitize.text as SanitizerConfig);
129
154
 
130
155
  this._data = { text };
131
156
 
@@ -145,7 +170,19 @@ export class ToggleItem implements BlockTool {
145
170
 
146
171
  this._data = result.newData;
147
172
 
173
+ if (typeof this._data.isOpen === 'boolean') {
174
+ this._isOpen = this._data.isOpen;
175
+ }
176
+
177
+ if (this._arrowElement && this._element) {
178
+ updateArrowState(this._arrowElement, this._element, this._isOpen, {
179
+ collapse: this.api.i18n.t(ARIA_LABEL_COLLAPSE_KEY),
180
+ expand: this.api.i18n.t(ARIA_LABEL_EXPAND_KEY),
181
+ });
182
+ }
183
+
148
184
  this.updateChildrenVisibility();
185
+ this.updateBodyPlaceholderVisibility();
149
186
 
150
187
  return result.inPlace;
151
188
  }
@@ -178,6 +215,32 @@ export class ToggleItem implements BlockTool {
178
215
  this.setOpenState(false);
179
216
  }
180
217
 
218
+ public removed(): void {
219
+ this.api.events.off('block changed', this.handleBlockChanged);
220
+ }
221
+
222
+ private handleBlockChanged = (data: unknown): void => {
223
+ if (!this.isBlockChangedPayload(data)) {
224
+ return;
225
+ }
226
+
227
+ if (data.event.type === 'block-removed' || data.event.type === 'block-added') {
228
+ this.updateBodyPlaceholderVisibility();
229
+ }
230
+ };
231
+
232
+ private isBlockChangedPayload(data: unknown): data is { event: { type: string } } {
233
+ return (
234
+ typeof data === 'object' &&
235
+ data !== null &&
236
+ 'event' in data &&
237
+ typeof (data as { event: unknown }).event === 'object' &&
238
+ (data as { event: unknown }).event !== null &&
239
+ 'type' in (data as { event: { type: unknown } }).event &&
240
+ typeof (data as { event: { type: unknown } }).event.type === 'string'
241
+ );
242
+ }
243
+
181
244
  private getContentElement(): HTMLElement | null {
182
245
  return this._contentElement;
183
246
  }
@@ -186,10 +249,14 @@ export class ToggleItem implements BlockTool {
186
249
  this._isOpen = open;
187
250
 
188
251
  if (this._arrowElement && this._element) {
189
- updateArrowState(this._arrowElement, this._element, this._isOpen);
252
+ updateArrowState(this._arrowElement, this._element, this._isOpen, {
253
+ collapse: this.api.i18n.t(ARIA_LABEL_COLLAPSE_KEY),
254
+ expand: this.api.i18n.t(ARIA_LABEL_EXPAND_KEY),
255
+ });
190
256
  }
191
257
 
192
258
  this.updateChildrenVisibility();
259
+ this.updateBodyPlaceholderVisibility();
193
260
  }
194
261
 
195
262
  private toggleOpen(): void {
@@ -201,7 +268,40 @@ export class ToggleItem implements BlockTool {
201
268
  return;
202
269
  }
203
270
 
204
- updateChildrenVisibility(this.api, this.blockId, this._isOpen);
271
+ updateChildrenVisibility(this.api, this.blockId, this._isOpen, this._childContainerElement, this._arrowElement);
272
+ }
273
+
274
+ private updateBodyPlaceholderVisibility(): void {
275
+ if (this.blockId === undefined) {
276
+ return;
277
+ }
278
+
279
+ updateBodyPlaceholderVisibility(
280
+ this._bodyPlaceholderElement,
281
+ this.api,
282
+ this.blockId,
283
+ this._isOpen,
284
+ this.readOnly
285
+ );
286
+ }
287
+
288
+ private handleBodyPlaceholderClick(): void {
289
+ if (this.blockId === undefined) {
290
+ return;
291
+ }
292
+
293
+ const blockIndex = this.api.blocks.getBlockIndex(this.blockId);
294
+
295
+ if (blockIndex === undefined) {
296
+ return;
297
+ }
298
+
299
+ const newBlock = this.api.blocks.insertInsideParent(this.blockId, blockIndex + 1);
300
+
301
+ this.api.caret.setToBlock(newBlock.id, 'start');
302
+
303
+ // Hide the body placeholder now that a child exists
304
+ this._bodyPlaceholderElement?.classList.add('hidden');
205
305
  }
206
306
 
207
307
  private handleKeyDown(event: KeyboardEvent): void {
@@ -236,6 +336,9 @@ export class ToggleItem implements BlockTool {
236
336
 
237
337
  private async handleEnter(): Promise<void> {
238
338
  await handleToggleEnter(this.createKeyboardContext());
339
+
340
+ // After Enter may create a child, update body placeholder visibility
341
+ this.updateBodyPlaceholderVisibility();
239
342
  }
240
343
 
241
344
  private async handleBackspace(event: KeyboardEvent): Promise<void> {
@@ -57,11 +57,13 @@ export const handleToggleEnter = async (context: ToggleKeyboardContext): Promise
57
57
  /**
58
58
  * When toggle is open and caret is at the end (no content after caret),
59
59
  * create a child paragraph inside the toggle rather than a sibling toggle.
60
+ * insertInsideParent() groups both block creation and parent assignment into
61
+ * a single Yjs undo entry, so one CMD+Z removes the new block completely.
60
62
  */
61
63
  if (isOpen && afterContent === '') {
62
- const newBlock = api.blocks.insert('paragraph', { text: '' }, {}, currentBlockIndex + 1, true);
64
+ const newBlock = api.blocks.insertInsideParent(blockId, currentBlockIndex + 1);
63
65
 
64
- api.blocks.setBlockParent(newBlock.id, blockId);
66
+ api.caret.setToBlock(newBlock.id, 'start');
65
67
 
66
68
  return;
67
69
  }
@@ -127,7 +129,7 @@ export const handleToggleBackspace = async (
127
129
  * @param range - The current selection range
128
130
  * @returns Object with before/after HTML content
129
131
  */
130
- const splitContentAtRange = (
132
+ export const splitContentAtRange = (
131
133
  contentEl: HTMLElement,
132
134
  range: Range
133
135
  ): { beforeContent: string; afterContent: string } => {
@@ -26,19 +26,21 @@ export interface ToggleRenderResult {
26
26
  wrapper: HTMLElement;
27
27
  contentElement: HTMLElement;
28
28
  arrowElement: HTMLElement;
29
+ bodyPlaceholderElement: HTMLElement;
30
+ childContainerElement: HTMLElement;
29
31
  }
30
32
 
31
33
  /**
32
34
  * Render a toggle item with placeholder support.
33
35
  *
34
36
  * @param context - The render context
35
- * @returns Object containing the wrapper, content, and arrow elements
37
+ * @returns Object containing the wrapper, content, arrow, and body placeholder elements
36
38
  */
37
39
  export const renderToggleItem = (context: ToggleRenderContext): ToggleRenderResult => {
38
40
  const result = buildToggleItem(context);
39
41
 
40
42
  if (result.contentElement) {
41
- setupPlaceholder(result.contentElement, context.placeholder);
43
+ setupPlaceholder(result.contentElement, context.placeholder, 'data-blok-placeholder-active');
42
44
  }
43
45
 
44
46
  return result;
@@ -51,11 +53,19 @@ export const renderToggleItem = (context: ToggleRenderContext): ToggleRenderResu
51
53
  * @param wrapper - The wrapper element to update the open attribute on
52
54
  * @param isOpen - Whether the toggle is open
53
55
  */
54
- export const updateArrowState = (arrowEl: HTMLElement, wrapper: HTMLElement, isOpen: boolean): void => {
55
- const { style } = arrowEl;
56
+ export const updateArrowState = (
57
+ arrowEl: HTMLElement,
58
+ wrapper: HTMLElement,
59
+ isOpen: boolean,
60
+ ariaLabels: { collapse: string; expand: string } = { collapse: 'Collapse', expand: 'Expand' }
61
+ ): void => {
62
+ const svg = arrowEl.querySelector('svg');
56
63
 
57
- style.transform = isOpen ? 'rotate(90deg)' : '';
58
- arrowEl.setAttribute('aria-label', isOpen ? 'Collapse' : 'Expand');
64
+ if (svg) {
65
+ svg.style.transform = isOpen ? 'rotate(90deg)' : '';
66
+ }
67
+
68
+ arrowEl.setAttribute('aria-label', isOpen ? ariaLabels.collapse : ariaLabels.expand);
59
69
  arrowEl.setAttribute('aria-expanded', String(isOpen));
60
70
  wrapper.setAttribute(TOGGLE_ATTR.toggleOpen, String(isOpen));
61
71
  };
@@ -67,15 +77,77 @@ export const updateArrowState = (arrowEl: HTMLElement, wrapper: HTMLElement, isO
67
77
  * @param api - Blok API instance
68
78
  * @param blockId - The toggle block's id
69
79
  * @param isOpen - Whether the toggle is currently open (expanded)
80
+ * @param childContainer - Optional child container element for aria-hidden management
81
+ * @param arrowElement - Optional arrow element to receive focus when collapsing with focus inside children
70
82
  */
71
- export const updateChildrenVisibility = (api: API, blockId: string, isOpen: boolean): void => {
83
+ export const updateChildrenVisibility = (
84
+ api: API,
85
+ blockId: string,
86
+ isOpen: boolean,
87
+ childContainer?: HTMLElement | null,
88
+ arrowElement?: HTMLElement | null
89
+ ): void => {
72
90
  const children = api.blocks.getChildren(blockId);
73
91
 
92
+ // Before hiding, check if focus is inside the child container and move it to arrow
93
+ if (!isOpen && childContainer && arrowElement && childContainer.contains(document.activeElement)) {
94
+ arrowElement.focus();
95
+ }
96
+
74
97
  for (const child of children) {
98
+ if (childContainer && child.holder.parentElement !== childContainer) {
99
+ childContainer.appendChild(child.holder);
100
+ }
101
+
75
102
  if (isOpen) {
76
103
  child.holder.classList.remove('hidden');
77
104
  } else {
78
105
  child.holder.classList.add('hidden');
79
106
  }
80
107
  }
108
+
109
+ if (childContainer) {
110
+ if (isOpen) {
111
+ childContainer.removeAttribute('aria-hidden');
112
+ } else {
113
+ childContainer.setAttribute('aria-hidden', 'true');
114
+ }
115
+ }
116
+ };
117
+
118
+ /**
119
+ * Update visibility of the body placeholder based on toggle state and children.
120
+ * The placeholder is shown only when the toggle is open, has no children, and is not read-only.
121
+ *
122
+ * @param bodyPlaceholder - The body placeholder element
123
+ * @param api - Blok API instance
124
+ * @param blockId - The toggle block's id
125
+ * @param isOpen - Whether the toggle is currently open
126
+ * @param readOnly - Whether the editor is in read-only mode
127
+ */
128
+ export const updateBodyPlaceholderVisibility = (
129
+ bodyPlaceholder: HTMLElement | null,
130
+ api: API,
131
+ blockId: string,
132
+ isOpen: boolean,
133
+ readOnly: boolean
134
+ ): void => {
135
+ if (bodyPlaceholder === null) {
136
+ return;
137
+ }
138
+
139
+ if (readOnly) {
140
+ bodyPlaceholder.classList.add('hidden');
141
+
142
+ return;
143
+ }
144
+
145
+ const children = api.blocks.getChildren(blockId);
146
+ const shouldShow = isOpen && children.length === 0;
147
+
148
+ if (shouldShow) {
149
+ bodyPlaceholder.classList.remove('hidden');
150
+ } else {
151
+ bodyPlaceholder.classList.add('hidden');
152
+ }
81
153
  };