@latty-ds/web 0.10.3 → 1.0.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/custom-elements.json +1970 -229
- package/dist/components/accordion/index.js +36 -2
- package/dist/components/alert/alert.d.ts +29 -6
- package/dist/components/alert/alert.types.d.ts +5 -0
- package/dist/components/alert/index.js +127 -20
- package/dist/components/avatar/avatar.d.ts +2 -0
- package/dist/components/avatar/index.js +10 -3
- package/dist/components/badge/index.js +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
- package/dist/components/breadcrumb/breadcrumb.types.d.ts +5 -0
- package/dist/components/breadcrumb/index.js +20 -3
- package/dist/components/button/button.d.ts +33 -0
- package/dist/components/button/index.js +138 -15
- package/dist/components/calendar/calendar.d.ts +15 -7
- package/dist/components/calendar/calendar.types.d.ts +17 -1
- package/dist/components/calendar/index.js +80 -27
- package/dist/components/checkbox/checkbox.d.ts +14 -2
- package/dist/components/checkbox/checkbox.types.d.ts +5 -0
- package/dist/components/checkbox/index.js +82 -5
- package/dist/components/chip/chip.d.ts +4 -1
- package/dist/components/chip/chip.types.d.ts +5 -0
- package/dist/components/chip/index.js +29 -1
- package/dist/components/color-input/color-input.d.ts +11 -2
- package/dist/components/color-input/color-input.types.d.ts +5 -0
- package/dist/components/color-input/index.js +92 -8
- package/dist/components/combobox/combobox.d.ts +18 -2
- package/dist/components/combobox/combobox.types.d.ts +7 -0
- package/dist/components/combobox/index.js +164 -23
- package/dist/components/date-input/date-input.d.ts +22 -7
- package/dist/components/date-input/date-input.types.d.ts +7 -0
- package/dist/components/date-input/index.js +218 -55
- package/dist/components/datepicker/datepicker.d.ts +9 -1
- package/dist/components/datepicker/index.js +108 -15
- package/dist/components/dialog/dialog.d.ts +39 -19
- package/dist/components/dialog/dialog.types.d.ts +5 -0
- package/dist/components/dialog/index.js +240 -93
- package/dist/components/divider/index.js +2 -2
- package/dist/components/dropdown/dropdown-item.d.ts +1 -1
- package/dist/components/dropdown/dropdown.d.ts +4 -0
- package/dist/components/dropdown/index.js +43 -3
- package/dist/components/icon-button/icon-button.d.ts +4 -0
- package/dist/components/icon-button/index.js +39 -4
- package/dist/components/image/image.d.ts +7 -7
- package/dist/components/image/index.js +6 -11
- package/dist/components/list/index.js +14 -4
- package/dist/components/nav/index.js +37 -17
- package/dist/components/nav/nav-item.d.ts +8 -8
- package/dist/components/pagination/index.js +57 -16
- package/dist/components/pagination/pagination.d.ts +8 -1
- package/dist/components/pagination/pagination.types.d.ts +15 -0
- package/dist/components/progress/index.js +44 -6
- package/dist/components/progress/progress.d.ts +12 -0
- package/dist/components/radio/index.js +148 -9
- package/dist/components/radio/radio.d.ts +34 -2
- package/dist/components/radio/radio.types.d.ts +5 -0
- package/dist/components/radio-group/index.js +120 -29
- package/dist/components/radio-group/radio-group.d.ts +27 -2
- package/dist/components/select/index.js +197 -44
- package/dist/components/select/select.d.ts +38 -4
- package/dist/components/select/select.types.d.ts +5 -0
- package/dist/components/shared/aria-forwarding.d.ts +1 -1
- package/dist/components/shared/form-default.d.ts +30 -0
- package/dist/components/shared/form-disabled.d.ts +40 -0
- package/dist/components/shared/form-field.d.ts +7 -0
- package/dist/components/shared/form-validity.d.ts +17 -0
- package/dist/components/shared/roving.d.ts +11 -0
- package/dist/components/shared/scroll-lock.d.ts +2 -0
- package/dist/components/sidepanel/index.js +278 -75
- package/dist/components/sidepanel/sidepanel.d.ts +32 -10
- package/dist/components/sidepanel/sidepanel.types.d.ts +10 -1
- package/dist/components/skeleton/index.js +3 -8
- package/dist/components/slider/index.js +307 -75
- package/dist/components/slider/slider.d.ts +16 -3
- package/dist/components/snackbar/index.js +82 -9
- package/dist/components/snackbar/snackbar.d.ts +17 -1
- package/dist/components/snackbar/snackbar.types.d.ts +5 -0
- package/dist/components/surface/index.js +8 -0
- package/dist/components/switch/index.js +101 -5
- package/dist/components/switch/switch.d.ts +13 -2
- package/dist/components/switch/switch.types.d.ts +5 -0
- package/dist/components/tab/index.js +15 -14
- package/dist/components/tab/tab.d.ts +0 -1
- package/dist/components/tab-group/index.js +102 -30
- package/dist/components/tab-group/tab-group.d.ts +17 -2
- package/dist/components/table/index.js +105 -61
- package/dist/components/table/table.d.ts +8 -2
- package/dist/components/table/table.types.d.ts +4 -3
- package/dist/components/text/index.js +16 -1
- package/dist/components/text/text.d.ts +1 -1
- package/dist/components/textfield/index.js +243 -35
- package/dist/components/textfield/textfield.d.ts +24 -2
- package/dist/components/textfield/textfield.types.d.ts +11 -0
- package/dist/components/tooltip/index.js +176 -49
- package/dist/components/tooltip/tooltip.d.ts +17 -0
- package/dist/components/tooltip/tooltip.types.d.ts +5 -1
- package/dist/index.cjs +2467 -692
- package/dist/index.js +2478 -699
- package/dist/manifest.json +202 -32
- package/dist/utils/dev-warn.d.ts +5 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +4 -8
|
@@ -10,7 +10,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/components/button/button.ts
|
|
13
|
-
import { html as html2, nothing } from "lit";
|
|
13
|
+
import { html as html2, nothing, LitElement as LitElement2 } from "lit";
|
|
14
14
|
|
|
15
15
|
// src/base/themeable-element.ts
|
|
16
16
|
import { LitElement } from "lit";
|
|
@@ -37,6 +37,7 @@ __decorateClass([
|
|
|
37
37
|
// src/components/button/button.ts
|
|
38
38
|
import { customElement as customElement2, property as property3 } from "lit/decorators.js";
|
|
39
39
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
40
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
40
41
|
|
|
41
42
|
// src/components/button/button.styles.ts
|
|
42
43
|
import { css } from "lit";
|
|
@@ -77,6 +78,7 @@ var buttonStyles = css`
|
|
|
77
78
|
justify-content: center;
|
|
78
79
|
line-height: 1;
|
|
79
80
|
min-width: var(--lt-button-min-width, 88px);
|
|
81
|
+
position: relative;
|
|
80
82
|
user-select: none;
|
|
81
83
|
transition:
|
|
82
84
|
background-color 120ms ease,
|
|
@@ -84,6 +86,25 @@ var buttonStyles = css`
|
|
|
84
86
|
transform 50ms ease;
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
/* Wraps the icons/slot so it can be hidden (opacity, not display/visibility — that would drop it from the
|
|
90
|
+
accessibility tree too) while loading without collapsing the button's width or its accessible name. */
|
|
91
|
+
.label {
|
|
92
|
+
align-items: center;
|
|
93
|
+
display: inline-flex;
|
|
94
|
+
gap: inherit;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.label.is-loading {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Centered over the (invisible but still laid-out) label, not laid out alongside it. */
|
|
102
|
+
.spinner {
|
|
103
|
+
inset: 0;
|
|
104
|
+
margin: auto;
|
|
105
|
+
position: absolute;
|
|
106
|
+
}
|
|
107
|
+
|
|
87
108
|
[part='base']:active {
|
|
88
109
|
transform: translateY(1px);
|
|
89
110
|
}
|
|
@@ -139,10 +160,10 @@ var buttonStyles = css`
|
|
|
139
160
|
--_active-bg: var(--lt-interactive-secondary-bg-active);
|
|
140
161
|
}
|
|
141
162
|
:host([variant='neutral']) {
|
|
142
|
-
--_bg: var(--lt-
|
|
163
|
+
--_bg: var(--lt-interactive-neutral-bg);
|
|
143
164
|
--_color: var(--lt-color-neutral-50);
|
|
144
|
-
--_hover-bg: var(--lt-
|
|
145
|
-
--_active-bg: var(--lt-
|
|
165
|
+
--_hover-bg: var(--lt-interactive-neutral-bg-hover);
|
|
166
|
+
--_active-bg: var(--lt-interactive-neutral-bg-active);
|
|
146
167
|
}
|
|
147
168
|
:host([variant='success']) {
|
|
148
169
|
--_bg: var(--lt-interactive-success-bg);
|
|
@@ -329,6 +350,17 @@ var buttonStyles = css`
|
|
|
329
350
|
letter-spacing: 0.05rem;
|
|
330
351
|
text-transform: lowercase;
|
|
331
352
|
}
|
|
353
|
+
|
|
354
|
+
/* ── Reduced motion ─────────────────────────────────────────────────────── */
|
|
355
|
+
|
|
356
|
+
@media (prefers-reduced-motion: reduce) {
|
|
357
|
+
[part='base'] {
|
|
358
|
+
transition: none;
|
|
359
|
+
}
|
|
360
|
+
.spinner {
|
|
361
|
+
animation: none;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
332
364
|
`;
|
|
333
365
|
|
|
334
366
|
// src/components/shared/aria-forwarding.ts
|
|
@@ -337,7 +369,8 @@ var FORWARDED_ARIA_ATTRIBUTES = [
|
|
|
337
369
|
"aria-expanded",
|
|
338
370
|
"aria-haspopup",
|
|
339
371
|
"aria-controls",
|
|
340
|
-
"aria-current"
|
|
372
|
+
"aria-current",
|
|
373
|
+
"aria-description"
|
|
341
374
|
];
|
|
342
375
|
var AriaForwardingController = class {
|
|
343
376
|
constructor(host, getTarget) {
|
|
@@ -364,6 +397,29 @@ var AriaForwardingController = class {
|
|
|
364
397
|
}
|
|
365
398
|
};
|
|
366
399
|
|
|
400
|
+
// src/components/shared/form-disabled.ts
|
|
401
|
+
var FormDisabledController = class {
|
|
402
|
+
constructor(host) {
|
|
403
|
+
this._fieldsetDisabled = false;
|
|
404
|
+
this._host = host;
|
|
405
|
+
host.addController(this);
|
|
406
|
+
}
|
|
407
|
+
// No-op — this controller has no connect-time work of its own (formDisabledCallback drives it instead), but
|
|
408
|
+
// ReactiveController's members are all optional, and a class satisfying an all-optional interface with no
|
|
409
|
+
// members in common is a TS error (TS2559); this gives it one.
|
|
410
|
+
hostConnected() {
|
|
411
|
+
}
|
|
412
|
+
/** Whether an ancestor `<fieldset>` is currently disabled. */
|
|
413
|
+
get fieldsetDisabled() {
|
|
414
|
+
return this._fieldsetDisabled;
|
|
415
|
+
}
|
|
416
|
+
setFieldsetDisabled(disabled) {
|
|
417
|
+
if (this._fieldsetDisabled === disabled) return;
|
|
418
|
+
this._fieldsetDisabled = disabled;
|
|
419
|
+
this._host.requestUpdate();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
367
423
|
// src/components/spinner/spinner.ts
|
|
368
424
|
import { html } from "lit";
|
|
369
425
|
import { customElement, property as property2 } from "lit/decorators.js";
|
|
@@ -464,6 +520,13 @@ var Button = class extends ThemeableElement {
|
|
|
464
520
|
constructor() {
|
|
465
521
|
super();
|
|
466
522
|
this._ariaForwarding = new AriaForwardingController(this, () => this.shadowRoot?.querySelector('[part="base"]'));
|
|
523
|
+
this._fieldsetDisabled = new FormDisabledController(this);
|
|
524
|
+
this._blockClickWhileBusy = (e) => {
|
|
525
|
+
if (this.disabled || this.loading || this._fieldsetDisabled.fieldsetDisabled) {
|
|
526
|
+
e.preventDefault();
|
|
527
|
+
e.stopImmediatePropagation();
|
|
528
|
+
}
|
|
529
|
+
};
|
|
467
530
|
this.variant = "primary";
|
|
468
531
|
this.appearance = "filled";
|
|
469
532
|
this.bordered = false;
|
|
@@ -478,22 +541,69 @@ var Button = class extends ThemeableElement {
|
|
|
478
541
|
this.target = "";
|
|
479
542
|
this.rel = "";
|
|
480
543
|
this.type = "button";
|
|
544
|
+
this.name = "";
|
|
545
|
+
this.value = "";
|
|
546
|
+
this.formAction = "";
|
|
547
|
+
// Delegates the actual submission to a hidden native <button>, kept in sync and attached to the resolved form
|
|
548
|
+
// only for the duration of the submission (BTN-3). Two platform constraints make this necessary rather than
|
|
549
|
+
// calling form.requestSubmit() directly on the host or passing it as the submitter: a form-associated custom
|
|
550
|
+
// element can't itself be `requestSubmit()`'s submitter (Chromium: "the specified element is not a submit
|
|
551
|
+
// button"), and name/value/formaction submitter semantics only apply to the specific button that triggered the
|
|
552
|
+
// submission — setting this element's own form value unconditionally would submit name/value even when a
|
|
553
|
+
// *different* button (or an Enter keypress) triggered the submit. This is the same workaround other
|
|
554
|
+
// form-associated custom element implementations use for the same gap.
|
|
555
|
+
this._proxySubmitBtn = null;
|
|
481
556
|
this._internals = this.attachInternals();
|
|
482
557
|
}
|
|
558
|
+
connectedCallback() {
|
|
559
|
+
super.connectedCallback();
|
|
560
|
+
this.addEventListener("click", this._blockClickWhileBusy, { capture: true });
|
|
561
|
+
}
|
|
562
|
+
disconnectedCallback() {
|
|
563
|
+
super.disconnectedCallback();
|
|
564
|
+
this.removeEventListener("click", this._blockClickWhileBusy, { capture: true });
|
|
565
|
+
}
|
|
566
|
+
formDisabledCallback(disabled) {
|
|
567
|
+
this._fieldsetDisabled.setFieldsetDisabled(disabled);
|
|
568
|
+
}
|
|
569
|
+
_getProxySubmitBtn() {
|
|
570
|
+
if (!this._proxySubmitBtn) {
|
|
571
|
+
this._proxySubmitBtn = document.createElement("button");
|
|
572
|
+
this._proxySubmitBtn.type = "submit";
|
|
573
|
+
this._proxySubmitBtn.hidden = true;
|
|
574
|
+
}
|
|
575
|
+
return this._proxySubmitBtn;
|
|
576
|
+
}
|
|
483
577
|
_handleClick() {
|
|
484
|
-
if (this.disabled || this.loading) return;
|
|
485
|
-
const form = this.
|
|
578
|
+
if (this.disabled || this.loading || this._fieldsetDisabled.fieldsetDisabled) return;
|
|
579
|
+
const form = this._internals.form;
|
|
486
580
|
if (!form) return;
|
|
487
|
-
if (this.type === "
|
|
488
|
-
|
|
581
|
+
if (this.type === "reset") {
|
|
582
|
+
form.reset();
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (this.type !== "submit") return;
|
|
586
|
+
const proxy = this._getProxySubmitBtn();
|
|
587
|
+
if (this.name) proxy.name = this.name;
|
|
588
|
+
else proxy.removeAttribute("name");
|
|
589
|
+
if (this.value) proxy.value = this.value;
|
|
590
|
+
else proxy.removeAttribute("value");
|
|
591
|
+
if (this.formAction) proxy.formAction = this.formAction;
|
|
592
|
+
else proxy.removeAttribute("formaction");
|
|
593
|
+
form.appendChild(proxy);
|
|
594
|
+
form.requestSubmit(proxy);
|
|
595
|
+
proxy.remove();
|
|
489
596
|
}
|
|
490
597
|
render() {
|
|
491
|
-
const isDisabled = this.disabled || this.loading;
|
|
492
|
-
const inner =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
598
|
+
const isDisabled = this.disabled || this.loading || this._fieldsetDisabled.fieldsetDisabled;
|
|
599
|
+
const inner = html2`
|
|
600
|
+
<span class=${classMap({ label: true, "is-loading": this.loading })} part="label">
|
|
601
|
+
${this.iconStart ? html2`<lt-icon class="icon-start" name=${this.iconStart}></lt-icon>` : ""}
|
|
602
|
+
<slot></slot>
|
|
603
|
+
${this.iconEnd ? html2`<lt-icon class="icon-end" name=${this.iconEnd}></lt-icon>` : ""}
|
|
604
|
+
</span>
|
|
605
|
+
${this.loading ? html2`<lt-spinner class="spinner" part="spinner"></lt-spinner>` : ""}
|
|
606
|
+
`;
|
|
497
607
|
if (this.href) {
|
|
498
608
|
const rel = this.rel || (this.target === "_blank" ? "noopener noreferrer" : void 0);
|
|
499
609
|
return html2`
|
|
@@ -523,6 +633,10 @@ var Button = class extends ThemeableElement {
|
|
|
523
633
|
};
|
|
524
634
|
Button.styles = buttonStyles;
|
|
525
635
|
Button.formAssociated = true;
|
|
636
|
+
// Makes host.focus() move focus into the shadow root (the inner <button>/<a>) instead of being a no-op —
|
|
637
|
+
// without it, activeElement never leaves <body>, silently breaking autofocus and any focus restore that calls
|
|
638
|
+
// .focus() on this element (a closed dialog/dropdown returning focus to its trigger, for example).
|
|
639
|
+
Button.shadowRootOptions = { ...LitElement2.shadowRootOptions, delegatesFocus: true };
|
|
526
640
|
__decorateClass([
|
|
527
641
|
property3({ reflect: true })
|
|
528
642
|
], Button.prototype, "variant", 2);
|
|
@@ -565,6 +679,15 @@ __decorateClass([
|
|
|
565
679
|
__decorateClass([
|
|
566
680
|
property3({ reflect: true })
|
|
567
681
|
], Button.prototype, "type", 2);
|
|
682
|
+
__decorateClass([
|
|
683
|
+
property3({ reflect: true })
|
|
684
|
+
], Button.prototype, "name", 2);
|
|
685
|
+
__decorateClass([
|
|
686
|
+
property3({ reflect: true })
|
|
687
|
+
], Button.prototype, "value", 2);
|
|
688
|
+
__decorateClass([
|
|
689
|
+
property3({ attribute: "formaction" })
|
|
690
|
+
], Button.prototype, "formAction", 2);
|
|
568
691
|
Button = __decorateClass([
|
|
569
692
|
customElement2("lt-button")
|
|
570
693
|
], Button);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { type PropertyValues } from 'lit';
|
|
2
2
|
import { ThemeableElement } from '../../base';
|
|
3
3
|
import '@latty-ds/icons';
|
|
4
|
-
import type { CalendarMode, CalendarWeekStart } from './calendar.types';
|
|
4
|
+
import type { CalendarMode, CalendarWeekStart, CalendarLabels } from './calendar.types';
|
|
5
5
|
/**
|
|
6
6
|
* A visual calendar grid for single-date or date-range selection.
|
|
7
7
|
*
|
|
8
8
|
* @element lt-calendar
|
|
9
9
|
*
|
|
10
|
-
* @fires {CustomEvent<{value: string}>} change - Fired when a
|
|
11
|
-
*
|
|
12
|
-
* @fires {CustomEvent<{year: number, month: number}>}
|
|
10
|
+
* @fires {CustomEvent<{value: string} | {valueStart: string, valueEnd: string}>} change - Fired when a date is selected
|
|
11
|
+
* (mode="single", detail `{ value }`) or when a full range is selected (mode="range", detail `{ valueStart, valueEnd }`).
|
|
12
|
+
* @fires {CustomEvent<{year: number, month: number}>} monthchange - Fired when the viewed month changes.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```html
|
|
@@ -42,16 +42,24 @@ export declare class Calendar extends ThemeableElement {
|
|
|
42
42
|
max: string;
|
|
43
43
|
/** BCP 47 locale used for day and month names. */
|
|
44
44
|
locale: string;
|
|
45
|
-
/**
|
|
46
|
-
|
|
45
|
+
/**
|
|
46
|
+
* First day of the week (0 = Sunday … 6 = Saturday). Defaults to whatever `locale` implies (via
|
|
47
|
+
* `Intl.Locale#getWeekInfo`, falling back to Sunday) — set this explicitly only to override that.
|
|
48
|
+
*/
|
|
49
|
+
weekStart?: CalendarWeekStart;
|
|
50
|
+
/** `weekStart` if explicitly set, otherwise derived from `locale`. Internal code should always read this, never `weekStart` directly. */
|
|
51
|
+
private get _weekStart();
|
|
47
52
|
/** Whether to render filler days from the previous and next month. */
|
|
48
53
|
showOutsideDays: boolean;
|
|
49
54
|
/** Disables all interaction. */
|
|
50
55
|
disabled: boolean;
|
|
51
56
|
/** Number of month panels to display side-by-side. */
|
|
52
57
|
months: number;
|
|
53
|
-
/** Specific dates to disable, as an array of Date objects.
|
|
58
|
+
/** Specific dates to disable, as an array of Date objects. Set as a JS property (`.disabledDates`). */
|
|
54
59
|
disabledDates: Date[];
|
|
60
|
+
/** Overridable UI text (nav button labels, the "Today" footer button, the month/year picker toggle), for i18n. English defaults — override only the keys you need. */
|
|
61
|
+
labels: Partial<CalendarLabels>;
|
|
62
|
+
private get _labels();
|
|
55
63
|
private _viewYear;
|
|
56
64
|
private _viewMonth;
|
|
57
65
|
private _focusedDate;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** First day of the week, matching `Date#getDay()`'s convention: 0 = Sunday, 1 = Monday, … 6 = Saturday. */
|
|
2
|
+
export type CalendarWeekStart = '0' | '1' | '2' | '3' | '4' | '5' | '6';
|
|
2
3
|
export type CalendarMode = 'single' | 'range';
|
|
3
4
|
export interface CalendarDay {
|
|
4
5
|
date: Date;
|
|
@@ -13,3 +14,18 @@ export interface CalendarDay {
|
|
|
13
14
|
isRangeHoverEnd: boolean;
|
|
14
15
|
isInHoverRange: boolean;
|
|
15
16
|
}
|
|
17
|
+
/** Overridable UI text for `lt-calendar`, for i18n. */
|
|
18
|
+
export interface CalendarLabels {
|
|
19
|
+
/** Text of the footer button that jumps the view back to today. */
|
|
20
|
+
today: string;
|
|
21
|
+
/** Accessible label for the "view previous month" button. */
|
|
22
|
+
previousMonth: string;
|
|
23
|
+
/** Accessible label for the "view next month" button. */
|
|
24
|
+
nextMonth: string;
|
|
25
|
+
/** Accessible label for the "view previous year" button, shown while picking a month/year (single-month layout only). */
|
|
26
|
+
previousYear: string;
|
|
27
|
+
/** Accessible label for the "view next year" button, shown while picking a month/year (single-month layout only). */
|
|
28
|
+
nextYear: string;
|
|
29
|
+
/** Accessible label for the month/year picker toggle button (single-month layout only), given its current visible text. */
|
|
30
|
+
chooseMonthYear: (monthYear: string) => string;
|
|
31
|
+
}
|
|
@@ -10,7 +10,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/components/calendar/calendar.ts
|
|
13
|
-
import { html } from "lit";
|
|
13
|
+
import { html, nothing } from "lit";
|
|
14
14
|
import { customElement, property as property2, state } from "lit/decorators.js";
|
|
15
15
|
|
|
16
16
|
// src/base/themeable-element.ts
|
|
@@ -184,7 +184,9 @@ var calendarStyles = css`
|
|
|
184
184
|
width: 100%;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
/* aria-disabled, not the disabled attribute — a disabled day stays focusable, so keyboard users can arrow
|
|
188
|
+
through it instead of the roving tabindex skipping straight over it (it just can't be selected). */
|
|
189
|
+
.day[aria-disabled='true'] {
|
|
188
190
|
cursor: not-allowed;
|
|
189
191
|
}
|
|
190
192
|
|
|
@@ -207,7 +209,7 @@ var calendarStyles = css`
|
|
|
207
209
|
width: 2.25rem;
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
.day:hover:not(
|
|
212
|
+
.day:hover:not([aria-disabled='true']) .day__inner {
|
|
211
213
|
background: var(--lt-bg-neutral-subtle);
|
|
212
214
|
}
|
|
213
215
|
|
|
@@ -230,14 +232,14 @@ var calendarStyles = css`
|
|
|
230
232
|
font-weight: 600;
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
.day--selected:hover:not(
|
|
235
|
+
.day--selected:hover:not([aria-disabled='true']) .day__inner {
|
|
234
236
|
background: var(--lt-interactive-primary-bg-hover);
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
/* Outside days
|
|
239
|
+
/* Outside days — dimmed by the muted text color alone. They're enabled, clickable buttons, and a 0.5
|
|
240
|
+
opacity on top of muted text took them to ~2:1 (WCAG AA needs 4.5:1). */
|
|
238
241
|
.day--outside {
|
|
239
242
|
color: var(--lt-text-muted);
|
|
240
|
-
opacity: 0.5;
|
|
241
243
|
}
|
|
242
244
|
|
|
243
245
|
/* Empty filler cell */
|
|
@@ -248,7 +250,7 @@ var calendarStyles = css`
|
|
|
248
250
|
pointer-events: none;
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
.day
|
|
253
|
+
.day[aria-disabled='true'] .day__inner {
|
|
252
254
|
color: var(--lt-text-disabled);
|
|
253
255
|
opacity: 0.4;
|
|
254
256
|
}
|
|
@@ -275,7 +277,7 @@ var calendarStyles = css`
|
|
|
275
277
|
background: var(--lt-bg-primary-subtle);
|
|
276
278
|
}
|
|
277
279
|
|
|
278
|
-
.day--in-range:hover:not(
|
|
280
|
+
.day--in-range:hover:not([aria-disabled='true']) .day__inner {
|
|
279
281
|
background: var(--lt-bg-neutral-subtle);
|
|
280
282
|
}
|
|
281
283
|
|
|
@@ -293,7 +295,7 @@ var calendarStyles = css`
|
|
|
293
295
|
font-weight: 600;
|
|
294
296
|
}
|
|
295
297
|
|
|
296
|
-
.day--range-confirmed:hover:not(
|
|
298
|
+
.day--range-confirmed:hover:not([aria-disabled='true']) .day__inner {
|
|
297
299
|
background: var(--lt-interactive-primary-bg-hover);
|
|
298
300
|
}
|
|
299
301
|
|
|
@@ -416,12 +418,42 @@ var calendarStyles = css`
|
|
|
416
418
|
cursor: not-allowed;
|
|
417
419
|
opacity: 0.5;
|
|
418
420
|
}
|
|
421
|
+
|
|
422
|
+
/* ── Reduced motion ─────────────────────────────────────────────────────── */
|
|
423
|
+
|
|
424
|
+
@media (prefers-reduced-motion: reduce) {
|
|
425
|
+
.month-label-btn,
|
|
426
|
+
.picker-chevron,
|
|
427
|
+
.nav-btn,
|
|
428
|
+
.day,
|
|
429
|
+
.day__inner,
|
|
430
|
+
.picker-month-btn,
|
|
431
|
+
.today-btn {
|
|
432
|
+
transition: none;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
419
435
|
`;
|
|
420
436
|
|
|
421
437
|
// src/utils/dispatch.ts
|
|
422
438
|
var dispatch = (host, name, detail, init) => host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true, ...init }));
|
|
423
439
|
|
|
424
440
|
// src/components/calendar/calendar.ts
|
|
441
|
+
var DEFAULT_LABELS = {
|
|
442
|
+
today: "Today",
|
|
443
|
+
previousMonth: "Previous month",
|
|
444
|
+
nextMonth: "Next month",
|
|
445
|
+
previousYear: "Previous year",
|
|
446
|
+
nextYear: "Next year",
|
|
447
|
+
chooseMonthYear: (monthYear) => `${monthYear}, choose month and year`
|
|
448
|
+
};
|
|
449
|
+
function deriveWeekStart(locale) {
|
|
450
|
+
try {
|
|
451
|
+
const info = new Intl.Locale(locale).getWeekInfo?.();
|
|
452
|
+
if (info) return String(info.firstDay % 7);
|
|
453
|
+
} catch {
|
|
454
|
+
}
|
|
455
|
+
return "0";
|
|
456
|
+
}
|
|
425
457
|
var Calendar = class extends ThemeableElement {
|
|
426
458
|
constructor() {
|
|
427
459
|
super(...arguments);
|
|
@@ -432,12 +464,11 @@ var Calendar = class extends ThemeableElement {
|
|
|
432
464
|
this.min = "";
|
|
433
465
|
this.max = "";
|
|
434
466
|
this.locale = "en-US";
|
|
435
|
-
this.weekStart = "0";
|
|
436
467
|
this.showOutsideDays = true;
|
|
437
468
|
this.disabled = false;
|
|
438
469
|
this.months = 1;
|
|
439
|
-
/** Specific dates to disable, as an array of Date objects. Not reflected as an attribute. */
|
|
440
470
|
this.disabledDates = [];
|
|
471
|
+
this.labels = {};
|
|
441
472
|
this._viewYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
442
473
|
this._viewMonth = (/* @__PURE__ */ new Date()).getMonth();
|
|
443
474
|
this._focusedDate = null;
|
|
@@ -464,10 +495,10 @@ var Calendar = class extends ThemeableElement {
|
|
|
464
495
|
if (this.mode === "range") this._hoverDate = null;
|
|
465
496
|
};
|
|
466
497
|
this._handleKeyDown = (e) => {
|
|
467
|
-
const
|
|
468
|
-
const focusedDate = this.
|
|
498
|
+
const currentIso = e.currentTarget.dataset.date ?? "";
|
|
499
|
+
const focusedDate = this._parseDate(currentIso) ?? new Date(this._viewYear, this._viewMonth, 1);
|
|
469
500
|
const next = new Date(focusedDate);
|
|
470
|
-
const weekStart = Number(this.
|
|
501
|
+
const weekStart = Number(this._weekStart);
|
|
471
502
|
switch (e.key) {
|
|
472
503
|
case "ArrowRight":
|
|
473
504
|
next.setDate(next.getDate() + 1);
|
|
@@ -519,6 +550,13 @@ var Calendar = class extends ThemeableElement {
|
|
|
519
550
|
this._focusCell(this._toIso(next));
|
|
520
551
|
};
|
|
521
552
|
}
|
|
553
|
+
/** `weekStart` if explicitly set, otherwise derived from `locale`. Internal code should always read this, never `weekStart` directly. */
|
|
554
|
+
get _weekStart() {
|
|
555
|
+
return this.weekStart ?? deriveWeekStart(this.locale);
|
|
556
|
+
}
|
|
557
|
+
get _labels() {
|
|
558
|
+
return { ...DEFAULT_LABELS, ...this.labels };
|
|
559
|
+
}
|
|
522
560
|
willUpdate(changed) {
|
|
523
561
|
super.willUpdate(changed);
|
|
524
562
|
if (changed.has("value") && this.value) {
|
|
@@ -577,7 +615,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
577
615
|
_buildDays(year = this._viewYear, month = this._viewMonth) {
|
|
578
616
|
const today = /* @__PURE__ */ new Date();
|
|
579
617
|
const selected = this._parseDate(this.value);
|
|
580
|
-
const weekStart = Number(this.
|
|
618
|
+
const weekStart = Number(this._weekStart);
|
|
581
619
|
const rangeStart = this.mode === "range" ? this._parseDate(this.valueStart) : null;
|
|
582
620
|
const rangeEnd = this.mode === "range" ? this._parseDate(this.valueEnd) : null;
|
|
583
621
|
let visHoverStart = null;
|
|
@@ -630,7 +668,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
630
668
|
return days;
|
|
631
669
|
}
|
|
632
670
|
_getWeekdayLabels() {
|
|
633
|
-
const weekStart = Number(this.
|
|
671
|
+
const weekStart = Number(this._weekStart);
|
|
634
672
|
const fmt = new Intl.DateTimeFormat(this.locale, { weekday: "narrow" });
|
|
635
673
|
const refSunday = new Date(2026, 0, 4);
|
|
636
674
|
return Array.from({ length: 7 }, (_, i) => {
|
|
@@ -657,7 +695,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
657
695
|
this._viewMonth = month;
|
|
658
696
|
this._viewYear = year;
|
|
659
697
|
this._focusedDate = null;
|
|
660
|
-
dispatch(this, "
|
|
698
|
+
dispatch(this, "monthchange", { year: this._viewYear, month: this._viewMonth });
|
|
661
699
|
}
|
|
662
700
|
_openPicker() {
|
|
663
701
|
this._pickerYear = this._viewYear;
|
|
@@ -671,7 +709,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
671
709
|
this._viewMonth = month;
|
|
672
710
|
this._pickingMonthYear = false;
|
|
673
711
|
this._focusedDate = null;
|
|
674
|
-
dispatch(this, "
|
|
712
|
+
dispatch(this, "monthchange", { year: this._viewYear, month: this._viewMonth });
|
|
675
713
|
}
|
|
676
714
|
_selectDay(day) {
|
|
677
715
|
if (day.isDisabled || this.disabled) return;
|
|
@@ -757,7 +795,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
757
795
|
role="gridcell"
|
|
758
796
|
class=${classes}
|
|
759
797
|
data-date=${iso}
|
|
760
|
-
|
|
798
|
+
aria-disabled=${day.isDisabled ? "true" : nothing}
|
|
761
799
|
aria-selected=${isSelected ? "true" : "false"}
|
|
762
800
|
aria-label=${this._dayAriaLabel(day.date)}
|
|
763
801
|
tabindex=${this._isSameDay(day.date, rovingDate) ? 0 : -1}
|
|
@@ -791,11 +829,19 @@ var Calendar = class extends ThemeableElement {
|
|
|
791
829
|
render() {
|
|
792
830
|
const today = /* @__PURE__ */ new Date();
|
|
793
831
|
const isMulti = this.months > 1;
|
|
832
|
+
const isInView = (date) => {
|
|
833
|
+
if (!isMulti) return date.getFullYear() === this._viewYear && date.getMonth() === this._viewMonth;
|
|
834
|
+
for (let i = 0; i < this.months; i++) {
|
|
835
|
+
const { year, month } = this._monthAtOffset(i);
|
|
836
|
+
if (date.getFullYear() === year && date.getMonth() === month) return true;
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
};
|
|
794
840
|
const rovingAnchor = this._parseDate(this.mode === "range" ? this.valueStart : this.value);
|
|
795
|
-
const rovingDate = this._focusedDate ?? rovingAnchor ?? (today.getFullYear() === this._viewYear && today.getMonth() === this._viewMonth ? today : null) ?? new Date(this._viewYear, this._viewMonth, 1);
|
|
841
|
+
const rovingDate = (this._focusedDate && isInView(this._focusedDate) ? this._focusedDate : null) ?? (rovingAnchor && isInView(rovingAnchor) ? rovingAnchor : null) ?? (today.getFullYear() === this._viewYear && today.getMonth() === this._viewMonth ? today : null) ?? new Date(this._viewYear, this._viewMonth, 1);
|
|
796
842
|
const footer = html`
|
|
797
843
|
<div class="footer">
|
|
798
|
-
<button class="today-btn" ?disabled=${this.disabled} @click=${this._goToToday}
|
|
844
|
+
<button class="today-btn" ?disabled=${this.disabled} @click=${this._goToToday}>${this._labels.today}</button>
|
|
799
845
|
</div>
|
|
800
846
|
`;
|
|
801
847
|
if (isMulti) {
|
|
@@ -808,7 +854,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
808
854
|
<div class="header">
|
|
809
855
|
<button
|
|
810
856
|
class="nav-btn"
|
|
811
|
-
aria-label
|
|
857
|
+
aria-label=${this._labels.previousMonth}
|
|
812
858
|
?disabled=${this.disabled}
|
|
813
859
|
@click=${() => this._navigate(-1)}
|
|
814
860
|
>
|
|
@@ -823,7 +869,7 @@ var Calendar = class extends ThemeableElement {
|
|
|
823
869
|
</div>
|
|
824
870
|
<button
|
|
825
871
|
class="nav-btn"
|
|
826
|
-
aria-label
|
|
872
|
+
aria-label=${this._labels.nextMonth}
|
|
827
873
|
?disabled=${this.disabled}
|
|
828
874
|
@click=${() => this._navigate(1)}
|
|
829
875
|
>
|
|
@@ -852,8 +898,9 @@ var Calendar = class extends ThemeableElement {
|
|
|
852
898
|
const monthLabel = new Intl.DateTimeFormat(this.locale, { month: "long", year: "numeric" }).format(
|
|
853
899
|
new Date(this._viewYear, this._viewMonth)
|
|
854
900
|
);
|
|
855
|
-
const prevLabel = this._pickingMonthYear ?
|
|
856
|
-
const nextLabel = this._pickingMonthYear ?
|
|
901
|
+
const prevLabel = this._pickingMonthYear ? this._labels.previousYear : this._labels.previousMonth;
|
|
902
|
+
const nextLabel = this._pickingMonthYear ? this._labels.nextYear : this._labels.nextMonth;
|
|
903
|
+
const monthYearVisibleText = this._pickingMonthYear ? String(this._pickerYear) : monthLabel;
|
|
857
904
|
const header = html`
|
|
858
905
|
<div class="header">
|
|
859
906
|
<button class="nav-btn" aria-label=${prevLabel} ?disabled=${this.disabled} @click=${() => this._navigate(-1)}>
|
|
@@ -862,11 +909,11 @@ var Calendar = class extends ThemeableElement {
|
|
|
862
909
|
<button
|
|
863
910
|
class="month-label-btn ${this._pickingMonthYear ? "month-label-btn--open" : ""}"
|
|
864
911
|
aria-expanded=${this._pickingMonthYear ? "true" : "false"}
|
|
865
|
-
aria-label
|
|
912
|
+
aria-label=${this._labels.chooseMonthYear(monthYearVisibleText)}
|
|
866
913
|
?disabled=${this.disabled}
|
|
867
914
|
@click=${() => this._pickingMonthYear ? this._closePicker() : this._openPicker()}
|
|
868
915
|
>
|
|
869
|
-
<span aria-live="polite" aria-atomic="true">${
|
|
916
|
+
<span aria-live="polite" aria-atomic="true">${monthYearVisibleText}</span>
|
|
870
917
|
<lt-icon name="arrow-down" size="sm" class="picker-chevron"></lt-icon>
|
|
871
918
|
</button>
|
|
872
919
|
<button class="nav-btn" aria-label=${nextLabel} ?disabled=${this.disabled} @click=${() => this._navigate(1)}>
|
|
@@ -932,6 +979,12 @@ __decorateClass([
|
|
|
932
979
|
__decorateClass([
|
|
933
980
|
property2({ type: Number, reflect: true })
|
|
934
981
|
], Calendar.prototype, "months", 2);
|
|
982
|
+
__decorateClass([
|
|
983
|
+
property2({ attribute: false })
|
|
984
|
+
], Calendar.prototype, "disabledDates", 2);
|
|
985
|
+
__decorateClass([
|
|
986
|
+
property2({ type: Object })
|
|
987
|
+
], Calendar.prototype, "labels", 2);
|
|
935
988
|
__decorateClass([
|
|
936
989
|
state()
|
|
937
990
|
], Calendar.prototype, "_viewYear", 2);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ThemeableElement } from '../../base';
|
|
2
|
-
import { CheckboxSize, CheckboxVariant, CheckboxLabelPosition } from './checkbox.types';
|
|
2
|
+
import { CheckboxSize, CheckboxVariant, CheckboxLabelPosition, type CheckboxLabels } from './checkbox.types';
|
|
3
3
|
import '@latty-ds/icons';
|
|
4
4
|
/**
|
|
5
5
|
* A customizable checkbox component with support for multiple variants and sizes.
|
|
@@ -35,6 +35,8 @@ export declare class Checkbox extends ThemeableElement {
|
|
|
35
35
|
static styles: import("lit").CSSResult;
|
|
36
36
|
static formAssociated: boolean;
|
|
37
37
|
private _internals;
|
|
38
|
+
private _default;
|
|
39
|
+
private _fieldsetDisabled;
|
|
38
40
|
constructor();
|
|
39
41
|
/**
|
|
40
42
|
* Visual variant that determines the color when checked.
|
|
@@ -47,7 +49,10 @@ export declare class Checkbox extends ThemeableElement {
|
|
|
47
49
|
*/
|
|
48
50
|
size: CheckboxSize;
|
|
49
51
|
/**
|
|
50
|
-
* Whether the checkbox is checked.
|
|
52
|
+
* Whether the checkbox is checked. Not reflected as an attribute — a native checkbox's `checked` _content_
|
|
53
|
+
* attribute stays fixed at the markup default and never tracks interaction (that's what `defaultChecked` is
|
|
54
|
+
* for); reflecting it live meant `formResetCallback` could no longer tell what the original default was. Read
|
|
55
|
+
* the property, or the `change` event's `detail.checked`, instead of `[checked]`/`hasAttribute('checked')`.
|
|
51
56
|
* @default false
|
|
52
57
|
*/
|
|
53
58
|
checked: boolean;
|
|
@@ -87,6 +92,9 @@ export declare class Checkbox extends ThemeableElement {
|
|
|
87
92
|
* @default 'on'
|
|
88
93
|
*/
|
|
89
94
|
value: string;
|
|
95
|
+
/** Overridable UI text (the required-field validation message), for i18n. English defaults — override only the keys you need. */
|
|
96
|
+
labels: Partial<CheckboxLabels>;
|
|
97
|
+
private get _labels();
|
|
90
98
|
/**
|
|
91
99
|
* Reference to the native input element.
|
|
92
100
|
* @private
|
|
@@ -94,6 +102,9 @@ export declare class Checkbox extends ThemeableElement {
|
|
|
94
102
|
private input;
|
|
95
103
|
updated(changedProperties: Map<string, unknown>): void;
|
|
96
104
|
formResetCallback(): void;
|
|
105
|
+
formDisabledCallback(disabled: boolean): void;
|
|
106
|
+
/** `this.disabled` combined with an ancestor `<fieldset>`'s own disabled state (see `FormDisabledController`). */
|
|
107
|
+
private get _isDisabled();
|
|
97
108
|
checkValidity(): boolean;
|
|
98
109
|
reportValidity(): boolean;
|
|
99
110
|
/**
|
|
@@ -111,5 +122,6 @@ export declare class Checkbox extends ThemeableElement {
|
|
|
111
122
|
* @private
|
|
112
123
|
*/
|
|
113
124
|
private renderCheckmark;
|
|
125
|
+
protected firstUpdated(): void;
|
|
114
126
|
render(): import("lit").TemplateResult<1>;
|
|
115
127
|
}
|
|
@@ -19,3 +19,8 @@ export type CheckboxSize = 'sm' | 'md' | 'lg';
|
|
|
19
19
|
*/
|
|
20
20
|
export type CheckboxVariant = 'primary' | 'secondary' | 'success' | 'error' | 'info';
|
|
21
21
|
export type CheckboxLabelPosition = 'left' | 'right';
|
|
22
|
+
/** Overridable UI text for `lt-checkbox`, for i18n. */
|
|
23
|
+
export interface CheckboxLabels {
|
|
24
|
+
/** Validation message shown when a required checkbox is left unchecked. */
|
|
25
|
+
validationMessage: string;
|
|
26
|
+
}
|