@jackuait/blok 0.7.3 → 0.8.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.
Files changed (230) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-BmlbETK7.mjs → blok-Ddy2vDUq.mjs} +2158 -2147
  3. package/dist/chunks/{constants-WhLyFkza.mjs → constants-CuG2mgIj.mjs} +84 -84
  4. package/dist/chunks/{i18next-loader-CZARkla1.mjs → i18next-loader-p-7ioTwr.mjs} +1 -1
  5. package/dist/chunks/{lightweight-i18n-BQa0F2X6.mjs → lightweight-i18n-BPeH69Dl.mjs} +5 -1
  6. package/dist/{messages-BbLCMWln2.mjs → chunks/messages--aM83pib2.mjs} +14 -1
  7. package/dist/chunks/{messages-ni0ahgYk2.mjs → messages-4OvVdaG52.mjs} +14 -1
  8. package/dist/chunks/{messages-DnPkoMz1.mjs → messages-5ohIWynJ.mjs} +14 -1
  9. package/dist/{messages-CzSLUJQt.mjs → chunks/messages-5thhSeME.mjs} +14 -1
  10. package/dist/chunks/{messages-hngFJrES2.mjs → messages-8pf7gRQm2.mjs} +14 -1
  11. package/dist/chunks/{messages-DcPtg90i.mjs → messages-B-i-d1Bc.mjs} +14 -1
  12. package/dist/chunks/{messages-CRxkRJRN.mjs → messages-B9zocutJ.mjs} +14 -1
  13. package/dist/chunks/{messages-BUy3odZo.mjs → messages-BAC7nLeM.mjs} +14 -1
  14. package/dist/chunks/{messages-DjOY_EqX.mjs → messages-BARPMN7R.mjs} +14 -1
  15. package/dist/{messages-Bsz7Qgj_.mjs → chunks/messages-BAgTIgPH.mjs} +15 -2
  16. package/dist/{messages-C7R0m6oE2.mjs → chunks/messages-BDTgiBJY2.mjs} +14 -1
  17. package/dist/chunks/{messages-BM1Su_Uy2.mjs → messages-BHf_VcXb2.mjs} +14 -1
  18. package/dist/{messages-wRvz0vQ3.mjs → chunks/messages-BIrkzbBP.mjs} +14 -1
  19. package/dist/chunks/{messages-DotEkUpQ.mjs → messages-BKjLgO5d.mjs} +14 -1
  20. package/dist/{messages-gldjQk7M.mjs → chunks/messages-BMzmli1K.mjs} +14 -1
  21. package/dist/chunks/{messages-CficsmSH2.mjs → messages-BO_jtRbZ2.mjs} +14 -1
  22. package/dist/{messages-B2bHgIcC2.mjs → chunks/messages-B_fFjpX12.mjs} +14 -1
  23. package/dist/chunks/{messages-CnzaTbel.mjs → messages-Bdk5tBNu.mjs} +14 -1
  24. package/dist/{messages-CRMZ79Xf.mjs → chunks/messages-BgaGPFuy2.mjs} +14 -1
  25. package/dist/{messages-DCOKudVN.mjs → chunks/messages-BsYzg2le.mjs} +14 -1
  26. package/dist/chunks/{messages-Dxrg70jo.mjs → messages-BtS6JWMT.mjs} +14 -1
  27. package/dist/{messages-Cgy54529.mjs → chunks/messages-BvlSf_pu.mjs} +14 -1
  28. package/dist/{messages-oH0ADQ362.mjs → chunks/messages-C03I_oR-2.mjs} +14 -1
  29. package/dist/chunks/{messages-B_nVGWdk.mjs → messages-CDJLoStb.mjs} +14 -1
  30. package/dist/chunks/{messages-ClOxDE0y2.mjs → messages-CH_cexo62.mjs} +14 -1
  31. package/dist/chunks/{messages-DoDbCS02.mjs → messages-CIBuZccC.mjs} +14 -1
  32. package/dist/{messages-CkmVEyEQ2.mjs → chunks/messages-CIZkNCpW.mjs} +14 -1
  33. package/dist/{messages-CJYE0_hr2.mjs → chunks/messages-CIugNDDO2.mjs} +14 -1
  34. package/dist/{messages-DpY9s4Qi2.mjs → chunks/messages-COkQfKa72.mjs} +14 -1
  35. package/dist/{messages-Bw-GC0m5.mjs → chunks/messages-CSpfBhlK2.mjs} +14 -1
  36. package/dist/chunks/{messages-tBHnC2Rj2.mjs → messages-CWp6-Y8f2.mjs} +14 -1
  37. package/dist/{messages-GrVSCmXW.mjs → chunks/messages-Co4WFeQ8.mjs} +14 -1
  38. package/dist/{messages-voUPclMU.mjs → chunks/messages-CoRQE_Jc.mjs} +14 -1
  39. package/dist/{messages-DGZQXeav2.mjs → chunks/messages-CuRN1_ep2.mjs} +14 -1
  40. package/dist/{messages-BwdowdYD.mjs → chunks/messages-D0IKicWg.mjs} +14 -1
  41. package/dist/chunks/{messages-1C3OS98e.mjs → messages-D14soBOO.mjs} +14 -1
  42. package/dist/chunks/{messages-nE9Ko73n2.mjs → messages-D1SqLcxI2.mjs} +14 -1
  43. package/dist/{messages-COU4L-pL2.mjs → chunks/messages-D2uBlGXR2.mjs} +14 -1
  44. package/dist/{messages-m1uf_AMy2.mjs → chunks/messages-DDpgr0B1.mjs} +14 -1
  45. package/dist/{messages-CB0RKGVM.mjs → chunks/messages-DEGzGmEQ2.mjs} +14 -1
  46. package/dist/{messages--XEfVx572.mjs → chunks/messages-DKChC8Qu.mjs} +14 -1
  47. package/dist/{messages-BP8ZuVaD.mjs → chunks/messages-DKCoHa5D.mjs} +14 -1
  48. package/dist/chunks/{messages-C3AJz_i6.mjs → messages-DPoPrTiZ.mjs} +14 -1
  49. package/dist/{messages-CaJRIGUu2.mjs → chunks/messages-DQOk-dTH.mjs} +14 -1
  50. package/dist/chunks/{messages-uLIUXFmU.mjs → messages-DUp8NnKT.mjs} +15 -2
  51. package/dist/chunks/{messages-rJdSnvyi.mjs → messages-DVg69mRj.mjs} +14 -1
  52. package/dist/chunks/{messages-Cq6wj6FG.mjs → messages-DeLzropc.mjs} +14 -1
  53. package/dist/{messages-BlnZ8CkJ2.mjs → chunks/messages-Df69rfTF2.mjs} +15 -2
  54. package/dist/{messages-BZgGD0zf2.mjs → chunks/messages-DfsHFEWa.mjs} +14 -1
  55. package/dist/chunks/{messages-yJmwc3zD.mjs → messages-DgZnRQRS.mjs} +14 -1
  56. package/dist/chunks/{messages-m6bLP64R2.mjs → messages-DhGHk-Ma2.mjs} +14 -1
  57. package/dist/{messages-3aRjZXpv.mjs → chunks/messages-Di7mfvB8.mjs} +14 -1
  58. package/dist/chunks/{messages-C1iQkKu82.mjs → messages-DlM9TmqS2.mjs} +14 -1
  59. package/dist/chunks/{messages-CejEH4FW2.mjs → messages-DmrsEYQm2.mjs} +14 -1
  60. package/dist/chunks/{messages-DK6pBwD2.mjs → messages-Dr-Ig3sw.mjs} +14 -1
  61. package/dist/{messages-BOxe7ewT.mjs → chunks/messages-DrXNb1gu.mjs} +14 -1
  62. package/dist/chunks/{messages-CvLXClh9.mjs → messages-EDTq4Q52.mjs} +14 -1
  63. package/dist/{messages-huTzItxA.mjs → chunks/messages-Ehx9YYeb2.mjs} +14 -1
  64. package/dist/chunks/{messages-CxTq0x772.mjs → messages-FCmAVA792.mjs} +14 -1
  65. package/dist/{messages-q7HzQPVt.mjs → chunks/messages-LL3Tflph.mjs} +14 -1
  66. package/dist/chunks/{messages-D9qyilS0.mjs → messages-N72K1hw3.mjs} +14 -1
  67. package/dist/chunks/{messages-C9XSSqS5.mjs → messages-Nz8C7Znm.mjs} +15 -2
  68. package/dist/chunks/{messages-D6SAC8Lc2.mjs → messages-OIelQDL32.mjs} +14 -1
  69. package/dist/{messages-BQJzUYP-.mjs → chunks/messages-SsrFJhTN.mjs} +14 -1
  70. package/dist/{messages-CVBsztOg.mjs → chunks/messages-ohtcmr1w.mjs} +14 -1
  71. package/dist/chunks/{messages-CVzvKl6U.mjs → messages-stUQR58d.mjs} +14 -1
  72. package/dist/{messages-DlrZrm3s.mjs → chunks/messages-wUoSWFsJ2.mjs} +14 -1
  73. package/dist/chunks/{tools-BCb5bMO3.mjs → tools-F4UyWgGo.mjs} +1086 -802
  74. package/dist/full.mjs +15 -13
  75. package/dist/locales.mjs +72 -68
  76. package/dist/{messages-mVFAkdcY.mjs → messages-1_FCq0It.mjs} +14 -1
  77. package/dist/{messages-DkkrjINb2.mjs → messages-8zo-T-Nx2.mjs} +14 -1
  78. package/dist/{chunks/messages-DacahKek.mjs → messages-B21zLG6b.mjs} +14 -1
  79. package/dist/{messages-BBgyeB_N.mjs → messages-B9ythxux.mjs} +14 -1
  80. package/dist/{messages-C_RPN2GV.mjs → messages-BAZK-8Zb.mjs} +14 -1
  81. package/dist/{messages-D6Sr5cUE.mjs → messages-BB8umWL1.mjs} +14 -1
  82. package/dist/{chunks/messages-Dhe8_mnQ.mjs → messages-BD_U2EnE.mjs} +14 -1
  83. package/dist/{messages-6G0Eia-2.mjs → messages-BRC9E_sp.mjs} +14 -1
  84. package/dist/{messages-pgqtPci-.mjs → messages-BSLYh59S.mjs} +14 -1
  85. package/dist/{messages-K7ROT6ea.mjs → messages-BSwhWcYw.mjs} +14 -1
  86. package/dist/{messages-BztXgybv2.mjs → messages-BTR3QlIb2.mjs} +14 -1
  87. package/dist/{messages-C0gyqo4h2.mjs → messages-BZXBdD_S2.mjs} +14 -1
  88. package/dist/{chunks/messages-DDRCk44J2.mjs → messages-Bm8I_Li12.mjs} +15 -2
  89. package/dist/{chunks/messages-BKtWlK39.mjs → messages-BrcgNZOJ.mjs} +14 -1
  90. package/dist/{messages-Be1CCcsp2.mjs → messages-BzZ8LahA2.mjs} +14 -1
  91. package/dist/{chunks/messages-B_90PYaG.mjs → messages-C4HpNHfK.mjs} +14 -1
  92. package/dist/{messages-B8M4YRFO2.mjs → messages-C5hD5pSd2.mjs} +14 -1
  93. package/dist/{messages-ruU_e2LK.mjs → messages-C7Rz00Tp.mjs} +14 -1
  94. package/dist/{chunks/messages-C1lqY56F2.mjs → messages-C92tAUYT2.mjs} +14 -1
  95. package/dist/{messages-Kye1BINC.mjs → messages-C9LsEUfG.mjs} +14 -1
  96. package/dist/{chunks/messages-tg78NAmW.mjs → messages-CHWfj4ik.mjs} +14 -1
  97. package/dist/{chunks/messages-DziA-L3p.mjs → messages-CHeucLGl2.mjs} +14 -1
  98. package/dist/{messages-eCyczLYY.mjs → messages-CIxT1nSh.mjs} +14 -1
  99. package/dist/{chunks/messages-Cb-x47kY2.mjs → messages-CKX9iXIb2.mjs} +14 -1
  100. package/dist/{chunks/messages-BjkDJuqh.mjs → messages-CKmmJ9tW.mjs} +14 -1
  101. package/dist/{chunks/messages-Cn5n0nHe2.mjs → messages-CTFwu5-h2.mjs} +14 -1
  102. package/dist/{messages-DAssrN5L2.mjs → messages-CTPFrtK92.mjs} +14 -1
  103. package/dist/{chunks/messages-6EJxSImH.mjs → messages-CWzET_9H2.mjs} +14 -1
  104. package/dist/{chunks/messages-BZ45xBlV.mjs → messages-CkIRmpfZ2.mjs} +14 -1
  105. package/dist/{chunks/messages-kHTrX3wo2.mjs → messages-CmoTIebG2.mjs} +14 -1
  106. package/dist/{messages-DZbsds_k2.mjs → messages-Co26RSCV2.mjs} +14 -1
  107. package/dist/{messages-DrouoDgp.mjs → messages-CqNzlpWi.mjs} +15 -2
  108. package/dist/{chunks/messages-DDr8J4FE.mjs → messages-CrWsU4Xw2.mjs} +14 -1
  109. package/dist/{messages-TseLyyoU.mjs → messages-CsmTziC6.mjs} +14 -1
  110. package/dist/{messages-CAffVeAE2.mjs → messages-CsnglxbV2.mjs} +14 -1
  111. package/dist/{messages-CE305J0p.mjs → messages-Cu7Lr1wp.mjs} +14 -1
  112. package/dist/{chunks/messages-Ul43l29K2.mjs → messages-Cy3Ne_M9.mjs} +14 -1
  113. package/dist/{chunks/messages-CzCqu58X2.mjs → messages-CzZAfGif.mjs} +14 -1
  114. package/dist/{messages-C_4VGaBC.mjs → messages-D5rnT-BC.mjs} +14 -1
  115. package/dist/{messages-MaHxNgKA.mjs → messages-D8iCBMg7.mjs} +14 -1
  116. package/dist/{chunks/messages-BtNOlsMj.mjs → messages-DDJOu049.mjs} +14 -1
  117. package/dist/{messages-BU9luYgO.mjs → messages-DDiP6yex.mjs} +14 -1
  118. package/dist/{messages-DB9U3VIh.mjs → messages-DHJ1fZLL.mjs} +14 -1
  119. package/dist/{chunks/messages-3ePgbbpx2.mjs → messages-DToWAonn2.mjs} +14 -1
  120. package/dist/{messages-BxQ1gzJF2.mjs → messages-DV29fJMD2.mjs} +14 -1
  121. package/dist/{chunks/messages-DJWRON2S.mjs → messages-D_-rh8gl.mjs} +14 -1
  122. package/dist/{messages-e-KHuxtY2.mjs → messages-D_cAZ4Ic2.mjs} +14 -1
  123. package/dist/{chunks/messages-pKUiAqlX2.mjs → messages-Dc7ZzqYN.mjs} +14 -1
  124. package/dist/{chunks/messages-JRavIeeW.mjs → messages-DiSeSE8p.mjs} +14 -1
  125. package/dist/{chunks/messages-DbS9Oibb.mjs → messages-Djhu5RJd.mjs} +14 -1
  126. package/dist/{messages-BdJ1lCo_.mjs → messages-Dr9L1psl.mjs} +15 -2
  127. package/dist/{chunks/messages-IJhiftj5.mjs → messages-EIeWKoc5.mjs} +14 -1
  128. package/dist/{chunks/messages-CteKp81J.mjs → messages-EwoT2jof.mjs} +14 -1
  129. package/dist/{messages-C8iAUPzI.mjs → messages-F7cRf-20.mjs} +14 -1
  130. package/dist/{messages-B9qltgXa.mjs → messages-JZhs_0pf.mjs} +14 -1
  131. package/dist/{chunks/messages-C232njMF2.mjs → messages-JwMkLben.mjs} +14 -1
  132. package/dist/{messages-Cb5JJ8C_2.mjs → messages-LyzjEEIj2.mjs} +14 -1
  133. package/dist/{chunks/messages-DQ4VyVJf2.mjs → messages-SepwOOcg.mjs} +14 -1
  134. package/dist/{chunks/messages-DfTU2I8J.mjs → messages-TI0u6Ked.mjs} +14 -1
  135. package/dist/{chunks/messages-D7aoKTPD.mjs → messages-Tx25QErT.mjs} +14 -1
  136. package/dist/{chunks/messages-D9nReG4C2.mjs → messages-bRqMCja-2.mjs} +14 -1
  137. package/dist/{chunks/messages-h474TGR72.mjs → messages-lEyiemqU2.mjs} +14 -1
  138. package/dist/{messages-BENRci-_2.mjs → messages-mVLfVtQX2.mjs} +14 -1
  139. package/dist/{messages-CEhkWwqI.mjs → messages-ouO9js8Z.mjs} +14 -1
  140. package/dist/{chunks/messages-6z-ULVyk.mjs → messages-ouRGTAKo2.mjs} +14 -1
  141. package/dist/{chunks/messages-DNrK8lCg2.mjs → messages-qV14y_oA2.mjs} +14 -1
  142. package/dist/{chunks/messages-Yk__PXZQ.mjs → messages-rM6YFLZH.mjs} +15 -2
  143. package/dist/react.mjs +2 -2
  144. package/dist/tools.mjs +3 -3
  145. package/package.json +1 -1
  146. package/src/components/i18n/locales/am/messages.json +14 -1
  147. package/src/components/i18n/locales/ar/messages.json +14 -1
  148. package/src/components/i18n/locales/az/messages.json +14 -1
  149. package/src/components/i18n/locales/bg/messages.json +15 -2
  150. package/src/components/i18n/locales/bn/messages.json +14 -1
  151. package/src/components/i18n/locales/bs/messages.json +14 -1
  152. package/src/components/i18n/locales/cs/messages.json +14 -1
  153. package/src/components/i18n/locales/da/messages.json +14 -1
  154. package/src/components/i18n/locales/de/messages.json +14 -1
  155. package/src/components/i18n/locales/dv/messages.json +14 -1
  156. package/src/components/i18n/locales/el/messages.json +14 -1
  157. package/src/components/i18n/locales/en/messages.json +5 -1
  158. package/src/components/i18n/locales/es/messages.json +14 -1
  159. package/src/components/i18n/locales/et/messages.json +15 -2
  160. package/src/components/i18n/locales/fa/messages.json +14 -1
  161. package/src/components/i18n/locales/fi/messages.json +14 -1
  162. package/src/components/i18n/locales/fil/messages.json +14 -1
  163. package/src/components/i18n/locales/fr/messages.json +14 -1
  164. package/src/components/i18n/locales/gu/messages.json +14 -1
  165. package/src/components/i18n/locales/he/messages.json +14 -1
  166. package/src/components/i18n/locales/hi/messages.json +14 -1
  167. package/src/components/i18n/locales/hr/messages.json +14 -1
  168. package/src/components/i18n/locales/hu/messages.json +14 -1
  169. package/src/components/i18n/locales/hy/messages.json +14 -1
  170. package/src/components/i18n/locales/id/messages.json +14 -1
  171. package/src/components/i18n/locales/it/messages.json +14 -1
  172. package/src/components/i18n/locales/ja/messages.json +14 -1
  173. package/src/components/i18n/locales/ka/messages.json +14 -1
  174. package/src/components/i18n/locales/km/messages.json +14 -1
  175. package/src/components/i18n/locales/kn/messages.json +14 -1
  176. package/src/components/i18n/locales/ko/messages.json +14 -1
  177. package/src/components/i18n/locales/ku/messages.json +14 -1
  178. package/src/components/i18n/locales/lo/messages.json +14 -1
  179. package/src/components/i18n/locales/lt/messages.json +14 -1
  180. package/src/components/i18n/locales/lv/messages.json +14 -1
  181. package/src/components/i18n/locales/mk/messages.json +14 -1
  182. package/src/components/i18n/locales/ml/messages.json +14 -1
  183. package/src/components/i18n/locales/mn/messages.json +14 -1
  184. package/src/components/i18n/locales/mr/messages.json +14 -1
  185. package/src/components/i18n/locales/ms/messages.json +14 -1
  186. package/src/components/i18n/locales/my/messages.json +14 -1
  187. package/src/components/i18n/locales/ne/messages.json +14 -1
  188. package/src/components/i18n/locales/nl/messages.json +14 -1
  189. package/src/components/i18n/locales/no/messages.json +14 -1
  190. package/src/components/i18n/locales/pa/messages.json +14 -1
  191. package/src/components/i18n/locales/pl/messages.json +14 -1
  192. package/src/components/i18n/locales/ps/messages.json +14 -1
  193. package/src/components/i18n/locales/pt/messages.json +14 -1
  194. package/src/components/i18n/locales/ro/messages.json +14 -1
  195. package/src/components/i18n/locales/ru/messages.json +15 -2
  196. package/src/components/i18n/locales/sd/messages.json +14 -1
  197. package/src/components/i18n/locales/si/messages.json +14 -1
  198. package/src/components/i18n/locales/sk/messages.json +14 -1
  199. package/src/components/i18n/locales/sl/messages.json +14 -1
  200. package/src/components/i18n/locales/sq/messages.json +14 -1
  201. package/src/components/i18n/locales/sr/messages.json +14 -1
  202. package/src/components/i18n/locales/sv/messages.json +14 -1
  203. package/src/components/i18n/locales/sw/messages.json +14 -1
  204. package/src/components/i18n/locales/ta/messages.json +14 -1
  205. package/src/components/i18n/locales/te/messages.json +14 -1
  206. package/src/components/i18n/locales/th/messages.json +14 -1
  207. package/src/components/i18n/locales/tr/messages.json +14 -1
  208. package/src/components/i18n/locales/ug/messages.json +14 -1
  209. package/src/components/i18n/locales/uk/messages.json +15 -2
  210. package/src/components/i18n/locales/ur/messages.json +14 -1
  211. package/src/components/i18n/locales/vi/messages.json +14 -1
  212. package/src/components/i18n/locales/yi/messages.json +14 -1
  213. package/src/components/i18n/locales/zh/messages.json +14 -1
  214. package/src/components/icons/index.ts +18 -0
  215. package/src/components/inline-tools/inline-tool-strikethrough.ts +408 -0
  216. package/src/components/inline-tools/inline-tool-underline.ts +408 -0
  217. package/src/components/modules/paste/handlers/base.ts +14 -4
  218. package/src/components/modules/rectangleSelection.ts +48 -0
  219. package/src/components/modules/toolbar/index.ts +5 -2
  220. package/src/components/modules/toolbar/inline/shortcuts-manager.ts +4 -3
  221. package/src/components/shared/color-picker.ts +10 -2
  222. package/src/full.ts +7 -1
  223. package/src/stories/MarkerColors.stories.ts +13 -34
  224. package/src/styles/main.css +1 -1
  225. package/src/tools/header/index.ts +6 -4
  226. package/src/tools/index.ts +4 -0
  227. package/src/tools/list/constants.ts +3 -3
  228. package/src/tools/list/dom-builder.ts +1 -0
  229. package/src/tools/table/table-cell-blocks.ts +25 -0
  230. package/src/tools/toggle/constants.ts +12 -5
@@ -0,0 +1,408 @@
1
+ import type { InlineTool, SanitizerConfig } from '../../../types';
2
+ import type { MenuConfig } from '../../../types/tools';
3
+ import { IconUnderline } from '../icons';
4
+
5
+ import {
6
+ isRangeFormatted,
7
+ findFormattingAncestor,
8
+ hasFormattingAncestor,
9
+ collectFormattingAncestors,
10
+ } from './utils/formatting-range-utils';
11
+
12
+ /**
13
+ * Check if an element is an underline tag (<u>)
14
+ * @param element - The element to check
15
+ */
16
+ const isUnderlineTag = (element: Element): boolean => {
17
+ return element.tagName === 'U';
18
+ };
19
+
20
+ /**
21
+ * Underline Tool
22
+ *
23
+ * Inline Toolbar Tool
24
+ *
25
+ * Style selected text with underline
26
+ */
27
+ export class UnderlineInlineTool implements InlineTool {
28
+ /**
29
+ * Specifies Tool as Inline Toolbar Tool
30
+ * @returns {boolean}
31
+ */
32
+ public static isInline = true;
33
+
34
+ /**
35
+ * Title for the Inline Tool
36
+ */
37
+ public static title = 'Underline';
38
+
39
+ /**
40
+ * Translation key for i18n
41
+ */
42
+ public static titleKey = 'underline';
43
+
44
+ /**
45
+ * Sanitizer Rule
46
+ * Leave <u> tags
47
+ * @returns {object}
48
+ */
49
+ public static get sanitize(): SanitizerConfig {
50
+ return {
51
+ u: {},
52
+ } as SanitizerConfig;
53
+ }
54
+
55
+ /**
56
+ * Create button for Inline Toolbar
57
+ */
58
+ public render(): MenuConfig {
59
+ return {
60
+ icon: IconUnderline,
61
+ name: 'underline',
62
+ onActivate: () => {
63
+ this.toggleUnderline();
64
+ },
65
+ isActive: () => {
66
+ const selection = window.getSelection();
67
+
68
+ return selection ? this.isSelectionVisuallyUnderline(selection) : false;
69
+ },
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Shortcut for underline tool
75
+ */
76
+ public static shortcut = 'CMD+U';
77
+
78
+ /**
79
+ * Apply or remove underline formatting using modern Selection API
80
+ */
81
+ private toggleUnderline(): void {
82
+ const selection = window.getSelection();
83
+
84
+ if (!selection || selection.rangeCount === 0) {
85
+ return;
86
+ }
87
+
88
+ const range = selection.getRangeAt(0);
89
+
90
+ if (range.collapsed) {
91
+ this.toggleCollapsedUnderline(range, selection);
92
+
93
+ return;
94
+ }
95
+
96
+ const shouldUnwrap = this.isRangeUnderline(range, { ignoreWhitespace: true });
97
+
98
+ if (shouldUnwrap) {
99
+ this.unwrapUnderlineTags(range);
100
+ } else {
101
+ this.wrapWithUnderline(range);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Handle toggle for collapsed selection (caret)
107
+ * @param range - Current range
108
+ * @param selection - Current selection
109
+ */
110
+ private toggleCollapsedUnderline(range: Range, selection: Selection): void {
111
+ const isUnderline = this.isRangeUnderline(range, { ignoreWhitespace: true });
112
+
113
+ if (isUnderline) {
114
+ const textNode = document.createTextNode('\u200B');
115
+
116
+ range.insertNode(textNode);
117
+ range.selectNode(textNode);
118
+ this.unwrapUnderlineTags(range);
119
+
120
+ const newRange = document.createRange();
121
+
122
+ newRange.setStart(textNode, 1);
123
+ newRange.setEnd(textNode, 1);
124
+
125
+ selection.removeAllRanges();
126
+ selection.addRange(newRange);
127
+ } else {
128
+ const u = document.createElement('u');
129
+ const textNode = document.createTextNode('\u200B');
130
+
131
+ u.appendChild(textNode);
132
+ range.insertNode(u);
133
+
134
+ const newRange = document.createRange();
135
+
136
+ newRange.setStart(textNode, 1);
137
+ newRange.setEnd(textNode, 1);
138
+
139
+ selection.removeAllRanges();
140
+ selection.addRange(newRange);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Check if current selection is within an underline tag
146
+ * @param selection - The Selection object to check
147
+ */
148
+ private isSelectionVisuallyUnderline(selection: Selection): boolean {
149
+ if (selection.rangeCount === 0) {
150
+ return false;
151
+ }
152
+
153
+ const range = selection.getRangeAt(0);
154
+
155
+ return this.isRangeUnderline(range, { ignoreWhitespace: true });
156
+ }
157
+
158
+ /**
159
+ * Check if a range contains underline text
160
+ * @param range - The range to check
161
+ * @param options - Options for checking underline status
162
+ */
163
+ private isRangeUnderline(range: Range, options: { ignoreWhitespace: boolean }): boolean {
164
+ return isRangeFormatted(range, isUnderlineTag, options);
165
+ }
166
+
167
+ /**
168
+ * Wrap selection with <u> tag
169
+ * @param range - The Range object containing the selection to wrap
170
+ */
171
+ private wrapWithUnderline(range: Range): void {
172
+ const html = this.getRangeHtmlWithoutUnderline(range);
173
+ const insertedRange = this.replaceRangeWithHtml(range, `<u>${html}</u>`);
174
+ const selection = window.getSelection();
175
+
176
+ if (selection && insertedRange) {
177
+ selection.removeAllRanges();
178
+ selection.addRange(insertedRange);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Remove underline tags (<u>) while preserving content
184
+ * @param range - The Range object containing the selection to unwrap
185
+ */
186
+ private unwrapUnderlineTags(range: Range): void {
187
+ const underlineAncestors = this.collectUnderlineAncestors(range);
188
+ const selection = window.getSelection();
189
+
190
+ if (!selection) {
191
+ return;
192
+ }
193
+
194
+ const marker = document.createElement('span');
195
+ const fragment = range.extractContents();
196
+
197
+ marker.appendChild(fragment);
198
+ this.removeNestedUnderline(marker);
199
+
200
+ range.insertNode(marker);
201
+
202
+ const markerRange = document.createRange();
203
+
204
+ markerRange.selectNodeContents(marker);
205
+ selection.removeAllRanges();
206
+ selection.addRange(markerRange);
207
+
208
+ for (; ;) {
209
+ const currentUnderline = this.findUnderlineElement(marker);
210
+
211
+ if (!currentUnderline) {
212
+ break;
213
+ }
214
+
215
+ this.moveMarkerOutOfUnderline(marker, currentUnderline);
216
+ }
217
+
218
+ const firstChild = marker.firstChild;
219
+ const lastChild = marker.lastChild;
220
+
221
+ this.unwrapElement(marker);
222
+
223
+ const finalRange = firstChild && lastChild ? (() => {
224
+ const newRange = document.createRange();
225
+
226
+ newRange.setStartBefore(firstChild);
227
+ newRange.setEndAfter(lastChild);
228
+
229
+ selection.removeAllRanges();
230
+ selection.addRange(newRange);
231
+
232
+ return newRange;
233
+ })() : undefined;
234
+
235
+ if (!finalRange) {
236
+ selection.removeAllRanges();
237
+ }
238
+
239
+ underlineAncestors.forEach((element) => {
240
+ if (element.textContent.length === 0) {
241
+ element.remove();
242
+ }
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Check if a node or any of its parents is an underline tag
248
+ * @param node - The node to check
249
+ */
250
+ private hasUnderlineParent(node: Node | null): boolean {
251
+ return hasFormattingAncestor(node, isUnderlineTag);
252
+ }
253
+
254
+ /**
255
+ * Find an underline element in the parent chain
256
+ * @param node - The node to start searching from
257
+ */
258
+ private findUnderlineElement(node: Node | null): HTMLElement | null {
259
+ return findFormattingAncestor(node, isUnderlineTag);
260
+ }
261
+
262
+ /**
263
+ * Collect all underline ancestor elements within a range
264
+ * @param range - The range to search for underline ancestors
265
+ */
266
+ private collectUnderlineAncestors(range: Range): HTMLElement[] {
267
+ return collectFormattingAncestors(range, isUnderlineTag);
268
+ }
269
+
270
+ /**
271
+ * Get HTML content of a range with underline tags removed
272
+ * @param range - The range to extract HTML from
273
+ */
274
+ private getRangeHtmlWithoutUnderline(range: Range): string {
275
+ const contents = range.cloneContents();
276
+
277
+ this.removeNestedUnderline(contents);
278
+
279
+ const container = document.createElement('div');
280
+
281
+ container.appendChild(contents);
282
+
283
+ return container.innerHTML;
284
+ }
285
+
286
+ /**
287
+ * Remove nested underline tags from a root node
288
+ * @param root - The root node to process
289
+ */
290
+ private removeNestedUnderline(root: ParentNode): void {
291
+ const underlineNodes = root.querySelectorAll('u');
292
+
293
+ underlineNodes.forEach((node) => {
294
+ this.unwrapElement(node);
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Unwrap an element by moving its children to the parent
300
+ * @param element - The element to unwrap
301
+ */
302
+ private unwrapElement(element: Element): void {
303
+ const parent = element.parentNode;
304
+
305
+ if (!parent) {
306
+ element.remove();
307
+
308
+ return;
309
+ }
310
+
311
+ while (element.firstChild) {
312
+ parent.insertBefore(element.firstChild, element);
313
+ }
314
+
315
+ parent.removeChild(element);
316
+ }
317
+
318
+ /**
319
+ * Replace the current range contents with provided HTML snippet
320
+ * @param range - Range to replace
321
+ * @param html - HTML string to insert
322
+ */
323
+ private replaceRangeWithHtml(range: Range, html: string): Range | undefined {
324
+ const fragment = this.createFragmentFromHtml(html);
325
+ const firstInserted = fragment.firstChild ?? null;
326
+ const lastInserted = fragment.lastChild ?? null;
327
+
328
+ range.deleteContents();
329
+
330
+ if (!firstInserted || !lastInserted) {
331
+ return;
332
+ }
333
+
334
+ range.insertNode(fragment);
335
+
336
+ const newRange = document.createRange();
337
+
338
+ newRange.setStartBefore(firstInserted);
339
+ newRange.setEndAfter(lastInserted);
340
+
341
+ return newRange;
342
+ }
343
+
344
+ /**
345
+ * Convert an HTML snippet to a document fragment
346
+ * @param html - HTML string to convert
347
+ */
348
+ private createFragmentFromHtml(html: string): DocumentFragment {
349
+ const template = document.createElement('template');
350
+
351
+ template.innerHTML = html;
352
+
353
+ return template.content;
354
+ }
355
+
356
+ /**
357
+ * Move a temporary marker element outside of an underline ancestor while preserving content order
358
+ * @param marker - Marker element wrapping the selection contents
359
+ * @param underlineElement - Underline ancestor containing the marker
360
+ */
361
+ private moveMarkerOutOfUnderline(marker: HTMLElement, underlineElement: HTMLElement): void {
362
+ const parent = underlineElement.parentNode;
363
+
364
+ if (!parent) {
365
+ return;
366
+ }
367
+
368
+ // Remove empty text nodes to ensure accurate child count
369
+ Array.from(underlineElement.childNodes).forEach((node) => {
370
+ if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? '').length === 0) {
371
+ node.remove();
372
+ }
373
+ });
374
+
375
+ const isOnlyChild = underlineElement.childNodes.length === 1 && underlineElement.firstChild === marker;
376
+
377
+ if (isOnlyChild) {
378
+ underlineElement.replaceWith(marker);
379
+
380
+ return;
381
+ }
382
+
383
+ const isFirstChild = underlineElement.firstChild === marker;
384
+
385
+ if (isFirstChild) {
386
+ parent.insertBefore(marker, underlineElement);
387
+
388
+ return;
389
+ }
390
+
391
+ const isLastChild = underlineElement.lastChild === marker;
392
+
393
+ if (isLastChild) {
394
+ parent.insertBefore(marker, underlineElement.nextSibling);
395
+
396
+ return;
397
+ }
398
+
399
+ const trailingClone = underlineElement.cloneNode(false) as HTMLElement;
400
+
401
+ while (marker.nextSibling) {
402
+ trailingClone.appendChild(marker.nextSibling);
403
+ }
404
+
405
+ parent.insertBefore(trailingClone, underlineElement.nextSibling);
406
+ parent.insertBefore(marker, trailingClone);
407
+ }
408
+ }
@@ -115,6 +115,13 @@ export abstract class BasePasteHandler implements PasteHandler {
115
115
  if (isMultipleItems) {
116
116
  this.redirectToTableParentIfNeeded(data, BlockManager);
117
117
 
118
+ const currentBlock = BlockManager.currentBlock;
119
+ const childContainer = currentBlock?.holder?.querySelector('[data-blok-toggle-children]') ?? null;
120
+ const isInContainerTitle = childContainer !== null &&
121
+ !childContainer.contains(currentBlock?.currentInput ?? null);
122
+ const contextParentId = isInContainerTitle
123
+ ? (currentBlock?.id ?? null)
124
+ : (currentBlock?.parentId ?? null);
118
125
  const insertedByIndex: Array<Awaited<ReturnType<BlokModules['BlockManager']['paste']>>> = [];
119
126
 
120
127
  for (const [index, pasteData] of data.entries()) {
@@ -136,7 +143,7 @@ export abstract class BasePasteHandler implements PasteHandler {
136
143
  Caret.setToBlock(block, Caret.positions.END);
137
144
  insertedByIndex.push(block);
138
145
 
139
- this.applyPastedBlockParent(block, pasteData, insertedByIndex, BlockManager);
146
+ this.applyPastedBlockParent(block, pasteData, insertedByIndex, BlockManager, contextParentId);
140
147
  }
141
148
 
142
149
  BlockManager.currentBlock && Caret.setToBlock(BlockManager.currentBlock, Caret.positions.END);
@@ -163,7 +170,8 @@ export abstract class BasePasteHandler implements PasteHandler {
163
170
  block: Awaited<ReturnType<BlokModules['BlockManager']['paste']>>,
164
171
  pasteData: PasteData,
165
172
  insertedByIndex: Array<Awaited<ReturnType<BlokModules['BlockManager']['paste']>>>,
166
- BlockManager: BlokModules['BlockManager']
173
+ BlockManager: BlokModules['BlockManager'],
174
+ contextParentId: string | null
167
175
  ): void {
168
176
  if (pasteData.parentPasteIndex !== undefined) {
169
177
  const parentBlock = insertedByIndex[pasteData.parentPasteIndex];
@@ -171,9 +179,11 @@ export abstract class BasePasteHandler implements PasteHandler {
171
179
  if (parentBlock) {
172
180
  BlockManager.setBlockParent(block, parentBlock.id);
173
181
  }
182
+ } else if (contextParentId !== null) {
183
+ // Container-title paste context: assign all flat blocks to the container
184
+ BlockManager.setBlockParent(block, contextParentId);
174
185
  } else if (block.parentId != null) {
175
- // Root-level pasted block: clear any parent that was inherited from
176
- // the predecessor (e.g. the previous child block that had a parent).
186
+ // Root-level paste context: clear any parent inherited from predecessor
177
187
  BlockManager.setBlockParent(block, null);
178
188
  }
179
189
  }
@@ -65,6 +65,13 @@ export class RectangleSelection extends Module {
65
65
  */
66
66
  private mousedown = false;
67
67
 
68
+ /**
69
+ * Set when mousedown starts from a contentEditable element that is within the
70
+ * editor's horizontal content bounds. Prevents the toolbar from re-opening via
71
+ * the BlockHovered event while the user is dragging. Cleared on mouseup.
72
+ */
73
+ private mouseDownWithinBoundsFromContentEditable = false;
74
+
68
75
  /**
69
76
  * Is scrolling now
70
77
  */
@@ -213,6 +220,7 @@ export class RectangleSelection extends Module {
213
220
  */
214
221
  public endSelection(): void {
215
222
  this.mousedown = false;
223
+ this.mouseDownWithinBoundsFromContentEditable = false;
216
224
  this.startX = 0;
217
225
  this.startY = 0;
218
226
  this.anchorBlockIndex = null;
@@ -229,6 +237,18 @@ export class RectangleSelection extends Module {
229
237
  return this.isRectSelectionActivated || this.mousedown;
230
238
  }
231
239
 
240
+ /**
241
+ * Returns true when the user pressed down a mouse button within the editor's horizontal
242
+ * content bounds, even if the click originated on a contentEditable element (in which
243
+ * case rubber-band selection is not started but the toolbar is still closed).
244
+ *
245
+ * Used by the Toolbar module to suppress toolbar reopening while the user is dragging
246
+ * inside the editor content area.
247
+ */
248
+ public get isMouseDownWithinBounds(): boolean {
249
+ return this.mouseDownWithinBoundsFromContentEditable;
250
+ }
251
+
232
252
  /**
233
253
  * Mark that selection is end
234
254
  */
@@ -305,6 +325,24 @@ export class RectangleSelection extends Module {
305
325
 
306
326
  if (!startedFromContentEditable) {
307
327
  this.startSelection(mouseEvent.pageX, mouseEvent.pageY, mouseEvent.shiftKey);
328
+
329
+ return;
330
+ }
331
+
332
+ /**
333
+ * When dragging starts from a contentEditable element, track whether the
334
+ * pointer is within the editor's horizontal content bounds.
335
+ * On the first subsequent mousemove (i.e. when the user actually drags rather
336
+ * than just clicking), the toolbar will be closed and hover-based reopening
337
+ * will be suppressed while the mouse button is held.
338
+ */
339
+ const scrollLeft = this.getScrollLeft();
340
+ const pointerX = mouseEvent.pageX - scrollLeft;
341
+ const contentRect = this.Blok.UI.contentRect;
342
+ const withinEditorHorizontally = pointerX >= contentRect.left && pointerX <= contentRect.right;
343
+
344
+ if (withinEditorHorizontally) {
345
+ this.mouseDownWithinBoundsFromContentEditable = true;
308
346
  }
309
347
  }
310
348
 
@@ -313,6 +351,16 @@ export class RectangleSelection extends Module {
313
351
  * @param {MouseEvent} mouseEvent - mouse event payload
314
352
  */
315
353
  private processMouseMove(mouseEvent: MouseEvent): void {
354
+ /**
355
+ * When the user clicked inside a contentEditable element within the editor's
356
+ * horizontal bounds and is now dragging, close the toolbar on the first move.
357
+ * We defer this to mousemove (rather than mousedown) so that a plain click
358
+ * does not accidentally close the toolbar.
359
+ */
360
+ if (this.mouseDownWithinBoundsFromContentEditable) {
361
+ this.Blok.Toolbar.close();
362
+ }
363
+
316
364
  this.changingRectangle(mouseEvent);
317
365
  this.scrollByZones(mouseEvent.clientY);
318
366
  }
@@ -1009,9 +1009,12 @@ export class Toolbar extends Module<ToolbarNodes> {
1009
1009
  */
1010
1010
  this.eventsDispatcher.on(BlockHovered, (data) => {
1011
1011
  /**
1012
- * Do not move toolbar during drag or rectangle selection operations
1012
+ * Do not move toolbar during drag, rectangle selection, or when the user
1013
+ * started a mouse-drag from within the editor's content area (even if the
1014
+ * drag originated on a contentEditable element, i.e. rubber-band is not
1015
+ * activated but the toolbar was closed and should stay closed).
1013
1016
  */
1014
- if (this.Blok.DragManager.isDragging || this.Blok.RectangleSelection.isRectActivated()) {
1017
+ if (this.Blok.DragManager.isDragging || this.Blok.RectangleSelection.isRectActivated() || this.Blok.RectangleSelection.isMouseDownWithinBounds) {
1015
1018
  return;
1016
1019
  }
1017
1020
 
@@ -172,13 +172,14 @@ export class InlineShortcutManager {
172
172
  name: shortcut,
173
173
  handler: (event) => {
174
174
  const { BlockManager } = this.getBlok();
175
- const { currentBlock } = BlockManager;
175
+ const block = BlockManager.currentBlock
176
+ ?? BlockManager.getBlockByChildNode(window.getSelection()?.anchorNode as Node);
176
177
 
177
- if (!currentBlock) {
178
+ if (!block) {
178
179
  return;
179
180
  }
180
181
 
181
- if (currentBlock.tool.enabledInlineTools === false) {
182
+ if (block.tool.enabledInlineTools === false) {
182
183
  return;
183
184
  }
184
185
 
@@ -165,7 +165,11 @@ export function createColorPicker(options: ColorPickerOptions): ColorPickerHandl
165
165
  defaultSwatch.addEventListener('click', () => {
166
166
  onColorSelect(null, mode.key);
167
167
  });
168
- onHover(defaultSwatch, `${i18n.t('tools.marker.default')} ${i18n.t(mode.labelKey).toLowerCase()}`, { placement: 'top' });
168
+ const defaultLabel = i18n.t('tools.colorPicker.defaultSwatchLabel')
169
+ .replace('{default}', i18n.t('tools.marker.default'))
170
+ .replace('{mode}', i18n.t(mode.labelKey).toLowerCase());
171
+
172
+ onHover(defaultSwatch, defaultLabel.charAt(0).toUpperCase() + defaultLabel.slice(1), { placement: 'top' });
169
173
  grid.appendChild(defaultSwatch);
170
174
 
171
175
  for (const preset of presets) {
@@ -193,7 +197,11 @@ export function createColorPicker(options: ColorPickerOptions): ColorPickerHandl
193
197
  swatch.addEventListener('click', () => {
194
198
  onColorSelect(swatchColor, mode.key);
195
199
  });
196
- onHover(swatch, `${i18n.t('tools.colorPicker.color.' + preset.name)} ${i18n.t(mode.labelKey).toLowerCase()}`, { placement: 'top' });
200
+ const colorLabel = i18n.t('tools.colorPicker.colorSwatchLabel')
201
+ .replace('{color}', i18n.t('tools.colorPicker.color.' + preset.name))
202
+ .replace('{mode}', i18n.t(mode.labelKey).toLowerCase());
203
+
204
+ onHover(swatch, colorLabel.charAt(0).toUpperCase() + colorLabel.slice(1), { placement: 'top' });
197
205
  grid.appendChild(swatch);
198
206
  }
199
207
  };
package/src/full.ts CHANGED
@@ -19,6 +19,8 @@ import { BoldInlineTool as Bold } from './components/inline-tools/inline-tool-bo
19
19
  import { ItalicInlineTool as Italic } from './components/inline-tools/inline-tool-italic';
20
20
  import { LinkInlineTool as Link } from './components/inline-tools/inline-tool-link';
21
21
  import { MarkerInlineTool as Marker } from './components/inline-tools/inline-tool-marker';
22
+ import { UnderlineInlineTool as Underline } from './components/inline-tools/inline-tool-underline';
23
+ import { StrikethroughInlineTool as Strikethrough } from './components/inline-tools/inline-tool-strikethrough';
22
24
  import { Header } from './tools/header';
23
25
  import { ListItem as List } from './tools/list';
24
26
  import { Paragraph } from './tools/paragraph';
@@ -35,6 +37,8 @@ export {
35
37
  Italic,
36
38
  Link,
37
39
  Marker,
40
+ Underline,
41
+ Strikethrough,
38
42
  defaultBlockTools,
39
43
  defaultInlineTools,
40
44
  } from './tools';
@@ -68,6 +72,8 @@ export const allTools = {
68
72
  ...defaultTools,
69
73
  bold: { class: Bold },
70
74
  italic: { class: Italic },
71
- link: { class: Link },
72
75
  marker: { class: Marker },
76
+ underline: { class: Underline },
77
+ strikethrough: { class: Strikethrough },
78
+ link: { class: Link },
73
79
  } as const;