noph-ui 0.46.1 → 0.48.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 (74) hide show
  1. package/README.md +3 -1
  2. package/dist/button/ButtonGroup.svelte +1 -3
  3. package/dist/carousel/Carousel.svelte +410 -0
  4. package/dist/carousel/Carousel.svelte.d.ts +4 -0
  5. package/dist/carousel/CarouselItem.svelte +300 -0
  6. package/dist/carousel/CarouselItem.svelte.d.ts +5 -0
  7. package/dist/carousel/carouselMorph.d.ts +13 -0
  8. package/dist/carousel/carouselMorph.js +100 -0
  9. package/dist/carousel/carouselStrategy.d.ts +61 -0
  10. package/dist/carousel/carouselStrategy.js +344 -0
  11. package/dist/carousel/context.d.ts +2 -0
  12. package/dist/carousel/context.js +2 -0
  13. package/dist/carousel/index.d.ts +3 -0
  14. package/dist/carousel/index.js +3 -0
  15. package/dist/carousel/types.d.ts +31 -0
  16. package/dist/carousel/types.js +1 -0
  17. package/dist/date-picker/DockedDatePicker.svelte +2 -3
  18. package/dist/date-picker/DockedDateTimePicker.svelte +2 -3
  19. package/dist/date-picker/dateUtils.d.ts +2 -13
  20. package/dist/date-picker/dateUtils.js +4 -66
  21. package/dist/date-picker/formatters.d.ts +4 -0
  22. package/dist/date-picker/formatters.js +26 -0
  23. package/dist/date-picker/timeUtils.d.ts +34 -0
  24. package/dist/date-picker/timeUtils.js +139 -0
  25. package/dist/fab/FabMenu.svelte +1 -2
  26. package/dist/form-reset.d.ts +2 -1
  27. package/dist/form-reset.js +2 -2
  28. package/dist/icons/KeyboardIcon.svelte +5 -0
  29. package/dist/icons/KeyboardIcon.svelte.d.ts +26 -0
  30. package/dist/icons/ScheduleIcon.svelte +5 -0
  31. package/dist/icons/ScheduleIcon.svelte.d.ts +26 -0
  32. package/dist/icons/index.d.ts +2 -0
  33. package/dist/icons/index.js +2 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +2 -0
  36. package/dist/keyboard-nav.d.ts +3 -1
  37. package/dist/keyboard-nav.js +9 -4
  38. package/dist/media.d.ts +4 -0
  39. package/dist/media.js +4 -0
  40. package/dist/menu/Menu.svelte +2 -1
  41. package/dist/progress/CircularProgress.svelte +9 -9
  42. package/dist/progress/LinearProgress.svelte +1 -2
  43. package/dist/ripple/Ripple.svelte +7 -8
  44. package/dist/search/Search.svelte +7 -6
  45. package/dist/select/Select.svelte +9 -10
  46. package/dist/select/VirtualList.svelte +12 -13
  47. package/dist/slider/Slider.svelte +5 -6
  48. package/dist/tabs/Tabs.svelte +21 -14
  49. package/dist/time-picker/ClockDial.svelte +528 -0
  50. package/dist/time-picker/ClockDial.svelte.d.ts +4 -0
  51. package/dist/time-picker/DockedTimePicker.svelte +378 -0
  52. package/dist/time-picker/DockedTimePicker.svelte.d.ts +7 -0
  53. package/dist/time-picker/PeriodSelector.svelte +156 -0
  54. package/dist/time-picker/PeriodSelector.svelte.d.ts +4 -0
  55. package/dist/time-picker/TimeInputs.svelte +233 -0
  56. package/dist/time-picker/TimeInputs.svelte.d.ts +4 -0
  57. package/dist/time-picker/TimePickerDialog.svelte +229 -0
  58. package/dist/time-picker/TimePickerDialog.svelte.d.ts +7 -0
  59. package/dist/time-picker/TimePickerPanel.svelte +203 -0
  60. package/dist/time-picker/TimePickerPanel.svelte.d.ts +4 -0
  61. package/dist/time-picker/TimeSelectors.svelte +157 -0
  62. package/dist/time-picker/TimeSelectors.svelte.d.ts +4 -0
  63. package/dist/time-picker/index.d.ts +4 -0
  64. package/dist/time-picker/index.js +4 -0
  65. package/dist/time-picker/timePickerState.svelte.d.ts +27 -0
  66. package/dist/time-picker/timePickerState.svelte.js +63 -0
  67. package/dist/time-picker/types.d.ts +148 -0
  68. package/dist/time-picker/types.js +1 -0
  69. package/dist/tooltip/Tooltip.svelte +2 -4
  70. package/dist/types.d.ts +2 -0
  71. package/dist/types.js +2 -0
  72. package/dist/validity.d.ts +2 -0
  73. package/dist/validity.js +4 -0
  74. package/package.json +1 -1
@@ -0,0 +1,300 @@
1
+ <script lang="ts">
2
+ import '../internal/focus-ring.css'
3
+ import { pressMorph } from '../press.svelte.js'
4
+ import Ripple from '../ripple/Ripple.svelte'
5
+ import { minVisibleFraction } from './carouselStrategy.js'
6
+ import { getCarouselContext } from './context.js'
7
+ import type { CarouselItemElement, CarouselItemProps } from './types.js'
8
+
9
+ let {
10
+ type,
11
+ label,
12
+ image,
13
+ aspectRatio,
14
+ disabled = false,
15
+ children,
16
+ element = $bindable(),
17
+ onpointerdown,
18
+ style,
19
+ ...attributes
20
+ }: CarouselItemProps = $props()
21
+
22
+ const carouselContext = getCarouselContext()
23
+ const press = pressMorph()
24
+
25
+ const total = $derived(carouselContext.items.length)
26
+ const position = $derived(element ? carouselContext.position(element) : 0)
27
+
28
+ const itemStyle = $derived(
29
+ [
30
+ `--_visible:${minVisibleFraction(aspectRatio)}`,
31
+ aspectRatio != null && `--_aspect:${aspectRatio}`,
32
+ style,
33
+ ]
34
+ .filter(Boolean)
35
+ .join(';'),
36
+ )
37
+
38
+ const accessibleName = $derived(
39
+ attributes['aria-label'] ??
40
+ (type !== 'text' && label && position > 0
41
+ ? carouselContext.itemLabel(label, position, total)
42
+ : undefined),
43
+ )
44
+
45
+ const labelHidden = $derived(accessibleName !== undefined)
46
+
47
+ const handlePointerdown = (
48
+ event: PointerEvent & { currentTarget: EventTarget & CarouselItemElement },
49
+ ) => {
50
+ onpointerdown?.(event)
51
+ if (!disabled && type !== 'text') press.press()
52
+ }
53
+ </script>
54
+
55
+ {#snippet content()}
56
+ {#if image}
57
+ <div class="np-carousel-item-image" style="background-image: url({image})"></div>
58
+ {/if}
59
+ {#if children}
60
+ {@render children()}
61
+ {/if}
62
+ {#if label}
63
+ <span class="np-carousel-item-label" aria-hidden={labelHidden || undefined}>{label}</span>
64
+ {/if}
65
+ {#if !disabled && type !== 'text'}
66
+ <Ripple />
67
+ {/if}
68
+ {/snippet}
69
+
70
+ {#if type === 'text'}
71
+ <div
72
+ {...attributes}
73
+ bind:this={element}
74
+ aria-disabled={disabled}
75
+ style={itemStyle}
76
+ class={[
77
+ 'np-carousel-item',
78
+ 'np-carousel-item-plain',
79
+ aspectRatio != null && 'np-carousel-item-aspect',
80
+ disabled && 'np-carousel-item-disabled',
81
+ attributes.class,
82
+ ]}
83
+ >
84
+ {@render content()}
85
+ </div>
86
+ {:else if type === 'button'}
87
+ <button
88
+ {...attributes}
89
+ bind:this={element}
90
+ type="button"
91
+ {disabled}
92
+ aria-disabled={disabled}
93
+ aria-label={accessibleName}
94
+ onpointerdown={handlePointerdown}
95
+ style={itemStyle}
96
+ class={[
97
+ 'np-carousel-item',
98
+ press.pressed && 'np-carousel-item-pressed',
99
+ aspectRatio != null && 'np-carousel-item-aspect',
100
+ disabled && 'np-carousel-item-disabled',
101
+ attributes.class,
102
+ ]}
103
+ >
104
+ {@render content()}
105
+ </button>
106
+ {:else if type === 'link'}
107
+ <a
108
+ {...attributes}
109
+ bind:this={element}
110
+ role="link"
111
+ aria-disabled={disabled}
112
+ aria-label={accessibleName}
113
+ tabindex={disabled ? -1 : undefined}
114
+ onpointerdown={handlePointerdown}
115
+ style={itemStyle}
116
+ class={[
117
+ 'np-carousel-item',
118
+ press.pressed && 'np-carousel-item-pressed',
119
+ aspectRatio != null && 'np-carousel-item-aspect',
120
+ disabled && 'np-carousel-item-disabled',
121
+ attributes.class,
122
+ ]}
123
+ >
124
+ {@render content()}
125
+ </a>
126
+ {/if}
127
+
128
+ <style>
129
+ .np-carousel-item {
130
+ position: relative;
131
+ box-sizing: border-box;
132
+ flex: none;
133
+ display: block;
134
+ margin: 0;
135
+ padding: 0;
136
+ border: none;
137
+ font: inherit;
138
+ color: var(--np-color-on-surface);
139
+ text-align: start;
140
+ text-decoration: none;
141
+ overflow: clip;
142
+ cursor: pointer;
143
+ scroll-snap-stop: var(--np-carousel-snap-stop, normal);
144
+ -webkit-tap-highlight-color: transparent;
145
+ inline-size: var(--np-carousel-item-width, 12.5rem);
146
+ block-size: var(--np-carousel-item-height, auto);
147
+ align-self: stretch;
148
+ border-radius: var(--np-carousel-item-container-shape, var(--np-shape-corner-extra-large));
149
+ box-shadow: var(--np-carousel-item-elevation, none);
150
+ background-color: var(--np-carousel-item-container-color, transparent);
151
+ --np-ripple-hover-color: var(--np-carousel-item-state-layer-color, var(--np-color-on-surface));
152
+ --np-ripple-pressed-color: var(
153
+ --np-carousel-item-state-layer-color,
154
+ var(--np-color-on-surface)
155
+ );
156
+ }
157
+
158
+ .np-carousel-item-aspect {
159
+ aspect-ratio: var(--_aspect);
160
+ inline-size: auto;
161
+ block-size: var(--np-carousel-item-height, 12.5rem);
162
+ align-self: center;
163
+ flex: none;
164
+ }
165
+
166
+ .np-carousel-item-plain {
167
+ cursor: unset;
168
+ }
169
+
170
+ .np-carousel-item::after {
171
+ content: '';
172
+ position: absolute;
173
+ inset: 0;
174
+ border-radius: inherit;
175
+ pointer-events: none;
176
+ outline-style: solid;
177
+ outline-width: var(--np-carousel-item-outline-width, 0);
178
+ outline-color: var(--np-carousel-item-outline-color, var(--np-color-outline));
179
+ outline-offset: calc(-1 * var(--np-carousel-item-outline-width, 0px));
180
+ }
181
+
182
+ .np-carousel-item-image {
183
+ position: absolute;
184
+ inset: 0;
185
+ background-position: center;
186
+ background-size: cover;
187
+ background-repeat: no-repeat;
188
+ }
189
+
190
+ .np-carousel-item-image,
191
+ .np-carousel-item > :global(img),
192
+ .np-carousel-item > :global(picture),
193
+ .np-carousel-item > :global(video) {
194
+ translate: var(--np-carousel-parallax, 0);
195
+ }
196
+
197
+ .np-carousel-item > :global(img),
198
+ .np-carousel-item > :global(picture) {
199
+ display: block;
200
+ inline-size: 100%;
201
+ block-size: 100%;
202
+ object-fit: cover;
203
+ }
204
+
205
+ .np-carousel-item-label {
206
+ position: absolute;
207
+ inset-inline: var(--np-carousel-clip, 0px);
208
+ inset-block-end: 0;
209
+ display: block;
210
+ padding: 1rem;
211
+ font-size: 0.875rem;
212
+ line-height: 1.25rem;
213
+ font-weight: 500;
214
+ overflow: hidden;
215
+ text-overflow: ellipsis;
216
+ white-space: nowrap;
217
+ pointer-events: none;
218
+ color: var(
219
+ --np-carousel-item-label-text-color,
220
+ light-dark(var(--np-color-surface), var(--np-color-on-surface))
221
+ );
222
+ background-image: linear-gradient(
223
+ to top,
224
+ var(
225
+ --np-carousel-item-label-scrim-color,
226
+ color-mix(in srgb, var(--np-color-scrim) 60%, transparent)
227
+ ),
228
+ transparent
229
+ );
230
+ }
231
+
232
+ @media (hover: hover) {
233
+ .np-carousel-item:is(button, a):not(.np-carousel-item-disabled):hover {
234
+ box-shadow: var(--np-carousel-item-hover-elevation, var(--np-elevation-1));
235
+ }
236
+ }
237
+
238
+ .np-carousel-item:focus-visible {
239
+ outline: none;
240
+ box-shadow: var(--np-carousel-item-elevation, none);
241
+ }
242
+
243
+ .np-carousel-item:focus-visible::after {
244
+ outline-width: 3px;
245
+ outline-color: var(--np-color-secondary);
246
+ outline-offset: -3px;
247
+ }
248
+
249
+ @media (prefers-reduced-motion: no-preference) {
250
+ .np-carousel-item:focus-visible::after {
251
+ animation: focusAnimationInset var(--np-motion-expressive-slow-effects) forwards;
252
+ }
253
+ }
254
+
255
+ .np-carousel-item:focus-visible :global(.np-ripple-surface)::before {
256
+ opacity: var(--np-ripple-focus-opacity, 0.1);
257
+ }
258
+
259
+ .np-carousel-item:is(:active, .np-carousel-item-pressed) {
260
+ box-shadow: var(--np-carousel-item-elevation, none);
261
+ }
262
+
263
+ @media (prefers-reduced-motion: no-preference) {
264
+ .np-carousel-item {
265
+ transition: border-radius var(--np-motion-expressive-fast-effects);
266
+ }
267
+
268
+ .np-carousel-item:is(button, a):is(:active, .np-carousel-item-pressed) {
269
+ border-radius: var(--np-carousel-item-pressed-container-shape, var(--np-shape-corner-medium));
270
+ }
271
+ }
272
+
273
+ .np-carousel-item-disabled {
274
+ opacity: 0.38;
275
+ cursor: unset;
276
+ pointer-events: none;
277
+ box-shadow: none;
278
+ }
279
+
280
+ .np-carousel-item-disabled::after {
281
+ outline-color: color-mix(in srgb, var(--np-color-outline) 12%, transparent);
282
+ }
283
+
284
+ @media (forced-colors: active) {
285
+ .np-carousel-item::after {
286
+ outline-width: 1px;
287
+ outline-color: CanvasText;
288
+ }
289
+
290
+ .np-carousel-item-disabled::after {
291
+ outline-color: GrayText;
292
+ }
293
+
294
+ .np-carousel-item-label {
295
+ background-image: none;
296
+ background-color: Canvas;
297
+ color: CanvasText;
298
+ }
299
+ }
300
+ </style>
@@ -0,0 +1,5 @@
1
+ import '../internal/focus-ring.css';
2
+ import type { CarouselItemProps } from './types.ts';
3
+ declare const CarouselItem: import("svelte").Component<CarouselItemProps, {}, "element">;
4
+ type CarouselItem = ReturnType<typeof CarouselItem>;
5
+ export default CarouselItem;
@@ -0,0 +1,13 @@
1
+ import type { Attachment } from 'svelte/attachments';
2
+ import type { CarouselAlignment, CarouselItemElement } from './types.ts';
3
+ export interface CarouselMorphOptions {
4
+ variant: 'multi-browse' | 'hero';
5
+ alignment: CarouselAlignment;
6
+ vertical: boolean;
7
+ items: CarouselItemElement[];
8
+ track: HTMLElement | undefined;
9
+ metrics: HTMLElement | undefined;
10
+ uid: string;
11
+ }
12
+ export declare const scrollTimelines: boolean;
13
+ export declare const carouselMorph: (options: CarouselMorphOptions) => Attachment<HTMLElement>;
@@ -0,0 +1,100 @@
1
+ import { arrange, itemKeyframes, morphTrack, notches } from "./carouselStrategy.js";
2
+ export const scrollTimelines = typeof CSS !== 'undefined' && CSS.supports('animation-timeline', 'scroll(nearest inline)');
3
+ const px = (value) => `${Math.round(value * 100) / 100}px`;
4
+ const lengthOf = (value, fallback) => {
5
+ const parsed = Number.parseFloat(value ?? '');
6
+ return Number.isFinite(parsed) ? parsed : fallback;
7
+ };
8
+ export const carouselMorph = (options) => (scroller) => {
9
+ const { items, vertical, track, metrics, uid } = options;
10
+ if (!track || items.length === 0)
11
+ return;
12
+ const name = `np-carousel-morph-${uid}`;
13
+ const sheet = new CSSStyleSheet();
14
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
15
+ const markers = scroller.querySelector('.np-carousel-snap-points')?.children ?? [];
16
+ const itemProbe = metrics?.firstElementChild;
17
+ const smallProbe = metrics?.lastElementChild;
18
+ for (const [index, item] of items.entries()) {
19
+ item.style.setProperty('--_morph', `${name}-${index}`);
20
+ }
21
+ let built;
22
+ const measure = () => {
23
+ const style = getComputedStyle(track);
24
+ const spacing = lengthOf(vertical ? style.rowGap : style.columnGap, 0);
25
+ const lead = lengthOf(vertical ? style.paddingBlockStart : style.paddingInlineStart, 0);
26
+ const trail = lengthOf(vertical ? style.paddingBlockEnd : style.paddingInlineEnd, 0);
27
+ const port = vertical ? scroller.clientHeight : scroller.clientWidth;
28
+ const itemSize = itemProbe ? (vertical ? itemProbe.offsetHeight : itemProbe.offsetWidth) : 0;
29
+ const smallStyle = smallProbe && getComputedStyle(smallProbe);
30
+ const smallMin = lengthOf(vertical ? smallStyle?.minBlockSize : smallStyle?.minInlineSize, 0);
31
+ const smallMax = lengthOf(vertical ? smallStyle?.maxBlockSize : smallStyle?.maxInlineSize, Number.POSITIVE_INFINITY);
32
+ const signature = [port, lead, trail, spacing, itemSize, smallMin, smallMax].join(' ');
33
+ if (signature === built)
34
+ return;
35
+ built = signature;
36
+ const arrangement = arrange({
37
+ variant: options.variant,
38
+ alignment: options.alignment,
39
+ itemCount: items.length,
40
+ availableSpace: Math.max(0, port - lead - trail),
41
+ itemSize,
42
+ spacing,
43
+ smallMin,
44
+ smallMax,
45
+ });
46
+ const morph = morphTrack(arrangement);
47
+ scroller.style.setProperty('--_item', px(arrangement.itemSize));
48
+ scroller.style.setProperty('--_track', px(morph.trackSize));
49
+ const snapAt = notches(morph);
50
+ for (let index = 0; index < markers.length; index += 1) {
51
+ const marker = markers[index];
52
+ const offset = snapAt[index];
53
+ marker.style.setProperty('--_at', offset === undefined ? '' : px(offset));
54
+ marker.toggleAttribute('hidden', offset === undefined);
55
+ }
56
+ if (arrangement.keylines.length === 0 || arrangement.scrollMax <= 0) {
57
+ sheet.replaceSync('');
58
+ return;
59
+ }
60
+ const shape = 'var(--np-carousel-item-container-shape, var(--np-shape-corner-extra-large))';
61
+ const offset = (along) => vertical ? `inset-block-start:${px(along)}` : `inset-inline-start:${px(along)}`;
62
+ const mask = (clip) => vertical ? `inset(${px(clip)} 0 round ${shape})` : `inset(0 ${px(clip)} round ${shape})`;
63
+ const at = (percent) => `${Math.round(percent * 1000) / 1000}%`;
64
+ const blocks = [];
65
+ for (const index of items.keys()) {
66
+ const stops = itemKeyframes(morph, index)
67
+ .map(({ percent, x, clip }) => `${at(percent)}{${offset(x)};clip-path:${mask(clip)};--np-carousel-clip:${px(clip)}}`)
68
+ .join('');
69
+ blocks.push(`@keyframes ${name}-${index}{${stops}}`);
70
+ }
71
+ sheet.replaceSync(blocks.join(''));
72
+ };
73
+ const axisClass = vertical ? 'np-carousel-morphing-block' : 'np-carousel-morphing-inline';
74
+ scroller.classList.add('np-carousel-morphing', axisClass);
75
+ measure();
76
+ let frame = 0;
77
+ const resize = new ResizeObserver(() => {
78
+ if (frame)
79
+ return;
80
+ frame = requestAnimationFrame(() => {
81
+ frame = 0;
82
+ measure();
83
+ });
84
+ });
85
+ resize.observe(scroller);
86
+ if (itemProbe)
87
+ resize.observe(itemProbe);
88
+ return () => {
89
+ if (frame)
90
+ cancelAnimationFrame(frame);
91
+ resize.disconnect();
92
+ document.adoptedStyleSheets = document.adoptedStyleSheets.filter((one) => one !== sheet);
93
+ scroller.classList.remove('np-carousel-morphing', axisClass);
94
+ for (const property of ['--_item', '--_track']) {
95
+ scroller.style.removeProperty(property);
96
+ }
97
+ for (const item of items)
98
+ item.style.removeProperty('--_morph');
99
+ };
100
+ };
@@ -0,0 +1,61 @@
1
+ import type { CarouselAlignment, CarouselVariant } from './types.ts';
2
+ export type CarouselKeylineKind = 'collapsed' | 'small' | 'medium' | 'large';
3
+ export interface CarouselKeyline {
4
+ kind: CarouselKeylineKind;
5
+ size: number;
6
+ loc: number;
7
+ locOffset: number;
8
+ }
9
+ export interface CarouselKeyframe {
10
+ percent: number;
11
+ x: number;
12
+ clip: number;
13
+ }
14
+ export interface CarouselStrategyInput {
15
+ variant: CarouselVariant;
16
+ alignment: CarouselAlignment;
17
+ itemCount: number;
18
+ availableSpace: number;
19
+ itemSize: number;
20
+ spacing: number;
21
+ smallMin: number;
22
+ smallMax: number;
23
+ }
24
+ export interface CarouselArrangement {
25
+ smallCount: number;
26
+ mediumCount: number;
27
+ largeCount: number;
28
+ smallSize: number;
29
+ mediumSize: number;
30
+ largeSize: number;
31
+ stride: number;
32
+ itemSize: number;
33
+ spacing: number;
34
+ focal: number;
35
+ keylines: CarouselKeyline[];
36
+ scrollMax: number;
37
+ trackSize: number;
38
+ }
39
+ export declare const arrange: (input: CarouselStrategyInput) => CarouselArrangement;
40
+ export declare const placeItem: (arrangement: CarouselArrangement, index: number, scroll: number) => {
41
+ x: number;
42
+ clip: number;
43
+ };
44
+ export declare const shiftSteps: (arrangement: CarouselArrangement, towards: "start" | "end") => CarouselArrangement[];
45
+ export interface CarouselTrack {
46
+ arrangement: CarouselArrangement;
47
+ atStart: CarouselArrangement[];
48
+ atEnd: CarouselArrangement[];
49
+ startShift: number;
50
+ endShift: number;
51
+ scrollMax: number;
52
+ trackSize: number;
53
+ }
54
+ export declare const morphTrack: (arrangement: CarouselArrangement) => CarouselTrack;
55
+ export declare const placeItemOnTrack: (track: CarouselTrack, index: number, scroll: number) => {
56
+ x: number;
57
+ clip: number;
58
+ };
59
+ export declare const notches: (track: CarouselTrack) => number[];
60
+ export declare const itemKeyframes: (track: CarouselTrack, index: number, subdivisions?: number) => CarouselKeyframe[];
61
+ export declare const minVisibleFraction: (ratio: number | null | undefined) => number;