@reliability-design/web 1.4.4 → 1.4.6
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/dist/components/rel-accordion/rel-accordion.ts +13 -1
- package/dist/components/rel-alert/rel-alert.ts +1 -1
- package/dist/components/rel-autocomplete/rel-autocomplete.ts +33 -2
- package/dist/components/rel-badge/rel-badge.ts +6 -1
- package/dist/components/rel-button/rel-button.ts +18 -2
- package/dist/components/rel-calendar/rel-calendar.ts +86 -18
- package/dist/components/rel-carousel/rel-carousel.ts +45 -2
- package/dist/components/rel-checkbox/rel-checkbox.ts +4 -1
- package/dist/components/rel-chip/rel-chip.ts +59 -19
- package/dist/components/rel-date-picker/date-parse.test.ts +116 -0
- package/dist/components/rel-date-picker/date-parse.ts +178 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +675 -93
- package/dist/components/rel-drawer/rel-drawer.ts +28 -61
- package/dist/components/rel-menu/rel-menu.ts +130 -15
- package/dist/components/rel-menubar/rel-menubar.ts +221 -63
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +39 -3
- package/dist/components/rel-otp/rel-otp.ts +14 -4
- package/dist/components/rel-pagination/rel-pagination.ts +13 -0
- package/dist/components/rel-popover/rel-popover.ts +64 -5
- package/dist/components/rel-progress/rel-progress.ts +13 -4
- package/dist/components/rel-radio/README.md +6 -3
- package/dist/components/rel-radio/rel-radio.ts +236 -60
- package/dist/components/rel-range-picker/rel-range-picker.ts +581 -91
- package/dist/components/rel-resizable/rel-resizable-group.ts +20 -0
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +55 -13
- package/dist/components/rel-select/rel-select.ts +203 -24
- package/dist/components/rel-sheet/rel-sheet.ts +184 -18
- package/dist/components/rel-sidebar/rel-sidebar.ts +50 -40
- package/dist/components/rel-skeleton/rel-skeleton.ts +9 -0
- package/dist/components/rel-slider/rel-slider.ts +6 -3
- package/dist/components/rel-snackbar/rel-snackbar.ts +20 -16
- package/dist/components/rel-stepper/rel-stepper.ts +47 -2
- package/dist/components/rel-switch/rel-switch.ts +3 -1
- package/dist/components/rel-tab/rel-tab.ts +32 -7
- package/dist/components/rel-textfield/rel-textfield.ts +263 -75
- package/dist/components/rel-time-picker/rel-time-picker.ts +749 -261
- package/dist/components/rel-time-picker/time-parse.test.ts +67 -0
- package/dist/components/rel-time-picker/time-parse.ts +85 -0
- package/dist/components/rel-toast/rel-toast.ts +4 -2
- package/dist/components/rel-tooltip/README.md +13 -5
- package/dist/components/rel-tooltip/rel-tooltip.ts +33 -0
- package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-brand.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-item.ts +318 -319
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-profile.ts +224 -224
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar.ts +440 -440
- package/dist/components/rel-upload/rel-upload.ts +25 -0
- package/dist/index.d.mts +461 -23
- package/dist/index.d.ts +461 -23
- package/dist/index.js +3906 -807
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4143 -1055
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { LitElement, html, css, nothing } from "lit";
|
|
2
2
|
import { customElement, property, state, query } from "lit/decorators.js";
|
|
3
|
+
import { live } from "lit/directives/live.js";
|
|
3
4
|
import "@reliability-design/icons/rel-icon";
|
|
4
5
|
import "../rel-button/rel-button";
|
|
6
|
+
import { announce } from "../../utils/announce";
|
|
7
|
+
import { isEditableTarget } from "../../utils/shortcuts";
|
|
8
|
+
import { parseDateInput } from "../rel-date-picker/date-parse";
|
|
5
9
|
|
|
6
10
|
interface RangeDay {
|
|
7
11
|
date: Date;
|
|
@@ -42,6 +46,7 @@ export class RelRangePicker extends LitElement {
|
|
|
42
46
|
@property({ type: String, attribute: "max-height" }) maxHeight: string = "";
|
|
43
47
|
@property({ type: String }) placeholder: string = "Select date range";
|
|
44
48
|
@property({ type: String, attribute: "trigger-variant" }) triggerVariant: "default" | "icon" = "default";
|
|
49
|
+
@property({ type: Boolean }) clearable = false;
|
|
45
50
|
|
|
46
51
|
@state() private draftFrom: string = "";
|
|
47
52
|
@state() private draftTo: string = "";
|
|
@@ -50,8 +55,14 @@ export class RelRangePicker extends LitElement {
|
|
|
50
55
|
@state() private viewMonth: number = new Date().getMonth();
|
|
51
56
|
@state() private isSelectingRange: boolean = false;
|
|
52
57
|
@state() private hoverDate: string | null = null;
|
|
53
|
-
|
|
54
|
-
@
|
|
58
|
+
/** ISO date of the cell that owns the grid's single Tab stop (roving tabindex). */
|
|
59
|
+
@state() private focusedDate: string | null = null;
|
|
60
|
+
/** Uncommitted typed text; null → the input shows the committed range. */
|
|
61
|
+
@state() private inputText: string | null = null;
|
|
62
|
+
/** Parse/range error message for the typed text (cleared on next input). */
|
|
63
|
+
@state() private inputError: string | null = null;
|
|
64
|
+
|
|
65
|
+
@query(".trigger-input") private triggerInput?: HTMLInputElement;
|
|
55
66
|
@query(".popover") private popoverEl?: HTMLDivElement;
|
|
56
67
|
@query(".backdrop") private backdropEl?: HTMLDivElement;
|
|
57
68
|
|
|
@@ -95,22 +106,81 @@ export class RelRangePicker extends LitElement {
|
|
|
95
106
|
letter-spacing: var(--rel-body-md-regular-letter-spacing);
|
|
96
107
|
}
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
:host(:not([disabled])) .trigger-button:hover {
|
|
99
110
|
border-color: var(--rel-border-accent);
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
.trigger-button:focus-
|
|
113
|
+
.trigger-button:focus-within {
|
|
103
114
|
outline: 2px solid var(--rel-border-focus);
|
|
104
115
|
outline-offset: 2px;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
.trigger-
|
|
118
|
+
.trigger-button.error {
|
|
119
|
+
border-color: var(--rel-border-danger);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.trigger-button.error:focus-within {
|
|
123
|
+
outline-color: var(--rel-border-danger);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* The editable field itself — the wrapper carries the box styles. */
|
|
127
|
+
.trigger-input {
|
|
108
128
|
flex: 1;
|
|
129
|
+
min-width: 0;
|
|
130
|
+
border: none;
|
|
131
|
+
background: transparent;
|
|
132
|
+
padding: 0;
|
|
133
|
+
margin: 0;
|
|
134
|
+
outline: none;
|
|
109
135
|
color: var(--rel-text-default);
|
|
136
|
+
font: inherit;
|
|
137
|
+
letter-spacing: inherit;
|
|
138
|
+
cursor: inherit;
|
|
110
139
|
}
|
|
111
140
|
|
|
112
|
-
.trigger-
|
|
141
|
+
.trigger-input::placeholder {
|
|
113
142
|
color: var(--rel-text-placeholder);
|
|
143
|
+
opacity: 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Typed-entry error line (the component has no helper-text API). */
|
|
147
|
+
.range-picker-support.error {
|
|
148
|
+
margin-top: var(--rel-spacing-2xs);
|
|
149
|
+
color: var(--rel-text-danger);
|
|
150
|
+
font-family: var(--rel-body-xs-regular-font-family);
|
|
151
|
+
font-weight: var(--rel-body-xs-regular-font-weight);
|
|
152
|
+
font-size: var(--rel-body-xs-regular-font-size);
|
|
153
|
+
line-height: var(--rel-body-xs-regular-line-height);
|
|
154
|
+
letter-spacing: var(--rel-body-xs-regular-letter-spacing);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.trigger-icons {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
gap: var(--rel-spacing-2xs);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.icon-button {
|
|
164
|
+
background: none;
|
|
165
|
+
border: none;
|
|
166
|
+
padding: var(--rel-spacing-3xs);
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
color: var(--rel-text-placeholder);
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
border-radius: var(--rel-border-radius-xs);
|
|
173
|
+
transition: all 0.15s ease;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.icon-button:hover {
|
|
177
|
+
background: var(--rel-bg-surface-elevated);
|
|
178
|
+
color: var(--rel-text-default);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.icon-button svg {
|
|
182
|
+
width: 16px;
|
|
183
|
+
height: 16px;
|
|
114
184
|
}
|
|
115
185
|
|
|
116
186
|
/* ── Popover shell ── */
|
|
@@ -134,7 +204,13 @@ export class RelRangePicker extends LitElement {
|
|
|
134
204
|
border-radius: var(--rel-border-radius-sm, 8px);
|
|
135
205
|
background: var(--rel-bg-neutral);
|
|
136
206
|
box-shadow: var(--rel-shadows-shadow-200);
|
|
137
|
-
|
|
207
|
+
/* Never wider or taller than the viewport (small screens) — the
|
|
208
|
+
two-calendar layout scrolls inside the shell instead of bleeding
|
|
209
|
+
off-screen. border-box so the caps include the 1px borders. */
|
|
210
|
+
box-sizing: border-box;
|
|
211
|
+
max-width: calc(100vw - 16px);
|
|
212
|
+
max-height: calc(100vh - 16px);
|
|
213
|
+
overflow: auto;
|
|
138
214
|
display: none;
|
|
139
215
|
}
|
|
140
216
|
|
|
@@ -225,6 +301,28 @@ export class RelRangePicker extends LitElement {
|
|
|
225
301
|
grid-column: 1 / -1;
|
|
226
302
|
}
|
|
227
303
|
|
|
304
|
+
/* Single-key hint chip on each preset (T, M, W, …). */
|
|
305
|
+
.preset-key {
|
|
306
|
+
flex-shrink: 0;
|
|
307
|
+
margin-left: var(--rel-spacing-2xs);
|
|
308
|
+
border: 1px solid var(--rel-border-default);
|
|
309
|
+
border-bottom-width: 2px;
|
|
310
|
+
border-radius: var(--rel-border-radius-xs);
|
|
311
|
+
background: var(--rel-bg-surface);
|
|
312
|
+
padding: 0 var(--rel-spacing-3xs);
|
|
313
|
+
min-width: 18px;
|
|
314
|
+
text-align: center;
|
|
315
|
+
font-family: var(--rel-body-xs-regular-font-family);
|
|
316
|
+
font-weight: var(--rel-body-xs-regular-font-weight);
|
|
317
|
+
font-size: var(--rel-body-xs-regular-font-size);
|
|
318
|
+
line-height: var(--rel-body-xs-regular-line-height);
|
|
319
|
+
color: var(--rel-text-label);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.preset-item.active .preset-key {
|
|
323
|
+
display: none;
|
|
324
|
+
}
|
|
325
|
+
|
|
228
326
|
/* ── FROM/TO inputs ── */
|
|
229
327
|
.date-inputs {
|
|
230
328
|
display: flex;
|
|
@@ -338,11 +436,23 @@ export class RelRangePicker extends LitElement {
|
|
|
338
436
|
}
|
|
339
437
|
|
|
340
438
|
/* ── Calendar grid ── */
|
|
439
|
+
.calendar-table {
|
|
440
|
+
display: flex;
|
|
441
|
+
flex-direction: column;
|
|
442
|
+
gap: 6px;
|
|
443
|
+
}
|
|
444
|
+
|
|
341
445
|
.calendar-grid {
|
|
342
446
|
display: grid;
|
|
343
447
|
grid-template-columns: repeat(7, 1fr);
|
|
344
448
|
}
|
|
345
449
|
|
|
450
|
+
.calendar-week {
|
|
451
|
+
grid-column: 1 / -1;
|
|
452
|
+
display: grid;
|
|
453
|
+
grid-template-columns: repeat(7, 1fr);
|
|
454
|
+
}
|
|
455
|
+
|
|
346
456
|
.day-cell {
|
|
347
457
|
height: 36px;
|
|
348
458
|
position: relative;
|
|
@@ -352,6 +462,12 @@ export class RelRangePicker extends LitElement {
|
|
|
352
462
|
cursor: pointer;
|
|
353
463
|
}
|
|
354
464
|
|
|
465
|
+
.day-cell:focus-visible {
|
|
466
|
+
outline: 2px solid var(--rel-border-focus);
|
|
467
|
+
outline-offset: -2px;
|
|
468
|
+
z-index: 2;
|
|
469
|
+
}
|
|
470
|
+
|
|
355
471
|
.day-cell.other-month {
|
|
356
472
|
opacity: 0.35;
|
|
357
473
|
pointer-events: none;
|
|
@@ -450,7 +566,7 @@ export class RelRangePicker extends LitElement {
|
|
|
450
566
|
}
|
|
451
567
|
|
|
452
568
|
@media (prefers-reduced-motion: reduce) {
|
|
453
|
-
.preset-item, .nav-btn, .day-inner {
|
|
569
|
+
.preset-item, .nav-btn, .day-inner, .icon-button {
|
|
454
570
|
transition: none;
|
|
455
571
|
}
|
|
456
572
|
}
|
|
@@ -557,8 +673,9 @@ export class RelRangePicker extends LitElement {
|
|
|
557
673
|
}
|
|
558
674
|
let left = t.left;
|
|
559
675
|
if (left + c.width > window.innerWidth - 8) {
|
|
560
|
-
left =
|
|
676
|
+
left = window.innerWidth - c.width - 8;
|
|
561
677
|
}
|
|
678
|
+
left = Math.max(8, left);
|
|
562
679
|
|
|
563
680
|
// Without top-layer support a transformed ancestor may be the containing
|
|
564
681
|
// block for position:fixed, shifting the shell away from the viewport
|
|
@@ -588,11 +705,15 @@ export class RelRangePicker extends LitElement {
|
|
|
588
705
|
|
|
589
706
|
if (e.key === "Escape") {
|
|
590
707
|
this.cancelPopover();
|
|
591
|
-
this.
|
|
708
|
+
this.triggerInput?.focus();
|
|
592
709
|
return;
|
|
593
710
|
}
|
|
594
711
|
|
|
595
|
-
|
|
712
|
+
// composedPath-aware: a document-level e.target retargets to the host,
|
|
713
|
+
// so a plain tagName check misses the typed-entry input in the shadow
|
|
714
|
+
// root — single-key preset shortcuts must not fire while typing.
|
|
715
|
+
if (isEditableTarget(e)) return;
|
|
716
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
596
717
|
|
|
597
718
|
const key = e.key.toUpperCase();
|
|
598
719
|
const keyMap: Record<string, string> = {
|
|
@@ -627,6 +748,7 @@ export class RelRangePicker extends LitElement {
|
|
|
627
748
|
this.draftActivePreset = null;
|
|
628
749
|
this.isSelectingRange = false;
|
|
629
750
|
this.hoverDate = null;
|
|
751
|
+
this.focusedDate = null;
|
|
630
752
|
|
|
631
753
|
if (this.draftFrom) {
|
|
632
754
|
const date = this.parseISODate(this.draftFrom);
|
|
@@ -668,27 +790,30 @@ export class RelRangePicker extends LitElement {
|
|
|
668
790
|
}
|
|
669
791
|
|
|
670
792
|
private applySelection() {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
793
|
+
// An empty draft is committable when a committed range exists — this is
|
|
794
|
+
// how Reset propagates to the trigger (Reset → Apply clears the input).
|
|
795
|
+
if (!this.draftFrom && !this.draftTo && !this.from && !this.to) return;
|
|
796
|
+
this.from = this.draftFrom;
|
|
797
|
+
this.to = this.draftTo;
|
|
798
|
+
this.inputText = null;
|
|
799
|
+
this.inputError = null;
|
|
800
|
+
this.dispatchEvent(
|
|
801
|
+
new CustomEvent("rel-change", {
|
|
802
|
+
detail: { from: this.from, to: this.to, label: this.displayLabel },
|
|
803
|
+
bubbles: true,
|
|
804
|
+
composed: true,
|
|
805
|
+
})
|
|
806
|
+
);
|
|
807
|
+
this.open = false;
|
|
808
|
+
this.isSelectingRange = false;
|
|
809
|
+
this.hoverDate = null;
|
|
810
|
+
this.dispatchEvent(
|
|
811
|
+
new CustomEvent("rel-open-change", {
|
|
812
|
+
detail: { open: false },
|
|
813
|
+
bubbles: true,
|
|
814
|
+
composed: true,
|
|
815
|
+
})
|
|
816
|
+
);
|
|
692
817
|
}
|
|
693
818
|
|
|
694
819
|
private resetDraft() {
|
|
@@ -701,6 +826,162 @@ export class RelRangePicker extends LitElement {
|
|
|
701
826
|
}
|
|
702
827
|
}
|
|
703
828
|
|
|
829
|
+
private clearRange(e: Event) {
|
|
830
|
+
e.stopPropagation();
|
|
831
|
+
this.from = "";
|
|
832
|
+
this.to = "";
|
|
833
|
+
this.draftFrom = "";
|
|
834
|
+
this.draftTo = "";
|
|
835
|
+
this.draftActivePreset = null;
|
|
836
|
+
this.isSelectingRange = false;
|
|
837
|
+
this.hoverDate = null;
|
|
838
|
+
this.inputText = null;
|
|
839
|
+
this.inputError = null;
|
|
840
|
+
this.dispatchEvent(
|
|
841
|
+
new CustomEvent("rel-change", {
|
|
842
|
+
detail: { from: "", to: "", label: "" },
|
|
843
|
+
bubbles: true,
|
|
844
|
+
composed: true,
|
|
845
|
+
})
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// ── Typed keyboard entry ──
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Parse "date → date" text into an ISO from/to pair, or null. Each side
|
|
853
|
+
* goes through rel-date-picker's parseDateInput, so ISO / numeric /
|
|
854
|
+
* month-name dates and the t / t±N shortcuts all work per side.
|
|
855
|
+
* Separators: "→", "..", spaced "-", or the word "to" ("t - t+7",
|
|
856
|
+
* "21/7 to 28/7"). A single date yields a one-day range. Reversed input
|
|
857
|
+
* is normalized (from ≤ to). dayFirst is hard-coded false because the
|
|
858
|
+
* displayed format is always MM/DD/YYYY.
|
|
859
|
+
*/
|
|
860
|
+
private parseRangeText(text: string): { from: string; to: string } | null {
|
|
861
|
+
const parts = text
|
|
862
|
+
.split(/\s+to\s+|\s*(?:→|\.\.)\s*|\s+-\s+/i)
|
|
863
|
+
.map((p) => p.trim())
|
|
864
|
+
.filter(Boolean);
|
|
865
|
+
if (parts.length < 1 || parts.length > 2) return null;
|
|
866
|
+
|
|
867
|
+
const opts = { referenceDate: new Date(), dayFirst: false, locale: this.locale };
|
|
868
|
+
const fromDate = parseDateInput(parts[0], opts);
|
|
869
|
+
if (!fromDate) return null;
|
|
870
|
+
const toDate = parts.length === 2 ? parseDateInput(parts[1], opts) : fromDate;
|
|
871
|
+
if (!toDate) return null;
|
|
872
|
+
|
|
873
|
+
const [a, b] =
|
|
874
|
+
fromDate.getTime() <= toDate.getTime() ? [fromDate, toDate] : [toDate, fromDate];
|
|
875
|
+
return { from: this.formatISODate(a), to: this.formatISODate(b) };
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Parse and commit the typed text (Enter / blur). Invalid or out-of-range
|
|
880
|
+
* text keeps the field in an error state with the text preserved for
|
|
881
|
+
* correction; the committed range is untouched (Escape reverts the text).
|
|
882
|
+
*/
|
|
883
|
+
private commitTypedValue() {
|
|
884
|
+
if (this.inputText === null) return; // nothing typed since last commit
|
|
885
|
+
const text = this.inputText.trim();
|
|
886
|
+
|
|
887
|
+
if (!text) {
|
|
888
|
+
// Field cleared by keyboard — behaves like the clear button.
|
|
889
|
+
this.inputText = null;
|
|
890
|
+
this.inputError = null;
|
|
891
|
+
if (this.from || this.to) {
|
|
892
|
+
this.from = "";
|
|
893
|
+
this.to = "";
|
|
894
|
+
this.draftFrom = "";
|
|
895
|
+
this.draftTo = "";
|
|
896
|
+
this.draftActivePreset = null;
|
|
897
|
+
this.dispatchEvent(
|
|
898
|
+
new CustomEvent("rel-change", {
|
|
899
|
+
detail: { from: "", to: "", label: "" },
|
|
900
|
+
bubbles: true,
|
|
901
|
+
composed: true,
|
|
902
|
+
})
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (this.open) this.cancelPopover();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const range = this.parseRangeText(text);
|
|
910
|
+
if (!range) {
|
|
911
|
+
this.inputError = "Invalid date range";
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const fromDate = this.parseISODate(range.from)!;
|
|
915
|
+
const toDate = this.parseISODate(range.to)!;
|
|
916
|
+
if (this.isDateDisabled(fromDate) || this.isDateDisabled(toDate)) {
|
|
917
|
+
this.inputError = "Date not available";
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
this.from = range.from;
|
|
922
|
+
this.to = range.to;
|
|
923
|
+
this.inputText = null;
|
|
924
|
+
this.inputError = null;
|
|
925
|
+
this.dispatchEvent(
|
|
926
|
+
new CustomEvent("rel-change", {
|
|
927
|
+
detail: { from: this.from, to: this.to, label: this.displayLabel },
|
|
928
|
+
bubbles: true,
|
|
929
|
+
composed: true,
|
|
930
|
+
})
|
|
931
|
+
);
|
|
932
|
+
if (this.open) this.cancelPopover();
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
private handleInputInput(e: Event) {
|
|
936
|
+
this.inputText = (e.target as HTMLInputElement).value;
|
|
937
|
+
if (this.inputError) this.inputError = null;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
private handleInputKeydown(e: KeyboardEvent) {
|
|
941
|
+
if (e.key === "Enter") {
|
|
942
|
+
e.preventDefault();
|
|
943
|
+
if (this.inputText === null) {
|
|
944
|
+
// Nothing typed — Enter opens the panel (previous trigger behavior).
|
|
945
|
+
if (!this.open) this.openPopover();
|
|
946
|
+
} else {
|
|
947
|
+
this.commitTypedValue();
|
|
948
|
+
}
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (e.key === "ArrowDown") {
|
|
952
|
+
e.preventDefault();
|
|
953
|
+
if (!this.open) this.openPopover();
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
if (e.key === "Escape" && this.inputText !== null) {
|
|
957
|
+
// Revert the typed text; the document listener closes the panel.
|
|
958
|
+
this.inputText = null;
|
|
959
|
+
this.inputError = null;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/** Clicking anywhere on the field opens the panel (never closes). */
|
|
964
|
+
private handleFieldClick() {
|
|
965
|
+
if (this.open || this.disabled) return;
|
|
966
|
+
this.openPopover();
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/** The calendar icon toggles the panel (previous trigger behavior). */
|
|
970
|
+
private handleIconClick(e: Event) {
|
|
971
|
+
e.stopPropagation();
|
|
972
|
+
this.toggleOpen();
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/** Commit typed text when focus leaves the component entirely. */
|
|
976
|
+
private handleInputBlur = (e: FocusEvent) => {
|
|
977
|
+
this.dispatchEvent(new CustomEvent("rel-blur", { bubbles: true, composed: true }));
|
|
978
|
+
const related = e.relatedTarget as Node | null;
|
|
979
|
+
if (related && (this.renderRoot.contains(related) || this.contains(related))) {
|
|
980
|
+
return; // focus moved into the panel/icons — their own handlers commit
|
|
981
|
+
}
|
|
982
|
+
this.commitTypedValue();
|
|
983
|
+
};
|
|
984
|
+
|
|
704
985
|
private getTriggerLabel(): string {
|
|
705
986
|
const from = this.from ? this.formatDisplayDate(this.from) : null;
|
|
706
987
|
const to = this.to ? this.formatDisplayDate(this.to) : null;
|
|
@@ -823,6 +1104,7 @@ export class RelRangePicker extends LitElement {
|
|
|
823
1104
|
} else {
|
|
824
1105
|
this.viewMonth--;
|
|
825
1106
|
}
|
|
1107
|
+
announce(`${this.getMonthName()} ${this.viewYear}`);
|
|
826
1108
|
}
|
|
827
1109
|
|
|
828
1110
|
private nextMonth() {
|
|
@@ -832,6 +1114,7 @@ export class RelRangePicker extends LitElement {
|
|
|
832
1114
|
} else {
|
|
833
1115
|
this.viewMonth++;
|
|
834
1116
|
}
|
|
1117
|
+
announce(`${this.getMonthName()} ${this.viewYear}`);
|
|
835
1118
|
}
|
|
836
1119
|
|
|
837
1120
|
private getMonthName(): string {
|
|
@@ -840,6 +1123,23 @@ export class RelRangePicker extends LitElement {
|
|
|
840
1123
|
);
|
|
841
1124
|
}
|
|
842
1125
|
|
|
1126
|
+
private dayLabelFormatter: Intl.DateTimeFormat | null = null;
|
|
1127
|
+
|
|
1128
|
+
private formatDayLabel(day: RangeDay): string {
|
|
1129
|
+
if (!this.dayLabelFormatter) {
|
|
1130
|
+
this.dayLabelFormatter = new Intl.DateTimeFormat("en-US", {
|
|
1131
|
+
weekday: "long",
|
|
1132
|
+
month: "long",
|
|
1133
|
+
day: "numeric",
|
|
1134
|
+
year: "numeric",
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
let label = this.dayLabelFormatter.format(day.date);
|
|
1138
|
+
if (day.isDraftFrom) label += ", range start";
|
|
1139
|
+
if (day.isDraftTo) label += ", range end";
|
|
1140
|
+
return label;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
843
1143
|
private getWeekdayLabels(): string[] {
|
|
844
1144
|
const formatter = new Intl.DateTimeFormat("en-US", { weekday: "short" });
|
|
845
1145
|
const base = new Date(2024, 0, 7); // Sunday Jan 7 2024
|
|
@@ -956,19 +1256,23 @@ export class RelRangePicker extends LitElement {
|
|
|
956
1256
|
this.draftActivePreset = null;
|
|
957
1257
|
this.isSelectingRange = true;
|
|
958
1258
|
this.hoverDate = null;
|
|
1259
|
+
announce(`From date ${this.formatDisplayDate(iso)}`);
|
|
959
1260
|
} else {
|
|
960
1261
|
const fromDate = this.parseISODate(this.draftFrom);
|
|
961
1262
|
if (!fromDate) {
|
|
962
1263
|
this.draftFrom = iso;
|
|
963
1264
|
this.draftTo = "";
|
|
1265
|
+
announce(`From date ${this.formatDisplayDate(iso)}`);
|
|
964
1266
|
return;
|
|
965
1267
|
}
|
|
966
1268
|
|
|
967
1269
|
if (day.date.getTime() >= fromDate.getTime()) {
|
|
968
1270
|
this.draftTo = iso;
|
|
1271
|
+
announce(`To date ${this.formatDisplayDate(iso)}`);
|
|
969
1272
|
} else {
|
|
970
1273
|
this.draftTo = this.draftFrom;
|
|
971
1274
|
this.draftFrom = iso;
|
|
1275
|
+
announce(`From date ${this.formatDisplayDate(iso)}`);
|
|
972
1276
|
}
|
|
973
1277
|
|
|
974
1278
|
this.isSelectingRange = false;
|
|
@@ -982,12 +1286,135 @@ export class RelRangePicker extends LitElement {
|
|
|
982
1286
|
this.hoverDate = this.formatISODate(day.date);
|
|
983
1287
|
}
|
|
984
1288
|
|
|
1289
|
+
// ── Grid keyboard navigation (APG grid pattern) ──
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* The cell that carries tabindex="0". Prefers the last roved date, then the
|
|
1293
|
+
* draft start, then today, then the first selectable day — always exactly
|
|
1294
|
+
* one Tab stop, even after arrowing away with nothing selected.
|
|
1295
|
+
*/
|
|
1296
|
+
private getRovingIso(days: RangeDay[]): string {
|
|
1297
|
+
const inView = days.filter((d) => d.isCurrentMonth);
|
|
1298
|
+
if (inView.length === 0) return "";
|
|
1299
|
+
|
|
1300
|
+
const candidates = [
|
|
1301
|
+
this.focusedDate,
|
|
1302
|
+
this.draftFrom,
|
|
1303
|
+
this.formatISODate(new Date()),
|
|
1304
|
+
];
|
|
1305
|
+
for (const iso of candidates) {
|
|
1306
|
+
if (iso && inView.some((d) => this.formatISODate(d.date) === iso)) {
|
|
1307
|
+
return iso;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
const firstEnabled = inView.find((d) => !d.isDisabled) ?? inView[0];
|
|
1311
|
+
return this.formatISODate(firstEnabled.date);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
private handleGridKeydown = (e: KeyboardEvent) => {
|
|
1315
|
+
const cell = (e.target as HTMLElement).closest?.(".day-cell") as
|
|
1316
|
+
| HTMLElement
|
|
1317
|
+
| null;
|
|
1318
|
+
const iso = cell?.dataset.iso;
|
|
1319
|
+
if (!iso) return;
|
|
1320
|
+
const date = this.parseISODate(iso);
|
|
1321
|
+
if (!date) return;
|
|
1322
|
+
|
|
1323
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1324
|
+
e.preventDefault();
|
|
1325
|
+
const isDisabled = this.isDateDisabled(date);
|
|
1326
|
+
if (!isDisabled) {
|
|
1327
|
+
this.handleDayClick({
|
|
1328
|
+
date,
|
|
1329
|
+
day: date.getDate(),
|
|
1330
|
+
isCurrentMonth: true,
|
|
1331
|
+
isToday: false,
|
|
1332
|
+
isDraftFrom: false,
|
|
1333
|
+
isDraftTo: false,
|
|
1334
|
+
isInRange: false,
|
|
1335
|
+
isDisabled,
|
|
1336
|
+
isWeekend: false,
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const next = new Date(date);
|
|
1343
|
+
const dow = next.getDay();
|
|
1344
|
+
switch (e.key) {
|
|
1345
|
+
case "ArrowLeft":
|
|
1346
|
+
next.setDate(next.getDate() - 1);
|
|
1347
|
+
break;
|
|
1348
|
+
case "ArrowRight":
|
|
1349
|
+
next.setDate(next.getDate() + 1);
|
|
1350
|
+
break;
|
|
1351
|
+
case "ArrowUp":
|
|
1352
|
+
next.setDate(next.getDate() - 7);
|
|
1353
|
+
break;
|
|
1354
|
+
case "ArrowDown":
|
|
1355
|
+
next.setDate(next.getDate() + 7);
|
|
1356
|
+
break;
|
|
1357
|
+
case "Home":
|
|
1358
|
+
next.setDate(
|
|
1359
|
+
next.getDate() -
|
|
1360
|
+
(this.weekStart === "mon" ? (dow === 0 ? 6 : dow - 1) : dow)
|
|
1361
|
+
);
|
|
1362
|
+
break;
|
|
1363
|
+
case "End":
|
|
1364
|
+
next.setDate(
|
|
1365
|
+
next.getDate() +
|
|
1366
|
+
(this.weekStart === "mon" ? (dow === 0 ? 0 : 7 - dow) : 6 - dow)
|
|
1367
|
+
);
|
|
1368
|
+
break;
|
|
1369
|
+
case "PageUp":
|
|
1370
|
+
if (e.shiftKey) next.setFullYear(next.getFullYear() - 1);
|
|
1371
|
+
else next.setMonth(next.getMonth() - 1);
|
|
1372
|
+
break;
|
|
1373
|
+
case "PageDown":
|
|
1374
|
+
if (e.shiftKey) next.setFullYear(next.getFullYear() + 1);
|
|
1375
|
+
else next.setMonth(next.getMonth() + 1);
|
|
1376
|
+
break;
|
|
1377
|
+
default:
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
e.preventDefault();
|
|
1382
|
+
this.moveGridFocusTo(next);
|
|
1383
|
+
};
|
|
1384
|
+
|
|
1385
|
+
private async moveGridFocusTo(date: Date) {
|
|
1386
|
+
this.focusedDate = this.formatISODate(date);
|
|
1387
|
+
|
|
1388
|
+
if (
|
|
1389
|
+
date.getMonth() !== this.viewMonth ||
|
|
1390
|
+
date.getFullYear() !== this.viewYear
|
|
1391
|
+
) {
|
|
1392
|
+
this.viewMonth = date.getMonth();
|
|
1393
|
+
this.viewYear = date.getFullYear();
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Keyboard twin of handleDayHover: keep the range preview following focus.
|
|
1397
|
+
if (this.isSelectingRange && !this.isDateDisabled(date)) {
|
|
1398
|
+
this.hoverDate = this.focusedDate;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
await this.updateComplete;
|
|
1402
|
+
this.renderRoot
|
|
1403
|
+
.querySelector<HTMLElement>(`.day-cell[data-iso="${this.focusedDate}"]`)
|
|
1404
|
+
?.focus();
|
|
1405
|
+
}
|
|
1406
|
+
|
|
985
1407
|
// ── Render ──
|
|
986
1408
|
|
|
987
1409
|
render() {
|
|
988
1410
|
const quickSelects = this.getQuickSelects();
|
|
989
1411
|
const days = this.getCalendarDays();
|
|
1412
|
+
const rovingIso = this.getRovingIso(days);
|
|
990
1413
|
const weekdayLabels = this.getWeekdayLabels();
|
|
1414
|
+
const weeks: RangeDay[][] = [];
|
|
1415
|
+
for (let i = 0; i < days.length; i += 7) {
|
|
1416
|
+
weeks.push(days.slice(i, i + 7));
|
|
1417
|
+
}
|
|
991
1418
|
|
|
992
1419
|
const draftFromDisplay = this.draftFrom ? this.formatDisplayDate(this.draftFrom) : "";
|
|
993
1420
|
const draftToDisplay = this.draftTo ? this.formatDisplayDate(this.draftTo) : "";
|
|
@@ -1011,22 +1438,71 @@ export class RelRangePicker extends LitElement {
|
|
|
1011
1438
|
</rel-button>
|
|
1012
1439
|
`
|
|
1013
1440
|
: html`
|
|
1014
|
-
<
|
|
1015
|
-
part="trigger"
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
aria-haspopup="dialog"
|
|
1019
|
-
aria-expanded=${this.open}
|
|
1020
|
-
aria-label=${triggerLabel || this.placeholder}
|
|
1021
|
-
@click=${this.toggleOpen}
|
|
1441
|
+
<div
|
|
1442
|
+
part="trigger"
|
|
1443
|
+
class="trigger-button ${this.inputError ? "error" : ""}"
|
|
1444
|
+
@click=${this.handleFieldClick}
|
|
1022
1445
|
>
|
|
1023
|
-
<
|
|
1024
|
-
|
|
1446
|
+
<input
|
|
1447
|
+
part="value"
|
|
1448
|
+
class="trigger-input"
|
|
1449
|
+
type="text"
|
|
1450
|
+
.value=${live(this.inputText ?? triggerLabel)}
|
|
1451
|
+
placeholder=${this.placeholder}
|
|
1452
|
+
?disabled=${this.disabled}
|
|
1453
|
+
autocomplete="off"
|
|
1454
|
+
spellcheck="false"
|
|
1455
|
+
enterkeyhint="done"
|
|
1456
|
+
@input=${this.handleInputInput}
|
|
1457
|
+
@keydown=${this.handleInputKeydown}
|
|
1458
|
+
@blur=${this.handleInputBlur}
|
|
1459
|
+
role="combobox"
|
|
1460
|
+
aria-label=${this.placeholder}
|
|
1461
|
+
aria-haspopup="dialog"
|
|
1462
|
+
aria-expanded=${this.open}
|
|
1463
|
+
aria-controls="range-picker-panel"
|
|
1464
|
+
aria-invalid=${this.inputError !== null}
|
|
1465
|
+
aria-describedby=${this.inputError ? "range-picker-desc" : nothing}
|
|
1466
|
+
/>
|
|
1467
|
+
<span class="trigger-icons">
|
|
1468
|
+
${this.clearable && (this.from || this.to)
|
|
1469
|
+
? html`
|
|
1470
|
+
<button
|
|
1471
|
+
class="icon-button"
|
|
1472
|
+
type="button"
|
|
1473
|
+
aria-label="Clear date range"
|
|
1474
|
+
@click=${this.clearRange}
|
|
1475
|
+
>
|
|
1476
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
1477
|
+
<path
|
|
1478
|
+
d="M4 4l8 8M12 4l-8 8"
|
|
1479
|
+
stroke="currentColor"
|
|
1480
|
+
stroke-width="2"
|
|
1481
|
+
stroke-linecap="round"
|
|
1482
|
+
/>
|
|
1483
|
+
</svg>
|
|
1484
|
+
</button>
|
|
1485
|
+
`
|
|
1486
|
+
: nothing}
|
|
1487
|
+
<button
|
|
1488
|
+
class="icon-button"
|
|
1489
|
+
type="button"
|
|
1490
|
+
aria-label="Open date range panel"
|
|
1491
|
+
aria-expanded=${this.open}
|
|
1492
|
+
?disabled=${this.disabled}
|
|
1493
|
+
@click=${this.handleIconClick}
|
|
1494
|
+
>
|
|
1495
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
1496
|
+
<path d="M4 2h1V1h2v1h2V1h2v1h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm8 3H4v7h8V5z"/>
|
|
1497
|
+
</svg>
|
|
1498
|
+
</button>
|
|
1025
1499
|
</span>
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1500
|
+
</div>
|
|
1501
|
+
${this.inputError
|
|
1502
|
+
? html`<div class="range-picker-support error" id="range-picker-desc">
|
|
1503
|
+
${this.inputError}
|
|
1504
|
+
</div>`
|
|
1505
|
+
: nothing}
|
|
1030
1506
|
`}
|
|
1031
1507
|
|
|
1032
1508
|
${isPopup && this.open
|
|
@@ -1035,6 +1511,7 @@ export class RelRangePicker extends LitElement {
|
|
|
1035
1511
|
|
|
1036
1512
|
<div
|
|
1037
1513
|
part="panel" class="popover ${this.open ? "open" : ""} ${isPopup ? "popup" : ""}"
|
|
1514
|
+
id="range-picker-panel"
|
|
1038
1515
|
popover="manual"
|
|
1039
1516
|
role="dialog"
|
|
1040
1517
|
aria-label="Select date range"
|
|
@@ -1043,18 +1520,20 @@ export class RelRangePicker extends LitElement {
|
|
|
1043
1520
|
|
|
1044
1521
|
<!-- ── Left: Quick select ── -->
|
|
1045
1522
|
<div part="list" class="presets">
|
|
1046
|
-
<div class="presets-grid" role="
|
|
1523
|
+
<div class="presets-grid" role="group" aria-label="Quick date selections">
|
|
1047
1524
|
${quickSelects.map(
|
|
1048
1525
|
(qs) => html`
|
|
1049
1526
|
<button
|
|
1050
1527
|
part="option" class="preset-item ${qs.fullWidth ? "full-width" : ""} ${this.draftActivePreset === qs.label ? "active" : ""}"
|
|
1051
1528
|
type="button"
|
|
1052
|
-
role="listitem"
|
|
1053
1529
|
?disabled=${this.disabled}
|
|
1054
1530
|
aria-pressed=${this.draftActivePreset === qs.label}
|
|
1055
1531
|
@click=${() => this.selectQuickSelect(qs)}
|
|
1056
1532
|
>
|
|
1057
1533
|
<span>${qs.label}</span>
|
|
1534
|
+
${this.draftActivePreset !== qs.label
|
|
1535
|
+
? html`<kbd class="preset-key" aria-hidden="true" title="Press ${qs.key} while the panel is open">${qs.key}</kbd>`
|
|
1536
|
+
: nothing}
|
|
1058
1537
|
${this.draftActivePreset === qs.label
|
|
1059
1538
|
? html`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
1060
1539
|
<circle cx="12" cy="12" r="12" fill="var(--rel-bg-brand)"/>
|
|
@@ -1106,49 +1585,60 @@ export class RelRangePicker extends LitElement {
|
|
|
1106
1585
|
</button>
|
|
1107
1586
|
</div>
|
|
1108
1587
|
|
|
1109
|
-
<div class="
|
|
1110
|
-
|
|
1111
|
-
(
|
|
1112
|
-
|
|
1113
|
-
${label}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1588
|
+
<div class="calendar-table" role="grid" aria-label="Select date range">
|
|
1589
|
+
<div class="week-header" role="row">
|
|
1590
|
+
${weekdayLabels.map(
|
|
1591
|
+
(label) => html`
|
|
1592
|
+
<div class="week-day" role="columnheader" aria-label="${label}">
|
|
1593
|
+
${label}
|
|
1594
|
+
</div>
|
|
1595
|
+
`
|
|
1596
|
+
)}
|
|
1597
|
+
</div>
|
|
1118
1598
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1599
|
+
<div
|
|
1600
|
+
part="list" class="calendar-grid"
|
|
1601
|
+
@mouseleave=${() => { this.hoverDate = null; }}
|
|
1602
|
+
@keydown=${this.handleGridKeydown}
|
|
1603
|
+
>
|
|
1604
|
+
${weeks.map(
|
|
1605
|
+
(week) => html`
|
|
1606
|
+
<div class="calendar-week" role="row">
|
|
1607
|
+
${week.map((day) => {
|
|
1608
|
+
const iso = this.formatISODate(day.date);
|
|
1609
|
+
const cellClasses = [
|
|
1610
|
+
"day-cell",
|
|
1611
|
+
day.isInRange ? "in-range" : "",
|
|
1612
|
+
day.isDraftFrom ? "range-start" : "",
|
|
1613
|
+
day.isDraftTo ? "range-end" : "",
|
|
1614
|
+
day.isToday ? "today" : "",
|
|
1615
|
+
day.isDisabled ? "disabled" : "",
|
|
1616
|
+
!day.isCurrentMonth ? "other-month" : "",
|
|
1617
|
+
day.isWeekend ? "weekend" : "",
|
|
1618
|
+
]
|
|
1619
|
+
.filter(Boolean)
|
|
1620
|
+
.join(" ");
|
|
1621
|
+
|
|
1622
|
+
return html`
|
|
1623
|
+
<div
|
|
1624
|
+
class="${cellClasses}"
|
|
1625
|
+
role="gridcell"
|
|
1626
|
+
tabindex="${day.isCurrentMonth && iso === rovingIso ? 0 : -1}"
|
|
1627
|
+
data-iso="${iso}"
|
|
1628
|
+
aria-label="${this.formatDayLabel(day)}"
|
|
1629
|
+
aria-selected="${day.isDraftFrom || day.isDraftTo || day.isInRange}"
|
|
1630
|
+
aria-disabled="${day.isDisabled ? "true" : nothing}"
|
|
1631
|
+
@click=${() => this.handleDayClick(day)}
|
|
1632
|
+
@mouseenter=${() => this.handleDayHover(day)}
|
|
1633
|
+
>
|
|
1634
|
+
<span class="day-inner">${day.day}</span>
|
|
1635
|
+
</div>
|
|
1636
|
+
`;
|
|
1637
|
+
})}
|
|
1638
|
+
</div>
|
|
1639
|
+
`
|
|
1640
|
+
)}
|
|
1641
|
+
</div>
|
|
1152
1642
|
</div>
|
|
1153
1643
|
|
|
1154
1644
|
<div part="footer" class="calendar-footer">
|
|
@@ -1175,7 +1665,7 @@ export class RelRangePicker extends LitElement {
|
|
|
1175
1665
|
tone="default"
|
|
1176
1666
|
size="small"
|
|
1177
1667
|
border="corner-radius"
|
|
1178
|
-
?disabled=${isDraftEmpty}
|
|
1668
|
+
?disabled=${isDraftEmpty && !this.from && !this.to}
|
|
1179
1669
|
@click=${() => this.applySelection()}
|
|
1180
1670
|
>
|
|
1181
1671
|
Apply
|