@bluevolt-tech/lumen 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +37 -23
  2. package/dist/components/_shared/adopt-styles.d.ts +8 -0
  3. package/dist/components/_shared/date-range-validator.d.ts +86 -0
  4. package/dist/components/_shared/dropdown-controller.d.ts +60 -0
  5. package/dist/components/_shared/selectable-card.d.ts +10 -0
  6. package/dist/components/bv-alert-banner/bv-alert-banner.js +19 -17
  7. package/dist/components/bv-avatar/bv-avatar.js +19 -11
  8. package/dist/components/bv-badge/bv-badge.js +19 -11
  9. package/dist/components/bv-button/bv-button.js +18 -16
  10. package/dist/components/bv-category-pill/bv-category-pill.js +18 -16
  11. package/dist/components/bv-checkbox/bv-checkbox.js +19 -11
  12. package/dist/components/bv-chip/bv-chip.js +19 -11
  13. package/dist/components/bv-course-detail/bv-course-detail.js +20 -34
  14. package/dist/components/bv-course-detail-modal/bv-course-detail-modal.js +24 -90
  15. package/dist/components/bv-course-grid-card/bv-course-grid-card.d.ts +2 -6
  16. package/dist/components/bv-course-grid-card/bv-course-grid-card.js +98 -105
  17. package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +2 -6
  18. package/dist/components/bv-course-list-card/bv-course-list-card.js +98 -105
  19. package/dist/components/bv-course-outline/bv-course-outline.js +19 -11
  20. package/dist/components/bv-date-range-input/bv-date-range-input.d.ts +10 -6
  21. package/dist/components/bv-date-range-input/bv-date-range-input.js +602 -125
  22. package/dist/components/bv-date-range-picker/bv-date-range-picker.d.ts +71 -4
  23. package/dist/components/bv-date-range-picker/bv-date-range-picker.js +441 -76
  24. package/dist/components/bv-empty-state/bv-empty-state.js +20 -28
  25. package/dist/components/bv-filter-dropdown/bv-filter-dropdown.d.ts +1 -4
  26. package/dist/components/bv-filter-dropdown/bv-filter-dropdown.js +80 -53
  27. package/dist/components/bv-filter-panel/bv-filter-panel.js +20 -22
  28. package/dist/components/bv-icon/bv-icon.js +19 -11
  29. package/dist/components/bv-input/bv-input.js +18 -16
  30. package/dist/components/bv-input-dropdown/bv-input-dropdown.d.ts +1 -4
  31. package/dist/components/bv-input-dropdown/bv-input-dropdown.js +79 -78
  32. package/dist/components/bv-modal/bv-modal.js +19 -11
  33. package/dist/components/bv-page-title/bv-page-title.js +19 -6
  34. package/dist/components/bv-pagination/bv-pagination.d.ts +1 -0
  35. package/dist/components/bv-pagination/bv-pagination.js +23 -186
  36. package/dist/components/bv-portal-header/bv-portal-header.js +25 -83
  37. package/dist/components/bv-portal-layout/bv-portal-layout.js +19 -11
  38. package/dist/components/bv-progress-bar/bv-progress-bar.js +19 -11
  39. package/dist/components/bv-promo-banner/bv-promo-banner.js +19 -11
  40. package/dist/components/bv-radio/bv-radio.js +19 -11
  41. package/dist/components/bv-searchbox/bv-searchbox.js +19 -11
  42. package/dist/components/bv-segmented-control/bv-segmented-control.js +18 -16
  43. package/dist/components/bv-select/bv-select.d.ts +1 -4
  44. package/dist/components/bv-select/bv-select.js +78 -44
  45. package/dist/components/bv-split-button/bv-split-button.d.ts +1 -4
  46. package/dist/components/bv-split-button/bv-split-button.js +78 -42
  47. package/dist/components/bv-star-rating/bv-star-rating.js +19 -11
  48. package/dist/components/bv-table/bv-table.d.ts +5 -0
  49. package/dist/components/bv-table/bv-table.js +70 -19
  50. package/dist/components/bv-toggle/bv-toggle.js +19 -11
  51. package/dist/components/bv-tooltip/bv-tooltip.js +20 -17
  52. package/dist/components/bv-user-row/bv-user-row.js +22 -50
  53. package/dist/components/bv-view-switcher/bv-view-switcher.js +19 -11
  54. package/dist/themes/base.css +1 -1
  55. package/package.json +8 -1
  56. package/themes/base.css +1 -1
@@ -1,5 +1,116 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-date-range-picker/bv-date-range-picker.css
2
- var bv_date_range_picker_default = ":host {\n display: inline-block;\n font-family: var(--bv-font-family-base);\n}\n\n/* \u2500\u2500 Picker shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.picker {\n display: flex;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-200);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-lg);\n overflow: hidden;\n user-select: none;\n}\n\n/* \u2500\u2500 Sidebar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.sidebar {\n display: flex;\n flex-direction: column;\n width: 152px;\n flex-shrink: 0;\n padding: var(--bv-spacing-sm) 0;\n border-right: 1px solid var(--bv-color-neutral-200);\n}\n\n.preset-btn {\n display: block;\n width: 100%;\n padding: 10px var(--bv-spacing-md);\n text-align: left;\n background: none;\n border: none;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n white-space: nowrap;\n}\n\n.preset-btn:hover {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-900);\n}\n\n.sidebar-sep {\n margin: var(--bv-spacing-xs) 0;\n border: none;\n border-top: 1px solid var(--bv-color-neutral-200);\n}\n\n.preset-btn.active {\n background: var(--bv-color-secondary-light);\n color: var(--bv-color-neutral-900);\n font-weight: var(--bv-font-weight-medium);\n}\n\n/* \u2500\u2500 Main panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.main {\n display: flex;\n flex-direction: column;\n flex: 1;\n}\n\n/* \u2500\u2500 Calendars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.calendars {\n display: flex;\n border-bottom: 1px solid var(--bv-color-neutral-200);\n}\n\n.calendar {\n padding: var(--bv-spacing-md) var(--bv-spacing-lg);\n flex: 1;\n}\n\n.calendar--left {\n border-right: 1px solid var(--bv-color-neutral-200);\n}\n\n/* \u2500\u2500 Calendar header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.calendar-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: var(--bv-spacing-md);\n}\n\n.calendar-title {\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n}\n\n.nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n background: none;\n border: none;\n border-radius: var(--bv-radius-md);\n color: var(--bv-color-neutral-500);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n flex-shrink: 0;\n}\n\n.nav-btn:hover {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-800);\n}\n\n/* \u2500\u2500 Weekdays row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.weekdays {\n display: grid;\n grid-template-columns: repeat(7, 36px);\n gap: 0;\n margin-bottom: var(--bv-spacing-xs);\n}\n\n.weekday {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 28px;\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-500);\n}\n\n/* \u2500\u2500 Days grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.days-grid {\n display: grid;\n grid-template-columns: repeat(7, 36px);\n gap: 0;\n}\n\n/* \u2500\u2500 Day cell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n border-radius: 0;\n transition: background var(--bv-transition-fast);\n}\n\n.day:hover:not(.start):not(.end) .day-inner {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-900);\n}\n\n.day--blank {\n pointer-events: none;\n}\n\n.day.today:not(.start):not(.end) .day-inner::after {\n content: '';\n position: absolute;\n bottom: 2px;\n left: 50%;\n transform: translateX(-50%);\n width: 4px;\n height: 4px;\n border-radius: var(--bv-radius-full);\n background: var(--bv-color-primary);\n}\n\n/* \u2500\u2500 Range band \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day.in-range {\n background: var(--bv-color-primary-light);\n}\n\n.day.in-range.week-start,\n.day.range-end.week-start {\n border-radius: var(--bv-radius-full) 0 0 var(--bv-radius-full);\n}\n\n.day.in-range.week-end,\n.day.range-start.week-end {\n border-radius: 0 var(--bv-radius-full) var(--bv-radius-full) 0;\n}\n\n.day.range-start {\n background: linear-gradient(to right, transparent 50%, var(--bv-color-primary-light) 50%);\n}\n\n.day.range-end {\n background: linear-gradient(to left, transparent 50%, var(--bv-color-primary-light) 50%);\n}\n\n/* \u2500\u2500 Day inner (circle) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day-inner {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: var(--bv-radius-full);\n z-index: 1;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n}\n\n.day.start .day-inner,\n.day.end .day-inner {\n background: var(--bv-color-primary);\n color: var(--bv-color-neutral-0);\n font-weight: var(--bv-font-weight-semibold);\n}\n\n.day.start:hover .day-inner,\n.day.end:hover .day-inner {\n background: var(--bv-color-primary-dark);\n}\n\n/* \u2500\u2500 5-year warning banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-wrap {\n display: none;\n padding: var(--bv-spacing-lg);\n}\n\n.picker--with-banner .banner-wrap {\n display: block;\n}\n\n/* Footer loses its top padding when the banner is shown above it */\n.picker--with-banner .footer {\n padding-top: 0;\n}\n\n/* \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--bv-spacing-lg);\n gap: var(--bv-spacing-md);\n}\n\n/* \u2500\u2500 Date inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.date-inputs {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-sm);\n}\n\n.date-input {\n width: 112px;\n height: 36px;\n padding: 0 var(--bv-spacing-sm);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-lg);\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-700);\n text-align: center;\n outline: none;\n transition:\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n.date-input:focus {\n border-color: var(--bv-color-primary);\n box-shadow: 0 0 0 3px var(--bv-color-primary-light);\n}\n\n.date-input::placeholder {\n color: var(--bv-color-neutral-400);\n}\n\n.date-sep {\n color: var(--bv-color-neutral-400);\n font-size: var(--bv-font-size-sm);\n}\n\n/* \u2500\u2500 Action buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.footer-actions {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n}\n\n.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 0 var(--bv-spacing-md);\n border-radius: var(--bv-radius-lg);\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n cursor: pointer;\n white-space: nowrap;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n color var(--bv-transition-fast);\n}\n\n.btn-cancel {\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n color: var(--bv-color-neutral-700);\n}\n\n.btn-cancel:hover {\n background: var(--bv-color-secondary);\n border-color: var(--bv-color-neutral-400);\n}\n\n.btn-apply {\n background: var(--bv-color-primary);\n border: 1px solid var(--bv-color-primary);\n color: var(--bv-color-neutral-0);\n}\n\n.btn-apply:hover:not(:disabled) {\n background: var(--bv-color-primary-dark);\n border-color: var(--bv-color-primary-dark);\n}\n\n.btn-apply:disabled {\n background: var(--bv-color-neutral-300);\n border-color: var(--bv-color-neutral-300);\n cursor: not-allowed;\n}\n";
2
+ var bv_date_range_picker_default = ":host {\n display: inline-block;\n font-family: var(--bv-font-family-base);\n}\n\n/* \u2500\u2500 Picker shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.picker {\n display: flex;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-200);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-lg);\n overflow: hidden;\n user-select: none;\n}\n\n/* \u2500\u2500 Sidebar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.sidebar {\n display: flex;\n flex-direction: column;\n width: 152px;\n flex-shrink: 0;\n padding: var(--bv-spacing-sm) 0;\n border-right: 1px solid var(--bv-color-neutral-200);\n}\n\n.preset-btn {\n display: block;\n width: 100%;\n padding: 10px var(--bv-spacing-md);\n text-align: left;\n background: none;\n border: none;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n white-space: nowrap;\n}\n\n.preset-btn:hover {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-900);\n}\n\n.sidebar-sep {\n margin: var(--bv-spacing-xs) 0;\n border: none;\n border-top: 1px solid var(--bv-color-neutral-200);\n}\n\n.preset-btn.active {\n background: var(--bv-color-secondary-light);\n color: var(--bv-color-neutral-900);\n font-weight: var(--bv-font-weight-medium);\n}\n\n/* \u2500\u2500 Main panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.main {\n display: flex;\n flex-direction: column;\n flex: 1;\n}\n\n/* \u2500\u2500 Calendars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.calendars {\n display: flex;\n border-bottom: 1px solid var(--bv-color-neutral-200);\n}\n\n.calendar {\n padding: var(--bv-spacing-md) var(--bv-spacing-lg);\n flex: 1;\n}\n\n.calendar--left {\n border-right: 1px solid var(--bv-color-neutral-200);\n}\n\n/* \u2500\u2500 Calendar header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.calendar-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: var(--bv-spacing-md);\n}\n\n.calendar-title {\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n}\n\n.nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n background: none;\n border: none;\n border-radius: var(--bv-radius-md);\n color: var(--bv-color-neutral-500);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n flex-shrink: 0;\n}\n\n.nav-btn:hover {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-800);\n}\n\n/* \u2500\u2500 Weekdays row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.weekdays {\n display: grid;\n grid-template-columns: repeat(7, 36px);\n gap: 0;\n margin-bottom: var(--bv-spacing-xs);\n}\n\n.weekday {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 28px;\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-500);\n}\n\n/* \u2500\u2500 Days grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.days-grid {\n display: grid;\n grid-template-columns: repeat(7, 36px);\n gap: 0;\n}\n\n/* \u2500\u2500 Day cell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n border-radius: 0;\n transition: background var(--bv-transition-fast);\n}\n\n.day:hover:not(.start):not(.end) .day-inner {\n background: var(--bv-color-secondary);\n color: var(--bv-color-neutral-900);\n}\n\n.day--blank {\n pointer-events: none;\n}\n\n.day.today:not(.start):not(.end) .day-inner::after {\n content: '';\n position: absolute;\n bottom: 2px;\n left: 50%;\n transform: translateX(-50%);\n width: 4px;\n height: 4px;\n border-radius: var(--bv-radius-full);\n background: var(--bv-color-primary);\n}\n\n/* \u2500\u2500 Range band \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day.in-range {\n background: var(--bv-color-primary-light);\n}\n\n.day.in-range.week-start,\n.day.range-end.week-start {\n border-radius: var(--bv-radius-full) 0 0 var(--bv-radius-full);\n}\n\n.day.in-range.week-end,\n.day.range-start.week-end {\n border-radius: 0 var(--bv-radius-full) var(--bv-radius-full) 0;\n}\n\n.day.range-start {\n background: linear-gradient(to right, transparent 50%, var(--bv-color-primary-light) 50%);\n}\n\n.day.range-end {\n background: linear-gradient(to left, transparent 50%, var(--bv-color-primary-light) 50%);\n}\n\n/* \u2500\u2500 Day inner (circle) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.day-inner {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: var(--bv-radius-full);\n z-index: 1;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast);\n}\n\n.day.start .day-inner,\n.day.end .day-inner {\n background: var(--bv-color-primary);\n color: var(--bv-color-neutral-0);\n font-weight: var(--bv-font-weight-semibold);\n}\n\n.day.start:hover .day-inner,\n.day.end:hover .day-inner {\n background: var(--bv-color-primary-dark);\n}\n\n/* \u2500\u2500 Alert banner (BTN-10312 \u2014 unified for all validation errors) \u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-wrap {\n display: none;\n padding: var(--bv-spacing-lg);\n}\n\n.picker--with-banner .banner-wrap {\n display: block;\n}\n\n/* Footer loses its top padding when the banner is shown above it */\n.picker--with-banner .footer {\n padding-top: 0;\n}\n\n/* \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--bv-spacing-lg);\n gap: var(--bv-spacing-md);\n}\n\n/* \u2500\u2500 Date inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.date-inputs {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-sm);\n}\n\n.date-input {\n width: 112px;\n height: 36px;\n padding: 0 var(--bv-spacing-sm);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-lg);\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-700);\n text-align: center;\n outline: none;\n transition:\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n.date-input:focus {\n border-color: var(--bv-color-primary);\n box-shadow: 0 0 0 3px var(--bv-color-primary-light);\n}\n\n.date-input::placeholder {\n color: var(--bv-color-neutral-400);\n}\n\n.date-sep {\n color: var(--bv-color-neutral-400);\n font-size: var(--bv-font-size-sm);\n}\n\n/* \u2500\u2500 Action buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.footer-actions {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n}\n\n.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 0 var(--bv-spacing-md);\n border-radius: var(--bv-radius-lg);\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n cursor: pointer;\n white-space: nowrap;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n color var(--bv-transition-fast);\n}\n\n.btn-cancel {\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n color: var(--bv-color-neutral-700);\n}\n\n.btn-cancel:hover {\n background: var(--bv-color-secondary);\n border-color: var(--bv-color-neutral-400);\n}\n\n.btn-apply {\n background: var(--bv-color-primary);\n border: 1px solid var(--bv-color-primary);\n color: var(--bv-color-neutral-0);\n}\n\n.btn-apply:hover:not(:disabled) {\n background: var(--bv-color-primary-dark);\n border-color: var(--bv-color-primary-dark);\n}\n\n.btn-apply:disabled {\n background: var(--bv-color-neutral-300);\n border-color: var(--bv-color-neutral-300);\n cursor: not-allowed;\n}\n\n/* \u2500\u2500 Validation error states (BTN-10191 / BTN-10312) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/*\n * `.invalid` is applied to input fields only when the field's error resolves\n * to severity 'error' (blocking). Non-blocking severities (warning/primary/\n * success) surface via the banner but do not paint the input red.\n */\n.date-input.invalid,\n.date-input.invalid:focus {\n border-color: var(--bv-color-danger);\n box-shadow: 0 0 0 3px var(--bv-color-danger-light);\n}\n\n.day--disabled,\n.day[disabled] {\n color: var(--bv-color-neutral-300);\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.day--disabled .day-inner,\n.day[disabled] .day-inner {\n color: var(--bv-color-neutral-300);\n}\n";
3
+
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
22
+ // components/_shared/date-range-validator.ts
23
+ var ALLOWED_SEVERITIES = ["error", "warning", "primary", "success"];
24
+ function normalizeSeverity(value) {
25
+ if (value && ALLOWED_SEVERITIES.includes(value)) {
26
+ return value;
27
+ }
28
+ return "error";
29
+ }
30
+ function validateDateRange(input, config = {}) {
31
+ const { start, end } = input;
32
+ const { min, max, maxRangeYears, validateOrder, validateFormat } = config;
33
+ const resolvedMax = max === "today" ? isoToday() : max;
34
+ let startError;
35
+ let endError;
36
+ const startDate = start ? parseIso(start) : null;
37
+ const endDate = end ? parseIso(end) : null;
38
+ if (validateFormat) {
39
+ if (start && !startDate) startError = "format";
40
+ if (end && !endDate) endError = "format";
41
+ }
42
+ if (!startError && startDate) {
43
+ if (min && belowMin(startDate, min)) startError = "min";
44
+ else if (resolvedMax && aboveMax(startDate, resolvedMax)) startError = "max";
45
+ }
46
+ if (!endError && endDate) {
47
+ if (min && belowMin(endDate, min)) endError = "min";
48
+ else if (resolvedMax && aboveMax(endDate, resolvedMax)) endError = "max";
49
+ }
50
+ let formError;
51
+ const bothFieldsClean = startDate && endDate && !startError && !endError;
52
+ if (bothFieldsClean) {
53
+ if (validateOrder && startDate > endDate) {
54
+ formError = "order";
55
+ } else if (maxRangeYears !== void 0 && exceedsYearSpan(startDate, endDate, maxRangeYears)) {
56
+ formError = "range-limit";
57
+ }
58
+ }
59
+ const valid = !startError && !endError && !formError;
60
+ const startSev = startError ? severityOfFieldError(startError, config) : null;
61
+ const endSev = endError ? severityOfFieldError(endError, config) : null;
62
+ const formSev = formError ? severityOfFormError(formError, config) : null;
63
+ const hasBlockingError = startSev === "error" || endSev === "error" || formSev === "error";
64
+ const applyEnabled = !hasBlockingError && !!startDate && !!endDate;
65
+ return {
66
+ valid,
67
+ applyEnabled,
68
+ ...startError && { startError },
69
+ ...endError && { endError },
70
+ ...formError && { formError }
71
+ };
72
+ }
73
+ function severityOfFieldError(err, config) {
74
+ if (err === "format") return normalizeSeverity(config.formatSeverity);
75
+ if (err === "min") return normalizeSeverity(config.minSeverity);
76
+ return normalizeSeverity(config.maxSeverity);
77
+ }
78
+ function severityOfFormError(err, config) {
79
+ if (err === "order") return normalizeSeverity(config.orderSeverity);
80
+ return normalizeSeverity(config.rangeLimitSeverity);
81
+ }
82
+ function parseIso(s) {
83
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
84
+ if (!m) return null;
85
+ const [, yStr, mStr, dStr] = m;
86
+ const y = Number(yStr);
87
+ const mo = Number(mStr) - 1;
88
+ const d = Number(dStr);
89
+ const dt = new Date(y, mo, d);
90
+ if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
91
+ dt.setHours(0, 0, 0, 0);
92
+ return dt;
93
+ }
94
+ function isoToday() {
95
+ const now = /* @__PURE__ */ new Date();
96
+ const y = now.getFullYear();
97
+ const m = String(now.getMonth() + 1).padStart(2, "0");
98
+ const d = String(now.getDate()).padStart(2, "0");
99
+ return `${y}-${m}-${d}`;
100
+ }
101
+ function belowMin(date, minIso) {
102
+ const minDate = parseIso(minIso);
103
+ return !!minDate && date < minDate;
104
+ }
105
+ function aboveMax(date, maxIso) {
106
+ const maxDate = parseIso(maxIso);
107
+ return !!maxDate && date > maxDate;
108
+ }
109
+ function exceedsYearSpan(start, end, years) {
110
+ const cutoff = new Date(start);
111
+ cutoff.setFullYear(cutoff.getFullYear() + years);
112
+ return end > cutoff;
113
+ }
3
114
 
4
115
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-alert-banner/bv-alert-banner.css
5
116
  var bv_alert_banner_default = ":host {\n --banner-bg: var(--bv-color-neutral-0);\n --banner-border: var(--bv-color-neutral-200);\n --banner-color: var(--bv-color-neutral-700);\n --banner-color-subtle: var(--bv-color-neutral-500);\n --banner-icon-color: var(--bv-color-neutral-500);\n\n display: block;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n:host([variant='primary']) {\n --banner-bg: var(--bv-color-primary-50);\n --banner-border: color-mix(in srgb, var(--bv-color-primary) 40%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-primary);\n --banner-color-subtle: var(--bv-color-primary);\n --banner-icon-color: var(--bv-color-primary);\n}\n\n:host([variant='error']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-danger-light) 35%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-danger) 45%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-danger-dark);\n --banner-color-subtle: var(--bv-color-danger-dark);\n --banner-icon-color: var(--bv-color-danger);\n}\n\n:host([variant='warning']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-warning-light) 35%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-warning) 50%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-warning-dark);\n --banner-color-subtle: var(--bv-color-warning-dark);\n --banner-icon-color: var(--bv-color-warning);\n}\n\n:host([variant='success']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-success-light) 30%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-success) 40%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-success-dark);\n --banner-color-subtle: var(--bv-color-success-dark);\n --banner-icon-color: var(--bv-color-success);\n}\n\n/* \u2500\u2500 Banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner {\n display: flex;\n align-items: flex-start;\n gap: var(--bv-spacing-md);\n padding: var(--bv-spacing-md);\n background: var(--banner-bg);\n border: 1px solid var(--banner-border);\n border-radius: var(--bv-radius-xl);\n}\n\n/* \u2500\u2500 Icon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-icon {\n display: flex;\n flex-shrink: 0;\n margin-top: 0;\n color: var(--banner-icon-color);\n line-height: 0;\n}\n\n/* \u2500\u2500 Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-xs);\n min-width: 0;\n}\n\n.banner-heading {\n margin: 0;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n color: var(--banner-color);\n}\n\n.banner-heading[hidden] {\n display: none;\n}\n\n.banner-message {\n margin: 0;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n line-height: 20px;\n color: var(--banner-color);\n}\n\n.banner-message[hidden] {\n display: none;\n}\n\n/* \u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-actions {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-md);\n margin-top: var(--bv-spacing-xs);\n}\n\n.banner-actions[hidden] {\n display: none;\n}\n\n.action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n color: var(--banner-color);\n transition: opacity 0.15s;\n}\n\n.action-btn[hidden] {\n display: none;\n}\n\n.action-btn:hover {\n opacity: 0.75;\n}\n\n.action-btn:focus-visible {\n outline: 2px solid var(--banner-color);\n outline-offset: 2px;\n border-radius: var(--bv-radius-sm);\n}\n\n/* \u2500\u2500 Close button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n margin: -6px -6px 0 0;\n background: none;\n border: none;\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n color: var(--banner-icon-color);\n transition: background 0.15s;\n padding: 0;\n}\n\n.close-btn[hidden] {\n display: none;\n}\n\n.close-btn:hover {\n background: color-mix(in srgb, var(--banner-border) 60%, var(--banner-bg));\n}\n\n.close-btn:focus-visible {\n outline: 2px solid var(--banner-color);\n outline-offset: 2px;\n}\n";
@@ -18,14 +129,6 @@ var VARIANT_ICONS = {
18
129
  warning: ICON_ALERT_TRIANGLE,
19
130
  success: ICON_CHECK_CIRCLE
20
131
  };
21
- var _sheet = null;
22
- function getSheet() {
23
- if (!_sheet) {
24
- _sheet = new CSSStyleSheet();
25
- _sheet.replaceSync(bv_alert_banner_default);
26
- }
27
- return _sheet;
28
- }
29
132
  var BvAlertBanner = class extends HTMLElement {
30
133
  static observedAttributes = [
31
134
  "variant",
@@ -45,10 +148,7 @@ var BvAlertBanner = class extends HTMLElement {
45
148
  _closeBtn = null;
46
149
  constructor() {
47
150
  super();
48
- const shadow = this.attachShadow({ mode: "open" });
49
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
50
- shadow.adoptedStyleSheets = [getSheet()];
51
- }
151
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_alert_banner_default);
52
152
  }
53
153
  get variant() {
54
154
  return this.getAttribute("variant") ?? "default";
@@ -102,11 +202,6 @@ var BvAlertBanner = class extends HTMLElement {
102
202
  if (this._rendered) return;
103
203
  this._rendered = true;
104
204
  const shadow = this.shadowRoot;
105
- if (!shadow.adoptedStyleSheets.length) {
106
- const s = document.createElement("style");
107
- s.textContent = bv_alert_banner_default;
108
- shadow.appendChild(s);
109
- }
110
205
  const banner = document.createElement("div");
111
206
  banner.className = "banner";
112
207
  banner.setAttribute("role", "alert");
@@ -233,14 +328,6 @@ var MONTH_NAMES = [
233
328
  var WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sat", "Su"];
234
329
  var CHEVRON_LEFT = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg>`;
235
330
  var CHEVRON_RIGHT = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg>`;
236
- var _sheet2 = null;
237
- function getSheet2() {
238
- if (!_sheet2) {
239
- _sheet2 = new CSSStyleSheet();
240
- _sheet2.replaceSync(bv_date_range_picker_default);
241
- }
242
- return _sheet2;
243
- }
244
331
  function dateToIso(d) {
245
332
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
246
333
  }
@@ -256,6 +343,9 @@ function parseDisplay(str) {
256
343
  const d = match[2].padStart(2, "0");
257
344
  return `${match[3]}-${m}-${d}`;
258
345
  }
346
+ function capitalize(s) {
347
+ return s.charAt(0).toUpperCase() + s.slice(1);
348
+ }
259
349
  function getPresetDates(key) {
260
350
  const now = /* @__PURE__ */ new Date();
261
351
  now.setHours(0, 0, 0, 0);
@@ -298,7 +388,31 @@ function getPresetDates(key) {
298
388
  }
299
389
  }
300
390
  var BvDateRangePicker = class extends HTMLElement {
301
- static observedAttributes = ["start", "end", "preset"];
391
+ static observedAttributes = [
392
+ "start",
393
+ "end",
394
+ "preset",
395
+ // BTN-10191 — opt-in validation config
396
+ "min",
397
+ "max",
398
+ "max-range-years",
399
+ "validate-order",
400
+ "validate-format",
401
+ "show-range-limit-banner",
402
+ // BTN-10191 — configurable error copy (all attributes; defaults apply otherwise)
403
+ "error-min",
404
+ "error-max",
405
+ "error-order",
406
+ "error-format",
407
+ "range-limit-message",
408
+ "range-limit-heading",
409
+ // BTN-10312 — per-error severity (default error = blocks Apply + red banner)
410
+ "error-min-severity",
411
+ "error-max-severity",
412
+ "error-format-severity",
413
+ "error-order-severity",
414
+ "range-limit-severity"
415
+ ];
302
416
  _rendered = false;
303
417
  _today;
304
418
  _start = "";
@@ -308,6 +422,12 @@ var BvDateRangePicker = class extends HTMLElement {
308
422
  _hover = "";
309
423
  _leftYear;
310
424
  _leftMonth;
425
+ // BTN-10191 — raw display strings retained when the user types an
426
+ // unparseable value AND `validate-format` is on. Lets V8 fire without
427
+ // corrupting `_start`/`_end` (which stay strictly ISO for consumers).
428
+ _startRawInvalid = "";
429
+ _endRawInvalid = "";
430
+ _validity = { valid: true, applyEnabled: false };
311
431
  _leftGrid = null;
312
432
  _rightGrid = null;
313
433
  _leftTitle = null;
@@ -315,9 +435,22 @@ var BvDateRangePicker = class extends HTMLElement {
315
435
  _startInput = null;
316
436
  _endInput = null;
317
437
  _applyBtn = null;
318
- _bannerEl = null;
438
+ /**
439
+ * Unified alert banner (BTN-10312). Shows AT MOST ONE error at a time. When
440
+ * multiple errors are simultaneously active, the banner reflects the
441
+ * most-recently-edited field (see `_lastEditedField`).
442
+ */
443
+ _alertBannerEl = null;
319
444
  _leftDayMap = /* @__PURE__ */ new Map();
320
445
  _rightDayMap = /* @__PURE__ */ new Map();
446
+ /**
447
+ * BTN-10312 — tracks which field the user most-recently interacted with.
448
+ * When both `startError` and `endError` are active, the banner shows the
449
+ * error corresponding to this field. Set to `null` when no field has been
450
+ * touched (initial render, or external attribute set); the resolver falls
451
+ * back to a stable precedence in that case.
452
+ */
453
+ _lastEditedField = null;
321
454
  constructor() {
322
455
  super();
323
456
  const now = /* @__PURE__ */ new Date();
@@ -325,10 +458,7 @@ var BvDateRangePicker = class extends HTMLElement {
325
458
  this._today = dateToIso(now);
326
459
  this._leftYear = now.getFullYear();
327
460
  this._leftMonth = now.getMonth();
328
- const shadow = this.attachShadow({ mode: "open" });
329
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
330
- shadow.adoptedStyleSheets = [getSheet2()];
331
- }
461
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_date_range_picker_default);
332
462
  }
333
463
  get start() {
334
464
  return this._start;
@@ -352,14 +482,48 @@ var BvDateRangePicker = class extends HTMLElement {
352
482
  this._render();
353
483
  }
354
484
  attributeChangedCallback(name, _old, val) {
355
- if (name === "start") this._start = val ?? "";
356
- if (name === "end") this._end = val ?? "";
485
+ if (name === "start") {
486
+ this._start = val ?? "";
487
+ this._startRawInvalid = "";
488
+ this._lastEditedField = null;
489
+ }
490
+ if (name === "end") {
491
+ this._end = val ?? "";
492
+ this._endRawInvalid = "";
493
+ this._lastEditedField = null;
494
+ }
357
495
  if (name === "preset") this._preset = val ?? "";
358
496
  if (!this._rendered) return;
497
+ if (name === "start" || name === "end" || name === "preset") {
498
+ this._syncCalendars();
499
+ this._syncInputs();
500
+ this._syncPresets();
501
+ }
502
+ this._syncValidity();
503
+ }
504
+ /**
505
+ * BTN-10312 — public reset of the picker's internal draft state to the
506
+ * currently committed `start` / `end` / `preset` attributes. Called by
507
+ * `bv-date-range-input` when the popup closes without Apply (Cancel,
508
+ * Escape, outside-click) so the next open shows the last-committed value
509
+ * cleanly, with no leftover error banners from the discarded edit.
510
+ *
511
+ * The external value is not modified — this only clears the draft.
512
+ */
513
+ resetDraft() {
514
+ this._start = this.getAttribute("start") ?? "";
515
+ this._end = this.getAttribute("end") ?? "";
516
+ this._preset = this.getAttribute("preset") ?? "";
517
+ this._startRawInvalid = "";
518
+ this._endRawInvalid = "";
519
+ this._picking = false;
520
+ this._hover = "";
521
+ this._lastEditedField = null;
522
+ if (!this._rendered) return;
359
523
  this._syncCalendars();
360
524
  this._syncInputs();
361
525
  this._syncPresets();
362
- this._syncApply();
526
+ this._syncValidity();
363
527
  }
364
528
  _render() {
365
529
  if (this._rendered) return;
@@ -377,11 +541,6 @@ var BvDateRangePicker = class extends HTMLElement {
377
541
  }
378
542
  }
379
543
  const shadow = this.shadowRoot;
380
- if (!shadow.adoptedStyleSheets.length) {
381
- const s = document.createElement("style");
382
- s.textContent = bv_date_range_picker_default;
383
- shadow.appendChild(s);
384
- }
385
544
  const picker = document.createElement("div");
386
545
  picker.className = "picker";
387
546
  const sidebar = document.createElement("div");
@@ -452,14 +611,8 @@ var BvDateRangePicker = class extends HTMLElement {
452
611
  const bannerWrap = document.createElement("div");
453
612
  bannerWrap.className = "banner-wrap";
454
613
  const banner = document.createElement("bv-alert-banner");
455
- banner.setAttribute("variant", "warning");
456
- banner.setAttribute("heading", "Date range exceeds 5 years");
457
- banner.setAttribute(
458
- "message",
459
- "Only CSV download is available for this date range. Live report generation is not supported."
460
- );
461
614
  banner.hidden = true;
462
- this._bannerEl = banner;
615
+ this._alertBannerEl = banner;
463
616
  bannerWrap.appendChild(banner);
464
617
  main.append(calsWrap, bannerWrap, footer);
465
618
  picker.append(sidebar, main);
@@ -467,8 +620,7 @@ var BvDateRangePicker = class extends HTMLElement {
467
620
  this._syncCalendars();
468
621
  this._syncInputs();
469
622
  this._syncPresets();
470
- this._syncApply();
471
- this._syncBanner();
623
+ this._syncValidity();
472
624
  }
473
625
  _buildCalendarShell(side) {
474
626
  const cal = document.createElement("div");
@@ -633,6 +785,7 @@ var BvDateRangePicker = class extends HTMLElement {
633
785
  this._preset = "custom";
634
786
  this._picking = true;
635
787
  this._hover = "";
788
+ this._lastEditedField = "start";
636
789
  } else {
637
790
  if (iso < this._start) {
638
791
  this._end = this._start;
@@ -643,12 +796,14 @@ var BvDateRangePicker = class extends HTMLElement {
643
796
  this._picking = false;
644
797
  this._hover = "";
645
798
  this._preset = "custom";
799
+ this._lastEditedField = "end";
646
800
  }
801
+ this._startRawInvalid = "";
802
+ this._endRawInvalid = "";
647
803
  this._syncCalendars();
648
804
  this._syncInputs();
649
805
  this._syncPresets();
650
- this._syncApply();
651
- this._syncBanner();
806
+ this._syncValidity();
652
807
  }
653
808
  _selectPreset(key) {
654
809
  if (key === "custom") {
@@ -664,6 +819,9 @@ var BvDateRangePicker = class extends HTMLElement {
664
819
  this._preset = key;
665
820
  this._picking = false;
666
821
  this._hover = "";
822
+ this._startRawInvalid = "";
823
+ this._endRawInvalid = "";
824
+ this._lastEditedField = null;
667
825
  if (start) {
668
826
  const parts = start.split("-");
669
827
  const y = parseInt(parts[0], 10);
@@ -676,21 +834,48 @@ var BvDateRangePicker = class extends HTMLElement {
676
834
  this._syncCalendars();
677
835
  this._syncInputs();
678
836
  this._syncPresets();
679
- this._syncApply();
680
- this._syncBanner();
837
+ this._syncValidity();
681
838
  }
682
839
  _onInputBlur(input, field) {
683
- const iso = parseDisplay(input.value);
840
+ this._lastEditedField = field;
841
+ const raw = input.value;
842
+ if (raw === "") {
843
+ if (field === "start") {
844
+ this._start = "";
845
+ this._startRawInvalid = "";
846
+ } else {
847
+ this._end = "";
848
+ this._endRawInvalid = "";
849
+ }
850
+ this._syncCalendars();
851
+ this._syncPresets();
852
+ this._syncValidity();
853
+ return;
854
+ }
855
+ const iso = parseDisplay(raw);
684
856
  if (iso) {
685
- if (field === "start") this._start = iso;
686
- else this._end = iso;
857
+ if (field === "start") {
858
+ this._start = iso;
859
+ this._startRawInvalid = "";
860
+ } else {
861
+ this._end = iso;
862
+ this._endRawInvalid = "";
863
+ }
687
864
  this._picking = false;
688
865
  this._hover = "";
689
866
  this._preset = "custom";
690
867
  this._syncCalendars();
691
868
  this._syncPresets();
692
- this._syncApply();
693
- this._syncBanner();
869
+ this._syncValidity();
870
+ } else if (this.hasAttribute("validate-format")) {
871
+ if (field === "start") {
872
+ this._start = "";
873
+ this._startRawInvalid = raw;
874
+ } else {
875
+ this._end = "";
876
+ this._endRawInvalid = raw;
877
+ }
878
+ this._syncValidity();
694
879
  } else {
695
880
  input.value = formatDisplay(field === "start" ? this._start : this._end);
696
881
  }
@@ -705,33 +890,211 @@ var BvDateRangePicker = class extends HTMLElement {
705
890
  btn.classList.toggle("active", btn.dataset["preset"] === this._preset);
706
891
  });
707
892
  }
708
- _syncApply() {
709
- if (this._applyBtn) {
710
- this._applyBtn.disabled = !this._start || !this._end;
893
+ // ── BTN-10191 — validation orchestrator ──────────────────────────────
894
+ _syncValidity() {
895
+ const cfg = this._readConfig();
896
+ const previous = this._validity;
897
+ this._validity = validateDateRange(
898
+ {
899
+ start: this._start || this._startRawInvalid,
900
+ end: this._end || this._endRawInvalid
901
+ },
902
+ cfg
903
+ );
904
+ this._paintInputInvalidState();
905
+ this._applyAlertBanner(cfg);
906
+ this._applyApplyState();
907
+ this._applyCalendarDisabledState();
908
+ if (previous.valid !== this._validity.valid || previous.applyEnabled !== this._validity.applyEnabled || previous.startError !== this._validity.startError || previous.endError !== this._validity.endError || previous.formError !== this._validity.formError) {
909
+ this.dispatchEvent(
910
+ new CustomEvent("bv-validity-change", {
911
+ bubbles: true,
912
+ composed: true,
913
+ detail: { ...this._validity }
914
+ })
915
+ );
916
+ }
917
+ }
918
+ _readConfig() {
919
+ const min = this.getAttribute("min") || void 0;
920
+ const max = this.getAttribute("max") || void 0;
921
+ const mryStr = this.getAttribute("max-range-years");
922
+ const maxRangeYears = mryStr !== null && mryStr !== "" ? Number(mryStr) : void 0;
923
+ return {
924
+ min,
925
+ max,
926
+ maxRangeYears: Number.isFinite(maxRangeYears) ? maxRangeYears : void 0,
927
+ validateOrder: this.hasAttribute("validate-order"),
928
+ validateFormat: this.hasAttribute("validate-format"),
929
+ minSeverity: normalizeSeverity(this.getAttribute("error-min-severity")),
930
+ maxSeverity: normalizeSeverity(this.getAttribute("error-max-severity")),
931
+ formatSeverity: normalizeSeverity(this.getAttribute("error-format-severity")),
932
+ orderSeverity: normalizeSeverity(this.getAttribute("error-order-severity")),
933
+ rangeLimitSeverity: normalizeSeverity(this.getAttribute("range-limit-severity"))
934
+ };
935
+ }
936
+ /**
937
+ * BTN-10312 — apply the `.invalid` + `aria-invalid` state to input fields
938
+ * whose field-level error is currently blocking (severity `'error'`).
939
+ * Non-blocking severities (warning/primary/success) do not paint the input
940
+ * red so the visual matches the semantic ("not a fatal error").
941
+ */
942
+ _paintInputInvalidState() {
943
+ const v = this._validity;
944
+ const cfg = this._readConfig();
945
+ this._paintOne(this._startInput, v.startError ? severityOfFieldError(v.startError, cfg) : null);
946
+ this._paintOne(this._endInput, v.endError ? severityOfFieldError(v.endError, cfg) : null);
947
+ }
948
+ _paintOne(input, sev) {
949
+ if (!input) return;
950
+ if (sev === "error") {
951
+ input.classList.add("invalid");
952
+ input.setAttribute("aria-invalid", "true");
953
+ } else {
954
+ input.classList.remove("invalid");
955
+ input.removeAttribute("aria-invalid");
711
956
  }
712
957
  }
713
- _syncBanner() {
714
- if (!this._bannerEl) return;
958
+ /**
959
+ * BTN-10312 — single alert banner. Picks the "active" error following the
960
+ * precedence:
961
+ *
962
+ * 1. If a form error is active, show it (form errors don't coexist with
963
+ * field errors by validator design).
964
+ * 2. If both field errors are active, show the one for
965
+ * `_lastEditedField` (falling back to `start` if `null`).
966
+ * 3. Else show whichever single field error is active.
967
+ * 4. Else hide the banner.
968
+ *
969
+ * `show-range-limit-banner="false"` still suppresses the range-limit
970
+ * banner specifically (existing consumer contract from BTN-10191).
971
+ */
972
+ _applyAlertBanner(cfg) {
973
+ if (!this._alertBannerEl) return;
715
974
  const pickerEl = this.shadowRoot.querySelector(".picker");
716
- if (!this._start) {
717
- this._bannerEl.hidden = true;
975
+ const active = this._resolveActiveError();
976
+ if (!active) {
977
+ this._alertBannerEl.hidden = true;
978
+ this._alertBannerEl.removeAttribute("heading");
979
+ this._alertBannerEl.removeAttribute("message");
718
980
  pickerEl == null ? void 0 : pickerEl.classList.remove("picker--with-banner");
719
981
  return;
720
982
  }
721
- const startDate = /* @__PURE__ */ new Date(this._start + "T00:00:00");
722
- const fiveYearsAgo = /* @__PURE__ */ new Date();
723
- fiveYearsAgo.setFullYear(fiveYearsAgo.getFullYear() - 5);
724
- const show = startDate < fiveYearsAgo;
725
- this._bannerEl.hidden = !show;
726
- pickerEl == null ? void 0 : pickerEl.classList.toggle("picker--with-banner", show);
983
+ if (active.kind === "form" && active.error === "range-limit" && this.getAttribute("show-range-limit-banner") === "false") {
984
+ this._alertBannerEl.hidden = true;
985
+ pickerEl == null ? void 0 : pickerEl.classList.remove("picker--with-banner");
986
+ return;
987
+ }
988
+ const variant = active.kind === "field" ? severityOfFieldError(active.error, cfg) : severityOfFormError(active.error, cfg);
989
+ this._alertBannerEl.setAttribute("variant", variant);
990
+ if (active.kind === "form" && active.error === "range-limit") {
991
+ const yearsAttr = this.getAttribute("max-range-years") ?? "";
992
+ const heading = this.getAttribute("range-limit-heading") || (yearsAttr ? `Date range exceeds ${yearsAttr} years` : "Date range exceeds the maximum");
993
+ const message = this.getAttribute("range-limit-message") || "Only CSV download is available for this date range. Live report generation is not supported.";
994
+ this._alertBannerEl.setAttribute("heading", heading.replace(/\{N\}/g, yearsAttr));
995
+ this._alertBannerEl.setAttribute("message", message.replace(/\{N\}/g, yearsAttr));
996
+ } else {
997
+ this._alertBannerEl.removeAttribute("heading");
998
+ const message = active.kind === "field" ? this._resolveFieldError(active.error, active.field) : this._resolveOrderError();
999
+ this._alertBannerEl.setAttribute("message", message);
1000
+ }
1001
+ this._alertBannerEl.hidden = false;
1002
+ pickerEl == null ? void 0 : pickerEl.classList.add("picker--with-banner");
1003
+ }
1004
+ /**
1005
+ * Pick which error the banner should reflect. See `_applyAlertBanner` for
1006
+ * the precedence rules.
1007
+ */
1008
+ _resolveActiveError() {
1009
+ const v = this._validity;
1010
+ if (v.formError) return { kind: "form", error: v.formError };
1011
+ if (v.startError && v.endError) {
1012
+ const winner = this._lastEditedField ?? "start";
1013
+ return {
1014
+ kind: "field",
1015
+ error: winner === "start" ? v.startError : v.endError,
1016
+ field: winner
1017
+ };
1018
+ }
1019
+ if (v.startError) return { kind: "field", error: v.startError, field: "start" };
1020
+ if (v.endError) return { kind: "field", error: v.endError, field: "end" };
1021
+ return null;
1022
+ }
1023
+ _resolveFieldError(err, field) {
1024
+ if (err === "format") {
1025
+ const custom2 = this.getAttribute("error-format");
1026
+ if (custom2) return this._interpolateField(custom2, field);
1027
+ return `Enter a valid ${field} date`;
1028
+ }
1029
+ if (err === "min") {
1030
+ const minIso = this.getAttribute("min") ?? "";
1031
+ const minDisplay = formatDisplay(minIso);
1032
+ const custom2 = this.getAttribute("error-min");
1033
+ if (custom2) return this._interpolateField(custom2, field).replace(/\{min\}/g, minDisplay);
1034
+ return `${capitalize(field)} date must be on or after ${minDisplay}`;
1035
+ }
1036
+ const maxAttr = this.getAttribute("max") ?? "";
1037
+ const resolvedMax = maxAttr === "today" ? isoToday() : maxAttr;
1038
+ const maxDisplay = formatDisplay(resolvedMax);
1039
+ const custom = this.getAttribute("error-max");
1040
+ if (custom) return this._interpolateField(custom, field).replace(/\{max\}/g, maxDisplay);
1041
+ if (maxAttr === "today") return `${capitalize(field)} date cannot be in the future`;
1042
+ return `${capitalize(field)} date cannot be after ${maxDisplay}`;
1043
+ }
1044
+ /**
1045
+ * BTN-10312 — interpolate `{field}` (always lowercase) and `{Field}` (always
1046
+ * capitalized) in a user-provided template. Before this change, the historical
1047
+ * behavior was that `{field}` in custom copy resolved lowercase while the
1048
+ * built-in defaults capitalized inline — inconsistent. Now the split is
1049
+ * explicit: `{field}` = lowercase, `{Field}` = capitalized.
1050
+ */
1051
+ _interpolateField(template, field) {
1052
+ return template.replace(/\{Field\}/g, capitalize(field)).replace(/\{field\}/g, field);
1053
+ }
1054
+ _resolveOrderError() {
1055
+ return this.getAttribute("error-order") || "Start date must be on or before end date";
1056
+ }
1057
+ _applyApplyState() {
1058
+ if (this._applyBtn) {
1059
+ this._applyBtn.disabled = !this._validity.applyEnabled;
1060
+ }
1061
+ }
1062
+ _applyCalendarDisabledState() {
1063
+ const minIso = this.getAttribute("min") || "";
1064
+ const maxAttr = this.getAttribute("max") || "";
1065
+ const maxIso = maxAttr === "today" ? isoToday() : maxAttr;
1066
+ if (!minIso && !maxIso) {
1067
+ this._leftDayMap.forEach((btn) => this._resetDayDisabled(btn));
1068
+ this._rightDayMap.forEach((btn) => this._resetDayDisabled(btn));
1069
+ return;
1070
+ }
1071
+ const paint = (btn, iso) => {
1072
+ const outOfRange = !!minIso && iso < minIso || !!maxIso && iso > maxIso;
1073
+ if (outOfRange) {
1074
+ btn.disabled = true;
1075
+ btn.setAttribute("aria-disabled", "true");
1076
+ btn.classList.add("day--disabled");
1077
+ } else {
1078
+ this._resetDayDisabled(btn);
1079
+ }
1080
+ };
1081
+ this._leftDayMap.forEach(paint);
1082
+ this._rightDayMap.forEach(paint);
1083
+ }
1084
+ _resetDayDisabled(btn) {
1085
+ btn.disabled = false;
1086
+ btn.removeAttribute("aria-disabled");
1087
+ btn.classList.remove("day--disabled");
727
1088
  }
728
1089
  // Auto-insert slashes after MM and DD digits while the user types forward.
729
- // Also syncs the banner live once the date is fully typed.
1090
+ // Also runs full validity sync on every keystroke — this is what clears the
1091
+ // error state immediately on correction (per BTN-10191 a11y requirement).
730
1092
  _setupDateMask(input, field) {
731
1093
  input.addEventListener("input", (e) => {
732
1094
  var _a;
733
1095
  const ev = e;
734
1096
  if ((_a = ev.inputType) == null ? void 0 : _a.startsWith("delete")) return;
1097
+ this._lastEditedField = field;
735
1098
  const digits = input.value.replace(/[^\d]/g, "");
736
1099
  let formatted;
737
1100
  if (digits.length <= 2) {
@@ -743,17 +1106,19 @@ var BvDateRangePicker = class extends HTMLElement {
743
1106
  formatted = digits.slice(0, 2) + "/" + digits.slice(2, 4) + "/" + digits.slice(4, 8);
744
1107
  }
745
1108
  input.value = formatted;
1109
+ if (field === "start") this._startRawInvalid = "";
1110
+ else this._endRawInvalid = "";
746
1111
  if (formatted.length === 10) {
747
1112
  const iso = parseDisplay(formatted);
748
1113
  if (iso) {
749
1114
  if (field === "start") this._start = iso;
750
1115
  else this._end = iso;
751
- this._syncBanner();
752
1116
  }
753
- } else if (field === "start" && formatted.length < 10) {
754
- this._start = "";
755
- this._syncBanner();
1117
+ } else {
1118
+ if (field === "start") this._start = "";
1119
+ else this._end = "";
756
1120
  }
1121
+ this._syncValidity();
757
1122
  });
758
1123
  }
759
1124
  _apply() {