noph-ui 0.52.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/animation.d.ts +1 -13
  2. package/dist/animation.js +18 -13
  3. package/dist/button/Button.svelte +2 -14
  4. package/dist/button/IconButton.svelte +5 -18
  5. package/dist/button/SegmentedButton.svelte +8 -11
  6. package/dist/card/Card.svelte +3 -19
  7. package/dist/card/types.d.ts +12 -2
  8. package/dist/carousel/Carousel.svelte +77 -14
  9. package/dist/carousel/CarouselItem.svelte +26 -14
  10. package/dist/carousel/carouselMorph.js +1 -1
  11. package/dist/carousel/carouselStrategy.js +25 -27
  12. package/dist/checkbox/Checkbox.svelte +1 -8
  13. package/dist/chip/FilterChip.svelte +1 -13
  14. package/dist/date-picker/Calendar.svelte +31 -30
  15. package/dist/date-picker/Calendar.svelte.d.ts +2 -0
  16. package/dist/date-picker/CalendarHeader.svelte +84 -62
  17. package/dist/date-picker/CalendarHeader.svelte.d.ts +3 -0
  18. package/dist/date-picker/DatePickerDialog.svelte +134 -75
  19. package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -3
  20. package/dist/date-picker/DateRangePicker.svelte +226 -78
  21. package/dist/date-picker/DateRangePicker.svelte.d.ts +3 -3
  22. package/dist/date-picker/DockedDatePicker.svelte +76 -464
  23. package/dist/date-picker/DockedDatePicker.svelte.d.ts +2 -2
  24. package/dist/date-picker/DockedDateTimePicker.svelte +128 -525
  25. package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +2 -2
  26. package/dist/date-picker/DockedPickerField.svelte +221 -0
  27. package/dist/date-picker/DockedPickerField.svelte.d.ts +9 -0
  28. package/dist/date-picker/MonthPanel.svelte +291 -0
  29. package/dist/date-picker/MonthPanel.svelte.d.ts +12 -0
  30. package/dist/date-picker/SelectionList.svelte +7 -6
  31. package/dist/date-picker/SelectionList.svelte.d.ts +1 -0
  32. package/dist/date-picker/TimeColumn.svelte +2 -6
  33. package/dist/date-picker/TimeColumn.svelte.d.ts +1 -0
  34. package/dist/date-picker/YearGrid.svelte +2 -11
  35. package/dist/date-picker/YearGrid.svelte.d.ts +1 -0
  36. package/dist/date-picker/dateUtils.d.ts +4 -0
  37. package/dist/date-picker/dateUtils.js +15 -88
  38. package/dist/date-picker/exitVisibility.svelte.js +13 -6
  39. package/dist/date-picker/formatters.d.ts +16 -0
  40. package/dist/date-picker/formatters.js +48 -15
  41. package/dist/date-picker/internalTypes.d.ts +59 -1
  42. package/dist/date-picker/shortcuts.d.ts +11 -0
  43. package/dist/date-picker/shortcuts.js +40 -0
  44. package/dist/date-picker/timeUtils.d.ts +8 -3
  45. package/dist/date-picker/timeUtils.js +33 -51
  46. package/dist/date-picker/types.d.ts +22 -0
  47. package/dist/icons/Icon.svelte +1 -4
  48. package/dist/internal/exit.css +0 -14
  49. package/dist/internal/field.css +337 -0
  50. package/dist/internal/focus-ring.css +23 -8
  51. package/dist/internal/pop-in.css +19 -0
  52. package/dist/internal/visually-hidden.css +11 -0
  53. package/dist/list/Item.svelte +32 -18
  54. package/dist/menu/Menu.svelte +6 -25
  55. package/dist/menu/Menu.svelte.d.ts +1 -0
  56. package/dist/navigation-bar/NavigationBarItem.svelte +6 -30
  57. package/dist/navigation-drawer/NavigationDrawerItem.svelte +4 -11
  58. package/dist/navigation-rail/NavigationRailItem.svelte +2 -13
  59. package/dist/press.svelte.d.ts +0 -1
  60. package/dist/press.svelte.js +0 -11
  61. package/dist/progress/CircularProgress.svelte +26 -81
  62. package/dist/progress/CircularProgress.svelte.d.ts +1 -0
  63. package/dist/progress/LinearProgress.svelte +19 -151
  64. package/dist/progress/LinearProgress.svelte.d.ts +1 -0
  65. package/dist/progress/four-color.css +34 -0
  66. package/dist/radio/Radio.svelte +6 -9
  67. package/dist/ripple/Ripple.svelte +8 -16
  68. package/dist/search/Search.svelte +2 -9
  69. package/dist/search/Search.svelte.d.ts +1 -0
  70. package/dist/select/Check.svelte +8 -31
  71. package/dist/select/Check.svelte.d.ts +1 -1
  72. package/dist/select/NativeSelect.svelte +97 -366
  73. package/dist/select/NativeSelect.svelte.d.ts +1 -0
  74. package/dist/select/Option.svelte +1 -9
  75. package/dist/select/Select.svelte +139 -746
  76. package/dist/select/Select.svelte.d.ts +1 -0
  77. package/dist/select/VirtualList.svelte +0 -1
  78. package/dist/sheet/Sheet.svelte +11 -22
  79. package/dist/slider/Slider.svelte +13 -37
  80. package/dist/slider/Slider.svelte.d.ts +1 -0
  81. package/dist/switch/Switch.svelte +2 -8
  82. package/dist/tabs/Tab.svelte +2 -4
  83. package/dist/text-field/TextField.svelte +81 -609
  84. package/dist/text-field/TextField.svelte.d.ts +1 -0
  85. package/dist/time-picker/ClockDial.svelte +8 -25
  86. package/dist/time-picker/ClockDial.svelte.d.ts +2 -0
  87. package/dist/time-picker/DockedTimePicker.svelte +102 -244
  88. package/dist/time-picker/DockedTimePicker.svelte.d.ts +2 -2
  89. package/dist/time-picker/PeriodSelector.svelte +8 -7
  90. package/dist/time-picker/PeriodSelector.svelte.d.ts +1 -0
  91. package/dist/time-picker/TimeInputs.svelte +13 -2
  92. package/dist/time-picker/TimePickerDialog.svelte +32 -26
  93. package/dist/time-picker/TimePickerDialog.svelte.d.ts +2 -2
  94. package/dist/time-picker/TimePickerPanel.svelte +26 -4
  95. package/dist/time-picker/TimeSelectors.svelte +4 -9
  96. package/dist/time-picker/TimeSelectors.svelte.d.ts +1 -0
  97. package/dist/time-picker/internalTypes.d.ts +2 -0
  98. package/dist/time-picker/timePickerState.svelte.js +3 -9
  99. package/dist/tooltip/RichTooltip.svelte +2 -23
  100. package/dist/tooltip/RichTooltip.svelte.d.ts +1 -0
  101. package/dist/tooltip/Tooltip.svelte +2 -24
  102. package/dist/tooltip/Tooltip.svelte.d.ts +1 -0
  103. package/package.json +12 -12
@@ -1,7 +1,7 @@
1
1
  import type { DockedDateTimePickerProps } from './types.js';
2
2
  declare const DockedDateTimePicker: import("svelte").Component<DockedDateTimePickerProps, {
3
- show: () => void;
4
- close: () => void;
3
+ show: () => void | undefined;
4
+ close: () => any;
5
5
  }, "open" | "element" | "value" | "displayMonth">;
6
6
  type DockedDateTimePicker = ReturnType<typeof DockedDateTimePicker>;
7
7
  export default DockedDateTimePicker;
@@ -0,0 +1,221 @@
1
+ <script lang="ts">
2
+ import IconButton from '../button/IconButton.svelte'
3
+ import { formReset } from '../form-reset.js'
4
+ import Menu from '../menu/Menu.svelte'
5
+ import TextField from '../text-field/TextField.svelte'
6
+ import { exitVisibility } from './exitVisibility.svelte.js'
7
+ import type { DockedPickerFieldProps } from './internalTypes.js'
8
+
9
+ let {
10
+ kind,
11
+ // eslint-disable-next-line no-useless-assignment
12
+ value = $bindable(),
13
+ current,
14
+ resetValue,
15
+ open = $bindable(false),
16
+ element = $bindable(),
17
+ menuElement = $bindable(),
18
+ pattern,
19
+ openLabel,
20
+ invalidMessage,
21
+ format,
22
+ parse,
23
+ icon,
24
+ onopen,
25
+ onclosed,
26
+ onrestore,
27
+ ontyped,
28
+ onmenukeydown,
29
+ children,
30
+ label,
31
+ supportingText,
32
+ issues,
33
+ name,
34
+ form,
35
+ disabled,
36
+ readonly,
37
+ 'aria-invalid': ariaInvalid,
38
+ style,
39
+ onchange,
40
+ oninput,
41
+ onblur,
42
+ ...attributes
43
+ }: DockedPickerFieldProps = $props()
44
+
45
+ const uid = $props.id()
46
+ const menuId = `${uid}-menu`
47
+ const exit = exitVisibility()
48
+
49
+ let menu = $state<Menu>()
50
+ let inputElement = $state<HTMLInputElement | HTMLTextAreaElement>()
51
+ let valueInput = $state<HTMLInputElement>()
52
+
53
+ let typed = $state.raw<{ source: string | undefined; text: string } | undefined>()
54
+ let invalidText = $state<string | undefined>()
55
+ let text = $derived(typed && typed.source === current ? typed.text : format())
56
+ let inputInvalid = $derived(invalidText !== undefined && invalidText === text)
57
+
58
+ $effect(() => {
59
+ if (open && (disabled || readonly)) open = false
60
+ })
61
+
62
+ export const show = () => {
63
+ if (!disabled && !readonly) menu?.show()
64
+ }
65
+
66
+ export const close = () => menu?.close()
67
+
68
+ export const closePicker = () => {
69
+ close()
70
+ inputElement?.focus()
71
+ }
72
+
73
+ const commit = (next: string | undefined) => {
74
+ if (current === next) return
75
+ value = next
76
+ onchange?.(next)
77
+ }
78
+
79
+ /** Takes the value chosen in the menu. */
80
+ export const confirm = (next: string | undefined) => {
81
+ typed = undefined
82
+ invalidText = undefined
83
+ commit(next)
84
+ closePicker()
85
+ }
86
+
87
+ const restoreDefault = () => {
88
+ if (valueInput) valueInput.value = resetValue ?? ''
89
+ queueMicrotask(() => {
90
+ typed = undefined
91
+ invalidText = undefined
92
+ onrestore?.()
93
+ value = resetValue
94
+ })
95
+ }
96
+
97
+ const handleInput = (event: Event) => {
98
+ const next = (event.currentTarget as HTMLInputElement).value
99
+ const parsed = parse(next)
100
+ commit(parsed)
101
+ if (parsed !== undefined) ontyped?.(parsed)
102
+ typed = { source: current, text: next }
103
+ invalidText = undefined
104
+ }
105
+
106
+ const handleBlur = () => {
107
+ if (!text.trim()) {
108
+ invalidText = undefined
109
+ return
110
+ }
111
+ const parsed = parse(text)
112
+ invalidText = parsed === undefined ? text : undefined
113
+ if (parsed !== undefined) typed = undefined
114
+ }
115
+
116
+ $effect(() => {
117
+ if (!inputElement) return
118
+ const unusable = text.trim() !== '' && parse(text) === undefined
119
+ inputElement.setCustomValidity(unusable ? invalidMessage : '')
120
+ })
121
+ </script>
122
+
123
+ <TextField
124
+ {...attributes}
125
+ class={[`np-docked-${kind}`, attributes.class]}
126
+ style={`anchor-name:--${uid};${style ?? ''}`}
127
+ {label}
128
+ {issues}
129
+ {disabled}
130
+ {readonly}
131
+ supportingText={supportingText ?? pattern}
132
+ bind:value={text}
133
+ bind:element
134
+ bind:inputElement
135
+ aria-invalid={inputInvalid || issues?.length || ariaInvalid === true || ariaInvalid === 'true'
136
+ ? 'true'
137
+ : undefined}
138
+ oninput={(event) => {
139
+ handleInput(event)
140
+ oninput?.(event)
141
+ }}
142
+ onblur={(event) => {
143
+ handleBlur()
144
+ onblur?.(event)
145
+ }}
146
+ onkeydown={(event) => {
147
+ // Enter formats the entry, as leaving the field does.
148
+ if (event.key === 'Enter') handleBlur()
149
+ attributes.onkeydown?.(event)
150
+ }}
151
+ >
152
+ {#snippet end()}
153
+ <IconButton
154
+ type="button"
155
+ aria-label={openLabel}
156
+ aria-expanded={open}
157
+ aria-haspopup="dialog"
158
+ aria-controls={open ? menuId : undefined}
159
+ {disabled}
160
+ onclick={() => (open ? closePicker() : show())}
161
+ >
162
+ {@render icon()}
163
+ </IconButton>
164
+ {/snippet}
165
+ </TextField>
166
+ <input
167
+ bind:this={valueInput}
168
+ {@attach formReset(restoreDefault)}
169
+ class="np-docked-{kind}-value"
170
+ type="hidden"
171
+ value={current ?? ''}
172
+ {name}
173
+ {form}
174
+ {disabled}
175
+ />
176
+
177
+ <Menu
178
+ bind:this={menu}
179
+ id={menuId}
180
+ role="dialog"
181
+ aria-label={label}
182
+ aria-modal="false"
183
+ class={['np-docked-picker-menu', `np-docked-${kind}-menu`]}
184
+ style={`position-anchor:--${uid};`}
185
+ --np-menu-justify-self="none"
186
+ --np-menu-position-area="bottom span-right"
187
+ --np-menu-over-anchor-position-area="span-all span-right"
188
+ --np-menu-margin="0"
189
+ --np-menu-elevation="none"
190
+ --np-menu-container-color={`var(--np-docked-${kind}-container-color, var(--np-color-surface-container-high))`}
191
+ --np-menu-container-shape={`var(--np-docked-${kind}-container-shape, var(--np-shape-corner-large))`}
192
+ anchor={element}
193
+ bind:element={menuElement}
194
+ bind:open
195
+ tabindex={-1}
196
+ onkeydown={onmenukeydown}
197
+ ontoggle={({ newState }) => {
198
+ if (newState === 'open') {
199
+ exit.show()
200
+ onopen?.()
201
+ } else {
202
+ // Reset once the menu is gone, not while it still shows.
203
+ exit.scheduleExit(menuElement, onclosed)
204
+ }
205
+ }}
206
+ >
207
+ {#if open || exit.visible}
208
+ {@render children()}
209
+ {/if}
210
+ </Menu>
211
+
212
+ <style>
213
+ :global(.np-docked-picker-menu .np-menu) {
214
+ padding: 0;
215
+ }
216
+
217
+ /* The menu takes focus only after a click on a bare part of it, so its keys keep working. */
218
+ :global(.np-docked-picker-menu:focus) {
219
+ outline: none;
220
+ }
221
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { DockedPickerFieldProps } from './internalTypes.js';
2
+ declare const DockedPickerField: import("svelte").Component<DockedPickerFieldProps, {
3
+ show: () => void;
4
+ close: () => any;
5
+ closePicker: () => void;
6
+ /** Takes the value chosen in the menu. */ confirm: (next: string | undefined) => void;
7
+ }, "open" | "element" | "value" | "menuElement">;
8
+ type DockedPickerField = ReturnType<typeof DockedPickerField>;
9
+ export default DockedPickerField;
@@ -0,0 +1,291 @@
1
+ <script lang="ts">
2
+ import Divider from '../divider/Divider.svelte'
3
+ import { flushSync } from 'svelte'
4
+ import Calendar from './Calendar.svelte'
5
+ import CalendarHeader from './CalendarHeader.svelte'
6
+ import SelectionList from './SelectionList.svelte'
7
+ import {
8
+ addMonths,
9
+ clampMonthInYear,
10
+ getMonthNames,
11
+ isMonthWithin,
12
+ isSameMonth,
13
+ isYearWithin,
14
+ } from './dateUtils.js'
15
+ import type { MonthPanelProps } from './internalTypes.js'
16
+ import { pickerShortcut } from './shortcuts.js'
17
+
18
+ // The month of a docked picker: its header, its days and the month and year lists that slide
19
+ // over them.
20
+ let {
21
+ month,
22
+ selected,
23
+ min,
24
+ max,
25
+ locale,
26
+ firstDayOfWeek,
27
+ yearRange,
28
+ isDateEnabled,
29
+ adjacentMonthDays = false,
30
+ today,
31
+ previousMonthLabel,
32
+ nextMonthLabel,
33
+ previousYearLabel,
34
+ nextYearLabel,
35
+ selectMonthLabel,
36
+ selectYearLabel,
37
+ selectedDateLabel,
38
+ shortcutLabels,
39
+ onmonth,
40
+ onselect,
41
+ onconfirm,
42
+ }: MonthPanelProps = $props()
43
+
44
+ const uid = $props.id()
45
+ let panel = $state<HTMLElement>()
46
+ let mode = $state<'days' | 'months' | 'years'>('days')
47
+ // The list stays while it slides away.
48
+ let listMode = $state<'months' | 'years' | undefined>(undefined)
49
+ let focusedDay = $state<Date | undefined>(undefined)
50
+
51
+ let year = $derived(month.getFullYear())
52
+ // How far each arrow steps, 0 where the step would leave the bounds.
53
+ let steps = $derived({
54
+ previousMonth: isMonthWithin(year, month.getMonth() - 1, min, max) ? -1 : 0,
55
+ nextMonth: isMonthWithin(year, month.getMonth() + 1, min, max) ? 1 : 0,
56
+ previousYear:
57
+ year > yearRange[0] && isMonthWithin(year - 1, month.getMonth(), min, max) ? -12 : 0,
58
+ nextYear: year < yearRange[1] && isMonthWithin(year + 1, month.getMonth(), min, max) ? 12 : 0,
59
+ })
60
+ let monthNames = $derived(getMonthNames(locale, 'long'))
61
+ let shortMonthNames = $derived(getMonthNames(locale, 'short'))
62
+
63
+ let monthOptions = $derived(
64
+ monthNames.map((label, index) => ({
65
+ value: index,
66
+ label,
67
+ disabled: !isMonthWithin(year, index, min, max),
68
+ })),
69
+ )
70
+ let yearOptions = $derived(
71
+ Array.from({ length: yearRange[1] - yearRange[0] + 1 }, (_, index) => {
72
+ const option = yearRange[0] + index
73
+ return { value: option, label: `${option}`, disabled: !isYearWithin(option, min, max) }
74
+ }),
75
+ )
76
+
77
+ const setMode = (next: 'days' | 'months' | 'years') => {
78
+ mode = next
79
+ if (next !== 'days') listMode = next
80
+ }
81
+
82
+ /** Focuses the day that takes focus. */
83
+ export const focus = () =>
84
+ panel?.querySelector<HTMLElement>('.np-calendar-day[tabindex="0"]')?.focus()
85
+
86
+ /** Back to the days, for the next time the picker opens. */
87
+ export const reset = () => {
88
+ mode = 'days'
89
+ listMode = undefined
90
+ focusedDay = undefined
91
+ }
92
+
93
+ // The view changes and focus moves in one step. A focused part that turns inert drops focus to the
94
+ // page, where the next key, such as Escape, would close the picker.
95
+ const backToDays = () => {
96
+ flushSync(() => (mode = 'days'))
97
+ focus()
98
+ }
99
+
100
+ // Shift+M and Shift+Y toggle a list as its button does: open on its chosen entry, also from the
101
+ // other list, or back to the days.
102
+ const toggleList = (next: 'months' | 'years') => {
103
+ if (mode === next) return backToDays()
104
+ flushSync(() => setMode(next))
105
+ panel?.querySelector<HTMLElement>(`#${uid}-${next} [role="option"][tabindex="0"]`)?.focus()
106
+ }
107
+
108
+ /**
109
+ * The keys of the picker, for its whole menu: Shift+M and Shift+Y from anywhere in it, Page up
110
+ * and Page down from the header, as the arrows they stand for. Escape with a list open goes back
111
+ * to the days, before a second one closes the picker.
112
+ */
113
+ export const handleKeydown = (event: KeyboardEvent) => {
114
+ if (event.key === 'Escape' && mode !== 'days' && !event.defaultPrevented) {
115
+ event.preventDefault()
116
+ backToDays()
117
+ return
118
+ }
119
+ // The menu itself holds focus after a click on a bare part of it.
120
+ const target = event.target as Node
121
+ const onDate = target === event.currentTarget || !!panel?.contains(target)
122
+ const shortcut = pickerShortcut(event)
123
+ if (shortcut === 'monthList' || shortcut === 'yearList') {
124
+ event.preventDefault()
125
+ toggleList(shortcut === 'monthList' ? 'months' : 'years')
126
+ } else if (shortcut && mode === 'days' && onDate) {
127
+ event.preventDefault()
128
+ if (steps[shortcut]) onmonth(addMonths(month, steps[shortcut]))
129
+ }
130
+ }
131
+
132
+ const leaveList = (date: Date) => {
133
+ onmonth(date)
134
+ backToDays()
135
+ }
136
+ </script>
137
+
138
+ <div class="np-month-panel" bind:this={panel}>
139
+ <CalendarHeader
140
+ {mode}
141
+ monthLabel={shortMonthNames[month.getMonth()]}
142
+ yearLabel={`${year}`}
143
+ canPreviousMonth={steps.previousMonth !== 0}
144
+ canNextMonth={steps.nextMonth !== 0}
145
+ canPreviousYear={steps.previousYear !== 0}
146
+ canNextYear={steps.nextYear !== 0}
147
+ {previousMonthLabel}
148
+ {nextMonthLabel}
149
+ {previousYearLabel}
150
+ {nextYearLabel}
151
+ {selectMonthLabel}
152
+ {selectYearLabel}
153
+ {shortcutLabels}
154
+ monthListId="{uid}-months"
155
+ yearListId="{uid}-years"
156
+ onpreviousmonth={() => onmonth(addMonths(month, -1))}
157
+ onnextmonth={() => onmonth(addMonths(month, 1))}
158
+ onpreviousyear={() => onmonth(addMonths(month, -12))}
159
+ onnextyear={() => onmonth(addMonths(month, 12))}
160
+ ontogglemonths={() => setMode(mode === 'months' ? 'days' : 'months')}
161
+ ontoggleyears={() => setMode(mode === 'years' ? 'days' : 'years')}
162
+ />
163
+
164
+ <div class="np-month-panel-views">
165
+ <div class="np-month-panel-days" inert={mode !== 'days'}>
166
+ <Calendar
167
+ {month}
168
+ {selected}
169
+ {min}
170
+ {max}
171
+ {locale}
172
+ {firstDayOfWeek}
173
+ {isDateEnabled}
174
+ {adjacentMonthDays}
175
+ dynamicRows
176
+ todayDate={today}
177
+ focusedDate={focusedDay}
178
+ selectedLabel={selectedDateLabel}
179
+ onselect={(date) => {
180
+ focusedDay = date
181
+ onselect(date)
182
+ }}
183
+ {onconfirm}
184
+ onmonthstep={(delta) => onmonth(addMonths(month, delta))}
185
+ onfocusday={(date) => {
186
+ focusedDay = date
187
+ if (!isSameMonth(date, month)) onmonth(date)
188
+ }}
189
+ />
190
+ </div>
191
+
192
+ {#if listMode}
193
+ <div class={['np-month-panel-list', mode !== 'days' && 'open']}>
194
+ <div class="np-month-panel-shade">
195
+ <Divider --np-divider-color="var(--np-color-outline-variant)" />
196
+ {#if listMode === 'months'}
197
+ <SelectionList
198
+ id="{uid}-months"
199
+ aria-label={selectMonthLabel}
200
+ options={monthOptions}
201
+ value={month.getMonth()}
202
+ onselect={(index) => leaveList(new Date(year, index, 1))}
203
+ />
204
+ {:else}
205
+ <SelectionList
206
+ id="{uid}-years"
207
+ aria-label={selectYearLabel}
208
+ options={yearOptions}
209
+ value={year}
210
+ onselect={(next) =>
211
+ leaveList(new Date(next, clampMonthInYear(next, month.getMonth(), min, max), 1))}
212
+ />
213
+ {/if}
214
+ </div>
215
+ </div>
216
+ {/if}
217
+ </div>
218
+ </div>
219
+
220
+ <style>
221
+ .np-month-panel {
222
+ display: contents;
223
+ }
224
+
225
+ .np-month-panel :global(.np-date-picker-header) {
226
+ margin-inline: 0.75rem;
227
+ }
228
+
229
+ .np-month-panel-views {
230
+ position: relative;
231
+ margin-top: 1.875rem;
232
+ }
233
+
234
+ .np-month-panel-days {
235
+ padding-inline: 0.75rem;
236
+ }
237
+
238
+ .np-month-panel-list {
239
+ position: absolute;
240
+ inset: 0;
241
+ overflow: hidden;
242
+ visibility: hidden;
243
+ }
244
+
245
+ .np-month-panel-list.open {
246
+ visibility: visible;
247
+ }
248
+
249
+ .np-month-panel-shade {
250
+ display: flex;
251
+ flex-direction: column;
252
+ height: 100%;
253
+ translate: 0 -100%;
254
+ opacity: 0.6;
255
+ background-color: var(--np-menu-container-color, var(--np-color-surface-container-high));
256
+ }
257
+
258
+ .open .np-month-panel-shade {
259
+ translate: 0 0;
260
+ opacity: 1;
261
+ }
262
+
263
+ @media (prefers-reduced-motion: no-preference) {
264
+ /* Hidden only once the list has slid away. It shows at once, as a list still hidden while it
265
+ starts to slide in cannot take focus. */
266
+ .np-month-panel-list {
267
+ transition: visibility var(--np-motion-expressive-default-effects);
268
+ }
269
+
270
+ .np-month-panel-list.open {
271
+ transition: none;
272
+ }
273
+
274
+ .np-month-panel-shade {
275
+ transition:
276
+ translate var(--np-motion-expressive-default-effects),
277
+ opacity var(--np-motion-expressive-fast-effects);
278
+ }
279
+
280
+ .open .np-month-panel-shade {
281
+ transition:
282
+ translate var(--np-motion-expressive-default-effects),
283
+ opacity var(--np-motion-expressive-default-effects);
284
+
285
+ @starting-style {
286
+ translate: 0 -100%;
287
+ opacity: 0.6;
288
+ }
289
+ }
290
+ }
291
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { MonthPanelProps } from './internalTypes.js';
2
+ declare const MonthPanel: import("svelte").Component<MonthPanelProps, {
3
+ /** Focuses the day that takes focus. */ focus: () => void | undefined;
4
+ /** Back to the days, for the next time the picker opens. */ reset: () => void;
5
+ /**
6
+ * The keys of the picker, for its whole menu: Shift+M and Shift+Y from anywhere in it, Page up
7
+ * and Page down from the header, as the arrows they stand for. Escape with a list open goes back
8
+ * to the days, before a second one closes the picker.
9
+ */ handleKeydown: (event: KeyboardEvent) => void;
10
+ }, "">;
11
+ type MonthPanel = ReturnType<typeof MonthPanel>;
12
+ export default MonthPanel;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import '../internal/focus-ring.css'
2
3
  import CheckIcon from '../icons/CheckIcon.svelte'
3
4
  import { arrowKeyNav } from '../keyboard-nav.js'
4
5
  import Ripple from '../ripple/Ripple.svelte'
@@ -29,7 +30,12 @@
29
30
  aria-selected={selected}
30
31
  aria-disabled={option.disabled}
31
32
  tabindex={option.value === tabStopValue ? 0 : -1}
32
- class={['np-date-picker-selection-option', selected && 'selected']}
33
+ class={[
34
+ 'np-date-picker-selection-option',
35
+ 'np-focus-ring',
36
+ 'np-focus-ring-inset',
37
+ selected && 'selected',
38
+ ]}
33
39
  onclick={() => {
34
40
  if (option.disabled) return
35
41
  onselect?.(option.value)
@@ -114,9 +120,4 @@
114
120
  text-overflow: ellipsis;
115
121
  white-space: nowrap;
116
122
  }
117
-
118
- .np-date-picker-selection-option:focus-visible {
119
- outline: 3px solid var(--np-color-secondary);
120
- outline-offset: -3px;
121
- }
122
123
  </style>
@@ -1,3 +1,4 @@
1
+ import '../internal/focus-ring.css';
1
2
  import type { TimeColumnProps } from './internalTypes.js';
2
3
  declare const SelectionList: import("svelte").Component<TimeColumnProps, {}, "">;
3
4
  type SelectionList = ReturnType<typeof SelectionList>;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import '../internal/focus-ring.css'
2
3
  import { arrowKeyNav } from '../keyboard-nav.js'
3
4
  import Ripple from '../ripple/Ripple.svelte'
4
5
  import { revealSelected, tabStop } from './optionList.js'
@@ -28,7 +29,7 @@
28
29
  aria-selected={selected}
29
30
  aria-disabled={option.disabled}
30
31
  tabindex={option.value === tabStopValue ? 0 : -1}
31
- class={['np-time-option', selected && 'selected']}
32
+ class={['np-time-option', 'np-focus-ring', 'np-focus-ring-inset', selected && 'selected']}
32
33
  onclick={() => {
33
34
  if (option.disabled) return
34
35
  onselect?.(option.value)
@@ -106,9 +107,4 @@
106
107
  cursor: default;
107
108
  color: color-mix(in srgb, var(--np-color-on-surface) 38%, transparent);
108
109
  }
109
-
110
- .np-time-option:focus-visible {
111
- outline: 3px solid var(--np-color-secondary);
112
- outline-offset: -3px;
113
- }
114
110
  </style>
@@ -1,3 +1,4 @@
1
+ import '../internal/focus-ring.css';
1
2
  import type { TimeColumnProps } from './internalTypes.js';
2
3
  declare const TimeColumn: import("svelte").Component<TimeColumnProps, {}, "">;
3
4
  type TimeColumn = ReturnType<typeof TimeColumn>;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import '../internal/focus-ring.css'
2
3
  import Ripple from '../ripple/Ripple.svelte'
3
4
  import type { Attachment } from 'svelte/attachments'
4
5
  import type { HTMLAttributes } from 'svelte/elements'
@@ -98,6 +99,7 @@
98
99
  tabindex={year === tabStopYear ? 0 : -1}
99
100
  class={[
100
101
  'np-date-picker-year',
102
+ 'np-focus-ring',
101
103
  selected && 'selected',
102
104
  year === currentYear && !selected && 'current',
103
105
  ]}
@@ -132,12 +134,6 @@
132
134
  scrollbar-width: thin;
133
135
  }
134
136
 
135
- .np-date-picker-year-grid.entering {
136
- height: 0;
137
- opacity: 0.6;
138
- transition: none;
139
- }
140
-
141
137
  @media (prefers-reduced-motion: no-preference) {
142
138
  .np-date-picker-year-grid {
143
139
  transition:
@@ -189,9 +185,4 @@
189
185
  cursor: default;
190
186
  color: color-mix(in srgb, var(--np-color-on-surface) 38%, transparent);
191
187
  }
192
-
193
- .np-date-picker-year:focus-visible {
194
- outline: 3px solid var(--np-color-secondary);
195
- outline-offset: 2px;
196
- }
197
188
  </style>
@@ -1,3 +1,4 @@
1
+ import '../internal/focus-ring.css';
1
2
  import type { HTMLAttributes } from 'svelte/elements';
2
3
  interface YearGridProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect'> {
3
4
  yearRange: [number, number];
@@ -31,6 +31,10 @@ export declare const getWeekdayLabels: (locale: string | undefined, firstDayOfWe
31
31
  export declare const getMonthNames: (locale: string | undefined, month: "long" | "short") => string[];
32
32
  export declare const formatDate: (date: Date, locale?: string) => string;
33
33
  export declare const formatDateMedium: (date: Date, locale?: string) => string;
34
+ /** The headline of the modal picker, as M3 shows it: "Mon, Aug 17". */
35
+ export declare const formatDateHeadline: (date: Date, locale?: string) => string;
36
+ /** Each end in the headline of the range picker, as M3 shows it: "Aug 17". */
37
+ export declare const formatMonthDay: (date: Date, locale?: string) => string;
34
38
  export declare const formatDateLong: (date: Date, locale?: string) => string;
35
39
  export declare const formatMonthYear: (date: Date, locale?: string) => string;
36
40
  export declare const getDatePattern: (locale?: string) => string;