@aceshooting/lyra-ui 1.2.0 → 2.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/README.md +11 -0
- package/custom-elements.json +23341 -11202
- package/dist/components/app-rail/app-rail.d.ts +171 -0
- package/dist/components/app-rail/app-rail.js +483 -0
- package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail.styles.js +125 -0
- package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
- package/dist/components/attachment-chip/attachment-chip.js +312 -0
- package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
- package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
- package/dist/components/chart/box-plot.js +11 -1
- package/dist/components/chart/chart-loader.d.ts +44 -13
- package/dist/components/chart/chart-loader.js +65 -18
- package/dist/components/chart/chart.d.ts +1 -2
- package/dist/components/chart/chart.js +67 -12
- package/dist/components/chart/histogram-bin.d.ts +4 -1
- package/dist/components/chart/histogram-bin.js +24 -14
- package/dist/components/chart/lite-chart.d.ts +54 -0
- package/dist/components/chart/lite-chart.js +140 -22
- package/dist/components/chart/lite-chart.styles.js +9 -0
- package/dist/components/chat-composer/chat-composer.d.ts +92 -0
- package/dist/components/chat-composer/chat-composer.js +285 -0
- package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
- package/dist/components/chat-composer/chat-composer.styles.js +135 -0
- package/dist/components/chat-message/chat-message.d.ts +113 -0
- package/dist/components/chat-message/chat-message.js +291 -0
- package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
- package/dist/components/chat-message/chat-message.styles.js +203 -0
- package/dist/components/checkbox/checkbox.d.ts +59 -0
- package/dist/components/checkbox/checkbox.js +230 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +70 -0
- package/dist/components/chip/chip-group.d.ts +56 -0
- package/dist/components/chip/chip-group.js +124 -0
- package/dist/components/chip/chip-group.styles.d.ts +1 -0
- package/dist/components/chip/chip-group.styles.js +57 -0
- package/dist/components/chip/chip.d.ts +64 -0
- package/dist/components/chip/chip.js +115 -0
- package/dist/components/chip/chip.styles.d.ts +1 -0
- package/dist/components/chip/chip.styles.js +115 -0
- package/dist/components/citation-badge/citation-badge.d.ts +113 -0
- package/dist/components/citation-badge/citation-badge.js +300 -0
- package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
- package/dist/components/citation-badge/citation-badge.styles.js +96 -0
- package/dist/components/code-block/code-block.d.ts +89 -0
- package/dist/components/code-block/code-block.js +317 -0
- package/dist/components/code-block/code-block.styles.d.ts +1 -0
- package/dist/components/code-block/code-block.styles.js +187 -0
- package/dist/components/code-block/code-loader.d.ts +44 -0
- package/dist/components/code-block/code-loader.js +79 -0
- package/dist/components/combobox/combobox.d.ts +19 -1
- package/dist/components/combobox/combobox.js +84 -14
- package/dist/components/combobox/option.d.ts +5 -1
- package/dist/components/combobox/option.js +35 -0
- package/dist/components/context-meter/context-meter.d.ts +53 -0
- package/dist/components/context-meter/context-meter.js +150 -0
- package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
- package/dist/components/context-meter/context-meter.styles.js +120 -0
- package/dist/components/conversation-item/conversation-item.d.ts +128 -0
- package/dist/components/conversation-item/conversation-item.js +322 -0
- package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
- package/dist/components/conversation-item/conversation-item.styles.js +155 -0
- package/dist/components/date-picker/date-input.d.ts +24 -0
- package/dist/components/date-picker/date-input.js +134 -22
- package/dist/components/date-picker/date-picker.d.ts +7 -0
- package/dist/components/date-picker/date-picker.js +85 -16
- package/dist/components/dialog/confirm.d.ts +40 -0
- package/dist/components/dialog/confirm.js +79 -0
- package/dist/components/dialog/dialog.d.ts +85 -0
- package/dist/components/dialog/dialog.js +272 -0
- package/dist/components/dialog/dialog.styles.d.ts +1 -0
- package/dist/components/dialog/dialog.styles.js +52 -0
- package/dist/components/dock-panel/dock-panel.d.ts +134 -0
- package/dist/components/dock-panel/dock-panel.js +345 -0
- package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
- package/dist/components/dock-panel/dock-panel.styles.js +145 -0
- package/dist/components/document-preview/document-preview.d.ts +135 -0
- package/dist/components/document-preview/document-preview.js +372 -0
- package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
- package/dist/components/document-preview/document-preview.styles.js +191 -0
- package/dist/components/export-button/csv.js +1 -1
- package/dist/components/export-button/export-button.js +2 -0
- package/dist/components/flag/flag.d.ts +29 -13
- package/dist/components/flag/flag.js +39 -13
- package/dist/components/gauge/gauge.js +19 -10
- package/dist/components/generation-status/generation-status.d.ts +109 -0
- package/dist/components/generation-status/generation-status.js +273 -0
- package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
- package/dist/components/generation-status/generation-status.styles.js +84 -0
- package/dist/components/heatmap/calendar-grid.d.ts +4 -1
- package/dist/components/heatmap/calendar-grid.js +25 -12
- package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
- package/dist/components/heatmap/heatmap-scale.js +7 -1
- package/dist/components/heatmap/heatmap.d.ts +74 -0
- package/dist/components/heatmap/heatmap.js +135 -23
- package/dist/components/json-viewer/json-viewer.d.ts +53 -0
- package/dist/components/json-viewer/json-viewer.js +283 -0
- package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
- package/dist/components/json-viewer/json-viewer.styles.js +176 -0
- package/dist/components/kbd/kbd.d.ts +77 -0
- package/dist/components/kbd/kbd.js +214 -0
- package/dist/components/kbd/kbd.styles.d.ts +1 -0
- package/dist/components/kbd/kbd.styles.js +52 -0
- package/dist/components/live-region/live-region.d.ts +81 -0
- package/dist/components/live-region/live-region.js +153 -0
- package/dist/components/live-region/live-region.styles.d.ts +1 -0
- package/dist/components/live-region/live-region.styles.js +10 -0
- package/dist/components/map/map.d.ts +8 -6
- package/dist/components/map/map.js +44 -8
- package/dist/components/markdown/markdown-loader.d.ts +34 -0
- package/dist/components/markdown/markdown-loader.js +43 -0
- package/dist/components/markdown/markdown.d.ts +81 -0
- package/dist/components/markdown/markdown.js +284 -0
- package/dist/components/markdown/markdown.styles.d.ts +1 -0
- package/dist/components/markdown/markdown.styles.js +82 -0
- package/dist/components/media-card/media-card.d.ts +122 -0
- package/dist/components/media-card/media-card.js +295 -0
- package/dist/components/media-card/media-card.styles.d.ts +1 -0
- package/dist/components/media-card/media-card.styles.js +132 -0
- package/dist/components/mention-popover/mention-popover.d.ts +200 -0
- package/dist/components/mention-popover/mention-popover.js +475 -0
- package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
- package/dist/components/mention-popover/mention-popover.styles.js +101 -0
- package/dist/components/menu/menu-item.d.ts +62 -0
- package/dist/components/menu/menu-item.js +119 -0
- package/dist/components/menu/menu-item.styles.d.ts +1 -0
- package/dist/components/menu/menu-item.styles.js +67 -0
- package/dist/components/menu/menu.d.ts +143 -0
- package/dist/components/menu/menu.js +365 -0
- package/dist/components/menu/menu.styles.d.ts +1 -0
- package/dist/components/menu/menu.styles.js +66 -0
- package/dist/components/model-select/model-select.d.ts +125 -0
- package/dist/components/model-select/model-select.js +490 -0
- package/dist/components/model-select/model-select.styles.d.ts +1 -0
- package/dist/components/model-select/model-select.styles.js +189 -0
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
- package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
- package/dist/components/playback/playback.d.ts +13 -1
- package/dist/components/playback/playback.js +47 -14
- package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
- package/dist/components/responsive-panel/responsive-panel.js +365 -0
- package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
- package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
- package/dist/components/result-card/result-card.d.ts +44 -0
- package/dist/components/result-card/result-card.js +83 -0
- package/dist/components/result-card/result-card.styles.d.ts +1 -0
- package/dist/components/result-card/result-card.styles.js +50 -0
- package/dist/components/result-card/result-field.d.ts +36 -0
- package/dist/components/result-card/result-field.js +87 -0
- package/dist/components/result-card/result-field.styles.d.ts +1 -0
- package/dist/components/result-card/result-field.styles.js +32 -0
- package/dist/components/select/select.d.ts +43 -2
- package/dist/components/select/select.js +150 -17
- package/dist/components/slider/slider.d.ts +89 -0
- package/dist/components/slider/slider.js +352 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +87 -0
- package/dist/components/source-card/source-card.d.ts +92 -0
- package/dist/components/source-card/source-card.js +148 -0
- package/dist/components/source-card/source-card.styles.d.ts +1 -0
- package/dist/components/source-card/source-card.styles.js +69 -0
- package/dist/components/source-list/source-list.d.ts +63 -0
- package/dist/components/source-list/source-list.js +122 -0
- package/dist/components/source-list/source-list.styles.d.ts +1 -0
- package/dist/components/source-list/source-list.styles.js +59 -0
- package/dist/components/sparkline/sparkline.js +27 -13
- package/dist/components/split/split.d.ts +93 -1
- package/dist/components/split/split.js +284 -15
- package/dist/components/split/split.styles.js +30 -0
- package/dist/components/stat/stat.d.ts +6 -1
- package/dist/components/stat/stat.js +8 -2
- package/dist/components/stream-status/stream-status.d.ts +110 -0
- package/dist/components/stream-status/stream-status.js +210 -0
- package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
- package/dist/components/stream-status/stream-status.styles.js +103 -0
- package/dist/components/streaming-text/streaming-text.d.ts +98 -0
- package/dist/components/streaming-text/streaming-text.js +209 -0
- package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
- package/dist/components/streaming-text/streaming-text.styles.js +62 -0
- package/dist/components/switch/switch.d.ts +58 -0
- package/dist/components/switch/switch.js +179 -0
- package/dist/components/switch/switch.styles.d.ts +1 -0
- package/dist/components/switch/switch.styles.js +75 -0
- package/dist/components/table/table.d.ts +73 -4
- package/dist/components/table/table.js +169 -20
- package/dist/components/table/table.styles.js +6 -1
- package/dist/components/tabs/tabs.d.ts +51 -0
- package/dist/components/tabs/tabs.js +190 -0
- package/dist/components/tabs/tabs.styles.d.ts +1 -0
- package/dist/components/tabs/tabs.styles.js +63 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
- package/dist/components/thinking-panel/thinking-panel.js +235 -0
- package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
- package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
- package/dist/components/toast/toast-item.d.ts +1 -0
- package/dist/components/toast/toast-item.js +31 -6
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
- package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
- package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
- package/dist/components/tool-param-form/tool-param-form.js +366 -0
- package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
- package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
- package/dist/components/tool-result-view/registry.d.ts +81 -0
- package/dist/components/tool-result-view/registry.js +84 -0
- package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
- package/dist/components/tool-result-view/tool-result-view.js +138 -0
- package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
- package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
- package/dist/components/tree/tree-node.d.ts +11 -2
- package/dist/components/tree/tree-node.js +16 -6
- package/dist/components/tree/tree.d.ts +8 -2
- package/dist/components/tree/tree.js +18 -11
- package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
- package/dist/components/typing-indicator/typing-indicator.js +96 -0
- package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
- package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
- package/dist/components/virtual-list/virtual-list.d.ts +173 -0
- package/dist/components/virtual-list/virtual-list.js +461 -0
- package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
- package/dist/components/virtual-list/virtual-list.styles.js +44 -0
- package/dist/components/word-cloud/word-cloud-layout.js +14 -1
- package/dist/components/word-cloud/word-cloud.d.ts +6 -0
- package/dist/components/word-cloud/word-cloud.js +25 -14
- package/dist/components/word-cloud/word-cloud.styles.js +3 -2
- package/dist/internal/announcer.d.ts +57 -0
- package/dist/internal/announcer.js +71 -0
- package/dist/internal/form-associated.d.ts +1 -0
- package/dist/internal/form-associated.js +55 -11
- package/dist/internal/group-by-recency.d.ts +50 -0
- package/dist/internal/group-by-recency.js +80 -0
- package/dist/internal/rtl.d.ts +23 -0
- package/dist/internal/rtl.js +28 -0
- package/dist/internal/scroll-lock.d.ts +10 -5
- package/dist/internal/scroll-lock.js +30 -14
- package/dist/internal/tokens.styles.js +6 -0
- package/dist/lyra.d.ts +130 -31
- package/dist/lyra.js +94 -26
- package/llms-full.txt +3836 -17
- package/llms.txt +16 -1
- package/package.json +23 -5
- package/dist/components/chart/bar-chart.stories.d.ts +0 -5
- package/dist/components/chart/bar-chart.stories.js +0 -20
- package/dist/components/chart/box-plot.stories.d.ts +0 -5
- package/dist/components/chart/box-plot.stories.js +0 -29
- package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
- package/dist/components/chart/bubble-chart.stories.js +0 -26
- package/dist/components/chart/chart.stories.d.ts +0 -32
- package/dist/components/chart/chart.stories.js +0 -141
- package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
- package/dist/components/chart/doughnut-chart.stories.js +0 -26
- package/dist/components/chart/histogram.stories.d.ts +0 -5
- package/dist/components/chart/histogram.stories.js +0 -17
- package/dist/components/chart/line-chart.stories.d.ts +0 -22
- package/dist/components/chart/line-chart.stories.js +0 -99
- package/dist/components/chart/lite-chart.stories.d.ts +0 -14
- package/dist/components/chart/lite-chart.stories.js +0 -114
- package/dist/components/chart/pie-chart.stories.d.ts +0 -5
- package/dist/components/chart/pie-chart.stories.js +0 -26
- package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
- package/dist/components/chart/polar-area-chart.stories.js +0 -20
- package/dist/components/chart/radar-chart.stories.d.ts +0 -5
- package/dist/components/chart/radar-chart.stories.js +0 -20
- package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
- package/dist/components/chart/scatter-chart.stories.js +0 -31
- package/dist/components/combobox/combobox.stories.d.ts +0 -24
- package/dist/components/combobox/combobox.stories.js +0 -102
- package/dist/components/date-picker/date-input.stories.d.ts +0 -10
- package/dist/components/date-picker/date-input.stories.js +0 -48
- package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
- package/dist/components/date-picker/date-picker.stories.js +0 -25
- package/dist/components/empty/empty.stories.d.ts +0 -7
- package/dist/components/empty/empty.stories.js +0 -34
- package/dist/components/export-button/export-button.stories.d.ts +0 -7
- package/dist/components/export-button/export-button.stories.js +0 -37
- package/dist/components/file-input/file-input.stories.d.ts +0 -7
- package/dist/components/file-input/file-input.stories.js +0 -19
- package/dist/components/flag/flag.stories.d.ts +0 -6
- package/dist/components/flag/flag.stories.js +0 -31
- package/dist/components/gauge/gauge.stories.d.ts +0 -8
- package/dist/components/gauge/gauge.stories.js +0 -19
- package/dist/components/graph/graph.stories.d.ts +0 -8
- package/dist/components/graph/graph.stories.js +0 -68
- package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
- package/dist/components/heatmap/heatmap.stories.js +0 -179
- package/dist/components/map/map.stories.d.ts +0 -15
- package/dist/components/map/map.stories.js +0 -135
- package/dist/components/playback/playback.stories.d.ts +0 -7
- package/dist/components/playback/playback.stories.js +0 -16
- package/dist/components/select/select.stories.d.ts +0 -21
- package/dist/components/select/select.stories.js +0 -91
- package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
- package/dist/components/skeleton/skeleton.stories.js +0 -16
- package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
- package/dist/components/sparkline/sparkline.stories.js +0 -39
- package/dist/components/split/split.stories.d.ts +0 -7
- package/dist/components/split/split.stories.js +0 -36
- package/dist/components/stat/stat.stories.d.ts +0 -9
- package/dist/components/stat/stat.stories.js +0 -81
- package/dist/components/table/table.stories.d.ts +0 -12
- package/dist/components/table/table.stories.js +0 -67
- package/dist/components/time-range/time-range.stories.d.ts +0 -8
- package/dist/components/time-range/time-range.stories.js +0 -26
- package/dist/components/toast/toast.stories.d.ts +0 -8
- package/dist/components/toast/toast.stories.js +0 -72
- package/dist/components/tree/tree.stories.d.ts +0 -9
- package/dist/components/tree/tree.stories.js +0 -52
- package/dist/components/widget/widget.stories.d.ts +0 -9
- package/dist/components/widget/widget.stories.js +0 -74
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- package/dist/components/word-cloud/word-cloud.stories.js +0 -55
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
font-size: 0.875rem;
|
|
6
|
+
line-height: 1.4;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* [part='base'] is a plain layout wrapper (no ARIA role of its own) --
|
|
10
|
+
the interactive/selectable region is [part='option'] alone, kept free
|
|
11
|
+
of focusable descendants (see the class doc's nested-interactive note).
|
|
12
|
+
The row-level hover/active background treatments still live here so
|
|
13
|
+
hovering anywhere across the row -- including over the trailing
|
|
14
|
+
rename/actions controls -- highlights the whole thing as one row. */
|
|
15
|
+
[part='base'] {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: flex-start;
|
|
18
|
+
gap: var(--lyra-space-xs);
|
|
19
|
+
padding: var(--lyra-space-s) var(--lyra-space-m);
|
|
20
|
+
border-radius: var(--lyra-radius);
|
|
21
|
+
transition: background-color var(--lyra-transition-fast);
|
|
22
|
+
}
|
|
23
|
+
:host(:hover) [part='base'] {
|
|
24
|
+
background: color-mix(in srgb, var(--lyra-color-text) 6%, transparent);
|
|
25
|
+
}
|
|
26
|
+
/* Placed after the :hover rule (equal selector specificity) so an
|
|
27
|
+
active-and-hovered row keeps the stronger active tint instead of the
|
|
28
|
+
two backgrounds visually competing. */
|
|
29
|
+
:host([active]) [part='base'] {
|
|
30
|
+
background: var(--lyra-color-brand-quiet);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* text-quiet's contrast ratio against brand-quiet lands at ~4.25:1 --
|
|
34
|
+
just under the WCAG AA 4.5:1 floor for normal-size text -- even though
|
|
35
|
+
it comfortably passes against the plain (non-active) background used
|
|
36
|
+
the rest of the time. Same class of bug already hit and fixed in
|
|
37
|
+
lyra-attachment-chip's [part='size'] and lyra-chat-message's
|
|
38
|
+
[part='footer']; same fix, full-strength text color once active. */
|
|
39
|
+
:host([active]) [part='excerpt'],
|
|
40
|
+
:host([active]) [part='timestamp'] {
|
|
41
|
+
color: var(--lyra-color-text);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
[part='option'] {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: flex-start;
|
|
47
|
+
gap: var(--lyra-space-s);
|
|
48
|
+
flex: 1 1 auto;
|
|
49
|
+
min-inline-size: 0;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
outline: none; /* the visible ring below targets [part='option'] directly */
|
|
52
|
+
-webkit-tap-highlight-color: transparent;
|
|
53
|
+
}
|
|
54
|
+
[part='option']:focus-visible {
|
|
55
|
+
outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
|
|
56
|
+
outline-offset: var(--lyra-focus-ring-offset);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[part='content'] {
|
|
60
|
+
flex: 1 1 auto;
|
|
61
|
+
min-inline-size: 0;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: 0.125rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
[part='title'] {
|
|
68
|
+
display: block;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
text-overflow: ellipsis;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
color: var(--lyra-color-text);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[part='title-input'] {
|
|
77
|
+
display: block;
|
|
78
|
+
inline-size: 100%;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
padding: 0.125rem var(--lyra-space-xs);
|
|
81
|
+
border: 1px solid var(--lyra-color-brand);
|
|
82
|
+
border-radius: calc(var(--lyra-radius) * 0.6);
|
|
83
|
+
background: var(--lyra-color-surface);
|
|
84
|
+
color: var(--lyra-color-text);
|
|
85
|
+
font: inherit;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
}
|
|
88
|
+
[part='title-input']:focus-visible {
|
|
89
|
+
outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
|
|
90
|
+
outline-offset: var(--lyra-focus-ring-offset);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[part='excerpt'] {
|
|
94
|
+
display: block;
|
|
95
|
+
overflow: hidden;
|
|
96
|
+
text-overflow: ellipsis;
|
|
97
|
+
white-space: nowrap;
|
|
98
|
+
color: var(--lyra-color-text-quiet);
|
|
99
|
+
font-size: 0.8125rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[part='timestamp'] {
|
|
103
|
+
flex: 0 0 auto;
|
|
104
|
+
align-self: flex-start;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
color: var(--lyra-color-text-quiet);
|
|
107
|
+
font-size: 0.75rem;
|
|
108
|
+
font-variant-numeric: tabular-nums;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[part='rename-button'] {
|
|
112
|
+
flex: 0 0 auto;
|
|
113
|
+
display: inline-flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
min-inline-size: min(var(--lyra-icon-button-size), 1.75rem);
|
|
117
|
+
min-block-size: min(var(--lyra-icon-button-size), 1.75rem);
|
|
118
|
+
padding: 0;
|
|
119
|
+
border: none;
|
|
120
|
+
border-radius: calc(var(--lyra-radius) * 0.6);
|
|
121
|
+
background: transparent;
|
|
122
|
+
color: var(--lyra-color-text-quiet);
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
-webkit-tap-highlight-color: transparent;
|
|
125
|
+
transition: background-color var(--lyra-transition-fast);
|
|
126
|
+
}
|
|
127
|
+
[part='rename-button']:hover {
|
|
128
|
+
background: color-mix(in srgb, var(--lyra-color-text) 8%, transparent);
|
|
129
|
+
color: var(--lyra-color-text);
|
|
130
|
+
}
|
|
131
|
+
[part='rename-button']:focus-visible {
|
|
132
|
+
outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
|
|
133
|
+
outline-offset: var(--lyra-focus-ring-offset);
|
|
134
|
+
}
|
|
135
|
+
[part='rename-button'] svg {
|
|
136
|
+
display: block;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
[part='actions'] {
|
|
140
|
+
flex: 0 0 auto;
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: var(--lyra-space-xs);
|
|
144
|
+
}
|
|
145
|
+
[part='actions'][hidden] {
|
|
146
|
+
display: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@media (prefers-reduced-motion: reduce) {
|
|
150
|
+
[part='base'],
|
|
151
|
+
[part='rename-button'] {
|
|
152
|
+
transition: none !important;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
@@ -59,6 +59,30 @@ export declare class LyraDateInput extends LyraDateInput_base {
|
|
|
59
59
|
disconnectedCallback(): void;
|
|
60
60
|
protected updated(changed: PropertyValues): void;
|
|
61
61
|
private onInputChange;
|
|
62
|
+
/** Parses one date, ISO-first (so a calendar-invalid ISO string like
|
|
63
|
+
* "2026-02-30" is rejected rather than silently rolled over by Date.parse()).
|
|
64
|
+
* A 3-part, ambiguous slash/dot/dash-separated date (e.g. "15/07/2026") is
|
|
65
|
+
* parsed according to the locale's own day/month/year order (via a real
|
|
66
|
+
* Intl.DateTimeFormat sample, see localeDateOrder()) rather than
|
|
67
|
+
* Date.parse()'s implementation-defined heuristics -- this is what prevents
|
|
68
|
+
* e.g. an en-GB "15/07/2026" from being silently misread as some other day.
|
|
69
|
+
* A 4-digit first group (e.g. "2026-7-15") is never locale-guessed, though:
|
|
70
|
+
* it's unambiguously a year regardless of locale/separator (ISO's own
|
|
71
|
+
* year-first convention, just without zero-padding), so it's routed
|
|
72
|
+
* straight through parseISO() instead -- this is what lets a non-padded
|
|
73
|
+
* ISO-ish date keep parsing correctly (it did via Date.parse() before the
|
|
74
|
+
* ambiguous-date regex below existed at all).
|
|
75
|
+
* Anything else (e.g. "July 15, 2026") still falls through to Date.parse(). */
|
|
76
|
+
private parseOneDate;
|
|
77
|
+
private parseSingleText;
|
|
78
|
+
/** Only round-trips the exact `displayText` shape this component itself
|
|
79
|
+
* renders (`"<locale from> – <locale to>"`) — a raw ISO range typed
|
|
80
|
+
* directly (`"2026-05-01/2026-05-15"`) is also accepted as a convenience.
|
|
81
|
+
* A reversed typed range (`to` before `from`) is normalized into
|
|
82
|
+
* from-before-to order, matching what the date-picker's own UI-driven
|
|
83
|
+
* `commit()` already does for a UI-picked range. */
|
|
84
|
+
private parseRangeText;
|
|
85
|
+
private isWithinBounds;
|
|
62
86
|
private onInputKey;
|
|
63
87
|
private onFormControlKey;
|
|
64
88
|
private onInputBlur;
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { html } from 'lit';
|
|
7
|
+
import { html, nothing } from 'lit';
|
|
8
8
|
import { property, state } from 'lit/decorators.js';
|
|
9
9
|
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
10
|
import { FormAssociated } from '../../internal/form-associated.js';
|
|
@@ -16,6 +16,26 @@ import { parseISO, formatISO } from './calendar-core.js';
|
|
|
16
16
|
import { styles } from './date-input.styles.js';
|
|
17
17
|
import { LyraDatePicker } from './date-picker.js';
|
|
18
18
|
import './date-picker.js';
|
|
19
|
+
/** Determines the locale's day/month/year field order from a real formatted
|
|
20
|
+
* sample (Jan 2, 2026 -- a date where day/month/year are all numerically
|
|
21
|
+
* distinguishable), instead of relying on Date.parse()'s implementation-defined
|
|
22
|
+
* (commonly mm/dd/yyyy-biased) heuristics for an ambiguous separated date. */
|
|
23
|
+
function localeDateOrder(locale) {
|
|
24
|
+
try {
|
|
25
|
+
const parts = new Intl.DateTimeFormat(locale || undefined, {
|
|
26
|
+
year: 'numeric',
|
|
27
|
+
month: '2-digit',
|
|
28
|
+
day: '2-digit',
|
|
29
|
+
}).formatToParts(new Date(2026, 0, 2));
|
|
30
|
+
const order = parts
|
|
31
|
+
.filter((p) => p.type === 'day' || p.type === 'month' || p.type === 'year')
|
|
32
|
+
.map((p) => p.type);
|
|
33
|
+
return order.length === 3 ? order : ['month', 'day', 'year'];
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return ['month', 'day', 'year']; // Date.parse()'s own bias, as a last-resort fallback
|
|
37
|
+
}
|
|
38
|
+
}
|
|
19
39
|
/**
|
|
20
40
|
* `<lyra-date-input>` — a date field with an attached calendar popover.
|
|
21
41
|
* Mirrors the core `<wa-date-input>` API under `lyra-`. Value is ISO 8601
|
|
@@ -84,24 +104,18 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
84
104
|
this.emit('change');
|
|
85
105
|
return;
|
|
86
106
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// ISO-shaped string would silently resurrect the same rollover parseISO
|
|
91
|
-
// guards against (Date.parse('2026-02-30') also normalizes to March 2).
|
|
92
|
-
const looksLikeISO = /^\d{4}-\d{2}-\d{2}$/.test(raw);
|
|
93
|
-
const parsed = looksLikeISO ? parseISO(raw) : isNaN(Date.parse(raw)) ? null : new Date(Date.parse(raw));
|
|
94
|
-
if (parsed) {
|
|
95
|
-
this.value = formatISO(parsed);
|
|
107
|
+
const parsed = this.mode === 'range' ? this.parseRangeText(raw) : this.parseSingleText(raw);
|
|
108
|
+
if (parsed && this.isWithinBounds(parsed)) {
|
|
109
|
+
this.value = parsed;
|
|
96
110
|
this.emit('input');
|
|
97
111
|
this.emit('change');
|
|
98
112
|
}
|
|
99
113
|
else {
|
|
100
|
-
// Unparseable text: don't silently keep the
|
|
101
|
-
// field still shows garbage -- revert the
|
|
102
|
-
// last valid commit and flag the
|
|
103
|
-
// how `required` is already
|
|
104
|
-
// form-associated.ts).
|
|
114
|
+
// Unparseable (or out-of-bounds) text: don't silently keep the
|
|
115
|
+
// committed value while the field still shows garbage -- revert the
|
|
116
|
+
// displayed text back to the last valid commit and flag the
|
|
117
|
+
// constraint-validation state (mirrors how `required` is already
|
|
118
|
+
// wired via internals.setValidity(), see form-associated.ts).
|
|
105
119
|
target.value = this.displayText;
|
|
106
120
|
this.internals.setValidity({ badInput: true }, 'Enter a valid date');
|
|
107
121
|
}
|
|
@@ -175,7 +189,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
175
189
|
}
|
|
176
190
|
/** Open the calendar popover. */
|
|
177
191
|
show() {
|
|
178
|
-
if (this.open || this.
|
|
192
|
+
if (this.open || this.effectiveDisabled || this.readonly)
|
|
179
193
|
return;
|
|
180
194
|
this.open = true;
|
|
181
195
|
this.emit('lyra-show');
|
|
@@ -199,7 +213,14 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
199
213
|
disconnectedCallback() {
|
|
200
214
|
super.disconnectedCallback();
|
|
201
215
|
this.cleanupFn?.();
|
|
216
|
+
this.cleanupFn = undefined;
|
|
202
217
|
document.removeEventListener('pointerdown', this.onDocPointer);
|
|
218
|
+
// Reset so a reconnect (e.g. a drag-drop reparent) re-triggers
|
|
219
|
+
// `updated()`'s `open`-driven branch -- without this, `open` stays
|
|
220
|
+
// stuck `true` across the disconnect, `updated()` never sees it
|
|
221
|
+
// *change*, and `place()` never gets called again to re-bind
|
|
222
|
+
// positioning to the (possibly relocated) anchor.
|
|
223
|
+
this.open = false;
|
|
203
224
|
}
|
|
204
225
|
updated(changed) {
|
|
205
226
|
super.updated(changed);
|
|
@@ -217,11 +238,100 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
217
238
|
this.toggleAttribute('data-invalid', this.touched && !this.internals.validity.valid);
|
|
218
239
|
}
|
|
219
240
|
}
|
|
241
|
+
/** Parses one date, ISO-first (so a calendar-invalid ISO string like
|
|
242
|
+
* "2026-02-30" is rejected rather than silently rolled over by Date.parse()).
|
|
243
|
+
* A 3-part, ambiguous slash/dot/dash-separated date (e.g. "15/07/2026") is
|
|
244
|
+
* parsed according to the locale's own day/month/year order (via a real
|
|
245
|
+
* Intl.DateTimeFormat sample, see localeDateOrder()) rather than
|
|
246
|
+
* Date.parse()'s implementation-defined heuristics -- this is what prevents
|
|
247
|
+
* e.g. an en-GB "15/07/2026" from being silently misread as some other day.
|
|
248
|
+
* A 4-digit first group (e.g. "2026-7-15") is never locale-guessed, though:
|
|
249
|
+
* it's unambiguously a year regardless of locale/separator (ISO's own
|
|
250
|
+
* year-first convention, just without zero-padding), so it's routed
|
|
251
|
+
* straight through parseISO() instead -- this is what lets a non-padded
|
|
252
|
+
* ISO-ish date keep parsing correctly (it did via Date.parse() before the
|
|
253
|
+
* ambiguous-date regex below existed at all).
|
|
254
|
+
* Anything else (e.g. "July 15, 2026") still falls through to Date.parse(). */
|
|
255
|
+
parseOneDate(raw) {
|
|
256
|
+
const looksLikeISO = /^\d{4}-\d{2}-\d{2}$/.test(raw);
|
|
257
|
+
if (looksLikeISO)
|
|
258
|
+
return parseISO(raw);
|
|
259
|
+
const ambiguous = /^(\d{1,4})[/.-](\d{1,4})[/.-](\d{1,4})$/.exec(raw);
|
|
260
|
+
if (ambiguous) {
|
|
261
|
+
const [, g1, g2, g3] = ambiguous;
|
|
262
|
+
if (g1.length === 4) {
|
|
263
|
+
return parseISO(`${g1}-${g2.padStart(2, '0')}-${g3.padStart(2, '0')}`);
|
|
264
|
+
}
|
|
265
|
+
const order = localeDateOrder(this.locale);
|
|
266
|
+
const values = [Number(g1), Number(g2), Number(g3)];
|
|
267
|
+
const fields = {};
|
|
268
|
+
order.forEach((type, i) => {
|
|
269
|
+
fields[type] = values[i];
|
|
270
|
+
});
|
|
271
|
+
if (fields.day != null && fields.month != null && fields.year != null) {
|
|
272
|
+
const year = fields.year < 100 ? 2000 + fields.year : fields.year;
|
|
273
|
+
return parseISO(`${String(year).padStart(4, '0')}-${String(fields.month).padStart(2, '0')}-${String(fields.day).padStart(2, '0')}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return isNaN(Date.parse(raw)) ? null : new Date(Date.parse(raw));
|
|
277
|
+
}
|
|
278
|
+
parseSingleText(raw) {
|
|
279
|
+
const parsed = this.parseOneDate(raw);
|
|
280
|
+
return parsed ? formatISO(parsed) : null;
|
|
281
|
+
}
|
|
282
|
+
/** Only round-trips the exact `displayText` shape this component itself
|
|
283
|
+
* renders (`"<locale from> – <locale to>"`) — a raw ISO range typed
|
|
284
|
+
* directly (`"2026-05-01/2026-05-15"`) is also accepted as a convenience.
|
|
285
|
+
* A reversed typed range (`to` before `from`) is normalized into
|
|
286
|
+
* from-before-to order, matching what the date-picker's own UI-driven
|
|
287
|
+
* `commit()` already does for a UI-picked range. */
|
|
288
|
+
parseRangeText(raw) {
|
|
289
|
+
if (/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(raw)) {
|
|
290
|
+
const [a, b] = raw.split('/');
|
|
291
|
+
const from = parseISO(a);
|
|
292
|
+
const to = parseISO(b);
|
|
293
|
+
if (!from || !to)
|
|
294
|
+
return null;
|
|
295
|
+
return from <= to ? raw : `${b}/${a}`;
|
|
296
|
+
}
|
|
297
|
+
const separator = ' – '; // matches displayText's ` – ` join exactly
|
|
298
|
+
const idx = raw.indexOf(separator);
|
|
299
|
+
if (idx === -1)
|
|
300
|
+
return null;
|
|
301
|
+
const from = this.parseOneDate(raw.slice(0, idx).trim());
|
|
302
|
+
const to = this.parseOneDate(raw.slice(idx + separator.length).trim());
|
|
303
|
+
if (!from || !to)
|
|
304
|
+
return null;
|
|
305
|
+
return from <= to ? `${formatISO(from)}/${formatISO(to)}` : `${formatISO(to)}/${formatISO(from)}`;
|
|
306
|
+
}
|
|
307
|
+
isWithinBounds(isoValue) {
|
|
308
|
+
const min = parseISO(this.min);
|
|
309
|
+
const max = parseISO(this.max);
|
|
310
|
+
const today = new Date();
|
|
311
|
+
today.setHours(0, 0, 0, 0);
|
|
312
|
+
return isoValue.split('/').every((part) => {
|
|
313
|
+
const d = parseISO(part);
|
|
314
|
+
if (!d)
|
|
315
|
+
return false;
|
|
316
|
+
if (min && d < min)
|
|
317
|
+
return false;
|
|
318
|
+
if (max && d > max)
|
|
319
|
+
return false;
|
|
320
|
+
if (this.disablePast && d < today)
|
|
321
|
+
return false;
|
|
322
|
+
if (this.disableFuture && d > today)
|
|
323
|
+
return false;
|
|
324
|
+
return true;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
220
327
|
render() {
|
|
221
328
|
const hasValue = this.value.length > 0;
|
|
222
329
|
const hasHint = this.hasHintSlot || this.hint.length > 0;
|
|
223
330
|
const hasError = this.hasErrorSlot || this.errorText.length > 0;
|
|
224
331
|
const hasLabel = this.hasLabelSlot || this.label.length > 0;
|
|
332
|
+
const describedBy = [hasError ? 'date-input-error' : '', hasHint ? 'date-input-hint' : '']
|
|
333
|
+
.filter(Boolean)
|
|
334
|
+
.join(' ');
|
|
225
335
|
return html `
|
|
226
336
|
<div part="form-control" @keydown=${this.onFormControlKey}>
|
|
227
337
|
<label part="form-control-label" for=${this.inputId} ?hidden=${!hasLabel}>
|
|
@@ -232,10 +342,11 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
232
342
|
id=${this.inputId}
|
|
233
343
|
part="input"
|
|
234
344
|
type="text"
|
|
235
|
-
aria-label=${
|
|
345
|
+
aria-label=${hasLabel ? nothing : this.placeholder || 'Date'}
|
|
346
|
+
aria-describedby=${describedBy || nothing}
|
|
236
347
|
.value=${this.displayText}
|
|
237
348
|
placeholder=${this.placeholder}
|
|
238
|
-
?disabled=${this.
|
|
349
|
+
?disabled=${this.effectiveDisabled}
|
|
239
350
|
?readonly=${this.readonly}
|
|
240
351
|
@change=${this.onInputChange}
|
|
241
352
|
@keydown=${this.onInputKey}
|
|
@@ -245,6 +356,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
245
356
|
? html `<button
|
|
246
357
|
part="clear-button"
|
|
247
358
|
type="button"
|
|
359
|
+
?disabled=${this.effectiveDisabled || this.readonly}
|
|
248
360
|
aria-label="Clear"
|
|
249
361
|
@click=${() => this.clear()}
|
|
250
362
|
>
|
|
@@ -258,7 +370,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
258
370
|
aria-haspopup="dialog"
|
|
259
371
|
aria-expanded=${this.open ? 'true' : 'false'}
|
|
260
372
|
aria-controls=${this.popupId}
|
|
261
|
-
?disabled=${this.
|
|
373
|
+
?disabled=${this.effectiveDisabled || this.readonly}
|
|
262
374
|
@click=${() => (this.open ? this.hide() : this.show())}
|
|
263
375
|
>
|
|
264
376
|
<span part="expand-icon" aria-hidden="true">${calendarIcon()}</span>
|
|
@@ -273,7 +385,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
273
385
|
.max=${this.max}
|
|
274
386
|
.months=${this.months}
|
|
275
387
|
.locale=${this.locale}
|
|
276
|
-
.disabled=${this.
|
|
388
|
+
.disabled=${this.effectiveDisabled}
|
|
277
389
|
.readonly=${this.readonly}
|
|
278
390
|
.disablePast=${this.disablePast}
|
|
279
391
|
.disableFuture=${this.disableFuture}
|
|
@@ -284,10 +396,10 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
|
|
|
284
396
|
@change=${this.onPickerChange}
|
|
285
397
|
></lyra-date-picker>
|
|
286
398
|
</div>
|
|
287
|
-
<div part="error" ?hidden=${!hasError}>
|
|
399
|
+
<div id="date-input-error" part="error" ?hidden=${!hasError}>
|
|
288
400
|
${this.errorText}<slot name="error" @slotchange=${this.onErrorSlotChange}></slot>
|
|
289
401
|
</div>
|
|
290
|
-
<div part="hint" ?hidden=${!hasHint}>
|
|
402
|
+
<div id="date-input-hint" part="hint" ?hidden=${!hasHint}>
|
|
291
403
|
${this.hint}<slot name="hint" @slotchange=${this.onHintSlotChange}></slot>
|
|
292
404
|
</div>
|
|
293
405
|
</div>
|
|
@@ -43,6 +43,13 @@ export declare class LyraDatePicker extends LyraElement {
|
|
|
43
43
|
protected willUpdate(changed: PropertyValues): void;
|
|
44
44
|
private get fdow();
|
|
45
45
|
private isDisabled;
|
|
46
|
+
/** The first enabled day at/after the first visible month's start, scanning
|
|
47
|
+
* forward across all visible months -- used as the sole focusable day when
|
|
48
|
+
* there's no selection and no prior keyboard focus, so the empty-grid case
|
|
49
|
+
* never defaults to a possibly-disabled day 1. Returns null if every
|
|
50
|
+
* visible day is disabled (pathological but possible with a very narrow
|
|
51
|
+
* min/max window). */
|
|
52
|
+
private firstEnabledDate;
|
|
46
53
|
private commit;
|
|
47
54
|
private selectDate;
|
|
48
55
|
/** Clear the selection and emit input + change. */
|
|
@@ -50,32 +50,54 @@ export class LyraDatePicker extends LyraElement {
|
|
|
50
50
|
// `selection.from`'s month -- only an externally-set `value` should do that.
|
|
51
51
|
this.suppressViewSync = false;
|
|
52
52
|
this.onGridKey = (e) => {
|
|
53
|
-
const
|
|
53
|
+
const min = parseISO(this.min);
|
|
54
|
+
const max = parseISO(this.max);
|
|
55
|
+
const today = new Date();
|
|
56
|
+
today.setHours(0, 0, 0, 0);
|
|
57
|
+
const current = this.focusedDate ??
|
|
58
|
+
this.selection.from ??
|
|
59
|
+
this.firstEnabledDate(min, max, today) ??
|
|
60
|
+
new Date(this.viewDate.getFullYear(), this.viewDate.getMonth(), 1);
|
|
61
|
+
const step = (base, days) => new Date(base.getFullYear(), base.getMonth(), base.getDate() + days);
|
|
62
|
+
// Walks in the requested direction until an enabled day is found, bounded
|
|
63
|
+
// by a step cap (366 days -- more than a year) so an all-disabled range
|
|
64
|
+
// (e.g. min/max entirely in the past with disable-past set) can't loop
|
|
65
|
+
// forever; returns null rather than landing focus on a disabled cell,
|
|
66
|
+
// whose `.focus()` would be a silent no-op anyway.
|
|
67
|
+
const firstEnabledFrom = (base, dayStep) => {
|
|
68
|
+
let d = base;
|
|
69
|
+
for (let i = 0; i < 366; i++) {
|
|
70
|
+
if (!this.isDisabled(d, min, max, today))
|
|
71
|
+
return d;
|
|
72
|
+
d = step(d, dayStep);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
54
76
|
let next = null;
|
|
55
77
|
switch (e.key) {
|
|
56
78
|
case 'ArrowLeft':
|
|
57
|
-
next =
|
|
79
|
+
next = firstEnabledFrom(step(current, -1), -1);
|
|
58
80
|
break;
|
|
59
81
|
case 'ArrowRight':
|
|
60
|
-
next =
|
|
82
|
+
next = firstEnabledFrom(step(current, 1), 1);
|
|
61
83
|
break;
|
|
62
84
|
case 'ArrowUp':
|
|
63
|
-
next =
|
|
85
|
+
next = firstEnabledFrom(step(current, -7), -1);
|
|
64
86
|
break;
|
|
65
87
|
case 'ArrowDown':
|
|
66
|
-
next =
|
|
88
|
+
next = firstEnabledFrom(step(current, 7), 1);
|
|
67
89
|
break;
|
|
68
90
|
case 'PageUp':
|
|
69
|
-
next = new Date(current.getFullYear(), current.getMonth() - 1, current.getDate());
|
|
91
|
+
next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() - 1, current.getDate()), 1);
|
|
70
92
|
break;
|
|
71
93
|
case 'PageDown':
|
|
72
|
-
next = new Date(current.getFullYear(), current.getMonth() + 1, current.getDate());
|
|
94
|
+
next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() + 1, current.getDate()), 1);
|
|
73
95
|
break;
|
|
74
96
|
case 'Home':
|
|
75
|
-
next = new Date(current.getFullYear(), current.getMonth(), 1);
|
|
97
|
+
next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth(), 1), 1);
|
|
76
98
|
break;
|
|
77
99
|
case 'End':
|
|
78
|
-
next = new Date(current.getFullYear(), current.getMonth() + 1, 0);
|
|
100
|
+
next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() + 1, 0), -1);
|
|
79
101
|
break;
|
|
80
102
|
case 'Enter':
|
|
81
103
|
case ' ':
|
|
@@ -86,6 +108,8 @@ export class LyraDatePicker extends LyraElement {
|
|
|
86
108
|
return;
|
|
87
109
|
}
|
|
88
110
|
e.preventDefault();
|
|
111
|
+
if (!next)
|
|
112
|
+
return;
|
|
89
113
|
this.focusedDate = next;
|
|
90
114
|
this.viewDate = this.viewDateForFocus(next);
|
|
91
115
|
this.focusPending = true;
|
|
@@ -127,6 +151,23 @@ export class LyraDatePicker extends LyraElement {
|
|
|
127
151
|
return true;
|
|
128
152
|
return false;
|
|
129
153
|
}
|
|
154
|
+
/** The first enabled day at/after the first visible month's start, scanning
|
|
155
|
+
* forward across all visible months -- used as the sole focusable day when
|
|
156
|
+
* there's no selection and no prior keyboard focus, so the empty-grid case
|
|
157
|
+
* never defaults to a possibly-disabled day 1. Returns null if every
|
|
158
|
+
* visible day is disabled (pathological but possible with a very narrow
|
|
159
|
+
* min/max window). */
|
|
160
|
+
firstEnabledDate(min, max, today) {
|
|
161
|
+
let d = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth(), 1);
|
|
162
|
+
const lastVisibleMonth = addMonths(d, this.months - 1);
|
|
163
|
+
const boundEnd = new Date(lastVisibleMonth.getFullYear(), lastVisibleMonth.getMonth() + 1, 0);
|
|
164
|
+
for (let i = 0; i < 732 && d <= boundEnd; i++) {
|
|
165
|
+
if (!this.isDisabled(d, min, max, today))
|
|
166
|
+
return d;
|
|
167
|
+
d = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1);
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
130
171
|
commit(from, to, fire) {
|
|
131
172
|
const next = this.mode === 'range'
|
|
132
173
|
? from && to
|
|
@@ -217,7 +258,7 @@ export class LyraDatePicker extends LyraElement {
|
|
|
217
258
|
cell?.focus();
|
|
218
259
|
}
|
|
219
260
|
}
|
|
220
|
-
renderDay(date, shownMonth, selection, min, max, today, rowHasVisibleDay) {
|
|
261
|
+
renderDay(date, shownMonth, selection, min, max, today, rowHasVisibleDay, fallbackFocusDate) {
|
|
221
262
|
const outside = date.getMonth() !== shownMonth;
|
|
222
263
|
// Outside-month days are empty placeholders by default (matches WA), keeping the
|
|
223
264
|
// 6-row grid aligned without low-contrast faded numbers.
|
|
@@ -240,7 +281,19 @@ export class LyraDatePicker extends LyraElement {
|
|
|
240
281
|
const isEnd = to && isSameDay(date, to);
|
|
241
282
|
const selected = this.mode === 'single' ? isStart : isStart || isEnd;
|
|
242
283
|
const inRange = this.mode === 'range' && from && to && date > from && date < to;
|
|
243
|
-
|
|
284
|
+
// Falls back to the first *enabled* day across the visible month(s) (see
|
|
285
|
+
// firstEnabledDate()) when there's neither a focusedDate nor a selection,
|
|
286
|
+
// so the grid always has exactly one tabbable cell -- otherwise every
|
|
287
|
+
// cell computes `false` and keyboard users tabbing into an empty picker
|
|
288
|
+
// land nowhere. Must not just be "day 1 of the shown month" unconditionally:
|
|
289
|
+
// day 1 can itself be disabled (e.g. disable-past opened on any day other
|
|
290
|
+
// than the 1st), which would land the sole tabindex="0" cell on a button
|
|
291
|
+
// that can never actually receive focus.
|
|
292
|
+
const focused = this.focusedDate != null
|
|
293
|
+
? isSameDay(date, this.focusedDate)
|
|
294
|
+
: isStart
|
|
295
|
+
? true
|
|
296
|
+
: !from && fallbackFocusDate != null && isSameDay(date, fallbackFocusDate);
|
|
244
297
|
const parts = ['day'];
|
|
245
298
|
if (outside)
|
|
246
299
|
parts.push('day-outside');
|
|
@@ -272,7 +325,7 @@ export class LyraDatePicker extends LyraElement {
|
|
|
272
325
|
${date.getDate()}
|
|
273
326
|
</button>`;
|
|
274
327
|
}
|
|
275
|
-
renderMonth(offset, selection, min, max, today, fdow, labels) {
|
|
328
|
+
renderMonth(offset, selection, min, max, today, fdow, labels, fallbackFocusDate) {
|
|
276
329
|
const base = addMonths(this.viewDate, offset);
|
|
277
330
|
const year = base.getFullYear();
|
|
278
331
|
const month = base.getMonth();
|
|
@@ -282,13 +335,25 @@ export class LyraDatePicker extends LyraElement {
|
|
|
282
335
|
return html `<div part="month">
|
|
283
336
|
<div part="header">
|
|
284
337
|
${isFirst
|
|
285
|
-
? html `<button
|
|
338
|
+
? html `<button
|
|
339
|
+
part="previous"
|
|
340
|
+
type="button"
|
|
341
|
+
aria-label="Previous month"
|
|
342
|
+
?disabled=${this.disabled || this.readonly}
|
|
343
|
+
@click=${() => this.nav(-1)}
|
|
344
|
+
>
|
|
286
345
|
${chevronIcon()}
|
|
287
346
|
</button>`
|
|
288
347
|
: html `<span></span>`}
|
|
289
348
|
<div part="title">${monthTitle(year, month, this.locale)}</div>
|
|
290
349
|
${isLast
|
|
291
|
-
? html `<button
|
|
350
|
+
? html `<button
|
|
351
|
+
part="next"
|
|
352
|
+
type="button"
|
|
353
|
+
aria-label="Next month"
|
|
354
|
+
?disabled=${this.disabled || this.readonly}
|
|
355
|
+
@click=${() => this.nav(1)}
|
|
356
|
+
>
|
|
292
357
|
${chevronIcon()}
|
|
293
358
|
</button>`
|
|
294
359
|
: html `<span></span>`}
|
|
@@ -297,7 +362,7 @@ export class LyraDatePicker extends LyraElement {
|
|
|
297
362
|
<div part="grid" role="grid" @keydown=${this.onGridKey}>
|
|
298
363
|
${matrix.map((week) => {
|
|
299
364
|
const rowHasVisibleDay = week.some((d) => d.getMonth() === month);
|
|
300
|
-
return html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month, selection, min, max, today, rowHasVisibleDay))}</div>`;
|
|
365
|
+
return html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month, selection, min, max, today, rowHasVisibleDay, fallbackFocusDate))}</div>`;
|
|
301
366
|
})}
|
|
302
367
|
</div>
|
|
303
368
|
</div>`;
|
|
@@ -310,9 +375,13 @@ export class LyraDatePicker extends LyraElement {
|
|
|
310
375
|
today.setHours(0, 0, 0, 0);
|
|
311
376
|
const fdow = this.fdow;
|
|
312
377
|
const labels = weekdayLabels(fdow, this.weekdayFormat, this.locale);
|
|
378
|
+
// Only bother scanning for a fallback focus day when it's actually
|
|
379
|
+
// needed: there's no point walking the visible days if a focusedDate or
|
|
380
|
+
// an existing selection already determines the sole tabbable cell.
|
|
381
|
+
const fallbackFocusDate = this.focusedDate || selection.from ? null : this.firstEnabledDate(min, max, today);
|
|
313
382
|
const monthEls = [];
|
|
314
383
|
for (let i = 0; i < this.months; i++) {
|
|
315
|
-
monthEls.push(this.renderMonth(i, selection, min, max, today, fdow, labels));
|
|
384
|
+
monthEls.push(this.renderMonth(i, selection, min, max, today, fdow, labels, fallbackFocusDate));
|
|
316
385
|
}
|
|
317
386
|
return html `<div part="base">${monthEls}</div>`;
|
|
318
387
|
}
|