@jackuait/blok 0.8.2 → 0.8.3-beta.2

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 (377) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/am-CHDDMHkd.mjs +16490 -0
  3. package/dist/chunks/ar-DoqfNqut.mjs +19245 -0
  4. package/dist/chunks/az-C34P9iEa.mjs +14694 -0
  5. package/dist/chunks/bg-jroXLY8Y.mjs +15432 -0
  6. package/dist/chunks/{blok-GAbVCHTV.mjs → blok-0-8moMz9.mjs} +2090 -2052
  7. package/dist/chunks/bn-BRI-WqxY.mjs +16150 -0
  8. package/dist/chunks/bs-CCGUpNHu.mjs +12536 -0
  9. package/dist/chunks/{constants-CyDHDZyH.mjs → constants-PWnlOOrQ.mjs} +211 -172
  10. package/dist/chunks/cs-D5qZOGuc.mjs +22809 -0
  11. package/dist/chunks/da-DrJ7W37K.mjs +14573 -0
  12. package/dist/chunks/de-BW6-kp2c.mjs +16366 -0
  13. package/dist/chunks/el-C-Vc_Otu.mjs +16339 -0
  14. package/dist/chunks/es-B6fI5K9i.mjs +13597 -0
  15. package/dist/chunks/et-BhVlZ-Yz.mjs +14747 -0
  16. package/dist/chunks/fa-D55Ijdqa.mjs +16368 -0
  17. package/dist/chunks/fi-jNLjhKUQ.mjs +14408 -0
  18. package/dist/chunks/fil-DYd0T5aX.mjs +15915 -0
  19. package/dist/chunks/fr-yxy5xWw_.mjs +17010 -0
  20. package/dist/chunks/gu-CcY_LJe7.mjs +19021 -0
  21. package/dist/chunks/he-DL9s7wNw.mjs +15047 -0
  22. package/dist/chunks/hi-C8eGXgw5.mjs +15946 -0
  23. package/dist/chunks/hr-DLpybOhU.mjs +15099 -0
  24. package/dist/chunks/hu-BkT0gT00.mjs +16564 -0
  25. package/dist/chunks/hy-CVFDCp2S.mjs +12823 -0
  26. package/dist/chunks/{i18next-loader-p-7ioTwr.mjs → i18next-loader-BCAGutUy.mjs} +2 -2
  27. package/dist/chunks/id-0P4W9Az0.mjs +16690 -0
  28. package/dist/chunks/it-mLY6_uoW.mjs +19054 -0
  29. package/dist/chunks/ja-7RkeRNWG.mjs +15708 -0
  30. package/dist/chunks/ka-C7Lx-Qsh.mjs +13049 -0
  31. package/dist/chunks/km-Q8udaraH.mjs +12319 -0
  32. package/dist/chunks/kn-BiETM-iq.mjs +19123 -0
  33. package/dist/chunks/ko-tiB80pF1.mjs +17322 -0
  34. package/dist/chunks/ku-CY-OABkR.mjs +236 -0
  35. package/dist/chunks/{lightweight-i18n-BPeH69Dl.mjs → lightweight-i18n-CNUuUkuo.mjs} +31 -1
  36. package/dist/chunks/lo-CTBhEnyk.mjs +13576 -0
  37. package/dist/chunks/lt-BHKHEtqK.mjs +14587 -0
  38. package/dist/chunks/lv-DWxgtfUg.mjs +15042 -0
  39. package/dist/chunks/{messages-BHf_VcXb2.mjs → messages-2i0z2hFq2.mjs} +32 -2
  40. package/dist/{messages-8zo-T-Nx2.mjs → chunks/messages-4epWYTzK.mjs} +32 -2
  41. package/dist/chunks/{messages-DgZnRQRS.mjs → messages-B-x11A7Z.mjs} +32 -2
  42. package/dist/chunks/{messages-C03I_oR-2.mjs → messages-B1mCwtgm2.mjs} +32 -2
  43. package/dist/{messages-C5hD5pSd2.mjs → chunks/messages-B2Xnfn7m2.mjs} +32 -2
  44. package/dist/chunks/{messages-B-i-d1Bc.mjs → messages-B38lWmza.mjs} +32 -2
  45. package/dist/{messages-BRC9E_sp.mjs → chunks/messages-B3VMWaO8.mjs} +32 -2
  46. package/dist/{messages-BrcgNZOJ.mjs → chunks/messages-B6n2OYKJ.mjs} +32 -2
  47. package/dist/chunks/{messages-CIugNDDO2.mjs → messages-B847kJa02.mjs} +32 -2
  48. package/dist/chunks/{messages-CH_cexo62.mjs → messages-B8nL5cPA2.mjs} +32 -2
  49. package/dist/chunks/{messages-Bdk5tBNu.mjs → messages-BHMUmX3N.mjs} +32 -2
  50. package/dist/chunks/{messages-LL3Tflph.mjs → messages-BN78Am3l2.mjs} +32 -2
  51. package/dist/chunks/{messages-BIrkzbBP.mjs → messages-BRa1Itk_.mjs} +32 -2
  52. package/dist/chunks/{messages-BtS6JWMT.mjs → messages-BleThZm5.mjs} +32 -2
  53. package/dist/{messages-DHJ1fZLL.mjs → chunks/messages-Bo2PcQoc.mjs} +32 -2
  54. package/dist/{messages-D5rnT-BC.mjs → chunks/messages-Bqk7cuZY.mjs} +32 -2
  55. package/dist/chunks/{messages-Di7mfvB8.mjs → messages-BrsB1FRe.mjs} +32 -2
  56. package/dist/{messages-CsmTziC6.mjs → chunks/messages-ByeBRHpJ.mjs} +32 -2
  57. package/dist/{messages-C7Rz00Tp.mjs → chunks/messages-C35b_LVM.mjs} +32 -2
  58. package/dist/{messages-Co26RSCV2.mjs → chunks/messages-CAH4L_Mq2.mjs} +32 -2
  59. package/dist/chunks/{messages-BMzmli1K.mjs → messages-CBa6-NHn.mjs} +32 -2
  60. package/dist/chunks/{messages-wUoSWFsJ2.mjs → messages-CCywXuFz2.mjs} +32 -2
  61. package/dist/chunks/{messages-4OvVdaG52.mjs → messages-CDZrOiaR.mjs} +32 -2
  62. package/dist/{messages-C92tAUYT2.mjs → chunks/messages-CEVMB1TN2.mjs} +32 -2
  63. package/dist/{messages-CHeucLGl2.mjs → chunks/messages-CF7WlKOY.mjs} +32 -2
  64. package/dist/{messages-D_-rh8gl.mjs → chunks/messages-CLcwMAAe.mjs} +32 -2
  65. package/dist/chunks/{messages-Df69rfTF2.mjs → messages-CTGjizuJ2.mjs} +32 -2
  66. package/dist/chunks/{messages-Ehx9YYeb2.mjs → messages-CTI66ZU52.mjs} +32 -2
  67. package/dist/{messages-C9LsEUfG.mjs → chunks/messages-CTtFH-AE.mjs} +32 -2
  68. package/dist/{messages-BB8umWL1.mjs → chunks/messages-CbilQw5o2.mjs} +32 -2
  69. package/dist/chunks/{messages-BAgTIgPH.mjs → messages-CiSiPyKp.mjs} +32 -2
  70. package/dist/chunks/{messages-5thhSeME.mjs → messages-ClD_3Kmk.mjs} +32 -2
  71. package/dist/chunks/{messages--aM83pib2.mjs → messages-CqoOMgIb2.mjs} +32 -2
  72. package/dist/chunks/{messages-EDTq4Q52.mjs → messages-Cqxe2gIN.mjs} +32 -2
  73. package/dist/chunks/{messages-SsrFJhTN.mjs → messages-CxKX6wTt.mjs} +32 -2
  74. package/dist/chunks/{messages-DUp8NnKT.mjs → messages-CyrZJ9dG.mjs} +32 -2
  75. package/dist/{messages-EIeWKoc5.mjs → chunks/messages-D1eXOdyJ.mjs} +32 -2
  76. package/dist/chunks/{messages-ohtcmr1w.mjs → messages-D2QcrPYF.mjs} +32 -2
  77. package/dist/{messages-DV29fJMD2.mjs → chunks/messages-D6EtVUzk2.mjs} +32 -2
  78. package/dist/{messages-C4HpNHfK.mjs → chunks/messages-D7qp-JvC.mjs} +32 -2
  79. package/dist/chunks/{messages-Dr-Ig3sw.mjs → messages-DAhzpf-j.mjs} +32 -2
  80. package/dist/chunks/{messages-DKChC8Qu.mjs → messages-DEVxqfH0.mjs} +32 -2
  81. package/dist/{messages-CkIRmpfZ2.mjs → chunks/messages-DHJIlD2m.mjs} +32 -2
  82. package/dist/{messages-CmoTIebG2.mjs → chunks/messages-DJ_gZym52.mjs} +32 -2
  83. package/dist/{messages-Cu7Lr1wp.mjs → chunks/messages-DMENc4jZ.mjs} +32 -2
  84. package/dist/chunks/{messages-BAC7nLeM.mjs → messages-DVDFAJaw.mjs} +32 -2
  85. package/dist/chunks/{messages-Co4WFeQ8.mjs → messages-DYQ87v5m.mjs} +32 -2
  86. package/dist/chunks/{messages-BO_jtRbZ2.mjs → messages-Dc2wd0BC2.mjs} +32 -2
  87. package/dist/{messages-F7cRf-20.mjs → chunks/messages-DlSDBHih.mjs} +32 -2
  88. package/dist/chunks/{messages-Nz8C7Znm.mjs → messages-Dmxk7uOK.mjs} +32 -2
  89. package/dist/chunks/{messages-8pf7gRQm2.mjs → messages-Dqoj0eKD2.mjs} +32 -2
  90. package/dist/{messages-BD_U2EnE.mjs → chunks/messages-DvUNk-e7.mjs} +32 -2
  91. package/dist/{messages-CTFwu5-h2.mjs → chunks/messages-DvZa_OtS2.mjs} +32 -2
  92. package/dist/chunks/{messages-CSpfBhlK2.mjs → messages-Dxa27UhV2.mjs} +32 -2
  93. package/dist/chunks/{messages-D14soBOO.mjs → messages-JQR7Z2f0.mjs} +32 -2
  94. package/dist/{messages-bRqMCja-2.mjs → chunks/messages-K453lCht2.mjs} +32 -2
  95. package/dist/{messages-B9ythxux.mjs → chunks/messages-Kqu8QyYy2.mjs} +32 -2
  96. package/dist/{messages-CWzET_9H2.mjs → chunks/messages-LGqROvB5.mjs} +32 -2
  97. package/dist/{messages-JwMkLben.mjs → chunks/messages-LiQ7ni4i2.mjs} +32 -2
  98. package/dist/{messages-B21zLG6b.mjs → chunks/messages-PDaWSrFT.mjs} +32 -2
  99. package/dist/{messages-ouO9js8Z.mjs → chunks/messages-SOew8O6I.mjs} +32 -2
  100. package/dist/{messages-SepwOOcg.mjs → chunks/messages-cjqgX4JV2.mjs} +32 -2
  101. package/dist/chunks/{messages-BARPMN7R.mjs → messages-fdO2V2XC.mjs} +32 -2
  102. package/dist/chunks/{messages-DDpgr0B1.mjs → messages-lu4RI1A3.mjs} +32 -2
  103. package/dist/{messages-D_cAZ4Ic2.mjs → chunks/messages-qt_Tsj1u2.mjs} +32 -2
  104. package/dist/chunks/{messages-BDTgiBJY2.mjs → messages-uQOWdLDn2.mjs} +32 -2
  105. package/dist/chunks/{messages-FCmAVA792.mjs → messages-xG65ERBM2.mjs} +32 -2
  106. package/dist/chunks/mk-BjookGdx.mjs +16800 -0
  107. package/dist/chunks/ml-L-NnZcp9.mjs +13558 -0
  108. package/dist/chunks/mn-OMWi7Hl_.mjs +12107 -0
  109. package/dist/chunks/mr-B6JPzITo.mjs +17324 -0
  110. package/dist/chunks/ms-CG3S-sPB.mjs +14999 -0
  111. package/dist/chunks/my-BLAmGfhT.mjs +12534 -0
  112. package/dist/chunks/native-BPcABu9z.mjs +107 -0
  113. package/dist/chunks/ne-D1JHLfYw.mjs +13248 -0
  114. package/dist/chunks/nl-Ca7Q8FnY.mjs +16693 -0
  115. package/dist/chunks/no-Coxcohcz.mjs +14767 -0
  116. package/dist/chunks/pa-CCaXqpaI.mjs +17220 -0
  117. package/dist/chunks/pl-Cl_fAZ84.mjs +17221 -0
  118. package/dist/chunks/ps-WD5qGAWy.mjs +12185 -0
  119. package/dist/chunks/pt-C4zvLfvq.mjs +16334 -0
  120. package/dist/chunks/ro-DbefHcmM.mjs +18857 -0
  121. package/dist/chunks/ru-uU1J14jd.mjs +17485 -0
  122. package/dist/chunks/sd-DKu368Ip.mjs +10889 -0
  123. package/dist/chunks/si-BsJCiPkZ.mjs +12721 -0
  124. package/dist/chunks/sk-CD-a3SN6.mjs +19081 -0
  125. package/dist/chunks/sl-CXhrPJe_.mjs +15682 -0
  126. package/dist/chunks/sq-CTctCoFQ.mjs +15278 -0
  127. package/dist/chunks/sr-BZkhBwXj.mjs +14524 -0
  128. package/dist/chunks/sv-NmRZb_xi.mjs +12855 -0
  129. package/dist/chunks/sw-Be5ik3H6.mjs +14315 -0
  130. package/dist/chunks/ta-DsXh6neL.mjs +16836 -0
  131. package/dist/chunks/te-CwpCbM8M.mjs +18588 -0
  132. package/dist/chunks/th-CcZ15OLk.mjs +13894 -0
  133. package/dist/chunks/{tools-CW1RAIvp.mjs → tools-BBmJ8jqK.mjs} +1417 -761
  134. package/dist/chunks/tr-q3bTgvhW.mjs +16318 -0
  135. package/dist/chunks/ug-919EhLsL.mjs +9816 -0
  136. package/dist/chunks/uk-aNMEzd0Y.mjs +16725 -0
  137. package/dist/chunks/ur-BwQI77sh.mjs +18051 -0
  138. package/dist/chunks/vi-Dxq806-F.mjs +14006 -0
  139. package/dist/chunks/zh-BcHuy1Ti.mjs +16334 -0
  140. package/dist/full.mjs +10 -10
  141. package/dist/locales.mjs +98 -68
  142. package/dist/{messages-qV14y_oA2.mjs → messages--PYnorLu2.mjs} +32 -2
  143. package/dist/{messages-Cy3Ne_M9.mjs → messages-B2X5gO-s.mjs} +32 -2
  144. package/dist/{chunks/messages-D1SqLcxI2.mjs → messages-BD3lSUU_.mjs} +32 -2
  145. package/dist/{chunks/messages-CIZkNCpW.mjs → messages-BFbAM_pK2.mjs} +32 -2
  146. package/dist/{chunks/messages-CIBuZccC.mjs → messages-BMUQ7kA62.mjs} +32 -2
  147. package/dist/{messages-CrWsU4Xw2.mjs → messages-BR1Hlhdc2.mjs} +32 -2
  148. package/dist/{chunks/messages-D2uBlGXR2.mjs → messages-BRJBZB-H2.mjs} +32 -2
  149. package/dist/{chunks/messages-OIelQDL32.mjs → messages-BTFWPbHk2.mjs} +32 -2
  150. package/dist/{messages-EwoT2jof.mjs → messages-BXvE3YB4.mjs} +32 -2
  151. package/dist/{messages-CqNzlpWi.mjs → messages-BZg8xxIT.mjs} +32 -2
  152. package/dist/{messages-BAZK-8Zb.mjs → messages-B_idm-Pp.mjs} +32 -2
  153. package/dist/{messages-LyzjEEIj2.mjs → messages-BcrsCh5-2.mjs} +32 -2
  154. package/dist/{chunks/messages-DPoPrTiZ.mjs → messages-Bf0eqjDJ.mjs} +32 -2
  155. package/dist/{messages-CKmmJ9tW.mjs → messages-Bgu3IB_k.mjs} +32 -2
  156. package/dist/{chunks/messages-DeLzropc.mjs → messages-Bkv2sk7Y.mjs} +32 -2
  157. package/dist/{messages-rM6YFLZH.mjs → messages-Bn77ieXA.mjs} +32 -2
  158. package/dist/{messages-CTPFrtK92.mjs → messages-Buf_f_-32.mjs} +32 -2
  159. package/dist/{messages-ouRGTAKo2.mjs → messages-BvIBkUCG2.mjs} +32 -2
  160. package/dist/{chunks/messages-DVg69mRj.mjs → messages-Bw0kGTAV.mjs} +32 -2
  161. package/dist/{chunks/messages-CWp6-Y8f2.mjs → messages-C5MKR_WT2.mjs} +32 -2
  162. package/dist/{chunks/messages-N72K1hw3.mjs → messages-C6UkeZ0y.mjs} +32 -2
  163. package/dist/{messages-CzZAfGif.mjs → messages-C9AvcT0K.mjs} +32 -2
  164. package/dist/{chunks/messages-D0IKicWg.mjs → messages-CC596_yM.mjs} +32 -2
  165. package/dist/{messages-DiSeSE8p.mjs → messages-CGD9LR8l.mjs} +32 -2
  166. package/dist/{messages-Tx25QErT.mjs → messages-CH8tCpAX.mjs} +32 -2
  167. package/dist/{chunks/messages-DfsHFEWa.mjs → messages-CIGa1j8-2.mjs} +32 -2
  168. package/dist/{messages-TI0u6Ked.mjs → messages-CJ5pCncM.mjs} +32 -2
  169. package/dist/{chunks/messages-DEGzGmEQ2.mjs → messages-CJd52qP7.mjs} +32 -2
  170. package/dist/{messages-Bm8I_Li12.mjs → messages-CWIigMhw2.mjs} +32 -2
  171. package/dist/{messages-JZhs_0pf.mjs → messages-CY_Fj715.mjs} +32 -2
  172. package/dist/{messages-BSLYh59S.mjs → messages-C_YAmOyQ.mjs} +32 -2
  173. package/dist/{messages-CIxT1nSh.mjs → messages-CjCjr0wW.mjs} +32 -2
  174. package/dist/{messages-Dr9L1psl.mjs → messages-CqbeAhjH.mjs} +32 -2
  175. package/dist/{messages-BTR3QlIb2.mjs → messages-Cr2hTJyi2.mjs} +32 -2
  176. package/dist/{chunks/messages-BKjLgO5d.mjs → messages-Cuw_6akN.mjs} +32 -2
  177. package/dist/{messages-lEyiemqU2.mjs → messages-D0KBToO12.mjs} +32 -2
  178. package/dist/{messages-1_FCq0It.mjs → messages-D2Xo8E0P.mjs} +32 -2
  179. package/dist/{messages-Djhu5RJd.mjs → messages-D5Uk_qYf.mjs} +32 -2
  180. package/dist/{messages-CHWfj4ik.mjs → messages-D7dJJ7dx.mjs} +32 -2
  181. package/dist/{chunks/messages-DmrsEYQm2.mjs → messages-D8SS2hOs2.mjs} +32 -2
  182. package/dist/{chunks/messages-DQOk-dTH.mjs → messages-D9a5iSFC2.mjs} +32 -2
  183. package/dist/{messages-BSwhWcYw.mjs → messages-DAjtVSb6.mjs} +32 -2
  184. package/dist/{messages-BZXBdD_S2.mjs → messages-DBuHaabF2.mjs} +32 -2
  185. package/dist/{chunks/messages-CuRN1_ep2.mjs → messages-DCjAu6M72.mjs} +32 -2
  186. package/dist/{messages-CKX9iXIb2.mjs → messages-DFlT_TM02.mjs} +32 -2
  187. package/dist/{chunks/messages-DlM9TmqS2.mjs → messages-DPECmAwT.mjs} +32 -2
  188. package/dist/{chunks/messages-DhGHk-Ma2.mjs → messages-DR2Lgl002.mjs} +32 -2
  189. package/dist/{messages-DDiP6yex.mjs → messages-DUPXmcZh.mjs} +32 -2
  190. package/dist/{chunks/messages-CoRQE_Jc.mjs → messages-DVReQ5EC.mjs} +32 -2
  191. package/dist/{chunks/messages-BvlSf_pu.mjs → messages-DaZkRhoM.mjs} +32 -2
  192. package/dist/{chunks/messages-B9zocutJ.mjs → messages-Dc93MR86.mjs} +32 -2
  193. package/dist/{messages-Dc7ZzqYN.mjs → messages-Dd4OTpH3.mjs} +32 -2
  194. package/dist/{chunks/messages-BsYzg2le.mjs → messages-DffJ5W5G.mjs} +32 -2
  195. package/dist/{messages-D8iCBMg7.mjs → messages-DjWAR34D.mjs} +32 -2
  196. package/dist/{messages-BzZ8LahA2.mjs → messages-DkaJV53s2.mjs} +32 -2
  197. package/dist/{chunks/messages-stUQR58d.mjs → messages-Dl7LQ_fI.mjs} +32 -2
  198. package/dist/{chunks/messages-B_fFjpX12.mjs → messages-DlEdd01l2.mjs} +32 -2
  199. package/dist/{chunks/messages-5ohIWynJ.mjs → messages-ItP2i78l.mjs} +32 -2
  200. package/dist/{messages-DDJOu049.mjs → messages-Oqm_fCLp.mjs} +32 -2
  201. package/dist/{chunks/messages-DrXNb1gu.mjs → messages-UpoT1AS6.mjs} +32 -2
  202. package/dist/{chunks/messages-COkQfKa72.mjs → messages-Z8dpjy_K2.mjs} +32 -2
  203. package/dist/{messages-mVLfVtQX2.mjs → messages-ierNgYE02.mjs} +32 -2
  204. package/dist/{messages-DToWAonn2.mjs → messages-ikSI7M732.mjs} +32 -2
  205. package/dist/{chunks/messages-DKCoHa5D.mjs → messages-olK9oNtb.mjs} +32 -2
  206. package/dist/{messages-CsnglxbV2.mjs → messages-rgXeOQMh2.mjs} +32 -2
  207. package/dist/{chunks/messages-CDJLoStb.mjs → messages-syo0DKqf.mjs} +32 -2
  208. package/dist/{chunks/messages-BgaGPFuy2.mjs → messages-tFDVfvWL.mjs} +32 -2
  209. package/dist/react.mjs +2 -2
  210. package/dist/tools.mjs +3 -3
  211. package/dist/vendor.LICENSE.txt +34 -0
  212. package/package.json +3 -1
  213. package/src/components/i18n/locales/am/messages.json +32 -2
  214. package/src/components/i18n/locales/ar/messages.json +32 -2
  215. package/src/components/i18n/locales/az/messages.json +32 -2
  216. package/src/components/i18n/locales/bg/messages.json +32 -2
  217. package/src/components/i18n/locales/bn/messages.json +32 -2
  218. package/src/components/i18n/locales/bs/messages.json +32 -2
  219. package/src/components/i18n/locales/cs/messages.json +32 -2
  220. package/src/components/i18n/locales/da/messages.json +32 -2
  221. package/src/components/i18n/locales/de/messages.json +32 -2
  222. package/src/components/i18n/locales/dv/messages.json +32 -2
  223. package/src/components/i18n/locales/el/messages.json +32 -2
  224. package/src/components/i18n/locales/en/messages.json +31 -1
  225. package/src/components/i18n/locales/es/messages.json +32 -2
  226. package/src/components/i18n/locales/et/messages.json +32 -2
  227. package/src/components/i18n/locales/fa/messages.json +32 -2
  228. package/src/components/i18n/locales/fi/messages.json +32 -2
  229. package/src/components/i18n/locales/fil/messages.json +32 -2
  230. package/src/components/i18n/locales/fr/messages.json +32 -2
  231. package/src/components/i18n/locales/gu/messages.json +32 -2
  232. package/src/components/i18n/locales/he/messages.json +32 -2
  233. package/src/components/i18n/locales/hi/messages.json +32 -2
  234. package/src/components/i18n/locales/hr/messages.json +32 -2
  235. package/src/components/i18n/locales/hu/messages.json +32 -2
  236. package/src/components/i18n/locales/hy/messages.json +32 -2
  237. package/src/components/i18n/locales/id/messages.json +32 -2
  238. package/src/components/i18n/locales/it/messages.json +32 -2
  239. package/src/components/i18n/locales/ja/messages.json +32 -2
  240. package/src/components/i18n/locales/ka/messages.json +32 -2
  241. package/src/components/i18n/locales/km/messages.json +32 -2
  242. package/src/components/i18n/locales/kn/messages.json +32 -2
  243. package/src/components/i18n/locales/ko/messages.json +32 -2
  244. package/src/components/i18n/locales/ku/messages.json +32 -2
  245. package/src/components/i18n/locales/lo/messages.json +32 -2
  246. package/src/components/i18n/locales/lt/messages.json +32 -2
  247. package/src/components/i18n/locales/lv/messages.json +32 -2
  248. package/src/components/i18n/locales/mk/messages.json +32 -2
  249. package/src/components/i18n/locales/ml/messages.json +32 -2
  250. package/src/components/i18n/locales/mn/messages.json +32 -2
  251. package/src/components/i18n/locales/mr/messages.json +32 -2
  252. package/src/components/i18n/locales/ms/messages.json +32 -2
  253. package/src/components/i18n/locales/my/messages.json +32 -2
  254. package/src/components/i18n/locales/ne/messages.json +32 -2
  255. package/src/components/i18n/locales/nl/messages.json +32 -2
  256. package/src/components/i18n/locales/no/messages.json +32 -2
  257. package/src/components/i18n/locales/pa/messages.json +32 -2
  258. package/src/components/i18n/locales/pl/messages.json +32 -2
  259. package/src/components/i18n/locales/ps/messages.json +32 -2
  260. package/src/components/i18n/locales/pt/messages.json +32 -2
  261. package/src/components/i18n/locales/ro/messages.json +32 -2
  262. package/src/components/i18n/locales/ru/messages.json +32 -2
  263. package/src/components/i18n/locales/sd/messages.json +32 -2
  264. package/src/components/i18n/locales/si/messages.json +32 -2
  265. package/src/components/i18n/locales/sk/messages.json +32 -2
  266. package/src/components/i18n/locales/sl/messages.json +32 -2
  267. package/src/components/i18n/locales/sq/messages.json +32 -2
  268. package/src/components/i18n/locales/sr/messages.json +32 -2
  269. package/src/components/i18n/locales/sv/messages.json +32 -2
  270. package/src/components/i18n/locales/sw/messages.json +32 -2
  271. package/src/components/i18n/locales/ta/messages.json +32 -2
  272. package/src/components/i18n/locales/te/messages.json +32 -2
  273. package/src/components/i18n/locales/th/messages.json +32 -2
  274. package/src/components/i18n/locales/tr/messages.json +32 -2
  275. package/src/components/i18n/locales/ug/messages.json +32 -2
  276. package/src/components/i18n/locales/uk/messages.json +32 -2
  277. package/src/components/i18n/locales/ur/messages.json +32 -2
  278. package/src/components/i18n/locales/vi/messages.json +32 -2
  279. package/src/components/i18n/locales/yi/messages.json +32 -2
  280. package/src/components/i18n/locales/zh/messages.json +32 -2
  281. package/src/components/icons/index.ts +98 -0
  282. package/src/components/modules/api/i18n.ts +1 -0
  283. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +22 -5
  284. package/src/components/modules/blockManager/operations.ts +1 -0
  285. package/src/components/modules/toolbar/index.ts +102 -7
  286. package/src/components/modules/toolbar/plus-button.ts +17 -5
  287. package/src/components/modules/uiControllers/controllers/blockHover.ts +36 -9
  288. package/src/components/modules/uiControllers/handlers/touch.ts +20 -8
  289. package/src/components/tools/factory.ts +4 -0
  290. package/src/components/ui/toolbox.ts +38 -3
  291. package/src/components/utils/popover/popover-desktop.ts +48 -71
  292. package/src/components/utils/popover/popover-position.ts +105 -0
  293. package/src/stories/MarkerColors.stories.ts +8 -1
  294. package/src/styles/main.css +121 -17
  295. package/src/tools/callout/block-operations.ts +17 -0
  296. package/src/tools/callout/callout-keyboard.ts +45 -0
  297. package/src/tools/callout/constants.ts +30 -0
  298. package/src/tools/callout/dom-builder.ts +53 -0
  299. package/src/tools/callout/emoji-picker/emoji-data.ts +105 -0
  300. package/src/tools/callout/emoji-picker/emoji-locale.ts +165 -0
  301. package/src/tools/callout/emoji-picker/index.ts +762 -0
  302. package/src/tools/callout/emoji-picker/locales/am.json +1 -0
  303. package/src/tools/callout/emoji-picker/locales/ar.json +1 -0
  304. package/src/tools/callout/emoji-picker/locales/az.json +1 -0
  305. package/src/tools/callout/emoji-picker/locales/bg.json +1 -0
  306. package/src/tools/callout/emoji-picker/locales/bn.json +1 -0
  307. package/src/tools/callout/emoji-picker/locales/bs.json +1 -0
  308. package/src/tools/callout/emoji-picker/locales/cs.json +1 -0
  309. package/src/tools/callout/emoji-picker/locales/da.json +1 -0
  310. package/src/tools/callout/emoji-picker/locales/de.json +1 -0
  311. package/src/tools/callout/emoji-picker/locales/el.json +1 -0
  312. package/src/tools/callout/emoji-picker/locales/es.json +1 -0
  313. package/src/tools/callout/emoji-picker/locales/et.json +1 -0
  314. package/src/tools/callout/emoji-picker/locales/fa.json +1 -0
  315. package/src/tools/callout/emoji-picker/locales/fi.json +1 -0
  316. package/src/tools/callout/emoji-picker/locales/fil.json +1 -0
  317. package/src/tools/callout/emoji-picker/locales/fr.json +1 -0
  318. package/src/tools/callout/emoji-picker/locales/gu.json +1 -0
  319. package/src/tools/callout/emoji-picker/locales/he.json +1 -0
  320. package/src/tools/callout/emoji-picker/locales/hi.json +1 -0
  321. package/src/tools/callout/emoji-picker/locales/hr.json +1 -0
  322. package/src/tools/callout/emoji-picker/locales/hu.json +1 -0
  323. package/src/tools/callout/emoji-picker/locales/hy.json +1 -0
  324. package/src/tools/callout/emoji-picker/locales/id.json +1 -0
  325. package/src/tools/callout/emoji-picker/locales/it.json +1 -0
  326. package/src/tools/callout/emoji-picker/locales/ja.json +1 -0
  327. package/src/tools/callout/emoji-picker/locales/ka.json +1 -0
  328. package/src/tools/callout/emoji-picker/locales/km.json +1 -0
  329. package/src/tools/callout/emoji-picker/locales/kn.json +1 -0
  330. package/src/tools/callout/emoji-picker/locales/ko.json +1 -0
  331. package/src/tools/callout/emoji-picker/locales/ku.json +1 -0
  332. package/src/tools/callout/emoji-picker/locales/lo.json +1 -0
  333. package/src/tools/callout/emoji-picker/locales/lt.json +1 -0
  334. package/src/tools/callout/emoji-picker/locales/lv.json +1 -0
  335. package/src/tools/callout/emoji-picker/locales/mk.json +1 -0
  336. package/src/tools/callout/emoji-picker/locales/ml.json +1 -0
  337. package/src/tools/callout/emoji-picker/locales/mn.json +1 -0
  338. package/src/tools/callout/emoji-picker/locales/mr.json +1 -0
  339. package/src/tools/callout/emoji-picker/locales/ms.json +1 -0
  340. package/src/tools/callout/emoji-picker/locales/my.json +1 -0
  341. package/src/tools/callout/emoji-picker/locales/ne.json +1 -0
  342. package/src/tools/callout/emoji-picker/locales/nl.json +1 -0
  343. package/src/tools/callout/emoji-picker/locales/no.json +1 -0
  344. package/src/tools/callout/emoji-picker/locales/pa.json +1 -0
  345. package/src/tools/callout/emoji-picker/locales/pl.json +1 -0
  346. package/src/tools/callout/emoji-picker/locales/ps.json +1 -0
  347. package/src/tools/callout/emoji-picker/locales/pt.json +1 -0
  348. package/src/tools/callout/emoji-picker/locales/ro.json +1 -0
  349. package/src/tools/callout/emoji-picker/locales/ru.json +1 -0
  350. package/src/tools/callout/emoji-picker/locales/sd.json +1 -0
  351. package/src/tools/callout/emoji-picker/locales/si.json +1 -0
  352. package/src/tools/callout/emoji-picker/locales/sk.json +1 -0
  353. package/src/tools/callout/emoji-picker/locales/sl.json +1 -0
  354. package/src/tools/callout/emoji-picker/locales/sq.json +1 -0
  355. package/src/tools/callout/emoji-picker/locales/sr.json +1 -0
  356. package/src/tools/callout/emoji-picker/locales/sv.json +1 -0
  357. package/src/tools/callout/emoji-picker/locales/sw.json +1 -0
  358. package/src/tools/callout/emoji-picker/locales/ta.json +1 -0
  359. package/src/tools/callout/emoji-picker/locales/te.json +1 -0
  360. package/src/tools/callout/emoji-picker/locales/th.json +1 -0
  361. package/src/tools/callout/emoji-picker/locales/tr.json +1 -0
  362. package/src/tools/callout/emoji-picker/locales/ug.json +1 -0
  363. package/src/tools/callout/emoji-picker/locales/uk.json +1 -0
  364. package/src/tools/callout/emoji-picker/locales/ur.json +1 -0
  365. package/src/tools/callout/emoji-picker/locales/vi.json +1 -0
  366. package/src/tools/callout/emoji-picker/locales/zh.json +1 -0
  367. package/src/tools/callout/index.ts +338 -0
  368. package/src/tools/callout/types.ts +11 -0
  369. package/src/tools/header/header-toggle-keyboard.ts +3 -2
  370. package/src/tools/header/index.ts +1 -1
  371. package/src/tools/index.ts +2 -0
  372. package/src/tools/toggle/index.ts +1 -1
  373. package/src/tools/toggle/toggle-keyboard.ts +29 -1
  374. package/types/api/i18n.d.ts +7 -0
  375. package/types/tools-entry.d.ts +0 -4
  376. /package/dist/chunks/{i18next-DymC16cN.mjs → i18next-Ch0gVA3V.mjs} +0 -0
  377. /package/dist/chunks/{notifier-BqYxvxnV.mjs → notifier-Butv4Dvo.mjs} +0 -0
@@ -0,0 +1,762 @@
1
+ // src/tools/callout/emoji-picker/index.ts
2
+
3
+ import { loadEmojiData, searchEmojis, groupEmojisByCategory, CURATED_CALLOUT_EMOJIS, type ProcessedEmoji } from './emoji-data';
4
+ import { loadEmojiLocale, type EmojiLocaleData } from './emoji-locale';
5
+ import { onHover } from '../../../components/utils/tooltip';
6
+ import {
7
+ REMOVE_EMOJI_KEY, FILTER_EMOJIS_KEY, CALLOUT_EMOJI_CATEGORY_KEY, NO_EMOJIS_FOUND_KEY, PICK_RANDOM_KEY, SKIN_TONE_KEY,
8
+ EMOJI_CATEGORY_PEOPLE_KEY, EMOJI_CATEGORY_NATURE_KEY, EMOJI_CATEGORY_FOOD_KEY, EMOJI_CATEGORY_ACTIVITY_KEY,
9
+ EMOJI_CATEGORY_TRAVEL_KEY, EMOJI_CATEGORY_OBJECTS_KEY, EMOJI_CATEGORY_SYMBOLS_KEY, EMOJI_CATEGORY_FLAGS_KEY,
10
+ } from '../constants';
11
+
12
+ /** Maps emoji-mart category IDs to their i18n key. */
13
+ const CATEGORY_I18N_KEYS: Readonly<Record<string, string>> = {
14
+ callout: CALLOUT_EMOJI_CATEGORY_KEY,
15
+ people: EMOJI_CATEGORY_PEOPLE_KEY,
16
+ nature: EMOJI_CATEGORY_NATURE_KEY,
17
+ foods: EMOJI_CATEGORY_FOOD_KEY,
18
+ activity: EMOJI_CATEGORY_ACTIVITY_KEY,
19
+ places: EMOJI_CATEGORY_TRAVEL_KEY,
20
+ objects: EMOJI_CATEGORY_OBJECTS_KEY,
21
+ symbols: EMOJI_CATEGORY_SYMBOLS_KEY,
22
+ flags: EMOJI_CATEGORY_FLAGS_KEY,
23
+ };
24
+ import {
25
+ IconSearch,
26
+ IconEmojiStar,
27
+ IconEmojiSmile,
28
+ IconEmojiSprout,
29
+ IconEmojiUtensils,
30
+ IconEmojiBall,
31
+ IconEmojiGlobe,
32
+ IconEmojiLightbulb,
33
+ IconEmojiHash,
34
+ IconEmojiFlag,
35
+ } from '../../../components/icons';
36
+
37
+ interface I18n {
38
+ t: (key: string) => string;
39
+ }
40
+
41
+ interface EmojiPickerOptions {
42
+ onSelect: (native: string) => void;
43
+ onRemove: () => void;
44
+ i18n: I18n;
45
+ locale: string;
46
+ }
47
+
48
+ /** SVG icon for each emoji category (display order). */
49
+ const CATEGORY_NAV: ReadonlyArray<readonly [id: string, icon: string]> = [
50
+ ['callout', IconEmojiStar],
51
+ ['people', IconEmojiSmile],
52
+ ['nature', IconEmojiSprout],
53
+ ['foods', IconEmojiUtensils],
54
+ ['activity', IconEmojiBall],
55
+ ['places', IconEmojiGlobe],
56
+ ['objects', IconEmojiLightbulb],
57
+ ['symbols', IconEmojiHash],
58
+ ['flags', IconEmojiFlag],
59
+ ];
60
+
61
+ /** Raised-hand emoji for each skin tone (default + 5 Fitzpatrick modifiers). */
62
+ const SKIN_TONE_HANDS: readonly string[] = [
63
+ '✋', '✋🏻', '✋🏼', '✋🏽', '✋🏾', '✋🏿',
64
+ ];
65
+
66
+ /** Dice SVG for the random button. */
67
+ const ICON_DICE = '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="1.5" width="11" height="11" rx="2" stroke="currentColor" stroke-width="1.2"/><circle cx="4.5" cy="4.5" r=".9" fill="currentColor"/><circle cx="7" cy="7" r=".9" fill="currentColor"/><circle cx="9.5" cy="9.5" r=".9" fill="currentColor"/></svg>';
68
+
69
+ export class EmojiPicker {
70
+ private readonly onSelect: (native: string) => void;
71
+ private readonly onRemove: () => void;
72
+ private readonly i18n: I18n;
73
+ private readonly _locale: string;
74
+ private _localeData: EmojiLocaleData | null = null;
75
+
76
+ private _element: HTMLElement;
77
+ private _body: HTMLElement;
78
+ private _nav: HTMLElement;
79
+ private _filterInput: HTMLInputElement;
80
+ private _open = false;
81
+ private _allEmojis: ProcessedEmoji[] = [];
82
+ private _skinTone = 0;
83
+ private _showingEmptyState = false;
84
+
85
+ private _anchorEl: HTMLElement | null = null;
86
+ private _backdrop: HTMLElement | null = null;
87
+ private _savedOverflow = '';
88
+
89
+ /** Maps category id -> nav button for active-state management. */
90
+ private _navButtons = new Map<string, HTMLButtonElement>();
91
+ /** Maps category id -> section element for scroll targeting. */
92
+ private _sectionEls = new Map<string, HTMLElement>();
93
+ /** Currently highlighted category in the nav bar. */
94
+ private _activeNavId = '';
95
+ /** rAF handle for scroll-based nav updates. */
96
+ private _navRafId = 0;
97
+ /** Skin tone selector buttons for visual updates. */
98
+ private _skinToneButtons: HTMLButtonElement[] = [];
99
+ private _skinToneToggle!: HTMLButtonElement;
100
+ private _skinTonePopover!: HTMLElement;
101
+
102
+ constructor(options: EmojiPickerOptions) {
103
+ this.onSelect = options.onSelect;
104
+ this.onRemove = options.onRemove;
105
+ this.i18n = options.i18n;
106
+ this._locale = options.locale;
107
+ this._element = this.buildElement();
108
+
109
+ const body = this._element.querySelector<HTMLElement>('[data-emoji-picker-body]');
110
+ const nav = this._element.querySelector<HTMLElement>('[data-emoji-picker-nav]');
111
+ const filterInput = this._element.querySelector<HTMLInputElement>('input[type="text"]');
112
+
113
+ if (body === null || nav === null || filterInput === null) {
114
+ throw new Error('EmojiPicker: failed to build required elements');
115
+ }
116
+
117
+ this._body = body;
118
+ this._nav = nav;
119
+ this._filterInput = filterInput;
120
+
121
+ this._body.addEventListener('scroll', () => {
122
+ cancelAnimationFrame(this._navRafId);
123
+ this._navRafId = requestAnimationFrame(() => this.updateActiveNav());
124
+ }, { passive: true });
125
+ }
126
+
127
+ public getElement(): HTMLElement {
128
+ return this._element;
129
+ }
130
+
131
+ public isOpen(): boolean {
132
+ return this._open;
133
+ }
134
+
135
+ public async open(anchor: HTMLElement): Promise<void> {
136
+ this._anchorEl = anchor;
137
+ this._open = true;
138
+ this._filterInput.value = '';
139
+ this._element.setAttribute('data-theme', this.resolveTheme());
140
+
141
+ if (this._allEmojis.length === 0) {
142
+ this._allEmojis = await loadEmojiData();
143
+ }
144
+
145
+ if (this._locale !== 'en' && this._localeData === null) {
146
+ const localeData = await loadEmojiLocale(this._locale);
147
+
148
+ if (localeData !== null) {
149
+ this._localeData = localeData;
150
+ }
151
+ }
152
+
153
+ this.renderEmojiGrid(this._allEmojis);
154
+ this.showBackdrop();
155
+
156
+ // Unhide before positioning so getBoundingClientRect returns real dimensions
157
+ this._element.style.animation = 'none';
158
+ this._element.hidden = false;
159
+ this.position(anchor);
160
+
161
+ // Replay the opening animation
162
+ void this._element.offsetHeight;
163
+ this._element.style.animation = '';
164
+
165
+ this._filterInput.focus();
166
+ }
167
+
168
+ public close(): void {
169
+ this._open = false;
170
+ this._element.hidden = true;
171
+ this.closeSkinTonePopover();
172
+ this.removeBackdrop();
173
+ this._anchorEl?.focus();
174
+ }
175
+
176
+ // ─── DOM Construction ─────────────────────────────────────
177
+
178
+ private buildElement(): HTMLElement {
179
+ const el = document.createElement('div');
180
+
181
+ el.setAttribute('data-blok-emoji-picker', '');
182
+ el.className = [
183
+ 'fixed z-50 w-[400px] overflow-hidden rounded-xl',
184
+ 'border border-neutral-200/70 bg-white shadow-2xl',
185
+ 'theme-dark:border-neutral-700/50 theme-dark:bg-neutral-900',
186
+ ].join(' ');
187
+ el.hidden = true;
188
+
189
+ // Header: search input + random button + remove button
190
+ const header = document.createElement('div');
191
+ header.className = 'flex items-center gap-2.5 px-3 pt-3 pb-2';
192
+
193
+ const searchWrapper = document.createElement('div');
194
+ searchWrapper.className = 'relative flex-1 min-w-0';
195
+
196
+ const iconSpan = document.createElement('span');
197
+ iconSpan.className = [
198
+ 'pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 flex items-center',
199
+ 'text-neutral-400 theme-dark:text-neutral-500 [&>svg]:w-[16px] [&>svg]:h-[16px]',
200
+ ].join(' ');
201
+ iconSpan.innerHTML = IconSearch;
202
+
203
+ const input = document.createElement('input');
204
+ input.type = 'text';
205
+ input.placeholder = this.i18n.t(FILTER_EMOJIS_KEY);
206
+ input.className = [
207
+ 'w-full text-[13px] rounded-lg py-[7px] pl-8 pr-3 outline-hidden',
208
+ 'bg-neutral-100 text-neutral-800 placeholder:text-neutral-400',
209
+ 'theme-dark:bg-neutral-800 theme-dark:text-neutral-200 theme-dark:placeholder:text-neutral-500',
210
+ 'focus:ring-2 focus:ring-neutral-300/60 theme-dark:focus:ring-neutral-600/60',
211
+ 'transition-shadow duration-150',
212
+ ].join(' ');
213
+ input.addEventListener('input', () => this.handleFilterChange(input.value));
214
+
215
+ searchWrapper.appendChild(iconSpan);
216
+ searchWrapper.appendChild(input);
217
+
218
+ // Skin tone hand toggle (separate button next to search input)
219
+ const skinToneWrapper = document.createElement('div');
220
+
221
+ skinToneWrapper.className = 'relative flex-shrink-0';
222
+
223
+ const skinToggle = document.createElement('button');
224
+
225
+ skinToggle.type = 'button';
226
+ skinToggle.setAttribute('data-emoji-picker-skin-toggle', '');
227
+ skinToggle.setAttribute('aria-label', this.i18n.t(SKIN_TONE_KEY));
228
+ skinToggle.title = this.i18n.t(SKIN_TONE_KEY);
229
+ skinToggle.className = [
230
+ 'w-[28px] h-[28px] flex items-center justify-center rounded-lg',
231
+ 'text-[14px] leading-none cursor-pointer select-none',
232
+ 'hover:bg-neutral-100 theme-dark:hover:bg-neutral-800',
233
+ 'active:scale-90 transition-all duration-100',
234
+ ].join(' ');
235
+ skinToggle.textContent = SKIN_TONE_HANDS[this._skinTone];
236
+ skinToggle.addEventListener('click', () => this.toggleSkinTonePopover());
237
+ this._skinToneToggle = skinToggle;
238
+ skinToneWrapper.appendChild(skinToggle);
239
+
240
+ // Skin tone popover (hidden by default, anchored below toggle)
241
+ this._skinTonePopover = this.buildSkinTonePopover();
242
+ skinToneWrapper.appendChild(this._skinTonePopover);
243
+
244
+ // Random button
245
+ const randomBtn = document.createElement('button');
246
+ randomBtn.type = 'button';
247
+ randomBtn.setAttribute('data-emoji-picker-random', '');
248
+ randomBtn.setAttribute('aria-label', this.i18n.t(PICK_RANDOM_KEY));
249
+ randomBtn.title = this.i18n.t(PICK_RANDOM_KEY);
250
+ randomBtn.className = [
251
+ 'flex-shrink-0 w-[34px] h-[34px] flex items-center justify-center rounded-lg',
252
+ 'text-neutral-400 hover:bg-neutral-100 hover:text-neutral-600',
253
+ 'theme-dark:hover:bg-neutral-800 theme-dark:hover:text-neutral-300',
254
+ 'transition-colors duration-100 cursor-pointer',
255
+ ].join(' ');
256
+ randomBtn.innerHTML = ICON_DICE;
257
+ randomBtn.addEventListener('click', () => this.pickRandom());
258
+ onHover(randomBtn, this.i18n.t(PICK_RANDOM_KEY), { placement: 'bottom' });
259
+
260
+ // Remove button
261
+ const removeBtn = document.createElement('button');
262
+ removeBtn.type = 'button';
263
+ removeBtn.setAttribute('data-emoji-picker-remove', '');
264
+ removeBtn.setAttribute('aria-label', this.i18n.t(REMOVE_EMOJI_KEY));
265
+ removeBtn.title = this.i18n.t(REMOVE_EMOJI_KEY);
266
+ removeBtn.className = [
267
+ 'flex-shrink-0 w-[34px] h-[34px] flex items-center justify-center rounded-lg',
268
+ 'text-neutral-400 hover:bg-neutral-100 hover:text-neutral-600',
269
+ 'theme-dark:hover:bg-neutral-800 theme-dark:hover:text-neutral-300',
270
+ 'transition-colors duration-100 cursor-pointer',
271
+ ].join(' ');
272
+ removeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3.5 3.5l7 7M10.5 3.5l-7 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>';
273
+ removeBtn.addEventListener('click', () => {
274
+ this.onRemove();
275
+ this.close();
276
+ });
277
+ onHover(removeBtn, this.i18n.t(REMOVE_EMOJI_KEY), { placement: 'bottom' });
278
+
279
+ const actionGroup = document.createElement('div');
280
+ actionGroup.className = 'flex items-center gap-1';
281
+ actionGroup.appendChild(randomBtn);
282
+ actionGroup.appendChild(removeBtn);
283
+
284
+ header.appendChild(searchWrapper);
285
+ header.appendChild(skinToneWrapper);
286
+ header.appendChild(actionGroup);
287
+ el.appendChild(header);
288
+
289
+ // Scrollable body
290
+ const body = document.createElement('div');
291
+ body.setAttribute('data-emoji-picker-body', '');
292
+ body.className = 'overflow-y-auto max-h-[260px] px-1.5 pb-2';
293
+ el.appendChild(body);
294
+
295
+ // Category navigation bar (below emoji grid)
296
+ const nav = document.createElement('div');
297
+ nav.setAttribute('data-emoji-picker-nav', '');
298
+ nav.className = [
299
+ 'flex items-center gap-1 px-2 pt-1 pb-1',
300
+ 'border-t border-neutral-100 theme-dark:border-neutral-800',
301
+ ].join(' ');
302
+ el.appendChild(nav);
303
+
304
+ // Close skin tone popover on click outside toggle/popover
305
+ el.addEventListener('mousedown', (e: MouseEvent) => {
306
+ const target = e.target as Node;
307
+
308
+ if (!this._skinTonePopover.hidden
309
+ && !this._skinTonePopover.contains(target)
310
+ && !this._skinToneToggle.contains(target)) {
311
+ this.closeSkinTonePopover();
312
+ }
313
+ });
314
+
315
+ // Keyboard handler
316
+ el.addEventListener('keydown', (e: KeyboardEvent) => {
317
+ if (e.key === 'Escape') {
318
+ if (!this._skinTonePopover.hidden) {
319
+ this.closeSkinTonePopover();
320
+
321
+ return;
322
+ }
323
+
324
+ this.close();
325
+ }
326
+ });
327
+
328
+ return el;
329
+ }
330
+
331
+ // ─── Skin Tone Selector ─────────────────────────────────────
332
+
333
+ private buildSkinTonePopover(): HTMLElement {
334
+ const popover = document.createElement('div');
335
+
336
+ popover.setAttribute('data-emoji-picker-skin-tone', '');
337
+ popover.className = [
338
+ 'absolute right-0 top-full mt-1.5 z-20',
339
+ 'flex items-center gap-0.5 p-1 rounded-xl',
340
+ 'bg-white border border-neutral-200/70 shadow-lg',
341
+ 'theme-dark:bg-neutral-800 theme-dark:border-neutral-700/50',
342
+ ].join(' ');
343
+ popover.hidden = true;
344
+
345
+ this._skinToneButtons = [];
346
+
347
+ for (const [index, hand] of SKIN_TONE_HANDS.entries()) {
348
+ const btn = document.createElement('button');
349
+
350
+ btn.type = 'button';
351
+ btn.textContent = hand;
352
+ btn.setAttribute('aria-label', `${this.i18n.t(SKIN_TONE_KEY)} ${index + 1}`);
353
+ btn.className = [
354
+ 'w-[32px] h-[32px] flex items-center justify-center rounded-lg',
355
+ 'text-[1.2rem] leading-none cursor-pointer select-none',
356
+ 'hover:bg-neutral-100 theme-dark:hover:bg-neutral-700',
357
+ 'active:scale-90 transition-all duration-100',
358
+ ].join(' ');
359
+ this.applySkinToneActiveStyle(btn, index === this._skinTone);
360
+ btn.addEventListener('click', () => {
361
+ this.setSkinTone(index);
362
+ this.closeSkinTonePopover();
363
+ });
364
+ popover.appendChild(btn);
365
+ this._skinToneButtons.push(btn);
366
+ }
367
+
368
+ return popover;
369
+ }
370
+
371
+ private applySkinToneActiveStyle(btn: HTMLButtonElement, active: boolean): void {
372
+ const classes = ['bg-neutral-100', 'theme-dark:bg-neutral-700', 'ring-2', 'ring-neutral-300/60', 'theme-dark:ring-neutral-600/60'];
373
+
374
+ if (active) {
375
+ btn.classList.add(...classes);
376
+ } else {
377
+ btn.classList.remove(...classes);
378
+ }
379
+ }
380
+
381
+ private toggleSkinTonePopover(): void {
382
+ this._skinTonePopover.hidden = !this._skinTonePopover.hidden;
383
+ this.updateSkinToneToggleActive();
384
+ }
385
+
386
+ private closeSkinTonePopover(): void {
387
+ this._skinTonePopover.hidden = true;
388
+ this.updateSkinToneToggleActive();
389
+ }
390
+
391
+ private updateSkinToneToggleActive(): void {
392
+ const active = !this._skinTonePopover.hidden;
393
+ const classes = ['bg-neutral-100', 'theme-dark:bg-neutral-700'];
394
+
395
+ if (active) {
396
+ this._skinToneToggle.classList.add(...classes);
397
+ } else {
398
+ this._skinToneToggle.classList.remove(...classes);
399
+ }
400
+ }
401
+
402
+ private setSkinTone(index: number): void {
403
+ this._skinTone = index;
404
+
405
+ // Update the hand toggle to reflect current skin tone
406
+ this._skinToneToggle.textContent = SKIN_TONE_HANDS[index];
407
+
408
+ // Update skin tone popover button visuals
409
+ for (const [i, btn] of this._skinToneButtons.entries()) {
410
+ this.applySkinToneActiveStyle(btn, i === index);
411
+ }
412
+
413
+ // Update all emoji buttons in-place (no re-render, preserves scroll)
414
+ const buttons = Array.from(this._body.querySelectorAll<HTMLButtonElement>('[data-emoji-native]'));
415
+
416
+ for (const btn of buttons) {
417
+ const native = btn.getAttribute('data-emoji-native');
418
+
419
+ if (native === null) {
420
+ continue;
421
+ }
422
+
423
+ const emoji = this._allEmojis.find(e => e.native === native);
424
+
425
+ if (emoji !== undefined) {
426
+ btn.textContent = this.getSkinnedNative(emoji);
427
+ }
428
+ }
429
+ }
430
+
431
+ // ─── Random ─────────────────────────────────────────────────
432
+
433
+ private pickRandom(): void {
434
+ if (this._allEmojis.length === 0) {
435
+ return;
436
+ }
437
+
438
+ const randomIndex = Math.floor(Math.random() * this._allEmojis.length);
439
+ const emoji = this._allEmojis[randomIndex];
440
+
441
+ if (emoji === undefined) {
442
+ return;
443
+ }
444
+
445
+ this.onSelect(this.getSkinnedNative(emoji));
446
+ }
447
+
448
+ // ─── Category Navigation ──────────────────────────────────
449
+
450
+ private buildCategoryNav(visibleCategories: Set<string>): void {
451
+ this._nav.innerHTML = '';
452
+ this._navButtons.clear();
453
+ this._activeNavId = '';
454
+
455
+ for (const [catId, catIcon] of CATEGORY_NAV) {
456
+ if (!visibleCategories.has(catId)) {
457
+ continue;
458
+ }
459
+
460
+ const catLabel = this.translateCategory(catId);
461
+ const btn = document.createElement('button');
462
+ btn.type = 'button';
463
+ btn.innerHTML = catIcon;
464
+ btn.title = catLabel;
465
+ btn.setAttribute('aria-label', catLabel);
466
+ btn.setAttribute('data-emoji-nav', catId);
467
+ btn.className = [
468
+ 'flex-1 h-[36px] flex items-center justify-center',
469
+ 'rounded-lg cursor-pointer opacity-50',
470
+ 'text-neutral-500 theme-dark:text-neutral-400',
471
+ '[&>svg]:w-[20px] [&>svg]:h-[20px]',
472
+ 'hover:opacity-100 hover:bg-neutral-100',
473
+ 'theme-dark:hover:bg-neutral-800',
474
+ 'transition-all duration-100',
475
+ ].join(' ');
476
+ btn.addEventListener('click', () => this.scrollToSection(catId));
477
+
478
+ this._nav.appendChild(btn);
479
+ this._navButtons.set(catId, btn);
480
+ }
481
+ }
482
+
483
+ private scrollToSection(categoryId: string): void {
484
+ const section = this._sectionEls.get(categoryId);
485
+
486
+ if (section === undefined) {
487
+ return;
488
+ }
489
+
490
+ const bodyRect = this._body.getBoundingClientRect();
491
+ const sectionRect = section.getBoundingClientRect();
492
+
493
+ this._body.scrollTo({
494
+ top: this._body.scrollTop + (sectionRect.top - bodyRect.top),
495
+ behavior: 'smooth',
496
+ });
497
+ }
498
+
499
+ private updateActiveNav(): void {
500
+ const bodyTop = this._body.getBoundingClientRect().top;
501
+ const activeId = [...this._sectionEls.entries()]
502
+ .filter(([, el]) => el.getBoundingClientRect().top - bodyTop <= 20)
503
+ .reduce<string>((_, [id]) => id, '');
504
+
505
+ if (activeId === this._activeNavId) {
506
+ return;
507
+ }
508
+
509
+ this._activeNavId = activeId;
510
+
511
+ for (const [id, btn] of this._navButtons) {
512
+ if (id === activeId) {
513
+ btn.classList.remove('opacity-50');
514
+ btn.classList.add('opacity-100', 'bg-neutral-100', 'theme-dark:bg-neutral-800');
515
+ } else {
516
+ btn.classList.remove('opacity-100', 'bg-neutral-100', 'theme-dark:bg-neutral-800');
517
+ btn.classList.add('opacity-50');
518
+ }
519
+ }
520
+ }
521
+
522
+ // ─── Rendering ────────────────────────────────────────────
523
+
524
+ private getSkinnedNative(emoji: ProcessedEmoji): string {
525
+ return emoji.skins[this._skinTone] ?? emoji.native;
526
+ }
527
+
528
+ private handleFilterChange(query: string): void {
529
+ if (query.trim() === '') {
530
+ this._nav.hidden = false;
531
+ this.renderEmojiGrid(this._allEmojis);
532
+
533
+ return;
534
+ }
535
+
536
+ this._nav.hidden = true;
537
+ const results = searchEmojis(this._allEmojis, query, this._localeData);
538
+
539
+ if (results.length === 0) {
540
+ if (!this._showingEmptyState) {
541
+ this.renderEmptyState();
542
+ }
543
+ } else {
544
+ this._body.innerHTML = '';
545
+ this._sectionEls.clear();
546
+ this._showingEmptyState = false;
547
+
548
+ const byCategory = groupEmojisByCategory(results);
549
+
550
+ for (const [category, categoryEmojis] of byCategory) {
551
+ const section = this.buildSection(this.translateCategory(category), categoryEmojis);
552
+
553
+ section.setAttribute('data-emoji-section', category);
554
+ this._sectionEls.set(category, section);
555
+ this._body.appendChild(section);
556
+ }
557
+ }
558
+ }
559
+
560
+ private renderEmojiGrid(emojis: ProcessedEmoji[]): void {
561
+ this._body.innerHTML = '';
562
+ this._sectionEls.clear();
563
+ this._showingEmptyState = false;
564
+
565
+ const visibleCategories = new Set<string>();
566
+
567
+ // Curated callout section first
568
+ const calloutEmojis = CURATED_CALLOUT_EMOJIS
569
+ .map(native => emojis.find(e => e.native === native))
570
+ .filter((e): e is ProcessedEmoji => e !== undefined);
571
+
572
+ if (calloutEmojis.length > 0) {
573
+ visibleCategories.add('callout');
574
+ const section = this.buildSection(this.translateCategory('callout'), calloutEmojis);
575
+ section.setAttribute('data-emoji-section', 'callout');
576
+ this._sectionEls.set('callout', section);
577
+ this._body.appendChild(section);
578
+ }
579
+
580
+ // Standard categories
581
+ const byCategory = groupEmojisByCategory(emojis);
582
+
583
+ for (const [category, categoryEmojis] of byCategory) {
584
+ visibleCategories.add(category);
585
+ const section = this.buildSection(this.translateCategory(category), categoryEmojis);
586
+ section.setAttribute('data-emoji-section', category);
587
+ this._sectionEls.set(category, section);
588
+ this._body.appendChild(section);
589
+ }
590
+
591
+ this.buildCategoryNav(visibleCategories);
592
+ this._nav.hidden = false;
593
+
594
+ // Set initial active nav after layout
595
+ requestAnimationFrame(() => this.updateActiveNav());
596
+ }
597
+
598
+ private renderEmptyState(): void {
599
+ this._body.innerHTML = '';
600
+ this._showingEmptyState = true;
601
+ this._sectionEls.clear();
602
+
603
+ const empty = document.createElement('div');
604
+ empty.className = [
605
+ 'flex flex-col items-center justify-center py-10',
606
+ 'text-neutral-400 theme-dark:text-neutral-500 select-none',
607
+ 'animate-[blok-emoji-empty-in_300ms_ease-out_both]',
608
+ ].join(' ');
609
+
610
+ const icon = document.createElement('span');
611
+ icon.className = 'mb-3 opacity-20 [&>svg]:w-9 [&>svg]:h-9';
612
+ icon.innerHTML = IconSearch;
613
+
614
+ const text = document.createElement('span');
615
+ text.className = 'text-[13px] font-medium';
616
+ text.textContent = this.i18n.t(NO_EMOJIS_FOUND_KEY);
617
+
618
+ empty.appendChild(icon);
619
+ empty.appendChild(text);
620
+ this._body.appendChild(empty);
621
+ }
622
+
623
+ private translateCategory(categoryId: string): string {
624
+ const key = CATEGORY_I18N_KEYS[categoryId];
625
+
626
+ return key !== undefined ? this.i18n.t(key) : categoryId;
627
+ }
628
+
629
+ private buildSection(title: string, emojis: ProcessedEmoji[]): HTMLElement {
630
+ const section = document.createElement('div');
631
+ const heading = document.createElement('div');
632
+ heading.className = [
633
+ 'text-[11px] font-semibold uppercase tracking-wider px-2 pt-3 pb-1.5',
634
+ 'text-neutral-400/80 theme-dark:text-neutral-500/80',
635
+ 'sticky top-0 bg-white theme-dark:bg-neutral-900 z-10',
636
+ ].join(' ');
637
+ heading.textContent = title;
638
+ section.appendChild(heading);
639
+ section.appendChild(this.buildGrid(emojis));
640
+
641
+ return section;
642
+ }
643
+
644
+ private getDisplayName(emoji: ProcessedEmoji): string {
645
+ return (this._localeData?.[emoji.native]?.n ?? emoji.name).toLocaleLowerCase();
646
+ }
647
+
648
+ private buildGrid(emojis: ProcessedEmoji[]): HTMLElement {
649
+ const grid = document.createElement('div');
650
+ grid.className = 'grid grid-cols-10 gap-0.5 px-0.5 pt-1';
651
+
652
+ for (const emoji of emojis) {
653
+ const btn = document.createElement('button');
654
+ btn.type = 'button';
655
+ btn.textContent = this.getSkinnedNative(emoji);
656
+ btn.title = this.getDisplayName(emoji);
657
+ btn.setAttribute('data-emoji-native', emoji.native);
658
+ btn.className = [
659
+ 'aspect-square flex items-center justify-center',
660
+ 'text-[1.25rem] leading-none rounded-lg cursor-pointer',
661
+ 'hover:bg-neutral-100 theme-dark:hover:bg-neutral-800',
662
+ 'hover:scale-110 active:scale-95',
663
+ 'transition-transform duration-75',
664
+ ].join(' ');
665
+ btn.addEventListener('click', () => {
666
+ this.onSelect(this.getSkinnedNative(emoji));
667
+ this.close();
668
+ });
669
+ onHover(btn, this.getDisplayName(emoji), { placement: 'bottom' });
670
+ grid.appendChild(btn);
671
+ }
672
+
673
+ return grid;
674
+ }
675
+
676
+ // ─── Theme ──────────────────────────────────────────────
677
+
678
+ /**
679
+ * Resolves the current Blok theme using the same logic as ThemeManager:
680
+ * data-blok-theme="dark" → dark
681
+ * data-blok-theme="light" → light
682
+ * absent (auto) → follow prefers-color-scheme
683
+ */
684
+ private resolveTheme(): 'dark' | 'light' {
685
+ const attr = document.documentElement.getAttribute('data-blok-theme');
686
+
687
+ if (attr === 'dark') {
688
+ return 'dark';
689
+ }
690
+
691
+ if (attr === 'light') {
692
+ return 'light';
693
+ }
694
+
695
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
696
+ }
697
+
698
+ // ─── Backdrop ──────────────────────────────────────────────
699
+
700
+ private showBackdrop(): void {
701
+ this.removeBackdrop();
702
+
703
+ const backdrop = document.createElement('div');
704
+
705
+ backdrop.setAttribute('data-blok-emoji-picker-backdrop', '');
706
+ backdrop.style.position = 'fixed';
707
+ backdrop.style.inset = '0';
708
+ backdrop.style.zIndex = '50';
709
+
710
+ // Close only when clicking the backdrop itself, not the picker inside it
711
+ backdrop.addEventListener('mousedown', (e: MouseEvent) => {
712
+ if (e.target === backdrop) {
713
+ this.close();
714
+ }
715
+ });
716
+
717
+ // Wrap: insert backdrop where the picker is, then move picker inside it
718
+ this._element.parentElement?.insertBefore(backdrop, this._element);
719
+ backdrop.appendChild(this._element);
720
+ this._backdrop = backdrop;
721
+
722
+ // Lock page scroll so the picker stays in place
723
+ this._savedOverflow = document.documentElement.style.overflow;
724
+ document.documentElement.style.overflow = 'hidden';
725
+ }
726
+
727
+ private removeBackdrop(): void {
728
+ if (this._backdrop === null) {
729
+ return;
730
+ }
731
+
732
+ // Restore page scroll
733
+ document.documentElement.style.overflow = this._savedOverflow;
734
+
735
+ // Move picker back to where the backdrop is before removing it
736
+ this._backdrop.parentElement?.insertBefore(this._element, this._backdrop);
737
+ this._backdrop.remove();
738
+ this._backdrop = null;
739
+ }
740
+
741
+ // ─── Positioning ──────────────────────────────────────────
742
+
743
+ private position(anchor: HTMLElement): void {
744
+ const rect = anchor.getBoundingClientRect();
745
+ const pickerRect = this._element.getBoundingClientRect();
746
+ const viewportHeight = window.innerHeight;
747
+ const viewportWidth = window.innerWidth;
748
+
749
+ // Coordinates are viewport-relative (picker is inside a fixed backdrop)
750
+ const top = rect.bottom + pickerRect.height > viewportHeight
751
+ ? rect.top - pickerRect.height - 4
752
+ : rect.bottom + 4;
753
+
754
+ const idealLeft = rect.left - 8;
755
+ const left = idealLeft + pickerRect.width > viewportWidth
756
+ ? rect.right - pickerRect.width
757
+ : Math.max(0, idealLeft);
758
+
759
+ this._element.style.top = `${top}px`;
760
+ this._element.style.left = `${left}px`;
761
+ }
762
+ }