@prolibu-suite/cobalt-core 0.7.0 → 0.9.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 +8275 -5147
- package/dist/cjs/co-accordion.cjs.entry.js +4 -4
- package/dist/cjs/co-action-bar.cjs.entry.js +89 -0
- package/dist/cjs/co-app-shell.cjs.entry.js +4 -4
- package/dist/cjs/co-audio-player.cjs.entry.js +279 -0
- package/dist/cjs/co-autocomplete.cjs.entry.js +15 -6
- package/dist/cjs/co-avatar-group.cjs.entry.js +2 -2
- package/dist/cjs/co-avatar_2.cjs.entry.js +17 -6
- 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 +13 -6
- package/dist/cjs/co-calendar-month_2.cjs.entry.js +1601 -0
- package/dist/cjs/co-calendar_3.cjs.entry.js +13 -13
- package/dist/cjs/co-card.cjs.entry.js +4 -4
- package/dist/cjs/co-carousel-item.cjs.entry.js +2 -2
- package/dist/cjs/co-carousel.cjs.entry.js +4 -4
- package/dist/cjs/co-checkbox-group.cjs.entry.js +2 -2
- package/dist/cjs/co-checkbox.cjs.entry.js +5 -5
- package/dist/cjs/co-chip_2.cjs.entry.js +109 -8
- package/dist/cjs/co-color-picker.cjs.entry.js +5 -5
- package/dist/cjs/co-column-picker.cjs.entry.js +4 -4
- package/dist/cjs/co-combobox.cjs.entry.js +14 -6
- package/dist/cjs/co-command-search.cjs.entry.js +20 -6
- package/dist/cjs/co-data-table-pagination.cjs.entry.js +6 -6
- package/dist/cjs/co-data-table.cjs.entry.js +2 -2
- package/dist/cjs/co-date-picker.cjs.entry.js +3 -3
- package/dist/cjs/co-date-range-picker.cjs.entry.js +5 -5
- package/dist/cjs/co-date-range.cjs.entry.js +2 -2
- package/dist/cjs/co-date-time-picker.cjs.entry.js +409 -0
- package/dist/cjs/co-drive-card.cjs.entry.js +3 -3
- package/dist/cjs/co-drop-zone.cjs.entry.js +2 -2
- package/dist/cjs/co-empty-state.cjs.entry.js +3 -3
- package/dist/cjs/co-event-calendar.cjs.entry.js +119 -18
- package/dist/cjs/co-event-popover.cjs.entry.js +132 -0
- package/dist/cjs/co-fab.cjs.entry.js +4 -4
- 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-helper-text.cjs.entry.js +3 -3
- package/dist/cjs/co-icon-button.cjs.entry.js +3 -3
- package/dist/cjs/co-icon.cjs.entry.js +3 -3
- package/dist/cjs/co-input.cjs.entry.js +3 -3
- package/dist/cjs/co-json-field.cjs.entry.js +6 -6
- package/dist/cjs/co-lightbox.cjs.entry.js +7 -7
- package/dist/cjs/co-list-footer.cjs.entry.js +3 -3
- package/dist/cjs/co-list-item.cjs.entry.js +6 -6
- 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 +2 -2
- package/dist/cjs/co-menu-item.cjs.entry.js +3 -3
- package/dist/cjs/co-menu.cjs.entry.js +2 -2
- package/dist/cjs/co-modal.cjs.entry.js +4 -4
- 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 +3 -3
- package/dist/cjs/co-page-header.cjs.entry.js +2 -2
- package/dist/cjs/co-phone-field.cjs.entry.js +4 -4
- package/dist/cjs/co-popover.cjs.entry.js +33 -7
- package/dist/cjs/co-progress-bar.cjs.entry.js +3 -3
- package/dist/cjs/co-progress-circle.cjs.entry.js +4 -4
- package/dist/cjs/co-provider-logo_2.cjs.entry.js +72 -0
- package/dist/cjs/co-radio-group.cjs.entry.js +2 -2
- package/dist/cjs/co-radio.cjs.entry.js +5 -5
- package/dist/cjs/co-rating.cjs.entry.js +5 -5
- package/dist/cjs/co-ref-field.cjs.entry.js +22 -6
- package/dist/cjs/co-rich-text-editor.cjs.entry.js +5 -5
- package/dist/cjs/co-rich-text-toolbar.cjs.entry.js +2 -2
- package/dist/cjs/co-rich-text-viewer.cjs.entry.js +1 -1
- package/dist/cjs/co-row.cjs.entry.js +3 -3
- package/dist/cjs/co-segmented-bar.cjs.entry.js +1 -1
- package/dist/cjs/co-select.cjs.entry.js +4 -4
- package/dist/cjs/co-separator.cjs.entry.js +2 -2
- package/dist/cjs/co-sidebar-nav.cjs.entry.js +14 -4
- package/dist/cjs/co-sidebar.cjs.entry.js +4 -4
- package/dist/cjs/co-slider.cjs.entry.js +4 -4
- package/dist/cjs/co-switch.cjs.entry.js +6 -6
- package/dist/cjs/co-tab-group.cjs.entry.js +20 -4
- package/dist/cjs/co-tab.cjs.entry.js +4 -4
- package/dist/cjs/co-tags-field.cjs.entry.js +9 -9
- package/dist/cjs/co-textarea.cjs.entry.js +7 -7
- package/dist/cjs/co-toast.cjs.entry.js +35 -5
- package/dist/cjs/co-toggle-button-group.cjs.entry.js +3 -3
- package/dist/cjs/co-toggle-button.cjs.entry.js +4 -4
- package/dist/cjs/co-transfer-list.cjs.entry.js +4 -4
- package/dist/cjs/co-video.cjs.entry.js +4 -4
- package/dist/cjs/cobalt.cjs.js +2 -2
- package/dist/cjs/{dateI18n-BdiNAl0_.js → dateI18n-wblvKRq_.js} +21 -0
- package/dist/cjs/{dates-D8D-aS5d.js → dates-TQLHlFd2.js} +64 -13
- package/dist/cjs/{fieldChip-B_npd9rL.js → fieldChip-xNJPEFcS.js} +1 -1
- package/dist/cjs/{index-kBiXCEE7.js → index-DK-2Lzmf.js} +29 -13
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/{positioning-DwHW399W.js → positioning-DCj_AJ2T.js} +10 -0
- package/dist/cjs/{requiredMark-DuMIcKtt.js → requiredMark-C1xcrzSZ.js} +1 -1
- package/dist/cjs/time-LBHtzLHe.js +42 -0
- package/dist/cobalt/cobalt.css +1 -1
- package/dist/cobalt/cobalt.esm.js +1 -1
- package/dist/cobalt/p-09fe6ece.entry.js +1 -0
- package/dist/cobalt/p-12a0ae24.entry.js +1 -0
- package/dist/cobalt/p-142506d9.entry.js +1 -0
- package/dist/cobalt/{p-e4555fda.entry.js → p-19706923.entry.js} +1 -1
- package/dist/cobalt/p-19f5d480.entry.js +1 -0
- package/dist/cobalt/{p-B6JZk8fL.js → p-1EK-COrb.js} +1 -1
- package/dist/cobalt/p-1ddbc48b.entry.js +1 -0
- package/dist/cobalt/p-212ed2ba.entry.js +1 -0
- package/dist/cobalt/p-21832273.entry.js +1 -0
- package/dist/cobalt/p-24e93fcb.entry.js +1 -0
- package/dist/cobalt/{p-9211e794.entry.js → p-2a9ba4c8.entry.js} +1 -1
- package/dist/cobalt/p-2aeb1471.entry.js +1 -0
- package/dist/cobalt/p-2d0354cc.entry.js +1 -0
- package/dist/cobalt/p-30ec230e.entry.js +1 -0
- package/dist/cobalt/p-360714ae.entry.js +1 -0
- package/dist/cobalt/{p-3c681e3a.entry.js → p-37541725.entry.js} +1 -1
- package/dist/cobalt/p-39007b6c.entry.js +1 -0
- package/dist/cobalt/{p-2f6c7eb5.entry.js → p-39bce45f.entry.js} +1 -1
- package/dist/cobalt/{p-7f78e45c.entry.js → p-3ac316e0.entry.js} +1 -1
- package/dist/cobalt/{p-69723cc1.entry.js → p-3ced4269.entry.js} +1 -1
- package/dist/cobalt/{p-9994fcd8.entry.js → p-3ecc29fa.entry.js} +1 -1
- package/dist/cobalt/p-417ce874.entry.js +1 -0
- package/dist/cobalt/{p-28ab9cef.entry.js → p-43b1f8fb.entry.js} +1 -1
- package/dist/cobalt/p-49bde154.entry.js +1 -0
- package/dist/cobalt/{p-54f8c68c.entry.js → p-4e2379e4.entry.js} +1 -1
- package/dist/cobalt/p-5d7c26f2.entry.js +1 -0
- package/dist/cobalt/p-5ddebd10.entry.js +1 -0
- package/dist/cobalt/{p-bcd38114.entry.js → p-5f0f5d4b.entry.js} +1 -1
- package/dist/cobalt/{p-2661557c.entry.js → p-5f1b273b.entry.js} +1 -1
- package/dist/cobalt/p-60cb66f5.entry.js +1 -0
- package/dist/cobalt/{p-48fbf051.entry.js → p-6795a258.entry.js} +1 -1
- package/dist/cobalt/{p-5b84cf37.entry.js → p-6d532475.entry.js} +1 -1
- package/dist/cobalt/{p-0110108f.entry.js → p-724b9b54.entry.js} +1 -1
- package/dist/cobalt/{p-ceb85838.entry.js → p-7255ab63.entry.js} +1 -1
- package/dist/cobalt/p-7521ec0c.entry.js +1 -0
- package/dist/cobalt/{p-69ce2269.entry.js → p-76dff399.entry.js} +1 -1
- package/dist/cobalt/p-77593319.entry.js +1 -0
- package/dist/cobalt/p-7a2dab23.entry.js +1 -0
- package/dist/cobalt/p-7ec85887.entry.js +1 -0
- package/dist/cobalt/p-81866f71.entry.js +1 -0
- package/dist/cobalt/{p-c3004e1c.entry.js → p-8206c7c0.entry.js} +1 -1
- package/dist/cobalt/p-85a61e28.entry.js +1 -0
- package/dist/cobalt/p-87fc5aed.entry.js +1 -0
- package/dist/cobalt/p-8a93beb0.entry.js +1 -0
- package/dist/cobalt/p-8b7a4f6c.entry.js +1 -0
- package/dist/cobalt/{p-741cf623.entry.js → p-8e667eb6.entry.js} +1 -1
- package/dist/cobalt/{p-3b9be8a8.entry.js → p-94a8a119.entry.js} +1 -1
- package/dist/cobalt/p-9c2ce487.entry.js +1 -0
- package/dist/cobalt/p-9fe9e2fe.entry.js +1 -0
- package/dist/cobalt/p-BMdapAL8.js +1 -0
- package/dist/cobalt/p-BU7SQCje.js +1 -0
- package/dist/cobalt/p-CF8OB_TM.js +1 -0
- package/dist/cobalt/p-CQAYgqA6.js +1 -0
- package/dist/cobalt/p-CnNpoKwg.js +1 -0
- package/dist/cobalt/p-CrtiywTB.js +2 -0
- package/dist/cobalt/p-T4la7ly_.js +1 -0
- package/dist/cobalt/{p-2da09948.entry.js → p-a074f2ba.entry.js} +1 -1
- package/dist/cobalt/{p-0a294205.entry.js → p-a45e2c00.entry.js} +1 -1
- package/dist/cobalt/p-a530bec2.entry.js +1 -0
- package/dist/cobalt/p-a5d84c7a.entry.js +1 -0
- package/dist/cobalt/p-b004c3bb.entry.js +1 -0
- package/dist/cobalt/p-b1ef5107.entry.js +1 -0
- package/dist/cobalt/p-b28dc0fe.entry.js +1 -0
- package/dist/cobalt/{p-cdedaa82.entry.js → p-b64b0a07.entry.js} +1 -1
- package/dist/cobalt/p-b800e107.entry.js +1 -0
- package/dist/cobalt/p-b9ac1f25.entry.js +1 -0
- package/dist/cobalt/{p-202eadfc.entry.js → p-baeb5d55.entry.js} +1 -1
- package/dist/cobalt/{p-7178113f.entry.js → p-c31f5c1b.entry.js} +1 -1
- package/dist/cobalt/p-c3c2087e.entry.js +1 -0
- package/dist/cobalt/p-c42c505c.entry.js +1 -0
- package/dist/cobalt/p-c7a809c7.entry.js +1 -0
- package/dist/cobalt/{p-eec1c44c.entry.js → p-c8dba82f.entry.js} +1 -1
- package/dist/cobalt/p-cc72557d.entry.js +1 -0
- package/dist/cobalt/p-d21da71a.entry.js +1 -0
- package/dist/cobalt/{p-e1b468c1.entry.js → p-d3c765e3.entry.js} +1 -1
- package/dist/cobalt/p-d7b00e5f.entry.js +1 -0
- package/dist/cobalt/p-d85a751d.entry.js +1 -0
- package/dist/cobalt/{p-3af74ab8.entry.js → p-da546d08.entry.js} +1 -1
- package/dist/cobalt/p-debe0d79.entry.js +1 -0
- package/dist/cobalt/p-e054cb52.entry.js +1 -0
- package/dist/cobalt/p-e5296961.entry.js +1 -0
- package/dist/cobalt/p-e5b5ae89.entry.js +1 -0
- package/dist/cobalt/p-e612b43c.entry.js +1 -0
- package/dist/cobalt/{p-97875bde.entry.js → p-e6882ddd.entry.js} +1 -1
- package/dist/cobalt/p-e7e34852.entry.js +1 -0
- package/dist/cobalt/p-e99832a9.entry.js +1 -0
- package/dist/cobalt/{p-616e4286.entry.js → p-ed2ca588.entry.js} +1 -1
- package/dist/cobalt/p-f1284fcd.entry.js +1 -0
- package/dist/cobalt/{p-6b38e8fb.entry.js → p-f1731fe9.entry.js} +1 -1
- package/dist/cobalt/{p-ba5c6aa7.entry.js → p-f4cebc1f.entry.js} +1 -1
- package/dist/cobalt/p-f52f61d4.entry.js +1 -0
- package/dist/cobalt/{p-a252f8c5.entry.js → p-f83b48fd.entry.js} +1 -1
- package/dist/cobalt/p-fa5c25d6.entry.js +1 -0
- package/dist/cobalt/p-fd39f410.entry.js +1 -0
- package/dist/collection/collection-manifest.json +6 -0
- package/dist/collection/components/co-accordion/co-accordion.css +12 -4
- package/dist/collection/components/co-accordion/co-accordion.js +2 -2
- package/dist/collection/components/co-action-bar/co-action-bar.css +136 -0
- package/dist/collection/components/co-action-bar/co-action-bar.js +247 -0
- package/dist/collection/components/co-app-shell/co-app-shell.css +1 -1
- package/dist/collection/components/co-app-shell/co-app-shell.js +2 -2
- package/dist/collection/components/co-audio-player/co-audio-player.css +57 -0
- package/dist/collection/components/co-audio-player/co-audio-player.js +453 -0
- package/dist/collection/components/co-audio-player/co-audio-player.utils.js +44 -0
- package/dist/collection/components/co-autocomplete/co-autocomplete.css +2 -2
- package/dist/collection/components/co-autocomplete/co-autocomplete.js +14 -5
- package/dist/collection/components/co-avatar-group/co-avatar-group.css +1 -1
- package/dist/collection/components/co-badge/co-badge.js +1 -1
- package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.css +4 -12
- package/dist/collection/components/co-bottom-sheet/co-bottom-sheet.js +3 -3
- 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 +36 -1
- package/dist/collection/components/co-button/co-button.js +31 -5
- package/dist/collection/components/co-button-group/co-button-group.js +2 -2
- package/dist/collection/components/co-calendar/co-calendar.css +2 -2
- package/dist/collection/components/co-calendar/co-calendar.js +1 -1
- package/dist/collection/components/co-calendar-month/co-calendar-month.css +53 -4
- package/dist/collection/components/co-calendar-month/co-calendar-month.js +238 -55
- package/dist/collection/components/co-calendar-timegrid/co-calendar-timegrid.css +265 -0
- package/dist/collection/components/co-calendar-timegrid/co-calendar-timegrid.js +1305 -0
- package/dist/collection/components/co-card/co-card.css +1 -1
- 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 +85 -59
- package/dist/collection/components/co-checkbox/co-checkbox.js +3 -3
- package/dist/collection/components/co-checkbox-group/co-checkbox-group.css +1 -1
- package/dist/collection/components/co-chip/co-chip.css +2 -2
- package/dist/collection/components/co-chip/co-chip.js +2 -2
- package/dist/collection/components/co-color-picker/co-color-picker.css +3 -3
- package/dist/collection/components/co-color-picker/co-color-picker.js +2 -2
- package/dist/collection/components/co-column-picker/co-column-picker.css +8 -8
- package/dist/collection/components/co-column-picker/co-column-picker.js +2 -2
- package/dist/collection/components/co-combobox/co-combobox.css +2 -2
- package/dist/collection/components/co-combobox/co-combobox.js +13 -5
- package/dist/collection/components/co-command-search/co-command-search.css +13 -3
- package/dist/collection/components/co-command-search/co-command-search.js +18 -4
- package/dist/collection/components/co-data-table/co-data-table.css +9 -9
- package/dist/collection/components/co-data-table-pagination/co-data-table-pagination.css +5 -5
- package/dist/collection/components/co-data-table-pagination/co-data-table-pagination.js +4 -4
- package/dist/collection/components/co-date-field/co-date-field.css +2 -2
- package/dist/collection/components/co-date-field/co-date-field.js +4 -4
- package/dist/collection/components/co-date-range/co-date-range.js +1 -1
- package/dist/collection/components/co-date-range-picker/co-date-range-picker.css +2 -2
- package/dist/collection/components/co-date-time-picker/co-date-time-picker.css +325 -0
- package/dist/collection/components/co-date-time-picker/co-date-time-picker.js +1079 -0
- package/dist/collection/components/co-drawer/co-drawer.css +48 -2
- package/dist/collection/components/co-drawer/co-drawer.js +1 -1
- package/dist/collection/components/co-drive-card/co-drive-card.css +5 -5
- 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-empty-state/co-empty-state.js +2 -2
- package/dist/collection/components/co-event-calendar/co-event-calendar.css +21 -9
- package/dist/collection/components/co-event-calendar/co-event-calendar.js +342 -39
- package/dist/collection/components/co-event-popover/co-event-popover.css +25 -0
- package/dist/collection/components/co-event-popover/co-event-popover.js +326 -0
- package/dist/collection/components/co-fab/co-fab.css +18 -12
- package/dist/collection/components/co-fab/co-fab.js +2 -2
- package/dist/collection/components/co-file-field/co-file-field.css +5 -5
- package/dist/collection/components/co-file-field/co-file-field.js +5 -5
- package/dist/collection/components/co-file-type-tile/co-file-type-tile.js +2 -2
- 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 +10 -1
- package/dist/collection/components/co-icon-button/co-icon-button.js +1 -1
- package/dist/collection/components/co-input/co-input.css +3 -3
- package/dist/collection/components/co-json-field/co-json-field.css +1 -1
- package/dist/collection/components/co-json-field/co-json-field.js +3 -3
- package/dist/collection/components/co-lightbox/co-lightbox.css +4 -4
- package/dist/collection/components/co-lightbox/co-lightbox.js +4 -4
- package/dist/collection/components/co-list-footer/co-list-footer.css +1 -1
- package/dist/collection/components/co-list-footer/co-list-footer.js +1 -1
- package/dist/collection/components/co-list-item/co-list-item.css +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.js +1 -1
- package/dist/collection/components/co-menu-divider/co-menu-divider.js +1 -1
- package/dist/collection/components/co-menu-item/co-menu-item.css +22 -1
- package/dist/collection/components/co-menu-item/co-menu-item.js +20 -1
- package/dist/collection/components/co-modal/co-modal.css +2 -2
- package/dist/collection/components/co-modal/co-modal.js +2 -2
- package/dist/collection/components/co-navbar/co-navbar.js +2 -2
- package/dist/collection/components/co-number-field/co-number-field.css +3 -3
- package/dist/collection/components/co-number-field/co-number-field.js +2 -2
- package/dist/collection/components/co-option-list/co-option-list.css +20 -2
- package/dist/collection/components/co-option-list/co-option-list.js +141 -7
- package/dist/collection/components/co-option-list/optionListLogic.js +13 -0
- package/dist/collection/components/co-overlay/co-overlay.css +2 -2
- package/dist/collection/components/co-overlay/co-overlay.js +1 -1
- package/dist/collection/components/co-page-header/co-page-header.js +1 -1
- package/dist/collection/components/co-phone-field/co-phone-field.css +1 -1
- package/dist/collection/components/co-phone-field/co-phone-field.js +1 -1
- package/dist/collection/components/co-popover/co-popover.css +27 -0
- package/dist/collection/components/co-popover/co-popover.js +30 -4
- package/dist/collection/components/co-progress-bar/co-progress-bar.js +2 -2
- package/dist/collection/components/co-progress-circle/co-progress-circle.css +1 -1
- package/dist/collection/components/co-progress-circle/co-progress-circle.js +2 -2
- package/dist/collection/components/co-provider-logo/co-provider-logo.css +23 -0
- package/dist/collection/components/co-provider-logo/co-provider-logo.js +124 -0
- package/dist/collection/components/co-radio/co-radio.css +1 -1
- package/dist/collection/components/co-radio/co-radio.js +3 -3
- package/dist/collection/components/co-radio-group/co-radio-group.css +1 -1
- package/dist/collection/components/co-rating/co-rating.css +1 -1
- package/dist/collection/components/co-rating/co-rating.js +3 -3
- package/dist/collection/components/co-ref-field/co-ref-field.css +4 -4
- package/dist/collection/components/co-ref-field/co-ref-field.js +18 -2
- package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.css +16 -5
- package/dist/collection/components/co-rich-text-editor/co-rich-text-editor.js +3 -3
- package/dist/collection/components/co-rich-text-toolbar/co-rich-text-toolbar.css +20 -12
- package/dist/collection/components/co-row/co-row.js +2 -2
- package/dist/collection/components/co-select/co-select.css +4 -4
- package/dist/collection/components/co-select/co-select.js +1 -1
- package/dist/collection/components/co-separator/co-separator.js +1 -1
- package/dist/collection/components/co-sidebar/co-sidebar.css +1 -1
- package/dist/collection/components/co-sidebar/co-sidebar.js +2 -2
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.css +33 -7
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +12 -2
- package/dist/collection/components/co-skeleton/co-skeleton.js +1 -1
- package/dist/collection/components/co-slider/co-slider.css +9 -2
- package/dist/collection/components/co-slider/co-slider.js +2 -2
- package/dist/collection/components/co-switch/co-switch.css +5 -4
- package/dist/collection/components/co-switch/co-switch.js +3 -3
- package/dist/collection/components/co-tab/co-tab.css +2 -2
- package/dist/collection/components/co-tab/co-tab.js +2 -2
- package/dist/collection/components/co-tab-group/co-tab-group.css +25 -0
- package/dist/collection/components/co-tab-group/co-tab-group.js +18 -2
- package/dist/collection/components/co-tags-field/co-tags-field.css +3 -3
- package/dist/collection/components/co-tags-field/co-tags-field.js +5 -5
- package/dist/collection/components/co-textarea/co-textarea.css +2 -2
- package/dist/collection/components/co-textarea/co-textarea.js +4 -4
- package/dist/collection/components/co-toast/co-toast.css +63 -1
- package/dist/collection/components/co-toast/co-toast.js +38 -3
- package/dist/collection/components/co-toggle-button/co-toggle-button.css +1 -1
- package/dist/collection/components/co-toggle-button/co-toggle-button.js +2 -2
- package/dist/collection/components/co-toggle-button-group/co-toggle-button-group.js +2 -2
- package/dist/collection/components/co-tooltip/co-tooltip.css +1 -1
- package/dist/collection/components/co-tooltip/co-tooltip.js +14 -3
- package/dist/collection/components/co-transfer-list/co-transfer-list.css +3 -3
- package/dist/collection/components/co-transfer-list/co-transfer-list.js +2 -2
- package/dist/collection/components/co-video/co-video.css +9 -9
- package/dist/collection/components/co-video/co-video.js +2 -2
- package/dist/collection/utils/dateI18n.js +21 -0
- package/dist/collection/utils/eventColor.js +52 -0
- package/dist/collection/utils/positioning.js +10 -0
- package/dist/components/co-accordion.js +1 -1
- package/dist/components/co-action-bar.d.ts +11 -0
- package/dist/components/co-action-bar.js +1 -0
- package/dist/components/co-app-shell.js +1 -1
- package/dist/components/co-audio-player.d.ts +11 -0
- package/dist/components/co-audio-player.js +1 -0
- 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.d.ts +11 -0
- package/dist/components/co-calendar-timegrid.js +1 -0
- 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-picker.js +1 -1
- package/dist/components/co-column-picker.js +1 -1
- package/dist/components/co-combobox.js +1 -1
- package/dist/components/co-command-search.js +1 -1
- package/dist/components/co-data-table-pagination.js +1 -1
- package/dist/components/co-data-table.js +1 -1
- package/dist/components/co-date-field.js +1 -1
- package/dist/components/co-date-picker.js +1 -1
- package/dist/components/co-date-range-picker.js +1 -1
- package/dist/components/co-date-range.js +1 -1
- package/dist/components/co-date-time-picker.d.ts +11 -0
- package/dist/components/co-date-time-picker.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-empty-state.js +1 -1
- package/dist/components/co-event-calendar.js +1 -1
- package/dist/components/co-event-popover.d.ts +11 -0
- package/dist/components/co-event-popover.js +1 -0
- 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-helper-text.js +1 -1
- package/dist/components/co-icon-button.js +1 -1
- package/dist/components/co-icon.js +1 -1
- package/dist/components/co-input.js +1 -1
- package/dist/components/co-json-field.js +1 -1
- package/dist/components/co-lightbox.js +1 -1
- package/dist/components/co-list-footer.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-provider-logo.d.ts +11 -0
- package/dist/components/co-provider-logo.js +1 -0
- 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-row.js +1 -1
- package/dist/components/co-select.js +1 -1
- package/dist/components/co-separator.js +1 -1
- package/dist/components/co-sidebar-nav.js +1 -1
- package/dist/components/co-sidebar.js +1 -1
- package/dist/components/co-skeleton.js +1 -1
- package/dist/components/co-slider.js +1 -1
- package/dist/components/co-switch.js +1 -1
- package/dist/components/co-tab-group.js +1 -1
- package/dist/components/co-tab.js +1 -1
- package/dist/components/co-tags-field.js +1 -1
- package/dist/components/co-textarea.js +1 -1
- package/dist/components/co-toast.js +1 -1
- package/dist/components/co-toggle-button-group.js +1 -1
- package/dist/components/co-toggle-button.js +1 -1
- package/dist/components/co-tooltip.js +1 -1
- package/dist/components/co-transfer-list.js +1 -1
- package/dist/components/co-video.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/p-2zy1K6zg.js +1 -0
- package/dist/components/{p-B3U63yZw.js → p-BCnjq3jN.js} +1 -1
- package/dist/components/p-BMdapAL8.js +1 -0
- package/dist/components/{p-T0UfY6rA.js → p-BUE9mBqk.js} +1 -1
- package/dist/components/p-Bh2__nEw.js +1 -0
- package/dist/components/{p-Cd3inFSL.js → p-BiefJDu3.js} +1 -1
- package/dist/components/{p-Bul6EdDX.js → p-BlvsTlsS.js} +1 -1
- package/dist/components/p-Bsll5Ofy.js +1 -0
- package/dist/components/p-C-5cLeKr.js +1 -0
- package/dist/components/p-CF8OB_TM.js +1 -0
- package/dist/components/p-CJNo2JDc.js +1 -0
- package/dist/components/p-CKKBJaNB.js +1 -0
- package/dist/components/p-CL4dicvk.js +1 -0
- package/dist/components/{p-DfpOEXHC.js → p-CYv2ig6I.js} +1 -1
- package/dist/components/p-Ce0ZJcks.js +1 -0
- package/dist/components/p-Ch837Ggl.js +1 -0
- package/dist/components/p-CkQqqKRp.js +1 -0
- package/dist/components/p-CmxZEGYq.js +1 -0
- package/dist/components/p-CnNpoKwg.js +1 -0
- package/dist/components/p-Cv5yuGLz.js +1 -0
- package/dist/components/{p-oV_1MoAN.js → p-DAaKsGqj.js} +1 -1
- package/dist/components/p-DFT1qsVu.js +1 -0
- package/dist/components/p-DTJsO_Eg.js +1 -0
- package/dist/components/p-Dxa7c4Ap.js +1 -0
- package/dist/components/p-_YYd42XM.js +1 -0
- package/dist/components/p-e5KibEs8.js +1 -0
- package/dist/components/p-re2gJwnQ.js +1 -0
- package/dist/components/p-ushHimOL.js +1 -0
- package/dist/components/p-xvWmYN3W.js +1 -0
- package/dist/esm/co-accordion.entry.js +4 -4
- package/dist/esm/co-action-bar.entry.js +87 -0
- package/dist/esm/co-app-shell.entry.js +4 -4
- package/dist/esm/co-audio-player.entry.js +277 -0
- package/dist/esm/co-autocomplete.entry.js +15 -6
- package/dist/esm/co-avatar-group.entry.js +2 -2
- package/dist/esm/co-avatar_2.entry.js +17 -6
- 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 +13 -6
- package/dist/esm/co-calendar-month_2.entry.js +1598 -0
- package/dist/esm/co-calendar_3.entry.js +13 -13
- package/dist/esm/co-card.entry.js +4 -4
- package/dist/esm/co-carousel-item.entry.js +2 -2
- package/dist/esm/co-carousel.entry.js +4 -4
- package/dist/esm/co-checkbox-group.entry.js +2 -2
- package/dist/esm/co-checkbox.entry.js +5 -5
- package/dist/esm/co-chip_2.entry.js +109 -8
- package/dist/esm/co-color-picker.entry.js +5 -5
- package/dist/esm/co-column-picker.entry.js +4 -4
- package/dist/esm/co-combobox.entry.js +14 -6
- package/dist/esm/co-command-search.entry.js +20 -6
- package/dist/esm/co-data-table-pagination.entry.js +6 -6
- package/dist/esm/co-data-table.entry.js +2 -2
- package/dist/esm/co-date-picker.entry.js +3 -3
- package/dist/esm/co-date-range-picker.entry.js +5 -5
- package/dist/esm/co-date-range.entry.js +2 -2
- package/dist/esm/co-date-time-picker.entry.js +407 -0
- package/dist/esm/co-drive-card.entry.js +3 -3
- package/dist/esm/co-drop-zone.entry.js +2 -2
- package/dist/esm/co-empty-state.entry.js +3 -3
- package/dist/esm/co-event-calendar.entry.js +120 -19
- package/dist/esm/co-event-popover.entry.js +130 -0
- package/dist/esm/co-fab.entry.js +4 -4
- 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-helper-text.entry.js +3 -3
- package/dist/esm/co-icon-button.entry.js +3 -3
- package/dist/esm/co-icon.entry.js +3 -3
- package/dist/esm/co-input.entry.js +3 -3
- package/dist/esm/co-json-field.entry.js +6 -6
- package/dist/esm/co-lightbox.entry.js +7 -7
- package/dist/esm/co-list-footer.entry.js +3 -3
- package/dist/esm/co-list-item.entry.js +6 -6
- 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 +2 -2
- package/dist/esm/co-menu-item.entry.js +3 -3
- package/dist/esm/co-menu.entry.js +2 -2
- package/dist/esm/co-modal.entry.js +4 -4
- 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 +3 -3
- package/dist/esm/co-page-header.entry.js +2 -2
- package/dist/esm/co-phone-field.entry.js +4 -4
- package/dist/esm/co-popover.entry.js +33 -7
- package/dist/esm/co-progress-bar.entry.js +3 -3
- package/dist/esm/co-progress-circle.entry.js +4 -4
- package/dist/esm/co-provider-logo_2.entry.js +69 -0
- package/dist/esm/co-radio-group.entry.js +2 -2
- package/dist/esm/co-radio.entry.js +5 -5
- package/dist/esm/co-rating.entry.js +5 -5
- package/dist/esm/co-ref-field.entry.js +22 -6
- package/dist/esm/co-rich-text-editor.entry.js +5 -5
- package/dist/esm/co-rich-text-toolbar.entry.js +2 -2
- package/dist/esm/co-rich-text-viewer.entry.js +1 -1
- package/dist/esm/co-row.entry.js +3 -3
- package/dist/esm/co-segmented-bar.entry.js +1 -1
- package/dist/esm/co-select.entry.js +4 -4
- package/dist/esm/co-separator.entry.js +2 -2
- package/dist/esm/co-sidebar-nav.entry.js +14 -4
- package/dist/esm/co-sidebar.entry.js +4 -4
- package/dist/esm/co-slider.entry.js +4 -4
- package/dist/esm/co-switch.entry.js +6 -6
- package/dist/esm/co-tab-group.entry.js +20 -4
- package/dist/esm/co-tab.entry.js +4 -4
- package/dist/esm/co-tags-field.entry.js +9 -9
- package/dist/esm/co-textarea.entry.js +7 -7
- package/dist/esm/co-toast.entry.js +35 -5
- package/dist/esm/co-toggle-button-group.entry.js +3 -3
- package/dist/esm/co-toggle-button.entry.js +4 -4
- package/dist/esm/co-transfer-list.entry.js +4 -4
- package/dist/esm/co-video.entry.js +4 -4
- package/dist/esm/cobalt.js +3 -3
- package/dist/esm/{dateI18n-C1DCLOEl.js → dateI18n-CnNpoKwg.js} +21 -0
- package/dist/esm/{dates-tPYikBRJ.js → dates-BU7SQCje.js} +64 -13
- package/dist/esm/{fieldChip-Bb8YIN0J.js → fieldChip-BZDUy9Z-.js} +1 -1
- package/dist/esm/{index-BCEz3S-z.js → index-CrtiywTB.js} +29 -13
- package/dist/esm/loader.js +3 -3
- package/dist/esm/{positioning-C2mZ7x_Y.js → positioning-BMdapAL8.js} +10 -0
- package/dist/esm/{requiredMark-D7kAMXHM.js → requiredMark-DCD-ZA51.js} +1 -1
- package/dist/esm/time-CF8OB_TM.js +37 -0
- package/dist/esm/{tz-B6JZk8fL.js → tz-1EK-COrb.js} +1 -1
- package/dist/types/components/co-action-bar/co-action-bar.d.ts +66 -0
- package/dist/types/components/co-audio-player/co-audio-player.d.ts +62 -0
- package/dist/types/components/co-audio-player/co-audio-player.utils.d.ts +11 -0
- package/dist/types/components/co-autocomplete/co-autocomplete.d.ts +1 -1
- package/dist/types/components/co-button/co-button.d.ts +18 -1
- package/dist/types/components/co-calendar-month/co-calendar-month.d.ts +33 -1
- package/dist/types/components/co-calendar-timegrid/co-calendar-timegrid.d.ts +178 -0
- package/dist/types/components/co-combobox/co-combobox.d.ts +1 -1
- package/dist/types/components/co-command-search/co-command-search.d.ts +7 -0
- package/dist/types/components/co-date-time-picker/co-date-time-picker.d.ts +131 -0
- package/dist/types/components/co-event-calendar/co-event-calendar.d.ts +61 -8
- package/dist/types/components/co-event-popover/co-event-popover.d.ts +45 -0
- package/dist/types/components/co-menu-item/co-menu-item.d.ts +3 -0
- package/dist/types/components/co-option-list/co-option-list.d.ts +28 -1
- package/dist/types/components/co-option-list/optionListLogic.d.ts +3 -0
- package/dist/types/components/co-provider-logo/co-provider-logo.d.ts +17 -0
- package/dist/types/components/co-ref-field/co-ref-field.d.ts +5 -0
- package/dist/types/components/co-tab-group/co-tab-group.d.ts +5 -0
- package/dist/types/components/co-toast/co-toast.d.ts +19 -0
- package/dist/types/components/co-tooltip/co-tooltip.d.ts +6 -0
- package/dist/types/components.d.ts +1251 -82
- package/dist/types/utils/dateI18n.d.ts +7 -0
- package/dist/types/utils/eventColor.d.ts +20 -0
- package/package.json +3 -3
- package/dist/cjs/co-calendar-month.cjs.entry.js +0 -381
- package/dist/cjs/co-skeleton.cjs.entry.js +0 -26
- package/dist/cobalt/p-037cc571.entry.js +0 -1
- package/dist/cobalt/p-080f62e4.entry.js +0 -1
- package/dist/cobalt/p-0b7359bb.entry.js +0 -1
- package/dist/cobalt/p-0b92167a.entry.js +0 -1
- package/dist/cobalt/p-0bc5a962.entry.js +0 -1
- package/dist/cobalt/p-264508ae.entry.js +0 -1
- package/dist/cobalt/p-27dce0ac.entry.js +0 -1
- package/dist/cobalt/p-30c80e3a.entry.js +0 -1
- package/dist/cobalt/p-331a0108.entry.js +0 -1
- package/dist/cobalt/p-36ad3a96.entry.js +0 -1
- package/dist/cobalt/p-3ecbbe93.entry.js +0 -1
- package/dist/cobalt/p-3f1030fb.entry.js +0 -1
- package/dist/cobalt/p-3f7215e7.entry.js +0 -1
- package/dist/cobalt/p-431cec34.entry.js +0 -1
- package/dist/cobalt/p-4f615cbd.entry.js +0 -1
- package/dist/cobalt/p-5319ac3f.entry.js +0 -1
- package/dist/cobalt/p-532baca4.entry.js +0 -1
- package/dist/cobalt/p-53a276a8.entry.js +0 -1
- package/dist/cobalt/p-543cc00d.entry.js +0 -1
- package/dist/cobalt/p-54857f78.entry.js +0 -1
- package/dist/cobalt/p-585c132d.entry.js +0 -1
- package/dist/cobalt/p-59b114ab.entry.js +0 -1
- package/dist/cobalt/p-5c250889.entry.js +0 -1
- package/dist/cobalt/p-5d0510a5.entry.js +0 -1
- package/dist/cobalt/p-5e246304.entry.js +0 -1
- package/dist/cobalt/p-64115d66.entry.js +0 -1
- package/dist/cobalt/p-75515a8e.entry.js +0 -1
- package/dist/cobalt/p-8155d781.entry.js +0 -1
- package/dist/cobalt/p-8fe5b07e.entry.js +0 -1
- package/dist/cobalt/p-9824c478.entry.js +0 -1
- package/dist/cobalt/p-9bf42bf4.entry.js +0 -1
- package/dist/cobalt/p-9fab9c1f.entry.js +0 -1
- package/dist/cobalt/p-BCEz3S-z.js +0 -2
- package/dist/cobalt/p-C-d15gih.js +0 -1
- package/dist/cobalt/p-C1DCLOEl.js +0 -1
- package/dist/cobalt/p-C2mZ7x_Y.js +0 -1
- package/dist/cobalt/p-D5gr2ent.js +0 -1
- package/dist/cobalt/p-b06af5c6.entry.js +0 -1
- package/dist/cobalt/p-b87a2e7a.entry.js +0 -1
- package/dist/cobalt/p-bb00ae4a.entry.js +0 -1
- package/dist/cobalt/p-c237add6.entry.js +0 -1
- package/dist/cobalt/p-c589ae25.entry.js +0 -1
- package/dist/cobalt/p-c635417b.entry.js +0 -1
- package/dist/cobalt/p-c7d9ff39.entry.js +0 -1
- package/dist/cobalt/p-c9528a5f.entry.js +0 -1
- package/dist/cobalt/p-cbac0a11.entry.js +0 -1
- package/dist/cobalt/p-cd28aa6d.entry.js +0 -1
- package/dist/cobalt/p-d3a1a604.entry.js +0 -1
- package/dist/cobalt/p-d71b800a.entry.js +0 -1
- package/dist/cobalt/p-df752f6e.entry.js +0 -1
- package/dist/cobalt/p-ec9dd3a3.entry.js +0 -1
- package/dist/cobalt/p-eeed80ac.entry.js +0 -1
- package/dist/cobalt/p-f0a98453.entry.js +0 -1
- package/dist/cobalt/p-ff581807.entry.js +0 -1
- package/dist/cobalt/p-fffeb610.entry.js +0 -1
- package/dist/cobalt/p-tPYikBRJ.js +0 -1
- package/dist/components/p-9aNhJfTT.js +0 -1
- package/dist/components/p-B0b9Etx4.js +0 -1
- package/dist/components/p-B7Yv00g9.js +0 -1
- package/dist/components/p-BTa6ss5i.js +0 -1
- package/dist/components/p-BtpRnpaF.js +0 -1
- package/dist/components/p-Bzg5FPSA.js +0 -1
- package/dist/components/p-C1DCLOEl.js +0 -1
- package/dist/components/p-C2mZ7x_Y.js +0 -1
- package/dist/components/p-CQ-8domm.js +0 -1
- package/dist/components/p-CVeEoHr7.js +0 -1
- package/dist/components/p-CYi6V_Ss.js +0 -1
- package/dist/components/p-CmCsm4L-.js +0 -1
- package/dist/components/p-CuQjKAf5.js +0 -1
- package/dist/components/p-CyYF8IJE.js +0 -1
- package/dist/components/p-DKvTqEaf.js +0 -1
- package/dist/components/p-DwwxS5bY.js +0 -1
- package/dist/esm/co-calendar-month.entry.js +0 -379
- package/dist/esm/co-skeleton.entry.js +0 -24
|
@@ -0,0 +1,1601 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('./index-DK-2Lzmf.js');
|
|
4
|
+
var dates = require('./dates-TQLHlFd2.js');
|
|
5
|
+
var tz = require('./tz-Dgom0W69.js');
|
|
6
|
+
var time = require('./time-LBHtzLHe.js');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* interval.ts — overlap detection, greedy column packing, and day splitting.
|
|
10
|
+
*
|
|
11
|
+
* This is the math a calendar library would give you "for free" and the part the
|
|
12
|
+
* pre-mortem flagged as the main build-own risk — so it lives in pure TS with
|
|
13
|
+
* pathological-case tests rather than inside a component.
|
|
14
|
+
*
|
|
15
|
+
* All ranges are END-EXCLUSIVE: [start, end). Touching ranges (a.end === b.start)
|
|
16
|
+
* do NOT overlap.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Greedy column packing for overlapping timed events (time-grid layout).
|
|
20
|
+
*
|
|
21
|
+
* Events are grouped into collision clusters (transitively-overlapping sets) and
|
|
22
|
+
* within each cluster placed into the first column whose previous occupant has
|
|
23
|
+
* already ended. Every event in a cluster shares the cluster's column count, so
|
|
24
|
+
* widths are `1 / columnCount`. Deterministic and order-stable.
|
|
25
|
+
*/
|
|
26
|
+
function packColumns(items, getStart, getEnd) {
|
|
27
|
+
const sorted = [...items].sort((a, b) => getStart(a) - getStart(b) || getEnd(a) - getEnd(b));
|
|
28
|
+
const result = [];
|
|
29
|
+
let clusterIndices = []; // indices into `result` for the open cluster
|
|
30
|
+
let columnEnds = []; // last end time per open column
|
|
31
|
+
let clusterMaxEnd = -Infinity;
|
|
32
|
+
const flush = () => {
|
|
33
|
+
const count = columnEnds.length;
|
|
34
|
+
for (const i of clusterIndices)
|
|
35
|
+
result[i].columnCount = count;
|
|
36
|
+
clusterIndices = [];
|
|
37
|
+
columnEnds = [];
|
|
38
|
+
clusterMaxEnd = -Infinity;
|
|
39
|
+
};
|
|
40
|
+
for (const item of sorted) {
|
|
41
|
+
const start = getStart(item);
|
|
42
|
+
const end = getEnd(item);
|
|
43
|
+
// No active column still running past `start` → the cluster is closed.
|
|
44
|
+
if (clusterIndices.length && start >= clusterMaxEnd)
|
|
45
|
+
flush();
|
|
46
|
+
let col = -1;
|
|
47
|
+
for (let i = 0; i < columnEnds.length; i++) {
|
|
48
|
+
if (columnEnds[i] <= start) {
|
|
49
|
+
col = i;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (col === -1) {
|
|
54
|
+
col = columnEnds.length;
|
|
55
|
+
columnEnds.push(end);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
columnEnds[col] = end;
|
|
59
|
+
}
|
|
60
|
+
result.push({ event: item, columnIndex: col, columnCount: 1 });
|
|
61
|
+
clusterIndices.push(result.length - 1);
|
|
62
|
+
clusterMaxEnd = Math.max(clusterMaxEnd, end);
|
|
63
|
+
}
|
|
64
|
+
flush();
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Split a timed instant range into per-wall-clock-day segments in `timeZone`,
|
|
69
|
+
* each clamped to that day's [0,1440] minute window. An event crossing midnight
|
|
70
|
+
* yields one segment per day. End-exclusive: an event ending exactly at midnight
|
|
71
|
+
* does NOT produce an empty trailing segment.
|
|
72
|
+
*/
|
|
73
|
+
function splitByDay(start, end, timeZone) {
|
|
74
|
+
const startMs = tz.toEpoch(start);
|
|
75
|
+
const endMs = tz.toEpoch(end);
|
|
76
|
+
if (endMs <= startMs)
|
|
77
|
+
return [];
|
|
78
|
+
const segments = [];
|
|
79
|
+
let dateKey = tz.zonedDateKey(startMs, timeZone);
|
|
80
|
+
let dayStartMs = midnightMs(dateKey, timeZone);
|
|
81
|
+
// Safety cap: a single event spanning >366 days is almost certainly a bug.
|
|
82
|
+
for (let guard = 0; guard < 366 && dayStartMs < endMs; guard++) {
|
|
83
|
+
const nextKey = dates.addDays(dateKey, 1);
|
|
84
|
+
const nextDayStartMs = midnightMs(nextKey, timeZone);
|
|
85
|
+
const segStartMs = Math.max(startMs, dayStartMs);
|
|
86
|
+
const segEndMs = Math.min(endMs, nextDayStartMs);
|
|
87
|
+
segments.push({
|
|
88
|
+
dateKey,
|
|
89
|
+
startMinutes: segStartMs === dayStartMs ? 0 : tz.zonedMinutesOfDay(segStartMs, timeZone),
|
|
90
|
+
endMinutes: segEndMs === nextDayStartMs ? 1440 : tz.zonedMinutesOfDay(segEndMs, timeZone),
|
|
91
|
+
isStart: segStartMs === startMs,
|
|
92
|
+
isEnd: segEndMs === endMs,
|
|
93
|
+
});
|
|
94
|
+
dateKey = nextKey;
|
|
95
|
+
dayStartMs = nextDayStartMs;
|
|
96
|
+
}
|
|
97
|
+
return segments;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Local-midnight epoch for a "YYYY-MM-DD" wall-clock day in `timeZone`.
|
|
101
|
+
*
|
|
102
|
+
* Memoized module-level: a midnight for a given (timeZone, dateKey) pair is an
|
|
103
|
+
* immutable fact, but `splitByDay` recomputes it once per event AND once per day
|
|
104
|
+
* of that event's span — a week view re-derives the same ~8 unique midnights
|
|
105
|
+
* dozens of times per render. Each miss costs a `wallClockToEpoch` call (2-3
|
|
106
|
+
* `formatToParts` round-trips), so caching turns O(events × days) Intl work into
|
|
107
|
+
* O(unique days).
|
|
108
|
+
*/
|
|
109
|
+
const MIDNIGHT_CACHE = new Map();
|
|
110
|
+
function midnightMs(dateKey, timeZone) {
|
|
111
|
+
const key = `${timeZone}|${dateKey}`;
|
|
112
|
+
let ms = MIDNIGHT_CACHE.get(key);
|
|
113
|
+
if (ms === undefined) {
|
|
114
|
+
const [year, month, day] = dateKey.split('-').map(Number);
|
|
115
|
+
ms = tz.wallClockToEpoch({ year, month, day, hour: 0, minute: 0, second: 0 }, timeZone);
|
|
116
|
+
// Hygiene cap, not an LRU: bounds unbounded growth from pathological callers
|
|
117
|
+
// (e.g. programmatic iteration over years of dates) without adding eviction
|
|
118
|
+
// bookkeeping cost to the hot path. Legitimate usage (calendar views) touches
|
|
119
|
+
// a few dozen unique days per session, far under this ceiling.
|
|
120
|
+
if (MIDNIGHT_CACHE.size > 1000)
|
|
121
|
+
MIDNIGHT_CACHE.clear();
|
|
122
|
+
MIDNIGHT_CACHE.set(key, ms);
|
|
123
|
+
}
|
|
124
|
+
return ms;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* layout-month.ts — lane assignment for the month grid.
|
|
129
|
+
*
|
|
130
|
+
* Multi-day (all-day) events render as continuous bars spanning day cells; they
|
|
131
|
+
* must keep a stable vertical lane across the days they cover, and overlapping
|
|
132
|
+
* events must not share a lane. Single-day chips (timed events, single all-day)
|
|
133
|
+
* are degenerate one-column bars. Days exceeding `maxLanes` report an overflow
|
|
134
|
+
* count for the "+N more" affordance.
|
|
135
|
+
*
|
|
136
|
+
* Days are addressed by a week matrix of "YYYY-MM-DD" keys (e.g. from
|
|
137
|
+
* `getMonthMatrix` in `@prolibu-suite/cobalt-date-utils`). Lexicographic string
|
|
138
|
+
* comparison is correct for ISO dates, so no Date objects are needed here.
|
|
139
|
+
*/
|
|
140
|
+
/** Inclusive day span of an event in calendar days. */
|
|
141
|
+
function eventDaySpan(event, timeZone) {
|
|
142
|
+
if (event.allDay) {
|
|
143
|
+
// end is exclusive → last visible day is end - 1.
|
|
144
|
+
const firstDay = event.start.slice(0, 10);
|
|
145
|
+
const lastDay = dates.addDays(event.end.slice(0, 10), -1);
|
|
146
|
+
if (dates.compareYMD(lastDay, firstDay) < 0)
|
|
147
|
+
return { event, firstDay, lastDay: firstDay };
|
|
148
|
+
return { event, firstDay, lastDay };
|
|
149
|
+
}
|
|
150
|
+
// Timed events occupy their (wall-clock) start day in the month grid.
|
|
151
|
+
const day = timeZone ? tz.zonedDateKey(event.startMs, timeZone) : event.start.slice(0, 10);
|
|
152
|
+
return { event, firstDay: day, lastDay: day };
|
|
153
|
+
}
|
|
154
|
+
function layoutMonth(events, weeks, options = {}) {
|
|
155
|
+
const maxLanes = options.maxLanes ?? Infinity;
|
|
156
|
+
const segments = [];
|
|
157
|
+
const overflow = {};
|
|
158
|
+
let laneCount = 0;
|
|
159
|
+
const spans = events
|
|
160
|
+
.map((e) => eventDaySpan(e, options.timeZone))
|
|
161
|
+
.filter((s) => s !== null);
|
|
162
|
+
weeks.forEach((days, weekIndex) => {
|
|
163
|
+
if (!days.length)
|
|
164
|
+
return;
|
|
165
|
+
const weekStart = days[0];
|
|
166
|
+
const weekEnd = days[days.length - 1];
|
|
167
|
+
const colOf = new Map(days.map((d, i) => [d, i]));
|
|
168
|
+
// Candidate segments intersecting this week.
|
|
169
|
+
const candidates = spans
|
|
170
|
+
.filter((s) => dates.compareYMD(s.firstDay, weekEnd) <= 0 && dates.compareYMD(weekStart, s.lastDay) <= 0)
|
|
171
|
+
.map((s) => {
|
|
172
|
+
const segFirst = dates.compareYMD(s.firstDay, weekStart) >= 0 ? s.firstDay : weekStart;
|
|
173
|
+
const segLast = dates.compareYMD(s.lastDay, weekEnd) <= 0 ? s.lastDay : weekEnd;
|
|
174
|
+
return {
|
|
175
|
+
span: s,
|
|
176
|
+
startCol: colOf.get(segFirst) ?? 0,
|
|
177
|
+
endCol: colOf.get(segLast) ?? days.length - 1,
|
|
178
|
+
};
|
|
179
|
+
})
|
|
180
|
+
// Multi-day/earlier events claim lanes first → stable, readable bars.
|
|
181
|
+
.sort((a, b) => a.startCol - b.startCol ||
|
|
182
|
+
b.endCol - b.startCol - (a.endCol - a.startCol) ||
|
|
183
|
+
a.span.event.startMs - b.span.event.startMs ||
|
|
184
|
+
(a.span.event.id < b.span.event.id ? -1 : 1));
|
|
185
|
+
const placed = [];
|
|
186
|
+
for (const cand of candidates) {
|
|
187
|
+
let lane = 0;
|
|
188
|
+
while (placed.some((p) => p.lane === lane && !(cand.endCol < p.startCol || cand.startCol > p.endCol))) {
|
|
189
|
+
lane++;
|
|
190
|
+
}
|
|
191
|
+
placed.push({ lane, startCol: cand.startCol, endCol: cand.endCol });
|
|
192
|
+
laneCount = Math.max(laneCount, lane + 1);
|
|
193
|
+
segments.push({
|
|
194
|
+
eventId: cand.span.event.id,
|
|
195
|
+
weekIndex,
|
|
196
|
+
lane,
|
|
197
|
+
startCol: cand.startCol,
|
|
198
|
+
endCol: cand.endCol,
|
|
199
|
+
isStart: dates.compareYMD(cand.span.firstDay, weekStart) >= 0,
|
|
200
|
+
isEnd: dates.compareYMD(cand.span.lastDay, weekEnd) <= 0,
|
|
201
|
+
continuesBefore: dates.compareYMD(cand.span.firstDay, weekStart) < 0,
|
|
202
|
+
continuesAfter: dates.compareYMD(cand.span.lastDay, weekEnd) > 0,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
if (Number.isFinite(maxLanes)) {
|
|
207
|
+
for (const seg of segments) {
|
|
208
|
+
if (seg.lane >= maxLanes) {
|
|
209
|
+
for (let c = seg.startCol; c <= seg.endCol; c++) {
|
|
210
|
+
const dk = weeks[seg.weekIndex][c];
|
|
211
|
+
overflow[dk] = (overflow[dk] ?? 0) + 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { segments, overflow, laneCount };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* layout-allday.ts — lane assignment for the time-grid all-day band.
|
|
221
|
+
*
|
|
222
|
+
* The all-day band is a single row spanning the visible `dateKeys` (7 for week,
|
|
223
|
+
* 1 for day). Multi-day all-day events render as continuous bars across the days
|
|
224
|
+
* they cover, keeping a stable lane and never sharing a lane with an overlapping
|
|
225
|
+
* bar. Reuses `layoutMonth` with a single "week" = `dateKeys`; timed events are
|
|
226
|
+
* excluded (they live in the time grid, not the band).
|
|
227
|
+
*/
|
|
228
|
+
function layoutAllDay(events, dateKeys, options = {}) {
|
|
229
|
+
const allDayEvents = events.filter((e) => e.allDay);
|
|
230
|
+
const m = layoutMonth(allDayEvents, [dateKeys], options);
|
|
231
|
+
return {
|
|
232
|
+
segments: m.segments.map((s) => ({
|
|
233
|
+
eventId: s.eventId,
|
|
234
|
+
lane: s.lane,
|
|
235
|
+
startCol: s.startCol,
|
|
236
|
+
endCol: s.endCol,
|
|
237
|
+
isStart: s.isStart,
|
|
238
|
+
isEnd: s.isEnd,
|
|
239
|
+
continuesBefore: s.continuesBefore,
|
|
240
|
+
continuesAfter: s.continuesAfter,
|
|
241
|
+
})),
|
|
242
|
+
overflow: m.overflow,
|
|
243
|
+
laneCount: m.laneCount,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* layout-timegrid.ts — positions timed events on the Week/Day time-grid.
|
|
249
|
+
*
|
|
250
|
+
* Pure composition of the engine's existing primitives: splitByDay (tz-aware,
|
|
251
|
+
* DST-safe, per-day segments) + packColumns (overlap → columns) + minutesToFraction
|
|
252
|
+
* (px positioning). All-day events are routed to a separate per-day bucket.
|
|
253
|
+
* No DOM, no framework, no new timezone math.
|
|
254
|
+
*/
|
|
255
|
+
function layoutTimeGrid(events, dateKeys, options) {
|
|
256
|
+
const minM = options.minMinutes ?? 0;
|
|
257
|
+
const maxM = options.maxMinutes ?? time.MINUTES_PER_DAY;
|
|
258
|
+
const visible = new Set(dateKeys);
|
|
259
|
+
const timed = {};
|
|
260
|
+
const allDay = {};
|
|
261
|
+
const segsByDay = {};
|
|
262
|
+
for (const k of dateKeys) {
|
|
263
|
+
timed[k] = [];
|
|
264
|
+
allDay[k] = [];
|
|
265
|
+
segsByDay[k] = [];
|
|
266
|
+
}
|
|
267
|
+
for (const ev of events) {
|
|
268
|
+
if (ev.allDay) {
|
|
269
|
+
const startDay = ev.start.slice(0, 10);
|
|
270
|
+
const endDay = ev.end.slice(0, 10); // end-exclusive
|
|
271
|
+
for (const k of dateKeys) {
|
|
272
|
+
if (k >= startDay && k < endDay)
|
|
273
|
+
allDay[k].push(ev);
|
|
274
|
+
}
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
for (const s of splitByDay(ev.start, ev.end, options.timeZone)) {
|
|
278
|
+
if (!visible.has(s.dateKey))
|
|
279
|
+
continue;
|
|
280
|
+
const startMinutes = time.clampMinutes(s.startMinutes, minM, maxM);
|
|
281
|
+
const endMinutes = time.clampMinutes(s.endMinutes, minM, maxM);
|
|
282
|
+
if (endMinutes <= startMinutes)
|
|
283
|
+
continue; // fully outside the window
|
|
284
|
+
segsByDay[s.dateKey].push({
|
|
285
|
+
event: ev,
|
|
286
|
+
startMinutes,
|
|
287
|
+
endMinutes,
|
|
288
|
+
isStart: s.isStart && startMinutes === s.startMinutes,
|
|
289
|
+
isEnd: s.isEnd && endMinutes === s.endMinutes,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
for (const k of dateKeys) {
|
|
294
|
+
const packed = packColumns(segsByDay[k], (s) => s.startMinutes, (s) => s.endMinutes);
|
|
295
|
+
timed[k] = packed.map((p) => {
|
|
296
|
+
const top = time.minutesToFraction(p.event.startMinutes, minM, maxM);
|
|
297
|
+
const bottom = time.minutesToFraction(p.event.endMinutes, minM, maxM);
|
|
298
|
+
return {
|
|
299
|
+
event: p.event.event,
|
|
300
|
+
dateKey: k,
|
|
301
|
+
startMinutes: p.event.startMinutes,
|
|
302
|
+
endMinutes: p.event.endMinutes,
|
|
303
|
+
topFraction: top,
|
|
304
|
+
heightFraction: bottom - top,
|
|
305
|
+
columnIndex: p.columnIndex,
|
|
306
|
+
columnCount: p.columnCount,
|
|
307
|
+
isStart: p.event.isStart,
|
|
308
|
+
isEnd: p.event.isEnd,
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return { timed, allDay };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Color de texto legible sobre el fondo de un evento de calendario.
|
|
317
|
+
*
|
|
318
|
+
* El host pinta los estados con TOKENS (`var(--co-color-primary-azul)`), no con
|
|
319
|
+
* hex — es la regla del design system. El cálculo de contraste necesita un color
|
|
320
|
+
* REAL, así que el token se resuelve contra el host antes de medir luminancia.
|
|
321
|
+
*/
|
|
322
|
+
/** Cache token→hex. La clave incluye el tema: light y dark pueden resolver
|
|
323
|
+
* distinto el MISMO token, y servir el valor viejo daría texto ilegible. */
|
|
324
|
+
const RESOLVED = new Map();
|
|
325
|
+
function currentTheme() {
|
|
326
|
+
var _a, _b, _c;
|
|
327
|
+
return (_c = (_b = (_a = document.documentElement) === null || _a === void 0 ? void 0 : _a.dataset) === null || _b === void 0 ? void 0 : _b.theme) !== null && _c !== void 0 ? _c : '';
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Resuelve `var(--x)` / `var(--x, fallback)` contra `host`. Un valor que no es
|
|
331
|
+
* `var(...)` (hex, rgb, nombre CSS) vuelve tal cual.
|
|
332
|
+
*/
|
|
333
|
+
function resolveCssColor(host, value) {
|
|
334
|
+
const raw = (value !== null && value !== void 0 ? value : '').trim();
|
|
335
|
+
if (!raw.startsWith('var(') || !host)
|
|
336
|
+
return raw;
|
|
337
|
+
const key = `${currentTheme()}|${raw}`;
|
|
338
|
+
const hit = RESOLVED.get(key);
|
|
339
|
+
if (hit !== undefined)
|
|
340
|
+
return hit;
|
|
341
|
+
const inner = raw.slice(4, raw.lastIndexOf(')'));
|
|
342
|
+
const [name, ...fallback] = inner.split(',');
|
|
343
|
+
const resolved = getComputedStyle(host).getPropertyValue(name.trim()).trim() || fallback.join(',').trim();
|
|
344
|
+
RESOLVED.set(key, resolved);
|
|
345
|
+
return resolved;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Token de color de texto legible sobre `bg` (luminancia YIQ), o `''` cuando el
|
|
349
|
+
* color no se puede medir — en ese caso el llamador NO debe escribir la
|
|
350
|
+
* variable: pisarla con un `var(--co-cal-event-text)` la vuelve
|
|
351
|
+
* AUTORREFERENTE, la declaración se invalida y el texto termina heredando el
|
|
352
|
+
* color del calendario (negro sobre un chip de color).
|
|
353
|
+
*/
|
|
354
|
+
function readableTextToken(host, bg) {
|
|
355
|
+
const hex = resolveCssColor(host, bg).replace('#', '').trim();
|
|
356
|
+
if (hex.length !== 3 && hex.length !== 6)
|
|
357
|
+
return '';
|
|
358
|
+
const full = hex.length === 3 ? hex.split('').map((c) => c + c).join('') : hex;
|
|
359
|
+
const r = parseInt(full.slice(0, 2), 16);
|
|
360
|
+
const g = parseInt(full.slice(2, 4), 16);
|
|
361
|
+
const b = parseInt(full.slice(4, 6), 16);
|
|
362
|
+
if ([r, g, b].some((n) => Number.isNaN(n)))
|
|
363
|
+
return '';
|
|
364
|
+
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
|
365
|
+
return yiq >= 150 ? 'var(--co-cal-event-text-dark)' : 'var(--co-cal-event-text-light)';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const coCalendarMonthCss = () => `:host{display:block;font-family:var(--co-font-family-primary);--co-cal-daynum-h:24px;--co-cal-lane-h:22px;--co-cal-min-week-h:92px;--co-cal-gap:2px;--co-cal-surface:var(--co-semantic-surface-page);--co-cal-border:var(--co-semantic-border-default);--co-cal-text:var(--co-semantic-text-default);--co-cal-text-muted:var(--co-semantic-text-muted);--co-cal-hover:var(--co-semantic-surface-hover);--co-cal-today-ring:var(--co-color-primary-azul);--co-cal-event-radius:6px;--co-cal-elevation-hover:var(--co-shadow-lg);--co-cal-event-bg:var(--co-color-primary-azul);--co-cal-event-border:transparent;--co-cal-event-text:var(--co-color-base-blanco, #fff);--co-cal-event-text-light:var(--co-color-base-blanco, #fff);--co-cal-event-text-dark:var(--co-semantic-text-default)}.cal-month{display:flex;flex-direction:column;height:100%;border-radius:var(--co-border-radius-xs);overflow:hidden;background:var(--co-cal-surface)}.cal-month__dow{display:grid;grid-template-columns:repeat(7, 1fr);border-bottom:1px solid var(--co-cal-border)}.cal-month__dow-cell{padding:var(--co-spacing-xs) var(--co-spacing-sm);font-size:var(--co-font-size-10, 10px);font-weight:600;text-transform:uppercase;letter-spacing:0.04em;color:var(--co-cal-text-muted);text-align:right}.cal-month__week{position:relative;min-height:var(--co-cal-min-week-h);flex:1 0 auto}.cal-month__week:not(:last-child){border-bottom:1px solid var(--co-cal-border)}.cal-month__days{display:grid;grid-template-columns:repeat(7, 1fr);height:100%;position:absolute;inset:0}.cal-month__day{border-right:1px solid var(--co-cal-border);padding:var(--co-spacing-xxs);cursor:pointer;transition:background var(--co-motion-duration-fast) var(--co-motion-ease-out)}.cal-month__day:last-child{border-right:0}.cal-month__day:hover{background:var(--co-cal-hover)}.cal-month__day:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:-2px}.cal-month__day--outside .cal-month__daynum{color:var(--co-cal-text-muted)}.cal-month__day--disabled{cursor:not-allowed;background:color-mix(in srgb, var(--co-cal-border) 20%, transparent)}.cal-month__day--drag-over{background:color-mix(in srgb, var(--co-cal-today-ring) 14%, transparent);box-shadow:inset 0 0 0 2px var(--co-cal-today-ring)}.cal-month__day--selected{background:color-mix(in srgb, var(--co-cal-today-ring) 10%, transparent)}.cal-month__daynum{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;margin-left:auto;float:right;font-size:var(--co-font-size-12, 12px);color:var(--co-cal-text);border-radius:var(--co-border-radius-full)}.cal-month__day--today .cal-month__daynum{background:var(--co-cal-today-ring);color:var(--co-color-base-blanco, #fff);font-weight:600}.cal-month__events{position:absolute;inset:0;pointer-events:none;}.cal-chip,.cal-month__more{position:absolute;height:calc(var(--co-cal-lane-h) - 3px);box-sizing:border-box;pointer-events:auto;display:flex;align-items:center;gap:var(--co-spacing-xxs);margin:0 3px;padding:0 var(--co-spacing-xs);border:1px solid var(--co-cal-event-border);border-radius:var(--co-cal-event-radius);font-family:inherit;font-size:var(--co-font-size-12, 12px);line-height:1;text-align:left;cursor:pointer;overflow:hidden;white-space:nowrap}.cal-chip{background:var(--co-cal-event-bg);color:var(--co-cal-event-text);user-select:none;-webkit-user-select:none;touch-action:none}.cal-month__chip-skeleton{position:absolute;height:calc(var(--co-cal-lane-h) - 3px);box-sizing:border-box;margin:0 3px;border-radius:var(--co-cal-event-radius);overflow:hidden;pointer-events:none}.cal-month__chip-skeleton co-skeleton{display:block;block-size:100%}.cal-chip--all-day:hover{filter:brightness(0.95);box-shadow:var(--co-cal-elevation-hover)}.cal-chip:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:1px}.cal-chip--all-day{font-weight:500}.cal-chip__dot{flex:0 0 auto;width:7px;height:7px;border-radius:var(--co-border-radius-full);background:var(--co-cal-event-bg)}.cal-chip:not(.cal-chip--all-day){background:transparent;color:var(--co-cal-text);border-color:transparent}.cal-chip:not(.cal-chip--all-day):hover{background:var(--co-cal-hover);filter:none}.cal-chip__time{flex:0 0 auto;color:var(--co-cal-text-muted);font-variant-numeric:tabular-nums}.cal-chip__title{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.cal-chip__ext{flex:0 0 auto;margin-inline-start:auto;opacity:0.8}.cal-chip--past{opacity:0.55}.cal-chip--locked{cursor:default}.cal-chip--external{border-style:dashed}.cal-chip--dragging{opacity:0.4}.cal-month--interacting .cal-chip{pointer-events:none}.cal-chip--all-day.cal-chip--tentative{background-image:repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.5) 0 4px, transparent 4px 8px)}.cal-chip--cancelled{opacity:0.6}.cal-chip--cancelled .cal-chip__title{text-decoration:line-through}.cal-chip--continues-before{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:0}.cal-chip--continues-after{border-top-right-radius:0;border-bottom-right-radius:0;margin-right:0}.cal-month__more{background:transparent;color:var(--co-cal-text-muted);border-color:transparent;font-weight:600;font-size:var(--co-font-size-11, 11px)}.cal-month__more:hover{background:var(--co-cal-hover);color:var(--co-cal-text)}`;
|
|
369
|
+
|
|
370
|
+
const CoCalendarMonth = class {
|
|
371
|
+
constructor(hostRef) {
|
|
372
|
+
index.registerInstance(this, hostRef);
|
|
373
|
+
this.coDaySelect = index.createEvent(this, "coDaySelect", 1);
|
|
374
|
+
this.coEventClick = index.createEvent(this, "coEventClick", 1);
|
|
375
|
+
this.coEventDrop = index.createEvent(this, "coEventDrop", 1);
|
|
376
|
+
this.coMoreClick = index.createEvent(this, "coMoreClick", 1);
|
|
377
|
+
this.coEventDblClick = index.createEvent(this, "coEventDblClick", 1);
|
|
378
|
+
/** Normalized events to render (from the engine's `normalizeEvents`). */
|
|
379
|
+
this.events = [];
|
|
380
|
+
/** BCP-47 locale for weekday labels. */
|
|
381
|
+
this.locale = 'es-CO';
|
|
382
|
+
/** First day of week, 0=Sun..6=Sat. */
|
|
383
|
+
this.firstDayOfWeek = 1;
|
|
384
|
+
/** Master interaction gate. */
|
|
385
|
+
this.editable = true;
|
|
386
|
+
/** Allow empty-day click to request a create. */
|
|
387
|
+
this.selectable = true;
|
|
388
|
+
/** Max event lanes per day before "+N more". */
|
|
389
|
+
this.maxLanes = 3;
|
|
390
|
+
/** Cargando: en vez de los chips pinta bloques skeleton en sus carriles. La
|
|
391
|
+
* grilla del mes (cabecera de días, celdas, números, bordes) no se toca. */
|
|
392
|
+
this.loading = false;
|
|
393
|
+
/** Day currently hovered while dragging an event. */
|
|
394
|
+
this.dragOverDate = null;
|
|
395
|
+
/** Id of the event being dragged. */
|
|
396
|
+
this.dragId = null;
|
|
397
|
+
/** Epoch ms of "now", refreshed on a 60s timer so `past` styling stays live. */
|
|
398
|
+
this.nowMs = 0;
|
|
399
|
+
this.onPointerUp = () => this.finishDrag();
|
|
400
|
+
// ── Selection / activation ──────────────────────────────────────────────
|
|
401
|
+
/** Día seleccionado que PERSISTE resaltado mientras el host tiene abierto su
|
|
402
|
+
* form/bubble de creación (referencia visual, paridad Google). */
|
|
403
|
+
this.selectedDate = null;
|
|
404
|
+
this.handleKeyDown = (e) => {
|
|
405
|
+
const cur = this.focused;
|
|
406
|
+
let handled = true;
|
|
407
|
+
switch (e.key) {
|
|
408
|
+
case 'ArrowLeft':
|
|
409
|
+
this.moveFocus(dates.addDays(cur, -1));
|
|
410
|
+
break;
|
|
411
|
+
case 'ArrowRight':
|
|
412
|
+
this.moveFocus(dates.addDays(cur, 1));
|
|
413
|
+
break;
|
|
414
|
+
case 'ArrowUp':
|
|
415
|
+
this.moveFocus(dates.addDays(cur, -7));
|
|
416
|
+
break;
|
|
417
|
+
case 'ArrowDown':
|
|
418
|
+
this.moveFocus(dates.addDays(cur, 7));
|
|
419
|
+
break;
|
|
420
|
+
case 'Enter':
|
|
421
|
+
case ' ':
|
|
422
|
+
this.selectDay(cur);
|
|
423
|
+
break;
|
|
424
|
+
default:
|
|
425
|
+
handled = false;
|
|
426
|
+
}
|
|
427
|
+
if (handled) {
|
|
428
|
+
e.preventDefault();
|
|
429
|
+
e.stopPropagation();
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
componentWillLoad() {
|
|
434
|
+
this.focused = this.defaultFocus();
|
|
435
|
+
this.nowMs = Date.now();
|
|
436
|
+
}
|
|
437
|
+
componentDidLoad() {
|
|
438
|
+
// setInterval acá (no en connectedCallback): componentDidLoad corre una sola
|
|
439
|
+
// vez por instancia → sin timer duplicado en reconexión (espeja co-calendar-timegrid).
|
|
440
|
+
this.nowTimer = window.setInterval(() => (this.nowMs = Date.now()), 60000);
|
|
441
|
+
}
|
|
442
|
+
connectedCallback() {
|
|
443
|
+
window.addEventListener('pointerup', this.onPointerUp);
|
|
444
|
+
}
|
|
445
|
+
disconnectedCallback() {
|
|
446
|
+
window.removeEventListener('pointerup', this.onPointerUp);
|
|
447
|
+
if (this.nowTimer)
|
|
448
|
+
window.clearInterval(this.nowTimer);
|
|
449
|
+
}
|
|
450
|
+
defaultFocus() {
|
|
451
|
+
const first = `${pad4(this.year)}-${pad2(this.month)}-01`;
|
|
452
|
+
const today = dates.todayYMD();
|
|
453
|
+
const t = dates.parseISO(today);
|
|
454
|
+
return t.year === this.year && t.month === this.month ? today : first;
|
|
455
|
+
}
|
|
456
|
+
eventById(id) {
|
|
457
|
+
var _a, _b;
|
|
458
|
+
return (_b = (_a = this._layoutCache) === null || _a === void 0 ? void 0 : _a.byId.get(id)) !== null && _b !== void 0 ? _b : this.events.find((e) => e.id === id);
|
|
459
|
+
}
|
|
460
|
+
isDayInteractable(date) {
|
|
461
|
+
return !dates.isDisabled(date, { min: this.minDate, max: this.maxDate });
|
|
462
|
+
}
|
|
463
|
+
selectDay(date, rect) {
|
|
464
|
+
if (!this.selectable || !this.isDayInteractable(date))
|
|
465
|
+
return;
|
|
466
|
+
this.selectedDate = date;
|
|
467
|
+
this.coDaySelect.emit({ date, rect });
|
|
468
|
+
}
|
|
469
|
+
/** Limpia el resaltado persistente (lo llama el host al cerrar su form). */
|
|
470
|
+
async clearSelection() {
|
|
471
|
+
this.selectedDate = null;
|
|
472
|
+
}
|
|
473
|
+
/** Un refetch (post-save) invalida la referencia visual. */
|
|
474
|
+
onEventsChange() {
|
|
475
|
+
this.selectedDate = null;
|
|
476
|
+
}
|
|
477
|
+
activateEvent(id, rect) {
|
|
478
|
+
this.coEventClick.emit({ id, rect });
|
|
479
|
+
}
|
|
480
|
+
// ── Keyboard (WAI-ARIA grid, forked from co-calendar) ───────────────────
|
|
481
|
+
moveFocus(next) {
|
|
482
|
+
this.focused = next;
|
|
483
|
+
requestAnimationFrame(() => this.focusCell());
|
|
484
|
+
}
|
|
485
|
+
focusCell() {
|
|
486
|
+
var _a, _b;
|
|
487
|
+
(_b = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-date="${this.focused}"]`)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
488
|
+
}
|
|
489
|
+
// ── Drag-move (day granularity) ─────────────────────────────────────────
|
|
490
|
+
/** Marca la interacción en el root (pointer-events:none en los chips durante el
|
|
491
|
+
* drag — mata el hover/hit-testing de las barras all-day barridas en cada
|
|
492
|
+
* celda cruzada). No es @State a propósito: no debe disparar un render. */
|
|
493
|
+
setInteracting(on) {
|
|
494
|
+
var _a;
|
|
495
|
+
(_a = this.rootEl) === null || _a === void 0 ? void 0 : _a.classList.toggle('cal-month--interacting', on);
|
|
496
|
+
}
|
|
497
|
+
startDrag(e, id) {
|
|
498
|
+
const ev = this.eventById(id);
|
|
499
|
+
if (!ev || !this.editable || ev.editable === false || ev.startEditable === false)
|
|
500
|
+
return;
|
|
501
|
+
// Ver la nota en co-calendar-timegrid#startMove: preventDefault en
|
|
502
|
+
// pointerdown mata el click y el chip arrastrable no podía activarse.
|
|
503
|
+
e.stopPropagation();
|
|
504
|
+
this.dragId = id;
|
|
505
|
+
this.dragOverDate = null;
|
|
506
|
+
this.setInteracting(true);
|
|
507
|
+
}
|
|
508
|
+
finishDrag() {
|
|
509
|
+
const id = this.dragId;
|
|
510
|
+
const target = this.dragOverDate;
|
|
511
|
+
this.dragId = null;
|
|
512
|
+
this.dragOverDate = null;
|
|
513
|
+
this.setInteracting(false);
|
|
514
|
+
if (!id || !target)
|
|
515
|
+
return;
|
|
516
|
+
const ev = this.eventById(id);
|
|
517
|
+
if (!ev)
|
|
518
|
+
return;
|
|
519
|
+
const originDay = ev.allDay
|
|
520
|
+
? ev.start.slice(0, 10)
|
|
521
|
+
: tz.zonedDateKey(ev.startMs, this.timeZone);
|
|
522
|
+
const deltaDays = daysBetween(originDay, target);
|
|
523
|
+
if (deltaDays === 0)
|
|
524
|
+
return;
|
|
525
|
+
if (!this.isDayInteractable(target))
|
|
526
|
+
return;
|
|
527
|
+
const shifted = shiftEventByDays(ev, deltaDays, this.timeZone);
|
|
528
|
+
this.coEventDrop.emit({
|
|
529
|
+
id,
|
|
530
|
+
start: shifted.start,
|
|
531
|
+
end: shifted.end,
|
|
532
|
+
oldStart: ev.start,
|
|
533
|
+
oldEnd: ev.end,
|
|
534
|
+
allDay: ev.allDay,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
// ── Render ──────────────────────────────────────────────────────────────
|
|
538
|
+
getLayouts() {
|
|
539
|
+
var _a;
|
|
540
|
+
const c = this._layoutCache;
|
|
541
|
+
if (c &&
|
|
542
|
+
c.events === this.events &&
|
|
543
|
+
c.year === this.year &&
|
|
544
|
+
c.month === this.month &&
|
|
545
|
+
c.firstDayOfWeek === this.firstDayOfWeek &&
|
|
546
|
+
c.timeZone === this.timeZone &&
|
|
547
|
+
c.maxLanes === this.maxLanes) {
|
|
548
|
+
return c;
|
|
549
|
+
}
|
|
550
|
+
const matrix = dates.getMonthMatrix(this.year, this.month, this.firstDayOfWeek).map((row) => row.map((cell) => cell.value));
|
|
551
|
+
const layout = layoutMonth(this.events, matrix, {
|
|
552
|
+
maxLanes: this.maxLanes,
|
|
553
|
+
timeZone: this.timeZone,
|
|
554
|
+
});
|
|
555
|
+
const segByWeek = new Map();
|
|
556
|
+
for (const seg of layout.segments) {
|
|
557
|
+
if (seg.lane >= this.maxLanes)
|
|
558
|
+
continue;
|
|
559
|
+
const arr = (_a = segByWeek.get(seg.weekIndex)) !== null && _a !== void 0 ? _a : [];
|
|
560
|
+
arr.push(seg);
|
|
561
|
+
segByWeek.set(seg.weekIndex, arr);
|
|
562
|
+
}
|
|
563
|
+
const byId = new Map(this.events.map((e) => [e.id, e]));
|
|
564
|
+
this._layoutCache = {
|
|
565
|
+
events: this.events,
|
|
566
|
+
year: this.year,
|
|
567
|
+
month: this.month,
|
|
568
|
+
firstDayOfWeek: this.firstDayOfWeek,
|
|
569
|
+
timeZone: this.timeZone,
|
|
570
|
+
maxLanes: this.maxLanes,
|
|
571
|
+
matrix,
|
|
572
|
+
layout,
|
|
573
|
+
segByWeek,
|
|
574
|
+
byId,
|
|
575
|
+
};
|
|
576
|
+
return this._layoutCache;
|
|
577
|
+
}
|
|
578
|
+
renderChip(seg, weekDays) {
|
|
579
|
+
var _a;
|
|
580
|
+
const ev = this.eventById(seg.eventId);
|
|
581
|
+
if (!ev)
|
|
582
|
+
return null;
|
|
583
|
+
const cols = weekDays.length;
|
|
584
|
+
const left = (seg.startCol / cols) * 100;
|
|
585
|
+
const width = ((seg.endCol - seg.startCol + 1) / cols) * 100;
|
|
586
|
+
const isPast = ev.endMs <= this.nowMs;
|
|
587
|
+
const isExternal = ((_a = ev.extendedProps) === null || _a === void 0 ? void 0 : _a.isExternal) === true;
|
|
588
|
+
const locked = !this.editable || ev.editable === false;
|
|
589
|
+
const timeText = ev.allDay || !seg.isStart
|
|
590
|
+
? ''
|
|
591
|
+
: minutesToLabel$1(tz.zonedMinutesOfDay(ev.startMs, this.timeZone), this.locale);
|
|
592
|
+
const style = {
|
|
593
|
+
left: `${left}%`,
|
|
594
|
+
width: `${width}%`,
|
|
595
|
+
top: `calc(var(--co-cal-daynum-h) + ${seg.lane} * var(--co-cal-lane-h))`,
|
|
596
|
+
};
|
|
597
|
+
if (ev.backgroundColor) {
|
|
598
|
+
style['--co-cal-event-bg'] = ev.backgroundColor;
|
|
599
|
+
const textToken = readableTextToken(this.rootEl, ev.backgroundColor);
|
|
600
|
+
if (textToken)
|
|
601
|
+
style['--co-cal-event-text'] = textToken;
|
|
602
|
+
}
|
|
603
|
+
if (ev.borderColor)
|
|
604
|
+
style['--co-cal-event-border'] = ev.borderColor;
|
|
605
|
+
return (index.h("button", { type: "button", key: `${seg.eventId}-${seg.weekIndex}`, class: {
|
|
606
|
+
'cal-chip': true,
|
|
607
|
+
'cal-chip--all-day': ev.allDay,
|
|
608
|
+
'cal-chip--past': isPast,
|
|
609
|
+
'cal-chip--external': isExternal,
|
|
610
|
+
'cal-chip--locked': locked,
|
|
611
|
+
'cal-chip--dragging': this.dragId === ev.id,
|
|
612
|
+
'cal-chip--continues-before': seg.continuesBefore,
|
|
613
|
+
'cal-chip--continues-after': seg.continuesAfter,
|
|
614
|
+
'cal-chip--tentative': ev.responseState === 'tentative',
|
|
615
|
+
'cal-chip--cancelled': ev.responseState === 'cancelled' || ev.responseState === 'declined',
|
|
616
|
+
}, style: style, title: ev.title, "aria-label": ev.title, onClick: (e) => {
|
|
617
|
+
e.stopPropagation();
|
|
618
|
+
this.activateEvent(ev.id, e.currentTarget.getBoundingClientRect());
|
|
619
|
+
}, onDblClick: (e) => {
|
|
620
|
+
e.stopPropagation();
|
|
621
|
+
this.coEventDblClick.emit({ id: ev.id, rect: e.currentTarget.getBoundingClientRect() });
|
|
622
|
+
}, onPointerDown: (e) => this.startDrag(e, ev.id) }, !ev.allDay && index.h("span", { class: "cal-chip__dot", "aria-hidden": "true" }), timeText && index.h("span", { class: "cal-chip__time" }, timeText), index.h("span", { class: "cal-chip__title" }, ev.title || '—'), isExternal && (providerBrand$1(ev)
|
|
623
|
+
? index.h("co-provider-logo", { provider: providerBrand$1(ev), size: 11, plated: true, class: "cal-chip__ext" })
|
|
624
|
+
: index.h("co-icon", { name: "link", size: 11, class: "cal-chip__ext" }))));
|
|
625
|
+
}
|
|
626
|
+
render() {
|
|
627
|
+
const { matrix, layout, segByWeek } = this.getLayouts();
|
|
628
|
+
const short = dates.getWeekdayLabels(this.locale, this.firstDayOfWeek, 'short');
|
|
629
|
+
const long = dates.getWeekdayLabels(this.locale, this.firstDayOfWeek, 'long');
|
|
630
|
+
const today = dates.todayYMD();
|
|
631
|
+
return (index.h("div", { key: '2ed94ebcddb0f595f740adf3859dc4dcaff49b3c', class: "cal-month", part: "grid", role: "grid", onKeyDown: this.handleKeyDown, ref: (el) => (this.rootEl = el) }, index.h("div", { key: '7348dc62431ab4a27295529ac8d596798eee87b5', class: "cal-month__dow", role: "row" }, short.map((lbl, i) => (index.h("span", { class: "cal-month__dow-cell", role: "columnheader", "aria-label": long[i] }, lbl)))), matrix.map((week, wi) => {
|
|
632
|
+
var _a;
|
|
633
|
+
return (index.h("div", { class: "cal-month__week", role: "row" }, index.h("div", { class: "cal-month__days" }, week.map((date) => {
|
|
634
|
+
const ymd = dates.parseISO(date);
|
|
635
|
+
const inMonth = ymd.month === this.month;
|
|
636
|
+
const isToday = dates.compareYMD(date, today) === 0;
|
|
637
|
+
const isFocused = dates.compareYMD(date, this.focused) === 0;
|
|
638
|
+
const interactable = this.isDayInteractable(date);
|
|
639
|
+
return (index.h("div", { role: "gridcell", "data-date": date, tabIndex: isFocused ? 0 : -1, "aria-current": isToday ? 'date' : undefined, "aria-disabled": interactable ? undefined : 'true', class: {
|
|
640
|
+
'cal-month__day': true,
|
|
641
|
+
'cal-month__day--outside': !inMonth,
|
|
642
|
+
'cal-month__day--today': isToday,
|
|
643
|
+
'cal-month__day--disabled': !interactable,
|
|
644
|
+
'cal-month__day--drag-over': this.dragId !== null && this.dragOverDate === date,
|
|
645
|
+
'cal-month__day--selected': this.selectedDate === date,
|
|
646
|
+
}, onClick: (e) => this.selectDay(date, e.currentTarget.getBoundingClientRect()), onPointerEnter: () => {
|
|
647
|
+
if (this.dragId)
|
|
648
|
+
this.dragOverDate = date;
|
|
649
|
+
} }, index.h("span", { class: "cal-month__daynum" }, ymd.day)));
|
|
650
|
+
})), index.h("div", { class: "cal-month__events" }, this.loading && SKELETON_CHIPS[wi % SKELETON_CHIPS.length].map(([col, span, lane]) => (index.h("div", { class: "cal-month__chip-skeleton", "aria-hidden": "true", style: {
|
|
651
|
+
left: `${(col / week.length) * 100}%`,
|
|
652
|
+
width: `${(span / week.length) * 100}%`,
|
|
653
|
+
top: `calc(var(--co-cal-daynum-h) + ${lane} * var(--co-cal-lane-h))`,
|
|
654
|
+
} }, index.h("co-skeleton", { variant: "rect", width: "100%", height: "100%" })))), !this.loading && ((_a = segByWeek.get(wi)) !== null && _a !== void 0 ? _a : []).map((seg) => this.renderChip(seg, week)), !this.loading && week.map((date, col) => {
|
|
655
|
+
var _a;
|
|
656
|
+
const more = (_a = layout.overflow[date]) !== null && _a !== void 0 ? _a : 0;
|
|
657
|
+
if (more <= 0)
|
|
658
|
+
return null;
|
|
659
|
+
return (index.h("button", { type: "button", class: "cal-month__more", style: {
|
|
660
|
+
left: `${(col / week.length) * 100}%`,
|
|
661
|
+
width: `${(1 / week.length) * 100}%`,
|
|
662
|
+
top: `calc(var(--co-cal-daynum-h) + ${this.maxLanes} * var(--co-cal-lane-h))`,
|
|
663
|
+
}, onClick: (e) => {
|
|
664
|
+
e.stopPropagation();
|
|
665
|
+
this.coMoreClick.emit({ date });
|
|
666
|
+
} }, "+", more));
|
|
667
|
+
}))));
|
|
668
|
+
})));
|
|
669
|
+
}
|
|
670
|
+
get el() { return index.getElement(this); }
|
|
671
|
+
static get watchers() { return {
|
|
672
|
+
"events": [{
|
|
673
|
+
"onEventsChange": 0
|
|
674
|
+
}]
|
|
675
|
+
}; }
|
|
676
|
+
};
|
|
677
|
+
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
678
|
+
/** Chips del skeleton por semana: `[columna, ancho en días, carril]`. Patrón
|
|
679
|
+
* FIJO (nada de random) y desparejo, como se ve un mes real. */
|
|
680
|
+
const SKELETON_CHIPS = [
|
|
681
|
+
[[0, 2, 0], [3, 1, 0], [5, 1, 1]],
|
|
682
|
+
[[1, 1, 0], [2, 3, 1]],
|
|
683
|
+
[[0, 1, 0], [2, 1, 0], [4, 2, 1], [6, 1, 0]],
|
|
684
|
+
[[1, 2, 0], [4, 1, 1]],
|
|
685
|
+
[[2, 1, 0], [3, 2, 1], [6, 1, 0]],
|
|
686
|
+
[[0, 3, 0], [5, 1, 1]],
|
|
687
|
+
];
|
|
688
|
+
function pad2(n) {
|
|
689
|
+
return String(n).padStart(2, '0');
|
|
690
|
+
}
|
|
691
|
+
function pad4(n) {
|
|
692
|
+
return String(n).padStart(4, '0');
|
|
693
|
+
}
|
|
694
|
+
/** Whole-day delta between two "YYYY-MM-DD" using UTC-midnight arithmetic. */
|
|
695
|
+
function daysBetween(a, b) {
|
|
696
|
+
const [ay, am, ad] = a.split('-').map(Number);
|
|
697
|
+
const [by, bm, bd] = b.split('-').map(Number);
|
|
698
|
+
return Math.round((Date.UTC(by, bm - 1, bd) - Date.UTC(ay, am - 1, ad)) / 86400000);
|
|
699
|
+
}
|
|
700
|
+
/** Shift an event by N days, preserving wall-clock time (DST-correct for timed). */
|
|
701
|
+
function shiftEventByDays(ev, deltaDays, timeZone) {
|
|
702
|
+
if (ev.allDay) {
|
|
703
|
+
return {
|
|
704
|
+
start: dates.addDays(ev.start.slice(0, 10), deltaDays),
|
|
705
|
+
end: dates.addDays(ev.end.slice(0, 10), deltaDays),
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
start: tz.toUtcISO(shiftInstant(ev.startMs, deltaDays, timeZone)),
|
|
710
|
+
end: tz.toUtcISO(shiftInstant(ev.endMs, deltaDays, timeZone)),
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function shiftInstant(ms, deltaDays, timeZone) {
|
|
714
|
+
const wc = tz.getWallClock(ms, timeZone);
|
|
715
|
+
const newDate = dates.addDays(`${pad4(wc.year)}-${pad2(wc.month)}-${pad2(wc.day)}`, deltaDays);
|
|
716
|
+
const [y, m, d] = newDate.split('-').map(Number);
|
|
717
|
+
return tz.wallClockToEpoch({ year: y, month: m, day: d, hour: wc.hour, minute: wc.minute, second: wc.second }, timeZone);
|
|
718
|
+
}
|
|
719
|
+
// Intl.DateTimeFormat es caro de construir y este label se recalcula por chip
|
|
720
|
+
// por render sobre un dominio ACOTADO (≤96 minutos distintos, 1-2 locales):
|
|
721
|
+
// cacheamos el RESULTADO (y el formatter, para el miss). Higiene: clear a las
|
|
722
|
+
// 2000 entradas — inalcanzable en uso normal, solo protege de iteración
|
|
723
|
+
// patológica. Mismo patrón que co-calendar-timegrid.tsx (duplicado a propósito:
|
|
724
|
+
// componentes Stencil separados, consistencia > DRY entre ellos).
|
|
725
|
+
const MINUTE_LABEL_CACHE$1 = new Map();
|
|
726
|
+
const MINUTE_FMT_CACHE$1 = new Map();
|
|
727
|
+
/** Short localized time label, e.g. "9:00 a. m." / "09:00". */
|
|
728
|
+
function minutesToLabel$1(minutes, locale) {
|
|
729
|
+
const key = `${locale}|${minutes}`;
|
|
730
|
+
const hit = MINUTE_LABEL_CACHE$1.get(key);
|
|
731
|
+
if (hit !== undefined)
|
|
732
|
+
return hit;
|
|
733
|
+
let fmt = MINUTE_FMT_CACHE$1.get(locale);
|
|
734
|
+
if (!fmt) {
|
|
735
|
+
fmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', minute: '2-digit' });
|
|
736
|
+
MINUTE_FMT_CACHE$1.set(locale, fmt);
|
|
737
|
+
}
|
|
738
|
+
const h = Math.floor(minutes / 60);
|
|
739
|
+
const m = minutes % 60;
|
|
740
|
+
const out = fmt.format(new Date(2000, 0, 1, h, m));
|
|
741
|
+
if (MINUTE_LABEL_CACHE$1.size > 2000)
|
|
742
|
+
MINUTE_LABEL_CACHE$1.clear();
|
|
743
|
+
MINUTE_LABEL_CACHE$1.set(key, out);
|
|
744
|
+
return out;
|
|
745
|
+
}
|
|
746
|
+
/** Marca del evento externo (`co-provider-logo`), o '' si no se reconoce — el
|
|
747
|
+
* llamador cae al ícono genérico de enlace. */
|
|
748
|
+
function providerBrand$1(ev) {
|
|
749
|
+
var _a, _b;
|
|
750
|
+
const p = String((_b = (_a = ev.extendedProps) === null || _a === void 0 ? void 0 : _a.provider) !== null && _b !== void 0 ? _b : '').toLowerCase();
|
|
751
|
+
if (p.includes('google'))
|
|
752
|
+
return 'google';
|
|
753
|
+
if (p.includes('microsoft') || p.includes('outlook'))
|
|
754
|
+
return 'microsoft';
|
|
755
|
+
return '';
|
|
756
|
+
}
|
|
757
|
+
CoCalendarMonth.style = coCalendarMonthCss();
|
|
758
|
+
|
|
759
|
+
const coCalendarTimegridCss = () => `:host{display:flex;flex-direction:column;min-block-size:0;font-family:var(--co-font-family-primary);--co-cal-slot-h:12px;--co-cal-axis-w:56px;--co-cal-surface:var(--co-semantic-surface-page);--co-cal-border:var(--co-semantic-border-default);--co-cal-grid-line:color-mix(in srgb, var(--co-cal-border) 60%, transparent);--co-cal-text:var(--co-semantic-text-default);--co-cal-text-muted:var(--co-semantic-text-muted);--co-cal-hover:var(--co-semantic-surface-hover);--co-cal-today-ring:var(--co-color-primary-azul);--co-cal-now-color:var(--co-color-status-error, #AA2E1B);--co-cal-allday-h:28px;--co-cal-event-radius:6px;--co-cal-elevation-hover:var(--co-shadow-lg);--co-cal-event-bg:var(--co-color-primary-azul);--co-cal-event-border:transparent;--co-cal-event-text:var(--co-color-base-blanco, #fff);--co-cal-event-text-light:var(--co-color-base-blanco, #fff);--co-cal-event-text-dark:var(--co-semantic-text-default)}.tg{display:flex;flex-direction:column;min-block-size:0;border-radius:var(--co-border-radius-xs);overflow:hidden;background:var(--co-cal-surface)}.tg__head{display:grid;grid-template-columns:var(--co-cal-axis-w) 1fr;border-bottom:1px solid var(--co-cal-border)}.tg__head-spacer{border-right:1px solid var(--co-cal-border)}.tg__head-days{display:grid}.tg__head-day{display:flex;flex-direction:column;align-items:center;gap:2px;padding:var(--co-spacing-xs)}.tg__head-dow{font-size:var(--co-font-size-11, 11px);text-transform:uppercase;letter-spacing:0.04em;color:var(--co-cal-text-muted)}.tg__head-num{font-size:var(--co-font-size-20, 20px);color:var(--co-cal-text);min-width:34px;height:34px;line-height:34px;text-align:center;border-radius:var(--co-border-radius-full)}.tg__head-day--today .tg__head-dow{color:var(--co-cal-today-ring)}.tg__head-day--today .tg__head-num{background:var(--co-cal-today-ring);color:var(--co-color-base-blanco, #fff);font-weight:500}.tg__allday{display:grid;grid-template-columns:var(--co-cal-axis-w) 1fr;min-height:var(--co-cal-allday-h);border-bottom:1px solid var(--co-cal-border)}.tg__allday-axis{border-right:1px solid var(--co-cal-border);font-size:var(--co-font-size-10, 10px);color:var(--co-cal-text-muted);padding:var(--co-spacing-xxs)}.tg__allday-lane{position:relative;min-height:var(--co-cal-allday-h);padding:2px 0}.tg__allday-bar{position:absolute;height:20px;box-sizing:border-box;padding:0 var(--co-spacing-xs);border:0;border-radius:var(--co-cal-event-radius);background:var(--co-cal-event-bg);color:var(--co-cal-event-text);font:500 var(--co-font-size-11, 11px)/20px var(--co-font-family-primary);text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;cursor:pointer}.tg__allday-bar:hover{box-shadow:var(--co-cal-elevation-hover);z-index:2}.tg__allday-bar--continues-before{border-top-left-radius:0;border-bottom-left-radius:0}.tg__allday-bar--continues-after{border-top-right-radius:0;border-bottom-right-radius:0}.tg__allday-bar--tentative{background-image:repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.5) 0 4px, transparent 4px 8px)}.tg__allday-bar--cancelled{opacity:0.6;text-decoration:line-through}.tg__allday-bar:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:1px}.tg__scroll{flex:1 1 auto;min-block-size:0;overflow-y:auto}.tg__body{display:grid;grid-template-columns:var(--co-cal-axis-w) 1fr;position:relative}.tg__axis{border-right:1px solid var(--co-cal-border);position:relative}.tg__axis-hour{position:absolute;right:var(--co-spacing-xs);transform:translateY(-50%);font-size:var(--co-font-size-11, 11px);color:var(--co-cal-text-muted);white-space:nowrap}.tg__cols{display:grid;grid-auto-rows:100%;position:relative}.tg__col{position:relative;border-right:1px solid var(--co-cal-border);background-image:linear-gradient( to bottom, var(--co-cal-border) 0, var(--co-cal-border) 1px, transparent 1px );background-size:100% var(--co-cal-hour-h, calc(4 * var(--co-cal-slot-h)));background-position:0 var(--co-cal-hour-offset, 0px);background-repeat:repeat-y}.tg__col:last-child{border-right:0}.tg--interacting .tg__event{pointer-events:none}.tg--interacting .tg__col{contain:layout}.tg__event{position:absolute;user-select:none;-webkit-user-select:none;touch-action:none;box-sizing:border-box;border-radius:var(--co-cal-event-radius);background:var(--co-cal-event-bg);color:var(--co-cal-event-text);border:1px solid var(--co-cal-event-border);padding:2px var(--co-spacing-xs);font-size:var(--co-font-size-11, 11px);line-height:1.15;overflow:hidden;cursor:pointer;text-align:left}.tg__event-skeleton{position:absolute;inset-inline:0 2px;box-sizing:border-box;border-radius:var(--co-cal-event-radius);overflow:hidden;pointer-events:none}.tg__event-skeleton co-skeleton{display:block;block-size:100%}.tg__event:hover{box-shadow:var(--co-cal-elevation-hover);z-index:5}.tg__event--dragging:hover{box-shadow:none}.tg__event:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:1px}.tg__event-time{display:block;font-variant-numeric:tabular-nums;opacity:0.85}.tg__event-title{display:block;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tg__event-ext{position:absolute;top:3px;right:3px;opacity:0.9}.tg__event--past{opacity:0.55}.tg__event--external{border-style:dashed}.tg__event--locked{cursor:default}.tg__event--dragging{opacity:0.5}.tg__event--tentative{background-image:repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.5) 0 4px, transparent 4px 8px)}.tg__event--cancelled{opacity:0.6}.tg__event--cancelled .tg__event-title{text-decoration:line-through}.tg__event-resize{position:absolute;left:0;right:0;bottom:0;height:6px;cursor:ns-resize}.tg__selection{position:absolute;left:0;right:0;box-sizing:border-box;z-index:2;background:color-mix(in srgb, var(--co-cal-today-ring) 22%, transparent);border:1px solid var(--co-cal-today-ring);border-radius:var(--co-border-radius-xxs);pointer-events:none}.tg__drag-ghost{position:absolute;display:none;box-sizing:border-box;z-index:4;pointer-events:none;background:color-mix(in srgb, var(--co-cal-today-ring) 22%, transparent);border:1px solid var(--co-cal-today-ring);border-radius:var(--co-border-radius-xxs);will-change:transform}.tg__focus{position:absolute;left:0;right:0;z-index:1;pointer-events:none;box-sizing:border-box;outline:2px solid var(--co-semantic-border-focus);outline-offset:-2px;border-radius:var(--co-border-radius-xxs)}.tg__now{position:absolute;left:0;right:0;height:0;z-index:3;pointer-events:none}.tg__now::before{content:'';position:absolute;left:-4px;top:-4px;width:8px;height:8px;border-radius:50%;background:var(--co-cal-now-color)}.tg__now::after{content:'';position:absolute;left:0;right:0;top:0;border-top:2px solid var(--co-cal-now-color)}`;
|
|
760
|
+
|
|
761
|
+
const ALLDAY_LANE_H = 22; // px per all-day lane (bar 20px + 2px gap)
|
|
762
|
+
// Distancia (px) que el puntero debe recorrer tras el pointerdown para que un drag
|
|
763
|
+
// de evento se comprometa. Por debajo de esto el gesto es un CLICK: el drag no
|
|
764
|
+
// arranca, el chip conserva sus pointer-events y su click abre el popover. Sin este
|
|
765
|
+
// umbral, `startMove` entraba en modo interacting (pointer-events:none en los chips)
|
|
766
|
+
// en el pointerdown mismo y se comía el click de un evento arrastrable.
|
|
767
|
+
const DRAG_THRESHOLD_PX = 5;
|
|
768
|
+
const CoCalendarTimegrid = class {
|
|
769
|
+
constructor(hostRef) {
|
|
770
|
+
index.registerInstance(this, hostRef);
|
|
771
|
+
this.coRangeSelect = index.createEvent(this, "coRangeSelect", 1);
|
|
772
|
+
this.coEventClick = index.createEvent(this, "coEventClick", 1);
|
|
773
|
+
this.coEventDrop = index.createEvent(this, "coEventDrop", 1);
|
|
774
|
+
this.coEventDblClick = index.createEvent(this, "coEventDblClick", 1);
|
|
775
|
+
this.events = [];
|
|
776
|
+
/** Visible day keys "YYYY-MM-DD" (7 = week, 1 = day). */
|
|
777
|
+
this.dates = [];
|
|
778
|
+
this.locale = 'es-CO';
|
|
779
|
+
this.editable = true;
|
|
780
|
+
this.selectable = true;
|
|
781
|
+
this.slotMinutes = 15;
|
|
782
|
+
this.snapMinutes = 15;
|
|
783
|
+
this.defaultDurationMinutes = 60;
|
|
784
|
+
this.scrollTime = '07:00';
|
|
785
|
+
this.nowIndicator = true;
|
|
786
|
+
/** Cargando: en vez de los eventos pinta bloques skeleton en las columnas.
|
|
787
|
+
* La grilla (eje de horas, líneas, cabecera de días, franja de todo-el-día)
|
|
788
|
+
* se mantiene: lo único que todavía no se sabe son los eventos. */
|
|
789
|
+
this.loading = false;
|
|
790
|
+
this.allDaySlot = true;
|
|
791
|
+
this.minTime = '00:00';
|
|
792
|
+
this.maxTime = '24:00';
|
|
793
|
+
/** Minute of "now" for the indicator; refreshed on a 60s timer. */
|
|
794
|
+
this.nowMinutes = 0;
|
|
795
|
+
this.nowDay = '';
|
|
796
|
+
/** Roving-tabindex focus position for keyboard navigation over slots. */
|
|
797
|
+
this.focusDate = '';
|
|
798
|
+
this.focusMin = 0;
|
|
799
|
+
this.selDate = null;
|
|
800
|
+
this.selStartMin = 0;
|
|
801
|
+
this.selEndMin = 0;
|
|
802
|
+
this.selecting = false;
|
|
803
|
+
/** Coords crudas del último pointermove: el getBoundingClientRect corre UNA vez
|
|
804
|
+
* por frame, dentro del rAF (antes corría a ~120Hz, en el move crudo). */
|
|
805
|
+
this.pendingSel = null;
|
|
806
|
+
this.selRaf = 0;
|
|
807
|
+
this.pendingDrag = null;
|
|
808
|
+
this.dragRaf = 0;
|
|
809
|
+
this.onWindowPointerUp = () => this.finishSelection();
|
|
810
|
+
this.dragId = null;
|
|
811
|
+
this.dragMode = null;
|
|
812
|
+
this.dragGrabOffsetMin = 0; // minutes between event start and the grab point (move)
|
|
813
|
+
this.dragDurationMin = 0; // preserved duration in minutes (move)
|
|
814
|
+
this.dragDate = null; // current target day (move can change columns)
|
|
815
|
+
this.dragStartMin = 0; // provisional new start (move) / fixed start (resize)
|
|
816
|
+
this.dragEndMin = 0; // provisional new end
|
|
817
|
+
this.origStartMin = 0; // pre-drag start minute (no-op comparison)
|
|
818
|
+
this.origEndMin = 0; // pre-drag end minute (no-op comparison)
|
|
819
|
+
this.origDate = null; // pre-drag day (no-op comparison)
|
|
820
|
+
this.suppressNextClick = false; // set right before coEventDrop so the trailing click no-ops
|
|
821
|
+
// Drag ARMADO pero aún no comprometido: `startMove`/`startResize` guardan el id y
|
|
822
|
+
// el punto de agarre acá; el drag pasa a activo (`dragId`) recién cuando el puntero
|
|
823
|
+
// cruza DRAG_THRESHOLD_PX. Así un click simple en un evento arrastrable no arranca
|
|
824
|
+
// drag y su popover abre.
|
|
825
|
+
this.dragArmedId = null;
|
|
826
|
+
this.dragStartPointer = null;
|
|
827
|
+
this.onWindowPointerMove = (e) => this.trackDrag(e);
|
|
828
|
+
this.onWindowPointerUpDrag = () => this.finishDrag();
|
|
829
|
+
/** Rects de las columnas cacheados AL AGARRAR: con esto el frame de drag es
|
|
830
|
+
* aritmética pura (antes: ~9 getBoundingClientRect + 2 querySelector/frame,
|
|
831
|
+
* que además forzaban reflow síncrono al escribir estilos en el mismo rAF). */
|
|
832
|
+
this.colRects = [];
|
|
833
|
+
this.gridRect = null;
|
|
834
|
+
this.onDragViewportChange = () => this.captureDragRects();
|
|
835
|
+
this.startSelection = (e, dateKey, colEl) => {
|
|
836
|
+
if (!this.selectable || !this.isDayInteractable(dateKey))
|
|
837
|
+
return;
|
|
838
|
+
// ignore clicks that land on an event box (those are handled in Task 4/5)
|
|
839
|
+
if (e.target.closest('.tg__event, .tg__allday-bar'))
|
|
840
|
+
return;
|
|
841
|
+
e.preventDefault();
|
|
842
|
+
const m = this.minutesFromPointer(e.clientY, colEl);
|
|
843
|
+
this.selecting = true;
|
|
844
|
+
this.selDate = dateKey;
|
|
845
|
+
this.selStartMin = m;
|
|
846
|
+
this.selEndMin = m;
|
|
847
|
+
this.setInteracting(true);
|
|
848
|
+
};
|
|
849
|
+
// pointermove llega ~120Hz y cada @State re-renderiza la grilla entera: sin
|
|
850
|
+
// batching el ghost persigue al puntero con lag. Un update por frame — el
|
|
851
|
+
// estado intermedio nunca se pinta, así que no se pierde nada. El gBCR (vía
|
|
852
|
+
// minutesFromPointer) también vive DENTRO del rAF: 1 por frame, no 1 por move.
|
|
853
|
+
this.extendSelection = (e, colEl) => {
|
|
854
|
+
if (!this.selecting)
|
|
855
|
+
return;
|
|
856
|
+
this.pendingSel = { y: e.clientY, colEl };
|
|
857
|
+
if (this.selRaf)
|
|
858
|
+
return;
|
|
859
|
+
this.selRaf = requestAnimationFrame(() => {
|
|
860
|
+
this.selRaf = 0;
|
|
861
|
+
const p = this.pendingSel;
|
|
862
|
+
this.pendingSel = null;
|
|
863
|
+
if (!p || !this.selecting)
|
|
864
|
+
return;
|
|
865
|
+
const min = this.minutesFromPointer(p.y, p.colEl);
|
|
866
|
+
if (min !== this.selEndMin)
|
|
867
|
+
this.selEndMin = min; // snap: la mayoría de los moves no cambian nada
|
|
868
|
+
});
|
|
869
|
+
};
|
|
870
|
+
this.startMove = (e, id) => {
|
|
871
|
+
const ev = this.eventById(id);
|
|
872
|
+
if (!ev || ev.allDay || !this.editable || ev.editable === false || ev.startEditable === false)
|
|
873
|
+
return;
|
|
874
|
+
// Sin preventDefault: en pointerdown CANCELA el click de compatibilidad del
|
|
875
|
+
// navegador, y entonces un evento ARRASTRABLE nunca podía abrir su popover
|
|
876
|
+
// (solo los externos, que no arrancan drag). Lo que preventDefault daba —no
|
|
877
|
+
// seleccionar texto, no scrollear en touch— lo dan `user-select`/
|
|
878
|
+
// `touch-action` en el CSS del bloque. El click POSTERIOR a un drag real
|
|
879
|
+
// sigue anulado por `suppressNextClick`.
|
|
880
|
+
e.stopPropagation();
|
|
881
|
+
const dateKey = tz.zonedDateKey(ev.startMs, this.timeZone);
|
|
882
|
+
this.captureDragRects();
|
|
883
|
+
if (!this.gridRect || !this.colRects.some((c) => c.d === dateKey))
|
|
884
|
+
return;
|
|
885
|
+
const grabMin = this.minutesFromCachedY(e.clientY);
|
|
886
|
+
const startMin = tz.zonedMinutesOfDay(ev.startMs, this.timeZone);
|
|
887
|
+
this.dragArmedId = id;
|
|
888
|
+
this.dragMode = 'move';
|
|
889
|
+
this.dragDate = dateKey;
|
|
890
|
+
this.dragGrabOffsetMin = grabMin - startMin;
|
|
891
|
+
this.dragDurationMin = Math.round((ev.endMs - ev.startMs) / 60000);
|
|
892
|
+
this.dragStartMin = startMin;
|
|
893
|
+
this.dragEndMin = startMin + this.dragDurationMin;
|
|
894
|
+
this.origStartMin = startMin;
|
|
895
|
+
this.origEndMin = startMin + this.dragDurationMin;
|
|
896
|
+
this.origDate = dateKey;
|
|
897
|
+
this.dragStartPointer = { x: e.clientX, y: e.clientY };
|
|
898
|
+
};
|
|
899
|
+
this.startResize = (e, id) => {
|
|
900
|
+
const ev = this.eventById(id);
|
|
901
|
+
if (!ev || ev.allDay || !this.editable || ev.editable === false || ev.durationEditable === false)
|
|
902
|
+
return;
|
|
903
|
+
e.stopPropagation();
|
|
904
|
+
const dateKey = tz.zonedDateKey(ev.startMs, this.timeZone);
|
|
905
|
+
this.captureDragRects();
|
|
906
|
+
if (!this.gridRect || !this.colRects.some((c) => c.d === dateKey))
|
|
907
|
+
return;
|
|
908
|
+
this.dragArmedId = id;
|
|
909
|
+
this.dragMode = 'resize';
|
|
910
|
+
this.dragDate = dateKey;
|
|
911
|
+
this.dragStartMin = tz.zonedMinutesOfDay(ev.startMs, this.timeZone);
|
|
912
|
+
this.dragEndMin = tz.zonedMinutesOfDay(ev.endMs, this.timeZone);
|
|
913
|
+
this.origStartMin = this.dragStartMin;
|
|
914
|
+
this.origEndMin = this.dragEndMin;
|
|
915
|
+
this.origDate = dateKey;
|
|
916
|
+
this.dragStartPointer = { x: e.clientX, y: e.clientY };
|
|
917
|
+
};
|
|
918
|
+
/** Roving-tabindex keyboard nav: ↑/↓ by slot, ←/→ by day, Enter/Space to select. */
|
|
919
|
+
this.handleKeyDown = (e) => {
|
|
920
|
+
const step = this.slotMinutes;
|
|
921
|
+
let handled = true;
|
|
922
|
+
switch (e.key) {
|
|
923
|
+
case 'ArrowUp':
|
|
924
|
+
this.focusMin = time.clampMinutes(this.focusMin - step, this.minM, this.maxM - step);
|
|
925
|
+
break;
|
|
926
|
+
case 'ArrowDown':
|
|
927
|
+
this.focusMin = time.clampMinutes(this.focusMin + step, this.minM, this.maxM - step);
|
|
928
|
+
break;
|
|
929
|
+
case 'ArrowLeft':
|
|
930
|
+
this.focusDate = this.shiftFocusDate(-1);
|
|
931
|
+
break;
|
|
932
|
+
case 'ArrowRight':
|
|
933
|
+
this.focusDate = this.shiftFocusDate(1);
|
|
934
|
+
break;
|
|
935
|
+
case 'Enter':
|
|
936
|
+
case ' ': {
|
|
937
|
+
if (!this.isDayInteractable(this.focusDate))
|
|
938
|
+
break;
|
|
939
|
+
const start = this.toInstant(this.focusDate, this.focusMin);
|
|
940
|
+
const end = this.toInstant(this.focusDate, Math.min(this.focusMin + this.defaultDurationMinutes, this.maxM));
|
|
941
|
+
if (new Date(start).getTime() >= Date.now())
|
|
942
|
+
this.coRangeSelect.emit({ start, end });
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
default:
|
|
946
|
+
handled = false;
|
|
947
|
+
}
|
|
948
|
+
if (handled) {
|
|
949
|
+
e.preventDefault();
|
|
950
|
+
e.stopPropagation();
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
componentWillLoad() {
|
|
955
|
+
this.refreshNow();
|
|
956
|
+
this.focusDate = (this.dates && this.dates[0]) || dates.todayYMD();
|
|
957
|
+
this.focusMin = hhmmToMinutes(this.scrollTime, 420);
|
|
958
|
+
}
|
|
959
|
+
componentDidLoad() {
|
|
960
|
+
this.scrollToInitial();
|
|
961
|
+
this.nowTimer = window.setInterval(() => this.refreshNow(), 60000);
|
|
962
|
+
window.addEventListener('pointerup', this.onWindowPointerUp);
|
|
963
|
+
window.addEventListener('pointermove', this.onWindowPointerMove);
|
|
964
|
+
window.addEventListener('pointerup', this.onWindowPointerUpDrag);
|
|
965
|
+
}
|
|
966
|
+
disconnectedCallback() {
|
|
967
|
+
if (this.nowTimer)
|
|
968
|
+
window.clearInterval(this.nowTimer);
|
|
969
|
+
if (this.selRaf)
|
|
970
|
+
cancelAnimationFrame(this.selRaf);
|
|
971
|
+
if (this.dragRaf)
|
|
972
|
+
cancelAnimationFrame(this.dragRaf);
|
|
973
|
+
window.removeEventListener('pointerup', this.onWindowPointerUp);
|
|
974
|
+
window.removeEventListener('pointermove', this.onWindowPointerMove);
|
|
975
|
+
window.removeEventListener('pointerup', this.onWindowPointerUpDrag);
|
|
976
|
+
this.bindDragViewportListeners(false);
|
|
977
|
+
this.hideGhost();
|
|
978
|
+
this.colRects = [];
|
|
979
|
+
this.gridRect = null;
|
|
980
|
+
}
|
|
981
|
+
/** Limpia el resaltado persistente de selección (lo llama el host al cerrar
|
|
982
|
+
* su bubble/form de creación). */
|
|
983
|
+
async clearSelection() {
|
|
984
|
+
this.selDate = null;
|
|
985
|
+
}
|
|
986
|
+
/** Un refetch (post-save) invalida la referencia visual de la selección. */
|
|
987
|
+
onEventsChange() {
|
|
988
|
+
if (!this.selecting)
|
|
989
|
+
this.selDate = null;
|
|
990
|
+
}
|
|
991
|
+
refreshNow() {
|
|
992
|
+
const ms = Date.now();
|
|
993
|
+
this.nowDay = tz.zonedDateKey(ms, this.timeZone);
|
|
994
|
+
this.nowMinutes = tz.zonedMinutesOfDay(ms, this.timeZone);
|
|
995
|
+
}
|
|
996
|
+
get minM() {
|
|
997
|
+
return hhmmToMinutes(this.minTime, 0);
|
|
998
|
+
}
|
|
999
|
+
get maxM() {
|
|
1000
|
+
return hhmmToMinutes(this.maxTime, 1440);
|
|
1001
|
+
}
|
|
1002
|
+
scrollToInitial() {
|
|
1003
|
+
var _a, _b, _c;
|
|
1004
|
+
const scroller = (_a = this.scrollEl) !== null && _a !== void 0 ? _a : (_b = this.el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.tg__scroll');
|
|
1005
|
+
const body = (_c = this.el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('.tg__body');
|
|
1006
|
+
if (!scroller || !body)
|
|
1007
|
+
return;
|
|
1008
|
+
const frac = time.minutesToFraction(hhmmToMinutes(this.scrollTime, 420), this.minM, this.maxM);
|
|
1009
|
+
scroller.scrollTop = frac * body.offsetHeight;
|
|
1010
|
+
}
|
|
1011
|
+
/** Visible day keys, with the same fallback the render uses. */
|
|
1012
|
+
visibleDates() {
|
|
1013
|
+
return this.dates.length ? this.dates : [dates.todayYMD()];
|
|
1014
|
+
}
|
|
1015
|
+
/** Slot size guarded: ahora alimenta un `calc()` (un 0/NaN dejaría la grilla
|
|
1016
|
+
* sin altura, silenciosamente). */
|
|
1017
|
+
get slotM() {
|
|
1018
|
+
return this.slotMinutes > 0 ? this.slotMinutes : 15;
|
|
1019
|
+
}
|
|
1020
|
+
/** Number of slot rows in the visible window (drives the body height). */
|
|
1021
|
+
slotCount() {
|
|
1022
|
+
return Math.max(1, Math.ceil((this.maxM - this.minM) / this.slotM));
|
|
1023
|
+
}
|
|
1024
|
+
/** Hour marks inside the visible window — one axis label each (was 1 div per
|
|
1025
|
+
* slot with a label only on the hour). */
|
|
1026
|
+
hourList() {
|
|
1027
|
+
const out = [];
|
|
1028
|
+
const first = Math.ceil(this.minM / 60) * 60;
|
|
1029
|
+
for (let m = first; m < this.maxM; m += 60)
|
|
1030
|
+
out.push(m);
|
|
1031
|
+
return out;
|
|
1032
|
+
}
|
|
1033
|
+
/** Minutes-of-day at a clientY within a column element, snapped and clamped. */
|
|
1034
|
+
minutesFromPointer(clientY, colEl) {
|
|
1035
|
+
const rect = colEl.getBoundingClientRect();
|
|
1036
|
+
return this.minutesAtFraction((clientY - rect.top) / rect.height);
|
|
1037
|
+
}
|
|
1038
|
+
/** Same math as minutesFromPointer but over the cached grid rect (drag path):
|
|
1039
|
+
* cero lecturas de layout por frame. */
|
|
1040
|
+
minutesFromCachedY(clientY) {
|
|
1041
|
+
const g = this.gridRect;
|
|
1042
|
+
if (!g || !g.height)
|
|
1043
|
+
return this.minM;
|
|
1044
|
+
return this.minutesAtFraction((clientY - g.top) / g.height);
|
|
1045
|
+
}
|
|
1046
|
+
minutesAtFraction(frac) {
|
|
1047
|
+
const raw = this.minM + frac * (this.maxM - this.minM);
|
|
1048
|
+
return time.clampMinutes(time.snapMinutes(raw, this.snapMinutes), this.minM, this.maxM);
|
|
1049
|
+
}
|
|
1050
|
+
/** Snapshot de la geometría de columnas al empezar un drag. Se re-toma solo
|
|
1051
|
+
* cuando el viewport cambia (scroll del body / resize), nunca por frame. */
|
|
1052
|
+
captureDragRects() {
|
|
1053
|
+
const shadow = this.el.shadowRoot;
|
|
1054
|
+
const colsEl = shadow === null || shadow === void 0 ? void 0 : shadow.querySelector('.tg__cols');
|
|
1055
|
+
const cols = shadow === null || shadow === void 0 ? void 0 : shadow.querySelectorAll('.tg__col');
|
|
1056
|
+
if (!colsEl || !cols || !cols.length) {
|
|
1057
|
+
this.colRects = [];
|
|
1058
|
+
this.gridRect = null;
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const box = colsEl.getBoundingClientRect();
|
|
1062
|
+
this.gridRect = { top: box.top, height: box.height };
|
|
1063
|
+
this.colRects = Array.from(cols).map((c) => {
|
|
1064
|
+
var _a;
|
|
1065
|
+
const r = c.getBoundingClientRect();
|
|
1066
|
+
return { d: (_a = c.dataset.colDate) !== null && _a !== void 0 ? _a : '', left: r.left, right: r.right };
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
bindDragViewportListeners(on) {
|
|
1070
|
+
const scroller = this.scrollEl;
|
|
1071
|
+
if (on) {
|
|
1072
|
+
scroller === null || scroller === void 0 ? void 0 : scroller.addEventListener('scroll', this.onDragViewportChange, { passive: true });
|
|
1073
|
+
window.addEventListener('resize', this.onDragViewportChange);
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
scroller === null || scroller === void 0 ? void 0 : scroller.removeEventListener('scroll', this.onDragViewportChange);
|
|
1077
|
+
window.removeEventListener('resize', this.onDragViewportChange);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/** Marca la interacción en el root (hover/hit-testing off en los chips). No es
|
|
1081
|
+
* @State a propósito: no debe disparar un render por drag. */
|
|
1082
|
+
setInteracting(on) {
|
|
1083
|
+
var _a;
|
|
1084
|
+
(_a = this.rootEl) === null || _a === void 0 ? void 0 : _a.classList.toggle('tg--interacting', on);
|
|
1085
|
+
}
|
|
1086
|
+
/** Build an ISO instant (UTC) for a wall-clock minute-of-day on a given day. */
|
|
1087
|
+
toInstant(dateKey, minutes) {
|
|
1088
|
+
const [year, month, day] = dateKey.split('-').map(Number);
|
|
1089
|
+
const epoch = tz.wallClockToEpoch({ year, month, day, hour: Math.floor(minutes / 60), minute: minutes % 60, second: 0 }, this.timeZone);
|
|
1090
|
+
return tz.toUtcISO(epoch);
|
|
1091
|
+
}
|
|
1092
|
+
isDayInteractable(dateKey) {
|
|
1093
|
+
if (this.minDate && dateKey < this.minDate)
|
|
1094
|
+
return false;
|
|
1095
|
+
if (this.maxDate && dateKey > this.maxDate)
|
|
1096
|
+
return false;
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
finishSelection() {
|
|
1100
|
+
if (!this.selecting)
|
|
1101
|
+
return;
|
|
1102
|
+
// Flush del frame pendiente: sin esto el rango emitido se quedaría un
|
|
1103
|
+
// pointermove atrás del punto donde el usuario soltó.
|
|
1104
|
+
if (this.selRaf) {
|
|
1105
|
+
cancelAnimationFrame(this.selRaf);
|
|
1106
|
+
this.selRaf = 0;
|
|
1107
|
+
}
|
|
1108
|
+
const pending = this.pendingSel;
|
|
1109
|
+
this.pendingSel = null;
|
|
1110
|
+
if (pending) {
|
|
1111
|
+
const min = this.minutesFromPointer(pending.y, pending.colEl);
|
|
1112
|
+
if (min !== this.selEndMin)
|
|
1113
|
+
this.selEndMin = min;
|
|
1114
|
+
}
|
|
1115
|
+
this.selecting = false;
|
|
1116
|
+
this.setInteracting(false);
|
|
1117
|
+
const dateKey = this.selDate;
|
|
1118
|
+
let a = Math.min(this.selStartMin, this.selEndMin);
|
|
1119
|
+
let b = Math.max(this.selStartMin, this.selEndMin);
|
|
1120
|
+
if (b - a < this.snapMinutes)
|
|
1121
|
+
b = a + this.defaultDurationMinutes; // simple click → default duration
|
|
1122
|
+
b = Math.min(b, this.maxM);
|
|
1123
|
+
const rect = dateKey ? this.selectionRect(dateKey, a, b) : undefined;
|
|
1124
|
+
if (!dateKey)
|
|
1125
|
+
return;
|
|
1126
|
+
const start = this.toInstant(dateKey, a);
|
|
1127
|
+
const end = this.toInstant(dateKey, b);
|
|
1128
|
+
if (new Date(start).getTime() < Date.now()) {
|
|
1129
|
+
this.selDate = null; // past-block: no habrá create, limpiar el ghost
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
// El ghost PERSISTE tras soltar (rango FINAL ajustado — un click simple muestra
|
|
1133
|
+
// el bloque de duración default): referencia visual mientras el host tiene la
|
|
1134
|
+
// bubble/form abiertos (paridad Google). Se limpia vía clearSelection() del
|
|
1135
|
+
// host, al cambiar `events` (refetch post-save) o al iniciar otra selección.
|
|
1136
|
+
this.selStartMin = a;
|
|
1137
|
+
this.selEndMin = b;
|
|
1138
|
+
this.coRangeSelect.emit({ start, end, rect });
|
|
1139
|
+
}
|
|
1140
|
+
/** Viewport rect of the selected span, mirroring the `.tg__selection` ghost's
|
|
1141
|
+
* fraction math over the column's own bounding box. Never throws: falls back to
|
|
1142
|
+
* the whole-column rect, or undefined if the column isn't in the DOM. */
|
|
1143
|
+
selectionRect(dateKey, startMin, endMin) {
|
|
1144
|
+
var _a;
|
|
1145
|
+
const colEl = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-col-date="${dateKey}"]`);
|
|
1146
|
+
if (!colEl)
|
|
1147
|
+
return undefined;
|
|
1148
|
+
const colRect = colEl.getBoundingClientRect();
|
|
1149
|
+
try {
|
|
1150
|
+
const fracStart = time.minutesToFraction(startMin, this.minM, this.maxM);
|
|
1151
|
+
const fracEnd = time.minutesToFraction(endMin, this.minM, this.maxM);
|
|
1152
|
+
return {
|
|
1153
|
+
top: colRect.top + fracStart * colRect.height,
|
|
1154
|
+
left: colRect.left,
|
|
1155
|
+
width: colRect.width,
|
|
1156
|
+
height: (fracEnd - fracStart) * colRect.height,
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
catch (_b) {
|
|
1160
|
+
return colRect;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
eventById(id) {
|
|
1164
|
+
return this.events.find((e) => e.id === id);
|
|
1165
|
+
}
|
|
1166
|
+
/** The day column under a clientX (week can drop across days) — resuelto por
|
|
1167
|
+
* aritmética sobre los rects cacheados, sin tocar el DOM. */
|
|
1168
|
+
dateKeyAtX(clientX) {
|
|
1169
|
+
for (const c of this.colRects) {
|
|
1170
|
+
if (clientX >= c.left && clientX < c.right)
|
|
1171
|
+
return c.d || null;
|
|
1172
|
+
}
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
beginDragInteraction() {
|
|
1176
|
+
this.setInteracting(true);
|
|
1177
|
+
this.bindDragViewportListeners(true);
|
|
1178
|
+
this.paintGhost();
|
|
1179
|
+
}
|
|
1180
|
+
/** Ghost del drag escrito DIRECTO al DOM: el vdom no participa (los campos del
|
|
1181
|
+
* drag son privados a propósito — promoverlos a @State volvería a re-renderizar
|
|
1182
|
+
* la grilla entera por frame). */
|
|
1183
|
+
paintGhost() {
|
|
1184
|
+
const g = this.ghostEl;
|
|
1185
|
+
if (!g)
|
|
1186
|
+
return;
|
|
1187
|
+
const nCols = this.colRects.length;
|
|
1188
|
+
const idx = this.dragDate ? this.colRects.findIndex((c) => c.d === this.dragDate) : -1;
|
|
1189
|
+
if (!nCols || idx < 0) {
|
|
1190
|
+
g.style.display = 'none';
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
const span = this.maxM - this.minM || 1;
|
|
1194
|
+
const fracTop = (this.dragStartMin - this.minM) / span;
|
|
1195
|
+
const fracH = (this.dragEndMin - this.dragStartMin) / span;
|
|
1196
|
+
g.style.display = 'block';
|
|
1197
|
+
g.style.left = `${(idx / nCols) * 100}%`;
|
|
1198
|
+
g.style.width = `calc(${100 / nCols}% - 2px)`;
|
|
1199
|
+
g.style.top = `${fracTop * 100}%`;
|
|
1200
|
+
g.style.height = `${fracH * 100}%`;
|
|
1201
|
+
}
|
|
1202
|
+
hideGhost() {
|
|
1203
|
+
if (this.ghostEl)
|
|
1204
|
+
this.ghostEl.style.display = 'none';
|
|
1205
|
+
}
|
|
1206
|
+
/** Igual que extendSelection: un solo apply por frame (ver comentario allí). */
|
|
1207
|
+
trackDrag(e) {
|
|
1208
|
+
// Fase armado → activo: el drag se compromete recién al cruzar el umbral.
|
|
1209
|
+
// Antes de eso NO se toca interacting/ghost, así el click de un evento
|
|
1210
|
+
// arrastrable llega a su onClick y abre el popover.
|
|
1211
|
+
if (this.dragArmedId && !this.dragId) {
|
|
1212
|
+
const sp = this.dragStartPointer;
|
|
1213
|
+
if (!sp)
|
|
1214
|
+
return;
|
|
1215
|
+
if (Math.hypot(e.clientX - sp.x, e.clientY - sp.y) < DRAG_THRESHOLD_PX)
|
|
1216
|
+
return;
|
|
1217
|
+
this.dragId = this.dragArmedId;
|
|
1218
|
+
this.dragArmedId = null;
|
|
1219
|
+
this.beginDragInteraction();
|
|
1220
|
+
}
|
|
1221
|
+
if (!this.dragId || !this.dragMode || !this.dragDate)
|
|
1222
|
+
return;
|
|
1223
|
+
this.pendingDrag = { x: e.clientX, y: e.clientY };
|
|
1224
|
+
if (this.dragRaf)
|
|
1225
|
+
return;
|
|
1226
|
+
this.dragRaf = requestAnimationFrame(() => {
|
|
1227
|
+
this.dragRaf = 0;
|
|
1228
|
+
const p = this.pendingDrag;
|
|
1229
|
+
this.pendingDrag = null;
|
|
1230
|
+
if (p && this.dragId && this.dragMode && this.dragDate)
|
|
1231
|
+
this.applyDrag(p.x, p.y);
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
applyDrag(clientX, clientY) {
|
|
1235
|
+
var _a;
|
|
1236
|
+
if (!this.dragId || !this.dragMode || !this.dragDate)
|
|
1237
|
+
return;
|
|
1238
|
+
if (!this.gridRect)
|
|
1239
|
+
return;
|
|
1240
|
+
const ptr = this.minutesFromCachedY(clientY);
|
|
1241
|
+
if (this.dragMode === 'move') {
|
|
1242
|
+
const targetDay = (_a = this.dateKeyAtX(clientX)) !== null && _a !== void 0 ? _a : this.dragDate;
|
|
1243
|
+
let newStart = ptr - this.dragGrabOffsetMin;
|
|
1244
|
+
newStart = time.clampMinutes(time.snapMinutes(newStart, this.snapMinutes), this.minM, this.maxM - this.dragDurationMin);
|
|
1245
|
+
this.dragDate = targetDay;
|
|
1246
|
+
this.dragStartMin = newStart;
|
|
1247
|
+
this.dragEndMin = newStart + this.dragDurationMin;
|
|
1248
|
+
}
|
|
1249
|
+
else {
|
|
1250
|
+
this.dragEndMin = Math.max(this.dragStartMin + this.slotMinutes, ptr);
|
|
1251
|
+
}
|
|
1252
|
+
this.paintGhost();
|
|
1253
|
+
}
|
|
1254
|
+
finishDrag() {
|
|
1255
|
+
// pointerup habiendo solo ARMADO (sin cruzar el umbral) = fue un click, no un
|
|
1256
|
+
// drag: desarmar en silencio y dejar pasar el click nativo → abre el popover.
|
|
1257
|
+
if (this.dragArmedId && !this.dragId) {
|
|
1258
|
+
this.dragArmedId = null;
|
|
1259
|
+
this.dragStartPointer = null;
|
|
1260
|
+
this.dragMode = null;
|
|
1261
|
+
this.dragDate = null;
|
|
1262
|
+
this.origDate = null;
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
if (!this.dragId)
|
|
1266
|
+
return; // pointerup sin drag activo (el listener es global)
|
|
1267
|
+
// Flush del frame pendiente (mismo motivo que en finishSelection).
|
|
1268
|
+
if (this.dragRaf) {
|
|
1269
|
+
cancelAnimationFrame(this.dragRaf);
|
|
1270
|
+
this.dragRaf = 0;
|
|
1271
|
+
}
|
|
1272
|
+
const pending = this.pendingDrag;
|
|
1273
|
+
this.pendingDrag = null;
|
|
1274
|
+
if (pending && this.dragMode && this.dragDate)
|
|
1275
|
+
this.applyDrag(pending.x, pending.y);
|
|
1276
|
+
this.setInteracting(false);
|
|
1277
|
+
this.bindDragViewportListeners(false);
|
|
1278
|
+
this.hideGhost();
|
|
1279
|
+
this.colRects = [];
|
|
1280
|
+
this.gridRect = null;
|
|
1281
|
+
const id = this.dragId;
|
|
1282
|
+
const mode = this.dragMode;
|
|
1283
|
+
const date = this.dragDate;
|
|
1284
|
+
const newStartMin = this.dragStartMin;
|
|
1285
|
+
const newEndMin = this.dragEndMin;
|
|
1286
|
+
const oStartMin = this.origStartMin;
|
|
1287
|
+
const oEndMin = this.origEndMin;
|
|
1288
|
+
const oDate = this.origDate;
|
|
1289
|
+
this.dragId = null;
|
|
1290
|
+
this.dragMode = null;
|
|
1291
|
+
this.dragDate = null;
|
|
1292
|
+
this.origDate = null;
|
|
1293
|
+
this.dragStartPointer = null;
|
|
1294
|
+
if (!id || !mode || !date)
|
|
1295
|
+
return;
|
|
1296
|
+
const ev = this.eventById(id);
|
|
1297
|
+
if (!ev)
|
|
1298
|
+
return;
|
|
1299
|
+
// No-op detection by minutes/day (avoids ISO-canonicalization false negatives).
|
|
1300
|
+
const changed = mode === 'move'
|
|
1301
|
+
? newStartMin !== oStartMin || date !== oDate
|
|
1302
|
+
: newEndMin !== oEndMin;
|
|
1303
|
+
if (!changed)
|
|
1304
|
+
return;
|
|
1305
|
+
const start = mode === 'move' ? this.toInstant(date, newStartMin) : ev.start;
|
|
1306
|
+
const end = this.toInstant(date, newEndMin);
|
|
1307
|
+
// Past-block on the edge actually being changed (move→start, resize→end).
|
|
1308
|
+
const pastRef = mode === 'move' ? start : end;
|
|
1309
|
+
if (new Date(pastRef).getTime() < Date.now())
|
|
1310
|
+
return;
|
|
1311
|
+
if (!this.isDayInteractable(date))
|
|
1312
|
+
return;
|
|
1313
|
+
this.suppressNextClick = true;
|
|
1314
|
+
// Clear on the next macrotask: a same-target synthetic click fires before this
|
|
1315
|
+
// (and is suppressed); a later unrelated click is not.
|
|
1316
|
+
setTimeout(() => { this.suppressNextClick = false; }, 0);
|
|
1317
|
+
this.coEventDrop.emit({
|
|
1318
|
+
id,
|
|
1319
|
+
start,
|
|
1320
|
+
end,
|
|
1321
|
+
oldStart: ev.start,
|
|
1322
|
+
oldEnd: ev.end,
|
|
1323
|
+
allDay: false,
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
shiftFocusDate(dir) {
|
|
1327
|
+
const idx = this.dates.indexOf(this.focusDate);
|
|
1328
|
+
const next = idx + dir;
|
|
1329
|
+
if (next < 0 || next >= this.dates.length)
|
|
1330
|
+
return this.focusDate;
|
|
1331
|
+
return this.dates[next];
|
|
1332
|
+
}
|
|
1333
|
+
renderSelectionGhost() {
|
|
1334
|
+
const a = Math.min(this.selStartMin, this.selEndMin);
|
|
1335
|
+
const b = Math.max(this.selStartMin, this.selEndMin);
|
|
1336
|
+
const top = time.minutesToFraction(a, this.minM, this.maxM);
|
|
1337
|
+
const bottom = time.minutesToFraction(Math.max(b, a + this.snapMinutes), this.minM, this.maxM);
|
|
1338
|
+
return (index.h("div", { class: "tg__selection", style: { top: `${top * 100}%`, height: `${(bottom - top) * 100}%` } }));
|
|
1339
|
+
}
|
|
1340
|
+
renderEventBox(box) {
|
|
1341
|
+
var _a;
|
|
1342
|
+
const ev = box.event;
|
|
1343
|
+
const isPast = ev.endMs <= Date.now();
|
|
1344
|
+
const isExternal = ((_a = ev.extendedProps) === null || _a === void 0 ? void 0 : _a.isExternal) === true;
|
|
1345
|
+
const locked = !this.editable || ev.editable === false;
|
|
1346
|
+
const widthPct = 100 / box.columnCount;
|
|
1347
|
+
const leftPct = box.columnIndex * widthPct;
|
|
1348
|
+
const style = {
|
|
1349
|
+
top: `${box.topFraction * 100}%`,
|
|
1350
|
+
height: `${box.heightFraction * 100}%`,
|
|
1351
|
+
left: `${leftPct}%`,
|
|
1352
|
+
width: `calc(${widthPct}% - 2px)`,
|
|
1353
|
+
};
|
|
1354
|
+
if (ev.backgroundColor) {
|
|
1355
|
+
style['--co-cal-event-bg'] = ev.backgroundColor;
|
|
1356
|
+
const textToken = readableTextToken(this.rootEl, ev.backgroundColor);
|
|
1357
|
+
if (textToken)
|
|
1358
|
+
style['--co-cal-event-text'] = textToken;
|
|
1359
|
+
}
|
|
1360
|
+
return (index.h("button", { type: "button", key: `${ev.id}-${box.dateKey}`, "data-event-id": ev.id, "data-date": box.dateKey, class: {
|
|
1361
|
+
tg__event: true,
|
|
1362
|
+
'tg__event--past': isPast,
|
|
1363
|
+
'tg__event--external': isExternal,
|
|
1364
|
+
'tg__event--locked': locked,
|
|
1365
|
+
'tg__event--dragging': this.dragId === ev.id,
|
|
1366
|
+
'tg__event--tentative': ev.responseState === 'tentative',
|
|
1367
|
+
'tg__event--cancelled': ev.responseState === 'cancelled' || ev.responseState === 'declined',
|
|
1368
|
+
}, style: style, title: ev.title, "aria-label": ev.title, onClick: (e) => {
|
|
1369
|
+
e.stopPropagation();
|
|
1370
|
+
if (this.suppressNextClick) {
|
|
1371
|
+
this.suppressNextClick = false;
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
this.coEventClick.emit({ id: ev.id, rect: e.currentTarget.getBoundingClientRect() });
|
|
1375
|
+
}, onDblClick: (e) => {
|
|
1376
|
+
e.stopPropagation();
|
|
1377
|
+
this.coEventDblClick.emit({ id: ev.id, rect: e.currentTarget.getBoundingClientRect() });
|
|
1378
|
+
}, onPointerDown: (e) => {
|
|
1379
|
+
if (!locked)
|
|
1380
|
+
this.startMove(e, ev.id);
|
|
1381
|
+
} }, box.isStart && (index.h("span", { class: "tg__event-time" }, minutesToLabel(box.startMinutes, this.locale))), index.h("span", { class: "tg__event-title" }, ev.title || '—'), isExternal && (providerBrand(ev)
|
|
1382
|
+
? index.h("co-provider-logo", { provider: providerBrand(ev), size: 11, plated: true, class: "tg__event-ext" })
|
|
1383
|
+
: index.h("co-icon", { name: "link", size: 11, class: "tg__event-ext" })), box.isEnd && !locked && ev.durationEditable !== false && (index.h("span", { class: "tg__event-resize", onPointerDown: (e) => this.startResize(e, ev.id) }))));
|
|
1384
|
+
}
|
|
1385
|
+
renderAllDayBar(seg, cols, byId) {
|
|
1386
|
+
const ev = byId.get(seg.eventId);
|
|
1387
|
+
if (!ev)
|
|
1388
|
+
return null;
|
|
1389
|
+
const left = (seg.startCol / cols) * 100;
|
|
1390
|
+
const width = ((seg.endCol - seg.startCol + 1) / cols) * 100;
|
|
1391
|
+
const style = {
|
|
1392
|
+
left: `${left}%`,
|
|
1393
|
+
width: `${width}%`,
|
|
1394
|
+
top: `${seg.lane * ALLDAY_LANE_H}px`,
|
|
1395
|
+
};
|
|
1396
|
+
if (ev.backgroundColor) {
|
|
1397
|
+
style['--co-cal-event-bg'] = ev.backgroundColor;
|
|
1398
|
+
const textToken = readableTextToken(this.rootEl, ev.backgroundColor);
|
|
1399
|
+
if (textToken)
|
|
1400
|
+
style['--co-cal-event-text'] = textToken;
|
|
1401
|
+
}
|
|
1402
|
+
return (index.h("button", { type: "button", key: ev.id, "data-event-id": ev.id, class: {
|
|
1403
|
+
'tg__allday-bar': true,
|
|
1404
|
+
'tg__allday-bar--continues-before': seg.continuesBefore,
|
|
1405
|
+
'tg__allday-bar--continues-after': seg.continuesAfter,
|
|
1406
|
+
'tg__allday-bar--tentative': ev.responseState === 'tentative',
|
|
1407
|
+
'tg__allday-bar--cancelled': ev.responseState === 'cancelled' || ev.responseState === 'declined',
|
|
1408
|
+
}, style: style, title: ev.title, onClick: (e) => {
|
|
1409
|
+
e.stopPropagation();
|
|
1410
|
+
this.coEventClick.emit({ id: ev.id, rect: e.currentTarget.getBoundingClientRect() });
|
|
1411
|
+
}, onDblClick: (e) => {
|
|
1412
|
+
e.stopPropagation();
|
|
1413
|
+
this.coEventDblClick.emit({ id: ev.id, rect: e.currentTarget.getBoundingClientRect() });
|
|
1414
|
+
} }, ev.title || '—'));
|
|
1415
|
+
}
|
|
1416
|
+
/** Los 3 cómputos de layout del render, memoizados por IDENTIDAD de inputs
|
|
1417
|
+
* (`events` llega memoizado del shell): un re-render por drag/now-tick/focus
|
|
1418
|
+
* no vuelve a empaquetar la semana. */
|
|
1419
|
+
getLayouts(dates) {
|
|
1420
|
+
const datesKey = dates.join(',');
|
|
1421
|
+
const c = this.layoutCache;
|
|
1422
|
+
if (c &&
|
|
1423
|
+
c.events === this.events &&
|
|
1424
|
+
c.datesKey === datesKey &&
|
|
1425
|
+
c.tz === this.timeZone &&
|
|
1426
|
+
c.minM === this.minM &&
|
|
1427
|
+
c.maxM === this.maxM) {
|
|
1428
|
+
return c;
|
|
1429
|
+
}
|
|
1430
|
+
const layout = layoutTimeGrid(this.events, dates, {
|
|
1431
|
+
timeZone: this.timeZone,
|
|
1432
|
+
minMinutes: this.minM,
|
|
1433
|
+
maxMinutes: this.maxM,
|
|
1434
|
+
});
|
|
1435
|
+
const allDay = layoutAllDay(this.events, dates, { timeZone: this.timeZone });
|
|
1436
|
+
const byId = new Map(this.events.map((e) => [e.id, e]));
|
|
1437
|
+
this.layoutCache = {
|
|
1438
|
+
events: this.events,
|
|
1439
|
+
datesKey,
|
|
1440
|
+
tz: this.timeZone,
|
|
1441
|
+
minM: this.minM,
|
|
1442
|
+
maxM: this.maxM,
|
|
1443
|
+
layout,
|
|
1444
|
+
allDay,
|
|
1445
|
+
byId,
|
|
1446
|
+
};
|
|
1447
|
+
return this.layoutCache;
|
|
1448
|
+
}
|
|
1449
|
+
render() {
|
|
1450
|
+
const dates$1 = this.visibleDates();
|
|
1451
|
+
const { layout, allDay, byId } = this.getLayouts(dates$1);
|
|
1452
|
+
const short = dates.getWeekdayLabels(this.locale, 0, 'short');
|
|
1453
|
+
const colsTemplate = `repeat(${dates$1.length}, 1fr)`;
|
|
1454
|
+
const today = dates.todayYMD();
|
|
1455
|
+
// La grilla ya no se dibuja con divs por slot: la altura es explícita y las
|
|
1456
|
+
// líneas de hora salen de un background repetido (ver .tg__col en el CSS).
|
|
1457
|
+
const bodyHeight = `calc(${this.slotCount()} * var(--co-cal-slot-h))`;
|
|
1458
|
+
const hourVars = {
|
|
1459
|
+
'--co-cal-hour-h': `calc(${60 / this.slotM} * var(--co-cal-slot-h))`,
|
|
1460
|
+
'--co-cal-hour-offset': `calc(${((60 - (this.minM % 60)) % 60) / this.slotM} * var(--co-cal-slot-h))`,
|
|
1461
|
+
};
|
|
1462
|
+
return (index.h("div", { key: '57f7c14647a8dbca49f999bfc256cf4dd58c6d5c', class: "tg", tabIndex: 0, onKeyDown: this.handleKeyDown, ref: (el) => (this.rootEl = el) }, index.h("div", { key: 'f50ac115f074e8749c19d57d47b3bb3486ae68c1', class: "tg__head" }, index.h("div", { key: 'adf1e34eae02f86ae7eb3721374660ff4c1e4fe8', class: "tg__head-spacer" }), index.h("div", { key: '5907deb540459a618374a1eb42736ddd3f3d23f6', class: "tg__head-days", style: { gridTemplateColumns: colsTemplate } }, dates$1.map((d) => {
|
|
1463
|
+
const ymd = dates.parseISO(d);
|
|
1464
|
+
const dow = utcDayOfWeek(d);
|
|
1465
|
+
return (index.h("div", { class: { 'tg__head-day': true, 'tg__head-day--today': dates.compareYMD(d, today) === 0 } }, index.h("span", { class: "tg__head-dow" }, short[dow]), index.h("span", { class: "tg__head-num" }, ymd.day)));
|
|
1466
|
+
}))), this.allDaySlot && (index.h("div", { key: 'e517b7a6418d50c418d5d799d0bc623b5946cafd', class: "tg__allday" }, index.h("div", { key: 'b470f6f08482c15459f05a95508c66f0e2244860', class: "tg__allday-axis" }, gmtLabel(this.timeZone, dates$1[0])), index.h("div", { key: '50f2a093550f7320b602cc81dcfd1d1438071539', class: "tg__allday-lane", style: { height: `${Math.max(1, allDay.laneCount) * ALLDAY_LANE_H + 4}px` } }, allDay.segments.map((seg) => this.renderAllDayBar(seg, dates$1.length, byId))))), index.h("div", { key: 'fc2f4ad9bf8ab22930907c6ca2f7239ed4a57659', class: "tg__scroll", ref: (el) => (this.scrollEl = el) }, index.h("div", { key: 'db4a5798f1d587485b31884ebaa2ed853417c031', class: "tg__body" }, index.h("div", { key: '168e7a54caee97f7219a26d46e6b42b51ac86f88', class: "tg__axis", style: { height: bodyHeight } }, this.hourList().map((m) => (index.h("span", { class: "tg__axis-hour", style: { top: `${time.minutesToFraction(m, this.minM, this.maxM) * 100}%` } }, hourLabel(m, this.locale))))), index.h("div", { key: 'e0f1a5558b07db8abb5586d01cdf3f30eb5f0e3f', class: "tg__cols", style: Object.assign({ gridTemplateColumns: colsTemplate, height: bodyHeight }, hourVars) }, dates$1.map((d, i) => {
|
|
1467
|
+
var _a;
|
|
1468
|
+
const isToday = dates.compareYMD(d, today) === 0;
|
|
1469
|
+
return (index.h("div", { class: { tg__col: true, 'tg__col--today': isToday }, "data-col-date": d, onPointerDown: (e) => this.startSelection(e, d, e.currentTarget), onPointerMove: (e) => this.extendSelection(e, e.currentTarget) }, this.loading
|
|
1470
|
+
? SKELETON_BOXES[i % SKELETON_BOXES.length].map(([top, height]) => (index.h("div", { class: "tg__event-skeleton", "aria-hidden": "true", style: { top: `${top}%`, height: `${height}%` } }, index.h("co-skeleton", { variant: "rect", width: "100%", height: "100%" }))))
|
|
1471
|
+
: ((_a = layout.timed[d]) !== null && _a !== void 0 ? _a : []).map((box) => this.renderEventBox(box)), this.selDate === d && this.renderSelectionGhost(), this.focusDate === d && (index.h("div", { class: "tg__focus", style: {
|
|
1472
|
+
top: `${time.minutesToFraction(this.focusMin, this.minM, this.maxM) * 100}%`,
|
|
1473
|
+
height: `${(time.minutesToFraction(this.focusMin + this.slotMinutes, this.minM, this.maxM) - time.minutesToFraction(this.focusMin, this.minM, this.maxM)) * 100}%`,
|
|
1474
|
+
} })), this.nowIndicator && this.nowDay === d && this.nowMinutes >= this.minM && this.nowMinutes < this.maxM && (index.h("div", { class: "tg__now", style: { top: `${time.minutesToFraction(this.nowMinutes, this.minM, this.maxM) * 100}%` } }))));
|
|
1475
|
+
}), index.h("div", { class: "tg__drag-ghost", key: "tg-drag-ghost", ref: (el) => (this.ghostEl = el) }))))));
|
|
1476
|
+
}
|
|
1477
|
+
get el() { return index.getElement(this); }
|
|
1478
|
+
static get watchers() { return {
|
|
1479
|
+
"events": [{
|
|
1480
|
+
"onEventsChange": 0
|
|
1481
|
+
}]
|
|
1482
|
+
}; }
|
|
1483
|
+
};
|
|
1484
|
+
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
1485
|
+
function hhmmToMinutes(hhmm, fallback) {
|
|
1486
|
+
const m = /^(\d{1,2}):(\d{2})$/.exec(hhmm);
|
|
1487
|
+
if (!m)
|
|
1488
|
+
return fallback;
|
|
1489
|
+
return Number(m[1]) * 60 + Number(m[2]);
|
|
1490
|
+
}
|
|
1491
|
+
/** UTC day-of-week (0=Sun) of a date-only key — tz-independent. */
|
|
1492
|
+
function utcDayOfWeek(dateKey) {
|
|
1493
|
+
const [y, mo, d] = dateKey.split('-').map(Number);
|
|
1494
|
+
return new Date(Date.UTC(y, mo - 1, d)).getUTCDay();
|
|
1495
|
+
}
|
|
1496
|
+
// Intl.DateTimeFormat es caro de construir y estos tres labels se recalculan por
|
|
1497
|
+
// frame sobre un dominio ACOTADO (≤96 minutos distintos, ≤24 horas, 1-2 locales):
|
|
1498
|
+
// cacheamos el RESULTADO (y el formatter, para el miss). Higiene: clear a las 2000
|
|
1499
|
+
// entradas — inalcanzable en uso normal, solo protege de iteración patológica.
|
|
1500
|
+
const MINUTE_LABEL_CACHE = new Map();
|
|
1501
|
+
const MINUTE_FMT_CACHE = new Map();
|
|
1502
|
+
const HOUR_LABEL_CACHE = new Map();
|
|
1503
|
+
const HOUR_FMT_CACHE = new Map();
|
|
1504
|
+
const GMT_LABEL_CACHE = new Map();
|
|
1505
|
+
function minutesToLabel(minutes, locale) {
|
|
1506
|
+
const key = `${locale}|${minutes}`;
|
|
1507
|
+
const hit = MINUTE_LABEL_CACHE.get(key);
|
|
1508
|
+
if (hit !== undefined)
|
|
1509
|
+
return hit;
|
|
1510
|
+
let fmt = MINUTE_FMT_CACHE.get(locale);
|
|
1511
|
+
if (!fmt) {
|
|
1512
|
+
fmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', minute: '2-digit' });
|
|
1513
|
+
MINUTE_FMT_CACHE.set(locale, fmt);
|
|
1514
|
+
}
|
|
1515
|
+
const h = Math.floor(minutes / 60);
|
|
1516
|
+
const m = minutes % 60;
|
|
1517
|
+
const out = fmt.format(new Date(2000, 0, 1, h, m));
|
|
1518
|
+
if (MINUTE_LABEL_CACHE.size > 2000)
|
|
1519
|
+
MINUTE_LABEL_CACHE.clear();
|
|
1520
|
+
MINUTE_LABEL_CACHE.set(key, out);
|
|
1521
|
+
return out;
|
|
1522
|
+
}
|
|
1523
|
+
/** Bloques del skeleton por columna, `[top%, alto%]` sobre el alto del día.
|
|
1524
|
+
* Patrón FIJO (jamás random: tiene que verse igual entre renders) y desparejo
|
|
1525
|
+
* entre columnas, como una agenda de verdad. */
|
|
1526
|
+
const SKELETON_BOXES = [
|
|
1527
|
+
[[26, 8], [42, 12]],
|
|
1528
|
+
[[30, 14]],
|
|
1529
|
+
[[24, 6], [38, 9], [56, 7]],
|
|
1530
|
+
[[34, 11], [52, 6]],
|
|
1531
|
+
[[28, 9]],
|
|
1532
|
+
[[40, 13], [60, 8]],
|
|
1533
|
+
[[32, 7], [46, 10]],
|
|
1534
|
+
];
|
|
1535
|
+
/** Marca del evento externo (`co-provider-logo`), o '' si no se reconoce — el
|
|
1536
|
+
* llamador cae al ícono genérico de enlace. */
|
|
1537
|
+
function providerBrand(ev) {
|
|
1538
|
+
var _a, _b;
|
|
1539
|
+
const p = String((_b = (_a = ev.extendedProps) === null || _a === void 0 ? void 0 : _a.provider) !== null && _b !== void 0 ? _b : '').toLowerCase();
|
|
1540
|
+
if (p.includes('google'))
|
|
1541
|
+
return 'google';
|
|
1542
|
+
if (p.includes('microsoft') || p.includes('outlook'))
|
|
1543
|
+
return 'microsoft';
|
|
1544
|
+
return '';
|
|
1545
|
+
}
|
|
1546
|
+
/** Hour-only 12h label for the time axis, e.g. "10 a. m." / "1 p. m." (Google style).
|
|
1547
|
+
* hour12 forzado: locales 24h (p.ej. "es" pelado) darían números sueltos sin am/pm. */
|
|
1548
|
+
function hourLabel(minutes, locale) {
|
|
1549
|
+
const key = `${locale}|${minutes}`;
|
|
1550
|
+
const hit = HOUR_LABEL_CACHE.get(key);
|
|
1551
|
+
if (hit !== undefined)
|
|
1552
|
+
return hit;
|
|
1553
|
+
let fmt = HOUR_FMT_CACHE.get(locale);
|
|
1554
|
+
if (!fmt) {
|
|
1555
|
+
fmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true });
|
|
1556
|
+
HOUR_FMT_CACHE.set(locale, fmt);
|
|
1557
|
+
}
|
|
1558
|
+
const h = Math.floor(minutes / 60);
|
|
1559
|
+
const out = fmt.format(new Date(2000, 0, 1, h, 0));
|
|
1560
|
+
if (HOUR_LABEL_CACHE.size > 2000)
|
|
1561
|
+
HOUR_LABEL_CACHE.clear();
|
|
1562
|
+
HOUR_LABEL_CACHE.set(key, out);
|
|
1563
|
+
return out;
|
|
1564
|
+
}
|
|
1565
|
+
/** Timezone offset label for the axis corner, e.g. "GMT-05" (like Google Calendar). */
|
|
1566
|
+
function gmtLabel(timeZone, refYMD) {
|
|
1567
|
+
if (!timeZone)
|
|
1568
|
+
return '';
|
|
1569
|
+
const key = `${timeZone}|${refYMD !== null && refYMD !== void 0 ? refYMD : ''}`;
|
|
1570
|
+
const hit = GMT_LABEL_CACHE.get(key);
|
|
1571
|
+
if (hit !== undefined)
|
|
1572
|
+
return hit;
|
|
1573
|
+
const out = computeGmtLabel(timeZone, refYMD);
|
|
1574
|
+
if (GMT_LABEL_CACHE.size > 2000)
|
|
1575
|
+
GMT_LABEL_CACHE.clear();
|
|
1576
|
+
GMT_LABEL_CACHE.set(key, out);
|
|
1577
|
+
return out;
|
|
1578
|
+
}
|
|
1579
|
+
function computeGmtLabel(timeZone, refYMD) {
|
|
1580
|
+
var _a, _b;
|
|
1581
|
+
try {
|
|
1582
|
+
const d = new Date(`${refYMD !== null && refYMD !== void 0 ? refYMD : '2026-01-01'}T12:00:00Z`);
|
|
1583
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
1584
|
+
timeZone,
|
|
1585
|
+
timeZoneName: 'shortOffset',
|
|
1586
|
+
}).formatToParts(d);
|
|
1587
|
+
const raw = (_b = (_a = parts.find((p) => p.type === 'timeZoneName')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '';
|
|
1588
|
+
const m = raw.match(/GMT([+-])(\d{1,2})(?::(\d{2}))?/);
|
|
1589
|
+
if (!m)
|
|
1590
|
+
return raw;
|
|
1591
|
+
const hh = m[2].padStart(2, '0');
|
|
1592
|
+
return m[3] ? `GMT${m[1]}${hh}:${m[3]}` : `GMT${m[1]}${hh}`;
|
|
1593
|
+
}
|
|
1594
|
+
catch (_c) {
|
|
1595
|
+
return '';
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
CoCalendarTimegrid.style = coCalendarTimegridCss();
|
|
1599
|
+
|
|
1600
|
+
exports.co_calendar_month = CoCalendarMonth;
|
|
1601
|
+
exports.co_calendar_timegrid = CoCalendarTimegrid;
|