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
package/dist/animation.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
1
2
|
export declare const afterTwoFrames: (fn: () => void) => () => void;
|
|
2
|
-
export declare const afterExit: (element: Element | undefined, fn: () => void
|
|
3
|
+
export declare const afterExit: (element: Element | undefined, fn: () => void, { subtree }?: {
|
|
4
|
+
subtree?: boolean;
|
|
5
|
+
}) => () => void;
|
|
6
|
+
export declare const parseMotion: (token: string) => {
|
|
7
|
+
duration: number;
|
|
8
|
+
easing: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const motionOf: (element: Element, token: string) => {
|
|
11
|
+
duration: number;
|
|
12
|
+
easing: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const exitAnimation: Attachment<HTMLElement>;
|
|
15
|
+
export declare const morphSize: (element: HTMLElement | undefined, apply: () => void, token: string) => void;
|
package/dist/animation.js
CHANGED
|
@@ -8,7 +8,7 @@ export const afterTwoFrames = (fn) => {
|
|
|
8
8
|
cancelAnimationFrame(inner);
|
|
9
9
|
};
|
|
10
10
|
};
|
|
11
|
-
export const afterExit = (element, fn) => {
|
|
11
|
+
export const afterExit = (element, fn, { subtree = false } = {}) => {
|
|
12
12
|
if (!element) {
|
|
13
13
|
fn();
|
|
14
14
|
return () => { };
|
|
@@ -19,7 +19,9 @@ export const afterExit = (element, fn) => {
|
|
|
19
19
|
fn();
|
|
20
20
|
};
|
|
21
21
|
const cancelFrames = afterTwoFrames(() => {
|
|
22
|
-
const animations = element
|
|
22
|
+
const animations = element
|
|
23
|
+
.getAnimations({ subtree })
|
|
24
|
+
.filter((animation) => animation.effect?.getTiming().iterations !== Infinity);
|
|
23
25
|
if (animations.length === 0) {
|
|
24
26
|
finish();
|
|
25
27
|
return;
|
|
@@ -31,3 +33,54 @@ export const afterExit = (element, fn) => {
|
|
|
31
33
|
cancelFrames();
|
|
32
34
|
};
|
|
33
35
|
};
|
|
36
|
+
export const parseMotion = (token) => {
|
|
37
|
+
const [time, ...rest] = token.trim().split(/\s+/);
|
|
38
|
+
const value = Number.parseFloat(time);
|
|
39
|
+
const easing = rest.join(' ');
|
|
40
|
+
return {
|
|
41
|
+
duration: Number.isNaN(value) ? 0 : time.endsWith('ms') ? value : value * 1000,
|
|
42
|
+
easing: easing || 'linear',
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export const motionOf = (element, token) => parseMotion(getComputedStyle(element).getPropertyValue(token));
|
|
46
|
+
export const exitAnimation = (element) => {
|
|
47
|
+
if (CSS.supports('overlay: auto'))
|
|
48
|
+
return;
|
|
49
|
+
let cancel = () => { };
|
|
50
|
+
const done = () => {
|
|
51
|
+
cancel();
|
|
52
|
+
delete element.dataset.npExiting;
|
|
53
|
+
};
|
|
54
|
+
const onBeforeToggle = (event) => {
|
|
55
|
+
done();
|
|
56
|
+
if (event.newState !== 'closed')
|
|
57
|
+
return;
|
|
58
|
+
if (matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
59
|
+
return;
|
|
60
|
+
element.dataset.npExiting = '';
|
|
61
|
+
cancel = afterExit(element, done, { subtree: true });
|
|
62
|
+
};
|
|
63
|
+
element.addEventListener('beforetoggle', onBeforeToggle);
|
|
64
|
+
return () => {
|
|
65
|
+
element.removeEventListener('beforetoggle', onBeforeToggle);
|
|
66
|
+
done();
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export const morphSize = (element, apply, token) => {
|
|
70
|
+
if (!element || matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
71
|
+
apply();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const from = element.getBoundingClientRect();
|
|
75
|
+
element.getAnimations().forEach((animation) => animation.cancel());
|
|
76
|
+
apply();
|
|
77
|
+
const to = element.getBoundingClientRect();
|
|
78
|
+
const frame = (rect) => ({
|
|
79
|
+
...(from.width !== to.width && { width: `${rect.width}px` }),
|
|
80
|
+
...(from.height !== to.height && { height: `${rect.height}px` }),
|
|
81
|
+
});
|
|
82
|
+
if (from.width === to.width && from.height === to.height)
|
|
83
|
+
return;
|
|
84
|
+
element.style.overflow = 'hidden';
|
|
85
|
+
element.animate([frame(from), frame(to)], motionOf(element, token)).finished.then(() => element.style.removeProperty('overflow'), () => { });
|
|
86
|
+
};
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
let {
|
|
5
5
|
variant = 'small',
|
|
6
6
|
headline,
|
|
7
|
+
headlineLevel,
|
|
7
8
|
subtitle,
|
|
9
|
+
alignment = 'start',
|
|
10
|
+
image,
|
|
8
11
|
leading,
|
|
9
12
|
search,
|
|
10
13
|
trailing,
|
|
@@ -22,7 +25,15 @@
|
|
|
22
25
|
|
|
23
26
|
{#snippet titles(inline: boolean)}
|
|
24
27
|
<div class={['np-app-bar-titles', inline && 'np-app-bar-titles-inline']}>
|
|
25
|
-
|
|
28
|
+
{#if image && !inline}
|
|
29
|
+
<div class="np-app-bar-image">{@render image()}</div>
|
|
30
|
+
{/if}
|
|
31
|
+
<!-- The copy shown while collapsed is hidden from assistive technology, so only the other one
|
|
32
|
+
is the heading. -->
|
|
33
|
+
<svelte:element
|
|
34
|
+
this={headlineLevel && !inline ? `h${headlineLevel}` : 'div'}
|
|
35
|
+
class="np-app-bar-headline">{headline}</svelte:element
|
|
36
|
+
>
|
|
26
37
|
{#if subtitle}
|
|
27
38
|
<div class="np-app-bar-subtitle">{subtitle}</div>
|
|
28
39
|
{/if}
|
|
@@ -36,6 +47,7 @@
|
|
|
36
47
|
'np-app-bar',
|
|
37
48
|
`np-app-bar-${variant}`,
|
|
38
49
|
collapses && 'np-app-bar-collapsible',
|
|
50
|
+
alignment === 'center' && 'np-app-bar-center',
|
|
39
51
|
scroller === 'nearest' && 'np-app-bar-scroller-nearest',
|
|
40
52
|
attributes.class,
|
|
41
53
|
]}
|
|
@@ -48,6 +60,9 @@
|
|
|
48
60
|
<div class="np-app-bar-search-field">{@render search?.()}</div>
|
|
49
61
|
{:else if twoLine}
|
|
50
62
|
<div class="np-app-bar-inline" aria-hidden="true">{@render titles(true)}</div>
|
|
63
|
+
{:else if image}
|
|
64
|
+
<!-- In a small app bar the image takes the headline's place. -->
|
|
65
|
+
<div class="np-app-bar-titles">{@render image()}</div>
|
|
51
66
|
{:else}
|
|
52
67
|
{@render titles(false)}
|
|
53
68
|
{/if}
|
|
@@ -82,8 +97,10 @@
|
|
|
82
97
|
--_timeline: scroll(nearest block);
|
|
83
98
|
}
|
|
84
99
|
|
|
100
|
+
/* The bar is the open search's stacking context, so it rises to the search's own level, above
|
|
101
|
+
other bars and a page header. */
|
|
85
102
|
.np-app-bar:has(:global(.np-search-expanded)) {
|
|
86
|
-
z-index:
|
|
103
|
+
z-index: var(--np-search-z-index, 24);
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
.np-app-bar-row {
|
|
@@ -105,6 +122,11 @@
|
|
|
105
122
|
.np-app-bar-trailing {
|
|
106
123
|
margin-inline-start: auto;
|
|
107
124
|
}
|
|
125
|
+
/* The leading button is on surface, the trailing ones on surface variant. In a search app bar
|
|
126
|
+
they are all on surface variant. */
|
|
127
|
+
.np-app-bar:not(.np-app-bar-search) .np-app-bar-leading {
|
|
128
|
+
--np-icon-button-icon-color: var(--np-color-on-surface);
|
|
129
|
+
}
|
|
108
130
|
|
|
109
131
|
.np-app-bar-titles {
|
|
110
132
|
display: flex;
|
|
@@ -125,6 +147,7 @@
|
|
|
125
147
|
}
|
|
126
148
|
|
|
127
149
|
.np-app-bar-headline {
|
|
150
|
+
margin: 0;
|
|
128
151
|
font-size: var(--_headline-size);
|
|
129
152
|
line-height: var(--_headline-line-height);
|
|
130
153
|
font-weight: 400;
|
|
@@ -152,12 +175,20 @@
|
|
|
152
175
|
--_subtitle-line-height: 1rem;
|
|
153
176
|
}
|
|
154
177
|
|
|
178
|
+
/* The search fills the space between the leading and trailing elements up to 312dp, then
|
|
179
|
+
grows only to half of it, centred. The bar sits 8dp from the elements beside it, so the
|
|
180
|
+
margin a search keeps for its open view stays out of the row. */
|
|
155
181
|
.np-app-bar-search-field {
|
|
156
182
|
flex: 1;
|
|
157
183
|
min-width: 0;
|
|
158
184
|
display: flex;
|
|
159
185
|
align-items: center;
|
|
160
|
-
|
|
186
|
+
justify-content: center;
|
|
187
|
+
container-type: inline-size;
|
|
188
|
+
--np-search-pane-margin: 0;
|
|
189
|
+
}
|
|
190
|
+
.np-app-bar-search-field :global(.np-search:not(.np-search-full-screen.np-search-expanded)) {
|
|
191
|
+
width: min(100%, max(19.5rem, 50cqi));
|
|
161
192
|
}
|
|
162
193
|
.np-app-bar-search-field :global(.np-search:not(.np-search-expanded) .np-search-bar) {
|
|
163
194
|
background-color: var(--np-search-container-color, var(--np-color-surface-container));
|
|
@@ -179,7 +210,7 @@
|
|
|
179
210
|
--_headline-line-height: 2.75rem;
|
|
180
211
|
--_subtitle-size: 1rem;
|
|
181
212
|
--_subtitle-line-height: 1.5rem;
|
|
182
|
-
--_second-row-height:
|
|
213
|
+
--_second-row-height: 3.5rem;
|
|
183
214
|
}
|
|
184
215
|
|
|
185
216
|
.np-app-bar-second-row {
|
|
@@ -190,11 +221,44 @@
|
|
|
190
221
|
height: auto;
|
|
191
222
|
min-height: var(--_second-row-height);
|
|
192
223
|
padding-inline: 1rem;
|
|
193
|
-
padding-block-end:
|
|
224
|
+
padding-block-end: 0.75rem;
|
|
194
225
|
overflow: hidden;
|
|
195
226
|
}
|
|
196
|
-
.np-app-bar-
|
|
197
|
-
|
|
227
|
+
.np-app-bar-image {
|
|
228
|
+
display: flex;
|
|
229
|
+
margin-block-end: 0.5rem;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Centred, a small app bar puts its headline in the middle of the bar whatever sits beside it. */
|
|
233
|
+
.np-app-bar-center .np-app-bar-row {
|
|
234
|
+
display: grid;
|
|
235
|
+
grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr);
|
|
236
|
+
}
|
|
237
|
+
.np-app-bar-center .np-app-bar-leading {
|
|
238
|
+
grid-column: 1;
|
|
239
|
+
}
|
|
240
|
+
.np-app-bar-center .np-app-bar-row > .np-app-bar-titles,
|
|
241
|
+
.np-app-bar-center .np-app-bar-inline,
|
|
242
|
+
.np-app-bar-center .np-app-bar-search-field {
|
|
243
|
+
grid-column: 2;
|
|
244
|
+
padding-inline: 0;
|
|
245
|
+
}
|
|
246
|
+
.np-app-bar-center.np-app-bar-search .np-app-bar-row {
|
|
247
|
+
grid-template-columns: auto 1fr auto;
|
|
248
|
+
}
|
|
249
|
+
.np-app-bar-center .np-app-bar-trailing {
|
|
250
|
+
grid-column: 3;
|
|
251
|
+
justify-self: end;
|
|
252
|
+
}
|
|
253
|
+
.np-app-bar-center .np-app-bar-titles {
|
|
254
|
+
align-items: center;
|
|
255
|
+
text-align: center;
|
|
256
|
+
}
|
|
257
|
+
.np-app-bar-center .np-app-bar-second-row {
|
|
258
|
+
justify-content: center;
|
|
259
|
+
}
|
|
260
|
+
.np-app-bar-center .np-app-bar-search-field :global(.np-search-input) {
|
|
261
|
+
text-align: center;
|
|
198
262
|
}
|
|
199
263
|
|
|
200
264
|
.np-app-bar-inline {
|
package/dist/app-bar/types.d.ts
CHANGED
|
@@ -3,7 +3,13 @@ import type { HTMLAttributes } from 'svelte/elements';
|
|
|
3
3
|
export interface AppBarProps extends HTMLAttributes<HTMLElement> {
|
|
4
4
|
variant?: 'search' | 'small' | 'medium' | 'large';
|
|
5
5
|
headline?: string;
|
|
6
|
+
/** Renders the headline as a heading of this level. Without it the headline is plain text. */
|
|
7
|
+
headlineLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
6
8
|
subtitle?: string;
|
|
9
|
+
/** Headline and subtitle at the leading edge or centered. */
|
|
10
|
+
alignment?: 'start' | 'center';
|
|
11
|
+
/** An image or logo. It replaces the headline of a small app bar and sits above it otherwise. */
|
|
12
|
+
image?: Snippet;
|
|
7
13
|
leading?: Snippet;
|
|
8
14
|
search?: Snippet;
|
|
9
15
|
trailing?: Snippet;
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
let useVirtualList = $derived(displayOptions.length > virtualThreshold)
|
|
39
39
|
let widthProp = $derived(clampMenuWidth || useVirtualList ? 'width' : 'min-width')
|
|
40
40
|
let menuElement = $state<HTMLDivElement>()
|
|
41
|
+
let virtualList = $state<VirtualList<AutoCompleteOption>>()
|
|
41
42
|
let finalPopulated = $derived(populated)
|
|
42
43
|
let activeIndex = $state(NO_INDEX)
|
|
43
44
|
|
|
@@ -81,25 +82,7 @@
|
|
|
81
82
|
optEl.scrollIntoView({ block: 'nearest' })
|
|
82
83
|
return
|
|
83
84
|
}
|
|
84
|
-
if (useVirtualList
|
|
85
|
-
const viewport = menuElement.querySelector(
|
|
86
|
-
'svelte-virtual-list-viewport',
|
|
87
|
-
) as HTMLElement | null
|
|
88
|
-
if (!viewport) return
|
|
89
|
-
let rowHeight = 48
|
|
90
|
-
const firstRow = viewport.querySelector('[id^="' + uid + '-opt-"]') as HTMLElement | null
|
|
91
|
-
if (firstRow) {
|
|
92
|
-
rowHeight = firstRow.offsetHeight || rowHeight
|
|
93
|
-
}
|
|
94
|
-
const top = activeIndex * rowHeight
|
|
95
|
-
const bottom = top + rowHeight
|
|
96
|
-
const { scrollTop, clientHeight } = viewport
|
|
97
|
-
if (top < scrollTop) {
|
|
98
|
-
viewport.scrollTop = top
|
|
99
|
-
} else if (bottom > scrollTop + clientHeight) {
|
|
100
|
-
viewport.scrollTop = bottom - clientHeight
|
|
101
|
-
}
|
|
102
|
-
}
|
|
85
|
+
if (useVirtualList) virtualList?.scrollToIndex(activeIndex)
|
|
103
86
|
})
|
|
104
87
|
</script>
|
|
105
88
|
|
|
@@ -169,13 +152,9 @@
|
|
|
169
152
|
event.preventDefault()
|
|
170
153
|
return
|
|
171
154
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
event.
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
if (event.key === 'End') {
|
|
178
|
-
setActive(displayOptions.length - 1)
|
|
155
|
+
// Home and End move the caret, unless the arrows have already moved into the options.
|
|
156
|
+
if ((event.key === 'Home' || event.key === 'End') && open && activeIndex >= 0) {
|
|
157
|
+
setActive(event.key === 'Home' ? 0 : displayOptions.length - 1)
|
|
179
158
|
event.preventDefault()
|
|
180
159
|
return
|
|
181
160
|
}
|
|
@@ -227,11 +206,7 @@
|
|
|
227
206
|
bind:element={menuElement}
|
|
228
207
|
>
|
|
229
208
|
{#if useVirtualList}
|
|
230
|
-
<VirtualList
|
|
231
|
-
height="250px"
|
|
232
|
-
itemHeight={displayOptions.some(({ supportingText }) => supportingText) ? 68 : 48}
|
|
233
|
-
items={displayOptions}
|
|
234
|
-
>
|
|
209
|
+
<VirtualList bind:this={virtualList} height="250px" items={displayOptions}>
|
|
235
210
|
{#snippet row(option, index)}
|
|
236
211
|
{@render item(option, index)}
|
|
237
212
|
{/snippet}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AutoCompleteProps } from './types.js';
|
|
2
|
-
declare const AutoComplete: import("svelte").Component<AutoCompleteProps, {}, "
|
|
2
|
+
declare const AutoComplete: import("svelte").Component<AutoCompleteProps, {}, "open" | "element" | "value">;
|
|
3
3
|
type AutoComplete = ReturnType<typeof AutoComplete>;
|
|
4
4
|
export default AutoComplete;
|
package/dist/badge/Badge.svelte
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { badgeText } from './description.js'
|
|
2
3
|
import type { BadgeProps } from './types.js'
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
>
|
|
24
25
|
{#if label !== undefined}
|
|
25
26
|
<div class="np-badge-label">
|
|
26
|
-
{label}
|
|
27
|
+
{badgeText(label)}
|
|
27
28
|
</div>
|
|
28
29
|
{/if}
|
|
29
30
|
</div>
|
|
@@ -35,15 +36,21 @@
|
|
|
35
36
|
background-color: var(--np-color-error);
|
|
36
37
|
border-radius: var(--np-shape-corner-full);
|
|
37
38
|
position: var(--np-badge-position, absolute);
|
|
38
|
-
top: var(--np-badge-top,
|
|
39
|
-
inset-inline-start: var(--np-badge-start,
|
|
40
|
-
inset-inline-end: var(--np-badge-end, auto);
|
|
39
|
+
top: var(--np-badge-top, var(--_top));
|
|
40
|
+
inset-inline-start: var(--np-badge-start, var(--_start));
|
|
41
41
|
}
|
|
42
|
+
/* Anchored inside the icon at its top trailing corner. A large badge keeps its leading edge and
|
|
43
|
+
grows toward the trailing side. */
|
|
42
44
|
.np-badge-container-label {
|
|
45
|
+
--_top: -0.125rem;
|
|
46
|
+
--_start: calc(100% - 0.75rem);
|
|
47
|
+
box-sizing: border-box;
|
|
43
48
|
height: 1rem;
|
|
44
49
|
min-width: 1rem;
|
|
45
50
|
}
|
|
46
51
|
.np-badge-container-no-label {
|
|
52
|
+
--_top: 0;
|
|
53
|
+
--_start: calc(100% - 0.375rem);
|
|
47
54
|
width: 0.375rem;
|
|
48
55
|
height: 0.375rem;
|
|
49
56
|
}
|
|
@@ -52,6 +59,7 @@
|
|
|
52
59
|
padding-inline: 0.25rem;
|
|
53
60
|
font-weight: 500;
|
|
54
61
|
font-size: 0.6875rem;
|
|
62
|
+
white-space: nowrap;
|
|
55
63
|
display: flex;
|
|
56
64
|
align-items: center;
|
|
57
65
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** What a navigation destination reads after its name for its badge: the given label, else the count, else "New notification". */
|
|
2
|
+
export declare const badgeDescription: (label: string | number | undefined, ariaLabel?: string) => string;
|
|
3
|
+
/** Shows counts above 999 as "999+", so a large badge keeps to four characters. */
|
|
4
|
+
export declare const badgeText: (label: string | number) => string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** What a navigation destination reads after its name for its badge: the given label, else the count, else "New notification". */
|
|
2
|
+
export const badgeDescription = (label, ariaLabel) => ariaLabel ?? (label !== undefined ? String(label) : 'New notification');
|
|
3
|
+
/** Shows counts above 999 as "999+", so a large badge keeps to four characters. */
|
|
4
|
+
export const badgeText = (label) => typeof label === 'number' && label > 999 ? '999+' : String(label);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import Ripple from '../ripple/Ripple.svelte'
|
|
6
6
|
import Tooltip from '../tooltip/Tooltip.svelte'
|
|
7
7
|
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
8
|
+
import { getButtonGroupContext } from './groupContext.js'
|
|
8
9
|
import type { ButtonProps } from './types.js'
|
|
9
10
|
|
|
10
11
|
let {
|
|
@@ -17,8 +18,8 @@
|
|
|
17
18
|
disabled = false,
|
|
18
19
|
loading = false,
|
|
19
20
|
loadingAriaLabel,
|
|
20
|
-
size
|
|
21
|
-
shape
|
|
21
|
+
size: sizeProp,
|
|
22
|
+
shape: shapeProp,
|
|
22
23
|
toggle = false,
|
|
23
24
|
selected = $bindable(false),
|
|
24
25
|
...attributes
|
|
@@ -29,6 +30,24 @@
|
|
|
29
30
|
let isLink = $derived(attributes.href != null && !disabled && !loading)
|
|
30
31
|
|
|
31
32
|
let tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
|
|
33
|
+
// A visible label names the button, the tooltip only describes it. Without one, the tooltip
|
|
34
|
+
// text is the name, and describing the button with it again would read it twice.
|
|
35
|
+
let tooltipNames = $derived(!!tooltipId && !children && !attributes['aria-label'])
|
|
36
|
+
|
|
37
|
+
// Inside a ButtonGroup, the group sets the size and shape its buttons leave open, and with
|
|
38
|
+
// `selection` it decides what is selected.
|
|
39
|
+
const group = getButtonGroupContext()
|
|
40
|
+
let size = $derived(sizeProp ?? group?.size ?? 's')
|
|
41
|
+
let shape = $derived(shapeProp ?? group?.shape ?? 'round')
|
|
42
|
+
let groupValue = $derived(
|
|
43
|
+
group?.selection && attributes.value != null ? String(attributes.value) : undefined,
|
|
44
|
+
)
|
|
45
|
+
let isToggle = $derived(toggle || groupValue !== undefined)
|
|
46
|
+
let isSelected = $derived(groupValue !== undefined ? group!.isSelected(groupValue) : selected)
|
|
47
|
+
// A selected toggle swaps its resting shape: round turns square, square turns round.
|
|
48
|
+
let shapeClass = $derived(
|
|
49
|
+
isSelected ? (shape === 'square' ? 'round' : 'square') : loading ? 'square' : shape,
|
|
50
|
+
)
|
|
32
51
|
|
|
33
52
|
const morph = pressMorph()
|
|
34
53
|
|
|
@@ -41,6 +60,7 @@
|
|
|
41
60
|
{#snippet content()}
|
|
42
61
|
{#if !disabled && !loading}
|
|
43
62
|
<Ripple forElement={element} />
|
|
63
|
+
<span class="np-touch"></span>
|
|
44
64
|
{/if}
|
|
45
65
|
{#if loading}
|
|
46
66
|
<div class="circular-progress">
|
|
@@ -68,21 +88,22 @@
|
|
|
68
88
|
<a
|
|
69
89
|
{...attributes}
|
|
70
90
|
onclick={(event) => {
|
|
71
|
-
if (!
|
|
91
|
+
if (!isToggle) {
|
|
72
92
|
handlePress()
|
|
73
93
|
}
|
|
74
94
|
attributes.onclick?.(event)
|
|
75
95
|
}}
|
|
76
|
-
aria-describedby={tooltipId
|
|
96
|
+
aria-describedby={(!tooltipNames && tooltipId) || attributes['aria-describedby']}
|
|
77
97
|
interestfor={tooltipId ?? attributes['interestfor']}
|
|
78
|
-
aria-label={
|
|
98
|
+
aria-label={attributes['aria-label'] ?? (tooltipNames ? title : undefined)}
|
|
79
99
|
bind:this={element}
|
|
80
100
|
class={[
|
|
81
101
|
'np-button',
|
|
102
|
+
'np-focus-ring',
|
|
82
103
|
size,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
shapeClass,
|
|
105
|
+
isToggle && 'toggle',
|
|
106
|
+
isSelected && 'selected',
|
|
86
107
|
'enabled',
|
|
87
108
|
variant,
|
|
88
109
|
morph.pressed && 'pressed',
|
|
@@ -94,16 +115,18 @@
|
|
|
94
115
|
{:else}
|
|
95
116
|
<button
|
|
96
117
|
{...attributes as HTMLButtonAttributes}
|
|
97
|
-
aria-describedby={tooltipId
|
|
118
|
+
aria-describedby={(!tooltipNames && tooltipId) || attributes['aria-describedby']}
|
|
98
119
|
interestfor={tooltipId ?? attributes['interestfor']}
|
|
99
|
-
aria-label={
|
|
120
|
+
aria-label={attributes['aria-label'] ?? (tooltipNames ? title : undefined)}
|
|
100
121
|
disabled={disabled || loading}
|
|
101
|
-
aria-pressed={
|
|
122
|
+
aria-pressed={isToggle ? isSelected : undefined}
|
|
102
123
|
aria-busy={loading}
|
|
103
124
|
type={(attributes['type'] as 'button' | 'submit' | 'reset' | 'button') ?? undefined}
|
|
104
125
|
bind:this={element}
|
|
105
126
|
onclick={(event) => {
|
|
106
|
-
if (
|
|
127
|
+
if (groupValue !== undefined) {
|
|
128
|
+
group!.toggle(groupValue)
|
|
129
|
+
} else if (toggle) {
|
|
107
130
|
selected = !selected
|
|
108
131
|
} else {
|
|
109
132
|
handlePress()
|
|
@@ -112,10 +135,11 @@
|
|
|
112
135
|
}}
|
|
113
136
|
class={[
|
|
114
137
|
'np-button',
|
|
138
|
+
'np-focus-ring',
|
|
115
139
|
size,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
shapeClass,
|
|
141
|
+
isToggle && 'toggle',
|
|
142
|
+
isSelected && 'selected',
|
|
119
143
|
loading && 'np-loading',
|
|
120
144
|
disabled || loading ? `${variant}-disabled disabled` : `${variant} enabled`,
|
|
121
145
|
morph.pressed && 'pressed',
|
|
@@ -131,10 +155,10 @@
|
|
|
131
155
|
{/if}
|
|
132
156
|
|
|
133
157
|
<style>
|
|
158
|
+
/* The label is never truncated or wrapped, the button grows to fit it instead. */
|
|
134
159
|
.children-wrapper {
|
|
135
|
-
|
|
136
|
-
overflow: var(--_button-label-overflow,
|
|
137
|
-
text-overflow: var(--_button-label-text-overflow, ellipsis);
|
|
160
|
+
min-width: 0;
|
|
161
|
+
overflow: var(--_button-label-overflow, visible);
|
|
138
162
|
text-wrap: nowrap;
|
|
139
163
|
}
|
|
140
164
|
.circular-progress {
|
|
@@ -162,8 +186,10 @@
|
|
|
162
186
|
display: inline-flex;
|
|
163
187
|
user-select: none;
|
|
164
188
|
align-items: center;
|
|
165
|
-
|
|
166
|
-
|
|
189
|
+
/* Icon and label stay grouped and centered, also in a stretched button. */
|
|
190
|
+
justify-content: center;
|
|
191
|
+
text-align: center;
|
|
192
|
+
min-width: var(--_button-min-width, max-content);
|
|
167
193
|
font-weight: 500;
|
|
168
194
|
text-decoration: none;
|
|
169
195
|
--np-icon-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
@@ -270,16 +296,13 @@
|
|
|
270
296
|
.outlined-disabled {
|
|
271
297
|
--_outlined-border-color: color-mix(in srgb, var(--np-color-on-surface) 12%, transparent);
|
|
272
298
|
}
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
.enabled:focus-visible {
|
|
281
|
-
animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
|
|
282
|
-
}
|
|
299
|
+
/* Extra small and small buttons keep a 48px tall target around them. */
|
|
300
|
+
.np-touch {
|
|
301
|
+
position: absolute;
|
|
302
|
+
inset-inline: 0;
|
|
303
|
+
top: 50%;
|
|
304
|
+
translate: 0 -50%;
|
|
305
|
+
height: max(3rem, 100%);
|
|
283
306
|
}
|
|
284
307
|
.text {
|
|
285
308
|
color: var(--np-text-button-label-text-color, var(--np-color-primary));
|