@siemens/element-ng 47.2.0 → 47.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/autocomplete/index.d.ts +8 -0
- package/autocomplete/package.json +3 -0
- package/autocomplete/si-autocomplete-listbox.directive.d.ts +31 -0
- package/autocomplete/si-autocomplete-option.directive.d.ts +31 -0
- package/autocomplete/si-autocomplete.directive.d.ts +14 -0
- package/autocomplete/si-autocomplete.model.d.ts +7 -0
- package/autocomplete/si-autocomplete.module.d.ts +9 -0
- package/badge/index.d.ts +5 -0
- package/badge/package.json +3 -0
- package/badge/si-badge.component.d.ts +17 -0
- package/breadcrumb/breadcrumb-item.model.d.ts +36 -0
- package/breadcrumb/index.d.ts +7 -0
- package/breadcrumb/package.json +3 -0
- package/breadcrumb/si-breadcrumb-item-template.directive.d.ts +10 -0
- package/breadcrumb/si-breadcrumb.component.d.ts +46 -0
- package/breadcrumb/si-breadcrumb.module.d.ts +7 -0
- package/card/index.d.ts +6 -0
- package/card/package.json +3 -0
- package/card/si-card.component.d.ts +79 -0
- package/card/si-card.module.d.ts +7 -0
- package/circle-status/index.d.ts +6 -0
- package/circle-status/package.json +3 -0
- package/circle-status/si-circle-status.component.d.ts +66 -0
- package/circle-status/si-circle-status.module.d.ts +7 -0
- package/column-selection-dialog/column-selection-editor/si-column-selection-editor.component.d.ts +23 -0
- package/column-selection-dialog/index.d.ts +6 -0
- package/column-selection-dialog/package.json +3 -0
- package/column-selection-dialog/si-column-selection-dialog.component.d.ts +114 -0
- package/column-selection-dialog/si-column-selection-dialog.service.d.ts +20 -0
- package/column-selection-dialog/si-column-selection-dialog.types.d.ts +68 -0
- package/common/models/status-type.model.d.ts +2 -2
- package/content-action-bar/index.d.ts +7 -0
- package/content-action-bar/package.json +3 -0
- package/content-action-bar/si-content-action-bar-toggle.component.d.ts +6 -0
- package/content-action-bar/si-content-action-bar.component.d.ts +72 -0
- package/content-action-bar/si-content-action-bar.model.d.ts +9 -0
- package/content-action-bar/si-content-action-bar.module.d.ts +7 -0
- package/date-range-filter/index.d.ts +8 -0
- package/date-range-filter/package.json +3 -0
- package/date-range-filter/si-date-range-calculation.service.d.ts +33 -0
- package/date-range-filter/si-date-range-filter.component.d.ts +248 -0
- package/date-range-filter/si-date-range-filter.module.d.ts +7 -0
- package/date-range-filter/si-date-range-filter.types.d.ts +40 -0
- package/date-range-filter/si-relative-date.component.d.ts +31 -0
- package/datepicker/components/si-calendar-body.component.d.ts +137 -0
- package/datepicker/components/si-calendar-date-cell.directive.d.ts +16 -0
- package/datepicker/components/si-calendar-direction-button.component.d.ts +18 -0
- package/datepicker/components/si-compare-adapter.d.ts +37 -0
- package/datepicker/components/si-day-selection.component.d.ts +76 -0
- package/datepicker/components/si-initial-focus.component.d.ts +74 -0
- package/datepicker/components/si-month-selection.component.d.ts +62 -0
- package/datepicker/components/si-year-selection.component.d.ts +65 -0
- package/datepicker/date-time-helper.d.ts +302 -0
- package/datepicker/index.d.ts +15 -0
- package/datepicker/package.json +3 -0
- package/datepicker/si-calendar-button.component.d.ts +49 -0
- package/datepicker/si-date-input.directive.d.ts +114 -0
- package/datepicker/si-date-range.component.d.ts +150 -0
- package/datepicker/si-datepicker-overlay.component.d.ts +82 -0
- package/datepicker/si-datepicker-overlay.directive.d.ts +104 -0
- package/datepicker/si-datepicker.component.d.ts +228 -0
- package/datepicker/si-datepicker.directive.d.ts +62 -0
- package/datepicker/si-datepicker.model.d.ts +129 -0
- package/datepicker/si-datepicker.module.d.ts +12 -0
- package/datepicker/si-timepicker.component.d.ts +214 -0
- package/electron-titlebar/electron.helpers.d.ts +5 -0
- package/electron-titlebar/index.d.ts +7 -0
- package/electron-titlebar/package.json +3 -0
- package/electron-titlebar/si-electron-titlebar.component.d.ts +72 -0
- package/electron-titlebar/si-electron-titlebar.module.d.ts +7 -0
- package/fesm2022/siemens-element-ng-application-header.mjs +2 -2
- package/fesm2022/siemens-element-ng-application-header.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-autocomplete.mjs +235 -0
- package/fesm2022/siemens-element-ng-autocomplete.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-badge.mjs +59 -0
- package/fesm2022/siemens-element-ng-badge.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-breadcrumb.mjs +302 -0
- package/fesm2022/siemens-element-ng-breadcrumb.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-card.mjs +122 -0
- package/fesm2022/siemens-element-ng-card.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-circle-status.mjs +146 -0
- package/fesm2022/siemens-element-ng-circle-status.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-column-selection-dialog.mjs +369 -0
- package/fesm2022/siemens-element-ng-column-selection-dialog.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-common.mjs +1 -1
- package/fesm2022/siemens-element-ng-common.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-content-action-bar.mjs +200 -0
- package/fesm2022/siemens-element-ng-content-action-bar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-date-range-filter.mjs +649 -0
- package/fesm2022/siemens-element-ng-date-range-filter.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-datepicker.mjs +4231 -0
- package/fesm2022/siemens-element-ng-datepicker.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-electron-titlebar.mjs +142 -0
- package/fesm2022/siemens-element-ng-electron-titlebar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-file-uploader.mjs +751 -0
- package/fesm2022/siemens-element-ng-file-uploader.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-filter-bar.mjs +153 -0
- package/fesm2022/siemens-element-ng-filter-bar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-form.mjs +827 -0
- package/fesm2022/siemens-element-ng-form.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-icon-status.mjs +65 -0
- package/fesm2022/siemens-element-ng-icon-status.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-icon.mjs +22 -2
- package/fesm2022/siemens-element-ng-icon.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-info-page.mjs +63 -0
- package/fesm2022/siemens-element-ng-info-page.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-inline-notification.mjs +4 -6
- package/fesm2022/siemens-element-ng-inline-notification.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-ip-input.mjs +451 -0
- package/fesm2022/siemens-element-ng-ip-input.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-language-switcher.mjs +90 -0
- package/fesm2022/siemens-element-ng-language-switcher.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-localization.mjs +306 -0
- package/fesm2022/siemens-element-ng-localization.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-number-input.mjs +267 -0
- package/fesm2022/siemens-element-ng-number-input.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-password-strength.mjs +177 -0
- package/fesm2022/siemens-element-ng-password-strength.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-photo-upload.mjs +480 -0
- package/fesm2022/siemens-element-ng-photo-upload.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-pills-input.mjs +397 -0
- package/fesm2022/siemens-element-ng-pills-input.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-popover-next.mjs +259 -0
- package/fesm2022/siemens-element-ng-popover-next.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-popover.mjs +256 -0
- package/fesm2022/siemens-element-ng-popover.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-progressbar.mjs +83 -0
- package/fesm2022/siemens-element-ng-progressbar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-search-bar.mjs +193 -0
- package/fesm2022/siemens-element-ng-search-bar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-select.mjs +1166 -0
- package/fesm2022/siemens-element-ng-select.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-skip-links.mjs +117 -0
- package/fesm2022/siemens-element-ng-skip-links.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-slider.mjs +313 -0
- package/fesm2022/siemens-element-ng-slider.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-sort-bar.mjs +89 -0
- package/fesm2022/siemens-element-ng-sort-bar.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-split.mjs +575 -0
- package/fesm2022/siemens-element-ng-split.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-status-toggle.mjs +196 -0
- package/fesm2022/siemens-element-ng-status-toggle.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-summary-widget.mjs +77 -0
- package/fesm2022/siemens-element-ng-summary-widget.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-system-banner.mjs +47 -0
- package/fesm2022/siemens-element-ng-system-banner.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-tabs.mjs +395 -0
- package/fesm2022/siemens-element-ng-tabs.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-toast-notification.mjs +227 -0
- package/fesm2022/siemens-element-ng-toast-notification.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-translate.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-typeahead.mjs +746 -0
- package/fesm2022/siemens-element-ng-typeahead.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-unauthorized-page.mjs +76 -0
- package/fesm2022/siemens-element-ng-unauthorized-page.mjs.map +1 -0
- package/fesm2022/siemens-element-ng-wizard.mjs +465 -0
- package/fesm2022/siemens-element-ng-wizard.mjs.map +1 -0
- package/file-uploader/index.d.ts +8 -0
- package/file-uploader/package.json +3 -0
- package/file-uploader/si-file-dropzone.component.d.ts +106 -0
- package/file-uploader/si-file-uploader.component.d.ts +296 -0
- package/file-uploader/si-file-uploader.model.d.ts +12 -0
- package/file-uploader/si-file-uploader.module.d.ts +8 -0
- package/filter-bar/filter.d.ts +26 -0
- package/filter-bar/index.d.ts +8 -0
- package/filter-bar/package.json +3 -0
- package/filter-bar/si-filter-bar.component.d.ts +65 -0
- package/filter-bar/si-filter-bar.module.d.ts +7 -0
- package/filter-bar/si-filter-pill.component.d.ts +20 -0
- package/form/form-fieldset/si-form-fieldset.component.d.ts +40 -0
- package/form/index.d.ts +14 -0
- package/form/package.json +3 -0
- package/form/si-form-container/si-form-container.component.d.ts +155 -0
- package/form/si-form-item/si-form-field-native.control.d.ts +22 -0
- package/form/si-form-item/si-form-item.component.d.ts +90 -0
- package/form/si-form-item-control-input.directive.d.ts +18 -0
- package/form/si-form-item.control.d.ts +35 -0
- package/form/si-form-validation-error.model.d.ts +55 -0
- package/form/si-form-validation-error.provider.d.ts +11 -0
- package/form/si-form-validation-error.service.d.ts +42 -0
- package/form/si-form-validation-tooltip/si-form-validation-tooltip.component.d.ts +13 -0
- package/form/si-form-validation-tooltip/si-form-validation-tooltip.directive.d.ts +42 -0
- package/form/si-form.module.d.ts +25 -0
- package/icon/element-icons.d.ts +20 -0
- package/icon-status/index.d.ts +6 -0
- package/icon-status/package.json +3 -0
- package/icon-status/si-icon-status.component.d.ts +24 -0
- package/icon-status/si-icon-status.module.d.ts +7 -0
- package/info-page/index.d.ts +5 -0
- package/info-page/package.json +3 -0
- package/info-page/si-info-page.component.d.ts +38 -0
- package/inline-notification/si-inline-notification.component.d.ts +0 -2
- package/ip-input/address-utils.d.ts +28 -0
- package/ip-input/address-validators.d.ts +21 -0
- package/ip-input/index.d.ts +7 -0
- package/ip-input/package.json +3 -0
- package/ip-input/si-ip-input.directive.d.ts +53 -0
- package/ip-input/si-ip4-input.directive.d.ts +9 -0
- package/ip-input/si-ip6-input.directive.d.ts +10 -0
- package/language-switcher/index.d.ts +7 -0
- package/language-switcher/iso-language-value.d.ts +14 -0
- package/language-switcher/package.json +3 -0
- package/language-switcher/si-language-switcher.component.d.ts +32 -0
- package/language-switcher/si-language-switcher.module.d.ts +7 -0
- package/localization/index.d.ts +8 -0
- package/localization/package.json +3 -0
- package/localization/si-directionality.d.ts +41 -0
- package/localization/si-locale-id.d.ts +22 -0
- package/localization/si-locale-store.d.ts +16 -0
- package/localization/si-locale.service.d.ts +73 -0
- package/number-input/index.d.ts +6 -0
- package/number-input/package.json +3 -0
- package/number-input/si-number-input.component.d.ts +106 -0
- package/number-input/si-number-input.module.d.ts +7 -0
- package/package.json +163 -3
- package/password-strength/index.d.ts +7 -0
- package/password-strength/package.json +3 -0
- package/password-strength/si-password-strength.component.d.ts +25 -0
- package/password-strength/si-password-strength.directive.d.ts +54 -0
- package/password-strength/si-password-strength.module.d.ts +8 -0
- package/photo-upload/index.d.ts +6 -0
- package/photo-upload/package.json +3 -0
- package/photo-upload/si-image-cropper-style.component.d.ts +5 -0
- package/photo-upload/si-photo-upload.component.d.ts +298 -0
- package/pills-input/index.d.ts +9 -0
- package/pills-input/package.json +3 -0
- package/pills-input/si-input-pill.component.d.ts +9 -0
- package/pills-input/si-pills-input-csv.directive.d.ts +8 -0
- package/pills-input/si-pills-input-email.directive.d.ts +10 -0
- package/pills-input/si-pills-input-pattern-base.d.ts +19 -0
- package/pills-input/si-pills-input-value-handler.d.ts +12 -0
- package/pills-input/si-pills-input.component.d.ts +87 -0
- package/pills-input/si-pills-input.module.d.ts +9 -0
- package/popover/index.d.ts +6 -0
- package/popover/package.json +3 -0
- package/popover/si-popover.component.d.ts +26 -0
- package/popover/si-popover.directive.d.ts +89 -0
- package/popover/si-popover.module.d.ts +7 -0
- package/popover-next/index.d.ts +7 -0
- package/popover-next/package.json +3 -0
- package/popover-next/si-popover-description.directive.d.ts +7 -0
- package/popover-next/si-popover-next.directive.d.ts +61 -0
- package/popover-next/si-popover-title.directive.d.ts +7 -0
- package/popover-next/si-popover.component.d.ts +27 -0
- package/progressbar/index.d.ts +6 -0
- package/progressbar/package.json +3 -0
- package/progressbar/si-progressbar.component.d.ts +43 -0
- package/progressbar/si-progressbar.module.d.ts +7 -0
- package/search-bar/index.d.ts +6 -0
- package/search-bar/package.json +3 -0
- package/search-bar/si-search-bar.component.d.ts +87 -0
- package/search-bar/si-search-bar.module.d.ts +7 -0
- package/select/index.d.ts +18 -0
- package/select/options/si-select-complex-options.directive.d.ts +69 -0
- package/select/options/si-select-lazy-options.directive.d.ts +38 -0
- package/select/options/si-select-option.source.d.ts +49 -0
- package/select/options/si-select-options-strategy.base.d.ts +35 -0
- package/select/options/si-select-options-strategy.d.ts +37 -0
- package/select/options/si-select-simple-options.directive.d.ts +34 -0
- package/select/package.json +3 -0
- package/select/select-input/si-select-input.component.d.ts +43 -0
- package/select/select-list/si-select-list-has-filter.component.d.ts +20 -0
- package/select/select-list/si-select-list.base.d.ts +37 -0
- package/select/select-list/si-select-list.component.d.ts +15 -0
- package/select/select-option/si-select-option-row.component.d.ts +16 -0
- package/select/select-option/si-select-option.component.d.ts +9 -0
- package/select/selection/si-select-multi-value.directive.d.ts +26 -0
- package/select/selection/si-select-selection-strategy.d.ts +58 -0
- package/select/selection/si-select-single-value.directive.d.ts +26 -0
- package/select/si-select-action.directive.d.ts +12 -0
- package/select/si-select-actions.directive.d.ts +5 -0
- package/select/si-select-group-template.directive.d.ts +20 -0
- package/select/si-select-option-row-template.directive.d.ts +9 -0
- package/select/si-select-option-template.directive.d.ts +21 -0
- package/select/si-select.component.d.ts +96 -0
- package/select/si-select.module.d.ts +15 -0
- package/select/si-select.types.d.ts +65 -0
- package/skip-links/index.d.ts +5 -0
- package/skip-links/package.json +3 -0
- package/skip-links/si-skip-link-target.directive.d.ts +27 -0
- package/skip-links/si-skip-links.component.d.ts +9 -0
- package/skip-links/skip-link.service.d.ts +14 -0
- package/slider/index.d.ts +6 -0
- package/slider/package.json +3 -0
- package/slider/si-slider.component.d.ts +129 -0
- package/slider/si-slider.module.d.ts +7 -0
- package/sort-bar/index.d.ts +6 -0
- package/sort-bar/package.json +3 -0
- package/sort-bar/si-sort-bar.component.d.ts +42 -0
- package/sort-bar/si-sort-bar.module.d.ts +7 -0
- package/split/index.d.ts +8 -0
- package/split/package.json +3 -0
- package/split/si-split-part.component.d.ts +154 -0
- package/split/si-split.component.d.ts +48 -0
- package/split/si-split.interfaces.d.ts +17 -0
- package/split/si-split.module.d.ts +8 -0
- package/status-toggle/index.d.ts +6 -0
- package/status-toggle/package.json +3 -0
- package/status-toggle/si-status-toggle.component.d.ts +54 -0
- package/status-toggle/status-toggle.model.d.ts +26 -0
- package/summary-widget/index.d.ts +5 -0
- package/summary-widget/package.json +3 -0
- package/summary-widget/si-summary-widget.component.d.ts +44 -0
- package/system-banner/index.d.ts +5 -0
- package/system-banner/package.json +3 -0
- package/system-banner/system-banner.component.d.ts +23 -0
- package/tabs/index.d.ts +7 -0
- package/tabs/package.json +3 -0
- package/tabs/si-tab/index.d.ts +5 -0
- package/tabs/si-tab/si-tab.component.d.ts +58 -0
- package/tabs/si-tabs.module.d.ts +8 -0
- package/tabs/si-tabset/index.d.ts +5 -0
- package/tabs/si-tabset/si-tabset.component.d.ts +100 -0
- package/template-i18n.json +111 -1
- package/toast-notification/index.d.ts +6 -0
- package/toast-notification/package.json +3 -0
- package/toast-notification/si-toast-notification/si-toast-notification.component.d.ts +17 -0
- package/toast-notification/si-toast-notification-drawer/si-toast-notification-drawer.component.d.ts +9 -0
- package/toast-notification/si-toast-notification.service.d.ts +41 -0
- package/toast-notification/si-toast.model.d.ts +25 -0
- package/translate/si-translatable-keys.interface.d.ts +110 -0
- package/typeahead/index.d.ts +8 -0
- package/typeahead/package.json +3 -0
- package/typeahead/si-typeahead-item-template.directive.d.ts +7 -0
- package/typeahead/si-typeahead.component.d.ts +22 -0
- package/typeahead/si-typeahead.directive.d.ts +196 -0
- package/typeahead/si-typeahead.model.d.ts +60 -0
- package/typeahead/si-typeahead.module.d.ts +8 -0
- package/typeahead/si-typeahead.sorting.d.ts +10 -0
- package/unauthorized-page/index.d.ts +6 -0
- package/unauthorized-page/package.json +3 -0
- package/unauthorized-page/si-unauthorized-page.component.d.ts +35 -0
- package/unauthorized-page/si-unauthorized-page.module.d.ts +7 -0
- package/wizard/index.d.ts +7 -0
- package/wizard/package.json +3 -0
- package/wizard/si-wizard-step.component.d.ts +21 -0
- package/wizard/si-wizard.component.d.ts +196 -0
- package/wizard/si-wizard.module.d.ts +8 -0
|
@@ -0,0 +1,4231 @@
|
|
|
1
|
+
import { getLocaleDayPeriods, FormStyle, TranslationWidth, getLocaleId, getLocaleDateTimeFormat, FormatWidth, getLocaleTimeFormat, getLocaleDateFormat, formatDate, NgClass, DatePipe, NgTemplateOutlet, getLocaleFirstDayOfWeek, WeekDay, DOCUMENT } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { input, model, output, booleanAttribute, computed, signal, inject, LOCALE_ID, HostListener, Directive, ElementRef, viewChildren, Component, ChangeDetectionStrategy, viewChild, HostAttributeToken, ChangeDetectorRef, SimpleChange, DestroyRef, contentChild, NgModule } from '@angular/core';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
5
|
+
import { Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule, NgControl } from '@angular/forms';
|
|
6
|
+
import { SI_FORM_ITEM_CONTROL } from '@siemens/element-ng/form';
|
|
7
|
+
import { MediaMatcher, BreakpointObserver } from '@angular/cdk/layout';
|
|
8
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
9
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
10
|
+
import { takeUntilDestroyed, outputToObservable } from '@angular/core/rxjs-interop';
|
|
11
|
+
import { isRTL, getOverlayPositions, positionBottomCenter, positionBottomStart, positionBottomEnd, positionTopCenter, positionTopStart, positionTopEnd } from '@siemens/element-ng/common';
|
|
12
|
+
import { BOOTSTRAP_BREAKPOINTS } from '@siemens/element-ng/resize-observer';
|
|
13
|
+
import { Subject, merge } from 'rxjs';
|
|
14
|
+
import { takeUntil, skip, map, filter, tap } from 'rxjs/operators';
|
|
15
|
+
import * as i3 from '@angular/cdk/a11y';
|
|
16
|
+
import { A11yModule, FocusMonitor, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
|
17
|
+
import * as i2 from '@siemens/element-translate-ng/translate';
|
|
18
|
+
import { SiTranslateModule } from '@siemens/element-translate-ng/translate';
|
|
19
|
+
import { addIcons, elementRight2, elementLeft2, SiIconNextComponent, elementCalendar } from '@siemens/element-ng/icon';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Copyright Siemens 2016 - 2025.
|
|
23
|
+
* SPDX-License-Identifier: MIT
|
|
24
|
+
*/
|
|
25
|
+
const WEEK_ISO = [
|
|
26
|
+
{ id: 'monday', index: 1, offset: 0, isWeekend: false },
|
|
27
|
+
{ id: 'tuesday', index: 2, offset: 1, isWeekend: false },
|
|
28
|
+
{ id: 'wednesday', index: 3, offset: 2, isWeekend: false },
|
|
29
|
+
{ id: 'thursday', index: 4, offset: 3, isWeekend: false },
|
|
30
|
+
{ id: 'friday', index: 5, offset: 4, isWeekend: false },
|
|
31
|
+
{ id: 'saturday', index: 6, offset: 5, isWeekend: true },
|
|
32
|
+
{ id: 'sunday', index: 7, offset: 6, isWeekend: true }
|
|
33
|
+
];
|
|
34
|
+
const WEEK_START_OFFSET = {
|
|
35
|
+
'monday': 0,
|
|
36
|
+
'saturday': 5,
|
|
37
|
+
'sunday': 6
|
|
38
|
+
};
|
|
39
|
+
const UNITS = {
|
|
40
|
+
millisecondsPerDay: 86400000,
|
|
41
|
+
daysPerWeek: 7,
|
|
42
|
+
thursday: 4
|
|
43
|
+
};
|
|
44
|
+
const dayOfWeekMap = {};
|
|
45
|
+
const getDaysOfWeek = (weekStart) => {
|
|
46
|
+
weekStart = weekStart ?? 'monday';
|
|
47
|
+
let weekdays = dayOfWeekMap[weekStart];
|
|
48
|
+
if (!weekdays) {
|
|
49
|
+
dayOfWeekMap[weekStart] = weekdays = [];
|
|
50
|
+
const offset = WEEK_START_OFFSET[weekStart];
|
|
51
|
+
for (let i = 0; i < 7; i++) {
|
|
52
|
+
weekdays.push({ ...WEEK_ISO[(i + offset) % 7], offset: i });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return weekdays;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Get local specific months using DateTimeFormat.
|
|
59
|
+
* @param locale - current locale
|
|
60
|
+
*/
|
|
61
|
+
const getLocaleMonthNames = (locale) => {
|
|
62
|
+
const format = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' }).format;
|
|
63
|
+
return Array(12)
|
|
64
|
+
.fill(0)
|
|
65
|
+
.map((v, m) => format(new Date(Date.UTC(0, m))));
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Get local specific weekdays as string using DateTimeFormat.
|
|
69
|
+
* @param locale - current local
|
|
70
|
+
* @param weekStart - start of the week
|
|
71
|
+
* @param format - display format
|
|
72
|
+
* @returns array of week days.
|
|
73
|
+
*/
|
|
74
|
+
const getDayStrings = (locale, weekStart = 'monday', format = 'short') => {
|
|
75
|
+
const dateFormatter = new Intl.DateTimeFormat(locale, { weekday: format, timeZone: 'utc' });
|
|
76
|
+
const days = [];
|
|
77
|
+
// Get local specific day strings from sunday (0) .. saturady (6)
|
|
78
|
+
for (let index = 1; index <= 7; index++) {
|
|
79
|
+
const day = new Date(Date.UTC(2023, 0, index));
|
|
80
|
+
days.push(dateFormatter.format(day));
|
|
81
|
+
}
|
|
82
|
+
const map = { 'sunday': 0, 'monday': 1, 'saturday': 6 };
|
|
83
|
+
const index = map[weekStart];
|
|
84
|
+
return days.slice(index).concat(days.slice(0, index));
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Gets the first day in the specified month.
|
|
88
|
+
* Expects the month as a value between 1 and 12.
|
|
89
|
+
* The year is required to handle leap years.
|
|
90
|
+
*
|
|
91
|
+
* @returns The first day of the month as a Date.
|
|
92
|
+
*/
|
|
93
|
+
const getFirstDayInMonth = (year, month) => new Date(year, month - 1, 1);
|
|
94
|
+
/**
|
|
95
|
+
* Gets the week number of the specified date.
|
|
96
|
+
* Week number according to the ISO-8601 standard, weeks starting on Monday.
|
|
97
|
+
* The first week of a year is the week that contains the first Thursday of the year (='First 4-day week').
|
|
98
|
+
* The highest week number in a year is either 52 or 53.
|
|
99
|
+
*
|
|
100
|
+
* @param date -The JavaScript date object.
|
|
101
|
+
* @param weekStart -Name of the first day of the week
|
|
102
|
+
* @returns The number of the Week
|
|
103
|
+
*/
|
|
104
|
+
const getWeekOfYear = (date, weekStart) => {
|
|
105
|
+
// Algorithm rewritten from C# example given at http://en.wikipedia.org/wiki/Talk:ISO_week_date
|
|
106
|
+
const dayOfWeek = getWeekDayOffset(date, weekStart) + 1;
|
|
107
|
+
const nearestThu = new Date(date);
|
|
108
|
+
nearestThu.setDate(date.getDate() + (UNITS.thursday - dayOfWeek)); // get nearest Thursday (-3..+3 days)
|
|
109
|
+
const year = nearestThu.getFullYear();
|
|
110
|
+
const janfirst = getFirstDayInMonth(year, 1);
|
|
111
|
+
const days = Math.floor((nearestThu - janfirst) / UNITS.millisecondsPerDay);
|
|
112
|
+
const week = 1 + Math.floor(days / UNITS.daysPerWeek); // Count of Thursdays
|
|
113
|
+
return week;
|
|
114
|
+
};
|
|
115
|
+
const getWeekDayOffset = (date, weekStart) => {
|
|
116
|
+
const offset = WEEK_START_OFFSET[weekStart ?? 'monday'];
|
|
117
|
+
return (date.getDay() + 6 - offset) % 7;
|
|
118
|
+
};
|
|
119
|
+
/** returns the date string in format YYYY-MM-DD for given date */
|
|
120
|
+
const getStringforDate = (date) => {
|
|
121
|
+
let month = '' + (date.getMonth() + 1);
|
|
122
|
+
let day = '' + date.getDate();
|
|
123
|
+
const year = date.getFullYear();
|
|
124
|
+
if (month.length < 2) {
|
|
125
|
+
month = '0' + month;
|
|
126
|
+
}
|
|
127
|
+
if (day.length < 2) {
|
|
128
|
+
day = '0' + day;
|
|
129
|
+
}
|
|
130
|
+
return `${year}-${month}-${day}`;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Creates a new date object on from a date with or without time.
|
|
134
|
+
* @param value - The date from with the year, month and day is taken.
|
|
135
|
+
* @param hours - Optional numeric value of the hours, otherwise 0.
|
|
136
|
+
* @param minutes - Optional numeric value of the minutes, otherwise 0.
|
|
137
|
+
* @param seconds - Optional numeric value of the seconds, otherwise 0.
|
|
138
|
+
* @param milliseconds - Optional numeric value of the milliseconds, otherwise 0.
|
|
139
|
+
* @returns
|
|
140
|
+
*/
|
|
141
|
+
const createDate = (value, hours = 0, minutes = 0, seconds = 0, milliseconds = 0) => {
|
|
142
|
+
const newDate = new Date(value.getFullYear(), value.getMonth(), value.getDate(), hours, minutes, seconds, milliseconds);
|
|
143
|
+
// Seems redundant, but makes sure that increasing
|
|
144
|
+
// the hours does no change the date when switching
|
|
145
|
+
// from 11pm up.
|
|
146
|
+
newDate.setFullYear(value.getFullYear());
|
|
147
|
+
newDate.setMonth(value.getMonth());
|
|
148
|
+
newDate.setDate(value.getDate());
|
|
149
|
+
return newDate;
|
|
150
|
+
};
|
|
151
|
+
/** Creates a date but allows the month and date to overflow. */
|
|
152
|
+
const createDateInternal = (year, month, date) => {
|
|
153
|
+
const d = new Date();
|
|
154
|
+
d.setFullYear(year, month, date);
|
|
155
|
+
d.setHours(0, 0, 0, 0);
|
|
156
|
+
return d;
|
|
157
|
+
};
|
|
158
|
+
const NAMED_FORMATS = {};
|
|
159
|
+
const INVALID_DATE = new Date(NaN);
|
|
160
|
+
/**
|
|
161
|
+
* @param input - String containing a date or dateTime value (Ex. "05/15/2020"
|
|
162
|
+
* @param format - Format of the input value (Ex. "M/d/YYYY")
|
|
163
|
+
* @param locale - Locale of the input value
|
|
164
|
+
* @returns A JS Date in accordance of the three parameters
|
|
165
|
+
*/
|
|
166
|
+
const parseDate = (input, format, locale) => {
|
|
167
|
+
if (!input) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
const splitFormat = format.split(/[^MydhHmsSa]+/);
|
|
171
|
+
const splitDate = input.toUpperCase().match(/\d+|\w+/g);
|
|
172
|
+
if (!splitDate) {
|
|
173
|
+
return INVALID_DATE;
|
|
174
|
+
}
|
|
175
|
+
// in case of some locales where meridian is with special chars
|
|
176
|
+
const dateAndMeridian = input.split(/ +/);
|
|
177
|
+
if (splitDate.length > 0 && dateAndMeridian.length > 0) {
|
|
178
|
+
const parsedMeridian = dateAndMeridian[dateAndMeridian.length - 1].toUpperCase();
|
|
179
|
+
if (splitDate[splitDate.length - 1] !== parsedMeridian &&
|
|
180
|
+
getMeridian(parsedMeridian, locale)) {
|
|
181
|
+
splitDate?.push(parsedMeridian);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
let year = 0;
|
|
185
|
+
let month = 0;
|
|
186
|
+
/*
|
|
187
|
+
date as in the numeric day of the month to be provided for the Date() constructor.
|
|
188
|
+
default value is 1 since 0 refers to the last date of previous month.
|
|
189
|
+
*/
|
|
190
|
+
let date = 1;
|
|
191
|
+
let hour = 0;
|
|
192
|
+
let minute = 0;
|
|
193
|
+
let second = 0;
|
|
194
|
+
let milliseconds = 0;
|
|
195
|
+
// check if date is invalid and return if it is invalid
|
|
196
|
+
for (let i = 0; i < splitDate.length; i++) {
|
|
197
|
+
if (i === splitDate.length - 1) {
|
|
198
|
+
const parsedMeridian = splitDate[splitDate.length - 1];
|
|
199
|
+
// skip if its meridian
|
|
200
|
+
if (getMeridian(parsedMeridian, locale)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const parsedDateVal = parseInt(splitDate[i], 10);
|
|
205
|
+
if (isNaN(parsedDateVal)) {
|
|
206
|
+
return INVALID_DATE;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (let i = 0; i < splitFormat.length; i++) {
|
|
210
|
+
const f = splitFormat[i];
|
|
211
|
+
let parsedNumber = parseInt(splitDate[i], 10);
|
|
212
|
+
if (f !== 'a' && isNaN(parsedNumber)) {
|
|
213
|
+
// auto fill seconds and milliseconds if not passed by user
|
|
214
|
+
if (f === 'ss' || f === 'SSS') {
|
|
215
|
+
parsedNumber = 0;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
return INVALID_DATE;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (f === 'M' || f === 'MM') {
|
|
222
|
+
month = parsedNumber;
|
|
223
|
+
}
|
|
224
|
+
else if (f === 'd' || f === 'dd') {
|
|
225
|
+
date = parsedNumber;
|
|
226
|
+
}
|
|
227
|
+
else if (f === 'y' || f === 'yy' || f === 'yyyy') {
|
|
228
|
+
// JS adds 1900 for numbers between 0 and 99. Adjust to be more user-friendly
|
|
229
|
+
year =
|
|
230
|
+
parsedNumber < 50
|
|
231
|
+
? parsedNumber + 2000
|
|
232
|
+
: parsedNumber < 100
|
|
233
|
+
? parsedNumber + 1900
|
|
234
|
+
: parsedNumber;
|
|
235
|
+
}
|
|
236
|
+
else if (f === 'h' || f === 'HH') {
|
|
237
|
+
hour = parsedNumber;
|
|
238
|
+
}
|
|
239
|
+
else if (f === 'mm') {
|
|
240
|
+
minute = parsedNumber;
|
|
241
|
+
}
|
|
242
|
+
else if (f === 'ss') {
|
|
243
|
+
second = parsedNumber;
|
|
244
|
+
}
|
|
245
|
+
else if (f === 'SSS') {
|
|
246
|
+
milliseconds = parsedNumber;
|
|
247
|
+
}
|
|
248
|
+
else if (f === 'a') {
|
|
249
|
+
if (!isNaN(parseInt(splitDate[splitDate.length - 1], 10))) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const parsedMeridian = splitDate[splitDate.length - 1];
|
|
253
|
+
const meridian = getMeridian(parsedMeridian, locale);
|
|
254
|
+
if (hour === 12 && meridian === 'AM') {
|
|
255
|
+
hour = 0;
|
|
256
|
+
}
|
|
257
|
+
else if (meridian === 'PM') {
|
|
258
|
+
hour = (hour % 12) + 12;
|
|
259
|
+
}
|
|
260
|
+
else if (!meridian) {
|
|
261
|
+
return INVALID_DATE;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (month > 12 || date > 31 || date > new Date(year, month, 0).getDate()) {
|
|
266
|
+
return INVALID_DATE;
|
|
267
|
+
}
|
|
268
|
+
return new Date(year, month - 1, date, hour, minute, second, milliseconds);
|
|
269
|
+
};
|
|
270
|
+
const getMeridian = (parsedMeridian, locale) => {
|
|
271
|
+
const meridian = getLocaleDayPeriods(locale, FormStyle.Format, TranslationWidth.Short);
|
|
272
|
+
const usingAM = parsedMeridian === meridian[0].toUpperCase();
|
|
273
|
+
if (usingAM) {
|
|
274
|
+
return 'AM';
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
const usingPM = parsedMeridian === meridian[1].toUpperCase();
|
|
278
|
+
return usingPM ? 'PM' : undefined;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
// Adapted from: https://github.com/angular/angular/blob/91954cf20e17a386d71cc8ea25d1d17b9ae1e31c/packages/common/src/i18n/format_date.ts
|
|
282
|
+
// unfortunately it is not exported there
|
|
283
|
+
const getNamedFormat = (locale, format) => {
|
|
284
|
+
const localeId = getLocaleId(locale);
|
|
285
|
+
NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {};
|
|
286
|
+
if (NAMED_FORMATS[localeId][format]) {
|
|
287
|
+
return NAMED_FORMATS[localeId][format];
|
|
288
|
+
}
|
|
289
|
+
let formatValue = '';
|
|
290
|
+
switch (format) {
|
|
291
|
+
case 'shortDate':
|
|
292
|
+
formatValue = getLocaleDateFormat(locale, FormatWidth.Short);
|
|
293
|
+
break;
|
|
294
|
+
case 'shortTime':
|
|
295
|
+
formatValue = getLocaleTimeFormat(locale, FormatWidth.Short);
|
|
296
|
+
break;
|
|
297
|
+
case 'mediumTime':
|
|
298
|
+
formatValue = getLocaleTimeFormat(locale, FormatWidth.Medium);
|
|
299
|
+
break;
|
|
300
|
+
case 'longTime':
|
|
301
|
+
formatValue = getLocaleTimeFormat(locale, FormatWidth.Long);
|
|
302
|
+
break;
|
|
303
|
+
case 'fullTime':
|
|
304
|
+
formatValue = getLocaleTimeFormat(locale, FormatWidth.Full);
|
|
305
|
+
break;
|
|
306
|
+
case 'short':
|
|
307
|
+
case 'medium':
|
|
308
|
+
{
|
|
309
|
+
const shortTime = getNamedFormat(locale, format === 'short' ? 'shortTime' : 'mediumTime');
|
|
310
|
+
const shortDate = getNamedFormat(locale, 'shortDate');
|
|
311
|
+
formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Short), [
|
|
312
|
+
shortTime,
|
|
313
|
+
shortDate
|
|
314
|
+
]);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
if (formatValue) {
|
|
319
|
+
NAMED_FORMATS[localeId][format] = formatValue;
|
|
320
|
+
}
|
|
321
|
+
return formatValue;
|
|
322
|
+
};
|
|
323
|
+
const formatDateTime = (str, optVals) => {
|
|
324
|
+
if (optVals) {
|
|
325
|
+
str = str.replace(/\{([^}]+)}/g, (match, key) => optVals != null && key in optVals ? optVals[key] : match);
|
|
326
|
+
}
|
|
327
|
+
return str;
|
|
328
|
+
};
|
|
329
|
+
const getDateWithoutTime = (date) => createDate(date, 0, 0, 0, 0);
|
|
330
|
+
/**
|
|
331
|
+
* Get today
|
|
332
|
+
* @returns date of today
|
|
333
|
+
*/
|
|
334
|
+
const today = () => new Date();
|
|
335
|
+
/**
|
|
336
|
+
* Calculate a new date based on the offset while considering the min and max date.
|
|
337
|
+
* @param current - input date.
|
|
338
|
+
* @param daysOffset - numeric offset of days.
|
|
339
|
+
* @returns new date if the range is valid or original date
|
|
340
|
+
*/
|
|
341
|
+
const addDaysInRange = (current, daysOffset, minDate, maxDate) => {
|
|
342
|
+
const newDate = addDays(current, daysOffset);
|
|
343
|
+
// Make sure the new date is within the specified limits
|
|
344
|
+
// MinDate < new data < MaxDate
|
|
345
|
+
if (isSameOrBetween(newDate, minDate, maxDate)) {
|
|
346
|
+
return newDate;
|
|
347
|
+
}
|
|
348
|
+
return getDateSameOrBetween(newDate, minDate, maxDate);
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Get date delta based on the offset.
|
|
352
|
+
* @param date - source date object.
|
|
353
|
+
* @param days - numeric offset of days.
|
|
354
|
+
* @returns new date.
|
|
355
|
+
*/
|
|
356
|
+
const addDays = (date, days) => {
|
|
357
|
+
const d = createDate(date);
|
|
358
|
+
d.setDate(d.getDate() + days);
|
|
359
|
+
return d;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Update date/day of give date without changing the month.
|
|
363
|
+
* In case the day exceed the number of days in the current month the last day in month will be assigned.
|
|
364
|
+
* @param date - the date object to update the day.
|
|
365
|
+
* @param day - the day which shall be set.
|
|
366
|
+
* @returns the updated date object.
|
|
367
|
+
*/
|
|
368
|
+
const changeDay = (date, day) => {
|
|
369
|
+
const lastDay = daysInMonth(date.getMonth(), date.getFullYear());
|
|
370
|
+
date.setDate(lastDay <= day ? lastDay : day);
|
|
371
|
+
return date;
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Get date delta specifically for months based on the offset.
|
|
375
|
+
* @param current - starting date.
|
|
376
|
+
* @param monthsOffset - numeric offset of months.
|
|
377
|
+
* @param minDate - optional minimum allowed date.
|
|
378
|
+
* @param maxDate - optional maximum allowed date.
|
|
379
|
+
* @returns a new date object with the updated month.
|
|
380
|
+
*/
|
|
381
|
+
const addMonthsInRange = (current, monthsOffset, minDate, maxDate) => {
|
|
382
|
+
let newDate = createDateInternal(current.getFullYear(), current.getMonth() + monthsOffset, 1);
|
|
383
|
+
newDate = changeDay(newDate, current.getDate());
|
|
384
|
+
if (isSameOrBetween(newDate, minDate, maxDate)) {
|
|
385
|
+
return newDate;
|
|
386
|
+
}
|
|
387
|
+
return getDateSameOrBetween(newDate, minDate, maxDate);
|
|
388
|
+
};
|
|
389
|
+
/**
|
|
390
|
+
* Get date delta specifically for years based on the offset.
|
|
391
|
+
* @param current - starting date.
|
|
392
|
+
* @param yearsOffset - numeric offset of years.
|
|
393
|
+
* @param minDate - optional minimum allowed date.
|
|
394
|
+
* @param maxDate - optional maximum allowed date.
|
|
395
|
+
* @returns a new date object with the updated year.
|
|
396
|
+
*/
|
|
397
|
+
const addYearsInRange = (current, yearsOffset, minDate, maxDate) => {
|
|
398
|
+
const newDate = createDateInternal(current.getFullYear() + yearsOffset, current.getMonth(), current.getDate());
|
|
399
|
+
if (isSameOrBetween(newDate, minDate, maxDate)) {
|
|
400
|
+
return newDate;
|
|
401
|
+
}
|
|
402
|
+
return getDateSameOrBetween(newDate, minDate, maxDate);
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Get number of days for the given month and year.
|
|
406
|
+
* @param month - month as number (0..11).
|
|
407
|
+
* @param year - year as number.
|
|
408
|
+
* @returns the number of days for the given month.
|
|
409
|
+
*/
|
|
410
|
+
const daysInMonth = (month, year) =>
|
|
411
|
+
// By using 0 as the day it will give us the last day of the prior
|
|
412
|
+
// month. So passing in 1 as the month number will return the last day
|
|
413
|
+
new Date(year, month + 1, 0).getDate();
|
|
414
|
+
/**
|
|
415
|
+
* Get the first date of the week based on the input date.
|
|
416
|
+
* @param current - a date object from where the last date of a week is derived.
|
|
417
|
+
* @param weekStartDay - optional when a week shall start.
|
|
418
|
+
* @returns a new date object which is the start of the current week.
|
|
419
|
+
*/
|
|
420
|
+
const getWeekStartDate = (current, weekStartDay = 'monday') => {
|
|
421
|
+
const weekStartDate = createDate(current);
|
|
422
|
+
const diff = current.getDate() - getWeekDayOffset(current, weekStartDay);
|
|
423
|
+
weekStartDate.setDate(diff);
|
|
424
|
+
return weekStartDate;
|
|
425
|
+
};
|
|
426
|
+
/**
|
|
427
|
+
* Get the last date of the week based on the input date.
|
|
428
|
+
* @param current - a date object from where the last date of a week is derived.
|
|
429
|
+
* @param weekStartDay - optional when a week shall start.
|
|
430
|
+
* @returns the last date within the week.
|
|
431
|
+
*/
|
|
432
|
+
const getWeekEndDate = (current, weekStartDay = 'monday') => {
|
|
433
|
+
const weekStartDate = createDate(current);
|
|
434
|
+
weekStartDate.setDate(current.getDate() + 6 - getWeekDayOffset(current, weekStartDay));
|
|
435
|
+
return weekStartDate;
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* Get the beginning of the month.
|
|
439
|
+
* @param current - a date object from where the first date in a month is derived.
|
|
440
|
+
* @returns a new date object which starts a the first.
|
|
441
|
+
*/
|
|
442
|
+
const getFirstDateInMonth = (current) => new Date(current.getFullYear(), current.getMonth(), 1);
|
|
443
|
+
/**
|
|
444
|
+
* Get the last date of the month.
|
|
445
|
+
* @param current - a date object from where we derive the last date in a month.
|
|
446
|
+
* @returns a new date object which is the last day in the current month.
|
|
447
|
+
*/
|
|
448
|
+
const getLastDateInMonth = (current) => new Date(current.getFullYear(), current.getMonth() + 1, 0);
|
|
449
|
+
/**
|
|
450
|
+
* Get the beginning of the year.
|
|
451
|
+
* @param current - a date object from where the first date in a year is derived.
|
|
452
|
+
* @returns a new date object which starts a the first.
|
|
453
|
+
*/
|
|
454
|
+
const getFirstDateInYear = (current) => new Date(current.getFullYear(), 0, 1);
|
|
455
|
+
/**
|
|
456
|
+
* Are the two dates identical without considering time.
|
|
457
|
+
*/
|
|
458
|
+
const isSameDate = (current, compareTo) => {
|
|
459
|
+
if (!compareTo) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
return (current.getFullYear() === compareTo.getFullYear() &&
|
|
463
|
+
current.getMonth() === compareTo.getMonth() &&
|
|
464
|
+
current.getDate() === compareTo.getDate());
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* Are the two months identical without considering time.
|
|
468
|
+
*/
|
|
469
|
+
const isSameMonth = (current, compareTo) => {
|
|
470
|
+
if (!compareTo) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
return isSameYear(current, compareTo) && current.getMonth() === compareTo.getMonth();
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* Are the two years identical.
|
|
477
|
+
*/
|
|
478
|
+
const isSameYear = (current, compareTo) => {
|
|
479
|
+
if (!compareTo) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
return current.getFullYear() === compareTo.getFullYear();
|
|
483
|
+
};
|
|
484
|
+
/**
|
|
485
|
+
* Compares two dates.
|
|
486
|
+
* @param first - The first date to compare.
|
|
487
|
+
* @param second - The second date to compare.
|
|
488
|
+
* @returns 0 if the dates are equal, a number less than 0 if the first date is earlier,
|
|
489
|
+
* a number greater than 0 if the first date is later.
|
|
490
|
+
*/
|
|
491
|
+
const compareDate = (first, second) => compareMonth(first, second) || first.getDate() - second.getDate();
|
|
492
|
+
/**
|
|
493
|
+
* Compares two months.
|
|
494
|
+
* @param first - The first month to compare.
|
|
495
|
+
* @param second - The second month to compare.
|
|
496
|
+
* @returns 0 if the months are equal, a number less than 0 if the first month is earlier,
|
|
497
|
+
* a number greater than 0 if the first month is later.
|
|
498
|
+
*/
|
|
499
|
+
const compareMonth = (first, second) => compareYear(first, second) || first.getMonth() - second.getMonth();
|
|
500
|
+
/**
|
|
501
|
+
* Compares two years.
|
|
502
|
+
* @param first - The first year to compare.
|
|
503
|
+
* @param second - The second year to compare.
|
|
504
|
+
* @returns 0 if the years are equal, a number less than 0 if the first year is earlier,
|
|
505
|
+
* a number greater than 0 if the first year is later.
|
|
506
|
+
*/
|
|
507
|
+
const compareYear = (first, second) => first.getFullYear() - second.getFullYear();
|
|
508
|
+
/**
|
|
509
|
+
* Compare the current date is the same date or between start and end date.
|
|
510
|
+
* @param current - the date object.
|
|
511
|
+
* @param from - optional min date, if no value is provided we assume true for the min value.
|
|
512
|
+
* @param to - optional max date, if no value is provided we assume true for the max value.
|
|
513
|
+
* @returns true if the date is in the provided range.
|
|
514
|
+
*/
|
|
515
|
+
const isSameOrBetween = (current, from, to) => {
|
|
516
|
+
// from <= d
|
|
517
|
+
const isInMinRange = from ? compareDate(from, current) <= 0 : true;
|
|
518
|
+
// d <= to
|
|
519
|
+
const isInMaxRange = to ? compareDate(current, to) <= 0 : true;
|
|
520
|
+
return isInMinRange && isInMaxRange;
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Compare the current month is the same month or between start and end month.
|
|
524
|
+
* @param current - the month object.
|
|
525
|
+
* @param from - optional min month, if no value is provided we assume true for the min value.
|
|
526
|
+
* @param to - optional max month, if no value is provided we assume true for the max value.
|
|
527
|
+
* @returns true if the date is in the provided range.
|
|
528
|
+
*/
|
|
529
|
+
const isSameOrBetweenMonth = (current, from, to) => {
|
|
530
|
+
// from <= d
|
|
531
|
+
const isInMinRange = from ? compareMonth(from, current) <= 0 : true;
|
|
532
|
+
// d <= to
|
|
533
|
+
const isInMaxRange = to ? compareMonth(current, to) <= 0 : true;
|
|
534
|
+
return isInMinRange && isInMaxRange;
|
|
535
|
+
};
|
|
536
|
+
/**
|
|
537
|
+
* Compare the current year is the same year or between start and end year.
|
|
538
|
+
* @param current - the year object.
|
|
539
|
+
* @param from - optional min year, if no value is provided we assume true for the min value.
|
|
540
|
+
* @param to - optional max year, if no value is provided we assume true for the max value.
|
|
541
|
+
* @returns true if the date is in the provided range.
|
|
542
|
+
*/
|
|
543
|
+
const isSameOrBetweenYears = (current, from, to) => {
|
|
544
|
+
// from <= d
|
|
545
|
+
const isInMinRange = from ? compareYear(from, current) <= 0 : true;
|
|
546
|
+
// d <= to
|
|
547
|
+
const isInMaxRange = to ? compareYear(current, to) <= 0 : true;
|
|
548
|
+
return isInMinRange && isInMaxRange;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Compare the current date is between start and end date.
|
|
552
|
+
* from \< current \< to
|
|
553
|
+
*/
|
|
554
|
+
const isBetween = (current, from, to) => {
|
|
555
|
+
const isInMinRange = from ? compareDate(current, from) > 0 : true;
|
|
556
|
+
const isInMaxRange = to ? compareDate(current, to) < 0 : true;
|
|
557
|
+
return isInMinRange && isInMaxRange;
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* Compare the current month is between start and end month.
|
|
561
|
+
* from \< current \< to
|
|
562
|
+
*/
|
|
563
|
+
const isBetweenMonth = (current, from, to) => {
|
|
564
|
+
const isInMinRange = from ? compareMonth(current, from) > 0 : true;
|
|
565
|
+
const isInMaxRange = to ? compareMonth(current, to) < 0 : true;
|
|
566
|
+
return isInMinRange && isInMaxRange;
|
|
567
|
+
};
|
|
568
|
+
/**
|
|
569
|
+
* Compare the current year is between start and end year.
|
|
570
|
+
* from \< current \< to
|
|
571
|
+
*/
|
|
572
|
+
const isBetweenYears = (current, from, to) => {
|
|
573
|
+
const isInMinRange = from ? compareYear(current, from) > 0 : true;
|
|
574
|
+
const isInMaxRange = to ? compareYear(current, to) < 0 : true;
|
|
575
|
+
return isInMinRange && isInMaxRange;
|
|
576
|
+
};
|
|
577
|
+
/**
|
|
578
|
+
* Is first date after the second date (without considering the time).
|
|
579
|
+
* current \> compareTo
|
|
580
|
+
*/
|
|
581
|
+
const isAfter = (current, compareTo) => compareDate(current, compareTo) > 0;
|
|
582
|
+
/**
|
|
583
|
+
* Is first month after the second month (without considering the time).
|
|
584
|
+
* current \> compareTo
|
|
585
|
+
*/
|
|
586
|
+
const isAfterMonth = (current, compareTo) => compareMonth(current, compareTo) > 0;
|
|
587
|
+
/**
|
|
588
|
+
* Is first year after the second year.
|
|
589
|
+
* current \> compareTo
|
|
590
|
+
*/
|
|
591
|
+
const isAfterYear = (current, compareTo) => compareYear(current, compareTo) > 0;
|
|
592
|
+
/**
|
|
593
|
+
* Is first date equal or before the second date (without considering the time).
|
|
594
|
+
* current \>= compareTo
|
|
595
|
+
*/
|
|
596
|
+
const isSameOrBefore = (current, compareTo) => compareDate(current, compareTo) <= 0;
|
|
597
|
+
/**
|
|
598
|
+
* Is first month equal or before the second month.
|
|
599
|
+
* current \>= compareTo
|
|
600
|
+
*/
|
|
601
|
+
const isSameOrBeforeMonth = (current, compareTo) => compareMonth(current, compareTo) <= 0;
|
|
602
|
+
/**
|
|
603
|
+
* Is first year equal or before the second year.
|
|
604
|
+
* current \>= compareTo
|
|
605
|
+
*/
|
|
606
|
+
const isSameOrBeforeYear = (current, compareTo) => compareMonth(current, compareTo) <= 0;
|
|
607
|
+
/**
|
|
608
|
+
* Are the two dates in different months.
|
|
609
|
+
*/
|
|
610
|
+
const isAnotherMonth = (current, compareTo) => current.getMonth() !== compareTo.getMonth();
|
|
611
|
+
/**
|
|
612
|
+
* Are the two dates in different years.
|
|
613
|
+
*/
|
|
614
|
+
const isAnotherYear = (current, compareTo) => current.getFullYear() !== compareTo.getFullYear();
|
|
615
|
+
/**
|
|
616
|
+
* Are the two dates either in different months or years.
|
|
617
|
+
*/
|
|
618
|
+
const isAnotherMonthOrYear = (current, compareTo) => isAnotherMonth(current, compareTo) || isAnotherYear(current, compareTo);
|
|
619
|
+
/**
|
|
620
|
+
* Get a date which is in within the range and the close to current.
|
|
621
|
+
*/
|
|
622
|
+
const getDateSameOrBetween = (current, minDate, maxDate) => {
|
|
623
|
+
if (isSameOrBetween(current, minDate, maxDate)) {
|
|
624
|
+
return current;
|
|
625
|
+
}
|
|
626
|
+
const minDistance = minDate
|
|
627
|
+
? Math.ceil(Math.abs(current.getTime() - minDate.getTime()))
|
|
628
|
+
: Number.MAX_VALUE;
|
|
629
|
+
const maxDistance = maxDate
|
|
630
|
+
? Math.ceil(Math.abs(current.getTime() - maxDate.getTime()))
|
|
631
|
+
: Number.MAX_VALUE;
|
|
632
|
+
return minDistance < maxDistance ? createDate(minDate) : createDate(maxDate);
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* Get date or absolute min date.
|
|
636
|
+
* @param date - input date.
|
|
637
|
+
* @returns date or date of 1.1.1900
|
|
638
|
+
*/
|
|
639
|
+
const getMinDate = (date) => date && !isNaN(date.getTime()) ? date : new Date(Date.UTC(1900, 0, 1));
|
|
640
|
+
/**
|
|
641
|
+
* Get date or absolute max date.
|
|
642
|
+
* @param date - input date.
|
|
643
|
+
* @returns date or date of 31.12.2154
|
|
644
|
+
*/
|
|
645
|
+
const getMaxDate = (date) => date && !isNaN(date.getTime()) ? date : new Date(Date.UTC(2154, 11, 31));
|
|
646
|
+
/**
|
|
647
|
+
* Is valid date object.
|
|
648
|
+
*/
|
|
649
|
+
const isValid = (date) => !!date && !isNaN(date.getTime());
|
|
650
|
+
/**
|
|
651
|
+
* Returns the next months of the given date.
|
|
652
|
+
* @param date - The date for which the next month is returned.
|
|
653
|
+
* @returns A date of the first day of the following month of the given day.
|
|
654
|
+
*/
|
|
655
|
+
const nextMonth = (date) => {
|
|
656
|
+
if (date.getMonth() === 11) {
|
|
657
|
+
return new Date(date.getFullYear() + 1, 0, 1);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
return new Date(date.getFullYear(), date.getMonth() + 1, 1);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* Returns the last day of the previous month of the given date object.
|
|
665
|
+
* @param date - The date for which the previous month is returned.
|
|
666
|
+
* @returns A date of the last day of the previous month of the given day.
|
|
667
|
+
*/
|
|
668
|
+
const previousMonth = (date) => {
|
|
669
|
+
if (date.getMonth() === 0) {
|
|
670
|
+
return new Date(date.getFullYear() - 1, 12, 0);
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
return new Date(date.getFullYear(), date.getMonth(), 0);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
const minDate = (first, second) => {
|
|
677
|
+
return !!first && !!second ? (first < second ? first : second) : (first ?? second);
|
|
678
|
+
};
|
|
679
|
+
const maxDate = (first, second) => {
|
|
680
|
+
return !!first && !!second ? (first > second ? first : second) : (first ?? second);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Returns date / datetime format to be used for rendering a date object as text
|
|
685
|
+
* to an Html input element, which has the `SiDatepickerDirective`.
|
|
686
|
+
*
|
|
687
|
+
* @see https://angular.dev/api/common/DatePipe?tab=usage-notes
|
|
688
|
+
* @param locale - The locale for which the format is returned.
|
|
689
|
+
* @param config - The config object of the datepicker.
|
|
690
|
+
* @param timeWhenDisabled - If `true`, a format with time (medium or short) is returned, even if the `disabledTime` config is `true`.
|
|
691
|
+
* @returns Either
|
|
692
|
+
* - a custom format provided by the config,
|
|
693
|
+
* - the localized `medium` format when time and seconds included
|
|
694
|
+
* - the localized `short` format when time and no seconds included
|
|
695
|
+
*/
|
|
696
|
+
const getDatepickerFormat = (locale, config, timeWhenDisabled = false) => {
|
|
697
|
+
// try format from consumer
|
|
698
|
+
let dateFormat = config?.showTime && !config.disabledTime ? config.dateTimeFormat : config?.dateFormat;
|
|
699
|
+
if (!dateFormat) {
|
|
700
|
+
// no format from consumer - use default depending on configuration
|
|
701
|
+
let named;
|
|
702
|
+
if (config?.showTime && (!config.disabledTime || timeWhenDisabled)) {
|
|
703
|
+
named = config.showSeconds ? 'medium' : 'short';
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
named = 'shortDate';
|
|
707
|
+
}
|
|
708
|
+
dateFormat = getNamedFormat(locale, named);
|
|
709
|
+
}
|
|
710
|
+
// patch 2-digit year to 4-digit year
|
|
711
|
+
if (!dateFormat.includes('yyyy')) {
|
|
712
|
+
dateFormat = dateFormat.replace('yy', 'yyyy');
|
|
713
|
+
}
|
|
714
|
+
if (config?.onlyMonthSelection && dateFormat.includes('d')) {
|
|
715
|
+
dateFormat = dateFormat
|
|
716
|
+
.replace(/\sd/, '')
|
|
717
|
+
.replace(/\/d/, '')
|
|
718
|
+
.replace(/d/g, '')
|
|
719
|
+
.replace(/^\./, '');
|
|
720
|
+
}
|
|
721
|
+
return dateFormat;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Copyright Siemens 2016 - 2025.
|
|
726
|
+
* SPDX-License-Identifier: MIT
|
|
727
|
+
*/
|
|
728
|
+
/**
|
|
729
|
+
* Base directive for date input fields.
|
|
730
|
+
*/
|
|
731
|
+
class SiDateInputDirective {
|
|
732
|
+
static idCounter = 0;
|
|
733
|
+
/**
|
|
734
|
+
* @defaultValue
|
|
735
|
+
* ```
|
|
736
|
+
* `si-date-input-${SiDateInputDirective.idCounter++}`
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
id = input(`si-date-input-${SiDateInputDirective.idCounter++}`);
|
|
740
|
+
/**
|
|
741
|
+
* Configuration object for the datepicker.
|
|
742
|
+
*
|
|
743
|
+
* @defaultValue
|
|
744
|
+
* ```
|
|
745
|
+
* {}
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
siDatepickerConfig = model({});
|
|
749
|
+
/**
|
|
750
|
+
* @deprecated Property has no effect and will be removed without a replacement.
|
|
751
|
+
*
|
|
752
|
+
* @defaultValue 200
|
|
753
|
+
*/
|
|
754
|
+
dateInputDebounceTime = input(200);
|
|
755
|
+
/**
|
|
756
|
+
* Emits an event to notify about disabling the time from the datepicker.
|
|
757
|
+
* When time is disable, we construct a pure date object in UTC 00:00:00 time.
|
|
758
|
+
*/
|
|
759
|
+
siDatepickerDisabledTime = output();
|
|
760
|
+
/**
|
|
761
|
+
* Emits an event on state changes e.g. readonly, disable, ... .
|
|
762
|
+
*/
|
|
763
|
+
stateChange = output();
|
|
764
|
+
/**
|
|
765
|
+
* Whether the date range input is disabled.
|
|
766
|
+
* @defaultValue false
|
|
767
|
+
*/
|
|
768
|
+
// eslint-disable-next-line @angular-eslint/no-input-rename
|
|
769
|
+
disabledInput = input(false, { alias: 'disabled' });
|
|
770
|
+
/**
|
|
771
|
+
* Whether the date range input is readonly.
|
|
772
|
+
* @defaultValue false
|
|
773
|
+
*/
|
|
774
|
+
readonly = input(false, { transform: booleanAttribute });
|
|
775
|
+
/**
|
|
776
|
+
* Overrides the value of aria-describedby
|
|
777
|
+
*
|
|
778
|
+
* @defaultValue
|
|
779
|
+
* ```
|
|
780
|
+
* `${this.id()}-errormessage`
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
errormessageId = input(`${this.id()}-errormessage`);
|
|
784
|
+
/** @internal */
|
|
785
|
+
validatorOnChange = () => { };
|
|
786
|
+
/**
|
|
787
|
+
* Date form input validator function, validating text format, min and max value.
|
|
788
|
+
*/
|
|
789
|
+
validator = Validators.compose([
|
|
790
|
+
() => this.formatValidator(),
|
|
791
|
+
() => this.minValidator(),
|
|
792
|
+
() => this.maxValidator()
|
|
793
|
+
]);
|
|
794
|
+
date;
|
|
795
|
+
/**
|
|
796
|
+
* Emits a new `date` value on input field value changes.
|
|
797
|
+
*/
|
|
798
|
+
dateChange = output();
|
|
799
|
+
/** @internal */
|
|
800
|
+
disabled = computed(() => this.disabledInput() || this.disabledNgControl());
|
|
801
|
+
onTouched = () => { };
|
|
802
|
+
onModelChange = () => { };
|
|
803
|
+
dateString = signal('');
|
|
804
|
+
disabledNgControl = signal(false);
|
|
805
|
+
locale = inject(LOCALE_ID).toString();
|
|
806
|
+
format = '';
|
|
807
|
+
ngOnChanges(changes) {
|
|
808
|
+
if (changes.siDatepickerConfig && !changes.siDatepickerConfig.currentValue) {
|
|
809
|
+
this.siDatepickerConfig.set({});
|
|
810
|
+
}
|
|
811
|
+
if (changes.readonly || changes.disabledInput) {
|
|
812
|
+
this.stateChange.emit();
|
|
813
|
+
}
|
|
814
|
+
if (changes.siDatepickerConfig) {
|
|
815
|
+
// reflect possible change is date/time format
|
|
816
|
+
const format = this.format;
|
|
817
|
+
this.format = '';
|
|
818
|
+
this.getFormat();
|
|
819
|
+
const formatChanged = format !== this.format;
|
|
820
|
+
if (this.date && formatChanged) {
|
|
821
|
+
this.updateNativeValue();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
validate(c) {
|
|
826
|
+
return this.validator(c);
|
|
827
|
+
}
|
|
828
|
+
registerOnChange(fn) {
|
|
829
|
+
this.onModelChange = fn;
|
|
830
|
+
}
|
|
831
|
+
registerOnTouched(fn) {
|
|
832
|
+
this.onTouched = fn;
|
|
833
|
+
}
|
|
834
|
+
registerOnValidatorChange(fn) {
|
|
835
|
+
this.validatorOnChange = fn;
|
|
836
|
+
}
|
|
837
|
+
setDisabledState(isDisabled) {
|
|
838
|
+
this.disabledNgControl.set(isDisabled);
|
|
839
|
+
this.stateChange.emit();
|
|
840
|
+
}
|
|
841
|
+
writeValue(value) {
|
|
842
|
+
// remove date when user input is empty
|
|
843
|
+
let emptyString = false;
|
|
844
|
+
// Flag to define invalid string
|
|
845
|
+
let invalidDate = false;
|
|
846
|
+
if (typeof value === 'string') {
|
|
847
|
+
emptyString = value.trim().length === 0;
|
|
848
|
+
value = parseDate(value, this.getFormat(), this.locale);
|
|
849
|
+
invalidDate = !value;
|
|
850
|
+
}
|
|
851
|
+
// Only emit changes to prevent that a destroyed output emit a value
|
|
852
|
+
if (this.date !== value) {
|
|
853
|
+
this.date = value;
|
|
854
|
+
this.dateChange.emit(this.date);
|
|
855
|
+
// We should not change the content of the input field when the user typed
|
|
856
|
+
// a wrong input. Otherwise the typed content changes and the user cannot
|
|
857
|
+
// correct the content.
|
|
858
|
+
if (!invalidDate || emptyString) {
|
|
859
|
+
this.updateNativeValue();
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
updateNativeValue() {
|
|
864
|
+
let dtStr = '';
|
|
865
|
+
if (isValid(this.date)) {
|
|
866
|
+
dtStr = formatDate(this.date, this.getFormat(), this.locale);
|
|
867
|
+
}
|
|
868
|
+
this.dateString.set(dtStr);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Handles `input` events on the input element.
|
|
872
|
+
* @param value - current input value.
|
|
873
|
+
*/
|
|
874
|
+
onInput(value) {
|
|
875
|
+
const parsedDate = parseDate(value, this.getFormat(), this.locale);
|
|
876
|
+
// Is same date
|
|
877
|
+
const hasChanged = !(parsedDate === this.date);
|
|
878
|
+
if (hasChanged) {
|
|
879
|
+
this.date = parsedDate;
|
|
880
|
+
this.onModelChange(this.date);
|
|
881
|
+
this.dateChange.emit(this.date);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
onBlur(event) {
|
|
885
|
+
this.onTouched();
|
|
886
|
+
}
|
|
887
|
+
getFormat() {
|
|
888
|
+
if (!this.format) {
|
|
889
|
+
this.format = getDatepickerFormat(this.locale, this.siDatepickerConfig());
|
|
890
|
+
}
|
|
891
|
+
return this.format;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Callback when the datepicker changes his value.
|
|
895
|
+
* @param date - updated date
|
|
896
|
+
*/
|
|
897
|
+
onDateChanged(date) {
|
|
898
|
+
// update input element
|
|
899
|
+
this.writeValue(date);
|
|
900
|
+
// update the Forms ngModel
|
|
901
|
+
this.onModelChange(this.date);
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Datepicker consider time / ignore time changed.
|
|
905
|
+
* @param disabledTime - disable time
|
|
906
|
+
* @internal
|
|
907
|
+
*/
|
|
908
|
+
onDisabledTime(disabledTime) {
|
|
909
|
+
this.format = '';
|
|
910
|
+
// Temporary reset internal date to force an update with the new date format
|
|
911
|
+
const currentDate = this.date;
|
|
912
|
+
this.date = undefined;
|
|
913
|
+
this.siDatepickerConfig().disabledTime = disabledTime;
|
|
914
|
+
// Ensure the time format will be removed
|
|
915
|
+
this.onDateChanged(currentDate);
|
|
916
|
+
this.siDatepickerDisabledTime.emit(disabledTime);
|
|
917
|
+
}
|
|
918
|
+
/** The form control validator for date format */
|
|
919
|
+
formatValidator() {
|
|
920
|
+
const invalidFormat = this.date && isNaN(this.date.getTime());
|
|
921
|
+
return invalidFormat ? { dateFormat: { format: this.getFormat() } } : null;
|
|
922
|
+
}
|
|
923
|
+
/** The form control validator for the min date. */
|
|
924
|
+
minValidator() {
|
|
925
|
+
const controlValue = this.date;
|
|
926
|
+
const siDatepickerConfig = this.siDatepickerConfig();
|
|
927
|
+
const min = getMinDate(siDatepickerConfig?.minDate);
|
|
928
|
+
const withTime = siDatepickerConfig?.showTime;
|
|
929
|
+
return !min ||
|
|
930
|
+
!isValid(controlValue) ||
|
|
931
|
+
(withTime ? controlValue >= min : compareDate(controlValue, min) >= 0)
|
|
932
|
+
? null
|
|
933
|
+
: { minDate: { min, actual: controlValue } };
|
|
934
|
+
}
|
|
935
|
+
/** The form control validator for the min date. */
|
|
936
|
+
maxValidator() {
|
|
937
|
+
const controlValue = this.date;
|
|
938
|
+
const siDatepickerConfig = this.siDatepickerConfig();
|
|
939
|
+
const max = getMaxDate(siDatepickerConfig?.maxDate);
|
|
940
|
+
const withTime = siDatepickerConfig?.showTime;
|
|
941
|
+
return !max ||
|
|
942
|
+
!isValid(controlValue) ||
|
|
943
|
+
(withTime ? controlValue <= max : compareDate(controlValue, max) <= 0)
|
|
944
|
+
? null
|
|
945
|
+
: { maxDate: { max, actual: controlValue } };
|
|
946
|
+
}
|
|
947
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDateInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
948
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: SiDateInputDirective, isStandalone: true, selector: "[siDateInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, siDatepickerConfig: { classPropertyName: "siDatepickerConfig", publicName: "siDatepickerConfig", isSignal: true, isRequired: false, transformFunction: null }, dateInputDebounceTime: { classPropertyName: "dateInputDebounceTime", publicName: "dateInputDebounceTime", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, errormessageId: { classPropertyName: "errormessageId", publicName: "errormessageId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { siDatepickerConfig: "siDatepickerConfigChange", siDatepickerDisabledTime: "siDatepickerDisabledTime", stateChange: "stateChange", dateChange: "dateChange" }, host: { listeners: { "input": "onInput($event.target.value)", "blur": "onBlur($event)" }, properties: { "attr.id": "id()", "attr.disabled": "disabled() || null", "attr.readonly": "readonly() || null", "class.readonly": "readonly()", "attr.aria-describedby": "errormessageId()", "value": "dateString()" } }, providers: [
|
|
949
|
+
{
|
|
950
|
+
provide: NG_VALUE_ACCESSOR,
|
|
951
|
+
useExisting: SiDateInputDirective,
|
|
952
|
+
multi: true
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
provide: NG_VALIDATORS,
|
|
956
|
+
useExisting: SiDateInputDirective,
|
|
957
|
+
multi: true
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
961
|
+
useExisting: SiDateInputDirective
|
|
962
|
+
}
|
|
963
|
+
], exportAs: ["siDateInput"], usesOnChanges: true, ngImport: i0 });
|
|
964
|
+
}
|
|
965
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDateInputDirective, decorators: [{
|
|
966
|
+
type: Directive,
|
|
967
|
+
args: [{
|
|
968
|
+
selector: '[siDateInput]',
|
|
969
|
+
exportAs: 'siDateInput',
|
|
970
|
+
providers: [
|
|
971
|
+
{
|
|
972
|
+
provide: NG_VALUE_ACCESSOR,
|
|
973
|
+
useExisting: SiDateInputDirective,
|
|
974
|
+
multi: true
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
provide: NG_VALIDATORS,
|
|
978
|
+
useExisting: SiDateInputDirective,
|
|
979
|
+
multi: true
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
983
|
+
useExisting: SiDateInputDirective
|
|
984
|
+
}
|
|
985
|
+
],
|
|
986
|
+
host: {
|
|
987
|
+
'[attr.id]': 'id()',
|
|
988
|
+
'[attr.disabled]': 'disabled() || null',
|
|
989
|
+
'[attr.readonly]': 'readonly() || null',
|
|
990
|
+
'[class.readonly]': 'readonly()',
|
|
991
|
+
'[attr.aria-describedby]': 'errormessageId()',
|
|
992
|
+
'[value]': 'dateString()'
|
|
993
|
+
}
|
|
994
|
+
}]
|
|
995
|
+
}], propDecorators: { onInput: [{
|
|
996
|
+
type: HostListener,
|
|
997
|
+
args: ['input', ['$event.target.value']]
|
|
998
|
+
}], onBlur: [{
|
|
999
|
+
type: HostListener,
|
|
1000
|
+
args: ['blur', ['$event']]
|
|
1001
|
+
}] } });
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Copyright Siemens 2016 - 2025.
|
|
1005
|
+
* SPDX-License-Identifier: MIT
|
|
1006
|
+
*/
|
|
1007
|
+
class SiCalendarDateCellDirective {
|
|
1008
|
+
cell = input.required();
|
|
1009
|
+
compareAdapter = input.required();
|
|
1010
|
+
/** @defaultValue inject(ElementRef) */
|
|
1011
|
+
ref = inject(ElementRef);
|
|
1012
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarDateCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1013
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: SiCalendarDateCellDirective, isStandalone: true, selector: "[siCalendarDateCell]", inputs: { cell: { classPropertyName: "cell", publicName: "cell", isSignal: true, isRequired: true, transformFunction: null }, compareAdapter: { classPropertyName: "compareAdapter", publicName: "compareAdapter", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.aria-disabled": "cell().disabled", "class.disabled": "cell().disabled", "attr.aria-label": "cell().ariaLabel", "class.today": "this.cell().isToday", "attr.aria-current": "this.cell().isToday ? \"date\" : null" }, classAttribute: "si-calendar-date-cell" }, ngImport: i0 });
|
|
1014
|
+
}
|
|
1015
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarDateCellDirective, decorators: [{
|
|
1016
|
+
type: Directive,
|
|
1017
|
+
args: [{
|
|
1018
|
+
selector: '[siCalendarDateCell]',
|
|
1019
|
+
host: {
|
|
1020
|
+
class: 'si-calendar-date-cell',
|
|
1021
|
+
'[attr.aria-disabled]': 'cell().disabled',
|
|
1022
|
+
'[class.disabled]': 'cell().disabled',
|
|
1023
|
+
'[attr.aria-label]': 'cell().ariaLabel',
|
|
1024
|
+
'[class.today]': 'this.cell().isToday',
|
|
1025
|
+
'[attr.aria-current]': 'this.cell().isToday ? "date" : null'
|
|
1026
|
+
}
|
|
1027
|
+
}]
|
|
1028
|
+
}] });
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Copyright Siemens 2016 - 2025.
|
|
1032
|
+
* SPDX-License-Identifier: MIT
|
|
1033
|
+
*/
|
|
1034
|
+
/**
|
|
1035
|
+
* Compare dates in the month view.
|
|
1036
|
+
*/
|
|
1037
|
+
class DayCompareAdapter {
|
|
1038
|
+
isAfter(current, start) {
|
|
1039
|
+
return isAfter(current, start);
|
|
1040
|
+
}
|
|
1041
|
+
isBetween(current, from, to) {
|
|
1042
|
+
return isBetween(current, from, to);
|
|
1043
|
+
}
|
|
1044
|
+
isEqual(current, other) {
|
|
1045
|
+
return isSameDate(current, other);
|
|
1046
|
+
}
|
|
1047
|
+
isEqualOrBefore(current, other) {
|
|
1048
|
+
return isSameOrBefore(current, other);
|
|
1049
|
+
}
|
|
1050
|
+
isEqualOrBetween(current, from, to) {
|
|
1051
|
+
return isSameOrBetween(current, from, to);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Compare dates in the year view.
|
|
1056
|
+
*/
|
|
1057
|
+
class MonthCompareAdapter {
|
|
1058
|
+
isAfter(current, start) {
|
|
1059
|
+
return isAfterMonth(current, start);
|
|
1060
|
+
}
|
|
1061
|
+
isBetween(current, from, to) {
|
|
1062
|
+
return isBetweenMonth(current, from, to);
|
|
1063
|
+
}
|
|
1064
|
+
isEqual(current, other) {
|
|
1065
|
+
return isSameMonth(current, other);
|
|
1066
|
+
}
|
|
1067
|
+
isEqualOrBefore(current, other) {
|
|
1068
|
+
return isSameOrBeforeMonth(current, other);
|
|
1069
|
+
}
|
|
1070
|
+
isEqualOrBetween(current, from, to) {
|
|
1071
|
+
return isSameOrBetweenMonth(current, from, to);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
class YearCompareAdapter {
|
|
1075
|
+
isAfter(current, other) {
|
|
1076
|
+
return isAfterYear(current, other);
|
|
1077
|
+
}
|
|
1078
|
+
isBetween(current, from, to) {
|
|
1079
|
+
return isBetweenYears(current, from, to);
|
|
1080
|
+
}
|
|
1081
|
+
isEqual(current, other) {
|
|
1082
|
+
return isSameYear(current, other);
|
|
1083
|
+
}
|
|
1084
|
+
isEqualOrBefore(current, other) {
|
|
1085
|
+
if (!other) {
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
return isSameOrBeforeYear(current, other);
|
|
1089
|
+
}
|
|
1090
|
+
isEqualOrBetween(current, from, to) {
|
|
1091
|
+
return isSameOrBetweenYears(current, from, to);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Copyright Siemens 2016 - 2025.
|
|
1097
|
+
* SPDX-License-Identifier: MIT
|
|
1098
|
+
*/
|
|
1099
|
+
/**
|
|
1100
|
+
* Base interface for selections.
|
|
1101
|
+
*/
|
|
1102
|
+
class SelectionStrategy {
|
|
1103
|
+
compare;
|
|
1104
|
+
constructor(compare) {
|
|
1105
|
+
this.compare = compare;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Strategy the handle single selection within the {@link SiCalendarBodyComponent}.
|
|
1110
|
+
*/
|
|
1111
|
+
class SingleSelectionStrategy extends SelectionStrategy {
|
|
1112
|
+
isSelected(cell, start, end) {
|
|
1113
|
+
return this.compare.isEqual(cell.valueRaw, start);
|
|
1114
|
+
}
|
|
1115
|
+
inRange(cell, start, end) {
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
isRangeSelected(cell, date) {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
previewRangeHover(cell, hoverCell, start) {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
previewRangeHoverEnd(cell, hoverCell, start) {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Strategy the handle range selection within the {@link SiCalendarBodyComponent}.
|
|
1130
|
+
*/
|
|
1131
|
+
class RangeSelectionStrategy extends SelectionStrategy {
|
|
1132
|
+
isSelected(cell, start, end) {
|
|
1133
|
+
return this.compare.isEqual(cell.valueRaw, start) || this.compare.isEqual(cell.valueRaw, end);
|
|
1134
|
+
}
|
|
1135
|
+
inRange(c, start, end) {
|
|
1136
|
+
if (!start || !end) {
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
return this.compare.isBetween(c.valueRaw, start, end);
|
|
1140
|
+
}
|
|
1141
|
+
isRangeSelected(cell, date) {
|
|
1142
|
+
return this.compare.isEqual(cell.valueRaw, date);
|
|
1143
|
+
}
|
|
1144
|
+
previewRangeHover(cell, hoverCell, start) {
|
|
1145
|
+
if (!hoverCell || cell.disabled || !start) {
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
return (this.compare.isAfter(cell.valueRaw, start) &&
|
|
1149
|
+
this.compare.isEqualOrBefore(cell.valueRaw, hoverCell.valueRaw));
|
|
1150
|
+
}
|
|
1151
|
+
previewRangeHoverEnd(cell, hoverCell, start) {
|
|
1152
|
+
if (!hoverCell || cell.disabled || !start) {
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
return (this.compare.isAfter(cell.valueRaw, start) &&
|
|
1156
|
+
this.compare.isEqual(cell.valueRaw, hoverCell.valueRaw));
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
class SiCalendarBodyComponent {
|
|
1160
|
+
/** The active date, the cell which will receive the focus. */
|
|
1161
|
+
focusedDate = model.required();
|
|
1162
|
+
/** The date which shall be indicated as currently selected. */
|
|
1163
|
+
startDate = input();
|
|
1164
|
+
/** Selected end value which is only considered with enableRangeSelection. */
|
|
1165
|
+
endDate = input();
|
|
1166
|
+
/**
|
|
1167
|
+
* The cells to display in the table.
|
|
1168
|
+
*
|
|
1169
|
+
* @defaultValue []
|
|
1170
|
+
*/
|
|
1171
|
+
rows = input([]);
|
|
1172
|
+
/**
|
|
1173
|
+
* Labels for each row, which can be used to display the week number.
|
|
1174
|
+
* @defaultValue undefined
|
|
1175
|
+
*/
|
|
1176
|
+
rowLabels = input(undefined);
|
|
1177
|
+
/**
|
|
1178
|
+
* Additional row label CSS class(es).
|
|
1179
|
+
*
|
|
1180
|
+
* @defaultValue []
|
|
1181
|
+
*/
|
|
1182
|
+
rowLabelCssClasses = input([]);
|
|
1183
|
+
/**
|
|
1184
|
+
* Choose the selection strategy between single or range selection.
|
|
1185
|
+
* @defaultValue false
|
|
1186
|
+
*/
|
|
1187
|
+
enableRangeSelection = input(false);
|
|
1188
|
+
/**
|
|
1189
|
+
* Indicate whether a range preview shall be displayed.
|
|
1190
|
+
* It's necessary since to display a preview also datepicker has a valid endDate.
|
|
1191
|
+
*
|
|
1192
|
+
* @defaultValue true
|
|
1193
|
+
*/
|
|
1194
|
+
previewRange = input(true, { transform: booleanAttribute });
|
|
1195
|
+
/** The cell which which has the mouse hover. */
|
|
1196
|
+
activeHover = model();
|
|
1197
|
+
/**
|
|
1198
|
+
* Compare date functions which are necessary to compare a the dates according the current view.
|
|
1199
|
+
*
|
|
1200
|
+
* @defaultValue new DayCompareAdapter()
|
|
1201
|
+
*/
|
|
1202
|
+
compareAdapter = input(new DayCompareAdapter());
|
|
1203
|
+
/** Emits when a user select a cell via click, space or enter. */
|
|
1204
|
+
selectedValueChange = output();
|
|
1205
|
+
calendarDateCells = viewChildren(SiCalendarDateCellDirective);
|
|
1206
|
+
selection = computed(() => this.enableRangeSelection()
|
|
1207
|
+
? new RangeSelectionStrategy(this.compareAdapter())
|
|
1208
|
+
: new SingleSelectionStrategy(this.compareAdapter()));
|
|
1209
|
+
/**
|
|
1210
|
+
* Focus calendar cell which is marked as active cell.
|
|
1211
|
+
*/
|
|
1212
|
+
focusActiveCell() {
|
|
1213
|
+
setTimeout(() => {
|
|
1214
|
+
const focusedDateCells = this.calendarDateCells().filter(dateCell => this.compareAdapter().isEqual(this.focusedDate(), dateCell.cell().valueRaw));
|
|
1215
|
+
if (focusedDateCells.length > 0) {
|
|
1216
|
+
focusedDateCells[0].ref.nativeElement.focus();
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
isActive(cell) {
|
|
1221
|
+
return this.compareAdapter().isEqual(this.focusedDate(), cell.valueRaw);
|
|
1222
|
+
}
|
|
1223
|
+
cellCss(cell) {
|
|
1224
|
+
return cell.cssClasses;
|
|
1225
|
+
}
|
|
1226
|
+
emitActiveHover(cell) {
|
|
1227
|
+
this.activeHover.set(cell);
|
|
1228
|
+
}
|
|
1229
|
+
emitSelectCell(selection) {
|
|
1230
|
+
if (selection.disabled) {
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
this.selectedValueChange.emit(selection.valueRaw);
|
|
1234
|
+
}
|
|
1235
|
+
emitActiveDateChange(cell) {
|
|
1236
|
+
if (!cell.disabled && !cell.isPreview) {
|
|
1237
|
+
// To provide a date-range preview it is necessary to maintain hoverCell also in case of keyboard usage
|
|
1238
|
+
this.emitActiveHover(cell);
|
|
1239
|
+
this.focusedDate.set(cell.valueRaw);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1243
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SiCalendarBodyComponent, isStandalone: true, selector: "[si-calendar-body]", inputs: { focusedDate: { classPropertyName: "focusedDate", publicName: "focusedDate", isSignal: true, isRequired: true, transformFunction: null }, startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowLabels: { classPropertyName: "rowLabels", publicName: "rowLabels", isSignal: true, isRequired: false, transformFunction: null }, rowLabelCssClasses: { classPropertyName: "rowLabelCssClasses", publicName: "rowLabelCssClasses", isSignal: true, isRequired: false, transformFunction: null }, enableRangeSelection: { classPropertyName: "enableRangeSelection", publicName: "enableRangeSelection", isSignal: true, isRequired: false, transformFunction: null }, previewRange: { classPropertyName: "previewRange", publicName: "previewRange", isSignal: true, isRequired: false, transformFunction: null }, activeHover: { classPropertyName: "activeHover", publicName: "activeHover", isSignal: true, isRequired: false, transformFunction: null }, compareAdapter: { classPropertyName: "compareAdapter", publicName: "compareAdapter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { focusedDate: "focusedDateChange", activeHover: "activeHoverChange", selectedValueChange: "selectedValueChange" }, host: { classAttribute: "si-calendar-body" }, viewQueries: [{ propertyName: "calendarDateCells", predicate: SiCalendarDateCellDirective, descendants: true, isSignal: true }], exportAs: ["siCalendarBody"], ngImport: i0, template: "@for (row of rows(); track rowIndex; let rowIndex = $index) {\n <tr role=\"row\">\n <!-- Typically used for week numbers -->\n @if (rowLabels()) {\n <td class=\"si-calendar-row-label\" [attr.data-row]=\"rowIndex\" [ngClass]=\"rowLabelCssClasses()\">\n {{ rowLabels()?.at(rowIndex) ?? '' }}\n </td>\n }\n @for (col of row; track colIndex; let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"si-calendar-cell\"\n [attr.data-row]=\"rowIndex\"\n [attr.data-col]=\"colIndex\"\n [class.range-hover]=\"\n previewRange() && selection().previewRangeHover(col, activeHover(), startDate())\n \"\n [class.range-hover-end]=\"\n previewRange() && selection().previewRangeHoverEnd(col, activeHover(), startDate())\n \"\n [class.range]=\"selection().inRange(col, startDate(), endDate())\"\n [class.range-start]=\"selection().isRangeSelected(col, startDate())\"\n [class.range-end]=\"selection().isRangeSelected(col, endDate())\"\n >\n <button\n siCalendarDateCell\n type=\"button\"\n [cell]=\"col\"\n [compareAdapter]=\"compareAdapter()\"\n [ngClass]=\"cellCss(col)\"\n [attr.cdkFocusInitial]=\"isActive(col) ? '' : null\"\n [class.selected]=\"selection().isSelected(col, startDate(), endDate())\"\n [class.text-secondary]=\"\n col.isPreview &&\n !col.disabled &&\n !selection().isRangeSelected(col, startDate()) &&\n !selection().isRangeSelected(col, endDate()) &&\n !selection().inRange(col, startDate(), endDate())\n \"\n [tabindex]=\"isActive(col) ? '0' : '-1'\"\n (mouseover)=\"emitActiveHover(col)\"\n (click)=\"emitSelectCell(col)\"\n (focus)=\"emitActiveDateChange(col)\"\n >\n {{ col.displayValue }}\n </button>\n </td>\n }\n </tr>\n}\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: SiCalendarDateCellDirective, selector: "[siCalendarDateCell]", inputs: ["cell", "compareAdapter"] }] });
|
|
1244
|
+
}
|
|
1245
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarBodyComponent, decorators: [{
|
|
1246
|
+
type: Component,
|
|
1247
|
+
args: [{ selector: '[si-calendar-body]', exportAs: 'siCalendarBody', imports: [NgClass, A11yModule, SiCalendarDateCellDirective], host: {
|
|
1248
|
+
class: 'si-calendar-body'
|
|
1249
|
+
}, template: "@for (row of rows(); track rowIndex; let rowIndex = $index) {\n <tr role=\"row\">\n <!-- Typically used for week numbers -->\n @if (rowLabels()) {\n <td class=\"si-calendar-row-label\" [attr.data-row]=\"rowIndex\" [ngClass]=\"rowLabelCssClasses()\">\n {{ rowLabels()?.at(rowIndex) ?? '' }}\n </td>\n }\n @for (col of row; track colIndex; let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"si-calendar-cell\"\n [attr.data-row]=\"rowIndex\"\n [attr.data-col]=\"colIndex\"\n [class.range-hover]=\"\n previewRange() && selection().previewRangeHover(col, activeHover(), startDate())\n \"\n [class.range-hover-end]=\"\n previewRange() && selection().previewRangeHoverEnd(col, activeHover(), startDate())\n \"\n [class.range]=\"selection().inRange(col, startDate(), endDate())\"\n [class.range-start]=\"selection().isRangeSelected(col, startDate())\"\n [class.range-end]=\"selection().isRangeSelected(col, endDate())\"\n >\n <button\n siCalendarDateCell\n type=\"button\"\n [cell]=\"col\"\n [compareAdapter]=\"compareAdapter()\"\n [ngClass]=\"cellCss(col)\"\n [attr.cdkFocusInitial]=\"isActive(col) ? '' : null\"\n [class.selected]=\"selection().isSelected(col, startDate(), endDate())\"\n [class.text-secondary]=\"\n col.isPreview &&\n !col.disabled &&\n !selection().isRangeSelected(col, startDate()) &&\n !selection().isRangeSelected(col, endDate()) &&\n !selection().inRange(col, startDate(), endDate())\n \"\n [tabindex]=\"isActive(col) ? '0' : '-1'\"\n (mouseover)=\"emitActiveHover(col)\"\n (click)=\"emitSelectCell(col)\"\n (focus)=\"emitActiveDateChange(col)\"\n >\n {{ col.displayValue }}\n </button>\n </td>\n }\n </tr>\n}\n" }]
|
|
1250
|
+
}] });
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Copyright Siemens 2016 - 2025.
|
|
1254
|
+
* SPDX-License-Identifier: MIT
|
|
1255
|
+
*/
|
|
1256
|
+
class SiCalendarDirectionButtonComponent {
|
|
1257
|
+
ariaLabel = input.required();
|
|
1258
|
+
/** @defaultValue false */
|
|
1259
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
1260
|
+
direction = input();
|
|
1261
|
+
/**
|
|
1262
|
+
* Emit on button click.
|
|
1263
|
+
*/
|
|
1264
|
+
clicked = output();
|
|
1265
|
+
icon = computed(() => this.direction() === 'left' ? this.icons.elementLeft2 : this.icons.elementRight2);
|
|
1266
|
+
buttonClass = computed(() => this.direction() === 'left' ? 'previous-button' : 'next-button');
|
|
1267
|
+
icons = addIcons({ elementLeft2, elementRight2 });
|
|
1268
|
+
onClick() {
|
|
1269
|
+
this.clicked.emit();
|
|
1270
|
+
}
|
|
1271
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarDirectionButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1272
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.6", type: SiCalendarDirectionButtonComponent, isStandalone: true, selector: "si-calendar-direction-button", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: `<button
|
|
1273
|
+
role="button"
|
|
1274
|
+
type="button"
|
|
1275
|
+
class="btn btn-circle btn-sm btn-tertiary"
|
|
1276
|
+
[ngClass]="buttonClass()"
|
|
1277
|
+
[disabled]="disabled() || null"
|
|
1278
|
+
[attr.aria-label]="ariaLabel()"
|
|
1279
|
+
(click)="onClick()"
|
|
1280
|
+
>
|
|
1281
|
+
<si-icon-next class="icon flip-rtl" [icon]="icon()" />
|
|
1282
|
+
</button>`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SiIconNextComponent, selector: "si-icon-next", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1283
|
+
}
|
|
1284
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarDirectionButtonComponent, decorators: [{
|
|
1285
|
+
type: Component,
|
|
1286
|
+
args: [{
|
|
1287
|
+
selector: 'si-calendar-direction-button',
|
|
1288
|
+
template: `<button
|
|
1289
|
+
role="button"
|
|
1290
|
+
type="button"
|
|
1291
|
+
class="btn btn-circle btn-sm btn-tertiary"
|
|
1292
|
+
[ngClass]="buttonClass()"
|
|
1293
|
+
[disabled]="disabled() || null"
|
|
1294
|
+
[attr.aria-label]="ariaLabel()"
|
|
1295
|
+
(click)="onClick()"
|
|
1296
|
+
>
|
|
1297
|
+
<si-icon-next class="icon flip-rtl" [icon]="icon()" />
|
|
1298
|
+
</button>`,
|
|
1299
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1300
|
+
imports: [NgClass, SiIconNextComponent]
|
|
1301
|
+
}]
|
|
1302
|
+
}] });
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* Copyright Siemens 2016 - 2025.
|
|
1306
|
+
* SPDX-License-Identifier: MIT
|
|
1307
|
+
*/
|
|
1308
|
+
/**
|
|
1309
|
+
* Helper directive to set the initial focus to the calendar body cell.
|
|
1310
|
+
*/
|
|
1311
|
+
class SiInitialFocusComponent {
|
|
1312
|
+
/** The cell which has the mouse hover. */
|
|
1313
|
+
activeHover = model();
|
|
1314
|
+
/** The date which is displayed as selected. */
|
|
1315
|
+
startDate = input();
|
|
1316
|
+
/** The date which is displayed as selected end. The value is only considered with enableRangeSelection. */
|
|
1317
|
+
endDate = input();
|
|
1318
|
+
/**
|
|
1319
|
+
* Guard to set the focus during ngAfterViewInit, we just set the focus after we first initialized the view
|
|
1320
|
+
*
|
|
1321
|
+
* @defaultValue true
|
|
1322
|
+
*/
|
|
1323
|
+
initialFocus = input(true);
|
|
1324
|
+
/** The minimum selectable date. */
|
|
1325
|
+
minDate = input();
|
|
1326
|
+
/** The maximum selectable date. */
|
|
1327
|
+
maxDate = input();
|
|
1328
|
+
/**
|
|
1329
|
+
* Optional input to control the minimum month the datepicker can show and the user can navigate.
|
|
1330
|
+
* The `minMonth` can be larger than the `minDate` This option enables the usage of multiple
|
|
1331
|
+
* datepickers next to each other while the more left calendar always
|
|
1332
|
+
* shows an earlier month the more right one.
|
|
1333
|
+
*/
|
|
1334
|
+
minMonth = input();
|
|
1335
|
+
/**
|
|
1336
|
+
* Optional input to control the maximum month the datepicker can show and the user can navigate.
|
|
1337
|
+
* The `maxMonth` can be smaller than the `maxDate` This option enables the usage of multiple
|
|
1338
|
+
* datepickers next to each other while the more left calendar always
|
|
1339
|
+
* shows an earlier month the more right one.
|
|
1340
|
+
*/
|
|
1341
|
+
maxMonth = input();
|
|
1342
|
+
/** Aria label for the next button. Needed for a11y. */
|
|
1343
|
+
previousLabel = input.required();
|
|
1344
|
+
/** Aria label for the next button. Needed for a11y. */
|
|
1345
|
+
nextLabel = input.required();
|
|
1346
|
+
/**
|
|
1347
|
+
* Is range selection enabled, when enabled it shows a preview between the startDate and the focused date.
|
|
1348
|
+
*
|
|
1349
|
+
* @defaultValue false
|
|
1350
|
+
*/
|
|
1351
|
+
isRangeSelection = input(false, { transform: booleanAttribute });
|
|
1352
|
+
/**
|
|
1353
|
+
* Indicate whether a range preview shall be displayed.
|
|
1354
|
+
* When enabled a preview is visible between startDate and focusedDate.
|
|
1355
|
+
*
|
|
1356
|
+
* @defaultValue true
|
|
1357
|
+
*/
|
|
1358
|
+
previewRange = input(true, { transform: booleanAttribute });
|
|
1359
|
+
/**
|
|
1360
|
+
* Emits when a new value is selected. In case where the value is null to
|
|
1361
|
+
* user aborted the selection by Escape.
|
|
1362
|
+
*/
|
|
1363
|
+
selectedValueChange = output();
|
|
1364
|
+
/** The body of calendar table */
|
|
1365
|
+
calendarBody = viewChild.required(SiCalendarBodyComponent);
|
|
1366
|
+
ngAfterViewInit() {
|
|
1367
|
+
if (this.initialFocus()) {
|
|
1368
|
+
this.focusActiveCell();
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Focus calendar cell which is marked as active cell.
|
|
1373
|
+
*/
|
|
1374
|
+
focusActiveCell() {
|
|
1375
|
+
this.calendarBody().focusActiveCell();
|
|
1376
|
+
}
|
|
1377
|
+
onActiveHoverChange(event) {
|
|
1378
|
+
this.activeHover.set(event);
|
|
1379
|
+
}
|
|
1380
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiInitialFocusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1381
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.6", type: SiInitialFocusComponent, isStandalone: true, selector: "ng-component", inputs: { activeHover: { classPropertyName: "activeHover", publicName: "activeHover", isSignal: true, isRequired: false, transformFunction: null }, startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, initialFocus: { classPropertyName: "initialFocus", publicName: "initialFocus", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, minMonth: { classPropertyName: "minMonth", publicName: "minMonth", isSignal: true, isRequired: false, transformFunction: null }, maxMonth: { classPropertyName: "maxMonth", publicName: "maxMonth", isSignal: true, isRequired: false, transformFunction: null }, previousLabel: { classPropertyName: "previousLabel", publicName: "previousLabel", isSignal: true, isRequired: true, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: true, transformFunction: null }, isRangeSelection: { classPropertyName: "isRangeSelection", publicName: "isRangeSelection", isSignal: true, isRequired: false, transformFunction: null }, previewRange: { classPropertyName: "previewRange", publicName: "previewRange", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeHover: "activeHoverChange", selectedValueChange: "selectedValueChange" }, viewQueries: [{ propertyName: "calendarBody", first: true, predicate: SiCalendarBodyComponent, descendants: true, isSignal: true }], ngImport: i0, template: '', isInline: true });
|
|
1382
|
+
}
|
|
1383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiInitialFocusComponent, decorators: [{
|
|
1384
|
+
type: Component,
|
|
1385
|
+
args: [{
|
|
1386
|
+
template: ''
|
|
1387
|
+
}]
|
|
1388
|
+
}] });
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Copyright Siemens 2016 - 2025.
|
|
1392
|
+
* SPDX-License-Identifier: MIT
|
|
1393
|
+
*/
|
|
1394
|
+
/**
|
|
1395
|
+
* Show dates of a single month as table and handles the keyboard interactions.
|
|
1396
|
+
* The focusedDate is handled according the keyboard interactions.
|
|
1397
|
+
*/
|
|
1398
|
+
class SiDaySelectionComponent extends SiInitialFocusComponent {
|
|
1399
|
+
/**
|
|
1400
|
+
* Indicate whether the week numbers shall be hidden.
|
|
1401
|
+
*
|
|
1402
|
+
* @defaultValue false
|
|
1403
|
+
*/
|
|
1404
|
+
hideWeekNumbers = input(false, { transform: booleanAttribute });
|
|
1405
|
+
/**
|
|
1406
|
+
* Defines the starting day of the week. Default is `monday`.
|
|
1407
|
+
*
|
|
1408
|
+
* @defaultValue 'monday'
|
|
1409
|
+
*/
|
|
1410
|
+
weekStartDay = input('monday');
|
|
1411
|
+
/**
|
|
1412
|
+
* The active date, the cell which will receive the focus.
|
|
1413
|
+
* @defaultValue calendarUtils.today()
|
|
1414
|
+
*/
|
|
1415
|
+
focusedDate = model.required();
|
|
1416
|
+
/** Today button text */
|
|
1417
|
+
todayLabel = input();
|
|
1418
|
+
/** Aria label for calendar week column */
|
|
1419
|
+
calenderWeekLabel = input();
|
|
1420
|
+
/** Emits when the active focused date changed to another month / year, typically during keyboard navigation */
|
|
1421
|
+
activeMonthChange = output();
|
|
1422
|
+
/** Emits when the user requests a different to show a different view */
|
|
1423
|
+
viewChange = output();
|
|
1424
|
+
/** The translated list of week days. */
|
|
1425
|
+
days = computed(() => getDayStrings(this.locale, this.weekStartDay()));
|
|
1426
|
+
/** The week numbers which are shown as row label */
|
|
1427
|
+
weekNumbers = computed(() => this.weeks().map(w => getWeekOfYear(w[0].valueRaw, this.weekStartDay()).toString()));
|
|
1428
|
+
/**
|
|
1429
|
+
* The current visible list of calendar days.
|
|
1430
|
+
* Every time the focusedDate changes to either another month or year the list will be rebuild.
|
|
1431
|
+
*/
|
|
1432
|
+
weeks = computed(() => {
|
|
1433
|
+
const focusedDate = this.focusedDate();
|
|
1434
|
+
const monthStart = getFirstDateInMonth(focusedDate);
|
|
1435
|
+
const monthEnd = getLastDateInMonth(focusedDate);
|
|
1436
|
+
/**
|
|
1437
|
+
* We start the month with the first day in the week which has the effect that dates are
|
|
1438
|
+
* visible which aren't in the active month.
|
|
1439
|
+
*/
|
|
1440
|
+
const startDate = getWeekStartDate(monthStart, this.weekStartDay());
|
|
1441
|
+
const minDate = this.minDate();
|
|
1442
|
+
const maxDate = this.maxDate();
|
|
1443
|
+
const weeks = [[], [], [], [], [], []];
|
|
1444
|
+
let weekIndex = 0;
|
|
1445
|
+
for (let i = 0, date = new Date(startDate); weeks[weeks.length - 1].length < 7; i++, date.setDate(date.getDate() + 1)) {
|
|
1446
|
+
if (i > 0 && i % 7 === 0) {
|
|
1447
|
+
weekIndex++;
|
|
1448
|
+
}
|
|
1449
|
+
const activeMonth = isSameOrBetween(date, monthStart, monthEnd);
|
|
1450
|
+
const isToday = isSameDate(date, today());
|
|
1451
|
+
const outOfRange = !isSameOrBetween(date, minDate, maxDate);
|
|
1452
|
+
weeks.at(weekIndex)?.push({
|
|
1453
|
+
value: date.getDate(),
|
|
1454
|
+
disabled: outOfRange,
|
|
1455
|
+
ariaLabel: date.toDateString(),
|
|
1456
|
+
displayValue: date.getDate().toString(),
|
|
1457
|
+
isPreview: !activeMonth,
|
|
1458
|
+
isToday,
|
|
1459
|
+
valueRaw: createDate(date),
|
|
1460
|
+
cssClasses: ['day', activeMonth ? 'si-title-1' : 'si-body-1']
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
return weeks;
|
|
1464
|
+
});
|
|
1465
|
+
/** Compare date based on the current view */
|
|
1466
|
+
compareAdapter = new DayCompareAdapter();
|
|
1467
|
+
/** Disable today button if it is the same month */
|
|
1468
|
+
isTodayButtonDisabled = computed(() => isSameMonth(today(), this.focusedDate()));
|
|
1469
|
+
/**
|
|
1470
|
+
* Indicate the previous button shall be disabled.
|
|
1471
|
+
* This happens when the focusedDate is equal or before the minDate.
|
|
1472
|
+
*/
|
|
1473
|
+
isPreviousButtonDisabled = computed(() => {
|
|
1474
|
+
const minDate = this.minDate();
|
|
1475
|
+
const focusedDate = this.focusedDate();
|
|
1476
|
+
return minDate && (isSameMonth(focusedDate, minDate) || isAfterMonth(minDate, focusedDate));
|
|
1477
|
+
});
|
|
1478
|
+
/**
|
|
1479
|
+
* Indicate the next button shall be disabled.
|
|
1480
|
+
* This happens when the focusedDate is equal or after the maxDate.
|
|
1481
|
+
*/
|
|
1482
|
+
isNextButtonDisabled = computed(() => {
|
|
1483
|
+
const maxDate = this.maxDate();
|
|
1484
|
+
const focusedDate = this.focusedDate();
|
|
1485
|
+
return maxDate && (isSameMonth(focusedDate, maxDate) || isAfterMonth(focusedDate, maxDate));
|
|
1486
|
+
});
|
|
1487
|
+
locale = inject(LOCALE_ID).toString();
|
|
1488
|
+
calendarBodyKeyDown(event) {
|
|
1489
|
+
const isRtl = isRTL();
|
|
1490
|
+
switch (event.key) {
|
|
1491
|
+
case 'ArrowLeft':
|
|
1492
|
+
this.updateFocusedDateByDay(isRtl ? 1 : -1);
|
|
1493
|
+
break;
|
|
1494
|
+
case 'ArrowRight':
|
|
1495
|
+
this.updateFocusedDateByDay(isRtl ? -1 : 1);
|
|
1496
|
+
break;
|
|
1497
|
+
case 'ArrowUp':
|
|
1498
|
+
this.updateFocusedDateByDay(-7);
|
|
1499
|
+
break;
|
|
1500
|
+
case 'ArrowDown':
|
|
1501
|
+
this.updateFocusedDateByDay(7);
|
|
1502
|
+
break;
|
|
1503
|
+
case 'Home':
|
|
1504
|
+
this.updateFocusedDate(getWeekStartDate(this.focusedDate(), this.weekStartDay()));
|
|
1505
|
+
break;
|
|
1506
|
+
case 'End':
|
|
1507
|
+
this.updateFocusedDate(getWeekEndDate(this.focusedDate(), this.weekStartDay()));
|
|
1508
|
+
break;
|
|
1509
|
+
case 'PageDown':
|
|
1510
|
+
this.updateFocusedDateByMonth(1);
|
|
1511
|
+
break;
|
|
1512
|
+
case 'PageUp':
|
|
1513
|
+
this.updateFocusedDateByMonth(-1);
|
|
1514
|
+
break;
|
|
1515
|
+
case 'Enter':
|
|
1516
|
+
case 'Space':
|
|
1517
|
+
default:
|
|
1518
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
// Prevent unexpected default actions such as form submission.
|
|
1522
|
+
event.preventDefault();
|
|
1523
|
+
}
|
|
1524
|
+
updateFocusedDateByDay(offset) {
|
|
1525
|
+
this.updateFocusedDate(addDaysInRange(this.focusedDate(), offset, this.minDate(), this.maxDate()));
|
|
1526
|
+
}
|
|
1527
|
+
updateFocusedDateByMonth(offset) {
|
|
1528
|
+
this.updateFocusedDate(addMonthsInRange(this.focusedDate(), offset, this.minDate(), this.maxDate()));
|
|
1529
|
+
}
|
|
1530
|
+
updateFocusedDate(newDate) {
|
|
1531
|
+
const prevDate = this.focusedDate();
|
|
1532
|
+
if (!isSameDate(prevDate, newDate)) {
|
|
1533
|
+
this.focusedDate.set(newDate);
|
|
1534
|
+
if (isAnotherMonthOrYear(newDate, prevDate)) {
|
|
1535
|
+
this.activeMonthChange.emit(newDate);
|
|
1536
|
+
}
|
|
1537
|
+
this.focusActiveCell();
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Update month of focusedDate.
|
|
1542
|
+
* @param offset -1 or -1.
|
|
1543
|
+
*/
|
|
1544
|
+
setMonthOffset(offset) {
|
|
1545
|
+
// Only update emit focusedDate since the focus shall stay on the button.
|
|
1546
|
+
const actualMonth = addMonthsInRange(this.focusedDate(), offset, this.minDate(), this.maxDate());
|
|
1547
|
+
this.focusedDate.set(actualMonth);
|
|
1548
|
+
this.activeMonthChange.emit(actualMonth);
|
|
1549
|
+
}
|
|
1550
|
+
/** Change the focusedDate to today */
|
|
1551
|
+
goToToday() {
|
|
1552
|
+
this.focusedDate.set(today());
|
|
1553
|
+
this.focusActiveCell();
|
|
1554
|
+
}
|
|
1555
|
+
emitSelectedValue(selected) {
|
|
1556
|
+
if (selected !== this.startDate() || selected !== this.endDate()) {
|
|
1557
|
+
this.selectedValueChange.emit(selected);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
emitActiveDate(active) {
|
|
1561
|
+
this.focusedDate.set(active);
|
|
1562
|
+
}
|
|
1563
|
+
emitViewChange(view) {
|
|
1564
|
+
this.viewChange.emit(view);
|
|
1565
|
+
}
|
|
1566
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDaySelectionComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1567
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SiDaySelectionComponent, isStandalone: true, selector: "si-day-selection", inputs: { hideWeekNumbers: { classPropertyName: "hideWeekNumbers", publicName: "hideWeekNumbers", isSignal: true, isRequired: false, transformFunction: null }, weekStartDay: { classPropertyName: "weekStartDay", publicName: "weekStartDay", isSignal: true, isRequired: false, transformFunction: null }, focusedDate: { classPropertyName: "focusedDate", publicName: "focusedDate", isSignal: true, isRequired: true, transformFunction: null }, todayLabel: { classPropertyName: "todayLabel", publicName: "todayLabel", isSignal: true, isRequired: false, transformFunction: null }, calenderWeekLabel: { classPropertyName: "calenderWeekLabel", publicName: "calenderWeekLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { focusedDate: "focusedDateChange", activeMonthChange: "activeMonthChange", viewChange: "viewChange" }, usesInheritance: true, ngImport: i0, template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel() | translate\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"setMonthOffset(-1)\"\n />\n <div class=\"flex-fill\">\n <button\n type=\"button\"\n class=\"open-month-view flex-fill text-end px-2 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange('month')\"\n >\n {{ focusedDate() | date: 'MMMM' }}\n </button>\n <button\n type=\"button\"\n class=\"open-year-view flex-fill text-start px-2 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange('year')\"\n >\n {{ focusedDate() | date: 'yyyy' }}\n </button>\n </div>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel() | translate\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"setMonthOffset(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <thead class=\"si-calendar-table-header\">\n <tr>\n @if (!hideWeekNumbers()) {\n <th scope=\"col\" class=\"week-num\">\n <span class=\"visually-hidden\">{{ calenderWeekLabel() | translate }}</span>\n </th>\n }\n @for (day of days(); track $index) {\n <th scope=\"col\">\n <span class=\"si-hidden-xs si-hidden-sm\">{{ day }}</span>\n </th>\n }\n </tr>\n </thead>\n <tbody\n si-calendar-body\n rowLabelCssClasses=\"week-num\"\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"weeks()\"\n [rowLabels]=\"hideWeekNumbers() ? undefined : weekNumbers()\"\n [activeHover]=\"activeHover()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (focusedDateChange)=\"emitActiveDate($event!)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n<div class=\"footer pt-2 ps-8 pe-8\">\n <button\n type=\"button\"\n class=\"today-button btn btn-tertiary flex-fill\"\n [disabled]=\"isTodayButtonDisabled()\"\n (click)=\"goToToday()\"\n >\n {{ todayLabel() ?? 'Today' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "component", type: SiCalendarBodyComponent, selector: "[si-calendar-body]", inputs: ["focusedDate", "startDate", "endDate", "rows", "rowLabels", "rowLabelCssClasses", "enableRangeSelection", "previewRange", "activeHover", "compareAdapter"], outputs: ["focusedDateChange", "activeHoverChange", "selectedValueChange"], exportAs: ["siCalendarBody"] }, { kind: "component", type: SiCalendarDirectionButtonComponent, selector: "si-calendar-direction-button", inputs: ["ariaLabel", "disabled", "direction"], outputs: ["clicked"] }, { kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i2.SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1568
|
+
}
|
|
1569
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDaySelectionComponent, decorators: [{
|
|
1570
|
+
type: Component,
|
|
1571
|
+
args: [{ selector: 'si-day-selection', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1572
|
+
DatePipe,
|
|
1573
|
+
SiCalendarBodyComponent,
|
|
1574
|
+
SiCalendarDirectionButtonComponent,
|
|
1575
|
+
SiTranslateModule
|
|
1576
|
+
], template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel() | translate\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"setMonthOffset(-1)\"\n />\n <div class=\"flex-fill\">\n <button\n type=\"button\"\n class=\"open-month-view flex-fill text-end px-2 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange('month')\"\n >\n {{ focusedDate() | date: 'MMMM' }}\n </button>\n <button\n type=\"button\"\n class=\"open-year-view flex-fill text-start px-2 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange('year')\"\n >\n {{ focusedDate() | date: 'yyyy' }}\n </button>\n </div>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel() | translate\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"setMonthOffset(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <thead class=\"si-calendar-table-header\">\n <tr>\n @if (!hideWeekNumbers()) {\n <th scope=\"col\" class=\"week-num\">\n <span class=\"visually-hidden\">{{ calenderWeekLabel() | translate }}</span>\n </th>\n }\n @for (day of days(); track $index) {\n <th scope=\"col\">\n <span class=\"si-hidden-xs si-hidden-sm\">{{ day }}</span>\n </th>\n }\n </tr>\n </thead>\n <tbody\n si-calendar-body\n rowLabelCssClasses=\"week-num\"\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"weeks()\"\n [rowLabels]=\"hideWeekNumbers() ? undefined : weekNumbers()\"\n [activeHover]=\"activeHover()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (focusedDateChange)=\"emitActiveDate($event!)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n<div class=\"footer pt-2 ps-8 pe-8\">\n <button\n type=\"button\"\n class=\"today-button btn btn-tertiary flex-fill\"\n [disabled]=\"isTodayButtonDisabled()\"\n (click)=\"goToToday()\"\n >\n {{ todayLabel() ?? 'Today' | translate }}\n </button>\n</div>\n" }]
|
|
1577
|
+
}] });
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Copyright Siemens 2016 - 2025.
|
|
1581
|
+
* SPDX-License-Identifier: MIT
|
|
1582
|
+
*/
|
|
1583
|
+
/**
|
|
1584
|
+
* Show months of a single year as table and handles the keyboard interactions.
|
|
1585
|
+
* The focus and focusedDate is handled according the keyboard interactions.
|
|
1586
|
+
*/
|
|
1587
|
+
class SiMonthSelectionComponent extends SiInitialFocusComponent {
|
|
1588
|
+
/**
|
|
1589
|
+
* The translated list of months.
|
|
1590
|
+
*
|
|
1591
|
+
* @defaultValue []
|
|
1592
|
+
*/
|
|
1593
|
+
months = input([]);
|
|
1594
|
+
/**
|
|
1595
|
+
* The active date, the cell which will receive the focus.
|
|
1596
|
+
*/
|
|
1597
|
+
focusedDate = model.required();
|
|
1598
|
+
/** Emits when the active focused date is changed to another month / year, typically during keyboard navigation. */
|
|
1599
|
+
activeMonthChange = output();
|
|
1600
|
+
/** Emits when the user requests a different to show a different view. */
|
|
1601
|
+
viewChange = output();
|
|
1602
|
+
/** Listen Escape event to switch view back */
|
|
1603
|
+
triggerEsc(event) {
|
|
1604
|
+
this.selectedValueChange.emit(null);
|
|
1605
|
+
event.preventDefault();
|
|
1606
|
+
event.stopPropagation(); // Prevents the overlay from closing.
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* The current visible list of calendar months.
|
|
1610
|
+
* Every time the focusedDate changes to another year the list will update.
|
|
1611
|
+
*/
|
|
1612
|
+
monthCells = [];
|
|
1613
|
+
compareAdapter = new MonthCompareAdapter();
|
|
1614
|
+
/** Number of column before the row is wrapped */
|
|
1615
|
+
columnCount = 2;
|
|
1616
|
+
/**
|
|
1617
|
+
* Indicate the previous button shall be disabled.
|
|
1618
|
+
* This happens when the focusedDate is equal or before the minDate.
|
|
1619
|
+
*/
|
|
1620
|
+
isPreviousButtonDisabled = computed(() => {
|
|
1621
|
+
const minDate = this.minDate();
|
|
1622
|
+
const focusedDate = this.focusedDate();
|
|
1623
|
+
return minDate && (isSameYear(focusedDate, minDate) || isAfterYear(minDate, focusedDate));
|
|
1624
|
+
});
|
|
1625
|
+
/**
|
|
1626
|
+
* Indicate the next button shall be disabled.
|
|
1627
|
+
* This happens when the focusedDate is equal or after the maxDate.
|
|
1628
|
+
*/
|
|
1629
|
+
isNextButtonDisabled = computed(() => {
|
|
1630
|
+
const maxDate = this.maxDate();
|
|
1631
|
+
const focusedDate = this.focusedDate();
|
|
1632
|
+
return maxDate && (isSameYear(focusedDate, maxDate) || isAfterYear(focusedDate, maxDate));
|
|
1633
|
+
});
|
|
1634
|
+
ngOnChanges(changes) {
|
|
1635
|
+
if (changes.maxDate ||
|
|
1636
|
+
changes.minDate ||
|
|
1637
|
+
changes.maxMonth ||
|
|
1638
|
+
changes.minMonth ||
|
|
1639
|
+
changes.focusedDate) {
|
|
1640
|
+
this.initView();
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
calendarBodyKeyDown(event) {
|
|
1644
|
+
const isRtl = isRTL();
|
|
1645
|
+
switch (event.key) {
|
|
1646
|
+
case 'ArrowLeft':
|
|
1647
|
+
this.updateFocusedDate(isRtl ? 1 : -1);
|
|
1648
|
+
break;
|
|
1649
|
+
case 'ArrowRight':
|
|
1650
|
+
this.updateFocusedDate(isRtl ? -1 : 1);
|
|
1651
|
+
break;
|
|
1652
|
+
case 'ArrowUp':
|
|
1653
|
+
this.updateFocusedDate(-1 * this.columnCount);
|
|
1654
|
+
break;
|
|
1655
|
+
case 'ArrowDown':
|
|
1656
|
+
this.updateFocusedDate(this.columnCount);
|
|
1657
|
+
break;
|
|
1658
|
+
case 'Escape':
|
|
1659
|
+
this.selectedValueChange.emit(null);
|
|
1660
|
+
event.preventDefault();
|
|
1661
|
+
event.stopPropagation(); // Prevents the overlay from closing.
|
|
1662
|
+
return;
|
|
1663
|
+
case 'Enter':
|
|
1664
|
+
case 'Space':
|
|
1665
|
+
default:
|
|
1666
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
// Prevent unexpected default actions such as form submission.
|
|
1670
|
+
event.preventDefault();
|
|
1671
|
+
}
|
|
1672
|
+
updateFocusedDate(offset) {
|
|
1673
|
+
const prevDate = this.focusedDate();
|
|
1674
|
+
const newDate = addMonthsInRange(prevDate, offset, this.minDate(), this.maxDate());
|
|
1675
|
+
this.focusedDate.set(newDate);
|
|
1676
|
+
if (!this.compareAdapter.isEqual(prevDate, newDate)) {
|
|
1677
|
+
// Synchronize focusedDate with year view
|
|
1678
|
+
this.focusActiveCell();
|
|
1679
|
+
this.emitActiveMonthChange(newDate, prevDate);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Add offset to year and update focusedDate.
|
|
1684
|
+
*/
|
|
1685
|
+
setYearOffset(offset) {
|
|
1686
|
+
const prevDate = this.focusedDate();
|
|
1687
|
+
const newActive = createDate(prevDate);
|
|
1688
|
+
newActive.setFullYear(newActive.getFullYear() + offset);
|
|
1689
|
+
this.focusedDate.set(newActive);
|
|
1690
|
+
this.emitActiveMonthChange(newActive, prevDate);
|
|
1691
|
+
}
|
|
1692
|
+
emitSelectedValue(selected) {
|
|
1693
|
+
this.selectedValueChange.emit(selected);
|
|
1694
|
+
}
|
|
1695
|
+
emitFocusedDate(focused) {
|
|
1696
|
+
// Take over the current day on month changes
|
|
1697
|
+
focused.setDate(this.focusedDate().getDate());
|
|
1698
|
+
this.focusedDate.set(focused);
|
|
1699
|
+
}
|
|
1700
|
+
emitActiveMonthChange(focus, prevFocus) {
|
|
1701
|
+
if (isAnotherYear(focus, prevFocus)) {
|
|
1702
|
+
this.activeMonthChange.emit(focus);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
emitViewChange() {
|
|
1706
|
+
this.viewChange.emit('year');
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Initialize view based on the focusedDate.
|
|
1710
|
+
*/
|
|
1711
|
+
initView() {
|
|
1712
|
+
this.monthCells = [];
|
|
1713
|
+
let row = [];
|
|
1714
|
+
// The cell date object needs to be the first to prevent that we jump to the next month when
|
|
1715
|
+
// setting the month. For example the focusedDate is 31. setting february would result in the
|
|
1716
|
+
// 3. March.
|
|
1717
|
+
const startDate = getFirstDateInYear(this.focusedDate());
|
|
1718
|
+
const today$1 = today();
|
|
1719
|
+
const months = this.months();
|
|
1720
|
+
for (let i = 0; i <= 11; i++) {
|
|
1721
|
+
if (i > 0 && i % this.columnCount === 0) {
|
|
1722
|
+
this.monthCells.push(row);
|
|
1723
|
+
row = [];
|
|
1724
|
+
}
|
|
1725
|
+
const date = new Date(startDate);
|
|
1726
|
+
date.setMonth(i);
|
|
1727
|
+
const isToday = this.compareAdapter.isEqual(date, today$1);
|
|
1728
|
+
const isDisabled = !this.compareAdapter.isEqualOrBetween(date, this.minDate(), this.maxDate());
|
|
1729
|
+
row.push({
|
|
1730
|
+
value: date.getDate(),
|
|
1731
|
+
disabled: isDisabled,
|
|
1732
|
+
ariaLabel: `${months[date.getMonth()]} ${this.focusedDate().getFullYear()}`,
|
|
1733
|
+
displayValue: months[date.getMonth()],
|
|
1734
|
+
isPreview: false,
|
|
1735
|
+
isToday,
|
|
1736
|
+
valueRaw: createDate(date),
|
|
1737
|
+
cssClasses: ['month', 'si-title-1', 'text-truncate']
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
this.monthCells.push(row);
|
|
1741
|
+
}
|
|
1742
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiMonthSelectionComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1743
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.6", type: SiMonthSelectionComponent, isStandalone: true, selector: "si-month-selection", inputs: { months: { classPropertyName: "months", publicName: "months", isSignal: true, isRequired: false, transformFunction: null }, focusedDate: { classPropertyName: "focusedDate", publicName: "focusedDate", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { focusedDate: "focusedDateChange", activeMonthChange: "activeMonthChange", viewChange: "viewChange" }, host: { listeners: { "keydown.Escape": "triggerEsc($event)" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel()\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"setYearOffset(-1)\"\n />\n <button\n type=\"button\"\n class=\"open-year-view flex-fill mx-4 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange()\"\n >\n {{ focusedDate() | date: 'yyyy' }}\n </button>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel()\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"setYearOffset(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <tbody\n si-calendar-body\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"monthCells\"\n [activeHover]=\"activeHover()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (focusedDateChange)=\"emitFocusedDate($event)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n", dependencies: [{ kind: "component", type: SiCalendarDirectionButtonComponent, selector: "si-calendar-direction-button", inputs: ["ariaLabel", "disabled", "direction"], outputs: ["clicked"] }, { kind: "component", type: SiCalendarBodyComponent, selector: "[si-calendar-body]", inputs: ["focusedDate", "startDate", "endDate", "rows", "rowLabels", "rowLabelCssClasses", "enableRangeSelection", "previewRange", "activeHover", "compareAdapter"], outputs: ["focusedDateChange", "activeHoverChange", "selectedValueChange"], exportAs: ["siCalendarBody"] }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1744
|
+
}
|
|
1745
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiMonthSelectionComponent, decorators: [{
|
|
1746
|
+
type: Component,
|
|
1747
|
+
args: [{ selector: 'si-month-selection', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SiCalendarDirectionButtonComponent, SiCalendarBodyComponent, DatePipe], template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel()\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"setYearOffset(-1)\"\n />\n <button\n type=\"button\"\n class=\"open-year-view flex-fill mx-4 btn btn-tertiary calendar-button\"\n tabindex=\"0\"\n (click)=\"emitViewChange()\"\n >\n {{ focusedDate() | date: 'yyyy' }}\n </button>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel()\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"setYearOffset(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <tbody\n si-calendar-body\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"monthCells\"\n [activeHover]=\"activeHover()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (focusedDateChange)=\"emitFocusedDate($event)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n" }]
|
|
1748
|
+
}], propDecorators: { triggerEsc: [{
|
|
1749
|
+
type: HostListener,
|
|
1750
|
+
args: ['keydown.Escape', ['$event']]
|
|
1751
|
+
}] } });
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* Copyright Siemens 2016 - 2025.
|
|
1755
|
+
* SPDX-License-Identifier: MIT
|
|
1756
|
+
*/
|
|
1757
|
+
/**
|
|
1758
|
+
* Show months of a single year as table and handles the keyboard interactions.
|
|
1759
|
+
* The focus and focusedDate is handled according the keyboard interactions.
|
|
1760
|
+
*/
|
|
1761
|
+
class SiYearSelectionComponent extends SiInitialFocusComponent {
|
|
1762
|
+
/**
|
|
1763
|
+
* The active date, the cell which will receive the focus.
|
|
1764
|
+
*/
|
|
1765
|
+
focusedDate = model.required();
|
|
1766
|
+
/** Emits when the active focused date changed to another month / year, typically during keyboard navigation. */
|
|
1767
|
+
yearRangeChange = output();
|
|
1768
|
+
/** Listen Escape event to switch view back */
|
|
1769
|
+
triggerEsc(event) {
|
|
1770
|
+
this.selectedValueChange.emit(null);
|
|
1771
|
+
event.preventDefault();
|
|
1772
|
+
event.stopPropagation(); // Prevents the overlay from closing.
|
|
1773
|
+
}
|
|
1774
|
+
/** Number of column before the row is wrapped */
|
|
1775
|
+
columnCount = 3;
|
|
1776
|
+
/** The number of years which shall be displayed, this number should be even and dividable by columnCount */
|
|
1777
|
+
yearsToDisplay = 18;
|
|
1778
|
+
/** Lower windows bound for displayed year range */
|
|
1779
|
+
fromYear = signal(undefined);
|
|
1780
|
+
/** Upper windows bound for displayed year range */
|
|
1781
|
+
toYear = signal(undefined);
|
|
1782
|
+
/**
|
|
1783
|
+
* The current visible list of calendar years.
|
|
1784
|
+
* Every time the focusedDate changes to another year the list will be rebuilt.
|
|
1785
|
+
*/
|
|
1786
|
+
yearCells = [];
|
|
1787
|
+
compareAdapter = new YearCompareAdapter();
|
|
1788
|
+
/** Is previous button disabled based on the minDate. */
|
|
1789
|
+
isPreviousButtonDisabled = computed(() => {
|
|
1790
|
+
const minDate$1 = this.minDate();
|
|
1791
|
+
const minMonth = this.minMonth();
|
|
1792
|
+
if (!minDate$1 && !minMonth) {
|
|
1793
|
+
return false;
|
|
1794
|
+
}
|
|
1795
|
+
const min = minDate(minDate$1, minMonth);
|
|
1796
|
+
return (this.compareAdapter.isEqual(this.fromYear(), min) ||
|
|
1797
|
+
this.compareAdapter.isAfter(min, this.fromYear()));
|
|
1798
|
+
});
|
|
1799
|
+
/** Is next button disabled based on the maxDate */
|
|
1800
|
+
isNextButtonDisabled = computed(() => {
|
|
1801
|
+
const maxDate = this.maxDate();
|
|
1802
|
+
if (!maxDate) {
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
return (this.compareAdapter.isEqual(this.toYear(), maxDate) ||
|
|
1806
|
+
this.compareAdapter.isAfter(this.toYear(), maxDate));
|
|
1807
|
+
});
|
|
1808
|
+
ngOnChanges(changes) {
|
|
1809
|
+
if (changes.startDate || changes.focusedDate || changes.maxDate || changes.minDate) {
|
|
1810
|
+
this.initView();
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
calendarBodyKeyDown(event) {
|
|
1814
|
+
const isRtl = isRTL();
|
|
1815
|
+
const oldActiveDate = this.focusedDate();
|
|
1816
|
+
switch (event.key) {
|
|
1817
|
+
case 'ArrowLeft':
|
|
1818
|
+
this.setYearOffset(isRtl ? 1 : -1);
|
|
1819
|
+
break;
|
|
1820
|
+
case 'ArrowRight':
|
|
1821
|
+
this.setYearOffset(isRtl ? -1 : 1);
|
|
1822
|
+
break;
|
|
1823
|
+
case 'ArrowUp':
|
|
1824
|
+
this.setYearOffset(-1 * this.columnCount);
|
|
1825
|
+
break;
|
|
1826
|
+
case 'ArrowDown':
|
|
1827
|
+
this.setYearOffset(this.columnCount);
|
|
1828
|
+
break;
|
|
1829
|
+
case 'PageUp':
|
|
1830
|
+
this.setYearOffset(-1 * (oldActiveDate.getFullYear() - this.fromYear().getFullYear()));
|
|
1831
|
+
break;
|
|
1832
|
+
case 'PageDown':
|
|
1833
|
+
this.setYearOffset(this.toYear().getFullYear() - oldActiveDate.getFullYear());
|
|
1834
|
+
break;
|
|
1835
|
+
case 'Escape':
|
|
1836
|
+
this.selectedValueChange.emit(null);
|
|
1837
|
+
event.preventDefault();
|
|
1838
|
+
event.stopPropagation(); // Prevents the overlay from closing.
|
|
1839
|
+
return;
|
|
1840
|
+
case 'Enter':
|
|
1841
|
+
case 'Space':
|
|
1842
|
+
default:
|
|
1843
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
if (!this.compareAdapter.isEqual(oldActiveDate, this.focusedDate())) {
|
|
1847
|
+
this.focusActiveCell();
|
|
1848
|
+
}
|
|
1849
|
+
// Prevent unexpected default actions such as form submission.
|
|
1850
|
+
event.preventDefault();
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Change the active date and the range of displayed years.
|
|
1854
|
+
* The windowOffset control the amount of ranges the view shall move forward or backward.
|
|
1855
|
+
* The number of displayed years ia controlled by yearsToDisplay.
|
|
1856
|
+
*/
|
|
1857
|
+
changeVisibleYearRange(windowOffset) {
|
|
1858
|
+
const offset = windowOffset * this.yearsToDisplay;
|
|
1859
|
+
this.setYearOffset(offset);
|
|
1860
|
+
}
|
|
1861
|
+
emitSelectedValue(selected) {
|
|
1862
|
+
this.selectedValueChange.emit(selected);
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Determine the year range start and end year.
|
|
1866
|
+
* - Based on the active date this function will find the start and
|
|
1867
|
+
* ending year of the current displayed range.
|
|
1868
|
+
* - In case the focusedDate is either before or after the current range the
|
|
1869
|
+
* start and end year will move the entire window (yearsToDisplay)
|
|
1870
|
+
*/
|
|
1871
|
+
initYearRange() {
|
|
1872
|
+
// Did we exceed the display current displayed year range
|
|
1873
|
+
let changed = false;
|
|
1874
|
+
const focusedDate = this.focusedDate();
|
|
1875
|
+
const fromYear = this.fromYear();
|
|
1876
|
+
const toYear = this.toYear();
|
|
1877
|
+
if (!fromYear || !toYear) {
|
|
1878
|
+
const start = focusedDate.getFullYear() - this.yearsToDisplay / 2;
|
|
1879
|
+
this.fromYear.set(new Date(start, 0, 1));
|
|
1880
|
+
this.toYear.set(new Date(start + this.yearsToDisplay - 1, 0, 1));
|
|
1881
|
+
changed = true;
|
|
1882
|
+
}
|
|
1883
|
+
else if (this.compareAdapter.isAfter(focusedDate, toYear)) {
|
|
1884
|
+
// Change window forward
|
|
1885
|
+
const rangeDistance = Math.floor(Math.abs(focusedDate.getFullYear() - fromYear.getFullYear()) / this.yearsToDisplay);
|
|
1886
|
+
const newFromYear = fromYear.getFullYear() + rangeDistance * this.yearsToDisplay;
|
|
1887
|
+
this.fromYear.set(new Date(newFromYear, 0, 1));
|
|
1888
|
+
this.toYear.set(new Date(newFromYear + this.yearsToDisplay - 1, 0, 1));
|
|
1889
|
+
changed = true;
|
|
1890
|
+
}
|
|
1891
|
+
else if (this.compareAdapter.isAfter(fromYear, focusedDate)) {
|
|
1892
|
+
// Change window backwards
|
|
1893
|
+
const rangeDistance = Math.ceil(Math.abs(focusedDate.getFullYear() - fromYear.getFullYear()) / this.yearsToDisplay);
|
|
1894
|
+
const newFromYear = fromYear.getFullYear() - rangeDistance * this.yearsToDisplay;
|
|
1895
|
+
this.fromYear.set(new Date(newFromYear, 0, 1));
|
|
1896
|
+
this.toYear.set(new Date(newFromYear + this.yearsToDisplay - 1, 0, 1));
|
|
1897
|
+
changed = true;
|
|
1898
|
+
}
|
|
1899
|
+
if (changed) {
|
|
1900
|
+
this.yearRangeChange.emit([this.fromYear(), this.toYear()]);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Initialize view based on the focusedDate.
|
|
1905
|
+
*/
|
|
1906
|
+
initView() {
|
|
1907
|
+
// Initial year limits
|
|
1908
|
+
this.initYearRange();
|
|
1909
|
+
this.yearCells = [];
|
|
1910
|
+
let row = [];
|
|
1911
|
+
// The cell date object needs to be the first to prevent that we jump to the next month when
|
|
1912
|
+
// setting the month. For example the focusedDate is 31. setting february would result in the
|
|
1913
|
+
// 3. March.
|
|
1914
|
+
const startDate = getFirstDateInYear(this.fromYear());
|
|
1915
|
+
const today$1 = today();
|
|
1916
|
+
for (let i = 0; i < this.yearsToDisplay; i++) {
|
|
1917
|
+
if (i > 0 && i % this.columnCount === 0) {
|
|
1918
|
+
this.yearCells.push(row);
|
|
1919
|
+
row = [];
|
|
1920
|
+
}
|
|
1921
|
+
const date = createDate(startDate);
|
|
1922
|
+
date.setFullYear(date.getFullYear() + i);
|
|
1923
|
+
const isToday = this.compareAdapter.isEqual(date, today$1);
|
|
1924
|
+
const isDisabled = !this.compareAdapter.isEqualOrBetween(date, this.minDate(), this.maxDate());
|
|
1925
|
+
const year = date.getFullYear().toString();
|
|
1926
|
+
row.push({
|
|
1927
|
+
value: date.getDate(),
|
|
1928
|
+
disabled: isDisabled,
|
|
1929
|
+
ariaLabel: year,
|
|
1930
|
+
displayValue: year,
|
|
1931
|
+
isPreview: false,
|
|
1932
|
+
isToday,
|
|
1933
|
+
valueRaw: createDate(date),
|
|
1934
|
+
cssClasses: ['year', 'si-title-1']
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
this.yearCells.push(row);
|
|
1938
|
+
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Add offset to year and update focusedDate.
|
|
1941
|
+
* If the new year is outside min/max date the year will set to the closest year in range.
|
|
1942
|
+
*/
|
|
1943
|
+
setYearOffset(offset) {
|
|
1944
|
+
const newActive = addYearsInRange(this.focusedDate(), offset, this.minDate(), this.maxDate());
|
|
1945
|
+
this.focusedDate.set(newActive);
|
|
1946
|
+
if (!this.fromYear() || !isBetween(this.focusedDate(), this.fromYear(), this.toYear())) {
|
|
1947
|
+
// Re-calc years view
|
|
1948
|
+
this.initView();
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiYearSelectionComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1952
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.6", type: SiYearSelectionComponent, isStandalone: true, selector: "si-year-selection", inputs: { focusedDate: { classPropertyName: "focusedDate", publicName: "focusedDate", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { focusedDate: "focusedDateChange", yearRangeChange: "yearRangeChange" }, host: { listeners: { "keydown.Escape": "triggerEsc($event)" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel()\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"changeVisibleYearRange(-1)\"\n />\n <span class=\"year-range-label flex-fill mx-4 si-title-1 text-secondary calendar-button\">\n {{ fromYear() | date: 'yyyy' }} - {{ toYear() | date: 'yyyy' }}\n </span>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel()\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"changeVisibleYearRange(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <tbody\n si-calendar-body\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"yearCells\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n", dependencies: [{ kind: "component", type: SiCalendarDirectionButtonComponent, selector: "si-calendar-direction-button", inputs: ["ariaLabel", "disabled", "direction"], outputs: ["clicked"] }, { kind: "component", type: SiCalendarBodyComponent, selector: "[si-calendar-body]", inputs: ["focusedDate", "startDate", "endDate", "rows", "rowLabels", "rowLabelCssClasses", "enableRangeSelection", "previewRange", "activeHover", "compareAdapter"], outputs: ["focusedDateChange", "activeHoverChange", "selectedValueChange"], exportAs: ["siCalendarBody"] }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1953
|
+
}
|
|
1954
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiYearSelectionComponent, decorators: [{
|
|
1955
|
+
type: Component,
|
|
1956
|
+
args: [{ selector: 'si-year-selection', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SiCalendarDirectionButtonComponent, SiCalendarBodyComponent, DatePipe], template: "<div class=\"header\">\n <si-calendar-direction-button\n direction=\"left\"\n [ariaLabel]=\"previousLabel()\"\n [disabled]=\"isPreviousButtonDisabled()\"\n (clicked)=\"changeVisibleYearRange(-1)\"\n />\n <span class=\"year-range-label flex-fill mx-4 si-title-1 text-secondary calendar-button\">\n {{ fromYear() | date: 'yyyy' }} - {{ toYear() | date: 'yyyy' }}\n </span>\n <si-calendar-direction-button\n direction=\"right\"\n [ariaLabel]=\"nextLabel()\"\n [disabled]=\"isNextButtonDisabled()\"\n (clicked)=\"changeVisibleYearRange(1)\"\n />\n</div>\n<table class=\"px-9 mt-6\" role=\"grid\">\n <tbody\n si-calendar-body\n [focusedDate]=\"focusedDate()\"\n [compareAdapter]=\"compareAdapter\"\n [startDate]=\"startDate()\"\n [endDate]=\"endDate()\"\n [enableRangeSelection]=\"isRangeSelection()\"\n [previewRange]=\"previewRange()\"\n [rows]=\"yearCells\"\n (selectedValueChange)=\"emitSelectedValue($event)\"\n (keydown)=\"calendarBodyKeyDown($event)\"\n >\n </tbody>\n</table>\n" }]
|
|
1957
|
+
}], propDecorators: { triggerEsc: [{
|
|
1958
|
+
type: HostListener,
|
|
1959
|
+
args: ['keydown.Escape', ['$event']]
|
|
1960
|
+
}] } });
|
|
1961
|
+
|
|
1962
|
+
/**
|
|
1963
|
+
* Copyright Siemens 2016 - 2025.
|
|
1964
|
+
* SPDX-License-Identifier: MIT
|
|
1965
|
+
*/
|
|
1966
|
+
class SiTimepickerComponent {
|
|
1967
|
+
static idCounter = 0;
|
|
1968
|
+
/** @internal */
|
|
1969
|
+
invalidHours = false;
|
|
1970
|
+
/** @internal */
|
|
1971
|
+
invalidMinutes = false;
|
|
1972
|
+
/** @internal */
|
|
1973
|
+
invalidSeconds = false;
|
|
1974
|
+
/** @internal */
|
|
1975
|
+
invalidMilliseconds = false;
|
|
1976
|
+
/**
|
|
1977
|
+
* @defaultValue
|
|
1978
|
+
* ```
|
|
1979
|
+
* `__si-timepicker-${SiTimepickerComponent.idCounter++}`
|
|
1980
|
+
* ```
|
|
1981
|
+
*/
|
|
1982
|
+
id = input(`__si-timepicker-${SiTimepickerComponent.idCounter++}`);
|
|
1983
|
+
labelledby = inject(new HostAttributeToken('aria-labelledby'), {
|
|
1984
|
+
optional: true
|
|
1985
|
+
}) ?? `${this.id()}-label`;
|
|
1986
|
+
/**
|
|
1987
|
+
* All input fields will be disabled if set to true.
|
|
1988
|
+
*
|
|
1989
|
+
* @defaultValue false
|
|
1990
|
+
*/
|
|
1991
|
+
// eslint-disable-next-line @angular-eslint/no-input-rename
|
|
1992
|
+
disabledInput = input(false, { alias: 'disabled' });
|
|
1993
|
+
/**
|
|
1994
|
+
* @defaultValue 'hh'
|
|
1995
|
+
*/
|
|
1996
|
+
hoursLabel = input('hh');
|
|
1997
|
+
/**
|
|
1998
|
+
* @defaultValue 'mm'
|
|
1999
|
+
*/
|
|
2000
|
+
minutesLabel = input('mm');
|
|
2001
|
+
/**
|
|
2002
|
+
* @defaultValue 'ss'
|
|
2003
|
+
*/
|
|
2004
|
+
secondsLabel = input('ss');
|
|
2005
|
+
/**
|
|
2006
|
+
* @defaultValue 'ms'
|
|
2007
|
+
*/
|
|
2008
|
+
millisecondsLabel = input('ms');
|
|
2009
|
+
/**
|
|
2010
|
+
* Hide the labels of the input fields.
|
|
2011
|
+
* @defaultValue false
|
|
2012
|
+
*/
|
|
2013
|
+
hideLabels = input(false, { transform: booleanAttribute });
|
|
2014
|
+
/**
|
|
2015
|
+
* @defaultValue
|
|
2016
|
+
* ```
|
|
2017
|
+
* $localize`:@@SI_DATEPICKER.HOURS:Hours`
|
|
2018
|
+
* ```
|
|
2019
|
+
*/
|
|
2020
|
+
hoursAriaLabel = input($localize `:@@SI_DATEPICKER.HOURS:Hours`);
|
|
2021
|
+
/**
|
|
2022
|
+
* @defaultValue
|
|
2023
|
+
* ```
|
|
2024
|
+
* $localize`:@@SI_DATEPICKER.MINUTES:Minutes`
|
|
2025
|
+
* ```
|
|
2026
|
+
*/
|
|
2027
|
+
minutesAriaLabel = input($localize `:@@SI_DATEPICKER.MINUTES:Minutes`);
|
|
2028
|
+
/**
|
|
2029
|
+
* @defaultValue
|
|
2030
|
+
* ```
|
|
2031
|
+
* $localize`:@@SI_DATEPICKER.SECONDS:Seconds`
|
|
2032
|
+
* ```
|
|
2033
|
+
*/
|
|
2034
|
+
secondsAriaLabel = input($localize `:@@SI_DATEPICKER.SECONDS:Seconds`);
|
|
2035
|
+
/**
|
|
2036
|
+
* @defaultValue
|
|
2037
|
+
* ```
|
|
2038
|
+
* $localize`:@@SI_DATEPICKER.MILLISECONDS:Milliseconds`
|
|
2039
|
+
* ```
|
|
2040
|
+
*/
|
|
2041
|
+
millisecondsAriaLabel = input($localize `:@@SI_DATEPICKER.MILLISECONDS:Milliseconds`);
|
|
2042
|
+
/**
|
|
2043
|
+
* @defaultValue 'hh'
|
|
2044
|
+
*/
|
|
2045
|
+
hoursPlaceholder = input('hh');
|
|
2046
|
+
/**
|
|
2047
|
+
* @defaultValue 'mm'
|
|
2048
|
+
*/
|
|
2049
|
+
minutesPlaceholder = input('mm');
|
|
2050
|
+
/**
|
|
2051
|
+
* @defaultValue 'ss'
|
|
2052
|
+
*/
|
|
2053
|
+
secondsPlaceholder = input('ss');
|
|
2054
|
+
/**
|
|
2055
|
+
* @defaultValue 'ms'
|
|
2056
|
+
*/
|
|
2057
|
+
millisecondsPlaceholder = input('ms');
|
|
2058
|
+
meridians = input();
|
|
2059
|
+
/**
|
|
2060
|
+
* @defaultValue 'am/pm'
|
|
2061
|
+
*/
|
|
2062
|
+
meridiansLabel = input('am/pm');
|
|
2063
|
+
/**
|
|
2064
|
+
* @defaultValue
|
|
2065
|
+
* ```
|
|
2066
|
+
* $localize`:@@SI_DATEPICKER.PERIOD:Period`
|
|
2067
|
+
* ```
|
|
2068
|
+
*/
|
|
2069
|
+
meridiansAriaLabel = input($localize `:@@SI_DATEPICKER.PERIOD:Period`);
|
|
2070
|
+
/** @defaultValue true */
|
|
2071
|
+
showMinutes = input(true, { transform: booleanAttribute });
|
|
2072
|
+
/** @defaultValue false */
|
|
2073
|
+
showSeconds = input(false, { transform: booleanAttribute });
|
|
2074
|
+
/** @defaultValue false */
|
|
2075
|
+
showMilliseconds = input(false, { transform: booleanAttribute });
|
|
2076
|
+
/**
|
|
2077
|
+
* Show time in 12-hour period including the select to toggle between AM/PM.
|
|
2078
|
+
*/
|
|
2079
|
+
showMeridian = input();
|
|
2080
|
+
/**
|
|
2081
|
+
* A minimum time limit. The date part of the date object will be ignored.
|
|
2082
|
+
*/
|
|
2083
|
+
min = input();
|
|
2084
|
+
/**
|
|
2085
|
+
* A maximum time limit. The date part of the date object will be ignored.
|
|
2086
|
+
*/
|
|
2087
|
+
max = input();
|
|
2088
|
+
/** @defaultValue false */
|
|
2089
|
+
readonly = input(false, { transform: booleanAttribute });
|
|
2090
|
+
isValid = output();
|
|
2091
|
+
meridianChange = output();
|
|
2092
|
+
inputCompleted = output();
|
|
2093
|
+
inputParts = viewChildren('inputPart');
|
|
2094
|
+
/** @internal */
|
|
2095
|
+
errormessageId = `${this.id()}-errormessage`;
|
|
2096
|
+
onChange = () => { };
|
|
2097
|
+
onTouched = () => { };
|
|
2098
|
+
// The following are the time values for the ui.
|
|
2099
|
+
hours = '';
|
|
2100
|
+
minutes = '';
|
|
2101
|
+
seconds = '';
|
|
2102
|
+
milliseconds = '';
|
|
2103
|
+
periods = computed(() => {
|
|
2104
|
+
const meridians = this.meridians();
|
|
2105
|
+
return meridians?.length ? meridians : this.periodDefaults;
|
|
2106
|
+
});
|
|
2107
|
+
use12HourClock = computed(() => this.showMeridian() ?? getLocaleTimeFormat(this.locale, FormatWidth.Full).includes('a'));
|
|
2108
|
+
disabled = computed(() => this.disabledInput() || this.disabledNgControl());
|
|
2109
|
+
meridian = signal('');
|
|
2110
|
+
disabledNgControl = signal(false);
|
|
2111
|
+
locale = inject(LOCALE_ID).toString();
|
|
2112
|
+
cdRef = inject(ChangeDetectorRef);
|
|
2113
|
+
/**
|
|
2114
|
+
* Holds the time as date object that is presented by this control.
|
|
2115
|
+
*/
|
|
2116
|
+
time;
|
|
2117
|
+
periodDefaults;
|
|
2118
|
+
constructor() {
|
|
2119
|
+
this.periodDefaults = getLocaleDayPeriods(this.locale, FormStyle.Format, TranslationWidth.Short).slice();
|
|
2120
|
+
}
|
|
2121
|
+
writeValue(obj) {
|
|
2122
|
+
if (this.isValidDate(obj)) {
|
|
2123
|
+
this.setTime(this.parseTime(obj));
|
|
2124
|
+
}
|
|
2125
|
+
else if (obj == null) {
|
|
2126
|
+
this.setTime();
|
|
2127
|
+
}
|
|
2128
|
+
if (obj) {
|
|
2129
|
+
this.isInputValid(this.hours, this.minutes, this.seconds, this.milliseconds, this.isPM());
|
|
2130
|
+
}
|
|
2131
|
+
this.cdRef.markForCheck();
|
|
2132
|
+
}
|
|
2133
|
+
/** @internal */
|
|
2134
|
+
isPM() {
|
|
2135
|
+
return this.use12HourClock() && this.meridian() === 'pm';
|
|
2136
|
+
}
|
|
2137
|
+
registerOnChange(fn) {
|
|
2138
|
+
this.onChange = fn;
|
|
2139
|
+
}
|
|
2140
|
+
registerOnTouched(fn) {
|
|
2141
|
+
this.onTouched = fn;
|
|
2142
|
+
}
|
|
2143
|
+
setDisabledState(isDisabled) {
|
|
2144
|
+
this.disabledNgControl.set(isDisabled);
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* Handle Enter, Arrow up/down and Space key press events.
|
|
2148
|
+
*/
|
|
2149
|
+
handleKeyPressEvent(event) {
|
|
2150
|
+
const target = event.target;
|
|
2151
|
+
switch (event.key) {
|
|
2152
|
+
case 'Enter':
|
|
2153
|
+
this.focusNext(event);
|
|
2154
|
+
break;
|
|
2155
|
+
case 'ArrowUp':
|
|
2156
|
+
case 'ArrowDown':
|
|
2157
|
+
if (!this.readonly()) {
|
|
2158
|
+
this.changeTimeComponent(target.name, event.key === 'ArrowUp');
|
|
2159
|
+
}
|
|
2160
|
+
else {
|
|
2161
|
+
event.preventDefault();
|
|
2162
|
+
}
|
|
2163
|
+
break;
|
|
2164
|
+
case ' ':
|
|
2165
|
+
if (this.readonly()) {
|
|
2166
|
+
event.preventDefault();
|
|
2167
|
+
}
|
|
2168
|
+
break;
|
|
2169
|
+
default:
|
|
2170
|
+
break;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
toHtmlInputElement = (target) => target;
|
|
2174
|
+
updateHours(value) {
|
|
2175
|
+
value = value.toString();
|
|
2176
|
+
if (this.hours !== value) {
|
|
2177
|
+
this.hours = value;
|
|
2178
|
+
const isValid = this.isHourInputValid(this.hours, this.isPM()) && this.isValidLimit();
|
|
2179
|
+
if (!isValid) {
|
|
2180
|
+
this.invalidHours = true;
|
|
2181
|
+
this.isValid.emit(false);
|
|
2182
|
+
this.onChange(null);
|
|
2183
|
+
}
|
|
2184
|
+
else {
|
|
2185
|
+
this.invalidHours = false;
|
|
2186
|
+
this.updateTime();
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
updateMinutes(value) {
|
|
2191
|
+
value = value.toString();
|
|
2192
|
+
if (this.minutes !== value) {
|
|
2193
|
+
this.minutes = value;
|
|
2194
|
+
const isValid = this.isMinuteInputValid(this.minutes) && this.isValidLimit();
|
|
2195
|
+
if (!isValid) {
|
|
2196
|
+
this.invalidMinutes = true;
|
|
2197
|
+
this.isValid.emit(false);
|
|
2198
|
+
this.onChange(null);
|
|
2199
|
+
}
|
|
2200
|
+
else {
|
|
2201
|
+
this.invalidMinutes = false;
|
|
2202
|
+
this.updateTime();
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
updateSeconds(value) {
|
|
2207
|
+
value = value.toString();
|
|
2208
|
+
if (this.seconds !== value) {
|
|
2209
|
+
this.seconds = value.toString();
|
|
2210
|
+
const isValid = this.isSecondInputValid(this.seconds) && this.isValidLimit();
|
|
2211
|
+
if (!isValid) {
|
|
2212
|
+
this.invalidSeconds = true;
|
|
2213
|
+
this.isValid.emit(false);
|
|
2214
|
+
this.onChange(null);
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
this.invalidSeconds = false;
|
|
2218
|
+
this.updateTime();
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
updateMilliseconds(value) {
|
|
2223
|
+
value = value.toString();
|
|
2224
|
+
if (this.milliseconds !== value) {
|
|
2225
|
+
this.milliseconds = value.toString();
|
|
2226
|
+
const isValid = this.isMillisecondInputValid(this.milliseconds) && this.isValidLimit();
|
|
2227
|
+
if (!isValid) {
|
|
2228
|
+
this.invalidMilliseconds = true;
|
|
2229
|
+
this.isValid.emit(false);
|
|
2230
|
+
this.onChange(null);
|
|
2231
|
+
}
|
|
2232
|
+
else {
|
|
2233
|
+
this.invalidMilliseconds = false;
|
|
2234
|
+
this.updateTime();
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
toggleMeridian() {
|
|
2239
|
+
const time = this.changeTime(this.time, { hour: 12 });
|
|
2240
|
+
this.setTime(time);
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Takes the current UI values and updates the time object value
|
|
2244
|
+
* accordingly, if they UI input values are valid.
|
|
2245
|
+
*/
|
|
2246
|
+
updateTime() {
|
|
2247
|
+
const minutes = this.showMinutes() ? this.minutes : undefined;
|
|
2248
|
+
const seconds = this.showSeconds() ? this.seconds : undefined;
|
|
2249
|
+
const milliseconds = this.showMilliseconds() ? this.milliseconds : undefined;
|
|
2250
|
+
if (!this.isInputValid(this.hours, minutes, seconds, milliseconds, this.isPM())) {
|
|
2251
|
+
this.isValid.emit(false);
|
|
2252
|
+
this.onChange(null);
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
const time = this.createDateUpdate(this.time, {
|
|
2256
|
+
hour: this.hours,
|
|
2257
|
+
minute: this.minutes,
|
|
2258
|
+
seconds: this.seconds,
|
|
2259
|
+
milliseconds: this.milliseconds,
|
|
2260
|
+
isPM: this.isPM()
|
|
2261
|
+
});
|
|
2262
|
+
this.setTime(time);
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Sets a new time object as model value, updates the user interface
|
|
2266
|
+
* and invokes onChange to let timepicker clients know about the update.
|
|
2267
|
+
* @param time - The new time to be set.
|
|
2268
|
+
*/
|
|
2269
|
+
setTime(time) {
|
|
2270
|
+
if (this.time !== time) {
|
|
2271
|
+
this.time = time;
|
|
2272
|
+
this.updateUI(this.time);
|
|
2273
|
+
this.onChange(this.time);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Updates the user interface by filling the time components
|
|
2278
|
+
* into the time input fields. Sets empty values if the date
|
|
2279
|
+
* is undefined or invalid.
|
|
2280
|
+
*
|
|
2281
|
+
* @param value - The date object or string from with the time components are taken.
|
|
2282
|
+
*/
|
|
2283
|
+
updateUI(value) {
|
|
2284
|
+
if (!value || !this.isValidDate(value)) {
|
|
2285
|
+
this.hours = '';
|
|
2286
|
+
this.minutes = '';
|
|
2287
|
+
this.seconds = '';
|
|
2288
|
+
this.milliseconds = '';
|
|
2289
|
+
this.meridian.set('am');
|
|
2290
|
+
this.meridianChange.emit(this.meridian());
|
|
2291
|
+
}
|
|
2292
|
+
else {
|
|
2293
|
+
const time = this.parseTime(value);
|
|
2294
|
+
if (!time) {
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
let hours = time.getHours();
|
|
2298
|
+
if (this.use12HourClock()) {
|
|
2299
|
+
this.meridian.set(hours >= 12 ? 'pm' : 'am');
|
|
2300
|
+
this.meridianChange.emit(this.meridian());
|
|
2301
|
+
hours = hours % 12;
|
|
2302
|
+
if (hours === 0) {
|
|
2303
|
+
hours = 12;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
this.hours = hours.toString().padStart(2, '0');
|
|
2307
|
+
this.minutes = time.getMinutes().toString().padStart(2, '0');
|
|
2308
|
+
this.seconds = time.getUTCSeconds().toString().padStart(2, '0');
|
|
2309
|
+
this.milliseconds = time.getUTCMilliseconds().toString().padStart(3, '0');
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
isValidDate(value) {
|
|
2313
|
+
if (!value) {
|
|
2314
|
+
return false;
|
|
2315
|
+
}
|
|
2316
|
+
if (typeof value === 'string') {
|
|
2317
|
+
return this.isValidDate(new Date(value));
|
|
2318
|
+
}
|
|
2319
|
+
if (value instanceof Date && isNaN(value.getHours())) {
|
|
2320
|
+
return false;
|
|
2321
|
+
}
|
|
2322
|
+
return true;
|
|
2323
|
+
}
|
|
2324
|
+
parseTime(value) {
|
|
2325
|
+
if (typeof value === 'string') {
|
|
2326
|
+
return new Date(value);
|
|
2327
|
+
}
|
|
2328
|
+
return value;
|
|
2329
|
+
}
|
|
2330
|
+
parseHours(value, isPM = false) {
|
|
2331
|
+
const hour = this.toNumber(value);
|
|
2332
|
+
if (isNaN(hour) || hour < 0 || hour > (isPM ? 12 : 24)) {
|
|
2333
|
+
return NaN;
|
|
2334
|
+
}
|
|
2335
|
+
return hour;
|
|
2336
|
+
}
|
|
2337
|
+
parseMinutes(value) {
|
|
2338
|
+
const minute = this.toNumber(value);
|
|
2339
|
+
if (isNaN(minute) || minute < 0 || minute > 60) {
|
|
2340
|
+
return NaN;
|
|
2341
|
+
}
|
|
2342
|
+
return minute;
|
|
2343
|
+
}
|
|
2344
|
+
parseSeconds(value) {
|
|
2345
|
+
const seconds = this.toNumber(value);
|
|
2346
|
+
if (isNaN(seconds) || seconds < 0 || seconds > 60) {
|
|
2347
|
+
return NaN;
|
|
2348
|
+
}
|
|
2349
|
+
return seconds;
|
|
2350
|
+
}
|
|
2351
|
+
parseMilliseconds(value) {
|
|
2352
|
+
const milliseconds = this.toNumber(value);
|
|
2353
|
+
if (isNaN(milliseconds) || milliseconds < 0 || milliseconds > 1000) {
|
|
2354
|
+
return NaN;
|
|
2355
|
+
}
|
|
2356
|
+
return milliseconds;
|
|
2357
|
+
}
|
|
2358
|
+
createDateUpdate(date, time) {
|
|
2359
|
+
let hour = this.parseHours(time.hour);
|
|
2360
|
+
const minute = this.parseMinutes(time.minute);
|
|
2361
|
+
const seconds = this.parseSeconds(time.seconds) || 0;
|
|
2362
|
+
const milliseconds = this.parseMilliseconds(time.milliseconds) || 0;
|
|
2363
|
+
if (time.isPM && hour !== 12) {
|
|
2364
|
+
hour += 12;
|
|
2365
|
+
}
|
|
2366
|
+
if (!date) {
|
|
2367
|
+
if (!isNaN(hour) && !isNaN(minute)) {
|
|
2368
|
+
return createDate(new Date(), hour, minute, seconds, milliseconds);
|
|
2369
|
+
}
|
|
2370
|
+
else {
|
|
2371
|
+
return date;
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
else if (isNaN(hour) || isNaN(minute)) {
|
|
2375
|
+
return date;
|
|
2376
|
+
}
|
|
2377
|
+
else {
|
|
2378
|
+
return createDate(date, hour, minute, seconds, milliseconds);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
toNumber(value) {
|
|
2382
|
+
if (typeof value === 'undefined') {
|
|
2383
|
+
return NaN;
|
|
2384
|
+
}
|
|
2385
|
+
else if (typeof value === 'number') {
|
|
2386
|
+
return value;
|
|
2387
|
+
}
|
|
2388
|
+
return parseInt(value, 10);
|
|
2389
|
+
}
|
|
2390
|
+
isInputValid(hours, minutes = '0', seconds = '0', milliseconds = '0', isPM) {
|
|
2391
|
+
if (!this.isValidLimit()) {
|
|
2392
|
+
this.invalidHours = true;
|
|
2393
|
+
this.invalidMinutes = true;
|
|
2394
|
+
this.invalidSeconds = true;
|
|
2395
|
+
this.invalidMilliseconds = true;
|
|
2396
|
+
}
|
|
2397
|
+
else {
|
|
2398
|
+
this.invalidHours = !this.isHourInputValid(hours, isPM);
|
|
2399
|
+
this.invalidMinutes = !this.isMinuteInputValid(minutes);
|
|
2400
|
+
this.invalidSeconds = !this.isSecondInputValid(seconds);
|
|
2401
|
+
this.invalidMilliseconds = !this.isMillisecondInputValid(milliseconds);
|
|
2402
|
+
}
|
|
2403
|
+
return (!this.invalidHours &&
|
|
2404
|
+
!this.invalidMinutes &&
|
|
2405
|
+
!this.invalidSeconds &&
|
|
2406
|
+
!this.invalidMilliseconds);
|
|
2407
|
+
}
|
|
2408
|
+
isHourInputValid(hours, isPM) {
|
|
2409
|
+
return !isNaN(this.parseHours(hours, isPM));
|
|
2410
|
+
}
|
|
2411
|
+
isMinuteInputValid(minutes) {
|
|
2412
|
+
return !isNaN(this.parseMinutes(minutes));
|
|
2413
|
+
}
|
|
2414
|
+
isSecondInputValid(seconds) {
|
|
2415
|
+
return !isNaN(this.parseSeconds(seconds));
|
|
2416
|
+
}
|
|
2417
|
+
isMillisecondInputValid(milliseconds) {
|
|
2418
|
+
return !isNaN(this.parseMilliseconds(milliseconds));
|
|
2419
|
+
}
|
|
2420
|
+
isValidLimit() {
|
|
2421
|
+
const refDate = new Date();
|
|
2422
|
+
const newDate = this.createDateUpdate(refDate, {
|
|
2423
|
+
hour: this.hours,
|
|
2424
|
+
minute: this.minutes,
|
|
2425
|
+
seconds: this.seconds,
|
|
2426
|
+
milliseconds: this.milliseconds,
|
|
2427
|
+
isPM: this.isPM()
|
|
2428
|
+
});
|
|
2429
|
+
if (!newDate) {
|
|
2430
|
+
return false;
|
|
2431
|
+
}
|
|
2432
|
+
let refMax;
|
|
2433
|
+
const max = this.max();
|
|
2434
|
+
if (max) {
|
|
2435
|
+
refMax = new Date(refDate);
|
|
2436
|
+
refMax.setHours(max.getHours());
|
|
2437
|
+
refMax.setMinutes(max.getMinutes());
|
|
2438
|
+
refMax.setSeconds(max.getSeconds());
|
|
2439
|
+
refMax.setMilliseconds(max.getMilliseconds());
|
|
2440
|
+
}
|
|
2441
|
+
let refMin;
|
|
2442
|
+
const min = this.min();
|
|
2443
|
+
if (min) {
|
|
2444
|
+
refMin = new Date(refDate);
|
|
2445
|
+
refMin.setHours(min.getHours());
|
|
2446
|
+
refMin.setMinutes(min.getMinutes());
|
|
2447
|
+
refMin.setSeconds(min.getSeconds());
|
|
2448
|
+
refMin.setMilliseconds(min.getMilliseconds());
|
|
2449
|
+
}
|
|
2450
|
+
if (refMax && newDate > refMax) {
|
|
2451
|
+
return false;
|
|
2452
|
+
}
|
|
2453
|
+
else if (refMin && newDate < refMin) {
|
|
2454
|
+
return false;
|
|
2455
|
+
}
|
|
2456
|
+
return true;
|
|
2457
|
+
}
|
|
2458
|
+
changeTimeComponent(key, up) {
|
|
2459
|
+
const change = up ? 1 : -1;
|
|
2460
|
+
const date = this.createDateUpdate(new Date(), {
|
|
2461
|
+
hour: this.hours,
|
|
2462
|
+
minute: this.minutes,
|
|
2463
|
+
seconds: this.seconds,
|
|
2464
|
+
milliseconds: this.milliseconds,
|
|
2465
|
+
isPM: this.isPM()
|
|
2466
|
+
});
|
|
2467
|
+
switch (key) {
|
|
2468
|
+
case 'hours': {
|
|
2469
|
+
const newTime = this.changeTime(date, { hour: change });
|
|
2470
|
+
let hour = newTime.getHours();
|
|
2471
|
+
if (this.use12HourClock()) {
|
|
2472
|
+
hour = hour % 12;
|
|
2473
|
+
if (hour === 0 && !this.isPM()) {
|
|
2474
|
+
hour = 12;
|
|
2475
|
+
}
|
|
2476
|
+
else if (hour === 0 && this.isPM()) {
|
|
2477
|
+
this.toggleMeridian();
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
this.updateHours(hour);
|
|
2481
|
+
break;
|
|
2482
|
+
}
|
|
2483
|
+
case 'minutes': {
|
|
2484
|
+
const newTime = this.changeTime(date, { minute: change });
|
|
2485
|
+
this.updateMinutes(newTime.getMinutes());
|
|
2486
|
+
break;
|
|
2487
|
+
}
|
|
2488
|
+
case 'seconds': {
|
|
2489
|
+
const newTime = this.changeTime(date, { seconds: change });
|
|
2490
|
+
this.updateSeconds(newTime.getSeconds());
|
|
2491
|
+
break;
|
|
2492
|
+
}
|
|
2493
|
+
case 'milliseconds': {
|
|
2494
|
+
const newTime = this.changeTime(date, { milliseconds: change });
|
|
2495
|
+
this.updateMilliseconds(newTime.getMilliseconds());
|
|
2496
|
+
break;
|
|
2497
|
+
}
|
|
2498
|
+
default:
|
|
2499
|
+
break;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
changeTime(value, diff) {
|
|
2503
|
+
if (!value) {
|
|
2504
|
+
return this.changeTime(createDate(new Date(), 0, 0, 0, 0), diff);
|
|
2505
|
+
}
|
|
2506
|
+
if (!diff) {
|
|
2507
|
+
return value;
|
|
2508
|
+
}
|
|
2509
|
+
let hour = value.getHours();
|
|
2510
|
+
let minutes = value.getMinutes();
|
|
2511
|
+
let seconds = value.getSeconds();
|
|
2512
|
+
let milliseconds = value.getMilliseconds();
|
|
2513
|
+
if (diff.hour) {
|
|
2514
|
+
hour = hour + this.toNumber(diff.hour);
|
|
2515
|
+
}
|
|
2516
|
+
if (diff.minute) {
|
|
2517
|
+
minutes = minutes + this.toNumber(diff.minute);
|
|
2518
|
+
}
|
|
2519
|
+
if (diff.seconds) {
|
|
2520
|
+
seconds = seconds + this.toNumber(diff.seconds);
|
|
2521
|
+
}
|
|
2522
|
+
if (diff.milliseconds) {
|
|
2523
|
+
milliseconds = milliseconds + this.toNumber(diff.milliseconds);
|
|
2524
|
+
}
|
|
2525
|
+
return createDate(value, hour, minutes, seconds, milliseconds);
|
|
2526
|
+
}
|
|
2527
|
+
/**
|
|
2528
|
+
* Focuses the next available input/select field or emit inputCompleted event.
|
|
2529
|
+
*/
|
|
2530
|
+
focusNext(event) {
|
|
2531
|
+
const target = event.target;
|
|
2532
|
+
if (!target) {
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const targets = this.inputParts();
|
|
2536
|
+
const position = targets?.findIndex(t => t.nativeElement === target);
|
|
2537
|
+
if (position === undefined || position === -1) {
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
if (position < targets.length - 1) {
|
|
2541
|
+
targets[position + 1].nativeElement.focus();
|
|
2542
|
+
}
|
|
2543
|
+
else {
|
|
2544
|
+
this.inputCompleted.emit();
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
focusChange(event) {
|
|
2548
|
+
if (event === null) {
|
|
2549
|
+
this.onTouched();
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiTimepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2553
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SiTimepickerComponent, isStandalone: true, selector: "si-timepicker", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, hoursLabel: { classPropertyName: "hoursLabel", publicName: "hoursLabel", isSignal: true, isRequired: false, transformFunction: null }, minutesLabel: { classPropertyName: "minutesLabel", publicName: "minutesLabel", isSignal: true, isRequired: false, transformFunction: null }, secondsLabel: { classPropertyName: "secondsLabel", publicName: "secondsLabel", isSignal: true, isRequired: false, transformFunction: null }, millisecondsLabel: { classPropertyName: "millisecondsLabel", publicName: "millisecondsLabel", isSignal: true, isRequired: false, transformFunction: null }, hideLabels: { classPropertyName: "hideLabels", publicName: "hideLabels", isSignal: true, isRequired: false, transformFunction: null }, hoursAriaLabel: { classPropertyName: "hoursAriaLabel", publicName: "hoursAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, minutesAriaLabel: { classPropertyName: "minutesAriaLabel", publicName: "minutesAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, secondsAriaLabel: { classPropertyName: "secondsAriaLabel", publicName: "secondsAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, millisecondsAriaLabel: { classPropertyName: "millisecondsAriaLabel", publicName: "millisecondsAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, hoursPlaceholder: { classPropertyName: "hoursPlaceholder", publicName: "hoursPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, minutesPlaceholder: { classPropertyName: "minutesPlaceholder", publicName: "minutesPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, secondsPlaceholder: { classPropertyName: "secondsPlaceholder", publicName: "secondsPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, millisecondsPlaceholder: { classPropertyName: "millisecondsPlaceholder", publicName: "millisecondsPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, meridians: { classPropertyName: "meridians", publicName: "meridians", isSignal: true, isRequired: false, transformFunction: null }, meridiansLabel: { classPropertyName: "meridiansLabel", publicName: "meridiansLabel", isSignal: true, isRequired: false, transformFunction: null }, meridiansAriaLabel: { classPropertyName: "meridiansAriaLabel", publicName: "meridiansAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, showMinutes: { classPropertyName: "showMinutes", publicName: "showMinutes", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, showMilliseconds: { classPropertyName: "showMilliseconds", publicName: "showMilliseconds", isSignal: true, isRequired: false, transformFunction: null }, showMeridian: { classPropertyName: "showMeridian", publicName: "showMeridian", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isValid: "isValid", meridianChange: "meridianChange", inputCompleted: "inputCompleted" }, host: { attributes: { "role": "group" }, properties: { "class.readonly": "readonly()", "attr.aria-labelledby": "labelledby" }, classAttribute: "form-custom-control" }, providers: [
|
|
2554
|
+
{
|
|
2555
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2556
|
+
useExisting: SiTimepickerComponent,
|
|
2557
|
+
multi: true
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
2561
|
+
useExisting: SiTimepickerComponent
|
|
2562
|
+
}
|
|
2563
|
+
], viewQueries: [{ propertyName: "inputParts", predicate: ["inputPart"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"d-flex flex-row flex-wrap\"\n cdkMonitorSubtreeFocus\n (cdkFocusChange)=\"focusChange($event)\"\n>\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (hoursLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"2[0-4]|[01]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"hours\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"hoursAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidHours\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"hoursPlaceholder()\"\n [value]=\"hours\"\n (change)=\"updateHours(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateHours(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n\n @if (showMinutes()) {\n <ng-container *ngTemplateOutlet=\"separator\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (minutesLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-5]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"minutes\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"minutesAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidMinutes\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"minutesPlaceholder()\"\n [value]=\"minutes\"\n (change)=\"updateMinutes(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateMinutes(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (showSeconds()) {\n <ng-container *ngTemplateOutlet=\"separator\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (secondsLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-5]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"seconds\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"secondsAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidSeconds\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"secondsPlaceholder()\"\n [value]=\"seconds\"\n (change)=\"updateSeconds(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateSeconds(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (showMilliseconds()) {\n <ng-container *ngTemplateOutlet=\"separator; context: { separator: '.' }\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (millisecondsLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-9]{1,3}\"\n class=\"form-control hide-feedback-icon\"\n name=\"milliseconds\"\n maxlength=\"3\"\n autocomplete=\"off\"\n [attr.aria-label]=\"millisecondsAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidMilliseconds\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"millisecondsPlaceholder()\"\n [value]=\"milliseconds\"\n (change)=\"updateMilliseconds(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateMilliseconds(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (use12HourClock()) {\n <label class=\"ms-2\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (meridiansLabel() | translate) || ' ' }}</span>\n }\n <select\n #inputPart\n class=\"form-control\"\n [attr.aria-label]=\"meridiansAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n (change)=\"toggleMeridian()\"\n (keydown)=\"handleKeyPressEvent($event)\"\n >\n <option value=\"am\" [selected]=\"meridian() === 'am'\">{{ periods()[0] }}</option>\n <option value=\"pm\" [selected]=\"meridian() === 'pm'\">{{ periods()[1] }}</option>\n </select>\n </label>\n }\n</div>\n\n<ng-template #separator let-separator=\"separator\">\n <div class=\"align-self-end pb-3 px-1\" aria-hidden=\"true\">{{ separator ? separator : ':' }}</div>\n</ng-template>\n", styles: [":host{display:block}.min-width{inline-size:100%;max-inline-size:45px;min-inline-size:35px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i2.SiTranslatePipe, name: "translate" }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i3.CdkMonitorFocus, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: ["cdkFocusChange"], exportAs: ["cdkMonitorFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2564
|
+
}
|
|
2565
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiTimepickerComponent, decorators: [{
|
|
2566
|
+
type: Component,
|
|
2567
|
+
args: [{ selector: 'si-timepicker', providers: [
|
|
2568
|
+
{
|
|
2569
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2570
|
+
useExisting: SiTimepickerComponent,
|
|
2571
|
+
multi: true
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
2575
|
+
useExisting: SiTimepickerComponent
|
|
2576
|
+
}
|
|
2577
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, FormsModule, SiTranslateModule, A11yModule], host: {
|
|
2578
|
+
role: 'group',
|
|
2579
|
+
class: 'form-custom-control',
|
|
2580
|
+
'[class.readonly]': 'readonly()',
|
|
2581
|
+
'[attr.aria-labelledby]': 'labelledby'
|
|
2582
|
+
}, template: "<div\n class=\"d-flex flex-row flex-wrap\"\n cdkMonitorSubtreeFocus\n (cdkFocusChange)=\"focusChange($event)\"\n>\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (hoursLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"2[0-4]|[01]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"hours\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"hoursAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidHours\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"hoursPlaceholder()\"\n [value]=\"hours\"\n (change)=\"updateHours(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateHours(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n\n @if (showMinutes()) {\n <ng-container *ngTemplateOutlet=\"separator\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (minutesLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-5]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"minutes\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"minutesAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidMinutes\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"minutesPlaceholder()\"\n [value]=\"minutes\"\n (change)=\"updateMinutes(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateMinutes(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (showSeconds()) {\n <ng-container *ngTemplateOutlet=\"separator\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (secondsLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-5]?[0-9]\"\n class=\"form-control hide-feedback-icon\"\n name=\"seconds\"\n maxlength=\"2\"\n autocomplete=\"off\"\n [attr.aria-label]=\"secondsAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidSeconds\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"secondsPlaceholder()\"\n [value]=\"seconds\"\n (change)=\"updateSeconds(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateSeconds(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (showMilliseconds()) {\n <ng-container *ngTemplateOutlet=\"separator; context: { separator: '.' }\" />\n <label class=\"min-width\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (millisecondsLabel() | translate) || ' ' }}</span>\n }\n <input\n #inputPart\n type=\"text\"\n inputmode=\"numeric\"\n pattern=\"[0-9]{1,3}\"\n class=\"form-control hide-feedback-icon\"\n name=\"milliseconds\"\n maxlength=\"3\"\n autocomplete=\"off\"\n [attr.aria-label]=\"millisecondsAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.is-invalid]=\"invalidMilliseconds\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"millisecondsPlaceholder()\"\n [value]=\"milliseconds\"\n (change)=\"updateMilliseconds(toHtmlInputElement($event.target).value)\"\n (blur)=\"updateMilliseconds(toHtmlInputElement($event.target).value)\"\n (keydown)=\"handleKeyPressEvent($event)\"\n />\n </label>\n }\n\n @if (use12HourClock()) {\n <label class=\"ms-2\">\n @if (!hideLabels()) {\n <span class=\"form-label\">{{ (meridiansLabel() | translate) || ' ' }}</span>\n }\n <select\n #inputPart\n class=\"form-control\"\n [attr.aria-label]=\"meridiansAriaLabel() | translate\"\n [attr.aria-describedby]=\"errormessageId\"\n [class.readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n (change)=\"toggleMeridian()\"\n (keydown)=\"handleKeyPressEvent($event)\"\n >\n <option value=\"am\" [selected]=\"meridian() === 'am'\">{{ periods()[0] }}</option>\n <option value=\"pm\" [selected]=\"meridian() === 'pm'\">{{ periods()[1] }}</option>\n </select>\n </label>\n }\n</div>\n\n<ng-template #separator let-separator=\"separator\">\n <div class=\"align-self-end pb-3 px-1\" aria-hidden=\"true\">{{ separator ? separator : ':' }}</div>\n</ng-template>\n", styles: [":host{display:block}.min-width{inline-size:100%;max-inline-size:45px;min-inline-size:35px}\n"] }]
|
|
2583
|
+
}], ctorParameters: () => [] });
|
|
2584
|
+
|
|
2585
|
+
/**
|
|
2586
|
+
* Copyright Siemens 2016 - 2025.
|
|
2587
|
+
* SPDX-License-Identifier: MIT
|
|
2588
|
+
*/
|
|
2589
|
+
let idCounter = 1;
|
|
2590
|
+
class SiDatepickerComponent {
|
|
2591
|
+
locale = inject(LOCALE_ID).toString();
|
|
2592
|
+
/**
|
|
2593
|
+
* The date which is currently focused
|
|
2594
|
+
* Compare to the selected date or range the calendar requires to have one element to focus.
|
|
2595
|
+
*/
|
|
2596
|
+
focusedDate = model();
|
|
2597
|
+
/**
|
|
2598
|
+
* The selected date of the datepicker. Use for
|
|
2599
|
+
* initialization and for bidirectional binding.
|
|
2600
|
+
*/
|
|
2601
|
+
date = model();
|
|
2602
|
+
/**
|
|
2603
|
+
* The selected date range of the datepicker. Use for
|
|
2604
|
+
* initialization and for bidirectional binding.
|
|
2605
|
+
*/
|
|
2606
|
+
dateRange = model();
|
|
2607
|
+
/** @internal */
|
|
2608
|
+
dateRangeRole = input();
|
|
2609
|
+
/**
|
|
2610
|
+
* Set initial focus to calendar body.
|
|
2611
|
+
*
|
|
2612
|
+
* @defaultValue false
|
|
2613
|
+
*/
|
|
2614
|
+
initialFocus = input(false);
|
|
2615
|
+
/**
|
|
2616
|
+
* Disabled the optional visible time picker.
|
|
2617
|
+
*
|
|
2618
|
+
* @defaultValue false
|
|
2619
|
+
*/
|
|
2620
|
+
disabledTime = model(false);
|
|
2621
|
+
/**
|
|
2622
|
+
* Object to configure the datepicker.
|
|
2623
|
+
*
|
|
2624
|
+
* @defaultValue
|
|
2625
|
+
* ```
|
|
2626
|
+
* {}
|
|
2627
|
+
* ```
|
|
2628
|
+
*/
|
|
2629
|
+
config = model({});
|
|
2630
|
+
/**
|
|
2631
|
+
* Aria label for the previous button. Needed for a11y.
|
|
2632
|
+
*
|
|
2633
|
+
* @defaultValue
|
|
2634
|
+
* ```
|
|
2635
|
+
* $localize`:@@SI_DATEPICKER.PREVIOUS:Previous`
|
|
2636
|
+
* ```
|
|
2637
|
+
*/
|
|
2638
|
+
previousLabel = input($localize `:@@SI_DATEPICKER.PREVIOUS:Previous`);
|
|
2639
|
+
/**
|
|
2640
|
+
* Aria label for the next button. Needed for a11y.
|
|
2641
|
+
*
|
|
2642
|
+
* @defaultValue
|
|
2643
|
+
* ```
|
|
2644
|
+
* $localize`:@@SI_DATEPICKER.NEXT:Next`
|
|
2645
|
+
* ```
|
|
2646
|
+
*/
|
|
2647
|
+
nextLabel = input($localize `:@@SI_DATEPICKER.NEXT:Next`);
|
|
2648
|
+
/**
|
|
2649
|
+
* Aria label for week number column
|
|
2650
|
+
*
|
|
2651
|
+
* @defaultValue
|
|
2652
|
+
* ```
|
|
2653
|
+
* $localize`:@@SI_DATEPICKER.CALENDAR_WEEK_LABEL:Calendar week`
|
|
2654
|
+
* ```
|
|
2655
|
+
*/
|
|
2656
|
+
calenderWeekLabel = input($localize `:@@SI_DATEPICKER.CALENDAR_WEEK_LABEL:Calendar week`);
|
|
2657
|
+
/**
|
|
2658
|
+
* Enable/disable 12H mode in timepicker. Defaults to locale
|
|
2659
|
+
*
|
|
2660
|
+
* @defaultValue undefined
|
|
2661
|
+
*/
|
|
2662
|
+
time12h = input(undefined, {
|
|
2663
|
+
transform: booleanAttribute
|
|
2664
|
+
});
|
|
2665
|
+
/**
|
|
2666
|
+
* Use this to force date range operation to select either start date or end date.
|
|
2667
|
+
*
|
|
2668
|
+
* @defaultValue 'START'
|
|
2669
|
+
*/
|
|
2670
|
+
rangeType = model('START');
|
|
2671
|
+
/**
|
|
2672
|
+
* Optional input to control the minimum month the datepicker can show and the user can navigate.
|
|
2673
|
+
* The `minMonth` can be larger than the `minDate` This option enables the usage of multiple
|
|
2674
|
+
* datepickers next to each other while the more left calendar always
|
|
2675
|
+
* shows a earlier month the the more right one.
|
|
2676
|
+
* @internal
|
|
2677
|
+
*/
|
|
2678
|
+
minMonth = input();
|
|
2679
|
+
/**
|
|
2680
|
+
* Optional input to control the maximum month the datepicker can show and the user can navigate.
|
|
2681
|
+
* The `maxMonth` can be smaller than the `maxDate` This option enables the usage of multiple
|
|
2682
|
+
* datepickers next to each other while the more left calendar always
|
|
2683
|
+
* shows a earlier month the the more right one.
|
|
2684
|
+
* @internal
|
|
2685
|
+
*/
|
|
2686
|
+
maxMonth = input();
|
|
2687
|
+
/**
|
|
2688
|
+
* Option to hide the time switch.
|
|
2689
|
+
*
|
|
2690
|
+
* @defaultValue false
|
|
2691
|
+
*/
|
|
2692
|
+
hideTimeToggle = input(false);
|
|
2693
|
+
/** @internal */
|
|
2694
|
+
hideCalendar = input(false);
|
|
2695
|
+
/**
|
|
2696
|
+
* Optional timepicker label.
|
|
2697
|
+
*/
|
|
2698
|
+
timepickerLabel = input();
|
|
2699
|
+
get startDate() {
|
|
2700
|
+
return this.config().enableDateRange ? this.dateRange()?.start : this.date();
|
|
2701
|
+
}
|
|
2702
|
+
get endDate() {
|
|
2703
|
+
return this.config().enableDateRange ? this.dateRange()?.end : undefined;
|
|
2704
|
+
}
|
|
2705
|
+
/**
|
|
2706
|
+
* Returns the date object if not range selection is enabled. Otherwise, if
|
|
2707
|
+
* the date range role is 'END', the date range end date is returned. If
|
|
2708
|
+
* date range role is not 'END', the date range start date is returned.
|
|
2709
|
+
*/
|
|
2710
|
+
getRelevantDate() {
|
|
2711
|
+
return !this.config().enableDateRange
|
|
2712
|
+
? this.date()
|
|
2713
|
+
: this.dateRangeRole() === 'END'
|
|
2714
|
+
? this.dateRange()?.end
|
|
2715
|
+
: this.dateRange()?.start;
|
|
2716
|
+
}
|
|
2717
|
+
defaultDisabledTimeText = $localize `:@@SI_DATEPICKER.DISABLED_TIME_TEXT:Ignore time`;
|
|
2718
|
+
defaultEnableTimeText = $localize `:@@SI_DATEPICKER.ENABLED_TIME_TEXT:Consider time`;
|
|
2719
|
+
includeTimeLabel = computed(() => this.disabledTime()
|
|
2720
|
+
? (this.config().disabledTimeText ?? this.defaultDisabledTimeText)
|
|
2721
|
+
: (this.config().enabledTimeText ?? this.defaultEnableTimeText));
|
|
2722
|
+
get weekStartDay() {
|
|
2723
|
+
return this.config().weekStartDay ?? this.localeWeekStart;
|
|
2724
|
+
}
|
|
2725
|
+
get hideWeekNumbers() {
|
|
2726
|
+
return this.config().hideWeekNumbers ?? false;
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* The active view
|
|
2730
|
+
*/
|
|
2731
|
+
view = signal('week');
|
|
2732
|
+
/**
|
|
2733
|
+
* Get the current shown view.
|
|
2734
|
+
*/
|
|
2735
|
+
activeView = computed(() => {
|
|
2736
|
+
switch (this.view()) {
|
|
2737
|
+
case 'month':
|
|
2738
|
+
return this.monthSelection();
|
|
2739
|
+
case 'year':
|
|
2740
|
+
return this.yearSelection();
|
|
2741
|
+
default:
|
|
2742
|
+
return this.daySelection();
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
actualFocusedDate = computed(() => this.focusedDate() ?? today());
|
|
2746
|
+
requireFocus = signal(this.initialFocus());
|
|
2747
|
+
/** When the user switch from the year or month view via keyboard selection we force the focus. */
|
|
2748
|
+
forceFocus = computed(() => this.requireFocus() || this.initialFocus());
|
|
2749
|
+
months = [];
|
|
2750
|
+
switchId = `__si-datepicker-switch-id-${idCounter++}`;
|
|
2751
|
+
timepickerId = `__si-datepicker-timepicker-id-${idCounter++}`;
|
|
2752
|
+
/**
|
|
2753
|
+
* Configuration which view shall be shown after year selection,
|
|
2754
|
+
* when onlyMonthSelection is enabled the month view is shown otherwise the week view.
|
|
2755
|
+
*/
|
|
2756
|
+
yearViewSwitchTo = 'week';
|
|
2757
|
+
monthViewSwitchTo = 'week';
|
|
2758
|
+
cdRef = inject(ChangeDetectorRef);
|
|
2759
|
+
localeWeekStart;
|
|
2760
|
+
/**
|
|
2761
|
+
* Date object to track and change the time. Keeping time and date
|
|
2762
|
+
* in separate objects to not change the date when flipping time.
|
|
2763
|
+
* After change, a new date object is created with an adapted time.
|
|
2764
|
+
*/
|
|
2765
|
+
time;
|
|
2766
|
+
/**
|
|
2767
|
+
* Used to hold the last time when setting the time to disabled.
|
|
2768
|
+
* Value will be reset on enabling the time again.
|
|
2769
|
+
*/
|
|
2770
|
+
previousTime;
|
|
2771
|
+
timePicker = viewChild(SiTimepickerComponent);
|
|
2772
|
+
/** Reference to the current day selection component. Shown when view === 'week' */
|
|
2773
|
+
daySelection = viewChild(SiDaySelectionComponent);
|
|
2774
|
+
/** Reference to the current month selection component. Shown when view === 'month' */
|
|
2775
|
+
monthSelection = viewChild(SiMonthSelectionComponent);
|
|
2776
|
+
/** Reference to the current year selection component. Shown when view === 'year' */
|
|
2777
|
+
yearSelection = viewChild(SiYearSelectionComponent);
|
|
2778
|
+
/**
|
|
2779
|
+
* The cell which which has the mouse hover.
|
|
2780
|
+
* @internal
|
|
2781
|
+
*/
|
|
2782
|
+
activeHover = model();
|
|
2783
|
+
constructor() {
|
|
2784
|
+
this.initCalendarLabels();
|
|
2785
|
+
const weekStart = getLocaleFirstDayOfWeek(this.locale);
|
|
2786
|
+
this.localeWeekStart =
|
|
2787
|
+
weekStart === WeekDay.Sunday
|
|
2788
|
+
? 'sunday'
|
|
2789
|
+
: weekStart === WeekDay.Saturday
|
|
2790
|
+
? 'saturday'
|
|
2791
|
+
: 'monday';
|
|
2792
|
+
}
|
|
2793
|
+
ngOnChanges(changes) {
|
|
2794
|
+
const config = this.config();
|
|
2795
|
+
if (changes.date && !config.enableDateRange) {
|
|
2796
|
+
if (this.date() && !isValid(this.date())) {
|
|
2797
|
+
this.date.set(undefined);
|
|
2798
|
+
}
|
|
2799
|
+
const date = this.date();
|
|
2800
|
+
if (date) {
|
|
2801
|
+
if (changes.date.isFirstChange()) {
|
|
2802
|
+
this.previousTime = new Date(date);
|
|
2803
|
+
this.time = date;
|
|
2804
|
+
}
|
|
2805
|
+
if (this.time?.getTime() !== date?.getTime()) {
|
|
2806
|
+
this.time = date;
|
|
2807
|
+
}
|
|
2808
|
+
this.focusedDate.set(date);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
if (changes.config?.currentValue?.disabledTime) {
|
|
2812
|
+
this.disabledTime.set(changes.config?.currentValue?.disabledTime);
|
|
2813
|
+
this.onDisabledTimeChanged();
|
|
2814
|
+
}
|
|
2815
|
+
if (changes.config?.firstChange) {
|
|
2816
|
+
if (config.onlyMonthSelection) {
|
|
2817
|
+
this.yearViewSwitchTo = 'month';
|
|
2818
|
+
this.monthViewSwitchTo = 'month';
|
|
2819
|
+
this.view.set('month');
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
// Date-range input field has changed
|
|
2823
|
+
if (changes.dateRange) {
|
|
2824
|
+
// Ensure the dateRange object only contain valid start/end dates
|
|
2825
|
+
const dateRange = this.dateRange();
|
|
2826
|
+
if (dateRange) {
|
|
2827
|
+
if (!isValid(dateRange?.start)) {
|
|
2828
|
+
dateRange.start = undefined;
|
|
2829
|
+
}
|
|
2830
|
+
if (!isValid(dateRange?.end)) {
|
|
2831
|
+
dateRange.end = undefined;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
// Only one calendar is used when no dateRangeRole is available.
|
|
2835
|
+
const dateRangeRole = this.dateRangeRole();
|
|
2836
|
+
if (!dateRangeRole) {
|
|
2837
|
+
const previous = changes.dateRange.previousValue;
|
|
2838
|
+
if (dateRange?.end && !isSameDate(dateRange.end, previous?.end)) {
|
|
2839
|
+
this.focusedDate.set(dateRange.end);
|
|
2840
|
+
}
|
|
2841
|
+
if (dateRange?.start && !isSameDate(dateRange.start, previous?.start)) {
|
|
2842
|
+
this.focusedDate.set(dateRange.start);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
else {
|
|
2846
|
+
// Date range selection with two calendars
|
|
2847
|
+
const newDate = dateRangeRole === 'START' ? dateRange?.start : dateRange?.end;
|
|
2848
|
+
if (newDate && changes.dateRange.isFirstChange()) {
|
|
2849
|
+
this.previousTime = new Date(newDate);
|
|
2850
|
+
this.time = newDate;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
if (changes.minMonth?.currentValue &&
|
|
2855
|
+
isAfter(changes.minMonth.currentValue, this.actualFocusedDate())) {
|
|
2856
|
+
this.focusedDate.set(changes.minMonth.currentValue);
|
|
2857
|
+
}
|
|
2858
|
+
if (changes.maxMonth?.currentValue &&
|
|
2859
|
+
isAfter(this.actualFocusedDate(), changes.maxMonth.currentValue)) {
|
|
2860
|
+
this.focusedDate.set(changes.maxMonth.currentValue);
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
ngOnInit() {
|
|
2864
|
+
const config = this.config();
|
|
2865
|
+
const dateRange = this.dateRange();
|
|
2866
|
+
const dateRangeRole = this.dateRangeRole();
|
|
2867
|
+
if (config.enableDateRange && this.rangeType() === 'END' && dateRange?.end && !dateRangeRole) {
|
|
2868
|
+
// The user chose to trigger the datepicker from the date-range end, in this
|
|
2869
|
+
// case we
|
|
2870
|
+
this.focusedDate.set(dateRange.end);
|
|
2871
|
+
}
|
|
2872
|
+
else if (config.enableDateRange && dateRangeRole === 'START') {
|
|
2873
|
+
const maxMonth = config?.onlyMonthSelection ? this.maxMonth() : config.maxDate;
|
|
2874
|
+
this.focusedDate.set(getDateSameOrBetween(dateRange?.start ? dateRange.start : (this.focusedDate() ?? today()), config.minDate, maxMonth));
|
|
2875
|
+
}
|
|
2876
|
+
else if (config.enableDateRange && dateRangeRole === 'END') {
|
|
2877
|
+
const minMonth = config?.onlyMonthSelection ? this.minMonth() : config.minDate;
|
|
2878
|
+
this.focusedDate.set(getDateSameOrBetween(dateRange?.end ? dateRange.end : (this.focusedDate() ?? today()), minMonth, config.maxDate));
|
|
2879
|
+
}
|
|
2880
|
+
else {
|
|
2881
|
+
this.focusedDate.set(getDateSameOrBetween(isValid(this.startDate) ? this.startDate : (this.focusedDate() ?? today()), config.minDate, config.maxDate));
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
ngAfterViewInit() {
|
|
2885
|
+
// After the view is created the first time we want that the children components set
|
|
2886
|
+
// the focus to the calendarBody. Means when we select a date in month-selection,
|
|
2887
|
+
// the day selection shall focus automatically the day in calendarBody.
|
|
2888
|
+
setTimeout(() => this.requireFocus.set(true));
|
|
2889
|
+
}
|
|
2890
|
+
/** Initialize day and month labels */
|
|
2891
|
+
initCalendarLabels() {
|
|
2892
|
+
this.months = getLocaleMonthNames(this.locale.toString());
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Validates and sets a new date to the this.date model object of this component
|
|
2896
|
+
* and fires the related events. The model object shall not be updated elsewhere
|
|
2897
|
+
* with a new date object. Shall only be called on simple date selection and not
|
|
2898
|
+
* on date range selection.
|
|
2899
|
+
*
|
|
2900
|
+
* @param newDate - The new date to be set.
|
|
2901
|
+
*/
|
|
2902
|
+
setDate(newDate) {
|
|
2903
|
+
const dateWithoutTime = getDateWithoutTime(newDate);
|
|
2904
|
+
const config = this.config();
|
|
2905
|
+
const validForMinDate = !(config?.minDate && dateWithoutTime < getDateWithoutTime(config.minDate));
|
|
2906
|
+
const configValue = this.config();
|
|
2907
|
+
const validForMaxDate = !(configValue?.maxDate && dateWithoutTime > getDateWithoutTime(configValue.maxDate));
|
|
2908
|
+
const date = this.date();
|
|
2909
|
+
if (date !== newDate && validForMinDate && validForMaxDate) {
|
|
2910
|
+
const previousValue = date;
|
|
2911
|
+
this.date.set(newDate);
|
|
2912
|
+
// eslint-disable-next-line @angular-eslint/no-lifecycle-call
|
|
2913
|
+
this.ngOnChanges({
|
|
2914
|
+
date: new SimpleChange(previousValue, date, previousValue === undefined)
|
|
2915
|
+
});
|
|
2916
|
+
//this.dateChange.emit(date);
|
|
2917
|
+
}
|
|
2918
|
+
else if (!validForMinDate || !validForMaxDate) {
|
|
2919
|
+
// eslint-disable-next-line @angular-eslint/no-lifecycle-call
|
|
2920
|
+
this.ngOnChanges({ date: new SimpleChange(undefined, date, true) });
|
|
2921
|
+
}
|
|
2922
|
+
if (config.enableTimeValidation && this.timePicker() && (config.minDate || config.maxDate)) {
|
|
2923
|
+
this.validateTime(newDate);
|
|
2924
|
+
}
|
|
2925
|
+
this.cdRef.markForCheck();
|
|
2926
|
+
}
|
|
2927
|
+
/**
|
|
2928
|
+
* Validates and sets the new date range to the dateRange model
|
|
2929
|
+
* object.
|
|
2930
|
+
* @param newDateRange - The new range to be set.
|
|
2931
|
+
* @returns True if the new range is valid and set. Otherwise false.
|
|
2932
|
+
*/
|
|
2933
|
+
setDateRange(newDateRange) {
|
|
2934
|
+
const config = this.config();
|
|
2935
|
+
if (newDateRange.start) {
|
|
2936
|
+
const isValidRange = isSameOrBetween(newDateRange.start, config?.minDate, config?.maxDate);
|
|
2937
|
+
if (!isValidRange) {
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
if (newDateRange.end) {
|
|
2942
|
+
const isValidRange = isSameOrBetween(newDateRange.end, config?.minDate, config?.maxDate);
|
|
2943
|
+
if (!isValidRange) {
|
|
2944
|
+
return false;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
this.dateRange.set(newDateRange);
|
|
2948
|
+
return true;
|
|
2949
|
+
}
|
|
2950
|
+
timeSelected(newTime) {
|
|
2951
|
+
if (!newTime) {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
// Break event cycle
|
|
2955
|
+
if (this.time?.getTime() === newTime.getTime()) {
|
|
2956
|
+
this.validateTime(newTime);
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
this.previousTime = this.time;
|
|
2960
|
+
this.time = newTime;
|
|
2961
|
+
const oldDate = this.getRelevantDate() ?? new Date();
|
|
2962
|
+
let newDate;
|
|
2963
|
+
if (this.disabledTime()) {
|
|
2964
|
+
// if time is disabled, ensure that 00:00:00 is displayed in any timezone
|
|
2965
|
+
newDate = createDate(oldDate);
|
|
2966
|
+
this.time = newDate;
|
|
2967
|
+
}
|
|
2968
|
+
else {
|
|
2969
|
+
newDate = createDate(oldDate, this.time.getHours(), this.time.getMinutes(), this.time.getSeconds(), this.time.getMilliseconds());
|
|
2970
|
+
}
|
|
2971
|
+
if (!this.config().enableDateRange) {
|
|
2972
|
+
this.setDate(newDate);
|
|
2973
|
+
}
|
|
2974
|
+
else {
|
|
2975
|
+
const newDateRange = this.dateRangeRole() === 'START'
|
|
2976
|
+
? { start: newDate, end: this.dateRange()?.end }
|
|
2977
|
+
: { start: this.dateRange()?.start, end: newDate };
|
|
2978
|
+
this.setDateRange(newDateRange);
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
toggleDisabledTime() {
|
|
2982
|
+
this.disabledTime.update(previous => !previous);
|
|
2983
|
+
this.config.update(c => {
|
|
2984
|
+
c.disabledTime = this.disabledTime();
|
|
2985
|
+
return c;
|
|
2986
|
+
});
|
|
2987
|
+
this.onDisabledTimeChanged();
|
|
2988
|
+
}
|
|
2989
|
+
onDisabledTimeChanged() {
|
|
2990
|
+
if (!this.config().enableDateRange) {
|
|
2991
|
+
if (this.disabledTime()) {
|
|
2992
|
+
const date = this.date() ?? new Date();
|
|
2993
|
+
const newTime = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0));
|
|
2994
|
+
this.timeSelected(newTime);
|
|
2995
|
+
}
|
|
2996
|
+
else if (this.previousTime) {
|
|
2997
|
+
this.timeSelected(this.previousTime);
|
|
2998
|
+
}
|
|
2999
|
+
else {
|
|
3000
|
+
this.timeSelected(new Date());
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
validateTime(date) {
|
|
3005
|
+
// wait for a cycle to initialize timepicker
|
|
3006
|
+
setTimeout(() => {
|
|
3007
|
+
const config = this.config();
|
|
3008
|
+
const timePicker = this.timePicker();
|
|
3009
|
+
if (!this.disabledTime() &&
|
|
3010
|
+
((config.minDate && date < config.minDate) || (config.maxDate && date > config.maxDate))) {
|
|
3011
|
+
timePicker.invalidHours = timePicker.invalidMinutes = true;
|
|
3012
|
+
timePicker.invalidSeconds = timePicker.invalidMilliseconds = true;
|
|
3013
|
+
}
|
|
3014
|
+
else {
|
|
3015
|
+
timePicker.invalidHours = timePicker.invalidMinutes = false;
|
|
3016
|
+
timePicker.invalidSeconds = timePicker.invalidMilliseconds = false;
|
|
3017
|
+
}
|
|
3018
|
+
this.cdRef.markForCheck();
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
/**
|
|
3022
|
+
* Handle selection in the day view.
|
|
3023
|
+
* @param selection - selected date.
|
|
3024
|
+
*/
|
|
3025
|
+
selectionChange(selection) {
|
|
3026
|
+
const newDate = createDate(selection, this.time?.getHours(), this.time?.getMinutes(), this.time?.getSeconds(), this.time?.getMilliseconds());
|
|
3027
|
+
if (this.config().enableDateRange) {
|
|
3028
|
+
const rangeType = this.rangeType();
|
|
3029
|
+
let newDateRange = !rangeType || rangeType === 'START'
|
|
3030
|
+
? { start: newDate, end: undefined }
|
|
3031
|
+
: { start: this.dateRange()?.start, end: newDate };
|
|
3032
|
+
let newRangeType = !rangeType || rangeType === 'START' ? 'START' : 'END';
|
|
3033
|
+
// The user selected a date before the current range start. Now the clicked day
|
|
3034
|
+
// is used as new start and the end is cleared
|
|
3035
|
+
if (newDateRange.start && newDateRange.end && newDateRange.end < newDateRange.start) {
|
|
3036
|
+
newDateRange = { start: newDateRange.end, end: undefined };
|
|
3037
|
+
newRangeType = 'START'; // Switch back to start so that the next selection is end
|
|
3038
|
+
}
|
|
3039
|
+
// Reset end range when we started start
|
|
3040
|
+
if (newRangeType === 'START' && newDateRange.end) {
|
|
3041
|
+
newDateRange.end = undefined;
|
|
3042
|
+
}
|
|
3043
|
+
const rangeValid = this.setDateRange(newDateRange);
|
|
3044
|
+
if (rangeValid) {
|
|
3045
|
+
// Toggle rangeType every time the user uses the datepicker to change the range
|
|
3046
|
+
this.rangeType.set(newRangeType === 'START' ? 'END' : 'START');
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
else {
|
|
3050
|
+
this.focusedDate.set(newDate);
|
|
3051
|
+
this.setDate(newDate);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* Handle month/year changes
|
|
3056
|
+
* @param selection - the selected month or null of cancelled.
|
|
3057
|
+
*/
|
|
3058
|
+
activeMonthChange(selection) {
|
|
3059
|
+
if (selection) {
|
|
3060
|
+
this.focusedDate.set(changeDay(selection, this.actualFocusedDate().getDate()));
|
|
3061
|
+
const config = this.config();
|
|
3062
|
+
if (config.onlyMonthSelection) {
|
|
3063
|
+
if (config.enableDateRange) {
|
|
3064
|
+
this.selectionChange(selection);
|
|
3065
|
+
}
|
|
3066
|
+
else {
|
|
3067
|
+
this.setDate(selection);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
this.view.set(this.monthViewSwitchTo);
|
|
3072
|
+
}
|
|
3073
|
+
/**
|
|
3074
|
+
* Handle year changes
|
|
3075
|
+
* @param selection - the selected year or null of cancelled.
|
|
3076
|
+
*/
|
|
3077
|
+
activeYearChange(selection) {
|
|
3078
|
+
if (selection) {
|
|
3079
|
+
selection.setMonth(this.actualFocusedDate().getMonth());
|
|
3080
|
+
this.focusedDate.set(changeDay(selection, this.actualFocusedDate().getDate()));
|
|
3081
|
+
}
|
|
3082
|
+
this.view.set(this.yearViewSwitchTo);
|
|
3083
|
+
}
|
|
3084
|
+
/**
|
|
3085
|
+
* Focus the active cell in view.
|
|
3086
|
+
* The function is required to transfer the focus from input to the active date cell.
|
|
3087
|
+
*/
|
|
3088
|
+
focusActiveCell() {
|
|
3089
|
+
this.activeView()?.focusActiveCell();
|
|
3090
|
+
}
|
|
3091
|
+
onActiveHoverChange(event) {
|
|
3092
|
+
this.activeHover.set(event);
|
|
3093
|
+
}
|
|
3094
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3095
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SiDatepickerComponent, isStandalone: true, selector: "si-datepicker", inputs: { focusedDate: { classPropertyName: "focusedDate", publicName: "focusedDate", isSignal: true, isRequired: false, transformFunction: null }, date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, dateRange: { classPropertyName: "dateRange", publicName: "dateRange", isSignal: true, isRequired: false, transformFunction: null }, dateRangeRole: { classPropertyName: "dateRangeRole", publicName: "dateRangeRole", isSignal: true, isRequired: false, transformFunction: null }, initialFocus: { classPropertyName: "initialFocus", publicName: "initialFocus", isSignal: true, isRequired: false, transformFunction: null }, disabledTime: { classPropertyName: "disabledTime", publicName: "disabledTime", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, previousLabel: { classPropertyName: "previousLabel", publicName: "previousLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, calenderWeekLabel: { classPropertyName: "calenderWeekLabel", publicName: "calenderWeekLabel", isSignal: true, isRequired: false, transformFunction: null }, time12h: { classPropertyName: "time12h", publicName: "time12h", isSignal: true, isRequired: false, transformFunction: null }, rangeType: { classPropertyName: "rangeType", publicName: "rangeType", isSignal: true, isRequired: false, transformFunction: null }, minMonth: { classPropertyName: "minMonth", publicName: "minMonth", isSignal: true, isRequired: false, transformFunction: null }, maxMonth: { classPropertyName: "maxMonth", publicName: "maxMonth", isSignal: true, isRequired: false, transformFunction: null }, hideTimeToggle: { classPropertyName: "hideTimeToggle", publicName: "hideTimeToggle", isSignal: true, isRequired: false, transformFunction: null }, hideCalendar: { classPropertyName: "hideCalendar", publicName: "hideCalendar", isSignal: true, isRequired: false, transformFunction: null }, timepickerLabel: { classPropertyName: "timepickerLabel", publicName: "timepickerLabel", isSignal: true, isRequired: false, transformFunction: null }, activeHover: { classPropertyName: "activeHover", publicName: "activeHover", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { focusedDate: "focusedDateChange", date: "dateChange", dateRange: "dateRangeChange", disabledTime: "disabledTimeChange", config: "configChange", rangeType: "rangeTypeChange", activeHover: "activeHoverChange" }, viewQueries: [{ propertyName: "timePicker", first: true, predicate: SiTimepickerComponent, descendants: true, isSignal: true }, { propertyName: "daySelection", first: true, predicate: SiDaySelectionComponent, descendants: true, isSignal: true }, { propertyName: "monthSelection", first: true, predicate: SiMonthSelectionComponent, descendants: true, isSignal: true }, { propertyName: "yearSelection", first: true, predicate: SiYearSelectionComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "@if (!hideCalendar()) {\n <div class=\"text-center pb-6 calendar\">\n @switch (view()) {\n @case ('year') {\n <si-year-selection\n [focusedDate]=\"actualFocusedDate()\"\n [startDate]=\"startDate\"\n [endDate]=\"endDate\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [maxMonth]=\"maxMonth()\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n (selectedValueChange)=\"activeYearChange($event)\"\n />\n }\n @case ('month') {\n <si-month-selection\n [startDate]=\"startDate\"\n [endDate]=\"endDate\"\n [months]=\"months\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [maxMonth]=\"maxMonth()\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previewRange]=\"rangeType() === 'END'\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n [activeHover]=\"activeHover()\"\n [focusedDate]=\"actualFocusedDate()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (focusedDateChange)=\"focusedDate.set($event)\"\n (selectedValueChange)=\"activeMonthChange($event)\"\n (viewChange)=\"view.set($event)\"\n />\n }\n @case ('week') {\n <si-day-selection\n [initialFocus]=\"forceFocus()\"\n [startDate]=\"startDate\"\n [endDate]=\"dateRange()?.end\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previewRange]=\"rangeType() === 'END'\"\n [hideWeekNumbers]=\"hideWeekNumbers\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [weekStartDay]=\"weekStartDay\"\n [calenderWeekLabel]=\"calenderWeekLabel()\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n [todayLabel]=\"config().todayText\"\n [activeHover]=\"activeHover()\"\n [focusedDate]=\"actualFocusedDate()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (focusedDateChange)=\"focusedDate.set($event)\"\n (selectedValueChange)=\"selectionChange($event!)\"\n (viewChange)=\"view.set($event)\"\n />\n }\n }\n </div>\n}\n@if (this.config().showTime) {\n <div\n class=\"timepicker-container px-9 pb-6\"\n [class.pt-6]=\"!hideCalendar()\"\n [class.border-top]=\"!hideCalendar()\"\n >\n @if (!config().mandatoryTime && !hideTimeToggle()) {\n <div class=\"mb-5 form-check form-switch\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n role=\"switch\"\n [id]=\"switchId\"\n [checked]=\"!disabledTime()\"\n (change)=\"toggleDisabledTime()\"\n />\n <label class=\"form-check-label\" [for]=\"switchId\">{{\n includeTimeLabel() | translate\n }}</label>\n </div>\n }\n <div class=\"mt-auto\">\n @if (timepickerLabel()) {\n <label class=\"form-label\" [for]=\"timepickerId\">{{ timepickerLabel() | translate }}</label>\n }\n <si-timepicker\n #timePicker\n [id]=\"timepickerId\"\n [ngModel]=\"time\"\n [disabled]=\"disabledTime()\"\n [hoursLabel]=\"config().hoursLabel ?? timePicker.hoursLabel()\"\n [minutesLabel]=\"config().minutesLabel ?? timePicker.minutesLabel()\"\n [secondsLabel]=\"config().secondsLabel ?? timePicker.secondsLabel()\"\n [millisecondsLabel]=\"config().millisecondsLabel ?? timePicker.millisecondsLabel()\"\n [hideLabels]=\"config().hideLabels ?? timePicker.hideLabels()\"\n [hoursAriaLabel]=\"config().hoursAriaLabel ?? timePicker.hoursAriaLabel()\"\n [minutesAriaLabel]=\"config().minutesAriaLabel ?? timePicker.minutesAriaLabel()\"\n [secondsAriaLabel]=\"config().secondsAriaLabel ?? timePicker.secondsAriaLabel()\"\n [millisecondsAriaLabel]=\"\n config().millisecondsAriaLabel ?? timePicker.millisecondsAriaLabel()\n \"\n [hoursPlaceholder]=\"config().hoursPlaceholder ?? timePicker.hoursPlaceholder()\"\n [minutesPlaceholder]=\"config().minutesPlaceholder ?? timePicker.minutesPlaceholder()\"\n [secondsPlaceholder]=\"config().secondsPlaceholder ?? timePicker.secondsPlaceholder()\"\n [millisecondsPlaceholder]=\"\n config().millisecondsPlaceholder ?? timePicker.millisecondsPlaceholder()\n \"\n [meridians]=\"config().meridians ?? timePicker.meridians()\"\n [meridiansLabel]=\"config().meridiansLabel ?? timePicker.meridiansLabel()\"\n [meridiansAriaLabel]=\"config().meridiansAriaLabel ?? timePicker.meridiansAriaLabel()\"\n [showMinutes]=\"config().showMinutes ?? true\"\n [showSeconds]=\"config().showSeconds ?? false\"\n [showMilliseconds]=\"config().showMilliseconds ?? false\"\n [showMeridian]=\"time12h()\"\n (ngModelChange)=\"timeSelected($event)\"\n />\n </div>\n </div>\n}\n", styles: [":host ::ng-deep .selection .month:not(.disabled):hover,:host ::ng-deep .selection .year:not(.disabled):hover,:host ::ng-deep .year:not(.disabled):not(.selected):hover,:host ::ng-deep .month:not(.disabled):not(.selected):hover,:host ::ng-deep .day:not(.disabled):not(.selected):hover{background:var(--element-base-1-hover);color:var(--element-ui-0)}:host ::ng-deep .year,:host ::ng-deep .month,:host ::ng-deep .day{block-size:32px;line-height:32px;inline-size:100%}:host ::ng-deep .day{inline-size:32px}:host ::ng-deep .year,:host ::ng-deep .month,:host ::ng-deep .day{margin-inline:auto;position:relative;border:unset;background-color:unset;padding:0;cursor:pointer;border-radius:var(--element-button-radius)}:host{background:var(--element-base-1);border-radius:var(--element-radius-2);display:flex;flex-direction:column;inline-size:348px}:host ::ng-deep .header,:host ::ng-deep .footer{display:flex;align-items:center}:host ::ng-deep .header{padding-block-start:12px;padding-block-end:16px;padding-inline:32px;border-block-end:1px solid var(--element-ui-4)}:host ::ng-deep .header a{text-decoration:none;cursor:pointer}:host ::ng-deep .footer{margin-block-start:4px;text-align:center}:host ::ng-deep table{inline-size:100%;border-collapse:separate;border-spacing:0 3px;table-layout:fixed;margin-block-end:-3px}:host ::ng-deep .day.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep .month.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep .year.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep td[role=gridcell]{text-align:center}:host ::ng-deep th{font-weight:400;text-align:center;block-size:32px}:host ::ng-deep .week-num,:host ::ng-deep th{font-size:.75rem;color:var(--element-text-secondary);inline-size:32px;cursor:default}:host ::ng-deep .today.selected:before{border-color:var(--element-ui-5)}:host ::ng-deep .today:before{position:absolute;content:\"\";inset:1px;border:1px solid var(--element-ui-1);border-radius:var(--element-radius-2);display:inline-block}:host ::ng-deep .selection .month,:host ::ng-deep .selection .year{block-size:30px;line-height:30px;cursor:pointer}:host ::ng-deep .selection .month{inline-size:50%}:host ::ng-deep .selection .year{inline-size:33.3%}:host ::ng-deep .disabled{color:var(--element-text-disabled);font-weight:400!important;cursor:default!important}:host ::ng-deep .range{background-color:var(--element-ui-4)}:host ::ng-deep .range:hover{background:var(--element-base-1-hover)}:host ::ng-deep .range:hover div.si-title-1:hover{background:transparent}:host ::ng-deep .range-hover{background-color:var(--element-base-1-hover)}:host ::ng-deep .range-hover-end{border-start-end-radius:var(--element-button-radius);border-end-end-radius:var(--element-button-radius)}:host ::ng-deep td:is(.range-start,.range-end){background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep td:is(.range-start,.range-end) div.si-title-1:hover,:host ::ng-deep td:is(.range-start,.range-end) div.si-body-1:hover{background:transparent;color:var(--element-base-1)}:host ::ng-deep td:is(.range-start,.range-end) .today:before{border-color:var(--element-ui-5)}:host ::ng-deep td.range-start{border-start-start-radius:var(--element-button-radius);border-end-start-radius:var(--element-button-radius)}:host ::ng-deep td.range-end{border-start-end-radius:var(--element-button-radius);border-end-end-radius:var(--element-button-radius)}:host ::ng-deep .calendar-button{font-weight:700;font-size:.875rem;line-height:1.143;color:var(--element-text-active);min-inline-size:0!important}:host ::ng-deep .calendar{block-size:28em}:host ::ng-deep .timepicker-container{display:flex;flex:1 1 auto;flex-direction:column}\n"], dependencies: [{ kind: "component", type: SiYearSelectionComponent, selector: "si-year-selection", inputs: ["focusedDate"], outputs: ["focusedDateChange", "yearRangeChange"] }, { kind: "component", type: SiMonthSelectionComponent, selector: "si-month-selection", inputs: ["months", "focusedDate"], outputs: ["focusedDateChange", "activeMonthChange", "viewChange"] }, { kind: "component", type: SiDaySelectionComponent, selector: "si-day-selection", inputs: ["hideWeekNumbers", "weekStartDay", "focusedDate", "todayLabel", "calenderWeekLabel"], outputs: ["focusedDateChange", "activeMonthChange", "viewChange"] }, { kind: "component", type: SiTimepickerComponent, selector: "si-timepicker", inputs: ["id", "disabled", "hoursLabel", "minutesLabel", "secondsLabel", "millisecondsLabel", "hideLabels", "hoursAriaLabel", "minutesAriaLabel", "secondsAriaLabel", "millisecondsAriaLabel", "hoursPlaceholder", "minutesPlaceholder", "secondsPlaceholder", "millisecondsPlaceholder", "meridians", "meridiansLabel", "meridiansAriaLabel", "showMinutes", "showSeconds", "showMilliseconds", "showMeridian", "min", "max", "readonly"], outputs: ["isValid", "meridianChange", "inputCompleted"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i2.SiTranslatePipe, name: "translate" }] });
|
|
3096
|
+
}
|
|
3097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerComponent, decorators: [{
|
|
3098
|
+
type: Component,
|
|
3099
|
+
args: [{ selector: 'si-datepicker', imports: [
|
|
3100
|
+
SiYearSelectionComponent,
|
|
3101
|
+
SiMonthSelectionComponent,
|
|
3102
|
+
SiDaySelectionComponent,
|
|
3103
|
+
SiTimepickerComponent,
|
|
3104
|
+
FormsModule,
|
|
3105
|
+
SiTranslateModule
|
|
3106
|
+
], template: "@if (!hideCalendar()) {\n <div class=\"text-center pb-6 calendar\">\n @switch (view()) {\n @case ('year') {\n <si-year-selection\n [focusedDate]=\"actualFocusedDate()\"\n [startDate]=\"startDate\"\n [endDate]=\"endDate\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [maxMonth]=\"maxMonth()\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n (selectedValueChange)=\"activeYearChange($event)\"\n />\n }\n @case ('month') {\n <si-month-selection\n [startDate]=\"startDate\"\n [endDate]=\"endDate\"\n [months]=\"months\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [maxMonth]=\"maxMonth()\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previewRange]=\"rangeType() === 'END'\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n [activeHover]=\"activeHover()\"\n [focusedDate]=\"actualFocusedDate()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (focusedDateChange)=\"focusedDate.set($event)\"\n (selectedValueChange)=\"activeMonthChange($event)\"\n (viewChange)=\"view.set($event)\"\n />\n }\n @case ('week') {\n <si-day-selection\n [initialFocus]=\"forceFocus()\"\n [startDate]=\"startDate\"\n [endDate]=\"dateRange()?.end\"\n [isRangeSelection]=\"config().enableDateRange!\"\n [previewRange]=\"rangeType() === 'END'\"\n [hideWeekNumbers]=\"hideWeekNumbers\"\n [minDate]=\"config().minDate\"\n [maxDate]=\"config().maxDate\"\n [minMonth]=\"minMonth()\"\n [weekStartDay]=\"weekStartDay\"\n [calenderWeekLabel]=\"calenderWeekLabel()\"\n [previousLabel]=\"previousLabel() | translate\"\n [nextLabel]=\"nextLabel() | translate\"\n [todayLabel]=\"config().todayText\"\n [activeHover]=\"activeHover()\"\n [focusedDate]=\"actualFocusedDate()\"\n (activeHoverChange)=\"onActiveHoverChange($event)\"\n (focusedDateChange)=\"focusedDate.set($event)\"\n (selectedValueChange)=\"selectionChange($event!)\"\n (viewChange)=\"view.set($event)\"\n />\n }\n }\n </div>\n}\n@if (this.config().showTime) {\n <div\n class=\"timepicker-container px-9 pb-6\"\n [class.pt-6]=\"!hideCalendar()\"\n [class.border-top]=\"!hideCalendar()\"\n >\n @if (!config().mandatoryTime && !hideTimeToggle()) {\n <div class=\"mb-5 form-check form-switch\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n role=\"switch\"\n [id]=\"switchId\"\n [checked]=\"!disabledTime()\"\n (change)=\"toggleDisabledTime()\"\n />\n <label class=\"form-check-label\" [for]=\"switchId\">{{\n includeTimeLabel() | translate\n }}</label>\n </div>\n }\n <div class=\"mt-auto\">\n @if (timepickerLabel()) {\n <label class=\"form-label\" [for]=\"timepickerId\">{{ timepickerLabel() | translate }}</label>\n }\n <si-timepicker\n #timePicker\n [id]=\"timepickerId\"\n [ngModel]=\"time\"\n [disabled]=\"disabledTime()\"\n [hoursLabel]=\"config().hoursLabel ?? timePicker.hoursLabel()\"\n [minutesLabel]=\"config().minutesLabel ?? timePicker.minutesLabel()\"\n [secondsLabel]=\"config().secondsLabel ?? timePicker.secondsLabel()\"\n [millisecondsLabel]=\"config().millisecondsLabel ?? timePicker.millisecondsLabel()\"\n [hideLabels]=\"config().hideLabels ?? timePicker.hideLabels()\"\n [hoursAriaLabel]=\"config().hoursAriaLabel ?? timePicker.hoursAriaLabel()\"\n [minutesAriaLabel]=\"config().minutesAriaLabel ?? timePicker.minutesAriaLabel()\"\n [secondsAriaLabel]=\"config().secondsAriaLabel ?? timePicker.secondsAriaLabel()\"\n [millisecondsAriaLabel]=\"\n config().millisecondsAriaLabel ?? timePicker.millisecondsAriaLabel()\n \"\n [hoursPlaceholder]=\"config().hoursPlaceholder ?? timePicker.hoursPlaceholder()\"\n [minutesPlaceholder]=\"config().minutesPlaceholder ?? timePicker.minutesPlaceholder()\"\n [secondsPlaceholder]=\"config().secondsPlaceholder ?? timePicker.secondsPlaceholder()\"\n [millisecondsPlaceholder]=\"\n config().millisecondsPlaceholder ?? timePicker.millisecondsPlaceholder()\n \"\n [meridians]=\"config().meridians ?? timePicker.meridians()\"\n [meridiansLabel]=\"config().meridiansLabel ?? timePicker.meridiansLabel()\"\n [meridiansAriaLabel]=\"config().meridiansAriaLabel ?? timePicker.meridiansAriaLabel()\"\n [showMinutes]=\"config().showMinutes ?? true\"\n [showSeconds]=\"config().showSeconds ?? false\"\n [showMilliseconds]=\"config().showMilliseconds ?? false\"\n [showMeridian]=\"time12h()\"\n (ngModelChange)=\"timeSelected($event)\"\n />\n </div>\n </div>\n}\n", styles: [":host ::ng-deep .selection .month:not(.disabled):hover,:host ::ng-deep .selection .year:not(.disabled):hover,:host ::ng-deep .year:not(.disabled):not(.selected):hover,:host ::ng-deep .month:not(.disabled):not(.selected):hover,:host ::ng-deep .day:not(.disabled):not(.selected):hover{background:var(--element-base-1-hover);color:var(--element-ui-0)}:host ::ng-deep .year,:host ::ng-deep .month,:host ::ng-deep .day{block-size:32px;line-height:32px;inline-size:100%}:host ::ng-deep .day{inline-size:32px}:host ::ng-deep .year,:host ::ng-deep .month,:host ::ng-deep .day{margin-inline:auto;position:relative;border:unset;background-color:unset;padding:0;cursor:pointer;border-radius:var(--element-button-radius)}:host{background:var(--element-base-1);border-radius:var(--element-radius-2);display:flex;flex-direction:column;inline-size:348px}:host ::ng-deep .header,:host ::ng-deep .footer{display:flex;align-items:center}:host ::ng-deep .header{padding-block-start:12px;padding-block-end:16px;padding-inline:32px;border-block-end:1px solid var(--element-ui-4)}:host ::ng-deep .header a{text-decoration:none;cursor:pointer}:host ::ng-deep .footer{margin-block-start:4px;text-align:center}:host ::ng-deep table{inline-size:100%;border-collapse:separate;border-spacing:0 3px;table-layout:fixed;margin-block-end:-3px}:host ::ng-deep .day.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep .month.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep .year.selected{background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep td[role=gridcell]{text-align:center}:host ::ng-deep th{font-weight:400;text-align:center;block-size:32px}:host ::ng-deep .week-num,:host ::ng-deep th{font-size:.75rem;color:var(--element-text-secondary);inline-size:32px;cursor:default}:host ::ng-deep .today.selected:before{border-color:var(--element-ui-5)}:host ::ng-deep .today:before{position:absolute;content:\"\";inset:1px;border:1px solid var(--element-ui-1);border-radius:var(--element-radius-2);display:inline-block}:host ::ng-deep .selection .month,:host ::ng-deep .selection .year{block-size:30px;line-height:30px;cursor:pointer}:host ::ng-deep .selection .month{inline-size:50%}:host ::ng-deep .selection .year{inline-size:33.3%}:host ::ng-deep .disabled{color:var(--element-text-disabled);font-weight:400!important;cursor:default!important}:host ::ng-deep .range{background-color:var(--element-ui-4)}:host ::ng-deep .range:hover{background:var(--element-base-1-hover)}:host ::ng-deep .range:hover div.si-title-1:hover{background:transparent}:host ::ng-deep .range-hover{background-color:var(--element-base-1-hover)}:host ::ng-deep .range-hover-end{border-start-end-radius:var(--element-button-radius);border-end-end-radius:var(--element-button-radius)}:host ::ng-deep td:is(.range-start,.range-end){background:var(--element-ui-0);color:var(--element-base-1)}:host ::ng-deep td:is(.range-start,.range-end) div.si-title-1:hover,:host ::ng-deep td:is(.range-start,.range-end) div.si-body-1:hover{background:transparent;color:var(--element-base-1)}:host ::ng-deep td:is(.range-start,.range-end) .today:before{border-color:var(--element-ui-5)}:host ::ng-deep td.range-start{border-start-start-radius:var(--element-button-radius);border-end-start-radius:var(--element-button-radius)}:host ::ng-deep td.range-end{border-start-end-radius:var(--element-button-radius);border-end-end-radius:var(--element-button-radius)}:host ::ng-deep .calendar-button{font-weight:700;font-size:.875rem;line-height:1.143;color:var(--element-text-active);min-inline-size:0!important}:host ::ng-deep .calendar{block-size:28em}:host ::ng-deep .timepicker-container{display:flex;flex:1 1 auto;flex-direction:column}\n"] }]
|
|
3107
|
+
}], ctorParameters: () => [] });
|
|
3108
|
+
|
|
3109
|
+
/**
|
|
3110
|
+
* Copyright Siemens 2016 - 2025.
|
|
3111
|
+
* SPDX-License-Identifier: MIT
|
|
3112
|
+
*/
|
|
3113
|
+
class SiDatepickerOverlayComponent {
|
|
3114
|
+
minMonth = signal(undefined);
|
|
3115
|
+
maxMonth = signal(undefined);
|
|
3116
|
+
datepicker = viewChild.required(SiDatepickerComponent);
|
|
3117
|
+
/**
|
|
3118
|
+
* {@inheritDoc SiDatepickerComponent#initialFocus}
|
|
3119
|
+
* @defaultValue false
|
|
3120
|
+
*/
|
|
3121
|
+
initialFocus = input(false, { transform: booleanAttribute });
|
|
3122
|
+
/**
|
|
3123
|
+
* {@inheritDoc SiDatepickerComponent#config}
|
|
3124
|
+
* @defaultValue
|
|
3125
|
+
* ```
|
|
3126
|
+
* {}
|
|
3127
|
+
* ```
|
|
3128
|
+
*/
|
|
3129
|
+
config = input({});
|
|
3130
|
+
/**
|
|
3131
|
+
* {@inheritDoc SiDatepickerComponent#date}
|
|
3132
|
+
*/
|
|
3133
|
+
date = model();
|
|
3134
|
+
/**
|
|
3135
|
+
* {@inheritDoc SiDatepickerComponent#dateRange}
|
|
3136
|
+
*/
|
|
3137
|
+
dateRange = model();
|
|
3138
|
+
/**
|
|
3139
|
+
* {@inheritDoc SiDatepickerComponent#rangeType}
|
|
3140
|
+
*/
|
|
3141
|
+
rangeType = model();
|
|
3142
|
+
/**
|
|
3143
|
+
* {@inheritDoc SiDatepickerComponent#time12h}
|
|
3144
|
+
* @defaultValue false
|
|
3145
|
+
*/
|
|
3146
|
+
time12h = input(false, { transform: booleanAttribute });
|
|
3147
|
+
/**
|
|
3148
|
+
* Emits an event to notify about disabling the time from the datepicker.
|
|
3149
|
+
* When time is disable, we construct a pure date object in UTC 00:00:00 time.
|
|
3150
|
+
*/
|
|
3151
|
+
disabledTimeChange = output();
|
|
3152
|
+
/**
|
|
3153
|
+
* @deprecated Property provides internal information that should not be used.
|
|
3154
|
+
*
|
|
3155
|
+
* @defaultValue false
|
|
3156
|
+
*/
|
|
3157
|
+
isFocused = false;
|
|
3158
|
+
document = inject(DOCUMENT);
|
|
3159
|
+
elementRef = inject(ElementRef);
|
|
3160
|
+
focusMonitor = inject(FocusMonitor);
|
|
3161
|
+
focusTrapFactory = inject(ConfigurableFocusTrapFactory);
|
|
3162
|
+
focusTrap;
|
|
3163
|
+
previousActiveElement;
|
|
3164
|
+
disableTime = false;
|
|
3165
|
+
activeHover;
|
|
3166
|
+
isTwoMonthDateRange = computed(() => !!this.config().enableDateRange &&
|
|
3167
|
+
(!!this.config().enableTwoMonthDateRange || !!this.config().showTime));
|
|
3168
|
+
firstDatepickerConfig = signal({});
|
|
3169
|
+
secondDatepickerConfig = signal({});
|
|
3170
|
+
/**
|
|
3171
|
+
* Indicate that the overlay is opened in small screen.
|
|
3172
|
+
* A modal dialog animation display when true and a wrapped two month calendar layout is displayed.
|
|
3173
|
+
*
|
|
3174
|
+
* @defaultValue false
|
|
3175
|
+
*/
|
|
3176
|
+
isMobile = input(false);
|
|
3177
|
+
completeAnimation = signal(false);
|
|
3178
|
+
ngOnChanges(changes) {
|
|
3179
|
+
if (changes.config) {
|
|
3180
|
+
const config = this.config();
|
|
3181
|
+
this.firstDatepickerConfig.set(!this.isTwoMonthDateRange ? config : { ...config, hideLabels: true });
|
|
3182
|
+
this.secondDatepickerConfig.set({ ...config, hideLabels: true });
|
|
3183
|
+
}
|
|
3184
|
+
const dateRange = this.dateRange();
|
|
3185
|
+
if (changes.config?.currentValue?.onlyMonthSelection &&
|
|
3186
|
+
this.isTwoMonthDateRange() &&
|
|
3187
|
+
dateRange?.start) {
|
|
3188
|
+
this.minMonth.set(new Date(dateRange?.start.getFullYear(), 0, 1));
|
|
3189
|
+
}
|
|
3190
|
+
this.rangeType.set('START');
|
|
3191
|
+
if (isValid(changes.dateRange?.currentValue?.start) &&
|
|
3192
|
+
!isValid(changes.dateRange?.currentValue?.end)) {
|
|
3193
|
+
this.rangeType.set('END');
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
ngOnInit() {
|
|
3197
|
+
this.focusTrap = this.focusTrapFactory.create(this.elementRef.nativeElement);
|
|
3198
|
+
this.previousActiveElement = this.document.activeElement ?? undefined;
|
|
3199
|
+
if (this.isMobile()) {
|
|
3200
|
+
setTimeout(() => this.completeAnimation.set(true), 150);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
ngAfterViewInit() {
|
|
3204
|
+
// Monitor focus events
|
|
3205
|
+
this.focusMonitor
|
|
3206
|
+
.monitor(this.elementRef, true)
|
|
3207
|
+
.subscribe(origin => (this.isFocused = origin !== undefined));
|
|
3208
|
+
}
|
|
3209
|
+
ngOnDestroy() {
|
|
3210
|
+
this.focusMonitor.stopMonitoring(this.elementRef);
|
|
3211
|
+
this.focusTrap.destroy();
|
|
3212
|
+
if (this.initialFocus() && this.previousActiveElement && 'focus' in this.previousActiveElement)
|
|
3213
|
+
this.previousActiveElement.focus();
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
3216
|
+
* Focus active cell in the current datepicker view.
|
|
3217
|
+
*/
|
|
3218
|
+
focusActiveCell() {
|
|
3219
|
+
this.datepicker().focusActiveCell();
|
|
3220
|
+
}
|
|
3221
|
+
firstDatepickerFocusDateChange(newFocusedDate) {
|
|
3222
|
+
if (!newFocusedDate) {
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
if (this.config()?.onlyMonthSelection) {
|
|
3226
|
+
this.minMonth.set(new Date(newFocusedDate.getFullYear() + 1, 0, 1));
|
|
3227
|
+
}
|
|
3228
|
+
else if (newFocusedDate !== this.maxMonth()) {
|
|
3229
|
+
this.minMonth.set(nextMonth(newFocusedDate));
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
secondDatepickerFocusDateChange(newFocusedDate) {
|
|
3233
|
+
if (newFocusedDate && newFocusedDate !== this.minMonth()) {
|
|
3234
|
+
if (this.config()?.onlyMonthSelection) {
|
|
3235
|
+
this.maxMonth.set(new Date(newFocusedDate.getFullYear() - 1, 11, 31));
|
|
3236
|
+
}
|
|
3237
|
+
else {
|
|
3238
|
+
this.maxMonth.set(previousMonth(newFocusedDate));
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3243
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SiDatepickerOverlayComponent, isStandalone: true, selector: "si-datepicker-overlay", inputs: { initialFocus: { classPropertyName: "initialFocus", publicName: "initialFocus", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, dateRange: { classPropertyName: "dateRange", publicName: "dateRange", isSignal: true, isRequired: false, transformFunction: null }, rangeType: { classPropertyName: "rangeType", publicName: "rangeType", isSignal: true, isRequired: false, transformFunction: null }, time12h: { classPropertyName: "time12h", publicName: "time12h", isSignal: true, isRequired: false, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { date: "dateChange", dateRange: "dateRangeChange", rangeType: "rangeTypeChange", disabledTimeChange: "disabledTimeChange" }, host: { properties: { "class.flex-wrap": "isMobile()", "class.mobile-datepicker-overlay": "isMobile()", "class.fade": "isMobile()", "class.show": "completeAnimation()" }, classAttribute: "mt-md-1 d-flex elevation-2 rounded-2 overflow-auto align-items-stretch" }, viewQueries: [{ propertyName: "datepicker", first: true, predicate: SiDatepickerComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
3244
|
+
<si-datepicker
|
|
3245
|
+
#datepicker
|
|
3246
|
+
tabindex="-1"
|
|
3247
|
+
[initialFocus]="initialFocus()"
|
|
3248
|
+
[config]="firstDatepickerConfig()"
|
|
3249
|
+
[class.first-datepicker]="isTwoMonthDateRange() && !isMobile()"
|
|
3250
|
+
[class.first-datepicker-mobile]="isTwoMonthDateRange() && isMobile()"
|
|
3251
|
+
[date]="date()"
|
|
3252
|
+
[dateRange]="dateRange()"
|
|
3253
|
+
[dateRangeRole]="isTwoMonthDateRange() ? 'START' : undefined"
|
|
3254
|
+
[time12h]="time12h()"
|
|
3255
|
+
[timepickerLabel]="firstDatepickerConfig().startTimeLabel"
|
|
3256
|
+
[maxMonth]="maxMonth()"
|
|
3257
|
+
[rangeType]="rangeType()"
|
|
3258
|
+
[(activeHover)]="activeHover"
|
|
3259
|
+
(dateChange)="date.set($event)"
|
|
3260
|
+
(dateRangeChange)="dateRange.set($event)"
|
|
3261
|
+
(disabledTimeChange)="disableTime = $event; disabledTimeChange.emit($event)"
|
|
3262
|
+
(focusedDateChange)="firstDatepickerFocusDateChange($event)"
|
|
3263
|
+
(rangeTypeChange)="rangeType.set($event)"
|
|
3264
|
+
/>
|
|
3265
|
+
@if (isTwoMonthDateRange()) {
|
|
3266
|
+
<si-datepicker
|
|
3267
|
+
#datepickerTwo
|
|
3268
|
+
class="mh-100 overflow-auto"
|
|
3269
|
+
tabindex="-1"
|
|
3270
|
+
dateRangeRole="END"
|
|
3271
|
+
[class.second-datepicker]="!isMobile()"
|
|
3272
|
+
[class.second-datepicker-mobile]="isMobile()"
|
|
3273
|
+
[hideTimeToggle]="true"
|
|
3274
|
+
[initialFocus]="initialFocus()"
|
|
3275
|
+
[config]="secondDatepickerConfig()"
|
|
3276
|
+
[date]="date()"
|
|
3277
|
+
[hideCalendar]="isMobile()"
|
|
3278
|
+
[minMonth]="minMonth()"
|
|
3279
|
+
[dateRange]="dateRange()"
|
|
3280
|
+
[disabledTime]="disableTime"
|
|
3281
|
+
[time12h]="time12h()"
|
|
3282
|
+
[timepickerLabel]="secondDatepickerConfig().endTimeLabel"
|
|
3283
|
+
[rangeType]="rangeType()"
|
|
3284
|
+
[(activeHover)]="activeHover"
|
|
3285
|
+
(dateRangeChange)="dateRange.set($event)"
|
|
3286
|
+
(focusedDateChange)="secondDatepickerFocusDateChange($event)"
|
|
3287
|
+
(rangeTypeChange)="rangeType.set($event)"
|
|
3288
|
+
/>
|
|
3289
|
+
}
|
|
3290
|
+
`, isInline: true, styles: [":host{max-block-size:max-content;max-inline-size:min-content}:host.fade{transition:transform calc(.3s * var(--element-animations-enabled, 1)) ease-out;transform:translateY(-50px)}:host.show{transform:none}.mobile-datepicker-overlay{max-inline-size:348px}.first-datepicker{border-inline-end:0;border-start-end-radius:0;border-end-end-radius:0}.second-datepicker{border-inline-start:0;border-start-start-radius:0;border-end-start-radius:0}.first-datepicker-mobile{border-block-end:0;border-end-start-radius:0;border-end-end-radius:0}.second-datepicker-mobile{border-block-start:0;border-start-start-radius:0;border-start-end-radius:0}\n"], dependencies: [{ kind: "component", type: SiDatepickerComponent, selector: "si-datepicker", inputs: ["focusedDate", "date", "dateRange", "dateRangeRole", "initialFocus", "disabledTime", "config", "previousLabel", "nextLabel", "calenderWeekLabel", "time12h", "rangeType", "minMonth", "maxMonth", "hideTimeToggle", "hideCalendar", "timepickerLabel", "activeHover"], outputs: ["focusedDateChange", "dateChange", "dateRangeChange", "disabledTimeChange", "configChange", "rangeTypeChange", "activeHoverChange"] }, { kind: "ngmodule", type: A11yModule }], changeDetection: i0.ChangeDetectionStrategy.Default });
|
|
3291
|
+
}
|
|
3292
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerOverlayComponent, decorators: [{
|
|
3293
|
+
type: Component,
|
|
3294
|
+
args: [{ selector: 'si-datepicker-overlay', host: {
|
|
3295
|
+
class: 'mt-md-1 d-flex elevation-2 rounded-2 overflow-auto align-items-stretch',
|
|
3296
|
+
'[class.flex-wrap]': 'isMobile()',
|
|
3297
|
+
'[class.mobile-datepicker-overlay]': 'isMobile()',
|
|
3298
|
+
'[class.fade]': 'isMobile()',
|
|
3299
|
+
'[class.show]': 'completeAnimation()'
|
|
3300
|
+
}, template: `
|
|
3301
|
+
<si-datepicker
|
|
3302
|
+
#datepicker
|
|
3303
|
+
tabindex="-1"
|
|
3304
|
+
[initialFocus]="initialFocus()"
|
|
3305
|
+
[config]="firstDatepickerConfig()"
|
|
3306
|
+
[class.first-datepicker]="isTwoMonthDateRange() && !isMobile()"
|
|
3307
|
+
[class.first-datepicker-mobile]="isTwoMonthDateRange() && isMobile()"
|
|
3308
|
+
[date]="date()"
|
|
3309
|
+
[dateRange]="dateRange()"
|
|
3310
|
+
[dateRangeRole]="isTwoMonthDateRange() ? 'START' : undefined"
|
|
3311
|
+
[time12h]="time12h()"
|
|
3312
|
+
[timepickerLabel]="firstDatepickerConfig().startTimeLabel"
|
|
3313
|
+
[maxMonth]="maxMonth()"
|
|
3314
|
+
[rangeType]="rangeType()"
|
|
3315
|
+
[(activeHover)]="activeHover"
|
|
3316
|
+
(dateChange)="date.set($event)"
|
|
3317
|
+
(dateRangeChange)="dateRange.set($event)"
|
|
3318
|
+
(disabledTimeChange)="disableTime = $event; disabledTimeChange.emit($event)"
|
|
3319
|
+
(focusedDateChange)="firstDatepickerFocusDateChange($event)"
|
|
3320
|
+
(rangeTypeChange)="rangeType.set($event)"
|
|
3321
|
+
/>
|
|
3322
|
+
@if (isTwoMonthDateRange()) {
|
|
3323
|
+
<si-datepicker
|
|
3324
|
+
#datepickerTwo
|
|
3325
|
+
class="mh-100 overflow-auto"
|
|
3326
|
+
tabindex="-1"
|
|
3327
|
+
dateRangeRole="END"
|
|
3328
|
+
[class.second-datepicker]="!isMobile()"
|
|
3329
|
+
[class.second-datepicker-mobile]="isMobile()"
|
|
3330
|
+
[hideTimeToggle]="true"
|
|
3331
|
+
[initialFocus]="initialFocus()"
|
|
3332
|
+
[config]="secondDatepickerConfig()"
|
|
3333
|
+
[date]="date()"
|
|
3334
|
+
[hideCalendar]="isMobile()"
|
|
3335
|
+
[minMonth]="minMonth()"
|
|
3336
|
+
[dateRange]="dateRange()"
|
|
3337
|
+
[disabledTime]="disableTime"
|
|
3338
|
+
[time12h]="time12h()"
|
|
3339
|
+
[timepickerLabel]="secondDatepickerConfig().endTimeLabel"
|
|
3340
|
+
[rangeType]="rangeType()"
|
|
3341
|
+
[(activeHover)]="activeHover"
|
|
3342
|
+
(dateRangeChange)="dateRange.set($event)"
|
|
3343
|
+
(focusedDateChange)="secondDatepickerFocusDateChange($event)"
|
|
3344
|
+
(rangeTypeChange)="rangeType.set($event)"
|
|
3345
|
+
/>
|
|
3346
|
+
}
|
|
3347
|
+
`, changeDetection: ChangeDetectionStrategy.Default, imports: [SiDatepickerComponent, A11yModule], styles: [":host{max-block-size:max-content;max-inline-size:min-content}:host.fade{transition:transform calc(.3s * var(--element-animations-enabled, 1)) ease-out;transform:translateY(-50px)}:host.show{transform:none}.mobile-datepicker-overlay{max-inline-size:348px}.first-datepicker{border-inline-end:0;border-start-end-radius:0;border-end-end-radius:0}.second-datepicker{border-inline-start:0;border-start-start-radius:0;border-end-start-radius:0}.first-datepicker-mobile{border-block-end:0;border-end-start-radius:0;border-end-end-radius:0}.second-datepicker-mobile{border-block-start:0;border-start-start-radius:0;border-start-end-radius:0}\n"] }]
|
|
3348
|
+
}] });
|
|
3349
|
+
|
|
3350
|
+
/**
|
|
3351
|
+
* Copyright Siemens 2016 - 2025.
|
|
3352
|
+
* SPDX-License-Identifier: MIT
|
|
3353
|
+
*/
|
|
3354
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3355
|
+
var CloseCause;
|
|
3356
|
+
(function (CloseCause) {
|
|
3357
|
+
CloseCause["Backdrop"] = "backdrop";
|
|
3358
|
+
CloseCause["Detach"] = "detach";
|
|
3359
|
+
CloseCause["Escape"] = "escape";
|
|
3360
|
+
CloseCause["Select"] = "select";
|
|
3361
|
+
})(CloseCause || (CloseCause = {}));
|
|
3362
|
+
/**
|
|
3363
|
+
* Directive with the responsibility to open/close datepicker overlay.
|
|
3364
|
+
*/
|
|
3365
|
+
class SiDatepickerOverlayDirective {
|
|
3366
|
+
/**
|
|
3367
|
+
* Position of the datepicker overlay. Accepts an array of positions or a single position.
|
|
3368
|
+
* The position will be chosen based on the first position that fits into the viewport.
|
|
3369
|
+
* The input is necessary since the positions between the siDatepicker directive and si-date-range
|
|
3370
|
+
* component are different.
|
|
3371
|
+
* @internal
|
|
3372
|
+
*/
|
|
3373
|
+
placement = signal([
|
|
3374
|
+
{
|
|
3375
|
+
overlayX: 'start',
|
|
3376
|
+
overlayY: 'top',
|
|
3377
|
+
originX: 'start',
|
|
3378
|
+
originY: 'bottom'
|
|
3379
|
+
},
|
|
3380
|
+
{
|
|
3381
|
+
overlayX: 'start',
|
|
3382
|
+
overlayY: 'bottom',
|
|
3383
|
+
originX: 'start',
|
|
3384
|
+
originY: 'top'
|
|
3385
|
+
}
|
|
3386
|
+
]);
|
|
3387
|
+
/**
|
|
3388
|
+
* Output event on closing datepicker e.g. by clicking backdrop or escape key.
|
|
3389
|
+
*/
|
|
3390
|
+
siDatepickerClose = output();
|
|
3391
|
+
overlayRef;
|
|
3392
|
+
datepickerRef;
|
|
3393
|
+
autoCloseSelection = new Subject();
|
|
3394
|
+
/** Guard for siDatepickerClose event emitter to make sure the cause is emitter just once */
|
|
3395
|
+
ignoreClose = false;
|
|
3396
|
+
overlay = inject(Overlay);
|
|
3397
|
+
triggerElementRef = inject(ElementRef);
|
|
3398
|
+
mediaMatcher = inject(MediaMatcher);
|
|
3399
|
+
breakpointObserver = inject(BreakpointObserver);
|
|
3400
|
+
destroyRef = inject(DestroyRef);
|
|
3401
|
+
/**
|
|
3402
|
+
* When the media query matches on open the overlay is displayed like a modal dialog.
|
|
3403
|
+
* In case, users change the screen size to the matching media query we close the overlay
|
|
3404
|
+
* if it is open with a connected overlay strategy.
|
|
3405
|
+
*/
|
|
3406
|
+
smallScreenQuery = `(max-width: ${BOOTSTRAP_BREAKPOINTS.mdMinimum}px) or (max-height: ${BOOTSTRAP_BREAKPOINTS.smMinimum}px)`;
|
|
3407
|
+
ngOnDestroy() {
|
|
3408
|
+
this.overlayRef?.dispose();
|
|
3409
|
+
this.datepickerRef = undefined;
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* Toggle open/close overlay.
|
|
3413
|
+
* @param focus - move focus to the datepicker.
|
|
3414
|
+
* @param inputs - for datepicker.
|
|
3415
|
+
* @returns create datepicker overlay instance or undefined
|
|
3416
|
+
*
|
|
3417
|
+
* @deprecated Use {@link showOverlay} or {@link closeOverlay} instead.
|
|
3418
|
+
*/
|
|
3419
|
+
toggleOverlay(focus, inputs) {
|
|
3420
|
+
if (!this.datepickerRef) {
|
|
3421
|
+
return this.showDatepicker().setInputs(inputs).focus(focus).datepickerRef;
|
|
3422
|
+
}
|
|
3423
|
+
else {
|
|
3424
|
+
return this.closeOverlay();
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
/**
|
|
3428
|
+
* Show datepicker overlay.
|
|
3429
|
+
* @param focus - move focus to the datepicker.
|
|
3430
|
+
* @returns create datepicker overlay instance
|
|
3431
|
+
*/
|
|
3432
|
+
showOverlay(focus = false, inputs) {
|
|
3433
|
+
return this.showDatepicker().setInputs(inputs).focus(focus).datepickerRef;
|
|
3434
|
+
}
|
|
3435
|
+
/**
|
|
3436
|
+
* Close datepicker.
|
|
3437
|
+
*/
|
|
3438
|
+
closeOverlay() {
|
|
3439
|
+
if (this.overlayRef?.hasAttached()) {
|
|
3440
|
+
this.overlayRef?.detach();
|
|
3441
|
+
this.overlayRef?.dispose();
|
|
3442
|
+
}
|
|
3443
|
+
this.datepickerRef = undefined;
|
|
3444
|
+
return undefined;
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* Focus active cell in datepicker.
|
|
3448
|
+
* @param focus - show transfer focus.
|
|
3449
|
+
* @returns current instance.
|
|
3450
|
+
*/
|
|
3451
|
+
focus(focus = true) {
|
|
3452
|
+
if (focus) {
|
|
3453
|
+
this.datepickerRef?.setInput('initialFocus', true);
|
|
3454
|
+
this.datepickerRef?.instance.focusActiveCell();
|
|
3455
|
+
this.datepickerRef?.changeDetectorRef.markForCheck();
|
|
3456
|
+
}
|
|
3457
|
+
return this;
|
|
3458
|
+
}
|
|
3459
|
+
/** @deprecated Property provides internal information that should not be used. */
|
|
3460
|
+
isFocused() {
|
|
3461
|
+
return this.datepickerRef?.instance.isFocused ?? false;
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Indicate the datepicker is visible.
|
|
3465
|
+
* @returns is visible.
|
|
3466
|
+
*/
|
|
3467
|
+
isShown() {
|
|
3468
|
+
return this.datepickerRef;
|
|
3469
|
+
}
|
|
3470
|
+
/** Set datepicker inputs */
|
|
3471
|
+
setInputs(inputs) {
|
|
3472
|
+
if (this.datepickerRef && inputs) {
|
|
3473
|
+
Object.entries(inputs).forEach(([key, value]) => {
|
|
3474
|
+
this.datepickerRef.setInput(key.toString(), value);
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
return this;
|
|
3478
|
+
}
|
|
3479
|
+
/** Close overlay with cause select, which will recover the focus */
|
|
3480
|
+
closeAfterSelection() {
|
|
3481
|
+
this.autoCloseSelection.next();
|
|
3482
|
+
}
|
|
3483
|
+
/** Indicate whether the HTML element is a child of the datepicker overlay. */
|
|
3484
|
+
contains(element) {
|
|
3485
|
+
if (!element) {
|
|
3486
|
+
return false;
|
|
3487
|
+
}
|
|
3488
|
+
return this.overlayRef?.overlayElement?.contains(element) ?? false;
|
|
3489
|
+
}
|
|
3490
|
+
showDatepicker() {
|
|
3491
|
+
if (this.overlayRef?.hasAttached()) {
|
|
3492
|
+
return this;
|
|
3493
|
+
}
|
|
3494
|
+
// Since the connected overlay strategy has some limitations in small screens e.g.
|
|
3495
|
+
// the overlay is moved via style top without recalculating the (max)height which
|
|
3496
|
+
// can result in a cut of time picker.
|
|
3497
|
+
// To overcome this issue the overlay use the global position strategy in small screens.
|
|
3498
|
+
const smallScreen = this.mediaMatcher.matchMedia(this.smallScreenQuery).matches;
|
|
3499
|
+
if (smallScreen) {
|
|
3500
|
+
this.createMobileOverlay();
|
|
3501
|
+
}
|
|
3502
|
+
else {
|
|
3503
|
+
this.createDesktopOverlay();
|
|
3504
|
+
}
|
|
3505
|
+
this.closeStream(this.overlayRef)
|
|
3506
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(outputToObservable(this.siDatepickerClose)))
|
|
3507
|
+
.subscribe(cause => {
|
|
3508
|
+
// The handler is called multiple times since we need to listen to detach events
|
|
3509
|
+
if (this.datepickerRef && !this.ignoreClose) {
|
|
3510
|
+
this.ignoreClose = true;
|
|
3511
|
+
this.closeOverlay();
|
|
3512
|
+
this.ignoreClose = false;
|
|
3513
|
+
this.siDatepickerClose.emit(cause);
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
const portal = new ComponentPortal(SiDatepickerOverlayComponent);
|
|
3517
|
+
this.datepickerRef = this.overlayRef.attach(portal);
|
|
3518
|
+
if (smallScreen) {
|
|
3519
|
+
this.datepickerRef.setInput('isMobile', true);
|
|
3520
|
+
}
|
|
3521
|
+
// Automatically close the overlay if we reach the small screen breakpoint
|
|
3522
|
+
// and the picker does not use the global position strategy.
|
|
3523
|
+
this.breakpointObserver
|
|
3524
|
+
.observe(this.smallScreenQuery)
|
|
3525
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(outputToObservable(this.siDatepickerClose)), skip(1))
|
|
3526
|
+
.subscribe(() => this.closeOverlay());
|
|
3527
|
+
return this;
|
|
3528
|
+
}
|
|
3529
|
+
createMobileOverlay() {
|
|
3530
|
+
this.overlayRef = this.overlay.create({
|
|
3531
|
+
positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
|
|
3532
|
+
direction: isRTL() ? 'rtl' : 'ltr',
|
|
3533
|
+
hasBackdrop: true,
|
|
3534
|
+
backdropClass: 'modal-backdrop'
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
createDesktopOverlay() {
|
|
3538
|
+
const popoverPositions = getOverlayPositions(this.triggerElementRef, this.placement(), false);
|
|
3539
|
+
this.overlayRef = this.overlay.create({
|
|
3540
|
+
positionStrategy: this.overlay
|
|
3541
|
+
.position()
|
|
3542
|
+
.flexibleConnectedTo(this.triggerElementRef)
|
|
3543
|
+
.withPositions(popoverPositions)
|
|
3544
|
+
.withPush(true)
|
|
3545
|
+
.withGrowAfterOpen(true)
|
|
3546
|
+
.withFlexibleDimensions(true)
|
|
3547
|
+
.withViewportMargin(4),
|
|
3548
|
+
direction: isRTL() ? 'rtl' : 'ltr',
|
|
3549
|
+
hasBackdrop: true,
|
|
3550
|
+
backdropClass: 'cdk-overlay-transparent-backdrop'
|
|
3551
|
+
});
|
|
3552
|
+
}
|
|
3553
|
+
/**
|
|
3554
|
+
* Merge events which shall close the overlay
|
|
3555
|
+
* @param overlayRef - source for backdrop, detach or escape events.
|
|
3556
|
+
* @returns merged observable
|
|
3557
|
+
*/
|
|
3558
|
+
closeStream(overlayRef) {
|
|
3559
|
+
return merge(this.autoCloseSelection.pipe(map(() => CloseCause.Select)), overlayRef.backdropClick().pipe(map(() => CloseCause.Backdrop)), overlayRef.detachments().pipe(map(() => CloseCause.Detach)), overlayRef.keydownEvents().pipe(filter(event => event.key === 'Escape'), tap(event => event.stopPropagation()), // ESC handled, prevent closing modal, etc.
|
|
3560
|
+
map(() => CloseCause.Escape)));
|
|
3561
|
+
}
|
|
3562
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerOverlayDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3563
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: SiDatepickerOverlayDirective, isStandalone: true, selector: "[siDatepickerOverlay]", outputs: { siDatepickerClose: "siDatepickerClose" }, exportAs: ["siDatepickerOverlay"], ngImport: i0 });
|
|
3564
|
+
}
|
|
3565
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerOverlayDirective, decorators: [{
|
|
3566
|
+
type: Directive,
|
|
3567
|
+
args: [{
|
|
3568
|
+
selector: '[siDatepickerOverlay]',
|
|
3569
|
+
exportAs: 'siDatepickerOverlay'
|
|
3570
|
+
}]
|
|
3571
|
+
}] });
|
|
3572
|
+
|
|
3573
|
+
/**
|
|
3574
|
+
* Copyright Siemens 2016 - 2025.
|
|
3575
|
+
* SPDX-License-Identifier: MIT
|
|
3576
|
+
*/
|
|
3577
|
+
class SiDatepickerDirective extends SiDateInputDirective {
|
|
3578
|
+
/**
|
|
3579
|
+
* Automatically close overlay on date selection.
|
|
3580
|
+
* Do not use this behavior with config showTime = true, because it
|
|
3581
|
+
* will close the overlay when the user change one of the time units.
|
|
3582
|
+
*
|
|
3583
|
+
* @defaultValue false
|
|
3584
|
+
*/
|
|
3585
|
+
autoClose = input(false, { transform: booleanAttribute });
|
|
3586
|
+
/**
|
|
3587
|
+
* @deprecated Property has no effect and will be removed without replacement.
|
|
3588
|
+
*
|
|
3589
|
+
* @defaultValue inject(ElementRef)
|
|
3590
|
+
*/
|
|
3591
|
+
triggeringInput = input(inject(ElementRef));
|
|
3592
|
+
/**
|
|
3593
|
+
* During focus on close the datepicker will not show since we recover the focus on element.
|
|
3594
|
+
* The focus on close is only relevant when the directive is configured without a calendar button.
|
|
3595
|
+
*/
|
|
3596
|
+
overlaySubscriptions;
|
|
3597
|
+
externalTrigger;
|
|
3598
|
+
overlayToggle = inject(SiDatepickerOverlayDirective);
|
|
3599
|
+
ngAfterViewInit() {
|
|
3600
|
+
// Update datepicker with new date value
|
|
3601
|
+
this.dateChange.subscribe(date => this.overlayToggle.setInputs({ date }));
|
|
3602
|
+
}
|
|
3603
|
+
/** @internal */
|
|
3604
|
+
touch() {
|
|
3605
|
+
this.onTouched();
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* On click shall show datepicker.
|
|
3609
|
+
*/
|
|
3610
|
+
onClick() {
|
|
3611
|
+
if (!this.externalTrigger) {
|
|
3612
|
+
this.show();
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
/**
|
|
3616
|
+
* Focus out shall close the datepicker except we are moving the focus to the datepicker.
|
|
3617
|
+
* @param event - focus out event with the related target
|
|
3618
|
+
*/
|
|
3619
|
+
onBlur(event) {
|
|
3620
|
+
const target = event.relatedTarget;
|
|
3621
|
+
if (!this.externalTrigger && !this.overlayToggle.contains(target)) {
|
|
3622
|
+
this.overlayToggle.closeOverlay();
|
|
3623
|
+
this.onTouched();
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
onTab() {
|
|
3627
|
+
if (this.overlayToggle.isShown()) {
|
|
3628
|
+
this.overlayToggle.closeOverlay();
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
/**
|
|
3632
|
+
* @internal
|
|
3633
|
+
*/
|
|
3634
|
+
show(initialFocus = false) {
|
|
3635
|
+
if (this.disabled() || this.readonly() || this.overlayToggle.isShown()) {
|
|
3636
|
+
return;
|
|
3637
|
+
}
|
|
3638
|
+
this.subscribeDateChanges(this.overlayToggle.showOverlay(initialFocus, {
|
|
3639
|
+
config: this.siDatepickerConfig(),
|
|
3640
|
+
date: this.date,
|
|
3641
|
+
time12h: this.getTime12h()
|
|
3642
|
+
}));
|
|
3643
|
+
}
|
|
3644
|
+
/**
|
|
3645
|
+
* @internal
|
|
3646
|
+
*/
|
|
3647
|
+
useExternalTrigger(element) {
|
|
3648
|
+
this.externalTrigger = element;
|
|
3649
|
+
}
|
|
3650
|
+
focusChange() {
|
|
3651
|
+
if (!this.externalTrigger) {
|
|
3652
|
+
this.show();
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
getTime12h() {
|
|
3656
|
+
const dateFormat = getDatepickerFormat(this.locale, this.siDatepickerConfig(), true);
|
|
3657
|
+
return dateFormat?.includes('a');
|
|
3658
|
+
}
|
|
3659
|
+
subscribeDateChanges(overlay) {
|
|
3660
|
+
this.overlaySubscriptions?.forEach(s => s.unsubscribe());
|
|
3661
|
+
overlay?.instance.date.subscribe(d => this.onDateChanged(d));
|
|
3662
|
+
overlay?.instance.disabledTimeChange.subscribe(d => this.onDisabledTime(d));
|
|
3663
|
+
}
|
|
3664
|
+
/**
|
|
3665
|
+
* Callback when the datepicker changes his value.
|
|
3666
|
+
* @param date - updated date
|
|
3667
|
+
*/
|
|
3668
|
+
onDateChanged(date) {
|
|
3669
|
+
// While typing a date can be invalid and the datepicker component will automatically change the date to undefined.
|
|
3670
|
+
// Since we don't want to reset the current input value it is necessary to ignore those updates.
|
|
3671
|
+
if (!date) {
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
super.onDateChanged(date);
|
|
3675
|
+
if (this.autoClose()) {
|
|
3676
|
+
// a tick later so the event won't end on the wrong element
|
|
3677
|
+
setTimeout(() => this.overlayToggle.closeAfterSelection());
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
3681
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: SiDatepickerDirective, isStandalone: true, selector: "[siDatepicker]", inputs: { autoClose: { classPropertyName: "autoClose", publicName: "autoClose", isSignal: true, isRequired: false, transformFunction: null }, triggeringInput: { classPropertyName: "triggeringInput", publicName: "triggeringInput", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick($event)", "keydown.tab": "onTab()", "focus": "focusChange()" } }, providers: [
|
|
3682
|
+
{
|
|
3683
|
+
provide: NG_VALUE_ACCESSOR,
|
|
3684
|
+
useExisting: SiDatepickerDirective,
|
|
3685
|
+
multi: true
|
|
3686
|
+
},
|
|
3687
|
+
{
|
|
3688
|
+
provide: NG_VALIDATORS,
|
|
3689
|
+
useExisting: SiDatepickerDirective,
|
|
3690
|
+
multi: true
|
|
3691
|
+
},
|
|
3692
|
+
{
|
|
3693
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
3694
|
+
useExisting: SiDatepickerDirective
|
|
3695
|
+
}
|
|
3696
|
+
], exportAs: ["siDatepicker"], usesInheritance: true, hostDirectives: [{ directive: SiDatepickerOverlayDirective, outputs: ["siDatepickerClose", "siDatepickerClose"] }], ngImport: i0 });
|
|
3697
|
+
}
|
|
3698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerDirective, decorators: [{
|
|
3699
|
+
type: Directive,
|
|
3700
|
+
args: [{
|
|
3701
|
+
selector: '[siDatepicker]',
|
|
3702
|
+
exportAs: 'siDatepicker',
|
|
3703
|
+
providers: [
|
|
3704
|
+
{
|
|
3705
|
+
provide: NG_VALUE_ACCESSOR,
|
|
3706
|
+
useExisting: SiDatepickerDirective,
|
|
3707
|
+
multi: true
|
|
3708
|
+
},
|
|
3709
|
+
{
|
|
3710
|
+
provide: NG_VALIDATORS,
|
|
3711
|
+
useExisting: SiDatepickerDirective,
|
|
3712
|
+
multi: true
|
|
3713
|
+
},
|
|
3714
|
+
{
|
|
3715
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
3716
|
+
useExisting: SiDatepickerDirective
|
|
3717
|
+
}
|
|
3718
|
+
],
|
|
3719
|
+
hostDirectives: [
|
|
3720
|
+
{
|
|
3721
|
+
directive: SiDatepickerOverlayDirective,
|
|
3722
|
+
outputs: ['siDatepickerClose']
|
|
3723
|
+
}
|
|
3724
|
+
]
|
|
3725
|
+
}]
|
|
3726
|
+
}], propDecorators: { onClick: [{
|
|
3727
|
+
type: HostListener,
|
|
3728
|
+
args: ['click', ['$event']]
|
|
3729
|
+
}], onTab: [{
|
|
3730
|
+
type: HostListener,
|
|
3731
|
+
args: ['keydown.tab']
|
|
3732
|
+
}], focusChange: [{
|
|
3733
|
+
type: HostListener,
|
|
3734
|
+
args: ['focus']
|
|
3735
|
+
}] } });
|
|
3736
|
+
|
|
3737
|
+
/**
|
|
3738
|
+
* Copyright Siemens 2016 - 2025.
|
|
3739
|
+
* SPDX-License-Identifier: MIT
|
|
3740
|
+
*/
|
|
3741
|
+
class SiDateRangeComponent {
|
|
3742
|
+
static idCounter = 0;
|
|
3743
|
+
inputDirectives = viewChildren(SiDateInputDirective);
|
|
3744
|
+
startInput = viewChild.required('startInput');
|
|
3745
|
+
endInput = viewChild.required('endInput');
|
|
3746
|
+
button = viewChild.required('button');
|
|
3747
|
+
/**
|
|
3748
|
+
* @defaultValue
|
|
3749
|
+
* ```
|
|
3750
|
+
* `__si-date-range-${SiDateRangeComponent.idCounter++}`
|
|
3751
|
+
* ```
|
|
3752
|
+
*/
|
|
3753
|
+
id = input(`__si-date-range-${SiDateRangeComponent.idCounter++}`);
|
|
3754
|
+
labelledby = inject(new HostAttributeToken('aria-labelledby'), {
|
|
3755
|
+
optional: true
|
|
3756
|
+
}) ?? `${this.id()}-label`;
|
|
3757
|
+
/**
|
|
3758
|
+
* Date range component configuration.
|
|
3759
|
+
*
|
|
3760
|
+
* @defaultValue
|
|
3761
|
+
* ```
|
|
3762
|
+
* { enableDateRange: true }
|
|
3763
|
+
* ```
|
|
3764
|
+
*/
|
|
3765
|
+
siDatepickerConfig = model({ enableDateRange: true });
|
|
3766
|
+
/**
|
|
3767
|
+
* Placeholder of the start date input.
|
|
3768
|
+
*
|
|
3769
|
+
* @defaultValue
|
|
3770
|
+
* ```
|
|
3771
|
+
* $localize`:@@SI_DATEPICKER.START_DATE_PLACEHOLDER:Start date`
|
|
3772
|
+
* ```
|
|
3773
|
+
*/
|
|
3774
|
+
startDatePlaceholder = input($localize `:@@SI_DATEPICKER.START_DATE_PLACEHOLDER:Start date`);
|
|
3775
|
+
/**
|
|
3776
|
+
* Placeholder of the end date input.
|
|
3777
|
+
*
|
|
3778
|
+
* @defaultValue
|
|
3779
|
+
* ```
|
|
3780
|
+
* $localize`:@@SI_DATEPICKER.END_DATE_PLACEHOLDER:End date`
|
|
3781
|
+
* ```
|
|
3782
|
+
*/
|
|
3783
|
+
endDatePlaceholder = input($localize `:@@SI_DATEPICKER.END_DATE_PLACEHOLDER:End date`);
|
|
3784
|
+
/**
|
|
3785
|
+
* Aria label of the date-range calendar toggle button.
|
|
3786
|
+
*
|
|
3787
|
+
* @defaultValue
|
|
3788
|
+
* ```
|
|
3789
|
+
* $localize`:@@SI_DATEPICKER.CALENDAR_TOGGLE_BUTTON:Open calendar`
|
|
3790
|
+
* ```
|
|
3791
|
+
*/
|
|
3792
|
+
ariaLabelCalendarButton = input($localize `:@@SI_DATEPICKER.CALENDAR_TOGGLE_BUTTON:Open calendar`);
|
|
3793
|
+
/**
|
|
3794
|
+
* Form label of the start timepicker.
|
|
3795
|
+
*
|
|
3796
|
+
* @defaultValue
|
|
3797
|
+
* ```
|
|
3798
|
+
* $localize`:@@SI_DATEPICKER.START_TIME_LABEL:from`
|
|
3799
|
+
* ```
|
|
3800
|
+
*/
|
|
3801
|
+
startTimeLabel = input($localize `:@@SI_DATEPICKER.START_TIME_LABEL:from`);
|
|
3802
|
+
/**
|
|
3803
|
+
* Form label of the start timepicker.
|
|
3804
|
+
*
|
|
3805
|
+
* @defaultValue
|
|
3806
|
+
* ```
|
|
3807
|
+
* $localize`:@@SI_DATEPICKER.END_TIME_LABEL:to`
|
|
3808
|
+
* ```
|
|
3809
|
+
*/
|
|
3810
|
+
endTimeLabel = input($localize `:@@SI_DATEPICKER.END_TIME_LABEL:to`);
|
|
3811
|
+
/**
|
|
3812
|
+
* @deprecated Property has no effect and will be removed without a replacement.
|
|
3813
|
+
*
|
|
3814
|
+
* @defaultValue 200
|
|
3815
|
+
*/
|
|
3816
|
+
debounceTime = input(200);
|
|
3817
|
+
/**
|
|
3818
|
+
* Automatically close overlay on date selection.
|
|
3819
|
+
*
|
|
3820
|
+
* @defaultValue false
|
|
3821
|
+
*/
|
|
3822
|
+
autoClose = input(false, { transform: booleanAttribute });
|
|
3823
|
+
/** Emits on the date range value changes. */
|
|
3824
|
+
siDatepickerRangeChange = output();
|
|
3825
|
+
/**
|
|
3826
|
+
* Emits an event to notify about disabling the time from the range picker.
|
|
3827
|
+
* When time is disable, we construct a pure date objects in UTC 00:00:00 time.
|
|
3828
|
+
*/
|
|
3829
|
+
disabledTimeChange = output();
|
|
3830
|
+
/**
|
|
3831
|
+
* Whether the date range input is disabled.
|
|
3832
|
+
*
|
|
3833
|
+
* @defaultValue false
|
|
3834
|
+
*/
|
|
3835
|
+
// eslint-disable-next-line @angular-eslint/no-input-rename
|
|
3836
|
+
disabledInput = input(false, { alias: 'disabled' });
|
|
3837
|
+
/**
|
|
3838
|
+
* Whether the date range input is readonly.
|
|
3839
|
+
*
|
|
3840
|
+
* @defaultValue false
|
|
3841
|
+
*/
|
|
3842
|
+
readonly = input(false, { transform: booleanAttribute });
|
|
3843
|
+
/**
|
|
3844
|
+
* Set the date-range object displayed in the control.
|
|
3845
|
+
* The input can be used if the control is used outside Angular forms.
|
|
3846
|
+
*/
|
|
3847
|
+
value = model();
|
|
3848
|
+
/** @internal */
|
|
3849
|
+
errormessageId = `${this.id()}-errormessage`;
|
|
3850
|
+
validator;
|
|
3851
|
+
onChange = (val) => { };
|
|
3852
|
+
onTouch = () => { };
|
|
3853
|
+
icons = addIcons({ elementCalendar });
|
|
3854
|
+
disabled = computed(() => this.disabledInput() || this.disabledNgControl());
|
|
3855
|
+
disabledNgControl = signal(false);
|
|
3856
|
+
cdRef = inject(ChangeDetectorRef);
|
|
3857
|
+
overlayToggle = inject(SiDatepickerOverlayDirective);
|
|
3858
|
+
elementRef = inject(ElementRef);
|
|
3859
|
+
defaultPlacement = [
|
|
3860
|
+
positionBottomCenter,
|
|
3861
|
+
positionBottomStart,
|
|
3862
|
+
positionBottomEnd,
|
|
3863
|
+
positionTopCenter,
|
|
3864
|
+
positionTopStart,
|
|
3865
|
+
positionTopEnd
|
|
3866
|
+
];
|
|
3867
|
+
ngOnChanges(changes) {
|
|
3868
|
+
if (changes.siDatepickerConfig) {
|
|
3869
|
+
this.siDatepickerConfig.set({
|
|
3870
|
+
...changes.siDatepickerConfig.currentValue,
|
|
3871
|
+
enableDateRange: true,
|
|
3872
|
+
startTimeLabel: changes.siDatepickerConfig.currentValue.startTimeLabel ?? this.startTimeLabel(),
|
|
3873
|
+
endTimeLabel: changes.siDatepickerConfig.currentValue.endTimeLabel ?? this.endTimeLabel()
|
|
3874
|
+
});
|
|
3875
|
+
if (changes.value) {
|
|
3876
|
+
this.updateValue(this.value());
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
this.overlayToggle.setInputs({ config: this.siDatepickerConfig(), dateRange: this.value() });
|
|
3880
|
+
}
|
|
3881
|
+
ngAfterViewInit() {
|
|
3882
|
+
this.validator = Validators.compose([
|
|
3883
|
+
() => this.endAfterStartValidator(),
|
|
3884
|
+
this.childValidation
|
|
3885
|
+
]);
|
|
3886
|
+
this.overlayToggle.placement.set(this.defaultPlacement);
|
|
3887
|
+
this.overlayToggle.siDatepickerClose.subscribe(cause => {
|
|
3888
|
+
if ([CloseCause.Escape, CloseCause.Select].includes(cause)) {
|
|
3889
|
+
this.button().nativeElement.focus();
|
|
3890
|
+
}
|
|
3891
|
+
else {
|
|
3892
|
+
// Mark component as touch when the focus isn't recovered on input
|
|
3893
|
+
this.onTouch();
|
|
3894
|
+
this.cdRef.markForCheck();
|
|
3895
|
+
}
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
writeValue(dateRange) {
|
|
3899
|
+
this.updateValue(dateRange);
|
|
3900
|
+
this.overlayToggle.setInputs({ dateRange });
|
|
3901
|
+
}
|
|
3902
|
+
registerOnChange(fn) {
|
|
3903
|
+
this.onChange = fn;
|
|
3904
|
+
}
|
|
3905
|
+
registerOnTouched(fn) {
|
|
3906
|
+
this.onTouch = fn;
|
|
3907
|
+
}
|
|
3908
|
+
setDisabledState(isDisabled) {
|
|
3909
|
+
this.disabledNgControl.set(isDisabled);
|
|
3910
|
+
}
|
|
3911
|
+
validate(c) {
|
|
3912
|
+
return this.validator ? this.validator(c) : null;
|
|
3913
|
+
}
|
|
3914
|
+
/**
|
|
3915
|
+
* Focus out shall close the datepicker except we are moving the focus to the datepicker or one of the input elements.
|
|
3916
|
+
* @param event - focus out event with the related target
|
|
3917
|
+
*/
|
|
3918
|
+
onFocusOut(event) {
|
|
3919
|
+
const target = event.relatedTarget;
|
|
3920
|
+
if (!this.overlayToggle.contains(target)) {
|
|
3921
|
+
this.overlayToggle.closeOverlay();
|
|
3922
|
+
// Only mark the component as touched when the focus is not moved to the datepicker or one of the input elements.
|
|
3923
|
+
if (!this.elementRef.nativeElement.contains(target)) {
|
|
3924
|
+
this.onTouch();
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
/** Forward date range input changes to datepicker overlay */
|
|
3929
|
+
onInputChanged(dateRange) {
|
|
3930
|
+
this.updateValue(dateRange);
|
|
3931
|
+
this.onChange(this.value());
|
|
3932
|
+
this.siDatepickerRangeChange.emit(this.value());
|
|
3933
|
+
}
|
|
3934
|
+
show() {
|
|
3935
|
+
if (this.readonly() || this.disabled()) {
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
this.subscribeRangeChanges(this.overlayToggle.showOverlay(true, {
|
|
3939
|
+
config: this.siDatepickerConfig(),
|
|
3940
|
+
dateRange: this.value()
|
|
3941
|
+
}));
|
|
3942
|
+
}
|
|
3943
|
+
subscribeRangeChanges(overlay) {
|
|
3944
|
+
overlay?.instance.dateRange.subscribe(d => this.onRangeChanged(d));
|
|
3945
|
+
overlay?.instance.disabledTimeChange.subscribe(disabledTime => {
|
|
3946
|
+
this.inputDirectives().forEach(inputDirective => inputDirective.onDisabledTime(disabledTime));
|
|
3947
|
+
this.disabledTimeChange.emit(disabledTime);
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
onRangeChanged(range) {
|
|
3951
|
+
this.updateValue(range);
|
|
3952
|
+
this.onChange(range);
|
|
3953
|
+
this.siDatepickerRangeChange.emit(range);
|
|
3954
|
+
this.validateChildren();
|
|
3955
|
+
if (this.autoClose() && this.value()?.start && this.value()?.end) {
|
|
3956
|
+
// We have to queue the close in the another cycle since other output event
|
|
3957
|
+
// emitters like rangeTypeChange can complete before we destroy the overlay.
|
|
3958
|
+
setTimeout(() => this.overlayToggle.closeAfterSelection());
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
/** Run validators on the start/end inputs. */
|
|
3962
|
+
validateChildren() {
|
|
3963
|
+
this.inputDirectives().forEach(d => d.validatorOnChange());
|
|
3964
|
+
}
|
|
3965
|
+
/** The form control validator for the end date is greater equal start date. */
|
|
3966
|
+
endAfterStartValidator() {
|
|
3967
|
+
const endDate = this.endInput().value;
|
|
3968
|
+
const startDate = this.startInput().value;
|
|
3969
|
+
return !endDate || !startDate || endDate >= startDate
|
|
3970
|
+
? null
|
|
3971
|
+
: {
|
|
3972
|
+
endBeforeStart: {
|
|
3973
|
+
start: startDate,
|
|
3974
|
+
end: endDate
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
childValidation = () => {
|
|
3979
|
+
const errors = {};
|
|
3980
|
+
this.readErrorsFromInnerControl(this.startInput(), 'Start', errors);
|
|
3981
|
+
this.readErrorsFromInnerControl(this.endInput(), 'End', errors);
|
|
3982
|
+
if (Object.keys(errors).length) {
|
|
3983
|
+
return errors;
|
|
3984
|
+
}
|
|
3985
|
+
return null;
|
|
3986
|
+
};
|
|
3987
|
+
readErrorsFromInnerControl(control, type, errors) {
|
|
3988
|
+
if (control.invalid) {
|
|
3989
|
+
const formatError = control.getError('dateFormat');
|
|
3990
|
+
if (formatError) {
|
|
3991
|
+
errors[`invalid${type}DateFormat`] = formatError;
|
|
3992
|
+
}
|
|
3993
|
+
const minError = control.getError('minDate');
|
|
3994
|
+
const siDatepickerConfig = this.siDatepickerConfig();
|
|
3995
|
+
if (minError) {
|
|
3996
|
+
errors.rangeBeforeMinDate = {
|
|
3997
|
+
min: getMinDate(siDatepickerConfig.minDate),
|
|
3998
|
+
start: this.startInput().value,
|
|
3999
|
+
end: this.endInput().value
|
|
4000
|
+
};
|
|
4001
|
+
}
|
|
4002
|
+
const maxError = control.getError('maxDate');
|
|
4003
|
+
if (maxError) {
|
|
4004
|
+
errors.rangeAfterMaxDate = {
|
|
4005
|
+
max: getMaxDate(siDatepickerConfig.maxDate),
|
|
4006
|
+
start: this.startInput().value,
|
|
4007
|
+
end: this.endInput().value
|
|
4008
|
+
};
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
updateValue(value) {
|
|
4013
|
+
// this allows angular's built in required validator to work correctly
|
|
4014
|
+
if (!value?.start && !value?.end) {
|
|
4015
|
+
this.value.set(undefined);
|
|
4016
|
+
}
|
|
4017
|
+
else {
|
|
4018
|
+
this.value.set(value);
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDateRangeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4022
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.6", type: SiDateRangeComponent, isStandalone: true, selector: "si-date-range", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, siDatepickerConfig: { classPropertyName: "siDatepickerConfig", publicName: "siDatepickerConfig", isSignal: true, isRequired: false, transformFunction: null }, startDatePlaceholder: { classPropertyName: "startDatePlaceholder", publicName: "startDatePlaceholder", isSignal: true, isRequired: false, transformFunction: null }, endDatePlaceholder: { classPropertyName: "endDatePlaceholder", publicName: "endDatePlaceholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelCalendarButton: { classPropertyName: "ariaLabelCalendarButton", publicName: "ariaLabelCalendarButton", isSignal: true, isRequired: false, transformFunction: null }, startTimeLabel: { classPropertyName: "startTimeLabel", publicName: "startTimeLabel", isSignal: true, isRequired: false, transformFunction: null }, endTimeLabel: { classPropertyName: "endTimeLabel", publicName: "endTimeLabel", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null }, autoClose: { classPropertyName: "autoClose", publicName: "autoClose", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { siDatepickerConfig: "siDatepickerConfigChange", siDatepickerRangeChange: "siDatepickerRangeChange", disabledTimeChange: "disabledTimeChange", value: "valueChange" }, host: { attributes: { "role": "group" }, listeners: { "focusout": "onFocusOut($event)" }, properties: { "class.disabled": "disabled()", "class.readonly": "readonly()", "attr.aria-labelledby": "labelledby" }, classAttribute: "form-control d-flex align-items-center pe-2" }, providers: [
|
|
4023
|
+
{
|
|
4024
|
+
provide: NG_VALUE_ACCESSOR,
|
|
4025
|
+
useExisting: SiDateRangeComponent,
|
|
4026
|
+
multi: true
|
|
4027
|
+
},
|
|
4028
|
+
{
|
|
4029
|
+
provide: NG_VALIDATORS,
|
|
4030
|
+
useExisting: SiDateRangeComponent,
|
|
4031
|
+
multi: true
|
|
4032
|
+
},
|
|
4033
|
+
{
|
|
4034
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
4035
|
+
useExisting: SiDateRangeComponent
|
|
4036
|
+
}
|
|
4037
|
+
], viewQueries: [{ propertyName: "inputDirectives", predicate: SiDateInputDirective, descendants: true, isSignal: true }, { propertyName: "startInput", first: true, predicate: ["startInput"], descendants: true, isSignal: true }, { propertyName: "endInput", first: true, predicate: ["endInput"], descendants: true, isSignal: true }, { propertyName: "button", first: true, predicate: ["button"], descendants: true, isSignal: true }], usesOnChanges: true, hostDirectives: [{ directive: SiDatepickerOverlayDirective, outputs: ["siDatepickerClose", "siDatepickerClose"] }], ngImport: i0, template: "<input\n #startInput=\"ngModel\"\n type=\"text\"\n class=\"border-0 p-0 focus-none\"\n siDateInput\n [ngModel]=\"value()?.start ?? null\"\n [siDatepickerConfig]=\"siDatepickerConfig()\"\n [placeholder]=\"startDatePlaceholder() | translate\"\n [attr.aria-label]=\"startDatePlaceholder() | translate\"\n [errormessageId]=\"errormessageId\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (ngModelChange)=\"onInputChanged({ start: $event, end: value()?.end })\"\n/>\n<span class=\"mx-3\">-</span>\n<input\n #endInput=\"ngModel\"\n type=\"text\"\n class=\"border-0 p-0 focus-none text-end\"\n siDateInput\n [ngModel]=\"value()?.end ?? null\"\n [siDatepickerConfig]=\"siDatepickerConfig()\"\n [placeholder]=\"endDatePlaceholder() | translate\"\n [attr.aria-label]=\"endDatePlaceholder() | translate\"\n [errormessageId]=\"errormessageId\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (ngModelChange)=\"onInputChanged({ start: value()?.start, end: $event })\"\n/>\n<button\n #button\n type=\"button\"\n class=\"btn btn-circle btn-tertiary btn-xs\"\n [attr.aria-label]=\"ariaLabelCalendarButton() | translate\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"show()\"\n>\n <si-icon-next [icon]=\"icons.elementCalendar\" />\n</button>\n", styles: [":host{display:block;min-inline-size:237px;--si-action-icon-offset: 22px}:host(:focus-within){outline:var(--element-button-focus-width) solid var(--element-focus-default);outline-offset:var(--element-button-focus-overlay-width)}input{flex-grow:1;background-color:transparent;min-inline-size:80px}input::placeholder{opacity:1;color:var(--element-text-secondary)}input:disabled,input[readonly]{background-color:var(--element-base-1);opacity:1}input:disabled::placeholder{color:transparent}input:not([readonly]):focus::placeholder,input:focus:not([readonly]):focus::placeholder{color:transparent}.disabled,.disabled:hover,.disabled:focus{--border-color: var(--element-ui-3);color:var(--element-text-disabled)}.btn-circle{margin-inline-start:var(--si-feedback-icon-offset, 4px)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: SiDateInputDirective, selector: "[siDateInput]", inputs: ["id", "siDatepickerConfig", "dateInputDebounceTime", "disabled", "readonly", "errormessageId"], outputs: ["siDatepickerConfigChange", "siDatepickerDisabledTime", "stateChange", "dateChange"], exportAs: ["siDateInput"] }, { kind: "component", type: SiIconNextComponent, selector: "si-icon-next", inputs: ["icon"] }, { kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i2.SiTranslatePipe, name: "translate" }, { kind: "ngmodule", type: A11yModule }] });
|
|
4038
|
+
}
|
|
4039
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDateRangeComponent, decorators: [{
|
|
4040
|
+
type: Component,
|
|
4041
|
+
args: [{ selector: 'si-date-range', host: {
|
|
4042
|
+
class: 'form-control d-flex align-items-center pe-2',
|
|
4043
|
+
role: 'group',
|
|
4044
|
+
'[class.disabled]': 'disabled()',
|
|
4045
|
+
'[class.readonly]': 'readonly()',
|
|
4046
|
+
'[attr.aria-labelledby]': 'labelledby'
|
|
4047
|
+
}, providers: [
|
|
4048
|
+
{
|
|
4049
|
+
provide: NG_VALUE_ACCESSOR,
|
|
4050
|
+
useExisting: SiDateRangeComponent,
|
|
4051
|
+
multi: true
|
|
4052
|
+
},
|
|
4053
|
+
{
|
|
4054
|
+
provide: NG_VALIDATORS,
|
|
4055
|
+
useExisting: SiDateRangeComponent,
|
|
4056
|
+
multi: true
|
|
4057
|
+
},
|
|
4058
|
+
{
|
|
4059
|
+
provide: SI_FORM_ITEM_CONTROL,
|
|
4060
|
+
useExisting: SiDateRangeComponent
|
|
4061
|
+
}
|
|
4062
|
+
], hostDirectives: [
|
|
4063
|
+
{
|
|
4064
|
+
directive: SiDatepickerOverlayDirective,
|
|
4065
|
+
outputs: ['siDatepickerClose']
|
|
4066
|
+
}
|
|
4067
|
+
], imports: [FormsModule, SiDateInputDirective, SiIconNextComponent, SiTranslateModule, A11yModule], template: "<input\n #startInput=\"ngModel\"\n type=\"text\"\n class=\"border-0 p-0 focus-none\"\n siDateInput\n [ngModel]=\"value()?.start ?? null\"\n [siDatepickerConfig]=\"siDatepickerConfig()\"\n [placeholder]=\"startDatePlaceholder() | translate\"\n [attr.aria-label]=\"startDatePlaceholder() | translate\"\n [errormessageId]=\"errormessageId\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (ngModelChange)=\"onInputChanged({ start: $event, end: value()?.end })\"\n/>\n<span class=\"mx-3\">-</span>\n<input\n #endInput=\"ngModel\"\n type=\"text\"\n class=\"border-0 p-0 focus-none text-end\"\n siDateInput\n [ngModel]=\"value()?.end ?? null\"\n [siDatepickerConfig]=\"siDatepickerConfig()\"\n [placeholder]=\"endDatePlaceholder() | translate\"\n [attr.aria-label]=\"endDatePlaceholder() | translate\"\n [errormessageId]=\"errormessageId\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (ngModelChange)=\"onInputChanged({ start: value()?.start, end: $event })\"\n/>\n<button\n #button\n type=\"button\"\n class=\"btn btn-circle btn-tertiary btn-xs\"\n [attr.aria-label]=\"ariaLabelCalendarButton() | translate\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"show()\"\n>\n <si-icon-next [icon]=\"icons.elementCalendar\" />\n</button>\n", styles: [":host{display:block;min-inline-size:237px;--si-action-icon-offset: 22px}:host(:focus-within){outline:var(--element-button-focus-width) solid var(--element-focus-default);outline-offset:var(--element-button-focus-overlay-width)}input{flex-grow:1;background-color:transparent;min-inline-size:80px}input::placeholder{opacity:1;color:var(--element-text-secondary)}input:disabled,input[readonly]{background-color:var(--element-base-1);opacity:1}input:disabled::placeholder{color:transparent}input:not([readonly]):focus::placeholder,input:focus:not([readonly]):focus::placeholder{color:transparent}.disabled,.disabled:hover,.disabled:focus{--border-color: var(--element-ui-3);color:var(--element-text-disabled)}.btn-circle{margin-inline-start:var(--si-feedback-icon-offset, 4px)}\n"] }]
|
|
4068
|
+
}], propDecorators: { onFocusOut: [{
|
|
4069
|
+
type: HostListener,
|
|
4070
|
+
args: ['focusout', ['$event']]
|
|
4071
|
+
}] } });
|
|
4072
|
+
|
|
4073
|
+
/**
|
|
4074
|
+
* Copyright Siemens 2016 - 2025.
|
|
4075
|
+
* SPDX-License-Identifier: MIT
|
|
4076
|
+
*/
|
|
4077
|
+
/**
|
|
4078
|
+
* Calendar toggle button used in combination with a datepicker directive.
|
|
4079
|
+
*
|
|
4080
|
+
* @example
|
|
4081
|
+
* ```
|
|
4082
|
+
* <si-calendar-button class="w-100">
|
|
4083
|
+
* <input
|
|
4084
|
+
* class="form-control"
|
|
4085
|
+
* type="text"
|
|
4086
|
+
* siDatepicker
|
|
4087
|
+
* [siDatepickerConfig]="config"
|
|
4088
|
+
* />
|
|
4089
|
+
* </si-calendar-button>
|
|
4090
|
+
* ```
|
|
4091
|
+
*/
|
|
4092
|
+
class SiCalendarButtonComponent {
|
|
4093
|
+
/**
|
|
4094
|
+
* Aria label for the calendar toggle button.
|
|
4095
|
+
*
|
|
4096
|
+
* @defaultValue
|
|
4097
|
+
* ```
|
|
4098
|
+
* $localize`:@@SI_DATEPICKER.CALENDAR_TOGGLE_BUTTON:Open calendar`
|
|
4099
|
+
* ```
|
|
4100
|
+
*/
|
|
4101
|
+
ariaLabel = input($localize `:@@SI_DATEPICKER.CALENDAR_TOGGLE_BUTTON:Open calendar`);
|
|
4102
|
+
button = viewChild.required('calendarButton');
|
|
4103
|
+
/** Datepicker input directive instance used to watch for state changes and required to open the calendar. */
|
|
4104
|
+
datepicker = contentChild.required(SiDatepickerDirective);
|
|
4105
|
+
datepickerOverlay = contentChild.required(SiDatepickerOverlayDirective);
|
|
4106
|
+
ngControl = contentChild(NgControl);
|
|
4107
|
+
disabled = signal(false);
|
|
4108
|
+
icons = addIcons({ elementCalendar });
|
|
4109
|
+
destroyerRef = inject(DestroyRef);
|
|
4110
|
+
focusMonitor = inject(FocusMonitor);
|
|
4111
|
+
elementRef = inject(ElementRef);
|
|
4112
|
+
// Add classes here to enable error messages in si-form-item
|
|
4113
|
+
showValidationMessages = signal(false);
|
|
4114
|
+
ngOnInit() {
|
|
4115
|
+
// Monitor input state changes and update the button accordingly
|
|
4116
|
+
this.datepicker().stateChange.subscribe(() => this.updateState());
|
|
4117
|
+
this.focusMonitor
|
|
4118
|
+
.monitor(this.elementRef, true)
|
|
4119
|
+
.pipe(takeUntilDestroyed(this.destroyerRef))
|
|
4120
|
+
.subscribe(origin => {
|
|
4121
|
+
setTimeout(() => {
|
|
4122
|
+
if (origin === null && !this.datepickerOverlay().isShown()) {
|
|
4123
|
+
this.datepicker().touch();
|
|
4124
|
+
}
|
|
4125
|
+
});
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
ngDoCheck() {
|
|
4129
|
+
const control = this.ngControl()?.control;
|
|
4130
|
+
this.showValidationMessages.set(!!control?.touched && !!control?.invalid);
|
|
4131
|
+
}
|
|
4132
|
+
ngAfterContentInit() {
|
|
4133
|
+
this.datepicker().useExternalTrigger(this.button());
|
|
4134
|
+
this.updateState();
|
|
4135
|
+
}
|
|
4136
|
+
show() {
|
|
4137
|
+
this.datepicker().show(true);
|
|
4138
|
+
}
|
|
4139
|
+
updateState() {
|
|
4140
|
+
const datepicker = this.datepicker();
|
|
4141
|
+
this.disabled.set(datepicker.disabled() || datepicker.readonly());
|
|
4142
|
+
}
|
|
4143
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4144
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.6", type: SiCalendarButtonComponent, isStandalone: true, selector: "si-calendar-button", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.ng-invalid": "showValidationMessages()", "class.ng-touched": "showValidationMessages()" }, classAttribute: "d-inline-block position-relative form-control-wrapper" }, queries: [{ propertyName: "datepicker", first: true, predicate: SiDatepickerDirective, descendants: true, isSignal: true }, { propertyName: "datepickerOverlay", first: true, predicate: SiDatepickerOverlayDirective, descendants: true, isSignal: true }, { propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "button", first: true, predicate: ["calendarButton"], descendants: true, isSignal: true }], ngImport: i0, template: `<ng-content />
|
|
4145
|
+
<button
|
|
4146
|
+
#calendarButton
|
|
4147
|
+
name="open-calendar"
|
|
4148
|
+
type="button"
|
|
4149
|
+
class="btn btn-circle btn-tertiary btn-xs position-absolute end-0 top-0 me-2 mt-2"
|
|
4150
|
+
[attr.aria-label]="ariaLabel() | translate"
|
|
4151
|
+
[disabled]="disabled()"
|
|
4152
|
+
(click)="show()"
|
|
4153
|
+
>
|
|
4154
|
+
<si-icon-next [icon]="icons.elementCalendar" />
|
|
4155
|
+
</button>`, isInline: true, styles: [":host{--si-action-icon-offset: 24px}\n"], dependencies: [{ kind: "component", type: SiIconNextComponent, selector: "si-icon-next", inputs: ["icon"] }, { kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i2.SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4156
|
+
}
|
|
4157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiCalendarButtonComponent, decorators: [{
|
|
4158
|
+
type: Component,
|
|
4159
|
+
args: [{ selector: 'si-calendar-button', template: `<ng-content />
|
|
4160
|
+
<button
|
|
4161
|
+
#calendarButton
|
|
4162
|
+
name="open-calendar"
|
|
4163
|
+
type="button"
|
|
4164
|
+
class="btn btn-circle btn-tertiary btn-xs position-absolute end-0 top-0 me-2 mt-2"
|
|
4165
|
+
[attr.aria-label]="ariaLabel() | translate"
|
|
4166
|
+
[disabled]="disabled()"
|
|
4167
|
+
(click)="show()"
|
|
4168
|
+
>
|
|
4169
|
+
<si-icon-next [icon]="icons.elementCalendar" />
|
|
4170
|
+
</button>`, host: {
|
|
4171
|
+
class: 'd-inline-block position-relative form-control-wrapper',
|
|
4172
|
+
'[class.ng-invalid]': 'showValidationMessages()',
|
|
4173
|
+
'[class.ng-touched]': 'showValidationMessages()'
|
|
4174
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SiIconNextComponent, SiTranslateModule], styles: [":host{--si-action-icon-offset: 24px}\n"] }]
|
|
4175
|
+
}] });
|
|
4176
|
+
|
|
4177
|
+
/**
|
|
4178
|
+
* Copyright Siemens 2016 - 2025.
|
|
4179
|
+
* SPDX-License-Identifier: MIT
|
|
4180
|
+
*/
|
|
4181
|
+
class SiDatepickerModule {
|
|
4182
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
4183
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerModule, imports: [SiCalendarButtonComponent,
|
|
4184
|
+
SiDateInputDirective,
|
|
4185
|
+
SiDatepickerComponent,
|
|
4186
|
+
SiDatepickerDirective,
|
|
4187
|
+
SiDateRangeComponent,
|
|
4188
|
+
SiTimepickerComponent], exports: [SiCalendarButtonComponent,
|
|
4189
|
+
SiDateInputDirective,
|
|
4190
|
+
SiDatepickerComponent,
|
|
4191
|
+
SiDatepickerDirective,
|
|
4192
|
+
SiDateRangeComponent,
|
|
4193
|
+
SiTimepickerComponent] });
|
|
4194
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerModule, imports: [SiCalendarButtonComponent,
|
|
4195
|
+
SiDatepickerComponent,
|
|
4196
|
+
SiDateRangeComponent,
|
|
4197
|
+
SiTimepickerComponent] });
|
|
4198
|
+
}
|
|
4199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SiDatepickerModule, decorators: [{
|
|
4200
|
+
type: NgModule,
|
|
4201
|
+
args: [{
|
|
4202
|
+
imports: [
|
|
4203
|
+
SiCalendarButtonComponent,
|
|
4204
|
+
SiDateInputDirective,
|
|
4205
|
+
SiDatepickerComponent,
|
|
4206
|
+
SiDatepickerDirective,
|
|
4207
|
+
SiDateRangeComponent,
|
|
4208
|
+
SiTimepickerComponent
|
|
4209
|
+
],
|
|
4210
|
+
exports: [
|
|
4211
|
+
SiCalendarButtonComponent,
|
|
4212
|
+
SiDateInputDirective,
|
|
4213
|
+
SiDatepickerComponent,
|
|
4214
|
+
SiDatepickerDirective,
|
|
4215
|
+
SiDateRangeComponent,
|
|
4216
|
+
SiTimepickerComponent
|
|
4217
|
+
]
|
|
4218
|
+
}]
|
|
4219
|
+
}] });
|
|
4220
|
+
|
|
4221
|
+
/**
|
|
4222
|
+
* Copyright Siemens 2016 - 2025.
|
|
4223
|
+
* SPDX-License-Identifier: MIT
|
|
4224
|
+
*/
|
|
4225
|
+
|
|
4226
|
+
/**
|
|
4227
|
+
* Generated bundle index. Do not edit.
|
|
4228
|
+
*/
|
|
4229
|
+
|
|
4230
|
+
export { CloseCause, SiCalendarButtonComponent, SiDateInputDirective, SiDateRangeComponent, SiDatepickerComponent, SiDatepickerDirective, SiDatepickerModule, SiDatepickerOverlayComponent, SiDatepickerOverlayDirective, SiTimepickerComponent, WEEK_START_OFFSET, addDays, addDaysInRange, addMonthsInRange, addYearsInRange, changeDay, compareDate, compareMonth, compareYear, createDate, daysInMonth, getDateSameOrBetween, getDateWithoutTime, getDatepickerFormat, getDayStrings, getDaysOfWeek, getFirstDateInMonth, getFirstDateInYear, getFirstDayInMonth, getLastDateInMonth, getLocaleMonthNames, getMaxDate, getMinDate, getNamedFormat, getStringforDate, getWeekDayOffset, getWeekEndDate, getWeekOfYear, getWeekStartDate, isAfter, isAfterMonth, isAfterYear, isAnotherMonth, isAnotherMonthOrYear, isAnotherYear, isBetween, isBetweenMonth, isBetweenYears, isSameDate, isSameMonth, isSameOrBefore, isSameOrBeforeMonth, isSameOrBeforeYear, isSameOrBetween, isSameOrBetweenMonth, isSameOrBetweenYears, isSameYear, isValid, maxDate, minDate, nextMonth, parseDate, previousMonth, today };
|
|
4231
|
+
//# sourceMappingURL=siemens-element-ng-datepicker.mjs.map
|