noph-ui 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/animation.d.ts +17 -0
- package/dist/animation.js +48 -0
- package/dist/app-bar/AppBar.svelte +280 -0
- package/dist/app-bar/AppBar.svelte.d.ts +4 -0
- package/dist/app-bar/index.d.ts +1 -0
- package/dist/app-bar/index.js +1 -0
- package/dist/app-bar/types.d.ts +35 -0
- package/dist/app-bar/types.js +1 -0
- package/dist/autocomplete/AutoComplete.svelte +10 -11
- package/dist/autocomplete/AutoComplete.svelte.d.ts +1 -1
- package/dist/autocomplete/types.d.ts +1 -1
- package/dist/badge/Badge.svelte +6 -1
- package/dist/badge/types.d.ts +0 -1
- package/dist/button/Button.svelte +5 -15
- package/dist/button/IconButton.svelte +5 -15
- package/dist/button/SegmentedButton.svelte +15 -1
- package/dist/button/SplitButton.svelte +92 -0
- package/dist/button/SplitButton.svelte.d.ts +4 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/button/types.d.ts +23 -4
- package/dist/card/types.d.ts +1 -1
- package/dist/checkbox/Checkbox.svelte +49 -58
- package/dist/chip/AssistChip.svelte +5 -5
- package/dist/chip/ChipSet.svelte +3 -11
- package/dist/chip/FilterChip.svelte +35 -27
- package/dist/chip/InputChip.svelte +13 -2
- package/dist/chip/SuggestionChip.svelte +29 -0
- package/dist/chip/SuggestionChip.svelte.d.ts +4 -0
- package/dist/chip/index.d.ts +1 -0
- package/dist/chip/index.js +1 -0
- package/dist/chip/types.d.ts +24 -9
- package/dist/date-picker/Calendar.svelte +1 -1
- package/dist/date-picker/DatePickerDialog.svelte +30 -8
- package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -1
- package/dist/date-picker/DateRangePicker.svelte +45 -10
- package/dist/date-picker/DateRangePicker.svelte.d.ts +4 -1
- package/dist/date-picker/DockedDatePicker.svelte +18 -4
- package/dist/date-picker/DockedDatePicker.svelte.d.ts +4 -1
- package/dist/date-picker/DockedDateTimePicker.svelte +18 -4
- package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +4 -1
- package/dist/date-picker/SelectionList.svelte +6 -32
- package/dist/date-picker/SelectionList.svelte.d.ts +2 -12
- package/dist/date-picker/TimeColumn.svelte +4 -29
- package/dist/date-picker/exitVisibility.svelte.d.ts +10 -0
- package/dist/date-picker/exitVisibility.svelte.js +30 -0
- package/dist/date-picker/optionList.d.ts +16 -0
- package/dist/date-picker/optionList.js +43 -0
- package/dist/date-picker/types.d.ts +11 -4
- package/dist/dialog/Dialog.svelte +77 -68
- package/dist/dialog/Dialog.svelte.d.ts +3 -3
- package/dist/dialog/types.d.ts +5 -3
- package/dist/fab/ExtendedFab.svelte +281 -0
- package/dist/fab/ExtendedFab.svelte.d.ts +5 -0
- package/dist/fab/Fab.svelte +239 -0
- package/dist/fab/Fab.svelte.d.ts +5 -0
- package/dist/fab/FabMenu.svelte +255 -0
- package/dist/fab/FabMenu.svelte.d.ts +7 -0
- package/dist/fab/index.d.ts +3 -0
- package/dist/fab/index.js +3 -0
- package/dist/fab/types.d.ts +58 -0
- package/dist/fab/types.js +1 -0
- package/dist/icons/ArrowBackIcon.svelte +13 -0
- package/dist/icons/ArrowBackIcon.svelte.d.ts +7 -0
- package/dist/icons/ChevronDownIcon.svelte +3 -0
- package/dist/icons/ChevronDownIcon.svelte.d.ts +26 -0
- package/dist/icons/SearchIcon.svelte +15 -0
- package/dist/icons/SearchIcon.svelte.d.ts +7 -0
- package/dist/icons/index.d.ts +3 -0
- package/dist/icons/index.js +3 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/internal/focus-ring.css +12 -0
- package/dist/menu/Menu.svelte +12 -7
- package/dist/menu/Menu.svelte.d.ts +2 -2
- package/dist/menu/types.d.ts +0 -2
- package/dist/navigation-bar/NavigationBar.svelte +66 -0
- package/dist/navigation-bar/NavigationBar.svelte.d.ts +4 -0
- package/dist/navigation-bar/NavigationBarItem.svelte +261 -0
- package/dist/navigation-bar/NavigationBarItem.svelte.d.ts +5 -0
- package/dist/navigation-bar/index.d.ts +2 -0
- package/dist/navigation-bar/index.js +2 -0
- package/dist/navigation-bar/types.d.ts +29 -0
- package/dist/navigation-bar/types.js +1 -0
- package/dist/navigation-drawer/NavigationDrawer.svelte +128 -76
- package/dist/navigation-drawer/NavigationDrawer.svelte.d.ts +4 -1
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +10 -3
- package/dist/navigation-drawer/types.d.ts +6 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +16 -0
- package/dist/navigation-rail/types.d.ts +3 -0
- package/dist/popover.svelte.d.ts +14 -0
- package/dist/popover.svelte.js +31 -0
- package/dist/press.svelte.d.ts +19 -0
- package/dist/press.svelte.js +45 -0
- package/dist/radio/Radio.svelte +26 -7
- package/dist/radio/types.d.ts +3 -0
- package/dist/search/Search.svelte +472 -0
- package/dist/search/Search.svelte.d.ts +5 -0
- package/dist/search/index.d.ts +1 -0
- package/dist/search/index.js +1 -0
- package/dist/search/types.d.ts +39 -0
- package/dist/search/types.js +1 -0
- package/dist/select/Select.svelte +48 -87
- package/dist/sheet/Sheet.svelte +260 -0
- package/dist/sheet/Sheet.svelte.d.ts +7 -0
- package/dist/sheet/index.d.ts +1 -0
- package/dist/sheet/index.js +1 -0
- package/dist/sheet/types.d.ts +19 -0
- package/dist/sheet/types.js +1 -0
- package/dist/slider/Slider.svelte +154 -30
- package/dist/slider/types.d.ts +3 -0
- package/dist/snackbar/Snackbar.svelte +48 -37
- package/dist/snackbar/Snackbar.svelte.d.ts +2 -2
- package/dist/snackbar/types.d.ts +3 -2
- package/dist/switch/Switch.svelte +51 -20
- package/dist/switch/types.d.ts +3 -0
- package/dist/tabs/Tab.svelte +8 -12
- package/dist/tabs/Tabs.svelte +3 -3
- package/dist/text-field/TextField.svelte +2 -2
- package/dist/toolbar/Toolbar.svelte +133 -0
- package/dist/toolbar/Toolbar.svelte.d.ts +4 -0
- package/dist/toolbar/index.d.ts +1 -0
- package/dist/toolbar/index.js +1 -0
- package/dist/toolbar/types.d.ts +15 -0
- package/dist/toolbar/types.js +1 -0
- package/dist/tooltip/RichTooltip.svelte +25 -12
- package/dist/tooltip/RichTooltip.svelte.d.ts +2 -2
- package/dist/tooltip/Tooltip.svelte +22 -11
- package/dist/tooltip/Tooltip.svelte.d.ts +4 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.js +6 -0
- package/package.json +13 -13
- package/dist/date-picker/enterAnimation.d.ts +0 -1
- package/dist/date-picker/enterAnimation.js +0 -10
|
@@ -1,70 +1,81 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Button from '../button/Button.svelte'
|
|
3
3
|
import IconButton from '../button/IconButton.svelte'
|
|
4
|
+
import { popoverController, syncOpenEffect } from '../popover.svelte.js'
|
|
4
5
|
import type { SnackbarProps } from './types.js'
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
label,
|
|
8
9
|
supportingText,
|
|
9
10
|
actionLabel,
|
|
10
|
-
|
|
11
|
+
onactionclick,
|
|
11
12
|
icon,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
},
|
|
13
|
+
iconAriaLabel = 'Close',
|
|
14
|
+
oniconclick = () => close(),
|
|
15
15
|
open = $bindable(false),
|
|
16
|
-
|
|
16
|
+
ontoggle,
|
|
17
17
|
timeout = 4000,
|
|
18
18
|
element = $bindable(),
|
|
19
19
|
popover = 'manual',
|
|
20
20
|
...attributes
|
|
21
21
|
}: SnackbarProps = $props()
|
|
22
22
|
|
|
23
|
-
let
|
|
24
|
-
|
|
23
|
+
let hovered = $state(false)
|
|
24
|
+
let focused = $state(false)
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
element?.showPopover()
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const hidePopover = () => {
|
|
31
|
-
element?.hidePopover()
|
|
32
|
-
}
|
|
26
|
+
const controller = popoverController(() => element)
|
|
33
27
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
if (!open || timeout <= 0) return
|
|
37
|
-
timeoutId = setTimeout(() => {
|
|
38
|
-
element?.hidePopover()
|
|
39
|
-
}, timeout)
|
|
40
|
-
}
|
|
28
|
+
export const show = () => controller.show()
|
|
29
|
+
export const close = () => controller.close()
|
|
41
30
|
|
|
42
|
-
|
|
31
|
+
/*
|
|
32
|
+
* `toggle` fires once the popover has settled, so `open` coming back from the DOM already
|
|
33
|
+
* matches it and this does nothing. Syncing on `beforetoggle` instead would land in the middle
|
|
34
|
+
* of the browser's show operation, where `showPopover()` throws.
|
|
35
|
+
*/
|
|
36
|
+
syncOpenEffect(
|
|
37
|
+
() => element,
|
|
38
|
+
() => open,
|
|
39
|
+
show,
|
|
40
|
+
close,
|
|
41
|
+
)
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
/*
|
|
44
|
+
* The timeout lives and dies with the snackbar being open, so there is no timer to clear by
|
|
45
|
+
* hand: closing early or unmounting runs the cleanup. It holds while the snackbar is hovered or
|
|
46
|
+
* keeps focus, and starts over once the snackbar is left alone, so an action stays reachable.
|
|
47
|
+
*/
|
|
48
|
+
$effect(() => {
|
|
49
|
+
if (!open || hovered || focused || timeout <= 0) return
|
|
50
|
+
const timeoutId = setTimeout(close, timeout)
|
|
51
|
+
return () => clearTimeout(timeoutId)
|
|
52
|
+
})
|
|
45
53
|
</script>
|
|
46
54
|
|
|
55
|
+
<!--
|
|
56
|
+
`alert` is an atomic live region, so a screen reader reads the whole snackbar out when it appears,
|
|
57
|
+
label and supporting text together. It needs no name of its own for that, and the ARIA alert
|
|
58
|
+
pattern gives it none: naming the region after an element inside it only risks that text being
|
|
59
|
+
announced twice. A caller's own `aria-label` reaches the element through `attributes`.
|
|
60
|
+
-->
|
|
47
61
|
<div
|
|
48
62
|
{...attributes}
|
|
49
63
|
{popover}
|
|
50
64
|
class={['np-snackbar', attributes.class]}
|
|
51
65
|
bind:this={element}
|
|
52
66
|
role="alert"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
open = newState === 'open'
|
|
61
|
-
startTimer()
|
|
62
|
-
onbeforetoggle?.(event)
|
|
67
|
+
onpointerenter={() => (hovered = true)}
|
|
68
|
+
onpointerleave={() => (hovered = false)}
|
|
69
|
+
onfocusin={() => (focused = true)}
|
|
70
|
+
onfocusout={() => (focused = false)}
|
|
71
|
+
ontoggle={(event) => {
|
|
72
|
+
open = event.newState === 'open'
|
|
73
|
+
ontoggle?.(event)
|
|
63
74
|
}}
|
|
64
75
|
>
|
|
65
76
|
<div class="np-snackbar-inner">
|
|
66
77
|
<div class="np-snackbar-label-container">
|
|
67
|
-
<div
|
|
78
|
+
<div class="np-snackbar-label">{label}</div>
|
|
68
79
|
{#if supportingText}
|
|
69
80
|
<div class="np-snackbar-supporting-text">{supportingText}</div>
|
|
70
81
|
{/if}
|
|
@@ -74,7 +85,7 @@
|
|
|
74
85
|
variant="text"
|
|
75
86
|
--np-text-button-label-text-color="var(--np-snackbar-action-color, var(--np-color-inverse-primary))"
|
|
76
87
|
aria-label={actionLabel}
|
|
77
|
-
onclick={
|
|
88
|
+
onclick={onactionclick}
|
|
78
89
|
>
|
|
79
90
|
{actionLabel}
|
|
80
91
|
</Button>
|
|
@@ -83,8 +94,8 @@
|
|
|
83
94
|
<div class="np-snackbar-icon-container">
|
|
84
95
|
<IconButton
|
|
85
96
|
--np-icon-button-icon-color="var(--np-snackbar-text-color, var(--np-color-inverse-on-surface))"
|
|
86
|
-
aria-label=
|
|
87
|
-
onclick={
|
|
97
|
+
aria-label={iconAriaLabel}
|
|
98
|
+
onclick={oniconclick}
|
|
88
99
|
>
|
|
89
100
|
{@render icon()}</IconButton
|
|
90
101
|
>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SnackbarProps } from './types.ts';
|
|
2
2
|
declare const Snackbar: import("svelte").Component<SnackbarProps, {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
5
|
}, "element" | "open">;
|
|
6
6
|
type Snackbar = ReturnType<typeof Snackbar>;
|
|
7
7
|
export default Snackbar;
|
package/dist/snackbar/types.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ export interface SnackbarProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
5
5
|
open?: boolean;
|
|
6
6
|
supportingText?: string | undefined;
|
|
7
7
|
actionLabel?: string | undefined;
|
|
8
|
-
|
|
8
|
+
onactionclick?: (event: Event) => void;
|
|
9
9
|
icon?: Snippet | undefined;
|
|
10
|
-
|
|
10
|
+
oniconclick?: (event: Event) => void;
|
|
11
|
+
iconAriaLabel?: string;
|
|
11
12
|
timeout?: number;
|
|
12
13
|
element?: HTMLElement;
|
|
13
14
|
popover?: 'auto' | 'manual';
|
|
@@ -10,11 +10,23 @@
|
|
|
10
10
|
inputElement = $bindable(),
|
|
11
11
|
icons,
|
|
12
12
|
onkeyup,
|
|
13
|
+
issues,
|
|
14
|
+
'aria-invalid': ariaInvalid,
|
|
13
15
|
...attributes
|
|
14
16
|
}: SwitchProps = $props()
|
|
17
|
+
|
|
18
|
+
let hasError = $derived(!!issues?.length)
|
|
15
19
|
</script>
|
|
16
20
|
|
|
17
|
-
<div
|
|
21
|
+
<div
|
|
22
|
+
bind:this={element}
|
|
23
|
+
class={[
|
|
24
|
+
'np-switch',
|
|
25
|
+
disabled && 'np-disabled',
|
|
26
|
+
hasError && !disabled && 'np-error',
|
|
27
|
+
attributes.class,
|
|
28
|
+
]}
|
|
29
|
+
>
|
|
18
30
|
<div class="np-track"></div>
|
|
19
31
|
<div class={['np-handle', icons === 'both' && 'both-icons']}>
|
|
20
32
|
{#if icons}
|
|
@@ -57,6 +69,7 @@
|
|
|
57
69
|
bind:this={inputElement}
|
|
58
70
|
bind:checked={selected}
|
|
59
71
|
{disabled}
|
|
72
|
+
aria-invalid={hasError ? 'true' : ariaInvalid}
|
|
60
73
|
/>
|
|
61
74
|
</div>
|
|
62
75
|
|
|
@@ -78,7 +91,7 @@
|
|
|
78
91
|
width: 3.25rem;
|
|
79
92
|
height: 2rem;
|
|
80
93
|
background-color: var(
|
|
81
|
-
--np-
|
|
94
|
+
--np-switch-unselected-track-color,
|
|
82
95
|
var(--np-color-surface-container-highest)
|
|
83
96
|
);
|
|
84
97
|
border-radius: var(--np-shape-corner-full);
|
|
@@ -86,7 +99,7 @@
|
|
|
86
99
|
background-color 0.2s,
|
|
87
100
|
outline-color 0.2s;
|
|
88
101
|
outline-width: 0.125rem;
|
|
89
|
-
outline-color: var(--np-
|
|
102
|
+
outline-color: var(--np-switch-unselected-track-outline-color, var(--np-color-outline));
|
|
90
103
|
outline-style: solid;
|
|
91
104
|
outline-offset: -0.125rem;
|
|
92
105
|
}
|
|
@@ -100,8 +113,8 @@
|
|
|
100
113
|
transition:
|
|
101
114
|
transform var(--np-motion-expressive-fast-spatial),
|
|
102
115
|
background-color var(--np-motion-expressive-default-effects);
|
|
103
|
-
background-color: var(--np-
|
|
104
|
-
border-radius: var(--np-
|
|
116
|
+
background-color: var(--np-switch-unselected-handle-color, var(--np-color-outline));
|
|
117
|
+
border-radius: var(--np-switch-handle-shape, var(--np-shape-corner-full));
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
.np-handle.both-icons {
|
|
@@ -110,12 +123,12 @@
|
|
|
110
123
|
|
|
111
124
|
.np-state-layer {
|
|
112
125
|
position: relative;
|
|
113
|
-
width: var(--np-
|
|
114
|
-
height: var(--np-
|
|
126
|
+
width: var(--np-switch-state-layer-size, 2.5rem);
|
|
127
|
+
height: var(--np-switch-state-layer-size, 2.5rem);
|
|
115
128
|
margin-block-start: -0.25rem;
|
|
116
129
|
margin-inline-start: -0.25rem;
|
|
117
130
|
transition: var(--np-motion-expressive-fast-spatial);
|
|
118
|
-
border-radius: var(--np-
|
|
131
|
+
border-radius: var(--np-switch-state-layer-shape, var(--np-shape-corner-full));
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
.np-switch:has(input:checked) .np-state-layer {
|
|
@@ -152,37 +165,55 @@
|
|
|
152
165
|
|
|
153
166
|
.np-switch:has(input:checked) .np-handle {
|
|
154
167
|
transform: translateX(1.25rem) scale(1.5);
|
|
155
|
-
color: var(--np-
|
|
156
|
-
background-color: var(--np-
|
|
168
|
+
color: var(--np-switch-selected-icon-color, var(--np-color-primary));
|
|
169
|
+
background-color: var(--np-switch-selected-handle-color, var(--np-color-on-primary));
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
.np-disabled .np-handle {
|
|
160
|
-
opacity: var(--np-
|
|
173
|
+
opacity: var(--np-switch-disabled-unselected-handle-opacity, 0.38);
|
|
161
174
|
}
|
|
162
175
|
|
|
163
176
|
.np-switch.np-disabled:has(input:checked) .np-handle {
|
|
164
|
-
opacity: var(--np-
|
|
165
|
-
background-color: var(--np-
|
|
177
|
+
opacity: var(--np-switch-disabled-selected-handle-opacity, 1);
|
|
178
|
+
background-color: var(--np-switch-selected-handle-color, var(--np-color-surface));
|
|
166
179
|
}
|
|
167
180
|
|
|
168
181
|
.np-switch:has(input:checked) .np-track {
|
|
169
|
-
background-color: var(--np-
|
|
182
|
+
background-color: var(--np-switch-selected-track-color, var(--np-color-primary));
|
|
170
183
|
outline-color: transparent;
|
|
171
184
|
}
|
|
172
185
|
|
|
186
|
+
.np-error .np-track {
|
|
187
|
+
outline-color: var(--np-color-error);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.np-error:has(input:checked) .np-track {
|
|
191
|
+
background-color: var(--np-color-error);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.np-error:has(input:checked) .np-handle {
|
|
195
|
+
background-color: var(--np-color-on-error);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.np-error:has(input:checked) .np-switch-icon {
|
|
199
|
+
fill: var(--np-color-error);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.np-error {
|
|
203
|
+
--np-ripple-hover-color: var(--np-color-error);
|
|
204
|
+
--np-ripple-pressed-color: var(--np-color-error);
|
|
205
|
+
}
|
|
206
|
+
|
|
173
207
|
.np-disabled .np-track {
|
|
174
|
-
opacity: var(--np-
|
|
208
|
+
opacity: var(--np-switch-disabled-track-opacity, 0.12);
|
|
175
209
|
background-color: var(
|
|
176
|
-
--np-
|
|
210
|
+
--np-switch-disabled-unselected-track-color,
|
|
177
211
|
var(--np-color-surface-container-highest)
|
|
178
212
|
);
|
|
179
213
|
}
|
|
180
214
|
|
|
181
215
|
.np-switch.np-disabled:has(input:checked) .np-track {
|
|
182
|
-
background-color: var(
|
|
183
|
-
--np-comp-switch-disabled-selected-track-color,
|
|
184
|
-
var(--np-color-on-surface)
|
|
185
|
-
);
|
|
216
|
+
background-color: var(--np-switch-disabled-selected-track-color, var(--np-color-on-surface));
|
|
186
217
|
}
|
|
187
218
|
|
|
188
219
|
.np-disabled {
|
package/dist/switch/types.d.ts
CHANGED
package/dist/tabs/Tab.svelte
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import '../internal/focus-ring.css'
|
|
3
3
|
import Badge from '../badge/Badge.svelte'
|
|
4
4
|
import Ripple from '../ripple/Ripple.svelte'
|
|
5
|
-
import { tick } from 'svelte'
|
|
6
5
|
import { getTabsContext } from './context.js'
|
|
7
6
|
import type { TabProps } from './types.js'
|
|
8
7
|
|
|
@@ -24,16 +23,15 @@
|
|
|
24
23
|
|
|
25
24
|
const tabsContext = getTabsContext()
|
|
26
25
|
|
|
26
|
+
/*
|
|
27
|
+
* The indicator follows the selection one flush behind, so the anchor it is tied to has moved
|
|
28
|
+
* to the tab that is already marked selected rather than to one still being rendered.
|
|
29
|
+
*/
|
|
27
30
|
$effect(() => {
|
|
28
31
|
if (tabsContext.value === value) {
|
|
29
32
|
tabsContext.indicatorValue = value
|
|
30
33
|
}
|
|
31
34
|
})
|
|
32
|
-
|
|
33
|
-
const setIndicatorValue = async () => {
|
|
34
|
-
await tick()
|
|
35
|
-
tabsContext.indicatorValue = value
|
|
36
|
-
}
|
|
37
35
|
</script>
|
|
38
36
|
|
|
39
37
|
{#snippet content()}
|
|
@@ -47,7 +45,7 @@
|
|
|
47
45
|
{#if icon}
|
|
48
46
|
{#if badge && tabsContext.variant === 'primary' && !inlineIcon}
|
|
49
47
|
<div class="np-tab-icon-badge">
|
|
50
|
-
<Badge label={badgeLabel}
|
|
48
|
+
<Badge label={badgeLabel} aria-label={badgeAriaLabel} />
|
|
51
49
|
{@render icon?.()}
|
|
52
50
|
</div>
|
|
53
51
|
{:else}
|
|
@@ -58,7 +56,7 @@
|
|
|
58
56
|
<div style="--np-badge-position:static;">
|
|
59
57
|
<span class="np-tab-label-badge">{@render children?.()}</span><Badge
|
|
60
58
|
label={badgeLabel}
|
|
61
|
-
|
|
59
|
+
aria-label={badgeAriaLabel}
|
|
62
60
|
/>
|
|
63
61
|
</div>
|
|
64
62
|
{:else}
|
|
@@ -116,13 +114,11 @@
|
|
|
116
114
|
]}
|
|
117
115
|
onclick={(event) => {
|
|
118
116
|
tabsContext.value = value
|
|
119
|
-
setIndicatorValue()
|
|
120
117
|
onclick?.(event)
|
|
121
118
|
}}
|
|
122
119
|
onkeydown={(event) => {
|
|
123
120
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
124
121
|
tabsContext.value = value
|
|
125
|
-
setIndicatorValue()
|
|
126
122
|
}
|
|
127
123
|
onkeydown?.(event)
|
|
128
124
|
}}
|
|
@@ -205,8 +201,8 @@
|
|
|
205
201
|
@supports not (anchor-name: --np-tab-indicator) {
|
|
206
202
|
.np-tab-content-active .np-indicator {
|
|
207
203
|
background-color: var(--np-color-primary);
|
|
208
|
-
border-start-start-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
209
|
-
border-start-end-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
204
|
+
border-start-start-radius: var(--np-tabs-indicator-radius, var(--np-shape-corner-full));
|
|
205
|
+
border-start-end-radius: var(--np-tabs-indicator-radius, var(--np-shape-corner-full));
|
|
210
206
|
}
|
|
211
207
|
}
|
|
212
208
|
|
package/dist/tabs/Tabs.svelte
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
const secondaryStyle = $derived(
|
|
32
32
|
tabsContext.variant === 'secondary'
|
|
33
|
-
? '--np-indicator-radius: 0;--_indicator-gap: 0;--_indicator-height: 2px'
|
|
33
|
+
? '--np-tabs-indicator-radius: 0;--_indicator-gap: 0;--_indicator-height: 2px'
|
|
34
34
|
: '',
|
|
35
35
|
)
|
|
36
36
|
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
right: anchor(right);
|
|
78
78
|
bottom: anchor(bottom);
|
|
79
79
|
background-color: var(--np-color-primary);
|
|
80
|
-
border-start-start-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
81
|
-
border-start-end-radius: var(--np-indicator-radius, var(--np-shape-corner-full));
|
|
80
|
+
border-start-start-radius: var(--np-tabs-indicator-radius, var(--np-shape-corner-full));
|
|
81
|
+
border-start-end-radius: var(--np-tabs-indicator-radius, var(--np-shape-corner-full));
|
|
82
82
|
position-anchor: --np-tab-indicator;
|
|
83
83
|
|
|
84
84
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
(children ? '--top-space:2rem;--bottom-space:1rem;' : '')) +
|
|
55
55
|
`--_min-height:calc(${minLines} * 1lh);--_max-height:${`calc(${maxLines} * 1lh)`};` +
|
|
56
56
|
style}
|
|
57
|
-
class={['text-field', attributes.class]}
|
|
57
|
+
class={['np-text-field', attributes.class]}
|
|
58
58
|
bind:this={element}
|
|
59
59
|
bind:clientWidth
|
|
60
60
|
bind:clientHeight
|
|
@@ -279,7 +279,7 @@
|
|
|
279
279
|
height: 100%;
|
|
280
280
|
position: relative;
|
|
281
281
|
}
|
|
282
|
-
.text-field {
|
|
282
|
+
.np-text-field {
|
|
283
283
|
display: inline-flex;
|
|
284
284
|
text-align: start;
|
|
285
285
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.js'
|
|
3
|
+
import type { ToolbarProps } from './types.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
variant = 'docked',
|
|
7
|
+
placement = 'bottom',
|
|
8
|
+
orientation = 'horizontal',
|
|
9
|
+
color = 'standard',
|
|
10
|
+
children,
|
|
11
|
+
element = $bindable(),
|
|
12
|
+
onkeydown: userKeydown,
|
|
13
|
+
...attributes
|
|
14
|
+
}: ToolbarProps = $props()
|
|
15
|
+
|
|
16
|
+
const ITEMS = 'button, a[href], input:not([type="hidden"])'
|
|
17
|
+
const attach = rovingTabindex(ITEMS)
|
|
18
|
+
const arrowHandler = $derived(arrowKeyNav(ITEMS, orientation))
|
|
19
|
+
|
|
20
|
+
const handleKeydown = (event: KeyboardEvent & { currentTarget: EventTarget & HTMLElement }) => {
|
|
21
|
+
userKeydown?.(event)
|
|
22
|
+
if (!event.defaultPrevented) arrowHandler(event)
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
{...attributes}
|
|
28
|
+
{@attach attach}
|
|
29
|
+
bind:this={element}
|
|
30
|
+
role="toolbar"
|
|
31
|
+
aria-orientation={orientation}
|
|
32
|
+
class={[
|
|
33
|
+
'np-toolbar',
|
|
34
|
+
`np-toolbar-${variant}`,
|
|
35
|
+
variant === 'floating' && `np-toolbar-${placement}`,
|
|
36
|
+
variant === 'floating' && color === 'vibrant' && 'np-toolbar-vibrant',
|
|
37
|
+
orientation === 'vertical' && 'np-toolbar-vertical',
|
|
38
|
+
attributes.class,
|
|
39
|
+
]}
|
|
40
|
+
onkeydown={handleKeydown}
|
|
41
|
+
>
|
|
42
|
+
{@render children?.()}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.np-toolbar {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
gap: var(--np-toolbar-gap, 0.25rem);
|
|
51
|
+
color: var(--np-toolbar-color, var(--np-color-on-surface));
|
|
52
|
+
--np-icon-button-icon-color: var(--np-toolbar-color, var(--np-color-on-surface-variant));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.np-toolbar-vertical {
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
/*
|
|
58
|
+
* Stacked actions sit right above one another, so a tooltip in its default place covers the
|
|
59
|
+
* button before it. Moving it to the trailing side keeps every action readable, and
|
|
60
|
+
* flip-inline sends it the other way when the window edge really is in the way. Material's
|
|
61
|
+
* Compose API exposes the same idea as TooltipAnchorPosition.End.
|
|
62
|
+
*/
|
|
63
|
+
--np-tooltip-position-area: inline-end;
|
|
64
|
+
--np-tooltip-justify-self: auto;
|
|
65
|
+
--np-tooltip-align-self: anchor-center;
|
|
66
|
+
--np-tooltip-margin: 0 4px;
|
|
67
|
+
--np-tooltip-position-try-fallbacks: flip-inline;
|
|
68
|
+
--np-rich-tooltip-position-area: inline-end;
|
|
69
|
+
--np-rich-tooltip-justify-self: auto;
|
|
70
|
+
--np-rich-tooltip-align-self: anchor-center;
|
|
71
|
+
--np-rich-tooltip-margin: 0 4px;
|
|
72
|
+
--np-rich-tooltip-position-try-fallbacks: flip-inline;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Docked: full width, flush to the edge, no corner rounding. */
|
|
76
|
+
.np-toolbar-docked {
|
|
77
|
+
width: 100%;
|
|
78
|
+
min-height: 4rem;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
padding-inline: 0.5rem;
|
|
81
|
+
background-color: var(--np-toolbar-container-color, var(--np-color-surface-container));
|
|
82
|
+
/* Clears the home inset when docked to the bottom of a phone screen. */
|
|
83
|
+
padding-block-end: max(0px, env(safe-area-inset-bottom));
|
|
84
|
+
}
|
|
85
|
+
.np-toolbar-docked.np-toolbar-vertical {
|
|
86
|
+
width: auto;
|
|
87
|
+
height: 100%;
|
|
88
|
+
min-height: 0;
|
|
89
|
+
min-width: 4rem;
|
|
90
|
+
padding-block: 0.5rem;
|
|
91
|
+
padding-inline: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
* Floating: an island above the content. `position: sticky` keeps it in flow so it never
|
|
96
|
+
* covers the end of the page, which `fixed` would.
|
|
97
|
+
*/
|
|
98
|
+
.np-toolbar-floating {
|
|
99
|
+
position: sticky;
|
|
100
|
+
z-index: 8;
|
|
101
|
+
width: fit-content;
|
|
102
|
+
margin-inline: auto;
|
|
103
|
+
padding: 0.5rem;
|
|
104
|
+
border-radius: var(--np-toolbar-shape, var(--np-shape-corner-full));
|
|
105
|
+
background-color: var(--np-toolbar-container-color, var(--np-color-surface-container));
|
|
106
|
+
box-shadow: var(--np-toolbar-elevation, var(--np-elevation-3));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.np-toolbar-bottom {
|
|
110
|
+
inset-block-end: var(--np-toolbar-inset, 1rem);
|
|
111
|
+
}
|
|
112
|
+
.np-toolbar-top {
|
|
113
|
+
inset-block-start: var(--np-toolbar-inset, 1rem);
|
|
114
|
+
}
|
|
115
|
+
.np-toolbar-start,
|
|
116
|
+
.np-toolbar-end {
|
|
117
|
+
margin-inline: 0;
|
|
118
|
+
margin-block: auto;
|
|
119
|
+
}
|
|
120
|
+
.np-toolbar-start {
|
|
121
|
+
inset-inline-start: var(--np-toolbar-inset, 1rem);
|
|
122
|
+
}
|
|
123
|
+
.np-toolbar-end {
|
|
124
|
+
inset-inline-end: var(--np-toolbar-inset, 1rem);
|
|
125
|
+
margin-inline-start: auto;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.np-toolbar-vibrant {
|
|
129
|
+
background-color: var(--np-toolbar-container-color, var(--np-color-primary-container));
|
|
130
|
+
--np-toolbar-color: var(--np-color-on-primary-container);
|
|
131
|
+
color: var(--np-toolbar-color);
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Toolbar } from './Toolbar.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Toolbar } from './Toolbar.svelte';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export interface ToolbarProps extends HTMLAttributes<HTMLElement> {
|
|
3
|
+
/**
|
|
4
|
+
* `'docked'` spans the full width of its container and sits flush against an edge, for actions
|
|
5
|
+
* that stay the same across pages. It replaces M3's deprecated bottom app bar. `'floating'`
|
|
6
|
+
* hovers above the content as a rounded island, for actions specific to the page.
|
|
7
|
+
*/
|
|
8
|
+
variant?: 'docked' | 'floating';
|
|
9
|
+
/** Which edge a floating toolbar hovers against. Ignored when docked. */
|
|
10
|
+
placement?: 'bottom' | 'top' | 'start' | 'end';
|
|
11
|
+
orientation?: 'horizontal' | 'vertical';
|
|
12
|
+
/** Colour role of a floating toolbar's container. */
|
|
13
|
+
color?: 'standard' | 'vibrant';
|
|
14
|
+
element?: HTMLElement;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { popoverController, syncOpenEffect } from '../popover.svelte.js'
|
|
2
3
|
import type { RichTooltipProps } from './types.js'
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -12,24 +13,35 @@
|
|
|
12
13
|
...attributes
|
|
13
14
|
}: RichTooltipProps = $props()
|
|
14
15
|
|
|
16
|
+
/*
|
|
17
|
+
* The control the tooltip is opened from, passed to `showPopover()` as its source so a
|
|
18
|
+
* programmatic open gets the invoker relationship the declarative one gets for free: the
|
|
19
|
+
* tooltip counts as nested inside any popover its control sits in, rather than dismissing it.
|
|
20
|
+
* Either attribute names a control, since `commandfor` and `popovertarget` are separate
|
|
21
|
+
* properties that set the same relationship.
|
|
22
|
+
*/
|
|
15
23
|
const findControl = () =>
|
|
16
|
-
id
|
|
24
|
+
id
|
|
25
|
+
? (document.querySelector<HTMLElement>(`[commandfor="${id}"], [popovertarget="${id}"]`) ??
|
|
26
|
+
undefined)
|
|
27
|
+
: undefined
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
const controller = popoverController(() => element)
|
|
30
|
+
|
|
31
|
+
export const show = () => {
|
|
19
32
|
if (!element || element.matches(':popover-open')) return
|
|
20
33
|
const source = findControl()
|
|
21
34
|
element.showPopover(source ? { source } : undefined)
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
export const
|
|
25
|
-
element?.hidePopover()
|
|
26
|
-
}
|
|
37
|
+
export const close = () => controller.close()
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
syncOpenEffect(
|
|
40
|
+
() => element,
|
|
41
|
+
() => open,
|
|
42
|
+
show,
|
|
43
|
+
close,
|
|
44
|
+
)
|
|
33
45
|
</script>
|
|
34
46
|
|
|
35
47
|
<div
|
|
@@ -68,7 +80,7 @@
|
|
|
68
80
|
max-width: 20rem;
|
|
69
81
|
word-wrap: break-word;
|
|
70
82
|
overflow-wrap: break-word;
|
|
71
|
-
margin: 4px 0;
|
|
83
|
+
margin: var(--np-rich-tooltip-margin, 4px 0);
|
|
72
84
|
padding: 0.25rem 1rem;
|
|
73
85
|
border: none;
|
|
74
86
|
border-radius: var(--np-shape-corner-medium);
|
|
@@ -78,8 +90,9 @@
|
|
|
78
90
|
font-size: 0.875rem;
|
|
79
91
|
line-height: 1.25rem;
|
|
80
92
|
justify-self: var(--np-rich-tooltip-justify-self, anchor-center);
|
|
93
|
+
align-self: var(--np-rich-tooltip-align-self, auto);
|
|
81
94
|
position-area: var(--np-rich-tooltip-position-area, bottom);
|
|
82
|
-
position-try-fallbacks: flip-block;
|
|
95
|
+
position-try-fallbacks: var(--np-rich-tooltip-position-try-fallbacks, flip-block);
|
|
83
96
|
}
|
|
84
97
|
.np-rich-tooltip-with-subhead[popover] {
|
|
85
98
|
padding-block: 0.75rem 1rem;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RichTooltipProps } from './types.ts';
|
|
2
2
|
declare const RichTooltip: import("svelte").Component<RichTooltipProps, {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
show: () => void;
|
|
4
|
+
close: () => void;
|
|
5
5
|
}, "element" | "open">;
|
|
6
6
|
type RichTooltip = ReturnType<typeof RichTooltip>;
|
|
7
7
|
export default RichTooltip;
|