@jackuait/blok 0.8.3-beta.4 → 0.10.0-beta.1
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/codemod/migrate-editorjs-to-blok.js +50 -0
- package/codemod/test.js +39 -0
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-eDCl1y00.mjs → blok-wFPimG2f.mjs} +1590 -1566
- package/dist/chunks/{constants-Duj_CSIT.mjs → constants-B_TnFMSA.mjs} +56 -56
- package/dist/chunks/{i18next-loader-BCAGutUy.mjs → i18next-loader-BOlOKRt8.mjs} +1 -1
- package/dist/chunks/{lightweight-i18n-CNUuUkuo.mjs → lightweight-i18n-D1n0OClP.mjs} +42 -1
- package/dist/chunks/{messages-B847kJa02.mjs → messages-0Nh_GHU02.mjs} +42 -1
- package/dist/{messages--PYnorLu2.mjs → chunks/messages-16UmLAWZ2.mjs} +42 -1
- package/dist/chunks/{messages-CiSiPyKp.mjs → messages-2nj1xBDo.mjs} +42 -1
- package/dist/chunks/{messages-LiQ7ni4i2.mjs → messages-5WyxUYVR2.mjs} +42 -1
- package/dist/chunks/{messages-DYQ87v5m.mjs → messages-7PIvzufT.mjs} +42 -1
- package/dist/{messages-BD3lSUU_.mjs → chunks/messages-92ma9RJD2.mjs} +42 -1
- package/dist/chunks/{messages-uQOWdLDn2.mjs → messages-A3Z4jxwt2.mjs} +42 -1
- package/dist/{messages-CJ5pCncM.mjs → chunks/messages-B18MZnaY.mjs} +42 -1
- package/dist/{messages-Dl7LQ_fI.mjs → chunks/messages-B1uFbxNg.mjs} +42 -1
- package/dist/{messages-D7dJJ7dx.mjs → chunks/messages-B7Vlzmgw.mjs} +42 -1
- package/dist/{messages-Bw0kGTAV.mjs → chunks/messages-BF8c-lMm.mjs} +42 -1
- package/dist/{messages-Oqm_fCLp.mjs → chunks/messages-BLD1DC722.mjs} +42 -1
- package/dist/chunks/{messages-C35b_LVM.mjs → messages-BN_zp4oj.mjs} +42 -1
- package/dist/chunks/{messages-JQR7Z2f0.mjs → messages-BORQKKT9.mjs} +42 -1
- package/dist/{messages-DkaJV53s2.mjs → chunks/messages-BPQA3B862.mjs} +42 -1
- package/dist/chunks/{messages-CyrZJ9dG.mjs → messages-BQvBhQem.mjs} +42 -1
- package/dist/{messages-D9a5iSFC2.mjs → chunks/messages-BTyEo5Kb.mjs} +42 -1
- package/dist/{messages-D8SS2hOs2.mjs → chunks/messages-BVl-X2wo2.mjs} +42 -1
- package/dist/chunks/{messages-B2Xnfn7m2.mjs → messages-BivhofAQ2.mjs} +42 -1
- package/dist/chunks/{messages-D1eXOdyJ.mjs → messages-BtZ8oQXS.mjs} +42 -1
- package/dist/{messages-D2Xo8E0P.mjs → chunks/messages-By-gACFM2.mjs} +42 -1
- package/dist/chunks/{messages-B6n2OYKJ.mjs → messages-C1pm6RWX.mjs} +42 -1
- package/dist/chunks/{messages-ByeBRHpJ.mjs → messages-C7YpgZ9m.mjs} +42 -1
- package/dist/{messages-C9AvcT0K.mjs → chunks/messages-C9qPNbrJ2.mjs} +42 -1
- package/dist/{messages-DPECmAwT.mjs → chunks/messages-CDV8VcSZ2.mjs} +42 -1
- package/dist/{messages-tFDVfvWL.mjs → chunks/messages-CQgP-Fo22.mjs} +42 -1
- package/dist/{messages-C5MKR_WT2.mjs → chunks/messages-CR48wvl32.mjs} +42 -1
- package/dist/chunks/{messages-qt_Tsj1u2.mjs → messages-CYZL_rhV2.mjs} +42 -1
- package/dist/{messages-CGD9LR8l.mjs → chunks/messages-CYZgPXFK.mjs} +42 -1
- package/dist/chunks/{messages-B3VMWaO8.mjs → messages-CbXL5f99.mjs} +42 -1
- package/dist/{messages-BcrsCh5-2.mjs → chunks/messages-CkWidbwX2.mjs} +42 -1
- package/dist/{messages-BvIBkUCG2.mjs → chunks/messages-CmSsyItg.mjs} +42 -1
- package/dist/chunks/{messages-Dmxk7uOK.mjs → messages-CogKhvJL.mjs} +42 -1
- package/dist/chunks/{messages-B8nL5cPA2.mjs → messages-CqdYWY192.mjs} +42 -1
- package/dist/chunks/{messages-2i0z2hFq2.mjs → messages-Ct_H_5cB2.mjs} +42 -1
- package/dist/{messages-Z8dpjy_K2.mjs → chunks/messages-CuHxfDvL2.mjs} +42 -1
- package/dist/chunks/{messages-CDZrOiaR.mjs → messages-CuJLHCj5.mjs} +42 -1
- package/dist/chunks/{messages-CLcwMAAe.mjs → messages-CvA7Fbqf.mjs} +42 -1
- package/dist/chunks/{messages-K453lCht2.mjs → messages-D06U2QNl2.mjs} +42 -1
- package/dist/{messages-CY_Fj715.mjs → chunks/messages-D3hNTep_.mjs} +42 -1
- package/dist/{messages-CWIigMhw2.mjs → chunks/messages-D4QXMtW52.mjs} +42 -1
- package/dist/chunks/{messages-ClD_3Kmk.mjs → messages-D5iRrf9M.mjs} +42 -1
- package/dist/{messages-D5Uk_qYf.mjs → chunks/messages-D6ZvH6hX.mjs} +42 -1
- package/dist/chunks/{messages-LGqROvB5.mjs → messages-D9SfB6MI.mjs} +42 -1
- package/dist/chunks/{messages-Cqxe2gIN.mjs → messages-DBZ-uuAV.mjs} +42 -1
- package/dist/{messages-Bkv2sk7Y.mjs → chunks/messages-DDM4t-j8.mjs} +42 -1
- package/dist/{messages-DlEdd01l2.mjs → chunks/messages-DJ9yyqUO2.mjs} +42 -1
- package/dist/chunks/{messages-B38lWmza.mjs → messages-DQUUtL5v.mjs} +42 -1
- package/dist/{messages-DjWAR34D.mjs → chunks/messages-DcrkPXXn.mjs} +42 -1
- package/dist/chunks/{messages-CBa6-NHn.mjs → messages-DiaQNuPV.mjs} +42 -1
- package/dist/{messages-Cuw_6akN.mjs → chunks/messages-DjhgPQtb.mjs} +42 -1
- package/dist/chunks/{messages-DlSDBHih.mjs → messages-Dl16RBg1.mjs} +42 -1
- package/dist/{messages-BFbAM_pK2.mjs → chunks/messages-Dn1ZDZUy.mjs} +42 -1
- package/dist/chunks/{messages-CEVMB1TN2.mjs → messages-Du6j7HqD2.mjs} +42 -1
- package/dist/{messages-BR1Hlhdc2.mjs → chunks/messages-FEjIF48t.mjs} +42 -1
- package/dist/{messages-DffJ5W5G.mjs → chunks/messages-IKrYzwFq.mjs} +42 -1
- package/dist/{messages-C_YAmOyQ.mjs → chunks/messages-KihEeXdr.mjs} +42 -1
- package/dist/{messages-Dd4OTpH3.mjs → chunks/messages-LiSIeruD2.mjs} +42 -1
- package/dist/{messages-B_idm-Pp.mjs → chunks/messages-OFFQT8Fg.mjs} +42 -1
- package/dist/{messages-ItP2i78l.mjs → chunks/messages-cMwiuDZM.mjs} +42 -1
- package/dist/chunks/{messages-DAhzpf-j.mjs → messages-fitmpwb3.mjs} +42 -1
- package/dist/{messages-D0KBToO12.mjs → chunks/messages-i9ThpxZk2.mjs} +42 -1
- package/dist/chunks/{messages-4epWYTzK.mjs → messages-nZP1GShd.mjs} +42 -1
- package/dist/{messages-UpoT1AS6.mjs → chunks/messages-naWwXCx3.mjs} +42 -1
- package/dist/{messages-B2X5gO-s.mjs → chunks/messages-rnd6qiJ12.mjs} +42 -1
- package/dist/chunks/{messages-BHMUmX3N.mjs → messages-rxlf-Ule.mjs} +42 -1
- package/dist/chunks/{messages-DvUNk-e7.mjs → messages-x6GyZWWT.mjs} +42 -1
- package/dist/chunks/{tools-BG9c26dK.mjs → tools-TuZCMYTS.mjs} +1137 -908
- package/dist/cli.mjs +1 -1
- package/dist/full.mjs +11 -11
- package/dist/locales.mjs +109 -68
- package/dist/{messages-Cr2hTJyi2.mjs → messages-4kMr3vfK2.mjs} +42 -1
- package/dist/{messages-BTFWPbHk2.mjs → messages-6Cq_jyNk2.mjs} +42 -1
- package/dist/{chunks/messages-DvZa_OtS2.mjs → messages-6edZhK922.mjs} +42 -1
- package/dist/{chunks/messages-B1mCwtgm2.mjs → messages-B-lXqB1z2.mjs} +42 -1
- package/dist/{messages-Bf0eqjDJ.mjs → messages-BBauvpFc.mjs} +42 -1
- package/dist/{chunks/messages-Kqu8QyYy2.mjs → messages-BCIuVjwb.mjs} +42 -1
- package/dist/{messages-DCjAu6M72.mjs → messages-BHIdzfAS2.mjs} +42 -1
- package/dist/{messages-ikSI7M732.mjs → messages-BLjz6V7y2.mjs} +42 -1
- package/dist/{chunks/messages-D2QcrPYF.mjs → messages-BQi_l2vs.mjs} +42 -1
- package/dist/{messages-syo0DKqf.mjs → messages-BRlbE8SE.mjs} +42 -1
- package/dist/{chunks/messages-D6EtVUzk2.mjs → messages-B_6S7hBj2.mjs} +42 -1
- package/dist/{messages-CIGa1j8-2.mjs → messages-BfbYJ8Wk2.mjs} +42 -1
- package/dist/{chunks/messages-CAH4L_Mq2.mjs → messages-BiK5fMYF2.mjs} +42 -1
- package/dist/{messages-CjCjr0wW.mjs → messages-BjEY7_jw.mjs} +42 -1
- package/dist/{chunks/messages-SOew8O6I.mjs → messages-BkZDaKu6.mjs} +42 -1
- package/dist/{messages-ierNgYE02.mjs → messages-BwyQiBdm2.mjs} +42 -1
- package/dist/{messages-DaZkRhoM.mjs → messages-BxcqzUx0.mjs} +42 -1
- package/dist/{chunks/messages-CxKX6wTt.mjs → messages-C-KPP7bC.mjs} +42 -1
- package/dist/{messages-BZg8xxIT.mjs → messages-C-v50b4r.mjs} +42 -1
- package/dist/{chunks/messages-BleThZm5.mjs → messages-C672uqt-.mjs} +42 -1
- package/dist/{chunks/messages-Dxa27UhV2.mjs → messages-CBWUNVHy.mjs} +42 -1
- package/dist/{chunks/messages-BN78Am3l2.mjs → messages-CCBgCmyq.mjs} +42 -1
- package/dist/{chunks/messages-CbilQw5o2.mjs → messages-CGqRnKaM.mjs} +42 -1
- package/dist/{messages-Bn77ieXA.mjs → messages-CHaVGY89.mjs} +42 -1
- package/dist/{messages-BRJBZB-H2.mjs → messages-CIpXgMRr2.mjs} +42 -1
- package/dist/{chunks/messages-DVDFAJaw.mjs → messages-CLB0caVL.mjs} +42 -1
- package/dist/{chunks/messages-D7qp-JvC.mjs → messages-CLN3oL77.mjs} +42 -1
- package/dist/{chunks/messages-Bo2PcQoc.mjs → messages-CXlAjnEQ.mjs} +42 -1
- package/dist/{chunks/messages-CqoOMgIb2.mjs → messages-CZkwcbV12.mjs} +42 -1
- package/dist/{chunks/messages-CTtFH-AE.mjs → messages-CcPrYMHE.mjs} +42 -1
- package/dist/{chunks/messages-BRa1Itk_.mjs → messages-CeyO7HXV.mjs} +42 -1
- package/dist/{chunks/messages-CF7WlKOY.mjs → messages-CfaiAQHW2.mjs} +42 -1
- package/dist/{chunks/messages-Dqoj0eKD2.mjs → messages-CsVZUVra.mjs} +42 -1
- package/dist/{messages-DBuHaabF2.mjs → messages-CyQQ8g9w2.mjs} +42 -1
- package/dist/{messages-DUPXmcZh.mjs → messages-CyZZ10br.mjs} +42 -1
- package/dist/{messages-CJd52qP7.mjs → messages-D-bnq2qy.mjs} +42 -1
- package/dist/{messages-CC596_yM.mjs → messages-D1-_eTfM.mjs} +42 -1
- package/dist/{chunks/messages-DEVxqfH0.mjs → messages-D2GHT83V2.mjs} +42 -1
- package/dist/{chunks/messages-CTI66ZU52.mjs → messages-D7Wofcg3.mjs} +42 -1
- package/dist/{chunks/messages-cjqgX4JV2.mjs → messages-DB1-0FXB.mjs} +42 -1
- package/dist/{chunks/messages-B-x11A7Z.mjs → messages-DEKkIgU6.mjs} +42 -1
- package/dist/{messages-DFlT_TM02.mjs → messages-DEfeDBuV2.mjs} +42 -1
- package/dist/{chunks/messages-CCywXuFz2.mjs → messages-DP9PhNTo.mjs} +42 -1
- package/dist/{messages-DR2Lgl002.mjs → messages-DVHWIOfr2.mjs} +42 -1
- package/dist/{chunks/messages-Dc2wd0BC2.mjs → messages-DVILiJw52.mjs} +42 -1
- package/dist/{chunks/messages-PDaWSrFT.mjs → messages-DaFDdCrr.mjs} +42 -1
- package/dist/{messages-DAjtVSb6.mjs → messages-Daza4lOM.mjs} +42 -1
- package/dist/{chunks/messages-fdO2V2XC.mjs → messages-DbPt9d2U.mjs} +42 -1
- package/dist/{chunks/messages-DMENc4jZ.mjs → messages-DflAKRLd.mjs} +42 -1
- package/dist/{messages-olK9oNtb.mjs → messages-Dn-khi3a.mjs} +42 -1
- package/dist/{chunks/messages-CTGjizuJ2.mjs → messages-DnNNd3RW2.mjs} +42 -1
- package/dist/{chunks/messages-BrsB1FRe.mjs → messages-Dtw27ih4.mjs} +42 -1
- package/dist/{messages-Bgu3IB_k.mjs → messages-Du0fWeyE.mjs} +42 -1
- package/dist/{chunks/messages-xG65ERBM2.mjs → messages-DwTwecgF2.mjs} +42 -1
- package/dist/{messages-Dc93MR86.mjs → messages-DwroI9YW.mjs} +42 -1
- package/dist/{messages-C6UkeZ0y.mjs → messages-DzlmoWqQ.mjs} +42 -1
- package/dist/{messages-BXvE3YB4.mjs → messages-Dzqb5lg6.mjs} +42 -1
- package/dist/{messages-rgXeOQMh2.mjs → messages-QkGAxuVC2.mjs} +42 -1
- package/dist/{messages-CH8tCpAX.mjs → messages-YlWV9cSl.mjs} +42 -1
- package/dist/{messages-DVReQ5EC.mjs → messages-ZMa-zmIc.mjs} +42 -1
- package/dist/{messages-Buf_f_-32.mjs → messages-axsznSTn2.mjs} +42 -1
- package/dist/{chunks/messages-DJ_gZym52.mjs → messages-h_VN1Kyo2.mjs} +42 -1
- package/dist/{chunks/messages-lu4RI1A3.mjs → messages-ke15helG2.mjs} +42 -1
- package/dist/{messages-CqbeAhjH.mjs → messages-kuLrhtV2.mjs} +42 -1
- package/dist/{chunks/messages-DHJIlD2m.mjs → messages-nosa-xnx2.mjs} +42 -1
- package/dist/{chunks/messages-Bqk7cuZY.mjs → messages-uP6wmMOs.mjs} +42 -1
- package/dist/{messages-BMUQ7kA62.mjs → messages-us2JotS-2.mjs} +42 -1
- package/dist/react.mjs +2 -2
- package/dist/tools.mjs +3 -3
- package/package.json +1 -1
- package/src/components/i18n/locales/am/messages.json +42 -1
- package/src/components/i18n/locales/ar/messages.json +42 -1
- package/src/components/i18n/locales/az/messages.json +42 -1
- package/src/components/i18n/locales/bg/messages.json +42 -1
- package/src/components/i18n/locales/bn/messages.json +42 -1
- package/src/components/i18n/locales/bs/messages.json +42 -1
- package/src/components/i18n/locales/cs/messages.json +42 -1
- package/src/components/i18n/locales/da/messages.json +42 -1
- package/src/components/i18n/locales/de/messages.json +42 -1
- package/src/components/i18n/locales/dv/messages.json +42 -1
- package/src/components/i18n/locales/el/messages.json +42 -1
- package/src/components/i18n/locales/en/messages.json +42 -1
- package/src/components/i18n/locales/es/messages.json +42 -1
- package/src/components/i18n/locales/et/messages.json +42 -1
- package/src/components/i18n/locales/fa/messages.json +42 -1
- package/src/components/i18n/locales/fi/messages.json +42 -1
- package/src/components/i18n/locales/fil/messages.json +42 -1
- package/src/components/i18n/locales/fr/messages.json +42 -1
- package/src/components/i18n/locales/gu/messages.json +42 -1
- package/src/components/i18n/locales/he/messages.json +42 -1
- package/src/components/i18n/locales/hi/messages.json +42 -1
- package/src/components/i18n/locales/hr/messages.json +42 -1
- package/src/components/i18n/locales/hu/messages.json +42 -1
- package/src/components/i18n/locales/hy/messages.json +42 -1
- package/src/components/i18n/locales/id/messages.json +42 -1
- package/src/components/i18n/locales/it/messages.json +42 -1
- package/src/components/i18n/locales/ja/messages.json +42 -1
- package/src/components/i18n/locales/ka/messages.json +42 -1
- package/src/components/i18n/locales/km/messages.json +42 -1
- package/src/components/i18n/locales/kn/messages.json +42 -1
- package/src/components/i18n/locales/ko/messages.json +42 -1
- package/src/components/i18n/locales/ku/messages.json +42 -1
- package/src/components/i18n/locales/lo/messages.json +42 -1
- package/src/components/i18n/locales/lt/messages.json +42 -1
- package/src/components/i18n/locales/lv/messages.json +42 -1
- package/src/components/i18n/locales/mk/messages.json +42 -1
- package/src/components/i18n/locales/ml/messages.json +42 -1
- package/src/components/i18n/locales/mn/messages.json +42 -1
- package/src/components/i18n/locales/mr/messages.json +42 -1
- package/src/components/i18n/locales/ms/messages.json +42 -1
- package/src/components/i18n/locales/my/messages.json +42 -1
- package/src/components/i18n/locales/ne/messages.json +42 -1
- package/src/components/i18n/locales/nl/messages.json +42 -1
- package/src/components/i18n/locales/no/messages.json +42 -1
- package/src/components/i18n/locales/pa/messages.json +42 -1
- package/src/components/i18n/locales/pl/messages.json +42 -1
- package/src/components/i18n/locales/ps/messages.json +42 -1
- package/src/components/i18n/locales/pt/messages.json +42 -1
- package/src/components/i18n/locales/ro/messages.json +42 -1
- package/src/components/i18n/locales/ru/messages.json +42 -1
- package/src/components/i18n/locales/sd/messages.json +42 -1
- package/src/components/i18n/locales/si/messages.json +42 -1
- package/src/components/i18n/locales/sk/messages.json +42 -1
- package/src/components/i18n/locales/sl/messages.json +42 -1
- package/src/components/i18n/locales/sq/messages.json +42 -1
- package/src/components/i18n/locales/sr/messages.json +42 -1
- package/src/components/i18n/locales/sv/messages.json +42 -1
- package/src/components/i18n/locales/sw/messages.json +42 -1
- package/src/components/i18n/locales/ta/messages.json +42 -1
- package/src/components/i18n/locales/te/messages.json +42 -1
- package/src/components/i18n/locales/th/messages.json +42 -1
- package/src/components/i18n/locales/tr/messages.json +42 -1
- package/src/components/i18n/locales/ug/messages.json +42 -1
- package/src/components/i18n/locales/uk/messages.json +42 -1
- package/src/components/i18n/locales/ur/messages.json +42 -1
- package/src/components/i18n/locales/vi/messages.json +42 -1
- package/src/components/i18n/locales/yi/messages.json +42 -1
- package/src/components/i18n/locales/zh/messages.json +42 -1
- package/src/components/icons/index.ts +17 -0
- package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +65 -2
- package/src/components/modules/blockEvents/constants.ts +11 -0
- package/src/components/modules/renderer.ts +17 -0
- package/src/components/ui/toolbox.ts +9 -0
- package/src/components/utils/caret/boundaries.ts +15 -4
- package/src/components/utils/data-model-transform.ts +8 -6
- package/src/styles/main.css +6 -4
- package/src/tools/callout/constants.ts +1 -1
- package/src/tools/callout/emoji-picker/index.ts +38 -3
- package/src/tools/callout/index.ts +1 -0
- package/src/tools/divider/index.ts +97 -0
- package/src/tools/divider/types.ts +6 -0
- package/src/tools/header/index.ts +2 -0
- package/src/tools/index.ts +4 -0
- package/src/tools/list/style-config.ts +3 -0
- package/src/tools/paragraph/index.ts +1 -0
- package/src/tools/quote/index.ts +229 -0
- package/src/tools/table/index.ts +1 -0
- package/src/tools/toggle/index.ts +1 -0
- package/types/tools/divider.d.ts +11 -0
- package/types/tools/tool-settings.d.ts +7 -0
- package/types/tools-entry.d.ts +9 -3
|
@@ -12,6 +12,14 @@ import {
|
|
|
12
12
|
} from '../utils/data-model-transform';
|
|
13
13
|
import { migrateMarkColors } from '../utils/color-migration';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Map of legacy EditorJS tool names to their Blok equivalents.
|
|
17
|
+
* Used during rendering to transparently migrate old article data.
|
|
18
|
+
*/
|
|
19
|
+
const TOOL_ALIASES: Readonly<Record<string, string>> = {
|
|
20
|
+
delimiter: 'divider',
|
|
21
|
+
};
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* Module that responsible for rendering Blocks on blok initialization
|
|
17
25
|
*/
|
|
@@ -111,6 +119,15 @@ export class Renderer extends Module {
|
|
|
111
119
|
};
|
|
112
120
|
}
|
|
113
121
|
|
|
122
|
+
const aliasTarget = TOOL_ALIASES[originalTool];
|
|
123
|
+
|
|
124
|
+
if (aliasTarget !== undefined && Tools.available.has(aliasTarget)) {
|
|
125
|
+
return {
|
|
126
|
+
tool: aliasTarget,
|
|
127
|
+
data: blockToolData,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
114
131
|
logLabeled(`Tool «${originalTool}» is not found. Check 'tools' property at the Blok config.`, 'warn');
|
|
115
132
|
|
|
116
133
|
return {
|
|
@@ -535,6 +535,15 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
535
535
|
const userSearchTerms = tool.searchTerms ?? [];
|
|
536
536
|
const mergedSearchTerms = [...new Set([...librarySearchTerms, ...userSearchTerms])];
|
|
537
537
|
|
|
538
|
+
// Resolve translated search aliases from searchTermKeys
|
|
539
|
+
for (const key of toolboxItem.searchTermKeys ?? []) {
|
|
540
|
+
const fullKey = `searchTerms.${key}`;
|
|
541
|
+
|
|
542
|
+
if (this.i18n.has(fullKey)) {
|
|
543
|
+
mergedSearchTerms.push(this.i18n.t(fullKey));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
538
547
|
// Use entry-level shortcut if available, otherwise fall back to tool-level shortcut (for first entry only)
|
|
539
548
|
const shortcut = toolboxItem.shortcut ?? (displaySecondaryLabel ? tool.shortcut : undefined);
|
|
540
549
|
|
|
@@ -56,13 +56,24 @@ export const checkContenteditableSliceForEmptiness = (
|
|
|
56
56
|
/**
|
|
57
57
|
* Check if we have any tags in the slice
|
|
58
58
|
* We should not ignore them to allow navigation inside (e.g. empty bold tag)
|
|
59
|
+
*
|
|
60
|
+
* When checking the right side, trailing <br> tags are browser artifacts
|
|
61
|
+
* in contenteditable elements (sentinels for cursor positioning after Shift+Enter).
|
|
62
|
+
* If the only significant tags are <br> and there's no text content,
|
|
63
|
+
* the slice is effectively empty — skip the early return.
|
|
59
64
|
*/
|
|
60
|
-
const
|
|
65
|
+
const significantTags = tempDiv.querySelectorAll(
|
|
61
66
|
'img, br, hr, input, area, base, col, embed, link, meta, param, source, track, wbr'
|
|
62
|
-
)
|
|
67
|
+
);
|
|
63
68
|
|
|
64
|
-
if (
|
|
65
|
-
|
|
69
|
+
if (significantTags.length > 0) {
|
|
70
|
+
const isOnlyTrailingBrs = direction === 'right'
|
|
71
|
+
&& tempDiv.querySelectorAll('img, hr, input, area, base, col, embed, link, meta, param, source, track, wbr').length === 0
|
|
72
|
+
&& textContent.trim() === '';
|
|
73
|
+
|
|
74
|
+
if (!isOnlyTrailingBrs) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -260,10 +260,6 @@ const hasHierarchicalRefs = (block: OutputBlockData): boolean => {
|
|
|
260
260
|
export const analyzeDataFormat = (blocks: OutputBlockData[]): DataFormatAnalysis => {
|
|
261
261
|
const foundHierarchicalRefs = blocks.some(hasHierarchicalRefs);
|
|
262
262
|
|
|
263
|
-
if (foundHierarchicalRefs) {
|
|
264
|
-
return { format: 'hierarchical', hasHierarchy: true };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
263
|
// Check if any block uses legacy list format (items[] array)
|
|
268
264
|
const foundLegacyList = blocks.some(isLegacyListBlock);
|
|
269
265
|
|
|
@@ -273,9 +269,15 @@ export const analyzeDataFormat = (blocks: OutputBlockData[]): DataFormatAnalysis
|
|
|
273
269
|
// Check if any block uses legacy callout format (has body field)
|
|
274
270
|
const foundLegacyCallout = blocks.some(isLegacyCalloutBlock);
|
|
275
271
|
|
|
276
|
-
|
|
272
|
+
const hasLegacyBlocks = foundLegacyList || foundLegacyToggle || foundLegacyCallout;
|
|
273
|
+
|
|
274
|
+
if (foundHierarchicalRefs && !hasLegacyBlocks) {
|
|
275
|
+
return { format: 'hierarchical', hasHierarchy: true };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (hasLegacyBlocks) {
|
|
277
279
|
// Check if there's actual nesting for the hasHierarchy flag
|
|
278
|
-
const hasNesting = blocks.some(hasNestedItems) || blocks.some(block =>
|
|
280
|
+
const hasNesting = foundHierarchicalRefs || blocks.some(hasNestedItems) || blocks.some(block =>
|
|
279
281
|
isLegacyToggleListBlock(block) && block.data.body?.blocks !== undefined && block.data.body.blocks.length > 0
|
|
280
282
|
) || blocks.some(block =>
|
|
281
283
|
isLegacyCalloutBlock(block) && block.data.body?.blocks !== undefined && block.data.body.blocks.length > 0
|
package/src/styles/main.css
CHANGED
|
@@ -1164,16 +1164,18 @@
|
|
|
1164
1164
|
* Callout emoji vertical centering for heading first children.
|
|
1165
1165
|
* Headings have taller line boxes than the emoji button, so the emoji
|
|
1166
1166
|
* needs a top margin to shift its center down to match the heading's
|
|
1167
|
-
* first line center. Values: (heading-line-height − emoji-
|
|
1167
|
+
* first line center. Values: (heading-line-height − emoji-line-height) / 2 + 1px mt-px.
|
|
1168
|
+
* Emoji button: text-[1.5rem] leading-[1] → line-height 24px.
|
|
1169
|
+
* H1 text-3xl leading-[1.3] → 39px; H2 text-2xl → 31.2px; H3 text-xl → 26px.
|
|
1168
1170
|
*/
|
|
1169
1171
|
[data-blok-component="callout"] button:has(+ [data-blok-toggle-children] > :first-child h1) {
|
|
1170
|
-
margin-top:
|
|
1172
|
+
margin-top: 8.5px;
|
|
1171
1173
|
}
|
|
1172
1174
|
[data-blok-component="callout"] button:has(+ [data-blok-toggle-children] > :first-child h2) {
|
|
1173
|
-
margin-top:
|
|
1175
|
+
margin-top: 4.6px;
|
|
1174
1176
|
}
|
|
1175
1177
|
[data-blok-component="callout"] button:has(+ [data-blok-toggle-children] > :first-child h3) {
|
|
1176
|
-
margin-top:
|
|
1178
|
+
margin-top: 2px;
|
|
1177
1179
|
}
|
|
1178
1180
|
|
|
1179
1181
|
/**
|
|
@@ -26,5 +26,5 @@ export const DEFAULT_EMOJI = '💡';
|
|
|
26
26
|
|
|
27
27
|
// CSS — Tailwind classes
|
|
28
28
|
export const WRAPPER_STYLES = 'rounded-xl px-4 py-[5px] my-1 flex items-start gap-2';
|
|
29
|
-
export const EMOJI_BUTTON_STYLES = 'text-[1.
|
|
29
|
+
export const EMOJI_BUTTON_STYLES = 'text-[1.5rem] leading-[1] cursor-pointer bg-transparent border-0 px-0 py-[7px] flex-shrink-0 select-none';
|
|
30
30
|
export const CHILDREN_STYLES = 'flex-1 min-w-0';
|
|
@@ -63,6 +63,32 @@ const SKIN_TONE_HANDS: readonly string[] = [
|
|
|
63
63
|
'✋', '✋🏻', '✋🏼', '✋🏽', '✋🏾', '✋🏿',
|
|
64
64
|
];
|
|
65
65
|
|
|
66
|
+
const SKIN_TONE_STORAGE_KEY = 'blok-emoji-skin-tone';
|
|
67
|
+
|
|
68
|
+
function loadSkinTone(): number {
|
|
69
|
+
try {
|
|
70
|
+
const raw = localStorage.getItem(SKIN_TONE_STORAGE_KEY);
|
|
71
|
+
|
|
72
|
+
if (raw === null) {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const n = parseInt(raw, 10);
|
|
77
|
+
|
|
78
|
+
return n >= 0 && n <= 5 ? n : 0;
|
|
79
|
+
} catch {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function saveSkinTone(index: number): void {
|
|
85
|
+
try {
|
|
86
|
+
localStorage.setItem(SKIN_TONE_STORAGE_KEY, String(index));
|
|
87
|
+
} catch {
|
|
88
|
+
// Silently ignore — storage quota or access denied
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
66
92
|
/** Dice SVG for the random button. */
|
|
67
93
|
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
94
|
|
|
@@ -138,6 +164,17 @@ export class EmojiPicker {
|
|
|
138
164
|
this._filterInput.value = '';
|
|
139
165
|
this._element.setAttribute('data-theme', this.resolveTheme());
|
|
140
166
|
|
|
167
|
+
const storedTone = loadSkinTone();
|
|
168
|
+
|
|
169
|
+
if (storedTone !== this._skinTone) {
|
|
170
|
+
this._skinTone = storedTone;
|
|
171
|
+
this._skinToneToggle.textContent = SKIN_TONE_HANDS[storedTone];
|
|
172
|
+
|
|
173
|
+
for (const [i, btn] of this._skinToneButtons.entries()) {
|
|
174
|
+
this.applySkinToneActiveStyle(btn, i === storedTone);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
141
178
|
if (this._allEmojis.length === 0) {
|
|
142
179
|
this._allEmojis = await loadEmojiData();
|
|
143
180
|
}
|
|
@@ -246,7 +283,6 @@ export class EmojiPicker {
|
|
|
246
283
|
randomBtn.type = 'button';
|
|
247
284
|
randomBtn.setAttribute('data-emoji-picker-random', '');
|
|
248
285
|
randomBtn.setAttribute('aria-label', this.i18n.t(PICK_RANDOM_KEY));
|
|
249
|
-
randomBtn.title = this.i18n.t(PICK_RANDOM_KEY);
|
|
250
286
|
randomBtn.className = [
|
|
251
287
|
'flex-shrink-0 w-[34px] h-[34px] flex items-center justify-center rounded-lg',
|
|
252
288
|
'text-neutral-400 hover:bg-neutral-100 hover:text-neutral-600',
|
|
@@ -262,7 +298,6 @@ export class EmojiPicker {
|
|
|
262
298
|
removeBtn.type = 'button';
|
|
263
299
|
removeBtn.setAttribute('data-emoji-picker-remove', '');
|
|
264
300
|
removeBtn.setAttribute('aria-label', this.i18n.t(REMOVE_EMOJI_KEY));
|
|
265
|
-
removeBtn.title = this.i18n.t(REMOVE_EMOJI_KEY);
|
|
266
301
|
removeBtn.className = [
|
|
267
302
|
'flex-shrink-0 w-[34px] h-[34px] flex items-center justify-center rounded-lg',
|
|
268
303
|
'text-neutral-400 hover:bg-neutral-100 hover:text-neutral-600',
|
|
@@ -401,6 +436,7 @@ export class EmojiPicker {
|
|
|
401
436
|
|
|
402
437
|
private setSkinTone(index: number): void {
|
|
403
438
|
this._skinTone = index;
|
|
439
|
+
saveSkinTone(index);
|
|
404
440
|
|
|
405
441
|
// Update the hand toggle to reflect current skin tone
|
|
406
442
|
this._skinToneToggle.textContent = SKIN_TONE_HANDS[index];
|
|
@@ -653,7 +689,6 @@ export class EmojiPicker {
|
|
|
653
689
|
const btn = document.createElement('button');
|
|
654
690
|
btn.type = 'button';
|
|
655
691
|
btn.textContent = this.getSkinnedNative(emoji);
|
|
656
|
-
btn.title = this.getDisplayName(emoji);
|
|
657
692
|
btn.setAttribute('data-emoji-native', emoji.native);
|
|
658
693
|
btn.className = [
|
|
659
694
|
'aspect-square flex items-center justify-center',
|
|
@@ -360,6 +360,7 @@ export class CalloutTool implements BlockTool {
|
|
|
360
360
|
titleKey: 'callout',
|
|
361
361
|
name: TOOL_NAME,
|
|
362
362
|
searchTerms: ['callout', 'note', 'info', 'warning', 'tip', 'alert'],
|
|
363
|
+
searchTermKeys: ['callout', 'note', 'info', 'warning', 'tip', 'alert'],
|
|
363
364
|
};
|
|
364
365
|
}
|
|
365
366
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BlockTool,
|
|
3
|
+
BlockToolConstructorOptions,
|
|
4
|
+
PasteConfig,
|
|
5
|
+
SanitizerConfig,
|
|
6
|
+
ToolboxConfig,
|
|
7
|
+
} from '../../../types';
|
|
8
|
+
import type { DividerData } from './types';
|
|
9
|
+
import { IconDivider } from '../../components/icons';
|
|
10
|
+
import { twMerge } from '../../components/utils/tw';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Divider block tool — renders a thin horizontal line separator.
|
|
14
|
+
* Contentless void block with no editable content or settings.
|
|
15
|
+
*/
|
|
16
|
+
export class DividerTool implements BlockTool {
|
|
17
|
+
/**
|
|
18
|
+
* Rendered wrapper element
|
|
19
|
+
*/
|
|
20
|
+
private element: HTMLElement | null = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param _options - block tool constructor options (unused for divider)
|
|
24
|
+
*/
|
|
25
|
+
constructor(_options: BlockToolConstructorOptions<DividerData>) {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Render a wrapper with a semantic <hr> element inside.
|
|
29
|
+
* Wrapper uses padding for spacing and minimal line-height so the
|
|
30
|
+
* toolbar positioning algorithm centers correctly on the divider line.
|
|
31
|
+
*/
|
|
32
|
+
public render(): HTMLElement {
|
|
33
|
+
const wrapper = document.createElement('div');
|
|
34
|
+
|
|
35
|
+
wrapper.className = twMerge('py-3', 'leading-[1px]');
|
|
36
|
+
|
|
37
|
+
const hr = document.createElement('hr');
|
|
38
|
+
|
|
39
|
+
hr.className = twMerge('border-t', 'border-border-primary', 'border-b-0', 'border-l-0', 'border-r-0');
|
|
40
|
+
wrapper.appendChild(hr);
|
|
41
|
+
this.element = wrapper;
|
|
42
|
+
|
|
43
|
+
return wrapper;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return empty data — divider has no content
|
|
48
|
+
*/
|
|
49
|
+
public save(): DividerData {
|
|
50
|
+
return {} as DividerData;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Always valid — nothing to validate
|
|
55
|
+
*/
|
|
56
|
+
public validate(_data: DividerData): boolean {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Toolbox appearance
|
|
62
|
+
*/
|
|
63
|
+
public static get toolbox(): ToolboxConfig {
|
|
64
|
+
return {
|
|
65
|
+
icon: IconDivider,
|
|
66
|
+
titleKey: 'divider',
|
|
67
|
+
shortcut: '---',
|
|
68
|
+
searchTerms: ['hr', 'line', 'separator', 'rule', '---', 'divider', 'delimiter', 'splitter'],
|
|
69
|
+
searchTermKeys: ['divider', 'separator', 'delimiter', 'splitter'],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Divider works in read-only mode
|
|
75
|
+
*/
|
|
76
|
+
public static get isReadOnlySupported(): boolean {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Paste three-or-more hyphens to create a divider
|
|
82
|
+
*/
|
|
83
|
+
public static get pasteConfig(): PasteConfig {
|
|
84
|
+
return {
|
|
85
|
+
patterns: {
|
|
86
|
+
divider: /^-{3,}$/,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Nothing to sanitize — no HTML content
|
|
93
|
+
*/
|
|
94
|
+
public static get sanitize(): SanitizerConfig {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -1096,6 +1096,7 @@ export class Header implements BlockTool {
|
|
|
1096
1096
|
name: `header-${level.number}`,
|
|
1097
1097
|
data: { level: level.number },
|
|
1098
1098
|
searchTerms: [`h${level.number}`, 'title', 'header', 'heading'],
|
|
1099
|
+
searchTermKeys: ['title', 'header', 'heading'],
|
|
1099
1100
|
shortcut: '#'.repeat(level.number),
|
|
1100
1101
|
}));
|
|
1101
1102
|
|
|
@@ -1114,6 +1115,7 @@ export class Header implements BlockTool {
|
|
|
1114
1115
|
name: `toggle-header-${level.number}`,
|
|
1115
1116
|
data: { level: level.number, isToggleable: true },
|
|
1116
1117
|
searchTerms: ['toggle', 'heading', `h${level.number}`, 'collapsible'],
|
|
1118
|
+
searchTermKeys: ['toggle', 'heading', 'collapsible'],
|
|
1117
1119
|
shortcut: '>' + '#'.repeat(level.number),
|
|
1118
1120
|
}));
|
|
1119
1121
|
|
package/src/tools/index.ts
CHANGED
|
@@ -26,6 +26,8 @@ export { ListItem as List } from './list';
|
|
|
26
26
|
export { Table } from './table';
|
|
27
27
|
export { ToggleItem as Toggle } from './toggle';
|
|
28
28
|
export { CalloutTool as Callout } from './callout';
|
|
29
|
+
export { DividerTool as Divider } from './divider';
|
|
30
|
+
export { Quote } from './quote';
|
|
29
31
|
|
|
30
32
|
// Inline tools
|
|
31
33
|
export { BoldInlineTool as Bold } from '../components/inline-tools/inline-tool-bold';
|
|
@@ -45,6 +47,8 @@ export const defaultBlockTools = {
|
|
|
45
47
|
table: {},
|
|
46
48
|
toggle: {},
|
|
47
49
|
callout: {},
|
|
50
|
+
divider: {},
|
|
51
|
+
quote: {},
|
|
48
52
|
} as const;
|
|
49
53
|
|
|
50
54
|
export const defaultInlineTools = {
|
|
@@ -42,6 +42,7 @@ export const getToolboxConfig = (): ToolboxConfig => [
|
|
|
42
42
|
data: { style: 'unordered' },
|
|
43
43
|
name: 'bulleted-list',
|
|
44
44
|
searchTerms: ['ul', 'bullet', 'unordered', 'list'],
|
|
45
|
+
searchTermKeys: ['bullet', 'unordered', 'list'],
|
|
45
46
|
shortcut: '-',
|
|
46
47
|
},
|
|
47
48
|
{
|
|
@@ -51,6 +52,7 @@ export const getToolboxConfig = (): ToolboxConfig => [
|
|
|
51
52
|
data: { style: 'ordered' },
|
|
52
53
|
name: 'numbered-list',
|
|
53
54
|
searchTerms: ['ol', 'ordered', 'number', 'list'],
|
|
55
|
+
searchTermKeys: ['ordered', 'number', 'list'],
|
|
54
56
|
shortcut: '1.',
|
|
55
57
|
},
|
|
56
58
|
{
|
|
@@ -60,6 +62,7 @@ export const getToolboxConfig = (): ToolboxConfig => [
|
|
|
60
62
|
data: { style: 'checklist' },
|
|
61
63
|
name: 'check-list',
|
|
62
64
|
searchTerms: ['checkbox', 'task', 'todo', 'check', 'list'],
|
|
65
|
+
searchTermKeys: ['checkbox', 'task', 'todo', 'check', 'list'],
|
|
63
66
|
shortcut: '[]',
|
|
64
67
|
},
|
|
65
68
|
];
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
API,
|
|
3
|
+
BlockTool,
|
|
4
|
+
BlockToolConstructorOptions,
|
|
5
|
+
BlockToolData,
|
|
6
|
+
PasteEvent,
|
|
7
|
+
ToolboxConfig,
|
|
8
|
+
ConversionConfig,
|
|
9
|
+
ToolSanitizerConfig,
|
|
10
|
+
PasteConfig,
|
|
11
|
+
} from '../../../types';
|
|
12
|
+
import type { MenuConfig } from '../../../types/tools/menu-config';
|
|
13
|
+
import { DATA_ATTR } from '../../components/constants';
|
|
14
|
+
import { IconQuote } from '../../components/icons';
|
|
15
|
+
import { stripFakeBackgroundElements } from '../../components/utils';
|
|
16
|
+
import { PLACEHOLDER_FOCUS_ONLY_CLASSES, setupPlaceholder } from '../../components/utils/placeholder';
|
|
17
|
+
import { twMerge } from '../../components/utils/tw';
|
|
18
|
+
|
|
19
|
+
export interface QuoteData extends BlockToolData {
|
|
20
|
+
text: string;
|
|
21
|
+
size: 'default' | 'large';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_PLACEHOLDER = 'tools.quote.placeholder';
|
|
25
|
+
|
|
26
|
+
const BASE_CLASSES = [
|
|
27
|
+
'border-l-[3px]',
|
|
28
|
+
'border-current',
|
|
29
|
+
'pl-[0.9em]',
|
|
30
|
+
'pr-[0.9em]',
|
|
31
|
+
'py-[0.2em]',
|
|
32
|
+
'leading-[1.5]',
|
|
33
|
+
'outline-hidden',
|
|
34
|
+
'mt-[0.3em]',
|
|
35
|
+
'mb-[0.3em]',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const LARGE_CLASS = 'text-[1.2em]';
|
|
39
|
+
|
|
40
|
+
export class Quote implements BlockTool {
|
|
41
|
+
private api: API;
|
|
42
|
+
private readOnly: boolean;
|
|
43
|
+
private _data: QuoteData;
|
|
44
|
+
private _element: HTMLQuoteElement | null = null;
|
|
45
|
+
|
|
46
|
+
constructor({ data, api, readOnly }: BlockToolConstructorOptions<QuoteData>) {
|
|
47
|
+
this.api = api;
|
|
48
|
+
this.readOnly = readOnly;
|
|
49
|
+
this._data = {
|
|
50
|
+
text: data?.text ?? '',
|
|
51
|
+
size: data?.size ?? 'default',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (!this.readOnly) {
|
|
55
|
+
this.onKeyUp = this.onKeyUp.bind(this);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public onKeyUp(e: KeyboardEvent): void {
|
|
60
|
+
if (e.code !== 'Backspace' && e.code !== 'Delete') {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!this._element) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this._element.textContent === '') {
|
|
69
|
+
this._element.innerHTML = '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public render(): HTMLQuoteElement {
|
|
74
|
+
const el = document.createElement('blockquote');
|
|
75
|
+
|
|
76
|
+
el.className = twMerge(
|
|
77
|
+
this.api.styles.block,
|
|
78
|
+
BASE_CLASSES,
|
|
79
|
+
PLACEHOLDER_FOCUS_ONLY_CLASSES,
|
|
80
|
+
this._data.size === 'large' ? LARGE_CLASS : ''
|
|
81
|
+
);
|
|
82
|
+
el.setAttribute(DATA_ATTR.tool, 'quote');
|
|
83
|
+
el.contentEditable = 'false';
|
|
84
|
+
|
|
85
|
+
if (this._data.text) {
|
|
86
|
+
el.innerHTML = this._data.text;
|
|
87
|
+
} else if (this.readOnly) {
|
|
88
|
+
el.innerHTML = '<br>';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!this.readOnly) {
|
|
92
|
+
el.contentEditable = 'true';
|
|
93
|
+
el.addEventListener('keyup', this.onKeyUp);
|
|
94
|
+
setupPlaceholder(el, this.api.i18n.t(DEFAULT_PLACEHOLDER), 'data-blok-placeholder-active');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this._element = el;
|
|
98
|
+
|
|
99
|
+
return el;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public save(blockContent: HTMLQuoteElement): QuoteData {
|
|
103
|
+
return {
|
|
104
|
+
text: stripFakeBackgroundElements(blockContent.innerHTML),
|
|
105
|
+
size: this._data.size,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public validate(savedData: QuoteData): boolean {
|
|
110
|
+
return savedData.text.trim() !== '';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public merge(data: QuoteData): void {
|
|
114
|
+
if (!this._element) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this._data.text += data.text;
|
|
119
|
+
|
|
120
|
+
const wrapper = document.createElement('div');
|
|
121
|
+
wrapper.innerHTML = data.text.trim();
|
|
122
|
+
const fragment = document.createDocumentFragment();
|
|
123
|
+
fragment.append(...Array.from(wrapper.childNodes));
|
|
124
|
+
|
|
125
|
+
this._element.appendChild(fragment);
|
|
126
|
+
this._element.normalize();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public renderSettings(): MenuConfig {
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
icon: IconQuote,
|
|
133
|
+
title: this.api.i18n.t('tools.quote.size'),
|
|
134
|
+
name: 'quote-size',
|
|
135
|
+
children: {
|
|
136
|
+
items: [
|
|
137
|
+
{
|
|
138
|
+
icon: IconQuote,
|
|
139
|
+
title: this.api.i18n.t('tools.quote.defaultSize'),
|
|
140
|
+
onActivate: (): void => this.setSize('default'),
|
|
141
|
+
closeOnActivate: true,
|
|
142
|
+
isActive: this._data.size === 'default',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
icon: IconQuote,
|
|
146
|
+
title: this.api.i18n.t('tools.quote.largeSize'),
|
|
147
|
+
onActivate: (): void => this.setSize('large'),
|
|
148
|
+
closeOnActivate: true,
|
|
149
|
+
isActive: this._data.size === 'large',
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private setSize(size: 'default' | 'large'): void {
|
|
158
|
+
this._data.size = size;
|
|
159
|
+
|
|
160
|
+
if (this._element) {
|
|
161
|
+
this._element.className = twMerge(
|
|
162
|
+
this.api.styles.block,
|
|
163
|
+
BASE_CLASSES,
|
|
164
|
+
PLACEHOLDER_FOCUS_ONLY_CLASSES,
|
|
165
|
+
size === 'large' ? LARGE_CLASS : ''
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public onPaste(event: PasteEvent): void {
|
|
171
|
+
const detail = event.detail;
|
|
172
|
+
|
|
173
|
+
if (!('data' in detail)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const content = detail.data as HTMLElement;
|
|
178
|
+
|
|
179
|
+
this._data = {
|
|
180
|
+
text: content.innerHTML,
|
|
181
|
+
size: this._data.size,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (this._element) {
|
|
185
|
+
this._element.innerHTML = this._data.text || '';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public static get toolbox(): ToolboxConfig {
|
|
190
|
+
return {
|
|
191
|
+
icon: IconQuote,
|
|
192
|
+
title: 'Quote',
|
|
193
|
+
titleKey: 'quote',
|
|
194
|
+
searchTerms: ['quote', 'blockquote', 'citation'],
|
|
195
|
+
searchTermKeys: ['quote', 'blockquote', 'citation'],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public static get conversionConfig(): ConversionConfig {
|
|
200
|
+
return {
|
|
201
|
+
export: 'text',
|
|
202
|
+
import: 'text',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public static get sanitize(): ToolSanitizerConfig {
|
|
207
|
+
return {
|
|
208
|
+
text: {
|
|
209
|
+
br: true,
|
|
210
|
+
b: true,
|
|
211
|
+
i: true,
|
|
212
|
+
a: true,
|
|
213
|
+
mark: {
|
|
214
|
+
style: true,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public static get isReadOnlySupported(): boolean {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public static get pasteConfig(): PasteConfig {
|
|
225
|
+
return {
|
|
226
|
+
tags: ['BLOCKQUOTE'],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
package/src/tools/table/index.ts
CHANGED
|
@@ -374,6 +374,7 @@ export class ToggleItem implements BlockTool {
|
|
|
374
374
|
titleKey: 'toggleList',
|
|
375
375
|
name: TOOL_NAME,
|
|
376
376
|
searchTerms: ['toggle', 'collapse', 'expand', 'accordion'],
|
|
377
|
+
searchTermKeys: ['toggle', 'collapse', 'expand', 'accordion'],
|
|
377
378
|
shortcut: '>',
|
|
378
379
|
};
|
|
379
380
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BlockTool, BlockToolConstructable, BlockToolConstructorOptions, BlockToolData } from './block-tool';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Divider Tool's input and output data format.
|
|
5
|
+
* Empty — dividers have no configurable properties.
|
|
6
|
+
*/
|
|
7
|
+
export interface DividerData extends BlockToolData {}
|
|
8
|
+
|
|
9
|
+
export interface DividerConstructable extends BlockToolConstructable {
|
|
10
|
+
new(options: BlockToolConstructorOptions<DividerData>): BlockTool;
|
|
11
|
+
}
|
|
@@ -53,6 +53,13 @@ export interface ToolboxConfigEntry {
|
|
|
53
53
|
*/
|
|
54
54
|
searchTerms?: string[];
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Translation keys for localized search aliases (e.g., ['divider', 'separator']).
|
|
58
|
+
* Each key is resolved via `searchTerms.{key}` in the i18n system.
|
|
59
|
+
* The translated strings are added to searchTerms at runtime for multilingual search.
|
|
60
|
+
*/
|
|
61
|
+
searchTermKeys?: string[];
|
|
62
|
+
|
|
56
63
|
/**
|
|
57
64
|
* Shortcut hint to display in the toolbox (e.g., '#', '##', '-', '1.', '[]').
|
|
58
65
|
* This is displayed as a secondary label next to the tool title.
|