@jackuait/blok 0.10.0-beta.2 → 0.10.0-beta.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 (252) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-mvtXLxUE.mjs → blok-D_0qAdUd.mjs} +2213 -2200
  3. package/dist/chunks/{constants-20AZAgSJ.mjs → constants-DxSXr7kd.mjs} +86 -64
  4. package/dist/chunks/{i18next-Ch0gVA3V.mjs → i18next-G6FKbZqA.mjs} +1 -1
  5. package/dist/chunks/{i18next-loader-BOlOKRt8.mjs → i18next-loader-CwNimni3.mjs} +2 -2
  6. package/dist/chunks/{lightweight-i18n-D1n0OClP.mjs → lightweight-i18n-DWCdzAw0.mjs} +17 -1
  7. package/dist/{messages-D7Wofcg3.mjs → chunks/messages-1Raf1IK82.mjs} +16 -0
  8. package/dist/chunks/{messages-Du6j7HqD2.mjs → messages-6mikOS4D2.mjs} +16 -0
  9. package/dist/{messages-CyQQ8g9w2.mjs → chunks/messages-B0ffBqzr.mjs} +16 -0
  10. package/dist/{messages-CfaiAQHW2.mjs → chunks/messages-B2pW6jO_.mjs} +16 -0
  11. package/dist/{messages-4kMr3vfK2.mjs → chunks/messages-B3s2vra72.mjs} +16 -0
  12. package/dist/{messages-CBWUNVHy.mjs → chunks/messages-B7MIRzCa2.mjs} +16 -0
  13. package/dist/chunks/{messages-0Nh_GHU02.mjs → messages-BPog17132.mjs} +16 -0
  14. package/dist/chunks/{messages-CbXL5f99.mjs → messages-BS1nOvZ-.mjs} +16 -0
  15. package/dist/{messages-BCIuVjwb.mjs → chunks/messages-BW_7lfqG2.mjs} +16 -0
  16. package/dist/chunks/{messages-naWwXCx3.mjs → messages-BaPZuLjN.mjs} +16 -0
  17. package/dist/chunks/{messages-DcrkPXXn.mjs → messages-BbdNugdi.mjs} +16 -0
  18. package/dist/{messages-CeyO7HXV.mjs → chunks/messages-BdnSVKOw.mjs} +16 -0
  19. package/dist/chunks/{messages-BORQKKT9.mjs → messages-BgVEGd4c.mjs} +16 -0
  20. package/dist/chunks/{messages-C1pm6RWX.mjs → messages-BgsPQXfP.mjs} +16 -0
  21. package/dist/chunks/{messages-CYZgPXFK.mjs → messages-BkCjgGxc.mjs} +16 -0
  22. package/dist/{messages-DB1-0FXB.mjs → chunks/messages-BnznaKEP2.mjs} +16 -0
  23. package/dist/{messages-CIpXgMRr2.mjs → chunks/messages-Bpda_3PM2.mjs} +16 -0
  24. package/dist/{messages-D2GHT83V2.mjs → chunks/messages-BrFl5773.mjs} +16 -0
  25. package/dist/chunks/{messages-BLD1DC722.mjs → messages-C-b6tPad2.mjs} +16 -0
  26. package/dist/chunks/{messages-LiSIeruD2.mjs → messages-C11byid72.mjs} +16 -0
  27. package/dist/chunks/{messages-IKrYzwFq.mjs → messages-C1OqT_nL.mjs} +16 -0
  28. package/dist/chunks/{messages-fitmpwb3.mjs → messages-Cdx4QMR1.mjs} +16 -0
  29. package/dist/{messages-CLB0caVL.mjs → chunks/messages-CgRvtOEY.mjs} +16 -0
  30. package/dist/{messages-BxcqzUx0.mjs → chunks/messages-ClRHDxzh.mjs} +16 -0
  31. package/dist/{messages-DwroI9YW.mjs → chunks/messages-CljStrYi.mjs} +16 -0
  32. package/dist/chunks/{messages-CR48wvl32.mjs → messages-CmXADeab2.mjs} +16 -0
  33. package/dist/{messages-BkZDaKu6.mjs → chunks/messages-CmrMwBv3.mjs} +16 -0
  34. package/dist/chunks/{messages-B7Vlzmgw.mjs → messages-CpzO7KRA.mjs} +16 -0
  35. package/dist/{messages-DwTwecgF2.mjs → chunks/messages-Cqc-6rfh2.mjs} +16 -0
  36. package/dist/chunks/{messages-BN_zp4oj.mjs → messages-CrMfiGu5.mjs} +16 -0
  37. package/dist/{messages-BRlbE8SE.mjs → chunks/messages-Cs9XBt4T.mjs} +16 -0
  38. package/dist/chunks/{messages-DDM4t-j8.mjs → messages-Csvm4mtA.mjs} +16 -0
  39. package/dist/chunks/{messages-CuHxfDvL2.mjs → messages-Ct7AMBS82.mjs} +16 -0
  40. package/dist/{messages-BjEY7_jw.mjs → chunks/messages-CvfKofOP.mjs} +16 -0
  41. package/dist/{messages-Du0fWeyE.mjs → chunks/messages-CyNsByCY.mjs} +16 -0
  42. package/dist/{messages-6Cq_jyNk2.mjs → chunks/messages-Czny5pPT2.mjs} +16 -0
  43. package/dist/{messages-CGqRnKaM.mjs → chunks/messages-D5IgUbBD2.mjs} +16 -0
  44. package/dist/{messages-uP6wmMOs.mjs → chunks/messages-DA7Zk-Cy.mjs} +16 -0
  45. package/dist/chunks/{messages-DJ9yyqUO2.mjs → messages-DAVsuDWh2.mjs} +16 -0
  46. package/dist/chunks/{messages-i9ThpxZk2.mjs → messages-DBpXyvRe2.mjs} +16 -0
  47. package/dist/chunks/{messages-CogKhvJL.mjs → messages-DC7TX-YT.mjs} +16 -0
  48. package/dist/{messages-C-KPP7bC.mjs → chunks/messages-DD7BI6BK.mjs} +16 -0
  49. package/dist/{messages-ZMa-zmIc.mjs → chunks/messages-DHCVA7XQ.mjs} +16 -0
  50. package/dist/chunks/{messages-BPQA3B862.mjs → messages-DJA6fb_P2.mjs} +16 -0
  51. package/dist/chunks/{messages-D9SfB6MI.mjs → messages-DJkIeapn.mjs} +16 -0
  52. package/dist/{messages-CZkwcbV12.mjs → chunks/messages-DPykxECP2.mjs} +16 -0
  53. package/dist/chunks/{messages-BQvBhQem.mjs → messages-DQ5AyNCU.mjs} +16 -0
  54. package/dist/chunks/{messages-rxlf-Ule.mjs → messages-DR09nkcZ.mjs} +16 -0
  55. package/dist/chunks/{messages-B1uFbxNg.mjs → messages-DSjXen8E.mjs} +16 -0
  56. package/dist/chunks/{messages-OFFQT8Fg.mjs → messages-DT7fRpCy.mjs} +16 -0
  57. package/dist/{messages-DnNNd3RW2.mjs → chunks/messages-DUDgFEEe2.mjs} +16 -0
  58. package/dist/chunks/{messages-7PIvzufT.mjs → messages-Df87zXXG.mjs} +16 -0
  59. package/dist/chunks/{messages-CvA7Fbqf.mjs → messages-Dfpi8pDY.mjs} +16 -0
  60. package/dist/{messages-CXlAjnEQ.mjs → chunks/messages-Dm4YVlrm.mjs} +16 -0
  61. package/dist/chunks/{messages-CuJLHCj5.mjs → messages-Dplnp19q.mjs} +16 -0
  62. package/dist/{messages-DEfeDBuV2.mjs → chunks/messages-FHrCEJmY2.mjs} +16 -0
  63. package/dist/chunks/{messages-nZP1GShd.mjs → messages-ID1PHnMv.mjs} +16 -0
  64. package/dist/{messages-BiK5fMYF2.mjs → chunks/messages-IDEUsFhQ2.mjs} +16 -0
  65. package/dist/{messages-C672uqt-.mjs → chunks/messages-JQKFJo7C.mjs} +16 -0
  66. package/dist/{messages-DVHWIOfr2.mjs → chunks/messages-LxumrNue2.mjs} +16 -0
  67. package/dist/chunks/{messages-By-gACFM2.mjs → messages-R2W_rGOo2.mjs} +16 -0
  68. package/dist/chunks/{messages-FEjIF48t.mjs → messages-ZJ0b1C3a.mjs} +16 -0
  69. package/dist/{messages-nosa-xnx2.mjs → chunks/messages-d0Ky6QjR.mjs} +16 -0
  70. package/dist/{messages-CHaVGY89.mjs → chunks/messages-p4byLfvR.mjs} +16 -0
  71. package/dist/{messages-D-bnq2qy.mjs → chunks/messages-u2yxkNTE2.mjs} +16 -0
  72. package/dist/{messages-axsznSTn2.mjs → chunks/messages-wLSVQbsA2.mjs} +16 -0
  73. package/dist/chunks/{messages-DiaQNuPV.mjs → messages-xfjdrZmx.mjs} +16 -0
  74. package/dist/chunks/{objectSpread2-CyPxu8-u.mjs → objectSpread2-BY4mgzrQ.mjs} +1 -1
  75. package/dist/chunks/{tools-uP-rvRPy.mjs → tools-DVZ3zU40.mjs} +2868 -1082
  76. package/dist/full.mjs +11 -11
  77. package/dist/locales.mjs +83 -67
  78. package/dist/{messages-BfbYJ8Wk2.mjs → messages--S8_taOd2.mjs} +16 -0
  79. package/dist/{chunks/messages-CkWidbwX2.mjs → messages-B-4fku2H2.mjs} +16 -0
  80. package/dist/{chunks/messages-Dn1ZDZUy.mjs → messages-BAcH6PtT2.mjs} +16 -0
  81. package/dist/{chunks/messages-2nj1xBDo.mjs → messages-BBq0M604.mjs} +16 -0
  82. package/dist/{messages-Dzqb5lg6.mjs → messages-BBvDbp62.mjs} +16 -0
  83. package/dist/{messages-kuLrhtV2.mjs → messages-BCG_evLg.mjs} +16 -0
  84. package/dist/{messages-DEKkIgU6.mjs → messages-BCuTVHBV.mjs} +16 -0
  85. package/dist/{chunks/messages-CYZL_rhV2.mjs → messages-BGsDZTQp2.mjs} +16 -0
  86. package/dist/{chunks/messages-cMwiuDZM.mjs → messages-BJ7BuFZi.mjs} +16 -0
  87. package/dist/{chunks/messages-Dl16RBg1.mjs → messages-BKjqW08U.mjs} +16 -0
  88. package/dist/{messages-Daza4lOM.mjs → messages-BSe3QDnQ.mjs} +16 -0
  89. package/dist/{chunks/messages-CDV8VcSZ2.mjs → messages-BVKZK-3t.mjs} +16 -0
  90. package/dist/{messages-CcPrYMHE.mjs → messages-BXI3qIos.mjs} +16 -0
  91. package/dist/{messages-B_6S7hBj2.mjs → messages-BcFQFcJ92.mjs} +16 -0
  92. package/dist/{chunks/messages-CmSsyItg.mjs → messages-Bio7KYsr2.mjs} +16 -0
  93. package/dist/{chunks/messages-CqdYWY192.mjs → messages-Bk984gRE2.mjs} +16 -0
  94. package/dist/{chunks/messages-CQgP-Fo22.mjs → messages-BmNaAyKS.mjs} +16 -0
  95. package/dist/{messages-DbPt9d2U.mjs → messages-Bo_FUvVH.mjs} +16 -0
  96. package/dist/{chunks/messages-x6GyZWWT.mjs → messages-BokEflKa.mjs} +16 -0
  97. package/dist/{chunks/messages-C9qPNbrJ2.mjs → messages-BtxaN-xx.mjs} +16 -0
  98. package/dist/{messages-6edZhK922.mjs → messages-BvgTQLf72.mjs} +16 -0
  99. package/dist/{chunks/messages-BVl-X2wo2.mjs → messages-BvgXeMSL2.mjs} +16 -0
  100. package/dist/{messages-BBauvpFc.mjs → messages-BywbKcPC.mjs} +16 -0
  101. package/dist/{chunks/messages-D06U2QNl2.mjs → messages-C30Vz-UZ2.mjs} +16 -0
  102. package/dist/{messages-BwyQiBdm2.mjs → messages-C5XPUD9T2.mjs} +16 -0
  103. package/dist/{chunks/messages-BivhofAQ2.mjs → messages-C6OJvnJg2.mjs} +16 -0
  104. package/dist/{chunks/messages-D6ZvH6hX.mjs → messages-C6Y4Jv2N.mjs} +16 -0
  105. package/dist/{messages-DaFDdCrr.mjs → messages-CBdQ3XP9.mjs} +16 -0
  106. package/dist/{chunks/messages-KihEeXdr.mjs → messages-CBzd_x7H.mjs} +16 -0
  107. package/dist/{messages-CsVZUVra.mjs → messages-CHJ5SOZI.mjs} +16 -0
  108. package/dist/{messages-us2JotS-2.mjs → messages-CM5fsPo02.mjs} +16 -0
  109. package/dist/{chunks/messages-DjhgPQtb.mjs → messages-CVcQD-9u.mjs} +16 -0
  110. package/dist/{messages-BQi_l2vs.mjs → messages-CYX48nfg.mjs} +16 -0
  111. package/dist/{chunks/messages-BF8c-lMm.mjs → messages-Ccd587Yn.mjs} +16 -0
  112. package/dist/{chunks/messages-5WyxUYVR2.mjs → messages-CdlsTFB1.mjs} +16 -0
  113. package/dist/{messages-ke15helG2.mjs → messages-CgzbJ8_l2.mjs} +16 -0
  114. package/dist/{messages-D1-_eTfM.mjs → messages-CjmSrt1D.mjs} +16 -0
  115. package/dist/{chunks/messages-C7YpgZ9m.mjs → messages-CqkRG9mH.mjs} +16 -0
  116. package/dist/{messages-Dtw27ih4.mjs → messages-D3cAcyzj.mjs} +16 -0
  117. package/dist/{messages-CLN3oL77.mjs → messages-DA-o8X3A.mjs} +16 -0
  118. package/dist/{chunks/messages-BtZ8oQXS.mjs → messages-DD5pW0zJ.mjs} +16 -0
  119. package/dist/{chunks/messages-A3Z4jxwt2.mjs → messages-DIRha_gg2.mjs} +16 -0
  120. package/dist/{chunks/messages-rnd6qiJ12.mjs → messages-DJKLtW7u.mjs} +16 -0
  121. package/dist/{messages-h_VN1Kyo2.mjs → messages-DJT4Bt_02.mjs} +16 -0
  122. package/dist/{chunks/messages-D3hNTep_.mjs → messages-DOTJ2NvJ.mjs} +16 -0
  123. package/dist/{chunks/messages-B18MZnaY.mjs → messages-DUBHHfEt.mjs} +16 -0
  124. package/dist/{chunks/messages-92ma9RJD2.mjs → messages-DV9e1DW7.mjs} +16 -0
  125. package/dist/{chunks/messages-DQUUtL5v.mjs → messages-DVQNjdPk.mjs} +16 -0
  126. package/dist/{messages-DflAKRLd.mjs → messages-DZEcrbmH.mjs} +16 -0
  127. package/dist/{messages-BLjz6V7y2.mjs → messages-DbxbxUiK2.mjs} +16 -0
  128. package/dist/{messages-QkGAxuVC2.mjs → messages-Dcyrzdxa2.mjs} +16 -0
  129. package/dist/{chunks/messages-DBZ-uuAV.mjs → messages-Dddxv8-f2.mjs} +16 -0
  130. package/dist/{messages-DVILiJw52.mjs → messages-DjJQoYvP2.mjs} +16 -0
  131. package/dist/{messages-B-lXqB1z2.mjs → messages-Dkg99bfr2.mjs} +16 -0
  132. package/dist/{messages-CCBgCmyq.mjs → messages-DnatBKPm.mjs} +16 -0
  133. package/dist/{messages-Dn-khi3a.mjs → messages-DqyqEw1_.mjs} +16 -0
  134. package/dist/{chunks/messages-D4QXMtW52.mjs → messages-DtrSrdfE2.mjs} +16 -0
  135. package/dist/{messages-BHIdzfAS2.mjs → messages-JhoVMjfX2.mjs} +16 -0
  136. package/dist/{messages-YlWV9cSl.mjs → messages-eTourT12.mjs} +16 -0
  137. package/dist/{messages-DzlmoWqQ.mjs → messages-fLi0P2dP.mjs} +16 -0
  138. package/dist/{messages-C-v50b4r.mjs → messages-hTpeKUaW.mjs} +16 -0
  139. package/dist/{chunks/messages-16UmLAWZ2.mjs → messages-i4S6q64n2.mjs} +16 -0
  140. package/dist/{messages-DP9PhNTo.mjs → messages-pgPcitDH.mjs} +16 -0
  141. package/dist/{chunks/messages-D5iRrf9M.mjs → messages-tK67CBqn.mjs} +16 -0
  142. package/dist/{chunks/messages-BTyEo5Kb.mjs → messages-tsHpMdDT2.mjs} +16 -0
  143. package/dist/{chunks/messages-Ct_H_5cB2.mjs → messages-upqrRZQH2.mjs} +16 -0
  144. package/dist/{messages-CyZZ10br.mjs → messages-xEI8gEDK.mjs} +16 -0
  145. package/dist/react.mjs +3 -3
  146. package/dist/tools.mjs +3 -3
  147. package/dist/vendor.LICENSE.txt +136 -1
  148. package/package.json +2 -1
  149. package/src/components/block/style-manager.ts +1 -1
  150. package/src/components/i18n/locales/am/messages.json +16 -0
  151. package/src/components/i18n/locales/ar/messages.json +16 -0
  152. package/src/components/i18n/locales/az/messages.json +16 -0
  153. package/src/components/i18n/locales/bg/messages.json +16 -0
  154. package/src/components/i18n/locales/bn/messages.json +16 -0
  155. package/src/components/i18n/locales/bs/messages.json +16 -0
  156. package/src/components/i18n/locales/cs/messages.json +16 -0
  157. package/src/components/i18n/locales/da/messages.json +16 -0
  158. package/src/components/i18n/locales/de/messages.json +16 -0
  159. package/src/components/i18n/locales/dv/messages.json +16 -0
  160. package/src/components/i18n/locales/el/messages.json +16 -0
  161. package/src/components/i18n/locales/en/messages.json +16 -0
  162. package/src/components/i18n/locales/es/messages.json +16 -0
  163. package/src/components/i18n/locales/et/messages.json +16 -0
  164. package/src/components/i18n/locales/fa/messages.json +16 -0
  165. package/src/components/i18n/locales/fi/messages.json +16 -0
  166. package/src/components/i18n/locales/fil/messages.json +16 -0
  167. package/src/components/i18n/locales/fr/messages.json +16 -0
  168. package/src/components/i18n/locales/gu/messages.json +16 -0
  169. package/src/components/i18n/locales/he/messages.json +16 -0
  170. package/src/components/i18n/locales/hi/messages.json +16 -0
  171. package/src/components/i18n/locales/hr/messages.json +16 -0
  172. package/src/components/i18n/locales/hu/messages.json +16 -0
  173. package/src/components/i18n/locales/hy/messages.json +16 -0
  174. package/src/components/i18n/locales/id/messages.json +16 -0
  175. package/src/components/i18n/locales/it/messages.json +16 -0
  176. package/src/components/i18n/locales/ja/messages.json +16 -0
  177. package/src/components/i18n/locales/ka/messages.json +16 -0
  178. package/src/components/i18n/locales/km/messages.json +16 -0
  179. package/src/components/i18n/locales/kn/messages.json +16 -0
  180. package/src/components/i18n/locales/ko/messages.json +16 -0
  181. package/src/components/i18n/locales/ku/messages.json +16 -0
  182. package/src/components/i18n/locales/lo/messages.json +16 -0
  183. package/src/components/i18n/locales/lt/messages.json +16 -0
  184. package/src/components/i18n/locales/lv/messages.json +16 -0
  185. package/src/components/i18n/locales/mk/messages.json +16 -0
  186. package/src/components/i18n/locales/ml/messages.json +16 -0
  187. package/src/components/i18n/locales/mn/messages.json +16 -0
  188. package/src/components/i18n/locales/mr/messages.json +16 -0
  189. package/src/components/i18n/locales/ms/messages.json +16 -0
  190. package/src/components/i18n/locales/my/messages.json +16 -0
  191. package/src/components/i18n/locales/ne/messages.json +16 -0
  192. package/src/components/i18n/locales/nl/messages.json +16 -0
  193. package/src/components/i18n/locales/no/messages.json +16 -0
  194. package/src/components/i18n/locales/pa/messages.json +16 -0
  195. package/src/components/i18n/locales/pl/messages.json +16 -0
  196. package/src/components/i18n/locales/ps/messages.json +16 -0
  197. package/src/components/i18n/locales/pt/messages.json +16 -0
  198. package/src/components/i18n/locales/ro/messages.json +16 -0
  199. package/src/components/i18n/locales/ru/messages.json +16 -0
  200. package/src/components/i18n/locales/sd/messages.json +16 -0
  201. package/src/components/i18n/locales/si/messages.json +16 -0
  202. package/src/components/i18n/locales/sk/messages.json +16 -0
  203. package/src/components/i18n/locales/sl/messages.json +16 -0
  204. package/src/components/i18n/locales/sq/messages.json +16 -0
  205. package/src/components/i18n/locales/sr/messages.json +16 -0
  206. package/src/components/i18n/locales/sv/messages.json +16 -0
  207. package/src/components/i18n/locales/sw/messages.json +16 -0
  208. package/src/components/i18n/locales/ta/messages.json +16 -0
  209. package/src/components/i18n/locales/te/messages.json +16 -0
  210. package/src/components/i18n/locales/th/messages.json +16 -0
  211. package/src/components/i18n/locales/tr/messages.json +16 -0
  212. package/src/components/i18n/locales/ug/messages.json +16 -0
  213. package/src/components/i18n/locales/uk/messages.json +16 -0
  214. package/src/components/i18n/locales/ur/messages.json +16 -0
  215. package/src/components/i18n/locales/vi/messages.json +16 -0
  216. package/src/components/i18n/locales/yi/messages.json +16 -0
  217. package/src/components/i18n/locales/zh/messages.json +16 -0
  218. package/src/components/icons/index.ts +56 -0
  219. package/src/components/modules/api/tools.ts +19 -0
  220. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +5 -5
  221. package/src/components/modules/blockManager/blockManager.ts +11 -0
  222. package/src/components/modules/blockManager/event-binder.ts +12 -1
  223. package/src/components/modules/themeManager.ts +3 -1
  224. package/src/components/modules/toolbar/blockSettings.ts +0 -1
  225. package/src/components/modules/toolbar/inline/index.ts +0 -3
  226. package/src/components/modules/uiControllers/controllers/keyboard.ts +29 -0
  227. package/src/components/modules/uiControllers/controllers/selection.ts +14 -2
  228. package/src/components/ui/toolbox.ts +0 -1
  229. package/src/components/utils/popover/popover-position.ts +4 -2
  230. package/src/styles/main.css +1139 -0
  231. package/src/tools/callout/constants.ts +2 -1
  232. package/src/tools/database/database-backend-sync.ts +101 -0
  233. package/src/tools/database/database-board-view.ts +301 -0
  234. package/src/tools/database/database-card-drag.ts +306 -0
  235. package/src/tools/database/database-card-drawer.ts +546 -0
  236. package/src/tools/database/database-column-controls.ts +46 -0
  237. package/src/tools/database/database-column-drag.ts +262 -0
  238. package/src/tools/database/database-keyboard.ts +35 -0
  239. package/src/tools/database/database-list-row-drag.ts +245 -0
  240. package/src/tools/database/database-list-view.ts +333 -0
  241. package/src/tools/database/database-model.ts +246 -0
  242. package/src/tools/database/database-property-type-popover.ts +108 -0
  243. package/src/tools/database/database-tab-bar.ts +532 -0
  244. package/src/tools/database/database-view-popover.ts +109 -0
  245. package/src/tools/database/database-view-renderer.ts +25 -0
  246. package/src/tools/database/index.ts +948 -0
  247. package/src/tools/database/types.ts +144 -0
  248. package/src/tools/index.ts +2 -0
  249. package/types/api/tools.d.ts +18 -0
  250. package/types/tools/callout.d.ts +16 -0
  251. package/types/tools/quote.d.ts +13 -0
  252. package/types/tools-entry.d.ts +7 -0
@@ -0,0 +1,333 @@
1
+ import type { I18n } from '../../../types';
2
+ import type { DatabaseRow, PropertyDefinition, PropertyValue, SelectOption } from './types';
3
+ import type { DatabaseViewRenderer } from './database-view-renderer';
4
+
5
+ interface DatabaseListViewOptions {
6
+ readOnly: boolean;
7
+ i18n: I18n;
8
+ rows: DatabaseRow[];
9
+ titlePropertyId: string;
10
+ schema: PropertyDefinition[];
11
+ visiblePropertyIds: string[];
12
+ options?: SelectOption[];
13
+ getRows?: (optionId: string) => DatabaseRow[];
14
+ }
15
+
16
+ /**
17
+ * DOM rendering layer for the flat list view.
18
+ * Renders rows as a flat list with optional property badges.
19
+ * All interactive elements use data-blok-database-* attributes for test selectors and event delegation.
20
+ */
21
+ export class DatabaseListView implements DatabaseViewRenderer {
22
+ private readonly readOnly: boolean;
23
+ private readonly i18n: I18n;
24
+ private readonly rows: DatabaseRow[];
25
+ private readonly titlePropertyId: string;
26
+ private readonly schema: PropertyDefinition[];
27
+ private readonly visiblePropertyIds: string[];
28
+ private readonly groupOptions: SelectOption[] | undefined;
29
+ private readonly getGroupRows: ((optionId: string) => DatabaseRow[]) | undefined;
30
+
31
+ constructor({ readOnly, i18n, rows, titlePropertyId, schema, visiblePropertyIds, options, getRows }: DatabaseListViewOptions) {
32
+ this.readOnly = readOnly;
33
+ this.i18n = i18n;
34
+ this.rows = rows;
35
+ this.titlePropertyId = titlePropertyId;
36
+ this.schema = schema;
37
+ this.visiblePropertyIds = visiblePropertyIds;
38
+ this.groupOptions = options;
39
+ this.getGroupRows = getRows;
40
+ }
41
+
42
+ /**
43
+ * Creates the full list DOM from row data.
44
+ * When options are provided, renders grouped sections; otherwise renders a flat list.
45
+ */
46
+ createView(): HTMLDivElement {
47
+ const wrapper = document.createElement('div');
48
+
49
+ wrapper.setAttribute('data-blok-database-list', '');
50
+
51
+ if (this.groupOptions !== undefined && this.groupOptions.length > 0 && this.getGroupRows !== undefined) {
52
+ for (const option of this.groupOptions) {
53
+ const rows = this.getGroupRows(option.id);
54
+ const groupEl = this.createGroupElement(option, rows);
55
+
56
+ wrapper.appendChild(groupEl);
57
+ }
58
+ } else {
59
+ wrapper.setAttribute('role', 'list');
60
+ wrapper.setAttribute('aria-label', 'List view');
61
+
62
+ for (const row of this.rows) {
63
+ const rowEl = this.createRowElement(row);
64
+
65
+ wrapper.appendChild(rowEl);
66
+ }
67
+
68
+ if (!this.readOnly) {
69
+ const addRowBtn = this.createAddRowButton();
70
+
71
+ wrapper.appendChild(addRowBtn);
72
+ }
73
+ }
74
+
75
+ return wrapper;
76
+ }
77
+
78
+ /**
79
+ * Creates a collapsible group element for a select option.
80
+ */
81
+ private createGroupElement(option: SelectOption, rows: DatabaseRow[]): HTMLDivElement {
82
+ const groupEl = document.createElement('div');
83
+
84
+ groupEl.setAttribute('data-blok-database-list-group', '');
85
+ groupEl.setAttribute('data-option-id', option.id);
86
+
87
+ // Header
88
+ const header = document.createElement('div');
89
+
90
+ header.setAttribute('data-blok-database-list-group-header', '');
91
+
92
+ const toggle = document.createElement('span');
93
+
94
+ toggle.setAttribute('data-blok-database-list-group-toggle', '');
95
+ toggle.textContent = '▼';
96
+
97
+ const dot = document.createElement('span');
98
+
99
+ dot.setAttribute('data-blok-database-list-group-dot', '');
100
+
101
+ if (option.color !== undefined) {
102
+ dot.style.backgroundColor = `var(--blok-color-${option.color}-text)`;
103
+ }
104
+
105
+ const title = document.createElement('span');
106
+
107
+ title.setAttribute('data-blok-database-list-group-title', '');
108
+ title.textContent = option.label;
109
+
110
+ const count = document.createElement('span');
111
+
112
+ count.setAttribute('data-blok-database-list-group-count', '');
113
+ count.textContent = String(rows.length);
114
+
115
+ header.appendChild(toggle);
116
+ header.appendChild(dot);
117
+ header.appendChild(title);
118
+ header.appendChild(count);
119
+
120
+ // Rows container
121
+ const rowsContainer = document.createElement('div');
122
+
123
+ rowsContainer.setAttribute('data-blok-database-list-rows', '');
124
+ rowsContainer.setAttribute('role', 'list');
125
+
126
+ for (const row of rows) {
127
+ const rowEl = this.createRowElement(row);
128
+
129
+ rowsContainer.appendChild(rowEl);
130
+ }
131
+
132
+ // Add-row button
133
+ const addRowBtn = this.readOnly ? null : this.createAddRowButton(option.id);
134
+
135
+ // Collapse/expand toggle
136
+ header.addEventListener('click', () => {
137
+ const collapsed = rowsContainer.style.display === 'none';
138
+
139
+ rowsContainer.style.display = collapsed ? '' : 'none';
140
+ toggle.textContent = collapsed ? '▼' : '▶';
141
+
142
+ if (addRowBtn !== null) {
143
+ addRowBtn.style.display = collapsed ? '' : 'none';
144
+ }
145
+ });
146
+
147
+ groupEl.appendChild(header);
148
+ groupEl.appendChild(rowsContainer);
149
+
150
+ if (addRowBtn !== null) {
151
+ groupEl.appendChild(addRowBtn);
152
+ }
153
+
154
+ return groupEl;
155
+ }
156
+
157
+ /**
158
+ * Creates a row element and inserts it before the add-row button (if present), else appends.
159
+ */
160
+ appendRow(container: HTMLElement, row: DatabaseRow): void {
161
+ const rowEl = this.createRowElement(row);
162
+ const addRowBtn = container.querySelector('[data-blok-database-add-row]');
163
+
164
+ if (addRowBtn !== null) {
165
+ container.insertBefore(rowEl, addRowBtn);
166
+ } else {
167
+ container.appendChild(rowEl);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Removes a row element from the wrapper by its data-row-id.
173
+ */
174
+ removeRow(wrapper: HTMLElement, rowId: string): void {
175
+ const rowEl = wrapper.querySelector(`[data-row-id="${rowId}"]`);
176
+
177
+ rowEl?.remove();
178
+ }
179
+
180
+ /**
181
+ * Updates the visible title of a row element found by its data-row-id.
182
+ */
183
+ updateRowTitle(wrapper: HTMLElement, rowId: string, title: string): void {
184
+ const rowEl = wrapper.querySelector(`[data-row-id="${rowId}"]`);
185
+ const titleEl = rowEl?.querySelector('[data-blok-database-list-row-title]');
186
+
187
+ if (titleEl === null || titleEl === undefined) {
188
+ return;
189
+ }
190
+
191
+ if (title) {
192
+ titleEl.textContent = title;
193
+ titleEl.removeAttribute('data-placeholder');
194
+ } else {
195
+ titleEl.textContent = this.i18n.t('tools.database.rowTitlePlaceholder');
196
+ titleEl.setAttribute('data-placeholder', '');
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Creates a single row element.
202
+ */
203
+ private createRowElement(row: DatabaseRow): HTMLDivElement {
204
+ const rowEl = document.createElement('div');
205
+ const title = (row.properties[this.titlePropertyId] as string) ?? '';
206
+
207
+ rowEl.setAttribute('data-blok-database-list-row', '');
208
+ rowEl.setAttribute('data-row-id', row.id);
209
+ rowEl.setAttribute('role', 'listitem');
210
+
211
+ const titleEl = document.createElement('div');
212
+
213
+ titleEl.setAttribute('data-blok-database-list-row-title', '');
214
+
215
+ if (title) {
216
+ titleEl.textContent = title;
217
+ } else {
218
+ titleEl.textContent = this.i18n.t('tools.database.rowTitlePlaceholder');
219
+ titleEl.setAttribute('data-placeholder', '');
220
+ }
221
+
222
+ rowEl.appendChild(titleEl);
223
+
224
+ const propertiesEl = this.createPropertiesElement(row);
225
+
226
+ rowEl.appendChild(propertiesEl);
227
+
228
+ return rowEl;
229
+ }
230
+
231
+ /**
232
+ * Creates the properties container with badges, open button, and delete button.
233
+ */
234
+ private createPropertiesElement(row: DatabaseRow): HTMLDivElement {
235
+ const propertiesEl = document.createElement('div');
236
+
237
+ propertiesEl.setAttribute('data-blok-database-list-row-properties', '');
238
+
239
+ for (const propId of this.visiblePropertyIds) {
240
+ const propDef = this.schema.find(p => p.id === propId);
241
+
242
+ if (propDef === undefined) {
243
+ continue;
244
+ }
245
+
246
+ const value = row.properties[propId];
247
+ const badge = this.createPropertyBadge(propDef, value);
248
+
249
+ if (badge !== null) {
250
+ propertiesEl.appendChild(badge);
251
+ }
252
+ }
253
+
254
+ const openBtn = document.createElement('button');
255
+
256
+ openBtn.setAttribute('data-blok-database-list-row-open', '');
257
+ openBtn.setAttribute('aria-label', this.i18n.t('tools.database.openRow'));
258
+ propertiesEl.appendChild(openBtn);
259
+
260
+ if (!this.readOnly) {
261
+ const deleteBtn = document.createElement('button');
262
+
263
+ deleteBtn.setAttribute('data-blok-database-delete-row', '');
264
+ deleteBtn.setAttribute('data-row-id', row.id);
265
+ deleteBtn.setAttribute('aria-label', this.i18n.t('tools.database.deleteRow'));
266
+ deleteBtn.textContent = '\u00d7';
267
+ propertiesEl.appendChild(deleteBtn);
268
+ }
269
+
270
+ return propertiesEl;
271
+ }
272
+
273
+ /**
274
+ * Creates a property badge span for a given property definition and value.
275
+ * Returns null if the value is undefined/null or if the select option is not found.
276
+ */
277
+ private createPropertyBadge(propDef: PropertyDefinition, value: PropertyValue): HTMLSpanElement | null {
278
+ if (value === undefined || value === null) {
279
+ return null;
280
+ }
281
+
282
+ const badge = document.createElement('span');
283
+
284
+ badge.setAttribute('data-blok-database-list-row-property', '');
285
+ badge.setAttribute('data-property-id', propDef.id);
286
+
287
+ if (propDef.type === 'select') {
288
+ const options = propDef.config?.options ?? [];
289
+ const option = options.find(o => o.id === value);
290
+
291
+ if (option === undefined) {
292
+ return null;
293
+ }
294
+
295
+ badge.textContent = option.label;
296
+
297
+ if (option.color !== undefined) {
298
+ badge.style.backgroundColor = `var(--blok-color-${option.color}-bg)`;
299
+ badge.style.color = `var(--blok-color-${option.color}-text)`;
300
+ }
301
+ } else if (propDef.type === 'checkbox') {
302
+ const checkbox = document.createElement('input');
303
+
304
+ checkbox.type = 'checkbox';
305
+ checkbox.checked = value === true;
306
+ badge.appendChild(checkbox);
307
+ } else if (typeof value === 'string' || typeof value === 'number') {
308
+ badge.textContent = String(value);
309
+ } else {
310
+ return null;
311
+ }
312
+
313
+ return badge;
314
+ }
315
+
316
+ /**
317
+ * Creates the "+ New" add-row button.
318
+ * When optionId is provided, sets data-option-id on the button.
319
+ */
320
+ private createAddRowButton(optionId?: string): HTMLButtonElement {
321
+ const addRowBtn = document.createElement('button');
322
+
323
+ addRowBtn.setAttribute('data-blok-database-add-row', '');
324
+ addRowBtn.setAttribute('aria-label', this.i18n.t('tools.database.addRow'));
325
+ addRowBtn.textContent = '+ ' + this.i18n.t('tools.database.newRow');
326
+
327
+ if (optionId !== undefined) {
328
+ addRowBtn.setAttribute('data-option-id', optionId);
329
+ }
330
+
331
+ return addRowBtn;
332
+ }
333
+ }
@@ -0,0 +1,246 @@
1
+ import { generateKeyBetween } from 'fractional-indexing';
2
+ import { nanoid } from 'nanoid';
3
+ import type {
4
+ DatabaseData,
5
+ DatabaseRow,
6
+ DatabaseViewConfig,
7
+ PropertyConfig,
8
+ PropertyDefinition,
9
+ PropertyType,
10
+ PropertyValue,
11
+ SelectOption,
12
+ ViewType,
13
+ } from './types';
14
+
15
+ export class DatabaseModel {
16
+ private schema: PropertyDefinition[];
17
+ private rows: Record<string, DatabaseRow>;
18
+ private views: DatabaseViewConfig[];
19
+
20
+ constructor(data?: Partial<DatabaseData>) {
21
+ if (data?.schema !== undefined && data.schema.length > 0) {
22
+ this.schema = data.schema.map((p) => ({ ...p }));
23
+ } else {
24
+ this.schema = DatabaseModel.createDefaultSchema();
25
+ }
26
+
27
+ this.rows = {};
28
+ if (data?.rows !== undefined) {
29
+ for (const [id, row] of Object.entries(data.rows)) {
30
+ this.rows[id] = { ...row, properties: { ...row.properties } };
31
+ }
32
+ }
33
+
34
+ if (data?.views !== undefined && data.views.length > 0) {
35
+ this.views = data.views.map((v) => ({ ...v, sorts: [...v.sorts], filters: [...v.filters], visibleProperties: [...v.visibleProperties] }));
36
+ } else {
37
+ const statusProp = this.schema.find((p) => p.type === 'select');
38
+ this.views = [DatabaseModel.createDefaultView(statusProp?.id)];
39
+ }
40
+ }
41
+
42
+ // ─── Schema ───
43
+
44
+ getSchema(): PropertyDefinition[] {
45
+ return [...this.schema];
46
+ }
47
+
48
+ getProperty(propertyId: string): PropertyDefinition | undefined {
49
+ return this.schema.find((p) => p.id === propertyId);
50
+ }
51
+
52
+ addProperty(name: string, type: PropertyType, config?: PropertyConfig): PropertyDefinition {
53
+ const lastPosition = this.schema.length > 0 ? this.schema[this.schema.length - 1].position : null;
54
+ const prop: PropertyDefinition = {
55
+ id: nanoid(),
56
+ name,
57
+ type,
58
+ position: generateKeyBetween(lastPosition, null),
59
+ ...(config !== undefined ? { config } : {}),
60
+ };
61
+ this.schema.push(prop);
62
+ return prop;
63
+ }
64
+
65
+ updateProperty(propertyId: string, changes: Partial<Pick<PropertyDefinition, 'name' | 'config'>>): void {
66
+ const prop = this.schema.find((p) => p.id === propertyId);
67
+ if (prop === undefined) return;
68
+ if (changes.name !== undefined) prop.name = changes.name;
69
+ if (changes.config !== undefined) prop.config = changes.config;
70
+ }
71
+
72
+ deleteProperty(propertyId: string): void {
73
+ this.schema = this.schema.filter((p) => p.id !== propertyId);
74
+ for (const row of Object.values(this.rows)) {
75
+ const { [propertyId]: _, ...rest } = row.properties;
76
+ row.properties = rest;
77
+ }
78
+ }
79
+
80
+ // ─── Rows ───
81
+
82
+ getOrderedRows(): DatabaseRow[] {
83
+ return Object.values(this.rows).sort((a, b) => (a.position < b.position ? -1 : 1));
84
+ }
85
+
86
+ getRow(rowId: string): DatabaseRow | undefined {
87
+ return this.rows[rowId];
88
+ }
89
+
90
+ addRow(properties: Record<string, PropertyValue> = {}): DatabaseRow {
91
+ const orderedRows = this.getOrderedRows();
92
+ const lastPosition = orderedRows.length > 0 ? orderedRows[orderedRows.length - 1].position : null;
93
+ const row: DatabaseRow = {
94
+ id: nanoid(),
95
+ position: generateKeyBetween(lastPosition, null),
96
+ properties: { ...properties },
97
+ };
98
+ this.rows[row.id] = row;
99
+ return row;
100
+ }
101
+
102
+ updateRow(rowId: string, properties: Record<string, PropertyValue>): void {
103
+ const row = this.rows[rowId];
104
+ if (row === undefined) return;
105
+ Object.assign(row.properties, properties);
106
+ }
107
+
108
+ moveRow(rowId: string, position: string): void {
109
+ const row = this.rows[rowId];
110
+ if (row === undefined) return;
111
+ row.position = position;
112
+ }
113
+
114
+ deleteRow(rowId: string): void {
115
+ const { [rowId]: _, ...rest } = this.rows;
116
+ this.rows = rest;
117
+ }
118
+
119
+ // ─── View-oriented queries ───
120
+
121
+ getRowsGroupedBy(propertyId: string): Map<string, DatabaseRow[]> {
122
+ const groups = new Map<string, DatabaseRow[]>();
123
+ const ordered = this.getOrderedRows();
124
+ for (const row of ordered) {
125
+ const rawValue = row.properties[propertyId];
126
+ const key = this.toGroupKey(rawValue);
127
+ const existing = groups.get(key);
128
+ if (existing !== undefined) {
129
+ existing.push(row);
130
+ } else {
131
+ groups.set(key, [row]);
132
+ }
133
+ }
134
+ return groups;
135
+ }
136
+
137
+ getSelectOptions(propertyId: string): SelectOption[] {
138
+ const prop = this.getProperty(propertyId);
139
+ if (prop === undefined || (prop.type !== 'select' && prop.type !== 'multiSelect')) return [];
140
+ const options = prop.config?.options ?? [];
141
+ return [...options].sort((a, b) => (a.position < b.position ? -1 : 1));
142
+ }
143
+
144
+ // ─── Views ───
145
+
146
+ getViews(): DatabaseViewConfig[] {
147
+ return [...this.views];
148
+ }
149
+
150
+ getView(viewId: string): DatabaseViewConfig | undefined {
151
+ return this.views.find((v) => v.id === viewId);
152
+ }
153
+
154
+ addView(name: string, type: ViewType, config: Partial<Pick<DatabaseViewConfig, 'groupBy' | 'sorts' | 'filters' | 'visibleProperties'>> = {}): DatabaseViewConfig {
155
+ const sorted = [...this.views].sort((a, b) => (a.position < b.position ? -1 : 1));
156
+ const lastPosition = sorted.length > 0 ? sorted[sorted.length - 1].position : null;
157
+ const view: DatabaseViewConfig = {
158
+ id: nanoid(),
159
+ name,
160
+ type,
161
+ position: generateKeyBetween(lastPosition, null),
162
+ groupBy: config.groupBy,
163
+ sorts: config.sorts ?? [],
164
+ filters: config.filters ?? [],
165
+ visibleProperties: config.visibleProperties ?? [],
166
+ };
167
+ this.views.push(view);
168
+ return view;
169
+ }
170
+
171
+ updateView(viewId: string, changes: Partial<Pick<DatabaseViewConfig, 'name' | 'type' | 'position' | 'groupBy' | 'sorts' | 'filters' | 'visibleProperties'>>): void {
172
+ const view = this.views.find((v) => v.id === viewId);
173
+ if (view === undefined) return;
174
+ Object.assign(view, changes);
175
+ }
176
+
177
+ deleteView(viewId: string): void {
178
+ this.views = this.views.filter((v) => v.id !== viewId);
179
+ }
180
+
181
+ // ─── Hydrate ───
182
+
183
+ hydrate(data: { schema: PropertyDefinition[]; rows: Record<string, DatabaseRow>; views: DatabaseViewConfig[] }): void {
184
+ this.schema = data.schema.map((p) => ({ ...p }));
185
+ this.rows = {};
186
+ for (const [id, row] of Object.entries(data.rows)) {
187
+ this.rows[id] = { ...row, properties: { ...row.properties } };
188
+ }
189
+ this.views = data.views.map((v) => ({ ...v, sorts: [...v.sorts], filters: [...v.filters], visibleProperties: [...v.visibleProperties] }));
190
+ }
191
+
192
+ // ─── Snapshot ───
193
+
194
+ snapshot(): DatabaseData {
195
+ const rowsCopy: Record<string, DatabaseRow> = {};
196
+ for (const [id, row] of Object.entries(this.rows)) {
197
+ rowsCopy[id] = {
198
+ id: row.id,
199
+ position: row.position,
200
+ properties: JSON.parse(JSON.stringify(row.properties)) as Record<string, PropertyValue>,
201
+ };
202
+ }
203
+ return {
204
+ schema: JSON.parse(JSON.stringify(this.schema)) as PropertyDefinition[],
205
+ rows: rowsCopy,
206
+ views: JSON.parse(JSON.stringify(this.views)) as DatabaseViewConfig[],
207
+ activeViewId: this.views.length > 0 ? this.views[0].id : '',
208
+ };
209
+ }
210
+
211
+ // ─── Static helpers ───
212
+
213
+ private toGroupKey(value: PropertyValue | undefined): string {
214
+ if (value === undefined || value === null) return '';
215
+ if (typeof value === 'string') return value;
216
+ if (typeof value === 'boolean' || typeof value === 'number') return String(value);
217
+ return '';
218
+ }
219
+
220
+ static positionBetween(after: string | null, before: string | null): string {
221
+ return generateKeyBetween(after, before);
222
+ }
223
+
224
+ private static createDefaultSchema(): PropertyDefinition[] {
225
+ return [
226
+ { id: nanoid(), name: 'Title', type: 'title', position: 'a0' },
227
+ {
228
+ id: nanoid(), name: 'Status', type: 'select', position: 'a1',
229
+ config: {
230
+ options: [
231
+ { id: nanoid(), label: 'Not started', color: 'gray', position: 'a0' },
232
+ { id: nanoid(), label: 'In progress', color: 'blue', position: 'a1' },
233
+ { id: nanoid(), label: 'Done', color: 'green', position: 'a2' },
234
+ ],
235
+ },
236
+ },
237
+ ];
238
+ }
239
+
240
+ private static createDefaultView(groupByPropertyId?: string): DatabaseViewConfig {
241
+ return {
242
+ id: nanoid(), name: 'Board', type: 'board', position: 'a0',
243
+ groupBy: groupByPropertyId, sorts: [], filters: [], visibleProperties: [],
244
+ };
245
+ }
246
+ }
@@ -0,0 +1,108 @@
1
+ import {
2
+ IconText,
3
+ IconHash,
4
+ IconList,
5
+ IconListBulleted,
6
+ IconCalendar,
7
+ IconListChecklist,
8
+ IconGlobe,
9
+ } from '../../components/icons';
10
+ import type { PropertyType } from './types';
11
+
12
+ interface PropertyTypeOption {
13
+ type: PropertyType;
14
+ icon: string;
15
+ label: string;
16
+ }
17
+
18
+ const PROPERTY_TYPES: PropertyTypeOption[] = [
19
+ { type: 'text', icon: IconText, label: 'Text' },
20
+ { type: 'number', icon: IconHash, label: 'Number' },
21
+ { type: 'select', icon: IconList, label: 'Select' },
22
+ { type: 'multiSelect', icon: IconListBulleted, label: 'Multi-select' },
23
+ { type: 'date', icon: IconCalendar, label: 'Date' },
24
+ { type: 'checkbox', icon: IconListChecklist, label: 'Checkbox' },
25
+ { type: 'url', icon: IconGlobe, label: 'URL' },
26
+ ];
27
+
28
+ export interface PropertyTypePopoverOptions {
29
+ onSelect: (type: PropertyType) => void;
30
+ }
31
+
32
+ export class DatabasePropertyTypePopover {
33
+ private readonly onSelect: (type: PropertyType) => void;
34
+ private popoverEl: HTMLElement | null = null;
35
+ private boundOutsideClick: ((e: MouseEvent) => void) | null = null;
36
+
37
+ constructor(options: PropertyTypePopoverOptions) {
38
+ this.onSelect = options.onSelect;
39
+ }
40
+
41
+ open(anchor: HTMLElement): void {
42
+ this.close();
43
+
44
+ const popover = document.createElement('div');
45
+ popover.setAttribute('data-blok-popover', '');
46
+ popover.setAttribute('data-blok-popover-opened', '');
47
+ popover.setAttribute('data-blok-database-property-type-popover', '');
48
+ popover.style.position = 'fixed';
49
+ popover.style.zIndex = '1000';
50
+
51
+ const rect = anchor.getBoundingClientRect();
52
+ popover.style.top = `${rect.bottom + 4}px`;
53
+ popover.style.left = `${rect.left}px`;
54
+
55
+ const heading = document.createElement('div');
56
+ heading.setAttribute('data-blok-database-property-type-heading', '');
57
+ heading.textContent = 'Property type';
58
+ popover.appendChild(heading);
59
+
60
+ for (const option of PROPERTY_TYPES) {
61
+ const item = document.createElement('div');
62
+ item.setAttribute('data-blok-database-property-type-option', option.type);
63
+
64
+ const iconEl = document.createElement('div');
65
+ iconEl.setAttribute('data-blok-database-property-type-option-icon', '');
66
+ iconEl.innerHTML = option.icon;
67
+ item.appendChild(iconEl);
68
+
69
+ const label = document.createElement('span');
70
+ label.textContent = option.label;
71
+ item.appendChild(label);
72
+
73
+ item.addEventListener('click', () => {
74
+ this.onSelect(option.type);
75
+ this.close();
76
+ });
77
+
78
+ popover.appendChild(item);
79
+ }
80
+
81
+ document.body.appendChild(popover);
82
+ this.popoverEl = popover;
83
+
84
+ this.boundOutsideClick = (e: MouseEvent): void => {
85
+ const target = e.target as HTMLElement;
86
+ if (!popover.contains(target) && !anchor.contains(target)) {
87
+ this.close();
88
+ }
89
+ };
90
+
91
+ document.addEventListener('mousedown', this.boundOutsideClick);
92
+ }
93
+
94
+ close(): void {
95
+ if (this.popoverEl !== null) {
96
+ this.popoverEl.remove();
97
+ this.popoverEl = null;
98
+ }
99
+ if (this.boundOutsideClick !== null) {
100
+ document.removeEventListener('mousedown', this.boundOutsideClick);
101
+ this.boundOutsideClick = null;
102
+ }
103
+ }
104
+
105
+ destroy(): void {
106
+ this.close();
107
+ }
108
+ }