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.
Files changed (44) hide show
  1. package/dist/badge/Badge.svelte +4 -2
  2. package/dist/badge/Badge.svelte.d.ts +1 -0
  3. package/dist/button/Button.svelte +3 -5
  4. package/dist/button/IconButton.svelte +7 -7
  5. package/dist/button/SegmentedButton.svelte +22 -25
  6. package/dist/card/Card.svelte +11 -35
  7. package/dist/checkbox/Checkbox.svelte +68 -32
  8. package/dist/checkbox/types.d.ts +1 -0
  9. package/dist/chip/ChipSet.svelte +10 -2
  10. package/dist/chip/FilterChip.svelte +48 -52
  11. package/dist/chip/InputChip.svelte +32 -31
  12. package/dist/chip/InputChip.svelte.d.ts +1 -1
  13. package/dist/chip/types.d.ts +3 -1
  14. package/dist/dialog/Dialog.svelte +53 -10
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/keyboard-nav.js +12 -4
  18. package/dist/list/Item.svelte +39 -66
  19. package/dist/menu/Menu.svelte +10 -1
  20. package/dist/menu/MenuItem.svelte +4 -4
  21. package/dist/navigation-drawer/NavigationDrawer.svelte +35 -1
  22. package/dist/navigation-rail/NavigationRailItem.svelte +2 -2
  23. package/dist/progress/CircularProgress.svelte +214 -117
  24. package/dist/progress/LinearProgress.svelte +382 -102
  25. package/dist/progress/types.d.ts +2 -0
  26. package/dist/ripple/Ripple.svelte +6 -7
  27. package/dist/select/Select.svelte +41 -20
  28. package/dist/slider/Slider.svelte +555 -0
  29. package/dist/slider/Slider.svelte.d.ts +4 -0
  30. package/dist/slider/index.d.ts +1 -0
  31. package/dist/slider/index.js +1 -0
  32. package/dist/slider/types.d.ts +23 -0
  33. package/dist/slider/types.js +1 -0
  34. package/dist/snackbar/Snackbar.svelte +21 -10
  35. package/dist/switch/Switch.svelte +1 -1
  36. package/dist/tabs/Tab.svelte +18 -16
  37. package/dist/tabs/Tabs.svelte +4 -2
  38. package/dist/tabs/types.d.ts +2 -0
  39. package/dist/text-field/TextField.svelte +2 -0
  40. package/dist/themes/defaultTheme.css +3 -9
  41. package/dist/tooltip/Tooltip.svelte +53 -10
  42. package/dist/types.d.ts +1 -0
  43. package/dist/types.js +1 -0
  44. package/package.json +11 -11
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MediaQuery } from 'svelte/reactivity'
2
3
  import type { CircularProgressProps } from './types.ts'
3
4
 
4
5
  let {
@@ -6,33 +7,108 @@
6
7
  max = 1,
7
8
  indeterminate = false,
8
9
  fourColor = false,
10
+ track = true,
11
+ wavy = false,
9
12
  ...attributes
10
13
  }: CircularProgressProps = $props()
14
+
15
+ const WAVE_AMPLITUDE_DP = 1.6
16
+ const WAVE_WAVELENGTH_DP = 15
17
+
18
+ const VIEWBOX = 4800
19
+ const DEFAULT_STROKE_PERCENT = 8.3333
20
+ const BOX_DP = 48
21
+ const unitsPerDp = VIEWBOX / BOX_DP
22
+ const ampUnits = WAVE_AMPLITUDE_DP * unitsPerDp
23
+ const ringRadius = (strokePercent: number) =>
24
+ VIEWBOX / 2 - ((strokePercent / 100) * VIEWBOX) / 2 - ampUnits
25
+ const ringWaveCount = (radius: number) =>
26
+ Math.max(1, Math.round((2 * Math.PI * radius) / (WAVE_WAVELENGTH_DP * unitsPerDp)))
27
+
28
+ const buildWavePath = (phase: number, strokePercent: number) => {
29
+ const radius = ringRadius(strokePercent)
30
+ const count = ringWaveCount(radius)
31
+ const c = VIEWBOX / 2
32
+ const steps = count * 24
33
+ let d = ''
34
+ for (let i = 0; i <= steps; i++) {
35
+ const t = (i / steps) * Math.PI * 2
36
+ const r = radius + ampUnits * Math.cos(count * t - phase)
37
+ const x = (c + r * Math.cos(t)).toFixed(2)
38
+ const y = (c + r * Math.sin(t)).toFixed(2)
39
+ d += (i === 0 ? 'M' : 'L') + x + ',' + y
40
+ }
41
+ return d + 'Z'
42
+ }
43
+
44
+ let waveEl = $state<SVGPathElement>()
45
+ const wavePath = buildWavePath(0, DEFAULT_STROKE_PERCENT)
46
+
47
+ const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)', false)
48
+ let wave = $derived(wavy && !reducedMotion.current)
49
+
50
+ $effect(() => {
51
+ if (!wave || !waveEl) return
52
+ const path = waveEl
53
+ const strokePercent =
54
+ parseFloat(
55
+ getComputedStyle(path).getPropertyValue('--np-circular-progress-active-indicator-width'),
56
+ ) || DEFAULT_STROKE_PERCENT
57
+ let raf = 0
58
+ const frame = (now: number) => {
59
+ raf = requestAnimationFrame(frame)
60
+ path.setAttribute('d', buildWavePath(((now / 1000) % 1) * Math.PI * 2, strokePercent))
61
+ }
62
+ raf = requestAnimationFrame(frame)
63
+ return () => cancelAnimationFrame(raf)
64
+ })
65
+
66
+ const clamp = (n: number) => Math.min(100, Math.max(0, n))
67
+ let activeLength = $derived(clamp((value / max) * 100))
68
+ let activeStyle = $derived(`--_active:${activeLength}`)
11
69
  </script>
12
70
 
13
71
  <div class="np-container">
14
72
  <div
15
73
  {...attributes}
16
- class={['progress', indeterminate && 'indeterminate', fourColor && 'four-color']}
74
+ class={[
75
+ 'progress',
76
+ indeterminate && 'indeterminate',
77
+ fourColor && 'four-color',
78
+ wave && 'wavy',
79
+ ]}
80
+ style={activeStyle}
17
81
  role="progressbar"
18
82
  aria-valuemin="0"
19
83
  aria-valuemax={max}
20
84
  aria-valuenow={indeterminate ? undefined : value}
21
85
  >
22
86
  {#if indeterminate}
23
- <div class="spinner">
24
- <div class="left">
25
- <div class="circle"></div>
26
- </div>
27
- <div class="right">
28
- <div class="circle"></div>
29
- </div>
30
- </div>
87
+ <svg class="spinner" viewBox="0 0 4800 4800">
88
+ {#if track}
89
+ <circle class="track spinner-track" pathLength="100"></circle>
90
+ {/if}
91
+ {#if wave}
92
+ <path
93
+ bind:this={waveEl}
94
+ class="active-track spinner-arc wave"
95
+ d={wavePath}
96
+ pathLength="100"
97
+ ></path>
98
+ {:else}
99
+ <circle class="active-track spinner-arc" pathLength="100"></circle>
100
+ {/if}
101
+ </svg>
31
102
  {:else}
32
103
  <svg viewBox="0 0 4800 4800">
33
- <circle class="track" pathLength="100"></circle>
34
- <circle class="active-track" pathLength="100" stroke-dashoffset={(1 - value / max) * 100}
35
- ></circle>
104
+ {#if track}
105
+ <circle class="track" pathLength="100"></circle>
106
+ {/if}
107
+ {#if wave}
108
+ <path bind:this={waveEl} class="active-track wave" d={wavePath} pathLength="100"></path>
109
+ {:else}
110
+ <circle class="active-track" pathLength="100"></circle>
111
+ {/if}
36
112
  </svg>
37
113
  {/if}
38
114
  </div>
@@ -57,11 +133,12 @@
57
133
  align-self: stretch;
58
134
  margin: 0.25rem;
59
135
  }
136
+
137
+ .progress.wavy {
138
+ margin: 0;
139
+ }
60
140
  .progress,
61
141
  .spinner,
62
- .left,
63
- .right,
64
- .circle,
65
142
  svg,
66
143
  .track,
67
144
  .active-track {
@@ -71,164 +148,184 @@
71
148
 
72
149
  svg {
73
150
  transform: rotate(-90deg);
151
+ transform-origin: 50% 50%;
74
152
  }
75
- circle {
76
- cx: 50%;
77
- cy: 50%;
78
- r: calc(50% * (1 - var(--np-circular-progress-active-indicator-width, 8.3333) / 100));
153
+ circle,
154
+ .wave {
79
155
  stroke-width: calc(var(--np-circular-progress-active-indicator-width, 8.3333) * 1%);
80
156
  stroke-dasharray: 100;
81
157
  fill: transparent;
82
158
  }
83
159
 
84
- .active-track {
85
- transition: stroke-dashoffset 500ms cubic-bezier(0, 0, 0.2, 1);
86
- stroke: var(--np-circular-progress-color, var(--np-color-primary));
160
+ circle {
161
+ cx: 50%;
162
+ cy: 50%;
163
+ r: calc(50% * (1 - var(--np-circular-progress-active-indicator-width, 8.3333) / 100));
164
+ transform-box: view-box;
165
+ transform-origin: 50% 50%;
87
166
  }
88
167
 
89
- .track {
90
- stroke: transparent;
168
+ .progress {
169
+ --_stroke: var(--np-circular-progress-active-indicator-width, 8.3333);
170
+ --_gap: calc((0.1 + var(--_stroke) / 100) / (3.14159265 * (1 - var(--_stroke) / 100)) * 100);
91
171
  }
92
172
 
93
- .progress.indeterminate {
94
- animation: linear infinite linear-rotate;
95
- animation-duration: calc(1333ms * 360 / 306);
173
+ .wavy .track {
174
+ r: calc(50% * (1 - var(--_stroke) / 100 - 0.0666667));
96
175
  }
97
176
 
98
- .spinner {
99
- animation: infinite both rotate-arc;
100
- animation-duration: calc(4 * 1333ms);
101
- animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
177
+ .progress.wavy {
178
+ --_gap: calc(
179
+ (0.0833333 + var(--_stroke) / 100) / (3.14159265 * (1 - var(--_stroke) / 100 - 0.0666667)) *
180
+ 100
181
+ );
102
182
  }
103
183
 
104
- .left {
105
- overflow: hidden;
106
- inset: 0 50% 0 0;
184
+ .progress:not(.indeterminate) .active-track {
185
+ stroke-dashoffset: calc((100 - var(--_active)) * 1px);
107
186
  }
108
187
 
109
- .right {
110
- overflow: hidden;
111
- inset: 0 0 0 50%;
188
+ .progress:not(.indeterminate) .track {
189
+ --_g: min(var(--_active), var(--_gap));
190
+ stroke-dasharray: calc(max(0, 100 - var(--_active) - 2 * var(--_g)) * 1px) 100px;
191
+ stroke-dashoffset: calc((var(--_active) + var(--_g)) * -1px);
112
192
  }
113
193
 
114
- .circle {
115
- box-sizing: border-box;
116
- border-radius: 50%;
117
- border: solid
118
- calc(
119
- calc(var(--np-circular-progress-active-indicator-width, 8.3333) / 100) *
120
- calc(var(--np-circular-progress-size, 3rem) - 2 * 0.25rem)
121
- );
122
- border-color: var(--np-circular-progress-color, var(--np-color-primary))
123
- var(--np-circular-progress-color, var(--np-color-primary)) transparent transparent;
124
- animation: expand-arc;
125
- animation-iteration-count: infinite;
126
- animation-fill-mode: both;
127
- animation-duration: 1333ms, calc(4 * 1333ms);
128
- animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
194
+ .active-track,
195
+ .track {
196
+ stroke-linecap: round;
129
197
  }
130
198
 
131
- .four-color .circle {
132
- animation-name: expand-arc, four-color;
199
+ .active-track {
200
+ transition: stroke-dashoffset var(--np-motion-expressive-slow-effects);
201
+ stroke: var(--np-circular-progress-color, var(--np-color-primary));
133
202
  }
134
203
 
135
- .left .circle {
136
- rotate: 135deg;
137
- inset: 0 -100% 0 0;
204
+ .track {
205
+ stroke: var(--np-circular-progress-track-color, var(--np-color-secondary-container));
206
+ transition:
207
+ stroke-dasharray var(--np-motion-expressive-slow-effects),
208
+ stroke-dashoffset var(--np-motion-expressive-slow-effects);
138
209
  }
139
- .right .circle {
140
- rotate: 100deg;
141
- inset: 0 0 0 -100%;
142
- animation-delay: calc(-0.5 * 1333ms), 0ms;
210
+
211
+ .progress.indeterminate {
212
+ animation: cp-global-rotation 6000ms linear infinite;
143
213
  }
144
214
 
145
- @media (forced-colors: active) {
146
- .active-track {
147
- stroke: CanvasText;
215
+ @keyframes cp-global-rotation {
216
+ to {
217
+ rotate: 1080deg;
148
218
  }
219
+ }
149
220
 
150
- .circle {
151
- border-color: CanvasText CanvasText Canvas Canvas;
152
- }
221
+ .spinner {
222
+ animation: cp-step-rotation 6000ms linear infinite;
153
223
  }
154
- @keyframes expand-arc {
224
+
225
+ @keyframes cp-step-rotation {
155
226
  0% {
156
- transform: rotate(265deg);
227
+ rotate: 0deg;
157
228
  }
158
- 50% {
159
- transform: rotate(130deg);
160
- }
161
- 100% {
162
- transform: rotate(265deg);
163
- }
164
- }
165
- @keyframes rotate-arc {
166
- 12.5% {
167
- transform: rotate(135deg);
229
+ 5% {
230
+ rotate: 90deg;
168
231
  }
169
232
  25% {
170
- transform: rotate(270deg);
233
+ rotate: 90deg;
171
234
  }
172
- 37.5% {
173
- transform: rotate(405deg);
235
+ 30% {
236
+ rotate: 180deg;
174
237
  }
175
238
  50% {
176
- transform: rotate(540deg);
239
+ rotate: 180deg;
177
240
  }
178
- 62.5% {
179
- transform: rotate(675deg);
241
+ 55% {
242
+ rotate: 270deg;
180
243
  }
181
244
  75% {
182
- transform: rotate(810deg);
245
+ rotate: 270deg;
183
246
  }
184
- 87.5% {
185
- transform: rotate(945deg);
247
+ 80% {
248
+ rotate: 360deg;
186
249
  }
187
250
  100% {
188
- transform: rotate(1080deg);
251
+ rotate: 360deg;
189
252
  }
190
253
  }
191
- @keyframes linear-rotate {
192
- to {
193
- transform: rotate(360deg);
194
- }
254
+
255
+ .spinner-arc {
256
+ transition: none;
257
+ animation: cp-arc-sweep 6000ms infinite;
195
258
  }
196
- @keyframes four-color {
259
+
260
+ @keyframes cp-arc-sweep {
197
261
  0% {
198
- border-top-color: var(--np-color-primary);
199
- border-right-color: var(--np-color-primary);
262
+ stroke-dasharray: 10px 100px;
263
+ animation-timing-function: linear;
200
264
  }
201
- 15% {
202
- border-top-color: var(--np-color-primary);
203
- border-right-color: var(--np-color-primary);
265
+ 50% {
266
+ stroke-dasharray: calc((100 - 2 * var(--_gap)) * 1px) 100px;
267
+ animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
204
268
  }
205
- 25% {
206
- border-top-color: var(--np-color-primary-container);
207
- border-right-color: var(--np-color-primary-container);
269
+ 100% {
270
+ stroke-dasharray: 10px 100px;
208
271
  }
209
- 40% {
210
- border-top-color: var(--np-color-primary-container);
211
- border-right-color: var(--np-color-primary-container);
272
+ }
273
+
274
+ .spinner-track {
275
+ transition: none;
276
+ animation: cp-track-sweep 6000ms infinite;
277
+ }
278
+
279
+ @keyframes cp-track-sweep {
280
+ 0% {
281
+ stroke-dasharray: calc((90 - 2 * var(--_gap)) * 1px) 100px;
282
+ rotate: calc((10 + var(--_gap)) * 3.6deg);
283
+ animation-timing-function: linear;
212
284
  }
213
285
  50% {
214
- border-top-color: var(--np-color-tertiary);
215
- border-right-color: var(--np-color-tertiary);
286
+ stroke-dasharray: 0px 100px;
287
+ rotate: calc((100 - var(--_gap)) * 3.6deg);
288
+ animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
216
289
  }
217
- 65% {
218
- border-top-color: var(--np-color-tertiary);
219
- border-right-color: var(--np-color-tertiary);
290
+ 100% {
291
+ stroke-dasharray: calc((90 - 2 * var(--_gap)) * 1px) 100px;
292
+ rotate: calc((10 + var(--_gap)) * 3.6deg);
220
293
  }
221
- 75% {
222
- border-top-color: var(--np-color-tertiary-container);
223
- border-right-color: var(--np-color-tertiary-container);
294
+ }
295
+
296
+ .four-color .spinner-arc {
297
+ animation-name: cp-arc-sweep, four-color;
298
+ animation-duration: 6000ms, calc(4 * 1333ms);
299
+ }
300
+
301
+ @media (forced-colors: active) {
302
+ .active-track {
303
+ stroke: CanvasText;
304
+ }
305
+
306
+ .spinner-arc {
307
+ stroke: CanvasText;
308
+ }
309
+ }
310
+ @keyframes four-color {
311
+ 0%,
312
+ 15% {
313
+ stroke: var(--np-color-primary);
314
+ }
315
+ 25%,
316
+ 40% {
317
+ stroke: var(--np-color-primary-container);
318
+ }
319
+ 50%,
320
+ 65% {
321
+ stroke: var(--np-color-tertiary);
224
322
  }
323
+ 75%,
225
324
  90% {
226
- border-top-color: var(--np-color-tertiary-container);
227
- border-right-color: var(--np-color-tertiary-container);
325
+ stroke: var(--np-color-tertiary-container);
228
326
  }
229
327
  100% {
230
- border-top-color: var(--np-color-primary);
231
- border-right-color: var(--np-color-primary);
328
+ stroke: var(--np-color-primary);
232
329
  }
233
330
  }
234
331
  </style>