noph-ui 0.51.2 → 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.
- package/dist/animation.d.ts +14 -1
- package/dist/animation.js +55 -2
- package/dist/app-bar/AppBar.svelte +71 -7
- package/dist/app-bar/types.d.ts +6 -0
- package/dist/autocomplete/AutoComplete.svelte +6 -31
- package/dist/autocomplete/AutoComplete.svelte.d.ts +1 -1
- package/dist/badge/Badge.svelte +12 -4
- package/dist/badge/description.d.ts +4 -0
- package/dist/badge/description.js +4 -0
- package/dist/button/Button.svelte +53 -30
- package/dist/button/ButtonGroup.svelte +139 -19
- package/dist/button/ButtonGroup.svelte.d.ts +1 -1
- package/dist/button/IconButton.svelte +51 -33
- package/dist/button/SegmentedButton.svelte +52 -15
- package/dist/button/SplitButton.svelte +77 -3
- package/dist/button/SplitButton.svelte.d.ts +1 -1
- package/dist/button/buttonGroup.d.ts +0 -4
- package/dist/button/buttonGroup.js +0 -9
- package/dist/button/groupContext.d.ts +10 -0
- package/dist/button/groupContext.js +4 -0
- package/dist/button/types.d.ts +19 -0
- package/dist/card/Card.svelte +3 -19
- package/dist/card/types.d.ts +12 -2
- package/dist/carousel/Carousel.svelte +77 -14
- package/dist/carousel/CarouselItem.svelte +26 -14
- package/dist/carousel/carouselMorph.js +1 -1
- package/dist/carousel/carouselStrategy.js +25 -27
- package/dist/checkbox/Checkbox.svelte +6 -8
- package/dist/chip/ChipSet.svelte +28 -2
- package/dist/chip/FilterChip.svelte +33 -17
- package/dist/chip/InputChip.svelte +127 -26
- package/dist/chip/SuggestionChip.svelte +6 -22
- package/dist/chip/SuggestionChip.svelte.d.ts +1 -2
- package/dist/chip/types.d.ts +8 -1
- package/dist/date-picker/Calendar.svelte +34 -31
- package/dist/date-picker/Calendar.svelte.d.ts +2 -0
- package/dist/date-picker/CalendarHeader.svelte +84 -62
- package/dist/date-picker/CalendarHeader.svelte.d.ts +3 -0
- package/dist/date-picker/DatePickerDialog.svelte +135 -75
- package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -3
- package/dist/date-picker/DateRangePicker.svelte +227 -78
- package/dist/date-picker/DateRangePicker.svelte.d.ts +3 -3
- package/dist/date-picker/DockedDatePicker.svelte +76 -457
- package/dist/date-picker/DockedDatePicker.svelte.d.ts +3 -3
- package/dist/date-picker/DockedDateTimePicker.svelte +128 -518
- package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +3 -3
- package/dist/date-picker/DockedPickerField.svelte +221 -0
- package/dist/date-picker/DockedPickerField.svelte.d.ts +9 -0
- package/dist/date-picker/MonthPanel.svelte +291 -0
- package/dist/date-picker/MonthPanel.svelte.d.ts +12 -0
- package/dist/date-picker/SelectionList.svelte +7 -6
- package/dist/date-picker/SelectionList.svelte.d.ts +1 -0
- package/dist/date-picker/TimeColumn.svelte +2 -6
- package/dist/date-picker/TimeColumn.svelte.d.ts +1 -0
- package/dist/date-picker/YearGrid.svelte +6 -13
- package/dist/date-picker/YearGrid.svelte.d.ts +1 -0
- package/dist/date-picker/dateUtils.d.ts +4 -0
- package/dist/date-picker/dateUtils.js +15 -88
- package/dist/date-picker/exitVisibility.svelte.js +13 -6
- package/dist/date-picker/formatters.d.ts +16 -0
- package/dist/date-picker/formatters.js +48 -15
- package/dist/date-picker/internalTypes.d.ts +59 -1
- package/dist/date-picker/shortcuts.d.ts +11 -0
- package/dist/date-picker/shortcuts.js +40 -0
- package/dist/date-picker/timeUtils.d.ts +8 -3
- package/dist/date-picker/timeUtils.js +33 -51
- package/dist/date-picker/types.d.ts +22 -0
- package/dist/dialog/Dialog.svelte +171 -33
- package/dist/dialog/Dialog.svelte.d.ts +2 -1
- package/dist/dialog/types.d.ts +6 -0
- package/dist/divider/Divider.svelte +36 -10
- package/dist/divider/types.d.ts +1 -0
- package/dist/fab/ExtendedFab.svelte +26 -60
- package/dist/fab/Fab.svelte +13 -48
- package/dist/fab/FabMenu.svelte +102 -44
- package/dist/fab/FabMenu.svelte.d.ts +2 -1
- package/dist/fab/FabMenuItem.svelte +8 -0
- package/dist/fab/FabMenuItem.svelte.d.ts +4 -0
- package/dist/fab/index.d.ts +1 -0
- package/dist/fab/index.js +1 -0
- package/dist/fab/types.d.ts +6 -4
- package/dist/icons/Icon.svelte +1 -4
- package/dist/icons/MenuIcon.svelte +13 -0
- package/dist/icons/MenuIcon.svelte.d.ts +7 -0
- package/dist/icons/MenuOpenIcon.svelte +15 -0
- package/dist/icons/MenuOpenIcon.svelte.d.ts +7 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/icons/index.js +2 -0
- package/dist/internal/exit.css +33 -0
- package/dist/internal/field.css +337 -0
- package/dist/internal/fieldTypes.d.ts +6 -0
- package/dist/internal/focus-ring.css +25 -6
- package/dist/internal/interest.css +3 -1
- package/dist/internal/pop-in.css +19 -0
- package/dist/internal/visually-hidden.css +11 -0
- package/dist/keyboard-nav.d.ts +12 -1
- package/dist/keyboard-nav.js +104 -14
- package/dist/list/Item.svelte +84 -24
- package/dist/list/List.svelte +35 -2
- package/dist/list/ListItem.svelte +16 -2
- package/dist/list/context.d.ts +6 -0
- package/dist/list/context.js +4 -0
- package/dist/list/types.d.ts +7 -0
- package/dist/media.d.ts +1 -0
- package/dist/media.js +1 -0
- package/dist/menu/Menu.svelte +53 -20
- package/dist/menu/Menu.svelte.d.ts +3 -1
- package/dist/menu/MenuItem.svelte +9 -3
- package/dist/menu/types.d.ts +7 -1
- package/dist/navigation-bar/NavigationBar.svelte +9 -3
- package/dist/navigation-bar/NavigationBarItem.svelte +31 -39
- package/dist/navigation-drawer/NavigationDrawer.svelte +96 -20
- package/dist/navigation-drawer/NavigationDrawer.svelte.d.ts +2 -1
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +10 -12
- package/dist/navigation-drawer/NavigationDrawerSection.svelte +49 -0
- package/dist/navigation-drawer/NavigationDrawerSection.svelte.d.ts +4 -0
- package/dist/navigation-drawer/index.d.ts +1 -0
- package/dist/navigation-drawer/index.js +1 -0
- package/dist/navigation-drawer/types.d.ts +12 -0
- package/dist/navigation-rail/NavigationRail.svelte +367 -7
- package/dist/navigation-rail/NavigationRail.svelte.d.ts +1 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +185 -59
- package/dist/navigation-rail/NavigationRailSection.svelte +57 -0
- package/dist/navigation-rail/NavigationRailSection.svelte.d.ts +4 -0
- package/dist/navigation-rail/context.d.ts +6 -0
- package/dist/navigation-rail/context.js +5 -0
- package/dist/navigation-rail/index.d.ts +1 -0
- package/dist/navigation-rail/index.js +1 -0
- package/dist/navigation-rail/morph.d.ts +14 -0
- package/dist/navigation-rail/morph.js +78 -0
- package/dist/navigation-rail/types.d.ts +21 -0
- package/dist/press.svelte.d.ts +0 -1
- package/dist/press.svelte.js +0 -11
- package/dist/progress/CircularProgress.svelte +28 -82
- package/dist/progress/CircularProgress.svelte.d.ts +1 -0
- package/dist/progress/LinearProgress.svelte +67 -171
- package/dist/progress/LinearProgress.svelte.d.ts +1 -0
- package/dist/progress/four-color.css +34 -0
- package/dist/progress/types.d.ts +1 -0
- package/dist/radio/Radio.svelte +13 -10
- package/dist/ripple/Ripple.svelte +8 -16
- package/dist/search/Search.svelte +238 -58
- package/dist/search/Search.svelte.d.ts +1 -0
- package/dist/search/types.d.ts +4 -0
- package/dist/select/Check.svelte +8 -31
- package/dist/select/Check.svelte.d.ts +1 -1
- package/dist/select/NativeSelect.svelte +113 -374
- package/dist/select/NativeSelect.svelte.d.ts +1 -0
- package/dist/select/Option.svelte +2 -11
- package/dist/select/Select.svelte +170 -858
- package/dist/select/Select.svelte.d.ts +1 -0
- package/dist/select/VirtualList.svelte +16 -2
- package/dist/select/VirtualList.svelte.d.ts +6 -2
- package/dist/sheet/Sheet.svelte +229 -22
- package/dist/sheet/Sheet.svelte.d.ts +3 -1
- package/dist/sheet/types.d.ts +10 -0
- package/dist/slider/Slider.svelte +14 -64
- package/dist/slider/Slider.svelte.d.ts +1 -0
- package/dist/snackbar/Snackbar.svelte +3 -0
- package/dist/snackbar/Snackbar.svelte.d.ts +2 -1
- package/dist/switch/Switch.svelte +53 -19
- package/dist/tabs/Tab.svelte +30 -18
- package/dist/tabs/Tabs.svelte +28 -4
- package/dist/tabs/types.d.ts +3 -0
- package/dist/text-field/TextField.svelte +124 -616
- package/dist/text-field/TextField.svelte.d.ts +1 -0
- package/dist/time-picker/ClockDial.svelte +8 -25
- package/dist/time-picker/ClockDial.svelte.d.ts +2 -0
- package/dist/time-picker/DockedTimePicker.svelte +102 -239
- package/dist/time-picker/DockedTimePicker.svelte.d.ts +3 -3
- package/dist/time-picker/PeriodSelector.svelte +8 -7
- package/dist/time-picker/PeriodSelector.svelte.d.ts +1 -0
- package/dist/time-picker/TimeInputs.svelte +13 -2
- package/dist/time-picker/TimePickerDialog.svelte +33 -26
- package/dist/time-picker/TimePickerDialog.svelte.d.ts +3 -3
- package/dist/time-picker/TimePickerPanel.svelte +26 -4
- package/dist/time-picker/TimeSelectors.svelte +4 -9
- package/dist/time-picker/TimeSelectors.svelte.d.ts +1 -0
- package/dist/time-picker/internalTypes.d.ts +2 -0
- package/dist/time-picker/timePickerState.svelte.js +3 -9
- package/dist/toolbar/Toolbar.svelte +21 -11
- package/dist/toolbar/types.d.ts +1 -0
- package/dist/tooltip/RichTooltip.svelte +2 -23
- package/dist/tooltip/RichTooltip.svelte.d.ts +2 -1
- package/dist/tooltip/Tooltip.svelte +5 -25
- package/dist/tooltip/Tooltip.svelte.d.ts +2 -1
- package/package.json +12 -12
|
@@ -21,12 +21,3 @@ export const expandedWidths = (widths, index, expandedRatio, compressionLimit) =
|
|
|
21
21
|
}
|
|
22
22
|
return next;
|
|
23
23
|
};
|
|
24
|
-
export const parseMotion = (token) => {
|
|
25
|
-
const [time, ...rest] = token.trim().split(/\s+/);
|
|
26
|
-
const value = Number.parseFloat(time);
|
|
27
|
-
const easing = rest.join(' ');
|
|
28
|
-
return {
|
|
29
|
-
duration: Number.isNaN(value) ? 0 : time.endsWith('ms') ? value : value * 1000,
|
|
30
|
-
easing: easing || 'linear',
|
|
31
|
-
};
|
|
32
|
-
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** What a `ButtonGroup` decides for its buttons: their size and shape, and with `selection` which are selected. */
|
|
2
|
+
export interface ButtonGroupContext {
|
|
3
|
+
readonly selection: 'single' | 'multiple' | undefined;
|
|
4
|
+
readonly size: 'xs' | 's' | 'm' | 'l' | 'xl' | undefined;
|
|
5
|
+
readonly shape: 'round' | 'square' | undefined;
|
|
6
|
+
isSelected: (value: string) => boolean;
|
|
7
|
+
toggle: (value: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const setButtonGroupContext: (context: ButtonGroupContext) => ButtonGroupContext;
|
|
10
|
+
export declare const getButtonGroupContext: () => ButtonGroupContext | undefined;
|
package/dist/button/types.d.ts
CHANGED
|
@@ -25,12 +25,31 @@ export interface IconButtonProps extends BaseButtonProps {
|
|
|
25
25
|
}
|
|
26
26
|
export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
27
27
|
variant?: 'standard' | 'connected';
|
|
28
|
+
/** Size of the buttons that don't set their own. */
|
|
29
|
+
size?: BaseButtonProps['size'];
|
|
30
|
+
/** Shape of the buttons that don't set their own. */
|
|
31
|
+
shape?: BaseButtonProps['shape'];
|
|
32
|
+
/**
|
|
33
|
+
* Lets the group manage selection: its buttons with a `value` become toggles, and `value`
|
|
34
|
+
* holds the selected one (`single`) or the selected ones (`multiple`).
|
|
35
|
+
*/
|
|
36
|
+
selection?: 'single' | 'multiple';
|
|
37
|
+
value?: string | string[] | null;
|
|
38
|
+
/** With `selection`, keeps at least one button selected and makes a form require one. */
|
|
39
|
+
required?: boolean;
|
|
40
|
+
/** With `selection`, submits the selected values under this name, like a radio or checkbox group. */
|
|
41
|
+
name?: string;
|
|
42
|
+
form?: string;
|
|
43
|
+
/** Arrow keys move between the buttons, which share one tab stop. Off, Tab visits each. */
|
|
44
|
+
arrowKeys?: boolean;
|
|
28
45
|
expandedRatio?: number;
|
|
29
46
|
compressionLimit?: number;
|
|
30
47
|
element?: HTMLElement;
|
|
31
48
|
}
|
|
32
49
|
export interface SplitButtonProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onclick'> {
|
|
33
50
|
label?: string;
|
|
51
|
+
/** Shows only `icon` on the leading button. `label` then names it and becomes its tooltip. */
|
|
52
|
+
iconOnly?: boolean;
|
|
34
53
|
icon?: Snippet;
|
|
35
54
|
menu?: Snippet<[string]>;
|
|
36
55
|
variant?: Exclude<ButtonProps['variant'], 'text'>;
|
package/dist/card/Card.svelte
CHANGED
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
aria-disabled={disabled}
|
|
67
67
|
class={[
|
|
68
68
|
'np-card-container',
|
|
69
|
+
'np-focus-ring',
|
|
69
70
|
`np-card-${variant}`,
|
|
70
71
|
disabled && 'np-card-disabled',
|
|
71
72
|
attributes.class,
|
|
@@ -82,6 +83,7 @@
|
|
|
82
83
|
{disabled}
|
|
83
84
|
class={[
|
|
84
85
|
'np-card-container',
|
|
86
|
+
'np-focus-ring',
|
|
85
87
|
`np-card-${variant}`,
|
|
86
88
|
disabled && 'np-card-disabled',
|
|
87
89
|
attributes.class,
|
|
@@ -98,6 +100,7 @@
|
|
|
98
100
|
role="link"
|
|
99
101
|
class={[
|
|
100
102
|
'np-card-container',
|
|
103
|
+
'np-focus-ring',
|
|
101
104
|
`np-card-${variant}`,
|
|
102
105
|
disabled && 'np-card-disabled',
|
|
103
106
|
attributes.class,
|
|
@@ -111,7 +114,6 @@
|
|
|
111
114
|
.np-card-elevated {
|
|
112
115
|
border-width: 0;
|
|
113
116
|
box-shadow: var(--np-elevation-1);
|
|
114
|
-
color: var(--np-color-on-surface);
|
|
115
117
|
--background-color: var(
|
|
116
118
|
--np-elevated-card-container-color,
|
|
117
119
|
var(--np-color-surface-container-low)
|
|
@@ -123,7 +125,6 @@
|
|
|
123
125
|
}
|
|
124
126
|
.np-card-filled {
|
|
125
127
|
border-width: 0;
|
|
126
|
-
color: var(--np-color-on-surface);
|
|
127
128
|
--background-color: var(
|
|
128
129
|
--np-filled-card-container-color,
|
|
129
130
|
var(--np-color-surface-container-highest)
|
|
@@ -134,7 +135,6 @@
|
|
|
134
135
|
--background-color: var(--np-color-surface-variant);
|
|
135
136
|
}
|
|
136
137
|
.np-card-outlined {
|
|
137
|
-
color: var(--np-color-on-surface);
|
|
138
138
|
border: 1px solid var(--np-color-outline-variant);
|
|
139
139
|
--background-color: var(--np-outlined-card-container-color, var(--np-color-surface));
|
|
140
140
|
--border-radius: var(--np-outlined-card-container-shape, var(--np-shape-corner-medium));
|
|
@@ -159,21 +159,6 @@
|
|
|
159
159
|
cursor: pointer;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
.np-card-container:focus-visible :global(.np-ripple-surface)::before {
|
|
163
|
-
opacity: var(--np-ripple-focus-opacity, 0.1);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.np-card-container:focus-visible {
|
|
167
|
-
outline-style: solid;
|
|
168
|
-
outline-color: var(--np-color-secondary);
|
|
169
|
-
outline-width: 3px;
|
|
170
|
-
outline-offset: 2px;
|
|
171
|
-
}
|
|
172
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
173
|
-
.np-card-container:focus-visible {
|
|
174
|
-
animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
162
|
.np-card-disabled {
|
|
178
163
|
opacity: 0.38;
|
|
179
164
|
cursor: unset;
|
|
@@ -199,7 +184,6 @@
|
|
|
199
184
|
flex: 1 0 auto;
|
|
200
185
|
min-width: calc(100% - 32px);
|
|
201
186
|
margin: 1rem;
|
|
202
|
-
grid-gap: 0.5rem;
|
|
203
187
|
gap: 0.5rem;
|
|
204
188
|
}
|
|
205
189
|
|
package/dist/card/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { ElementKindProps } from '../internal/elementTag.js';
|
|
3
3
|
export type CardElement = HTMLDivElement | HTMLButtonElement | HTMLAnchorElement;
|
|
4
|
-
|
|
4
|
+
interface CardBaseProps extends ElementKindProps<CardElement> {
|
|
5
5
|
variant?: 'elevated' | 'filled' | 'outlined';
|
|
6
6
|
image?: string | null;
|
|
7
7
|
/** Describes `image` for assistive technology. Leave it out for a decorative image. */
|
|
@@ -10,5 +10,15 @@ export interface CardProps extends ElementKindProps<CardElement> {
|
|
|
10
10
|
headline?: string | null;
|
|
11
11
|
subhead?: string | null;
|
|
12
12
|
supportingText?: string | null;
|
|
13
|
-
action?: Snippet<[]>;
|
|
14
13
|
}
|
|
14
|
+
export type CardProps = CardBaseProps & ({
|
|
15
|
+
action?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
action?: Snippet<[]>;
|
|
18
|
+
type?: 'text' | null;
|
|
19
|
+
href?: never;
|
|
20
|
+
onclick?: never;
|
|
21
|
+
command?: never;
|
|
22
|
+
popovertarget?: never;
|
|
23
|
+
});
|
|
24
|
+
export {};
|
|
@@ -62,13 +62,47 @@
|
|
|
62
62
|
: undefined,
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const ITEMS = '.np-carousel-item:is(button, a[href]):not(.np-carousel-item-disabled)'
|
|
66
|
+
const TABBABLE =
|
|
67
|
+
'a[href], button, input:not([type="hidden"]), select, textarea, summary, [tabindex], [contenteditable]'
|
|
68
|
+
|
|
69
|
+
let arrowHandler = $derived(arrowKeyNav(ITEMS, axis, { wrap: false }))
|
|
70
|
+
|
|
71
|
+
const tabbable = (node: HTMLElement) =>
|
|
72
|
+
node.tabIndex >= 0 &&
|
|
73
|
+
!node.matches(':disabled') &&
|
|
74
|
+
!node.closest('[inert]') &&
|
|
75
|
+
node.checkVisibility({ visibilityProperty: true })
|
|
76
|
+
|
|
77
|
+
const leave = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLDivElement }) => {
|
|
78
|
+
const { key, target, currentTarget: carousel } = event
|
|
79
|
+
if (vertical || (key !== 'ArrowUp' && key !== 'ArrowDown')) return
|
|
80
|
+
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
|
|
81
|
+
if (!(target instanceof HTMLElement) || !target.matches(ITEMS)) return
|
|
82
|
+
|
|
83
|
+
// A modal dialog makes the rest of the page inert without an [inert] attribute.
|
|
84
|
+
const modal = carousel.closest('dialog')
|
|
85
|
+
const scope = modal?.matches(':modal') ? modal : document
|
|
86
|
+
const nodes = [...scope.querySelectorAll<HTMLElement>(TABBABLE)]
|
|
87
|
+
const after = (node: HTMLElement) =>
|
|
88
|
+
!!(carousel.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
89
|
+
const candidates =
|
|
90
|
+
key === 'ArrowDown' ? nodes.filter(after) : nodes.filter((node) => !after(node)).reverse()
|
|
91
|
+
for (const node of candidates) {
|
|
92
|
+
if (carousel.contains(node) || node.contains(carousel) || !tabbable(node)) continue
|
|
93
|
+
node.focus()
|
|
94
|
+
if (document.activeElement !== node) continue
|
|
95
|
+
event.preventDefault()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
}
|
|
66
99
|
|
|
67
100
|
const handleKeydown = (
|
|
68
101
|
event: KeyboardEvent & { currentTarget: EventTarget & HTMLDivElement },
|
|
69
102
|
) => {
|
|
70
103
|
userKeydown?.(event)
|
|
71
104
|
if (!event.defaultPrevented) arrowHandler(event)
|
|
105
|
+
if (!event.defaultPrevented) leave(event)
|
|
72
106
|
}
|
|
73
107
|
|
|
74
108
|
let places = $derived(new Map(items.map((item, index) => [item, index + 1])))
|
|
@@ -81,7 +115,10 @@
|
|
|
81
115
|
const notch =
|
|
82
116
|
place > 0 ? (snapPoints?.children[place - 1] as HTMLElement | undefined) : undefined
|
|
83
117
|
if (notch) notch.scrollIntoView({ block: 'start', inline: 'start', behavior })
|
|
84
|
-
else
|
|
118
|
+
else {
|
|
119
|
+
const inline = alignment === 'center' && !vertical ? 'center' : 'nearest'
|
|
120
|
+
item.scrollIntoView({ block: 'nearest', inline, behavior })
|
|
121
|
+
}
|
|
85
122
|
}
|
|
86
123
|
|
|
87
124
|
const carouselContext: CarouselContext = {
|
|
@@ -113,6 +150,7 @@
|
|
|
113
150
|
`np-carousel-${variant}`,
|
|
114
151
|
`np-carousel-${axis}`,
|
|
115
152
|
snapping && 'np-carousel-snap',
|
|
153
|
+
alignment === 'center' && !morphing && 'np-carousel-center',
|
|
116
154
|
cropping && 'np-carousel-cropping',
|
|
117
155
|
attributes.class,
|
|
118
156
|
]}
|
|
@@ -302,29 +340,20 @@
|
|
|
302
340
|
|
|
303
341
|
.np-carousel-cropping.np-carousel-horizontal .np-carousel-track :global(.np-carousel-item) {
|
|
304
342
|
animation-timeline: view(inline), view(inline);
|
|
305
|
-
clip-path: inset(
|
|
306
|
-
0 var(--_crop-end) 0 var(--_crop-start) round
|
|
307
|
-
var(--np-carousel-item-container-shape, var(--np-shape-corner-extra-large))
|
|
308
|
-
);
|
|
343
|
+
clip-path: inset(0 var(--_crop-end) 0 var(--_crop-start) round var(--_carousel-item-corner));
|
|
309
344
|
--np-carousel-parallax: var(--_shift) 0;
|
|
310
345
|
}
|
|
311
346
|
|
|
312
347
|
.np-carousel-cropping.np-carousel-horizontal
|
|
313
348
|
.np-carousel-track:dir(rtl)
|
|
314
349
|
:global(.np-carousel-item) {
|
|
315
|
-
clip-path: inset(
|
|
316
|
-
0 var(--_crop-start) 0 var(--_crop-end) round
|
|
317
|
-
var(--np-carousel-item-container-shape, var(--np-shape-corner-extra-large))
|
|
318
|
-
);
|
|
350
|
+
clip-path: inset(0 var(--_crop-start) 0 var(--_crop-end) round var(--_carousel-item-corner));
|
|
319
351
|
--_shift: calc(var(--_visible, 0.3333) * (var(--_in) - var(--_out)) * 100%);
|
|
320
352
|
}
|
|
321
353
|
|
|
322
354
|
.np-carousel-cropping.np-carousel-vertical .np-carousel-track :global(.np-carousel-item) {
|
|
323
355
|
animation-timeline: view(block), view(block);
|
|
324
|
-
clip-path: inset(
|
|
325
|
-
var(--_crop-start) 0 var(--_crop-end) 0 round
|
|
326
|
-
var(--np-carousel-item-container-shape, var(--np-shape-corner-extra-large))
|
|
327
|
-
);
|
|
356
|
+
clip-path: inset(var(--_crop-start) 0 var(--_crop-end) 0 round var(--_carousel-item-corner));
|
|
328
357
|
--np-carousel-parallax: 0 var(--_shift);
|
|
329
358
|
}
|
|
330
359
|
|
|
@@ -332,6 +361,20 @@
|
|
|
332
361
|
inset-inline-start: var(--_crop-start);
|
|
333
362
|
inset-inline-end: var(--_crop-end);
|
|
334
363
|
}
|
|
364
|
+
|
|
365
|
+
.np-carousel-cropping.np-carousel-horizontal
|
|
366
|
+
.np-carousel-track
|
|
367
|
+
:global(.np-carousel-item::after) {
|
|
368
|
+
inset-inline-start: var(--_crop-start);
|
|
369
|
+
inset-inline-end: var(--_crop-end);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.np-carousel-cropping.np-carousel-vertical
|
|
373
|
+
.np-carousel-track
|
|
374
|
+
:global(.np-carousel-item::after) {
|
|
375
|
+
inset-block-start: var(--_crop-start);
|
|
376
|
+
inset-block-end: var(--_crop-end);
|
|
377
|
+
}
|
|
335
378
|
}
|
|
336
379
|
|
|
337
380
|
@supports (animation-timeline: scroll(nearest inline)) {
|
|
@@ -346,6 +389,14 @@
|
|
|
346
389
|
:global(.np-carousel-morphing-block) .np-carousel-track :global(.np-carousel-item) {
|
|
347
390
|
animation-timeline: scroll(nearest block);
|
|
348
391
|
}
|
|
392
|
+
|
|
393
|
+
:global(.np-carousel-morphing-inline) .np-carousel-track :global(.np-carousel-item::after) {
|
|
394
|
+
inset-inline: var(--np-carousel-clip);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
:global(.np-carousel-morphing-block) .np-carousel-track :global(.np-carousel-item::after) {
|
|
398
|
+
inset-block: var(--np-carousel-clip);
|
|
399
|
+
}
|
|
349
400
|
}
|
|
350
401
|
|
|
351
402
|
.np-carousel-snap-points > span {
|
|
@@ -374,6 +425,18 @@
|
|
|
374
425
|
scroll-snap-align: start;
|
|
375
426
|
}
|
|
376
427
|
|
|
428
|
+
.np-carousel-snap.np-carousel-center .np-carousel-track :global(.np-carousel-item) {
|
|
429
|
+
scroll-snap-align: center;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.np-carousel-center.np-carousel-horizontal .np-carousel-scroller {
|
|
433
|
+
scroll-padding-inline: var(--_pad);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.np-carousel-center.np-carousel-vertical .np-carousel-scroller {
|
|
437
|
+
scroll-padding-block: var(--_pad);
|
|
438
|
+
}
|
|
439
|
+
|
|
377
440
|
:global(.np-carousel-morphing) .np-carousel-track :global(.np-carousel-item) {
|
|
378
441
|
scroll-snap-align: none;
|
|
379
442
|
}
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
disabled = false,
|
|
17
17
|
children,
|
|
18
18
|
element = $bindable(),
|
|
19
|
-
onpointerdown,
|
|
20
19
|
style,
|
|
21
20
|
...attributes
|
|
22
21
|
}: CarouselItemProps = $props()
|
|
@@ -39,20 +38,20 @@
|
|
|
39
38
|
.join(';'),
|
|
40
39
|
)
|
|
41
40
|
|
|
41
|
+
let text = $derived(attributes['aria-label'] ?? label ?? imageAlt)
|
|
42
42
|
let accessibleName = $derived(
|
|
43
|
-
|
|
44
|
-
(
|
|
45
|
-
|
|
46
|
-
: undefined),
|
|
43
|
+
tag !== 'div' && text && position > 0
|
|
44
|
+
? carouselContext.itemLabel(text, position, total)
|
|
45
|
+
: (attributes['aria-label'] ?? undefined),
|
|
47
46
|
)
|
|
48
47
|
|
|
49
48
|
let labelHidden = $derived(accessibleName !== undefined)
|
|
50
49
|
|
|
51
|
-
const
|
|
52
|
-
event:
|
|
50
|
+
const handleClick = (
|
|
51
|
+
event: MouseEvent & { currentTarget: EventTarget & CarouselItemElement },
|
|
53
52
|
) => {
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if (!disabled) press.press()
|
|
54
|
+
attributes.onclick?.(event)
|
|
56
55
|
}
|
|
57
56
|
</script>
|
|
58
57
|
|
|
@@ -100,7 +99,7 @@
|
|
|
100
99
|
{disabled}
|
|
101
100
|
aria-disabled={disabled}
|
|
102
101
|
aria-label={accessibleName}
|
|
103
|
-
|
|
102
|
+
onclick={handleClick}
|
|
104
103
|
style={itemStyle}
|
|
105
104
|
class={[
|
|
106
105
|
'np-carousel-item',
|
|
@@ -120,7 +119,7 @@
|
|
|
120
119
|
aria-disabled={disabled}
|
|
121
120
|
aria-label={accessibleName}
|
|
122
121
|
tabindex={disabled ? -1 : undefined}
|
|
123
|
-
|
|
122
|
+
onclick={handleClick}
|
|
124
123
|
style={itemStyle}
|
|
125
124
|
class={[
|
|
126
125
|
'np-carousel-item',
|
|
@@ -135,6 +134,12 @@
|
|
|
135
134
|
{/if}
|
|
136
135
|
|
|
137
136
|
<style>
|
|
137
|
+
@property --_carousel-item-corner {
|
|
138
|
+
syntax: '<length-percentage>';
|
|
139
|
+
inherits: false;
|
|
140
|
+
initial-value: 0px;
|
|
141
|
+
}
|
|
142
|
+
|
|
138
143
|
.np-carousel-item {
|
|
139
144
|
position: relative;
|
|
140
145
|
box-sizing: border-box;
|
|
@@ -154,7 +159,11 @@
|
|
|
154
159
|
inline-size: var(--np-carousel-item-width, 12.5rem);
|
|
155
160
|
block-size: var(--np-carousel-item-height, auto);
|
|
156
161
|
align-self: stretch;
|
|
157
|
-
|
|
162
|
+
--_carousel-item-corner: var(
|
|
163
|
+
--np-carousel-item-container-shape,
|
|
164
|
+
var(--np-shape-corner-extra-large)
|
|
165
|
+
);
|
|
166
|
+
border-radius: var(--_carousel-item-corner);
|
|
158
167
|
box-shadow: var(--np-carousel-item-elevation, none);
|
|
159
168
|
background-color: var(--np-carousel-item-container-color, transparent);
|
|
160
169
|
--np-ripple-hover-color: var(--np-carousel-item-state-layer-color, var(--np-color-on-surface));
|
|
@@ -271,11 +280,14 @@
|
|
|
271
280
|
|
|
272
281
|
@media (prefers-reduced-motion: no-preference) {
|
|
273
282
|
.np-carousel-item {
|
|
274
|
-
transition:
|
|
283
|
+
transition: --_carousel-item-corner var(--np-motion-expressive-fast-effects);
|
|
275
284
|
}
|
|
276
285
|
|
|
277
286
|
.np-carousel-item:is(button, a):is(:active, .np-carousel-item-pressed) {
|
|
278
|
-
|
|
287
|
+
--_carousel-item-corner: var(
|
|
288
|
+
--np-carousel-item-pressed-container-shape,
|
|
289
|
+
var(--np-shape-corner-large)
|
|
290
|
+
);
|
|
279
291
|
}
|
|
280
292
|
}
|
|
281
293
|
|
|
@@ -57,7 +57,7 @@ export const carouselMorph = (options) => (scroller) => {
|
|
|
57
57
|
sheet.replaceSync('');
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
|
-
const shape = 'var(--
|
|
60
|
+
const shape = 'var(--_carousel-item-corner)';
|
|
61
61
|
const offset = (along) => vertical ? `inset-block-start:${px(along)}` : `inset-inline-start:${px(along)}`;
|
|
62
62
|
const mask = (clip) => vertical ? `inset(${px(clip)} 0 round ${shape})` : `inset(0 ${px(clip)} round ${shape})`;
|
|
63
63
|
const at = (percent) => `${Math.round(percent * 1000) / 1000}%`;
|
|
@@ -103,7 +103,7 @@ const build = (input, sizes, counts) => {
|
|
|
103
103
|
keylines.push({
|
|
104
104
|
kind,
|
|
105
105
|
size,
|
|
106
|
-
loc: stride / 2 +
|
|
106
|
+
loc: stride / 2 + index * stride,
|
|
107
107
|
locOffset: offset + size / 2,
|
|
108
108
|
});
|
|
109
109
|
offset += size;
|
|
@@ -120,7 +120,7 @@ const build = (input, sizes, counts) => {
|
|
|
120
120
|
loc: keylines[keylines.length - 1].loc + stride,
|
|
121
121
|
locOffset: availableSpace + spacing / 2,
|
|
122
122
|
});
|
|
123
|
-
const scrollMax = Math.max(0, (itemCount - order.length
|
|
123
|
+
const scrollMax = Math.max(0, (itemCount - order.length) * stride);
|
|
124
124
|
return {
|
|
125
125
|
smallCount: counts.small,
|
|
126
126
|
mediumCount: counts.medium,
|
|
@@ -221,38 +221,39 @@ const withOrder = (arrangement, order) => {
|
|
|
221
221
|
keylines: [keylines[0], ...shifted, keylines[keylines.length - 1]],
|
|
222
222
|
};
|
|
223
223
|
};
|
|
224
|
+
const movableSlots = (arrangement, towards) => {
|
|
225
|
+
const order = arrangement.keylines.slice(1, -1);
|
|
226
|
+
const first = order.findIndex((slot) => slot.kind === 'large');
|
|
227
|
+
if (first < 0)
|
|
228
|
+
return 0;
|
|
229
|
+
if (towards === 'start')
|
|
230
|
+
return first;
|
|
231
|
+
return order.length - 1 - order.findLastIndex((slot) => slot.kind === 'large');
|
|
232
|
+
};
|
|
224
233
|
export const shiftSteps = (arrangement, towards) => {
|
|
225
234
|
const states = [arrangement];
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const order = arrangement.keylines.slice(1, -1);
|
|
229
|
-
const movable = order.filter((slot) => slot.kind !== 'large').length;
|
|
230
|
-
if (movable === 0 || !order.some((slot) => slot.kind === 'large'))
|
|
231
|
-
return states;
|
|
232
|
-
const working = [...order];
|
|
235
|
+
const movable = movableSlots(arrangement, towards);
|
|
236
|
+
const working = arrangement.keylines.slice(1, -1);
|
|
233
237
|
for (let step = 0; step < movable; step += 1) {
|
|
234
238
|
if (towards === 'end') {
|
|
235
239
|
const [moved] = working.splice(working.length - 1, 1);
|
|
236
|
-
working.
|
|
240
|
+
const first = working.findIndex((slot) => slot.kind === 'large');
|
|
241
|
+
const larger = working.findIndex((slot, index) => index < first && slot.size > moved.size);
|
|
242
|
+
working.splice(larger < 0 ? first : larger, 0, moved);
|
|
237
243
|
}
|
|
238
244
|
else {
|
|
239
245
|
const [moved] = working.splice(0, 1);
|
|
240
|
-
working.
|
|
246
|
+
const last = working.findLastIndex((slot) => slot.kind === 'large');
|
|
247
|
+
const smaller = working.findIndex((slot, index) => index > last && slot.size < moved.size);
|
|
248
|
+
working.splice(smaller < 0 ? working.length : smaller, 0, moved);
|
|
241
249
|
}
|
|
242
250
|
states.push(withOrder(arrangement, working));
|
|
243
251
|
}
|
|
244
252
|
return states;
|
|
245
253
|
};
|
|
246
|
-
const shiftDistance = (arrangement) => {
|
|
247
|
-
const moving = arrangement.keylines
|
|
248
|
-
.slice(1, -1)
|
|
249
|
-
.filter((keyline) => keyline.kind !== 'large').length;
|
|
250
|
-
return moving * arrangement.stride;
|
|
251
|
-
};
|
|
252
254
|
export const morphTrack = (arrangement) => {
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
const endShift = shift;
|
|
255
|
+
const startShift = movableSlots(arrangement, 'start') * arrangement.stride;
|
|
256
|
+
const endShift = movableSlots(arrangement, 'end') * arrangement.stride;
|
|
256
257
|
const scrollMax = arrangement.scrollMax + startShift + endShift;
|
|
257
258
|
return {
|
|
258
259
|
arrangement,
|
|
@@ -290,14 +291,11 @@ export const placeItemOnTrack = (track, index, scroll) => {
|
|
|
290
291
|
return { x: base.x + held, clip: base.clip };
|
|
291
292
|
};
|
|
292
293
|
export const notches = (track) => {
|
|
293
|
-
const { arrangement,
|
|
294
|
-
const
|
|
295
|
-
const step = uniform ? Math.max(1, arrangement.stride) : arrangement.stride;
|
|
296
|
-
const last = uniform ? scrollMax : arrangement.scrollMax;
|
|
297
|
-
const base = uniform ? 0 : startShift;
|
|
294
|
+
const { arrangement, scrollMax } = track;
|
|
295
|
+
const step = Math.max(1, arrangement.stride);
|
|
298
296
|
const out = [0];
|
|
299
|
-
for (let at = step; at <=
|
|
300
|
-
out.push(
|
|
297
|
+
for (let at = step; at <= scrollMax + 0.001; at += step)
|
|
298
|
+
out.push(at);
|
|
301
299
|
if (scrollMax > out[out.length - 1] + 0.001)
|
|
302
300
|
out.push(scrollMax);
|
|
303
301
|
return out;
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<rect class="mark short" />
|
|
55
55
|
<rect class="mark long" />
|
|
56
56
|
</svg>
|
|
57
|
-
<div class="np-input-wrapper">
|
|
57
|
+
<div class="np-input-wrapper np-focus-ring-within">
|
|
58
58
|
{#if !attributes.disabled}
|
|
59
59
|
<Ripple forElement={inputEl} />
|
|
60
60
|
{/if}
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
cursor: pointer;
|
|
88
88
|
-webkit-tap-highlight-color: transparent;
|
|
89
89
|
margin: var(--np-checkbox-margin, max(0px, (48px - 18px)/2));
|
|
90
|
+
--np-ripple-pressed-color: var(--np-color-primary);
|
|
90
91
|
}
|
|
91
92
|
:global(label) {
|
|
92
93
|
-webkit-tap-highlight-color: transparent;
|
|
@@ -114,13 +115,6 @@
|
|
|
114
115
|
cursor: inherit;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
.np-input-wrapper:has(input:focus-visible) {
|
|
118
|
-
outline-style: solid;
|
|
119
|
-
outline-color: var(--np-color-secondary);
|
|
120
|
-
outline-width: 3px;
|
|
121
|
-
outline-offset: 2px;
|
|
122
|
-
animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
|
|
123
|
-
}
|
|
124
118
|
.np-outline,
|
|
125
119
|
.np-background,
|
|
126
120
|
.np-icon {
|
|
@@ -188,6 +182,10 @@
|
|
|
188
182
|
.np-error .np-icon {
|
|
189
183
|
fill: var(--np-color-on-error);
|
|
190
184
|
}
|
|
185
|
+
.np-checkbox-container:not(.np-error):has(input:checked, input:indeterminate) {
|
|
186
|
+
--np-ripple-hover-color: var(--np-color-primary);
|
|
187
|
+
--np-ripple-pressed-color: var(--np-color-on-surface);
|
|
188
|
+
}
|
|
191
189
|
.np-error {
|
|
192
190
|
--np-ripple-hover-color: var(--np-color-error);
|
|
193
191
|
--np-ripple-pressed-color: var(--np-color-error);
|
package/dist/chip/ChipSet.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.js'
|
|
2
|
+
import { arrowKeyNav, focusableItems, focusedItem, rovingTabindex } from '../keyboard-nav.js'
|
|
3
3
|
import type { ChipSetProps } from './types.js'
|
|
4
4
|
|
|
5
5
|
let { children, element = $bindable(), ...attributes }: ChipSetProps = $props()
|
|
@@ -9,7 +9,33 @@
|
|
|
9
9
|
currentAttr: 'aria-current',
|
|
10
10
|
currentValue: 'true',
|
|
11
11
|
})
|
|
12
|
-
const
|
|
12
|
+
const arrows = arrowKeyNav(CHIP_SELECTOR, 'horizontal')
|
|
13
|
+
|
|
14
|
+
// In a set that wraps, ↑ and ↓ move to the chip closest above or below the focused one.
|
|
15
|
+
const toRow = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
16
|
+
const chips = focusableItems(event.currentTarget, CHIP_SELECTOR)
|
|
17
|
+
const focused = focusedItem(event.currentTarget, CHIP_SELECTOR, chips)
|
|
18
|
+
if (!focused) return
|
|
19
|
+
const from = focused.getBoundingClientRect()
|
|
20
|
+
const fromX = from.left + from.width / 2
|
|
21
|
+
const below = event.key === 'ArrowDown'
|
|
22
|
+
const candidates = chips
|
|
23
|
+
.map((chip) => ({ chip, box: chip.getBoundingClientRect() }))
|
|
24
|
+
.filter(({ box }) => (below ? box.top >= from.bottom : box.bottom <= from.top))
|
|
25
|
+
if (!candidates.length) return
|
|
26
|
+
const rowTop = below
|
|
27
|
+
? Math.min(...candidates.map(({ box }) => box.top))
|
|
28
|
+
: Math.max(...candidates.map(({ box }) => box.top))
|
|
29
|
+
const row = candidates.filter(({ box }) => Math.abs(box.top - rowTop) < 1)
|
|
30
|
+
const distance = (box: DOMRect) => Math.abs(box.left + box.width / 2 - fromX)
|
|
31
|
+
row.sort((a, b) => distance(a.box) - distance(b.box))[0].chip.focus()
|
|
32
|
+
event.preventDefault()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const onkeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
36
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') toRow(event)
|
|
37
|
+
else arrows(event)
|
|
38
|
+
}
|
|
13
39
|
</script>
|
|
14
40
|
|
|
15
41
|
<div {...attributes} bind:this={element} class={['np-chip-set-wrapper', attributes.class]}>
|