@jackuait/blok 0.7.3 → 0.8.0

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 (228) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-BmlbETK7.mjs → blok-CdiMFzDz.mjs} +2143 -2134
  3. package/dist/chunks/{constants-WhLyFkza.mjs → constants-CjWRVOdm.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-B5yNeTZh.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/rectangleSelection.ts +48 -0
  218. package/src/components/modules/toolbar/index.ts +5 -2
  219. package/src/components/shared/color-picker.ts +10 -2
  220. package/src/full.ts +7 -1
  221. package/src/stories/MarkerColors.stories.ts +13 -34
  222. package/src/styles/main.css +1 -1
  223. package/src/tools/header/index.ts +6 -4
  224. package/src/tools/index.ts +4 -0
  225. package/src/tools/list/constants.ts +3 -3
  226. package/src/tools/list/dom-builder.ts +1 -0
  227. package/src/tools/table/table-cell-blocks.ts +25 -0
  228. 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 { IconStrikethrough } 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 a strikethrough tag (<s>)
14
+ * @param element - The element to check
15
+ */
16
+ const isStrikethroughTag = (element: Element): boolean => {
17
+ return element.tagName === 'S';
18
+ };
19
+
20
+ /**
21
+ * Strikethrough Tool
22
+ *
23
+ * Inline Toolbar Tool
24
+ *
25
+ * Style selected text with strikethrough
26
+ */
27
+ export class StrikethroughInlineTool 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 = 'Strikethrough';
38
+
39
+ /**
40
+ * Translation key for i18n
41
+ */
42
+ public static titleKey = 'strikethrough';
43
+
44
+ /**
45
+ * Sanitizer Rule
46
+ * Leave <s> tags
47
+ * @returns {object}
48
+ */
49
+ public static get sanitize(): SanitizerConfig {
50
+ return {
51
+ s: {},
52
+ } as SanitizerConfig;
53
+ }
54
+
55
+ /**
56
+ * Create button for Inline Toolbar
57
+ */
58
+ public render(): MenuConfig {
59
+ return {
60
+ icon: IconStrikethrough,
61
+ name: 'strikethrough',
62
+ onActivate: () => {
63
+ this.toggleStrikethrough();
64
+ },
65
+ isActive: () => {
66
+ const selection = window.getSelection();
67
+
68
+ return selection ? this.isSelectionVisuallyStrikethrough(selection) : false;
69
+ },
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Shortcut for strikethrough tool
75
+ */
76
+ public static shortcut = 'CMD+SHIFT+S';
77
+
78
+ /**
79
+ * Apply or remove strikethrough formatting using modern Selection API
80
+ */
81
+ private toggleStrikethrough(): 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.toggleCollapsedStrikethrough(range, selection);
92
+
93
+ return;
94
+ }
95
+
96
+ const shouldUnwrap = this.isRangeStrikethrough(range, { ignoreWhitespace: true });
97
+
98
+ if (shouldUnwrap) {
99
+ this.unwrapStrikethroughTags(range);
100
+ } else {
101
+ this.wrapWithStrikethrough(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 toggleCollapsedStrikethrough(range: Range, selection: Selection): void {
111
+ const isStrikethrough = this.isRangeStrikethrough(range, { ignoreWhitespace: true });
112
+
113
+ if (isStrikethrough) {
114
+ const textNode = document.createTextNode('\u200B');
115
+
116
+ range.insertNode(textNode);
117
+ range.selectNode(textNode);
118
+ this.unwrapStrikethroughTags(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 s = document.createElement('s');
129
+ const textNode = document.createTextNode('\u200B');
130
+
131
+ s.appendChild(textNode);
132
+ range.insertNode(s);
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 a strikethrough tag
146
+ * @param selection - The Selection object to check
147
+ */
148
+ private isSelectionVisuallyStrikethrough(selection: Selection): boolean {
149
+ if (selection.rangeCount === 0) {
150
+ return false;
151
+ }
152
+
153
+ const range = selection.getRangeAt(0);
154
+
155
+ return this.isRangeStrikethrough(range, { ignoreWhitespace: true });
156
+ }
157
+
158
+ /**
159
+ * Check if a range contains strikethrough text
160
+ * @param range - The range to check
161
+ * @param options - Options for checking strikethrough status
162
+ */
163
+ private isRangeStrikethrough(range: Range, options: { ignoreWhitespace: boolean }): boolean {
164
+ return isRangeFormatted(range, isStrikethroughTag, options);
165
+ }
166
+
167
+ /**
168
+ * Wrap selection with <s> tag
169
+ * @param range - The Range object containing the selection to wrap
170
+ */
171
+ private wrapWithStrikethrough(range: Range): void {
172
+ const html = this.getRangeHtmlWithoutStrikethrough(range);
173
+ const insertedRange = this.replaceRangeWithHtml(range, `<s>${html}</s>`);
174
+ const selection = window.getSelection();
175
+
176
+ if (selection && insertedRange) {
177
+ selection.removeAllRanges();
178
+ selection.addRange(insertedRange);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Remove strikethrough tags (<s>) while preserving content
184
+ * @param range - The Range object containing the selection to unwrap
185
+ */
186
+ private unwrapStrikethroughTags(range: Range): void {
187
+ const strikethroughAncestors = this.collectStrikethroughAncestors(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.removeNestedStrikethrough(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 currentStrikethrough = this.findStrikethroughElement(marker);
210
+
211
+ if (!currentStrikethrough) {
212
+ break;
213
+ }
214
+
215
+ this.moveMarkerOutOfStrikethrough(marker, currentStrikethrough);
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
+ strikethroughAncestors.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 a strikethrough tag
248
+ * @param node - The node to check
249
+ */
250
+ private hasStrikethroughParent(node: Node | null): boolean {
251
+ return hasFormattingAncestor(node, isStrikethroughTag);
252
+ }
253
+
254
+ /**
255
+ * Find a strikethrough element in the parent chain
256
+ * @param node - The node to start searching from
257
+ */
258
+ private findStrikethroughElement(node: Node | null): HTMLElement | null {
259
+ return findFormattingAncestor(node, isStrikethroughTag);
260
+ }
261
+
262
+ /**
263
+ * Collect all strikethrough ancestor elements within a range
264
+ * @param range - The range to search for strikethrough ancestors
265
+ */
266
+ private collectStrikethroughAncestors(range: Range): HTMLElement[] {
267
+ return collectFormattingAncestors(range, isStrikethroughTag);
268
+ }
269
+
270
+ /**
271
+ * Get HTML content of a range with strikethrough tags removed
272
+ * @param range - The range to extract HTML from
273
+ */
274
+ private getRangeHtmlWithoutStrikethrough(range: Range): string {
275
+ const contents = range.cloneContents();
276
+
277
+ this.removeNestedStrikethrough(contents);
278
+
279
+ const container = document.createElement('div');
280
+
281
+ container.appendChild(contents);
282
+
283
+ return container.innerHTML;
284
+ }
285
+
286
+ /**
287
+ * Remove nested strikethrough tags from a root node
288
+ * @param root - The root node to process
289
+ */
290
+ private removeNestedStrikethrough(root: ParentNode): void {
291
+ const strikethroughNodes = root.querySelectorAll('s');
292
+
293
+ strikethroughNodes.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 a strikethrough ancestor while preserving content order
358
+ * @param marker - Marker element wrapping the selection contents
359
+ * @param strikethroughElement - Strikethrough ancestor containing the marker
360
+ */
361
+ private moveMarkerOutOfStrikethrough(marker: HTMLElement, strikethroughElement: HTMLElement): void {
362
+ const parent = strikethroughElement.parentNode;
363
+
364
+ if (!parent) {
365
+ return;
366
+ }
367
+
368
+ // Remove empty text nodes to ensure accurate child count
369
+ Array.from(strikethroughElement.childNodes).forEach((node) => {
370
+ if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? '').length === 0) {
371
+ node.remove();
372
+ }
373
+ });
374
+
375
+ const isOnlyChild = strikethroughElement.childNodes.length === 1 && strikethroughElement.firstChild === marker;
376
+
377
+ if (isOnlyChild) {
378
+ strikethroughElement.replaceWith(marker);
379
+
380
+ return;
381
+ }
382
+
383
+ const isFirstChild = strikethroughElement.firstChild === marker;
384
+
385
+ if (isFirstChild) {
386
+ parent.insertBefore(marker, strikethroughElement);
387
+
388
+ return;
389
+ }
390
+
391
+ const isLastChild = strikethroughElement.lastChild === marker;
392
+
393
+ if (isLastChild) {
394
+ parent.insertBefore(marker, strikethroughElement.nextSibling);
395
+
396
+ return;
397
+ }
398
+
399
+ const trailingClone = strikethroughElement.cloneNode(false) as HTMLElement;
400
+
401
+ while (marker.nextSibling) {
402
+ trailingClone.appendChild(marker.nextSibling);
403
+ }
404
+
405
+ parent.insertBefore(trailingClone, strikethroughElement.nextSibling);
406
+ parent.insertBefore(marker, trailingClone);
407
+ }
408
+ }