polymorph-ui-components 0.7.0 → 0.9.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 +4 -3
- package/dist/Button/Button.svelte +5 -5
- package/dist/Carousel/Carousel.svelte +266 -145
- package/dist/Carousel/properties.d.ts +14 -2
- package/dist/Combobox/Combobox.svelte.d.ts +1 -1
- package/dist/CommandMenu/CommandMenu.svelte +3 -3
- package/dist/Gallery/Gallery.svelte +10 -10
- package/dist/Gallery/Gallery.svelte.d.ts +1 -1
- package/dist/Input/Input.svelte +12 -3
- package/dist/Input/properties.d.ts +2 -0
- package/dist/InputButton/InputButton.svelte +13 -5
- package/dist/Loader/Loader.svelte +20 -4
- package/dist/Loader/properties.d.ts +1 -0
- package/dist/Modal/Modal.svelte +4 -5
- package/dist/NumberStepper/NumberStepper.svelte +154 -0
- package/dist/NumberStepper/NumberStepper.svelte.d.ts +4 -0
- package/dist/NumberStepper/properties.d.ts +22 -0
- package/dist/NumberStepper/properties.js +1 -0
- package/dist/Pill/Pill.svelte +2 -1
- package/dist/Pill/properties.d.ts +1 -0
- package/dist/Progress/Progress.svelte +146 -8
- package/dist/Progress/properties.d.ts +9 -0
- package/dist/Select/Select.svelte +11 -1
- package/dist/Select/properties.d.ts +1 -0
- package/dist/Sheet/Sheet.svelte +96 -40
- package/dist/Sheet/properties.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/utils.d.ts +20 -0
- package/dist/utils.js +107 -0
- package/dist-wc/index.js +4085 -3323
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,6 +224,7 @@ Now you can ask: _"Using the polymorph-ui MCP, theme the Select to match my bran
|
|
|
224
224
|
| **SplitInput** | Segmented input (OTP / PIN) with paste distribution and auto-advance. | [docs](docs/SplitInput.md) |
|
|
225
225
|
| **ColorPicker** | HSV color picker with pointer-drag saturation panel and hue slider. | [docs](docs/ColorPicker.md) |
|
|
226
226
|
| **Calendar** | Date / range picker with roving-tabindex grid and `Intl` formatting. | [docs](docs/Calendar.md) |
|
|
227
|
+
| **NumberStepper** | − value + control for quantities; shows a loader while an async `onchange` settles. | [docs](docs/NumberStepper.md) |
|
|
227
228
|
|
|
228
229
|
</details>
|
|
229
230
|
|
|
@@ -233,7 +234,7 @@ Now you can ask: _"Using the polymorph-ui MCP, theme the Select to match my bran
|
|
|
233
234
|
| Component | Description | |
|
|
234
235
|
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------ |
|
|
235
236
|
| **Modal** | Dialog overlay with size/alignment, header, footer, transitions, scroll-lock, and back-press support. | [docs](docs/Modal.md) |
|
|
236
|
-
| **Sheet** | Slide-in panel from any edge with header, scrollable body, footer, and focus
|
|
237
|
+
| **Sheet** | Slide-in panel from any edge with header, scrollable body, footer, focus trap and focus return. | [docs](docs/Sheet.md) |
|
|
237
238
|
| **Menu** | Dropdown action menu with keyboard navigation, typeahead, disabled/danger items, and separators. | [docs](docs/Menu.md) |
|
|
238
239
|
| **ContextMenu** | Right-click menu with separators, disabled/danger items, and keyboard navigation. | [docs](docs/ContextMenu.md) |
|
|
239
240
|
| **CommandMenu** | Command palette (Ctrl/Cmd+K) with search, grouped commands, and keyboard navigation. | [docs](docs/CommandMenu.md) |
|
|
@@ -268,7 +269,7 @@ Now you can ask: _"Using the polymorph-ui MCP, theme the Select to match my bran
|
|
|
268
269
|
| **Pagination** | Windowed page navigation with ellipsis truncation. | [docs](docs/Pagination.md) |
|
|
269
270
|
| **Stepper** / **Step** | Multi-step progress indicator with completed/active/pending states. | [docs](docs/Stepper.md) |
|
|
270
271
|
| **Accordion** | Collapsible container with CSS grid animation. | [docs](docs/Accordion.md) |
|
|
271
|
-
| **Carousel** | Swipeable
|
|
272
|
+
| **Carousel** | Swipeable slider with snippet or component slides, arrows, dots, seamless looping and autoplay that pauses on hover/focus. | [docs](docs/Carousel.md) |
|
|
272
273
|
| **Scroller** | Overflowing list with arrow nav, gradient edges, and drag-to-scroll. | [docs](docs/Scroller.md) |
|
|
273
274
|
| **CheckListItem** | Checklist row with checkbox, label, and checked state. | [docs](docs/CheckListItem.md) |
|
|
274
275
|
| **Toolbar** | Header bar with back button, title, and customizable content areas. | [docs](docs/Toolbar.md) |
|
|
@@ -283,7 +284,7 @@ Now you can ask: _"Using the polymorph-ui MCP, theme the Select to match my bran
|
|
|
283
284
|
| ------------------------------------------ | ----------------------------------------------------------------------- | ------------------------ |
|
|
284
285
|
| **Toast** | Animated slide-in notification with auto-dismiss and per-direction fly. | [docs](docs/Toast.md) |
|
|
285
286
|
| **Banner** | Notification banner with icon snippet, link text, and dismiss. | [docs](docs/Banner.md) |
|
|
286
|
-
| **Progress** | Animated
|
|
287
|
+
| **Progress** | Animated progress bar (determinate / indeterminate) with optional milestones. | [docs](docs/Progress.md) |
|
|
287
288
|
| **Gauge** | Full-circle ring gauge (0–100) with animated SVG arc. | [docs](docs/Gauge.md) |
|
|
288
289
|
| **Loader** / **LoadingDots** / **Shimmer** | Spinner, animated dots, and skeleton shimmer — all CSS-variable themed. | [docs](docs/Loader.md) |
|
|
289
290
|
|
|
@@ -110,9 +110,9 @@
|
|
|
110
110
|
cursor: var(--button-disabled-cursor, not-allowed);
|
|
111
111
|
opacity: var(--button-disabled-opacity, 0.4);
|
|
112
112
|
color: var(--button-disabled-text-color, var(--button-text-color, #ffffff));
|
|
113
|
-
font-size: var(--button-disabled-font-size);
|
|
114
|
-
font-weight: var(--button-disabled-font-weight);
|
|
115
|
-
border: var(--button-disabled-border);
|
|
113
|
+
font-size: var(--button-disabled-font-size, var(--button-font-size, 14px));
|
|
114
|
+
font-weight: var(--button-disabled-font-weight, var(--button-font-weight, 500));
|
|
115
|
+
border: var(--button-disabled-border, var(--button-border, none));
|
|
116
116
|
background: var(--button-disabled-background-color, var(--button-color, #18181b));
|
|
117
117
|
text-decoration: var(--button-disabled-text-decoration, var(--button-text-decoration, none));
|
|
118
118
|
}
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
display: var(--button-text-display);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
button:hover {
|
|
134
|
+
button:hover:not(:disabled) {
|
|
135
135
|
background: var(--button-hover-color, var(--button-color, #18181b));
|
|
136
136
|
color: var(--button-hover-text-color, var(--button-text-color, #ffffff));
|
|
137
137
|
border: var(--button-hover-border, var(--button-border, none));
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
opacity: var(--button-hover-opacity, var(--button-opacity, 1));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
button:active {
|
|
142
|
+
button:active:not(:disabled) {
|
|
143
143
|
transform: var(--button-active-transform);
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -1,180 +1,298 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { flushSync } from 'svelte';
|
|
3
3
|
import type { CarouselProperties } from './properties';
|
|
4
|
+
import Button from '../Button/Button.svelte';
|
|
5
|
+
import chevronLeftSvg from '../assets/chevron-left.svg?raw';
|
|
6
|
+
import chevronRightSvg from '../assets/chevron-right.svg?raw';
|
|
7
|
+
|
|
8
|
+
type Rotation = { enabled: boolean; interval: number; restarts: number };
|
|
4
9
|
|
|
5
10
|
let {
|
|
6
|
-
views,
|
|
11
|
+
views = [],
|
|
12
|
+
count = 0,
|
|
13
|
+
slide,
|
|
7
14
|
autoplay = false,
|
|
8
15
|
autoplayInterval = 1000,
|
|
9
16
|
showDots = false,
|
|
17
|
+
showArrows = false,
|
|
10
18
|
isScrollableLast = false,
|
|
19
|
+
loop = false,
|
|
20
|
+
announce = true,
|
|
21
|
+
ariaLabel = 'Carousel',
|
|
22
|
+
previousLabel = 'Previous slide',
|
|
23
|
+
nextLabel = 'Next slide',
|
|
24
|
+
previousIcon,
|
|
25
|
+
nextIcon,
|
|
11
26
|
testId,
|
|
12
27
|
onkeydown,
|
|
13
28
|
classes
|
|
14
29
|
}: CarouselProperties = $props();
|
|
15
30
|
|
|
16
31
|
let slidesDiv: HTMLDivElement | null = $state(null);
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
let
|
|
21
|
-
let
|
|
22
|
-
let
|
|
23
|
-
let carouselDiv: HTMLDivElement | null = $state(null);
|
|
24
|
-
let activeSlideIndex = $state(0);
|
|
25
|
-
let widthUnits: string;
|
|
32
|
+
let requestedIndex = $state(0);
|
|
33
|
+
let edge = $state<'none' | 'before' | 'after'>('none');
|
|
34
|
+
let hovered = $state(false);
|
|
35
|
+
let focused = $state(false);
|
|
36
|
+
let animate = $state(true);
|
|
37
|
+
let restarts = $state(0);
|
|
26
38
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
let total = $derived(typeof slide === 'function' ? count : views.length);
|
|
40
|
+
let activeSlideIndex = $derived(Math.min(requestedIndex, Math.max(total - 1, 0)));
|
|
41
|
+
let wraps = $derived(isScrollableLast || loop);
|
|
42
|
+
let seamless = $derived(loop && total > 1);
|
|
43
|
+
let trackPosition = $derived.by(() => {
|
|
44
|
+
if (!seamless) {
|
|
45
|
+
return activeSlideIndex;
|
|
31
46
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function nextSlide() {
|
|
36
|
-
if (activeSlideIndex != views.length - 1 || isScrollableLast) {
|
|
37
|
-
activeSlideIndex++;
|
|
38
|
-
changeCurrentSlide();
|
|
39
|
-
if (autoplay) {
|
|
40
|
-
resetInterval();
|
|
41
|
-
}
|
|
47
|
+
if (edge === 'before') {
|
|
48
|
+
return 0;
|
|
42
49
|
}
|
|
50
|
+
if (edge === 'after') {
|
|
51
|
+
return total + 1;
|
|
52
|
+
}
|
|
53
|
+
return activeSlideIndex + 1;
|
|
54
|
+
});
|
|
55
|
+
let rotating = $derived(autoplay && total > 1 && !hovered && !focused);
|
|
56
|
+
|
|
57
|
+
function hasTransition(): boolean {
|
|
58
|
+
return slidesDiv !== null && parseFloat(getComputedStyle(slidesDiv).transitionDuration) > 0;
|
|
43
59
|
}
|
|
44
60
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
function snapFromClone() {
|
|
62
|
+
animate = false;
|
|
63
|
+
edge = 'none';
|
|
64
|
+
flushSync();
|
|
65
|
+
// Reading layout commits the jump before the transition is switched back on.
|
|
66
|
+
if (slidesDiv !== null) {
|
|
67
|
+
void slidesDiv.offsetWidth;
|
|
52
68
|
}
|
|
69
|
+
animate = true;
|
|
53
70
|
}
|
|
54
71
|
|
|
55
|
-
function
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
72
|
+
function moveTo(target: number) {
|
|
73
|
+
if (edge !== 'none') {
|
|
74
|
+
snapFromClone();
|
|
75
|
+
}
|
|
76
|
+
if (wraps) {
|
|
77
|
+
requestedIndex = ((target % total) + total) % total;
|
|
78
|
+
edge = seamless && target < 0 ? 'before' : seamless && target >= total ? 'after' : 'none';
|
|
79
|
+
} else {
|
|
80
|
+
requestedIndex = Math.min(total - 1, Math.max(0, target));
|
|
60
81
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}${widthUnits})`;
|
|
82
|
+
flushSync();
|
|
83
|
+
if (edge !== 'none' && !hasTransition()) {
|
|
84
|
+
snapFromClone();
|
|
65
85
|
}
|
|
66
86
|
}
|
|
67
87
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
88
|
+
function navigate(target: number) {
|
|
89
|
+
moveTo(target);
|
|
90
|
+
restarts += 1;
|
|
71
91
|
}
|
|
72
92
|
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
function handleTransitionEnd(event: TransitionEvent) {
|
|
94
|
+
if (event.target === slidesDiv && event.propertyName === 'transform' && edge !== 'none') {
|
|
95
|
+
snapFromClone();
|
|
96
|
+
}
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
function
|
|
79
|
-
if (event.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
startTouch = touch.clientX;
|
|
83
|
-
}
|
|
99
|
+
function handleDotKeydown(event: KeyboardEvent, index: number) {
|
|
100
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
navigate(index);
|
|
84
103
|
}
|
|
104
|
+
onkeydown?.(event);
|
|
85
105
|
}
|
|
86
106
|
|
|
87
|
-
function
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
if (changedTouch !== null) {
|
|
91
|
-
endTouch = changedTouch.clientX;
|
|
92
|
-
if (startTouch - endTouch > 20) {
|
|
93
|
-
nextSlide();
|
|
94
|
-
} else {
|
|
95
|
-
if (endTouch - startTouch > 20) {
|
|
96
|
-
previousSlide();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
107
|
+
function nextSlide() {
|
|
108
|
+
if (activeSlideIndex !== total - 1 || wraps) {
|
|
109
|
+
navigate(activeSlideIndex + 1);
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
function
|
|
104
|
-
if (
|
|
105
|
-
|
|
113
|
+
function previousSlide() {
|
|
114
|
+
if (activeSlideIndex !== 0 || wraps) {
|
|
115
|
+
navigate(activeSlideIndex - 1);
|
|
106
116
|
}
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
function
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
if (startMouse - endMouse > 20) {
|
|
113
|
-
nextSlide();
|
|
114
|
-
} else {
|
|
115
|
-
if (endMouse - startMouse > 20) {
|
|
116
|
-
previousSlide();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
+
function advance() {
|
|
120
|
+
if (rotating) {
|
|
121
|
+
moveTo(activeSlideIndex + 1);
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
function rotation(_node: HTMLElement, initial: Rotation) {
|
|
126
|
+
let timer: number | null = null;
|
|
127
|
+
const stop = () => {
|
|
128
|
+
if (timer !== null) {
|
|
129
|
+
clearInterval(timer);
|
|
130
|
+
timer = null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const start = (settings: Rotation) => {
|
|
134
|
+
stop();
|
|
135
|
+
if (settings.enabled) {
|
|
136
|
+
timer = window.setInterval(advance, settings.interval);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
start(initial);
|
|
140
|
+
return { update: start, destroy: stop };
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
function swipe(node: HTMLElement) {
|
|
144
|
+
let startX = 0;
|
|
145
|
+
const finish = (endX: number) => {
|
|
146
|
+
if (startX - endX > 20) {
|
|
147
|
+
nextSlide();
|
|
148
|
+
} else if (endX - startX > 20) {
|
|
149
|
+
previousSlide();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const touchStart = (event: TouchEvent) => {
|
|
153
|
+
const touch = event.touches.item(0);
|
|
154
|
+
if (touch !== null) {
|
|
155
|
+
startX = touch.clientX;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const touchEnd = (event: TouchEvent) => {
|
|
159
|
+
const touch = event.changedTouches.item(0);
|
|
160
|
+
if (touch !== null) {
|
|
161
|
+
finish(touch.clientX);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const mouseDown = (event: MouseEvent) => {
|
|
165
|
+
startX = event.clientX;
|
|
166
|
+
};
|
|
167
|
+
const mouseUp = (event: MouseEvent) => {
|
|
168
|
+
finish(event.clientX);
|
|
169
|
+
};
|
|
170
|
+
node.addEventListener('touchstart', touchStart);
|
|
171
|
+
node.addEventListener('touchend', touchEnd);
|
|
172
|
+
node.addEventListener('mousedown', mouseDown);
|
|
173
|
+
node.addEventListener('mouseup', mouseUp);
|
|
174
|
+
return {
|
|
175
|
+
destroy() {
|
|
176
|
+
node.removeEventListener('touchstart', touchStart);
|
|
177
|
+
node.removeEventListener('touchend', touchEnd);
|
|
178
|
+
node.removeEventListener('mousedown', mouseDown);
|
|
179
|
+
node.removeEventListener('mouseup', mouseUp);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
142
183
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
184
|
+
function handleFocusOut(event: FocusEvent) {
|
|
185
|
+
const container = event.currentTarget;
|
|
186
|
+
if (
|
|
187
|
+
container instanceof Node &&
|
|
188
|
+
event.relatedTarget instanceof Node &&
|
|
189
|
+
container.contains(event.relatedTarget)
|
|
190
|
+
) {
|
|
191
|
+
return;
|
|
146
192
|
}
|
|
147
|
-
|
|
193
|
+
focused = false;
|
|
194
|
+
}
|
|
148
195
|
</script>
|
|
149
196
|
|
|
150
|
-
|
|
151
|
-
{#if
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
197
|
+
{#snippet renderSlide(index: number)}
|
|
198
|
+
{#if typeof slide === 'function'}
|
|
199
|
+
{@render slide(index)}
|
|
200
|
+
{:else}
|
|
201
|
+
{@const view = views.at(index)}
|
|
202
|
+
{#if typeof view === 'object'}
|
|
203
|
+
{#if typeof view.properties === 'object'}
|
|
204
|
+
<view.component properties={view.properties} />
|
|
205
|
+
{:else}
|
|
206
|
+
<view.component />
|
|
207
|
+
{/if}
|
|
208
|
+
{/if}
|
|
209
|
+
{/if}
|
|
210
|
+
{/snippet}
|
|
211
|
+
|
|
212
|
+
<div
|
|
213
|
+
class="carousel-container {classes ?? ''}"
|
|
214
|
+
role="region"
|
|
215
|
+
aria-roledescription="carousel"
|
|
216
|
+
aria-label={ariaLabel}
|
|
217
|
+
data-pw={testId}
|
|
218
|
+
use:rotation={{ enabled: autoplay && total > 1, interval: autoplayInterval, restarts }}
|
|
219
|
+
onmouseenter={() => (hovered = true)}
|
|
220
|
+
onmouseleave={() => (hovered = false)}
|
|
221
|
+
onfocusin={() => (focused = true)}
|
|
222
|
+
onfocusout={handleFocusOut}
|
|
223
|
+
>
|
|
224
|
+
{#if total > 0}
|
|
225
|
+
<div class="carousel-row">
|
|
226
|
+
{#if showArrows && total > 1}
|
|
227
|
+
<div class="carousel-arrow">
|
|
228
|
+
<Button ariaLabel={previousLabel} onclick={previousSlide}>
|
|
229
|
+
{#if typeof previousIcon === 'function'}
|
|
230
|
+
{@render previousIcon()}
|
|
158
231
|
{:else}
|
|
159
|
-
|
|
232
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
233
|
+
{@html chevronLeftSvg}
|
|
160
234
|
{/if}
|
|
161
|
-
</
|
|
162
|
-
|
|
235
|
+
</Button>
|
|
236
|
+
</div>
|
|
237
|
+
{/if}
|
|
238
|
+
<div class="carousel" use:swipe>
|
|
239
|
+
<div
|
|
240
|
+
class="slidesDiv"
|
|
241
|
+
class:animate
|
|
242
|
+
bind:this={slidesDiv}
|
|
243
|
+
style:transform="translateX({-trackPosition * 100}%)"
|
|
244
|
+
aria-live={announce && !rotating ? 'polite' : 'off'}
|
|
245
|
+
ontransitionend={handleTransitionEnd}
|
|
246
|
+
>
|
|
247
|
+
{#if seamless}
|
|
248
|
+
<div class="current-slide" aria-hidden="true" inert>
|
|
249
|
+
{@render renderSlide(total - 1)}
|
|
250
|
+
</div>
|
|
251
|
+
{/if}
|
|
252
|
+
{#each { length: total } as _, index (index)}
|
|
253
|
+
<div
|
|
254
|
+
class="current-slide"
|
|
255
|
+
role="group"
|
|
256
|
+
aria-roledescription="slide"
|
|
257
|
+
aria-label="{index + 1} of {total}"
|
|
258
|
+
aria-hidden={index !== activeSlideIndex}
|
|
259
|
+
inert={index !== activeSlideIndex}
|
|
260
|
+
>
|
|
261
|
+
{@render renderSlide(index)}
|
|
262
|
+
</div>
|
|
263
|
+
{/each}
|
|
264
|
+
{#if seamless}
|
|
265
|
+
<div class="current-slide" aria-hidden="true" inert>
|
|
266
|
+
{@render renderSlide(0)}
|
|
267
|
+
</div>
|
|
268
|
+
{/if}
|
|
269
|
+
</div>
|
|
163
270
|
</div>
|
|
271
|
+
{#if showArrows && total > 1}
|
|
272
|
+
<div class="carousel-arrow">
|
|
273
|
+
<Button ariaLabel={nextLabel} onclick={nextSlide}>
|
|
274
|
+
{#if typeof nextIcon === 'function'}
|
|
275
|
+
{@render nextIcon()}
|
|
276
|
+
{:else}
|
|
277
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
278
|
+
{@html chevronRightSvg}
|
|
279
|
+
{/if}
|
|
280
|
+
</Button>
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
164
283
|
</div>
|
|
165
284
|
{/if}
|
|
166
285
|
{#if showDots}
|
|
167
286
|
<div class="dots-wrapper">
|
|
168
|
-
|
|
169
|
-
{#each views as _, index}
|
|
287
|
+
{#each { length: total } as _, index (index)}
|
|
170
288
|
<div
|
|
171
|
-
class={activeSlideIndex
|
|
172
|
-
onclick={() =>
|
|
289
|
+
class={activeSlideIndex === index ? 'active-dot' : 'dot'}
|
|
290
|
+
onclick={() => navigate(index)}
|
|
173
291
|
onkeydown={(event) => handleDotKeydown(event, index)}
|
|
174
292
|
role="button"
|
|
175
293
|
tabindex="0"
|
|
176
294
|
aria-label={`Go to slide ${index + 1}`}
|
|
177
|
-
aria-current={activeSlideIndex
|
|
295
|
+
aria-current={activeSlideIndex === index}
|
|
178
296
|
></div>
|
|
179
297
|
{/each}
|
|
180
298
|
</div>
|
|
@@ -185,15 +303,21 @@
|
|
|
185
303
|
.carousel-container {
|
|
186
304
|
width: var(--carousel-width);
|
|
187
305
|
}
|
|
306
|
+
.carousel-row {
|
|
307
|
+
display: flex;
|
|
308
|
+
align-items: center;
|
|
309
|
+
gap: var(--carousel-arrow-gap, 8px);
|
|
310
|
+
}
|
|
188
311
|
.current-slide {
|
|
189
|
-
width:
|
|
312
|
+
width: 100%;
|
|
190
313
|
height: var(--carousel-height, 100px);
|
|
191
314
|
flex-shrink: 0;
|
|
192
315
|
}
|
|
193
316
|
.carousel {
|
|
194
317
|
box-shadow: var(--carousel-shadow);
|
|
195
318
|
height: var(--carousel-height, 100px);
|
|
196
|
-
|
|
319
|
+
flex: 0 1 var(--carousel-width, 300px);
|
|
320
|
+
min-width: 0;
|
|
197
321
|
overflow: hidden;
|
|
198
322
|
border-radius: var(--carousel-border-radius, 0%);
|
|
199
323
|
}
|
|
@@ -202,8 +326,24 @@
|
|
|
202
326
|
}
|
|
203
327
|
.slidesDiv {
|
|
204
328
|
display: flex;
|
|
205
|
-
|
|
206
|
-
|
|
329
|
+
}
|
|
330
|
+
.slidesDiv.animate {
|
|
331
|
+
transition: transform var(--carousel-transition-duration, 0.5s)
|
|
332
|
+
var(--carousel-transition-easing, ease-in-out);
|
|
333
|
+
}
|
|
334
|
+
.carousel-arrow {
|
|
335
|
+
--button-color: var(--carousel-arrow-background, transparent);
|
|
336
|
+
--button-text-color: var(--carousel-arrow-color, currentColor);
|
|
337
|
+
--button-hover-color: var(--carousel-arrow-hover-background, transparent);
|
|
338
|
+
--button-padding: var(--carousel-arrow-padding, 4px);
|
|
339
|
+
--button-border: var(--carousel-arrow-border, none);
|
|
340
|
+
--button-border-radius: var(--carousel-arrow-border-radius, 999px);
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-shrink: 0;
|
|
343
|
+
}
|
|
344
|
+
.carousel-arrow :global(svg) {
|
|
345
|
+
width: var(--carousel-arrow-size, 16px);
|
|
346
|
+
height: var(--carousel-arrow-size, 16px);
|
|
207
347
|
}
|
|
208
348
|
.dots-wrapper {
|
|
209
349
|
gap: var(--carousel-dot-gap, 10px);
|
|
@@ -228,31 +368,12 @@
|
|
|
228
368
|
background: var(--carousel-active-dot-color, currentColor);
|
|
229
369
|
transition: 0.3s ease;
|
|
230
370
|
}
|
|
231
|
-
/*
|
|
232
|
-
@media only screen and (max-width: 324px) {
|
|
233
371
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
width: 270px;
|
|
240
|
-
}
|
|
241
|
-
.carousel {
|
|
242
|
-
width:270px;
|
|
372
|
+
@media (prefers-reduced-motion: reduce) {
|
|
373
|
+
.slidesDiv.animate,
|
|
374
|
+
.dot,
|
|
375
|
+
.active-dot {
|
|
376
|
+
transition-duration: 0s;
|
|
243
377
|
}
|
|
244
378
|
}
|
|
245
|
-
|
|
246
|
-
@media only screen and (max-width: 269px) {
|
|
247
|
-
.carousel-container {
|
|
248
|
-
width:240px;
|
|
249
|
-
}
|
|
250
|
-
.current-slide {
|
|
251
|
-
width: 240px;
|
|
252
|
-
}
|
|
253
|
-
.carousel {
|
|
254
|
-
width:240px;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
} */
|
|
258
379
|
</style>
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
import type { Component } from 'svelte';
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
2
|
export type CarouselView = {
|
|
3
3
|
properties?: Record<string, unknown>;
|
|
4
4
|
component: Component<Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
|
-
export type CarouselProperties = CarouselEventProperties & OptionalCarouselProperties
|
|
6
|
+
export type CarouselProperties = CarouselEventProperties & OptionalCarouselProperties;
|
|
7
|
+
/** @deprecated `views` is optional now that slides can also come from the `slide` snippet. */
|
|
7
8
|
export type MandatoryCarouselProperties = {
|
|
8
9
|
views: CarouselView[];
|
|
9
10
|
};
|
|
10
11
|
export type OptionalCarouselProperties = {
|
|
12
|
+
views?: CarouselView[];
|
|
13
|
+
count?: number;
|
|
14
|
+
slide?: Snippet<[number]>;
|
|
11
15
|
autoplay?: boolean;
|
|
12
16
|
autoplayInterval?: number;
|
|
13
17
|
showDots?: boolean;
|
|
18
|
+
showArrows?: boolean;
|
|
14
19
|
isScrollableLast?: boolean;
|
|
20
|
+
loop?: boolean;
|
|
21
|
+
announce?: boolean;
|
|
22
|
+
ariaLabel?: string;
|
|
23
|
+
previousLabel?: string;
|
|
24
|
+
nextLabel?: string;
|
|
25
|
+
previousIcon?: Snippet;
|
|
26
|
+
nextIcon?: Snippet;
|
|
15
27
|
testId?: string;
|
|
16
28
|
classes?: string;
|
|
17
29
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComboboxProperties } from './properties';
|
|
2
2
|
declare const Combobox: import("svelte").Component<ComboboxProperties, {
|
|
3
3
|
getInputRef: () => HTMLInputElement | HTMLTextAreaElement | null;
|
|
4
|
-
}, "
|
|
4
|
+
}, "open" | "value" | "inputValue" | "highlightedIndex">;
|
|
5
5
|
type Combobox = ReturnType<typeof Combobox>;
|
|
6
6
|
export default Combobox;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { SvelteMap } from 'svelte/reactivity';
|
|
5
5
|
import Img from '../Img/Img.svelte';
|
|
6
6
|
import searchSvg from '../assets/search.svg?raw';
|
|
7
|
+
import { lockDocumentScroll } from '../utils';
|
|
7
8
|
|
|
8
9
|
let {
|
|
9
10
|
items,
|
|
@@ -162,7 +163,7 @@
|
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
function scrollLockAction(_node: HTMLElement) {
|
|
165
|
-
|
|
166
|
+
const unlockScroll = lockDocumentScroll();
|
|
166
167
|
tick().then(() => {
|
|
167
168
|
if (inputElement !== null) {
|
|
168
169
|
inputElement.focus();
|
|
@@ -170,7 +171,7 @@
|
|
|
170
171
|
});
|
|
171
172
|
return {
|
|
172
173
|
destroy() {
|
|
173
|
-
|
|
174
|
+
unlockScroll();
|
|
174
175
|
}
|
|
175
176
|
};
|
|
176
177
|
}
|
|
@@ -181,7 +182,6 @@
|
|
|
181
182
|
|
|
182
183
|
onDestroy(() => {
|
|
183
184
|
if (typeof window !== 'undefined') {
|
|
184
|
-
document.body.style.overflow = '';
|
|
185
185
|
window.removeEventListener('keydown', handleGlobalKeyDown);
|
|
186
186
|
}
|
|
187
187
|
});
|