@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.
- package/dist/blok.mjs +2 -2
- package/dist/chunks/am-CHDDMHkd.mjs +16490 -0
- package/dist/chunks/ar-DoqfNqut.mjs +19245 -0
- package/dist/chunks/az-C34P9iEa.mjs +14694 -0
- package/dist/chunks/bg-jroXLY8Y.mjs +15432 -0
- package/dist/chunks/{blok-GAbVCHTV.mjs → blok-0-8moMz9.mjs} +2090 -2052
- package/dist/chunks/bn-BRI-WqxY.mjs +16150 -0
- package/dist/chunks/bs-CCGUpNHu.mjs +12536 -0
- package/dist/chunks/{constants-CyDHDZyH.mjs → constants-PWnlOOrQ.mjs} +211 -172
- package/dist/chunks/cs-D5qZOGuc.mjs +22809 -0
- package/dist/chunks/da-DrJ7W37K.mjs +14573 -0
- package/dist/chunks/de-BW6-kp2c.mjs +16366 -0
- package/dist/chunks/el-C-Vc_Otu.mjs +16339 -0
- package/dist/chunks/es-B6fI5K9i.mjs +13597 -0
- package/dist/chunks/et-BhVlZ-Yz.mjs +14747 -0
- package/dist/chunks/fa-D55Ijdqa.mjs +16368 -0
- package/dist/chunks/fi-jNLjhKUQ.mjs +14408 -0
- package/dist/chunks/fil-DYd0T5aX.mjs +15915 -0
- package/dist/chunks/fr-yxy5xWw_.mjs +17010 -0
- package/dist/chunks/gu-CcY_LJe7.mjs +19021 -0
- package/dist/chunks/he-DL9s7wNw.mjs +15047 -0
- package/dist/chunks/hi-C8eGXgw5.mjs +15946 -0
- package/dist/chunks/hr-DLpybOhU.mjs +15099 -0
- package/dist/chunks/hu-BkT0gT00.mjs +16564 -0
- package/dist/chunks/hy-CVFDCp2S.mjs +12823 -0
- package/dist/chunks/{i18next-loader-p-7ioTwr.mjs → i18next-loader-BCAGutUy.mjs} +2 -2
- package/dist/chunks/id-0P4W9Az0.mjs +16690 -0
- package/dist/chunks/it-mLY6_uoW.mjs +19054 -0
- package/dist/chunks/ja-7RkeRNWG.mjs +15708 -0
- package/dist/chunks/ka-C7Lx-Qsh.mjs +13049 -0
- package/dist/chunks/km-Q8udaraH.mjs +12319 -0
- package/dist/chunks/kn-BiETM-iq.mjs +19123 -0
- package/dist/chunks/ko-tiB80pF1.mjs +17322 -0
- package/dist/chunks/ku-CY-OABkR.mjs +236 -0
- package/dist/chunks/{lightweight-i18n-BPeH69Dl.mjs → lightweight-i18n-CNUuUkuo.mjs} +31 -1
- package/dist/chunks/lo-CTBhEnyk.mjs +13576 -0
- package/dist/chunks/lt-BHKHEtqK.mjs +14587 -0
- package/dist/chunks/lv-DWxgtfUg.mjs +15042 -0
- package/dist/chunks/{messages-BHf_VcXb2.mjs → messages-2i0z2hFq2.mjs} +32 -2
- package/dist/{messages-8zo-T-Nx2.mjs → chunks/messages-4epWYTzK.mjs} +32 -2
- package/dist/chunks/{messages-DgZnRQRS.mjs → messages-B-x11A7Z.mjs} +32 -2
- package/dist/chunks/{messages-C03I_oR-2.mjs → messages-B1mCwtgm2.mjs} +32 -2
- package/dist/{messages-C5hD5pSd2.mjs → chunks/messages-B2Xnfn7m2.mjs} +32 -2
- package/dist/chunks/{messages-B-i-d1Bc.mjs → messages-B38lWmza.mjs} +32 -2
- package/dist/{messages-BRC9E_sp.mjs → chunks/messages-B3VMWaO8.mjs} +32 -2
- package/dist/{messages-BrcgNZOJ.mjs → chunks/messages-B6n2OYKJ.mjs} +32 -2
- package/dist/chunks/{messages-CIugNDDO2.mjs → messages-B847kJa02.mjs} +32 -2
- package/dist/chunks/{messages-CH_cexo62.mjs → messages-B8nL5cPA2.mjs} +32 -2
- package/dist/chunks/{messages-Bdk5tBNu.mjs → messages-BHMUmX3N.mjs} +32 -2
- package/dist/chunks/{messages-LL3Tflph.mjs → messages-BN78Am3l2.mjs} +32 -2
- package/dist/chunks/{messages-BIrkzbBP.mjs → messages-BRa1Itk_.mjs} +32 -2
- package/dist/chunks/{messages-BtS6JWMT.mjs → messages-BleThZm5.mjs} +32 -2
- package/dist/{messages-DHJ1fZLL.mjs → chunks/messages-Bo2PcQoc.mjs} +32 -2
- package/dist/{messages-D5rnT-BC.mjs → chunks/messages-Bqk7cuZY.mjs} +32 -2
- package/dist/chunks/{messages-Di7mfvB8.mjs → messages-BrsB1FRe.mjs} +32 -2
- package/dist/{messages-CsmTziC6.mjs → chunks/messages-ByeBRHpJ.mjs} +32 -2
- package/dist/{messages-C7Rz00Tp.mjs → chunks/messages-C35b_LVM.mjs} +32 -2
- package/dist/{messages-Co26RSCV2.mjs → chunks/messages-CAH4L_Mq2.mjs} +32 -2
- package/dist/chunks/{messages-BMzmli1K.mjs → messages-CBa6-NHn.mjs} +32 -2
- package/dist/chunks/{messages-wUoSWFsJ2.mjs → messages-CCywXuFz2.mjs} +32 -2
- package/dist/chunks/{messages-4OvVdaG52.mjs → messages-CDZrOiaR.mjs} +32 -2
- package/dist/{messages-C92tAUYT2.mjs → chunks/messages-CEVMB1TN2.mjs} +32 -2
- package/dist/{messages-CHeucLGl2.mjs → chunks/messages-CF7WlKOY.mjs} +32 -2
- package/dist/{messages-D_-rh8gl.mjs → chunks/messages-CLcwMAAe.mjs} +32 -2
- package/dist/chunks/{messages-Df69rfTF2.mjs → messages-CTGjizuJ2.mjs} +32 -2
- package/dist/chunks/{messages-Ehx9YYeb2.mjs → messages-CTI66ZU52.mjs} +32 -2
- package/dist/{messages-C9LsEUfG.mjs → chunks/messages-CTtFH-AE.mjs} +32 -2
- package/dist/{messages-BB8umWL1.mjs → chunks/messages-CbilQw5o2.mjs} +32 -2
- package/dist/chunks/{messages-BAgTIgPH.mjs → messages-CiSiPyKp.mjs} +32 -2
- package/dist/chunks/{messages-5thhSeME.mjs → messages-ClD_3Kmk.mjs} +32 -2
- package/dist/chunks/{messages--aM83pib2.mjs → messages-CqoOMgIb2.mjs} +32 -2
- package/dist/chunks/{messages-EDTq4Q52.mjs → messages-Cqxe2gIN.mjs} +32 -2
- package/dist/chunks/{messages-SsrFJhTN.mjs → messages-CxKX6wTt.mjs} +32 -2
- package/dist/chunks/{messages-DUp8NnKT.mjs → messages-CyrZJ9dG.mjs} +32 -2
- package/dist/{messages-EIeWKoc5.mjs → chunks/messages-D1eXOdyJ.mjs} +32 -2
- package/dist/chunks/{messages-ohtcmr1w.mjs → messages-D2QcrPYF.mjs} +32 -2
- package/dist/{messages-DV29fJMD2.mjs → chunks/messages-D6EtVUzk2.mjs} +32 -2
- package/dist/{messages-C4HpNHfK.mjs → chunks/messages-D7qp-JvC.mjs} +32 -2
- package/dist/chunks/{messages-Dr-Ig3sw.mjs → messages-DAhzpf-j.mjs} +32 -2
- package/dist/chunks/{messages-DKChC8Qu.mjs → messages-DEVxqfH0.mjs} +32 -2
- package/dist/{messages-CkIRmpfZ2.mjs → chunks/messages-DHJIlD2m.mjs} +32 -2
- package/dist/{messages-CmoTIebG2.mjs → chunks/messages-DJ_gZym52.mjs} +32 -2
- package/dist/{messages-Cu7Lr1wp.mjs → chunks/messages-DMENc4jZ.mjs} +32 -2
- package/dist/chunks/{messages-BAC7nLeM.mjs → messages-DVDFAJaw.mjs} +32 -2
- package/dist/chunks/{messages-Co4WFeQ8.mjs → messages-DYQ87v5m.mjs} +32 -2
- package/dist/chunks/{messages-BO_jtRbZ2.mjs → messages-Dc2wd0BC2.mjs} +32 -2
- package/dist/{messages-F7cRf-20.mjs → chunks/messages-DlSDBHih.mjs} +32 -2
- package/dist/chunks/{messages-Nz8C7Znm.mjs → messages-Dmxk7uOK.mjs} +32 -2
- package/dist/chunks/{messages-8pf7gRQm2.mjs → messages-Dqoj0eKD2.mjs} +32 -2
- package/dist/{messages-BD_U2EnE.mjs → chunks/messages-DvUNk-e7.mjs} +32 -2
- package/dist/{messages-CTFwu5-h2.mjs → chunks/messages-DvZa_OtS2.mjs} +32 -2
- package/dist/chunks/{messages-CSpfBhlK2.mjs → messages-Dxa27UhV2.mjs} +32 -2
- package/dist/chunks/{messages-D14soBOO.mjs → messages-JQR7Z2f0.mjs} +32 -2
- package/dist/{messages-bRqMCja-2.mjs → chunks/messages-K453lCht2.mjs} +32 -2
- package/dist/{messages-B9ythxux.mjs → chunks/messages-Kqu8QyYy2.mjs} +32 -2
- package/dist/{messages-CWzET_9H2.mjs → chunks/messages-LGqROvB5.mjs} +32 -2
- package/dist/{messages-JwMkLben.mjs → chunks/messages-LiQ7ni4i2.mjs} +32 -2
- package/dist/{messages-B21zLG6b.mjs → chunks/messages-PDaWSrFT.mjs} +32 -2
- package/dist/{messages-ouO9js8Z.mjs → chunks/messages-SOew8O6I.mjs} +32 -2
- package/dist/{messages-SepwOOcg.mjs → chunks/messages-cjqgX4JV2.mjs} +32 -2
- package/dist/chunks/{messages-BARPMN7R.mjs → messages-fdO2V2XC.mjs} +32 -2
- package/dist/chunks/{messages-DDpgr0B1.mjs → messages-lu4RI1A3.mjs} +32 -2
- package/dist/{messages-D_cAZ4Ic2.mjs → chunks/messages-qt_Tsj1u2.mjs} +32 -2
- package/dist/chunks/{messages-BDTgiBJY2.mjs → messages-uQOWdLDn2.mjs} +32 -2
- package/dist/chunks/{messages-FCmAVA792.mjs → messages-xG65ERBM2.mjs} +32 -2
- package/dist/chunks/mk-BjookGdx.mjs +16800 -0
- package/dist/chunks/ml-L-NnZcp9.mjs +13558 -0
- package/dist/chunks/mn-OMWi7Hl_.mjs +12107 -0
- package/dist/chunks/mr-B6JPzITo.mjs +17324 -0
- package/dist/chunks/ms-CG3S-sPB.mjs +14999 -0
- package/dist/chunks/my-BLAmGfhT.mjs +12534 -0
- package/dist/chunks/native-BPcABu9z.mjs +107 -0
- package/dist/chunks/ne-D1JHLfYw.mjs +13248 -0
- package/dist/chunks/nl-Ca7Q8FnY.mjs +16693 -0
- package/dist/chunks/no-Coxcohcz.mjs +14767 -0
- package/dist/chunks/pa-CCaXqpaI.mjs +17220 -0
- package/dist/chunks/pl-Cl_fAZ84.mjs +17221 -0
- package/dist/chunks/ps-WD5qGAWy.mjs +12185 -0
- package/dist/chunks/pt-C4zvLfvq.mjs +16334 -0
- package/dist/chunks/ro-DbefHcmM.mjs +18857 -0
- package/dist/chunks/ru-uU1J14jd.mjs +17485 -0
- package/dist/chunks/sd-DKu368Ip.mjs +10889 -0
- package/dist/chunks/si-BsJCiPkZ.mjs +12721 -0
- package/dist/chunks/sk-CD-a3SN6.mjs +19081 -0
- package/dist/chunks/sl-CXhrPJe_.mjs +15682 -0
- package/dist/chunks/sq-CTctCoFQ.mjs +15278 -0
- package/dist/chunks/sr-BZkhBwXj.mjs +14524 -0
- package/dist/chunks/sv-NmRZb_xi.mjs +12855 -0
- package/dist/chunks/sw-Be5ik3H6.mjs +14315 -0
- package/dist/chunks/ta-DsXh6neL.mjs +16836 -0
- package/dist/chunks/te-CwpCbM8M.mjs +18588 -0
- package/dist/chunks/th-CcZ15OLk.mjs +13894 -0
- package/dist/chunks/{tools-CW1RAIvp.mjs → tools-BBmJ8jqK.mjs} +1417 -761
- package/dist/chunks/tr-q3bTgvhW.mjs +16318 -0
- package/dist/chunks/ug-919EhLsL.mjs +9816 -0
- package/dist/chunks/uk-aNMEzd0Y.mjs +16725 -0
- package/dist/chunks/ur-BwQI77sh.mjs +18051 -0
- package/dist/chunks/vi-Dxq806-F.mjs +14006 -0
- package/dist/chunks/zh-BcHuy1Ti.mjs +16334 -0
- package/dist/full.mjs +10 -10
- package/dist/locales.mjs +98 -68
- package/dist/{messages-qV14y_oA2.mjs → messages--PYnorLu2.mjs} +32 -2
- package/dist/{messages-Cy3Ne_M9.mjs → messages-B2X5gO-s.mjs} +32 -2
- package/dist/{chunks/messages-D1SqLcxI2.mjs → messages-BD3lSUU_.mjs} +32 -2
- package/dist/{chunks/messages-CIZkNCpW.mjs → messages-BFbAM_pK2.mjs} +32 -2
- package/dist/{chunks/messages-CIBuZccC.mjs → messages-BMUQ7kA62.mjs} +32 -2
- package/dist/{messages-CrWsU4Xw2.mjs → messages-BR1Hlhdc2.mjs} +32 -2
- package/dist/{chunks/messages-D2uBlGXR2.mjs → messages-BRJBZB-H2.mjs} +32 -2
- package/dist/{chunks/messages-OIelQDL32.mjs → messages-BTFWPbHk2.mjs} +32 -2
- package/dist/{messages-EwoT2jof.mjs → messages-BXvE3YB4.mjs} +32 -2
- package/dist/{messages-CqNzlpWi.mjs → messages-BZg8xxIT.mjs} +32 -2
- package/dist/{messages-BAZK-8Zb.mjs → messages-B_idm-Pp.mjs} +32 -2
- package/dist/{messages-LyzjEEIj2.mjs → messages-BcrsCh5-2.mjs} +32 -2
- package/dist/{chunks/messages-DPoPrTiZ.mjs → messages-Bf0eqjDJ.mjs} +32 -2
- package/dist/{messages-CKmmJ9tW.mjs → messages-Bgu3IB_k.mjs} +32 -2
- package/dist/{chunks/messages-DeLzropc.mjs → messages-Bkv2sk7Y.mjs} +32 -2
- package/dist/{messages-rM6YFLZH.mjs → messages-Bn77ieXA.mjs} +32 -2
- package/dist/{messages-CTPFrtK92.mjs → messages-Buf_f_-32.mjs} +32 -2
- package/dist/{messages-ouRGTAKo2.mjs → messages-BvIBkUCG2.mjs} +32 -2
- package/dist/{chunks/messages-DVg69mRj.mjs → messages-Bw0kGTAV.mjs} +32 -2
- package/dist/{chunks/messages-CWp6-Y8f2.mjs → messages-C5MKR_WT2.mjs} +32 -2
- package/dist/{chunks/messages-N72K1hw3.mjs → messages-C6UkeZ0y.mjs} +32 -2
- package/dist/{messages-CzZAfGif.mjs → messages-C9AvcT0K.mjs} +32 -2
- package/dist/{chunks/messages-D0IKicWg.mjs → messages-CC596_yM.mjs} +32 -2
- package/dist/{messages-DiSeSE8p.mjs → messages-CGD9LR8l.mjs} +32 -2
- package/dist/{messages-Tx25QErT.mjs → messages-CH8tCpAX.mjs} +32 -2
- package/dist/{chunks/messages-DfsHFEWa.mjs → messages-CIGa1j8-2.mjs} +32 -2
- package/dist/{messages-TI0u6Ked.mjs → messages-CJ5pCncM.mjs} +32 -2
- package/dist/{chunks/messages-DEGzGmEQ2.mjs → messages-CJd52qP7.mjs} +32 -2
- package/dist/{messages-Bm8I_Li12.mjs → messages-CWIigMhw2.mjs} +32 -2
- package/dist/{messages-JZhs_0pf.mjs → messages-CY_Fj715.mjs} +32 -2
- package/dist/{messages-BSLYh59S.mjs → messages-C_YAmOyQ.mjs} +32 -2
- package/dist/{messages-CIxT1nSh.mjs → messages-CjCjr0wW.mjs} +32 -2
- package/dist/{messages-Dr9L1psl.mjs → messages-CqbeAhjH.mjs} +32 -2
- package/dist/{messages-BTR3QlIb2.mjs → messages-Cr2hTJyi2.mjs} +32 -2
- package/dist/{chunks/messages-BKjLgO5d.mjs → messages-Cuw_6akN.mjs} +32 -2
- package/dist/{messages-lEyiemqU2.mjs → messages-D0KBToO12.mjs} +32 -2
- package/dist/{messages-1_FCq0It.mjs → messages-D2Xo8E0P.mjs} +32 -2
- package/dist/{messages-Djhu5RJd.mjs → messages-D5Uk_qYf.mjs} +32 -2
- package/dist/{messages-CHWfj4ik.mjs → messages-D7dJJ7dx.mjs} +32 -2
- package/dist/{chunks/messages-DmrsEYQm2.mjs → messages-D8SS2hOs2.mjs} +32 -2
- package/dist/{chunks/messages-DQOk-dTH.mjs → messages-D9a5iSFC2.mjs} +32 -2
- package/dist/{messages-BSwhWcYw.mjs → messages-DAjtVSb6.mjs} +32 -2
- package/dist/{messages-BZXBdD_S2.mjs → messages-DBuHaabF2.mjs} +32 -2
- package/dist/{chunks/messages-CuRN1_ep2.mjs → messages-DCjAu6M72.mjs} +32 -2
- package/dist/{messages-CKX9iXIb2.mjs → messages-DFlT_TM02.mjs} +32 -2
- package/dist/{chunks/messages-DlM9TmqS2.mjs → messages-DPECmAwT.mjs} +32 -2
- package/dist/{chunks/messages-DhGHk-Ma2.mjs → messages-DR2Lgl002.mjs} +32 -2
- package/dist/{messages-DDiP6yex.mjs → messages-DUPXmcZh.mjs} +32 -2
- package/dist/{chunks/messages-CoRQE_Jc.mjs → messages-DVReQ5EC.mjs} +32 -2
- package/dist/{chunks/messages-BvlSf_pu.mjs → messages-DaZkRhoM.mjs} +32 -2
- package/dist/{chunks/messages-B9zocutJ.mjs → messages-Dc93MR86.mjs} +32 -2
- package/dist/{messages-Dc7ZzqYN.mjs → messages-Dd4OTpH3.mjs} +32 -2
- package/dist/{chunks/messages-BsYzg2le.mjs → messages-DffJ5W5G.mjs} +32 -2
- package/dist/{messages-D8iCBMg7.mjs → messages-DjWAR34D.mjs} +32 -2
- package/dist/{messages-BzZ8LahA2.mjs → messages-DkaJV53s2.mjs} +32 -2
- package/dist/{chunks/messages-stUQR58d.mjs → messages-Dl7LQ_fI.mjs} +32 -2
- package/dist/{chunks/messages-B_fFjpX12.mjs → messages-DlEdd01l2.mjs} +32 -2
- package/dist/{chunks/messages-5ohIWynJ.mjs → messages-ItP2i78l.mjs} +32 -2
- package/dist/{messages-DDJOu049.mjs → messages-Oqm_fCLp.mjs} +32 -2
- package/dist/{chunks/messages-DrXNb1gu.mjs → messages-UpoT1AS6.mjs} +32 -2
- package/dist/{chunks/messages-COkQfKa72.mjs → messages-Z8dpjy_K2.mjs} +32 -2
- package/dist/{messages-mVLfVtQX2.mjs → messages-ierNgYE02.mjs} +32 -2
- package/dist/{messages-DToWAonn2.mjs → messages-ikSI7M732.mjs} +32 -2
- package/dist/{chunks/messages-DKCoHa5D.mjs → messages-olK9oNtb.mjs} +32 -2
- package/dist/{messages-CsnglxbV2.mjs → messages-rgXeOQMh2.mjs} +32 -2
- package/dist/{chunks/messages-CDJLoStb.mjs → messages-syo0DKqf.mjs} +32 -2
- package/dist/{chunks/messages-BgaGPFuy2.mjs → messages-tFDVfvWL.mjs} +32 -2
- package/dist/react.mjs +2 -2
- package/dist/tools.mjs +3 -3
- package/dist/vendor.LICENSE.txt +34 -0
- package/package.json +3 -1
- package/src/components/i18n/locales/am/messages.json +32 -2
- package/src/components/i18n/locales/ar/messages.json +32 -2
- package/src/components/i18n/locales/az/messages.json +32 -2
- package/src/components/i18n/locales/bg/messages.json +32 -2
- package/src/components/i18n/locales/bn/messages.json +32 -2
- package/src/components/i18n/locales/bs/messages.json +32 -2
- package/src/components/i18n/locales/cs/messages.json +32 -2
- package/src/components/i18n/locales/da/messages.json +32 -2
- package/src/components/i18n/locales/de/messages.json +32 -2
- package/src/components/i18n/locales/dv/messages.json +32 -2
- package/src/components/i18n/locales/el/messages.json +32 -2
- package/src/components/i18n/locales/en/messages.json +31 -1
- package/src/components/i18n/locales/es/messages.json +32 -2
- package/src/components/i18n/locales/et/messages.json +32 -2
- package/src/components/i18n/locales/fa/messages.json +32 -2
- package/src/components/i18n/locales/fi/messages.json +32 -2
- package/src/components/i18n/locales/fil/messages.json +32 -2
- package/src/components/i18n/locales/fr/messages.json +32 -2
- package/src/components/i18n/locales/gu/messages.json +32 -2
- package/src/components/i18n/locales/he/messages.json +32 -2
- package/src/components/i18n/locales/hi/messages.json +32 -2
- package/src/components/i18n/locales/hr/messages.json +32 -2
- package/src/components/i18n/locales/hu/messages.json +32 -2
- package/src/components/i18n/locales/hy/messages.json +32 -2
- package/src/components/i18n/locales/id/messages.json +32 -2
- package/src/components/i18n/locales/it/messages.json +32 -2
- package/src/components/i18n/locales/ja/messages.json +32 -2
- package/src/components/i18n/locales/ka/messages.json +32 -2
- package/src/components/i18n/locales/km/messages.json +32 -2
- package/src/components/i18n/locales/kn/messages.json +32 -2
- package/src/components/i18n/locales/ko/messages.json +32 -2
- package/src/components/i18n/locales/ku/messages.json +32 -2
- package/src/components/i18n/locales/lo/messages.json +32 -2
- package/src/components/i18n/locales/lt/messages.json +32 -2
- package/src/components/i18n/locales/lv/messages.json +32 -2
- package/src/components/i18n/locales/mk/messages.json +32 -2
- package/src/components/i18n/locales/ml/messages.json +32 -2
- package/src/components/i18n/locales/mn/messages.json +32 -2
- package/src/components/i18n/locales/mr/messages.json +32 -2
- package/src/components/i18n/locales/ms/messages.json +32 -2
- package/src/components/i18n/locales/my/messages.json +32 -2
- package/src/components/i18n/locales/ne/messages.json +32 -2
- package/src/components/i18n/locales/nl/messages.json +32 -2
- package/src/components/i18n/locales/no/messages.json +32 -2
- package/src/components/i18n/locales/pa/messages.json +32 -2
- package/src/components/i18n/locales/pl/messages.json +32 -2
- package/src/components/i18n/locales/ps/messages.json +32 -2
- package/src/components/i18n/locales/pt/messages.json +32 -2
- package/src/components/i18n/locales/ro/messages.json +32 -2
- package/src/components/i18n/locales/ru/messages.json +32 -2
- package/src/components/i18n/locales/sd/messages.json +32 -2
- package/src/components/i18n/locales/si/messages.json +32 -2
- package/src/components/i18n/locales/sk/messages.json +32 -2
- package/src/components/i18n/locales/sl/messages.json +32 -2
- package/src/components/i18n/locales/sq/messages.json +32 -2
- package/src/components/i18n/locales/sr/messages.json +32 -2
- package/src/components/i18n/locales/sv/messages.json +32 -2
- package/src/components/i18n/locales/sw/messages.json +32 -2
- package/src/components/i18n/locales/ta/messages.json +32 -2
- package/src/components/i18n/locales/te/messages.json +32 -2
- package/src/components/i18n/locales/th/messages.json +32 -2
- package/src/components/i18n/locales/tr/messages.json +32 -2
- package/src/components/i18n/locales/ug/messages.json +32 -2
- package/src/components/i18n/locales/uk/messages.json +32 -2
- package/src/components/i18n/locales/ur/messages.json +32 -2
- package/src/components/i18n/locales/vi/messages.json +32 -2
- package/src/components/i18n/locales/yi/messages.json +32 -2
- package/src/components/i18n/locales/zh/messages.json +32 -2
- package/src/components/icons/index.ts +98 -0
- package/src/components/modules/api/i18n.ts +1 -0
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +22 -5
- package/src/components/modules/blockManager/operations.ts +1 -0
- package/src/components/modules/toolbar/index.ts +102 -7
- package/src/components/modules/toolbar/plus-button.ts +17 -5
- package/src/components/modules/uiControllers/controllers/blockHover.ts +36 -9
- package/src/components/modules/uiControllers/handlers/touch.ts +20 -8
- package/src/components/tools/factory.ts +4 -0
- package/src/components/ui/toolbox.ts +38 -3
- package/src/components/utils/popover/popover-desktop.ts +48 -71
- package/src/components/utils/popover/popover-position.ts +105 -0
- package/src/stories/MarkerColors.stories.ts +8 -1
- package/src/styles/main.css +121 -17
- package/src/tools/callout/block-operations.ts +17 -0
- package/src/tools/callout/callout-keyboard.ts +45 -0
- package/src/tools/callout/constants.ts +30 -0
- package/src/tools/callout/dom-builder.ts +53 -0
- package/src/tools/callout/emoji-picker/emoji-data.ts +105 -0
- package/src/tools/callout/emoji-picker/emoji-locale.ts +165 -0
- package/src/tools/callout/emoji-picker/index.ts +762 -0
- package/src/tools/callout/emoji-picker/locales/am.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ar.json +1 -0
- package/src/tools/callout/emoji-picker/locales/az.json +1 -0
- package/src/tools/callout/emoji-picker/locales/bg.json +1 -0
- package/src/tools/callout/emoji-picker/locales/bn.json +1 -0
- package/src/tools/callout/emoji-picker/locales/bs.json +1 -0
- package/src/tools/callout/emoji-picker/locales/cs.json +1 -0
- package/src/tools/callout/emoji-picker/locales/da.json +1 -0
- package/src/tools/callout/emoji-picker/locales/de.json +1 -0
- package/src/tools/callout/emoji-picker/locales/el.json +1 -0
- package/src/tools/callout/emoji-picker/locales/es.json +1 -0
- package/src/tools/callout/emoji-picker/locales/et.json +1 -0
- package/src/tools/callout/emoji-picker/locales/fa.json +1 -0
- package/src/tools/callout/emoji-picker/locales/fi.json +1 -0
- package/src/tools/callout/emoji-picker/locales/fil.json +1 -0
- package/src/tools/callout/emoji-picker/locales/fr.json +1 -0
- package/src/tools/callout/emoji-picker/locales/gu.json +1 -0
- package/src/tools/callout/emoji-picker/locales/he.json +1 -0
- package/src/tools/callout/emoji-picker/locales/hi.json +1 -0
- package/src/tools/callout/emoji-picker/locales/hr.json +1 -0
- package/src/tools/callout/emoji-picker/locales/hu.json +1 -0
- package/src/tools/callout/emoji-picker/locales/hy.json +1 -0
- package/src/tools/callout/emoji-picker/locales/id.json +1 -0
- package/src/tools/callout/emoji-picker/locales/it.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ja.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ka.json +1 -0
- package/src/tools/callout/emoji-picker/locales/km.json +1 -0
- package/src/tools/callout/emoji-picker/locales/kn.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ko.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ku.json +1 -0
- package/src/tools/callout/emoji-picker/locales/lo.json +1 -0
- package/src/tools/callout/emoji-picker/locales/lt.json +1 -0
- package/src/tools/callout/emoji-picker/locales/lv.json +1 -0
- package/src/tools/callout/emoji-picker/locales/mk.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ml.json +1 -0
- package/src/tools/callout/emoji-picker/locales/mn.json +1 -0
- package/src/tools/callout/emoji-picker/locales/mr.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ms.json +1 -0
- package/src/tools/callout/emoji-picker/locales/my.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ne.json +1 -0
- package/src/tools/callout/emoji-picker/locales/nl.json +1 -0
- package/src/tools/callout/emoji-picker/locales/no.json +1 -0
- package/src/tools/callout/emoji-picker/locales/pa.json +1 -0
- package/src/tools/callout/emoji-picker/locales/pl.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ps.json +1 -0
- package/src/tools/callout/emoji-picker/locales/pt.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ro.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ru.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sd.json +1 -0
- package/src/tools/callout/emoji-picker/locales/si.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sk.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sl.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sq.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sr.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sv.json +1 -0
- package/src/tools/callout/emoji-picker/locales/sw.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ta.json +1 -0
- package/src/tools/callout/emoji-picker/locales/te.json +1 -0
- package/src/tools/callout/emoji-picker/locales/th.json +1 -0
- package/src/tools/callout/emoji-picker/locales/tr.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ug.json +1 -0
- package/src/tools/callout/emoji-picker/locales/uk.json +1 -0
- package/src/tools/callout/emoji-picker/locales/ur.json +1 -0
- package/src/tools/callout/emoji-picker/locales/vi.json +1 -0
- package/src/tools/callout/emoji-picker/locales/zh.json +1 -0
- package/src/tools/callout/index.ts +338 -0
- package/src/tools/callout/types.ts +11 -0
- package/src/tools/header/header-toggle-keyboard.ts +3 -2
- package/src/tools/header/index.ts +1 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/toggle/index.ts +1 -1
- package/src/tools/toggle/toggle-keyboard.ts +29 -1
- package/types/api/i18n.d.ts +7 -0
- package/types/tools-entry.d.ts +0 -4
- /package/dist/chunks/{i18next-DymC16cN.mjs → i18next-Ch0gVA3V.mjs} +0 -0
- /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
|
+
}
|