@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.
- package/README.md +37 -23
- package/dist/components/_shared/adopt-styles.d.ts +8 -0
- package/dist/components/_shared/date-range-validator.d.ts +61 -0
- package/dist/components/_shared/dropdown-controller.d.ts +31 -0
- package/dist/components/_shared/selectable-card.d.ts +10 -0
- package/dist/components/bv-alert-banner/bv-alert-banner.js +19 -17
- package/dist/components/bv-avatar/bv-avatar.js +19 -11
- package/dist/components/bv-badge/bv-badge.js +19 -11
- package/dist/components/bv-button/bv-button.js +18 -16
- package/dist/components/bv-category-pill/bv-category-pill.js +18 -16
- package/dist/components/bv-checkbox/bv-checkbox.js +19 -11
- package/dist/components/bv-chip/bv-chip.js +19 -11
- package/dist/components/bv-course-detail/bv-course-detail.js +20 -34
- package/dist/components/bv-course-detail-modal/bv-course-detail-modal.js +24 -90
- package/dist/components/bv-course-grid-card/bv-course-grid-card.d.ts +2 -2
- package/dist/components/bv-course-grid-card/bv-course-grid-card.js +105 -55
- package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +2 -2
- package/dist/components/bv-course-list-card/bv-course-list-card.js +105 -55
- package/dist/components/bv-course-outline/bv-course-outline.js +19 -11
- package/dist/components/bv-date-range-input/bv-date-range-input.d.ts +4 -6
- package/dist/components/bv-date-range-input/bv-date-range-input.js +460 -122
- package/dist/components/bv-date-range-picker/bv-date-range-picker.d.ts +17 -3
- package/dist/components/bv-date-range-picker/bv-date-range-picker.js +339 -73
- package/dist/components/bv-empty-state/bv-empty-state.js +20 -28
- package/dist/components/bv-filter-dropdown/bv-filter-dropdown.d.ts +1 -4
- package/dist/components/bv-filter-dropdown/bv-filter-dropdown.js +78 -53
- package/dist/components/bv-filter-panel/bv-filter-panel.js +20 -22
- package/dist/components/bv-icon/bv-icon.js +19 -11
- package/dist/components/bv-input/bv-input.js +18 -16
- package/dist/components/bv-input-dropdown/bv-input-dropdown.d.ts +1 -4
- package/dist/components/bv-input-dropdown/bv-input-dropdown.js +77 -78
- package/dist/components/bv-modal/bv-modal.js +19 -11
- package/dist/components/bv-page-title/bv-page-title.js +19 -6
- package/dist/components/bv-pagination/bv-pagination.d.ts +1 -0
- package/dist/components/bv-pagination/bv-pagination.js +23 -186
- package/dist/components/bv-portal-header/bv-portal-header.js +25 -83
- package/dist/components/bv-portal-layout/bv-portal-layout.js +19 -11
- package/dist/components/bv-progress-bar/bv-progress-bar.js +19 -11
- package/dist/components/bv-promo-banner/bv-promo-banner.js +19 -11
- package/dist/components/bv-radio/bv-radio.js +19 -11
- package/dist/components/bv-searchbox/bv-searchbox.js +19 -11
- package/dist/components/bv-segmented-control/bv-segmented-control.js +18 -16
- package/dist/components/bv-select/bv-select.d.ts +1 -4
- package/dist/components/bv-select/bv-select.js +76 -44
- package/dist/components/bv-split-button/bv-split-button.d.ts +1 -4
- package/dist/components/bv-split-button/bv-split-button.js +76 -42
- package/dist/components/bv-star-rating/bv-star-rating.js +19 -11
- package/dist/components/bv-table/bv-table.d.ts +5 -0
- package/dist/components/bv-table/bv-table.js +69 -19
- package/dist/components/bv-toggle/bv-toggle.js +19 -11
- package/dist/components/bv-tooltip/bv-tooltip.js +19 -17
- package/dist/components/bv-user-row/bv-user-row.js +22 -50
- package/dist/components/bv-view-switcher/bv-view-switcher.js +19 -11
- package/dist/themes/base.css +2 -1
- package/package.json +8 -1
- package/themes/base.css +2 -1
|
@@ -1,8 +1,148 @@
|
|
|
1
1
|
// css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-date-range-input/bv-date-range-input.css
|
|
2
|
-
var bv_date_range_input_default = ":host {\n display: block;\n position: relative;\n font-family: var(--bv-font-family-base);\n}\n\n/* \u2500\u2500 Label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.field-label {\n display: block;\n font-size: var(--bv-font-size-sm);\n line-height: 20px;\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n margin-bottom: 6px;\n}\n\n.required-mark {\n color: var(--bv-color-danger);\n margin-left: 2px;\n}\n\n/* \u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n height: 44px;\n box-sizing: border-box;\n padding: 0 var(--bv-spacing-md);\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-800);\n text-align: left;\n transition:\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n:host([open]) .trigger {\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n.trigger-display {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.trigger-display.is-placeholder {\n color: var(--bv-color-neutral-400);\n}\n\n.trigger-icon {\n display: flex;\n flex-shrink: 0;\n margin-left: var(--bv-spacing-sm);\n color: var(--bv-color-neutral-400);\n transition: color var(--bv-transition-fast);\n}\n\n:host([open]) .trigger-icon {\n color: var(--bv-color-primary);\n}\n\n/* \u2500\u2500 Picker popup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-popup {\n display: none;\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: 300;\n}\n\n:host([open]) .picker-popup {\n display: block;\n}\n";
|
|
2
|
+
var bv_date_range_input_default = ":host {\n display: block;\n position: relative;\n font-family: var(--bv-font-family-base);\n}\n\n/* \u2500\u2500 Label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.field-label {\n display: block;\n font-size: var(--bv-font-size-sm);\n line-height: 20px;\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n margin-bottom: 6px;\n}\n\n.required-mark {\n color: var(--bv-color-danger);\n margin-left: 2px;\n}\n\n/* \u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n height: 44px;\n box-sizing: border-box;\n padding: 0 var(--bv-spacing-md);\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-800);\n text-align: left;\n transition:\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n:host([open]) .trigger {\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n/* BTN-10191 \u2014 reflected from the internal picker's validity state */\n.trigger.invalid,\n.trigger.invalid:focus-visible,\n:host([open]) .trigger.invalid {\n border-color: var(--bv-color-danger);\n box-shadow: 0 0 0 3px var(--bv-color-danger-light);\n}\n\n.trigger-display {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.trigger-display.is-placeholder {\n color: var(--bv-color-neutral-400);\n}\n\n.trigger-icon {\n display: flex;\n flex-shrink: 0;\n margin-left: var(--bv-spacing-sm);\n color: var(--bv-color-neutral-400);\n transition: color var(--bv-transition-fast);\n}\n\n:host([open]) .trigger-icon {\n color: var(--bv-color-primary);\n}\n\n/* \u2500\u2500 Picker popup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-popup {\n display: none;\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: 300;\n}\n\n:host([open]) .picker-popup {\n display: block;\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/dropdown-controller.ts
|
|
23
|
+
function bindDropdownController(host, options = {}) {
|
|
24
|
+
const shadow = host.shadowRoot;
|
|
25
|
+
if (!shadow) {
|
|
26
|
+
throw new Error("bindDropdownController requires the host to have an open shadow root.");
|
|
27
|
+
}
|
|
28
|
+
const syncExpanded = () => {
|
|
29
|
+
var _a;
|
|
30
|
+
(_a = options.trigger) == null ? void 0 : _a.setAttribute("aria-expanded", host.hasAttribute("open") ? "true" : "false");
|
|
31
|
+
};
|
|
32
|
+
const open = () => {
|
|
33
|
+
var _a;
|
|
34
|
+
if (host.hasAttribute("open")) return;
|
|
35
|
+
(_a = options.onBeforeOpen) == null ? void 0 : _a.call(options);
|
|
36
|
+
host.setAttribute("open", "");
|
|
37
|
+
syncExpanded();
|
|
38
|
+
host.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
|
|
39
|
+
};
|
|
40
|
+
const close = () => {
|
|
41
|
+
if (!host.hasAttribute("open")) return;
|
|
42
|
+
host.removeAttribute("open");
|
|
43
|
+
syncExpanded();
|
|
44
|
+
};
|
|
45
|
+
const toggle = () => {
|
|
46
|
+
if (host.hasAttribute("open")) close();
|
|
47
|
+
else open();
|
|
48
|
+
};
|
|
49
|
+
const offClick = (e) => {
|
|
50
|
+
if (!host.hasAttribute("open")) return;
|
|
51
|
+
const target = e.target;
|
|
52
|
+
if (host.contains(target) || shadow.contains(target)) return;
|
|
53
|
+
close();
|
|
54
|
+
};
|
|
55
|
+
document.addEventListener("click", offClick);
|
|
56
|
+
const onKeyDown = (e) => {
|
|
57
|
+
if (e.key === "Escape") close();
|
|
58
|
+
};
|
|
59
|
+
host.addEventListener("keydown", onKeyDown);
|
|
60
|
+
const observer = new MutationObserver(syncExpanded);
|
|
61
|
+
observer.observe(host, { attributes: true, attributeFilter: ["open"] });
|
|
62
|
+
const cleanup = () => {
|
|
63
|
+
observer.disconnect();
|
|
64
|
+
document.removeEventListener("click", offClick);
|
|
65
|
+
host.removeEventListener("keydown", onKeyDown);
|
|
66
|
+
};
|
|
67
|
+
syncExpanded();
|
|
68
|
+
return { open, close, toggle, cleanup };
|
|
69
|
+
}
|
|
3
70
|
|
|
4
71
|
// css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-date-range-picker/bv-date-range-picker.css
|
|
5
|
-
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";
|
|
72
|
+
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";
|
|
73
|
+
|
|
74
|
+
// components/_shared/date-range-validator.ts
|
|
75
|
+
function validateDateRange(input, config = {}) {
|
|
76
|
+
const { start, end } = input;
|
|
77
|
+
const { min, max, maxRangeYears, validateOrder, validateFormat } = config;
|
|
78
|
+
const resolvedMax = max === "today" ? isoToday() : max;
|
|
79
|
+
let startError;
|
|
80
|
+
let endError;
|
|
81
|
+
const startDate = start ? parseIso(start) : null;
|
|
82
|
+
const endDate = end ? parseIso(end) : null;
|
|
83
|
+
if (validateFormat) {
|
|
84
|
+
if (start && !startDate) startError = "format";
|
|
85
|
+
if (end && !endDate) endError = "format";
|
|
86
|
+
}
|
|
87
|
+
if (!startError && startDate) {
|
|
88
|
+
if (min && belowMin(startDate, min)) startError = "min";
|
|
89
|
+
else if (resolvedMax && aboveMax(startDate, resolvedMax)) startError = "max";
|
|
90
|
+
}
|
|
91
|
+
if (!endError && endDate) {
|
|
92
|
+
if (min && belowMin(endDate, min)) endError = "min";
|
|
93
|
+
else if (resolvedMax && aboveMax(endDate, resolvedMax)) endError = "max";
|
|
94
|
+
}
|
|
95
|
+
let formError;
|
|
96
|
+
const bothFieldsClean = startDate && endDate && !startError && !endError;
|
|
97
|
+
if (bothFieldsClean) {
|
|
98
|
+
if (validateOrder && startDate > endDate) {
|
|
99
|
+
formError = "order";
|
|
100
|
+
} else if (maxRangeYears !== void 0 && exceedsYearSpan(startDate, endDate, maxRangeYears)) {
|
|
101
|
+
formError = "range-limit";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const valid = !startError && !endError && !formError;
|
|
105
|
+
const applyEnabled = valid && !!startDate && !!endDate;
|
|
106
|
+
return {
|
|
107
|
+
valid,
|
|
108
|
+
applyEnabled,
|
|
109
|
+
...startError && { startError },
|
|
110
|
+
...endError && { endError },
|
|
111
|
+
...formError && { formError }
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseIso(s) {
|
|
115
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
116
|
+
if (!m) return null;
|
|
117
|
+
const [, yStr, mStr, dStr] = m;
|
|
118
|
+
const y = Number(yStr);
|
|
119
|
+
const mo = Number(mStr) - 1;
|
|
120
|
+
const d = Number(dStr);
|
|
121
|
+
const dt = new Date(y, mo, d);
|
|
122
|
+
if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
|
|
123
|
+
dt.setHours(0, 0, 0, 0);
|
|
124
|
+
return dt;
|
|
125
|
+
}
|
|
126
|
+
function isoToday() {
|
|
127
|
+
const now = /* @__PURE__ */ new Date();
|
|
128
|
+
const y = now.getFullYear();
|
|
129
|
+
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
130
|
+
const d = String(now.getDate()).padStart(2, "0");
|
|
131
|
+
return `${y}-${m}-${d}`;
|
|
132
|
+
}
|
|
133
|
+
function belowMin(date, minIso) {
|
|
134
|
+
const minDate = parseIso(minIso);
|
|
135
|
+
return !!minDate && date < minDate;
|
|
136
|
+
}
|
|
137
|
+
function aboveMax(date, maxIso) {
|
|
138
|
+
const maxDate = parseIso(maxIso);
|
|
139
|
+
return !!maxDate && date > maxDate;
|
|
140
|
+
}
|
|
141
|
+
function exceedsYearSpan(start, end, years) {
|
|
142
|
+
const cutoff = new Date(start);
|
|
143
|
+
cutoff.setFullYear(cutoff.getFullYear() + years);
|
|
144
|
+
return end > cutoff;
|
|
145
|
+
}
|
|
6
146
|
|
|
7
147
|
// css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-alert-banner/bv-alert-banner.css
|
|
8
148
|
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";
|
|
@@ -21,14 +161,6 @@ var VARIANT_ICONS = {
|
|
|
21
161
|
warning: ICON_ALERT_TRIANGLE,
|
|
22
162
|
success: ICON_CHECK_CIRCLE
|
|
23
163
|
};
|
|
24
|
-
var _sheet = null;
|
|
25
|
-
function getSheet() {
|
|
26
|
-
if (!_sheet) {
|
|
27
|
-
_sheet = new CSSStyleSheet();
|
|
28
|
-
_sheet.replaceSync(bv_alert_banner_default);
|
|
29
|
-
}
|
|
30
|
-
return _sheet;
|
|
31
|
-
}
|
|
32
164
|
var BvAlertBanner = class extends HTMLElement {
|
|
33
165
|
static observedAttributes = [
|
|
34
166
|
"variant",
|
|
@@ -48,10 +180,7 @@ var BvAlertBanner = class extends HTMLElement {
|
|
|
48
180
|
_closeBtn = null;
|
|
49
181
|
constructor() {
|
|
50
182
|
super();
|
|
51
|
-
|
|
52
|
-
if ("adoptedStyleSheets" in ShadowRoot.prototype) {
|
|
53
|
-
shadow.adoptedStyleSheets = [getSheet()];
|
|
54
|
-
}
|
|
183
|
+
adoptStyles(this.attachShadow({ mode: "open" }), bv_alert_banner_default);
|
|
55
184
|
}
|
|
56
185
|
get variant() {
|
|
57
186
|
return this.getAttribute("variant") ?? "default";
|
|
@@ -105,11 +234,6 @@ var BvAlertBanner = class extends HTMLElement {
|
|
|
105
234
|
if (this._rendered) return;
|
|
106
235
|
this._rendered = true;
|
|
107
236
|
const shadow = this.shadowRoot;
|
|
108
|
-
if (!shadow.adoptedStyleSheets.length) {
|
|
109
|
-
const s = document.createElement("style");
|
|
110
|
-
s.textContent = bv_alert_banner_default;
|
|
111
|
-
shadow.appendChild(s);
|
|
112
|
-
}
|
|
113
237
|
const banner = document.createElement("div");
|
|
114
238
|
banner.className = "banner";
|
|
115
239
|
banner.setAttribute("role", "alert");
|
|
@@ -236,14 +360,6 @@ var MONTH_NAMES = [
|
|
|
236
360
|
var WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sat", "Su"];
|
|
237
361
|
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>`;
|
|
238
362
|
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>`;
|
|
239
|
-
var _sheet2 = null;
|
|
240
|
-
function getSheet2() {
|
|
241
|
-
if (!_sheet2) {
|
|
242
|
-
_sheet2 = new CSSStyleSheet();
|
|
243
|
-
_sheet2.replaceSync(bv_date_range_picker_default);
|
|
244
|
-
}
|
|
245
|
-
return _sheet2;
|
|
246
|
-
}
|
|
247
363
|
function dateToIso(d) {
|
|
248
364
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
249
365
|
}
|
|
@@ -259,6 +375,9 @@ function parseDisplay(str) {
|
|
|
259
375
|
const d = match[2].padStart(2, "0");
|
|
260
376
|
return `${match[3]}-${m}-${d}`;
|
|
261
377
|
}
|
|
378
|
+
function capitalize(s) {
|
|
379
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
380
|
+
}
|
|
262
381
|
function getPresetDates(key) {
|
|
263
382
|
const now = /* @__PURE__ */ new Date();
|
|
264
383
|
now.setHours(0, 0, 0, 0);
|
|
@@ -301,7 +420,25 @@ function getPresetDates(key) {
|
|
|
301
420
|
}
|
|
302
421
|
}
|
|
303
422
|
var BvDateRangePicker = class extends HTMLElement {
|
|
304
|
-
static observedAttributes = [
|
|
423
|
+
static observedAttributes = [
|
|
424
|
+
"start",
|
|
425
|
+
"end",
|
|
426
|
+
"preset",
|
|
427
|
+
// BTN-10191 — opt-in validation config
|
|
428
|
+
"min",
|
|
429
|
+
"max",
|
|
430
|
+
"max-range-years",
|
|
431
|
+
"validate-order",
|
|
432
|
+
"validate-format",
|
|
433
|
+
"show-range-limit-banner",
|
|
434
|
+
// BTN-10191 — configurable error copy (all attributes; defaults apply otherwise)
|
|
435
|
+
"error-min",
|
|
436
|
+
"error-max",
|
|
437
|
+
"error-order",
|
|
438
|
+
"error-format",
|
|
439
|
+
"range-limit-message",
|
|
440
|
+
"range-limit-heading"
|
|
441
|
+
];
|
|
305
442
|
_rendered = false;
|
|
306
443
|
_today;
|
|
307
444
|
_start = "";
|
|
@@ -311,6 +448,12 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
311
448
|
_hover = "";
|
|
312
449
|
_leftYear;
|
|
313
450
|
_leftMonth;
|
|
451
|
+
// BTN-10191 — raw display strings retained when the user types an
|
|
452
|
+
// unparseable value AND `validate-format` is on. Lets V8 fire without
|
|
453
|
+
// corrupting `_start`/`_end` (which stay strictly ISO for consumers).
|
|
454
|
+
_startRawInvalid = "";
|
|
455
|
+
_endRawInvalid = "";
|
|
456
|
+
_validity = { valid: true, applyEnabled: false };
|
|
314
457
|
_leftGrid = null;
|
|
315
458
|
_rightGrid = null;
|
|
316
459
|
_leftTitle = null;
|
|
@@ -319,6 +462,9 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
319
462
|
_endInput = null;
|
|
320
463
|
_applyBtn = null;
|
|
321
464
|
_bannerEl = null;
|
|
465
|
+
_startErrorEl = null;
|
|
466
|
+
_endErrorEl = null;
|
|
467
|
+
_formErrorEl = null;
|
|
322
468
|
_leftDayMap = /* @__PURE__ */ new Map();
|
|
323
469
|
_rightDayMap = /* @__PURE__ */ new Map();
|
|
324
470
|
constructor() {
|
|
@@ -328,10 +474,7 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
328
474
|
this._today = dateToIso(now);
|
|
329
475
|
this._leftYear = now.getFullYear();
|
|
330
476
|
this._leftMonth = now.getMonth();
|
|
331
|
-
|
|
332
|
-
if ("adoptedStyleSheets" in ShadowRoot.prototype) {
|
|
333
|
-
shadow.adoptedStyleSheets = [getSheet2()];
|
|
334
|
-
}
|
|
477
|
+
adoptStyles(this.attachShadow({ mode: "open" }), bv_date_range_picker_default);
|
|
335
478
|
}
|
|
336
479
|
get start() {
|
|
337
480
|
return this._start;
|
|
@@ -355,14 +498,22 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
355
498
|
this._render();
|
|
356
499
|
}
|
|
357
500
|
attributeChangedCallback(name, _old, val) {
|
|
358
|
-
if (name === "start")
|
|
359
|
-
|
|
501
|
+
if (name === "start") {
|
|
502
|
+
this._start = val ?? "";
|
|
503
|
+
this._startRawInvalid = "";
|
|
504
|
+
}
|
|
505
|
+
if (name === "end") {
|
|
506
|
+
this._end = val ?? "";
|
|
507
|
+
this._endRawInvalid = "";
|
|
508
|
+
}
|
|
360
509
|
if (name === "preset") this._preset = val ?? "";
|
|
361
510
|
if (!this._rendered) return;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
511
|
+
if (name === "start" || name === "end" || name === "preset") {
|
|
512
|
+
this._syncCalendars();
|
|
513
|
+
this._syncInputs();
|
|
514
|
+
this._syncPresets();
|
|
515
|
+
}
|
|
516
|
+
this._syncValidity();
|
|
366
517
|
}
|
|
367
518
|
_render() {
|
|
368
519
|
if (this._rendered) return;
|
|
@@ -380,11 +531,6 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
380
531
|
}
|
|
381
532
|
}
|
|
382
533
|
const shadow = this.shadowRoot;
|
|
383
|
-
if (!shadow.adoptedStyleSheets.length) {
|
|
384
|
-
const s = document.createElement("style");
|
|
385
|
-
s.textContent = bv_date_range_picker_default;
|
|
386
|
-
shadow.appendChild(s);
|
|
387
|
-
}
|
|
388
534
|
const picker = document.createElement("div");
|
|
389
535
|
picker.className = "picker";
|
|
390
536
|
const sidebar = document.createElement("div");
|
|
@@ -452,15 +598,29 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
452
598
|
this._applyBtn = applyBtn;
|
|
453
599
|
actions.append(cancelBtn, applyBtn);
|
|
454
600
|
footer.append(inputsWrap, actions);
|
|
601
|
+
const errorsWrap = document.createElement("div");
|
|
602
|
+
errorsWrap.className = "error-messages";
|
|
603
|
+
const startErrorEl = document.createElement("div");
|
|
604
|
+
startErrorEl.className = "input-error input-error--start";
|
|
605
|
+
startErrorEl.setAttribute("role", "alert");
|
|
606
|
+
startErrorEl.hidden = true;
|
|
607
|
+
this._startErrorEl = startErrorEl;
|
|
608
|
+
const endErrorEl = document.createElement("div");
|
|
609
|
+
endErrorEl.className = "input-error input-error--end";
|
|
610
|
+
endErrorEl.setAttribute("role", "alert");
|
|
611
|
+
endErrorEl.hidden = true;
|
|
612
|
+
this._endErrorEl = endErrorEl;
|
|
613
|
+
const formErrorEl = document.createElement("div");
|
|
614
|
+
formErrorEl.className = "form-error";
|
|
615
|
+
formErrorEl.setAttribute("role", "alert");
|
|
616
|
+
formErrorEl.hidden = true;
|
|
617
|
+
this._formErrorEl = formErrorEl;
|
|
618
|
+
errorsWrap.append(startErrorEl, endErrorEl, formErrorEl);
|
|
619
|
+
footer.insertBefore(errorsWrap, actions);
|
|
455
620
|
const bannerWrap = document.createElement("div");
|
|
456
621
|
bannerWrap.className = "banner-wrap";
|
|
457
622
|
const banner = document.createElement("bv-alert-banner");
|
|
458
623
|
banner.setAttribute("variant", "warning");
|
|
459
|
-
banner.setAttribute("heading", "Date range exceeds 5 years");
|
|
460
|
-
banner.setAttribute(
|
|
461
|
-
"message",
|
|
462
|
-
"Only CSV download is available for this date range. Live report generation is not supported."
|
|
463
|
-
);
|
|
464
624
|
banner.hidden = true;
|
|
465
625
|
this._bannerEl = banner;
|
|
466
626
|
bannerWrap.appendChild(banner);
|
|
@@ -470,8 +630,7 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
470
630
|
this._syncCalendars();
|
|
471
631
|
this._syncInputs();
|
|
472
632
|
this._syncPresets();
|
|
473
|
-
this.
|
|
474
|
-
this._syncBanner();
|
|
633
|
+
this._syncValidity();
|
|
475
634
|
}
|
|
476
635
|
_buildCalendarShell(side) {
|
|
477
636
|
const cal = document.createElement("div");
|
|
@@ -647,11 +806,12 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
647
806
|
this._hover = "";
|
|
648
807
|
this._preset = "custom";
|
|
649
808
|
}
|
|
809
|
+
this._startRawInvalid = "";
|
|
810
|
+
this._endRawInvalid = "";
|
|
650
811
|
this._syncCalendars();
|
|
651
812
|
this._syncInputs();
|
|
652
813
|
this._syncPresets();
|
|
653
|
-
this.
|
|
654
|
-
this._syncBanner();
|
|
814
|
+
this._syncValidity();
|
|
655
815
|
}
|
|
656
816
|
_selectPreset(key) {
|
|
657
817
|
if (key === "custom") {
|
|
@@ -667,6 +827,8 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
667
827
|
this._preset = key;
|
|
668
828
|
this._picking = false;
|
|
669
829
|
this._hover = "";
|
|
830
|
+
this._startRawInvalid = "";
|
|
831
|
+
this._endRawInvalid = "";
|
|
670
832
|
if (start) {
|
|
671
833
|
const parts = start.split("-");
|
|
672
834
|
const y = parseInt(parts[0], 10);
|
|
@@ -679,21 +841,47 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
679
841
|
this._syncCalendars();
|
|
680
842
|
this._syncInputs();
|
|
681
843
|
this._syncPresets();
|
|
682
|
-
this.
|
|
683
|
-
this._syncBanner();
|
|
844
|
+
this._syncValidity();
|
|
684
845
|
}
|
|
685
846
|
_onInputBlur(input, field) {
|
|
686
|
-
const
|
|
847
|
+
const raw = input.value;
|
|
848
|
+
if (raw === "") {
|
|
849
|
+
if (field === "start") {
|
|
850
|
+
this._start = "";
|
|
851
|
+
this._startRawInvalid = "";
|
|
852
|
+
} else {
|
|
853
|
+
this._end = "";
|
|
854
|
+
this._endRawInvalid = "";
|
|
855
|
+
}
|
|
856
|
+
this._syncCalendars();
|
|
857
|
+
this._syncPresets();
|
|
858
|
+
this._syncValidity();
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const iso = parseDisplay(raw);
|
|
687
862
|
if (iso) {
|
|
688
|
-
if (field === "start")
|
|
689
|
-
|
|
863
|
+
if (field === "start") {
|
|
864
|
+
this._start = iso;
|
|
865
|
+
this._startRawInvalid = "";
|
|
866
|
+
} else {
|
|
867
|
+
this._end = iso;
|
|
868
|
+
this._endRawInvalid = "";
|
|
869
|
+
}
|
|
690
870
|
this._picking = false;
|
|
691
871
|
this._hover = "";
|
|
692
872
|
this._preset = "custom";
|
|
693
873
|
this._syncCalendars();
|
|
694
874
|
this._syncPresets();
|
|
695
|
-
this.
|
|
696
|
-
|
|
875
|
+
this._syncValidity();
|
|
876
|
+
} else if (this.hasAttribute("validate-format")) {
|
|
877
|
+
if (field === "start") {
|
|
878
|
+
this._start = "";
|
|
879
|
+
this._startRawInvalid = raw;
|
|
880
|
+
} else {
|
|
881
|
+
this._end = "";
|
|
882
|
+
this._endRawInvalid = raw;
|
|
883
|
+
}
|
|
884
|
+
this._syncValidity();
|
|
697
885
|
} else {
|
|
698
886
|
input.value = formatDisplay(field === "start" ? this._start : this._end);
|
|
699
887
|
}
|
|
@@ -708,28 +896,153 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
708
896
|
btn.classList.toggle("active", btn.dataset["preset"] === this._preset);
|
|
709
897
|
});
|
|
710
898
|
}
|
|
711
|
-
|
|
899
|
+
// ── BTN-10191 — validation orchestrator ──────────────────────────────
|
|
900
|
+
_syncValidity() {
|
|
901
|
+
const cfg = this._readConfig();
|
|
902
|
+
const previous = this._validity;
|
|
903
|
+
this._validity = validateDateRange(
|
|
904
|
+
{
|
|
905
|
+
start: this._start || this._startRawInvalid,
|
|
906
|
+
end: this._end || this._endRawInvalid
|
|
907
|
+
},
|
|
908
|
+
cfg
|
|
909
|
+
);
|
|
910
|
+
this._applyErrorStates();
|
|
911
|
+
this._applyApplyState();
|
|
912
|
+
this._applyBannerState();
|
|
913
|
+
this._applyCalendarDisabledState();
|
|
914
|
+
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) {
|
|
915
|
+
this.dispatchEvent(
|
|
916
|
+
new CustomEvent("bv-validity-change", {
|
|
917
|
+
bubbles: true,
|
|
918
|
+
composed: true,
|
|
919
|
+
detail: { ...this._validity }
|
|
920
|
+
})
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
_readConfig() {
|
|
925
|
+
const min = this.getAttribute("min") || void 0;
|
|
926
|
+
const max = this.getAttribute("max") || void 0;
|
|
927
|
+
const mryStr = this.getAttribute("max-range-years");
|
|
928
|
+
const maxRangeYears = mryStr !== null && mryStr !== "" ? Number(mryStr) : void 0;
|
|
929
|
+
return {
|
|
930
|
+
min,
|
|
931
|
+
max,
|
|
932
|
+
maxRangeYears: Number.isFinite(maxRangeYears) ? maxRangeYears : void 0,
|
|
933
|
+
validateOrder: this.hasAttribute("validate-order"),
|
|
934
|
+
validateFormat: this.hasAttribute("validate-format")
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
_applyErrorStates() {
|
|
938
|
+
const v = this._validity;
|
|
939
|
+
this._paintInputError(this._startInput, this._startErrorEl, v.startError, "start");
|
|
940
|
+
this._paintInputError(this._endInput, this._endErrorEl, v.endError, "end");
|
|
941
|
+
if (this._formErrorEl) {
|
|
942
|
+
if (v.formError === "order") {
|
|
943
|
+
this._formErrorEl.textContent = this._resolveOrderError();
|
|
944
|
+
this._formErrorEl.hidden = false;
|
|
945
|
+
} else {
|
|
946
|
+
this._formErrorEl.textContent = "";
|
|
947
|
+
this._formErrorEl.hidden = true;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
_paintInputError(input, errorEl, err, field) {
|
|
952
|
+
if (input) {
|
|
953
|
+
if (err) {
|
|
954
|
+
input.classList.add("invalid");
|
|
955
|
+
input.setAttribute("aria-invalid", "true");
|
|
956
|
+
} else {
|
|
957
|
+
input.classList.remove("invalid");
|
|
958
|
+
input.removeAttribute("aria-invalid");
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (errorEl) {
|
|
962
|
+
if (err) {
|
|
963
|
+
errorEl.textContent = this._resolveFieldError(err, field);
|
|
964
|
+
errorEl.hidden = false;
|
|
965
|
+
} else {
|
|
966
|
+
errorEl.textContent = "";
|
|
967
|
+
errorEl.hidden = true;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
_resolveFieldError(err, field) {
|
|
972
|
+
if (err === "format") {
|
|
973
|
+
return this.getAttribute("error-format") ?? "Enter a valid date in MM/DD/YYYY format";
|
|
974
|
+
}
|
|
975
|
+
if (err === "min") {
|
|
976
|
+
const minIso = this.getAttribute("min") ?? "";
|
|
977
|
+
const minDisplay = formatDisplay(minIso);
|
|
978
|
+
const custom2 = this.getAttribute("error-min");
|
|
979
|
+
if (custom2) return custom2.replace("{min}", minDisplay).replace("{field}", field);
|
|
980
|
+
return `${capitalize(field)} date must be on or after ${minDisplay}`;
|
|
981
|
+
}
|
|
982
|
+
const maxAttr = this.getAttribute("max") ?? "";
|
|
983
|
+
const resolvedMax = maxAttr === "today" ? isoToday() : maxAttr;
|
|
984
|
+
const maxDisplay = formatDisplay(resolvedMax);
|
|
985
|
+
const custom = this.getAttribute("error-max");
|
|
986
|
+
if (custom) return custom.replace("{max}", maxDisplay).replace("{field}", field);
|
|
987
|
+
if (maxAttr === "today") return `${capitalize(field)} date cannot be in the future`;
|
|
988
|
+
return `${capitalize(field)} date cannot be after ${maxDisplay}`;
|
|
989
|
+
}
|
|
990
|
+
_resolveOrderError() {
|
|
991
|
+
return this.getAttribute("error-order") ?? "Start date must be on or before end date";
|
|
992
|
+
}
|
|
993
|
+
_applyApplyState() {
|
|
712
994
|
if (this._applyBtn) {
|
|
713
|
-
this._applyBtn.disabled = !this.
|
|
995
|
+
this._applyBtn.disabled = !this._validity.applyEnabled;
|
|
714
996
|
}
|
|
715
997
|
}
|
|
716
|
-
|
|
998
|
+
_applyBannerState() {
|
|
717
999
|
if (!this._bannerEl) return;
|
|
718
1000
|
const pickerEl = this.shadowRoot.querySelector(".picker");
|
|
719
|
-
|
|
1001
|
+
const bannerAllowed = this.getAttribute("show-range-limit-banner") !== "false";
|
|
1002
|
+
const show = this._validity.formError === "range-limit" && bannerAllowed;
|
|
1003
|
+
if (show) {
|
|
1004
|
+
const yearsAttr = this.getAttribute("max-range-years") ?? "";
|
|
1005
|
+
const heading = this.getAttribute("range-limit-heading") ?? (yearsAttr ? `Date range exceeds ${yearsAttr} years` : "Date range exceeds the maximum");
|
|
1006
|
+
const message = this.getAttribute("range-limit-message") ?? "Only CSV download is available for this date range. Live report generation is not supported.";
|
|
1007
|
+
this._bannerEl.setAttribute("heading", heading.replace("{N}", yearsAttr));
|
|
1008
|
+
this._bannerEl.setAttribute("message", message.replace("{N}", yearsAttr));
|
|
1009
|
+
this._bannerEl.hidden = false;
|
|
1010
|
+
pickerEl == null ? void 0 : pickerEl.classList.add("picker--with-banner");
|
|
1011
|
+
} else {
|
|
720
1012
|
this._bannerEl.hidden = true;
|
|
721
1013
|
pickerEl == null ? void 0 : pickerEl.classList.remove("picker--with-banner");
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
_applyCalendarDisabledState() {
|
|
1017
|
+
const minIso = this.getAttribute("min") || "";
|
|
1018
|
+
const maxAttr = this.getAttribute("max") || "";
|
|
1019
|
+
const maxIso = maxAttr === "today" ? isoToday() : maxAttr;
|
|
1020
|
+
if (!minIso && !maxIso) {
|
|
1021
|
+
this._leftDayMap.forEach((btn) => this._resetDayDisabled(btn));
|
|
1022
|
+
this._rightDayMap.forEach((btn) => this._resetDayDisabled(btn));
|
|
722
1023
|
return;
|
|
723
1024
|
}
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
1025
|
+
const paint = (btn, iso) => {
|
|
1026
|
+
const outOfRange = !!minIso && iso < minIso || !!maxIso && iso > maxIso;
|
|
1027
|
+
if (outOfRange) {
|
|
1028
|
+
btn.disabled = true;
|
|
1029
|
+
btn.setAttribute("aria-disabled", "true");
|
|
1030
|
+
btn.classList.add("day--disabled");
|
|
1031
|
+
} else {
|
|
1032
|
+
this._resetDayDisabled(btn);
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
this._leftDayMap.forEach(paint);
|
|
1036
|
+
this._rightDayMap.forEach(paint);
|
|
1037
|
+
}
|
|
1038
|
+
_resetDayDisabled(btn) {
|
|
1039
|
+
btn.disabled = false;
|
|
1040
|
+
btn.removeAttribute("aria-disabled");
|
|
1041
|
+
btn.classList.remove("day--disabled");
|
|
730
1042
|
}
|
|
731
1043
|
// Auto-insert slashes after MM and DD digits while the user types forward.
|
|
732
|
-
// Also
|
|
1044
|
+
// Also runs full validity sync on every keystroke — this is what clears the
|
|
1045
|
+
// error state immediately on correction (per BTN-10191 a11y requirement).
|
|
733
1046
|
_setupDateMask(input, field) {
|
|
734
1047
|
input.addEventListener("input", (e) => {
|
|
735
1048
|
var _a;
|
|
@@ -746,17 +1059,19 @@ var BvDateRangePicker = class extends HTMLElement {
|
|
|
746
1059
|
formatted = digits.slice(0, 2) + "/" + digits.slice(2, 4) + "/" + digits.slice(4, 8);
|
|
747
1060
|
}
|
|
748
1061
|
input.value = formatted;
|
|
1062
|
+
if (field === "start") this._startRawInvalid = "";
|
|
1063
|
+
else this._endRawInvalid = "";
|
|
749
1064
|
if (formatted.length === 10) {
|
|
750
1065
|
const iso = parseDisplay(formatted);
|
|
751
1066
|
if (iso) {
|
|
752
1067
|
if (field === "start") this._start = iso;
|
|
753
1068
|
else this._end = iso;
|
|
754
|
-
this._syncBanner();
|
|
755
1069
|
}
|
|
756
|
-
} else
|
|
757
|
-
this._start = "";
|
|
758
|
-
this.
|
|
1070
|
+
} else {
|
|
1071
|
+
if (field === "start") this._start = "";
|
|
1072
|
+
else this._end = "";
|
|
759
1073
|
}
|
|
1074
|
+
this._syncValidity();
|
|
760
1075
|
});
|
|
761
1076
|
}
|
|
762
1077
|
_apply() {
|
|
@@ -779,14 +1094,20 @@ if (!customElements.get("bv-date-range-picker")) {
|
|
|
779
1094
|
|
|
780
1095
|
// components/bv-date-range-input/bv-date-range-input.ts
|
|
781
1096
|
var CALENDAR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>`;
|
|
782
|
-
var
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
1097
|
+
var PICKER_PASSTHROUGH_ATTRS = [
|
|
1098
|
+
"min",
|
|
1099
|
+
"max",
|
|
1100
|
+
"max-range-years",
|
|
1101
|
+
"validate-order",
|
|
1102
|
+
"validate-format",
|
|
1103
|
+
"show-range-limit-banner",
|
|
1104
|
+
"error-min",
|
|
1105
|
+
"error-max",
|
|
1106
|
+
"error-order",
|
|
1107
|
+
"error-format",
|
|
1108
|
+
"range-limit-message",
|
|
1109
|
+
"range-limit-heading"
|
|
1110
|
+
];
|
|
790
1111
|
function isoToDisplay(iso) {
|
|
791
1112
|
if (!iso) return "";
|
|
792
1113
|
const [y, m, d] = iso.split("-");
|
|
@@ -798,19 +1119,18 @@ var BvDateRangeInput = class extends HTMLElement {
|
|
|
798
1119
|
"start",
|
|
799
1120
|
"end",
|
|
800
1121
|
"placeholder",
|
|
801
|
-
"required"
|
|
1122
|
+
"required",
|
|
1123
|
+
...PICKER_PASSTHROUGH_ATTRS
|
|
802
1124
|
];
|
|
803
1125
|
_rendered = false;
|
|
804
1126
|
_labelEl = null;
|
|
805
1127
|
_displayEl = null;
|
|
1128
|
+
_triggerEl = null;
|
|
806
1129
|
_pickerEl = null;
|
|
807
|
-
|
|
1130
|
+
_dropdown = null;
|
|
808
1131
|
constructor() {
|
|
809
1132
|
super();
|
|
810
|
-
|
|
811
|
-
if ("adoptedStyleSheets" in ShadowRoot.prototype) {
|
|
812
|
-
shadow.adoptedStyleSheets = [getSheet3()];
|
|
813
|
-
}
|
|
1133
|
+
adoptStyles(this.attachShadow({ mode: "open" }), bv_date_range_input_default);
|
|
814
1134
|
}
|
|
815
1135
|
get start() {
|
|
816
1136
|
return this.getAttribute("start") ?? "";
|
|
@@ -828,23 +1148,29 @@ var BvDateRangeInput = class extends HTMLElement {
|
|
|
828
1148
|
this._render();
|
|
829
1149
|
}
|
|
830
1150
|
disconnectedCallback() {
|
|
831
|
-
|
|
1151
|
+
var _a;
|
|
1152
|
+
(_a = this._dropdown) == null ? void 0 : _a.cleanup();
|
|
832
1153
|
}
|
|
833
|
-
attributeChangedCallback(name, _old,
|
|
1154
|
+
attributeChangedCallback(name, _old, val) {
|
|
834
1155
|
if (!this._rendered) return;
|
|
835
1156
|
if (name === "label" || name === "required") this._syncLabel();
|
|
836
|
-
if (name === "start" || name === "end") this._syncDisplay();
|
|
837
1157
|
if (name === "placeholder") this._syncDisplay();
|
|
1158
|
+
if (name === "start" || name === "end") {
|
|
1159
|
+
this._syncDisplay();
|
|
1160
|
+
if (this._pickerEl) {
|
|
1161
|
+
if (val === null) this._pickerEl.removeAttribute(name);
|
|
1162
|
+
else this._pickerEl.setAttribute(name, val);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (PICKER_PASSTHROUGH_ATTRS.includes(name) && this._pickerEl) {
|
|
1166
|
+
if (val === null) this._pickerEl.removeAttribute(name);
|
|
1167
|
+
else this._pickerEl.setAttribute(name, val);
|
|
1168
|
+
}
|
|
838
1169
|
}
|
|
839
1170
|
_render() {
|
|
840
1171
|
if (this._rendered) return;
|
|
841
1172
|
this._rendered = true;
|
|
842
1173
|
const shadow = this.shadowRoot;
|
|
843
|
-
if (!shadow.adoptedStyleSheets.length) {
|
|
844
|
-
const s = document.createElement("style");
|
|
845
|
-
s.textContent = bv_date_range_input_default;
|
|
846
|
-
shadow.appendChild(s);
|
|
847
|
-
}
|
|
848
1174
|
const label = document.createElement("label");
|
|
849
1175
|
label.className = "field-label";
|
|
850
1176
|
this._labelEl = label;
|
|
@@ -854,6 +1180,7 @@ var BvDateRangeInput = class extends HTMLElement {
|
|
|
854
1180
|
trigger.setAttribute("aria-haspopup", "dialog");
|
|
855
1181
|
trigger.setAttribute("aria-expanded", "false");
|
|
856
1182
|
trigger.setAttribute("part", "trigger");
|
|
1183
|
+
this._triggerEl = trigger;
|
|
857
1184
|
const display = document.createElement("span");
|
|
858
1185
|
display.className = "trigger-display";
|
|
859
1186
|
this._displayEl = display;
|
|
@@ -862,20 +1189,26 @@ var BvDateRangeInput = class extends HTMLElement {
|
|
|
862
1189
|
icon.innerHTML = CALENDAR_ICON;
|
|
863
1190
|
trigger.append(display, icon);
|
|
864
1191
|
trigger.addEventListener("click", (e) => {
|
|
1192
|
+
var _a;
|
|
865
1193
|
e.stopPropagation();
|
|
866
|
-
this.
|
|
1194
|
+
(_a = this._dropdown) == null ? void 0 : _a.toggle();
|
|
867
1195
|
});
|
|
868
1196
|
const popup = document.createElement("div");
|
|
869
1197
|
popup.className = "picker-popup";
|
|
870
1198
|
const picker = document.createElement("bv-date-range-picker");
|
|
871
1199
|
picker.setAttribute("start", this.getAttribute("start") ?? "");
|
|
872
1200
|
picker.setAttribute("end", this.getAttribute("end") ?? "");
|
|
1201
|
+
for (const attr of PICKER_PASSTHROUGH_ATTRS) {
|
|
1202
|
+
const val = this.getAttribute(attr);
|
|
1203
|
+
if (val !== null) picker.setAttribute(attr, val);
|
|
1204
|
+
}
|
|
873
1205
|
this._pickerEl = picker;
|
|
874
1206
|
picker.addEventListener("bv-change", (e) => {
|
|
1207
|
+
var _a;
|
|
875
1208
|
const detail = e.detail;
|
|
876
1209
|
this.setAttribute("start", detail.start);
|
|
877
1210
|
this.setAttribute("end", detail.end);
|
|
878
|
-
this.
|
|
1211
|
+
(_a = this._dropdown) == null ? void 0 : _a.close();
|
|
879
1212
|
this.dispatchEvent(
|
|
880
1213
|
new CustomEvent("bv-change", {
|
|
881
1214
|
bubbles: true,
|
|
@@ -885,36 +1218,41 @@ var BvDateRangeInput = class extends HTMLElement {
|
|
|
885
1218
|
);
|
|
886
1219
|
});
|
|
887
1220
|
picker.addEventListener("bv-cancel", () => {
|
|
888
|
-
|
|
1221
|
+
var _a;
|
|
1222
|
+
(_a = this._dropdown) == null ? void 0 : _a.close();
|
|
1223
|
+
});
|
|
1224
|
+
picker.addEventListener("bv-validity-change", (e) => {
|
|
1225
|
+
const detail = e.detail;
|
|
1226
|
+
if (this._triggerEl) {
|
|
1227
|
+
if (detail.valid) {
|
|
1228
|
+
this._triggerEl.classList.remove("invalid");
|
|
1229
|
+
this._triggerEl.removeAttribute("aria-invalid");
|
|
1230
|
+
} else {
|
|
1231
|
+
this._triggerEl.classList.add("invalid");
|
|
1232
|
+
this._triggerEl.setAttribute("aria-invalid", "true");
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
this.dispatchEvent(
|
|
1236
|
+
new CustomEvent("bv-validity-change", {
|
|
1237
|
+
bubbles: true,
|
|
1238
|
+
composed: true,
|
|
1239
|
+
detail
|
|
1240
|
+
})
|
|
1241
|
+
);
|
|
889
1242
|
});
|
|
890
1243
|
popup.appendChild(picker);
|
|
891
1244
|
shadow.append(label, trigger, popup);
|
|
892
1245
|
this._syncLabel();
|
|
893
1246
|
this._syncDisplay();
|
|
894
|
-
this.
|
|
895
|
-
|
|
896
|
-
|
|
1247
|
+
this._dropdown = bindDropdownController(this, {
|
|
1248
|
+
trigger,
|
|
1249
|
+
onBeforeOpen: () => {
|
|
1250
|
+
if (this._pickerEl) {
|
|
1251
|
+
this._pickerEl.setAttribute("start", this.getAttribute("start") ?? "");
|
|
1252
|
+
this._pickerEl.setAttribute("end", this.getAttribute("end") ?? "");
|
|
1253
|
+
}
|
|
897
1254
|
}
|
|
898
|
-
};
|
|
899
|
-
document.addEventListener("click", this._offClick);
|
|
900
|
-
}
|
|
901
|
-
_toggle() {
|
|
902
|
-
if (this.hasAttribute("open")) this._close();
|
|
903
|
-
else this._open();
|
|
904
|
-
}
|
|
905
|
-
_open() {
|
|
906
|
-
var _a;
|
|
907
|
-
this.setAttribute("open", "");
|
|
908
|
-
(_a = this.shadowRoot.querySelector(".trigger")) == null ? void 0 : _a.setAttribute("aria-expanded", "true");
|
|
909
|
-
if (this._pickerEl) {
|
|
910
|
-
this._pickerEl.setAttribute("start", this.getAttribute("start") ?? "");
|
|
911
|
-
this._pickerEl.setAttribute("end", this.getAttribute("end") ?? "");
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
_close() {
|
|
915
|
-
var _a;
|
|
916
|
-
this.removeAttribute("open");
|
|
917
|
-
(_a = this.shadowRoot.querySelector(".trigger")) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
|
|
1255
|
+
});
|
|
918
1256
|
}
|
|
919
1257
|
_syncLabel() {
|
|
920
1258
|
if (!this._labelEl) return;
|