@prolibu-suite/cobalt-core 0.2.0 → 0.3.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.
- package/custom-elements.json +15675 -11199
- package/dist/cjs/co-accordion.cjs.entry.js +3 -3
- package/dist/cjs/co-app-shell.cjs.entry.js +4 -4
- package/dist/cjs/co-autocomplete.cjs.entry.js +10 -7
- package/dist/cjs/co-avatar-group.cjs.entry.js +77 -0
- package/dist/cjs/co-avatar.cjs.entry.js +6 -4
- package/dist/cjs/co-badge.cjs.entry.js +2 -2
- package/dist/cjs/co-bottom-sheet.cjs.entry.js +325 -0
- package/dist/cjs/co-button-group.cjs.entry.js +3 -3
- package/dist/cjs/co-button.cjs.entry.js +6 -6
- package/dist/cjs/co-calendar-month.cjs.entry.js +381 -0
- package/dist/cjs/co-calendar_3.cjs.entry.js +11 -11
- package/dist/cjs/co-card.cjs.entry.js +3 -3
- package/dist/cjs/co-checkbox-group.cjs.entry.js +5 -5
- package/dist/cjs/co-checkbox.cjs.entry.js +5 -5
- package/dist/cjs/co-chip.cjs.entry.js +11 -8
- package/dist/cjs/co-color-picker.cjs.entry.js +7 -5
- package/dist/cjs/co-column-picker.cjs.entry.js +3 -3
- package/dist/cjs/co-combobox.cjs.entry.js +7 -7
- package/dist/cjs/co-command-search.cjs.entry.js +151 -12
- package/dist/cjs/co-data-table-pagination.cjs.entry.js +5 -5
- package/dist/cjs/co-data-table.cjs.entry.js +25 -11
- package/dist/cjs/co-date-picker.cjs.entry.js +2 -2
- package/dist/cjs/co-date-range-picker.cjs.entry.js +3 -3
- package/dist/cjs/co-date-range.cjs.entry.js +2 -2
- package/dist/cjs/co-empty-state.cjs.entry.js +2 -2
- package/dist/cjs/co-event-calendar.cjs.entry.js +318 -0
- package/dist/cjs/co-fab.cjs.entry.js +3 -3
- package/dist/cjs/co-file-field.cjs.entry.js +8 -8
- package/dist/cjs/co-helper-text.cjs.entry.js +3 -3
- package/dist/cjs/co-icon-button.cjs.entry.js +5 -5
- package/dist/cjs/co-icon.cjs.entry.js +33 -25
- package/dist/cjs/co-input.cjs.entry.js +6 -6
- package/dist/cjs/co-json-field.cjs.entry.js +10 -6
- package/dist/cjs/co-lightbox.cjs.entry.js +5 -5
- package/dist/cjs/co-list-footer.cjs.entry.js +25 -0
- package/dist/cjs/co-list-item.cjs.entry.js +60 -4
- package/dist/cjs/co-loader_2.cjs.entry.js +293 -0
- package/dist/cjs/co-menu-divider.cjs.entry.js +2 -2
- package/dist/cjs/co-menu-item.cjs.entry.js +17 -10
- package/dist/cjs/co-menu.cjs.entry.js +2 -2
- package/dist/cjs/co-modal.cjs.entry.js +16 -4
- package/dist/cjs/co-navbar.cjs.entry.js +3 -3
- package/dist/cjs/co-number-field.cjs.entry.js +18 -6
- package/dist/cjs/co-page-header.cjs.entry.js +2 -2
- package/dist/cjs/co-phone-field.cjs.entry.js +6 -4
- package/dist/cjs/co-popover.cjs.entry.js +45 -14
- package/dist/cjs/co-progress-bar.cjs.entry.js +8 -4
- package/dist/cjs/co-progress-circle.cjs.entry.js +3 -3
- package/dist/cjs/co-radio-group.cjs.entry.js +4 -4
- package/dist/cjs/co-radio.cjs.entry.js +4 -4
- package/dist/cjs/co-rating.cjs.entry.js +4 -4
- package/dist/cjs/co-ref-field.cjs.entry.js +36 -13
- package/dist/cjs/co-rich-text-editor.cjs.entry.js +1879 -33
- package/dist/cjs/co-rich-text-toolbar.cjs.entry.js +995 -71
- package/dist/cjs/co-rich-text-viewer.cjs.entry.js +55 -19
- package/dist/cjs/co-row.cjs.entry.js +3 -3
- package/dist/cjs/co-select.cjs.entry.js +8 -8
- package/dist/cjs/co-separator.cjs.entry.js +2 -2
- package/dist/cjs/co-sidebar-nav.cjs.entry.js +29 -9
- package/dist/cjs/co-sidebar.cjs.entry.js +3 -3
- package/dist/cjs/co-skeleton.cjs.entry.js +26 -0
- package/dist/cjs/co-slider.cjs.entry.js +3 -3
- package/dist/cjs/co-switch.cjs.entry.js +6 -6
- package/dist/cjs/co-tab-group.cjs.entry.js +2 -2
- package/dist/cjs/co-tab.cjs.entry.js +3 -3
- package/dist/cjs/co-tags-field.cjs.entry.js +19 -13
- package/dist/cjs/co-textarea.cjs.entry.js +11 -7
- package/dist/cjs/co-toast.cjs.entry.js +2 -2
- package/dist/cjs/co-toggle-button-group.cjs.entry.js +4 -4
- package/dist/cjs/co-toggle-button.cjs.entry.js +4 -4
- package/dist/cjs/co-tooltip.cjs.entry.js +51 -16
- package/dist/cjs/co-transfer-list.cjs.entry.js +3 -3
- package/dist/cjs/co-video.cjs.entry.js +839 -0
- package/dist/cjs/cobalt.cjs.js +2 -2
- package/dist/cjs/fieldChip-CDIMz_Y6.js +7 -0
- package/dist/cjs/{index-CaEJnpZe.js → index-5NdsiFjZ.js} +14509 -6317
- package/dist/cjs/{index-DoFnrUVH.js → index-BumlM92r.js} +45 -21
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/overlayAfterClose-BdeXrhVY.js +12 -0
- package/dist/{esm/sanitizer-DcK0GGZC.js → cjs/purify.es-DX41ZWS2.js} +3 -107
- package/dist/cjs/{requiredMark-DA6y-4rp.js → requiredMark-CqMGt1H1.js} +1 -1
- package/dist/cjs/richTextI18n-B7ktXg3M.js +382 -0
- package/dist/cjs/toolbar-config-3PlT-3zm.js +216 -0
- package/dist/cjs/tz-Dgom0W69.js +127 -0
- package/dist/cobalt/cobalt.css +1 -1
- package/dist/cobalt/cobalt.esm.js +1 -1
- package/dist/cobalt/p-0a506c9d.entry.js +1 -0
- package/dist/cobalt/p-0bb55163.entry.js +1 -0
- package/dist/cobalt/{p-6404bd21.entry.js → p-0e8c0d87.entry.js} +1 -1
- package/dist/cobalt/p-12e2c2c4.entry.js +1 -0
- package/dist/cobalt/p-136ad468.entry.js +1 -0
- package/dist/cobalt/{p-654bbc2a.entry.js → p-14b59277.entry.js} +1 -1
- package/dist/cobalt/p-1531d3c9.entry.js +1 -0
- package/dist/cobalt/p-1774d211.entry.js +1 -0
- package/dist/cobalt/p-180e1106.entry.js +1 -0
- package/dist/cobalt/p-1a07c23c.entry.js +1 -0
- package/dist/cobalt/p-1dc9c49e.entry.js +1 -0
- package/dist/cobalt/p-1f747e45.entry.js +1 -0
- package/dist/cobalt/p-201eaf35.entry.js +1 -0
- package/dist/cobalt/p-260101ab.entry.js +1 -0
- package/dist/cobalt/p-279bf91c.entry.js +1 -0
- package/dist/cobalt/p-29be61a9.entry.js +1 -0
- package/dist/cobalt/p-2d2ce434.entry.js +1 -0
- package/dist/cobalt/{p-aa63812a.entry.js → p-2f86e1e0.entry.js} +1 -1
- package/dist/cobalt/{p-522fcdb7.entry.js → p-31ab9a46.entry.js} +1 -1
- package/dist/cobalt/{p-fe9e85b6.entry.js → p-37595103.entry.js} +1 -1
- package/dist/cobalt/{p-def448e4.entry.js → p-377c2ccc.entry.js} +1 -1
- package/dist/cobalt/p-3863ea91.entry.js +1 -0
- package/dist/cobalt/p-3a257cb0.entry.js +1 -0
- package/dist/cobalt/p-3e187456.entry.js +1 -0
- package/dist/cobalt/{p-5d01bd4d.entry.js → p-4431faac.entry.js} +1 -1
- package/dist/cobalt/{p-54f26e95.entry.js → p-4504f7b0.entry.js} +1 -1
- package/dist/cobalt/{p-f8728b21.entry.js → p-456fa86f.entry.js} +1 -1
- package/dist/cobalt/{p-3f9b3fcb.entry.js → p-4871690e.entry.js} +1 -1
- package/dist/cobalt/{p-d17e1444.entry.js → p-4c01d984.entry.js} +1 -1
- package/dist/cobalt/p-51a53177.entry.js +1 -0
- package/dist/cobalt/p-52de17da.entry.js +1 -0
- package/dist/cobalt/p-5890e4d1.entry.js +1 -0
- package/dist/cobalt/{p-f2c5cb74.entry.js → p-58a53421.entry.js} +1 -1
- package/dist/cobalt/p-62274eba.entry.js +1 -0
- package/dist/cobalt/p-635b03d5.entry.js +1 -0
- package/dist/cobalt/p-64ddee92.entry.js +1 -0
- package/dist/cobalt/p-65af5017.entry.js +1 -0
- package/dist/cobalt/p-69d4c0c8.entry.js +1 -0
- package/dist/cobalt/p-6c480916.entry.js +1 -0
- package/dist/cobalt/p-6d1217c3.entry.js +1 -0
- package/dist/cobalt/{p-f0e8fa7d.entry.js → p-6e808b3a.entry.js} +1 -1
- package/dist/cobalt/{p-f97ad9ad.entry.js → p-70b14b05.entry.js} +1 -1
- package/dist/cobalt/{p-97c20011.entry.js → p-790d384e.entry.js} +1 -1
- package/dist/cobalt/p-8012e152.entry.js +1 -0
- package/dist/cobalt/p-80b27203.entry.js +1 -0
- package/dist/cobalt/p-867c1465.entry.js +1 -0
- package/dist/cobalt/p-86a143d7.entry.js +1 -0
- package/dist/cobalt/{p-e8b58332.entry.js → p-8d59f5ad.entry.js} +1 -1
- package/dist/cobalt/p-8d7fdb9d.entry.js +1 -0
- package/dist/cobalt/{p-7f818368.entry.js → p-8dc1dab8.entry.js} +1 -1
- package/dist/cobalt/{p-beb9c11d.entry.js → p-8e1dc062.entry.js} +1 -1
- package/dist/cobalt/p-929e647f.entry.js +1 -0
- package/dist/cobalt/p-92bd9b29.entry.js +1 -0
- package/dist/cobalt/p-93046365.entry.js +1 -0
- package/dist/cobalt/p-98ab5f3f.entry.js +1 -0
- package/dist/cobalt/{p-c87285b6.entry.js → p-9a04ca90.entry.js} +1 -1
- package/dist/cobalt/{p-7ef766c6.entry.js → p-9a088515.entry.js} +1 -1
- package/dist/cobalt/p-9abbd436.entry.js +1 -0
- package/dist/cobalt/p-9d69c067.entry.js +1 -0
- package/dist/cobalt/p-B-ckjCUA.js +1 -0
- package/dist/cobalt/p-B2QLgWrz.js +2 -0
- package/dist/cobalt/p-B6JZk8fL.js +1 -0
- package/dist/cobalt/p-BZJ7NCm5.js +1 -0
- package/dist/cobalt/p-CYsftTKf.js +2 -0
- package/dist/cobalt/p-CegCk7FL.js +1 -0
- package/dist/cobalt/p-Ch1jfwZ2.js +1 -0
- package/dist/cobalt/p-ChibIpZv.js +1 -0
- package/dist/cobalt/p-afb7e19e.entry.js +1 -0
- package/dist/cobalt/{p-b758c2e6.entry.js → p-b6f20268.entry.js} +1 -1
- package/dist/cobalt/{p-bb07da22.entry.js → p-ba90e871.entry.js} +1 -1
- package/dist/cobalt/p-c10697e1.entry.js +1 -0
- package/dist/cobalt/p-c4f59532.entry.js +1 -0
- package/dist/cobalt/{p-1e0282b8.entry.js → p-cb235aa7.entry.js} +1 -1
- package/dist/cobalt/p-cfb36943.entry.js +1 -0
- package/dist/cobalt/p-d809bd44.entry.js +1 -0
- package/dist/cobalt/p-e53861e4.entry.js +1 -0
- package/dist/cobalt/p-e54dd404.entry.js +1 -0
- package/dist/cobalt/{p-01fc77c4.entry.js → p-eb18fdbf.entry.js} +1 -1
- package/dist/cobalt/{p-95d14878.entry.js → p-eb73052a.entry.js} +1 -1
- package/dist/cobalt/{p-273afd93.entry.js → p-ef7d4ae3.entry.js} +1 -1
- package/dist/cobalt/p-f4063c41.entry.js +1 -0
- package/dist/cobalt/p-oImf-7fN.js +1 -0
- package/dist/cobalt/{p-CyYF8IJE.js → p-tPYikBRJ.js} +1 -1
- package/dist/collection/collection-manifest.json +10 -3
- package/dist/collection/components/co-accordion/co-accordion.js +2 -2
- package/dist/collection/components/co-app-shell/co-app-shell.css +79 -6
- package/dist/collection/components/co-app-shell/co-app-shell.js +2 -2
- package/dist/collection/components/co-autocomplete/co-autocomplete.css +28 -1
- package/dist/collection/components/co-autocomplete/co-autocomplete.js +27 -4
- package/dist/collection/components/co-avatar/co-avatar.css +38 -1
- package/dist/collection/components/co-avatar/co-avatar.js +23 -2
- package/dist/collection/components/co-avatar-group/co-avatar-group.css +86 -0
- package/dist/collection/components/co-avatar-group/co-avatar-group.js +224 -0
- package/dist/collection/components/co-badge/co-badge.js +1 -1
- package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.css +136 -0
- package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.js +507 -0
- package/dist/collection/components/co-button/co-button.css +40 -7
- package/dist/collection/components/co-button/co-button.js +26 -7
- package/dist/collection/components/co-button-group/co-button-group.js +2 -2
- package/dist/collection/components/co-calendar/co-calendar.js +1 -1
- package/dist/collection/components/co-calendar-month/co-calendar-month.css +231 -0
- package/dist/collection/components/co-calendar-month/co-calendar-month.js +594 -0
- package/dist/collection/components/co-card/co-card.js +2 -2
- package/dist/collection/components/co-checkbox/co-checkbox.css +7 -1
- package/dist/collection/components/co-checkbox/co-checkbox.js +3 -3
- package/dist/collection/components/co-checkbox-group/co-checkbox-group.css +7 -1
- package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +3 -3
- package/dist/collection/components/co-chip/co-chip.css +27 -7
- package/dist/collection/components/co-chip/co-chip.js +52 -10
- package/dist/collection/components/co-color-picker/co-color-picker.css +35 -1
- package/dist/collection/components/co-color-picker/co-color-picker.js +24 -2
- package/dist/collection/components/co-column-picker/co-column-picker.js +2 -2
- package/dist/collection/components/co-combobox/co-combobox.css +7 -47
- package/dist/collection/components/co-combobox/co-combobox.js +4 -4
- package/dist/collection/components/co-command-search/co-command-search.css +163 -1
- package/dist/collection/components/co-command-search/co-command-search.js +355 -14
- package/dist/collection/components/co-command-search/commandSearchGroup.js +17 -0
- package/dist/collection/components/co-command-search/commandSearchLogic.js +27 -0
- package/dist/collection/components/co-data-table/co-data-table.css +37 -1
- package/dist/collection/components/co-data-table/co-data-table.js +25 -11
- package/dist/collection/components/co-data-table-pagination/co-data-table-pagination.js +4 -4
- package/dist/collection/components/co-date-field/co-date-field.css +7 -1
- package/dist/collection/components/co-date-field/co-date-field.js +4 -4
- package/dist/collection/components/co-date-range/co-date-range.js +1 -1
- package/dist/collection/components/co-date-range-picker/co-date-range-picker.css +7 -1
- package/dist/collection/components/co-drawer/co-drawer.css +1 -0
- package/dist/collection/components/co-drawer/co-drawer.js +2 -2
- package/dist/collection/components/co-empty-state/co-empty-state.js +1 -1
- package/dist/collection/components/co-event-calendar/co-event-calendar.css +146 -0
- package/dist/collection/components/co-event-calendar/co-event-calendar.js +620 -0
- package/dist/collection/components/co-fab/co-fab.js +2 -2
- package/dist/collection/components/co-file-field/co-file-field.css +7 -1
- package/dist/collection/components/co-file-field/co-file-field.js +5 -5
- package/dist/collection/components/co-helper-text/co-helper-text.js +2 -2
- package/dist/collection/components/co-icon/co-icon.js +7 -37
- package/dist/collection/components/co-icon/iconLoader.js +39 -0
- package/dist/collection/components/co-icon-button/co-icon-button.css +19 -8
- package/dist/collection/components/co-icon-button/co-icon-button.js +23 -4
- package/dist/collection/components/co-input/co-input.css +7 -1
- package/dist/collection/components/co-input/co-input.js +3 -3
- package/dist/collection/components/co-json-field/co-json-field.css +25 -1
- package/dist/collection/components/co-json-field/co-json-field.js +27 -3
- package/dist/collection/components/co-lightbox/co-lightbox.js +4 -4
- package/dist/collection/components/co-list-footer/co-list-footer.css +50 -0
- package/dist/collection/components/co-list-footer/co-list-footer.js +149 -0
- package/dist/collection/components/co-list-item/co-list-item.css +7 -0
- package/dist/collection/components/co-list-item/co-list-item.js +65 -2
- package/dist/collection/components/co-loader/co-loader.css +9 -0
- package/dist/collection/components/co-menu/co-menu.js +1 -1
- package/dist/collection/components/co-menu-divider/co-menu-divider.js +1 -1
- package/dist/collection/components/co-menu-item/co-menu-item.css +18 -0
- package/dist/collection/components/co-menu-item/co-menu-item.js +15 -8
- package/dist/collection/components/co-modal/co-modal.css +42 -2
- package/dist/collection/components/co-modal/co-modal.js +42 -2
- package/dist/collection/components/co-navbar/co-navbar.js +2 -2
- package/dist/collection/components/co-number-field/co-number-field.css +19 -1
- package/dist/collection/components/co-number-field/co-number-field.js +34 -3
- package/dist/collection/components/co-overlay/co-overlay.css +15 -1
- package/dist/collection/components/co-overlay/co-overlay.js +266 -1
- package/dist/collection/components/co-overlay/focusTrap.js +53 -0
- package/dist/collection/components/co-overlay/overlayAfterClose.js +8 -0
- package/dist/collection/components/co-page-header/co-page-header.js +1 -1
- package/dist/collection/components/co-phone-field/co-phone-field.css +30 -2
- package/dist/collection/components/co-phone-field/co-phone-field.js +23 -1
- package/dist/collection/components/co-popover/co-popover.css +9 -0
- package/dist/collection/components/co-popover/co-popover.js +43 -12
- package/dist/collection/components/co-progress-bar/co-progress-bar.css +22 -0
- package/dist/collection/components/co-progress-bar/co-progress-bar.js +26 -2
- package/dist/collection/components/co-progress-circle/co-progress-circle.js +2 -2
- package/dist/collection/components/co-radio/co-radio.js +3 -3
- package/dist/collection/components/co-radio-group/co-radio-group.js +3 -3
- package/dist/collection/components/co-rating/co-rating.js +3 -3
- package/dist/collection/components/co-ref-field/co-ref-field.css +64 -6
- package/dist/collection/components/co-ref-field/co-ref-field.js +0 -0
- package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.css +1125 -41
- package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.js +2008 -53
- package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.css +791 -9
- package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.js +1071 -71
- package/dist/collection/components/co-rich-text-viewer/co-rich-text-viewer.css +78 -0
- package/dist/collection/components/co-rich-text-viewer/co-rich-text-viewer.js +46 -41
- package/dist/collection/components/co-row/co-row.js +2 -2
- package/dist/collection/components/co-select/co-select.css +7 -1
- package/dist/collection/components/co-select/co-select.js +5 -5
- package/dist/collection/components/co-separator/co-separator.js +1 -1
- package/dist/collection/components/co-sidebar/co-sidebar.js +2 -2
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.css +99 -12
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +28 -8
- package/dist/collection/components/co-skeleton/co-skeleton.css +28 -0
- package/dist/collection/components/co-skeleton/co-skeleton.js +97 -0
- package/dist/collection/components/co-slider/co-slider.js +2 -2
- package/dist/collection/components/co-switch/co-switch.css +9 -1
- package/dist/collection/components/co-switch/co-switch.js +3 -3
- package/dist/collection/components/co-tab/co-tab.js +2 -2
- package/dist/collection/components/co-tab-group/co-tab-group.js +1 -1
- package/dist/collection/components/co-tags-field/co-tags-field.css +39 -1
- package/dist/collection/components/co-tags-field/co-tags-field.js +36 -10
- package/dist/collection/components/co-textarea/co-textarea.css +26 -1
- package/dist/collection/components/co-textarea/co-textarea.js +28 -4
- package/dist/collection/components/co-toast/co-toast.js +1 -1
- package/dist/collection/components/co-toggle-button/co-toggle-button.css +26 -4
- package/dist/collection/components/co-toggle-button/co-toggle-button.js +3 -3
- package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.css +16 -12
- package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.js +2 -2
- package/dist/collection/components/co-tooltip/co-tooltip.css +8 -0
- package/dist/collection/components/co-tooltip/co-tooltip.js +69 -14
- package/dist/collection/components/co-transfer-list/co-transfer-list.js +2 -2
- package/dist/collection/components/co-video/co-video.css +497 -0
- package/dist/collection/components/co-video/co-video.js +1772 -0
- package/dist/collection/utils/fieldChip.js +2 -0
- package/dist/collection/utils/icon-config.js +12 -0
- package/dist/collection/utils/richTextI18n.js +378 -0
- package/dist/collection/utils/sheetGesture.js +53 -0
- package/dist/components/co-accordion.js +1 -1
- package/dist/components/co-app-shell.js +1 -1
- package/dist/components/co-autocomplete.js +1 -1
- package/dist/components/co-avatar-group.d.ts +11 -0
- package/dist/components/co-avatar-group.js +1 -0
- package/dist/components/co-avatar.js +1 -1
- package/dist/components/co-badge.js +1 -1
- package/dist/components/co-bottom-sheet.d.ts +11 -0
- package/dist/components/co-bottom-sheet.js +1 -0
- package/dist/components/co-button-group.js +1 -1
- package/dist/components/co-button.js +1 -1
- package/dist/components/co-calendar-month.d.ts +11 -0
- package/dist/components/co-calendar-month.js +1 -0
- package/dist/components/co-calendar.js +1 -1
- package/dist/components/co-card.js +1 -1
- package/dist/components/co-checkbox-group.js +1 -1
- package/dist/components/co-checkbox.js +1 -1
- package/dist/components/co-chip.js +1 -1
- package/dist/components/co-color-picker.js +1 -1
- package/dist/components/co-column-picker.js +1 -1
- package/dist/components/co-combobox.js +1 -1
- package/dist/components/co-command-search.js +1 -1
- package/dist/components/co-data-table-pagination.js +1 -1
- package/dist/components/co-data-table.js +1 -1
- package/dist/components/co-date-field.js +1 -1
- package/dist/components/co-date-picker.js +1 -1
- package/dist/components/co-date-range-picker.js +1 -1
- package/dist/components/co-date-range.js +1 -1
- package/dist/components/co-drawer.js +1 -1
- package/dist/components/co-empty-state.js +1 -1
- package/dist/components/co-event-calendar.d.ts +11 -0
- package/dist/components/co-event-calendar.js +1 -0
- package/dist/components/co-fab.js +1 -1
- package/dist/components/co-file-field.js +1 -1
- package/dist/components/co-helper-text.js +1 -1
- package/dist/components/co-icon-button.js +1 -1
- package/dist/components/co-icon.js +1 -1
- package/dist/components/co-input.js +1 -1
- package/dist/components/co-json-field.js +1 -1
- package/dist/components/co-lightbox.js +1 -1
- package/dist/components/co-list-footer.d.ts +11 -0
- package/dist/components/co-list-footer.js +1 -0
- package/dist/components/co-list-item.js +1 -1
- package/dist/components/co-loader.js +1 -1
- package/dist/components/co-menu-divider.js +1 -1
- package/dist/components/co-menu-item.js +1 -1
- package/dist/components/co-menu.js +1 -1
- package/dist/components/co-modal.js +1 -1
- package/dist/components/co-navbar.js +1 -1
- package/dist/components/co-number-field.js +1 -1
- package/dist/components/co-overlay.js +1 -1
- package/dist/components/co-page-header.js +1 -1
- package/dist/components/co-phone-field.js +1 -1
- package/dist/components/co-popover.js +1 -1
- package/dist/components/co-progress-bar.js +1 -1
- package/dist/components/co-progress-circle.js +1 -1
- package/dist/components/co-radio-group.js +1 -1
- package/dist/components/co-radio.js +1 -1
- package/dist/components/co-rating.js +1 -1
- package/dist/components/co-ref-field.js +1 -1
- package/dist/components/co-rich-text-editor.js +1 -1
- package/dist/components/co-rich-text-toolbar.js +1 -1
- package/dist/components/co-rich-text-viewer.js +1 -1
- package/dist/components/co-row.js +1 -1
- package/dist/components/co-select.js +1 -1
- package/dist/components/co-separator.js +1 -1
- package/dist/components/co-sidebar-nav.js +1 -1
- package/dist/components/co-sidebar.js +1 -1
- package/dist/components/co-skeleton.d.ts +11 -0
- package/dist/components/co-skeleton.js +1 -0
- package/dist/components/co-slider.js +1 -1
- package/dist/components/co-switch.js +1 -1
- package/dist/components/co-tab-group.js +1 -1
- package/dist/components/co-tab.js +1 -1
- package/dist/components/co-tags-field.js +1 -1
- package/dist/components/co-textarea.js +1 -1
- package/dist/components/co-toast.js +1 -1
- package/dist/components/co-toggle-button-group.js +1 -1
- package/dist/components/co-toggle-button.js +1 -1
- package/dist/components/co-tooltip.js +1 -1
- package/dist/components/co-transfer-list.js +1 -1
- package/dist/components/co-video.d.ts +11 -0
- package/dist/components/co-video.js +1 -0
- package/dist/components/index.js +1 -1
- package/dist/components/p-B2QLgWrz.js +2 -0
- package/dist/components/{p-BeWXpX8I.js → p-B7vGYFbO.js} +1 -1
- package/dist/components/p-BB2OaYKF.js +1 -0
- package/dist/components/p-BFeE_fiq.js +1 -0
- package/dist/components/p-BNKz39GA.js +1 -0
- package/dist/components/p-Ba4v1NXo.js +1 -0
- package/dist/components/p-C_ULDaks.js +1 -0
- package/dist/components/p-CegCk7FL.js +1 -0
- package/dist/components/p-ChibIpZv.js +1 -0
- package/dist/components/p-D3Y_ti3Q.js +1 -0
- package/dist/components/{p-DObrQi5F.js → p-DKh8ZK4U.js} +1 -1
- package/dist/components/{p-C9tHLXKS.js → p-DeZxp9vb.js} +1 -1
- package/dist/components/p-DvhR3H61.js +1 -0
- package/dist/components/p-DxCFMMfY.js +1 -0
- package/dist/components/{p-OGpfa9Yd.js → p-DzoNGEDU.js} +1 -1
- package/dist/components/p-EPZfLt5l.js +1 -0
- package/dist/components/p-FMnqPWcM.js +1 -0
- package/dist/components/p-Hzh9U2cM.js +1 -0
- package/dist/components/p-WZ06pT2Y.js +1 -0
- package/dist/components/p-tloAgDin.js +1 -0
- package/dist/esm/co-accordion.entry.js +3 -3
- package/dist/esm/co-app-shell.entry.js +4 -4
- package/dist/esm/co-autocomplete.entry.js +10 -7
- package/dist/esm/co-avatar-group.entry.js +75 -0
- package/dist/esm/co-avatar.entry.js +6 -4
- package/dist/esm/co-badge.entry.js +2 -2
- package/dist/esm/co-bottom-sheet.entry.js +323 -0
- package/dist/esm/co-button-group.entry.js +3 -3
- package/dist/esm/co-button.entry.js +6 -6
- package/dist/esm/co-calendar-month.entry.js +379 -0
- package/dist/esm/co-calendar_3.entry.js +12 -12
- package/dist/esm/co-card.entry.js +3 -3
- package/dist/esm/co-checkbox-group.entry.js +5 -5
- package/dist/esm/co-checkbox.entry.js +5 -5
- package/dist/esm/co-chip.entry.js +11 -8
- package/dist/esm/co-color-picker.entry.js +7 -5
- package/dist/esm/co-column-picker.entry.js +3 -3
- package/dist/esm/co-combobox.entry.js +7 -7
- package/dist/esm/co-command-search.entry.js +151 -12
- package/dist/esm/co-data-table-pagination.entry.js +5 -5
- package/dist/esm/co-data-table.entry.js +25 -11
- package/dist/esm/co-date-picker.entry.js +2 -2
- package/dist/esm/co-date-range-picker.entry.js +4 -4
- package/dist/esm/co-date-range.entry.js +2 -2
- package/dist/esm/co-empty-state.entry.js +2 -2
- package/dist/esm/co-event-calendar.entry.js +316 -0
- package/dist/esm/co-fab.entry.js +3 -3
- package/dist/esm/co-file-field.entry.js +8 -8
- package/dist/esm/co-helper-text.entry.js +3 -3
- package/dist/esm/co-icon-button.entry.js +5 -5
- package/dist/esm/co-icon.entry.js +33 -25
- package/dist/esm/co-input.entry.js +6 -6
- package/dist/esm/co-json-field.entry.js +10 -6
- package/dist/esm/co-lightbox.entry.js +5 -5
- package/dist/esm/co-list-footer.entry.js +23 -0
- package/dist/esm/co-list-item.entry.js +60 -4
- package/dist/esm/co-loader_2.entry.js +290 -0
- package/dist/esm/co-menu-divider.entry.js +2 -2
- package/dist/esm/co-menu-item.entry.js +17 -10
- package/dist/esm/co-menu.entry.js +2 -2
- package/dist/esm/co-modal.entry.js +16 -4
- package/dist/esm/co-navbar.entry.js +3 -3
- package/dist/esm/co-number-field.entry.js +18 -6
- package/dist/esm/co-page-header.entry.js +2 -2
- package/dist/esm/co-phone-field.entry.js +6 -4
- package/dist/esm/co-popover.entry.js +45 -14
- package/dist/esm/co-progress-bar.entry.js +8 -4
- package/dist/esm/co-progress-circle.entry.js +3 -3
- package/dist/esm/co-radio-group.entry.js +4 -4
- package/dist/esm/co-radio.entry.js +4 -4
- package/dist/esm/co-rating.entry.js +4 -4
- package/dist/esm/co-ref-field.entry.js +36 -13
- package/dist/esm/co-rich-text-editor.entry.js +1879 -33
- package/dist/esm/co-rich-text-toolbar.entry.js +995 -71
- package/dist/esm/co-rich-text-viewer.entry.js +55 -19
- package/dist/esm/co-row.entry.js +3 -3
- package/dist/esm/co-select.entry.js +8 -8
- package/dist/esm/co-separator.entry.js +2 -2
- package/dist/esm/co-sidebar-nav.entry.js +29 -9
- package/dist/esm/co-sidebar.entry.js +3 -3
- package/dist/esm/co-skeleton.entry.js +24 -0
- package/dist/esm/co-slider.entry.js +3 -3
- package/dist/esm/co-switch.entry.js +6 -6
- package/dist/esm/co-tab-group.entry.js +2 -2
- package/dist/esm/co-tab.entry.js +3 -3
- package/dist/esm/co-tags-field.entry.js +19 -13
- package/dist/esm/co-textarea.entry.js +11 -7
- package/dist/esm/co-toast.entry.js +2 -2
- package/dist/esm/co-toggle-button-group.entry.js +4 -4
- package/dist/esm/co-toggle-button.entry.js +4 -4
- package/dist/esm/co-tooltip.entry.js +51 -16
- package/dist/esm/co-transfer-list.entry.js +3 -3
- package/dist/esm/co-video.entry.js +837 -0
- package/dist/esm/cobalt.js +3 -3
- package/dist/esm/{dates-CyYF8IJE.js → dates-tPYikBRJ.js} +1 -1
- package/dist/esm/fieldChip-GPikjpYw.js +5 -0
- package/dist/esm/{index-BKmfyPZK.js → index-CYsftTKf.js} +45 -21
- package/dist/esm/{index-BjAqSidM.js → index-w_Wxgclf.js} +14508 -6305
- package/dist/esm/loader.js +3 -3
- package/dist/esm/overlayAfterClose-Ch1jfwZ2.js +10 -0
- package/dist/{cjs/sanitizer-CjHZAkOm.js → esm/purify.es-B2QLgWrz.js} +1 -110
- package/dist/esm/{requiredMark-DFvtYtkE.js → requiredMark-CEbXKHcU.js} +1 -1
- package/dist/esm/richTextI18n-B-ckjCUA.js +380 -0
- package/dist/esm/toolbar-config-ChibIpZv.js +212 -0
- package/dist/esm/tz-B6JZk8fL.js +119 -0
- package/dist/types/components/co-autocomplete/co-autocomplete.d.ts +2 -0
- package/dist/types/components/co-avatar/co-avatar.d.ts +2 -0
- package/dist/types/components/co-avatar-group/co-avatar-group.d.ts +33 -0
- package/dist/types/components/co-bottom-sheet/co-bottom-sheet.d.ts +81 -0
- package/dist/types/components/co-button/co-button.d.ts +3 -1
- package/dist/types/components/co-calendar-month/co-calendar-month.d.ts +83 -0
- package/dist/types/components/co-chip/co-chip.d.ts +7 -2
- package/dist/types/components/co-color-picker/co-color-picker.d.ts +2 -0
- package/dist/types/components/co-command-search/co-command-search.d.ts +39 -6
- package/dist/types/components/co-command-search/commandSearchGroup.d.ts +20 -0
- package/dist/types/components/co-command-search/commandSearchLogic.d.ts +17 -0
- package/dist/types/components/co-data-table/co-data-table.d.ts +2 -1
- package/dist/types/components/co-event-calendar/co-event-calendar.d.ts +110 -0
- package/dist/types/components/co-icon/co-icon.d.ts +1 -1
- package/dist/types/components/co-icon/iconLoader.d.ts +6 -0
- package/dist/types/components/co-icon-button/co-icon-button.d.ts +2 -0
- package/dist/types/components/co-json-field/co-json-field.d.ts +3 -0
- package/dist/types/components/co-list-footer/co-list-footer.d.ts +26 -0
- package/dist/types/components/co-list-item/co-list-item.d.ts +11 -0
- package/dist/types/components/co-modal/co-modal.d.ts +6 -0
- package/dist/types/components/co-number-field/co-number-field.d.ts +13 -0
- package/dist/types/components/co-overlay/co-overlay.d.ts +45 -0
- package/dist/types/components/co-overlay/focusTrap.d.ts +20 -0
- package/dist/types/components/co-overlay/overlayAfterClose.d.ts +5 -0
- package/dist/types/components/co-phone-field/co-phone-field.d.ts +2 -0
- package/dist/types/components/co-popover/co-popover.d.ts +9 -0
- package/dist/types/components/co-progress-bar/co-progress-bar.d.ts +2 -0
- package/dist/types/components/co-ref-field/co-ref-field.d.ts +17 -2
- package/dist/types/components/co-rich-text-editor/co-rich-text-editor.d.ts +161 -9
- package/dist/types/components/co-rich-text-toolbar/co-rich-text-toolbar.d.ts +119 -13
- package/dist/types/components/co-rich-text-viewer/co-rich-text-viewer.d.ts +1 -14
- package/dist/types/components/co-skeleton/co-skeleton.d.ts +13 -0
- package/dist/types/components/co-tags-field/co-tags-field.d.ts +2 -0
- package/dist/types/components/co-textarea/co-textarea.d.ts +3 -0
- package/dist/types/components/co-tooltip/co-tooltip.d.ts +12 -0
- package/dist/types/components/co-video/co-video.d.ts +203 -0
- package/dist/types/components.d.ts +1729 -72
- package/dist/types/utils/fieldChip.d.ts +37 -0
- package/dist/types/utils/icon-config.d.ts +11 -0
- package/dist/types/utils/richTextI18n.d.ts +130 -0
- package/dist/types/utils/sheetGesture.d.ts +31 -0
- package/package.json +3 -2
- package/dist/cjs/co-loader.cjs.entry.js +0 -34
- package/dist/cjs/co-overlay.cjs.entry.js +0 -23
- package/dist/cobalt/p-0a6d8061.entry.js +0 -1
- package/dist/cobalt/p-0bf288cd.entry.js +0 -1
- package/dist/cobalt/p-136bdbd2.entry.js +0 -1
- package/dist/cobalt/p-15708d32.entry.js +0 -1
- package/dist/cobalt/p-1578b019.entry.js +0 -1
- package/dist/cobalt/p-23d48581.entry.js +0 -1
- package/dist/cobalt/p-2718ff27.entry.js +0 -1
- package/dist/cobalt/p-2b4d8535.entry.js +0 -1
- package/dist/cobalt/p-3190d715.entry.js +0 -1
- package/dist/cobalt/p-41228dce.entry.js +0 -1
- package/dist/cobalt/p-44672953.entry.js +0 -1
- package/dist/cobalt/p-53ce9023.entry.js +0 -1
- package/dist/cobalt/p-5e7141aa.entry.js +0 -1
- package/dist/cobalt/p-5f905739.entry.js +0 -1
- package/dist/cobalt/p-6678ac0a.entry.js +0 -1
- package/dist/cobalt/p-66a912dd.entry.js +0 -1
- package/dist/cobalt/p-6759ee1c.entry.js +0 -1
- package/dist/cobalt/p-69a40bfd.entry.js +0 -1
- package/dist/cobalt/p-6a6a62cb.entry.js +0 -1
- package/dist/cobalt/p-6d43b6cd.entry.js +0 -1
- package/dist/cobalt/p-759c3dd0.entry.js +0 -1
- package/dist/cobalt/p-782f729f.entry.js +0 -1
- package/dist/cobalt/p-83b89bf8.entry.js +0 -1
- package/dist/cobalt/p-88dc561a.entry.js +0 -1
- package/dist/cobalt/p-8c27aff7.entry.js +0 -1
- package/dist/cobalt/p-996179f7.entry.js +0 -1
- package/dist/cobalt/p-9c533830.entry.js +0 -1
- package/dist/cobalt/p-9d89bf47.entry.js +0 -1
- package/dist/cobalt/p-BKmfyPZK.js +0 -2
- package/dist/cobalt/p-BchB6Hdr.js +0 -1
- package/dist/cobalt/p-DcK0GGZC.js +0 -2
- package/dist/cobalt/p-Qqq8fysz.js +0 -1
- package/dist/cobalt/p-aa17c70b.entry.js +0 -1
- package/dist/cobalt/p-ad4c1b41.entry.js +0 -1
- package/dist/cobalt/p-ae4d3207.entry.js +0 -1
- package/dist/cobalt/p-af64da37.entry.js +0 -1
- package/dist/cobalt/p-c6a242e4.entry.js +0 -1
- package/dist/cobalt/p-cd57e8b4.entry.js +0 -1
- package/dist/cobalt/p-ce3a535f.entry.js +0 -1
- package/dist/cobalt/p-d958566a.entry.js +0 -1
- package/dist/cobalt/p-dd873bf8.entry.js +0 -1
- package/dist/cobalt/p-e4de8865.entry.js +0 -1
- package/dist/cobalt/p-eba0bf7f.entry.js +0 -1
- package/dist/cobalt/p-fad3605d.entry.js +0 -1
- package/dist/cobalt/p-ff2690e5.entry.js +0 -1
- package/dist/components/p-BXdfh__-.js +0 -1
- package/dist/components/p-BchB6Hdr.js +0 -1
- package/dist/components/p-BiW0ubLs.js +0 -1
- package/dist/components/p-CaMgu2_g.js +0 -1
- package/dist/components/p-DBhNS7JD.js +0 -1
- package/dist/components/p-DPi_U-Kl.js +0 -1
- package/dist/components/p-DcK0GGZC.js +0 -2
- package/dist/components/p-MiSJXuBx.js +0 -1
- package/dist/components/p-Sgn-2g8m.js +0 -1
- package/dist/components/p-jDjfGZJp.js +0 -1
- package/dist/components/p-o8hrDIKe.js +0 -1
- package/dist/esm/co-loader.entry.js +0 -32
- package/dist/esm/co-overlay.entry.js +0 -21
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
-
import
|
|
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
|
-
|
|
37
|
-
this.
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
94
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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: '
|
|
2024
|
+
return (h("div", { key: 'd24b8e5db1151bcd983790ea15f68aaf4838ff43', 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: '
|
|
2031
|
+
}, part: "wrapper" }, (this.label || this.secondaryLabel) && (h("div", { key: '1622079a94d8ebb190642f91463b715f3456fcf4', class: "co-rich-text-editor__labels" }, this.label && h("label", { key: 'bb5c0754a63d46d45ce8f211ae0f0e8c4ddbc478', class: "co-rich-text-editor__label", part: "label" }, this.label), this.secondaryLabel && (h("span", { key: '1af0a217a38746c3a78693c552be44df6d03b276', class: "co-rich-text-editor__secondary-label" }, this.secondaryLabel)))), h("div", { key: 'e5ad90dd90061be3e40136eece0f485e5cfb89a1', class: "co-rich-text-editor__frame" }, showToolbar && (h("co-rich-text-toolbar", { key: '56f3da29e9259cd37fdf6e7511dc54efb30085ca', 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: 'bb0dc27e8625171284cae02b5cae28dcbe638991', class: "co-rich-text-editor__box" }, h("div", { key: '647563e73112023ec546418787dbf61acb58853d', 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: '460dad69fd1781f11d751565d0ca09015013e8ce', class: "co-rich-text-editor__footer" }, this.helperText && (h("div", { key: '69d8fb55797eb8f35885fa10e86cf08e4d0e45ec', class: `co-rich-text-editor__helper co-rich-text-editor__helper--${this.status}`, part: "helper" }, h("co-icon", { key: 'd1c3cc4f06284b865c889dac7492305a4b7d8dfd', name: this.helperIcon, size: 14 }), h("span", { key: '361940f354fcee5a309cd2ef5cff84b73689650e' }, this.helperText))), showCount && (h("span", { key: '2b5a1b6e04ba3d428346034d092bf83978de0124', 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
|
+
};
|