@bluevolt-tech/lumen 2.0.0 → 2.1.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 +61 -0
  4. package/dist/components/_shared/dropdown-controller.d.ts +31 -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 -2
  16. package/dist/components/bv-course-grid-card/bv-course-grid-card.js +105 -55
  17. package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +2 -2
  18. package/dist/components/bv-course-list-card/bv-course-list-card.js +105 -55
  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 +4 -6
  21. package/dist/components/bv-date-range-input/bv-date-range-input.js +460 -122
  22. package/dist/components/bv-date-range-picker/bv-date-range-picker.d.ts +17 -3
  23. package/dist/components/bv-date-range-picker/bv-date-range-picker.js +339 -73
  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 +78 -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 +77 -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 +76 -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 +76 -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 +69 -19
  50. package/dist/components/bv-toggle/bv-toggle.js +19 -11
  51. package/dist/components/bv-tooltip/bv-tooltip.js +19 -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 +2 -1
  55. package/package.json +8 -1
  56. package/themes/base.css +2 -1
@@ -1,5 +1,96 @@
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 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\n/* \u2500\u2500 BTN-10191 \xB7 validation error states \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\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.error-messages {\n padding: var(--bv-spacing-2xs) 0 0 0;\n}\n\n.input-error,\n.form-error {\n color: var(--bv-color-danger);\n font-size: var(--bv-font-size-xs);\n line-height: 1.25;\n margin-top: var(--bv-spacing-2xs);\n}\n\n.form-error {\n font-size: var(--bv-font-size-sm);\n margin-top: var(--bv-spacing-xs);\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
+ function validateDateRange(input, config = {}) {
24
+ const { start, end } = input;
25
+ const { min, max, maxRangeYears, validateOrder, validateFormat } = config;
26
+ const resolvedMax = max === "today" ? isoToday() : max;
27
+ let startError;
28
+ let endError;
29
+ const startDate = start ? parseIso(start) : null;
30
+ const endDate = end ? parseIso(end) : null;
31
+ if (validateFormat) {
32
+ if (start && !startDate) startError = "format";
33
+ if (end && !endDate) endError = "format";
34
+ }
35
+ if (!startError && startDate) {
36
+ if (min && belowMin(startDate, min)) startError = "min";
37
+ else if (resolvedMax && aboveMax(startDate, resolvedMax)) startError = "max";
38
+ }
39
+ if (!endError && endDate) {
40
+ if (min && belowMin(endDate, min)) endError = "min";
41
+ else if (resolvedMax && aboveMax(endDate, resolvedMax)) endError = "max";
42
+ }
43
+ let formError;
44
+ const bothFieldsClean = startDate && endDate && !startError && !endError;
45
+ if (bothFieldsClean) {
46
+ if (validateOrder && startDate > endDate) {
47
+ formError = "order";
48
+ } else if (maxRangeYears !== void 0 && exceedsYearSpan(startDate, endDate, maxRangeYears)) {
49
+ formError = "range-limit";
50
+ }
51
+ }
52
+ const valid = !startError && !endError && !formError;
53
+ const applyEnabled = valid && !!startDate && !!endDate;
54
+ return {
55
+ valid,
56
+ applyEnabled,
57
+ ...startError && { startError },
58
+ ...endError && { endError },
59
+ ...formError && { formError }
60
+ };
61
+ }
62
+ function parseIso(s) {
63
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
64
+ if (!m) return null;
65
+ const [, yStr, mStr, dStr] = m;
66
+ const y = Number(yStr);
67
+ const mo = Number(mStr) - 1;
68
+ const d = Number(dStr);
69
+ const dt = new Date(y, mo, d);
70
+ if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
71
+ dt.setHours(0, 0, 0, 0);
72
+ return dt;
73
+ }
74
+ function isoToday() {
75
+ const now = /* @__PURE__ */ new Date();
76
+ const y = now.getFullYear();
77
+ const m = String(now.getMonth() + 1).padStart(2, "0");
78
+ const d = String(now.getDate()).padStart(2, "0");
79
+ return `${y}-${m}-${d}`;
80
+ }
81
+ function belowMin(date, minIso) {
82
+ const minDate = parseIso(minIso);
83
+ return !!minDate && date < minDate;
84
+ }
85
+ function aboveMax(date, maxIso) {
86
+ const maxDate = parseIso(maxIso);
87
+ return !!maxDate && date > maxDate;
88
+ }
89
+ function exceedsYearSpan(start, end, years) {
90
+ const cutoff = new Date(start);
91
+ cutoff.setFullYear(cutoff.getFullYear() + years);
92
+ return end > cutoff;
93
+ }
3
94
 
4
95
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-alert-banner/bv-alert-banner.css
5
96
  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 +109,6 @@ var VARIANT_ICONS = {
18
109
  warning: ICON_ALERT_TRIANGLE,
19
110
  success: ICON_CHECK_CIRCLE
20
111
  };
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
112
  var BvAlertBanner = class extends HTMLElement {
30
113
  static observedAttributes = [
31
114
  "variant",
@@ -45,10 +128,7 @@ var BvAlertBanner = class extends HTMLElement {
45
128
  _closeBtn = null;
46
129
  constructor() {
47
130
  super();
48
- const shadow = this.attachShadow({ mode: "open" });
49
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
50
- shadow.adoptedStyleSheets = [getSheet()];
51
- }
131
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_alert_banner_default);
52
132
  }
53
133
  get variant() {
54
134
  return this.getAttribute("variant") ?? "default";
@@ -102,11 +182,6 @@ var BvAlertBanner = class extends HTMLElement {
102
182
  if (this._rendered) return;
103
183
  this._rendered = true;
104
184
  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
185
  const banner = document.createElement("div");
111
186
  banner.className = "banner";
112
187
  banner.setAttribute("role", "alert");
@@ -233,14 +308,6 @@ var MONTH_NAMES = [
233
308
  var WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sat", "Su"];
234
309
  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
310
  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
311
  function dateToIso(d) {
245
312
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
246
313
  }
@@ -256,6 +323,9 @@ function parseDisplay(str) {
256
323
  const d = match[2].padStart(2, "0");
257
324
  return `${match[3]}-${m}-${d}`;
258
325
  }
326
+ function capitalize(s) {
327
+ return s.charAt(0).toUpperCase() + s.slice(1);
328
+ }
259
329
  function getPresetDates(key) {
260
330
  const now = /* @__PURE__ */ new Date();
261
331
  now.setHours(0, 0, 0, 0);
@@ -298,7 +368,25 @@ function getPresetDates(key) {
298
368
  }
299
369
  }
300
370
  var BvDateRangePicker = class extends HTMLElement {
301
- static observedAttributes = ["start", "end", "preset"];
371
+ static observedAttributes = [
372
+ "start",
373
+ "end",
374
+ "preset",
375
+ // BTN-10191 — opt-in validation config
376
+ "min",
377
+ "max",
378
+ "max-range-years",
379
+ "validate-order",
380
+ "validate-format",
381
+ "show-range-limit-banner",
382
+ // BTN-10191 — configurable error copy (all attributes; defaults apply otherwise)
383
+ "error-min",
384
+ "error-max",
385
+ "error-order",
386
+ "error-format",
387
+ "range-limit-message",
388
+ "range-limit-heading"
389
+ ];
302
390
  _rendered = false;
303
391
  _today;
304
392
  _start = "";
@@ -308,6 +396,12 @@ var BvDateRangePicker = class extends HTMLElement {
308
396
  _hover = "";
309
397
  _leftYear;
310
398
  _leftMonth;
399
+ // BTN-10191 — raw display strings retained when the user types an
400
+ // unparseable value AND `validate-format` is on. Lets V8 fire without
401
+ // corrupting `_start`/`_end` (which stay strictly ISO for consumers).
402
+ _startRawInvalid = "";
403
+ _endRawInvalid = "";
404
+ _validity = { valid: true, applyEnabled: false };
311
405
  _leftGrid = null;
312
406
  _rightGrid = null;
313
407
  _leftTitle = null;
@@ -316,6 +410,9 @@ var BvDateRangePicker = class extends HTMLElement {
316
410
  _endInput = null;
317
411
  _applyBtn = null;
318
412
  _bannerEl = null;
413
+ _startErrorEl = null;
414
+ _endErrorEl = null;
415
+ _formErrorEl = null;
319
416
  _leftDayMap = /* @__PURE__ */ new Map();
320
417
  _rightDayMap = /* @__PURE__ */ new Map();
321
418
  constructor() {
@@ -325,10 +422,7 @@ var BvDateRangePicker = class extends HTMLElement {
325
422
  this._today = dateToIso(now);
326
423
  this._leftYear = now.getFullYear();
327
424
  this._leftMonth = now.getMonth();
328
- const shadow = this.attachShadow({ mode: "open" });
329
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
330
- shadow.adoptedStyleSheets = [getSheet2()];
331
- }
425
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_date_range_picker_default);
332
426
  }
333
427
  get start() {
334
428
  return this._start;
@@ -352,14 +446,22 @@ var BvDateRangePicker = class extends HTMLElement {
352
446
  this._render();
353
447
  }
354
448
  attributeChangedCallback(name, _old, val) {
355
- if (name === "start") this._start = val ?? "";
356
- if (name === "end") this._end = val ?? "";
449
+ if (name === "start") {
450
+ this._start = val ?? "";
451
+ this._startRawInvalid = "";
452
+ }
453
+ if (name === "end") {
454
+ this._end = val ?? "";
455
+ this._endRawInvalid = "";
456
+ }
357
457
  if (name === "preset") this._preset = val ?? "";
358
458
  if (!this._rendered) return;
359
- this._syncCalendars();
360
- this._syncInputs();
361
- this._syncPresets();
362
- this._syncApply();
459
+ if (name === "start" || name === "end" || name === "preset") {
460
+ this._syncCalendars();
461
+ this._syncInputs();
462
+ this._syncPresets();
463
+ }
464
+ this._syncValidity();
363
465
  }
364
466
  _render() {
365
467
  if (this._rendered) return;
@@ -377,11 +479,6 @@ var BvDateRangePicker = class extends HTMLElement {
377
479
  }
378
480
  }
379
481
  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
482
  const picker = document.createElement("div");
386
483
  picker.className = "picker";
387
484
  const sidebar = document.createElement("div");
@@ -449,15 +546,29 @@ var BvDateRangePicker = class extends HTMLElement {
449
546
  this._applyBtn = applyBtn;
450
547
  actions.append(cancelBtn, applyBtn);
451
548
  footer.append(inputsWrap, actions);
549
+ const errorsWrap = document.createElement("div");
550
+ errorsWrap.className = "error-messages";
551
+ const startErrorEl = document.createElement("div");
552
+ startErrorEl.className = "input-error input-error--start";
553
+ startErrorEl.setAttribute("role", "alert");
554
+ startErrorEl.hidden = true;
555
+ this._startErrorEl = startErrorEl;
556
+ const endErrorEl = document.createElement("div");
557
+ endErrorEl.className = "input-error input-error--end";
558
+ endErrorEl.setAttribute("role", "alert");
559
+ endErrorEl.hidden = true;
560
+ this._endErrorEl = endErrorEl;
561
+ const formErrorEl = document.createElement("div");
562
+ formErrorEl.className = "form-error";
563
+ formErrorEl.setAttribute("role", "alert");
564
+ formErrorEl.hidden = true;
565
+ this._formErrorEl = formErrorEl;
566
+ errorsWrap.append(startErrorEl, endErrorEl, formErrorEl);
567
+ footer.insertBefore(errorsWrap, actions);
452
568
  const bannerWrap = document.createElement("div");
453
569
  bannerWrap.className = "banner-wrap";
454
570
  const banner = document.createElement("bv-alert-banner");
455
571
  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
572
  banner.hidden = true;
462
573
  this._bannerEl = banner;
463
574
  bannerWrap.appendChild(banner);
@@ -467,8 +578,7 @@ var BvDateRangePicker = class extends HTMLElement {
467
578
  this._syncCalendars();
468
579
  this._syncInputs();
469
580
  this._syncPresets();
470
- this._syncApply();
471
- this._syncBanner();
581
+ this._syncValidity();
472
582
  }
473
583
  _buildCalendarShell(side) {
474
584
  const cal = document.createElement("div");
@@ -644,11 +754,12 @@ var BvDateRangePicker = class extends HTMLElement {
644
754
  this._hover = "";
645
755
  this._preset = "custom";
646
756
  }
757
+ this._startRawInvalid = "";
758
+ this._endRawInvalid = "";
647
759
  this._syncCalendars();
648
760
  this._syncInputs();
649
761
  this._syncPresets();
650
- this._syncApply();
651
- this._syncBanner();
762
+ this._syncValidity();
652
763
  }
653
764
  _selectPreset(key) {
654
765
  if (key === "custom") {
@@ -664,6 +775,8 @@ var BvDateRangePicker = class extends HTMLElement {
664
775
  this._preset = key;
665
776
  this._picking = false;
666
777
  this._hover = "";
778
+ this._startRawInvalid = "";
779
+ this._endRawInvalid = "";
667
780
  if (start) {
668
781
  const parts = start.split("-");
669
782
  const y = parseInt(parts[0], 10);
@@ -676,21 +789,47 @@ var BvDateRangePicker = class extends HTMLElement {
676
789
  this._syncCalendars();
677
790
  this._syncInputs();
678
791
  this._syncPresets();
679
- this._syncApply();
680
- this._syncBanner();
792
+ this._syncValidity();
681
793
  }
682
794
  _onInputBlur(input, field) {
683
- const iso = parseDisplay(input.value);
795
+ const raw = input.value;
796
+ if (raw === "") {
797
+ if (field === "start") {
798
+ this._start = "";
799
+ this._startRawInvalid = "";
800
+ } else {
801
+ this._end = "";
802
+ this._endRawInvalid = "";
803
+ }
804
+ this._syncCalendars();
805
+ this._syncPresets();
806
+ this._syncValidity();
807
+ return;
808
+ }
809
+ const iso = parseDisplay(raw);
684
810
  if (iso) {
685
- if (field === "start") this._start = iso;
686
- else this._end = iso;
811
+ if (field === "start") {
812
+ this._start = iso;
813
+ this._startRawInvalid = "";
814
+ } else {
815
+ this._end = iso;
816
+ this._endRawInvalid = "";
817
+ }
687
818
  this._picking = false;
688
819
  this._hover = "";
689
820
  this._preset = "custom";
690
821
  this._syncCalendars();
691
822
  this._syncPresets();
692
- this._syncApply();
693
- this._syncBanner();
823
+ this._syncValidity();
824
+ } else if (this.hasAttribute("validate-format")) {
825
+ if (field === "start") {
826
+ this._start = "";
827
+ this._startRawInvalid = raw;
828
+ } else {
829
+ this._end = "";
830
+ this._endRawInvalid = raw;
831
+ }
832
+ this._syncValidity();
694
833
  } else {
695
834
  input.value = formatDisplay(field === "start" ? this._start : this._end);
696
835
  }
@@ -705,28 +844,153 @@ var BvDateRangePicker = class extends HTMLElement {
705
844
  btn.classList.toggle("active", btn.dataset["preset"] === this._preset);
706
845
  });
707
846
  }
708
- _syncApply() {
847
+ // ── BTN-10191 — validation orchestrator ──────────────────────────────
848
+ _syncValidity() {
849
+ const cfg = this._readConfig();
850
+ const previous = this._validity;
851
+ this._validity = validateDateRange(
852
+ {
853
+ start: this._start || this._startRawInvalid,
854
+ end: this._end || this._endRawInvalid
855
+ },
856
+ cfg
857
+ );
858
+ this._applyErrorStates();
859
+ this._applyApplyState();
860
+ this._applyBannerState();
861
+ this._applyCalendarDisabledState();
862
+ 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) {
863
+ this.dispatchEvent(
864
+ new CustomEvent("bv-validity-change", {
865
+ bubbles: true,
866
+ composed: true,
867
+ detail: { ...this._validity }
868
+ })
869
+ );
870
+ }
871
+ }
872
+ _readConfig() {
873
+ const min = this.getAttribute("min") || void 0;
874
+ const max = this.getAttribute("max") || void 0;
875
+ const mryStr = this.getAttribute("max-range-years");
876
+ const maxRangeYears = mryStr !== null && mryStr !== "" ? Number(mryStr) : void 0;
877
+ return {
878
+ min,
879
+ max,
880
+ maxRangeYears: Number.isFinite(maxRangeYears) ? maxRangeYears : void 0,
881
+ validateOrder: this.hasAttribute("validate-order"),
882
+ validateFormat: this.hasAttribute("validate-format")
883
+ };
884
+ }
885
+ _applyErrorStates() {
886
+ const v = this._validity;
887
+ this._paintInputError(this._startInput, this._startErrorEl, v.startError, "start");
888
+ this._paintInputError(this._endInput, this._endErrorEl, v.endError, "end");
889
+ if (this._formErrorEl) {
890
+ if (v.formError === "order") {
891
+ this._formErrorEl.textContent = this._resolveOrderError();
892
+ this._formErrorEl.hidden = false;
893
+ } else {
894
+ this._formErrorEl.textContent = "";
895
+ this._formErrorEl.hidden = true;
896
+ }
897
+ }
898
+ }
899
+ _paintInputError(input, errorEl, err, field) {
900
+ if (input) {
901
+ if (err) {
902
+ input.classList.add("invalid");
903
+ input.setAttribute("aria-invalid", "true");
904
+ } else {
905
+ input.classList.remove("invalid");
906
+ input.removeAttribute("aria-invalid");
907
+ }
908
+ }
909
+ if (errorEl) {
910
+ if (err) {
911
+ errorEl.textContent = this._resolveFieldError(err, field);
912
+ errorEl.hidden = false;
913
+ } else {
914
+ errorEl.textContent = "";
915
+ errorEl.hidden = true;
916
+ }
917
+ }
918
+ }
919
+ _resolveFieldError(err, field) {
920
+ if (err === "format") {
921
+ return this.getAttribute("error-format") ?? "Enter a valid date in MM/DD/YYYY format";
922
+ }
923
+ if (err === "min") {
924
+ const minIso = this.getAttribute("min") ?? "";
925
+ const minDisplay = formatDisplay(minIso);
926
+ const custom2 = this.getAttribute("error-min");
927
+ if (custom2) return custom2.replace("{min}", minDisplay).replace("{field}", field);
928
+ return `${capitalize(field)} date must be on or after ${minDisplay}`;
929
+ }
930
+ const maxAttr = this.getAttribute("max") ?? "";
931
+ const resolvedMax = maxAttr === "today" ? isoToday() : maxAttr;
932
+ const maxDisplay = formatDisplay(resolvedMax);
933
+ const custom = this.getAttribute("error-max");
934
+ if (custom) return custom.replace("{max}", maxDisplay).replace("{field}", field);
935
+ if (maxAttr === "today") return `${capitalize(field)} date cannot be in the future`;
936
+ return `${capitalize(field)} date cannot be after ${maxDisplay}`;
937
+ }
938
+ _resolveOrderError() {
939
+ return this.getAttribute("error-order") ?? "Start date must be on or before end date";
940
+ }
941
+ _applyApplyState() {
709
942
  if (this._applyBtn) {
710
- this._applyBtn.disabled = !this._start || !this._end;
943
+ this._applyBtn.disabled = !this._validity.applyEnabled;
711
944
  }
712
945
  }
713
- _syncBanner() {
946
+ _applyBannerState() {
714
947
  if (!this._bannerEl) return;
715
948
  const pickerEl = this.shadowRoot.querySelector(".picker");
716
- if (!this._start) {
949
+ const bannerAllowed = this.getAttribute("show-range-limit-banner") !== "false";
950
+ const show = this._validity.formError === "range-limit" && bannerAllowed;
951
+ if (show) {
952
+ const yearsAttr = this.getAttribute("max-range-years") ?? "";
953
+ const heading = this.getAttribute("range-limit-heading") ?? (yearsAttr ? `Date range exceeds ${yearsAttr} years` : "Date range exceeds the maximum");
954
+ const message = this.getAttribute("range-limit-message") ?? "Only CSV download is available for this date range. Live report generation is not supported.";
955
+ this._bannerEl.setAttribute("heading", heading.replace("{N}", yearsAttr));
956
+ this._bannerEl.setAttribute("message", message.replace("{N}", yearsAttr));
957
+ this._bannerEl.hidden = false;
958
+ pickerEl == null ? void 0 : pickerEl.classList.add("picker--with-banner");
959
+ } else {
717
960
  this._bannerEl.hidden = true;
718
961
  pickerEl == null ? void 0 : pickerEl.classList.remove("picker--with-banner");
962
+ }
963
+ }
964
+ _applyCalendarDisabledState() {
965
+ const minIso = this.getAttribute("min") || "";
966
+ const maxAttr = this.getAttribute("max") || "";
967
+ const maxIso = maxAttr === "today" ? isoToday() : maxAttr;
968
+ if (!minIso && !maxIso) {
969
+ this._leftDayMap.forEach((btn) => this._resetDayDisabled(btn));
970
+ this._rightDayMap.forEach((btn) => this._resetDayDisabled(btn));
719
971
  return;
720
972
  }
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);
973
+ const paint = (btn, iso) => {
974
+ const outOfRange = !!minIso && iso < minIso || !!maxIso && iso > maxIso;
975
+ if (outOfRange) {
976
+ btn.disabled = true;
977
+ btn.setAttribute("aria-disabled", "true");
978
+ btn.classList.add("day--disabled");
979
+ } else {
980
+ this._resetDayDisabled(btn);
981
+ }
982
+ };
983
+ this._leftDayMap.forEach(paint);
984
+ this._rightDayMap.forEach(paint);
985
+ }
986
+ _resetDayDisabled(btn) {
987
+ btn.disabled = false;
988
+ btn.removeAttribute("aria-disabled");
989
+ btn.classList.remove("day--disabled");
727
990
  }
728
991
  // 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.
992
+ // Also runs full validity sync on every keystroke — this is what clears the
993
+ // error state immediately on correction (per BTN-10191 a11y requirement).
730
994
  _setupDateMask(input, field) {
731
995
  input.addEventListener("input", (e) => {
732
996
  var _a;
@@ -743,17 +1007,19 @@ var BvDateRangePicker = class extends HTMLElement {
743
1007
  formatted = digits.slice(0, 2) + "/" + digits.slice(2, 4) + "/" + digits.slice(4, 8);
744
1008
  }
745
1009
  input.value = formatted;
1010
+ if (field === "start") this._startRawInvalid = "";
1011
+ else this._endRawInvalid = "";
746
1012
  if (formatted.length === 10) {
747
1013
  const iso = parseDisplay(formatted);
748
1014
  if (iso) {
749
1015
  if (field === "start") this._start = iso;
750
1016
  else this._end = iso;
751
- this._syncBanner();
752
1017
  }
753
- } else if (field === "start" && formatted.length < 10) {
754
- this._start = "";
755
- this._syncBanner();
1018
+ } else {
1019
+ if (field === "start") this._start = "";
1020
+ else this._end = "";
756
1021
  }
1022
+ this._syncValidity();
757
1023
  });
758
1024
  }
759
1025
  _apply() {