noph-ui 0.44.0 → 0.45.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 +10 -1
- package/dist/animation.d.ts +17 -0
- package/dist/animation.js +48 -0
- package/dist/app-bar/AppBar.svelte +280 -0
- package/dist/app-bar/AppBar.svelte.d.ts +4 -0
- package/dist/app-bar/index.d.ts +1 -0
- package/dist/app-bar/index.js +1 -0
- package/dist/app-bar/types.d.ts +35 -0
- package/dist/app-bar/types.js +1 -0
- package/dist/autocomplete/AutoComplete.svelte +10 -11
- package/dist/autocomplete/AutoComplete.svelte.d.ts +1 -1
- package/dist/autocomplete/types.d.ts +1 -1
- package/dist/badge/Badge.svelte +6 -1
- package/dist/badge/types.d.ts +0 -1
- package/dist/button/Button.svelte +5 -15
- package/dist/button/IconButton.svelte +5 -15
- package/dist/button/SegmentedButton.svelte +15 -1
- package/dist/button/SplitButton.svelte +92 -0
- package/dist/button/SplitButton.svelte.d.ts +4 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/button/types.d.ts +23 -4
- package/dist/card/types.d.ts +1 -1
- package/dist/checkbox/Checkbox.svelte +49 -58
- package/dist/chip/AssistChip.svelte +5 -5
- package/dist/chip/ChipSet.svelte +3 -11
- package/dist/chip/FilterChip.svelte +35 -27
- package/dist/chip/InputChip.svelte +13 -2
- package/dist/chip/SuggestionChip.svelte +29 -0
- package/dist/chip/SuggestionChip.svelte.d.ts +4 -0
- package/dist/chip/index.d.ts +1 -0
- package/dist/chip/index.js +1 -0
- package/dist/chip/types.d.ts +24 -9
- package/dist/date-picker/Calendar.svelte +1 -1
- package/dist/date-picker/DatePickerDialog.svelte +30 -8
- package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -1
- package/dist/date-picker/DateRangePicker.svelte +45 -10
- package/dist/date-picker/DateRangePicker.svelte.d.ts +4 -1
- package/dist/date-picker/DockedDatePicker.svelte +18 -4
- package/dist/date-picker/DockedDatePicker.svelte.d.ts +4 -1
- package/dist/date-picker/DockedDateTimePicker.svelte +18 -4
- package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +4 -1
- package/dist/date-picker/SelectionList.svelte +6 -32
- package/dist/date-picker/SelectionList.svelte.d.ts +2 -12
- package/dist/date-picker/TimeColumn.svelte +4 -29
- package/dist/date-picker/exitVisibility.svelte.d.ts +10 -0
- package/dist/date-picker/exitVisibility.svelte.js +30 -0
- package/dist/date-picker/optionList.d.ts +16 -0
- package/dist/date-picker/optionList.js +43 -0
- package/dist/date-picker/types.d.ts +11 -4
- package/dist/dialog/Dialog.svelte +77 -68
- package/dist/dialog/Dialog.svelte.d.ts +3 -3
- package/dist/dialog/types.d.ts +5 -3
- package/dist/fab/ExtendedFab.svelte +281 -0
- package/dist/fab/ExtendedFab.svelte.d.ts +5 -0
- package/dist/fab/Fab.svelte +239 -0
- package/dist/fab/Fab.svelte.d.ts +5 -0
- package/dist/fab/FabMenu.svelte +255 -0
- package/dist/fab/FabMenu.svelte.d.ts +7 -0
- package/dist/fab/index.d.ts +3 -0
- package/dist/fab/index.js +3 -0
- package/dist/fab/types.d.ts +58 -0
- package/dist/fab/types.js +1 -0
- package/dist/icons/ArrowBackIcon.svelte +13 -0
- package/dist/icons/ArrowBackIcon.svelte.d.ts +7 -0
- package/dist/icons/ChevronDownIcon.svelte +3 -0
- package/dist/icons/ChevronDownIcon.svelte.d.ts +26 -0
- package/dist/icons/SearchIcon.svelte +15 -0
- package/dist/icons/SearchIcon.svelte.d.ts +7 -0
- package/dist/icons/index.d.ts +3 -0
- package/dist/icons/index.js +3 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/internal/focus-ring.css +12 -0
- package/dist/menu/Menu.svelte +12 -7
- package/dist/menu/Menu.svelte.d.ts +2 -2
- package/dist/menu/types.d.ts +0 -2
- package/dist/navigation-bar/NavigationBar.svelte +66 -0
- package/dist/navigation-bar/NavigationBar.svelte.d.ts +4 -0
- package/dist/navigation-bar/NavigationBarItem.svelte +261 -0
- package/dist/navigation-bar/NavigationBarItem.svelte.d.ts +5 -0
- package/dist/navigation-bar/index.d.ts +2 -0
- package/dist/navigation-bar/index.js +2 -0
- package/dist/navigation-bar/types.d.ts +29 -0
- package/dist/navigation-bar/types.js +1 -0
- package/dist/navigation-drawer/NavigationDrawer.svelte +128 -76
- package/dist/navigation-drawer/NavigationDrawer.svelte.d.ts +4 -1
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +10 -3
- package/dist/navigation-drawer/types.d.ts +6 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +16 -0
- package/dist/navigation-rail/types.d.ts +3 -0
- package/dist/popover.svelte.d.ts +14 -0
- package/dist/popover.svelte.js +31 -0
- package/dist/press.svelte.d.ts +19 -0
- package/dist/press.svelte.js +45 -0
- package/dist/radio/Radio.svelte +26 -7
- package/dist/radio/types.d.ts +3 -0
- package/dist/search/Search.svelte +472 -0
- package/dist/search/Search.svelte.d.ts +5 -0
- package/dist/search/index.d.ts +1 -0
- package/dist/search/index.js +1 -0
- package/dist/search/types.d.ts +39 -0
- package/dist/search/types.js +1 -0
- package/dist/select/Select.svelte +48 -87
- package/dist/sheet/Sheet.svelte +260 -0
- package/dist/sheet/Sheet.svelte.d.ts +7 -0
- package/dist/sheet/index.d.ts +1 -0
- package/dist/sheet/index.js +1 -0
- package/dist/sheet/types.d.ts +19 -0
- package/dist/sheet/types.js +1 -0
- package/dist/slider/Slider.svelte +154 -30
- package/dist/slider/types.d.ts +3 -0
- package/dist/snackbar/Snackbar.svelte +48 -37
- package/dist/snackbar/Snackbar.svelte.d.ts +2 -2
- package/dist/snackbar/types.d.ts +3 -2
- package/dist/switch/Switch.svelte +51 -20
- package/dist/switch/types.d.ts +3 -0
- package/dist/tabs/Tab.svelte +8 -12
- package/dist/tabs/Tabs.svelte +3 -3
- package/dist/text-field/TextField.svelte +2 -2
- package/dist/toolbar/Toolbar.svelte +133 -0
- package/dist/toolbar/Toolbar.svelte.d.ts +4 -0
- package/dist/toolbar/index.d.ts +1 -0
- package/dist/toolbar/index.js +1 -0
- package/dist/toolbar/types.d.ts +15 -0
- package/dist/toolbar/types.js +1 -0
- package/dist/tooltip/RichTooltip.svelte +25 -12
- package/dist/tooltip/RichTooltip.svelte.d.ts +2 -2
- package/dist/tooltip/Tooltip.svelte +22 -11
- package/dist/tooltip/Tooltip.svelte.d.ts +4 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.js +6 -0
- package/package.json +13 -13
- package/dist/date-picker/enterAnimation.d.ts +0 -1
- package/dist/date-picker/enterAnimation.js +0 -10
package/dist/chip/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as FilterChip } from './FilterChip.svelte';
|
|
2
2
|
export { default as AssistChip } from './AssistChip.svelte';
|
|
3
3
|
export { default as InputChip } from './InputChip.svelte';
|
|
4
|
+
export { default as SuggestionChip } from './SuggestionChip.svelte';
|
|
4
5
|
export { default as ChipSet } from './ChipSet.svelte';
|
package/dist/chip/types.d.ts
CHANGED
|
@@ -1,44 +1,59 @@
|
|
|
1
|
-
import type { ButtonElement
|
|
1
|
+
import type { ButtonElement } from '../button/types.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
import type { HTMLAttributes, MouseEventHandler } from 'svelte/elements';
|
|
3
|
+
import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes, MouseEventHandler } from 'svelte/elements';
|
|
4
4
|
export interface ChipSetProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
chipsCount?: number;
|
|
6
5
|
element?: HTMLDivElement;
|
|
7
6
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Declared from the element attributes rather than `ButtonProps`, which leaked `size`, `shape`,
|
|
9
|
+
* `toggle`, `loading` and `selected` onto a chip. A chip has one size and one shape.
|
|
10
|
+
*/
|
|
11
|
+
export interface AssistChipProps extends HTMLAttributes<ButtonElement>, Omit<HTMLButtonAttributes, keyof HTMLAttributes<HTMLButtonElement> | 'type'>, Omit<HTMLAnchorAttributes, keyof HTMLAttributes<HTMLAnchorElement> | 'type'> {
|
|
12
|
+
variant?: 'outlined' | 'elevated';
|
|
13
|
+
disabled?: boolean | null;
|
|
11
14
|
label?: string;
|
|
12
15
|
icon?: Snippet;
|
|
13
16
|
element?: HTMLElement;
|
|
17
|
+
type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
|
|
14
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* A suggestion chip carries a generated suggestion rather than an action, so it is text-first and
|
|
21
|
+
* takes no leading icon. Everything else matches an assist chip.
|
|
22
|
+
*/
|
|
23
|
+
export type SuggestionChipProps = Omit<AssistChipProps, 'icon'>;
|
|
15
24
|
export interface FilterChipProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
25
|
selected?: boolean;
|
|
17
26
|
removable?: boolean;
|
|
18
27
|
disabled?: boolean;
|
|
19
|
-
|
|
28
|
+
variant?: 'outlined' | 'elevated';
|
|
20
29
|
label?: string;
|
|
21
30
|
icon?: Snippet;
|
|
22
|
-
|
|
31
|
+
removeAriaLabel?: string;
|
|
23
32
|
element?: HTMLDivElement;
|
|
24
33
|
name?: string;
|
|
25
34
|
value?: string;
|
|
26
35
|
group?: (string | number)[] | null;
|
|
27
36
|
defaultSelected?: boolean | null;
|
|
28
37
|
onremove?: MouseEventHandler<ButtonElement>;
|
|
38
|
+
issues?: {
|
|
39
|
+
message: string;
|
|
40
|
+
}[];
|
|
29
41
|
}
|
|
30
42
|
export interface InputChipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onclick'> {
|
|
31
43
|
selected?: boolean;
|
|
32
44
|
disabled?: boolean;
|
|
33
45
|
label?: string;
|
|
34
46
|
icon?: Snippet;
|
|
35
|
-
|
|
47
|
+
removeAriaLabel?: string;
|
|
36
48
|
element?: HTMLDivElement;
|
|
37
49
|
actionElement?: HTMLButtonElement;
|
|
38
50
|
name?: string;
|
|
39
51
|
value?: string | number;
|
|
40
52
|
onclick?: MouseEventHandler<HTMLButtonElement>;
|
|
41
53
|
onremove?: MouseEventHandler<ButtonElement>;
|
|
54
|
+
issues?: {
|
|
55
|
+
message: string;
|
|
56
|
+
}[];
|
|
42
57
|
}
|
|
43
58
|
export interface ChipSetContext {
|
|
44
59
|
chips: {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { afterTwoFrames } from '../animation.js'
|
|
2
3
|
import Button from '../button/Button.svelte'
|
|
3
4
|
import IconButton from '../button/IconButton.svelte'
|
|
4
5
|
import Dialog from '../dialog/Dialog.svelte'
|
|
6
|
+
import { syncOpenEffect } from '../popover.svelte.js'
|
|
5
7
|
import Divider from '../divider/Divider.svelte'
|
|
8
|
+
import { exitVisibility } from './exitVisibility.svelte.js'
|
|
6
9
|
import ArrowDropDownIcon from '../icons/ArrowDropDownIcon.svelte'
|
|
7
10
|
import CalendarToday from '../icons/CalendarToday.svelte'
|
|
8
11
|
import ChevronLeftIcon from '../icons/ChevronLeftIcon.svelte'
|
|
@@ -29,7 +32,6 @@
|
|
|
29
32
|
toISODate,
|
|
30
33
|
today as getToday,
|
|
31
34
|
} from './dateUtils.js'
|
|
32
|
-
import { afterTwoFrames } from './enterAnimation.js'
|
|
33
35
|
import type { DatePickerDialogProps, ISODate } from './types.js'
|
|
34
36
|
|
|
35
37
|
let {
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
headline,
|
|
49
51
|
label = 'Date',
|
|
50
52
|
supportingText,
|
|
53
|
+
issues,
|
|
51
54
|
name,
|
|
52
55
|
form,
|
|
53
56
|
modeToggle = false,
|
|
@@ -77,6 +80,8 @@
|
|
|
77
80
|
let confirming = false
|
|
78
81
|
let monthBeforeOpen: ISODate | undefined
|
|
79
82
|
|
|
83
|
+
const exit = exitVisibility()
|
|
84
|
+
|
|
80
85
|
let todayValue = $derived(getToday())
|
|
81
86
|
let week = $derived(firstDayOfWeek ?? getFirstDayOfWeek(locale))
|
|
82
87
|
let minDate = $derived(parseISODate(min))
|
|
@@ -125,12 +130,25 @@
|
|
|
125
130
|
cancelYearEnter = afterTwoFrames(() => (yearEnter = false))
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
/*
|
|
134
|
+
* The same pair every overlay in the library exports. A trigger with `command="show-modal"` and
|
|
135
|
+
* `commandfor` needs neither, so these are for the times there is no trigger to point at the
|
|
136
|
+
* dialog. `open` follows either way, written back from the dialog's own toggle event.
|
|
137
|
+
*/
|
|
138
|
+
export const show = () => {
|
|
139
|
+
if (element && !element.open) element.showModal()
|
|
140
|
+
}
|
|
129
141
|
|
|
130
|
-
|
|
131
|
-
if (open
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
export const close = () => {
|
|
143
|
+
if (element?.open) element.close()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
syncOpenEffect(
|
|
147
|
+
() => element,
|
|
148
|
+
() => open,
|
|
149
|
+
show,
|
|
150
|
+
close,
|
|
151
|
+
)
|
|
134
152
|
|
|
135
153
|
const setMonth = (date: Date) => {
|
|
136
154
|
displayMonth = toISODate(startOfMonth(date))
|
|
@@ -197,6 +215,7 @@
|
|
|
197
215
|
const nowOpen = event.newState === 'open'
|
|
198
216
|
open = nowOpen
|
|
199
217
|
if (nowOpen) {
|
|
218
|
+
exit.show()
|
|
200
219
|
monthBeforeOpen = displayMonth
|
|
201
220
|
confirming = false
|
|
202
221
|
pending = value ?? undefined
|
|
@@ -206,14 +225,16 @@
|
|
|
206
225
|
syncText()
|
|
207
226
|
focusCalendarWhenReady()
|
|
208
227
|
} else {
|
|
209
|
-
displayMonth = monthBeforeOpen
|
|
210
228
|
if (!confirming) oncancel?.()
|
|
211
229
|
confirming = false
|
|
230
|
+
// Handing the month back before the dialog has gone would step the calendar to another
|
|
231
|
+
// month in front of the user, so that waits for the fade as well.
|
|
232
|
+
exit.scheduleExit(element, () => (displayMonth = monthBeforeOpen))
|
|
212
233
|
}
|
|
213
234
|
}}
|
|
214
235
|
>
|
|
215
236
|
<input class="np-date-picker-dialog-value" type="hidden" value={value ?? ''} {name} {form} />
|
|
216
|
-
{#if
|
|
237
|
+
{#if exit.visible}
|
|
217
238
|
<div class="np-date-picker-dialog-content">
|
|
218
239
|
<div class="np-date-picker-dialog-header">
|
|
219
240
|
<div class="np-date-picker-dialog-header-text">
|
|
@@ -247,6 +268,7 @@
|
|
|
247
268
|
<div class={['np-date-picker-dialog-input', modeEnter && modeEnter]}>
|
|
248
269
|
<TextField
|
|
249
270
|
{label}
|
|
271
|
+
{issues}
|
|
250
272
|
supportingText={supportingText ?? pattern}
|
|
251
273
|
value={text}
|
|
252
274
|
aria-invalid={inputInvalid ? 'true' : undefined}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { DatePickerDialogProps } from './types.ts';
|
|
2
|
-
declare const DatePickerDialog: import("svelte").Component<DatePickerDialogProps, {
|
|
2
|
+
declare const DatePickerDialog: import("svelte").Component<DatePickerDialogProps, {
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}, "element" | "value" | "open" | "displayMonth">;
|
|
3
6
|
type DatePickerDialog = ReturnType<typeof DatePickerDialog>;
|
|
4
7
|
export default DatePickerDialog;
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
import Dialog from '../dialog/Dialog.svelte'
|
|
5
5
|
import Divider from '../divider/Divider.svelte'
|
|
6
6
|
import CloseIcon from '../icons/CloseIcon.svelte'
|
|
7
|
+
import { syncOpenEffect } from '../popover.svelte.js'
|
|
7
8
|
import { tick } from 'svelte'
|
|
8
9
|
import Calendar from './Calendar.svelte'
|
|
10
|
+
import { exitVisibility } from './exitVisibility.svelte.js'
|
|
9
11
|
import {
|
|
10
12
|
clampDate,
|
|
11
13
|
compareDays,
|
|
@@ -34,6 +36,10 @@
|
|
|
34
36
|
isDateEnabled,
|
|
35
37
|
adjacentMonthDays = false,
|
|
36
38
|
title = 'Select range',
|
|
39
|
+
headline,
|
|
40
|
+
name,
|
|
41
|
+
endName,
|
|
42
|
+
form,
|
|
37
43
|
startLabel = 'Start date',
|
|
38
44
|
endLabel = 'End date',
|
|
39
45
|
cancelLabel = 'Cancel',
|
|
@@ -52,6 +58,8 @@
|
|
|
52
58
|
let focusedDay = $state<Date | undefined>(undefined)
|
|
53
59
|
let confirming = false
|
|
54
60
|
|
|
61
|
+
const exit = exitVisibility()
|
|
62
|
+
|
|
55
63
|
let todayValue = $derived(getToday())
|
|
56
64
|
let week = $derived(firstDayOfWeek ?? getFirstDayOfWeek(locale))
|
|
57
65
|
let minDate = $derived(parseISODate(min))
|
|
@@ -108,12 +116,25 @@
|
|
|
108
116
|
if (target && scroller) scroller.scrollTop = target.offsetTop
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
/*
|
|
120
|
+
* The same pair every overlay in the library exports. A trigger with `command="show-modal"` and
|
|
121
|
+
* `commandfor` needs neither, so these are for the times there is no trigger to point at the
|
|
122
|
+
* dialog. `open` follows either way, written back from the dialog's own toggle event.
|
|
123
|
+
*/
|
|
124
|
+
export const show = () => {
|
|
125
|
+
if (element && !element.open) element.showModal()
|
|
126
|
+
}
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
if (open
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
export const close = () => {
|
|
129
|
+
if (element?.open) element.close()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
syncOpenEffect(
|
|
133
|
+
() => element,
|
|
134
|
+
() => open,
|
|
135
|
+
show,
|
|
136
|
+
close,
|
|
137
|
+
)
|
|
117
138
|
|
|
118
139
|
let prepending = false
|
|
119
140
|
|
|
@@ -176,6 +197,7 @@
|
|
|
176
197
|
const nowOpen = event.newState === 'open'
|
|
177
198
|
open = nowOpen
|
|
178
199
|
if (nowOpen) {
|
|
200
|
+
exit.show()
|
|
179
201
|
confirming = false
|
|
180
202
|
pending = { ...value }
|
|
181
203
|
focusedDay = undefined
|
|
@@ -184,10 +206,19 @@
|
|
|
184
206
|
} else {
|
|
185
207
|
if (!confirming) oncancel?.()
|
|
186
208
|
confirming = false
|
|
209
|
+
exit.scheduleExit(element)
|
|
187
210
|
}
|
|
188
211
|
}}
|
|
189
212
|
>
|
|
190
|
-
{
|
|
213
|
+
<input class="np-date-range-picker-value" type="hidden" value={value.start ?? ''} {name} {form} />
|
|
214
|
+
<input
|
|
215
|
+
class="np-date-range-picker-value"
|
|
216
|
+
type="hidden"
|
|
217
|
+
value={value.end ?? ''}
|
|
218
|
+
name={endName}
|
|
219
|
+
{form}
|
|
220
|
+
/>
|
|
221
|
+
{#if exit.visible}
|
|
191
222
|
<div class="np-date-range-picker-content">
|
|
192
223
|
<div class="np-date-range-picker-header">
|
|
193
224
|
<div class="np-date-range-picker-top-bar">
|
|
@@ -201,9 +232,13 @@
|
|
|
201
232
|
<div class="np-date-range-picker-header-text">
|
|
202
233
|
<span class="np-date-range-picker-title">{title}</span>
|
|
203
234
|
<h2 class="np-date-range-picker-headline">
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
235
|
+
{#if headline}
|
|
236
|
+
<span>{headline}</span>
|
|
237
|
+
{:else}
|
|
238
|
+
<span class={[!startDate && 'placeholder']}>{headlineStart}</span>
|
|
239
|
+
<span aria-hidden="true">–</span>
|
|
240
|
+
<span class={[!endDate && 'placeholder']}>{headlineEnd}</span>
|
|
241
|
+
{/if}
|
|
207
242
|
</h2>
|
|
208
243
|
</div>
|
|
209
244
|
</div>
|
|
@@ -248,7 +283,7 @@
|
|
|
248
283
|
{/if}
|
|
249
284
|
|
|
250
285
|
{#snippet actions()}
|
|
251
|
-
{#if
|
|
286
|
+
{#if exit.visible}
|
|
252
287
|
<Button type="button" variant="text" onclick={cancel}>{cancelLabel}</Button>
|
|
253
288
|
<Button type="button" variant="text" disabled={!canConfirm} onclick={confirm}>
|
|
254
289
|
{confirmLabel}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { DateRangePickerProps } from './types.ts';
|
|
2
|
-
declare const DateRangePicker: import("svelte").Component<DateRangePickerProps, {
|
|
2
|
+
declare const DateRangePicker: import("svelte").Component<DateRangePickerProps, {
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}, "element" | "value" | "open">;
|
|
3
6
|
type DateRangePicker = ReturnType<typeof DateRangePicker>;
|
|
4
7
|
export default DateRangePicker;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import CalendarToday from '../icons/CalendarToday.svelte'
|
|
6
6
|
import Menu from '../menu/Menu.svelte'
|
|
7
7
|
import { onFormReset } from '../form-reset.js'
|
|
8
|
+
import { syncOpenEffect } from '../popover.svelte.js'
|
|
8
9
|
import TextField from '../text-field/TextField.svelte'
|
|
9
10
|
import { onMount, tick } from 'svelte'
|
|
10
11
|
import Calendar from './Calendar.svelte'
|
|
@@ -214,10 +215,23 @@
|
|
|
214
215
|
focusCalendar()
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
/*
|
|
219
|
+
* The same pair every overlay in the library exports, for the times there is no trigger to point
|
|
220
|
+
* at the picker. `close()` leaves focus where it is; only a close the user asked for, through the
|
|
221
|
+
* field or the cancel button, hands focus back to the input.
|
|
222
|
+
*/
|
|
223
|
+
export const show = () => openPicker()
|
|
224
|
+
|
|
225
|
+
export const close = () => {
|
|
226
|
+
if (isShowing()) menuElement?.hidePopover()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
syncOpenEffect(
|
|
230
|
+
() => menuElement,
|
|
231
|
+
() => open,
|
|
232
|
+
show,
|
|
233
|
+
close,
|
|
234
|
+
)
|
|
221
235
|
|
|
222
236
|
const focusCalendar = () => {
|
|
223
237
|
menuElement?.querySelector<HTMLElement>('.np-calendar-day[tabindex="0"]')?.focus()
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { DockedDatePickerProps } from './types.ts';
|
|
2
|
-
declare const DockedDatePicker: import("svelte").Component<DockedDatePickerProps, {
|
|
2
|
+
declare const DockedDatePicker: import("svelte").Component<DockedDatePickerProps, {
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}, "element" | "value" | "open" | "displayMonth">;
|
|
3
6
|
type DockedDatePicker = ReturnType<typeof DockedDatePicker>;
|
|
4
7
|
export default DockedDatePicker;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import CalendarToday from '../icons/CalendarToday.svelte'
|
|
6
6
|
import Menu from '../menu/Menu.svelte'
|
|
7
7
|
import { onFormReset } from '../form-reset.js'
|
|
8
|
+
import { syncOpenEffect } from '../popover.svelte.js'
|
|
8
9
|
import TextField from '../text-field/TextField.svelte'
|
|
9
10
|
import { onMount, tick } from 'svelte'
|
|
10
11
|
import Calendar from './Calendar.svelte'
|
|
@@ -297,10 +298,23 @@
|
|
|
297
298
|
focusCalendar()
|
|
298
299
|
}
|
|
299
300
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
301
|
+
/*
|
|
302
|
+
* The same pair every overlay in the library exports, for the times there is no trigger to point
|
|
303
|
+
* at the picker. `close()` leaves focus where it is; only a close the user asked for, through the
|
|
304
|
+
* field or the cancel button, hands focus back to the input.
|
|
305
|
+
*/
|
|
306
|
+
export const show = () => openPicker()
|
|
307
|
+
|
|
308
|
+
export const close = () => {
|
|
309
|
+
if (isShowing()) menuElement?.hidePopover()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
syncOpenEffect(
|
|
313
|
+
() => menuElement,
|
|
314
|
+
() => open,
|
|
315
|
+
show,
|
|
316
|
+
close,
|
|
317
|
+
)
|
|
304
318
|
|
|
305
319
|
const focusCalendar = () => {
|
|
306
320
|
menuElement?.querySelector<HTMLElement>('.np-calendar-day[tabindex="0"]')?.focus()
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { DockedDateTimePickerProps } from './types.ts';
|
|
2
|
-
declare const DockedDateTimePicker: import("svelte").Component<DockedDateTimePickerProps, {
|
|
2
|
+
declare const DockedDateTimePicker: import("svelte").Component<DockedDateTimePickerProps, {
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}, "element" | "value" | "open" | "displayMonth">;
|
|
3
6
|
type DockedDateTimePicker = ReturnType<typeof DockedDateTimePicker>;
|
|
4
7
|
export default DockedDateTimePicker;
|
|
@@ -2,41 +2,15 @@
|
|
|
2
2
|
import CheckIcon from '../icons/CheckIcon.svelte'
|
|
3
3
|
import { arrowKeyNav } from '../keyboard-nav.js'
|
|
4
4
|
import Ripple from '../ripple/Ripple.svelte'
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
5
|
+
import { revealSelected, tabStop } from './optionList.js'
|
|
6
|
+
import type { TimeColumnProps } from './types.js'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
value: number
|
|
10
|
-
label: string
|
|
11
|
-
disabled?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface SelectionListProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect'> {
|
|
15
|
-
options: SelectionOption[]
|
|
16
|
-
value?: number
|
|
17
|
-
onselect?: (value: number) => void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let { options, value, onselect, ...attributes }: SelectionListProps = $props()
|
|
8
|
+
let { options, value, onselect, ...attributes }: TimeColumnProps = $props()
|
|
21
9
|
|
|
22
10
|
const handleKeydown = arrowKeyNav('[role="option"]')
|
|
11
|
+
const reveal = revealSelected(true)
|
|
23
12
|
|
|
24
|
-
let tabStopValue = $derived
|
|
25
|
-
if (options.some((option) => option.value === value)) return value
|
|
26
|
-
const offered = options.filter((option) => !option.disabled)
|
|
27
|
-
const pool = offered.length ? offered : options
|
|
28
|
-
if (!pool.length) return undefined
|
|
29
|
-
if (value === undefined) return pool[0].value
|
|
30
|
-
return pool.reduce((nearest, option) =>
|
|
31
|
-
Math.abs(option.value - value) < Math.abs(nearest.value - value) ? option : nearest,
|
|
32
|
-
).value
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const centerInList: Attachment<HTMLElement> = (element) => {
|
|
36
|
-
const list = element.closest<HTMLElement>('.np-date-picker-selection-list')
|
|
37
|
-
if (!list) return
|
|
38
|
-
list.scrollTop = element.offsetTop - list.clientHeight / 2 + element.offsetHeight / 2
|
|
39
|
-
}
|
|
13
|
+
let tabStopValue = $derived(tabStop(options, value))
|
|
40
14
|
</script>
|
|
41
15
|
|
|
42
16
|
<div
|
|
@@ -49,7 +23,7 @@
|
|
|
49
23
|
{#each options as option (option.value)}
|
|
50
24
|
{@const selected = option.value === value}
|
|
51
25
|
<button
|
|
52
|
-
{@attach selected &&
|
|
26
|
+
{@attach selected && reveal}
|
|
53
27
|
type="button"
|
|
54
28
|
role="option"
|
|
55
29
|
aria-selected={selected}
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
value: number;
|
|
4
|
-
label: string;
|
|
5
|
-
disabled?: boolean;
|
|
6
|
-
}
|
|
7
|
-
interface SelectionListProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect'> {
|
|
8
|
-
options: SelectionOption[];
|
|
9
|
-
value?: number;
|
|
10
|
-
onselect?: (value: number) => void;
|
|
11
|
-
}
|
|
12
|
-
declare const SelectionList: import("svelte").Component<SelectionListProps, {}, "">;
|
|
1
|
+
import type { TimeColumnProps } from './types.ts';
|
|
2
|
+
declare const SelectionList: import("svelte").Component<TimeColumnProps, {}, "">;
|
|
13
3
|
type SelectionList = ReturnType<typeof SelectionList>;
|
|
14
4
|
export default SelectionList;
|
|
@@ -1,40 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { arrowKeyNav } from '../keyboard-nav.js'
|
|
3
3
|
import Ripple from '../ripple/Ripple.svelte'
|
|
4
|
-
import
|
|
4
|
+
import { revealSelected, tabStop } from './optionList.js'
|
|
5
5
|
import type { TimeColumnProps } from './types.js'
|
|
6
6
|
|
|
7
7
|
let { options, value, onselect, ...attributes }: TimeColumnProps = $props()
|
|
8
8
|
|
|
9
9
|
const handleKeydown = arrowKeyNav('[role="option"]')
|
|
10
|
+
const reveal = revealSelected()
|
|
10
11
|
|
|
11
|
-
let tabStopValue = $derived
|
|
12
|
-
if (options.some((option) => option.value === value)) return value
|
|
13
|
-
const offered = options.filter((option) => !option.disabled)
|
|
14
|
-
const pool = offered.length ? offered : options
|
|
15
|
-
if (!pool.length) return undefined
|
|
16
|
-
if (value === undefined) return pool[0].value
|
|
17
|
-
return pool.reduce((nearest, option) =>
|
|
18
|
-
Math.abs(option.value - value) < Math.abs(nearest.value - value) ? option : nearest,
|
|
19
|
-
).value
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
let centred = false
|
|
23
|
-
|
|
24
|
-
const revealSelected: Attachment<HTMLElement> = (element) => {
|
|
25
|
-
const column = element.parentElement
|
|
26
|
-
if (!column) return
|
|
27
|
-
const top = element.offsetTop
|
|
28
|
-
const bottom = top + element.offsetHeight
|
|
29
|
-
if (!centred) {
|
|
30
|
-
centred = true
|
|
31
|
-
column.scrollTop = top - column.clientHeight / 2 + element.offsetHeight / 2
|
|
32
|
-
} else if (top < column.scrollTop) {
|
|
33
|
-
column.scrollTop = top
|
|
34
|
-
} else if (bottom > column.scrollTop + column.clientHeight) {
|
|
35
|
-
column.scrollTop = bottom - column.clientHeight
|
|
36
|
-
}
|
|
37
|
-
}
|
|
12
|
+
let tabStopValue = $derived(tabStop(options, value))
|
|
38
13
|
</script>
|
|
39
14
|
|
|
40
15
|
<div
|
|
@@ -47,7 +22,7 @@
|
|
|
47
22
|
{#each options as option (option.value)}
|
|
48
23
|
{@const selected = option.value === value}
|
|
49
24
|
<button
|
|
50
|
-
{@attach selected &&
|
|
25
|
+
{@attach selected && reveal}
|
|
51
26
|
type="button"
|
|
52
27
|
role="option"
|
|
53
28
|
aria-selected={selected}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `visible` for a dialog that fades itself out, so it stays on screen after `open` has flipped to
|
|
3
|
+
* `false`. Content tied to `open` would leave the fading dialog empty a frame before it starts to
|
|
4
|
+
* leave, so this holds `visible` true until the exit animation has actually finished.
|
|
5
|
+
*/
|
|
6
|
+
export declare const exitVisibility: () => {
|
|
7
|
+
readonly visible: boolean;
|
|
8
|
+
show: () => void;
|
|
9
|
+
scheduleExit: (element: Element | undefined, onDone?: () => void) => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { afterExit } from '../animation.js';
|
|
2
|
+
/**
|
|
3
|
+
* `visible` for a dialog that fades itself out, so it stays on screen after `open` has flipped to
|
|
4
|
+
* `false`. Content tied to `open` would leave the fading dialog empty a frame before it starts to
|
|
5
|
+
* leave, so this holds `visible` true until the exit animation has actually finished.
|
|
6
|
+
*/
|
|
7
|
+
export const exitVisibility = () => {
|
|
8
|
+
let visible = $state(false);
|
|
9
|
+
let cancelExit;
|
|
10
|
+
// A dialog torn down mid fade has nothing left to reveal, and running a stale `onDone` against a
|
|
11
|
+
// parent that has already dropped its state is worse than not running it at all.
|
|
12
|
+
$effect(() => () => cancelExit?.());
|
|
13
|
+
return {
|
|
14
|
+
get visible() {
|
|
15
|
+
return visible;
|
|
16
|
+
},
|
|
17
|
+
show: () => {
|
|
18
|
+
cancelExit?.();
|
|
19
|
+
cancelExit = undefined;
|
|
20
|
+
visible = true;
|
|
21
|
+
},
|
|
22
|
+
scheduleExit: (element, onDone) => {
|
|
23
|
+
cancelExit = afterExit(element, () => {
|
|
24
|
+
visible = false;
|
|
25
|
+
onDone?.();
|
|
26
|
+
cancelExit = undefined;
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
import type { TimeOption } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The option that carries the list's single tab stop: the selected one, or else the offered option
|
|
5
|
+
* nearest to the value, so arriving with the keyboard lands next to where the value already is.
|
|
6
|
+
*/
|
|
7
|
+
export declare const tabStop: (options: TimeOption[], value: number | undefined) => number | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Scrolls the selected option into view inside its own list, rather than through `scrollIntoView`,
|
|
10
|
+
* which takes every scrollable ancestor with it and would move the page behind the picker.
|
|
11
|
+
*
|
|
12
|
+
* `centre` puts the option in the middle of the list every time it changes. Otherwise the list is
|
|
13
|
+
* centred once, on the option it opens with, and afterwards the selection is only pulled to the
|
|
14
|
+
* nearest edge when it has moved out of view, so stepping through options does not jump the list.
|
|
15
|
+
*/
|
|
16
|
+
export declare const revealSelected: (centre?: boolean) => Attachment<HTMLElement>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The option that carries the list's single tab stop: the selected one, or else the offered option
|
|
3
|
+
* nearest to the value, so arriving with the keyboard lands next to where the value already is.
|
|
4
|
+
*/
|
|
5
|
+
export const tabStop = (options, value) => {
|
|
6
|
+
if (options.some((option) => option.value === value))
|
|
7
|
+
return value;
|
|
8
|
+
const offered = options.filter((option) => !option.disabled);
|
|
9
|
+
const pool = offered.length ? offered : options;
|
|
10
|
+
if (!pool.length)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return pool[0].value;
|
|
14
|
+
return pool.reduce((nearest, option) => Math.abs(option.value - value) < Math.abs(nearest.value - value) ? option : nearest).value;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Scrolls the selected option into view inside its own list, rather than through `scrollIntoView`,
|
|
18
|
+
* which takes every scrollable ancestor with it and would move the page behind the picker.
|
|
19
|
+
*
|
|
20
|
+
* `centre` puts the option in the middle of the list every time it changes. Otherwise the list is
|
|
21
|
+
* centred once, on the option it opens with, and afterwards the selection is only pulled to the
|
|
22
|
+
* nearest edge when it has moved out of view, so stepping through options does not jump the list.
|
|
23
|
+
*/
|
|
24
|
+
export const revealSelected = (centre = false) => {
|
|
25
|
+
let centred = false;
|
|
26
|
+
return (element) => {
|
|
27
|
+
const list = element.parentElement;
|
|
28
|
+
if (!list)
|
|
29
|
+
return;
|
|
30
|
+
const top = element.offsetTop;
|
|
31
|
+
const bottom = top + element.offsetHeight;
|
|
32
|
+
if (centre || !centred) {
|
|
33
|
+
centred = true;
|
|
34
|
+
list.scrollTop = top - list.clientHeight / 2 + element.offsetHeight / 2;
|
|
35
|
+
}
|
|
36
|
+
else if (top < list.scrollTop) {
|
|
37
|
+
list.scrollTop = top;
|
|
38
|
+
}
|
|
39
|
+
else if (bottom > list.scrollTop + list.clientHeight) {
|
|
40
|
+
list.scrollTop = bottom - list.clientHeight;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|