@prolibu-suite/cobalt-core 0.2.0 → 0.4.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 (678) hide show
  1. package/custom-elements.json +19263 -11548
  2. package/dist/cjs/co-accordion.cjs.entry.js +3 -3
  3. package/dist/cjs/co-app-shell.cjs.entry.js +4 -4
  4. package/dist/cjs/co-autocomplete.cjs.entry.js +10 -7
  5. package/dist/cjs/co-avatar-group.cjs.entry.js +77 -0
  6. package/dist/cjs/co-avatar.cjs.entry.js +12 -5
  7. package/dist/cjs/co-badge.cjs.entry.js +2 -2
  8. package/dist/cjs/co-bottom-sheet.cjs.entry.js +325 -0
  9. package/dist/cjs/co-breadcrumb-item.cjs.entry.js +37 -0
  10. package/dist/cjs/co-breadcrumb.cjs.entry.js +55 -0
  11. package/dist/cjs/co-button-group.cjs.entry.js +3 -3
  12. package/dist/cjs/co-button.cjs.entry.js +12 -6
  13. package/dist/cjs/co-calendar-month.cjs.entry.js +381 -0
  14. package/dist/cjs/co-calendar_3.cjs.entry.js +41 -11
  15. package/dist/cjs/co-card.cjs.entry.js +3 -3
  16. package/dist/cjs/co-checkbox-group.cjs.entry.js +5 -5
  17. package/dist/cjs/co-checkbox.cjs.entry.js +18 -6
  18. package/dist/cjs/co-chip.cjs.entry.js +19 -11
  19. package/dist/cjs/co-color-picker.cjs.entry.js +7 -5
  20. package/dist/cjs/co-column-picker.cjs.entry.js +3 -3
  21. package/dist/cjs/co-combobox.cjs.entry.js +7 -7
  22. package/dist/cjs/co-command-search.cjs.entry.js +151 -12
  23. package/dist/cjs/co-data-table-pagination.cjs.entry.js +5 -5
  24. package/dist/cjs/co-data-table.cjs.entry.js +44 -23
  25. package/dist/cjs/co-date-picker.cjs.entry.js +2 -2
  26. package/dist/cjs/co-date-range-picker.cjs.entry.js +3 -3
  27. package/dist/cjs/co-date-range.cjs.entry.js +2 -2
  28. package/dist/cjs/co-drive-card.cjs.entry.js +81 -0
  29. package/dist/cjs/co-drop-zone.cjs.entry.js +63 -0
  30. package/dist/cjs/co-empty-state.cjs.entry.js +13 -3
  31. package/dist/cjs/co-event-calendar.cjs.entry.js +318 -0
  32. package/dist/cjs/co-fab.cjs.entry.js +3 -3
  33. package/dist/cjs/co-file-field.cjs.entry.js +8 -8
  34. package/dist/cjs/co-file-type-tile.cjs.entry.js +78 -0
  35. package/dist/cjs/co-helper-text.cjs.entry.js +3 -3
  36. package/dist/cjs/co-icon-button.cjs.entry.js +24 -8
  37. package/dist/cjs/co-icon.cjs.entry.js +33 -25
  38. package/dist/cjs/co-input.cjs.entry.js +36 -7
  39. package/dist/cjs/co-json-field.cjs.entry.js +10 -6
  40. package/dist/cjs/co-lightbox.cjs.entry.js +5 -5
  41. package/dist/cjs/co-list-footer.cjs.entry.js +25 -0
  42. package/dist/cjs/co-list-item.cjs.entry.js +60 -4
  43. package/dist/cjs/co-loader.cjs.entry.js +2 -2
  44. package/dist/cjs/co-media-thumb.cjs.entry.js +36 -0
  45. package/dist/cjs/co-menu-divider.cjs.entry.js +2 -2
  46. package/dist/cjs/co-menu-item.cjs.entry.js +26 -11
  47. package/dist/cjs/co-menu.cjs.entry.js +3 -3
  48. package/dist/cjs/co-modal.cjs.entry.js +41 -5
  49. package/dist/cjs/co-navbar.cjs.entry.js +3 -3
  50. package/dist/cjs/co-number-field.cjs.entry.js +18 -6
  51. package/dist/cjs/co-option-list.cjs.entry.js +379 -0
  52. package/dist/cjs/co-overlay.cjs.entry.js +243 -3
  53. package/dist/cjs/co-page-header.cjs.entry.js +2 -2
  54. package/dist/cjs/co-phone-field.cjs.entry.js +6 -4
  55. package/dist/cjs/co-popover.cjs.entry.js +45 -14
  56. package/dist/cjs/co-progress-bar.cjs.entry.js +8 -4
  57. package/dist/cjs/co-progress-circle.cjs.entry.js +3 -3
  58. package/dist/cjs/co-radio-group.cjs.entry.js +4 -4
  59. package/dist/cjs/co-radio.cjs.entry.js +4 -4
  60. package/dist/cjs/co-rating.cjs.entry.js +4 -4
  61. package/dist/cjs/co-ref-field.cjs.entry.js +36 -13
  62. package/dist/cjs/co-rich-text-editor.cjs.entry.js +1879 -33
  63. package/dist/cjs/co-rich-text-toolbar.cjs.entry.js +995 -71
  64. package/dist/cjs/co-rich-text-viewer.cjs.entry.js +55 -19
  65. package/dist/cjs/co-row.cjs.entry.js +3 -3
  66. package/dist/cjs/co-segmented-bar.cjs.entry.js +58 -0
  67. package/dist/cjs/co-select.cjs.entry.js +8 -8
  68. package/dist/cjs/co-separator.cjs.entry.js +2 -2
  69. package/dist/cjs/co-sidebar-nav.cjs.entry.js +29 -9
  70. package/dist/cjs/co-sidebar.cjs.entry.js +3 -3
  71. package/dist/cjs/co-skeleton.cjs.entry.js +26 -0
  72. package/dist/cjs/co-slider.cjs.entry.js +3 -3
  73. package/dist/cjs/co-switch.cjs.entry.js +6 -6
  74. package/dist/cjs/co-tab-group.cjs.entry.js +15 -3
  75. package/dist/cjs/co-tab.cjs.entry.js +8 -5
  76. package/dist/cjs/co-tags-field.cjs.entry.js +19 -13
  77. package/dist/cjs/co-textarea.cjs.entry.js +11 -7
  78. package/dist/cjs/co-toast.cjs.entry.js +10 -4
  79. package/dist/cjs/co-toggle-button-group.cjs.entry.js +16 -4
  80. package/dist/cjs/co-toggle-button.cjs.entry.js +9 -5
  81. package/dist/cjs/co-tooltip.cjs.entry.js +51 -16
  82. package/dist/cjs/co-transfer-list.cjs.entry.js +3 -3
  83. package/dist/cjs/co-video.cjs.entry.js +839 -0
  84. package/dist/cjs/cobalt.cjs.js +2 -2
  85. package/dist/cjs/fieldChip-DtYUP0VZ.js +7 -0
  86. package/dist/{esm/index-BjAqSidM.js → cjs/index-4y1Dm145.js} +14436 -6178
  87. package/dist/cjs/{index-DoFnrUVH.js → index-NNvye1ul.js} +105 -21
  88. package/dist/cjs/loader.cjs.js +2 -2
  89. package/dist/cjs/overlayAfterClose-BdeXrhVY.js +12 -0
  90. package/dist/{esm/sanitizer-DcK0GGZC.js → cjs/purify.es-DX41ZWS2.js} +3 -107
  91. package/dist/cjs/{requiredMark-DA6y-4rp.js → requiredMark-DyjZzUCd.js} +1 -1
  92. package/dist/cjs/richTextI18n-B7ktXg3M.js +382 -0
  93. package/dist/cjs/toolbar-config-3PlT-3zm.js +216 -0
  94. package/dist/cjs/tz-Dgom0W69.js +127 -0
  95. package/dist/cobalt/cobalt.css +1 -1
  96. package/dist/cobalt/cobalt.esm.js +1 -1
  97. package/dist/cobalt/p-06c27cc5.entry.js +1 -0
  98. package/dist/cobalt/{p-136bdbd2.entry.js → p-075f6ca4.entry.js} +1 -1
  99. package/dist/cobalt/p-0810ee1e.entry.js +1 -0
  100. package/dist/cobalt/p-08519a7c.entry.js +1 -0
  101. package/dist/cobalt/p-0b41e76e.entry.js +1 -0
  102. package/dist/cobalt/p-0be124d5.entry.js +1 -0
  103. package/dist/cobalt/{p-e8b58332.entry.js → p-0e1afdea.entry.js} +1 -1
  104. package/dist/cobalt/p-1388eac6.entry.js +1 -0
  105. package/dist/cobalt/{p-7f818368.entry.js → p-19f7ee64.entry.js} +1 -1
  106. package/dist/cobalt/p-1af6ef83.entry.js +1 -0
  107. package/dist/cobalt/{p-f2c5cb74.entry.js → p-1cecb9e1.entry.js} +1 -1
  108. package/dist/cobalt/{p-273afd93.entry.js → p-20c68308.entry.js} +1 -1
  109. package/dist/cobalt/p-21916f82.entry.js +1 -0
  110. package/dist/cobalt/p-23c9208a.entry.js +1 -0
  111. package/dist/cobalt/p-2d4e1ca8.entry.js +1 -0
  112. package/dist/cobalt/{p-def448e4.entry.js → p-304c3be1.entry.js} +1 -1
  113. package/dist/cobalt/p-311118cb.entry.js +1 -0
  114. package/dist/cobalt/{p-2b4d8535.entry.js → p-328f0470.entry.js} +1 -1
  115. package/dist/cobalt/p-35c63fdc.entry.js +1 -0
  116. package/dist/cobalt/{p-f97ad9ad.entry.js → p-363a5262.entry.js} +1 -1
  117. package/dist/cobalt/p-376c43b8.entry.js +1 -0
  118. package/dist/cobalt/p-39a29bbb.entry.js +1 -0
  119. package/dist/cobalt/p-3d07e4a9.entry.js +1 -0
  120. package/dist/cobalt/{p-1e0282b8.entry.js → p-3dd6b57b.entry.js} +1 -1
  121. package/dist/cobalt/{p-7ef766c6.entry.js → p-48c5f311.entry.js} +1 -1
  122. package/dist/cobalt/{p-fe9e85b6.entry.js → p-4aeefe3f.entry.js} +1 -1
  123. package/dist/cobalt/p-523cfd98.entry.js +1 -0
  124. package/dist/cobalt/{p-c87285b6.entry.js → p-58aed8d6.entry.js} +1 -1
  125. package/dist/cobalt/p-593fd213.entry.js +1 -0
  126. package/dist/cobalt/p-5eecea26.entry.js +1 -0
  127. package/dist/cobalt/p-64a0b656.entry.js +1 -0
  128. package/dist/cobalt/p-69289e82.entry.js +1 -0
  129. package/dist/cobalt/p-6a3ed174.entry.js +1 -0
  130. package/dist/cobalt/p-6f12e721.entry.js +1 -0
  131. package/dist/cobalt/{p-3f9b3fcb.entry.js → p-7040e4c5.entry.js} +1 -1
  132. package/dist/cobalt/p-755ecee1.entry.js +1 -0
  133. package/dist/cobalt/p-76b46336.entry.js +1 -0
  134. package/dist/cobalt/p-79e49749.entry.js +1 -0
  135. package/dist/cobalt/p-7b5dec05.entry.js +1 -0
  136. package/dist/cobalt/p-7d9f2021.entry.js +1 -0
  137. package/dist/cobalt/p-804d025f.entry.js +1 -0
  138. package/dist/cobalt/p-860a685f.entry.js +1 -0
  139. package/dist/cobalt/p-87289d32.entry.js +1 -0
  140. package/dist/cobalt/{p-b758c2e6.entry.js → p-8ae5da0c.entry.js} +1 -1
  141. package/dist/cobalt/{p-ff2690e5.entry.js → p-8fdf2163.entry.js} +1 -1
  142. package/dist/cobalt/p-9310739f.entry.js +1 -0
  143. package/dist/cobalt/{p-522fcdb7.entry.js → p-96baec0e.entry.js} +1 -1
  144. package/dist/cobalt/p-B-ckjCUA.js +1 -0
  145. package/dist/cobalt/p-B2QLgWrz.js +2 -0
  146. package/dist/cobalt/p-B6JZk8fL.js +1 -0
  147. package/dist/cobalt/p-BUgUTjOB.js +1 -0
  148. package/dist/cobalt/p-Bgxofdfb.js +1 -0
  149. package/dist/cobalt/p-BxzuCRMr.js +1 -0
  150. package/dist/cobalt/p-Ch1jfwZ2.js +1 -0
  151. package/dist/cobalt/p-ChibIpZv.js +1 -0
  152. package/dist/cobalt/p-Es9IUmmt.js +2 -0
  153. package/dist/cobalt/p-a0749d7b.entry.js +1 -0
  154. package/dist/cobalt/p-a71fd4f1.entry.js +1 -0
  155. package/dist/cobalt/p-aa1ad9fa.entry.js +1 -0
  156. package/dist/cobalt/p-af0495d8.entry.js +1 -0
  157. package/dist/cobalt/p-b045d680.entry.js +1 -0
  158. package/dist/cobalt/p-b3c8fc9a.entry.js +1 -0
  159. package/dist/cobalt/p-b4dcc260.entry.js +1 -0
  160. package/dist/cobalt/p-b8200f7f.entry.js +1 -0
  161. package/dist/cobalt/p-b8f36f12.entry.js +1 -0
  162. package/dist/cobalt/p-ba6f0f90.entry.js +1 -0
  163. package/dist/cobalt/p-bae82095.entry.js +1 -0
  164. package/dist/cobalt/p-be3e61f4.entry.js +1 -0
  165. package/dist/cobalt/p-c1abc74b.entry.js +1 -0
  166. package/dist/cobalt/{p-bb07da22.entry.js → p-c23a2ff6.entry.js} +1 -1
  167. package/dist/cobalt/p-c682453a.entry.js +1 -0
  168. package/dist/cobalt/{p-97c20011.entry.js → p-c857b9ff.entry.js} +1 -1
  169. package/dist/cobalt/{p-54f26e95.entry.js → p-c85a6a9e.entry.js} +1 -1
  170. package/dist/cobalt/p-ca108c6d.entry.js +1 -0
  171. package/dist/cobalt/p-cf239452.entry.js +1 -0
  172. package/dist/cobalt/p-d2507f04.entry.js +1 -0
  173. package/dist/cobalt/p-d403d9c4.entry.js +1 -0
  174. package/dist/cobalt/{p-aa63812a.entry.js → p-d4086c34.entry.js} +1 -1
  175. package/dist/cobalt/p-d55652f2.entry.js +1 -0
  176. package/dist/cobalt/p-d7c9cf93.entry.js +1 -0
  177. package/dist/cobalt/p-d7cbb4c7.entry.js +1 -0
  178. package/dist/cobalt/p-da211abf.entry.js +1 -0
  179. package/dist/cobalt/p-dcc7fc95.entry.js +1 -0
  180. package/dist/cobalt/p-dd03c00b.entry.js +1 -0
  181. package/dist/cobalt/p-e55fb916.entry.js +1 -0
  182. package/dist/cobalt/p-e8e752f9.entry.js +1 -0
  183. package/dist/cobalt/p-e93644cb.entry.js +1 -0
  184. package/dist/cobalt/{p-f0e8fa7d.entry.js → p-f6b93f17.entry.js} +1 -1
  185. package/dist/cobalt/p-f72f0e38.entry.js +1 -0
  186. package/dist/cobalt/{p-beb9c11d.entry.js → p-fb86cdb9.entry.js} +1 -1
  187. package/dist/cobalt/p-ff6be242.entry.js +1 -0
  188. package/dist/cobalt/{p-CyYF8IJE.js → p-tPYikBRJ.js} +1 -1
  189. package/dist/collection/collection-manifest.json +18 -3
  190. package/dist/collection/components/co-accordion/co-accordion.js +2 -2
  191. package/dist/collection/components/co-app-shell/co-app-shell.css +79 -6
  192. package/dist/collection/components/co-app-shell/co-app-shell.js +2 -2
  193. package/dist/collection/components/co-autocomplete/co-autocomplete.css +28 -1
  194. package/dist/collection/components/co-autocomplete/co-autocomplete.js +27 -4
  195. package/dist/collection/components/co-avatar/co-avatar.css +38 -1
  196. package/dist/collection/components/co-avatar/co-avatar.js +48 -3
  197. package/dist/collection/components/co-avatar-group/co-avatar-group.css +86 -0
  198. package/dist/collection/components/co-avatar-group/co-avatar-group.js +224 -0
  199. package/dist/collection/components/co-badge/co-badge.js +1 -1
  200. package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.css +136 -0
  201. package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.js +507 -0
  202. package/dist/collection/components/co-breadcrumb/co-breadcrumb.css +6 -0
  203. package/dist/collection/components/co-breadcrumb/co-breadcrumb.js +111 -0
  204. package/dist/collection/components/co-breadcrumb-item/co-breadcrumb-item.css +66 -0
  205. package/dist/collection/components/co-breadcrumb-item/co-breadcrumb-item.js +169 -0
  206. package/dist/collection/components/co-button/co-button.css +63 -7
  207. package/dist/collection/components/co-button/co-button.js +72 -7
  208. package/dist/collection/components/co-button-group/co-button-group.js +2 -2
  209. package/dist/collection/components/co-calendar/co-calendar.js +1 -1
  210. package/dist/collection/components/co-calendar-month/co-calendar-month.css +231 -0
  211. package/dist/collection/components/co-calendar-month/co-calendar-month.js +594 -0
  212. package/dist/collection/components/co-card/co-card.js +2 -2
  213. package/dist/collection/components/co-checkbox/co-checkbox.css +70 -1
  214. package/dist/collection/components/co-checkbox/co-checkbox.js +58 -7
  215. package/dist/collection/components/co-checkbox-group/co-checkbox-group.css +7 -1
  216. package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +3 -3
  217. package/dist/collection/components/co-chip/co-chip.css +82 -7
  218. package/dist/collection/components/co-chip/co-chip.js +102 -17
  219. package/dist/collection/components/co-color-picker/co-color-picker.css +35 -1
  220. package/dist/collection/components/co-color-picker/co-color-picker.js +24 -2
  221. package/dist/collection/components/co-column-picker/co-column-picker.js +2 -2
  222. package/dist/collection/components/co-combobox/co-combobox.css +7 -47
  223. package/dist/collection/components/co-combobox/co-combobox.js +4 -4
  224. package/dist/collection/components/co-command-search/co-command-search.css +163 -1
  225. package/dist/collection/components/co-command-search/co-command-search.js +355 -14
  226. package/dist/collection/components/co-command-search/commandSearchGroup.js +17 -0
  227. package/dist/collection/components/co-command-search/commandSearchLogic.js +27 -0
  228. package/dist/collection/components/co-data-table/co-data-table.css +42 -2
  229. package/dist/collection/components/co-data-table/co-data-table.js +116 -23
  230. package/dist/collection/components/co-data-table-pagination/co-data-table-pagination.js +4 -4
  231. package/dist/collection/components/co-date-field/co-date-field.css +7 -1
  232. package/dist/collection/components/co-date-field/co-date-field.js +4 -4
  233. package/dist/collection/components/co-date-range/co-date-range.js +1 -1
  234. package/dist/collection/components/co-date-range-picker/co-date-range-picker.css +7 -1
  235. package/dist/collection/components/co-drawer/co-drawer.css +21 -3
  236. package/dist/collection/components/co-drawer/co-drawer.js +52 -2
  237. package/dist/collection/components/co-drive-card/co-drive-card.css +285 -0
  238. package/dist/collection/components/co-drive-card/co-drive-card.js +329 -0
  239. package/dist/collection/components/co-drop-zone/co-drop-zone.css +49 -0
  240. package/dist/collection/components/co-drop-zone/co-drop-zone.js +197 -0
  241. package/dist/collection/components/co-empty-state/co-empty-state.css +38 -0
  242. package/dist/collection/components/co-empty-state/co-empty-state.js +72 -1
  243. package/dist/collection/components/co-event-calendar/co-event-calendar.css +146 -0
  244. package/dist/collection/components/co-event-calendar/co-event-calendar.js +620 -0
  245. package/dist/collection/components/co-fab/co-fab.js +2 -2
  246. package/dist/collection/components/co-file-field/co-file-field.css +7 -1
  247. package/dist/collection/components/co-file-field/co-file-field.js +5 -5
  248. package/dist/collection/components/co-file-type-tile/co-file-type-tile.css +14 -0
  249. package/dist/collection/components/co-file-type-tile/co-file-type-tile.js +164 -0
  250. package/dist/collection/components/co-helper-text/co-helper-text.js +2 -2
  251. package/dist/collection/components/co-icon/co-icon.js +7 -37
  252. package/dist/collection/components/co-icon/iconLoader.js +39 -0
  253. package/dist/collection/components/co-icon-button/co-icon-button.css +26 -8
  254. package/dist/collection/components/co-icon-button/co-icon-button.js +82 -8
  255. package/dist/collection/components/co-input/co-input.css +65 -1
  256. package/dist/collection/components/co-input/co-input.js +54 -5
  257. package/dist/collection/components/co-json-field/co-json-field.css +25 -1
  258. package/dist/collection/components/co-json-field/co-json-field.js +27 -3
  259. package/dist/collection/components/co-lightbox/co-lightbox.js +4 -4
  260. package/dist/collection/components/co-list-footer/co-list-footer.css +50 -0
  261. package/dist/collection/components/co-list-footer/co-list-footer.js +149 -0
  262. package/dist/collection/components/co-list-item/co-list-item.css +7 -0
  263. package/dist/collection/components/co-list-item/co-list-item.js +65 -2
  264. package/dist/collection/components/co-loader/co-loader.css +9 -0
  265. package/dist/collection/components/co-media-thumb/co-media-thumb.css +43 -0
  266. package/dist/collection/components/co-media-thumb/co-media-thumb.js +148 -0
  267. package/dist/collection/components/co-menu/co-menu.css +6 -4
  268. package/dist/collection/components/co-menu/co-menu.js +20 -1
  269. package/dist/collection/components/co-menu-divider/co-menu-divider.js +1 -1
  270. package/dist/collection/components/co-menu-item/co-menu-item.css +44 -8
  271. package/dist/collection/components/co-menu-item/co-menu-item.js +84 -10
  272. package/dist/collection/components/co-modal/co-modal.css +59 -2
  273. package/dist/collection/components/co-modal/co-modal.js +99 -6
  274. package/dist/collection/components/co-navbar/co-navbar.js +2 -2
  275. package/dist/collection/components/co-number-field/co-number-field.css +19 -1
  276. package/dist/collection/components/co-number-field/co-number-field.js +34 -3
  277. package/dist/collection/components/co-option-list/co-option-list.css +228 -0
  278. package/dist/collection/components/co-option-list/co-option-list.js +860 -0
  279. package/dist/collection/components/co-option-list/optionListLogic.js +100 -0
  280. package/dist/collection/components/co-overlay/co-overlay.css +15 -1
  281. package/dist/collection/components/co-overlay/co-overlay.js +266 -1
  282. package/dist/collection/components/co-overlay/focusTrap.js +53 -0
  283. package/dist/collection/components/co-overlay/overlayAfterClose.js +8 -0
  284. package/dist/collection/components/co-page-header/co-page-header.js +1 -1
  285. package/dist/collection/components/co-phone-field/co-phone-field.css +30 -2
  286. package/dist/collection/components/co-phone-field/co-phone-field.js +23 -1
  287. package/dist/collection/components/co-popover/co-popover.css +9 -0
  288. package/dist/collection/components/co-popover/co-popover.js +43 -12
  289. package/dist/collection/components/co-progress-bar/co-progress-bar.css +22 -0
  290. package/dist/collection/components/co-progress-bar/co-progress-bar.js +26 -2
  291. package/dist/collection/components/co-progress-circle/co-progress-circle.js +2 -2
  292. package/dist/collection/components/co-radio/co-radio.js +3 -3
  293. package/dist/collection/components/co-radio-group/co-radio-group.js +3 -3
  294. package/dist/collection/components/co-rating/co-rating.js +3 -3
  295. package/dist/collection/components/co-ref-field/co-ref-field.css +64 -6
  296. package/dist/collection/components/co-ref-field/co-ref-field.js +0 -0
  297. package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.css +1125 -41
  298. package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.js +2008 -53
  299. package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.css +791 -9
  300. package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.js +1071 -71
  301. package/dist/collection/components/co-rich-text-viewer/co-rich-text-viewer.css +78 -0
  302. package/dist/collection/components/co-rich-text-viewer/co-rich-text-viewer.js +46 -41
  303. package/dist/collection/components/co-row/co-row.js +2 -2
  304. package/dist/collection/components/co-segmented-bar/co-segmented-bar.css +36 -0
  305. package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +176 -0
  306. package/dist/collection/components/co-select/co-select.css +7 -1
  307. package/dist/collection/components/co-select/co-select.js +5 -5
  308. package/dist/collection/components/co-separator/co-separator.js +1 -1
  309. package/dist/collection/components/co-sidebar/co-sidebar.js +2 -2
  310. package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.css +99 -12
  311. package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +28 -8
  312. package/dist/collection/components/co-skeleton/co-skeleton.css +28 -0
  313. package/dist/collection/components/co-skeleton/co-skeleton.js +97 -0
  314. package/dist/collection/components/co-slider/co-slider.js +2 -2
  315. package/dist/collection/components/co-switch/co-switch.css +9 -1
  316. package/dist/collection/components/co-switch/co-switch.js +3 -3
  317. package/dist/collection/components/co-tab/co-tab.css +54 -3
  318. package/dist/collection/components/co-tab/co-tab.js +45 -3
  319. package/dist/collection/components/co-tab-group/co-tab-group.css +8 -0
  320. package/dist/collection/components/co-tab-group/co-tab-group.js +35 -1
  321. package/dist/collection/components/co-tags-field/co-tags-field.css +39 -1
  322. package/dist/collection/components/co-tags-field/co-tags-field.js +36 -10
  323. package/dist/collection/components/co-textarea/co-textarea.css +26 -1
  324. package/dist/collection/components/co-textarea/co-textarea.js +28 -4
  325. package/dist/collection/components/co-toast/co-toast.css +56 -2
  326. package/dist/collection/components/co-toast/co-toast.js +27 -2
  327. package/dist/collection/components/co-toggle-button/co-toggle-button.css +64 -4
  328. package/dist/collection/components/co-toggle-button/co-toggle-button.js +47 -4
  329. package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.css +32 -12
  330. package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.js +34 -2
  331. package/dist/collection/components/co-tooltip/co-tooltip.css +8 -0
  332. package/dist/collection/components/co-tooltip/co-tooltip.js +69 -14
  333. package/dist/collection/components/co-transfer-list/co-transfer-list.js +2 -2
  334. package/dist/collection/components/co-video/co-video.css +497 -0
  335. package/dist/collection/components/co-video/co-video.js +1772 -0
  336. package/dist/collection/utils/fieldChip.js +2 -0
  337. package/dist/collection/utils/icon-config.js +12 -0
  338. package/dist/collection/utils/richTextI18n.js +378 -0
  339. package/dist/collection/utils/sheetGesture.js +53 -0
  340. package/dist/components/co-accordion.js +1 -1
  341. package/dist/components/co-app-shell.js +1 -1
  342. package/dist/components/co-autocomplete.js +1 -1
  343. package/dist/components/co-avatar-group.d.ts +11 -0
  344. package/dist/components/co-avatar-group.js +1 -0
  345. package/dist/components/co-avatar.js +1 -1
  346. package/dist/components/co-badge.js +1 -1
  347. package/dist/components/co-bottom-sheet.d.ts +11 -0
  348. package/dist/components/co-bottom-sheet.js +1 -0
  349. package/dist/components/co-breadcrumb-item.d.ts +11 -0
  350. package/dist/components/co-breadcrumb-item.js +1 -0
  351. package/dist/components/co-breadcrumb.d.ts +11 -0
  352. package/dist/components/co-breadcrumb.js +1 -0
  353. package/dist/components/co-button-group.js +1 -1
  354. package/dist/components/co-button.js +1 -1
  355. package/dist/components/co-calendar-month.d.ts +11 -0
  356. package/dist/components/co-calendar-month.js +1 -0
  357. package/dist/components/co-calendar.js +1 -1
  358. package/dist/components/co-card.js +1 -1
  359. package/dist/components/co-checkbox-group.js +1 -1
  360. package/dist/components/co-checkbox.js +1 -1
  361. package/dist/components/co-chip.js +1 -1
  362. package/dist/components/co-color-picker.js +1 -1
  363. package/dist/components/co-column-picker.js +1 -1
  364. package/dist/components/co-combobox.js +1 -1
  365. package/dist/components/co-command-search.js +1 -1
  366. package/dist/components/co-data-table-pagination.js +1 -1
  367. package/dist/components/co-data-table.js +1 -1
  368. package/dist/components/co-date-field.js +1 -1
  369. package/dist/components/co-date-picker.js +1 -1
  370. package/dist/components/co-date-range-picker.js +1 -1
  371. package/dist/components/co-date-range.js +1 -1
  372. package/dist/components/co-drawer.js +1 -1
  373. package/dist/components/co-drive-card.d.ts +11 -0
  374. package/dist/components/co-drive-card.js +1 -0
  375. package/dist/components/co-drop-zone.d.ts +11 -0
  376. package/dist/components/co-drop-zone.js +1 -0
  377. package/dist/components/co-empty-state.js +1 -1
  378. package/dist/components/co-event-calendar.d.ts +11 -0
  379. package/dist/components/co-event-calendar.js +1 -0
  380. package/dist/components/co-fab.js +1 -1
  381. package/dist/components/co-file-field.js +1 -1
  382. package/dist/components/co-file-type-tile.d.ts +11 -0
  383. package/dist/components/co-file-type-tile.js +1 -0
  384. package/dist/components/co-helper-text.js +1 -1
  385. package/dist/components/co-icon-button.js +1 -1
  386. package/dist/components/co-icon.js +1 -1
  387. package/dist/components/co-input.js +1 -1
  388. package/dist/components/co-json-field.js +1 -1
  389. package/dist/components/co-lightbox.js +1 -1
  390. package/dist/components/co-list-footer.d.ts +11 -0
  391. package/dist/components/co-list-footer.js +1 -0
  392. package/dist/components/co-list-item.js +1 -1
  393. package/dist/components/co-loader.js +1 -1
  394. package/dist/components/co-media-thumb.d.ts +11 -0
  395. package/dist/components/co-media-thumb.js +1 -0
  396. package/dist/components/co-menu-divider.js +1 -1
  397. package/dist/components/co-menu-item.js +1 -1
  398. package/dist/components/co-menu.js +1 -1
  399. package/dist/components/co-modal.js +1 -1
  400. package/dist/components/co-navbar.js +1 -1
  401. package/dist/components/co-number-field.js +1 -1
  402. package/dist/components/co-option-list.d.ts +11 -0
  403. package/dist/components/co-option-list.js +1 -0
  404. package/dist/components/co-overlay.js +1 -1
  405. package/dist/components/co-page-header.js +1 -1
  406. package/dist/components/co-phone-field.js +1 -1
  407. package/dist/components/co-popover.js +1 -1
  408. package/dist/components/co-progress-bar.js +1 -1
  409. package/dist/components/co-progress-circle.js +1 -1
  410. package/dist/components/co-radio-group.js +1 -1
  411. package/dist/components/co-radio.js +1 -1
  412. package/dist/components/co-rating.js +1 -1
  413. package/dist/components/co-ref-field.js +1 -1
  414. package/dist/components/co-rich-text-editor.js +1 -1
  415. package/dist/components/co-rich-text-toolbar.js +1 -1
  416. package/dist/components/co-rich-text-viewer.js +1 -1
  417. package/dist/components/co-row.js +1 -1
  418. package/dist/components/co-segmented-bar.d.ts +11 -0
  419. package/dist/components/co-segmented-bar.js +1 -0
  420. package/dist/components/co-select.js +1 -1
  421. package/dist/components/co-separator.js +1 -1
  422. package/dist/components/co-sidebar-nav.js +1 -1
  423. package/dist/components/co-sidebar.js +1 -1
  424. package/dist/components/co-skeleton.d.ts +11 -0
  425. package/dist/components/co-skeleton.js +1 -0
  426. package/dist/components/co-slider.js +1 -1
  427. package/dist/components/co-switch.js +1 -1
  428. package/dist/components/co-tab-group.js +1 -1
  429. package/dist/components/co-tab.js +1 -1
  430. package/dist/components/co-tags-field.js +1 -1
  431. package/dist/components/co-textarea.js +1 -1
  432. package/dist/components/co-toast.js +1 -1
  433. package/dist/components/co-toggle-button-group.js +1 -1
  434. package/dist/components/co-toggle-button.js +1 -1
  435. package/dist/components/co-tooltip.js +1 -1
  436. package/dist/components/co-transfer-list.js +1 -1
  437. package/dist/components/co-video.d.ts +11 -0
  438. package/dist/components/co-video.js +1 -0
  439. package/dist/components/index.js +1 -1
  440. package/dist/components/p-B0buzt_E.js +1 -0
  441. package/dist/components/p-B2QLgWrz.js +2 -0
  442. package/dist/components/p-B3igDRut.js +1 -0
  443. package/dist/components/p-Bgxofdfb.js +1 -0
  444. package/dist/components/p-BhSJcOUS.js +1 -0
  445. package/dist/components/{p-DObrQi5F.js → p-BnTTeIFH.js} +1 -1
  446. package/dist/components/{p-C9tHLXKS.js → p-Bx1OwdIV.js} +1 -1
  447. package/dist/components/p-BxiKVtk4.js +1 -0
  448. package/dist/components/p-C7tPadNy.js +1 -0
  449. package/dist/components/p-CBzpUqwG.js +1 -0
  450. package/dist/components/p-CUvAZhuM.js +1 -0
  451. package/dist/components/p-CZKHHCdL.js +1 -0
  452. package/dist/components/p-ChibIpZv.js +1 -0
  453. package/dist/components/p-CybHxq25.js +1 -0
  454. package/dist/components/p-CzkeIavk.js +1 -0
  455. package/dist/components/p-D7dk2gM7.js +1 -0
  456. package/dist/components/p-DTS-dgEe.js +1 -0
  457. package/dist/components/p-DvhR3H61.js +1 -0
  458. package/dist/components/{p-OGpfa9Yd.js → p-DzoNGEDU.js} +1 -1
  459. package/dist/components/{p-BeWXpX8I.js → p-HXlAaApx.js} +1 -1
  460. package/dist/components/p-K6LC1Hz4.js +1 -0
  461. package/dist/components/p-LAxCvyJl.js +1 -0
  462. package/dist/components/p-vLFwJ77w.js +1 -0
  463. package/dist/esm/co-accordion.entry.js +3 -3
  464. package/dist/esm/co-app-shell.entry.js +4 -4
  465. package/dist/esm/co-autocomplete.entry.js +10 -7
  466. package/dist/esm/co-avatar-group.entry.js +75 -0
  467. package/dist/esm/co-avatar.entry.js +12 -5
  468. package/dist/esm/co-badge.entry.js +2 -2
  469. package/dist/esm/co-bottom-sheet.entry.js +323 -0
  470. package/dist/esm/co-breadcrumb-item.entry.js +35 -0
  471. package/dist/esm/co-breadcrumb.entry.js +53 -0
  472. package/dist/esm/co-button-group.entry.js +3 -3
  473. package/dist/esm/co-button.entry.js +12 -6
  474. package/dist/esm/co-calendar-month.entry.js +379 -0
  475. package/dist/esm/co-calendar_3.entry.js +42 -12
  476. package/dist/esm/co-card.entry.js +3 -3
  477. package/dist/esm/co-checkbox-group.entry.js +5 -5
  478. package/dist/esm/co-checkbox.entry.js +18 -6
  479. package/dist/esm/co-chip.entry.js +19 -11
  480. package/dist/esm/co-color-picker.entry.js +7 -5
  481. package/dist/esm/co-column-picker.entry.js +3 -3
  482. package/dist/esm/co-combobox.entry.js +7 -7
  483. package/dist/esm/co-command-search.entry.js +151 -12
  484. package/dist/esm/co-data-table-pagination.entry.js +5 -5
  485. package/dist/esm/co-data-table.entry.js +44 -23
  486. package/dist/esm/co-date-picker.entry.js +2 -2
  487. package/dist/esm/co-date-range-picker.entry.js +4 -4
  488. package/dist/esm/co-date-range.entry.js +2 -2
  489. package/dist/esm/co-drive-card.entry.js +79 -0
  490. package/dist/esm/co-drop-zone.entry.js +61 -0
  491. package/dist/esm/co-empty-state.entry.js +13 -3
  492. package/dist/esm/co-event-calendar.entry.js +316 -0
  493. package/dist/esm/co-fab.entry.js +3 -3
  494. package/dist/esm/co-file-field.entry.js +8 -8
  495. package/dist/esm/co-file-type-tile.entry.js +76 -0
  496. package/dist/esm/co-helper-text.entry.js +3 -3
  497. package/dist/esm/co-icon-button.entry.js +24 -8
  498. package/dist/esm/co-icon.entry.js +33 -25
  499. package/dist/esm/co-input.entry.js +36 -7
  500. package/dist/esm/co-json-field.entry.js +10 -6
  501. package/dist/esm/co-lightbox.entry.js +5 -5
  502. package/dist/esm/co-list-footer.entry.js +23 -0
  503. package/dist/esm/co-list-item.entry.js +60 -4
  504. package/dist/esm/co-loader.entry.js +2 -2
  505. package/dist/esm/co-media-thumb.entry.js +34 -0
  506. package/dist/esm/co-menu-divider.entry.js +2 -2
  507. package/dist/esm/co-menu-item.entry.js +26 -11
  508. package/dist/esm/co-menu.entry.js +3 -3
  509. package/dist/esm/co-modal.entry.js +41 -5
  510. package/dist/esm/co-navbar.entry.js +3 -3
  511. package/dist/esm/co-number-field.entry.js +18 -6
  512. package/dist/esm/co-option-list.entry.js +377 -0
  513. package/dist/esm/co-overlay.entry.js +243 -3
  514. package/dist/esm/co-page-header.entry.js +2 -2
  515. package/dist/esm/co-phone-field.entry.js +6 -4
  516. package/dist/esm/co-popover.entry.js +45 -14
  517. package/dist/esm/co-progress-bar.entry.js +8 -4
  518. package/dist/esm/co-progress-circle.entry.js +3 -3
  519. package/dist/esm/co-radio-group.entry.js +4 -4
  520. package/dist/esm/co-radio.entry.js +4 -4
  521. package/dist/esm/co-rating.entry.js +4 -4
  522. package/dist/esm/co-ref-field.entry.js +36 -13
  523. package/dist/esm/co-rich-text-editor.entry.js +1879 -33
  524. package/dist/esm/co-rich-text-toolbar.entry.js +995 -71
  525. package/dist/esm/co-rich-text-viewer.entry.js +55 -19
  526. package/dist/esm/co-row.entry.js +3 -3
  527. package/dist/esm/co-segmented-bar.entry.js +56 -0
  528. package/dist/esm/co-select.entry.js +8 -8
  529. package/dist/esm/co-separator.entry.js +2 -2
  530. package/dist/esm/co-sidebar-nav.entry.js +29 -9
  531. package/dist/esm/co-sidebar.entry.js +3 -3
  532. package/dist/esm/co-skeleton.entry.js +24 -0
  533. package/dist/esm/co-slider.entry.js +3 -3
  534. package/dist/esm/co-switch.entry.js +6 -6
  535. package/dist/esm/co-tab-group.entry.js +15 -3
  536. package/dist/esm/co-tab.entry.js +8 -5
  537. package/dist/esm/co-tags-field.entry.js +19 -13
  538. package/dist/esm/co-textarea.entry.js +11 -7
  539. package/dist/esm/co-toast.entry.js +10 -4
  540. package/dist/esm/co-toggle-button-group.entry.js +16 -4
  541. package/dist/esm/co-toggle-button.entry.js +9 -5
  542. package/dist/esm/co-tooltip.entry.js +51 -16
  543. package/dist/esm/co-transfer-list.entry.js +3 -3
  544. package/dist/esm/co-video.entry.js +837 -0
  545. package/dist/esm/cobalt.js +3 -3
  546. package/dist/esm/{dates-CyYF8IJE.js → dates-tPYikBRJ.js} +1 -1
  547. package/dist/esm/fieldChip-BBu4ukje.js +5 -0
  548. package/dist/{cjs/index-CaEJnpZe.js → esm/index-Cyrxk8Qq.js} +14437 -6194
  549. package/dist/esm/{index-BKmfyPZK.js → index-Es9IUmmt.js} +105 -21
  550. package/dist/esm/loader.js +3 -3
  551. package/dist/esm/overlayAfterClose-Ch1jfwZ2.js +10 -0
  552. package/dist/{cjs/sanitizer-CjHZAkOm.js → esm/purify.es-B2QLgWrz.js} +1 -110
  553. package/dist/esm/{requiredMark-DFvtYtkE.js → requiredMark-Bg25QWFn.js} +1 -1
  554. package/dist/esm/richTextI18n-B-ckjCUA.js +380 -0
  555. package/dist/esm/toolbar-config-ChibIpZv.js +212 -0
  556. package/dist/esm/tz-B6JZk8fL.js +119 -0
  557. package/dist/types/components/co-autocomplete/co-autocomplete.d.ts +2 -0
  558. package/dist/types/components/co-avatar/co-avatar.d.ts +4 -0
  559. package/dist/types/components/co-avatar-group/co-avatar-group.d.ts +33 -0
  560. package/dist/types/components/co-bottom-sheet/co-bottom-sheet.d.ts +81 -0
  561. package/dist/types/components/co-breadcrumb/co-breadcrumb.d.ts +27 -0
  562. package/dist/types/components/co-breadcrumb-item/co-breadcrumb-item.d.ts +33 -0
  563. package/dist/types/components/co-button/co-button.d.ts +7 -1
  564. package/dist/types/components/co-calendar-month/co-calendar-month.d.ts +83 -0
  565. package/dist/types/components/co-checkbox/co-checkbox.d.ts +10 -2
  566. package/dist/types/components/co-chip/co-chip.d.ts +22 -5
  567. package/dist/types/components/co-color-picker/co-color-picker.d.ts +2 -0
  568. package/dist/types/components/co-command-search/co-command-search.d.ts +39 -6
  569. package/dist/types/components/co-command-search/commandSearchGroup.d.ts +20 -0
  570. package/dist/types/components/co-command-search/commandSearchLogic.d.ts +17 -0
  571. package/dist/types/components/co-data-table/co-data-table.d.ts +15 -2
  572. package/dist/types/components/co-drawer/co-drawer.d.ts +12 -0
  573. package/dist/types/components/co-drive-card/co-drive-card.d.ts +52 -0
  574. package/dist/types/components/co-drop-zone/co-drop-zone.d.ts +38 -0
  575. package/dist/types/components/co-empty-state/co-empty-state.d.ts +6 -0
  576. package/dist/types/components/co-event-calendar/co-event-calendar.d.ts +110 -0
  577. package/dist/types/components/co-file-type-tile/co-file-type-tile.d.ts +11 -0
  578. package/dist/types/components/co-icon/co-icon.d.ts +1 -1
  579. package/dist/types/components/co-icon/iconLoader.d.ts +6 -0
  580. package/dist/types/components/co-icon-button/co-icon-button.d.ts +18 -1
  581. package/dist/types/components/co-input/co-input.d.ts +13 -1
  582. package/dist/types/components/co-json-field/co-json-field.d.ts +3 -0
  583. package/dist/types/components/co-list-footer/co-list-footer.d.ts +26 -0
  584. package/dist/types/components/co-list-item/co-list-item.d.ts +11 -0
  585. package/dist/types/components/co-media-thumb/co-media-thumb.d.ts +21 -0
  586. package/dist/types/components/co-menu/co-menu.d.ts +4 -0
  587. package/dist/types/components/co-menu-item/co-menu-item.d.ts +10 -1
  588. package/dist/types/components/co-modal/co-modal.d.ts +26 -2
  589. package/dist/types/components/co-number-field/co-number-field.d.ts +13 -0
  590. package/dist/types/components/co-option-list/co-option-list.d.ts +101 -0
  591. package/dist/types/components/co-option-list/optionListLogic.d.ts +69 -0
  592. package/dist/types/components/co-overlay/co-overlay.d.ts +45 -0
  593. package/dist/types/components/co-overlay/focusTrap.d.ts +20 -0
  594. package/dist/types/components/co-overlay/overlayAfterClose.d.ts +5 -0
  595. package/dist/types/components/co-phone-field/co-phone-field.d.ts +2 -0
  596. package/dist/types/components/co-popover/co-popover.d.ts +9 -0
  597. package/dist/types/components/co-progress-bar/co-progress-bar.d.ts +2 -0
  598. package/dist/types/components/co-ref-field/co-ref-field.d.ts +17 -2
  599. package/dist/types/components/co-rich-text-editor/co-rich-text-editor.d.ts +161 -9
  600. package/dist/types/components/co-rich-text-toolbar/co-rich-text-toolbar.d.ts +119 -13
  601. package/dist/types/components/co-rich-text-viewer/co-rich-text-viewer.d.ts +1 -14
  602. package/dist/types/components/co-segmented-bar/co-segmented-bar.d.ts +32 -0
  603. package/dist/types/components/co-skeleton/co-skeleton.d.ts +13 -0
  604. package/dist/types/components/co-tab/co-tab.d.ts +4 -0
  605. package/dist/types/components/co-tab-group/co-tab-group.d.ts +3 -0
  606. package/dist/types/components/co-tags-field/co-tags-field.d.ts +2 -0
  607. package/dist/types/components/co-textarea/co-textarea.d.ts +3 -0
  608. package/dist/types/components/co-toast/co-toast.d.ts +7 -0
  609. package/dist/types/components/co-toggle-button/co-toggle-button.d.ts +6 -0
  610. package/dist/types/components/co-toggle-button-group/co-toggle-button-group.d.ts +5 -0
  611. package/dist/types/components/co-tooltip/co-tooltip.d.ts +12 -0
  612. package/dist/types/components/co-video/co-video.d.ts +203 -0
  613. package/dist/types/components.d.ts +3184 -221
  614. package/dist/types/utils/fieldChip.d.ts +37 -0
  615. package/dist/types/utils/icon-config.d.ts +11 -0
  616. package/dist/types/utils/richTextI18n.d.ts +130 -0
  617. package/dist/types/utils/sheetGesture.d.ts +31 -0
  618. package/package.json +5 -4
  619. package/dist/cobalt/p-01fc77c4.entry.js +0 -1
  620. package/dist/cobalt/p-0a6d8061.entry.js +0 -1
  621. package/dist/cobalt/p-0bf288cd.entry.js +0 -1
  622. package/dist/cobalt/p-15708d32.entry.js +0 -1
  623. package/dist/cobalt/p-1578b019.entry.js +0 -1
  624. package/dist/cobalt/p-23d48581.entry.js +0 -1
  625. package/dist/cobalt/p-2718ff27.entry.js +0 -1
  626. package/dist/cobalt/p-3190d715.entry.js +0 -1
  627. package/dist/cobalt/p-41228dce.entry.js +0 -1
  628. package/dist/cobalt/p-44672953.entry.js +0 -1
  629. package/dist/cobalt/p-53ce9023.entry.js +0 -1
  630. package/dist/cobalt/p-5d01bd4d.entry.js +0 -1
  631. package/dist/cobalt/p-5e7141aa.entry.js +0 -1
  632. package/dist/cobalt/p-5f905739.entry.js +0 -1
  633. package/dist/cobalt/p-6404bd21.entry.js +0 -1
  634. package/dist/cobalt/p-654bbc2a.entry.js +0 -1
  635. package/dist/cobalt/p-6678ac0a.entry.js +0 -1
  636. package/dist/cobalt/p-66a912dd.entry.js +0 -1
  637. package/dist/cobalt/p-6759ee1c.entry.js +0 -1
  638. package/dist/cobalt/p-69a40bfd.entry.js +0 -1
  639. package/dist/cobalt/p-6a6a62cb.entry.js +0 -1
  640. package/dist/cobalt/p-6d43b6cd.entry.js +0 -1
  641. package/dist/cobalt/p-759c3dd0.entry.js +0 -1
  642. package/dist/cobalt/p-782f729f.entry.js +0 -1
  643. package/dist/cobalt/p-83b89bf8.entry.js +0 -1
  644. package/dist/cobalt/p-88dc561a.entry.js +0 -1
  645. package/dist/cobalt/p-8c27aff7.entry.js +0 -1
  646. package/dist/cobalt/p-95d14878.entry.js +0 -1
  647. package/dist/cobalt/p-996179f7.entry.js +0 -1
  648. package/dist/cobalt/p-9c533830.entry.js +0 -1
  649. package/dist/cobalt/p-9d89bf47.entry.js +0 -1
  650. package/dist/cobalt/p-BKmfyPZK.js +0 -2
  651. package/dist/cobalt/p-BchB6Hdr.js +0 -1
  652. package/dist/cobalt/p-DcK0GGZC.js +0 -2
  653. package/dist/cobalt/p-Qqq8fysz.js +0 -1
  654. package/dist/cobalt/p-aa17c70b.entry.js +0 -1
  655. package/dist/cobalt/p-ad4c1b41.entry.js +0 -1
  656. package/dist/cobalt/p-ae4d3207.entry.js +0 -1
  657. package/dist/cobalt/p-af64da37.entry.js +0 -1
  658. package/dist/cobalt/p-c6a242e4.entry.js +0 -1
  659. package/dist/cobalt/p-cd57e8b4.entry.js +0 -1
  660. package/dist/cobalt/p-ce3a535f.entry.js +0 -1
  661. package/dist/cobalt/p-d17e1444.entry.js +0 -1
  662. package/dist/cobalt/p-d958566a.entry.js +0 -1
  663. package/dist/cobalt/p-dd873bf8.entry.js +0 -1
  664. package/dist/cobalt/p-e4de8865.entry.js +0 -1
  665. package/dist/cobalt/p-eba0bf7f.entry.js +0 -1
  666. package/dist/cobalt/p-f8728b21.entry.js +0 -1
  667. package/dist/cobalt/p-fad3605d.entry.js +0 -1
  668. package/dist/components/p-BXdfh__-.js +0 -1
  669. package/dist/components/p-BchB6Hdr.js +0 -1
  670. package/dist/components/p-BiW0ubLs.js +0 -1
  671. package/dist/components/p-CaMgu2_g.js +0 -1
  672. package/dist/components/p-DBhNS7JD.js +0 -1
  673. package/dist/components/p-DPi_U-Kl.js +0 -1
  674. package/dist/components/p-DcK0GGZC.js +0 -2
  675. package/dist/components/p-MiSJXuBx.js +0 -1
  676. package/dist/components/p-Sgn-2g8m.js +0 -1
  677. package/dist/components/p-jDjfGZJp.js +0 -1
  678. package/dist/components/p-o8hrDIKe.js +0 -1
@@ -1,5 +1,6 @@
1
1
  import { h } from "@stencil/core";
2
- import { RequiredMark } from "../../utils/requiredMark";
2
+ import * as RichText from "@prolibu-suite/cobalt-rich-text";
3
+ import { getRichTextStrings } from "../../utils/richTextI18n";
3
4
  /**
4
5
  * Rich text editor backed by Tiptap v3.
5
6
  *
@@ -20,8 +21,6 @@ export class CoRichTextEditor {
20
21
  /** Preset controlling allowed nodes/marks, toolbar and sanitization. */
21
22
  this.preset = 'task';
22
23
  this.placeholder = '';
23
- /** Label layout: vertical (label above) or horizontal (label beside) */
24
- this.layout = 'vertical';
25
24
  this.status = 'default';
26
25
  this.disabled = false;
27
26
  this.readonly = false;
@@ -30,42 +29,80 @@ export class CoRichTextEditor {
30
29
  this.autofocus = false;
31
30
  /** Toolbar layout. `auto` uses the preset default. */
32
31
  this.toolbar = 'auto';
32
+ /** BCP-47 locale for UI strings (e.g. 'en', 'es-CO', 'pt-BR'). Default English. */
33
+ this.locale = 'en';
33
34
  this.ready = false;
34
35
  this.hasFocus = false;
36
+ /** Character count — updated imperatively to avoid re-renders on every keystroke. */
35
37
  this.charCount = 0;
36
- /** Last value we emitted — guards the value Watch against feedback loops. */
37
- this.lastEmitted = null;
38
+ this.bubbleDragged = false;
39
+ this.bubbleDragOffset = { x: 0, y: 0 };
40
+ this.bubbleDragPos = { x: 0, y: 0 };
41
+ /** Position lock — prevents floating-ui from repositioning the bubble during toolbar interactions. */
42
+ this.bubblePositionLocked = false;
43
+ this.bubbleLockPos = { x: 0, y: 0 };
44
+ this.applyingBubbleLock = false;
45
+ /** Account fonts loaded via fontLoader callback. */
46
+ this._loadedAccountFonts = [];
47
+ this._fontLoaderCalled = false;
48
+ this._fontLoaderLoading = false;
49
+ /** Last emitted JSON string — guards the value Watch against feedback loops. */
50
+ this.lastEmittedJson = null;
51
+ /** Last emitted HTML string — parents often send HTML back via value prop. */
52
+ this.lastEmittedHtml = '';
38
53
  /** True while applying an external value (suppresses emit). */
39
54
  this.applyingExternal = false;
40
- /** Set by disconnectedCallback — aborts editor creation if the element was
41
- * removed while the lazy import was still in flight (ProseMirror leak). */
42
- this.destroyed = false;
55
+ this.rt = RichText;
56
+ this.imgLayoutVisible = false;
43
57
  this.handleUpdate = (editor) => {
44
58
  this.refreshCharCount();
45
59
  if (this.applyingExternal)
46
60
  return;
47
61
  const out = this.buildOutput(editor);
48
- const next = out.json ? JSON.stringify(out.json) : null;
49
- this.lastEmitted = next;
50
- this.value = next;
62
+ // Track both JSON and HTML forms to prevent feedback loops.
63
+ // The parent may send back either format via the value prop.
64
+ this.lastEmittedJson = out.json ? JSON.stringify(out.json) : null;
65
+ this.lastEmittedHtml = out.html;
66
+ // Do NOT set this.value here — it triggers unnecessary Stencil re-renders
67
+ // and the Watch cycle. The parent is the source of truth for the value prop.
51
68
  this.coInput.emit(out);
69
+ // In readonly mode there's no blur cycle, so commit immediately
70
+ // (e.g. task-list checkbox toggled by onReadOnlyChecked).
71
+ if (this.readonly)
72
+ this.coChange.emit(out);
52
73
  };
53
74
  this.handleFocus = () => {
54
75
  this.hasFocus = true;
55
76
  this.coFocus.emit();
56
77
  };
57
78
  this.handleBlur = () => {
58
- this.hasFocus = false;
59
- if (this.editor)
60
- this.coChange.emit(this.buildOutput(this.editor));
61
- this.coBlur.emit();
79
+ // Delay blur processing — if the user clicked a toolbar button,
80
+ // chain().focus() will re-focus the editor before this timeout fires.
81
+ clearTimeout(this.blurTimeout);
82
+ this.blurTimeout = setTimeout(() => {
83
+ var _a, _b;
84
+ if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.isFocused)
85
+ return; // Focus was restored (toolbar click)
86
+ // Don't blur if focus moved to an element inside the bubble (e.g. hex input)
87
+ if ((_b = this.bubbleEl) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement))
88
+ return;
89
+ this.hasFocus = false;
90
+ if (this.editor)
91
+ this.coChange.emit(this.buildOutput(this.editor));
92
+ this.coBlur.emit();
93
+ }, 100);
62
94
  };
63
95
  }
64
96
  async componentDidLoad() {
65
97
  var _a, _b;
66
- this.rt = await import('@prolibu-suite/cobalt-rich-text');
67
- if (this.destroyed || !this.contentEl)
98
+ if (!this.contentEl)
68
99
  return;
100
+ // Create bubble element imperatively so Stencil VDOM cannot interfere
101
+ // with BubbleMenu's DOM management (it calls element.remove/appendChild).
102
+ const showToolbar = this.resolvedToolbar !== 'none' && !this.readonly && !this.disabled;
103
+ if (showToolbar) {
104
+ this.bubbleEl = this.createBubbleElement();
105
+ }
69
106
  this.editor = this.rt.createEditor({
70
107
  preset: this.preset,
71
108
  element: this.contentEl,
@@ -75,40 +112,113 @@ export class CoRichTextEditor {
75
112
  maxLength: this.maxLength,
76
113
  features: this.parsedFeatures,
77
114
  customExtensions: (_a = this.customExtensions) !== null && _a !== void 0 ? _a : [],
115
+ bubbleMenuElement: this.bubbleEl,
78
116
  imageUpload: this.imageUpload,
79
117
  onUpdate: (editor) => this.handleUpdate(editor),
80
118
  onFocus: () => this.handleFocus(),
81
119
  onBlur: () => this.handleBlur(),
82
120
  });
83
- this.lastEmitted = (_b = this.value) !== null && _b !== void 0 ? _b : null;
121
+ // Document / "site" preset: fluid ~80% content width (see editor.css). Marks
122
+ // both editable and readonly so the reader matches the author's measure.
123
+ if (this.preset === 'document') {
124
+ this.contentEl.classList.add('co-rte-content--site');
125
+ }
126
+ // Image layout toolbar — only when actually editable. The layout itself is
127
+ // CSS-driven (keyed on `data-image-layout`); the bar just lets the author
128
+ // pick a reflow mode (inline / wrap-left / wrap-right / center / full-width).
129
+ if (showToolbar && this.imagesEnabled) {
130
+ this.contentEl.style.position = 'relative'; // offset parent for the bar
131
+ this.imgLayoutEl = this.createImageLayoutBar();
132
+ this.contentEl.appendChild(this.imgLayoutEl);
133
+ }
134
+ // Update bubble active states on every transaction
135
+ this.editor.on('transaction', ({ transaction }) => {
136
+ if (this.bubbleEl) {
137
+ this.updateBubbleActiveStates();
138
+ if ((this.bubbleDragged || this.bubblePositionLocked) && this.bubbleEl) {
139
+ this.applyBubbleFixedPosition();
140
+ }
141
+ }
142
+ // Reapply floating-image positions only when the doc changed (skip pure
143
+ // selection/cursor moves to avoid a full descendants walk on every keystroke).
144
+ if (this.imagesEnabled && transaction.docChanged)
145
+ this.syncImageLayouts();
146
+ this.updateImageLayoutBar();
147
+ });
148
+ // Clicking the toolbar closes bubble popups (link form, panels, etc.)
149
+ if (this.bubbleEl && this.toolbarEl) {
150
+ this.toolbarEl.addEventListener('mousedown', () => {
151
+ this.closeBubbleDropdowns();
152
+ });
153
+ // Hide the selection bubble while a toolbar dropdown/popover is open so
154
+ // the two never overlap in the same space (the bubble lives inside the
155
+ // content area, right where toolbar dropdowns expand into).
156
+ this.toolbarEl.addEventListener('coPopoverChange', (e) => {
157
+ var _a;
158
+ const open = (_a = e.detail) === null || _a === void 0 ? void 0 : _a.open;
159
+ this.suppressBubble(!!open);
160
+ });
161
+ }
162
+ this.lastEmittedJson = (_b = this.value) !== null && _b !== void 0 ? _b : null;
84
163
  this.ready = true;
85
164
  this.refreshCharCount();
86
165
  this.syncToolbar();
87
166
  this.coReady.emit();
88
167
  if (this.autofocus)
89
168
  this.editor.commands.focus();
169
+ // Tag every image container with its layout mode so the CSS rules apply.
170
+ // Runs even without the layout toolbar (readonly/disabled viewers).
171
+ requestAnimationFrame(() => this.syncImageLayouts());
90
172
  }
91
173
  disconnectedCallback() {
92
- var _a;
93
- this.destroyed = true;
94
- (_a = this.editor) === null || _a === void 0 ? void 0 : _a.destroy();
174
+ var _a, _b, _c, _d;
175
+ clearTimeout(this.blurTimeout);
176
+ clearTimeout(this.bubbleLockTimer);
177
+ (_a = this.bubbleStyleObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
178
+ if (this.bubbleDragMoveHandler)
179
+ document.removeEventListener('mousemove', this.bubbleDragMoveHandler);
180
+ if (this.bubbleDragEndHandler)
181
+ document.removeEventListener('mouseup', this.bubbleDragEndHandler);
182
+ if (this.bubbleOutsideClickHandler)
183
+ document.removeEventListener('click', this.bubbleOutsideClickHandler, true);
184
+ (_b = this.editor) === null || _b === void 0 ? void 0 : _b.destroy();
95
185
  this.editor = undefined;
186
+ (_c = this.bubbleEl) === null || _c === void 0 ? void 0 : _c.remove();
187
+ (_d = this.imgLayoutEl) === null || _d === void 0 ? void 0 : _d.remove();
96
188
  }
97
189
  componentDidRender() {
98
190
  this.syncToolbar();
99
191
  }
192
+ onAccountFontsChange() {
193
+ var _a;
194
+ // Re-render the font dropdown list if it's open
195
+ if (this._fontDropdownRenderFn) {
196
+ const filter = ((_a = this._fontDropdownSearchInput) === null || _a === void 0 ? void 0 : _a.value) || '';
197
+ this._fontDropdownRenderFn(filter);
198
+ }
199
+ }
100
200
  onValueChange(next) {
101
201
  if (!this.editor || this.applyingExternal)
102
202
  return;
103
- if ((next !== null && next !== void 0 ? next : null) === this.lastEmitted)
203
+ const incoming = next !== null && next !== void 0 ? next : null;
204
+ // Skip if this value matches what we last emitted (JSON or HTML form).
205
+ // This prevents the destructive feedback loop where the parent sends
206
+ // back our own output and setContent resets the editor on every keystroke.
207
+ if (incoming === this.lastEmittedJson || incoming === this.lastEmittedHtml)
104
208
  return;
105
209
  this.applyingExternal = true;
106
210
  try {
107
- this.editor.commands.setContent(this.parseValue(next), { emitUpdate: false });
211
+ if (incoming === '' || incoming === null) {
212
+ this.editor.commands.clearContent(false);
213
+ }
214
+ else {
215
+ this.editor.commands.setContent(this.parseValue(next), { emitUpdate: false });
216
+ }
108
217
  }
109
218
  finally {
110
219
  this.applyingExternal = false;
111
- this.lastEmitted = next !== null && next !== void 0 ? next : null;
220
+ this.lastEmittedJson = incoming;
221
+ this.lastEmittedHtml = '';
112
222
  this.refreshCharCount();
113
223
  }
114
224
  }
@@ -146,6 +256,12 @@ export class CoRichTextEditor {
146
256
  return (_a = this.editor) !== null && _a !== void 0 ? _a : null;
147
257
  }
148
258
  /* ── internals ── */
259
+ /** Whether the image node is enabled (mirrors presets.ts `wantImage` logic). */
260
+ get imagesEnabled() {
261
+ var _a;
262
+ const f = this.parsedFeatures;
263
+ return (_a = f === null || f === void 0 ? void 0 : f.image) !== null && _a !== void 0 ? _a : this.preset === 'document';
264
+ }
149
265
  get parsedFeatures() {
150
266
  if (!this.features)
151
267
  return undefined;
@@ -170,9 +286,7 @@ export class CoRichTextEditor {
170
286
  }
171
287
  }
172
288
  sanitizeHtml(html) {
173
- // Same features as the schema — otherwise feature-enabled nodes (images,
174
- // variables) would survive in `json` but be stripped from `html`.
175
- return this.rt ? this.rt.sanitize(html, this.preset, this.parsedFeatures) : html;
289
+ return this.rt.sanitize(html, this.preset);
176
290
  }
177
291
  buildOutput(editor) {
178
292
  const empty = editor.isEmpty;
@@ -182,21 +296,191 @@ export class CoRichTextEditor {
182
296
  text: editor.getText(),
183
297
  };
184
298
  }
299
+ /** Lock the bubble position so floating-ui doesn't reposition it. */
300
+ lockBubblePosition(durationMs = 1000) {
301
+ if (!this.bubbleEl)
302
+ return;
303
+ const rect = this.bubbleEl.getBoundingClientRect();
304
+ if (rect.width === 0)
305
+ return; // bubble not visible
306
+ this.bubbleLockPos = { x: rect.left, y: rect.top };
307
+ this.bubblePositionLocked = true;
308
+ clearTimeout(this.bubbleLockTimer);
309
+ this.bubbleLockTimer = setTimeout(() => {
310
+ this.bubblePositionLocked = false;
311
+ }, durationMs);
312
+ }
313
+ /** Apply fixed position to the bubble, overriding floating-ui. */
314
+ applyBubbleFixedPosition() {
315
+ if (!this.bubbleEl)
316
+ return;
317
+ const pos = this.bubbleDragged ? this.bubbleDragPos : this.bubbleLockPos;
318
+ this.applyingBubbleLock = true;
319
+ this.bubbleEl.style.position = 'fixed';
320
+ this.bubbleEl.style.top = `${pos.y}px`;
321
+ this.bubbleEl.style.left = `${pos.x}px`;
322
+ this.bubbleEl.style.transform = 'none';
323
+ this.bubbleEl.style.visibility = 'visible';
324
+ requestAnimationFrame(() => { this.applyingBubbleLock = false; });
325
+ }
326
+ /** Watch for floating-ui style changes and override them when locked/dragged. */
327
+ setupBubbleStyleObserver(bubble) {
328
+ // Use BubbleMenu plugin's onHide/onShow callbacks (dispatched as custom
329
+ // events from create-editor) for reliable drag state management.
330
+ // These fire synchronously inside hide()/show(), avoiding microtask races.
331
+ bubble.addEventListener('co-bubble-hide', () => {
332
+ this.bubbleDragged = false;
333
+ this.bubblePositionLocked = false;
334
+ clearTimeout(this.bubbleLockTimer);
335
+ this.closeBubbleDropdowns();
336
+ bubble.style.position = '';
337
+ bubble.style.top = '';
338
+ bubble.style.left = '';
339
+ bubble.style.transform = '';
340
+ });
341
+ bubble.addEventListener('co-bubble-show', () => {
342
+ this.bubbleDragged = false;
343
+ this.bubblePositionLocked = false;
344
+ });
345
+ // MutationObserver only handles overriding floating-ui position
346
+ // while the bubble is actively dragged or position-locked.
347
+ this.bubbleStyleObserver = new MutationObserver(() => {
348
+ if (this.applyingBubbleLock)
349
+ return;
350
+ if (!this.bubblePositionLocked && !this.bubbleDragged)
351
+ return;
352
+ this.applyBubbleFixedPosition();
353
+ });
354
+ this.bubbleStyleObserver.observe(bubble, {
355
+ attributes: true,
356
+ attributeFilter: ['style'],
357
+ });
358
+ }
359
+ /**
360
+ * Hide/show the selection bubble while a toolbar dropdown is open. Uses a
361
+ * class with `!important` so it wins over the BubbleMenu plugin's inline
362
+ * display/visibility writes. When unsuppressed, the plugin's own state
363
+ * (selection still active) reveals it again on the next update.
364
+ */
365
+ suppressBubble(on) {
366
+ if (!this.bubbleEl)
367
+ return;
368
+ this.bubbleEl.classList.toggle('co-rte-bubble--suppressed', on);
369
+ if (on)
370
+ this.closeBubbleDropdowns();
371
+ }
372
+ /** Close all open sub-dropdowns inside the bubble. */
373
+ closeBubbleDropdowns() {
374
+ if (!this.bubbleEl)
375
+ return;
376
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__select-dropdown').forEach(d => {
377
+ d.style.display = 'none';
378
+ });
379
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__color-dropdown').forEach(d => {
380
+ d.style.display = 'none';
381
+ });
382
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__panel').forEach(d => {
383
+ d.style.display = 'none';
384
+ });
385
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__overflow-menu').forEach(d => {
386
+ d.style.display = 'none';
387
+ });
388
+ if (this.bubbleLinkFormEl) {
389
+ this.bubbleLinkFormEl.style.display = 'none';
390
+ }
391
+ }
185
392
  refreshCharCount() {
186
393
  var _a;
187
394
  const storage = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.storage;
188
- if (storage === null || storage === void 0 ? void 0 : storage.characterCount)
395
+ if (storage === null || storage === void 0 ? void 0 : storage.characterCount) {
189
396
  this.charCount = storage.characterCount.characters();
397
+ // Update DOM directly to avoid Stencil re-renders on every keystroke
398
+ if (this.charCountEl) {
399
+ this.charCountEl.textContent = this.maxLength != null
400
+ ? `${this.charCount} / ${this.maxLength}`
401
+ : `${this.charCount}`;
402
+ }
403
+ }
190
404
  }
191
405
  syncToolbar() {
192
406
  if (this.toolbarEl && this.editor && this.toolbarEl.editor !== this.editor) {
193
407
  this.toolbarEl.editor = this.editor;
194
408
  }
409
+ if (this.toolbarEl) {
410
+ this.toolbarEl.imageUpload = this.imageUpload;
411
+ }
195
412
  }
196
413
  get resolvedToolbar() {
197
414
  if (this.toolbar !== 'auto')
198
415
  return this.toolbar;
199
- return this.rt ? this.rt.defaultToolbarForPreset(this.preset) : 'basic';
416
+ return this.rt.defaultToolbarForPreset(this.preset);
417
+ }
418
+ get resolvedFontFamilies() {
419
+ if (!this.fontFamilies)
420
+ return CoRichTextEditor.DEFAULT_FONTS;
421
+ if (typeof this.fontFamilies === 'string') {
422
+ try {
423
+ return JSON.parse(this.fontFamilies);
424
+ }
425
+ catch (_a) {
426
+ return CoRichTextEditor.DEFAULT_FONTS;
427
+ }
428
+ }
429
+ return this.fontFamilies;
430
+ }
431
+ get resolvedAccountFonts() {
432
+ // Prefer prop over loaded fonts
433
+ if (this.accountFonts) {
434
+ if (typeof this.accountFonts === 'string') {
435
+ try {
436
+ return JSON.parse(this.accountFonts);
437
+ }
438
+ catch (_a) {
439
+ return [];
440
+ }
441
+ }
442
+ return this.accountFonts;
443
+ }
444
+ return this._loadedAccountFonts;
445
+ }
446
+ registerFontFace(name, url) {
447
+ if (!url || !name || CoRichTextEditor._registeredFonts.has(name))
448
+ return;
449
+ CoRichTextEditor._registeredFonts.add(name);
450
+ try {
451
+ const face = new FontFace(name, `url("${url}")`);
452
+ face.load().then(() => document.fonts.add(face)).catch(() => { });
453
+ }
454
+ catch ( /* ignore */_a) { /* ignore */ }
455
+ }
456
+ /** Load account fonts via the fontLoader callback (called once). */
457
+ async loadFontsViaCallback() {
458
+ var _a, _b, _c;
459
+ if (this._fontLoaderCalled || !this.fontLoader)
460
+ return;
461
+ this._fontLoaderCalled = true;
462
+ this._fontLoaderLoading = true;
463
+ // Re-render to show loading state
464
+ (_a = this._fontDropdownRenderFn) === null || _a === void 0 ? void 0 : _a.call(this, '');
465
+ try {
466
+ const fonts = await this.fontLoader();
467
+ if (Array.isArray(fonts)) {
468
+ this._loadedAccountFonts = fonts.map(f => {
469
+ if (f.url)
470
+ this.registerFontFace(f.label, f.url);
471
+ return { label: f.label, value: f.value };
472
+ });
473
+ }
474
+ }
475
+ catch (e) {
476
+ console.error('[co-rich-text-editor] fontLoader error', e);
477
+ }
478
+ finally {
479
+ this._fontLoaderLoading = false;
480
+ // Re-render with loaded fonts
481
+ const filter = ((_b = this._fontDropdownSearchInput) === null || _b === void 0 ? void 0 : _b.value) || '';
482
+ (_c = this._fontDropdownRenderFn) === null || _c === void 0 ? void 0 : _c.call(this, filter);
483
+ }
200
484
  }
201
485
  get helperIcon() {
202
486
  switch (this.status) {
@@ -206,17 +490,1545 @@ export class CoRichTextEditor {
206
490
  default: return 'info';
207
491
  }
208
492
  }
493
+ /* ── Bubble menu (imperative — outside Stencil VDOM) ── */
494
+ /**
495
+ * Create the bubble menu element imperatively. BubbleMenu v3 manages
496
+ * the element via remove()/appendChild(), which conflicts with Stencil's
497
+ * virtual DOM reconciliation. By creating it outside the VDOM, Stencil
498
+ * won't recreate or interfere with the element.
499
+ */
500
+ createBubbleElement() {
501
+ const bubble = document.createElement('div');
502
+ bubble.className = 'co-rte-bubble';
503
+ // Start hidden and absolutely positioned so it never takes document flow space.
504
+ // BubbleMenu's floating-ui will manage position via inline styles when shown.
505
+ bubble.style.visibility = 'hidden';
506
+ bubble.style.position = 'absolute';
507
+ bubble.style.top = '0';
508
+ bubble.style.left = '0';
509
+ // Prevent any bubble click from stealing editor focus
510
+ // and lock bubble position during any toolbar interaction.
511
+ bubble.addEventListener('mousedown', (e) => {
512
+ if (e.target.tagName !== 'INPUT')
513
+ e.preventDefault();
514
+ this.lockBubblePosition();
515
+ });
516
+ // Close dropdowns/panels when clicking outside the bubble
517
+ this.bubbleOutsideClickHandler = (e) => {
518
+ if (!bubble.contains(e.target)) {
519
+ this.closeBubbleDropdowns();
520
+ }
521
+ };
522
+ document.addEventListener('click', this.bubbleOutsideClickHandler, true);
523
+ // Watch for floating-ui style overrides and restore locked position
524
+ this.setupBubbleStyleObserver(bubble);
525
+ const isDocument = this.preset === 'document';
526
+ /* ── Drag handle (document only) ── */
527
+ if (isDocument) {
528
+ const dragHandle = document.createElement('div');
529
+ dragHandle.className = 'co-rte-bubble__drag-handle';
530
+ dragHandle.innerHTML = `<co-icon name="dots-six-vertical" size="14"></co-icon>`;
531
+ dragHandle.addEventListener('mousedown', (e) => {
532
+ e.preventDefault();
533
+ e.stopPropagation();
534
+ const rect = bubble.getBoundingClientRect();
535
+ this.bubbleDragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
536
+ if (!this.bubbleDragged) {
537
+ this.bubbleDragPos = { x: rect.left, y: rect.top };
538
+ }
539
+ this.bubbleDragged = true;
540
+ this.bubbleDragMoveHandler = (ev) => {
541
+ this.bubbleDragPos.x = ev.clientX - this.bubbleDragOffset.x;
542
+ this.bubbleDragPos.y = ev.clientY - this.bubbleDragOffset.y;
543
+ bubble.style.position = 'fixed';
544
+ bubble.style.top = `${this.bubbleDragPos.y}px`;
545
+ bubble.style.left = `${this.bubbleDragPos.x}px`;
546
+ bubble.style.transform = 'none';
547
+ bubble.style.visibility = 'visible';
548
+ };
549
+ this.bubbleDragEndHandler = () => {
550
+ document.removeEventListener('mousemove', this.bubbleDragMoveHandler);
551
+ document.removeEventListener('mouseup', this.bubbleDragEndHandler);
552
+ };
553
+ document.addEventListener('mousemove', this.bubbleDragMoveHandler);
554
+ document.addEventListener('mouseup', this.bubbleDragEndHandler);
555
+ });
556
+ bubble.appendChild(dragHandle);
557
+ } // end isDocument drag handle
558
+ /* ── Single row: all controls ── */
559
+ const row = document.createElement('div');
560
+ row.className = 'co-rte-bubble__row';
561
+ // --- Advanced controls (document preset only) ---
562
+ // Panels will be appended to bubble root (not inside the row) at the end.
563
+ let fontSizePanel = null;
564
+ let fontWeightPanel = null;
565
+ let lineHeightPanelEl = null;
566
+ let letterSpacingPanelEl = null;
567
+ let bgColorPanelEl = null;
568
+ let overflowResult = null;
569
+ if (isDocument) {
570
+ // --- Font Family select (with search) ---
571
+ const fontFamilyWrap = this.createBubbleFontFamilySelect();
572
+ row.appendChild(fontFamilyWrap);
573
+ // --- Font Size panel (draggable with custom input) ---
574
+ fontSizePanel = this.createBubbleFontSizePanel();
575
+ row.appendChild(fontSizePanel.wrap);
576
+ // --- Font Weight panel (draggable, two-column) ---
577
+ fontWeightPanel = this.createBubbleFontWeightPanel();
578
+ row.appendChild(fontWeightPanel.wrap);
579
+ row.appendChild(this.createSep());
580
+ // --- Text color picker ---
581
+ const colorWrap = this.createBubbleColorPicker('textColor', (color) => {
582
+ if (!this.editor)
583
+ return;
584
+ this.editor.chain().focus().setColor(color).run();
585
+ });
586
+ row.appendChild(colorWrap);
587
+ row.appendChild(this.createSep());
588
+ } // end isDocument advanced controls
589
+ // --- B I U (all presets except comment which disables underline) ---
590
+ const _s = getRichTextStrings(this.locale);
591
+ const formatBtns = [
592
+ { action: 'bold', icon: 'text-b', label: _s.bold, check: 'bold' },
593
+ { action: 'italic', icon: 'text-italic', label: _s.italic, check: 'italic' },
594
+ ];
595
+ // Underline is available on task, email, document (not comment)
596
+ if (this.preset !== 'comment') {
597
+ formatBtns.push({ action: 'underline', icon: 'text-underline', label: _s.underline, check: 'underline' });
598
+ }
599
+ formatBtns.forEach(({ action, icon, label, check }) => {
600
+ const btn = this.createBubbleBtn(icon, label, action, check, () => {
601
+ if (!this.editor)
602
+ return;
603
+ const chain = this.editor.chain().focus();
604
+ switch (action) {
605
+ case 'bold':
606
+ chain.toggleBold().run();
607
+ break;
608
+ case 'italic':
609
+ chain.toggleItalic().run();
610
+ break;
611
+ case 'underline':
612
+ chain.toggleUnderline().run();
613
+ break;
614
+ }
615
+ });
616
+ row.appendChild(btn);
617
+ });
618
+ row.appendChild(this.createSep());
619
+ // --- Alignment buttons (document and email only) ---
620
+ if (isDocument || this.preset === 'email') {
621
+ const alignments = [
622
+ { align: 'left', icon: 'text-align-left', label: _s.alignLeft },
623
+ { align: 'center', icon: 'text-align-center', label: _s.center },
624
+ { align: 'right', icon: 'text-align-right', label: _s.alignRight },
625
+ { align: 'justify', icon: 'text-align-justify', label: _s.justify },
626
+ ];
627
+ alignments.forEach(({ align, icon, label }) => {
628
+ const btn = document.createElement('button');
629
+ btn.type = 'button';
630
+ btn.className = 'co-rte-bubble__btn';
631
+ btn.title = label;
632
+ btn.setAttribute('aria-label', label);
633
+ btn.setAttribute('data-action', 'textAlign');
634
+ btn.setAttribute('data-align', align);
635
+ btn.innerHTML = `<co-icon name="${icon}" size="16"></co-icon>`;
636
+ btn.addEventListener('click', () => {
637
+ if (!this.editor)
638
+ return;
639
+ this.editor.chain().focus().setTextAlign(align).run();
640
+ });
641
+ row.appendChild(btn);
642
+ });
643
+ row.appendChild(this.createSep());
644
+ } // end alignment
645
+ // --- Bullet / Ordered list (task, email — not comment, not document) ---
646
+ // For document preset, lists are moved to the overflow menu.
647
+ if (this.preset !== 'comment' && !isDocument) {
648
+ row.appendChild(this.createBubbleBtn('list-bullets', _s.bulletList, 'bulletList', 'bulletList', () => {
649
+ if (!this.editor)
650
+ return;
651
+ this.editor.chain().focus().toggleBulletList().run();
652
+ }));
653
+ row.appendChild(this.createBubbleBtn('list-numbers', _s.orderedList, 'orderedList', 'orderedList', () => {
654
+ if (!this.editor)
655
+ return;
656
+ this.editor.chain().focus().toggleOrderedList().run();
657
+ }));
658
+ row.appendChild(this.createSep());
659
+ } // end lists (non-document)
660
+ // --- Link (all presets) ---
661
+ const linkBtn = this.createBubbleBtn('link', _s.link, 'link', 'link', () => {
662
+ if (!this.editor)
663
+ return;
664
+ this.toggleBubbleLinkForm(true);
665
+ });
666
+ row.appendChild(linkBtn);
667
+ // --- Overflow menu (document only) ---
668
+ // Superscript, subscript, lists, strikethrough, bg color, line height,
669
+ // letter spacing, horizontal rule, clear formatting — all in overflow.
670
+ if (isDocument) {
671
+ row.appendChild(this.createSep());
672
+ lineHeightPanelEl = this.createBubbleLineHeightPanel();
673
+ letterSpacingPanelEl = this.createBubbleLetterSpacingPanel();
674
+ bgColorPanelEl = this.createBubbleBgColorPanel();
675
+ overflowResult = this.createBubbleOverflowMenu({
676
+ lineHeight: lineHeightPanelEl,
677
+ letterSpacing: letterSpacingPanelEl,
678
+ bgColor: bgColorPanelEl,
679
+ });
680
+ row.appendChild(overflowResult.overflowWrap);
681
+ } // end isDocument overflow
682
+ bubble.appendChild(row);
683
+ // Append floating panels to bubble root (outside the row, so they float freely)
684
+ if (isDocument) {
685
+ if (fontSizePanel)
686
+ bubble.appendChild(fontSizePanel.panelEl);
687
+ if (fontWeightPanel)
688
+ bubble.appendChild(fontWeightPanel.panelEl);
689
+ if (lineHeightPanelEl)
690
+ bubble.appendChild(lineHeightPanelEl);
691
+ if (letterSpacingPanelEl)
692
+ bubble.appendChild(letterSpacingPanelEl);
693
+ if (bgColorPanelEl)
694
+ bubble.appendChild(bgColorPanelEl);
695
+ }
696
+ // Link form (hidden by default)
697
+ const linkForm = document.createElement('div');
698
+ linkForm.className = 'co-rte-bubble__link-form';
699
+ linkForm.style.display = 'none';
700
+ this.bubbleLinkFormEl = linkForm;
701
+ const linkInput = document.createElement('input');
702
+ linkInput.type = 'url';
703
+ linkInput.className = 'co-rte-bubble__link-input';
704
+ linkInput.placeholder = 'https://...';
705
+ this.bubbleLinkInputEl = linkInput;
706
+ linkInput.addEventListener('keydown', (e) => {
707
+ if (e.key === 'Enter')
708
+ this.submitBubbleLinkImperative();
709
+ if (e.key === 'Escape')
710
+ this.toggleBubbleLinkForm(false);
711
+ });
712
+ const linkSubmit = document.createElement('button');
713
+ linkSubmit.type = 'button';
714
+ linkSubmit.className = 'co-rte-bubble__link-btn';
715
+ linkSubmit.innerHTML = `<co-icon name="check" size="14"></co-icon>`;
716
+ linkSubmit.addEventListener('click', () => this.submitBubbleLinkImperative());
717
+ const linkUnlink = document.createElement('button');
718
+ linkUnlink.type = 'button';
719
+ linkUnlink.className = 'co-rte-bubble__link-btn co-rte-bubble__link-unlink';
720
+ linkUnlink.title = _s.unlinkLink;
721
+ linkUnlink.innerHTML = `<co-icon name="link-break" size="14"></co-icon>`;
722
+ linkUnlink.addEventListener('click', () => {
723
+ if (!this.editor)
724
+ return;
725
+ this.editor.chain().focus().unsetLink().run();
726
+ this.toggleBubbleLinkForm(false);
727
+ });
728
+ this.bubbleLinkUnlinkEl = linkUnlink;
729
+ linkForm.appendChild(linkInput);
730
+ linkForm.appendChild(linkSubmit);
731
+ linkForm.appendChild(linkUnlink);
732
+ bubble.appendChild(linkForm);
733
+ return bubble;
734
+ }
735
+ /** Helper: create a separator span */
736
+ createSep() {
737
+ const sep = document.createElement('span');
738
+ sep.className = 'co-rte-bubble__sep';
739
+ return sep;
740
+ }
741
+ /**
742
+ * Position a dropdown above or below the trigger depending on available
743
+ * viewport space. Sets `data-position="above"|"below"` so CSS can flip.
744
+ */
745
+ positionDropdown(trigger, dropdown) {
746
+ const triggerRect = trigger.getBoundingClientRect();
747
+ // The dropdown is already display:block when this is called.
748
+ // Remove any previous position so it can be measured at its natural height.
749
+ dropdown.removeAttribute('data-position');
750
+ dropdown.style.removeProperty('left');
751
+ dropdown.style.removeProperty('transform');
752
+ // Force layout so offsetHeight is accurate
753
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
754
+ dropdown.offsetHeight;
755
+ const dropH = dropdown.offsetHeight || 320;
756
+ const dropW = dropdown.offsetWidth || 224;
757
+ const spaceAbove = triggerRect.top;
758
+ const spaceBelow = window.innerHeight - triggerRect.bottom;
759
+ if (spaceBelow >= dropH || spaceBelow > spaceAbove) {
760
+ dropdown.setAttribute('data-position', 'below');
761
+ }
762
+ else {
763
+ dropdown.setAttribute('data-position', 'above');
764
+ }
765
+ // Clamp horizontal position so the dropdown stays within the viewport.
766
+ // The dropdown is positioned relative to its parent (trigger wrapper).
767
+ // Default CSS uses left:50% + translateX(-50%) to center; we override
768
+ // when centering would push the dropdown outside the viewport.
769
+ const isColorDropdown = dropdown.classList.contains('co-rte-bubble__color-dropdown');
770
+ if (isColorDropdown) {
771
+ const parentRect = (trigger.parentElement || trigger).getBoundingClientRect();
772
+ // Desired center-aligned left edge in viewport coords
773
+ const idealLeft = parentRect.left + parentRect.width / 2 - dropW / 2;
774
+ const idealRight = idealLeft + dropW;
775
+ const viewportPad = 8;
776
+ if (idealLeft < viewportPad) {
777
+ // Would overflow left — align to left edge of trigger
778
+ dropdown.style.left = '0';
779
+ dropdown.style.transform = 'none';
780
+ }
781
+ else if (idealRight > window.innerWidth - viewportPad) {
782
+ // Would overflow right — align to right edge of trigger
783
+ dropdown.style.left = 'auto';
784
+ dropdown.style.right = '0';
785
+ dropdown.style.transform = 'none';
786
+ }
787
+ // else: default centering from CSS is fine
788
+ }
789
+ }
790
+ positionPanelNearBubble(panel) {
791
+ if (!this.bubbleEl)
792
+ return;
793
+ const overflowMenu = this.bubbleEl.querySelector('.co-rte-bubble__overflow-menu');
794
+ const anchorRect = overflowMenu && overflowMenu.style.display !== 'none'
795
+ ? overflowMenu.getBoundingClientRect()
796
+ : this.bubbleEl.getBoundingClientRect();
797
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
798
+ panel.offsetHeight;
799
+ const panelH = panel.offsetHeight || 320;
800
+ const panelW = panel.offsetWidth || 230;
801
+ const gap = 4;
802
+ const pad = 8;
803
+ let top = anchorRect.top;
804
+ if (top + panelH > window.innerHeight - pad)
805
+ top = window.innerHeight - pad - panelH;
806
+ if (top < pad)
807
+ top = pad;
808
+ let left = anchorRect.left - panelW - gap;
809
+ if (left < pad)
810
+ left = anchorRect.right + gap;
811
+ if (left + panelW > window.innerWidth - pad)
812
+ left = window.innerWidth - pad - panelW;
813
+ panel.style.position = 'fixed';
814
+ panel.style.top = `${top}px`;
815
+ panel.style.left = `${left}px`;
816
+ panel.style.bottom = 'auto';
817
+ panel.style.right = 'auto';
818
+ panel.style.transform = 'none';
819
+ panel.removeAttribute('data-position');
820
+ }
821
+ /** Helper: create a toggle button for the bubble */
822
+ createBubbleBtn(icon, label, action, check, onClick) {
823
+ const btn = document.createElement('button');
824
+ btn.type = 'button';
825
+ btn.className = 'co-rte-bubble__btn';
826
+ btn.title = label;
827
+ btn.setAttribute('aria-label', label);
828
+ btn.setAttribute('data-action', action);
829
+ btn.setAttribute('data-check', check);
830
+ btn.innerHTML = `<co-icon name="${icon}" size="16"></co-icon>`;
831
+ btn.addEventListener('click', onClick);
832
+ return btn;
833
+ }
834
+ /** Creates the font family dropdown with search for the bubble menu. */
835
+ createBubbleFontFamilySelect() {
836
+ const wrap = document.createElement('div');
837
+ wrap.className = 'co-rte-bubble__select co-rte-bubble__select--font';
838
+ wrap.setAttribute('data-select', 'fontFamily');
839
+ const trigger = document.createElement('button');
840
+ trigger.type = 'button';
841
+ trigger.className = 'co-rte-bubble__select-trigger';
842
+ trigger.innerHTML = `<span class="co-rte-bubble__select-label">Sans-serif</span><co-icon name="caret-down" size="12"></co-icon>`;
843
+ const dropdown = document.createElement('div');
844
+ dropdown.className = 'co-rte-bubble__select-dropdown co-rte-bubble__select-dropdown--font';
845
+ dropdown.style.display = 'none';
846
+ // Search input
847
+ const searchWrap = document.createElement('div');
848
+ searchWrap.className = 'co-rte-bubble__font-search';
849
+ const searchInput = document.createElement('input');
850
+ searchInput.type = 'text';
851
+ searchInput.className = 'co-rte-bubble__font-search-input';
852
+ searchInput.placeholder = getRichTextStrings(this.locale).searchFont;
853
+ searchWrap.appendChild(searchInput);
854
+ dropdown.appendChild(searchWrap);
855
+ // Font list container
856
+ const listEl = document.createElement('div');
857
+ listEl.className = 'co-rte-bubble__font-list';
858
+ dropdown.appendChild(listEl);
859
+ const createFontOption = (label, value) => {
860
+ const opt = document.createElement('button');
861
+ opt.type = 'button';
862
+ opt.className = 'co-rte-bubble__select-option';
863
+ opt.setAttribute('data-value', value);
864
+ opt.textContent = label;
865
+ opt.style.fontFamily = value.includes(',') ? value : `"${value}", sans-serif`;
866
+ opt.addEventListener('click', () => {
867
+ if (!this.editor)
868
+ return;
869
+ this.editor.chain().focus().setFontFamily(value).run();
870
+ dropdown.style.display = 'none';
871
+ const labelEl = trigger.querySelector('.co-rte-bubble__select-label');
872
+ if (labelEl)
873
+ labelEl.textContent = label;
874
+ searchInput.value = '';
875
+ this.coFontPopupHide.emit();
876
+ });
877
+ return opt;
878
+ };
879
+ const renderFonts = (filter) => {
880
+ listEl.innerHTML = '';
881
+ const term = filter.toLowerCase();
882
+ const accountFonts = this.resolvedAccountFonts;
883
+ const systemFonts = this.resolvedFontFamilies;
884
+ const filteredAccount = term ? accountFonts.filter(f => f.label.toLowerCase().includes(term)) : accountFonts;
885
+ const filteredSystem = term ? systemFonts.filter(f => f.label.toLowerCase().includes(term)) : systemFonts;
886
+ if (!filteredAccount.length && !filteredSystem.length) {
887
+ const empty = document.createElement('div');
888
+ empty.className = 'co-rte-bubble__font-empty';
889
+ empty.textContent = getRichTextStrings(this.locale).noResults;
890
+ listEl.appendChild(empty);
891
+ return;
892
+ }
893
+ // Loading indicator
894
+ if ((this.fontsLoading || this._fontLoaderLoading) && !filteredAccount.length) {
895
+ const loading = document.createElement('div');
896
+ loading.className = 'co-rte-bubble__font-empty';
897
+ loading.textContent = getRichTextStrings(this.locale).loadingFonts;
898
+ listEl.appendChild(loading);
899
+ }
900
+ // Imported fonts section
901
+ if (filteredAccount.length) {
902
+ const header = document.createElement('div');
903
+ header.className = 'co-rte-bubble__font-section-header';
904
+ header.textContent = getRichTextStrings(this.locale).importedFonts;
905
+ listEl.appendChild(header);
906
+ filteredAccount.forEach(({ label, value }) => listEl.appendChild(createFontOption(label, value)));
907
+ }
908
+ // System fonts section
909
+ if (filteredSystem.length) {
910
+ const header = document.createElement('div');
911
+ header.className = 'co-rte-bubble__font-section-header';
912
+ header.textContent = getRichTextStrings(this.locale).systemFonts;
913
+ listEl.appendChild(header);
914
+ filteredSystem.forEach(({ label, value }) => listEl.appendChild(createFontOption(label, value)));
915
+ }
916
+ };
917
+ searchInput.addEventListener('input', () => renderFonts(searchInput.value));
918
+ // Re-render fonts when accountFonts prop changes (MutationObserver on the host)
919
+ this._fontDropdownRenderFn = renderFonts;
920
+ this._fontDropdownSearchInput = searchInput;
921
+ trigger.addEventListener('click', () => {
922
+ const parent = wrap.closest('.co-rte-bubble');
923
+ if (parent) {
924
+ parent.querySelectorAll('.co-rte-bubble__select-dropdown').forEach(d => {
925
+ if (d !== dropdown)
926
+ d.style.display = 'none';
927
+ });
928
+ parent.querySelectorAll('.co-rte-bubble__color-dropdown').forEach(d => {
929
+ d.style.display = 'none';
930
+ });
931
+ }
932
+ const isOpen = dropdown.style.display !== 'none';
933
+ dropdown.style.display = isOpen ? 'none' : 'block';
934
+ if (!isOpen) {
935
+ this.coFontPopupShow.emit();
936
+ this.loadFontsViaCallback();
937
+ this.positionDropdown(trigger, dropdown);
938
+ renderFonts('');
939
+ setTimeout(() => searchInput.focus(), 0);
940
+ }
941
+ else {
942
+ this.coFontPopupHide.emit();
943
+ }
944
+ });
945
+ wrap.appendChild(trigger);
946
+ wrap.appendChild(dropdown);
947
+ return wrap;
948
+ }
949
+ /** Creates a compact dropdown-select for the bubble menu. */
950
+ createBubbleSelect(name, options, defaultLabel, onChange) {
951
+ const wrap = document.createElement('div');
952
+ wrap.className = 'co-rte-bubble__select';
953
+ wrap.setAttribute('data-select', name);
954
+ const trigger = document.createElement('button');
955
+ trigger.type = 'button';
956
+ trigger.className = 'co-rte-bubble__select-trigger';
957
+ trigger.innerHTML = `<span class="co-rte-bubble__select-label">${defaultLabel}</span><co-icon name="caret-down" size="12"></co-icon>`;
958
+ const dropdown = document.createElement('div');
959
+ dropdown.className = 'co-rte-bubble__select-dropdown';
960
+ dropdown.style.display = 'none';
961
+ options.forEach(({ label, value }) => {
962
+ const opt = document.createElement('button');
963
+ opt.type = 'button';
964
+ opt.className = 'co-rte-bubble__select-option';
965
+ opt.setAttribute('data-value', value);
966
+ opt.textContent = label;
967
+ // Preview font weight in the option
968
+ if (name === 'fontWeight') {
969
+ opt.style.fontWeight = value;
970
+ }
971
+ opt.addEventListener('click', () => {
972
+ onChange(value);
973
+ dropdown.style.display = 'none';
974
+ const labelEl = trigger.querySelector('.co-rte-bubble__select-label');
975
+ if (labelEl)
976
+ labelEl.textContent = label;
977
+ });
978
+ dropdown.appendChild(opt);
979
+ });
980
+ trigger.addEventListener('click', () => {
981
+ // Close other open dropdowns in the bubble
982
+ const parent = wrap.closest('.co-rte-bubble');
983
+ if (parent) {
984
+ parent.querySelectorAll('.co-rte-bubble__select-dropdown').forEach(d => {
985
+ if (d !== dropdown)
986
+ d.style.display = 'none';
987
+ });
988
+ parent.querySelectorAll('.co-rte-bubble__color-dropdown').forEach(d => {
989
+ d.style.display = 'none';
990
+ });
991
+ }
992
+ const isOpen = dropdown.style.display !== 'none';
993
+ dropdown.style.display = isOpen ? 'none' : 'block';
994
+ if (!isOpen)
995
+ this.positionDropdown(trigger, dropdown);
996
+ });
997
+ wrap.appendChild(trigger);
998
+ wrap.appendChild(dropdown);
999
+ return wrap;
1000
+ }
1001
+ /** Creates a compact color picker button for the bubble menu. */
1002
+ createBubbleColorPicker(name, onChange) {
1003
+ const colors = [
1004
+ '#000000', '#434343', '#666666', '#999999', '#b7b7b7', '#cccccc',
1005
+ '#d9d9d9', '#efefef', '#f3f3f3', '#ffffff',
1006
+ '#980000', '#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff',
1007
+ '#4a86e8', '#0000ff', '#9900ff', '#ff00ff',
1008
+ '#e6b8af', '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3',
1009
+ '#c9daf8', '#cfe2f3', '#d9d2e9', '#ead1dc',
1010
+ ];
1011
+ const wrap = document.createElement('div');
1012
+ wrap.className = 'co-rte-bubble__color';
1013
+ wrap.setAttribute('data-color', name);
1014
+ const iconName = name === 'textColor' ? '' : 'highlighter';
1015
+ const trigger = document.createElement('button');
1016
+ trigger.type = 'button';
1017
+ trigger.className = 'co-rte-bubble__btn';
1018
+ const _cs = getRichTextStrings(this.locale);
1019
+ trigger.title = name === 'textColor' ? _cs.textColor : _cs.backgroundColor;
1020
+ if (name === 'textColor') {
1021
+ trigger.innerHTML = `<span class="co-rte-bubble__color-indicator"><span class="co-rte-bubble__color-letter">A</span><span class="co-rte-bubble__color-bar" style="background:#000000"></span></span>`;
1022
+ }
1023
+ else {
1024
+ trigger.innerHTML = `<co-icon name="${iconName}" size="16"></co-icon>`;
1025
+ }
1026
+ const dropdown = document.createElement('div');
1027
+ dropdown.className = 'co-rte-bubble__color-dropdown';
1028
+ dropdown.style.display = 'none';
1029
+ const colorTitle = name === 'textColor' ? _cs.textColor : _cs.backgroundColor;
1030
+ const { resetDrag: resetColorDrag } = this.addDragHeader(dropdown, colorTitle);
1031
+ // Prevent focus loss
1032
+ dropdown.addEventListener('mousedown', (e) => {
1033
+ if (e.target.tagName !== 'INPUT')
1034
+ e.preventDefault();
1035
+ this.lockBubblePosition();
1036
+ });
1037
+ const grid = document.createElement('div');
1038
+ grid.className = 'co-rte-bubble__color-grid';
1039
+ colors.forEach(color => {
1040
+ const swatch = document.createElement('button');
1041
+ swatch.type = 'button';
1042
+ swatch.className = 'co-rte-bubble__color-swatch';
1043
+ swatch.style.background = color;
1044
+ if (color === '#ffffff') {
1045
+ swatch.style.border = '1px solid #e0e0e0';
1046
+ }
1047
+ swatch.addEventListener('click', () => {
1048
+ onChange(color);
1049
+ dropdown.style.display = 'none';
1050
+ });
1051
+ grid.appendChild(swatch);
1052
+ });
1053
+ // Hex input
1054
+ const hexRow = document.createElement('div');
1055
+ hexRow.className = 'co-rte-bubble__color-hex';
1056
+ const hexInput = document.createElement('input');
1057
+ hexInput.type = 'text';
1058
+ hexInput.className = 'co-rte-bubble__color-hex-input';
1059
+ hexInput.placeholder = '#hex';
1060
+ hexInput.maxLength = 7;
1061
+ hexInput.addEventListener('keydown', (e) => {
1062
+ if (e.key === 'Enter') {
1063
+ const val = hexInput.value.trim();
1064
+ if (/^#[0-9a-fA-F]{3,6}$/.test(val)) {
1065
+ onChange(val);
1066
+ dropdown.style.display = 'none';
1067
+ hexInput.value = '';
1068
+ }
1069
+ }
1070
+ });
1071
+ // Clear color button
1072
+ const clearBtn = document.createElement('button');
1073
+ clearBtn.type = 'button';
1074
+ clearBtn.className = 'co-rte-bubble__color-clear';
1075
+ clearBtn.innerHTML = `<co-icon name="x" size="12"></co-icon> ${_cs.noColor}`;
1076
+ clearBtn.addEventListener('click', () => {
1077
+ if (!this.editor)
1078
+ return;
1079
+ if (name === 'textColor')
1080
+ this.editor.chain().focus().unsetColor().run();
1081
+ else
1082
+ this.editor.chain().focus().unsetHighlight().run();
1083
+ dropdown.style.display = 'none';
1084
+ });
1085
+ dropdown.appendChild(grid);
1086
+ hexRow.appendChild(hexInput);
1087
+ dropdown.appendChild(hexRow);
1088
+ dropdown.appendChild(clearBtn);
1089
+ trigger.addEventListener('click', () => {
1090
+ const parent = wrap.closest('.co-rte-bubble');
1091
+ if (parent) {
1092
+ parent.querySelectorAll('.co-rte-bubble__select-dropdown').forEach(d => {
1093
+ d.style.display = 'none';
1094
+ });
1095
+ parent.querySelectorAll('.co-rte-bubble__color-dropdown').forEach(d => {
1096
+ if (d !== dropdown)
1097
+ d.style.display = 'none';
1098
+ });
1099
+ }
1100
+ const isOpen = dropdown.style.display !== 'none';
1101
+ if (isOpen) {
1102
+ dropdown.style.display = 'none';
1103
+ }
1104
+ else {
1105
+ resetColorDrag();
1106
+ dropdown.style.position = '';
1107
+ dropdown.style.top = '';
1108
+ dropdown.style.left = '';
1109
+ dropdown.style.bottom = '';
1110
+ dropdown.style.right = '';
1111
+ dropdown.style.transform = '';
1112
+ dropdown.style.display = 'block';
1113
+ this.positionDropdown(trigger, dropdown);
1114
+ }
1115
+ });
1116
+ wrap.appendChild(trigger);
1117
+ wrap.appendChild(dropdown);
1118
+ return wrap;
1119
+ }
1120
+ /** Close all bubble popups EXCEPT the given element. */
1121
+ closeAllBubblePopups(...except) {
1122
+ if (!this.bubbleEl)
1123
+ return;
1124
+ const keep = except.filter(Boolean);
1125
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__select-dropdown').forEach(d => {
1126
+ if (!keep.includes(d))
1127
+ d.style.display = 'none';
1128
+ });
1129
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__color-dropdown').forEach(d => {
1130
+ if (!keep.includes(d))
1131
+ d.style.display = 'none';
1132
+ });
1133
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__panel').forEach(d => {
1134
+ if (!keep.includes(d))
1135
+ d.style.display = 'none';
1136
+ });
1137
+ this.bubbleEl.querySelectorAll('.co-rte-bubble__overflow-menu').forEach(d => {
1138
+ if (!keep.includes(d))
1139
+ d.style.display = 'none';
1140
+ });
1141
+ if (this.bubbleLinkFormEl && !keep.includes(this.bubbleLinkFormEl)) {
1142
+ this.bubbleLinkFormEl.style.display = 'none';
1143
+ }
1144
+ }
1145
+ addDragHeader(panel, title) {
1146
+ let dragged = false;
1147
+ let dragPos = { x: 0, y: 0 };
1148
+ let dragOffset = { x: 0, y: 0 };
1149
+ const header = document.createElement('div');
1150
+ header.className = 'co-rte-bubble__panel-header';
1151
+ const dragHandle = document.createElement('div');
1152
+ dragHandle.className = 'co-rte-bubble__panel-drag';
1153
+ dragHandle.innerHTML = `<co-icon name="dots-six" size="14"></co-icon>`;
1154
+ const titleEl = document.createElement('span');
1155
+ titleEl.className = 'co-rte-bubble__panel-title';
1156
+ titleEl.textContent = title;
1157
+ const closeBtn = document.createElement('button');
1158
+ closeBtn.type = 'button';
1159
+ closeBtn.className = 'co-rte-bubble__panel-close';
1160
+ closeBtn.innerHTML = `<co-icon name="x" size="14"></co-icon>`;
1161
+ closeBtn.addEventListener('click', () => { panel.style.display = 'none'; });
1162
+ header.appendChild(dragHandle);
1163
+ header.appendChild(titleEl);
1164
+ header.appendChild(closeBtn);
1165
+ dragHandle.addEventListener('mousedown', (e) => {
1166
+ e.preventDefault();
1167
+ e.stopPropagation();
1168
+ const rect = panel.getBoundingClientRect();
1169
+ dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
1170
+ if (!dragged)
1171
+ dragPos = { x: rect.left, y: rect.top };
1172
+ dragged = true;
1173
+ const onMove = (ev) => {
1174
+ dragPos.x = ev.clientX - dragOffset.x;
1175
+ dragPos.y = ev.clientY - dragOffset.y;
1176
+ panel.style.position = 'fixed';
1177
+ panel.style.top = `${dragPos.y}px`;
1178
+ panel.style.left = `${dragPos.x}px`;
1179
+ panel.style.bottom = 'auto';
1180
+ panel.style.right = 'auto';
1181
+ panel.style.transform = 'none';
1182
+ panel.removeAttribute('data-position');
1183
+ };
1184
+ const onUp = () => {
1185
+ document.removeEventListener('mousemove', onMove);
1186
+ document.removeEventListener('mouseup', onUp);
1187
+ };
1188
+ document.addEventListener('mousemove', onMove);
1189
+ document.addEventListener('mouseup', onUp);
1190
+ });
1191
+ const resetDrag = () => { dragged = false; };
1192
+ panel.insertBefore(header, panel.firstChild);
1193
+ return { header, resetDrag };
1194
+ }
1195
+ /** Generic draggable panel factory. */
1196
+ createBubblePanel(config) {
1197
+ const wrap = document.createElement('div');
1198
+ wrap.className = 'co-rte-bubble__select';
1199
+ wrap.setAttribute('data-select', config.name);
1200
+ const trigger = document.createElement('button');
1201
+ trigger.type = 'button';
1202
+ trigger.className = 'co-rte-bubble__select-trigger';
1203
+ trigger.innerHTML = `<span class="co-rte-bubble__select-label">${config.triggerLabel}</span><co-icon name="caret-down" size="12"></co-icon>`;
1204
+ const panel = document.createElement('div');
1205
+ panel.className = 'co-rte-bubble__panel';
1206
+ panel.setAttribute('data-panel', config.name);
1207
+ panel.style.display = 'none';
1208
+ // Header
1209
+ const header = document.createElement('div');
1210
+ header.className = 'co-rte-bubble__panel-header';
1211
+ const dragHandle = document.createElement('div');
1212
+ dragHandle.className = 'co-rte-bubble__panel-drag';
1213
+ dragHandle.innerHTML = `<co-icon name="dots-six" size="14"></co-icon>`;
1214
+ const titleEl = document.createElement('span');
1215
+ titleEl.className = 'co-rte-bubble__panel-title';
1216
+ titleEl.textContent = config.title;
1217
+ const closeBtn = document.createElement('button');
1218
+ closeBtn.type = 'button';
1219
+ closeBtn.className = 'co-rte-bubble__panel-close';
1220
+ closeBtn.innerHTML = `<co-icon name="x" size="14"></co-icon>`;
1221
+ closeBtn.addEventListener('click', () => { panel.style.display = 'none'; });
1222
+ header.appendChild(dragHandle);
1223
+ header.appendChild(titleEl);
1224
+ header.appendChild(closeBtn);
1225
+ panel.appendChild(header);
1226
+ // Panel drag
1227
+ let panelDragged = false;
1228
+ let panelDragPos = { x: 0, y: 0 };
1229
+ let panelDragOffset = { x: 0, y: 0 };
1230
+ dragHandle.addEventListener('mousedown', (e) => {
1231
+ e.preventDefault();
1232
+ e.stopPropagation();
1233
+ const rect = panel.getBoundingClientRect();
1234
+ panelDragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
1235
+ if (!panelDragged)
1236
+ panelDragPos = { x: rect.left, y: rect.top };
1237
+ panelDragged = true;
1238
+ const onMove = (ev) => {
1239
+ panelDragPos.x = ev.clientX - panelDragOffset.x;
1240
+ panelDragPos.y = ev.clientY - panelDragOffset.y;
1241
+ panel.style.position = 'fixed';
1242
+ panel.style.top = `${panelDragPos.y}px`;
1243
+ panel.style.left = `${panelDragPos.x}px`;
1244
+ panel.style.bottom = 'auto';
1245
+ panel.style.right = 'auto';
1246
+ panel.style.transform = 'none';
1247
+ panel.removeAttribute('data-position');
1248
+ };
1249
+ const onUp = () => {
1250
+ document.removeEventListener('mousemove', onMove);
1251
+ document.removeEventListener('mouseup', onUp);
1252
+ };
1253
+ document.addEventListener('mousemove', onMove);
1254
+ document.addEventListener('mouseup', onUp);
1255
+ });
1256
+ // Custom input
1257
+ if (config.hasCustomInput) {
1258
+ const inputWrap = document.createElement('div');
1259
+ inputWrap.className = 'co-rte-bubble__panel-input-wrap';
1260
+ const input = document.createElement('input');
1261
+ input.type = config.inputType || 'text';
1262
+ input.className = 'co-rte-bubble__panel-input';
1263
+ input.placeholder = config.inputPlaceholder || '';
1264
+ input.addEventListener('mousedown', (e) => e.stopPropagation());
1265
+ input.addEventListener('keydown', (e) => {
1266
+ if (e.key === 'Enter') {
1267
+ const val = input.value.trim();
1268
+ if (val && config.onCustomInput) {
1269
+ config.onCustomInput(val);
1270
+ input.value = '';
1271
+ }
1272
+ }
1273
+ if (e.key === 'Escape') {
1274
+ panel.style.display = 'none';
1275
+ }
1276
+ });
1277
+ inputWrap.appendChild(input);
1278
+ panel.appendChild(inputWrap);
1279
+ }
1280
+ // Options
1281
+ const optionsWrap = document.createElement('div');
1282
+ optionsWrap.className = 'co-rte-bubble__panel-options';
1283
+ config.options.forEach(opt => {
1284
+ const btn = document.createElement('button');
1285
+ btn.type = 'button';
1286
+ btn.className = 'co-rte-bubble__panel-option';
1287
+ btn.setAttribute('data-value', opt.value);
1288
+ if (config.renderOption) {
1289
+ config.renderOption(opt, btn);
1290
+ }
1291
+ else {
1292
+ btn.textContent = opt.label;
1293
+ }
1294
+ btn.addEventListener('click', () => {
1295
+ config.onSelect(opt.value);
1296
+ panel.style.display = 'none';
1297
+ panelDragged = false;
1298
+ });
1299
+ optionsWrap.appendChild(btn);
1300
+ });
1301
+ panel.appendChild(optionsWrap);
1302
+ // Prevent bubble from losing focus
1303
+ panel.addEventListener('mousedown', (e) => {
1304
+ if (e.target.tagName !== 'INPUT')
1305
+ e.preventDefault();
1306
+ this.lockBubblePosition();
1307
+ });
1308
+ // Toggle logic
1309
+ const togglePanel = () => {
1310
+ this.closeAllBubblePopups(panel);
1311
+ const isOpen = panel.style.display !== 'none';
1312
+ if (isOpen) {
1313
+ panel.style.display = 'none';
1314
+ }
1315
+ else {
1316
+ panelDragged = false;
1317
+ panel.style.position = '';
1318
+ panel.style.top = '';
1319
+ panel.style.left = '';
1320
+ panel.style.bottom = '';
1321
+ panel.style.right = '';
1322
+ panel.style.transform = '';
1323
+ panel.style.display = 'block';
1324
+ this.positionDropdown(trigger, panel);
1325
+ }
1326
+ };
1327
+ trigger.addEventListener('click', togglePanel);
1328
+ wrap.appendChild(trigger);
1329
+ const updateLabel = (text) => {
1330
+ const labelEl = trigger.querySelector('.co-rte-bubble__select-label');
1331
+ if (labelEl)
1332
+ labelEl.textContent = text;
1333
+ };
1334
+ return { wrap, panelEl: panel, updateLabel };
1335
+ }
1336
+ /** Font size panel with visual preview and custom input. */
1337
+ createBubbleFontSizePanel() {
1338
+ const _fs = getRichTextStrings(this.locale);
1339
+ const headings = [
1340
+ { label: _fs.title, value: 'h1' },
1341
+ { label: _fs.heading1, value: 'h2' },
1342
+ { label: _fs.heading2, value: 'h3' },
1343
+ ];
1344
+ const sizes = ['8px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px', '36px', '48px', '64px', '72px'];
1345
+ const allOptions = [
1346
+ ...headings,
1347
+ { label: '---', value: '---' },
1348
+ ...sizes.map(s => ({ label: s, value: s })),
1349
+ ];
1350
+ return this.createBubblePanel({
1351
+ name: 'fontSize',
1352
+ title: _fs.fontSize,
1353
+ triggerLabel: '16px',
1354
+ hasCustomInput: true,
1355
+ inputType: 'number',
1356
+ inputPlaceholder: _fs.writeSize,
1357
+ options: allOptions,
1358
+ renderOption: (opt, btn) => {
1359
+ if (opt.value === '---') {
1360
+ btn.className = 'co-rte-bubble__panel-sep';
1361
+ btn.disabled = true;
1362
+ btn.style.pointerEvents = 'none';
1363
+ return;
1364
+ }
1365
+ if (opt.value === 'h1') {
1366
+ btn.textContent = opt.label;
1367
+ btn.style.fontSize = '24px';
1368
+ btn.style.fontWeight = '700';
1369
+ btn.style.padding = '6px 8px';
1370
+ }
1371
+ else if (opt.value === 'h2') {
1372
+ btn.textContent = opt.label;
1373
+ btn.style.fontSize = '18px';
1374
+ btn.style.fontWeight = '700';
1375
+ btn.style.padding = '5px 8px';
1376
+ }
1377
+ else if (opt.value === 'h3') {
1378
+ btn.textContent = opt.label;
1379
+ btn.style.fontSize = '15px';
1380
+ btn.style.fontWeight = '700';
1381
+ btn.style.padding = '4px 8px';
1382
+ }
1383
+ else {
1384
+ btn.textContent = opt.label;
1385
+ const px = parseInt(opt.value);
1386
+ btn.style.fontSize = `${Math.min(px, 36)}px`;
1387
+ btn.style.lineHeight = '1.3';
1388
+ }
1389
+ },
1390
+ onSelect: (val) => {
1391
+ if (!this.editor || val === '---')
1392
+ return;
1393
+ const chain = this.editor.chain().focus();
1394
+ if (val === 'h1')
1395
+ chain.toggleHeading({ level: 1 }).run();
1396
+ else if (val === 'h2')
1397
+ chain.toggleHeading({ level: 2 }).run();
1398
+ else if (val === 'h3')
1399
+ chain.toggleHeading({ level: 3 }).run();
1400
+ else
1401
+ chain.setFontSize(val).run();
1402
+ },
1403
+ onCustomInput: (val) => {
1404
+ if (!this.editor)
1405
+ return;
1406
+ const num = parseInt(val);
1407
+ if (num > 0)
1408
+ this.editor.chain().focus().setFontSize(`${num}px`).run();
1409
+ },
1410
+ });
1411
+ }
1412
+ /** Font weight panel with two-column layout and visual preview. */
1413
+ createBubbleFontWeightPanel() {
1414
+ const weights = [
1415
+ { label: 'Thin', value: '100' },
1416
+ { label: 'Extra Light', value: '200' },
1417
+ { label: 'Light', value: '300' },
1418
+ { label: 'Regular', value: '400' },
1419
+ { label: 'Medium', value: '500' },
1420
+ { label: 'Semi Bold', value: '600' },
1421
+ { label: 'Bold', value: '700' },
1422
+ { label: 'Extra Bold', value: '800' },
1423
+ { label: 'Black', value: '900' },
1424
+ ];
1425
+ return this.createBubblePanel({
1426
+ name: 'fontWeight',
1427
+ title: getRichTextStrings(this.locale).fontWeight,
1428
+ triggerLabel: 'Regular',
1429
+ hasCustomInput: false,
1430
+ options: weights,
1431
+ renderOption: (opt, btn) => {
1432
+ btn.style.fontWeight = opt.value;
1433
+ btn.innerHTML = `<span class="co-rte-bubble__panel-opt-row"><span>${opt.label}</span><span class="co-rte-bubble__panel-opt-value">${opt.value}</span></span>`;
1434
+ },
1435
+ onSelect: (val) => {
1436
+ if (!this.editor)
1437
+ return;
1438
+ this.editor.chain().focus().setFontWeight(val).run();
1439
+ },
1440
+ });
1441
+ }
1442
+ /** Line height panel with custom input. */
1443
+ createBubbleLineHeightPanel() {
1444
+ const options = [
1445
+ { label: '0.5', value: '0.5' },
1446
+ { label: '1', value: '1' },
1447
+ { label: '1.15', value: '1.15' },
1448
+ { label: '1.5', value: '1.5' },
1449
+ { label: '1.75', value: '1.75' },
1450
+ { label: '2', value: '2' },
1451
+ { label: '2.5', value: '2.5' },
1452
+ { label: '3', value: '3' },
1453
+ { label: '---', value: '---' },
1454
+ { label: '16px', value: '16px' },
1455
+ { label: '20px', value: '20px' },
1456
+ { label: '24px', value: '24px' },
1457
+ { label: '32px', value: '32px' },
1458
+ { label: '---', value: '---2' },
1459
+ { label: '100%', value: '100%' },
1460
+ { label: '150%', value: '150%' },
1461
+ { label: '200%', value: '200%' },
1462
+ ];
1463
+ const { panelEl } = this.createBubblePanel({
1464
+ name: 'lineHeight',
1465
+ title: getRichTextStrings(this.locale).lineSpacing,
1466
+ triggerLabel: '1.5',
1467
+ hasCustomInput: true,
1468
+ inputPlaceholder: '1.5, 20px, 150%...',
1469
+ options,
1470
+ renderOption: (opt, btn) => {
1471
+ if (opt.value.startsWith('---')) {
1472
+ btn.className = 'co-rte-bubble__panel-sep';
1473
+ btn.disabled = true;
1474
+ btn.style.pointerEvents = 'none';
1475
+ return;
1476
+ }
1477
+ btn.textContent = opt.label;
1478
+ },
1479
+ onSelect: (val) => {
1480
+ if (!this.editor || val.startsWith('---'))
1481
+ return;
1482
+ this.editor.chain().focus().setLineHeight(val).run();
1483
+ },
1484
+ onCustomInput: (val) => {
1485
+ if (!this.editor)
1486
+ return;
1487
+ this.editor.chain().focus().setLineHeight(val).run();
1488
+ },
1489
+ });
1490
+ return panelEl;
1491
+ }
1492
+ /** Letter spacing panel with custom input. */
1493
+ createBubbleLetterSpacingPanel() {
1494
+ const options = [
1495
+ { label: '-0.05em', value: '-0.05em' },
1496
+ { label: 'Normal', value: 'normal' },
1497
+ { label: '0.05em', value: '0.05em' },
1498
+ { label: '0.1em', value: '0.1em' },
1499
+ { label: '0.2em', value: '0.2em' },
1500
+ ];
1501
+ const { panelEl } = this.createBubblePanel({
1502
+ name: 'letterSpacing',
1503
+ title: getRichTextStrings(this.locale).letterSpacing,
1504
+ triggerLabel: getRichTextStrings(this.locale).normal,
1505
+ hasCustomInput: true,
1506
+ inputPlaceholder: 'Custom (em)...',
1507
+ options,
1508
+ onSelect: (val) => {
1509
+ if (!this.editor)
1510
+ return;
1511
+ if (val === 'normal')
1512
+ this.editor.chain().focus().unsetLetterSpacing().run();
1513
+ else
1514
+ this.editor.chain().focus().setLetterSpacing(val).run();
1515
+ },
1516
+ onCustomInput: (val) => {
1517
+ if (!this.editor)
1518
+ return;
1519
+ const v = val.includes('em') || val.includes('px') ? val : `${val}em`;
1520
+ this.editor.chain().focus().setLetterSpacing(v).run();
1521
+ },
1522
+ });
1523
+ return panelEl;
1524
+ }
1525
+ /** Background color panel for overflow menu. */
1526
+ createBubbleBgColorPanel() {
1527
+ const bgColorWrap = this.createBubbleColorPicker('bgColor', (color) => {
1528
+ if (!this.editor)
1529
+ return;
1530
+ this.editor.chain().focus().toggleHighlight({ color }).run();
1531
+ });
1532
+ // Hide the trigger button — bg color is only opened from the overflow menu.
1533
+ const triggerBtn = bgColorWrap.querySelector('.co-rte-bubble__btn');
1534
+ if (triggerBtn)
1535
+ triggerBtn.style.display = 'none';
1536
+ return bgColorWrap;
1537
+ }
1538
+ /** Overflow menu (three-dots ⋮) with dynamic table context section. */
1539
+ createBubbleOverflowMenu(panels) {
1540
+ const wrap = document.createElement('div');
1541
+ wrap.className = 'co-rte-bubble__overflow';
1542
+ const btn = document.createElement('button');
1543
+ btn.type = 'button';
1544
+ btn.className = 'co-rte-bubble__btn';
1545
+ const _os = getRichTextStrings(this.locale);
1546
+ btn.title = _os.moreOptions;
1547
+ btn.setAttribute('aria-label', _os.moreOptions);
1548
+ btn.innerHTML = `<co-icon name="dots-three" size="16"></co-icon>`;
1549
+ const menu = document.createElement('div');
1550
+ menu.className = 'co-rte-bubble__overflow-menu';
1551
+ menu.style.display = 'none';
1552
+ const { resetDrag: resetOverflowDrag } = this.addDragHeader(menu, _os.moreOptions);
1553
+ // Prevent focus loss
1554
+ menu.addEventListener('mousedown', (e) => {
1555
+ if (e.target.tagName !== 'INPUT')
1556
+ e.preventDefault();
1557
+ this.lockBubblePosition();
1558
+ });
1559
+ const items = [
1560
+ { icon: 'text-strikethrough', label: _os.strikethrough, action: 'strike', check: 'strike', type: 'toggle' },
1561
+ { icon: 'highlighter', label: _os.backgroundColor, action: 'bgColor', type: 'panel', panel: panels.bgColor },
1562
+ { icon: 'list-bullets', label: _os.bulletList, action: 'bulletList', check: 'bulletList', type: 'toggle' },
1563
+ { icon: 'list-numbers', label: _os.orderedList, action: 'orderedList', check: 'orderedList', type: 'toggle' },
1564
+ { icon: 'text-superscript', label: _os.superscript, action: 'superscript', check: 'superscript', type: 'toggle' },
1565
+ { icon: 'text-subscript', label: _os.subscript, action: 'subscript', check: 'subscript', type: 'toggle' },
1566
+ { icon: 'line-segments', label: _os.lineSpacing, action: 'lineHeight', type: 'panel', panel: panels.lineHeight, valueAttr: 'lineHeight' },
1567
+ { icon: 'text-aa', label: _os.letterSpacing, action: 'letterSpacing', type: 'panel', panel: panels.letterSpacing, valueAttr: 'letterSpacing' },
1568
+ { icon: 'minus', label: _os.horizontalRule, action: 'horizontalRule', type: 'toggle' },
1569
+ { icon: 'eraser', label: _os.clearFormat, action: 'clearFormat', type: 'toggle' },
1570
+ ];
1571
+ items.forEach((item) => {
1572
+ if (item.action === 'lineHeight' || item.action === 'horizontalRule') {
1573
+ const sep = document.createElement('div');
1574
+ sep.className = 'co-rte-bubble__overflow-sep';
1575
+ menu.appendChild(sep);
1576
+ }
1577
+ const itemBtn = document.createElement('button');
1578
+ itemBtn.type = 'button';
1579
+ itemBtn.className = 'co-rte-bubble__overflow-item';
1580
+ itemBtn.setAttribute('data-overflow-item', item.action);
1581
+ if (item.check)
1582
+ itemBtn.setAttribute('data-check', item.check);
1583
+ let valueSpan = '';
1584
+ if (item.valueAttr) {
1585
+ valueSpan = `<span class="co-rte-bubble__overflow-value" data-overflow-value="${item.valueAttr}"></span>`;
1586
+ }
1587
+ itemBtn.innerHTML = `<co-icon name="${item.icon}" size="16"></co-icon><span>${item.label}</span>${valueSpan}`;
1588
+ itemBtn.addEventListener('click', () => {
1589
+ if (!this.editor)
1590
+ return;
1591
+ if (item.type === 'panel' && item.panel) {
1592
+ if (item.action === 'bgColor') {
1593
+ const dd = item.panel.querySelector('.co-rte-bubble__color-dropdown');
1594
+ if (dd) {
1595
+ const isOpen = dd.style.display !== 'none';
1596
+ this.closeAllBubblePopups(menu, isOpen ? undefined : dd);
1597
+ dd.style.display = isOpen ? 'none' : 'block';
1598
+ if (!isOpen)
1599
+ this.positionPanelNearBubble(dd);
1600
+ }
1601
+ }
1602
+ else {
1603
+ const isOpen = item.panel.style.display !== 'none';
1604
+ this.closeAllBubblePopups(menu, isOpen ? undefined : item.panel);
1605
+ item.panel.style.display = isOpen ? 'none' : 'block';
1606
+ if (!isOpen)
1607
+ this.positionPanelNearBubble(item.panel);
1608
+ }
1609
+ return;
1610
+ }
1611
+ const chain = this.editor.chain().focus();
1612
+ switch (item.action) {
1613
+ case 'strike':
1614
+ chain.toggleStrike().run();
1615
+ break;
1616
+ case 'bulletList':
1617
+ chain.toggleBulletList().run();
1618
+ break;
1619
+ case 'orderedList':
1620
+ chain.toggleOrderedList().run();
1621
+ break;
1622
+ case 'superscript':
1623
+ chain.toggleSuperscript().run();
1624
+ break;
1625
+ case 'subscript':
1626
+ chain.toggleSubscript().run();
1627
+ break;
1628
+ case 'horizontalRule':
1629
+ chain.setHorizontalRule().run();
1630
+ menu.style.display = 'none';
1631
+ break;
1632
+ case 'clearFormat':
1633
+ chain.unsetAllMarks().clearNodes().run();
1634
+ menu.style.display = 'none';
1635
+ break;
1636
+ }
1637
+ });
1638
+ menu.appendChild(itemBtn);
1639
+ });
1640
+ // ── Table context section (hidden by default, shown when cursor is in a table) ──
1641
+ const tableSection = document.createElement('div');
1642
+ tableSection.className = 'co-rte-bubble__overflow-table-section';
1643
+ tableSection.style.display = 'none';
1644
+ const tableSep = document.createElement('div');
1645
+ tableSep.className = 'co-rte-bubble__overflow-sep';
1646
+ tableSection.appendChild(tableSep);
1647
+ const tableHeader = document.createElement('div');
1648
+ tableHeader.className = 'co-rte-bubble__overflow-section-title';
1649
+ tableHeader.textContent = _os.tableOptions;
1650
+ tableSection.appendChild(tableHeader);
1651
+ const tableItems = [
1652
+ { action: 'addColumnBefore', icon: 'columns-plus-left', label: _os.addColumnBefore },
1653
+ { action: 'addColumnAfter', icon: 'columns-plus-right', label: _os.addColumnAfter },
1654
+ { action: 'addRowBefore', icon: 'rows-plus-top', label: _os.addRowAbove },
1655
+ { action: 'addRowAfter', icon: 'rows-plus-bottom', label: _os.addRowBelow },
1656
+ { action: 'mergeCells', icon: 'arrows-merge', label: _os.mergeCells },
1657
+ { action: 'splitCell', icon: 'arrows-split', label: _os.splitCell },
1658
+ { action: 'toggleHeaderRow', icon: 'rows', label: _os.headerRow },
1659
+ ];
1660
+ tableItems.forEach(({ action, icon, label }) => {
1661
+ const tBtn = document.createElement('button');
1662
+ tBtn.type = 'button';
1663
+ tBtn.className = 'co-rte-bubble__overflow-item';
1664
+ tBtn.setAttribute('data-overflow-item', action);
1665
+ if (action === 'toggleHeaderRow')
1666
+ tBtn.setAttribute('data-check', 'toggleHeaderRow');
1667
+ tBtn.innerHTML = `<co-icon name="${icon}" size="16"></co-icon><span>${label}</span>`;
1668
+ tBtn.addEventListener('click', () => {
1669
+ if (!this.editor)
1670
+ return;
1671
+ const chain = this.editor.chain().focus();
1672
+ switch (action) {
1673
+ case 'addColumnAfter':
1674
+ chain.addColumnAfter().run();
1675
+ break;
1676
+ case 'addColumnBefore':
1677
+ chain.addColumnBefore().run();
1678
+ break;
1679
+ case 'addRowAfter':
1680
+ chain.addRowAfter().run();
1681
+ break;
1682
+ case 'addRowBefore':
1683
+ chain.addRowBefore().run();
1684
+ break;
1685
+ case 'mergeCells':
1686
+ chain.mergeCells().run();
1687
+ break;
1688
+ case 'splitCell':
1689
+ chain.splitCell().run();
1690
+ break;
1691
+ case 'toggleHeaderRow':
1692
+ chain.toggleHeaderRow().run();
1693
+ break;
1694
+ }
1695
+ });
1696
+ tableSection.appendChild(tBtn);
1697
+ });
1698
+ // Danger items (delete column, row, table)
1699
+ const dangerSep = document.createElement('div');
1700
+ dangerSep.className = 'co-rte-bubble__overflow-sep';
1701
+ tableSection.appendChild(dangerSep);
1702
+ const dangerItems = [
1703
+ { action: 'deleteColumn', icon: 'minus-square', label: _os.deleteColumn },
1704
+ { action: 'deleteRow', icon: 'x-square', label: _os.deleteRow },
1705
+ { action: 'deleteTable', icon: 'trash', label: _os.deleteTable },
1706
+ ];
1707
+ dangerItems.forEach(({ action, icon, label }) => {
1708
+ const dBtn = document.createElement('button');
1709
+ dBtn.type = 'button';
1710
+ dBtn.className = 'co-rte-bubble__overflow-item co-rte-bubble__overflow-item--danger';
1711
+ dBtn.setAttribute('data-overflow-item', action);
1712
+ dBtn.innerHTML = `<co-icon name="${icon}" size="16"></co-icon><span>${label}</span>`;
1713
+ dBtn.addEventListener('click', () => {
1714
+ if (!this.editor)
1715
+ return;
1716
+ const chain = this.editor.chain().focus();
1717
+ switch (action) {
1718
+ case 'deleteColumn':
1719
+ chain.deleteColumn().run();
1720
+ break;
1721
+ case 'deleteRow':
1722
+ chain.deleteRow().run();
1723
+ break;
1724
+ case 'deleteTable':
1725
+ chain.deleteTable().run();
1726
+ menu.style.display = 'none';
1727
+ break;
1728
+ }
1729
+ });
1730
+ tableSection.appendChild(dBtn);
1731
+ });
1732
+ menu.appendChild(tableSection);
1733
+ btn.addEventListener('click', () => {
1734
+ this.closeAllBubblePopups(menu);
1735
+ const isOpen = menu.style.display !== 'none';
1736
+ if (isOpen) {
1737
+ menu.style.display = 'none';
1738
+ }
1739
+ else {
1740
+ resetOverflowDrag();
1741
+ menu.style.position = '';
1742
+ menu.style.top = '';
1743
+ menu.style.left = '';
1744
+ menu.style.bottom = '';
1745
+ menu.style.right = '';
1746
+ menu.style.transform = '';
1747
+ menu.style.display = 'block';
1748
+ this.positionDropdown(btn, menu);
1749
+ }
1750
+ });
1751
+ wrap.appendChild(btn);
1752
+ wrap.appendChild(menu);
1753
+ return { overflowWrap: wrap, overflowMenu: menu };
1754
+ }
1755
+ toggleBubbleLinkForm(show) {
1756
+ var _a, _b;
1757
+ if (!this.bubbleLinkFormEl || !this.bubbleLinkInputEl)
1758
+ return;
1759
+ this.bubbleLinkFormEl.style.display = show ? 'flex' : 'none';
1760
+ if (show) {
1761
+ const existing = ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.isActive('link'))
1762
+ ? ((_b = this.editor.getAttributes('link')) === null || _b === void 0 ? void 0 : _b.href) || ''
1763
+ : '';
1764
+ this.bubbleLinkInputEl.value = existing;
1765
+ if (this.bubbleLinkUnlinkEl) {
1766
+ this.bubbleLinkUnlinkEl.style.display = existing ? 'flex' : 'none';
1767
+ }
1768
+ setTimeout(() => { var _a; return (_a = this.bubbleLinkInputEl) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
1769
+ }
1770
+ }
1771
+ submitBubbleLinkImperative() {
1772
+ if (!this.editor || !this.bubbleLinkInputEl)
1773
+ return;
1774
+ const url = this.bubbleLinkInputEl.value.trim();
1775
+ if (!url) {
1776
+ this.toggleBubbleLinkForm(false);
1777
+ return;
1778
+ }
1779
+ this.editor.chain().focus().setLink({ href: url }).run();
1780
+ this.toggleBubbleLinkForm(false);
1781
+ }
1782
+ updateBubbleActiveStates() {
1783
+ if (!this.editor || !this.bubbleEl)
1784
+ return;
1785
+ // Toggle buttons (bold, italic, etc.)
1786
+ const btns = this.bubbleEl.querySelectorAll('.co-rte-bubble__btn[data-check]');
1787
+ btns.forEach(btn => {
1788
+ const check = btn.getAttribute('data-check');
1789
+ if (check) {
1790
+ btn.classList.toggle('co-rte-bubble__btn--active', this.editor.isActive(check));
1791
+ }
1792
+ });
1793
+ // Alignment buttons
1794
+ const alignBtns = this.bubbleEl.querySelectorAll('.co-rte-bubble__btn[data-align]');
1795
+ alignBtns.forEach(btn => {
1796
+ const align = btn.getAttribute('data-align');
1797
+ if (align) {
1798
+ btn.classList.toggle('co-rte-bubble__btn--active', this.editor.isActive({ textAlign: align }));
1799
+ }
1800
+ });
1801
+ // Font family label
1802
+ const ffLabel = this.bubbleEl.querySelector('[data-select="fontFamily"] .co-rte-bubble__select-label');
1803
+ if (ffLabel) {
1804
+ const attrs = this.editor.getAttributes('textStyle');
1805
+ const family = (attrs === null || attrs === void 0 ? void 0 : attrs.fontFamily) || '';
1806
+ ffLabel.textContent = family ? family.split(',')[0].replace(/['"]/g, '').trim() : 'Sans-serif';
1807
+ }
1808
+ // Font size label (panel trigger)
1809
+ const fsTrigger = this.bubbleEl.querySelector('[data-select="fontSize"] .co-rte-bubble__select-label');
1810
+ if (fsTrigger) {
1811
+ const attrs = this.editor.getAttributes('textStyle');
1812
+ const _us = getRichTextStrings(this.locale);
1813
+ if (this.editor.isActive('heading', { level: 1 }))
1814
+ fsTrigger.textContent = _us.title;
1815
+ else if (this.editor.isActive('heading', { level: 2 }))
1816
+ fsTrigger.textContent = _us.headingAbbr1;
1817
+ else if (this.editor.isActive('heading', { level: 3 }))
1818
+ fsTrigger.textContent = _us.headingAbbr2;
1819
+ else
1820
+ fsTrigger.textContent = (attrs === null || attrs === void 0 ? void 0 : attrs.fontSize) || '16px';
1821
+ }
1822
+ // Font weight label (panel trigger)
1823
+ const fwTrigger = this.bubbleEl.querySelector('[data-select="fontWeight"] .co-rte-bubble__select-label');
1824
+ if (fwTrigger) {
1825
+ const attrs = this.editor.getAttributes('textStyle');
1826
+ const w = (attrs === null || attrs === void 0 ? void 0 : attrs.fontWeight) || '400';
1827
+ const wMap = { '100': 'Thin', '200': 'Extra Light', '300': 'Light', '400': 'Regular', '500': 'Medium', '600': 'Semi-bold', '700': 'Bold', '800': 'Extra-bold', '900': 'Black' };
1828
+ fwTrigger.textContent = wMap[w] || 'Regular';
1829
+ }
1830
+ // Overflow menu items active states
1831
+ const overflowItems = this.bubbleEl.querySelectorAll('.co-rte-bubble__overflow-item[data-check]');
1832
+ overflowItems.forEach(item => {
1833
+ const check = item.getAttribute('data-check');
1834
+ if (check) {
1835
+ item.classList.toggle('co-rte-bubble__overflow-item--active', this.editor.isActive(check));
1836
+ }
1837
+ });
1838
+ // Line height value in overflow (lineHeight is a block-level attribute on paragraph/heading)
1839
+ const lhValue = this.bubbleEl.querySelector('[data-overflow-value="lineHeight"]');
1840
+ if (lhValue) {
1841
+ const pAttrs = this.editor.getAttributes('paragraph');
1842
+ const hAttrs = this.editor.getAttributes('heading');
1843
+ lhValue.textContent = (pAttrs === null || pAttrs === void 0 ? void 0 : pAttrs.lineHeight) || (hAttrs === null || hAttrs === void 0 ? void 0 : hAttrs.lineHeight) || '';
1844
+ }
1845
+ // Letter spacing value in overflow
1846
+ const lsValue = this.bubbleEl.querySelector('[data-overflow-value="letterSpacing"]');
1847
+ if (lsValue) {
1848
+ const attrs = this.editor.getAttributes('textStyle');
1849
+ lsValue.textContent = (attrs === null || attrs === void 0 ? void 0 : attrs.letterSpacing) || '';
1850
+ }
1851
+ // Text color indicator bar
1852
+ const colorBar = this.bubbleEl.querySelector('.co-rte-bubble__color-bar');
1853
+ if (colorBar) {
1854
+ const attrs = this.editor.getAttributes('textStyle');
1855
+ colorBar.style.background = (attrs === null || attrs === void 0 ? void 0 : attrs.color) || '#000000';
1856
+ }
1857
+ // Table context section — show/hide based on cursor position
1858
+ const tableSection = this.bubbleEl.querySelector('.co-rte-bubble__overflow-table-section');
1859
+ if (tableSection) {
1860
+ const inTable = this.editor.isActive('table');
1861
+ tableSection.style.display = inTable ? 'block' : 'none';
1862
+ // Toggle header row active state
1863
+ if (inTable) {
1864
+ const headerBtn = tableSection.querySelector('[data-overflow-item="toggleHeaderRow"]');
1865
+ if (headerBtn) {
1866
+ const view = this.editor.view;
1867
+ const { $anchor } = this.editor.state.selection;
1868
+ let d = $anchor.depth;
1869
+ let hasHeader = false;
1870
+ while (d > 0) {
1871
+ if ($anchor.node(d).type.name === 'table') {
1872
+ const domEl = view.nodeDOM($anchor.start(d) - 1);
1873
+ if (domEl) {
1874
+ const tbl = domEl.querySelector('table') || domEl;
1875
+ const firstRow = tbl.querySelector('tr');
1876
+ hasHeader = firstRow ? firstRow.querySelector('th') !== null : false;
1877
+ }
1878
+ break;
1879
+ }
1880
+ d--;
1881
+ }
1882
+ headerBtn.classList.toggle('co-rte-bubble__overflow-item--active', hasHeader);
1883
+ }
1884
+ }
1885
+ }
1886
+ }
1887
+ createImageLayoutBar() {
1888
+ const bar = document.createElement('div');
1889
+ bar.className = 'co-rte-img-layout';
1890
+ bar.style.display = 'none';
1891
+ bar.addEventListener('mousedown', (e) => {
1892
+ e.preventDefault();
1893
+ e.stopPropagation();
1894
+ });
1895
+ const s = getRichTextStrings(this.locale);
1896
+ const labels = {
1897
+ 'inline': s.imageLayoutInline,
1898
+ 'wrap-left': s.imageLayoutWrapLeft,
1899
+ 'wrap-right': s.imageLayoutWrapRight,
1900
+ 'center': s.imageLayoutCenter,
1901
+ 'full-width': s.imageLayoutFullWidth,
1902
+ };
1903
+ const keys = ['inline', 'wrap-left', 'wrap-right', 'center', 'full-width'];
1904
+ keys.forEach(key => {
1905
+ const btn = document.createElement('button');
1906
+ btn.type = 'button';
1907
+ btn.className = 'co-rte-img-layout__btn';
1908
+ btn.setAttribute('data-layout', key);
1909
+ // Fast CSS tooltip (avoids the slow native `title` delay).
1910
+ btn.setAttribute('data-tooltip', labels[key] || key);
1911
+ btn.setAttribute('aria-label', labels[key] || key);
1912
+ btn.innerHTML = `<co-icon name="${CoRichTextEditor.IMG_LAYOUT_ICONS[key]}" size="18"></co-icon>`;
1913
+ btn.addEventListener('click', () => this.setImageLayout(key));
1914
+ bar.appendChild(btn);
1915
+ });
1916
+ return bar;
1917
+ }
1918
+ setImageLayout(layout) {
1919
+ if (!this.editor || !this.contentEl)
1920
+ return;
1921
+ const { state } = this.editor;
1922
+ const sel = state.selection;
1923
+ if (sel.constructor.name !== 'NodeSelection')
1924
+ return;
1925
+ const node = sel.node;
1926
+ if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'image')
1927
+ return;
1928
+ const pos = sel.from;
1929
+ const tr = state.tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, node.attrs), { imageLayout: layout }));
1930
+ this.editor.view.dispatch(tr);
1931
+ this.editor.commands.setNodeSelection(pos);
1932
+ requestAnimationFrame(() => {
1933
+ this.syncImageLayouts();
1934
+ this.updateImageLayoutBar();
1935
+ });
1936
+ }
1937
+ getSelectedImageInfo() {
1938
+ var _a;
1939
+ if (!this.editor)
1940
+ return null;
1941
+ const { state } = this.editor;
1942
+ const sel = state.selection;
1943
+ if (sel.constructor.name !== 'NodeSelection')
1944
+ return null;
1945
+ const node = sel.node;
1946
+ if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'image')
1947
+ return null;
1948
+ const pos = sel.from;
1949
+ const domNode = this.editor.view.nodeDOM(pos);
1950
+ if (!domNode)
1951
+ return null;
1952
+ const container = (((_a = domNode.closest) === null || _a === void 0 ? void 0 : _a.call(domNode, '[data-resize-container]')) || domNode);
1953
+ return { node, pos, container };
1954
+ }
1955
+ /**
1956
+ * Tag every image container with its layout mode (`data-image-layout`) so the
1957
+ * stylesheet's responsive rules apply, and clear any stray inline positioning
1958
+ * left over from legacy absolute-positioned content. Layout is CSS-only now
1959
+ * (it reflows and collapses on mobile); we only set the attribute here.
1960
+ *
1961
+ * Runs on every transaction. The resize NodeView's `onUpdate` returns `true`,
1962
+ * so ProseMirror never reflects attribute changes to the DOM — we must read
1963
+ * from state and write the attribute imperatively to the container.
1964
+ */
1965
+ syncImageLayouts() {
1966
+ if (!this.editor)
1967
+ return;
1968
+ const view = this.editor.view;
1969
+ this.editor.state.doc.descendants((node, pos) => {
1970
+ var _a;
1971
+ if (node.type.name !== 'image')
1972
+ return true;
1973
+ const dom = view.nodeDOM(pos);
1974
+ const c = (((_a = dom === null || dom === void 0 ? void 0 : dom.closest) === null || _a === void 0 ? void 0 : _a.call(dom, '[data-resize-container]')) || dom);
1975
+ if (!c)
1976
+ return true;
1977
+ c.dataset.imageLayout = node.attrs.imageLayout || 'inline';
1978
+ // Neutralise any absolute positioning baked by the old fixed-page model.
1979
+ c.style.position = '';
1980
+ c.style.left = '';
1981
+ c.style.top = '';
1982
+ c.style.zIndex = '';
1983
+ return true;
1984
+ });
1985
+ }
1986
+ /** Reposition only the layout bar (used during drag for performance). */
1987
+ updateImageLayoutBarPosition() {
1988
+ if (!this.imgLayoutEl || !this.contentEl)
1989
+ return;
1990
+ const info = this.getSelectedImageInfo();
1991
+ if (!info)
1992
+ return;
1993
+ const containerRect = info.container.getBoundingClientRect();
1994
+ const contentRect = this.contentEl.getBoundingClientRect();
1995
+ const barWidth = this.imgLayoutEl.offsetWidth || 194;
1996
+ let leftPos = (containerRect.left + containerRect.width / 2) - contentRect.left + this.contentEl.scrollLeft - barWidth / 2;
1997
+ leftPos = Math.max(4, Math.min(leftPos, contentRect.width - barWidth - 4));
1998
+ const topPos = containerRect.bottom - contentRect.top + this.contentEl.scrollTop + 8;
1999
+ this.imgLayoutEl.style.left = `${leftPos}px`;
2000
+ this.imgLayoutEl.style.top = `${topPos}px`;
2001
+ }
2002
+ updateImageLayoutBar() {
2003
+ if (!this.imgLayoutEl || !this.editor)
2004
+ return;
2005
+ const info = this.getSelectedImageInfo();
2006
+ if (!info) {
2007
+ if (this.imgLayoutVisible) {
2008
+ this.imgLayoutEl.style.display = 'none';
2009
+ this.imgLayoutVisible = false;
2010
+ }
2011
+ return;
2012
+ }
2013
+ this.imgLayoutEl.style.display = 'flex';
2014
+ this.imgLayoutVisible = true;
2015
+ this.updateImageLayoutBarPosition();
2016
+ const currentLayout = info.node.attrs.imageLayout || 'inline';
2017
+ this.imgLayoutEl.querySelectorAll('.co-rte-img-layout__btn').forEach(btn => {
2018
+ btn.classList.toggle('co-rte-img-layout__btn--active', btn.getAttribute('data-layout') === currentLayout);
2019
+ });
2020
+ }
209
2021
  render() {
210
2022
  const showToolbar = this.resolvedToolbar !== 'none' && !this.readonly && !this.disabled;
211
2023
  const showCount = this.showCharacterCount || this.maxLength != null;
212
- return (h("div", { key: 'c853d865716114025949e33e011394d824e4a961', class: {
2024
+ return (h("div", { key: '1ba222ae8d8e46a45619685771253cdd3d1befd3', class: {
213
2025
  'co-rich-text-editor': true,
214
2026
  'co-rich-text-editor--focused': this.hasFocus,
215
2027
  'co-rich-text-editor--disabled': this.disabled,
216
2028
  'co-rich-text-editor--readonly': this.readonly,
217
2029
  'co-rich-text-editor--loading': !this.ready,
218
2030
  [`co-rich-text-editor--${this.status}`]: this.status !== 'default',
219
- }, part: "wrapper" }, (this.label || this.secondaryLabel) && (h("div", { key: '049a14875b801e745c91f28410474d8139a366e6', class: "co-rich-text-editor__labels" }, this.label && h("label", { key: '4221652dd84a86192d84ba8889eafbd311ac27dd', class: "co-rich-text-editor__label", part: "label" }, this.label, h(RequiredMark, { key: '6576f8bdd8c744f6f4fd2990e3c3b58806a03bd4', required: this.required })), this.secondaryLabel && (h("span", { key: '5c28820cf466f06900ae3981b780f6664a14ca08', class: "co-rich-text-editor__secondary-label" }, this.secondaryLabel)))), h("div", { key: '94557b815f4dd9bfd33f2d4ae613554a54a391ad', class: "co-rich-text-editor__box" }, showToolbar && (h("co-rich-text-toolbar", { key: '7727dc54776263c3ef56f274e09d7d851df245d8', class: "co-rich-text-editor__toolbar", part: "toolbar", variant: this.resolvedToolbar, disabled: this.disabled || this.readonly, ref: (el) => (this.toolbarEl = el) })), h("div", { key: 'd81af8d3887e1f77062eb64efe02cda157f77216', class: "co-rte-content", part: "content", role: "textbox", "aria-multiline": "true", "aria-label": this.label, "aria-required": this.required ? 'true' : undefined, "aria-invalid": this.status === 'error' ? 'true' : undefined, ref: (el) => (this.contentEl = el) })), h("div", { key: 'f747db5469b5a805e88101cb7670432dc114c30f', class: "co-rich-text-editor__footer" }, this.helperText && (h("div", { key: '80c703179448310e86a16e908cc29edfc73260ba', class: `co-rich-text-editor__helper co-rich-text-editor__helper--${this.status}`, part: "helper" }, h("co-icon", { key: '7411c41c32e7c1845983df2608f604f764585230', name: this.helperIcon, size: 14 }), h("span", { key: 'be108ca9544684e2d6d0dbf68eca65d11f3c841b' }, this.helperText))), showCount && (h("span", { key: '5c88aeb55460778024664510a58ca9d331ef10c7', class: "co-rich-text-editor__count" }, this.charCount, this.maxLength != null ? ` / ${this.maxLength}` : '')))));
2031
+ }, part: "wrapper" }, (this.label || this.secondaryLabel) && (h("div", { key: '3bb65d173c680c2b8581aeeacdf9e94045378c43', class: "co-rich-text-editor__labels" }, this.label && h("label", { key: 'a8c743e49b37c486e80fe0227bf1d7954ade234f', class: "co-rich-text-editor__label", part: "label" }, this.label), this.secondaryLabel && (h("span", { key: 'a0d33d71573bd06d4c2b568973144ddd22424362', class: "co-rich-text-editor__secondary-label" }, this.secondaryLabel)))), h("div", { key: '39d4776d45ca2a616b64c9aab1eb15a99ae916ce', class: "co-rich-text-editor__frame" }, showToolbar && (h("co-rich-text-toolbar", { key: '80844f77a0a1aadd7a9b109110e99d218c98542c', class: "co-rich-text-editor__toolbar", part: "toolbar", variant: this.resolvedToolbar, disabled: this.disabled || this.readonly, locale: this.locale, ref: (el) => (this.toolbarEl = el) })), h("div", { key: 'f60519b8253b6070f6ac0f0a292e0402075f401a', class: "co-rich-text-editor__box" }, h("div", { key: '858aaa1ee60e6ed5142c72831d4b5f321b77132e', class: "co-rte-content", part: "content", role: "textbox", "aria-multiline": "true", "aria-label": this.label, "aria-required": this.required ? 'true' : undefined, "aria-invalid": this.status === 'error' ? 'true' : undefined, ref: (el) => (this.contentEl = el) }))), h("div", { key: '320d390eccc2fd2381c26568241e2ebb335a6aa2', class: "co-rich-text-editor__footer" }, this.helperText && (h("div", { key: 'de5d9e8605e3a8edd037e9e49884a55f641b4585', class: `co-rich-text-editor__helper co-rich-text-editor__helper--${this.status}`, part: "helper" }, h("co-icon", { key: 'ef7d3a8894a084e09e692839800bf8f1eb9c9b3f', name: this.helperIcon, size: 14 }), h("span", { key: 'a914b24fce4d891f7a7936051e04fa2f9dae6d62' }, this.helperText))), showCount && (h("span", { key: 'f9d197782e4e983b72f7432dd9b2fb8324f45a0b', class: "co-rich-text-editor__count", ref: (el) => (this.charCountEl = el) }, this.charCount, this.maxLength != null ? ` / ${this.maxLength}` : '')))));
220
2032
  }
221
2033
  static get is() { return "co-rich-text-editor"; }
222
2034
  static get originalStyleUrls() {
@@ -355,26 +2167,6 @@ export class CoRichTextEditor {
355
2167
  "reflect": false,
356
2168
  "attribute": "helper-text"
357
2169
  },
358
- "layout": {
359
- "type": "string",
360
- "mutable": false,
361
- "complexType": {
362
- "original": "'vertical' | 'horizontal'",
363
- "resolved": "\"horizontal\" | \"vertical\"",
364
- "references": {}
365
- },
366
- "required": false,
367
- "optional": false,
368
- "docs": {
369
- "tags": [],
370
- "text": "Label layout: vertical (label above) or horizontal (label beside)"
371
- },
372
- "getter": false,
373
- "setter": false,
374
- "reflect": true,
375
- "attribute": "layout",
376
- "defaultValue": "'vertical'"
377
- },
378
2170
  "status": {
379
2171
  "type": "string",
380
2172
  "mutable": false,
@@ -602,14 +2394,112 @@ export class CoRichTextEditor {
602
2394
  },
603
2395
  "getter": false,
604
2396
  "setter": false
2397
+ },
2398
+ "fontFamilies": {
2399
+ "type": "string",
2400
+ "mutable": false,
2401
+ "complexType": {
2402
+ "original": "string | { label: string; value: string }[]",
2403
+ "resolved": "string | undefined | { label: string; value: string; }[]",
2404
+ "references": {}
2405
+ },
2406
+ "required": false,
2407
+ "optional": true,
2408
+ "docs": {
2409
+ "tags": [],
2410
+ "text": "Font families available in the bubble menu font selector.\nJS property: `Array<{ label: string; value: string }>`.\nHTML attribute: JSON string of the same array.\nIf not provided, a default list of system fonts is used."
2411
+ },
2412
+ "getter": false,
2413
+ "setter": false,
2414
+ "reflect": false,
2415
+ "attribute": "font-families"
2416
+ },
2417
+ "accountFonts": {
2418
+ "type": "string",
2419
+ "mutable": false,
2420
+ "complexType": {
2421
+ "original": "string | { label: string; value: string }[]",
2422
+ "resolved": "string | undefined | { label: string; value: string; }[]",
2423
+ "references": {}
2424
+ },
2425
+ "required": false,
2426
+ "optional": true,
2427
+ "docs": {
2428
+ "tags": [],
2429
+ "text": "Account/imported fonts shown in a separate \"Imported fonts\" section.\nJS property: `Array<{ label: string; value: string }>`.\nHTML attribute: JSON string of the same array."
2430
+ },
2431
+ "getter": false,
2432
+ "setter": false,
2433
+ "reflect": false,
2434
+ "attribute": "account-fonts"
2435
+ },
2436
+ "fontsLoading": {
2437
+ "type": "boolean",
2438
+ "mutable": false,
2439
+ "complexType": {
2440
+ "original": "boolean",
2441
+ "resolved": "boolean | undefined",
2442
+ "references": {}
2443
+ },
2444
+ "required": false,
2445
+ "optional": true,
2446
+ "docs": {
2447
+ "tags": [],
2448
+ "text": "Whether account fonts are currently loading."
2449
+ },
2450
+ "getter": false,
2451
+ "setter": false,
2452
+ "reflect": false,
2453
+ "attribute": "fonts-loading"
2454
+ },
2455
+ "fontLoader": {
2456
+ "type": "unknown",
2457
+ "mutable": false,
2458
+ "complexType": {
2459
+ "original": "() => Promise<{ label: string; value: string; url?: string }[]>",
2460
+ "resolved": "(() => Promise<{ label: string; value: string; url?: string | undefined; }[]>) | undefined",
2461
+ "references": {
2462
+ "Promise": {
2463
+ "location": "global",
2464
+ "id": "global::Promise"
2465
+ }
2466
+ }
2467
+ },
2468
+ "required": false,
2469
+ "optional": true,
2470
+ "docs": {
2471
+ "tags": [],
2472
+ "text": "Async callback that loads account/imported fonts.\nCalled once when the font dropdown opens for the first time.\nMust return `Array<{ label: string; value: string; url?: string }>`.\nIf `url` is provided, the font is registered via FontFace API.\nJS property only \u2014 cannot be set via HTML attribute."
2473
+ },
2474
+ "getter": false,
2475
+ "setter": false
2476
+ },
2477
+ "locale": {
2478
+ "type": "string",
2479
+ "mutable": false,
2480
+ "complexType": {
2481
+ "original": "string",
2482
+ "resolved": "string",
2483
+ "references": {}
2484
+ },
2485
+ "required": false,
2486
+ "optional": false,
2487
+ "docs": {
2488
+ "tags": [],
2489
+ "text": "BCP-47 locale for UI strings (e.g. 'en', 'es-CO', 'pt-BR'). Default English."
2490
+ },
2491
+ "getter": false,
2492
+ "setter": false,
2493
+ "reflect": false,
2494
+ "attribute": "locale",
2495
+ "defaultValue": "'en'"
605
2496
  }
606
2497
  };
607
2498
  }
608
2499
  static get states() {
609
2500
  return {
610
2501
  "ready": {},
611
- "hasFocus": {},
612
- "charCount": {}
2502
+ "hasFocus": {}
613
2503
  };
614
2504
  }
615
2505
  static get events() {
@@ -717,6 +2607,36 @@ export class CoRichTextEditor {
717
2607
  "resolved": "void",
718
2608
  "references": {}
719
2609
  }
2610
+ }, {
2611
+ "method": "coFontPopupShow",
2612
+ "name": "coFontPopupShow",
2613
+ "bubbles": true,
2614
+ "cancelable": true,
2615
+ "composed": true,
2616
+ "docs": {
2617
+ "tags": [],
2618
+ "text": "Emitted when the font dropdown opens \u2014 host should load account fonts."
2619
+ },
2620
+ "complexType": {
2621
+ "original": "void",
2622
+ "resolved": "void",
2623
+ "references": {}
2624
+ }
2625
+ }, {
2626
+ "method": "coFontPopupHide",
2627
+ "name": "coFontPopupHide",
2628
+ "bubbles": true,
2629
+ "cancelable": true,
2630
+ "composed": true,
2631
+ "docs": {
2632
+ "tags": [],
2633
+ "text": "Emitted when the font dropdown closes."
2634
+ },
2635
+ "complexType": {
2636
+ "original": "void",
2637
+ "resolved": "void",
2638
+ "references": {}
2639
+ }
720
2640
  }];
721
2641
  }
722
2642
  static get methods() {
@@ -855,6 +2775,9 @@ export class CoRichTextEditor {
855
2775
  static get elementRef() { return "el"; }
856
2776
  static get watchers() {
857
2777
  return [{
2778
+ "propName": "accountFonts",
2779
+ "methodName": "onAccountFontsChange"
2780
+ }, {
858
2781
  "propName": "value",
859
2782
  "methodName": "onValueChange"
860
2783
  }, {
@@ -866,3 +2789,35 @@ export class CoRichTextEditor {
866
2789
  }];
867
2790
  }
868
2791
  }
2792
+ CoRichTextEditor.DEFAULT_FONTS = [
2793
+ { label: 'Arial', value: 'Arial, sans-serif' },
2794
+ { label: 'Calibri', value: 'Calibri, sans-serif' },
2795
+ { label: 'Cambria', value: 'Cambria, serif' },
2796
+ { label: 'Comic Sans MS', value: "'Comic Sans MS', cursive" },
2797
+ { label: 'Courier New', value: "'Courier New', monospace" },
2798
+ { label: 'Georgia', value: 'Georgia, serif' },
2799
+ { label: 'Geist', value: "'Geist', sans-serif" },
2800
+ { label: 'Helvetica', value: 'Helvetica, Arial, sans-serif' },
2801
+ { label: 'Impact', value: 'Impact, sans-serif' },
2802
+ { label: 'Inter', value: "'Inter', sans-serif" },
2803
+ { label: 'Lato', value: "'Lato', sans-serif" },
2804
+ { label: 'Montserrat', value: "'Montserrat', sans-serif" },
2805
+ { label: 'Open Sans', value: "'Open Sans', sans-serif" },
2806
+ { label: 'Poppins', value: "'Poppins', sans-serif" },
2807
+ { label: 'Roboto', value: "'Roboto', sans-serif" },
2808
+ { label: 'Segoe UI', value: "'Segoe UI', sans-serif" },
2809
+ { label: 'Tahoma', value: 'Tahoma, sans-serif' },
2810
+ { label: 'Times New Roman', value: "'Times New Roman', serif" },
2811
+ { label: 'Trebuchet MS', value: "'Trebuchet MS', sans-serif" },
2812
+ { label: 'Verdana', value: 'Verdana, sans-serif' },
2813
+ ];
2814
+ /** Register a font via FontFace API so it can be previewed. */
2815
+ CoRichTextEditor._registeredFonts = new Set();
2816
+ /* ── Image layout bar (Google-Docs-style toolbar below selected image) ── */
2817
+ CoRichTextEditor.IMG_LAYOUT_ICONS = {
2818
+ 'inline': 'image-square',
2819
+ 'wrap-left': 'text-align-left',
2820
+ 'wrap-right': 'text-align-right',
2821
+ 'center': 'text-align-center',
2822
+ 'full-width': 'arrows-horizontal',
2823
+ };