noph-ui 0.34.4 → 0.35.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/badge/Badge.svelte +4 -2
- package/dist/badge/Badge.svelte.d.ts +1 -0
- package/dist/button/Button.svelte +3 -5
- package/dist/button/IconButton.svelte +7 -7
- package/dist/button/SegmentedButton.svelte +22 -25
- package/dist/card/Card.svelte +11 -35
- package/dist/checkbox/Checkbox.svelte +68 -32
- package/dist/checkbox/types.d.ts +1 -0
- package/dist/chip/ChipSet.svelte +10 -2
- package/dist/chip/FilterChip.svelte +48 -52
- package/dist/chip/InputChip.svelte +32 -31
- package/dist/chip/InputChip.svelte.d.ts +1 -1
- package/dist/chip/types.d.ts +3 -1
- package/dist/dialog/Dialog.svelte +53 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/keyboard-nav.js +12 -4
- package/dist/list/Item.svelte +39 -66
- package/dist/menu/Menu.svelte +10 -1
- package/dist/menu/MenuItem.svelte +4 -4
- package/dist/navigation-drawer/NavigationDrawer.svelte +35 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +2 -2
- package/dist/progress/CircularProgress.svelte +214 -117
- package/dist/progress/LinearProgress.svelte +382 -102
- package/dist/progress/types.d.ts +2 -0
- package/dist/ripple/Ripple.svelte +6 -7
- package/dist/select/Select.svelte +41 -20
- package/dist/slider/Slider.svelte +555 -0
- package/dist/slider/Slider.svelte.d.ts +4 -0
- package/dist/slider/index.d.ts +1 -0
- package/dist/slider/index.js +1 -0
- package/dist/slider/types.d.ts +23 -0
- package/dist/slider/types.js +1 -0
- package/dist/snackbar/Snackbar.svelte +21 -10
- package/dist/switch/Switch.svelte +1 -1
- package/dist/tabs/Tab.svelte +18 -16
- package/dist/tabs/Tabs.svelte +4 -2
- package/dist/tabs/types.d.ts +2 -0
- package/dist/text-field/TextField.svelte +2 -0
- package/dist/themes/defaultTheme.css +3 -9
- package/dist/tooltip/Tooltip.svelte +53 -10
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- package/package.json +11 -11
package/dist/badge/Badge.svelte
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
let { label }: { label?: string | number } = $props()
|
|
2
|
+
let { label, ariaLabel }: { label?: string | number; ariaLabel?: string } = $props()
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<div
|
|
6
|
-
aria-hidden=
|
|
6
|
+
aria-hidden={ariaLabel ? undefined : 'true'}
|
|
7
|
+
role={ariaLabel ? 'status' : undefined}
|
|
8
|
+
aria-label={ariaLabel}
|
|
7
9
|
class={[
|
|
8
10
|
'np-badge-container',
|
|
9
11
|
label === undefined ? 'np-badge-container-no-label' : 'np-badge-container-label',
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
{/if}
|
|
50
50
|
{#if loading}
|
|
51
51
|
<div class="circular-progress">
|
|
52
|
-
<CircularProgress aria-label={loadingAriaLabel} indeterminate />
|
|
52
|
+
<CircularProgress aria-label={loadingAriaLabel} indeterminate track={false} />
|
|
53
53
|
</div>
|
|
54
54
|
{/if}
|
|
55
55
|
{#if start}
|
|
@@ -417,10 +417,8 @@
|
|
|
417
417
|
:global(.np-button .button-icon svg) {
|
|
418
418
|
fill: currentColor;
|
|
419
419
|
display: block;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
width: calc((var(--button-height) - 0.375rem) / 2);
|
|
423
|
-
height: calc((var(--button-height) - 0.375rem) / 2);
|
|
420
|
+
width: var(--_icon-size);
|
|
421
|
+
height: var(--_icon-size);
|
|
424
422
|
}
|
|
425
423
|
|
|
426
424
|
@media (forced-colors: active) {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
{/if}
|
|
51
51
|
{#if loading}
|
|
52
52
|
<div class="np-icon-button-circular-progress">
|
|
53
|
-
<CircularProgress aria-label={loadingAriaLabel} indeterminate />
|
|
53
|
+
<CircularProgress aria-label={loadingAriaLabel} indeterminate track={false} />
|
|
54
54
|
</div>
|
|
55
55
|
{/if}
|
|
56
56
|
{#if selectedIcon && selected}
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
}
|
|
70
70
|
attributes.onclick?.(event)
|
|
71
71
|
}}
|
|
72
|
-
aria-describedby={title ? uid :
|
|
73
|
-
aria-label={title}
|
|
72
|
+
aria-describedby={title ? uid : attributes['aria-describedby']}
|
|
73
|
+
aria-label={title || attributes['aria-label']}
|
|
74
74
|
bind:this={element}
|
|
75
75
|
class={[
|
|
76
76
|
'np-icon-button',
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
border-radius: 1.25rem;
|
|
185
185
|
}
|
|
186
186
|
.s.square {
|
|
187
|
-
border-radius: var(--np-button-shape, 0.75rem);
|
|
187
|
+
border-radius: var(--np-icon-button-shape, 0.75rem);
|
|
188
188
|
}
|
|
189
189
|
.s.narrow {
|
|
190
190
|
width: 2rem;
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
border-radius: 1.75rem;
|
|
207
207
|
}
|
|
208
208
|
.m.square {
|
|
209
|
-
border-radius: var(--np-button-shape, 1rem);
|
|
209
|
+
border-radius: var(--np-icon-button-shape, 1rem);
|
|
210
210
|
}
|
|
211
211
|
.m.narrow {
|
|
212
212
|
width: 3rem;
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
border-radius: 3rem;
|
|
229
229
|
}
|
|
230
230
|
.l.square {
|
|
231
|
-
border-radius: var(--np-button-shape, 1.75rem);
|
|
231
|
+
border-radius: var(--np-icon-button-shape, 1.75rem);
|
|
232
232
|
}
|
|
233
233
|
.l.narrow {
|
|
234
234
|
width: 4rem;
|
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
border-radius: 4.25rem;
|
|
251
251
|
}
|
|
252
252
|
.xl.square {
|
|
253
|
-
border-radius: var(--np-button-shape, 1.75rem);
|
|
253
|
+
border-radius: var(--np-icon-button-shape, 1.75rem);
|
|
254
254
|
}
|
|
255
255
|
.xl.narrow {
|
|
256
256
|
width: 6.5rem;
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
group = $bindable(),
|
|
12
12
|
...attributes
|
|
13
13
|
}: SegmentedButtonProps = $props()
|
|
14
|
-
let hoverState = $state(-1)
|
|
15
14
|
</script>
|
|
16
15
|
|
|
17
16
|
<div
|
|
@@ -21,6 +20,7 @@
|
|
|
21
20
|
style="{attributes.style};grid-template-columns: repeat({options.length}, minmax(max-content, 1fr));"
|
|
22
21
|
>
|
|
23
22
|
{#each options as option, i (i)}
|
|
23
|
+
{@const optionLabel = option.label ?? `${name}-${i}`}
|
|
24
24
|
<label class={['np-segmented-button', option.icon && 'width-icon']}>
|
|
25
25
|
<div class="check-icon-wrapper">
|
|
26
26
|
<div class="check-icon">
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{option.label}
|
|
43
43
|
{/if}
|
|
44
44
|
{#if !option.disabled}
|
|
45
|
-
<Ripple
|
|
45
|
+
<Ripple />
|
|
46
46
|
{/if}
|
|
47
47
|
{#if group !== undefined}
|
|
48
48
|
{#if multiSelect}
|
|
@@ -50,13 +50,9 @@
|
|
|
50
50
|
type="checkbox"
|
|
51
51
|
onclick={option.onclick}
|
|
52
52
|
bind:group
|
|
53
|
-
onfocus={(event) => {
|
|
54
|
-
if (event) hoverState = i
|
|
55
|
-
}}
|
|
56
|
-
onblur={() => (hoverState = -1)}
|
|
57
53
|
{name}
|
|
58
|
-
aria-label={
|
|
59
|
-
value={
|
|
54
|
+
aria-label={optionLabel}
|
|
55
|
+
value={optionLabel}
|
|
60
56
|
disabled={option.disabled}
|
|
61
57
|
checked={option.selected}
|
|
62
58
|
/>
|
|
@@ -65,13 +61,9 @@
|
|
|
65
61
|
type="radio"
|
|
66
62
|
onclick={option.onclick}
|
|
67
63
|
bind:group
|
|
68
|
-
onfocus={(event) => {
|
|
69
|
-
if (event) hoverState = i
|
|
70
|
-
}}
|
|
71
|
-
onblur={() => (hoverState = -1)}
|
|
72
64
|
{name}
|
|
73
|
-
aria-label={
|
|
74
|
-
value={
|
|
65
|
+
aria-label={optionLabel}
|
|
66
|
+
value={optionLabel}
|
|
75
67
|
disabled={option.disabled}
|
|
76
68
|
checked={option.selected}
|
|
77
69
|
/>
|
|
@@ -80,13 +72,9 @@
|
|
|
80
72
|
<input
|
|
81
73
|
type={multiSelect ? 'checkbox' : 'radio'}
|
|
82
74
|
onclick={option.onclick}
|
|
83
|
-
onfocus={(event) => {
|
|
84
|
-
if (event) hoverState = i
|
|
85
|
-
}}
|
|
86
|
-
onblur={() => (hoverState = -1)}
|
|
87
75
|
{name}
|
|
88
|
-
aria-label={
|
|
89
|
-
value={
|
|
76
|
+
aria-label={optionLabel}
|
|
77
|
+
value={optionLabel}
|
|
90
78
|
disabled={option.disabled}
|
|
91
79
|
checked={option.selected}
|
|
92
80
|
/>
|
|
@@ -108,7 +96,7 @@
|
|
|
108
96
|
|
|
109
97
|
.np-segmented-button {
|
|
110
98
|
flex: 1;
|
|
111
|
-
padding: 0.5rem
|
|
99
|
+
padding: 0.5rem 1.75rem;
|
|
112
100
|
text-align: center;
|
|
113
101
|
cursor: pointer;
|
|
114
102
|
font-size: 0.875rem;
|
|
@@ -136,7 +124,7 @@
|
|
|
136
124
|
|
|
137
125
|
.np-segmented-button:has(input:disabled) {
|
|
138
126
|
cursor: unset;
|
|
139
|
-
opacity: 0.
|
|
127
|
+
opacity: 0.38;
|
|
140
128
|
}
|
|
141
129
|
|
|
142
130
|
.np-segmented-button:last-child {
|
|
@@ -159,7 +147,7 @@
|
|
|
159
147
|
}
|
|
160
148
|
.width-icon,
|
|
161
149
|
.np-segmented-button:has(input:checked) {
|
|
162
|
-
padding: 0.5rem
|
|
150
|
+
padding: 0.5rem 0.75rem;
|
|
163
151
|
}
|
|
164
152
|
.np-segmented-button:has(input:checked) {
|
|
165
153
|
color: var(--np-color-on-secondary-container);
|
|
@@ -168,6 +156,7 @@
|
|
|
168
156
|
opacity: 1;
|
|
169
157
|
}
|
|
170
158
|
.check-icon {
|
|
159
|
+
display: flex;
|
|
171
160
|
width: 0;
|
|
172
161
|
overflow: hidden;
|
|
173
162
|
fill: currentColor;
|
|
@@ -182,6 +171,7 @@
|
|
|
182
171
|
transition: width 0.15s linear;
|
|
183
172
|
}
|
|
184
173
|
.alternate-icon {
|
|
174
|
+
display: flex;
|
|
185
175
|
width: 1.5rem;
|
|
186
176
|
overflow: hidden;
|
|
187
177
|
fill: currentColor;
|
|
@@ -213,21 +203,28 @@
|
|
|
213
203
|
width: 1.5rem;
|
|
214
204
|
}
|
|
215
205
|
|
|
216
|
-
.np-segmented-
|
|
206
|
+
.np-segmented-button:has(input:focus-visible) :global(.np-ripple-surface)::before {
|
|
207
|
+
opacity: var(--np-ripple-focus-opacity, 0.1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.np-segmented-button:has(input:focus-visible) {
|
|
217
211
|
outline-style: solid;
|
|
218
212
|
outline-color: var(--np-color-secondary);
|
|
219
213
|
outline-width: 3px;
|
|
220
|
-
outline-offset:
|
|
214
|
+
outline-offset: -3px;
|
|
221
215
|
animation: focusAnimation 0.3s ease forwards;
|
|
222
216
|
}
|
|
223
217
|
@keyframes focusAnimation {
|
|
224
218
|
0% {
|
|
219
|
+
outline-offset: -3px;
|
|
225
220
|
outline-width: 3px;
|
|
226
221
|
}
|
|
227
222
|
50% {
|
|
223
|
+
outline-offset: -6px;
|
|
228
224
|
outline-width: 6px;
|
|
229
225
|
}
|
|
230
226
|
100% {
|
|
227
|
+
outline-offset: -3px;
|
|
231
228
|
outline-width: 3px;
|
|
232
229
|
}
|
|
233
230
|
}
|
package/dist/card/Card.svelte
CHANGED
|
@@ -12,25 +12,9 @@
|
|
|
12
12
|
supportingText,
|
|
13
13
|
action,
|
|
14
14
|
children,
|
|
15
|
-
onfocus,
|
|
16
|
-
onblur,
|
|
17
15
|
type,
|
|
18
16
|
...attributes
|
|
19
17
|
}: CardProps = $props()
|
|
20
|
-
|
|
21
|
-
let focused = $state(false)
|
|
22
|
-
|
|
23
|
-
$effect(() => {
|
|
24
|
-
if (!element) return
|
|
25
|
-
// eslint-disable-next-line no-undef
|
|
26
|
-
const formElements: NodeListOf<
|
|
27
|
-
HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
28
|
-
> = element.querySelectorAll('input, button, select, textarea')
|
|
29
|
-
|
|
30
|
-
formElements.forEach((el) => {
|
|
31
|
-
el.disabled = disabled
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
18
|
</script>
|
|
35
19
|
|
|
36
20
|
{#snippet content()}
|
|
@@ -61,7 +45,7 @@
|
|
|
61
45
|
{/if}
|
|
62
46
|
</div>
|
|
63
47
|
{#if !disabled && type !== 'text'}
|
|
64
|
-
<Ripple
|
|
48
|
+
<Ripple />
|
|
65
49
|
{/if}
|
|
66
50
|
{/snippet}
|
|
67
51
|
|
|
@@ -85,14 +69,6 @@
|
|
|
85
69
|
{...attributes}
|
|
86
70
|
type="button"
|
|
87
71
|
bind:this={element}
|
|
88
|
-
onfocus={(event) => {
|
|
89
|
-
focused = true
|
|
90
|
-
onfocus?.(event)
|
|
91
|
-
}}
|
|
92
|
-
onblur={(event) => {
|
|
93
|
-
focused = false
|
|
94
|
-
onblur?.(event)
|
|
95
|
-
}}
|
|
96
72
|
{disabled}
|
|
97
73
|
class={[
|
|
98
74
|
'np-card-container',
|
|
@@ -110,14 +86,6 @@
|
|
|
110
86
|
aria-disabled={disabled}
|
|
111
87
|
tabindex={disabled ? -1 : undefined}
|
|
112
88
|
role="link"
|
|
113
|
-
onfocus={(event) => {
|
|
114
|
-
focused = true
|
|
115
|
-
onfocus?.(event)
|
|
116
|
-
}}
|
|
117
|
-
onblur={(event) => {
|
|
118
|
-
focused = false
|
|
119
|
-
onblur?.(event)
|
|
120
|
-
}}
|
|
121
89
|
class={[
|
|
122
90
|
'np-card-container',
|
|
123
91
|
`np-card-${variant}`,
|
|
@@ -162,8 +130,8 @@
|
|
|
162
130
|
--border-radius: var(--np-outlined-card-container-shape, var(--np-shape-corner-medium));
|
|
163
131
|
}
|
|
164
132
|
.np-card-outlined.np-card-disabled {
|
|
165
|
-
border-color: color-mix(in srgb, var(--np-color-outline)
|
|
166
|
-
--background-color:
|
|
133
|
+
border-color: color-mix(in srgb, var(--np-color-outline) 12%, transparent);
|
|
134
|
+
--background-color: transparent;
|
|
167
135
|
}
|
|
168
136
|
.np-card-container {
|
|
169
137
|
font: inherit;
|
|
@@ -181,6 +149,10 @@
|
|
|
181
149
|
cursor: pointer;
|
|
182
150
|
}
|
|
183
151
|
|
|
152
|
+
.np-card-container:focus-visible :global(.np-ripple-surface)::before {
|
|
153
|
+
opacity: var(--np-ripple-focus-opacity, 0.1);
|
|
154
|
+
}
|
|
155
|
+
|
|
184
156
|
.np-card-container:focus-visible {
|
|
185
157
|
outline-style: solid;
|
|
186
158
|
outline-color: var(--np-color-secondary);
|
|
@@ -211,6 +183,10 @@
|
|
|
211
183
|
overflow: hidden;
|
|
212
184
|
background-size: cover;
|
|
213
185
|
background-position: 50%;
|
|
186
|
+
border-start-start-radius: var(--border-radius);
|
|
187
|
+
border-start-end-radius: var(--border-radius);
|
|
188
|
+
}
|
|
189
|
+
.np-card-container:not(:has(.np-card-content:not(:empty))) .np-card-image {
|
|
214
190
|
border-radius: var(--border-radius);
|
|
215
191
|
}
|
|
216
192
|
.np-card-content {
|
|
@@ -7,37 +7,41 @@
|
|
|
7
7
|
checked = $bindable(),
|
|
8
8
|
element = $bindable(),
|
|
9
9
|
group = $bindable(),
|
|
10
|
+
error = false,
|
|
10
11
|
style,
|
|
11
12
|
value,
|
|
12
13
|
...attributes
|
|
13
14
|
}: CheckboxProps = $props()
|
|
14
15
|
|
|
15
|
-
$
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
let inputEl: HTMLInputElement | undefined = $state()
|
|
17
|
+
|
|
18
|
+
let groupValue = $derived(
|
|
19
|
+
typeof value === 'string' || typeof value === 'number' ? value : undefined,
|
|
20
|
+
)
|
|
21
|
+
let grouped = $derived(Array.isArray(group) && groupValue !== undefined)
|
|
22
|
+
let groupChecked = $derived(
|
|
23
|
+
Array.isArray(group) && groupValue !== undefined ? group.includes(groupValue) : false,
|
|
24
|
+
)
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
group
|
|
27
|
-
group
|
|
28
|
-
|
|
29
|
-
} else {
|
|
30
|
-
if (index >= 0) {
|
|
31
|
-
group.splice(index, 1)
|
|
32
|
-
group = group
|
|
33
|
-
}
|
|
34
|
-
}
|
|
26
|
+
const onchange = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
|
27
|
+
const next = event.currentTarget.checked
|
|
28
|
+
if (Array.isArray(group) && groupValue !== undefined) {
|
|
29
|
+
group = next
|
|
30
|
+
? group.includes(groupValue)
|
|
31
|
+
? group
|
|
32
|
+
: [...group, groupValue]
|
|
33
|
+
: group.filter((entry) => entry !== groupValue)
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
checked = next
|
|
36
|
+
attributes.onchange?.(event)
|
|
37
|
+
}
|
|
38
38
|
</script>
|
|
39
39
|
|
|
40
|
-
<div
|
|
40
|
+
<div
|
|
41
|
+
{style}
|
|
42
|
+
class={['np-container', error && !attributes.disabled && 'np-error', attributes.class]}
|
|
43
|
+
bind:this={element}
|
|
44
|
+
>
|
|
41
45
|
<div class="np-outline"></div>
|
|
42
46
|
<div class="np-background"></div>
|
|
43
47
|
<svg class="np-icon" viewBox="0 0 18 18" aria-hidden="true">
|
|
@@ -48,16 +52,32 @@
|
|
|
48
52
|
{#if !attributes.disabled}
|
|
49
53
|
<Ripple forElement={inputEl} />
|
|
50
54
|
{/if}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
{#if grouped}
|
|
56
|
+
<input
|
|
57
|
+
{...attributes}
|
|
58
|
+
{value}
|
|
59
|
+
class="np-input"
|
|
60
|
+
type="checkbox"
|
|
61
|
+
bind:indeterminate
|
|
62
|
+
bind:this={inputEl}
|
|
63
|
+
checked={groupChecked}
|
|
64
|
+
{onchange}
|
|
65
|
+
aria-invalid={error ? 'true' : undefined}
|
|
66
|
+
aria-checked={indeterminate ? 'mixed' : undefined}
|
|
67
|
+
/>
|
|
68
|
+
{:else}
|
|
69
|
+
<input
|
|
70
|
+
{...attributes}
|
|
71
|
+
{value}
|
|
72
|
+
class="np-input"
|
|
73
|
+
type="checkbox"
|
|
74
|
+
bind:indeterminate
|
|
75
|
+
bind:checked
|
|
76
|
+
bind:this={inputEl}
|
|
77
|
+
aria-invalid={error ? 'true' : undefined}
|
|
78
|
+
aria-checked={indeterminate ? 'mixed' : undefined}
|
|
79
|
+
/>
|
|
80
|
+
{/if}
|
|
61
81
|
</div>
|
|
62
82
|
</div>
|
|
63
83
|
|
|
@@ -172,6 +192,22 @@
|
|
|
172
192
|
background: var(--np-color-on-surface);
|
|
173
193
|
opacity: 0.38;
|
|
174
194
|
}
|
|
195
|
+
|
|
196
|
+
.np-error .np-outline,
|
|
197
|
+
.np-error:hover .np-outline,
|
|
198
|
+
.np-error:focus-within .np-outline {
|
|
199
|
+
border-color: var(--np-color-error);
|
|
200
|
+
}
|
|
201
|
+
.np-error .np-background {
|
|
202
|
+
background-color: var(--np-color-error);
|
|
203
|
+
}
|
|
204
|
+
.np-error .np-icon {
|
|
205
|
+
fill: var(--np-color-on-error);
|
|
206
|
+
}
|
|
207
|
+
.np-error {
|
|
208
|
+
--np-ripple-hover-color: var(--np-color-error);
|
|
209
|
+
--np-ripple-pressed-color: var(--np-color-error);
|
|
210
|
+
}
|
|
175
211
|
.np-icon {
|
|
176
212
|
fill: var(--np-checkbox-selected-icon-color, var(--np-color-on-primary));
|
|
177
213
|
height: 18px;
|
package/dist/checkbox/types.d.ts
CHANGED
package/dist/chip/ChipSet.svelte
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { arrowKeyNav, rovingTabindex } from '../keyboard-nav.js'
|
|
2
3
|
import type { ChipSetProps } from './types.ts'
|
|
3
4
|
|
|
4
5
|
let { children, chipsCount, ...attributes }: ChipSetProps = $props()
|
|
6
|
+
|
|
7
|
+
const CHIP_SELECTOR = "input:not([type='hidden']), button, a[href]"
|
|
8
|
+
const attach = rovingTabindex(CHIP_SELECTOR, {
|
|
9
|
+
currentAttr: 'aria-current',
|
|
10
|
+
currentValue: 'true',
|
|
11
|
+
})
|
|
12
|
+
const onkeydown = arrowKeyNav(CHIP_SELECTOR, 'horizontal')
|
|
5
13
|
</script>
|
|
6
14
|
|
|
7
15
|
<div
|
|
@@ -13,7 +21,7 @@
|
|
|
13
21
|
style={attributes.style}
|
|
14
22
|
>
|
|
15
23
|
{#if children}
|
|
16
|
-
<div class={['np-chip-set']} role="toolbar">
|
|
24
|
+
<div {@attach attach} class={['np-chip-set']} role="toolbar" tabindex="-1" {onkeydown}>
|
|
17
25
|
{@render children()}
|
|
18
26
|
</div>
|
|
19
27
|
{/if}
|
|
@@ -26,7 +34,7 @@
|
|
|
26
34
|
margin: -0.5rem;
|
|
27
35
|
}
|
|
28
36
|
.np-chip-set-has-chips {
|
|
29
|
-
margin-
|
|
37
|
+
margin-inline-end: 0;
|
|
30
38
|
}
|
|
31
39
|
.np-chip-set {
|
|
32
40
|
display: flex;
|
|
@@ -23,28 +23,23 @@
|
|
|
23
23
|
}: FilterChipProps = $props()
|
|
24
24
|
|
|
25
25
|
let chipLabel: HTMLLabelElement | undefined = $state()
|
|
26
|
-
let pressed = $state(false)
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const handlePointerUp = () => {
|
|
34
|
-
pressed = false
|
|
35
|
-
}
|
|
27
|
+
let grouped = $derived(Array.isArray(group) && value !== undefined)
|
|
28
|
+
let groupChecked = $derived(
|
|
29
|
+
Array.isArray(group) && value !== undefined ? group.includes(value) : false,
|
|
30
|
+
)
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
selected = included
|
|
32
|
+
const onchange = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
|
33
|
+
const next = event.currentTarget.checked
|
|
34
|
+
if (Array.isArray(group) && value !== undefined) {
|
|
35
|
+
group = next
|
|
36
|
+
? group.includes(value)
|
|
37
|
+
? group
|
|
38
|
+
: [...group, value]
|
|
39
|
+
: group.filter((entry) => entry !== value)
|
|
46
40
|
}
|
|
47
|
-
|
|
41
|
+
selected = next
|
|
42
|
+
}
|
|
48
43
|
</script>
|
|
49
44
|
|
|
50
45
|
<div
|
|
@@ -56,17 +51,10 @@
|
|
|
56
51
|
icon ? 'np-filter-chip-icon' : '',
|
|
57
52
|
removable ? 'np-filter-chip-removable' : '',
|
|
58
53
|
disabled ? 'np-filter-chip-disabled' : '',
|
|
59
|
-
pressed && 'np-filter-chip-pressed',
|
|
60
54
|
attributes.class,
|
|
61
55
|
]}
|
|
62
56
|
>
|
|
63
|
-
<label
|
|
64
|
-
bind:this={chipLabel}
|
|
65
|
-
class="np-filter-chip-label"
|
|
66
|
-
onpointerdown={handlePointerDown}
|
|
67
|
-
onpointerup={handlePointerUp}
|
|
68
|
-
onpointerleave={handlePointerUp}
|
|
69
|
-
>
|
|
57
|
+
<label bind:this={chipLabel} class="np-filter-chip-label">
|
|
70
58
|
{#if icon}
|
|
71
59
|
<div class="np-chip-icon">
|
|
72
60
|
{@render icon()}
|
|
@@ -76,14 +64,26 @@
|
|
|
76
64
|
<CheckIcon width={18} height={18} />
|
|
77
65
|
</div>
|
|
78
66
|
<div class="np-chip-label">{label}</div>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
67
|
+
{#if grouped}
|
|
68
|
+
<input
|
|
69
|
+
type="checkbox"
|
|
70
|
+
checked={groupChecked}
|
|
71
|
+
{onchange}
|
|
72
|
+
{value}
|
|
73
|
+
{name}
|
|
74
|
+
{disabled}
|
|
75
|
+
defaultChecked={defaultSelected}
|
|
76
|
+
/>
|
|
77
|
+
{:else}
|
|
78
|
+
<input
|
|
79
|
+
type="checkbox"
|
|
80
|
+
bind:checked={selected}
|
|
81
|
+
{value}
|
|
82
|
+
{name}
|
|
83
|
+
{disabled}
|
|
84
|
+
defaultChecked={defaultSelected}
|
|
85
|
+
/>
|
|
86
|
+
{/if}
|
|
87
87
|
</label>
|
|
88
88
|
{#if !disabled}
|
|
89
89
|
<Ripple forElement={chipLabel} />
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
display: none;
|
|
156
156
|
}
|
|
157
157
|
.np-chip-icon {
|
|
158
|
-
color: var(--np-color-
|
|
158
|
+
color: var(--np-color-on-surface-variant);
|
|
159
159
|
display: flex;
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -215,10 +215,12 @@
|
|
|
215
215
|
box-shadow: var(--np-elevation-1);
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
-
.np-filter-chip-elevated:not(.np-filter-chip-disabled).np-filter-chip-
|
|
218
|
+
.np-filter-chip-elevated:not(.np-filter-chip-disabled):has(.np-filter-chip-label:active) {
|
|
219
219
|
box-shadow: var(--np-elevation-1);
|
|
220
220
|
}
|
|
221
|
-
.np-filter-chip-default:not(.np-filter-chip-disabled):has(
|
|
221
|
+
.np-filter-chip-default:not(.np-filter-chip-disabled):has(
|
|
222
|
+
.np-filter-chip-label:active input:checked
|
|
223
|
+
) {
|
|
222
224
|
box-shadow: none;
|
|
223
225
|
}
|
|
224
226
|
.np-filter-chip:has(input:checked) {
|
|
@@ -257,20 +259,14 @@
|
|
|
257
259
|
.np-filter-chip-disabled.np-filter-chip-elevated {
|
|
258
260
|
box-shadow: none;
|
|
259
261
|
}
|
|
260
|
-
.np-filter-chip-disabled:has(input:not(:checked))
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
inset: 0;
|
|
264
|
-
border-radius: inherit;
|
|
265
|
-
pointer-events: none;
|
|
266
|
-
border-width: 1px;
|
|
267
|
-
border-style: solid;
|
|
268
|
-
border-color: var(--np-color-on-surface);
|
|
269
|
-
opacity: 0.12;
|
|
262
|
+
.np-filter-chip-disabled.np-filter-chip-default:has(input:not(:checked))::before {
|
|
263
|
+
outline-color: color-mix(in srgb, var(--np-color-on-surface) 12%, transparent);
|
|
264
|
+
background-color: transparent;
|
|
270
265
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
266
|
+
|
|
267
|
+
.np-filter-chip-disabled.np-filter-chip-elevated::before,
|
|
268
|
+
.np-filter-chip-disabled:has(input:checked)::before {
|
|
269
|
+
outline-color: transparent;
|
|
270
|
+
background-color: color-mix(in srgb, var(--np-color-on-surface) 12%, transparent);
|
|
275
271
|
}
|
|
276
272
|
</style>
|