@prolibu-suite/cobalt-core 0.10.3 → 0.11.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 +4385 -389
- package/dist/cjs/co-accordion.cjs.entry.js +3 -3
- package/dist/cjs/co-action-bar.cjs.entry.js +3 -3
- package/dist/cjs/co-app-shell.cjs.entry.js +3 -3
- package/dist/cjs/co-audio-player.cjs.entry.js +4 -4
- package/dist/cjs/co-autocomplete.cjs.entry.js +2 -2
- package/dist/cjs/co-avatar-group.cjs.entry.js +1 -1
- package/dist/cjs/co-avatar.cjs.entry.js +7 -4
- package/dist/cjs/co-badge.cjs.entry.js +2 -2
- package/dist/cjs/co-bottom-sheet.cjs.entry.js +5 -5
- package/dist/cjs/co-breadcrumb-item.cjs.entry.js +3 -3
- package/dist/cjs/co-breadcrumb.cjs.entry.js +2 -2
- package/dist/cjs/co-button-group.cjs.entry.js +3 -3
- package/dist/cjs/co-button.cjs.entry.js +9 -7
- package/dist/cjs/co-calendar-month_2.cjs.entry.js +4 -4
- package/dist/cjs/co-calendar_3.cjs.entry.js +72 -27
- package/dist/cjs/co-card.cjs.entry.js +3 -3
- package/dist/cjs/co-carousel-item.cjs.entry.js +2 -2
- package/dist/cjs/co-carousel.cjs.entry.js +3 -3
- package/dist/cjs/co-checkbox-group.cjs.entry.js +1 -1
- package/dist/cjs/co-checkbox.cjs.entry.js +13 -5
- package/dist/cjs/co-chip_2.cjs.entry.js +28 -6
- package/dist/cjs/co-color-area.cjs.entry.js +169 -0
- package/dist/cjs/co-color-picker.cjs.entry.js +5 -5
- package/dist/cjs/co-color-slider.cjs.entry.js +189 -0
- package/dist/cjs/co-column-picker.cjs.entry.js +3 -3
- package/dist/cjs/co-combobox.cjs.entry.js +5 -5
- package/dist/cjs/co-command-search.cjs.entry.js +5 -5
- package/dist/cjs/co-data-table-pagination.cjs.entry.js +5 -5
- package/dist/cjs/co-data-table.cjs.entry.js +3 -3
- package/dist/cjs/co-date-picker.cjs.entry.js +2 -2
- package/dist/cjs/co-date-range-picker.cjs.entry.js +2 -2
- package/dist/cjs/co-date-range.cjs.entry.js +2 -2
- package/dist/cjs/co-date-time-picker.cjs.entry.js +4 -4
- package/dist/cjs/co-document-editor.cjs.entry.js +1161 -0
- package/dist/cjs/co-drive-card.cjs.entry.js +2 -2
- package/dist/cjs/co-drop-zone.cjs.entry.js +2 -2
- package/dist/cjs/co-emoji-picker.cjs.entry.js +3 -3
- package/dist/cjs/co-empty-state.cjs.entry.js +3 -3
- package/dist/cjs/co-event-calendar.cjs.entry.js +3 -3
- package/dist/cjs/co-event-popover.cjs.entry.js +3 -3
- 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-file-type-tile.cjs.entry.js +3 -3
- package/dist/cjs/co-gradient-bar.cjs.entry.js +220 -0
- package/dist/cjs/co-helper-text.cjs.entry.js +3 -3
- package/dist/cjs/co-icon-button.cjs.entry.js +49 -9
- package/dist/cjs/co-icon.cjs.entry.js +3 -3
- package/dist/cjs/co-input.cjs.entry.js +14 -8
- package/dist/cjs/co-json-field.cjs.entry.js +5 -5
- package/dist/cjs/{co-tooltip.cjs.entry.js → co-kbd_2.cjs.entry.js} +194 -6
- package/dist/cjs/co-lightbox.cjs.entry.js +5 -5
- package/dist/cjs/co-list-footer.cjs.entry.js +2 -2
- package/dist/cjs/co-list-item.cjs.entry.js +5 -5
- package/dist/cjs/co-loader.cjs.entry.js +1 -1
- package/dist/cjs/co-media-thumb.cjs.entry.js +3 -3
- package/dist/cjs/co-menu-divider.cjs.entry.js +3 -3
- package/dist/cjs/co-menu-item.cjs.entry.js +41 -43
- package/dist/cjs/co-menu.cjs.entry.js +81 -5
- package/dist/cjs/co-modal.cjs.entry.js +350 -7
- package/dist/cjs/co-navbar.cjs.entry.js +3 -3
- package/dist/cjs/co-number-field.cjs.entry.js +5 -5
- package/dist/cjs/co-overlay.cjs.entry.js +8 -4
- package/dist/cjs/co-page-header.cjs.entry.js +2 -2
- package/dist/cjs/co-phone-field.cjs.entry.js +3 -3
- package/dist/cjs/co-popover.cjs.entry.js +81 -6
- package/dist/cjs/co-progress-bar.cjs.entry.js +3 -3
- package/dist/cjs/co-progress-circle.cjs.entry.js +3 -3
- package/dist/cjs/co-provider-logo_2.cjs.entry.js +2 -2
- 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 +5 -5
- package/dist/cjs/co-rich-text-editor.cjs.entry.js +26 -92
- package/dist/cjs/co-rich-text-toolbar.cjs.entry.js +79 -20
- package/dist/cjs/co-rich-text-viewer.cjs.entry.js +9 -2
- package/dist/cjs/co-row.cjs.entry.js +3 -3
- package/dist/cjs/co-segmented-bar.cjs.entry.js +3 -3
- package/dist/cjs/co-select.cjs.entry.js +158 -29
- package/dist/cjs/co-separator.cjs.entry.js +2 -2
- package/dist/cjs/co-sidebar-nav.cjs.entry.js +20 -5
- package/dist/cjs/co-sidebar.cjs.entry.js +3 -3
- package/dist/cjs/co-slider.cjs.entry.js +203 -23
- package/dist/cjs/co-swatch-grid.cjs.entry.js +121 -0
- package/dist/cjs/co-switch.cjs.entry.js +5 -5
- package/dist/cjs/co-tab-group.cjs.entry.js +113 -4
- package/dist/cjs/co-tab.cjs.entry.js +15 -5
- package/dist/cjs/co-tags-field.cjs.entry.js +8 -8
- package/dist/cjs/co-textarea.cjs.entry.js +6 -6
- package/dist/cjs/co-tile.cjs.entry.js +61 -0
- package/dist/cjs/co-time-field.cjs.entry.js +2 -2
- package/dist/cjs/co-toast.cjs.entry.js +3 -3
- package/dist/cjs/co-toggle-button-group.cjs.entry.js +26 -5
- package/dist/cjs/co-toggle-button.cjs.entry.js +67 -6
- package/dist/cjs/co-transfer-list.cjs.entry.js +3 -3
- package/dist/cjs/co-tree-item.cjs.entry.js +110 -0
- package/dist/cjs/co-video.cjs.entry.js +3 -3
- package/dist/cjs/cobalt.cjs.js +2 -2
- package/dist/cjs/{color-CML2Cfdh.js → color-DQVrQGi0.js} +10 -5
- package/dist/cjs/{fieldChip-B6YIGU1V.js → fieldChip-C7MxxBVO.js} +1 -1
- package/dist/cjs/gradientStops-CMpH4fB_.js +153 -0
- package/dist/cjs/hsv-DpfSF3M2.js +95 -0
- package/dist/cjs/{index-MDAnKsze.js → index-bHoidDwv.js} +67 -27
- package/dist/cjs/index-oMHzCXm6.js +144 -0
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/{index-2kMmd5-a.js → pagination-DV43TRj9.js} +1780 -297
- package/dist/cjs/{positioning-CyatXeT_.js → positioning-CILo5esi.js} +45 -18
- package/dist/cjs/{requiredMark-D-t-IPN0.js → requiredMark-21yHvvZS.js} +1 -1
- package/dist/cjs/rich-text-host-ueFSfW-6.js +119 -0
- package/dist/cjs/{richTextI18n-B28uAhh4.js → richTextI18n-DXGIv5w1.js} +45 -0
- package/dist/cjs/submenu-RlaX6irw.js +65 -0
- package/dist/cjs/{toolbar-config-CYACt9hh.js → toolbar-config-DczL6C4W.js} +32 -0
- package/dist/cobalt/cobalt.css +1 -1
- package/dist/cobalt/cobalt.esm.js +1 -1
- package/dist/cobalt/{p-ee149fd2.entry.js → p-05000ef3.entry.js} +1 -1
- package/dist/cobalt/p-06689166.entry.js +1 -0
- package/dist/cobalt/{p-1dc55e43.entry.js → p-0a5cfed0.entry.js} +1 -1
- package/dist/cobalt/p-1289131f.entry.js +1 -0
- package/dist/cobalt/{p-c7d7d7f7.entry.js → p-136beb2c.entry.js} +1 -1
- package/dist/cobalt/{p-b437eff6.entry.js → p-1693b614.entry.js} +1 -1
- package/dist/cobalt/p-1bfb69ae.entry.js +1 -0
- package/dist/cobalt/{p-2c9dba2f.entry.js → p-1f3d5313.entry.js} +1 -1
- package/dist/cobalt/{p-b821d9de.entry.js → p-22233b31.entry.js} +1 -1
- package/dist/cobalt/{p-c30b4cb5.entry.js → p-26fa36f8.entry.js} +1 -1
- package/dist/cobalt/p-2794c500.entry.js +1 -0
- package/dist/cobalt/p-297b55cd.entry.js +1 -0
- package/dist/cobalt/{p-17244412.entry.js → p-2a2b0c7a.entry.js} +1 -1
- package/dist/cobalt/{p-2636b7eb.entry.js → p-2e5702a0.entry.js} +1 -1
- package/dist/cobalt/p-3011c84f.entry.js +1 -0
- package/dist/cobalt/{p-4d65e713.entry.js → p-30760f77.entry.js} +1 -1
- package/dist/cobalt/{p-6dd220e0.entry.js → p-3b9a8e86.entry.js} +1 -1
- package/dist/cobalt/p-4111fefa.entry.js +1 -0
- package/dist/cobalt/{p-a32b20eb.entry.js → p-44edde81.entry.js} +1 -1
- package/dist/cobalt/{p-292b0389.entry.js → p-45007758.entry.js} +1 -1
- package/dist/cobalt/p-48c23310.entry.js +1 -0
- package/dist/cobalt/p-4b327483.entry.js +1 -0
- package/dist/cobalt/p-4c6caa41.entry.js +1 -0
- package/dist/cobalt/{p-000a15b8.entry.js → p-5534a64f.entry.js} +1 -1
- package/dist/cobalt/p-5573985a.entry.js +1 -0
- package/dist/cobalt/p-57d250ad.entry.js +1 -0
- package/dist/cobalt/{p-826ee074.entry.js → p-59d3e8fc.entry.js} +1 -1
- package/dist/cobalt/{p-4592ca00.entry.js → p-5a32b9a2.entry.js} +1 -1
- package/dist/cobalt/p-5b2aa70e.entry.js +1 -0
- package/dist/cobalt/{p-d29bb0fa.entry.js → p-5d6e02dd.entry.js} +1 -1
- package/dist/cobalt/{p-623cc195.entry.js → p-608d251d.entry.js} +1 -1
- package/dist/cobalt/{p-011dc80b.entry.js → p-66fc1528.entry.js} +1 -1
- package/dist/cobalt/{p-11c5ce78.entry.js → p-67ad9544.entry.js} +1 -1
- package/dist/cobalt/{p-290066ef.entry.js → p-6c157ea2.entry.js} +1 -1
- package/dist/cobalt/{p-a2462d7a.entry.js → p-6d541199.entry.js} +1 -1
- package/dist/cobalt/{p-58d9b650.entry.js → p-7376f4ed.entry.js} +1 -1
- package/dist/cobalt/{p-39a50e7f.entry.js → p-7569375a.entry.js} +1 -1
- package/dist/cobalt/{p-7b5c555c.entry.js → p-763698b3.entry.js} +1 -1
- package/dist/cobalt/{p-f57dffca.entry.js → p-7675cebe.entry.js} +1 -1
- package/dist/cobalt/p-78a20284.entry.js +1 -0
- package/dist/cobalt/{p-aa9400bf.entry.js → p-7f3523ce.entry.js} +1 -1
- package/dist/cobalt/{p-24196410.entry.js → p-86b6fd81.entry.js} +1 -1
- package/dist/cobalt/{p-996f4e38.entry.js → p-889563d5.entry.js} +1 -1
- package/dist/cobalt/{p-eadfd661.entry.js → p-8ab5b396.entry.js} +1 -1
- package/dist/cobalt/{p-c91d4fc6.entry.js → p-8b2e4579.entry.js} +1 -1
- package/dist/cobalt/p-8cd3acaa.entry.js +1 -0
- package/dist/cobalt/{p-23ab0cc5.entry.js → p-8f701eac.entry.js} +1 -1
- package/dist/cobalt/p-90fad0f9.entry.js +1 -0
- package/dist/cobalt/{p-76249983.entry.js → p-97a4fbba.entry.js} +1 -1
- package/dist/cobalt/p-9b46a8fe.entry.js +1 -0
- package/dist/cobalt/{p-ee2023da.entry.js → p-9bee9dc8.entry.js} +1 -1
- package/dist/cobalt/{p-2f9cbbd5.entry.js → p-9cf10331.entry.js} +1 -1
- package/dist/cobalt/p-9dc01092.entry.js +1 -0
- package/dist/cobalt/p-9e406172.entry.js +1 -0
- package/dist/cobalt/p-B1JT7us1.js +1 -0
- package/dist/cobalt/{p-BRbgGqLw.js → p-BVGGRfNG.js} +1 -1
- package/dist/cobalt/p-BmdKHBZP.js +1 -0
- package/dist/cobalt/p-BtliEQAT.js +1 -0
- package/dist/cobalt/p-COJdaKBU.js +1 -0
- package/dist/cobalt/{p-BAYo-RBt.js → p-CRxTFT_z.js} +1 -1
- package/dist/cobalt/p-CYN5B3IQ.js +1 -0
- package/dist/cobalt/p-DJkaRtbb.js +2 -0
- package/dist/cobalt/p-DU-KOQ8L.js +1 -0
- package/dist/cobalt/p-DXLeDZvr.js +1 -0
- package/dist/cobalt/{p-D_zA1Pv4.js → p-DgJovDdy.js} +1 -1
- package/dist/cobalt/p-RVQJEYxa.js +1 -0
- package/dist/cobalt/{p-1861fe41.entry.js → p-a07dedd1.entry.js} +1 -1
- package/dist/cobalt/{p-08e42e00.entry.js → p-a336178b.entry.js} +1 -1
- package/dist/cobalt/{p-f9a7b7c6.entry.js → p-a52ed6ff.entry.js} +1 -1
- package/dist/cobalt/{p-aaca6dab.entry.js → p-a930cdd7.entry.js} +1 -1
- package/dist/cobalt/{p-eb82358d.entry.js → p-b1994bb4.entry.js} +1 -1
- package/dist/cobalt/p-b2b13da8.entry.js +1 -0
- package/dist/cobalt/{p-c2ebf0ec.entry.js → p-b5ec4286.entry.js} +1 -1
- package/dist/cobalt/p-b9401dd7.entry.js +1 -0
- package/dist/cobalt/p-b9dfb333.entry.js +1 -0
- package/dist/cobalt/p-bc3ba0cd.entry.js +1 -0
- package/dist/cobalt/{p-d375014b.entry.js → p-bc4fbfa6.entry.js} +1 -1
- package/dist/cobalt/{p-d0b2c418.entry.js → p-c109a1e1.entry.js} +1 -1
- package/dist/cobalt/{p-ee9a623d.entry.js → p-c1caa112.entry.js} +1 -1
- package/dist/cobalt/p-c9990662.entry.js +1 -0
- package/dist/cobalt/p-cc4b51fa.entry.js +1 -0
- package/dist/cobalt/p-cc5989e9.entry.js +1 -0
- package/dist/cobalt/{p-e4264532.entry.js → p-ce2b2048.entry.js} +1 -1
- package/dist/cobalt/p-d1c378ff.entry.js +1 -0
- package/dist/cobalt/p-d1e83525.entry.js +1 -0
- package/dist/cobalt/{p-59963937.entry.js → p-d1f47915.entry.js} +1 -1
- package/dist/cobalt/{p-28764b8e.entry.js → p-d4979025.entry.js} +1 -1
- package/dist/cobalt/p-d6e7c258.entry.js +1 -0
- package/dist/cobalt/p-d75d2f3d.entry.js +1 -0
- package/dist/cobalt/p-db42c4ab.entry.js +1 -0
- package/dist/cobalt/{p-44c4e4a3.entry.js → p-e4ff2fcb.entry.js} +1 -1
- package/dist/cobalt/p-e6116149.entry.js +1 -0
- package/dist/cobalt/{p-0c004735.entry.js → p-e99ddf9c.entry.js} +1 -1
- package/dist/cobalt/{p-1cc818ca.entry.js → p-ea24eea8.entry.js} +1 -1
- package/dist/cobalt/{p-07416e31.entry.js → p-ea564148.entry.js} +1 -1
- package/dist/cobalt/{p-8471abe2.entry.js → p-ec7a6bf2.entry.js} +1 -1
- package/dist/cobalt/{p-fb2d1144.entry.js → p-ee42dca6.entry.js} +1 -1
- package/dist/cobalt/{p-49834b8e.entry.js → p-ee88ec40.entry.js} +1 -1
- package/dist/cobalt/p-eeb114da.entry.js +1 -0
- package/dist/cobalt/{p-f6691108.entry.js → p-f0898e21.entry.js} +1 -1
- package/dist/cobalt/p-f246946a.entry.js +1 -0
- package/dist/cobalt/{p-b90d41e4.entry.js → p-f5934cd9.entry.js} +1 -1
- package/dist/cobalt/p-f5e1873d.entry.js +1 -0
- package/dist/cobalt/p-f60bd4d6.entry.js +1 -0
- package/dist/cobalt/p-f813569c.entry.js +1 -0
- package/dist/cobalt/{p-f7eb1a40.entry.js → p-f8c7941f.entry.js} +1 -1
- package/dist/cobalt/{p-44ea675f.entry.js → p-f8d3c71e.entry.js} +1 -1
- package/dist/cobalt/{p-91e73a92.entry.js → p-ffbe955a.entry.js} +1 -1
- package/dist/cobalt/p-xnrKkNWQ.js +1 -0
- package/dist/collection/collection-manifest.json +8 -0
- package/dist/collection/components/co-accordion/co-accordion.js +2 -2
- package/dist/collection/components/co-action-bar/co-action-bar.js +2 -2
- package/dist/collection/components/co-app-shell/co-app-shell.js +2 -2
- package/dist/collection/components/co-audio-player/co-audio-player.js +3 -3
- package/dist/collection/components/co-autocomplete/co-autocomplete.js +1 -1
- package/dist/collection/components/co-avatar/co-avatar.css +11 -2
- package/dist/collection/components/co-avatar/co-avatar.js +7 -4
- package/dist/collection/components/co-avatar-group/co-avatar-group.js +1 -1
- package/dist/collection/components/co-badge/co-badge.js +1 -1
- package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.js +4 -4
- package/dist/collection/components/co-breadcrumb/co-breadcrumb.js +1 -1
- package/dist/collection/components/co-breadcrumb-item/co-breadcrumb-item.js +2 -2
- package/dist/collection/components/co-button/co-button.css +73 -6
- package/dist/collection/components/co-button/co-button.js +33 -11
- package/dist/collection/components/co-button-group/co-button-group.js +2 -2
- package/dist/collection/components/co-calendar/calendarMarks.js +40 -0
- package/dist/collection/components/co-calendar/co-calendar.css +11 -1
- package/dist/collection/components/co-calendar/co-calendar.js +82 -20
- package/dist/collection/components/co-calendar-month/co-calendar-month.js +1 -1
- package/dist/collection/components/co-calendar-timegrid/co-calendar-timegrid.js +2 -2
- package/dist/collection/components/co-card/co-card.js +2 -2
- package/dist/collection/components/co-carousel/co-carousel.js +2 -2
- package/dist/collection/components/co-carousel-item/co-carousel-item.js +1 -1
- package/dist/collection/components/co-checkbox/co-checkbox.css +43 -1
- package/dist/collection/components/co-checkbox/co-checkbox.js +31 -3
- package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +1 -1
- package/dist/collection/components/co-chip/co-chip.js +2 -2
- package/dist/collection/components/co-color-area/co-color-area.css +83 -0
- package/dist/collection/components/co-color-area/co-color-area.js +341 -0
- package/dist/collection/components/co-color-picker/co-color-picker.js +2 -2
- package/dist/collection/components/co-color-slider/co-color-slider.css +115 -0
- package/dist/collection/components/co-color-slider/co-color-slider.js +353 -0
- package/dist/collection/components/co-column-picker/co-column-picker.js +2 -2
- package/dist/collection/components/co-combobox/co-combobox.js +4 -4
- package/dist/collection/components/co-command-search/co-command-search.js +5 -5
- package/dist/collection/components/co-data-table/co-data-table.js +3 -3
- 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.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.js +2 -2
- package/dist/collection/components/co-date-time-picker/co-date-time-picker.js +3 -3
- package/dist/collection/components/co-document-editor/change-throttle.js +30 -0
- package/dist/collection/components/co-document-editor/co-document-editor.css +438 -0
- package/dist/collection/components/co-document-editor/co-document-editor.js +1542 -0
- package/dist/collection/components/co-document-editor/document-props.js +40 -0
- package/dist/collection/components/co-document-editor/header-tools.js +11 -0
- package/dist/collection/components/co-document-editor/image-bar.js +66 -0
- package/dist/collection/components/co-document-editor/page-styles.js +90 -0
- package/dist/collection/components/co-document-editor/zoom.js +45 -0
- package/dist/collection/components/co-drive-card/co-drive-card.js +1 -1
- package/dist/collection/components/co-drop-zone/co-drop-zone.js +1 -1
- package/dist/collection/components/co-emoji-picker/co-emoji-picker.js +2 -2
- package/dist/collection/components/co-empty-state/co-empty-state.js +2 -2
- package/dist/collection/components/co-event-calendar/co-event-calendar.js +2 -2
- package/dist/collection/components/co-event-popover/co-event-popover.js +1 -1
- package/dist/collection/components/co-fab/co-fab.js +2 -2
- package/dist/collection/components/co-file-field/co-file-field.js +6 -6
- package/dist/collection/components/co-file-type-tile/co-file-type-tile.js +2 -2
- package/dist/collection/components/co-gradient-bar/co-gradient-bar.css +138 -0
- package/dist/collection/components/co-gradient-bar/co-gradient-bar.js +407 -0
- package/dist/collection/components/co-helper-text/co-helper-text.js +2 -2
- package/dist/collection/components/co-icon/co-icon.js +2 -2
- package/dist/collection/components/co-icon-button/co-icon-button.css +95 -19
- package/dist/collection/components/co-icon-button/co-icon-button.js +120 -10
- package/dist/collection/components/co-input/co-input.css +122 -0
- package/dist/collection/components/co-input/co-input.js +37 -11
- package/dist/collection/components/co-json-field/co-json-field.js +3 -3
- package/dist/collection/components/co-kbd/co-kbd.css +99 -0
- package/dist/collection/components/co-kbd/co-kbd.js +291 -0
- package/dist/collection/components/co-lightbox/co-lightbox.js +4 -4
- package/dist/collection/components/co-list-footer/co-list-footer.js +1 -1
- package/dist/collection/components/co-list-item/co-list-item.js +4 -4
- package/dist/collection/components/co-media-thumb/co-media-thumb.js +2 -2
- package/dist/collection/components/co-menu/co-menu.css +60 -0
- package/dist/collection/components/co-menu/co-menu.js +143 -3
- package/dist/collection/components/co-menu-divider/co-menu-divider.css +17 -0
- package/dist/collection/components/co-menu-divider/co-menu-divider.js +1 -1
- package/dist/collection/components/co-menu-item/co-menu-item.css +42 -6
- package/dist/collection/components/co-menu-item/co-menu-item.js +58 -4
- package/dist/collection/components/co-modal/co-modal.css +212 -1
- package/dist/collection/components/co-modal/co-modal.js +339 -9
- package/dist/collection/components/co-navbar/co-navbar.js +2 -2
- package/dist/collection/components/co-number-field/co-number-field.js +3 -3
- package/dist/collection/components/co-option-list/co-option-list.css +64 -21
- package/dist/collection/components/co-option-list/co-option-list.js +43 -3
- package/dist/collection/components/co-overlay/co-overlay.js +7 -3
- package/dist/collection/components/co-page-header/co-page-header.js +1 -1
- package/dist/collection/components/co-phone-field/co-phone-field.js +1 -1
- package/dist/collection/components/co-popover/co-popover.css +15 -2
- package/dist/collection/components/co-popover/co-popover.js +144 -5
- package/dist/collection/components/co-progress-bar/co-progress-bar.js +2 -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 +4 -4
- package/dist/collection/components/co-rating/co-rating.js +3 -3
- package/dist/collection/components/co-ref-field/co-ref-field.js +5 -5
- package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.js +23 -90
- package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.css +12 -0
- package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.js +123 -18
- package/dist/collection/components/co-rich-text-viewer/co-rich-text-viewer.js +8 -1
- package/dist/collection/components/co-row/co-row.js +2 -2
- package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +3 -3
- package/dist/collection/components/co-select/co-select.css +194 -10
- package/dist/collection/components/co-select/co-select.js +163 -32
- package/dist/collection/components/co-select/virtualRows.js +57 -0
- 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 +10 -0
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +19 -4
- package/dist/collection/components/co-skeleton/co-skeleton.js +1 -1
- package/dist/collection/components/co-slider/co-slider.css +148 -5
- package/dist/collection/components/co-slider/co-slider.js +240 -21
- package/dist/collection/components/co-swatch-grid/co-swatch-grid.css +56 -0
- package/dist/collection/components/co-swatch-grid/co-swatch-grid.js +292 -0
- package/dist/collection/components/co-switch/co-switch.js +3 -3
- package/dist/collection/components/co-tab/co-tab.css +60 -0
- package/dist/collection/components/co-tab/co-tab.js +36 -6
- package/dist/collection/components/co-tab-group/co-tab-group.css +24 -0
- package/dist/collection/components/co-tab-group/co-tab-group.js +124 -2
- package/dist/collection/components/co-tab-group/revealTab.js +26 -0
- package/dist/collection/components/co-tags-field/co-tags-field.js +5 -5
- package/dist/collection/components/co-textarea/co-textarea.js +4 -4
- package/dist/collection/components/co-tile/co-tile.css +63 -0
- package/dist/collection/components/co-tile/co-tile.js +162 -0
- package/dist/collection/components/co-time-field/co-time-field.js +1 -1
- package/dist/collection/components/co-toast/co-toast.js +4 -4
- package/dist/collection/components/co-toggle-button/co-toggle-button.css +101 -0
- package/dist/collection/components/co-toggle-button/co-toggle-button.js +80 -8
- package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.css +21 -0
- package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.js +28 -7
- package/dist/collection/components/co-tooltip/co-tooltip.css +36 -0
- package/dist/collection/components/co-tooltip/co-tooltip.js +51 -3
- package/dist/collection/components/co-transfer-list/co-transfer-list.js +3 -3
- package/dist/collection/components/co-tree-item/co-tree-item.css +226 -0
- package/dist/collection/components/co-tree-item/co-tree-item.js +353 -0
- package/dist/collection/components/co-tree-item/treeItemI18n.js +16 -0
- package/dist/collection/components/co-video/co-video.js +2 -2
- package/dist/collection/utils/color.js +9 -5
- package/dist/collection/utils/dragMove.js +95 -0
- package/dist/collection/utils/gradientBarI18n.js +24 -0
- package/dist/collection/utils/gradientStops.js +142 -0
- package/dist/collection/utils/hsv.js +114 -0
- package/dist/collection/utils/kbdI18n.js +29 -0
- package/dist/collection/utils/positioning.js +44 -18
- package/dist/collection/utils/rich-text-host.js +109 -0
- package/dist/collection/utils/richTextI18n.js +45 -0
- package/dist/collection/utils/submenu.js +23 -3
- package/dist/collection/utils/swatchGridI18n.js +14 -0
- package/dist/collection/utils/test/fake-block-layout.js +41 -0
- package/dist/collection/utils/test/prosemirror-dom-shim.js +17 -0
- package/dist/collection/utils/test/shared-contract-css.js +9 -0
- package/dist/components/co-accordion.js +1 -1
- package/dist/components/co-action-bar.js +1 -1
- package/dist/components/co-app-shell.js +1 -1
- package/dist/components/co-audio-player.js +1 -1
- package/dist/components/co-autocomplete.js +1 -1
- package/dist/components/co-avatar-group.js +1 -1
- package/dist/components/co-avatar.js +1 -1
- package/dist/components/co-badge.js +1 -1
- package/dist/components/co-bottom-sheet.js +1 -1
- package/dist/components/co-breadcrumb-item.js +1 -1
- package/dist/components/co-breadcrumb.js +1 -1
- package/dist/components/co-button-group.js +1 -1
- package/dist/components/co-button.js +1 -1
- package/dist/components/co-calendar-month.js +1 -1
- package/dist/components/co-calendar-timegrid.js +1 -1
- package/dist/components/co-calendar.js +1 -1
- package/dist/components/co-card.js +1 -1
- package/dist/components/co-carousel-item.js +1 -1
- package/dist/components/co-carousel.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-area.d.ts +11 -0
- package/dist/components/co-color-area.js +1 -0
- package/dist/components/co-color-picker.js +1 -1
- package/dist/components/co-color-slider.d.ts +11 -0
- package/dist/components/co-color-slider.js +1 -0
- 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-date-time-picker.js +1 -1
- package/dist/components/co-document-editor.d.ts +11 -0
- package/dist/components/co-document-editor.js +1 -0
- package/dist/components/co-drawer.js +1 -1
- package/dist/components/co-drive-card.js +1 -1
- package/dist/components/co-drop-zone.js +1 -1
- package/dist/components/co-emoji-picker.js +1 -1
- package/dist/components/co-empty-state.js +1 -1
- package/dist/components/co-event-calendar.js +1 -1
- package/dist/components/co-event-popover.js +1 -1
- package/dist/components/co-fab.js +1 -1
- package/dist/components/co-file-field.js +1 -1
- package/dist/components/co-file-type-tile.js +1 -1
- package/dist/components/co-gradient-bar.d.ts +11 -0
- package/dist/components/co-gradient-bar.js +1 -0
- 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-kbd.d.ts +11 -0
- package/dist/components/co-kbd.js +1 -0
- package/dist/components/co-lightbox.js +1 -1
- package/dist/components/co-list-footer.js +1 -1
- package/dist/components/co-list-item.js +1 -1
- package/dist/components/co-media-thumb.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-option-list.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-segmented-bar.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.js +1 -1
- package/dist/components/co-slider.js +1 -1
- package/dist/components/co-swatch-grid.d.ts +11 -0
- package/dist/components/co-swatch-grid.js +1 -0
- 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-tile.d.ts +11 -0
- package/dist/components/co-tile.js +1 -0
- package/dist/components/co-time-field.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-tree-item.d.ts +11 -0
- package/dist/components/co-tree-item.js +1 -0
- package/dist/components/co-video.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/p-B1JT7us1.js +1 -0
- package/dist/components/{p-Nt4JJxbL.js → p-B20o5exO.js} +1 -1
- package/dist/components/{p-DNJAIVfI.js → p-B3kKOgsA.js} +1 -1
- package/dist/components/{p-P4HlMBlL.js → p-BIDSDP05.js} +1 -1
- package/dist/components/p-BOqtnWTJ.js +1 -0
- package/dist/components/p-BQltjDw5.js +1 -0
- package/dist/components/p-BR0LYW9v.js +1 -0
- package/dist/components/{p-BRbgGqLw.js → p-BVGGRfNG.js} +1 -1
- package/dist/components/p-BZDl9aFl.js +1 -0
- package/dist/components/p-BcXdId8y.js +1 -0
- package/dist/components/p-BmdKHBZP.js +1 -0
- package/dist/components/{p-NNxjElcX.js → p-BraiMwgs.js} +1 -1
- package/dist/components/p-BtliEQAT.js +1 -0
- package/dist/components/p-Bymi-A7C.js +1 -0
- package/dist/components/p-C-f8wTqe.js +1 -0
- package/dist/components/{p-BhCVadcA.js → p-C1jZYtUS.js} +1 -1
- package/dist/components/{p-CT2enQ6h.js → p-C3G8oPgU.js} +1 -1
- package/dist/components/{p-cndRd_-U.js → p-CGH3E8uK.js} +1 -1
- package/dist/components/p-COJdaKBU.js +1 -0
- package/dist/components/{p-BAYo-RBt.js → p-CRxTFT_z.js} +1 -1
- package/dist/components/{p-Du_CgT5E.js → p-CfJgypOl.js} +1 -1
- package/dist/components/{p-uxdXViMV.js → p-ChwSnCFf.js} +1 -1
- package/dist/components/{p-Dc_Kbari.js → p-CpGEV0iM.js} +1 -1
- package/dist/components/{p-DvR45CLa.js → p-D9or2g4j.js} +1 -1
- package/dist/components/{p-e5KibEs8.js → p-DJ468fPu.js} +1 -1
- package/dist/components/{p-DFlJMEJ-.js → p-DKXCr-hE.js} +1 -1
- package/dist/components/p-DU-KOQ8L.js +1 -0
- package/dist/components/p-DXLeDZvr.js +1 -0
- package/dist/components/{p-BGDqldEg.js → p-DfcPGitr.js} +1 -1
- package/dist/components/p-Dg-Ti_Bf.js +1 -0
- package/dist/components/p-Dh_eRYKu.js +1 -0
- package/dist/components/p-Dp650Gpp.js +1 -0
- package/dist/components/p-FA1kBmZ-.js +1 -0
- package/dist/components/p-RVQJEYxa.js +1 -0
- package/dist/components/p-d4rf3H8p.js +1 -0
- package/dist/components/{p-Dxa7c4Ap.js → p-dnaee7fS.js} +1 -1
- package/dist/components/p-f2xwcrXa.js +1 -0
- package/dist/esm/co-accordion.entry.js +3 -3
- package/dist/esm/co-action-bar.entry.js +3 -3
- package/dist/esm/co-app-shell.entry.js +3 -3
- package/dist/esm/co-audio-player.entry.js +4 -4
- package/dist/esm/co-autocomplete.entry.js +2 -2
- package/dist/esm/co-avatar-group.entry.js +1 -1
- package/dist/esm/co-avatar.entry.js +7 -4
- package/dist/esm/co-badge.entry.js +2 -2
- package/dist/esm/co-bottom-sheet.entry.js +5 -5
- package/dist/esm/co-breadcrumb-item.entry.js +3 -3
- package/dist/esm/co-breadcrumb.entry.js +2 -2
- package/dist/esm/co-button-group.entry.js +3 -3
- package/dist/esm/co-button.entry.js +9 -7
- package/dist/esm/co-calendar-month_2.entry.js +4 -4
- package/dist/esm/co-calendar_3.entry.js +72 -27
- package/dist/esm/co-card.entry.js +3 -3
- package/dist/esm/co-carousel-item.entry.js +2 -2
- package/dist/esm/co-carousel.entry.js +3 -3
- package/dist/esm/co-checkbox-group.entry.js +1 -1
- package/dist/esm/co-checkbox.entry.js +13 -5
- package/dist/esm/co-chip_2.entry.js +28 -6
- package/dist/esm/co-color-area.entry.js +167 -0
- package/dist/esm/co-color-picker.entry.js +5 -5
- package/dist/esm/co-color-slider.entry.js +187 -0
- package/dist/esm/co-column-picker.entry.js +3 -3
- package/dist/esm/co-combobox.entry.js +5 -5
- package/dist/esm/co-command-search.entry.js +5 -5
- package/dist/esm/co-data-table-pagination.entry.js +5 -5
- package/dist/esm/co-data-table.entry.js +3 -3
- package/dist/esm/co-date-picker.entry.js +2 -2
- package/dist/esm/co-date-range-picker.entry.js +2 -2
- package/dist/esm/co-date-range.entry.js +2 -2
- package/dist/esm/co-date-time-picker.entry.js +4 -4
- package/dist/esm/co-document-editor.entry.js +1159 -0
- package/dist/esm/co-drive-card.entry.js +2 -2
- package/dist/esm/co-drop-zone.entry.js +2 -2
- package/dist/esm/co-emoji-picker.entry.js +3 -3
- package/dist/esm/co-empty-state.entry.js +3 -3
- package/dist/esm/co-event-calendar.entry.js +3 -3
- package/dist/esm/co-event-popover.entry.js +3 -3
- package/dist/esm/co-fab.entry.js +3 -3
- package/dist/esm/co-file-field.entry.js +8 -8
- package/dist/esm/co-file-type-tile.entry.js +3 -3
- package/dist/esm/co-gradient-bar.entry.js +218 -0
- package/dist/esm/co-helper-text.entry.js +3 -3
- package/dist/esm/co-icon-button.entry.js +49 -9
- package/dist/esm/co-icon.entry.js +3 -3
- package/dist/esm/co-input.entry.js +14 -8
- package/dist/esm/co-json-field.entry.js +5 -5
- package/dist/esm/{co-tooltip.entry.js → co-kbd_2.entry.js} +194 -7
- package/dist/esm/co-lightbox.entry.js +5 -5
- package/dist/esm/co-list-footer.entry.js +2 -2
- package/dist/esm/co-list-item.entry.js +5 -5
- package/dist/esm/co-loader.entry.js +1 -1
- package/dist/esm/co-media-thumb.entry.js +3 -3
- package/dist/esm/co-menu-divider.entry.js +3 -3
- package/dist/esm/co-menu-item.entry.js +39 -41
- package/dist/esm/co-menu.entry.js +81 -5
- package/dist/esm/co-modal.entry.js +350 -7
- package/dist/esm/co-navbar.entry.js +3 -3
- package/dist/esm/co-number-field.entry.js +5 -5
- package/dist/esm/co-overlay.entry.js +8 -4
- package/dist/esm/co-page-header.entry.js +2 -2
- package/dist/esm/co-phone-field.entry.js +3 -3
- package/dist/esm/co-popover.entry.js +81 -6
- package/dist/esm/co-progress-bar.entry.js +3 -3
- package/dist/esm/co-progress-circle.entry.js +3 -3
- package/dist/esm/co-provider-logo_2.entry.js +2 -2
- 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 +5 -5
- package/dist/esm/co-rich-text-editor.entry.js +26 -92
- package/dist/esm/co-rich-text-toolbar.entry.js +79 -20
- package/dist/esm/co-rich-text-viewer.entry.js +9 -2
- package/dist/esm/co-row.entry.js +3 -3
- package/dist/esm/co-segmented-bar.entry.js +3 -3
- package/dist/esm/co-select.entry.js +158 -29
- package/dist/esm/co-separator.entry.js +2 -2
- package/dist/esm/co-sidebar-nav.entry.js +20 -5
- package/dist/esm/co-sidebar.entry.js +3 -3
- package/dist/esm/co-slider.entry.js +203 -23
- package/dist/esm/co-swatch-grid.entry.js +119 -0
- package/dist/esm/co-switch.entry.js +5 -5
- package/dist/esm/co-tab-group.entry.js +113 -4
- package/dist/esm/co-tab.entry.js +15 -5
- package/dist/esm/co-tags-field.entry.js +8 -8
- package/dist/esm/co-textarea.entry.js +6 -6
- package/dist/esm/co-tile.entry.js +59 -0
- package/dist/esm/co-time-field.entry.js +2 -2
- package/dist/esm/co-toast.entry.js +3 -3
- package/dist/esm/co-toggle-button-group.entry.js +26 -5
- package/dist/esm/co-toggle-button.entry.js +67 -6
- package/dist/esm/co-transfer-list.entry.js +3 -3
- package/dist/esm/co-tree-item.entry.js +108 -0
- package/dist/esm/co-video.entry.js +3 -3
- package/dist/esm/cobalt.js +3 -3
- package/dist/esm/{color-BRbgGqLw.js → color-BVGGRfNG.js} +10 -6
- package/dist/esm/{fieldChip-uyggU8VV.js → fieldChip-B2ctp2m6.js} +1 -1
- package/dist/esm/gradientStops-DDK0y8ra.js +145 -0
- package/dist/esm/hsv-COJdaKBU.js +90 -0
- package/dist/esm/index-Cpo8HTwr.js +142 -0
- package/dist/esm/{index-DH5lfRNj.js → index-DJkaRtbb.js} +67 -28
- package/dist/esm/loader.js +3 -3
- package/dist/esm/{index-BBIYyvq-.js → pagination-D9OfZUnY.js} +1728 -297
- package/dist/esm/{positioning-DFHRS9cJ.js → positioning-BtliEQAT.js} +45 -19
- package/dist/esm/{requiredMark-BCJUuZvC.js → requiredMark-D5C5NzOE.js} +1 -1
- package/dist/esm/rich-text-host-BmdKHBZP.js +111 -0
- package/dist/esm/{richTextI18n-BYKAgwq3.js → richTextI18n-xnrKkNWQ.js} +45 -0
- package/dist/esm/submenu-DU-KOQ8L.js +57 -0
- package/dist/esm/{toolbar-config-BAYo-RBt.js → toolbar-config-CRxTFT_z.js} +32 -0
- package/dist/types/components/co-avatar/co-avatar.d.ts +4 -2
- package/dist/types/components/co-button/co-button.d.ts +6 -4
- package/dist/types/components/co-calendar/calendarMarks.d.ts +15 -0
- package/dist/types/components/co-calendar/co-calendar.d.ts +16 -0
- package/dist/types/components/co-checkbox/co-checkbox.d.ts +7 -0
- package/dist/types/components/co-color-area/co-color-area.d.ts +64 -0
- package/dist/types/components/co-color-slider/co-color-slider.d.ts +61 -0
- package/dist/types/components/co-date-time-picker/co-date-time-picker.d.ts +1 -1
- package/dist/types/components/co-document-editor/change-throttle.d.ts +9 -0
- package/dist/types/components/co-document-editor/co-document-editor.d.ts +276 -0
- package/dist/types/components/co-document-editor/document-props.d.ts +6 -0
- package/dist/types/components/co-document-editor/header-tools.d.ts +11 -0
- package/dist/types/components/co-document-editor/image-bar.d.ts +50 -0
- package/dist/types/components/co-document-editor/page-styles.d.ts +1 -0
- package/dist/types/components/co-document-editor/zoom.d.ts +26 -0
- package/dist/types/components/co-gradient-bar/co-gradient-bar.d.ts +72 -0
- package/dist/types/components/co-icon-button/co-icon-button.d.ts +34 -2
- package/dist/types/components/co-input/co-input.d.ts +14 -5
- package/dist/types/components/co-kbd/co-kbd.d.ts +43 -0
- package/dist/types/components/co-menu/co-menu.d.ts +40 -1
- package/dist/types/components/co-menu-item/co-menu-item.d.ts +17 -0
- package/dist/types/components/co-modal/co-modal.d.ts +111 -2
- package/dist/types/components/co-option-list/co-option-list.d.ts +8 -0
- package/dist/types/components/co-option-list/optionListLogic.d.ts +3 -0
- package/dist/types/components/co-popover/co-popover.d.ts +38 -0
- package/dist/types/components/co-rich-text-editor/co-rich-text-editor.d.ts +2 -5
- package/dist/types/components/co-rich-text-toolbar/co-rich-text-toolbar.d.ts +17 -2
- package/dist/types/components/co-select/co-select.d.ts +47 -5
- package/dist/types/components/co-select/virtualRows.d.ts +44 -0
- package/dist/types/components/co-slider/co-slider.d.ts +143 -1
- package/dist/types/components/co-swatch-grid/co-swatch-grid.d.ts +37 -0
- package/dist/types/components/co-tab/co-tab.d.ts +12 -2
- package/dist/types/components/co-tab-group/co-tab-group.d.ts +37 -0
- package/dist/types/components/co-tab-group/revealTab.d.ts +29 -0
- package/dist/types/components/co-tile/co-tile.d.ts +40 -0
- package/dist/types/components/co-toggle-button/co-toggle-button.d.ts +37 -2
- package/dist/types/components/co-toggle-button-group/co-toggle-button-group.d.ts +20 -2
- package/dist/types/components/co-tooltip/co-tooltip.d.ts +12 -0
- package/dist/types/components/co-tree-item/co-tree-item.d.ts +73 -0
- package/dist/types/components/co-tree-item/treeItemI18n.d.ts +6 -0
- package/dist/types/components.d.ts +1748 -91
- package/dist/types/utils/color.d.ts +9 -1
- package/dist/types/utils/dragMove.d.ts +63 -0
- package/dist/types/utils/gradientBarI18n.d.ts +8 -0
- package/dist/types/utils/gradientStops.d.ts +63 -0
- package/dist/types/utils/hsv.d.ts +44 -0
- package/dist/types/utils/kbdI18n.d.ts +25 -0
- package/dist/types/utils/positioning.d.ts +24 -0
- package/dist/types/utils/rich-text-host.d.ts +41 -0
- package/dist/types/utils/richTextI18n.d.ts +15 -0
- package/dist/types/utils/submenu.d.ts +16 -2
- package/dist/types/utils/swatchGridI18n.d.ts +6 -0
- package/dist/types/utils/test/fake-block-layout.d.ts +7 -0
- package/dist/types/utils/test/prosemirror-dom-shim.d.ts +1 -0
- package/dist/types/utils/test/shared-contract-css.d.ts +9 -0
- package/package.json +5 -5
- package/dist/cobalt/p-0678c797.entry.js +0 -1
- package/dist/cobalt/p-216a63ee.entry.js +0 -1
- package/dist/cobalt/p-39dc4ec8.entry.js +0 -1
- package/dist/cobalt/p-3d31c575.entry.js +0 -1
- package/dist/cobalt/p-410bee4d.entry.js +0 -1
- package/dist/cobalt/p-416795f5.entry.js +0 -1
- package/dist/cobalt/p-56551580.entry.js +0 -1
- package/dist/cobalt/p-58137c9e.entry.js +0 -1
- package/dist/cobalt/p-5a3e4c81.entry.js +0 -1
- package/dist/cobalt/p-5c756787.entry.js +0 -1
- package/dist/cobalt/p-67f3b9d6.entry.js +0 -1
- package/dist/cobalt/p-6a4f3179.entry.js +0 -1
- package/dist/cobalt/p-71cbc53c.entry.js +0 -1
- package/dist/cobalt/p-72e52af7.entry.js +0 -1
- package/dist/cobalt/p-8530ad16.entry.js +0 -1
- package/dist/cobalt/p-8a74bf7d.entry.js +0 -1
- package/dist/cobalt/p-8e05b979.entry.js +0 -1
- package/dist/cobalt/p-8ebc0c12.entry.js +0 -1
- package/dist/cobalt/p-8f2199ff.entry.js +0 -1
- package/dist/cobalt/p-9246e09c.entry.js +0 -1
- package/dist/cobalt/p-99a655f6.entry.js +0 -1
- package/dist/cobalt/p-BYKAgwq3.js +0 -1
- package/dist/cobalt/p-CJW9H3Gi.js +0 -1
- package/dist/cobalt/p-DFHRS9cJ.js +0 -1
- package/dist/cobalt/p-DH5lfRNj.js +0 -2
- package/dist/cobalt/p-a1b422ca.entry.js +0 -1
- package/dist/cobalt/p-ade07307.entry.js +0 -1
- package/dist/cobalt/p-b698188c.entry.js +0 -1
- package/dist/cobalt/p-bde32f8b.entry.js +0 -1
- package/dist/cobalt/p-db805569.entry.js +0 -1
- package/dist/cobalt/p-e3aae282.entry.js +0 -1
- package/dist/cobalt/p-e49a564c.entry.js +0 -1
- package/dist/cobalt/p-ef301976.entry.js +0 -1
- package/dist/cobalt/p-f4e1c3c3.entry.js +0 -1
- package/dist/cobalt/p-fMqVDypp.js +0 -1
- package/dist/components/p-2EOI1nrz.js +0 -1
- package/dist/components/p-7TncUlLN.js +0 -1
- package/dist/components/p-B4NCaKrR.js +0 -1
- package/dist/components/p-BJh68Jqe.js +0 -1
- package/dist/components/p-BrAFTkAn.js +0 -1
- package/dist/components/p-Bsh0JMGf.js +0 -1
- package/dist/components/p-CJW9H3Gi.js +0 -1
- package/dist/components/p-CWCYYQrF.js +0 -1
- package/dist/components/p-D7Lhxz7L.js +0 -1
- package/dist/components/p-DFHRS9cJ.js +0 -1
- package/dist/components/p-DRP8D9ro.js +0 -1
- package/dist/components/p-DTPYZwaZ.js +0 -1
- package/dist/components/p-Dcg4krSX.js +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { p as purify } from './purify.es-B2QLgWrz.js';
|
|
2
|
-
import { C as COLOR_PALETTE, T as TABLE_CONTEXT_ITEMS, t as toolbarItems } from './toolbar-config-BAYo-RBt.js';
|
|
3
2
|
|
|
4
3
|
// ::- Persistent data structure representing an ordered mapping from
|
|
5
4
|
// strings to values, with some convenient update methods.
|
|
@@ -2655,7 +2654,7 @@ A DOM parser represents a strategy for parsing DOM content into a
|
|
|
2655
2654
|
ProseMirror document conforming to a given schema. Its behavior is
|
|
2656
2655
|
defined by an array of [rules](https://prosemirror.net/docs/ref/#model.ParseRule).
|
|
2657
2656
|
*/
|
|
2658
|
-
class DOMParser {
|
|
2657
|
+
let DOMParser$1 = class DOMParser {
|
|
2659
2658
|
/**
|
|
2660
2659
|
Create a parser that targets the given schema, using the given
|
|
2661
2660
|
parsing rules.
|
|
@@ -2806,7 +2805,7 @@ class DOMParser {
|
|
|
2806
2805
|
return schema.cached.domParser ||
|
|
2807
2806
|
(schema.cached.domParser = new DOMParser(schema, DOMParser.schemaRules(schema)));
|
|
2808
2807
|
}
|
|
2809
|
-
}
|
|
2808
|
+
};
|
|
2810
2809
|
const blockTags = {
|
|
2811
2810
|
address: true, article: true, aside: true, blockquote: true, canvas: true,
|
|
2812
2811
|
dd: true, div: true, dl: true, fieldset: true, figcaption: true, figure: true,
|
|
@@ -10005,7 +10004,7 @@ function skipIgnoredNodesBefore(view) {
|
|
|
10005
10004
|
break;
|
|
10006
10005
|
}
|
|
10007
10006
|
}
|
|
10008
|
-
else if (isBlockNode(node)) {
|
|
10007
|
+
else if (isBlockNode$1(node)) {
|
|
10009
10008
|
break;
|
|
10010
10009
|
}
|
|
10011
10010
|
else {
|
|
@@ -10053,7 +10052,7 @@ function skipIgnoredNodesAfter(view) {
|
|
|
10053
10052
|
else
|
|
10054
10053
|
break;
|
|
10055
10054
|
}
|
|
10056
|
-
else if (isBlockNode(node)) {
|
|
10055
|
+
else if (isBlockNode$1(node)) {
|
|
10057
10056
|
break;
|
|
10058
10057
|
}
|
|
10059
10058
|
else {
|
|
@@ -10079,7 +10078,7 @@ function skipIgnoredNodesAfter(view) {
|
|
|
10079
10078
|
if (moveNode)
|
|
10080
10079
|
setSelFocus(view, moveNode, moveOffset);
|
|
10081
10080
|
}
|
|
10082
|
-
function isBlockNode(dom) {
|
|
10081
|
+
function isBlockNode$1(dom) {
|
|
10083
10082
|
let desc = dom.pmViewDesc;
|
|
10084
10083
|
return desc && desc.node && desc.node.isBlock;
|
|
10085
10084
|
}
|
|
@@ -10359,7 +10358,7 @@ function parseFromClipboard(view, text, html, plainText, $context) {
|
|
|
10359
10358
|
dom = child;
|
|
10360
10359
|
}
|
|
10361
10360
|
if (!slice) {
|
|
10362
|
-
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
|
|
10361
|
+
let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser$1.fromSchema(view.state.schema);
|
|
10363
10362
|
slice = parser.parseSlice(dom, {
|
|
10364
10363
|
preserveWhitespace: !!(asText || sliceData),
|
|
10365
10364
|
context: $context,
|
|
@@ -12460,7 +12459,7 @@ function parseBetween(view, from_, to_) {
|
|
|
12460
12459
|
}
|
|
12461
12460
|
}
|
|
12462
12461
|
let startDoc = view.state.doc;
|
|
12463
|
-
let parser = view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
|
|
12462
|
+
let parser = view.someProp("domParser") || DOMParser$1.fromSchema(view.state.schema);
|
|
12464
12463
|
let $from = startDoc.resolve(from);
|
|
12465
12464
|
let sel = null, doc = parser.parse(parent, {
|
|
12466
12465
|
topNode: $from.parent,
|
|
@@ -14322,12 +14321,12 @@ function createNodeFromContent(content, schema, options) {
|
|
|
14322
14321
|
})
|
|
14323
14322
|
});
|
|
14324
14323
|
if (options.slice) {
|
|
14325
|
-
DOMParser.fromSchema(contentCheckSchema).parseSlice(
|
|
14324
|
+
DOMParser$1.fromSchema(contentCheckSchema).parseSlice(
|
|
14326
14325
|
elementFromString(content),
|
|
14327
14326
|
options.parseOptions
|
|
14328
14327
|
);
|
|
14329
14328
|
} else {
|
|
14330
|
-
DOMParser.fromSchema(contentCheckSchema).parse(
|
|
14329
|
+
DOMParser$1.fromSchema(contentCheckSchema).parse(
|
|
14331
14330
|
elementFromString(content),
|
|
14332
14331
|
options.parseOptions
|
|
14333
14332
|
);
|
|
@@ -14338,7 +14337,7 @@ function createNodeFromContent(content, schema, options) {
|
|
|
14338
14337
|
});
|
|
14339
14338
|
}
|
|
14340
14339
|
}
|
|
14341
|
-
const parser = DOMParser.fromSchema(schema);
|
|
14340
|
+
const parser = DOMParser$1.fromSchema(schema);
|
|
14342
14341
|
if (options.slice) {
|
|
14343
14342
|
return parser.parseSlice(elementFromString(content), options.parseOptions).content;
|
|
14344
14343
|
}
|
|
@@ -34994,6 +34993,799 @@ var Subscript = Mark.create({
|
|
|
34994
34993
|
}
|
|
34995
34994
|
});
|
|
34996
34995
|
|
|
34996
|
+
function sanitizeFieldName(raw) {
|
|
34997
|
+
return String(raw ?? '')
|
|
34998
|
+
.trim()
|
|
34999
|
+
.replace(/\s+/g, '_')
|
|
35000
|
+
.replace(/[^\p{L}\p{N}_.]/gu, '_');
|
|
35001
|
+
}
|
|
35002
|
+
/** Admite un grupo `[…]` al final del contenido (enum en JSON). Nueva instancia por llamada (flag g). */
|
|
35003
|
+
function fieldTokenRegex() {
|
|
35004
|
+
return /\[\[\s*([^[\]]+?(?:\[[^[\]]*\])?)\s*\]\]/g;
|
|
35005
|
+
}
|
|
35006
|
+
function parseFieldToken(inner) {
|
|
35007
|
+
const parts = String(inner ?? '').split('|').map((p) => p.trim());
|
|
35008
|
+
let raw = parts[0] ?? '';
|
|
35009
|
+
const required = raw.endsWith('*');
|
|
35010
|
+
if (required)
|
|
35011
|
+
raw = raw.slice(0, -1);
|
|
35012
|
+
const name = sanitizeFieldName(raw);
|
|
35013
|
+
if (!name)
|
|
35014
|
+
return null;
|
|
35015
|
+
const type = parts[1] || 'string';
|
|
35016
|
+
let values = null;
|
|
35017
|
+
if (parts[2]?.startsWith('enum:')) {
|
|
35018
|
+
const rawEnum = parts[2].slice(5).trim();
|
|
35019
|
+
try {
|
|
35020
|
+
const parsed = JSON.parse(rawEnum);
|
|
35021
|
+
values = Array.isArray(parsed) ? parsed.map(String) : null;
|
|
35022
|
+
}
|
|
35023
|
+
catch {
|
|
35024
|
+
values = rawEnum.split(',').map((v) => v.trim()).filter(Boolean);
|
|
35025
|
+
}
|
|
35026
|
+
}
|
|
35027
|
+
return { name, required, type, enum: values };
|
|
35028
|
+
}
|
|
35029
|
+
|
|
35030
|
+
/** Meta de las transacciones que escribe el HOST (valores): no son edición del usuario. */
|
|
35031
|
+
const FIELD_SYNC_META = 'coFieldSync';
|
|
35032
|
+
function parseEnumAttr(raw) {
|
|
35033
|
+
if (!raw)
|
|
35034
|
+
return null;
|
|
35035
|
+
try {
|
|
35036
|
+
const v = JSON.parse(raw);
|
|
35037
|
+
return Array.isArray(v) ? v.map(String) : null;
|
|
35038
|
+
}
|
|
35039
|
+
catch {
|
|
35040
|
+
return null;
|
|
35041
|
+
}
|
|
35042
|
+
}
|
|
35043
|
+
/** Lista de campos en orden de primera aparición (sin repetidos). Acepta un doc de ProseMirror o su JSON. */
|
|
35044
|
+
function collectFields(doc, into = []) {
|
|
35045
|
+
const index = new Map(into.map((f, i) => [f.name, i]));
|
|
35046
|
+
const visit = (attrs) => {
|
|
35047
|
+
const name = attrs?.name;
|
|
35048
|
+
if (!name)
|
|
35049
|
+
return;
|
|
35050
|
+
const at = index.get(name);
|
|
35051
|
+
if (at === undefined) {
|
|
35052
|
+
index.set(name, into.length);
|
|
35053
|
+
into.push({ name, required: !!attrs.required, type: attrs.type || 'string', enum: attrs.enum ?? null });
|
|
35054
|
+
}
|
|
35055
|
+
else if (attrs.required) {
|
|
35056
|
+
into[at].required = true;
|
|
35057
|
+
}
|
|
35058
|
+
};
|
|
35059
|
+
if (typeof doc.descendants === 'function') {
|
|
35060
|
+
doc.descendants((node) => {
|
|
35061
|
+
if (node.type.name === 'mergeField') {
|
|
35062
|
+
visit(node.attrs);
|
|
35063
|
+
return false;
|
|
35064
|
+
}
|
|
35065
|
+
return true;
|
|
35066
|
+
});
|
|
35067
|
+
}
|
|
35068
|
+
else {
|
|
35069
|
+
const walk = (node) => {
|
|
35070
|
+
if (!node)
|
|
35071
|
+
return;
|
|
35072
|
+
if (node.type === 'mergeField')
|
|
35073
|
+
visit(node.attrs ?? {});
|
|
35074
|
+
node.content?.forEach(walk);
|
|
35075
|
+
};
|
|
35076
|
+
walk(doc);
|
|
35077
|
+
}
|
|
35078
|
+
return into;
|
|
35079
|
+
}
|
|
35080
|
+
/** Textblocks tocados por las transacciones (para no recorrer todo el doc por tecla). */
|
|
35081
|
+
function touchedTextblocks(transactions, state) {
|
|
35082
|
+
let ranges = [];
|
|
35083
|
+
transactions.forEach((tr) => {
|
|
35084
|
+
// Lo tocado por transacciones anteriores se lleva a las posiciones de ésta.
|
|
35085
|
+
ranges = ranges.map(([a, b]) => [tr.mapping.map(a, -1), tr.mapping.map(b, 1)]);
|
|
35086
|
+
if (!tr.docChanged)
|
|
35087
|
+
return;
|
|
35088
|
+
tr.mapping.maps.forEach((map, i) => {
|
|
35089
|
+
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
|
35090
|
+
const rest = tr.mapping.slice(i + 1);
|
|
35091
|
+
ranges.push([rest.map(newStart, -1), rest.map(newEnd, 1)]);
|
|
35092
|
+
});
|
|
35093
|
+
});
|
|
35094
|
+
});
|
|
35095
|
+
const out = new Set();
|
|
35096
|
+
const size = state.doc.content.size;
|
|
35097
|
+
ranges.forEach(([from, to]) => {
|
|
35098
|
+
state.doc.nodesBetween(Math.max(0, from), Math.min(size, to), (node, pos) => {
|
|
35099
|
+
if (node.isTextblock) {
|
|
35100
|
+
out.add(pos);
|
|
35101
|
+
return false;
|
|
35102
|
+
}
|
|
35103
|
+
return true;
|
|
35104
|
+
});
|
|
35105
|
+
});
|
|
35106
|
+
return Array.from(out);
|
|
35107
|
+
}
|
|
35108
|
+
const MergeField = Node3.create({
|
|
35109
|
+
name: 'mergeField',
|
|
35110
|
+
group: 'inline',
|
|
35111
|
+
inline: true,
|
|
35112
|
+
atom: true,
|
|
35113
|
+
selectable: true,
|
|
35114
|
+
addOptions() {
|
|
35115
|
+
return { renameLabel: 'Field name' };
|
|
35116
|
+
},
|
|
35117
|
+
addAttributes() {
|
|
35118
|
+
return {
|
|
35119
|
+
name: { default: '', parseHTML: (el) => el.getAttribute('data-variable') || '', rendered: false },
|
|
35120
|
+
value: { default: '', parseHTML: (el) => el.getAttribute('data-value') ?? '', rendered: false },
|
|
35121
|
+
required: { default: false, parseHTML: (el) => el.getAttribute('data-required') === 'true', rendered: false },
|
|
35122
|
+
type: { default: 'string', parseHTML: (el) => el.getAttribute('data-type') || 'string', rendered: false },
|
|
35123
|
+
enum: { default: null, parseHTML: (el) => parseEnumAttr(el.getAttribute('data-enum')), rendered: false },
|
|
35124
|
+
};
|
|
35125
|
+
},
|
|
35126
|
+
// Prioridad 100: gana a las marcas `span[style]` (color/tamaño), como en V1.
|
|
35127
|
+
parseHTML() {
|
|
35128
|
+
return [{ tag: 'span[data-variable]', priority: 100 }];
|
|
35129
|
+
},
|
|
35130
|
+
// Forma canónica (spec §3.4): SIN estilos (D9), atributos en orden fijo.
|
|
35131
|
+
renderHTML({ node }) {
|
|
35132
|
+
const { name, value, type, required } = node.attrs;
|
|
35133
|
+
const attrs = {
|
|
35134
|
+
class: 'variable-node',
|
|
35135
|
+
'data-variable': name,
|
|
35136
|
+
'data-value': value ?? '',
|
|
35137
|
+
'data-type': type || 'string',
|
|
35138
|
+
'data-required': required ? 'true' : 'false',
|
|
35139
|
+
};
|
|
35140
|
+
if (node.attrs.enum)
|
|
35141
|
+
attrs['data-enum'] = JSON.stringify(node.attrs.enum);
|
|
35142
|
+
attrs.contenteditable = 'false';
|
|
35143
|
+
return ['span', attrs, value || `[[${name}]]`];
|
|
35144
|
+
},
|
|
35145
|
+
renderText({ node }) {
|
|
35146
|
+
return node.attrs.value || `[[${node.attrs.name}]]`;
|
|
35147
|
+
},
|
|
35148
|
+
addNodeView() {
|
|
35149
|
+
const renameLabel = this.options.renameLabel;
|
|
35150
|
+
return ({ node, getPos, editor }) => {
|
|
35151
|
+
const dom = document.createElement('span');
|
|
35152
|
+
dom.className = 'co-doc-field';
|
|
35153
|
+
dom.contentEditable = 'false';
|
|
35154
|
+
let current = node;
|
|
35155
|
+
let editing = false;
|
|
35156
|
+
const paint = (n) => {
|
|
35157
|
+
dom.textContent = n.attrs.value || `[[${n.attrs.name}]]`;
|
|
35158
|
+
dom.classList.toggle('co-doc-field--empty', !n.attrs.value);
|
|
35159
|
+
dom.classList.toggle('co-doc-field--required', !!n.attrs.required);
|
|
35160
|
+
dom.setAttribute('data-variable', n.attrs.name);
|
|
35161
|
+
};
|
|
35162
|
+
const startRename = () => {
|
|
35163
|
+
if (editing || !editor.isEditable)
|
|
35164
|
+
return;
|
|
35165
|
+
editing = true;
|
|
35166
|
+
const input = document.createElement('input');
|
|
35167
|
+
input.className = 'co-doc-field__input';
|
|
35168
|
+
input.value = current.attrs.name;
|
|
35169
|
+
input.setAttribute('aria-label', renameLabel);
|
|
35170
|
+
dom.textContent = '';
|
|
35171
|
+
dom.appendChild(input);
|
|
35172
|
+
input.focus();
|
|
35173
|
+
input.select();
|
|
35174
|
+
let done = false;
|
|
35175
|
+
const finish = (commit) => {
|
|
35176
|
+
if (done)
|
|
35177
|
+
return;
|
|
35178
|
+
done = true;
|
|
35179
|
+
editing = false;
|
|
35180
|
+
const next = sanitizeFieldName(input.value);
|
|
35181
|
+
const pos = typeof getPos === 'function' ? getPos() : undefined;
|
|
35182
|
+
if (commit && next && next !== current.attrs.name && typeof pos === 'number') {
|
|
35183
|
+
// Como V1: el campo renombrado arranca vacío (solo ESTE nodo).
|
|
35184
|
+
editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, undefined, { ...current.attrs, name: next, value: '' }));
|
|
35185
|
+
}
|
|
35186
|
+
else {
|
|
35187
|
+
paint(current);
|
|
35188
|
+
}
|
|
35189
|
+
};
|
|
35190
|
+
input.addEventListener('keydown', (ev) => {
|
|
35191
|
+
ev.stopPropagation(); // Esc no debe cerrar el overlay del host
|
|
35192
|
+
if (ev.key === 'Enter') {
|
|
35193
|
+
ev.preventDefault();
|
|
35194
|
+
finish(true);
|
|
35195
|
+
}
|
|
35196
|
+
else if (ev.key === 'Escape') {
|
|
35197
|
+
ev.preventDefault();
|
|
35198
|
+
finish(false);
|
|
35199
|
+
}
|
|
35200
|
+
});
|
|
35201
|
+
input.addEventListener('blur', () => finish(false));
|
|
35202
|
+
};
|
|
35203
|
+
dom.addEventListener('dblclick', (ev) => {
|
|
35204
|
+
ev.preventDefault();
|
|
35205
|
+
startRename();
|
|
35206
|
+
});
|
|
35207
|
+
paint(node);
|
|
35208
|
+
return {
|
|
35209
|
+
dom,
|
|
35210
|
+
update(next) {
|
|
35211
|
+
if (next.type.name !== 'mergeField')
|
|
35212
|
+
return false;
|
|
35213
|
+
current = next;
|
|
35214
|
+
if (!editing)
|
|
35215
|
+
paint(next);
|
|
35216
|
+
return true;
|
|
35217
|
+
},
|
|
35218
|
+
stopEvent: (ev) => ev.target instanceof HTMLInputElement,
|
|
35219
|
+
ignoreMutation: () => true,
|
|
35220
|
+
};
|
|
35221
|
+
};
|
|
35222
|
+
},
|
|
35223
|
+
addCommands() {
|
|
35224
|
+
return {
|
|
35225
|
+
insertField: (name, opts = {}) => ({ commands }) => {
|
|
35226
|
+
const clean = sanitizeFieldName(name);
|
|
35227
|
+
if (!clean)
|
|
35228
|
+
return false;
|
|
35229
|
+
return commands.insertContent({
|
|
35230
|
+
type: this.name,
|
|
35231
|
+
attrs: {
|
|
35232
|
+
name: clean,
|
|
35233
|
+
value: opts.value ?? '',
|
|
35234
|
+
required: !!opts.required,
|
|
35235
|
+
type: opts.type || 'string',
|
|
35236
|
+
enum: opts.enum ?? null,
|
|
35237
|
+
},
|
|
35238
|
+
});
|
|
35239
|
+
},
|
|
35240
|
+
setFieldValues: (values) => ({ tr, state, dispatch }) => {
|
|
35241
|
+
let changed = false;
|
|
35242
|
+
state.doc.descendants((node, pos) => {
|
|
35243
|
+
if (node.type.name !== this.name)
|
|
35244
|
+
return true;
|
|
35245
|
+
if (!Object.prototype.hasOwnProperty.call(values, node.attrs.name))
|
|
35246
|
+
return false;
|
|
35247
|
+
const next = String(values[node.attrs.name] ?? '');
|
|
35248
|
+
if (next !== node.attrs.value) {
|
|
35249
|
+
changed = true;
|
|
35250
|
+
if (dispatch)
|
|
35251
|
+
tr.setNodeMarkup(pos, undefined, { ...node.attrs, value: next });
|
|
35252
|
+
}
|
|
35253
|
+
return false;
|
|
35254
|
+
});
|
|
35255
|
+
if (!changed)
|
|
35256
|
+
return false;
|
|
35257
|
+
if (dispatch) {
|
|
35258
|
+
tr.setMeta(FIELD_SYNC_META, true);
|
|
35259
|
+
tr.setMeta('addToHistory', false);
|
|
35260
|
+
}
|
|
35261
|
+
return true;
|
|
35262
|
+
},
|
|
35263
|
+
renameField: (from, to) => ({ tr, state, dispatch }) => {
|
|
35264
|
+
const clean = sanitizeFieldName(to);
|
|
35265
|
+
if (!clean || clean === from)
|
|
35266
|
+
return false; // renombrar a sí mismo borraría los valores
|
|
35267
|
+
let changed = false;
|
|
35268
|
+
state.doc.descendants((node, pos) => {
|
|
35269
|
+
if (node.type.name !== this.name)
|
|
35270
|
+
return true;
|
|
35271
|
+
if (node.attrs.name === from) {
|
|
35272
|
+
changed = true;
|
|
35273
|
+
if (dispatch)
|
|
35274
|
+
tr.setNodeMarkup(pos, undefined, { ...node.attrs, name: clean, value: '' });
|
|
35275
|
+
}
|
|
35276
|
+
return false;
|
|
35277
|
+
});
|
|
35278
|
+
return changed;
|
|
35279
|
+
},
|
|
35280
|
+
};
|
|
35281
|
+
},
|
|
35282
|
+
addProseMirrorPlugins() {
|
|
35283
|
+
const type = this.type;
|
|
35284
|
+
return [
|
|
35285
|
+
new Plugin({
|
|
35286
|
+
key: new PluginKey('coMergeFieldTokens'),
|
|
35287
|
+
// Convierte `[[…]]` escritos o pegados en campos, solo en los textblocks tocados.
|
|
35288
|
+
appendTransaction: (transactions, _old, state) => {
|
|
35289
|
+
if (!transactions.some((t) => t.docChanged && !t.getMeta(FIELD_SYNC_META)))
|
|
35290
|
+
return null;
|
|
35291
|
+
const blocks = touchedTextblocks(transactions, state);
|
|
35292
|
+
if (!blocks.length)
|
|
35293
|
+
return null;
|
|
35294
|
+
const hits = [];
|
|
35295
|
+
blocks.forEach((blockPos) => {
|
|
35296
|
+
const block = state.doc.nodeAt(blockPos);
|
|
35297
|
+
if (!block || block.type.spec.code)
|
|
35298
|
+
return;
|
|
35299
|
+
block.forEach((child, offset) => {
|
|
35300
|
+
if (!child.isText || !child.text || !child.text.includes('[['))
|
|
35301
|
+
return;
|
|
35302
|
+
const re = fieldTokenRegex();
|
|
35303
|
+
let m;
|
|
35304
|
+
while ((m = re.exec(child.text)) !== null) {
|
|
35305
|
+
const def = parseFieldToken(m[1]);
|
|
35306
|
+
if (!def)
|
|
35307
|
+
continue;
|
|
35308
|
+
const from = blockPos + 1 + offset + m.index;
|
|
35309
|
+
hits.push({ from, to: from + m[0].length, def });
|
|
35310
|
+
}
|
|
35311
|
+
});
|
|
35312
|
+
});
|
|
35313
|
+
if (!hits.length)
|
|
35314
|
+
return null;
|
|
35315
|
+
const tr = state.tr;
|
|
35316
|
+
hits
|
|
35317
|
+
.sort((a, b) => b.from - a.from)
|
|
35318
|
+
.forEach(({ from, to, def }) => {
|
|
35319
|
+
tr.replaceWith(from, to, type.create({ ...def, value: '' }));
|
|
35320
|
+
});
|
|
35321
|
+
return tr;
|
|
35322
|
+
},
|
|
35323
|
+
}),
|
|
35324
|
+
];
|
|
35325
|
+
},
|
|
35326
|
+
});
|
|
35327
|
+
|
|
35328
|
+
/**
|
|
35329
|
+
* Marcador de firma `⟦sig:N⟧`. La forma canónica es la de V1
|
|
35330
|
+
* (tiptap-editor-html/utils/normalizeSignatureSlots.js) byte a byte: pdf.js lee
|
|
35331
|
+
* el token transparente de la capa de texto del PDF y deriva la posición.
|
|
35332
|
+
* Con font-size:1px Chromium colapsaba glifos: el token va a 8px y transparente.
|
|
35333
|
+
*
|
|
35334
|
+
* ⚠️ `editor.getHTML()` NO es la forma canónica: el DOMSerializer de ProseMirror
|
|
35335
|
+
* escribe `style` por CSSOM (`top:0` → `top: 0px;`). La forma exacta sale de
|
|
35336
|
+
* `signatureSlotHtml()` o de un serializador que escriba `style` con `setAttribute`
|
|
35337
|
+
* (`serializePrintHtml`).
|
|
35338
|
+
*/
|
|
35339
|
+
const signatureToken = (n) => `⟦sig:${n}⟧`;
|
|
35340
|
+
const SIGNATURE_TOKEN_STYLE = 'position:absolute;top:0;left:0;color:transparent;font-size:8px;line-height:1;white-space:nowrap;pointer-events:none;user-select:none';
|
|
35341
|
+
const signatureBoxStyle = (w, h) => `display:inline-block;position:relative;width:${w}px;height:${h}px;vertical-align:top`;
|
|
35342
|
+
const SIGNATURE_DEFAULT_W = 150;
|
|
35343
|
+
const SIGNATURE_DEFAULT_H = 50;
|
|
35344
|
+
function nextFreeSlot(used = []) {
|
|
35345
|
+
const taken = new Set(used);
|
|
35346
|
+
let n = 1;
|
|
35347
|
+
while (taken.has(n))
|
|
35348
|
+
n += 1;
|
|
35349
|
+
return n;
|
|
35350
|
+
}
|
|
35351
|
+
/** Spec de la forma canónica (orden de atributos = el de V1). Lo usa `renderHTML`. */
|
|
35352
|
+
function signatureSlotSpec({ slot, w, h }) {
|
|
35353
|
+
return [
|
|
35354
|
+
'span',
|
|
35355
|
+
{
|
|
35356
|
+
'data-sig-slot': String(slot),
|
|
35357
|
+
'data-w': String(w),
|
|
35358
|
+
'data-h': String(h),
|
|
35359
|
+
contenteditable: 'false',
|
|
35360
|
+
style: signatureBoxStyle(w, h),
|
|
35361
|
+
},
|
|
35362
|
+
['span', { style: SIGNATURE_TOKEN_STYLE }, signatureToken(slot)],
|
|
35363
|
+
];
|
|
35364
|
+
}
|
|
35365
|
+
/**
|
|
35366
|
+
* La forma canónica como string, armada del MISMO spec que `renderHTML` y sin pasar
|
|
35367
|
+
* por CSSOM. Los valores son números y constantes propias: no hay nada que escapar.
|
|
35368
|
+
*/
|
|
35369
|
+
function signatureSlotHtml(attrs) {
|
|
35370
|
+
const [tag, boxAttrs, [innerTag, innerAttrs, text]] = signatureSlotSpec(attrs);
|
|
35371
|
+
const attrsOf = (a) => Object.entries(a)
|
|
35372
|
+
.map(([k, v]) => ` ${k}="${v}"`)
|
|
35373
|
+
.join('');
|
|
35374
|
+
return `<${tag}${attrsOf(boxAttrs)}><${innerTag}${attrsOf(innerAttrs)}>${text}</${innerTag}></${tag}>`;
|
|
35375
|
+
}
|
|
35376
|
+
const toInt = (v, fallback) => {
|
|
35377
|
+
const n = parseInt(v ?? '', 10);
|
|
35378
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
35379
|
+
};
|
|
35380
|
+
const SignatureSlot = Node3.create({
|
|
35381
|
+
name: 'signatureSlot',
|
|
35382
|
+
group: 'inline',
|
|
35383
|
+
inline: true,
|
|
35384
|
+
atom: true,
|
|
35385
|
+
selectable: true,
|
|
35386
|
+
addOptions() {
|
|
35387
|
+
return { label: (slot) => `Signature ${slot}` };
|
|
35388
|
+
},
|
|
35389
|
+
addAttributes() {
|
|
35390
|
+
return {
|
|
35391
|
+
slot: { default: 1, parseHTML: (el) => toInt(el.getAttribute('data-sig-slot'), 1), rendered: false },
|
|
35392
|
+
w: { default: SIGNATURE_DEFAULT_W, parseHTML: (el) => toInt(el.getAttribute('data-w'), SIGNATURE_DEFAULT_W), rendered: false },
|
|
35393
|
+
h: { default: SIGNATURE_DEFAULT_H, parseHTML: (el) => toInt(el.getAttribute('data-h'), SIGNATURE_DEFAULT_H), rendered: false },
|
|
35394
|
+
};
|
|
35395
|
+
},
|
|
35396
|
+
parseHTML() {
|
|
35397
|
+
return [{ tag: 'span[data-sig-slot]', priority: 100 }];
|
|
35398
|
+
},
|
|
35399
|
+
renderHTML({ node }) {
|
|
35400
|
+
return signatureSlotSpec(node.attrs);
|
|
35401
|
+
},
|
|
35402
|
+
renderText({ node }) {
|
|
35403
|
+
return signatureToken(node.attrs.slot);
|
|
35404
|
+
},
|
|
35405
|
+
addNodeView() {
|
|
35406
|
+
const label = this.options.label;
|
|
35407
|
+
return ({ node }) => {
|
|
35408
|
+
const dom = document.createElement('span');
|
|
35409
|
+
dom.className = 'co-doc-signature';
|
|
35410
|
+
dom.contentEditable = 'false';
|
|
35411
|
+
const paint = (n) => {
|
|
35412
|
+
dom.style.width = `${n.attrs.w}px`;
|
|
35413
|
+
dom.style.height = `${n.attrs.h}px`;
|
|
35414
|
+
dom.textContent = label(n.attrs.slot);
|
|
35415
|
+
dom.setAttribute('data-sig-slot', String(n.attrs.slot));
|
|
35416
|
+
};
|
|
35417
|
+
paint(node);
|
|
35418
|
+
return {
|
|
35419
|
+
dom,
|
|
35420
|
+
update(next) {
|
|
35421
|
+
if (next.type.name !== 'signatureSlot')
|
|
35422
|
+
return false;
|
|
35423
|
+
paint(next);
|
|
35424
|
+
return true;
|
|
35425
|
+
},
|
|
35426
|
+
ignoreMutation: () => true,
|
|
35427
|
+
};
|
|
35428
|
+
};
|
|
35429
|
+
},
|
|
35430
|
+
addCommands() {
|
|
35431
|
+
return {
|
|
35432
|
+
insertSignatureSlot: (size = {}) => ({ state, commands }) => {
|
|
35433
|
+
const used = [];
|
|
35434
|
+
state.doc.descendants((n) => {
|
|
35435
|
+
if (n.type.name === this.name)
|
|
35436
|
+
used.push(n.attrs.slot);
|
|
35437
|
+
});
|
|
35438
|
+
return commands.insertContent({
|
|
35439
|
+
type: this.name,
|
|
35440
|
+
attrs: { slot: nextFreeSlot(used), w: size.w ?? SIGNATURE_DEFAULT_W, h: size.h ?? SIGNATURE_DEFAULT_H },
|
|
35441
|
+
});
|
|
35442
|
+
},
|
|
35443
|
+
};
|
|
35444
|
+
},
|
|
35445
|
+
});
|
|
35446
|
+
|
|
35447
|
+
/** Tamaño base del cuerpo de impresión (SharedContractUtils: 14px/1.5). */
|
|
35448
|
+
const BASE_FONT_PX = 14;
|
|
35449
|
+
/** Valor crudo de una propiedad del atributo style (conserva `#hex`; `el.style.color` lo pasaría a rgb). */
|
|
35450
|
+
function rawStyleValue(el, prop) {
|
|
35451
|
+
const style = el.getAttribute('style') || '';
|
|
35452
|
+
for (const decl of style.split(';')) {
|
|
35453
|
+
const i = decl.indexOf(':');
|
|
35454
|
+
if (i <= 0)
|
|
35455
|
+
continue;
|
|
35456
|
+
if (decl.slice(0, i).trim().toLowerCase() === prop) {
|
|
35457
|
+
const v = decl.slice(i + 1).trim();
|
|
35458
|
+
return v || null;
|
|
35459
|
+
}
|
|
35460
|
+
}
|
|
35461
|
+
return null;
|
|
35462
|
+
}
|
|
35463
|
+
/** V1 pasaba mal las unidades (12pt → 12px, 1.5em → 1px). Acá se convierten a px de verdad. */
|
|
35464
|
+
function normalizeFontSizePx(v) {
|
|
35465
|
+
if (v == null)
|
|
35466
|
+
return null;
|
|
35467
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*(px|pt|em|rem|%)?\s*$/i.exec(String(v));
|
|
35468
|
+
if (!m)
|
|
35469
|
+
return null;
|
|
35470
|
+
const n = parseFloat(m[1]);
|
|
35471
|
+
const unit = (m[2] || 'px').toLowerCase();
|
|
35472
|
+
const px = unit === 'px' ? n : unit === 'pt' ? (n * 4) / 3 : unit === '%' ? (n / 100) * BASE_FONT_PX : n * BASE_FONT_PX;
|
|
35473
|
+
const r = Math.round(px);
|
|
35474
|
+
return r >= 1 && r <= 400 ? `${r}px` : null;
|
|
35475
|
+
}
|
|
35476
|
+
/**
|
|
35477
|
+
* Color de texto en la forma de V1: `<span class="editor-text-style" style="color: X">`,
|
|
35478
|
+
* SIEMPRE por fuera del tamaño (un span con color + tamaño pierde el color en V1).
|
|
35479
|
+
* Se llama `textStyle` para que la barra lea `getAttributes('textStyle').color`.
|
|
35480
|
+
* Prioridad alta = marca más externa (link 1003 > color 1002 > tamaño 1001 > resto 100).
|
|
35481
|
+
*
|
|
35482
|
+
* La regla de parse queda con la prioridad por defecto (50): la firma
|
|
35483
|
+
* (`span[data-sig-slot]`) y el campo (`span[data-variable]`) van a 100 y le ganan
|
|
35484
|
+
* aunque lleven `style` inline.
|
|
35485
|
+
*/
|
|
35486
|
+
const PrintTextColor = Mark.create({
|
|
35487
|
+
name: 'textStyle',
|
|
35488
|
+
priority: 1002,
|
|
35489
|
+
addAttributes() {
|
|
35490
|
+
return {
|
|
35491
|
+
color: { default: null, parseHTML: (el) => rawStyleValue(el, 'color'), rendered: false },
|
|
35492
|
+
};
|
|
35493
|
+
},
|
|
35494
|
+
parseHTML() {
|
|
35495
|
+
return [{ tag: 'span', consuming: false, getAttrs: (el) => (rawStyleValue(el, 'color') ? {} : false) }];
|
|
35496
|
+
},
|
|
35497
|
+
renderHTML({ mark }) {
|
|
35498
|
+
return ['span', { class: 'editor-text-style', style: `color: ${mark.attrs.color}` }, 0];
|
|
35499
|
+
},
|
|
35500
|
+
addCommands() {
|
|
35501
|
+
return {
|
|
35502
|
+
setColor: (color) => ({ commands }) => commands.setMark(this.name, { color }),
|
|
35503
|
+
unsetColor: () => ({ commands }) => commands.unsetMark(this.name),
|
|
35504
|
+
};
|
|
35505
|
+
},
|
|
35506
|
+
});
|
|
35507
|
+
/** Tamaño en px como marca APARTE, anidada dentro del color: `<span style="font-size: Npx">`. */
|
|
35508
|
+
const PrintFontSize = Mark.create({
|
|
35509
|
+
name: 'printFontSize',
|
|
35510
|
+
priority: 1001,
|
|
35511
|
+
addAttributes() {
|
|
35512
|
+
return {
|
|
35513
|
+
size: { default: null, parseHTML: (el) => normalizeFontSizePx(rawStyleValue(el, 'font-size')), rendered: false },
|
|
35514
|
+
};
|
|
35515
|
+
},
|
|
35516
|
+
parseHTML() {
|
|
35517
|
+
return [{ tag: 'span', consuming: false, getAttrs: (el) => (normalizeFontSizePx(rawStyleValue(el, 'font-size')) ? {} : false) }];
|
|
35518
|
+
},
|
|
35519
|
+
renderHTML({ mark }) {
|
|
35520
|
+
return ['span', { style: `font-size: ${mark.attrs.size}` }, 0];
|
|
35521
|
+
},
|
|
35522
|
+
addCommands() {
|
|
35523
|
+
return {
|
|
35524
|
+
setFontSize: (size) => ({ commands }) => {
|
|
35525
|
+
const px = normalizeFontSizePx(size);
|
|
35526
|
+
return px ? commands.setMark(this.name, { size: px }) : false;
|
|
35527
|
+
},
|
|
35528
|
+
unsetFontSize: () => ({ commands }) => commands.unsetMark(this.name),
|
|
35529
|
+
};
|
|
35530
|
+
},
|
|
35531
|
+
});
|
|
35532
|
+
|
|
35533
|
+
const IMAGE_LAYOUT_MODES = ['inline', 'wrap-left', 'wrap-right', 'break'];
|
|
35534
|
+
const MODE_PROPS = new Set(['float', 'clear', 'display', 'padding-left', 'padding-right']);
|
|
35535
|
+
const MODE_STYLES = {
|
|
35536
|
+
inline: { wrapper: ['display: inline-block'], container: ['display: inline-block'] },
|
|
35537
|
+
'wrap-left': { wrapper: ['display: inline-block', 'float: left'], container: ['display: inline-block', 'float: left', 'padding-right: 8px'] },
|
|
35538
|
+
'wrap-right': { wrapper: ['display: inline-block', 'float: right'], container: ['display: inline-block', 'float: right', 'padding-left: 8px'] },
|
|
35539
|
+
break: { wrapper: ['display: block'], container: ['display: block'] },
|
|
35540
|
+
};
|
|
35541
|
+
/** Una imagen nueva nace en línea (ruling del ledger); al leer una de V1 manda su wrapperstyle. */
|
|
35542
|
+
const DEFAULT_WRAPPER_STYLE = 'display: inline-block;';
|
|
35543
|
+
const DEFAULT_CONTAINER_STYLE = 'height: auto; cursor: pointer; display: inline-block;';
|
|
35544
|
+
function stripModeProps(style) {
|
|
35545
|
+
return String(style || '')
|
|
35546
|
+
.split(';')
|
|
35547
|
+
.map((d) => d.trim())
|
|
35548
|
+
.filter((d) => d.includes(':'))
|
|
35549
|
+
.filter((d) => !MODE_PROPS.has(d.slice(0, d.indexOf(':')).trim().toLowerCase()));
|
|
35550
|
+
}
|
|
35551
|
+
function applyImageLayoutMode(mode, { wrapperStyle = '', containerStyle = '' } = {}) {
|
|
35552
|
+
const preset = MODE_STYLES[mode];
|
|
35553
|
+
if (!preset)
|
|
35554
|
+
return { wrapperStyle: wrapperStyle || '', containerStyle: containerStyle || '' };
|
|
35555
|
+
const join = (mods, rest) => [...mods, ...rest].join('; ').replace(/;?\s*$/, ';');
|
|
35556
|
+
return {
|
|
35557
|
+
wrapperStyle: join(preset.wrapper, stripModeProps(wrapperStyle || '')),
|
|
35558
|
+
containerStyle: join(preset.container, stripModeProps(containerStyle || '')),
|
|
35559
|
+
};
|
|
35560
|
+
}
|
|
35561
|
+
function getImageLayoutMode({ wrapperStyle = '', containerStyle = '' } = {}) {
|
|
35562
|
+
const style = `${containerStyle || ''};${wrapperStyle || ''}`.toLowerCase();
|
|
35563
|
+
if (/float:\s*left/.test(style))
|
|
35564
|
+
return 'wrap-left';
|
|
35565
|
+
if (/float:\s*right/.test(style))
|
|
35566
|
+
return 'wrap-right';
|
|
35567
|
+
if (/display:\s*block\b/.test(style))
|
|
35568
|
+
return 'break';
|
|
35569
|
+
return 'inline';
|
|
35570
|
+
}
|
|
35571
|
+
const HEADER_IMG_MAX = { w: 400, h: 40 };
|
|
35572
|
+
/** Ancho que entra en la banda del encabezado (port de headerContainerStyle de V1). */
|
|
35573
|
+
function headerImageWidth(dims, max = HEADER_IMG_MAX) {
|
|
35574
|
+
if (!dims || !dims.w || !dims.h)
|
|
35575
|
+
return null;
|
|
35576
|
+
const scale = Math.min(1, max.h / dims.h, max.w / dims.w);
|
|
35577
|
+
return Math.max(1, Math.round(dims.w * scale));
|
|
35578
|
+
}
|
|
35579
|
+
/**
|
|
35580
|
+
* `width:` anclado a inicio de declaración: sin el ancla, `min-width`/`max-width` también
|
|
35581
|
+
* matchean y se leía (y se pisaba) el ancho equivocado de un `containerstyle` de V1.
|
|
35582
|
+
*/
|
|
35583
|
+
const WIDTH_DECL = /(^|;)(\s*)width:\s*([0-9.]+)px/i;
|
|
35584
|
+
function withWidth(style, width) {
|
|
35585
|
+
if (!width)
|
|
35586
|
+
return style;
|
|
35587
|
+
const decl = `width: ${width}px`;
|
|
35588
|
+
return WIDTH_DECL.test(style)
|
|
35589
|
+
? style.replace(WIDTH_DECL, (_m, sep, ws) => `${sep}${ws}${decl}`)
|
|
35590
|
+
: `${decl}; ${style}`.trim();
|
|
35591
|
+
}
|
|
35592
|
+
function parseWidth(el) {
|
|
35593
|
+
const attr = parseInt(el.getAttribute('width') || '', 10);
|
|
35594
|
+
if (attr > 0)
|
|
35595
|
+
return attr;
|
|
35596
|
+
const m = WIDTH_DECL.exec(el.getAttribute('containerstyle') || el.getAttribute('style') || '');
|
|
35597
|
+
return m ? Math.round(parseFloat(m[3])) : null;
|
|
35598
|
+
}
|
|
35599
|
+
/**
|
|
35600
|
+
* H10: el node view de resize de Tiptap solo copia los atributos RENDERIZADOS, y
|
|
35601
|
+
* `wrapperStyle`/`containerStyle` no lo son → en el editor toda imagen se veía en línea.
|
|
35602
|
+
* Se lleva el modo al contenedor (`[data-resize-container]`) para que la hoja pagine
|
|
35603
|
+
* como el PDF. `inline-flex`/`flex` (no `inline-block`/`block`): el contenedor de
|
|
35604
|
+
* resize los necesita para ubicar las manijas.
|
|
35605
|
+
*/
|
|
35606
|
+
function syncImageModeToContainer(container, node) {
|
|
35607
|
+
const mode = getImageLayoutMode(node.attrs);
|
|
35608
|
+
container.dataset.imageLayout = mode;
|
|
35609
|
+
container.style.float = mode === 'wrap-left' ? 'left' : mode === 'wrap-right' ? 'right' : '';
|
|
35610
|
+
container.style.paddingLeft = mode === 'wrap-right' ? '8px' : '';
|
|
35611
|
+
container.style.paddingRight = mode === 'wrap-left' ? '8px' : '';
|
|
35612
|
+
container.style.display = mode === 'break' ? 'flex' : 'inline-flex';
|
|
35613
|
+
}
|
|
35614
|
+
const PrintImage = Image.extend({
|
|
35615
|
+
name: 'image',
|
|
35616
|
+
addOptions() {
|
|
35617
|
+
return { ...this.parent?.(), header: false };
|
|
35618
|
+
},
|
|
35619
|
+
addAttributes() {
|
|
35620
|
+
return {
|
|
35621
|
+
...this.parent?.(),
|
|
35622
|
+
width: { default: null, parseHTML: (el) => parseWidth(el), rendered: false },
|
|
35623
|
+
dataFileId: { default: null, parseHTML: (el) => el.getAttribute('data-file-id'), rendered: false },
|
|
35624
|
+
containerStyle: { default: null, parseHTML: (el) => el.getAttribute('containerstyle'), rendered: false },
|
|
35625
|
+
wrapperStyle: { default: null, parseHTML: (el) => el.getAttribute('wrapperstyle'), rendered: false },
|
|
35626
|
+
};
|
|
35627
|
+
},
|
|
35628
|
+
parseHTML() {
|
|
35629
|
+
return [{ tag: this.options.allowBase64 ? 'img[src]' : 'img[src]:not([src^="data:"])', priority: 100 }];
|
|
35630
|
+
},
|
|
35631
|
+
renderHTML({ node }) {
|
|
35632
|
+
const a = node.attrs;
|
|
35633
|
+
const width = a.width ? Math.round(Number(a.width)) : null;
|
|
35634
|
+
const wrapper = a.wrapperStyle || DEFAULT_WRAPPER_STYLE;
|
|
35635
|
+
const container = withWidth(a.containerStyle || DEFAULT_CONTAINER_STYLE, width);
|
|
35636
|
+
const img = { src: a.src };
|
|
35637
|
+
if (a.alt)
|
|
35638
|
+
img.alt = a.alt;
|
|
35639
|
+
if (a.title)
|
|
35640
|
+
img.title = a.title;
|
|
35641
|
+
if (a.dataFileId)
|
|
35642
|
+
img['data-file-id'] = String(a.dataFileId);
|
|
35643
|
+
if (width) {
|
|
35644
|
+
img.width = String(width);
|
|
35645
|
+
img.style = this.options.header ? `width: ${width}px; height: auto;` : `width: ${width}px;`;
|
|
35646
|
+
}
|
|
35647
|
+
img.containerstyle = container;
|
|
35648
|
+
img.wrapperstyle = wrapper;
|
|
35649
|
+
return ['span', { style: wrapper }, ['img', img]];
|
|
35650
|
+
},
|
|
35651
|
+
addNodeView() {
|
|
35652
|
+
const parent = this.parent?.();
|
|
35653
|
+
if (!parent)
|
|
35654
|
+
return null;
|
|
35655
|
+
return (props) => {
|
|
35656
|
+
const view = parent(props);
|
|
35657
|
+
const container = view.dom;
|
|
35658
|
+
syncImageModeToContainer(container, props.node);
|
|
35659
|
+
const update = view.update?.bind(view);
|
|
35660
|
+
view.update = (node, decorations, inner) => {
|
|
35661
|
+
const ok = update ? update(node, decorations, inner) : node.type === props.node.type;
|
|
35662
|
+
if (ok)
|
|
35663
|
+
syncImageModeToContainer(container, node);
|
|
35664
|
+
return ok;
|
|
35665
|
+
};
|
|
35666
|
+
return view;
|
|
35667
|
+
};
|
|
35668
|
+
},
|
|
35669
|
+
addCommands() {
|
|
35670
|
+
return {
|
|
35671
|
+
...this.parent?.(),
|
|
35672
|
+
setImageLayoutMode: (mode) => ({ state, tr, dispatch }) => {
|
|
35673
|
+
const sel = state.selection;
|
|
35674
|
+
if (!(sel instanceof NodeSelection) || sel.node.type.name !== this.name)
|
|
35675
|
+
return false;
|
|
35676
|
+
if (dispatch) {
|
|
35677
|
+
const next = applyImageLayoutMode(mode, {
|
|
35678
|
+
wrapperStyle: sel.node.attrs.wrapperStyle || DEFAULT_WRAPPER_STYLE,
|
|
35679
|
+
containerStyle: withWidth(sel.node.attrs.containerStyle || DEFAULT_CONTAINER_STYLE, sel.node.attrs.width),
|
|
35680
|
+
});
|
|
35681
|
+
tr.setNodeMarkup(sel.from, undefined, { ...sel.node.attrs, ...next });
|
|
35682
|
+
// H7: la NodeSelection no sobrevive al reemplazo de la hoja; sin esto la barra de imagen se cierra.
|
|
35683
|
+
tr.setSelection(NodeSelection.create(tr.doc, sel.from));
|
|
35684
|
+
}
|
|
35685
|
+
return true;
|
|
35686
|
+
},
|
|
35687
|
+
};
|
|
35688
|
+
},
|
|
35689
|
+
});
|
|
35690
|
+
|
|
35691
|
+
/** Validación de V1 (tiptap-editor-html/extensions/index.js): exige un `.`, rechaza `/` y `./`. */
|
|
35692
|
+
const printLinkAllowed = (url, ctx) => ctx.defaultValidate(url) && !url.startsWith('./') && !url.startsWith('/') && url.includes('.');
|
|
35693
|
+
/** Autolink APAGADO: generaba `<a>` dentro de `{{ }}` y rompía Handlebars. Prioridad 1003 = marca más externa. */
|
|
35694
|
+
const PrintLink = Link.extend({ inclusive: () => false, priority: 1003 }).configure({
|
|
35695
|
+
autolink: false,
|
|
35696
|
+
openOnClick: false,
|
|
35697
|
+
defaultProtocol: 'https',
|
|
35698
|
+
protocols: ['mailto', { scheme: 'tel', optionalSlashes: true }],
|
|
35699
|
+
HTMLAttributes: { target: '_blank', rel: 'noopener noreferrer', class: 'editor-link' },
|
|
35700
|
+
isAllowedUri: printLinkAllowed,
|
|
35701
|
+
});
|
|
35702
|
+
|
|
35703
|
+
/** Resalta `{{ … }}` con una DECORACIÓN: no toca el documento ni se serializa. */
|
|
35704
|
+
const HB_RE = /\{\{[^}]*\}\}/g;
|
|
35705
|
+
function build(doc) {
|
|
35706
|
+
const decos = [];
|
|
35707
|
+
doc.descendants((node, pos) => {
|
|
35708
|
+
if (!node.isText || !node.text)
|
|
35709
|
+
return true;
|
|
35710
|
+
HB_RE.lastIndex = 0;
|
|
35711
|
+
let m;
|
|
35712
|
+
while ((m = HB_RE.exec(node.text)) !== null) {
|
|
35713
|
+
decos.push(Decoration.inline(pos + m.index, pos + m.index + m[0].length, { class: 'co-doc-hb' }));
|
|
35714
|
+
}
|
|
35715
|
+
return true;
|
|
35716
|
+
});
|
|
35717
|
+
return DecorationSet.create(doc, decos);
|
|
35718
|
+
}
|
|
35719
|
+
const HandlebarsHighlight = Extension.create({
|
|
35720
|
+
name: 'handlebarsHighlight',
|
|
35721
|
+
addProseMirrorPlugins() {
|
|
35722
|
+
const key = new PluginKey('coHandlebarsHighlight');
|
|
35723
|
+
return [
|
|
35724
|
+
new Plugin({
|
|
35725
|
+
key,
|
|
35726
|
+
state: {
|
|
35727
|
+
init: (_config, { doc }) => build(doc),
|
|
35728
|
+
apply: (tr, old) => (tr.docChanged ? build(tr.doc) : old),
|
|
35729
|
+
},
|
|
35730
|
+
props: {
|
|
35731
|
+
decorations(state) {
|
|
35732
|
+
return key.getState(state);
|
|
35733
|
+
},
|
|
35734
|
+
},
|
|
35735
|
+
}),
|
|
35736
|
+
];
|
|
35737
|
+
},
|
|
35738
|
+
});
|
|
35739
|
+
|
|
35740
|
+
/**
|
|
35741
|
+
* Presets `print` (cuerpo) y `print-header` (banda). Paridad ESTRICTA de
|
|
35742
|
+
* funciones de V1 (spec §3.5/§3.6, D7): sin subrayado, familia, peso,
|
|
35743
|
+
* interlineado, espaciado ni sangría; autolink apagado; sin nodo `variable`
|
|
35744
|
+
* (comparte selector `span[data-variable]` con el campo). `trailingNode` apagado:
|
|
35745
|
+
* V1 no lo tenía y una tabla al final sumaría una línea vacía al PDF.
|
|
35746
|
+
* Las etiquetas (`labels`) las localiza el host; sin ellas quedan en inglés.
|
|
35747
|
+
*/
|
|
35748
|
+
function getPrintExtensions(preset, opts = {}) {
|
|
35749
|
+
const header = preset === 'print-header';
|
|
35750
|
+
const { features = {}, labels = {} } = opts;
|
|
35751
|
+
const starter = { underline: false, link: false, trailingNode: false };
|
|
35752
|
+
if (header) {
|
|
35753
|
+
Object.assign(starter, {
|
|
35754
|
+
heading: false, bulletList: false, orderedList: false, listItem: false, listKeymap: false,
|
|
35755
|
+
blockquote: false, codeBlock: false, horizontalRule: false,
|
|
35756
|
+
});
|
|
35757
|
+
}
|
|
35758
|
+
else {
|
|
35759
|
+
starter.heading = { levels: [1, 2, 3, 4] };
|
|
35760
|
+
}
|
|
35761
|
+
const exts = [
|
|
35762
|
+
StarterKit.configure(starter),
|
|
35763
|
+
PrintTextColor,
|
|
35764
|
+
PrintFontSize,
|
|
35765
|
+
Highlight.configure({ multicolor: true }),
|
|
35766
|
+
TextAlign.configure({ types: header ? ['paragraph'] : ['heading', 'paragraph'] }),
|
|
35767
|
+
HandlebarsHighlight,
|
|
35768
|
+
];
|
|
35769
|
+
if (!header) {
|
|
35770
|
+
exts.push(PrintLink, Table.configure({ resizable: true, renderWrapper: true }), TableRow, TableHeader, TableCell);
|
|
35771
|
+
}
|
|
35772
|
+
exts.push(PrintImage.configure({
|
|
35773
|
+
inline: true,
|
|
35774
|
+
allowBase64: true,
|
|
35775
|
+
header,
|
|
35776
|
+
resize: header
|
|
35777
|
+
? false
|
|
35778
|
+
: { enabled: true, directions: ['left', 'right', 'bottom-left', 'bottom-right'], minWidth: 50, minHeight: 20, alwaysPreserveAspectRatio: true },
|
|
35779
|
+
}));
|
|
35780
|
+
if (features.fields !== false) {
|
|
35781
|
+
exts.push(MergeField.configure({ renameLabel: labels.fieldName ?? 'Field name' }));
|
|
35782
|
+
}
|
|
35783
|
+
if (!header && features.signatureSlots !== false) {
|
|
35784
|
+
exts.push(labels.signature ? SignatureSlot.configure({ label: labels.signature }) : SignatureSlot);
|
|
35785
|
+
}
|
|
35786
|
+
return exts;
|
|
35787
|
+
}
|
|
35788
|
+
|
|
34997
35789
|
/** Shared link configuration — never auto-opens inside the editor, safe rel. */
|
|
34998
35790
|
const LINK_CONFIG = {
|
|
34999
35791
|
openOnClick: false,
|
|
@@ -35072,6 +35864,8 @@ const PRESET_TOOLBAR = {
|
|
|
35072
35864
|
email: 'full',
|
|
35073
35865
|
document: 'full',
|
|
35074
35866
|
'block-email': 'basic',
|
|
35867
|
+
print: 'document',
|
|
35868
|
+
'print-header': 'document',
|
|
35075
35869
|
};
|
|
35076
35870
|
/** Suggested max length per preset (null = no limit). */
|
|
35077
35871
|
const PRESET_MAXLENGTH = {
|
|
@@ -35081,10 +35875,22 @@ const PRESET_MAXLENGTH = {
|
|
|
35081
35875
|
email: null,
|
|
35082
35876
|
document: null,
|
|
35083
35877
|
'block-email': null,
|
|
35878
|
+
print: null,
|
|
35879
|
+
'print-header': null,
|
|
35084
35880
|
};
|
|
35085
35881
|
/** Build the ordered extension array for a preset. */
|
|
35086
35882
|
function getExtensions(preset = 'task', options = {}) {
|
|
35087
|
-
const { placeholder, maxLength, features = {}, includeUiExtensions = true } = options;
|
|
35883
|
+
const { placeholder, maxLength, features = {}, includeUiExtensions = true, labels } = options;
|
|
35884
|
+
// Presets de impresión: schema propio (spec contracts-v2-contract-editor §3.5/§3.6).
|
|
35885
|
+
if (preset === 'print' || preset === 'print-header') {
|
|
35886
|
+
const exts = getPrintExtensions(preset, {
|
|
35887
|
+
features: { fields: features.fields, signatureSlots: features.signatureSlots },
|
|
35888
|
+
labels,
|
|
35889
|
+
});
|
|
35890
|
+
if (includeUiExtensions && placeholder)
|
|
35891
|
+
exts.push(Placeholder.configure({ placeholder }));
|
|
35892
|
+
return exts;
|
|
35893
|
+
}
|
|
35088
35894
|
const config = STARTERKIT_CONFIG[preset] ?? STARTERKIT_CONFIG.task;
|
|
35089
35895
|
const exts = [StarterKit.configure(config), CobaltLink];
|
|
35090
35896
|
// Color requires TextStyle to be registered first. Only presets that keep
|
|
@@ -35182,7 +35988,573 @@ function defaultToolbarForPreset(preset = 'task') {
|
|
|
35182
35988
|
function maxLengthForPreset(preset = 'task') {
|
|
35183
35989
|
return PRESET_MAXLENGTH[preset] ?? null;
|
|
35184
35990
|
}
|
|
35185
|
-
const PRESETS = ['comment', 'task', 'notes', 'email', 'document', 'block-email'];
|
|
35991
|
+
const PRESETS = ['comment', 'task', 'notes', 'email', 'document', 'block-email', 'print', 'print-header'];
|
|
35992
|
+
|
|
35993
|
+
// src/generateHTML.ts
|
|
35994
|
+
function getHTMLFromFragment(doc, schema, options) {
|
|
35995
|
+
if (typeof window === "undefined") {
|
|
35996
|
+
throw new Error(
|
|
35997
|
+
"getHTMLFromFragment can only be used in a browser environment\nIf you want to use this in a Node environment, use the `@tiptap/html/server` import instead."
|
|
35998
|
+
);
|
|
35999
|
+
}
|
|
36000
|
+
const wrap = window.document.createElement("div");
|
|
36001
|
+
DOMSerializer.fromSchema(schema).serializeFragment(
|
|
36002
|
+
doc.content,
|
|
36003
|
+
{
|
|
36004
|
+
document: window.document
|
|
36005
|
+
},
|
|
36006
|
+
wrap
|
|
36007
|
+
);
|
|
36008
|
+
return wrap.innerHTML;
|
|
36009
|
+
}
|
|
36010
|
+
|
|
36011
|
+
// src/generateHTML.ts
|
|
36012
|
+
function generateHTML(doc, extensions) {
|
|
36013
|
+
if (typeof window === "undefined") {
|
|
36014
|
+
throw new Error(
|
|
36015
|
+
"generateHTML can only be used in a browser environment\nIf you want to use this in a Node environment, use the `@tiptap/html/server` import instead."
|
|
36016
|
+
);
|
|
36017
|
+
}
|
|
36018
|
+
const schema = getSchema(extensions);
|
|
36019
|
+
const contentNode = Node$1.fromJSON(schema, doc);
|
|
36020
|
+
return getHTMLFromFragment(contentNode, schema);
|
|
36021
|
+
}
|
|
36022
|
+
function generateJSON(html, extensions, options) {
|
|
36023
|
+
if (typeof window === "undefined") {
|
|
36024
|
+
throw new Error(
|
|
36025
|
+
"generateJSON can only be used in a browser environment\nIf you want to use this in a Node environment, use the `@tiptap/html/server` import instead."
|
|
36026
|
+
);
|
|
36027
|
+
}
|
|
36028
|
+
const schema = getSchema(extensions);
|
|
36029
|
+
const doc = new window.DOMParser().parseFromString(html, "text/html");
|
|
36030
|
+
if (!doc) {
|
|
36031
|
+
throw new Error("Failed to parse HTML string");
|
|
36032
|
+
}
|
|
36033
|
+
return DOMParser$1.fromSchema(schema).parse(doc.body, options).toJSON();
|
|
36034
|
+
}
|
|
36035
|
+
|
|
36036
|
+
const BASE_INLINE = ['p', 'br', 'strong', 'em', 'a', 'span'];
|
|
36037
|
+
const BASE_ATTR = ['href', 'target', 'rel', 'data-mention-id', 'data-mention-label'];
|
|
36038
|
+
const SANITIZE_CONFIGS = {
|
|
36039
|
+
// Más ancho que su schema A PROPÓSITO (única excepción al lock-step): el
|
|
36040
|
+
// editor `comment` no puede autorar listas ni headings (STARTERKIT_CONFIG los
|
|
36041
|
+
// apaga y ProseMirror descarta esos nodos al parsear el paste), pero el
|
|
36042
|
+
// VIEWER tiene que pintarlos cuando el HTML entra por API — una nota de
|
|
36043
|
+
// enriquecimiento (Apollo.io) trae <ul>/<li>/<h4> y con KEEP_CONTENT el
|
|
36044
|
+
// texto se hoisteaba al padre: lista corrida, heading fundido en el cuerpo.
|
|
36045
|
+
// Espejo inline en core/co-rich-text-viewer.tsx (ALLOWED_TAGS.comment).
|
|
36046
|
+
comment: {
|
|
36047
|
+
ALLOWED_TAGS: [...BASE_INLINE, 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4'],
|
|
36048
|
+
ALLOWED_ATTR: [...BASE_ATTR],
|
|
36049
|
+
ALLOW_DATA_ATTR: false,
|
|
36050
|
+
},
|
|
36051
|
+
task: {
|
|
36052
|
+
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'input', 'label'],
|
|
36053
|
+
ALLOWED_ATTR: [...BASE_ATTR, 'type', 'checked', 'disabled', 'data-type', 'data-checked', 'data-variant'],
|
|
36054
|
+
ALLOW_DATA_ATTR: false,
|
|
36055
|
+
},
|
|
36056
|
+
// Lock-step con getExtensions('notes'): task sin checklists + TextStyle/Color
|
|
36057
|
+
// (`style` capado a STYLE_ALLOWED_PROPS) + Highlight multicolor (`mark` con
|
|
36058
|
+
// `data-color`).
|
|
36059
|
+
notes: {
|
|
36060
|
+
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'mark'],
|
|
36061
|
+
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-color'],
|
|
36062
|
+
ALLOW_DATA_ATTR: false,
|
|
36063
|
+
},
|
|
36064
|
+
email: {
|
|
36065
|
+
ALLOWED_TAGS: [
|
|
36066
|
+
...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote',
|
|
36067
|
+
'h2', 'h3', 'div', 'img',
|
|
36068
|
+
],
|
|
36069
|
+
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'data-transform', 'data-fallback', 'src', 'alt', 'width', 'height', 'data-file-id', 'data-image-layout'],
|
|
36070
|
+
ALLOW_DATA_ATTR: false,
|
|
36071
|
+
},
|
|
36072
|
+
document: {
|
|
36073
|
+
ALLOWED_TAGS: [
|
|
36074
|
+
...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote',
|
|
36075
|
+
'h1', 'h2', 'h3', 'h4', 'hr', 'div', 'mark', 'img',
|
|
36076
|
+
'table', 'thead', 'tbody', 'tr', 'th', 'td',
|
|
36077
|
+
'input', 'label', 'iframe',
|
|
36078
|
+
],
|
|
36079
|
+
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'colspan', 'rowspan', 'src', 'alt', 'width', 'height', 'type', 'checked', 'disabled', 'allowfullscreen', 'frameborder', 'allow', 'loading'],
|
|
36080
|
+
ALLOW_DATA_ATTR: true,
|
|
36081
|
+
},
|
|
36082
|
+
'block-email': {
|
|
36083
|
+
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'h2', 'h3', 'div', 'img'],
|
|
36084
|
+
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'data-transform', 'data-fallback', 'src', 'alt', 'width', 'height', 'data-file-id', 'data-image-layout', 'data-type', 'data-label', 'data-href', 'data-bg', 'data-color', 'data-height'],
|
|
36085
|
+
ALLOW_DATA_ATTR: false,
|
|
36086
|
+
},
|
|
36087
|
+
// Espejo de getPrintExtensions('print'): cuerpo del documento de impresión.
|
|
36088
|
+
// `class` y data-* son parte de la forma que V1 re-parsea (editor-text-style,
|
|
36089
|
+
// variable-node, editor-link, tableWrapper, trailingBreak, data-variable…).
|
|
36090
|
+
print: {
|
|
36091
|
+
ALLOWED_TAGS: [
|
|
36092
|
+
'p', 'br', 'strong', 'em', 's', 'code', 'a', 'span', 'mark',
|
|
36093
|
+
'h1', 'h2', 'h3', 'h4', 'ul', 'ol', 'li', 'blockquote', 'pre', 'hr', 'img', 'div',
|
|
36094
|
+
'table', 'colgroup', 'col', 'thead', 'tbody', 'tr', 'th', 'td',
|
|
36095
|
+
],
|
|
36096
|
+
ALLOWED_ATTR: [
|
|
36097
|
+
'href', 'target', 'rel', 'class', 'style', 'src', 'alt', 'title', 'width', 'height',
|
|
36098
|
+
'colspan', 'rowspan', 'colwidth', 'start', 'contenteditable', 'containerstyle', 'wrapperstyle',
|
|
36099
|
+
],
|
|
36100
|
+
ALLOW_DATA_ATTR: true,
|
|
36101
|
+
ADD_URI_SAFE_ATTR: ['containerstyle', 'wrapperstyle'],
|
|
36102
|
+
},
|
|
36103
|
+
// Espejo de getPrintExtensions('print-header'): la banda del encabezado.
|
|
36104
|
+
'print-header': {
|
|
36105
|
+
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 's', 'code', 'span', 'mark', 'img'],
|
|
36106
|
+
ALLOWED_ATTR: ['class', 'style', 'src', 'alt', 'title', 'width', 'height', 'contenteditable', 'containerstyle', 'wrapperstyle'],
|
|
36107
|
+
ALLOW_DATA_ATTR: true,
|
|
36108
|
+
ADD_URI_SAFE_ATTR: ['containerstyle', 'wrapperstyle'],
|
|
36109
|
+
},
|
|
36110
|
+
};
|
|
36111
|
+
/**
|
|
36112
|
+
* `<iframe>`s are permitted only with an `https:` source (blocks javascript:,
|
|
36113
|
+
* data:, and plain http: embeds). This is what makes allowing iframes safe-ish
|
|
36114
|
+
* for a trusted-author document; YouTube embeds satisfy it too.
|
|
36115
|
+
*/
|
|
36116
|
+
const SAFE_IFRAME_SRC_RE = /^https:\/\/[^\s"'<>]+$/i;
|
|
36117
|
+
/** Tags forbidden in every preset, regardless of the allow-list. */
|
|
36118
|
+
const FORBID_TAGS = ['script', 'object', 'embed', 'form', 'textarea', 'style'];
|
|
36119
|
+
/** Attributes forbidden in every preset (event handlers handled by DOMPurify itself). */
|
|
36120
|
+
const FORBID_ATTR = ['onerror', 'onload', 'onclick'];
|
|
36121
|
+
/**
|
|
36122
|
+
* Resolve the effective config for a preset + feature overrides. Features can
|
|
36123
|
+
* only ADD capabilities (mirroring `getExtensions`, where they register extra
|
|
36124
|
+
* nodes); they never narrow a preset's base whitelist.
|
|
36125
|
+
*/
|
|
36126
|
+
function configFor(preset, features) {
|
|
36127
|
+
const cfg = SANITIZE_CONFIGS[preset] ?? SANITIZE_CONFIGS.task;
|
|
36128
|
+
if (!features)
|
|
36129
|
+
return cfg;
|
|
36130
|
+
const tags = new Set(cfg.ALLOWED_TAGS);
|
|
36131
|
+
const attr = new Set(cfg.ALLOWED_ATTR);
|
|
36132
|
+
if (features.image) {
|
|
36133
|
+
tags.add('img');
|
|
36134
|
+
['src', 'alt', 'width', 'height'].forEach((a) => attr.add(a));
|
|
36135
|
+
}
|
|
36136
|
+
if (features.variables)
|
|
36137
|
+
attr.add('data-variable');
|
|
36138
|
+
if (features.mentions) {
|
|
36139
|
+
// data-mention-id/label ya están en BASE_ATTR. Los attrs del markup V1
|
|
36140
|
+
// (quill-mention) se suman porque el BACKEND los parsea para notificar —
|
|
36141
|
+
// re-editar una nota no debe destruirlos.
|
|
36142
|
+
// Espejo inline en core/co-rich-text-viewer.tsx (el viewer no importa este módulo para no cargar el engine) — si cambias esta lista, cambia la del viewer.
|
|
36143
|
+
['data-id', 'data-value', 'data-denotation-char', 'data-index'].forEach((a) => attr.add(a));
|
|
36144
|
+
}
|
|
36145
|
+
return { ...cfg, ALLOWED_TAGS: [...tags], ALLOWED_ATTR: [...attr] };
|
|
36146
|
+
}
|
|
36147
|
+
/**
|
|
36148
|
+
* `style` is only allowed in notes/email/document, and even there we cap it to
|
|
36149
|
+
* the properties our extensions actually emit (TextStyle/Color/TextAlign,
|
|
36150
|
+
* ListStyleExtension's `list-style-type`). Everything else — position,
|
|
36151
|
+
* background-image url(), etc. — is UI-redressing/tracking surface.
|
|
36152
|
+
*/
|
|
36153
|
+
const STYLE_ALLOWED_PROPS = new Set(['color', 'background-color', 'text-align', 'list-style-type']);
|
|
36154
|
+
const UNSAFE_STYLE_VALUE = /url\s*\(|expression\s*\(/i;
|
|
36155
|
+
const DEFAULT_STYLE_POLICY = {
|
|
36156
|
+
attrs: new Set(['style']),
|
|
36157
|
+
allows: (prop) => STYLE_ALLOWED_PROPS.has(prop),
|
|
36158
|
+
keepOriginalWhenClean: false,
|
|
36159
|
+
};
|
|
36160
|
+
const COLOR_VALUE = /^(#[0-9a-f]{3,8}|rgba?\([\d.\s,%]+\)|hsla?\([\d.\s,%deg]+\)|[a-z]+)$/i;
|
|
36161
|
+
const LENGTH_TOKEN = /^(-?\d+(\.\d+)?(px|pt|em|rem|%|mm|cm|in)?|auto)$/i;
|
|
36162
|
+
const lengths = (v) => v.split(/\s+/).every((t) => LENGTH_TOKEN.test(t));
|
|
36163
|
+
const oneOf = (re) => (v) => re.test(v);
|
|
36164
|
+
/** Lo que emiten las extensiones del preset print y la firma canónica (spec §3.4, §4.1). */
|
|
36165
|
+
const PRINT_STYLE_RULES = {
|
|
36166
|
+
color: oneOf(COLOR_VALUE),
|
|
36167
|
+
'background-color': oneOf(COLOR_VALUE),
|
|
36168
|
+
'text-align': oneOf(/^(left|center|right|justify)$/i),
|
|
36169
|
+
'font-size': oneOf(/^\d+(\.\d+)?(px|pt|em|rem|%)$/i),
|
|
36170
|
+
width: lengths,
|
|
36171
|
+
height: lengths,
|
|
36172
|
+
'min-width': lengths,
|
|
36173
|
+
'max-width': lengths,
|
|
36174
|
+
display: oneOf(/^(inline-block|inline|block|flex|inline-flex)$/i),
|
|
36175
|
+
float: oneOf(/^(left|right|none)$/i),
|
|
36176
|
+
margin: lengths,
|
|
36177
|
+
'margin-top': lengths,
|
|
36178
|
+
'margin-right': lengths,
|
|
36179
|
+
'margin-bottom': lengths,
|
|
36180
|
+
'margin-left': lengths,
|
|
36181
|
+
padding: lengths,
|
|
36182
|
+
'padding-top': lengths,
|
|
36183
|
+
'padding-right': lengths,
|
|
36184
|
+
'padding-bottom': lengths,
|
|
36185
|
+
'padding-left': lengths,
|
|
36186
|
+
'vertical-align': oneOf(/^(top|middle|bottom|baseline|text-top|text-bottom)$/i),
|
|
36187
|
+
position: oneOf(/^(relative|absolute)$/i),
|
|
36188
|
+
top: lengths,
|
|
36189
|
+
left: lengths,
|
|
36190
|
+
'line-height': oneOf(/^\d+(\.\d+)?(px)?$/i),
|
|
36191
|
+
'white-space': oneOf(/^(nowrap|normal|pre-wrap)$/i),
|
|
36192
|
+
'pointer-events': oneOf(/^none$/i),
|
|
36193
|
+
'user-select': oneOf(/^none$/i),
|
|
36194
|
+
cursor: oneOf(/^(pointer|default)$/i),
|
|
36195
|
+
};
|
|
36196
|
+
const PRINT_STYLE_POLICY = {
|
|
36197
|
+
// containerstyle/wrapperstyle son CSS que V1 aplica inline a la imagen (H2).
|
|
36198
|
+
attrs: new Set(['style', 'containerstyle', 'wrapperstyle']),
|
|
36199
|
+
allows: (prop, value) => Object.prototype.hasOwnProperty.call(PRINT_STYLE_RULES, prop) && PRINT_STYLE_RULES[prop](value),
|
|
36200
|
+
keepOriginalWhenClean: true,
|
|
36201
|
+
};
|
|
36202
|
+
const STYLE_POLICIES = {
|
|
36203
|
+
print: PRINT_STYLE_POLICY,
|
|
36204
|
+
'print-header': PRINT_STYLE_POLICY,
|
|
36205
|
+
};
|
|
36206
|
+
/**
|
|
36207
|
+
* Sanitize an HTML string for the given preset (+ feature overrides — pass the
|
|
36208
|
+
* same `features` you gave `getExtensions`/`createEditor` so the whitelist
|
|
36209
|
+
* stays in lock-step with the schema). Always strips script/object/embed/style/
|
|
36210
|
+
* form/event-handlers and `javascript:` URLs (DOMPurify default). `<iframe>` is
|
|
36211
|
+
* allowed ONLY for the document preset and ONLY with an `https:` source
|
|
36212
|
+
* (YouTube, Vimeo, Loom, Slides, … survive save/render; unsafe embeds are
|
|
36213
|
+
* dropped). `style` is capped to color/background-color/text-align.
|
|
36214
|
+
*/
|
|
36215
|
+
function sanitize(html, preset = 'task', features) {
|
|
36216
|
+
if (!html)
|
|
36217
|
+
return '';
|
|
36218
|
+
const cfg = configFor(preset, features);
|
|
36219
|
+
purify.addHook('uponSanitizeElement', (node, data) => {
|
|
36220
|
+
// Only allow <input type="checkbox"> (task list checkboxes); strip others.
|
|
36221
|
+
if (data.tagName === 'input' && node.type !== 'checkbox') {
|
|
36222
|
+
node.parentNode?.removeChild(node);
|
|
36223
|
+
}
|
|
36224
|
+
// Allow only https iframes (YouTube and other embeds); drop anything else.
|
|
36225
|
+
if (data.tagName === 'iframe') {
|
|
36226
|
+
const src = node.getAttribute('src') || '';
|
|
36227
|
+
if (!SAFE_IFRAME_SRC_RE.test(src))
|
|
36228
|
+
node.parentNode?.removeChild(node);
|
|
36229
|
+
}
|
|
36230
|
+
});
|
|
36231
|
+
// Cap `style` (and, in print, the image's containerstyle/wrapperstyle) to
|
|
36232
|
+
// the properties the preset's extensions actually emit; strip the rest
|
|
36233
|
+
// (position:fixed, background-image url(), expression(), …). Drop the attr
|
|
36234
|
+
// entirely when nothing survives. The default policy is the pre-print rule
|
|
36235
|
+
// verbatim (guarded by sanitizer.presets.snapshot.test.ts).
|
|
36236
|
+
const policy = STYLE_POLICIES[preset] ?? DEFAULT_STYLE_POLICY;
|
|
36237
|
+
purify.addHook('uponSanitizeAttribute', (_node, data) => {
|
|
36238
|
+
if (!policy.attrs.has(data.attrName))
|
|
36239
|
+
return;
|
|
36240
|
+
const decls = data.attrValue.split(';').map((d) => d.trim()).filter(Boolean);
|
|
36241
|
+
const safe = decls.filter((d) => {
|
|
36242
|
+
const colon = d.indexOf(':');
|
|
36243
|
+
if (colon <= 0)
|
|
36244
|
+
return false;
|
|
36245
|
+
const prop = d.slice(0, colon).trim().toLowerCase();
|
|
36246
|
+
const value = d.slice(colon + 1).trim();
|
|
36247
|
+
return !UNSAFE_STYLE_VALUE.test(d) && policy.allows(prop, value);
|
|
36248
|
+
});
|
|
36249
|
+
if (policy.keepOriginalWhenClean && safe.length === decls.length && safe.length > 0)
|
|
36250
|
+
return;
|
|
36251
|
+
data.attrValue = safe.join('; ');
|
|
36252
|
+
if (!data.attrValue)
|
|
36253
|
+
data.keepAttr = false;
|
|
36254
|
+
});
|
|
36255
|
+
try {
|
|
36256
|
+
const clean = purify.sanitize(html, {
|
|
36257
|
+
ALLOWED_TAGS: cfg.ALLOWED_TAGS,
|
|
36258
|
+
ALLOWED_ATTR: cfg.ALLOWED_ATTR,
|
|
36259
|
+
ALLOW_DATA_ATTR: cfg.ALLOW_DATA_ATTR ?? false,
|
|
36260
|
+
FORBID_TAGS,
|
|
36261
|
+
FORBID_ATTR,
|
|
36262
|
+
ADD_ATTR: ['target', 'allowfullscreen', 'frameborder', 'allow'],
|
|
36263
|
+
...(cfg.ADD_URI_SAFE_ATTR ? { ADD_URI_SAFE_ATTR: cfg.ADD_URI_SAFE_ATTR } : {}),
|
|
36264
|
+
});
|
|
36265
|
+
return clean;
|
|
36266
|
+
}
|
|
36267
|
+
finally {
|
|
36268
|
+
purify.removeHook('uponSanitizeElement');
|
|
36269
|
+
purify.removeHook('uponSanitizeAttribute');
|
|
36270
|
+
}
|
|
36271
|
+
}
|
|
36272
|
+
|
|
36273
|
+
/**
|
|
36274
|
+
* Lector tolerante del HTML de impresión (spec contracts-v2-contract-editor §3.3).
|
|
36275
|
+
* Entrada: lo que guardó V1 (documento completo con CSS, fragmento que dejó
|
|
36276
|
+
* cheerio en el backend, cuerpo compilado de generateFromDeal) o lo que escribe
|
|
36277
|
+
* el serializador canónico. Salida: SOLO el cuerpo, sin restos de edición.
|
|
36278
|
+
* No usa schema: el parse lo hace readPrintHtml (print/io.ts).
|
|
36279
|
+
*/
|
|
36280
|
+
const EDITING_CLASSES = ['ProseMirror-selectednode', 'variable-node--editing', 'selectedCell'];
|
|
36281
|
+
const BLOCK_TAGS = new Set(['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'UL', 'OL', 'LI', 'TABLE', 'BLOCKQUOTE', 'PRE', 'HR']);
|
|
36282
|
+
const INLINE_CONTENT_TAGS = new Set(['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6']);
|
|
36283
|
+
const HEAD_ONLY = 'style, script, meta, title, link, base';
|
|
36284
|
+
function normalizePrintHtml(html) {
|
|
36285
|
+
if (!html || !html.trim())
|
|
36286
|
+
return '';
|
|
36287
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
36288
|
+
const root = doc.querySelector('.tiptap.ProseMirror, .ProseMirror') ?? doc.body;
|
|
36289
|
+
root.querySelectorAll('#document-metadata').forEach((el) => el.remove());
|
|
36290
|
+
root.querySelectorAll(HEAD_ONLY).forEach((el) => el.remove());
|
|
36291
|
+
// prosemirror-model convierte estos br en saltos REALES al re-parsear: fuera todos.
|
|
36292
|
+
root.querySelectorAll('br.ProseMirror-trailingBreak, img.ProseMirror-separator').forEach((el) => el.remove());
|
|
36293
|
+
// V1 serializa por error la decoración de {{ }}.
|
|
36294
|
+
root.querySelectorAll('span.hb-token').forEach(unwrap);
|
|
36295
|
+
Array.from(root.querySelectorAll('*')).forEach(cleanEditingResidue);
|
|
36296
|
+
cleanCellStyles(root);
|
|
36297
|
+
rebuildSplitImages(root);
|
|
36298
|
+
// Tras desenvolver hb-token y quitar breaks quedan textos adyacentes: un token repartido entre ellos no matchearía.
|
|
36299
|
+
root.normalize();
|
|
36300
|
+
tokensToFieldSpans(root);
|
|
36301
|
+
return root.innerHTML;
|
|
36302
|
+
}
|
|
36303
|
+
function unwrap(el) {
|
|
36304
|
+
el.replaceWith(...Array.from(el.childNodes));
|
|
36305
|
+
}
|
|
36306
|
+
function cleanEditingResidue(el) {
|
|
36307
|
+
Array.from(el.attributes).forEach((a) => {
|
|
36308
|
+
if (a.name.startsWith('data-v-') || a.name === 'data-node-view-wrapper')
|
|
36309
|
+
el.removeAttribute(a.name);
|
|
36310
|
+
});
|
|
36311
|
+
EDITING_CLASSES.forEach((c) => el.classList.remove(c));
|
|
36312
|
+
if (el.getAttribute('class') === '')
|
|
36313
|
+
el.removeAttribute('class');
|
|
36314
|
+
}
|
|
36315
|
+
function cleanCellStyles(root) {
|
|
36316
|
+
root.querySelectorAll('td[style], th[style]').forEach((cell) => {
|
|
36317
|
+
const kept = (cell.getAttribute('style') || '')
|
|
36318
|
+
.split(';')
|
|
36319
|
+
.map((d) => d.trim())
|
|
36320
|
+
.filter((d) => {
|
|
36321
|
+
const i = d.indexOf(':');
|
|
36322
|
+
if (i <= 0)
|
|
36323
|
+
return false;
|
|
36324
|
+
const v = d.slice(i + 1).trim().toLowerCase();
|
|
36325
|
+
return v !== '' && v !== 'null' && v !== 'undefined';
|
|
36326
|
+
});
|
|
36327
|
+
if (kept.length)
|
|
36328
|
+
cell.setAttribute('style', kept.join('; '));
|
|
36329
|
+
else
|
|
36330
|
+
cell.removeAttribute('style');
|
|
36331
|
+
});
|
|
36332
|
+
}
|
|
36333
|
+
const isBlockNode = (n) => n.nodeType === 1 && BLOCK_TAGS.has(n.tagName);
|
|
36334
|
+
const isBlankText = (n) => n.nodeType === 3 && !(n.textContent ?? '').trim();
|
|
36335
|
+
/**
|
|
36336
|
+
* V1 guarda la imagen como `<div wrapper><div container><img></div></div>`
|
|
36337
|
+
* DENTRO del `<p>`; el parser HTML5 parte el párrafo en `<p>antes</p><div>…</div>después<p></p>`.
|
|
36338
|
+
* Se vuelve a juntar en un solo párrafo, con los estilos del wrapper y del
|
|
36339
|
+
* container copiados al `<img>` (así los lee el nodo image del preset print).
|
|
36340
|
+
*/
|
|
36341
|
+
function rebuildSplitImages(root) {
|
|
36342
|
+
const doc = root.ownerDocument;
|
|
36343
|
+
Array.from(root.querySelectorAll('img')).forEach((img) => {
|
|
36344
|
+
const container = img.parentElement;
|
|
36345
|
+
// `container !== root`: un <img> suelto al tope del cuerpo no es una imagen partida.
|
|
36346
|
+
if (!container || container === root || container.tagName !== 'DIV' || container.firstElementChild !== img)
|
|
36347
|
+
return;
|
|
36348
|
+
const parent = container.parentElement;
|
|
36349
|
+
const wrapper = parent && parent.tagName === 'DIV' && parent.firstElementChild === container && parent !== root ? parent : null;
|
|
36350
|
+
const containerCss = container.getAttribute('style');
|
|
36351
|
+
if (containerCss && !img.hasAttribute('containerstyle'))
|
|
36352
|
+
img.setAttribute('containerstyle', containerCss);
|
|
36353
|
+
const wrapperCss = wrapper?.getAttribute('style');
|
|
36354
|
+
if (wrapperCss && !img.hasAttribute('wrapperstyle'))
|
|
36355
|
+
img.setAttribute('wrapperstyle', wrapperCss);
|
|
36356
|
+
const outer = wrapper ?? container;
|
|
36357
|
+
// El parser HTML5 solo parte <p>: dentro de un título (o de un <p> que no se
|
|
36358
|
+
// partió) la imagen queda en su lugar, sin recomponer hermanos ni crear <p>.
|
|
36359
|
+
if (outer.parentElement && INLINE_CONTENT_TAGS.has(outer.parentElement.tagName)) {
|
|
36360
|
+
outer.replaceWith(img);
|
|
36361
|
+
return;
|
|
36362
|
+
}
|
|
36363
|
+
const before = [];
|
|
36364
|
+
let prev = outer.previousSibling;
|
|
36365
|
+
while (prev && !isBlockNode(prev)) {
|
|
36366
|
+
before.unshift(prev);
|
|
36367
|
+
prev = prev.previousSibling;
|
|
36368
|
+
}
|
|
36369
|
+
const after = [];
|
|
36370
|
+
let next = outer.nextSibling;
|
|
36371
|
+
while (next && !isBlockNode(next)) {
|
|
36372
|
+
after.push(next);
|
|
36373
|
+
next = next.nextSibling;
|
|
36374
|
+
}
|
|
36375
|
+
// El `</p>` original quedó huérfano: el parser lo convierte en un <p></p> vacío.
|
|
36376
|
+
if (next && next.nodeName === 'P' && !next.hasChildNodes())
|
|
36377
|
+
next.remove();
|
|
36378
|
+
let target;
|
|
36379
|
+
if (before.every(isBlankText) && prev && prev.nodeName === 'P') {
|
|
36380
|
+
before.forEach((n) => n.parentNode?.removeChild(n));
|
|
36381
|
+
target = prev;
|
|
36382
|
+
}
|
|
36383
|
+
else {
|
|
36384
|
+
target = doc.createElement('p');
|
|
36385
|
+
outer.before(target);
|
|
36386
|
+
before.forEach((n) => target.appendChild(n));
|
|
36387
|
+
}
|
|
36388
|
+
target.appendChild(img);
|
|
36389
|
+
after.forEach((n) => target.appendChild(n));
|
|
36390
|
+
outer.remove();
|
|
36391
|
+
});
|
|
36392
|
+
}
|
|
36393
|
+
function tokensToFieldSpans(root) {
|
|
36394
|
+
const doc = root.ownerDocument;
|
|
36395
|
+
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
36396
|
+
const targets = [];
|
|
36397
|
+
let n;
|
|
36398
|
+
while ((n = walker.nextNode())) {
|
|
36399
|
+
const t = n;
|
|
36400
|
+
if (!t.data.includes('[['))
|
|
36401
|
+
continue;
|
|
36402
|
+
if (t.parentElement?.closest('[data-variable], [data-sig-slot], pre, code'))
|
|
36403
|
+
continue;
|
|
36404
|
+
targets.push(t);
|
|
36405
|
+
}
|
|
36406
|
+
targets.forEach((t) => {
|
|
36407
|
+
const text = t.data;
|
|
36408
|
+
const re = fieldTokenRegex();
|
|
36409
|
+
const frag = doc.createDocumentFragment();
|
|
36410
|
+
let last = 0;
|
|
36411
|
+
let any = false;
|
|
36412
|
+
let m;
|
|
36413
|
+
while ((m = re.exec(text)) !== null) {
|
|
36414
|
+
const def = parseFieldToken(m[1]);
|
|
36415
|
+
if (!def)
|
|
36416
|
+
continue;
|
|
36417
|
+
any = true;
|
|
36418
|
+
if (m.index > last)
|
|
36419
|
+
frag.appendChild(doc.createTextNode(text.slice(last, m.index)));
|
|
36420
|
+
const span = doc.createElement('span');
|
|
36421
|
+
span.setAttribute('data-variable', def.name);
|
|
36422
|
+
span.setAttribute('data-value', '');
|
|
36423
|
+
span.setAttribute('data-type', def.type);
|
|
36424
|
+
span.setAttribute('data-required', def.required ? 'true' : 'false');
|
|
36425
|
+
if (def.enum)
|
|
36426
|
+
span.setAttribute('data-enum', JSON.stringify(def.enum));
|
|
36427
|
+
span.textContent = `[[${def.name}]]`;
|
|
36428
|
+
frag.appendChild(span);
|
|
36429
|
+
last = m.index + m[0].length;
|
|
36430
|
+
}
|
|
36431
|
+
if (!any)
|
|
36432
|
+
return;
|
|
36433
|
+
if (last < text.length)
|
|
36434
|
+
frag.appendChild(doc.createTextNode(text.slice(last)));
|
|
36435
|
+
t.replaceWith(frag);
|
|
36436
|
+
});
|
|
36437
|
+
}
|
|
36438
|
+
|
|
36439
|
+
/**
|
|
36440
|
+
* Serializador canónico del preset print (spec contracts-v2-contract-editor §3.4).
|
|
36441
|
+
* Escribe desde el DOCUMENTO (renderHTML del schema), nunca desde el DOM de
|
|
36442
|
+
* pantalla: así no se cuelan NodeViews, decoraciones ni los hacks de
|
|
36443
|
+
* prosemirror-view que V1 guardaba con innerHTML.
|
|
36444
|
+
*
|
|
36445
|
+
* H1: `DOMSerializer` escribe `style` por CSSOM (`dom.style.cssText = …`), que
|
|
36446
|
+
* normaliza el string (`#ff0000` → `rgb(255, 0, 0)`, `top:0` → `top: 0px;`) y
|
|
36447
|
+
* rompe la forma byte a byte de V1 (firma, color, tamaño, imagen). Por eso se
|
|
36448
|
+
* serializa con un `document` sustituto cuyos elementos no exponen `style`:
|
|
36449
|
+
* ProseMirror cae a `setAttribute` y conserva el string crudo. `editor.getHTML()`
|
|
36450
|
+
* NO es la forma canónica.
|
|
36451
|
+
*/
|
|
36452
|
+
const TRAILING_BREAK_CLASS = 'ProseMirror-trailingBreak';
|
|
36453
|
+
const TRAILING_MARKER = 'data-co-trailing-break';
|
|
36454
|
+
/**
|
|
36455
|
+
* Como prosemirror-view, el trailingBreak se decide por el NODO: textblock vacío o
|
|
36456
|
+
* cuyo último hijo es un hardBreak (aunque el <br> quede dentro de un mark).
|
|
36457
|
+
*/
|
|
36458
|
+
function needsTrailingBreak(node) {
|
|
36459
|
+
if (!node.isTextblock)
|
|
36460
|
+
return false;
|
|
36461
|
+
return node.childCount === 0 || node.lastChild.type.name === 'hardBreak';
|
|
36462
|
+
}
|
|
36463
|
+
/**
|
|
36464
|
+
* Marca el elemento de bloque (spec `[tag, attrs?, 0]`, hueco directo en el
|
|
36465
|
+
* elemento: p/h*) para que el <br> vaya como su ÚLTIMO hijo, fuera de los marks.
|
|
36466
|
+
*/
|
|
36467
|
+
function withTrailingMarker(spec) {
|
|
36468
|
+
if (!Array.isArray(spec) || spec[spec.length - 1] !== 0)
|
|
36469
|
+
return spec;
|
|
36470
|
+
const attrs = spec[1];
|
|
36471
|
+
if (attrs && typeof attrs === 'object' && !Array.isArray(attrs) && !('nodeType' in attrs)) {
|
|
36472
|
+
return [spec[0], { ...attrs, [TRAILING_MARKER]: '' }, ...spec.slice(2)];
|
|
36473
|
+
}
|
|
36474
|
+
return [spec[0], { [TRAILING_MARKER]: '' }, ...spec.slice(1)];
|
|
36475
|
+
}
|
|
36476
|
+
function rawStyleDocument(real) {
|
|
36477
|
+
return {
|
|
36478
|
+
createElement: (tag) => {
|
|
36479
|
+
const el = real.createElement(tag);
|
|
36480
|
+
Object.defineProperty(el, 'style', { value: null });
|
|
36481
|
+
return el;
|
|
36482
|
+
},
|
|
36483
|
+
createElementNS: (ns, tag) => {
|
|
36484
|
+
const el = real.createElementNS(ns, tag);
|
|
36485
|
+
Object.defineProperty(el, 'style', { value: null });
|
|
36486
|
+
return el;
|
|
36487
|
+
},
|
|
36488
|
+
createTextNode: real.createTextNode.bind(real),
|
|
36489
|
+
createDocumentFragment: real.createDocumentFragment.bind(real),
|
|
36490
|
+
};
|
|
36491
|
+
}
|
|
36492
|
+
function printSerializer(schema) {
|
|
36493
|
+
const base = DOMSerializer.fromSchema(schema);
|
|
36494
|
+
const nodes = {};
|
|
36495
|
+
Object.entries(base.nodes).forEach(([name, render]) => {
|
|
36496
|
+
nodes[name] = (node) => {
|
|
36497
|
+
const spec = render(node);
|
|
36498
|
+
return needsTrailingBreak(node) ? withTrailingMarker(spec) : spec;
|
|
36499
|
+
};
|
|
36500
|
+
});
|
|
36501
|
+
return new DOMSerializer(nodes, base.marks);
|
|
36502
|
+
}
|
|
36503
|
+
function serializePrintHtml(doc) {
|
|
36504
|
+
const container = document.createElement('div');
|
|
36505
|
+
const fragment = printSerializer(doc.type.schema).serializeFragment(doc.content, {
|
|
36506
|
+
document: rawStyleDocument(document),
|
|
36507
|
+
});
|
|
36508
|
+
container.appendChild(fragment);
|
|
36509
|
+
// Sin el trailingBreak la línea vacía o final no ocupa alto en el PDF
|
|
36510
|
+
// (.tiptap p{margin:0}). El lector lo quita al volver.
|
|
36511
|
+
container.querySelectorAll(`[${TRAILING_MARKER}]`).forEach((el) => {
|
|
36512
|
+
el.removeAttribute(TRAILING_MARKER);
|
|
36513
|
+
const br = document.createElement('br');
|
|
36514
|
+
br.className = TRAILING_BREAK_CLASS;
|
|
36515
|
+
el.appendChild(br);
|
|
36516
|
+
});
|
|
36517
|
+
return container.innerHTML;
|
|
36518
|
+
}
|
|
36519
|
+
|
|
36520
|
+
function isPrintPreset(p) {
|
|
36521
|
+
return p === 'print' || p === 'print-header';
|
|
36522
|
+
}
|
|
36523
|
+
function schemaExtensions(preset, features) {
|
|
36524
|
+
return getExtensions(preset, { includeUiExtensions: false, features });
|
|
36525
|
+
}
|
|
36526
|
+
/** HTML de impresión (el de V1 incluido) → JSON: lector tolerante → limpieza del preset → parse. */
|
|
36527
|
+
function readPrintHtml(html, preset = 'print', features) {
|
|
36528
|
+
const clean = sanitize(normalizePrintHtml(html ?? ''), preset, features);
|
|
36529
|
+
return generateJSON(clean, schemaExtensions(preset, features));
|
|
36530
|
+
}
|
|
36531
|
+
/**
|
|
36532
|
+
* Documento → forma canónica (spec §3.4), pasada por la limpieza del preset.
|
|
36533
|
+
* Es la ÚNICA forma canónica: `editor.getHTML()` de Tiptap normaliza los `style`
|
|
36534
|
+
* (H1) y no sirve para guardar.
|
|
36535
|
+
*/
|
|
36536
|
+
function writePrintHtml(doc, preset = 'print', features) {
|
|
36537
|
+
if (!doc)
|
|
36538
|
+
return '';
|
|
36539
|
+
const node = doc instanceof Node$1
|
|
36540
|
+
? doc
|
|
36541
|
+
: Node$1.fromJSON(getSchema(schemaExtensions(preset, features)), doc);
|
|
36542
|
+
return sanitize(serializePrintHtml(node), preset, features);
|
|
36543
|
+
}
|
|
36544
|
+
/** Aplica valores a los campos de un HTML sin montar un editor (encabezado fuera de edición). */
|
|
36545
|
+
function setFieldValuesInHtml(html, values, preset = 'print-header', features) {
|
|
36546
|
+
if (!html)
|
|
36547
|
+
return '';
|
|
36548
|
+
const json = readPrintHtml(html, preset, features);
|
|
36549
|
+
const apply = (n) => {
|
|
36550
|
+
if (n.type === 'mergeField' && n.attrs && Object.prototype.hasOwnProperty.call(values, n.attrs.name)) {
|
|
36551
|
+
n.attrs = { ...n.attrs, value: String(values[n.attrs.name] ?? '') };
|
|
36552
|
+
}
|
|
36553
|
+
n.content?.forEach(apply);
|
|
36554
|
+
};
|
|
36555
|
+
apply(json);
|
|
36556
|
+
return writePrintHtml(json, preset, features);
|
|
36557
|
+
}
|
|
35186
36558
|
|
|
35187
36559
|
/**
|
|
35188
36560
|
* ProseMirror editorProps that insert pasted/dropped images optimistically
|
|
@@ -35308,6 +36680,11 @@ function autolinkTrailing(editor) {
|
|
|
35308
36680
|
editor.view.dispatch(tr);
|
|
35309
36681
|
return changed;
|
|
35310
36682
|
}
|
|
36683
|
+
/** El autolink al perder foco solo corre si el link del schema lo tiene activo (print lo apaga). */
|
|
36684
|
+
function autolinkEnabled(editor) {
|
|
36685
|
+
const link = editor.extensionManager.extensions.find((ext) => ext.name === 'link');
|
|
36686
|
+
return !!(link && link.options.autolink);
|
|
36687
|
+
}
|
|
35311
36688
|
/**
|
|
35312
36689
|
* Create a configured Tiptap v3 editor for a Cobalt preset.
|
|
35313
36690
|
*
|
|
@@ -35316,9 +36693,9 @@ function autolinkTrailing(editor) {
|
|
|
35316
36693
|
* always false — Cobalt owns every style.
|
|
35317
36694
|
*/
|
|
35318
36695
|
function createEditor(options = {}) {
|
|
35319
|
-
const { preset = 'task', element = null, content = null, editable = true, placeholder, maxLength, features, customExtensions = [], bubbleMenuElement, imageUpload, onUpdate, onSelectionUpdate, onFocus, onBlur, onCreate, onDestroy, onContentError, } = options;
|
|
36696
|
+
const { preset = 'task', element = null, content = null, editable = true, placeholder, maxLength, features, labels, customExtensions = [], bubbleMenuElement, imageUpload, onUpdate, onSelectionUpdate, onFocus, onBlur, onCreate, onDestroy, onContentError, } = options;
|
|
35320
36697
|
const extensions = [
|
|
35321
|
-
...getExtensions(preset, { placeholder, maxLength, features }),
|
|
36698
|
+
...getExtensions(preset, { placeholder, maxLength, features, labels }),
|
|
35322
36699
|
...customExtensions,
|
|
35323
36700
|
];
|
|
35324
36701
|
let editor;
|
|
@@ -35375,11 +36752,13 @@ function createEditor(options = {}) {
|
|
|
35375
36752
|
},
|
|
35376
36753
|
}));
|
|
35377
36754
|
}
|
|
36755
|
+
// En print, un string es HTML de impresión (quizá de V1): pasa por el lector tolerante.
|
|
36756
|
+
const initialContent = isPrintPreset(preset) && typeof content === 'string' ? readPrintHtml(content, preset, features) : content;
|
|
35378
36757
|
editor = new Editor({
|
|
35379
36758
|
...(element ? { element } : {}),
|
|
35380
36759
|
...(imageUpload ? { editorProps: imageDropPaste(imageUpload) } : {}),
|
|
35381
36760
|
extensions,
|
|
35382
|
-
content:
|
|
36761
|
+
content: initialContent ?? '',
|
|
35383
36762
|
editable,
|
|
35384
36763
|
injectCSS: false,
|
|
35385
36764
|
// `enableContentCheck: true` makes tiptap THROW on invalid content from
|
|
@@ -35394,304 +36773,356 @@ function createEditor(options = {}) {
|
|
|
35394
36773
|
onUpdate: ({ editor: e }) => onUpdate?.(e),
|
|
35395
36774
|
onSelectionUpdate: ({ editor: e }) => onSelectionUpdate?.(e),
|
|
35396
36775
|
onFocus: ({ editor: e }) => onFocus?.(e),
|
|
35397
|
-
onBlur: ({ editor: e }) => {
|
|
36776
|
+
onBlur: ({ editor: e }) => { if (autolinkEnabled(e))
|
|
36777
|
+
autolinkTrailing(e); onBlur?.(e); },
|
|
35398
36778
|
onDestroy: () => onDestroy?.(),
|
|
35399
36779
|
});
|
|
35400
36780
|
return editor;
|
|
35401
36781
|
}
|
|
35402
36782
|
|
|
35403
|
-
|
|
35404
|
-
|
|
35405
|
-
|
|
35406
|
-
|
|
35407
|
-
|
|
35408
|
-
|
|
35409
|
-
|
|
35410
|
-
|
|
35411
|
-
|
|
35412
|
-
|
|
35413
|
-
|
|
35414
|
-
|
|
35415
|
-
|
|
35416
|
-
|
|
35417
|
-
|
|
35418
|
-
|
|
35419
|
-
|
|
35420
|
-
|
|
35421
|
-
|
|
35422
|
-
|
|
35423
|
-
|
|
35424
|
-
|
|
35425
|
-
|
|
35426
|
-
|
|
35427
|
-
|
|
35428
|
-
|
|
35429
|
-
|
|
35430
|
-
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
throw new Error(
|
|
35435
|
-
"generateJSON can only be used in a browser environment\nIf you want to use this in a Node environment, use the `@tiptap/html/server` import instead."
|
|
35436
|
-
);
|
|
35437
|
-
}
|
|
35438
|
-
const schema = getSchema(extensions);
|
|
35439
|
-
const doc = new window.DOMParser().parseFromString(html, "text/html");
|
|
35440
|
-
if (!doc) {
|
|
35441
|
-
throw new Error("Failed to parse HTML string");
|
|
35442
|
-
}
|
|
35443
|
-
return DOMParser.fromSchema(schema).parse(doc.body, options).toJSON();
|
|
35444
|
-
}
|
|
35445
|
-
|
|
35446
|
-
const BASE_INLINE = ['p', 'br', 'strong', 'em', 'a', 'span'];
|
|
35447
|
-
const BASE_ATTR = ['href', 'target', 'rel', 'data-mention-id', 'data-mention-label'];
|
|
35448
|
-
const SANITIZE_CONFIGS = {
|
|
35449
|
-
// Más ancho que su schema A PROPÓSITO (única excepción al lock-step): el
|
|
35450
|
-
// editor `comment` no puede autorar listas ni headings (STARTERKIT_CONFIG los
|
|
35451
|
-
// apaga y ProseMirror descarta esos nodos al parsear el paste), pero el
|
|
35452
|
-
// VIEWER tiene que pintarlos cuando el HTML entra por API — una nota de
|
|
35453
|
-
// enriquecimiento (Apollo.io) trae <ul>/<li>/<h4> y con KEEP_CONTENT el
|
|
35454
|
-
// texto se hoisteaba al padre: lista corrida, heading fundido en el cuerpo.
|
|
35455
|
-
// Espejo inline en core/co-rich-text-viewer.tsx (ALLOWED_TAGS.comment).
|
|
35456
|
-
comment: {
|
|
35457
|
-
ALLOWED_TAGS: [...BASE_INLINE, 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4'],
|
|
35458
|
-
ALLOWED_ATTR: [...BASE_ATTR],
|
|
35459
|
-
ALLOW_DATA_ATTR: false,
|
|
35460
|
-
},
|
|
35461
|
-
task: {
|
|
35462
|
-
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'input', 'label'],
|
|
35463
|
-
ALLOWED_ATTR: [...BASE_ATTR, 'type', 'checked', 'disabled', 'data-type', 'data-checked', 'data-variant'],
|
|
35464
|
-
ALLOW_DATA_ATTR: false,
|
|
35465
|
-
},
|
|
35466
|
-
// Lock-step con getExtensions('notes'): task sin checklists + TextStyle/Color
|
|
35467
|
-
// (`style` capado a STYLE_ALLOWED_PROPS) + Highlight multicolor (`mark` con
|
|
35468
|
-
// `data-color`).
|
|
35469
|
-
notes: {
|
|
35470
|
-
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'mark'],
|
|
35471
|
-
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-color'],
|
|
35472
|
-
ALLOW_DATA_ATTR: false,
|
|
35473
|
-
},
|
|
35474
|
-
email: {
|
|
35475
|
-
ALLOWED_TAGS: [
|
|
35476
|
-
...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote',
|
|
35477
|
-
'h2', 'h3', 'div', 'img',
|
|
35478
|
-
],
|
|
35479
|
-
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'data-transform', 'data-fallback', 'src', 'alt', 'width', 'height', 'data-file-id', 'data-image-layout'],
|
|
35480
|
-
ALLOW_DATA_ATTR: false,
|
|
35481
|
-
},
|
|
35482
|
-
document: {
|
|
35483
|
-
ALLOWED_TAGS: [
|
|
35484
|
-
...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote',
|
|
35485
|
-
'h1', 'h2', 'h3', 'h4', 'hr', 'div', 'mark', 'img',
|
|
35486
|
-
'table', 'thead', 'tbody', 'tr', 'th', 'td',
|
|
35487
|
-
'input', 'label', 'iframe',
|
|
35488
|
-
],
|
|
35489
|
-
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'colspan', 'rowspan', 'src', 'alt', 'width', 'height', 'type', 'checked', 'disabled', 'allowfullscreen', 'frameborder', 'allow', 'loading'],
|
|
35490
|
-
ALLOW_DATA_ATTR: true,
|
|
35491
|
-
},
|
|
35492
|
-
'block-email': {
|
|
35493
|
-
ALLOWED_TAGS: [...BASE_INLINE, 'u', 's', 'code', 'ul', 'ol', 'li', 'blockquote', 'h2', 'h3', 'div', 'img'],
|
|
35494
|
-
ALLOWED_ATTR: [...BASE_ATTR, 'style', 'data-variable', 'data-transform', 'data-fallback', 'src', 'alt', 'width', 'height', 'data-file-id', 'data-image-layout', 'data-type', 'data-label', 'data-href', 'data-bg', 'data-color', 'data-height'],
|
|
35495
|
-
ALLOW_DATA_ATTR: false,
|
|
35496
|
-
},
|
|
35497
|
-
};
|
|
35498
|
-
/**
|
|
35499
|
-
* `<iframe>`s are permitted only with an `https:` source (blocks javascript:,
|
|
35500
|
-
* data:, and plain http: embeds). This is what makes allowing iframes safe-ish
|
|
35501
|
-
* for a trusted-author document; YouTube embeds satisfy it too.
|
|
35502
|
-
*/
|
|
35503
|
-
const SAFE_IFRAME_SRC_RE = /^https:\/\/[^\s"'<>]+$/i;
|
|
35504
|
-
/** Tags forbidden in every preset, regardless of the allow-list. */
|
|
35505
|
-
const FORBID_TAGS = ['script', 'object', 'embed', 'form', 'textarea', 'style'];
|
|
35506
|
-
/** Attributes forbidden in every preset (event handlers handled by DOMPurify itself). */
|
|
35507
|
-
const FORBID_ATTR = ['onerror', 'onload', 'onclick'];
|
|
35508
|
-
/**
|
|
35509
|
-
* Resolve the effective config for a preset + feature overrides. Features can
|
|
35510
|
-
* only ADD capabilities (mirroring `getExtensions`, where they register extra
|
|
35511
|
-
* nodes); they never narrow a preset's base whitelist.
|
|
35512
|
-
*/
|
|
35513
|
-
function configFor(preset, features) {
|
|
35514
|
-
const cfg = SANITIZE_CONFIGS[preset] ?? SANITIZE_CONFIGS.task;
|
|
35515
|
-
if (!features)
|
|
35516
|
-
return cfg;
|
|
35517
|
-
const tags = new Set(cfg.ALLOWED_TAGS);
|
|
35518
|
-
const attr = new Set(cfg.ALLOWED_ATTR);
|
|
35519
|
-
if (features.image) {
|
|
35520
|
-
tags.add('img');
|
|
35521
|
-
['src', 'alt', 'width', 'height'].forEach((a) => attr.add(a));
|
|
35522
|
-
}
|
|
35523
|
-
if (features.variables)
|
|
35524
|
-
attr.add('data-variable');
|
|
35525
|
-
if (features.mentions) {
|
|
35526
|
-
// data-mention-id/label ya están en BASE_ATTR. Los attrs del markup V1
|
|
35527
|
-
// (quill-mention) se suman porque el BACKEND los parsea para notificar —
|
|
35528
|
-
// re-editar una nota no debe destruirlos.
|
|
35529
|
-
// Espejo inline en core/co-rich-text-viewer.tsx (el viewer no importa este módulo para no cargar el engine) — si cambias esta lista, cambia la del viewer.
|
|
35530
|
-
['data-id', 'data-value', 'data-denotation-char', 'data-index'].forEach((a) => attr.add(a));
|
|
36783
|
+
function computePageBreaks(atoms, pageContentHeight) {
|
|
36784
|
+
const breaks = [];
|
|
36785
|
+
if (!atoms.length || !(pageContentHeight > 0))
|
|
36786
|
+
return { breaks, pageCount: 1 };
|
|
36787
|
+
let page = 1;
|
|
36788
|
+
let start = 0;
|
|
36789
|
+
let pageTop = atoms[0].top;
|
|
36790
|
+
let i = 0;
|
|
36791
|
+
while (i < atoms.length) {
|
|
36792
|
+
const atom = atoms[i];
|
|
36793
|
+
const bottom = atom.top + atom.height;
|
|
36794
|
+
if (bottom - pageTop <= pageContentHeight) {
|
|
36795
|
+
i += 1;
|
|
36796
|
+
continue;
|
|
36797
|
+
}
|
|
36798
|
+
if (i === start) {
|
|
36799
|
+
// Más alto que una hoja: ocupa las que necesite; no hay frontera adentro.
|
|
36800
|
+
const extra = Math.ceil((bottom - pageTop) / pageContentHeight) - 1;
|
|
36801
|
+
page += extra;
|
|
36802
|
+
pageTop += extra * pageContentHeight;
|
|
36803
|
+
i += 1;
|
|
36804
|
+
continue;
|
|
36805
|
+
}
|
|
36806
|
+
let b = i;
|
|
36807
|
+
while (b - 1 > start && atoms[b - 1].keepWithNext)
|
|
36808
|
+
b -= 1;
|
|
36809
|
+
page += 1;
|
|
36810
|
+
breaks.push({ pos: atoms[b].pos, page });
|
|
36811
|
+
start = b;
|
|
36812
|
+
pageTop = atoms[b].top;
|
|
36813
|
+
i = b;
|
|
35531
36814
|
}
|
|
35532
|
-
return {
|
|
36815
|
+
return { breaks, pageCount: page };
|
|
35533
36816
|
}
|
|
35534
36817
|
/**
|
|
35535
|
-
*
|
|
35536
|
-
*
|
|
35537
|
-
* ListStyleExtension's `list-style-type`). Everything else — position,
|
|
35538
|
-
* background-image url(), etc. — is UI-redressing/tracking surface.
|
|
36818
|
+
* Resta a cada átomo la altura de las bandas de salto que ya están dibujadas
|
|
36819
|
+
* por encima (V1 medía con las bandas viejas en el flujo y cortaba antes).
|
|
35539
36820
|
*/
|
|
35540
|
-
|
|
35541
|
-
|
|
35542
|
-
|
|
35543
|
-
|
|
35544
|
-
|
|
35545
|
-
|
|
35546
|
-
|
|
35547
|
-
|
|
35548
|
-
|
|
35549
|
-
|
|
35550
|
-
|
|
35551
|
-
if (!html)
|
|
35552
|
-
return '';
|
|
35553
|
-
const cfg = configFor(preset, features);
|
|
35554
|
-
purify.addHook('uponSanitizeElement', (node, data) => {
|
|
35555
|
-
// Only allow <input type="checkbox"> (task list checkboxes); strip others.
|
|
35556
|
-
if (data.tagName === 'input' && node.type !== 'checkbox') {
|
|
35557
|
-
node.parentNode?.removeChild(node);
|
|
35558
|
-
}
|
|
35559
|
-
// Allow only https iframes (YouTube and other embeds); drop anything else.
|
|
35560
|
-
if (data.tagName === 'iframe') {
|
|
35561
|
-
const src = node.getAttribute('src') || '';
|
|
35562
|
-
if (!SAFE_IFRAME_SRC_RE.test(src))
|
|
35563
|
-
node.parentNode?.removeChild(node);
|
|
36821
|
+
function stripBandOffsets(atoms, bands) {
|
|
36822
|
+
if (!bands.length)
|
|
36823
|
+
return atoms;
|
|
36824
|
+
const sorted = [...bands].sort((a, b) => a.top - b.top);
|
|
36825
|
+
return atoms.map((atom) => {
|
|
36826
|
+
let shift = 0;
|
|
36827
|
+
for (const band of sorted) {
|
|
36828
|
+
if (band.top < atom.top)
|
|
36829
|
+
shift += band.height;
|
|
36830
|
+
else
|
|
36831
|
+
break;
|
|
35564
36832
|
}
|
|
36833
|
+
return shift ? { ...atom, top: atom.top - shift } : atom;
|
|
35565
36834
|
});
|
|
35566
|
-
|
|
35567
|
-
|
|
35568
|
-
|
|
35569
|
-
|
|
35570
|
-
|
|
35571
|
-
|
|
35572
|
-
|
|
35573
|
-
|
|
35574
|
-
|
|
35575
|
-
|
|
35576
|
-
|
|
35577
|
-
|
|
35578
|
-
|
|
35579
|
-
|
|
35580
|
-
|
|
35581
|
-
|
|
35582
|
-
|
|
35583
|
-
|
|
35584
|
-
|
|
36835
|
+
}
|
|
36836
|
+
const ATOM_TYPES = new Set(['listItem', 'tableRow']);
|
|
36837
|
+
const CONTAINER_TYPES = new Set(['bulletList', 'orderedList', 'table']);
|
|
36838
|
+
function classifyPaginationNode(node) {
|
|
36839
|
+
const name = node?.type?.name;
|
|
36840
|
+
if (!name || name === 'doc')
|
|
36841
|
+
return 'skip';
|
|
36842
|
+
if (CONTAINER_TYPES.has(name))
|
|
36843
|
+
return 'container';
|
|
36844
|
+
if (ATOM_TYPES.has(name))
|
|
36845
|
+
return 'atom';
|
|
36846
|
+
if (node?.isBlock)
|
|
36847
|
+
return 'atom';
|
|
36848
|
+
return 'skip';
|
|
36849
|
+
}
|
|
36850
|
+
function isKeepWithNext(node) {
|
|
36851
|
+
return node.type.name === 'heading' && Number(node.attrs?.level) >= 2;
|
|
36852
|
+
}
|
|
36853
|
+
/** Columnas de una tabla = suma de colspan de su primera fila (banda-fila que cruza la tabla). */
|
|
36854
|
+
function tableColumnCount(tableNode) {
|
|
36855
|
+
const firstRow = tableNode && typeof tableNode.child === 'function' ? tableNode.child(0) : null;
|
|
36856
|
+
if (!firstRow || typeof firstRow.forEach !== 'function')
|
|
36857
|
+
return 1;
|
|
36858
|
+
let cols = 0;
|
|
36859
|
+
firstRow.forEach((cell) => {
|
|
36860
|
+
cols += cell?.attrs?.colspan || 1;
|
|
35585
36861
|
});
|
|
35586
|
-
|
|
35587
|
-
const clean = purify.sanitize(html, {
|
|
35588
|
-
ALLOWED_TAGS: cfg.ALLOWED_TAGS,
|
|
35589
|
-
ALLOWED_ATTR: cfg.ALLOWED_ATTR,
|
|
35590
|
-
ALLOW_DATA_ATTR: cfg.ALLOW_DATA_ATTR ?? false,
|
|
35591
|
-
FORBID_TAGS,
|
|
35592
|
-
FORBID_ATTR,
|
|
35593
|
-
ADD_ATTR: ['target', 'allowfullscreen', 'frameborder', 'allow'],
|
|
35594
|
-
});
|
|
35595
|
-
return clean;
|
|
35596
|
-
}
|
|
35597
|
-
finally {
|
|
35598
|
-
purify.removeHook('uponSanitizeElement');
|
|
35599
|
-
purify.removeHook('uponSanitizeAttribute');
|
|
35600
|
-
}
|
|
36862
|
+
return cols || 1;
|
|
35601
36863
|
}
|
|
35602
36864
|
|
|
35603
|
-
/**
|
|
35604
|
-
|
|
35605
|
-
|
|
35606
|
-
}
|
|
35607
|
-
|
|
35608
|
-
|
|
35609
|
-
|
|
35610
|
-
|
|
35611
|
-
|
|
35612
|
-
|
|
35613
|
-
|
|
35614
|
-
|
|
35615
|
-
|
|
35616
|
-
|
|
35617
|
-
|
|
35618
|
-
|
|
35619
|
-
|
|
35620
|
-
|
|
35621
|
-
return sanitize(html, preset, features);
|
|
35622
|
-
}
|
|
35623
|
-
/** HTML string → ProseMirror JSON (tolerant; unknown nodes are dropped by the schema). */
|
|
35624
|
-
function htmlToJson(html, preset = 'task', features) {
|
|
35625
|
-
const safe = sanitize(html ?? '', preset, features);
|
|
35626
|
-
return generateJSON(safe, schemaExtensions(preset, features));
|
|
36865
|
+
/** px a 96 dpi. */
|
|
36866
|
+
const PAGE_SIZES = {
|
|
36867
|
+
letter: { width: 816, height: 1056 },
|
|
36868
|
+
a4: { width: 794, height: 1123 },
|
|
36869
|
+
legal: { width: 816, height: 1344 },
|
|
36870
|
+
};
|
|
36871
|
+
/** Separación visual entre hojas (no existe en el PDF). */
|
|
36872
|
+
const PAGE_GAP_PX = 24;
|
|
36873
|
+
const PAGINATION_META = 'coPagination';
|
|
36874
|
+
function resolvePageSize(page) {
|
|
36875
|
+
if (!page)
|
|
36876
|
+
return PAGE_SIZES.letter;
|
|
36877
|
+
if (typeof page === 'string')
|
|
36878
|
+
return PAGE_SIZES[page] ?? PAGE_SIZES.letter;
|
|
36879
|
+
return { width: Number(page.width), height: Number(page.height) };
|
|
36880
|
+
}
|
|
36881
|
+
function pageContentHeight(o) {
|
|
36882
|
+
return o.page.height - o.margins.top - o.margins.bottom - o.header.height - o.footer.height;
|
|
35627
36883
|
}
|
|
35628
36884
|
/**
|
|
35629
|
-
*
|
|
35630
|
-
*
|
|
36885
|
+
* Top relativo a `root` sumando `offsetTop` por la cadena de offsetParent.
|
|
36886
|
+
* `root` tiene que ser posicionado (la extensión le pone `position: relative`).
|
|
36887
|
+
* Son px de LAYOUT: un `transform: scale()` de zoom no los altera.
|
|
35631
36888
|
*/
|
|
35632
|
-
function
|
|
35633
|
-
|
|
35634
|
-
|
|
35635
|
-
|
|
35636
|
-
|
|
35637
|
-
|
|
35638
|
-
|
|
35639
|
-
|
|
35640
|
-
|
|
35641
|
-
|
|
35642
|
-
|
|
35643
|
-
|
|
35644
|
-
|
|
35645
|
-
|
|
35646
|
-
|
|
35647
|
-
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
}
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
}
|
|
35655
|
-
if (node.type === 'hardBreak') {
|
|
35656
|
-
out.push('\n');
|
|
35657
|
-
return;
|
|
35658
|
-
}
|
|
35659
|
-
if (Array.isArray(node.content))
|
|
35660
|
-
node.content.forEach(walk);
|
|
35661
|
-
if (BLOCK.has(node.type))
|
|
35662
|
-
out.push('\n');
|
|
36889
|
+
function layoutBox(el, root) {
|
|
36890
|
+
let top = 0;
|
|
36891
|
+
let node = el;
|
|
36892
|
+
while (node && node !== root) {
|
|
36893
|
+
top += node.offsetTop;
|
|
36894
|
+
node = node.offsetParent;
|
|
36895
|
+
}
|
|
36896
|
+
return { top, height: el.offsetHeight };
|
|
36897
|
+
}
|
|
36898
|
+
const defaultSchedule = (cb) => {
|
|
36899
|
+
if (typeof requestAnimationFrame === 'function')
|
|
36900
|
+
requestAnimationFrame(() => cb());
|
|
36901
|
+
else
|
|
36902
|
+
setTimeout(cb, 16);
|
|
36903
|
+
};
|
|
36904
|
+
function toLayoutOptions(o) {
|
|
36905
|
+
return {
|
|
36906
|
+
page: resolvePageSize(o.page),
|
|
36907
|
+
margins: { ...o.margins },
|
|
36908
|
+
header: { height: o.header?.height ?? 0 },
|
|
36909
|
+
footer: { height: o.footer?.height ?? 0 },
|
|
36910
|
+
gap: o.gap ?? PAGE_GAP_PX,
|
|
35663
36911
|
};
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35667
|
-
|
|
35668
|
-
|
|
35669
|
-
|
|
35670
|
-
|
|
35671
|
-
|
|
35672
|
-
|
|
35673
|
-
|
|
35674
|
-
|
|
35675
|
-
|
|
35676
|
-
|
|
35677
|
-
|
|
35678
|
-
|
|
35679
|
-
|
|
35680
|
-
|
|
35681
|
-
|
|
35682
|
-
|
|
35683
|
-
|
|
35684
|
-
|
|
35685
|
-
|
|
35686
|
-
|
|
35687
|
-
|
|
35688
|
-
|
|
35689
|
-
|
|
35690
|
-
|
|
35691
|
-
|
|
35692
|
-
|
|
35693
|
-
|
|
35694
|
-
|
|
36912
|
+
}
|
|
36913
|
+
/**
|
|
36914
|
+
* Banda de salto: margen inferior (+ pie) de la hoja que termina, separación
|
|
36915
|
+
* entre hojas y margen superior (+ encabezado) de la que empieza. Dentro de una
|
|
36916
|
+
* tabla va envuelta en una fila con el colspan de la tabla (un div entre `tr`
|
|
36917
|
+
* lo expulsaría el navegador).
|
|
36918
|
+
*/
|
|
36919
|
+
function buildPageGap(page, pageCount, o, header, cols = 0) {
|
|
36920
|
+
const foot = o.margins.bottom + o.footer.height;
|
|
36921
|
+
const head = o.margins.top + o.header.height;
|
|
36922
|
+
const band = document.createElement('div');
|
|
36923
|
+
band.className = 'co-doc-page-gap';
|
|
36924
|
+
band.setAttribute('contenteditable', 'false');
|
|
36925
|
+
band.style.height = `${foot + o.gap + head}px`;
|
|
36926
|
+
const bottom = document.createElement('div');
|
|
36927
|
+
bottom.className = 'co-doc-page-gap__foot';
|
|
36928
|
+
bottom.style.height = `${foot}px`;
|
|
36929
|
+
const between = document.createElement('div');
|
|
36930
|
+
between.className = 'co-doc-page-gap__between';
|
|
36931
|
+
between.style.height = `${o.gap}px`;
|
|
36932
|
+
const top = document.createElement('div');
|
|
36933
|
+
top.className = 'co-doc-page-gap__head';
|
|
36934
|
+
top.style.height = `${head}px`;
|
|
36935
|
+
top.style.paddingTop = `${o.margins.top}px`;
|
|
36936
|
+
top.style.boxSizing = 'border-box';
|
|
36937
|
+
if (header)
|
|
36938
|
+
top.appendChild(header);
|
|
36939
|
+
band.append(bottom, between, top);
|
|
36940
|
+
band.setAttribute('data-page', String(page));
|
|
36941
|
+
band.setAttribute('data-page-count', String(pageCount));
|
|
36942
|
+
if (!cols) {
|
|
36943
|
+
band.setAttribute('data-co-page-gap', String(page));
|
|
36944
|
+
return band;
|
|
36945
|
+
}
|
|
36946
|
+
const tr = document.createElement('tr');
|
|
36947
|
+
tr.className = 'co-doc-page-gap-row';
|
|
36948
|
+
tr.setAttribute('contenteditable', 'false');
|
|
36949
|
+
tr.setAttribute('data-co-page-gap', String(page));
|
|
36950
|
+
const td = document.createElement('td');
|
|
36951
|
+
td.setAttribute('colspan', String(cols));
|
|
36952
|
+
td.appendChild(band);
|
|
36953
|
+
tr.appendChild(td);
|
|
36954
|
+
return tr;
|
|
36955
|
+
}
|
|
36956
|
+
function measureLayout(view, opts, measure) {
|
|
36957
|
+
const root = view.dom;
|
|
36958
|
+
const atoms = [];
|
|
36959
|
+
view.state.doc.descendants((node, pos) => {
|
|
36960
|
+
const kind = classifyPaginationNode(node);
|
|
36961
|
+
if (kind === 'container')
|
|
36962
|
+
return true;
|
|
36963
|
+
if (kind === 'skip')
|
|
36964
|
+
return false;
|
|
36965
|
+
const dom = view.nodeDOM(pos);
|
|
36966
|
+
if (!(dom instanceof HTMLElement))
|
|
36967
|
+
return false;
|
|
36968
|
+
const box = measure(dom, root);
|
|
36969
|
+
if (box.height > 0)
|
|
36970
|
+
atoms.push({ pos, top: box.top, height: box.height, keepWithNext: isKeepWithNext(node) });
|
|
36971
|
+
return false;
|
|
36972
|
+
});
|
|
36973
|
+
const bands = Array.from(root.querySelectorAll('[data-co-page-gap]')).map((el) => measure(el, root));
|
|
36974
|
+
return computePageBreaks(stripBandOffsets(atoms, bands), pageContentHeight(opts));
|
|
36975
|
+
}
|
|
36976
|
+
const paginationKey = new PluginKey('coPagination');
|
|
36977
|
+
const Pagination = Extension.create({
|
|
36978
|
+
name: 'pagination',
|
|
36979
|
+
addOptions() {
|
|
36980
|
+
return {
|
|
36981
|
+
page: 'letter',
|
|
36982
|
+
margins: { top: 38, right: 38, bottom: 38, left: 38 },
|
|
36983
|
+
header: { height: 0 },
|
|
36984
|
+
footer: { height: 0 },
|
|
36985
|
+
gap: PAGE_GAP_PX,
|
|
36986
|
+
renderHeader: null,
|
|
36987
|
+
onPagesChange: null,
|
|
36988
|
+
measure: layoutBox,
|
|
36989
|
+
schedule: defaultSchedule,
|
|
36990
|
+
};
|
|
36991
|
+
},
|
|
36992
|
+
addStorage() {
|
|
36993
|
+
return { pageCount: 1 };
|
|
36994
|
+
},
|
|
36995
|
+
addCommands() {
|
|
36996
|
+
return {
|
|
36997
|
+
setPaginationOptions: (patch) => ({ tr, dispatch }) => {
|
|
36998
|
+
if (dispatch)
|
|
36999
|
+
tr.setMeta(PAGINATION_META, { patch });
|
|
37000
|
+
return true;
|
|
37001
|
+
},
|
|
37002
|
+
remeasurePages: () => ({ tr, dispatch }) => {
|
|
37003
|
+
if (dispatch)
|
|
37004
|
+
tr.setMeta(PAGINATION_META, { remeasure: true });
|
|
37005
|
+
return true;
|
|
37006
|
+
},
|
|
37007
|
+
};
|
|
37008
|
+
},
|
|
37009
|
+
addProseMirrorPlugins() {
|
|
37010
|
+
const options = this.options;
|
|
37011
|
+
const storage = this.storage;
|
|
37012
|
+
let lastEmitted = 0;
|
|
37013
|
+
const decorationsFor = (state, st) => {
|
|
37014
|
+
const decos = [];
|
|
37015
|
+
for (const br of st.layout.breaks) {
|
|
37016
|
+
if (br.pos < 0 || br.pos > state.doc.content.size)
|
|
37017
|
+
continue;
|
|
37018
|
+
// Nunca un widget de bloque dentro de contenido inline.
|
|
37019
|
+
if (state.doc.resolve(br.pos).parent.inlineContent)
|
|
37020
|
+
continue;
|
|
37021
|
+
const node = state.doc.nodeAt(br.pos);
|
|
37022
|
+
const cols = node?.type.name === 'tableRow' ? tableColumnCount(state.doc.resolve(br.pos).parent) : 0;
|
|
37023
|
+
decos.push(Decoration.widget(br.pos, () => buildPageGap(br.page, st.layout.pageCount, st.opts, options.renderHeader?.(br.page, st.layout.pageCount) ?? null, cols), { side: -1, key: `co-pg-${br.page}-${st.layout.pageCount}-v${st.version}-c${cols}` }));
|
|
37024
|
+
}
|
|
37025
|
+
return DecorationSet.create(state.doc, decos);
|
|
37026
|
+
};
|
|
37027
|
+
return [
|
|
37028
|
+
new Plugin({
|
|
37029
|
+
key: paginationKey,
|
|
37030
|
+
state: {
|
|
37031
|
+
init: () => ({ layout: { breaks: [], pageCount: 1 }, opts: toLayoutOptions(options), version: 0, dirty: true }),
|
|
37032
|
+
apply(tr, prev) {
|
|
37033
|
+
const meta = tr.getMeta(PAGINATION_META);
|
|
37034
|
+
let next = prev;
|
|
37035
|
+
if (tr.docChanged) {
|
|
37036
|
+
next = {
|
|
37037
|
+
...next,
|
|
37038
|
+
dirty: true,
|
|
37039
|
+
// Un salto cuya frontera se borró (p. ej. unir párrafos en el borde de una hoja)
|
|
37040
|
+
// se descarta: mapearlo lo dejaría dentro de un textblock. La medición siguiente
|
|
37041
|
+
// lo recalcula.
|
|
37042
|
+
layout: {
|
|
37043
|
+
...next.layout,
|
|
37044
|
+
breaks: next.layout.breaks.flatMap((b) => {
|
|
37045
|
+
const r = tr.mapping.mapResult(b.pos);
|
|
37046
|
+
return r.deleted ? [] : [{ ...b, pos: r.pos }];
|
|
37047
|
+
}),
|
|
37048
|
+
},
|
|
37049
|
+
};
|
|
37050
|
+
}
|
|
37051
|
+
if (meta?.patch) {
|
|
37052
|
+
const merged = toLayoutOptions({ ...next.opts, ...meta.patch, page: meta.patch.page ?? next.opts.page });
|
|
37053
|
+
next = { ...next, opts: merged, version: next.version + 1, dirty: true };
|
|
37054
|
+
}
|
|
37055
|
+
if (meta?.remeasure)
|
|
37056
|
+
next = { ...next, dirty: true };
|
|
37057
|
+
if (meta?.layout)
|
|
37058
|
+
next = { ...next, layout: meta.layout, dirty: false };
|
|
37059
|
+
return next;
|
|
37060
|
+
},
|
|
37061
|
+
},
|
|
37062
|
+
props: {
|
|
37063
|
+
decorations(state) {
|
|
37064
|
+
const st = paginationKey.getState(state);
|
|
37065
|
+
return st ? decorationsFor(state, st) : DecorationSet.empty;
|
|
37066
|
+
},
|
|
37067
|
+
},
|
|
37068
|
+
view(view) {
|
|
37069
|
+
const root = view.dom;
|
|
37070
|
+
if (!root.style.position)
|
|
37071
|
+
root.style.position = 'relative';
|
|
37072
|
+
let scheduled = false;
|
|
37073
|
+
let alive = true;
|
|
37074
|
+
const run = () => {
|
|
37075
|
+
scheduled = false;
|
|
37076
|
+
if (!alive || view.isDestroyed)
|
|
37077
|
+
return;
|
|
37078
|
+
const st = paginationKey.getState(view.state);
|
|
37079
|
+
if (!st)
|
|
37080
|
+
return;
|
|
37081
|
+
const layout = measureLayout(view, st.opts, options.measure);
|
|
37082
|
+
storage.pageCount = layout.pageCount;
|
|
37083
|
+
view.dispatch(view.state.tr.setMeta(PAGINATION_META, { layout }));
|
|
37084
|
+
if (layout.pageCount !== lastEmitted) {
|
|
37085
|
+
lastEmitted = layout.pageCount;
|
|
37086
|
+
options.onPagesChange?.(layout.pageCount);
|
|
37087
|
+
}
|
|
37088
|
+
};
|
|
37089
|
+
const maybeSchedule = () => {
|
|
37090
|
+
const st = paginationKey.getState(view.state);
|
|
37091
|
+
if (!st?.dirty || scheduled)
|
|
37092
|
+
return;
|
|
37093
|
+
scheduled = true;
|
|
37094
|
+
options.schedule(run);
|
|
37095
|
+
};
|
|
37096
|
+
const remeasure = () => {
|
|
37097
|
+
if (alive && !view.isDestroyed) {
|
|
37098
|
+
view.dispatch(view.state.tr.setMeta(PAGINATION_META, { remeasure: true }));
|
|
37099
|
+
}
|
|
37100
|
+
};
|
|
37101
|
+
// Imágenes que cargan tarde cambian alturas (las de las bandas no: tienen alto fijo).
|
|
37102
|
+
const onLoad = (e) => {
|
|
37103
|
+
const t = e.target;
|
|
37104
|
+
if (!t || t.tagName !== 'IMG' || t.closest('[data-co-page-gap]'))
|
|
37105
|
+
return;
|
|
37106
|
+
remeasure();
|
|
37107
|
+
};
|
|
37108
|
+
root.addEventListener('load', onLoad, true);
|
|
37109
|
+
const fonts = document.fonts;
|
|
37110
|
+
fonts?.ready?.then(remeasure).catch(() => { });
|
|
37111
|
+
// Fuentes que cargan después (un peso o familia aplicado por primera vez).
|
|
37112
|
+
fonts?.addEventListener?.('loadingdone', remeasure);
|
|
37113
|
+
maybeSchedule();
|
|
37114
|
+
return {
|
|
37115
|
+
update: () => maybeSchedule(),
|
|
37116
|
+
destroy: () => {
|
|
37117
|
+
alive = false;
|
|
37118
|
+
root.removeEventListener('load', onLoad, true);
|
|
37119
|
+
fonts?.removeEventListener?.('loadingdone', remeasure);
|
|
37120
|
+
},
|
|
37121
|
+
};
|
|
37122
|
+
},
|
|
37123
|
+
}),
|
|
37124
|
+
];
|
|
37125
|
+
},
|
|
35695
37126
|
});
|
|
35696
37127
|
|
|
35697
|
-
export {
|
|
37128
|
+
export { defaultToolbarForPreset as A, getImageLayoutMode as B, CalloutNode as C, getPrintExtensions as D, headerImageWidth as E, FIELD_SYNC_META as F, layoutBox as G, maxLengthForPreset as H, IMAGE_LAYOUT_MODES as I, nextFreeSlot as J, normalizePrintHtml as K, LetterSpacingExtension as L, MentionNode as M, pageContentHeight as N, parseFieldToken as O, PAGE_GAP_PX as P, resolvePageSize as Q, sanitizeFieldName as R, SANITIZE_CONFIGS as S, TRAILING_BREAK_CLASS as T, serializePrintHtml as U, VariableNode as V, setFieldValuesInHtml as W, signatureBoxStyle as X, signatureSlotHtml as Y, signatureToken as Z, stripBandOffsets as _, generateJSON as a, getExtensions as b, CobaltTaskItem as c, CobaltTaskList as d, FontFamilyExtension as e, FontSizeExtension as f, generateHTML as g, FontWeightExtension as h, isPrintPreset as i, IndentExtension as j, LineHeightExtension as k, ListStyleExtension as l, MergeField as m, PAGE_SIZES as n, PAGINATION_META as o, PRESETS as p, Pagination as q, readPrintHtml as r, sanitize as s, SIGNATURE_TOKEN_STYLE as t, SignatureSlot as u, applyImageLayoutMode as v, writePrintHtml as w, collectFields as x, computePageBreaks as y, createEditor as z };
|