@jackuait/blok 0.10.3 → 0.10.4

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 (284) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-3wc3aInM.mjs → blok-NcdNQ0I6.mjs} +2364 -2178
  3. package/dist/chunks/{constants-Bp622jic.mjs → constants-DtfShkXT.mjs} +318 -227
  4. package/dist/chunks/{i18next-loader-CDnSPae_.mjs → i18next-loader-D32EUWLr.mjs} +1 -1
  5. package/dist/chunks/{lightweight-i18n-DZmo8dAI.mjs → lightweight-i18n-DpkvRXEd.mjs} +19 -0
  6. package/dist/{messages-Ddq3Ce3E2.mjs → chunks/messages-AD17iDBx.mjs} +18 -0
  7. package/dist/{messages-neGD3WGq.mjs → chunks/messages-B03yUEra2.mjs} +18 -0
  8. package/dist/chunks/{messages-CIfUm1Oa.mjs → messages-B2dU00Z3.mjs} +18 -0
  9. package/dist/chunks/{messages-BKN3YVIj.mjs → messages-B8hICx3L.mjs} +18 -0
  10. package/dist/{messages-Dnd5YSWv.mjs → chunks/messages-BBe45sPH.mjs} +18 -0
  11. package/dist/{messages-C7lJg8fy2.mjs → chunks/messages-BCifzMVO2.mjs} +18 -0
  12. package/dist/chunks/{messages-D7dx_6k8.mjs → messages-BGmvvtg_.mjs} +18 -0
  13. package/dist/{messages-Q5sQeVap2.mjs → chunks/messages-BJNFCDv42.mjs} +18 -0
  14. package/dist/chunks/{messages-BlxwW7M6.mjs → messages-BNy4e7Xl.mjs} +18 -0
  15. package/dist/chunks/{messages-C15z2r5U.mjs → messages-BcHZf9o-.mjs} +18 -0
  16. package/dist/{messages-A96tMxeU.mjs → chunks/messages-BjyYZeBm2.mjs} +18 -0
  17. package/dist/{messages-BbJ7ZXY8.mjs → chunks/messages-Bop7vrhU.mjs} +18 -0
  18. package/dist/{messages-BiTMwiKH.mjs → chunks/messages-BouFtpfO.mjs} +18 -0
  19. package/dist/chunks/{messages-ElIGUi0O2.mjs → messages-Br6bE1FD2.mjs} +18 -0
  20. package/dist/chunks/{messages-BHMiK51R.mjs → messages-C-EBhOHE.mjs} +18 -0
  21. package/dist/chunks/{messages-kGmxkeFH.mjs → messages-C3X7dv3f.mjs} +18 -0
  22. package/dist/chunks/{messages-4Ck88DYZ2.mjs → messages-C7Pjof0d2.mjs} +18 -0
  23. package/dist/{messages-D0lLw9KM.mjs → chunks/messages-C7sBaZOO2.mjs} +18 -0
  24. package/dist/chunks/{messages-QMOmwcZb.mjs → messages-C85zv_7x.mjs} +18 -0
  25. package/dist/chunks/{messages-DSrdy9Nw2.mjs → messages-CCEgR9GN2.mjs} +18 -0
  26. package/dist/chunks/{messages-DUr9WAkD.mjs → messages-CDSyoUft.mjs} +18 -0
  27. package/dist/{messages-bkGniiaz.mjs → chunks/messages-CGFlOwst.mjs} +18 -0
  28. package/dist/chunks/{messages-DBMaLL8b2.mjs → messages-CGLTjtRv2.mjs} +18 -0
  29. package/dist/{messages-2ZWBTerL.mjs → chunks/messages-CGPxUESo.mjs} +18 -0
  30. package/dist/chunks/{messages-BfAcUavP.mjs → messages-CNaaqQVz.mjs} +18 -0
  31. package/dist/{messages-DBhvm8NK.mjs → chunks/messages-CPFB2_m-2.mjs} +18 -0
  32. package/dist/chunks/{messages-zt6zdYWh.mjs → messages-CTdSIOAc.mjs} +18 -0
  33. package/dist/chunks/{messages-1_6UkKLS.mjs → messages-CXVWb9js.mjs} +18 -0
  34. package/dist/{messages-CdEASHDp2.mjs → chunks/messages-Cfbmwdep2.mjs} +18 -0
  35. package/dist/chunks/{messages-DxHh0O8j2.mjs → messages-ChayV9WY2.mjs} +18 -0
  36. package/dist/chunks/{messages-BgM91Lxm2.mjs → messages-Ci7UXRVI2.mjs} +18 -0
  37. package/dist/chunks/{messages-Clku7Cf-2.mjs → messages-CpXvyGWv2.mjs} +18 -0
  38. package/dist/chunks/{messages-DjvaiALg2.mjs → messages-Cql2ozf_2.mjs} +18 -0
  39. package/dist/{messages-DODrhcop.mjs → chunks/messages-Cxy_E2IS.mjs} +18 -0
  40. package/dist/chunks/{messages-CZSlfnkO2.mjs → messages-D9syZVGi2.mjs} +18 -0
  41. package/dist/chunks/{messages-BRAoJpOu.mjs → messages-D9uWAWjW.mjs} +18 -0
  42. package/dist/chunks/{messages-BK8Cp2d0.mjs → messages-DRJxSTqs.mjs} +18 -0
  43. package/dist/{messages-C_Qn9SbQ.mjs → chunks/messages-DSbI0vJf.mjs} +18 -0
  44. package/dist/chunks/{messages-CD_MnBln.mjs → messages-DVvrZRyZ.mjs} +18 -0
  45. package/dist/{messages-BE_z-zrb.mjs → chunks/messages-DY8zPIZW.mjs} +18 -0
  46. package/dist/chunks/{messages-Bz0-KNEB.mjs → messages-D_kZN9rB.mjs} +18 -0
  47. package/dist/{messages-C1vc5584.mjs → chunks/messages-DjSuq0-y2.mjs} +18 -0
  48. package/dist/chunks/{messages-DPzHD51Y.mjs → messages-DkP3Jf4F.mjs} +18 -0
  49. package/dist/{messages-_PLyRfVw.mjs → chunks/messages-DoPdy75l.mjs} +18 -0
  50. package/dist/chunks/{messages-JSQjKQ8I.mjs → messages-DpydMd36.mjs} +18 -0
  51. package/dist/{messages-BckDk9aq2.mjs → chunks/messages-DtZ9U9g72.mjs} +18 -0
  52. package/dist/{messages-JNrYldAa2.mjs → chunks/messages-H6vLy8wJ.mjs} +18 -0
  53. package/dist/chunks/{messages-DTN1XGll.mjs → messages-HzH9_QH8.mjs} +18 -0
  54. package/dist/chunks/{messages-C0IFfhnp.mjs → messages-O6FOfUgF.mjs} +18 -0
  55. package/dist/{messages-Be_2RHZD.mjs → chunks/messages-OSP4Hj5o.mjs} +18 -0
  56. package/dist/chunks/{messages-DMoERagV2.mjs → messages-RiqdVwuN2.mjs} +18 -0
  57. package/dist/chunks/{messages-BJ-vT1SU2.mjs → messages-SP659Sal2.mjs} +18 -0
  58. package/dist/{messages-Che99vKP.mjs → chunks/messages-THR8q8bJ.mjs} +18 -0
  59. package/dist/chunks/{messages-CvANwuht2.mjs → messages-VlEyFUxF2.mjs} +18 -0
  60. package/dist/{messages-apA6BStA.mjs → chunks/messages-VtfKWZ2S.mjs} +18 -0
  61. package/dist/{messages-DpJGbx3q.mjs → chunks/messages-YbckahVx2.mjs} +18 -0
  62. package/dist/{messages-DYuD5-rO.mjs → chunks/messages-ZhHLC6dk.mjs} +18 -0
  63. package/dist/{messages-C0GSBBCo2.mjs → chunks/messages-bFEdH3lv.mjs} +18 -0
  64. package/dist/chunks/{messages-euM2m3wQ.mjs → messages-dpXwA3Sz.mjs} +18 -0
  65. package/dist/chunks/{messages-CQBo3lmL2.mjs → messages-fbL5y58u2.mjs} +18 -0
  66. package/dist/chunks/{messages-CxiURE2X.mjs → messages-oPV2oMxM.mjs} +18 -0
  67. package/dist/{messages-DM4Gjc9h.mjs → chunks/messages-oXBbHW9A.mjs} +18 -0
  68. package/dist/chunks/{messages-QilfinOn2.mjs → messages-vDgsEqQW2.mjs} +18 -0
  69. package/dist/{messages-ClGvlFcH2.mjs → chunks/messages-wYQksm10.mjs} +18 -0
  70. package/dist/{messages-CnuH-BZK2.mjs → chunks/messages-yGedmr61.mjs} +18 -0
  71. package/dist/chunks/{messages-sDdNf8O9.mjs → messages-zQOpKjl3.mjs} +18 -0
  72. package/dist/chunks/{messages-eFd4YYzt.mjs → messages-zWqsggJh.mjs} +18 -0
  73. package/dist/chunks/{tools-BC1jRfoS.mjs → tools-DMSi-3RW.mjs} +3445 -1302
  74. package/dist/full.mjs +10 -10
  75. package/dist/locales.mjs +86 -67
  76. package/dist/{messages-BK_LsgY4.mjs → messages-0lOPMv8u.mjs} +18 -0
  77. package/dist/{messages-LYJbLq_F.mjs → messages-5wuR90qS.mjs} +18 -0
  78. package/dist/{messages-98nQiC7t2.mjs → messages-6eX0fWGR2.mjs} +18 -0
  79. package/dist/{chunks/messages-DUeiPraX.mjs → messages-9L4qqCKh2.mjs} +18 -0
  80. package/dist/{chunks/messages-Q7-4ZJLB2.mjs → messages-B4zPxKl62.mjs} +18 -0
  81. package/dist/{messages-D0005ti32.mjs → messages-BCMFYqKc2.mjs} +18 -0
  82. package/dist/{chunks/messages-CC_noR8y.mjs → messages-BKXjO3NH.mjs} +18 -0
  83. package/dist/{messages-CRNogopy2.mjs → messages-BLW2GX7J2.mjs} +18 -0
  84. package/dist/{messages-D81w6AmW.mjs → messages-BLfK27kX.mjs} +18 -0
  85. package/dist/{chunks/messages-CPBN4zWc.mjs → messages-BM2kx9Td.mjs} +18 -0
  86. package/dist/{messages-E8NjqzWq2.mjs → messages-BORkMoil2.mjs} +18 -0
  87. package/dist/{messages-Dqu4aX9s.mjs → messages-BPw_x-6H.mjs} +18 -0
  88. package/dist/{messages-DSmxJWju2.mjs → messages-BRY51SEw2.mjs} +18 -0
  89. package/dist/{chunks/messages-BONyZroH.mjs → messages-BSNsrZVN.mjs} +18 -0
  90. package/dist/{chunks/messages-BAlZjPcl.mjs → messages-B_UKuqrH.mjs} +18 -0
  91. package/dist/{chunks/messages-DB_-5Xln.mjs → messages-BrYeJsSE2.mjs} +18 -0
  92. package/dist/{messages-Brd5R-da2.mjs → messages-BwttyHDI2.mjs} +18 -0
  93. package/dist/{messages-qfvXgPpu2.mjs → messages-C-8qb9sf2.mjs} +18 -0
  94. package/dist/{chunks/messages-BbEW9bQz.mjs → messages-C34dTwF72.mjs} +18 -0
  95. package/dist/{messages-BmH2cQHQ.mjs → messages-C67YUZ9-.mjs} +18 -0
  96. package/dist/{messages-DpwMKDV0.mjs → messages-C6yKu_PJ.mjs} +18 -0
  97. package/dist/{messages-Do7Xjy0n.mjs → messages-CA6J_QoC.mjs} +18 -0
  98. package/dist/{messages-DVL0KZE5.mjs → messages-CFUBJfnf.mjs} +18 -0
  99. package/dist/{chunks/messages-DVr1sqfI2.mjs → messages-CLUBh7O_.mjs} +18 -0
  100. package/dist/{chunks/messages-wl8YrvGG.mjs → messages-CLZoy5fQ.mjs} +18 -0
  101. package/dist/{messages-CisR4PNV.mjs → messages-CNGwdIEz.mjs} +18 -0
  102. package/dist/{messages-DopaMHC42.mjs → messages-CR4gHjd82.mjs} +18 -0
  103. package/dist/{messages-DK6dA0O2.mjs → messages-CVMngZNA.mjs} +18 -0
  104. package/dist/{messages-Xc0KUbYl.mjs → messages-Cd5CW5Tt.mjs} +18 -0
  105. package/dist/{chunks/messages-ChK7v1PV.mjs → messages-CrjQ2Op0.mjs} +18 -0
  106. package/dist/{chunks/messages-CRF7nNrO.mjs → messages-Cv1PSaNk.mjs} +18 -0
  107. package/dist/{messages-DOGbHYv-2.mjs → messages-CxZarWTm2.mjs} +18 -0
  108. package/dist/{messages-D3rwCtKn.mjs → messages-D0eT_eWA.mjs} +18 -0
  109. package/dist/{messages-C6ONf71u2.mjs → messages-D6RYu9JW2.mjs} +18 -0
  110. package/dist/{messages-DQORja0D.mjs → messages-D8U5D391.mjs} +18 -0
  111. package/dist/{chunks/messages-EDMC5ukV.mjs → messages-D8dO6OMN.mjs} +18 -0
  112. package/dist/{messages-DfFZ6Yj5.mjs → messages-DA4T9WBe.mjs} +18 -0
  113. package/dist/{chunks/messages-D22e9h7V2.mjs → messages-DB4UKN8D.mjs} +18 -0
  114. package/dist/{chunks/messages-DEBy3nuJ2.mjs → messages-DCdP2ujL.mjs} +18 -0
  115. package/dist/{messages-D05jqBIa2.mjs → messages-DPFuzIdF2.mjs} +18 -0
  116. package/dist/{chunks/messages-DrfRYiM32.mjs → messages-DQ1icG7L.mjs} +18 -0
  117. package/dist/{chunks/messages-a07QVz8U.mjs → messages-DT7dwzEe.mjs} +18 -0
  118. package/dist/{chunks/messages-CszmHAvQ.mjs → messages-DUYxMxrQ2.mjs} +18 -0
  119. package/dist/{chunks/messages-DtoId_bw2.mjs → messages-D_V0kHD7.mjs} +18 -0
  120. package/dist/{messages-BesJaI6A.mjs → messages-DfqM_XvD.mjs} +18 -0
  121. package/dist/{messages-CT-Kdas6.mjs → messages-Di3-WVzq.mjs} +18 -0
  122. package/dist/{messages-BcVB3osF.mjs → messages-Dl0bfeA-.mjs} +18 -0
  123. package/dist/{chunks/messages-C1S9ztpF.mjs → messages-Do3mHd9U.mjs} +18 -0
  124. package/dist/{chunks/messages-BeGZqQwz.mjs → messages-DqDlcEPn.mjs} +18 -0
  125. package/dist/{chunks/messages-CTCe595D2.mjs → messages-DwiykEgr2.mjs} +18 -0
  126. package/dist/{chunks/messages-8Ld7P_9j2.mjs → messages-Dx5n6MLQ2.mjs} +18 -0
  127. package/dist/{messages-LMaR2_bE.mjs → messages-DxEiqa-B.mjs} +18 -0
  128. package/dist/{chunks/messages-CxxyR4vY.mjs → messages-Dxr1BBvo.mjs} +18 -0
  129. package/dist/{messages-D6VIFnSW.mjs → messages-DzknMM7W.mjs} +18 -0
  130. package/dist/{chunks/messages-oMc7qugU2.mjs → messages-ELvF3qMl2.mjs} +18 -0
  131. package/dist/{messages-53w0fPZS2.mjs → messages-JVJdC0Er2.mjs} +18 -0
  132. package/dist/{chunks/messages-BMD37y3q2.mjs → messages-KVerxvZC.mjs} +18 -0
  133. package/dist/{chunks/messages-Du2BffA7.mjs → messages-OOiDDmVw.mjs} +18 -0
  134. package/dist/{messages-uwK7ktqk.mjs → messages-PyOr_YgV.mjs} +18 -0
  135. package/dist/{messages-qbKjjvgd2.mjs → messages-VrQw3tQ62.mjs} +18 -0
  136. package/dist/{messages-CTTmWn4Y2.mjs → messages-WsUHzXMu2.mjs} +18 -0
  137. package/dist/{messages-CZbcxlZt2.mjs → messages-ZHgPRUj02.mjs} +18 -0
  138. package/dist/{messages-DKHbt-7l2.mjs → messages-aoO_TtoE2.mjs} +18 -0
  139. package/dist/{chunks/messages-CW35K1pq.mjs → messages-bh8BiOee2.mjs} +18 -0
  140. package/dist/{messages-BrOWqNCu2.mjs → messages-gZEhkRrR2.mjs} +18 -0
  141. package/dist/{chunks/messages-BRoa9tGl.mjs → messages-hya8YLMj.mjs} +18 -0
  142. package/dist/{messages-CdduYw-q.mjs → messages-tb1FD_ge.mjs} +18 -0
  143. package/dist/react.mjs +2 -2
  144. package/dist/tools.mjs +3 -3
  145. package/dist/vendor.LICENSE.txt +135 -0
  146. package/package.json +2 -1
  147. package/src/blok.ts +48 -0
  148. package/src/components/block/index.ts +21 -2
  149. package/src/components/block-tunes/block-tune-copy-link.ts +82 -0
  150. package/src/components/i18n/locales/am/messages.json +18 -0
  151. package/src/components/i18n/locales/ar/messages.json +18 -0
  152. package/src/components/i18n/locales/az/messages.json +18 -0
  153. package/src/components/i18n/locales/bg/messages.json +18 -0
  154. package/src/components/i18n/locales/bn/messages.json +18 -0
  155. package/src/components/i18n/locales/bs/messages.json +18 -0
  156. package/src/components/i18n/locales/cs/messages.json +18 -0
  157. package/src/components/i18n/locales/da/messages.json +18 -0
  158. package/src/components/i18n/locales/de/messages.json +18 -0
  159. package/src/components/i18n/locales/dv/messages.json +18 -0
  160. package/src/components/i18n/locales/el/messages.json +18 -0
  161. package/src/components/i18n/locales/en/messages.json +19 -0
  162. package/src/components/i18n/locales/es/messages.json +18 -0
  163. package/src/components/i18n/locales/et/messages.json +18 -0
  164. package/src/components/i18n/locales/fa/messages.json +18 -0
  165. package/src/components/i18n/locales/fi/messages.json +18 -0
  166. package/src/components/i18n/locales/fil/messages.json +18 -0
  167. package/src/components/i18n/locales/fr/messages.json +18 -0
  168. package/src/components/i18n/locales/gu/messages.json +18 -0
  169. package/src/components/i18n/locales/he/messages.json +18 -0
  170. package/src/components/i18n/locales/hi/messages.json +18 -0
  171. package/src/components/i18n/locales/hr/messages.json +18 -0
  172. package/src/components/i18n/locales/hu/messages.json +18 -0
  173. package/src/components/i18n/locales/hy/messages.json +18 -0
  174. package/src/components/i18n/locales/id/messages.json +18 -0
  175. package/src/components/i18n/locales/it/messages.json +18 -0
  176. package/src/components/i18n/locales/ja/messages.json +18 -0
  177. package/src/components/i18n/locales/ka/messages.json +18 -0
  178. package/src/components/i18n/locales/km/messages.json +18 -0
  179. package/src/components/i18n/locales/kn/messages.json +18 -0
  180. package/src/components/i18n/locales/ko/messages.json +18 -0
  181. package/src/components/i18n/locales/ku/messages.json +18 -0
  182. package/src/components/i18n/locales/lo/messages.json +18 -0
  183. package/src/components/i18n/locales/lt/messages.json +18 -0
  184. package/src/components/i18n/locales/lv/messages.json +18 -0
  185. package/src/components/i18n/locales/mk/messages.json +18 -0
  186. package/src/components/i18n/locales/ml/messages.json +18 -0
  187. package/src/components/i18n/locales/mn/messages.json +18 -0
  188. package/src/components/i18n/locales/mr/messages.json +18 -0
  189. package/src/components/i18n/locales/ms/messages.json +18 -0
  190. package/src/components/i18n/locales/my/messages.json +18 -0
  191. package/src/components/i18n/locales/ne/messages.json +18 -0
  192. package/src/components/i18n/locales/nl/messages.json +18 -0
  193. package/src/components/i18n/locales/no/messages.json +18 -0
  194. package/src/components/i18n/locales/pa/messages.json +18 -0
  195. package/src/components/i18n/locales/pl/messages.json +18 -0
  196. package/src/components/i18n/locales/ps/messages.json +18 -0
  197. package/src/components/i18n/locales/pt/messages.json +18 -0
  198. package/src/components/i18n/locales/ro/messages.json +18 -0
  199. package/src/components/i18n/locales/ru/messages.json +18 -0
  200. package/src/components/i18n/locales/sd/messages.json +18 -0
  201. package/src/components/i18n/locales/si/messages.json +18 -0
  202. package/src/components/i18n/locales/sk/messages.json +18 -0
  203. package/src/components/i18n/locales/sl/messages.json +18 -0
  204. package/src/components/i18n/locales/sq/messages.json +18 -0
  205. package/src/components/i18n/locales/sr/messages.json +18 -0
  206. package/src/components/i18n/locales/sv/messages.json +18 -0
  207. package/src/components/i18n/locales/sw/messages.json +18 -0
  208. package/src/components/i18n/locales/ta/messages.json +18 -0
  209. package/src/components/i18n/locales/te/messages.json +18 -0
  210. package/src/components/i18n/locales/th/messages.json +18 -0
  211. package/src/components/i18n/locales/tr/messages.json +18 -0
  212. package/src/components/i18n/locales/ug/messages.json +18 -0
  213. package/src/components/i18n/locales/uk/messages.json +18 -0
  214. package/src/components/i18n/locales/ur/messages.json +18 -0
  215. package/src/components/i18n/locales/vi/messages.json +18 -0
  216. package/src/components/i18n/locales/yi/messages.json +18 -0
  217. package/src/components/i18n/locales/zh/messages.json +18 -0
  218. package/src/components/icons/index.ts +65 -0
  219. package/src/components/inline-tools/inline-tool-bold.ts +10 -0
  220. package/src/components/inline-tools/inline-tool-code.ts +54 -1
  221. package/src/components/inline-tools/inline-tool-italic.ts +54 -1
  222. package/src/components/inline-tools/inline-tool-strikethrough.ts +54 -1
  223. package/src/components/inline-tools/inline-tool-underline.ts +54 -1
  224. package/src/components/inline-tools/services/bold-normalization-pass.ts +29 -3
  225. package/src/components/inline-tools/utils/formatting-range-utils.ts +83 -0
  226. package/src/components/modules/api/tools.ts +19 -0
  227. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +5 -5
  228. package/src/components/modules/blockManager/blockManager.ts +22 -0
  229. package/src/components/modules/blockManager/event-binder.ts +12 -1
  230. package/src/components/modules/blockManager/factory.ts +4 -0
  231. package/src/components/modules/blockManager/types.ts +4 -0
  232. package/src/components/modules/blockManager/yjs-sync.ts +16 -2
  233. package/src/components/modules/paste/google-docs-preprocessor.ts +49 -3
  234. package/src/components/modules/paste/handlers/table-cells-handler.ts +12 -2
  235. package/src/components/modules/paste/index.ts +8 -4
  236. package/src/components/modules/paste/types.ts +2 -0
  237. package/src/components/modules/renderer.ts +22 -2
  238. package/src/components/modules/saver.ts +19 -1
  239. package/src/components/modules/themeManager.ts +3 -1
  240. package/src/components/modules/toolbar/blockSettings.ts +51 -1
  241. package/src/components/modules/toolbar/inline/index.ts +0 -3
  242. package/src/components/modules/tools.ts +5 -0
  243. package/src/components/modules/uiControllers/controllers/keyboard.ts +29 -0
  244. package/src/components/modules/uiControllers/controllers/selection.ts +14 -2
  245. package/src/components/modules/yjs/document-store.ts +22 -0
  246. package/src/components/modules/yjs/index.ts +10 -0
  247. package/src/components/modules/yjs/serializer.ts +20 -0
  248. package/src/components/ui/toolbox.ts +0 -1
  249. package/src/components/utils/id-generator.ts +11 -0
  250. package/src/components/utils/key-icon.ts +187 -0
  251. package/src/components/utils/popover/components/hint/hint.ts +3 -1
  252. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +18 -5
  253. package/src/components/utils/popover/popover-abstract.ts +45 -0
  254. package/src/components/utils/popover/popover-desktop.ts +1 -0
  255. package/src/components/utils/popover/popover-position.ts +4 -2
  256. package/src/components/utils/popover/popover.const.ts +2 -0
  257. package/src/components/utils.ts +1 -0
  258. package/src/styles/main.css +1269 -0
  259. package/src/tools/code/index.ts +4 -0
  260. package/src/tools/database/database-backend-sync.ts +132 -0
  261. package/src/tools/database/database-board-view.ts +410 -0
  262. package/src/tools/database/database-card-drag.ts +306 -0
  263. package/src/tools/database/database-card-drawer.ts +546 -0
  264. package/src/tools/database/database-column-controls.ts +141 -0
  265. package/src/tools/database/database-column-drag.ts +262 -0
  266. package/src/tools/database/database-keyboard.ts +35 -0
  267. package/src/tools/database/database-list-row-drag.ts +245 -0
  268. package/src/tools/database/database-list-view.ts +333 -0
  269. package/src/tools/database/database-model.ts +214 -0
  270. package/src/tools/database/database-property-type-popover.ts +108 -0
  271. package/src/tools/database/database-tab-bar.ts +558 -0
  272. package/src/tools/database/database-view-popover.ts +129 -0
  273. package/src/tools/database/database-view-renderer.ts +25 -0
  274. package/src/tools/database/index.ts +1223 -0
  275. package/src/tools/database/types.ts +152 -0
  276. package/src/tools/database-row/index.ts +74 -0
  277. package/src/tools/index.ts +4 -0
  278. package/types/api/tools.d.ts +18 -0
  279. package/types/configs/blok-config.d.ts +27 -0
  280. package/types/data-formats/output-data.d.ts +13 -0
  281. package/types/index.d.ts +17 -0
  282. package/types/tools/database.d.ts +152 -0
  283. package/types/tools-entry.d.ts +7 -4
  284. package/types/utils/popover/popover.d.ts +6 -0
@@ -402,7 +402,11 @@ export class CodeTool implements BlockTool {
402
402
  /**
403
403
  * Builds the language items array. When a detected language differs from the
404
404
  * chosen one, it appears first with a wand icon and "auto" secondary label.
405
+ <<<<<<< Updated upstream
405
406
  * The currently selected language is shown with a trailing check icon
407
+ =======
408
+ * The currently selected language is shown with a check icon and active state
409
+ >>>>>>> Stashed changes
406
410
  * in its natural position in the full language list.
407
411
  */
408
412
  private buildLanguagePickerItems(): PopoverItemParams[] {
@@ -0,0 +1,132 @@
1
+ import type { DatabaseAdapter, PropertyDefinition, DatabaseViewConfig } from './types';
2
+
3
+ const UPDATE_DEBOUNCE_MS = 500;
4
+
5
+ export class DatabaseBackendSync {
6
+ private readonly adapter: DatabaseAdapter | undefined;
7
+ private readonly onError: ((error: unknown) => void) | undefined;
8
+ private readonly pendingTimers = new Map<string, ReturnType<typeof setTimeout>>();
9
+ private readonly pendingUpdates = new Map<string, Parameters<DatabaseAdapter['updateRow']>[0]>();
10
+ private readonly pendingPropertyTimers = new Map<string, ReturnType<typeof setTimeout>>();
11
+ private readonly pendingPropertyUpdates = new Map<string, Parameters<DatabaseAdapter['updateProperty']>[0]>();
12
+
13
+ constructor(adapter?: DatabaseAdapter, onError?: (error: unknown) => void) {
14
+ this.adapter = adapter;
15
+ this.onError = onError;
16
+ }
17
+
18
+ private async safeCall<T>(fn: (adapter: DatabaseAdapter) => Promise<T>): Promise<T | undefined> {
19
+ if (this.adapter === undefined) return undefined;
20
+ try { return await fn(this.adapter); }
21
+ catch (error) { this.onError?.(error); return undefined; }
22
+ }
23
+
24
+ // ─── Load ───
25
+
26
+ async syncLoadDatabase(): Promise<{ schema: PropertyDefinition[]; views: DatabaseViewConfig[] } | undefined> {
27
+ return this.safeCall((a) => a.loadDatabase());
28
+ }
29
+
30
+ // ─── Row operations ───
31
+
32
+ async syncCreateRow(params: Parameters<DatabaseAdapter['createRow']>[0]): Promise<ReturnType<DatabaseAdapter['createRow']> extends Promise<infer R> ? R | undefined : never> {
33
+ return this.safeCall((a) => a.createRow(params));
34
+ }
35
+
36
+ syncUpdateRow(params: Parameters<DatabaseAdapter['updateRow']>[0]): void {
37
+ if (this.adapter === undefined) return;
38
+ const { rowId } = params;
39
+ const existing = this.pendingTimers.get(rowId);
40
+ if (existing !== undefined) clearTimeout(existing);
41
+ const pending = this.pendingUpdates.get(rowId);
42
+ this.pendingUpdates.set(rowId, pending === undefined ? params : { ...pending, ...params, properties: { ...pending.properties, ...params.properties } });
43
+ this.pendingTimers.set(rowId, setTimeout(() => { this.flushRow(rowId); }, UPDATE_DEBOUNCE_MS));
44
+ }
45
+
46
+ async syncMoveRow(params: Parameters<DatabaseAdapter['moveRow']>[0]): Promise<ReturnType<DatabaseAdapter['moveRow']> extends Promise<infer R> ? R | undefined : never> {
47
+ this.flushRow(params.rowId);
48
+
49
+ return this.safeCall((a) => a.moveRow(params));
50
+ }
51
+
52
+ async syncDeleteRow(params: Parameters<DatabaseAdapter['deleteRow']>[0]): Promise<void> {
53
+ await this.safeCall((a) => a.deleteRow(params));
54
+ }
55
+
56
+ // ─── Property operations ───
57
+
58
+ async syncCreateProperty(params: Parameters<DatabaseAdapter['createProperty']>[0]): Promise<ReturnType<DatabaseAdapter['createProperty']> extends Promise<infer R> ? R | undefined : never> {
59
+ return this.safeCall((a) => a.createProperty(params));
60
+ }
61
+
62
+ async syncUpdateProperty(params: Parameters<DatabaseAdapter['updateProperty']>[0]): Promise<ReturnType<DatabaseAdapter['updateProperty']> extends Promise<infer R> ? R | undefined : never> {
63
+ return this.safeCall((a) => a.updateProperty(params));
64
+ }
65
+
66
+ syncUpdatePropertyDebounced(params: Parameters<DatabaseAdapter['updateProperty']>[0]): void {
67
+ if (this.adapter === undefined) return;
68
+ const { propertyId } = params;
69
+ const existing = this.pendingPropertyTimers.get(propertyId);
70
+ if (existing !== undefined) clearTimeout(existing);
71
+ this.pendingPropertyUpdates.set(propertyId, params);
72
+ this.pendingPropertyTimers.set(
73
+ propertyId,
74
+ setTimeout(() => { this.flushProperty(propertyId); }, UPDATE_DEBOUNCE_MS),
75
+ );
76
+ }
77
+
78
+ async syncDeleteProperty(params: Parameters<DatabaseAdapter['deleteProperty']>[0]): Promise<void> {
79
+ await this.safeCall((a) => a.deleteProperty(params));
80
+ }
81
+
82
+ // ─── View operations ───
83
+
84
+ async syncCreateView(params: Parameters<DatabaseAdapter['createView']>[0]): Promise<ReturnType<DatabaseAdapter['createView']> extends Promise<infer R> ? R | undefined : never> {
85
+ return this.safeCall((a) => a.createView(params));
86
+ }
87
+
88
+ async syncUpdateView(params: Parameters<DatabaseAdapter['updateView']>[0]): Promise<ReturnType<DatabaseAdapter['updateView']> extends Promise<infer R> ? R | undefined : never> {
89
+ return this.safeCall((a) => a.updateView(params));
90
+ }
91
+
92
+ async syncDeleteView(params: Parameters<DatabaseAdapter['deleteView']>[0]): Promise<void> {
93
+ await this.safeCall((a) => a.deleteView(params));
94
+ }
95
+
96
+ // ─── Flush & destroy ───
97
+
98
+ flushPendingUpdates(): void {
99
+ for (const rowId of this.pendingTimers.keys()) { this.flushRow(rowId); }
100
+ }
101
+
102
+ flushPendingPropertyUpdates(): void {
103
+ for (const propertyId of this.pendingPropertyTimers.keys()) { this.flushProperty(propertyId); }
104
+ }
105
+
106
+ destroy(): void {
107
+ for (const timer of this.pendingTimers.values()) clearTimeout(timer);
108
+ this.pendingTimers.clear();
109
+ this.pendingUpdates.clear();
110
+ for (const timer of this.pendingPropertyTimers.values()) clearTimeout(timer);
111
+ this.pendingPropertyTimers.clear();
112
+ this.pendingPropertyUpdates.clear();
113
+ }
114
+
115
+ private flushRow(rowId: string): void {
116
+ const timer = this.pendingTimers.get(rowId);
117
+ if (timer !== undefined) clearTimeout(timer);
118
+ this.pendingTimers.delete(rowId);
119
+ const params = this.pendingUpdates.get(rowId);
120
+ this.pendingUpdates.delete(rowId);
121
+ if (params !== undefined) void this.safeCall((a) => a.updateRow(params));
122
+ }
123
+
124
+ private flushProperty(propertyId: string): void {
125
+ const timer = this.pendingPropertyTimers.get(propertyId);
126
+ if (timer !== undefined) clearTimeout(timer);
127
+ this.pendingPropertyTimers.delete(propertyId);
128
+ const params = this.pendingPropertyUpdates.get(propertyId);
129
+ this.pendingPropertyUpdates.delete(propertyId);
130
+ if (params !== undefined) void this.safeCall((a) => a.updateProperty(params));
131
+ }
132
+ }
@@ -0,0 +1,410 @@
1
+ import type { I18n } from '../../../types';
2
+ import type { SelectOption, DatabaseRow } from './types';
3
+ import type { DatabaseViewRenderer } from './database-view-renderer';
4
+ import { IconPlus, IconPencil, IconDotsHorizontal } from '../../components/icons';
5
+
6
+ interface DatabaseBoardViewOptions {
7
+ readOnly: boolean;
8
+ i18n: I18n;
9
+ options: SelectOption[];
10
+ getRows: (optionId: string) => DatabaseRow[];
11
+ titlePropertyId: string;
12
+ onTitleEdit?: (rowId: string, newTitle: string) => void;
13
+ }
14
+
15
+ /**
16
+ * DOM rendering layer for the kanban board.
17
+ * Receives ordered data and creates plain DOM elements (NOT contenteditable).
18
+ * All interactive elements use data-blok-database-* attributes for test selectors and event delegation.
19
+ */
20
+ export class DatabaseBoardView implements DatabaseViewRenderer {
21
+ private readonly readOnly: boolean;
22
+ private readonly i18n: I18n;
23
+ private readonly options: SelectOption[];
24
+ private readonly getRows: (optionId: string) => DatabaseRow[];
25
+ private readonly titlePropertyId: string;
26
+ private readonly onTitleEdit: ((rowId: string, newTitle: string) => void) | undefined;
27
+
28
+ constructor({ readOnly, i18n, options, getRows, titlePropertyId, onTitleEdit }: DatabaseBoardViewOptions) {
29
+ this.readOnly = readOnly;
30
+ this.i18n = i18n;
31
+ this.options = options;
32
+ this.getRows = getRows;
33
+ this.titlePropertyId = titlePropertyId;
34
+ this.onTitleEdit = onTitleEdit;
35
+ }
36
+
37
+ /**
38
+ * Creates the full board DOM from option and row data.
39
+ */
40
+ createView(): HTMLDivElement {
41
+ const wrapper = document.createElement('div');
42
+
43
+ wrapper.setAttribute('data-blok-tool', 'database');
44
+ wrapper.setAttribute('role', 'region');
45
+ wrapper.setAttribute('aria-label', 'Kanban board');
46
+ wrapper.style.display = 'flex';
47
+
48
+ const boardArea = document.createElement('div');
49
+
50
+ boardArea.setAttribute('data-blok-database-board', '');
51
+ boardArea.style.display = 'flex';
52
+ boardArea.style.overflowX = 'auto';
53
+ boardArea.style.alignItems = 'flex-start';
54
+ boardArea.style.gap = '12px';
55
+ boardArea.style.paddingBottom = '24px';
56
+ boardArea.style.flex = '1';
57
+ boardArea.style.minWidth = '0';
58
+
59
+ for (const option of this.options) {
60
+ const columnEl = this.createColumnElement(option, this.getRows(option.id), this.titlePropertyId);
61
+
62
+ boardArea.appendChild(columnEl);
63
+ }
64
+
65
+ if (!this.readOnly) {
66
+ const addColumnBtn = document.createElement('button');
67
+
68
+ addColumnBtn.setAttribute('data-blok-database-add-column', '');
69
+ addColumnBtn.setAttribute('aria-label', this.i18n.t('tools.database.addColumn'));
70
+ addColumnBtn.textContent = '+ ' + this.i18n.t('tools.database.addColumn');
71
+ addColumnBtn.style.minWidth = '260px';
72
+ addColumnBtn.style.flex = '0 0 260px';
73
+ boardArea.appendChild(addColumnBtn);
74
+ }
75
+
76
+ wrapper.appendChild(boardArea);
77
+
78
+ return wrapper;
79
+ }
80
+
81
+ /**
82
+ * Creates and appends a row element to a cards container.
83
+ */
84
+ appendRow(cardsContainer: HTMLElement, row: DatabaseRow): void {
85
+ const cardEl = this.createCardElement(row, this.titlePropertyId);
86
+
87
+ cardsContainer.appendChild(cardEl);
88
+ this.updateColumnCount(cardsContainer);
89
+ }
90
+
91
+ /**
92
+ * Removes a row element from the wrapper by its data-row-id.
93
+ */
94
+ removeRow(wrapper: HTMLElement, rowId: string): void {
95
+ const cardEl = wrapper.querySelector(`[data-row-id="${rowId}"]`);
96
+ const cardsContainer = cardEl?.closest('[data-blok-database-cards]') as HTMLElement | null;
97
+
98
+ cardEl?.remove();
99
+
100
+ if (cardsContainer !== null) {
101
+ this.updateColumnCount(cardsContainer);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Updates the visible title of a row element found by its data-row-id.
107
+ */
108
+ updateRowTitle(wrapper: HTMLElement, rowId: string, title: string): void {
109
+ const cardEl = wrapper.querySelector(`[data-row-id="${rowId}"]`);
110
+ const titleEl = cardEl?.querySelector('[data-blok-database-card-title]');
111
+
112
+ if (titleEl !== null && titleEl !== undefined) {
113
+ if (title) {
114
+ titleEl.textContent = title;
115
+ titleEl.removeAttribute('data-placeholder');
116
+ cardEl?.removeAttribute('data-empty');
117
+ } else {
118
+ titleEl.textContent = this.i18n.t('tools.database.cardTitlePlaceholder');
119
+ titleEl.setAttribute('data-placeholder', '');
120
+ cardEl?.setAttribute('data-empty', '');
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Creates and inserts a group (column) element before the add-column button.
127
+ */
128
+ appendGroup(wrapper: HTMLElement, option: SelectOption): void {
129
+ const columnEl = this.createColumnElement(option, [], '');
130
+ const boardArea = wrapper.querySelector('[data-blok-database-board]');
131
+ const container = (boardArea as HTMLElement | null) ?? wrapper;
132
+ const addColumnBtn = container.querySelector('[data-blok-database-add-column]');
133
+
134
+ if (addColumnBtn) {
135
+ container.insertBefore(columnEl, addColumnBtn);
136
+ } else {
137
+ container.appendChild(columnEl);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Removes a group (column) element from the wrapper by its data-option-id.
143
+ */
144
+ removeGroup(wrapper: HTMLElement, optionId: string): void {
145
+ const columnEl = wrapper.querySelector(`[data-option-id="${optionId}"]`);
146
+
147
+ columnEl?.remove();
148
+ }
149
+
150
+ /**
151
+ * Creates a single column element with header, cards container, and optional add-card button.
152
+ */
153
+ private createColumnElement(option: SelectOption, rows: DatabaseRow[], titlePropertyId: string): HTMLDivElement {
154
+ const columnEl = document.createElement('div');
155
+
156
+ columnEl.setAttribute('data-blok-database-column', '');
157
+ columnEl.setAttribute('data-option-id', option.id);
158
+ columnEl.setAttribute('role', 'group');
159
+ columnEl.setAttribute('aria-label', option.label);
160
+ columnEl.style.display = 'flex';
161
+ columnEl.style.flexDirection = 'column';
162
+ columnEl.style.minWidth = '260px';
163
+ columnEl.style.flex = '0 0 260px';
164
+
165
+ if (option.color !== undefined) {
166
+ columnEl.style.backgroundColor = `var(--blok-color-${option.color}-bg)`;
167
+ columnEl.setAttribute('data-color', option.color);
168
+ }
169
+
170
+ const header = document.createElement('div');
171
+
172
+ header.setAttribute('data-blok-database-column-header', '');
173
+ header.style.display = 'flex';
174
+ header.style.alignItems = 'center';
175
+ header.style.padding = '0 0 6px 0';
176
+ header.style.borderRadius = '4px';
177
+ header.style.gap = '6px';
178
+ header.style.cursor = 'grab';
179
+
180
+ const pill = document.createElement('div');
181
+
182
+ pill.setAttribute('data-blok-database-column-pill', '');
183
+
184
+ if (option.color !== undefined) {
185
+ pill.style.backgroundColor = `color-mix(in srgb, var(--blok-color-${option.color}-text) 20%, var(--blok-color-${option.color}-bg))`;
186
+ pill.style.color = `var(--blok-color-${option.color}-text)`;
187
+
188
+ const dot = document.createElement('span');
189
+
190
+ dot.setAttribute('data-blok-database-column-dot', '');
191
+ dot.style.backgroundColor = `var(--blok-color-${option.color}-text)`;
192
+ pill.appendChild(dot);
193
+ }
194
+
195
+ const titleEl = document.createElement('div');
196
+
197
+ titleEl.setAttribute('data-blok-database-column-title', '');
198
+ titleEl.textContent = option.label;
199
+ pill.appendChild(titleEl);
200
+
201
+ header.appendChild(pill);
202
+
203
+ const countEl = document.createElement('span');
204
+
205
+ countEl.setAttribute('data-blok-database-column-count', '');
206
+ countEl.textContent = String(rows.length);
207
+
208
+ if (option.color !== undefined) {
209
+ countEl.style.color = `var(--blok-color-${option.color}-text)`;
210
+ }
211
+
212
+ header.appendChild(countEl);
213
+
214
+ columnEl.appendChild(header);
215
+
216
+ const cardsContainer = document.createElement('div');
217
+
218
+ cardsContainer.setAttribute('data-blok-database-cards', '');
219
+ cardsContainer.setAttribute('role', 'list');
220
+ cardsContainer.style.display = 'flex';
221
+ cardsContainer.style.flexDirection = 'column';
222
+ cardsContainer.style.gap = '8px';
223
+ cardsContainer.style.paddingTop = '6px';
224
+ cardsContainer.style.minHeight = '40px';
225
+
226
+ for (const row of rows) {
227
+ const cardEl = this.createCardElement(row, titlePropertyId);
228
+
229
+ cardsContainer.appendChild(cardEl);
230
+ }
231
+
232
+ columnEl.appendChild(cardsContainer);
233
+
234
+ if (!this.readOnly) {
235
+ const addCardBtn = document.createElement('button');
236
+
237
+ addCardBtn.setAttribute('data-blok-database-add-card', '');
238
+ addCardBtn.setAttribute('data-option-id', option.id);
239
+ addCardBtn.setAttribute('aria-label', this.i18n.t('tools.database.addCard'));
240
+
241
+ const iconEl = document.createElement('span');
242
+
243
+ iconEl.setAttribute('data-blok-database-add-card-icon', '');
244
+ iconEl.innerHTML = IconPlus;
245
+ addCardBtn.appendChild(iconEl);
246
+
247
+ const labelEl = document.createElement('span');
248
+
249
+ labelEl.textContent = this.i18n.t('tools.database.newPage');
250
+ addCardBtn.appendChild(labelEl);
251
+
252
+ if (option.color !== undefined) {
253
+ addCardBtn.style.borderColor = `color-mix(in srgb, var(--blok-color-${option.color}-text) 30%, transparent)`;
254
+ addCardBtn.style.color = `var(--blok-color-${option.color}-text)`;
255
+ }
256
+
257
+ columnEl.appendChild(addCardBtn);
258
+ }
259
+
260
+ return columnEl;
261
+ }
262
+
263
+ /**
264
+ * Creates a single card element.
265
+ */
266
+ private createCardElement(row: DatabaseRow, titlePropertyId: string): HTMLDivElement {
267
+ const cardEl = document.createElement('div');
268
+ const title = (row.properties[titlePropertyId] as string) ?? '';
269
+
270
+ cardEl.setAttribute('data-blok-database-card', '');
271
+ cardEl.setAttribute('data-row-id', row.id);
272
+ cardEl.setAttribute('role', 'listitem');
273
+ cardEl.style.padding = '10px 12px';
274
+ cardEl.style.borderRadius = '12px';
275
+ cardEl.style.cursor = 'pointer';
276
+ cardEl.style.position = 'relative';
277
+
278
+ const titleEl = document.createElement('div');
279
+
280
+ titleEl.setAttribute('data-blok-database-card-title', '');
281
+
282
+ if (title) {
283
+ titleEl.textContent = title;
284
+ } else {
285
+ titleEl.textContent = this.i18n.t('tools.database.cardTitlePlaceholder');
286
+ titleEl.setAttribute('data-placeholder', '');
287
+ cardEl.setAttribute('data-empty', '');
288
+ }
289
+
290
+ cardEl.appendChild(titleEl);
291
+
292
+ if (!this.readOnly) {
293
+ const actionsEl = document.createElement('div');
294
+
295
+ actionsEl.setAttribute('data-blok-database-card-actions', '');
296
+
297
+ const editBtn = document.createElement('button');
298
+
299
+ editBtn.setAttribute('data-blok-database-edit-card', '');
300
+ editBtn.setAttribute('data-row-id', row.id);
301
+ editBtn.setAttribute('aria-label', this.i18n.t('tools.database.editCardTitle'));
302
+ editBtn.innerHTML = IconPencil;
303
+ editBtn.addEventListener('click', (e) => {
304
+ e.stopPropagation();
305
+ this.startCardTitleEdit(cardEl, row.id);
306
+ });
307
+
308
+ const menuBtn = document.createElement('button');
309
+
310
+ menuBtn.setAttribute('data-blok-database-card-menu', '');
311
+ menuBtn.setAttribute('data-row-id', row.id);
312
+ menuBtn.setAttribute('aria-label', this.i18n.t('tools.database.cardMenuLabel'));
313
+ menuBtn.innerHTML = IconDotsHorizontal;
314
+
315
+ actionsEl.appendChild(editBtn);
316
+ actionsEl.appendChild(menuBtn);
317
+ cardEl.appendChild(actionsEl);
318
+ }
319
+
320
+ return cardEl;
321
+ }
322
+
323
+ /**
324
+ * Replaces the card title div with an inline input for editing.
325
+ * Mirrors the input-swap pattern in DatabaseColumnControls.
326
+ */
327
+ private startCardTitleEdit(cardEl: HTMLElement, rowId: string): void {
328
+ const titleEl = cardEl.querySelector<HTMLElement>('[data-blok-database-card-title]');
329
+
330
+ if (titleEl === null) {
331
+ return;
332
+ }
333
+
334
+ const originalTitle = titleEl.textContent ?? '';
335
+ const input = document.createElement('input');
336
+
337
+ input.type = 'text';
338
+ input.value = originalTitle;
339
+ input.setAttribute('data-blok-database-card-title-input', '');
340
+ input.setAttribute('aria-label', this.i18n.t('tools.database.editCardTitle'));
341
+ input.style.width = '100%';
342
+ input.style.boxSizing = 'border-box';
343
+
344
+ const state = { committed: false };
345
+
346
+ const commit = (): void => {
347
+ if (state.committed) return;
348
+ state.committed = true;
349
+ const newTitle = input.value.trim() || originalTitle;
350
+ const restoredDiv = document.createElement('div');
351
+
352
+ restoredDiv.setAttribute('data-blok-database-card-title', '');
353
+ restoredDiv.textContent = newTitle;
354
+ input.replaceWith(restoredDiv);
355
+
356
+ if (newTitle !== originalTitle) {
357
+ this.onTitleEdit?.(rowId, newTitle);
358
+ }
359
+
360
+ if (newTitle) {
361
+ cardEl.removeAttribute('data-empty');
362
+ }
363
+ };
364
+
365
+ const cancel = (): void => {
366
+ if (state.committed) return;
367
+ state.committed = true;
368
+ const restoredDiv = document.createElement('div');
369
+
370
+ restoredDiv.setAttribute('data-blok-database-card-title', '');
371
+ restoredDiv.textContent = originalTitle;
372
+ input.replaceWith(restoredDiv);
373
+ };
374
+
375
+ input.addEventListener('blur', commit);
376
+ input.addEventListener('keydown', (ke) => {
377
+ ke.stopPropagation();
378
+ if (ke.key === 'Enter') {
379
+ input.removeEventListener('blur', commit);
380
+ commit();
381
+ } else if (ke.key === 'Escape') {
382
+ input.removeEventListener('blur', commit);
383
+ cancel();
384
+ }
385
+ });
386
+
387
+ titleEl.replaceWith(input);
388
+ input.focus();
389
+ input.select();
390
+ }
391
+
392
+ /**
393
+ * Updates the card count badge for the column containing the given cards container.
394
+ */
395
+ private updateColumnCount(cardsContainer: HTMLElement): void {
396
+ const columnEl = cardsContainer.closest('[data-blok-database-column]');
397
+
398
+ if (columnEl === null) {
399
+ return;
400
+ }
401
+
402
+ const countEl = columnEl.querySelector('[data-blok-database-column-count]');
403
+
404
+ if (countEl !== null) {
405
+ const cardCount = cardsContainer.querySelectorAll('[data-blok-database-card]').length;
406
+
407
+ countEl.textContent = String(cardCount);
408
+ }
409
+ }
410
+ }