noph-ui 0.42.0 → 0.43.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/button/Button.svelte +22 -37
- package/dist/button/ButtonGroup.svelte +224 -0
- package/dist/button/ButtonGroup.svelte.d.ts +4 -0
- package/dist/button/IconButton.svelte +20 -35
- package/dist/button/buttonGroup.d.ts +5 -0
- package/dist/button/buttonGroup.js +22 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/button/types.d.ts +7 -0
- package/dist/menu/MenuItem.svelte +3 -5
- package/dist/navigation-drawer/NavigationDrawerItem.svelte +3 -1
- package/dist/navigation-rail/NavigationRailItem.svelte +3 -1
- package/package.json +1 -1
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
const uid = $props.id()
|
|
28
28
|
|
|
29
|
+
const isLink = $derived(attributes.href != null && !disabled && !loading)
|
|
30
|
+
|
|
29
31
|
const tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
|
|
30
32
|
|
|
31
33
|
let pressed = $state(false)
|
|
@@ -72,7 +74,7 @@
|
|
|
72
74
|
{/if}
|
|
73
75
|
{/snippet}
|
|
74
76
|
|
|
75
|
-
{#if
|
|
77
|
+
{#if isLink}
|
|
76
78
|
<a
|
|
77
79
|
{...attributes}
|
|
78
80
|
onclick={(event) => {
|
|
@@ -141,8 +143,8 @@
|
|
|
141
143
|
<style>
|
|
142
144
|
.children-wrapper {
|
|
143
145
|
flex: 1;
|
|
144
|
-
overflow: hidden;
|
|
145
|
-
text-overflow: ellipsis;
|
|
146
|
+
overflow: var(--_button-label-overflow, hidden);
|
|
147
|
+
text-overflow: var(--_button-label-text-overflow, ellipsis);
|
|
146
148
|
text-wrap: nowrap;
|
|
147
149
|
}
|
|
148
150
|
.circular-progress {
|
|
@@ -184,6 +186,9 @@
|
|
|
184
186
|
box-shadow var(--np-motion-expressive-default-effects);
|
|
185
187
|
}
|
|
186
188
|
}
|
|
189
|
+
.round {
|
|
190
|
+
border-radius: var(--_round-radius);
|
|
191
|
+
}
|
|
187
192
|
.xs {
|
|
188
193
|
font-size: 0.875rem;
|
|
189
194
|
height: 2rem;
|
|
@@ -191,17 +196,12 @@
|
|
|
191
196
|
gap: var(--np-button-gap, 0.25rem);
|
|
192
197
|
--_icon-size: var(--np-button-icon-size, 1.25rem);
|
|
193
198
|
--np-circular-progress-size: 1.75rem;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
border-radius: 1rem;
|
|
199
|
+
--_round-radius: 1rem;
|
|
200
|
+
--_pressed-radius: 0.5rem;
|
|
197
201
|
}
|
|
198
202
|
.xs.square {
|
|
199
203
|
border-radius: var(--np-button-shape, 0.75rem);
|
|
200
204
|
}
|
|
201
|
-
.xs:active,
|
|
202
|
-
.xs.pressed {
|
|
203
|
-
border-radius: 0.5rem;
|
|
204
|
-
}
|
|
205
205
|
.s {
|
|
206
206
|
font-size: 0.875rem;
|
|
207
207
|
height: 2.5rem;
|
|
@@ -209,17 +209,12 @@
|
|
|
209
209
|
gap: var(--np-button-gap, 0.5rem);
|
|
210
210
|
--_icon-size: var(--np-button-icon-size, 1.25rem);
|
|
211
211
|
--np-circular-progress-size: 2rem;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
border-radius: 1.25rem;
|
|
212
|
+
--_round-radius: 1.25rem;
|
|
213
|
+
--_pressed-radius: 0.5rem;
|
|
215
214
|
}
|
|
216
215
|
.s.square {
|
|
217
216
|
border-radius: var(--np-button-shape, 0.75rem);
|
|
218
217
|
}
|
|
219
|
-
.s:active,
|
|
220
|
-
.s.pressed {
|
|
221
|
-
border-radius: 0.5rem;
|
|
222
|
-
}
|
|
223
218
|
.m {
|
|
224
219
|
font-size: 1rem;
|
|
225
220
|
height: 3.5rem;
|
|
@@ -227,17 +222,12 @@
|
|
|
227
222
|
gap: var(--np-button-gap, 0.5rem);
|
|
228
223
|
--_icon-size: var(--np-button-icon-size, 1.5rem);
|
|
229
224
|
--np-circular-progress-size: 3rem;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
border-radius: 1.75rem;
|
|
225
|
+
--_round-radius: 1.75rem;
|
|
226
|
+
--_pressed-radius: 0.75rem;
|
|
233
227
|
}
|
|
234
228
|
.m.square {
|
|
235
229
|
border-radius: var(--np-button-shape, 1rem);
|
|
236
230
|
}
|
|
237
|
-
.m:active,
|
|
238
|
-
.m.pressed {
|
|
239
|
-
border-radius: 0.75rem;
|
|
240
|
-
}
|
|
241
231
|
.l {
|
|
242
232
|
font-size: 1.5rem;
|
|
243
233
|
height: 6rem;
|
|
@@ -245,17 +235,12 @@
|
|
|
245
235
|
gap: var(--np-button-gap, 0.75rem);
|
|
246
236
|
--_icon-size: var(--np-button-icon-size, 2rem);
|
|
247
237
|
--np-circular-progress-size: 5rem;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
border-radius: 3rem;
|
|
238
|
+
--_round-radius: 3rem;
|
|
239
|
+
--_pressed-radius: 1rem;
|
|
251
240
|
}
|
|
252
241
|
.l.square {
|
|
253
242
|
border-radius: var(--np-button-shape, 1.75rem);
|
|
254
243
|
}
|
|
255
|
-
.l:active,
|
|
256
|
-
.l.pressed {
|
|
257
|
-
border-radius: 1rem;
|
|
258
|
-
}
|
|
259
244
|
.xl {
|
|
260
245
|
font-size: 2rem;
|
|
261
246
|
height: 8.5rem;
|
|
@@ -263,16 +248,16 @@
|
|
|
263
248
|
gap: var(--np-button-gap, 1rem);
|
|
264
249
|
--_icon-size: var(--np-button-icon-size, 2.5rem);
|
|
265
250
|
--np-circular-progress-size: 7rem;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
border-radius: 4.25rem;
|
|
251
|
+
--_round-radius: 4.25rem;
|
|
252
|
+
--_pressed-radius: 1rem;
|
|
269
253
|
}
|
|
270
254
|
.xl.square {
|
|
271
255
|
border-radius: var(--np-button-shape, 1.75rem);
|
|
272
256
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
257
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
258
|
+
.np-button:is(:active, .pressed) {
|
|
259
|
+
border-radius: var(--_pressed-radius);
|
|
260
|
+
}
|
|
276
261
|
}
|
|
277
262
|
.toggle {
|
|
278
263
|
--np-icon-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte'
|
|
3
|
+
import { MediaQuery } from 'svelte/reactivity'
|
|
4
|
+
import { expandedWidths, parseMotion } from './buttonGroup.js'
|
|
5
|
+
import type { ButtonGroupProps } from './types.js'
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
variant = 'standard',
|
|
9
|
+
expandedRatio = 0.15,
|
|
10
|
+
compressionLimit = 24,
|
|
11
|
+
element = $bindable(),
|
|
12
|
+
children,
|
|
13
|
+
...attributes
|
|
14
|
+
}: ButtonGroupProps = $props()
|
|
15
|
+
|
|
16
|
+
const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)', false)
|
|
17
|
+
|
|
18
|
+
const settleRatio = 0.75
|
|
19
|
+
|
|
20
|
+
type Timing = { duration: number; easing: string }
|
|
21
|
+
|
|
22
|
+
let animations: Animation[] = []
|
|
23
|
+
let pressed: {
|
|
24
|
+
items: HTMLElement[]
|
|
25
|
+
widths: number[]
|
|
26
|
+
timing: Timing
|
|
27
|
+
growing?: Animation
|
|
28
|
+
} | null = null
|
|
29
|
+
let releaseFrame: number | undefined
|
|
30
|
+
let moving = $state(false)
|
|
31
|
+
|
|
32
|
+
const items = () =>
|
|
33
|
+
element
|
|
34
|
+
? (Array.from(element.children) as HTMLElement[]).filter(
|
|
35
|
+
(item) => !item.hasAttribute('popover'),
|
|
36
|
+
)
|
|
37
|
+
: []
|
|
38
|
+
|
|
39
|
+
const motion = () =>
|
|
40
|
+
element
|
|
41
|
+
? parseMotion(
|
|
42
|
+
getComputedStyle(element).getPropertyValue('--np-motion-expressive-fast-spatial'),
|
|
43
|
+
)
|
|
44
|
+
: { duration: 0, easing: 'linear' }
|
|
45
|
+
|
|
46
|
+
const animate = (
|
|
47
|
+
targets: HTMLElement[],
|
|
48
|
+
from: number[],
|
|
49
|
+
to: number[],
|
|
50
|
+
keep: boolean,
|
|
51
|
+
timing: Timing,
|
|
52
|
+
) =>
|
|
53
|
+
targets.map((item, index) => {
|
|
54
|
+
if (keep) {
|
|
55
|
+
item.style.width = `${to[index]}px`
|
|
56
|
+
} else {
|
|
57
|
+
item.style.removeProperty('width')
|
|
58
|
+
}
|
|
59
|
+
if (from[index] === to[index]) return undefined
|
|
60
|
+
const animation = item.animate(
|
|
61
|
+
[{ width: `${from[index]}px` }, { width: `${to[index]}px` }],
|
|
62
|
+
timing,
|
|
63
|
+
)
|
|
64
|
+
animations.push(animation)
|
|
65
|
+
return animation
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const stop = () => {
|
|
69
|
+
if (releaseFrame !== undefined) cancelAnimationFrame(releaseFrame)
|
|
70
|
+
releaseFrame = undefined
|
|
71
|
+
for (const animation of animations) animation.cancel()
|
|
72
|
+
animations = []
|
|
73
|
+
moving = false
|
|
74
|
+
if (!pressed) return
|
|
75
|
+
for (const item of pressed.items) item.style.removeProperty('width')
|
|
76
|
+
pressed = null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const press = (target: EventTarget | null) => {
|
|
80
|
+
if (variant === 'connected') return
|
|
81
|
+
if (!element || expandedRatio <= 0 || reducedMotion.current) return
|
|
82
|
+
stop()
|
|
83
|
+
const all = items()
|
|
84
|
+
if (all.length < 2) return
|
|
85
|
+
const index = all.findIndex((item) => item.contains(target as Node))
|
|
86
|
+
if (index < 0) return
|
|
87
|
+
const widths = all.map((item) => item.getBoundingClientRect().width)
|
|
88
|
+
const targets = expandedWidths(widths, index, expandedRatio, compressionLimit)
|
|
89
|
+
const timing = motion()
|
|
90
|
+
const growing = animate(all, widths, targets, true, timing)[index]
|
|
91
|
+
pressed = { items: all, widths, timing, growing }
|
|
92
|
+
moving = true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const settled = () => {
|
|
96
|
+
const progress = pressed?.growing?.effect?.getComputedTiming().progress
|
|
97
|
+
return progress == null || progress >= settleRatio
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const release = () => {
|
|
101
|
+
if (!pressed || releaseFrame !== undefined) return
|
|
102
|
+
const back = () => {
|
|
103
|
+
if (!pressed) {
|
|
104
|
+
releaseFrame = undefined
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
if (!settled()) {
|
|
108
|
+
releaseFrame = requestAnimationFrame(back)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
const { items: targets, widths, timing } = pressed
|
|
112
|
+
const from = targets.map((item) => item.getBoundingClientRect().width)
|
|
113
|
+
for (const animation of animations) animation.cancel()
|
|
114
|
+
animations = []
|
|
115
|
+
pressed = null
|
|
116
|
+
releaseFrame = undefined
|
|
117
|
+
animate(targets, from, widths, false, timing)
|
|
118
|
+
const returning = animations
|
|
119
|
+
Promise.allSettled(returning.map((animation) => animation.finished)).then(() => {
|
|
120
|
+
if (animations !== returning) return
|
|
121
|
+
animations = []
|
|
122
|
+
moving = false
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
releaseFrame = requestAnimationFrame(back)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
onMount(() => stop)
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<div
|
|
132
|
+
{...attributes}
|
|
133
|
+
role={attributes.role ?? 'group'}
|
|
134
|
+
bind:this={element}
|
|
135
|
+
class={['np-button-group', variant, moving && 'moving', attributes.class]}
|
|
136
|
+
onpointerdown={(event) => {
|
|
137
|
+
press(event.target)
|
|
138
|
+
attributes.onpointerdown?.(event)
|
|
139
|
+
}}
|
|
140
|
+
onpointerup={(event) => {
|
|
141
|
+
release()
|
|
142
|
+
attributes.onpointerup?.(event)
|
|
143
|
+
}}
|
|
144
|
+
onpointercancel={(event) => {
|
|
145
|
+
release()
|
|
146
|
+
attributes.onpointercancel?.(event)
|
|
147
|
+
}}
|
|
148
|
+
onpointerleave={(event) => {
|
|
149
|
+
release()
|
|
150
|
+
attributes.onpointerleave?.(event)
|
|
151
|
+
}}
|
|
152
|
+
onkeydown={(event) => {
|
|
153
|
+
if (!event.repeat && (event.key === ' ' || event.key === 'Enter')) {
|
|
154
|
+
press(event.target)
|
|
155
|
+
}
|
|
156
|
+
attributes.onkeydown?.(event)
|
|
157
|
+
}}
|
|
158
|
+
onkeyup={(event) => {
|
|
159
|
+
release()
|
|
160
|
+
attributes.onkeyup?.(event)
|
|
161
|
+
}}
|
|
162
|
+
onfocusout={(event) => {
|
|
163
|
+
if (!element?.contains(event.relatedTarget as Node)) release()
|
|
164
|
+
attributes.onfocusout?.(event)
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
{@render children?.()}
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<style>
|
|
171
|
+
.np-button-group {
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: flex-start;
|
|
174
|
+
gap: var(--np-button-group-space, 0.75rem);
|
|
175
|
+
isolation: isolate;
|
|
176
|
+
}
|
|
177
|
+
.connected {
|
|
178
|
+
gap: var(--np-button-group-space, 0.125rem);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.np-button-group > :global(:focus-visible) {
|
|
182
|
+
z-index: 1;
|
|
183
|
+
}
|
|
184
|
+
.moving {
|
|
185
|
+
--_button-label-overflow: visible;
|
|
186
|
+
--_button-label-text-overflow: clip;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.connected > :global(:not([popover]):has(~ :not([popover]))) {
|
|
190
|
+
border-start-end-radius: var(--np-button-group-inner-corner, var(--np-shape-corner-small));
|
|
191
|
+
border-end-end-radius: var(--np-button-group-inner-corner, var(--np-shape-corner-small));
|
|
192
|
+
}
|
|
193
|
+
.connected > :global(:not([popover]) ~ :not([popover])) {
|
|
194
|
+
border-start-start-radius: var(--np-button-group-inner-corner, var(--np-shape-corner-small));
|
|
195
|
+
border-end-start-radius: var(--np-button-group-inner-corner, var(--np-shape-corner-small));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
199
|
+
.connected > :global(:not([popover]):has(~ :not([popover])):is(:active, .pressed)) {
|
|
200
|
+
border-start-end-radius: var(
|
|
201
|
+
--np-button-group-pressed-inner-corner,
|
|
202
|
+
var(--np-shape-corner-extra-small)
|
|
203
|
+
);
|
|
204
|
+
border-end-end-radius: var(
|
|
205
|
+
--np-button-group-pressed-inner-corner,
|
|
206
|
+
var(--np-shape-corner-extra-small)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
.connected > :global(:not([popover]) ~ :not([popover]):is(:active, .pressed)) {
|
|
210
|
+
border-start-start-radius: var(
|
|
211
|
+
--np-button-group-pressed-inner-corner,
|
|
212
|
+
var(--np-shape-corner-extra-small)
|
|
213
|
+
);
|
|
214
|
+
border-end-start-radius: var(
|
|
215
|
+
--np-button-group-pressed-inner-corner,
|
|
216
|
+
var(--np-shape-corner-extra-small)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.connected > :global(:not([popover]).selected) {
|
|
222
|
+
border-radius: var(--_round-radius, var(--np-shape-corner-full));
|
|
223
|
+
}
|
|
224
|
+
</style>
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
}: IconButtonProps = $props()
|
|
26
26
|
|
|
27
27
|
const uid = $props.id()
|
|
28
|
+
|
|
29
|
+
const isLink = $derived(attributes.href != null && !disabled && !loading)
|
|
28
30
|
const tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
|
|
29
31
|
|
|
30
32
|
let pressed = $state(false)
|
|
@@ -62,7 +64,7 @@
|
|
|
62
64
|
{/if}
|
|
63
65
|
{/snippet}
|
|
64
66
|
|
|
65
|
-
{#if
|
|
67
|
+
{#if isLink}
|
|
66
68
|
<a
|
|
67
69
|
{...attributes}
|
|
68
70
|
onclick={(event) => {
|
|
@@ -161,22 +163,20 @@
|
|
|
161
163
|
width: var(--_icon-size, 1.25rem);
|
|
162
164
|
}
|
|
163
165
|
|
|
166
|
+
.round {
|
|
167
|
+
border-radius: var(--_round-radius);
|
|
168
|
+
}
|
|
164
169
|
.xs {
|
|
165
170
|
--_icon-size: var(--np-icon-button-icon-size, 1.25rem);
|
|
166
171
|
height: 2rem;
|
|
167
172
|
width: 2rem;
|
|
168
173
|
--np-circular-progress-size: 1.75rem;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
border-radius: 1rem;
|
|
174
|
+
--_round-radius: 1rem;
|
|
175
|
+
--_pressed-radius: 0.5rem;
|
|
172
176
|
}
|
|
173
177
|
.xs.square {
|
|
174
178
|
border-radius: var(--np-icon-button-shape, 0.75rem);
|
|
175
179
|
}
|
|
176
|
-
.xs:active,
|
|
177
|
-
.xs.pressed {
|
|
178
|
-
border-radius: 0.5rem;
|
|
179
|
-
}
|
|
180
180
|
.xs.narrow {
|
|
181
181
|
width: 1.75rem;
|
|
182
182
|
}
|
|
@@ -188,9 +188,8 @@
|
|
|
188
188
|
height: 2.5rem;
|
|
189
189
|
width: 2.5rem;
|
|
190
190
|
--np-circular-progress-size: 2rem;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
border-radius: 1.25rem;
|
|
191
|
+
--_round-radius: 1.25rem;
|
|
192
|
+
--_pressed-radius: 0.5rem;
|
|
194
193
|
}
|
|
195
194
|
.s.square {
|
|
196
195
|
border-radius: var(--np-icon-button-shape, 0.75rem);
|
|
@@ -201,18 +200,13 @@
|
|
|
201
200
|
.s.wide {
|
|
202
201
|
width: 3.25rem;
|
|
203
202
|
}
|
|
204
|
-
.s:active,
|
|
205
|
-
.s.pressed {
|
|
206
|
-
border-radius: 0.5rem;
|
|
207
|
-
}
|
|
208
203
|
.m {
|
|
209
204
|
--_icon-size: var(--np-icon-button-icon-size, 1.5rem);
|
|
210
205
|
height: 3.5rem;
|
|
211
206
|
width: 3.5rem;
|
|
212
207
|
--np-circular-progress-size: 3rem;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
border-radius: 1.75rem;
|
|
208
|
+
--_round-radius: 1.75rem;
|
|
209
|
+
--_pressed-radius: 0.75rem;
|
|
216
210
|
}
|
|
217
211
|
.m.square {
|
|
218
212
|
border-radius: var(--np-icon-button-shape, 1rem);
|
|
@@ -223,18 +217,13 @@
|
|
|
223
217
|
.m.wide {
|
|
224
218
|
width: 4.5rem;
|
|
225
219
|
}
|
|
226
|
-
.m:active,
|
|
227
|
-
.m.pressed {
|
|
228
|
-
border-radius: 0.75rem;
|
|
229
|
-
}
|
|
230
220
|
.l {
|
|
231
221
|
--_icon-size: var(--np-icon-button-icon-size, 2rem);
|
|
232
222
|
height: 6rem;
|
|
233
223
|
width: 6rem;
|
|
234
224
|
--np-circular-progress-size: 5rem;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
border-radius: 3rem;
|
|
225
|
+
--_round-radius: 3rem;
|
|
226
|
+
--_pressed-radius: 1rem;
|
|
238
227
|
}
|
|
239
228
|
.l.square {
|
|
240
229
|
border-radius: var(--np-icon-button-shape, 1.75rem);
|
|
@@ -245,18 +234,13 @@
|
|
|
245
234
|
.l.wide {
|
|
246
235
|
width: 8rem;
|
|
247
236
|
}
|
|
248
|
-
.l:active,
|
|
249
|
-
.l.pressed {
|
|
250
|
-
border-radius: 1rem;
|
|
251
|
-
}
|
|
252
237
|
.xl {
|
|
253
238
|
--_icon-size: var(--np-icon-button-icon-size, 2.5rem);
|
|
254
239
|
height: 8.5rem;
|
|
255
240
|
width: 8.5rem;
|
|
256
241
|
--np-circular-progress-size: 7rem;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
border-radius: 4.25rem;
|
|
242
|
+
--_round-radius: 4.25rem;
|
|
243
|
+
--_pressed-radius: 1rem;
|
|
260
244
|
}
|
|
261
245
|
.xl.square {
|
|
262
246
|
border-radius: var(--np-icon-button-shape, 1.75rem);
|
|
@@ -267,9 +251,10 @@
|
|
|
267
251
|
.xl.wide {
|
|
268
252
|
width: 11.5rem;
|
|
269
253
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
254
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
255
|
+
.np-icon-button:is(:active, .pressed) {
|
|
256
|
+
border-radius: var(--_pressed-radius);
|
|
257
|
+
}
|
|
273
258
|
}
|
|
274
259
|
|
|
275
260
|
.toggle {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const expandedWidths = (widths, index, expandedRatio, compressionLimit) => {
|
|
2
|
+
const next = [...widths];
|
|
3
|
+
if (widths.length < 2 || index < 0 || index >= widths.length || expandedRatio <= 0)
|
|
4
|
+
return next;
|
|
5
|
+
const neighbours = [index - 1, index + 1].filter((i) => i >= 0 && i < widths.length);
|
|
6
|
+
const target = Math.round(Math.min((expandedRatio * widths[index]) / neighbours.length, compressionLimit));
|
|
7
|
+
for (const neighbour of neighbours) {
|
|
8
|
+
const growth = Math.min(target, widths[neighbour]);
|
|
9
|
+
next[neighbour] -= growth;
|
|
10
|
+
next[index] += growth;
|
|
11
|
+
}
|
|
12
|
+
return next;
|
|
13
|
+
};
|
|
14
|
+
export const parseMotion = (token) => {
|
|
15
|
+
const [time, ...rest] = token.trim().split(/\s+/);
|
|
16
|
+
const value = Number.parseFloat(time);
|
|
17
|
+
const easing = rest.join(' ');
|
|
18
|
+
return {
|
|
19
|
+
duration: Number.isNaN(value) ? 0 : time.endsWith('ms') ? value : value * 1000,
|
|
20
|
+
easing: easing || 'linear',
|
|
21
|
+
};
|
|
22
|
+
};
|
package/dist/button/index.d.ts
CHANGED
package/dist/button/index.js
CHANGED
package/dist/button/types.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ export interface IconButtonProps extends BaseButtonProps {
|
|
|
22
22
|
selectedIcon?: Snippet;
|
|
23
23
|
width?: 'narrow' | 'wide' | 'default';
|
|
24
24
|
}
|
|
25
|
+
export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
26
|
+
variant?: 'standard' | 'connected';
|
|
27
|
+
expandedRatio?: number;
|
|
28
|
+
compressionLimit?: number;
|
|
29
|
+
element?: HTMLElement;
|
|
30
|
+
children?: Snippet;
|
|
31
|
+
}
|
|
25
32
|
export interface SegmentedButtonProps extends HTMLAttributes<HTMLDivElement> {
|
|
26
33
|
name: string;
|
|
27
34
|
multiSelect?: boolean;
|
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
import type { MenuItemProps } from './types.js'
|
|
4
4
|
|
|
5
5
|
let { ...attributes }: MenuItemProps = $props()
|
|
6
|
+
|
|
7
|
+
const isLink = $derived(attributes.href != null)
|
|
6
8
|
</script>
|
|
7
9
|
|
|
8
10
|
<div class="np-menu-item" role="none">
|
|
9
|
-
{
|
|
10
|
-
<Item role="menuitem" {...attributes} variant="link" />
|
|
11
|
-
{:else}
|
|
12
|
-
<Item role="menuitem" {...attributes} variant="button" />
|
|
13
|
-
{/if}
|
|
11
|
+
<Item role="menuitem" {...attributes} variant={isLink ? 'link' : 'button'} />
|
|
14
12
|
</div>
|
|
15
13
|
|
|
16
14
|
<style>
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
icon,
|
|
12
12
|
...attributes
|
|
13
13
|
}: NavigationDrawerItemProps = $props()
|
|
14
|
+
|
|
15
|
+
const isLink = $derived(attributes.href != null)
|
|
14
16
|
</script>
|
|
15
17
|
|
|
16
18
|
{#snippet content()}
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
{/if}
|
|
27
29
|
{/snippet}
|
|
28
30
|
|
|
29
|
-
{#if
|
|
31
|
+
{#if isLink}
|
|
30
32
|
<a
|
|
31
33
|
{...attributes}
|
|
32
34
|
href={attributes.href}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
let { selected, icon, label, ...attributes }: NavigationRailItemProps = $props()
|
|
8
8
|
let touchEl: HTMLSpanElement | undefined = $state()
|
|
9
|
+
|
|
10
|
+
const isLink = $derived(attributes.href != null)
|
|
9
11
|
</script>
|
|
10
12
|
|
|
11
13
|
{#snippet content()}
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
<span class="np-touch" bind:this={touchEl}></span>
|
|
18
20
|
{/snippet}
|
|
19
21
|
|
|
20
|
-
{#if
|
|
22
|
+
{#if isLink}
|
|
21
23
|
<a
|
|
22
24
|
{...attributes}
|
|
23
25
|
href={attributes.href}
|