noph-ui 0.45.0 → 0.46.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 +0 -15
- package/dist/animation.js +0 -15
- package/dist/app-bar/AppBar.svelte +2 -52
- package/dist/app-bar/types.d.ts +0 -22
- package/dist/button/SplitButton.svelte +0 -17
- package/dist/button/types.d.ts +0 -6
- package/dist/checkbox/Checkbox.svelte +0 -2
- package/dist/chip/FilterChip.svelte +0 -2
- package/dist/chip/types.d.ts +0 -8
- package/dist/date-picker/DatePickerDialog.svelte +0 -10
- package/dist/date-picker/DateRangePicker.svelte +0 -5
- package/dist/date-picker/DockedDatePicker.svelte +1 -11
- package/dist/date-picker/DockedDateTimePicker.svelte +1 -8
- package/dist/date-picker/exitVisibility.svelte.d.ts +0 -5
- package/dist/date-picker/exitVisibility.svelte.js +0 -7
- package/dist/date-picker/optionList.d.ts +0 -12
- package/dist/date-picker/optionList.js +0 -12
- package/dist/dialog/Dialog.svelte +0 -22
- package/dist/dialog/types.d.ts +0 -1
- package/dist/fab/ExtendedFab.svelte +0 -26
- package/dist/fab/Fab.svelte +0 -28
- package/dist/fab/FabMenu.svelte +0 -48
- package/dist/fab/types.d.ts +0 -24
- package/dist/loading-indicator/shapes.js +0 -7
- package/dist/navigation-bar/NavigationBar.svelte +0 -7
- package/dist/navigation-bar/NavigationBarItem.svelte +0 -22
- package/dist/navigation-bar/types.d.ts +0 -10
- package/dist/navigation-drawer/NavigationDrawer.svelte +0 -21
- package/dist/navigation-drawer/types.d.ts +0 -3
- package/dist/popover.svelte.d.ts +0 -9
- package/dist/popover.svelte.js +0 -9
- package/dist/press.svelte.d.ts +0 -13
- package/dist/press.svelte.js +0 -13
- package/dist/search/Search.svelte +23 -81
- package/dist/search/Search.svelte.d.ts +4 -1
- package/dist/search/types.d.ts +0 -20
- package/dist/sheet/Sheet.svelte +0 -25
- package/dist/sheet/types.d.ts +0 -8
- package/dist/slider/Slider.svelte +0 -36
- package/dist/snackbar/Snackbar.svelte +3 -17
- package/dist/tabs/Tab.svelte +0 -4
- package/dist/tabs/Tabs.svelte +2 -2
- package/dist/text-field/TextField.svelte +0 -2
- package/dist/toolbar/Toolbar.svelte +0 -12
- package/dist/toolbar/types.d.ts +0 -7
- package/dist/tooltip/RichTooltip.svelte +0 -7
- package/package.json +1 -1
package/dist/fab/Fab.svelte
CHANGED
|
@@ -109,13 +109,6 @@
|
|
|
109
109
|
--np-ripple-pressed-color: var(--np-fab-icon-color, var(--_icon-color));
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
/*
|
|
113
|
-
* `round` is fully rounded and `square` is the smaller radius, the same way Button reads.
|
|
114
|
-
* The FAB box is always square, so `50%` renders identically to `--np-shape-corner-full`
|
|
115
|
-
* (9999px) at rest. It matters for the pressed transition below: animating `border-radius`
|
|
116
|
-
* from a huge fixed value spends nearly the whole transition on radii that still render as
|
|
117
|
-
* fully round, so the morph never becomes visible during a normal, brief click.
|
|
118
|
-
*/
|
|
119
112
|
.round {
|
|
120
113
|
border-radius: var(--np-fab-shape, 50%);
|
|
121
114
|
}
|
|
@@ -123,13 +116,6 @@
|
|
|
123
116
|
border-radius: var(--np-fab-shape, var(--_square-radius));
|
|
124
117
|
}
|
|
125
118
|
|
|
126
|
-
/*
|
|
127
|
-
* A press morphs the corner towards the other shape. `:active` alone drops the moment the
|
|
128
|
-
* pointer lifts, which on a normal click is long before the morph has played, so the short
|
|
129
|
-
* `pressed` class holds the shape until the whole round trip is visible, the way Button does
|
|
130
|
-
* it. Both are gated on the motion preference, since without the transition below this is
|
|
131
|
-
* only an instant snap.
|
|
132
|
-
*/
|
|
133
119
|
@media (prefers-reduced-motion: no-preference) {
|
|
134
120
|
.round:not(:disabled):is(:active, .pressed) {
|
|
135
121
|
border-radius: var(--np-fab-pressed-shape, var(--_square-radius));
|
|
@@ -148,11 +134,6 @@
|
|
|
148
134
|
}
|
|
149
135
|
}
|
|
150
136
|
|
|
151
|
-
/*
|
|
152
|
-
* M3's three recommended sizes, matching the extended FAB's. The 40px FAB it used to also
|
|
153
|
-
* define is gone: M3 Expressive no longer recommends it. Each square radius is the shape token
|
|
154
|
-
* that goes with the size, large, large-increased and extra-large.
|
|
155
|
-
*/
|
|
156
137
|
.s {
|
|
157
138
|
width: 3.5rem;
|
|
158
139
|
height: 3.5rem;
|
|
@@ -172,10 +153,6 @@
|
|
|
172
153
|
--np-icon-size: 2.25rem;
|
|
173
154
|
}
|
|
174
155
|
|
|
175
|
-
/*
|
|
176
|
-
* M3's colour styles, named after the tokens they map to. The tone styles are the stronger
|
|
177
|
-
* pair, the container styles the softer one. The surface style M3 Expressive dropped is gone.
|
|
178
|
-
*/
|
|
179
156
|
.np-fab-primary {
|
|
180
157
|
--_container-color: var(--np-color-primary);
|
|
181
158
|
--_icon-color: var(--np-color-on-primary);
|
|
@@ -226,11 +203,6 @@
|
|
|
226
203
|
color: color-mix(in srgb, var(--np-color-on-surface) 38%, transparent);
|
|
227
204
|
}
|
|
228
205
|
|
|
229
|
-
/*
|
|
230
|
-
* The icon is never a hit target, matching `Button`'s. Otherwise swapping it out from under
|
|
231
|
-
* the pointer, which `FabMenu`'s trigger does when it opens, moves the pointer to a different
|
|
232
|
-
* element; the browser hands the button fresh interest on the way and reopens its tooltip.
|
|
233
|
-
*/
|
|
234
206
|
.np-fab-icon {
|
|
235
207
|
display: flex;
|
|
236
208
|
fill: currentColor;
|
package/dist/fab/FabMenu.svelte
CHANGED
|
@@ -24,18 +24,11 @@
|
|
|
24
24
|
|
|
25
25
|
const uid = $props.id()
|
|
26
26
|
const menuId = `np-fab-menu-${uid}`
|
|
27
|
-
/*
|
|
28
|
-
* One anchor name per instance. A fixed name in the stylesheet is shared by every FabMenu on
|
|
29
|
-
* the page, and a duplicated anchor name resolves to the last one in tree order, so every menu
|
|
30
|
-
* would open on the last trigger.
|
|
31
|
-
*/
|
|
32
27
|
const anchorName = `--np-fab-menu-${uid}`
|
|
33
28
|
|
|
34
29
|
let menuElement: HTMLDivElement | undefined = $state()
|
|
35
30
|
const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)', false)
|
|
36
31
|
|
|
37
|
-
// Declaring `role="menu"` means owing the keyboard model that goes with it: one tab stop, the
|
|
38
|
-
// arrows moving between actions. The same attachments the toolbar and the bars use.
|
|
39
32
|
const ITEMS = 'button, a[href], [role="menuitem"]'
|
|
40
33
|
const attach = rovingTabindex(ITEMS)
|
|
41
34
|
const arrowHandler = $derived(
|
|
@@ -51,12 +44,6 @@
|
|
|
51
44
|
controller.close()
|
|
52
45
|
}
|
|
53
46
|
|
|
54
|
-
/*
|
|
55
|
-
* An item runs its own press animation on click, the corner morph a Button or a FAB does. The
|
|
56
|
-
* menu is a popover, so closing it means `display: none`, which would cut that off before a
|
|
57
|
-
* frame of it is seen. Holding the close for the length of the press lets the item finish.
|
|
58
|
-
* Matched to Button's own timer, and skipped entirely when there is no animation to wait for.
|
|
59
|
-
*/
|
|
60
47
|
let closeTimeout: ReturnType<typeof setTimeout>
|
|
61
48
|
|
|
62
49
|
const closeAfterPress = () => {
|
|
@@ -82,10 +69,6 @@
|
|
|
82
69
|
)
|
|
83
70
|
</script>
|
|
84
71
|
|
|
85
|
-
<!--
|
|
86
|
-
Both icons stay mounted and turn into each other. Swapping one for the other cannot animate:
|
|
87
|
-
the icon arriving has no previous state to move from, so it can only appear.
|
|
88
|
-
-->
|
|
89
72
|
{#snippet triggerIcon()}
|
|
90
73
|
<span class="np-fab-menu-icons" aria-hidden="true">
|
|
91
74
|
<span class="np-fab-menu-icon" class:np-fab-menu-icon-away={open}>
|
|
@@ -103,12 +86,6 @@
|
|
|
103
86
|
style="anchor-name: {anchorName}; {attributes.style ?? ''}"
|
|
104
87
|
class={['np-fab-menu', `np-fab-menu-${placement}`, attributes.class]}
|
|
105
88
|
>
|
|
106
|
-
<!--
|
|
107
|
-
The tooltip is dropped while the menu is open, and the name given directly instead. A
|
|
108
|
-
tooltip is a `hint` popover, which the browser closes when the menu's `auto` popover opens,
|
|
109
|
-
and the trigger still holds interest at that point, so it would come straight back and sit
|
|
110
|
-
over the open menu. There is nothing to hint at anyway once the actions are on screen.
|
|
111
|
-
-->
|
|
112
89
|
<Fab
|
|
113
90
|
{variant}
|
|
114
91
|
{size}
|
|
@@ -134,8 +111,6 @@
|
|
|
134
111
|
{@attach attach}
|
|
135
112
|
onkeydown={arrowHandler}
|
|
136
113
|
onclick={(event) => {
|
|
137
|
-
// Picking an action closes the menu. `closeOnSelect={false}` keeps it open for a menu
|
|
138
|
-
// whose items toggle something instead of navigating away.
|
|
139
114
|
if (!closeOnSelect) return
|
|
140
115
|
const target = event.target as HTMLElement
|
|
141
116
|
if (target.closest(ITEMS)) closeAfterPress()
|
|
@@ -154,7 +129,6 @@
|
|
|
154
129
|
display: inline-flex;
|
|
155
130
|
}
|
|
156
131
|
|
|
157
|
-
/* One grid cell holds both icons, so they sit on top of each other rather than side by side. */
|
|
158
132
|
.np-fab-menu-icons {
|
|
159
133
|
display: grid;
|
|
160
134
|
}
|
|
@@ -172,11 +146,6 @@
|
|
|
172
146
|
opacity var(--np-motion-expressive-fast-effects),
|
|
173
147
|
rotate var(--np-motion-expressive-default-spatial);
|
|
174
148
|
}
|
|
175
|
-
/*
|
|
176
|
-
* Both turn the same way, so the pair reads as one icon rotating rather than two crossing
|
|
177
|
-
* over. The close icon spends its hidden time at -45deg, where an X sits square like the
|
|
178
|
-
* plus it replaces, so opening rotates the plus away and the X into place behind it.
|
|
179
|
-
*/
|
|
180
149
|
.np-fab-menu-icon-away {
|
|
181
150
|
rotate: -45deg;
|
|
182
151
|
}
|
|
@@ -194,16 +163,10 @@
|
|
|
194
163
|
overflow: visible;
|
|
195
164
|
}
|
|
196
165
|
|
|
197
|
-
/*
|
|
198
|
-
* `display` only while open. Setting it on the base rule would beat the user agent's
|
|
199
|
-
* `display: none` for a closed popover, because author styles win over user agent styles
|
|
200
|
-
* whatever the specificity, leaving an invisible sheet over the page that swallows clicks.
|
|
201
|
-
*/
|
|
202
166
|
.np-fab-menu-list:popover-open {
|
|
203
167
|
display: flex;
|
|
204
168
|
}
|
|
205
169
|
|
|
206
|
-
/* Which side the items fan out to, and which end they stack from. */
|
|
207
170
|
.np-fab-menu-block-start .np-fab-menu-list {
|
|
208
171
|
position-area: block-start;
|
|
209
172
|
margin-block-end: 0.75rem;
|
|
@@ -226,17 +189,6 @@
|
|
|
226
189
|
align-items: center;
|
|
227
190
|
}
|
|
228
191
|
|
|
229
|
-
/*
|
|
230
|
-
* The items rise into place one after another. Keyframes rather than a transition: a popover
|
|
231
|
-
* goes from `display: none` to shown in one step, and a transition has no start value to run
|
|
232
|
-
* from, so the items would simply appear. An animation runs when the element becomes displayed.
|
|
233
|
-
*
|
|
234
|
-
* The whole stagger fits one window, `--_stagger-window`, however many actions there are. That
|
|
235
|
-
* is what `sibling-count()` buys over a fixed step: eight actions land in the same time two do,
|
|
236
|
-
* each one just following the last a little sooner, rather than the menu taking four times as
|
|
237
|
-
* long to finish arriving. Item one starts at 0ms, so the action nearest the trigger moves
|
|
238
|
-
* first, and the last one starts as the window closes.
|
|
239
|
-
*/
|
|
240
192
|
@media (prefers-reduced-motion: no-preference) {
|
|
241
193
|
.np-fab-menu-list:popover-open > :global(*) {
|
|
242
194
|
--_stagger-window: 210ms;
|
package/dist/fab/types.d.ts
CHANGED
|
@@ -4,16 +4,7 @@ import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from
|
|
|
4
4
|
export type FabElement = ButtonElement;
|
|
5
5
|
interface BaseFabProps extends HTMLAttributes<FabElement>, Omit<HTMLButtonAttributes, keyof HTMLAttributes<HTMLButtonElement> | 'type'>, Omit<HTMLAnchorAttributes, keyof HTMLAttributes<HTMLAnchorElement> | 'type'> {
|
|
6
6
|
element?: FabElement;
|
|
7
|
-
/**
|
|
8
|
-
* Colour style, named after the tokens it maps to. The three tone styles are the stronger
|
|
9
|
-
* pair, the three container styles the softer one. M3 Expressive no longer recommends a
|
|
10
|
-
* surface-coloured FAB, so there is no `surface`.
|
|
11
|
-
*/
|
|
12
7
|
variant?: 'primary' | 'secondary' | 'tertiary' | 'primary-container' | 'secondary-container' | 'tertiary-container';
|
|
13
|
-
/**
|
|
14
|
-
* 56, 80 and 96 pixels, the same three `Fab` and `ExtendedFab` share. `s` is M3's baseline FAB
|
|
15
|
-
* and the default. The 40 pixel FAB is not here: M3 Expressive no longer recommends it.
|
|
16
|
-
*/
|
|
17
8
|
size?: 's' | 'm' | 'l';
|
|
18
9
|
shape?: 'round' | 'square';
|
|
19
10
|
lowered?: boolean;
|
|
@@ -22,36 +13,21 @@ interface BaseFabProps extends HTMLAttributes<FabElement>, Omit<HTMLButtonAttrib
|
|
|
22
13
|
}
|
|
23
14
|
export interface FabProps extends BaseFabProps {
|
|
24
15
|
icon?: Snippet;
|
|
25
|
-
/**
|
|
26
|
-
* A FAB shows no text, so its accessible name has to come from somewhere. Sets both
|
|
27
|
-
* `aria-label` and a plain tooltip, the way `Button` treats `title`.
|
|
28
|
-
*/
|
|
29
16
|
label?: string;
|
|
30
17
|
}
|
|
31
18
|
export interface ExtendedFabProps extends BaseFabProps {
|
|
32
19
|
icon?: Snippet;
|
|
33
|
-
/** Visible text. An extended FAB is named by it, so it needs no `aria-label`. */
|
|
34
20
|
label?: string;
|
|
35
|
-
/**
|
|
36
|
-
* Collapses to an icon-only FAB, animating the width. Drive it from a scroll or a media query
|
|
37
|
-
* to get M3's collapsing FAB.
|
|
38
|
-
*/
|
|
39
21
|
collapsed?: boolean;
|
|
40
22
|
}
|
|
41
23
|
export interface FabMenuProps extends HTMLAttributes<HTMLDivElement> {
|
|
42
|
-
/** Names the trigger and the menu. The trigger shows no text. */
|
|
43
24
|
label?: string;
|
|
44
|
-
/** Trigger icon while the menu is closed. */
|
|
45
25
|
icon?: Snippet;
|
|
46
|
-
/** Trigger icon while the menu is open. Defaults to a close icon. */
|
|
47
26
|
closeIcon?: Snippet;
|
|
48
27
|
variant?: FabProps['variant'];
|
|
49
28
|
size?: FabProps['size'];
|
|
50
|
-
/** Which side the items fan out to. */
|
|
51
29
|
placement?: 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
|
|
52
|
-
/** Bindable. Whether the menu is open. */
|
|
53
30
|
open?: boolean;
|
|
54
|
-
/** Closes the menu when an action is picked, which is what M3 does. */
|
|
55
31
|
closeOnSelect?: boolean;
|
|
56
32
|
element?: HTMLDivElement;
|
|
57
33
|
}
|
|
@@ -7,19 +7,12 @@ const decode = (encoded) => {
|
|
|
7
7
|
return radii;
|
|
8
8
|
};
|
|
9
9
|
export const SHAPES = [
|
|
10
|
-
// MaterialShapes.SoftBurst
|
|
11
10
|
'2zl30r3533bb3ik3pq3se3s33oh3gs39u33v30a2zn31b3693co3k53qk3sk3ro3n53fb38k32t2zz2zr32037h3e23ls3r83sm3r43lj3dv37c31y2zr30032w38p3fi3ne3rq3sj3qf3jw3ch3653192zm30b33y39z3h03oo3s53sd3pk3ib3b534z30q2zl30r3533bb3ik3pq3se3s33oh3gs39u33v30a2zn31b3693co3k53qk3sk3ro3n53fb38k32t2zz2zr32037h3e23ls3r83sm3r43lj3dv37c31y2zr30032w38p3fi3ne3rq3sj3qf3jw3ch3653192zm30b33y39z3h03oo3s53sd3pk3ib3b534z30q',
|
|
12
|
-
// MaterialShapes.Cookie9Sided
|
|
13
11
|
'3ne3kp3hr3g53fr3gl3iq3m33ot3qq3rw3sa3rz3qx3p33mg3jc3hi3gz3hn3jn3my3pp3rn3st3t73sv3rs3pw3n73jz3i23hg3i23jz3n73pw3rs3sv3t73st3rn3pp3my3jn3hn3gz3hi3jc3mg3p33qx3rz3sa3rw3qq3ot3m33iq3gl3fr3g53hr3kp3ne3pb3qg3qw3qm3pm3nv3lc3i13fk3eg3ei3fr3ie3lc3nh3ov3pk3pk3ou3ne3l83i93fb3dr3df3e83gb3jh3lz3nr3os3p53os3nr3lz3jh3gb3e83df3dr3fb3i93l83ne3ou3pk3pk3ov3nh3lc3ie3fr3ei3eg3fk3i13lc3nv3pm3qm3qw3qg3pb',
|
|
14
|
-
// MaterialShapes.Pentagon
|
|
15
12
|
'3ng3lh3jw3im3ho3h23gq3gq3h03hl3ih3jp3l93n63ph3s73vc3y23zz41241e40z3zr3xr3uz3se3q73od3mx3lt3l13kk3kf3kk3l13lt3mx3od3q73se3uz3xr3zr40z41e4123zz3y23vc3s73ph3n63l93jp3ih3hl3h03gq3gq3h23ho3im3jw3lh3ng3pp3rf3se3sp3sb3r93pg3mx3jm3gc3dh3b138z37935u34r33x33e33533533f33y34s35w37b3913b43df3f93gk3hb3hl3hb3gk3f93df3b439137b35w34s33y33f33533533e33x34r35u37938z3b13dh3gc3jm3mx3pg3r93sb3sp3se3rf3pp',
|
|
16
|
-
// MaterialShapes.Pill
|
|
17
13
|
'3t53rz3qr3pj3o93mz3lo3kd3j13hp3gd3f13dq3cp3by3bj3be3bj3by3cp3dq3f13gd3hp3j13kd3lo3mz3o93pj3qr3rz3t53ua3vd3wc3wu3xm3yg3z83zx40k41341j41x42842f42k42l42k42f42841x41j41340k3zx3z83yg3xm3wu3wc3vd3ua3t53rz3qr3pj3o93mz3lo3kd3j13hp3gd3f13dq3cp3by3bj3be3bj3by3cp3dq3f13gd3hp3j13kd3lo3mz3o93pj3qr3rz3t53ua3vd3wc3wu3xm3yg3z83zx40k41341j41x42842f42k42l42k42f42841x41j41340k3zx3z83yg3xm3wu3wc3vd3ua',
|
|
18
|
-
// MaterialShapes.Sunny
|
|
19
14
|
'3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa3t63sa3pe3l73hi3ea3bj3973893973bj3ea3hi3l73pe3sa',
|
|
20
|
-
// MaterialShapes.Cookie4Sided
|
|
21
15
|
'2yr2z530a32b35g39l3ea3iv3mz3qk3tm3w53y73zr40v41j41r41i40u3zr3y63w43tk3qi3mw3it3e739j35f32a3092z42yr2z530a32b35g39l3ea3iv3mz3qk3tm3w53y73zr40v41j41r41i40u3zr3y63w43tk3qi3mw3it3e739j35f32a3092z42yr2z530a32b35g39l3ea3iv3mz3qk3tm3w53y73zr40v41j41r41i40u3zr3y63w43tk3qi3mw3it3e739j35f32a3092z42yr2z530a32b35g39l3ea3iv3mz3qk3tm3w53y73zr40v41j41r41i40u3zr3y63w43tk3qi3mw3it3e739j35f32a3092z4',
|
|
22
|
-
// MaterialShapes.Oval
|
|
23
16
|
'3ec3bw39m37i35k33s32630q2zg2yb2xc2wj2vv2vd2uz2ur2up2ur2uz2vd2vv2wj2xc2yb2zg30q32633s35k37i39m3bw3ec3gx3jn3mi3ph3sj3vn3yr41t44r47j4a14c84e14fd4g64gg4g64fd4e14c84a147j44r41t3yr3vn3sj3ph3mi3jn3gx3ec3bw39m37i35k33s32630q2zg2yb2xc2wj2vv2vd2uz2ur2up2ur2uz2vd2vv2wj2xc2yb2zg30q32633s35k37i39m3bw3ec3gx3jn3mi3ph3sj3vn3yr41t44r47j4a14c84e14fd4g64gg4g64fd4e14c84a147j44r41t3yr3vn3sj3ph3mi3jn3gx',
|
|
24
17
|
].map(decode);
|
|
25
18
|
export const CIRCLE = decode('3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so3so');
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
...attributes
|
|
11
11
|
}: NavigationBarProps = $props()
|
|
12
12
|
|
|
13
|
-
// The same attachments the rail uses. They read no reactive state, so they never tear down.
|
|
14
13
|
const attach = rovingTabindex('.np-navigation-bar-item')
|
|
15
14
|
const arrowHandler = arrowKeyNav('.np-navigation-bar-item', 'horizontal')
|
|
16
15
|
|
|
@@ -45,20 +44,14 @@
|
|
|
45
44
|
padding-block: 0.75rem;
|
|
46
45
|
padding-inline: 0.5rem;
|
|
47
46
|
background-color: var(--np-navigation-bar-container-color, var(--np-color-surface-container));
|
|
48
|
-
/* Sits above page content the way a rail does, and clears the home inset on a phone. */
|
|
49
47
|
padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
|
|
50
48
|
z-index: 8;
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
/* Items keep their own content width instead of stretching, and group in the middle. */
|
|
54
51
|
.np-navigation-bar-centered {
|
|
55
52
|
justify-content: center;
|
|
56
53
|
gap: 0.5rem;
|
|
57
54
|
}
|
|
58
|
-
/*
|
|
59
|
-
* Doubled selector to outrank the item's own `.svelte-*`-scoped `flex: 1` rule, which has the
|
|
60
|
-
* same specificity and would otherwise win or lose depending on CSS bundle order.
|
|
61
|
-
*/
|
|
62
55
|
:global(.np-navigation-bar-centered .np-navigation-bar-item.np-navigation-bar-item) {
|
|
63
56
|
flex: 0 0 auto;
|
|
64
57
|
min-width: 0;
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
|
|
33
33
|
{#snippet content()}
|
|
34
34
|
{#if orientation === 'horizontal'}
|
|
35
|
-
<!-- Sized off its own parent, so it has to sit on the whole pill rather than the icon alone. -->
|
|
36
35
|
<Ripple forElement={touchEl} />
|
|
37
36
|
{/if}
|
|
38
37
|
<span class="np-navigation-bar-item-icon">
|
|
@@ -104,7 +103,6 @@
|
|
|
104
103
|
z-index: 1;
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
/* The active-indicator pill, grown from the selected class rather than measured. */
|
|
108
106
|
.np-navigation-bar-item-icon::before {
|
|
109
107
|
content: '';
|
|
110
108
|
position: absolute;
|
|
@@ -144,10 +142,6 @@
|
|
|
144
142
|
color: var(--np-color-on-surface);
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
/*
|
|
148
|
-
* `labelBehavior="selected"` keeps the label in the layout but hides it, so the row does not
|
|
149
|
-
* reflow as the selection moves.
|
|
150
|
-
*/
|
|
151
145
|
.np-navigation-bar-item-label-selected .np-navigation-bar-item-label {
|
|
152
146
|
visibility: hidden;
|
|
153
147
|
}
|
|
@@ -156,28 +150,13 @@
|
|
|
156
150
|
visibility: visible;
|
|
157
151
|
}
|
|
158
152
|
|
|
159
|
-
/*
|
|
160
|
-
* The icon sits beside the label instead of above it, for a bar with room to spare. Unlike the
|
|
161
|
-
* stacked layout, the active-indicator pill wraps the icon and label together, like a chip,
|
|
162
|
-
* rather than just the icon.
|
|
163
|
-
*/
|
|
164
153
|
.np-navigation-bar-item-horizontal {
|
|
165
154
|
flex-direction: row;
|
|
166
155
|
justify-content: center;
|
|
167
|
-
/* M3's "icon label space" token: 4dp between the icon and the label. */
|
|
168
156
|
gap: 0.25rem;
|
|
169
|
-
/*
|
|
170
|
-
* M3's "leading/trailing space" token (16dp) sets the inline padding, and the block padding
|
|
171
|
-
* is picked so a 1.5rem icon plus padding adds up to the "active indicator height" token
|
|
172
|
-
* (40dp).
|
|
173
|
-
*/
|
|
174
157
|
padding: 0.5rem 1rem;
|
|
175
158
|
border-radius: var(--np-shape-corner-full);
|
|
176
|
-
/* The bar stretches items to its own height by default; a pill sizes to its own content and
|
|
177
|
-
* centers in the bar instead. */
|
|
178
159
|
align-self: center;
|
|
179
|
-
/* Gives the item its own stacking context, so the pill's z-index: -1 stays behind the icon
|
|
180
|
-
* and label instead of escaping to sit behind the bar's own background. */
|
|
181
160
|
z-index: 0;
|
|
182
161
|
}
|
|
183
162
|
.np-navigation-bar-item-horizontal::before {
|
|
@@ -197,7 +176,6 @@
|
|
|
197
176
|
opacity: 1;
|
|
198
177
|
transform: scaleX(1);
|
|
199
178
|
}
|
|
200
|
-
/* The wrapping item now supplies the pill, so the icon's own is switched off. */
|
|
201
179
|
.np-navigation-bar-item-horizontal .np-navigation-bar-item-icon {
|
|
202
180
|
width: 1.5rem;
|
|
203
181
|
height: 1.5rem;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
3
3
|
export interface NavigationBarProps extends HTMLAttributes<HTMLElement> {
|
|
4
|
-
/**
|
|
5
|
-
* `'equal-weight'` stretches every item to the same width, which M3 recommends for compact
|
|
6
|
-
* screens. `'centered'` sizes each item to its content and groups them in the middle, which M3
|
|
7
|
-
* recommends once the bar has room to spare on a medium-width screen.
|
|
8
|
-
*/
|
|
9
4
|
arrangement?: 'equal-weight' | 'centered';
|
|
10
5
|
element?: HTMLElement;
|
|
11
6
|
}
|
|
@@ -17,12 +12,7 @@ export interface NavigationBarItemProps extends HTMLAttributes<NavigationBarItem
|
|
|
17
12
|
badge?: boolean;
|
|
18
13
|
badgeLabel?: string | number;
|
|
19
14
|
badgeAriaLabel?: string;
|
|
20
|
-
/** Hides the label until the item is selected, which M3 allows for four or five items. */
|
|
21
15
|
labelBehavior?: 'always' | 'selected';
|
|
22
|
-
/**
|
|
23
|
-
* `'vertical'` stacks the icon above the label. `'horizontal'` puts it beside the label instead,
|
|
24
|
-
* which M3 pairs with `arrangement="centered"` once a bar has room to spare.
|
|
25
|
-
*/
|
|
26
16
|
orientation?: 'vertical' | 'horizontal';
|
|
27
17
|
type?: 'submit' | 'reset' | 'button' | (string & {}) | null;
|
|
28
18
|
element?: NavigationBarItemElement;
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
if (!event.defaultPrevented) arrowHandler(event)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/* Only a modal drawer is a dialog; a standard one is a nav that is always in the layout. */
|
|
27
26
|
const asDialog = () => (element instanceof HTMLDialogElement ? element : undefined)
|
|
28
27
|
|
|
29
28
|
export const show = () => {
|
|
@@ -45,12 +44,6 @@
|
|
|
45
44
|
const start = $derived(direction === 'ltr' ? 'translateX(-100%)' : 'translateX(100%)')
|
|
46
45
|
</script>
|
|
47
46
|
|
|
48
|
-
<!--
|
|
49
|
-
A modal drawer is a `dialog` holding a nav landmark, so the browser keeps the page behind it
|
|
50
|
-
inert for as long as it is open and hands focus back on close. `::backdrop` is the scrim and
|
|
51
|
-
`closedby="any"` the tap-outside-to-close. Where `closedby` is unsupported the click handler
|
|
52
|
-
below covers it: a click on the backdrop is reported against the dialog itself.
|
|
53
|
-
-->
|
|
54
47
|
{#if modal}
|
|
55
48
|
<dialog
|
|
56
49
|
{...attributes}
|
|
@@ -70,11 +63,6 @@
|
|
|
70
63
|
onkeydown={handleKeydown}
|
|
71
64
|
ontoggle={(event) => {
|
|
72
65
|
open = event.newState === 'open'
|
|
73
|
-
/*
|
|
74
|
-
* Without this the browser autofocuses the first focusable descendant, the first nav
|
|
75
|
-
* item. Focus the drawer itself instead, so nothing is focused that the user didn't
|
|
76
|
-
* choose to interact with.
|
|
77
|
-
*/
|
|
78
66
|
if (event.newState === 'open') element?.focus()
|
|
79
67
|
ontoggle?.(event)
|
|
80
68
|
}}
|
|
@@ -83,12 +71,6 @@
|
|
|
83
71
|
if (event.target === element) close()
|
|
84
72
|
}}
|
|
85
73
|
>
|
|
86
|
-
<!--
|
|
87
|
-
The label belongs to the navigation landmark, not to the dialog wrapping it, so it is
|
|
88
|
-
pulled back off the dialog above and put here. Everything else stays on the dialog: it
|
|
89
|
-
is the container the layout and the custom properties are written against, and the
|
|
90
|
-
element `commandfor` has to reach.
|
|
91
|
-
-->
|
|
92
74
|
<nav
|
|
93
75
|
aria-label={attributes['aria-label']}
|
|
94
76
|
aria-labelledby={attributes['aria-labelledby']}
|
|
@@ -120,7 +102,6 @@
|
|
|
120
102
|
color: var(--np-color-on-surface-variant);
|
|
121
103
|
width: calc(var(--np-navigation-drawer-width, 22.5rem) + 3px);
|
|
122
104
|
border: 0;
|
|
123
|
-
/* Focus lands here on open only to stay off the first nav item, not as something to show. */
|
|
124
105
|
outline: none;
|
|
125
106
|
margin: 0;
|
|
126
107
|
padding: 0;
|
|
@@ -129,7 +110,6 @@
|
|
|
129
110
|
max-height: none;
|
|
130
111
|
}
|
|
131
112
|
|
|
132
|
-
/* The modal drawer is docked to the inline edge rather than centred as a dialog would be. */
|
|
133
113
|
.np-navigation-drawer-container-modal {
|
|
134
114
|
position: fixed;
|
|
135
115
|
inset-block: 0;
|
|
@@ -183,7 +163,6 @@
|
|
|
183
163
|
box-shadow: var(--np-elevation-1);
|
|
184
164
|
}
|
|
185
165
|
|
|
186
|
-
/* `backdrop` decides whether the scrim is painted; a modal dialog always has the layer. */
|
|
187
166
|
.np-navigation-drawer-container-modal::backdrop {
|
|
188
167
|
background-color: transparent;
|
|
189
168
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
3
3
|
export interface NavigationDrawerProps extends HTMLAttributes<HTMLElement> {
|
|
4
|
-
/** A modal drawer is a `dialog` over the page; a standard one is part of the layout. */
|
|
5
4
|
modal?: boolean;
|
|
6
|
-
/** Paints the scrim behind a modal drawer. */
|
|
7
5
|
backdrop?: boolean;
|
|
8
|
-
/** Bindable. Whether a modal drawer is showing. Ignored by a standard drawer. */
|
|
9
6
|
open?: boolean;
|
|
10
7
|
element?: HTMLElement;
|
|
11
8
|
direction?: 'rtl' | 'ltr';
|
package/dist/popover.svelte.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `show()`/`close()` for a popover-backed overlay, guarded both ways. `showPopover()` throws if
|
|
3
|
-
* the popover is already showing and `hidePopover()` throws if it is already hidden, so both
|
|
4
|
-
* directions need the `:popover-open` check, not just the one that happens to get exercised first.
|
|
5
|
-
*/
|
|
6
1
|
export declare const popoverController: (getElement: () => HTMLElement | undefined) => {
|
|
7
2
|
show: () => void;
|
|
8
3
|
close: () => void;
|
|
9
4
|
};
|
|
10
|
-
/**
|
|
11
|
-
* Keeps a bindable `open` prop in sync with an overlay's own `show`/`close`, so a caller can drive
|
|
12
|
-
* the overlay either by calling the exported methods or by writing to the bound prop.
|
|
13
|
-
*/
|
|
14
5
|
export declare const syncOpenEffect: (getElement: () => HTMLElement | undefined, getOpen: () => boolean | undefined, show: () => void, close: () => void) => void;
|
package/dist/popover.svelte.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `show()`/`close()` for a popover-backed overlay, guarded both ways. `showPopover()` throws if
|
|
3
|
-
* the popover is already showing and `hidePopover()` throws if it is already hidden, so both
|
|
4
|
-
* directions need the `:popover-open` check, not just the one that happens to get exercised first.
|
|
5
|
-
*/
|
|
6
1
|
export const popoverController = (getElement) => ({
|
|
7
2
|
show: () => {
|
|
8
3
|
const element = getElement();
|
|
@@ -15,10 +10,6 @@ export const popoverController = (getElement) => ({
|
|
|
15
10
|
element.hidePopover();
|
|
16
11
|
},
|
|
17
12
|
});
|
|
18
|
-
/**
|
|
19
|
-
* Keeps a bindable `open` prop in sync with an overlay's own `show`/`close`, so a caller can drive
|
|
20
|
-
* the overlay either by calling the exported methods or by writing to the bound prop.
|
|
21
|
-
*/
|
|
22
13
|
export const syncOpenEffect = (getElement, getOpen, show, close) => {
|
|
23
14
|
$effect(() => {
|
|
24
15
|
if (!getElement())
|
package/dist/press.svelte.d.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
export declare const PRESS_DURATION = 100;
|
|
2
|
-
/**
|
|
3
|
-
* The corner morph a Button, an IconButton or a FAB plays when it is activated. `:active` covers
|
|
4
|
-
* the press itself, but it drops the moment the pointer lifts, so a quick click would show only a
|
|
5
|
-
* frame or two of the morph. The `pressed` this returns is held for as long as the round trip
|
|
6
|
-
* takes to be seen, and is set from `click`, which arrives for the keyboard as well as the pointer.
|
|
7
|
-
*/
|
|
8
2
|
export declare const pressMorph: () => {
|
|
9
3
|
readonly pressed: boolean;
|
|
10
4
|
press: () => void;
|
|
11
|
-
/**
|
|
12
|
-
* A change of `shape` supersedes the morph. Both describe the same corner, and the pressed
|
|
13
|
-
* radius is the shape the element is not, so holding the press across a shape change cancels
|
|
14
|
-
* the change out and stalls it until the timer runs down. FabMenu's trigger does exactly
|
|
15
|
-
* that, flipping to `square` on the click that opens the menu; dropping the press there
|
|
16
|
-
* leaves that click one continuous morph.
|
|
17
|
-
*/
|
|
18
5
|
supersededBy: (shape: () => unknown) => void;
|
|
19
6
|
};
|
package/dist/press.svelte.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { onMount } from 'svelte';
|
|
2
2
|
export const PRESS_DURATION = 100;
|
|
3
|
-
/**
|
|
4
|
-
* The corner morph a Button, an IconButton or a FAB plays when it is activated. `:active` covers
|
|
5
|
-
* the press itself, but it drops the moment the pointer lifts, so a quick click would show only a
|
|
6
|
-
* frame or two of the morph. The `pressed` this returns is held for as long as the round trip
|
|
7
|
-
* takes to be seen, and is set from `click`, which arrives for the keyboard as well as the pointer.
|
|
8
|
-
*/
|
|
9
3
|
export const pressMorph = () => {
|
|
10
4
|
let pressed = $state(false);
|
|
11
5
|
let timeout;
|
|
@@ -23,13 +17,6 @@ export const pressMorph = () => {
|
|
|
23
17
|
pressed = true;
|
|
24
18
|
timeout = setTimeout(() => (pressed = false), PRESS_DURATION);
|
|
25
19
|
},
|
|
26
|
-
/**
|
|
27
|
-
* A change of `shape` supersedes the morph. Both describe the same corner, and the pressed
|
|
28
|
-
* radius is the shape the element is not, so holding the press across a shape change cancels
|
|
29
|
-
* the change out and stalls it until the timer runs down. FabMenu's trigger does exactly
|
|
30
|
-
* that, flipping to `square` on the click that opens the menu; dropping the press there
|
|
31
|
-
* leaves that click one continuous morph.
|
|
32
|
-
*/
|
|
33
20
|
supersededBy: (shape) => {
|
|
34
21
|
let previous;
|
|
35
22
|
$effect(() => {
|