@winkintel/bootstrap-svelte 1.0.4 → 1.0.6
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 +1 -1
- package/dist/Alert/alert.svelte +4 -2
- package/dist/Badge/badge.svelte +6 -4
- package/dist/Badge/badge.svelte.d.ts +2 -2
- package/dist/Badge/types.d.ts +2 -1
- package/dist/Badge/types.js +3 -11
- package/dist/Button/button.svelte +29 -9
- package/dist/ButtonGroup/button-group.svelte +3 -2
- package/dist/ButtonGroup/button-toolbar.svelte +3 -2
- package/dist/ButtonGroup/types.contract.d.ts +1 -0
- package/dist/ButtonGroup/types.contract.js +15 -0
- package/dist/ButtonGroup/types.d.ts +2 -2
- package/dist/Carousel/carousel-indicator-button.svelte +8 -1
- package/dist/Carousel/carousel-item.svelte +29 -9
- package/dist/Carousel/carousel.svelte +12 -5
- package/dist/Carousel/carousel.svelte.js +200 -42
- package/dist/Col/col.svelte +30 -22
- package/dist/Collapse/collapse.svelte +3 -3
- package/dist/Collapse/transition.js +78 -15
- package/dist/Collapse/types.d.ts +1 -0
- package/dist/Dropdown/dropdown-item.svelte +7 -0
- package/dist/Dropdown/dropdown.svelte.js +66 -19
- package/dist/Form/form-check-input.svelte +6 -6
- package/dist/Form/form-color-input.svelte +8 -3
- package/dist/Form/form-date-input.svelte +4 -4
- package/dist/Form/form-datetime-local-input.svelte +4 -4
- package/dist/Form/form-email-input.svelte +4 -4
- package/dist/Form/form-file-input.svelte +46 -6
- package/dist/Form/form-file-input.svelte.d.ts +3 -2
- package/dist/Form/form-hidden-input.svelte +1 -1
- package/dist/Form/form-hidden-input.svelte.d.ts +1 -1
- package/dist/Form/form-image-input.svelte +1 -1
- package/dist/Form/form-image-input.svelte.d.ts +2 -1
- package/dist/Form/form-month-input.svelte +4 -4
- package/dist/Form/form-number-input.svelte +4 -4
- package/dist/Form/form-password-input.svelte +4 -4
- package/dist/Form/form-radio-input.svelte +53 -3
- package/dist/Form/form-radio-input.svelte.d.ts +3 -2
- package/dist/Form/form-range-input.svelte +1 -1
- package/dist/Form/form-range-input.svelte.d.ts +2 -1
- package/dist/Form/form-search-input.svelte +4 -4
- package/dist/Form/form-select.svelte +7 -7
- package/dist/Form/form-switch-input.svelte +4 -4
- package/dist/Form/form-telephone-input.svelte +4 -4
- package/dist/Form/form-text-area.svelte +4 -4
- package/dist/Form/form-text-input.svelte +4 -4
- package/dist/Form/form-time-input.svelte +4 -4
- package/dist/Form/form-url-input.svelte +4 -4
- package/dist/Form/form-week-Input.svelte +4 -4
- package/dist/Form/types.d.ts +26 -20
- package/dist/ListGroup/list-group-item-action.svelte +14 -7
- package/dist/Modal/modal-title.svelte +7 -7
- package/dist/Modal/modal.svelte +86 -26
- package/dist/Modal/modal.svelte.js +15 -0
- package/dist/Nav/nav-link.svelte +12 -8
- package/dist/Navbar/navbar-collapse.svelte +18 -7
- package/dist/Navbar/navbar-collapse.svelte.d.ts +1 -1
- package/dist/Navbar/navbar-nav.svelte +1 -1
- package/dist/Navbar/navbar-toggler.svelte +9 -7
- package/dist/Navbar/navbar-toggler.svelte.d.ts +1 -1
- package/dist/Navbar/types.d.ts +3 -2
- package/dist/Offcanvas/offcanvas.svelte +38 -7
- package/dist/Pagination/pagination-item.svelte +27 -20
- package/dist/Pagination/pagination-link.svelte +20 -5
- package/dist/Pagination/pagination.svelte.js +28 -8
- package/dist/Placeholder/placeholder-item.svelte +7 -2
- package/dist/Placeholder/placeholder-item.svelte.d.ts +1 -1
- package/dist/Placeholder/types.d.ts +1 -0
- package/dist/Portal/portal.svelte +6 -1
- package/dist/Progress/progress-bar.svelte +22 -9
- package/dist/Row/row.svelte +9 -10
- package/dist/Row/types.d.ts +8 -7
- package/dist/Scrollspy/scrollspy-attachment.svelte.js +47 -28
- package/dist/Tooltip/tooltip.svelte +57 -10
- package/dist/common/css.js +96 -24
- package/dist/common/navbar-offcanvas.svelte.d.ts +2 -0
- package/dist/common/navbar-offcanvas.svelte.js +5 -4
- package/dist/common/overlay-stack.d.ts +13 -0
- package/dist/common/overlay-stack.js +49 -0
- package/dist/common/scrollbar.js +24 -8
- package/package.json +3 -1
|
@@ -12,11 +12,18 @@ export class CarouselRootState {
|
|
|
12
12
|
static PAUSE_DEFAULT = 'hover';
|
|
13
13
|
static RIDE_DEFAULT = false;
|
|
14
14
|
static TRANSITION_DURATION_DEFAULT = 600;
|
|
15
|
+
static normalizeInterval(value) {
|
|
16
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : CarouselRootState.INTERNAL_DEFAULT;
|
|
17
|
+
}
|
|
18
|
+
static normalizeTransitionDuration(value) {
|
|
19
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : CarouselRootState.TRANSITION_DURATION_DEFAULT;
|
|
20
|
+
}
|
|
15
21
|
// Private state properties...
|
|
16
22
|
#activeItemIndex = $state(0); // Zero-based index for active item
|
|
17
23
|
#animation = $state(CarouselRootState.ANIMATION_DEFAULT);
|
|
18
24
|
#hasUserInteraction = $state(false); // Used to delay autoplay until user interacts when ride=true
|
|
19
|
-
#indicators = [];
|
|
25
|
+
#indicators = $state([]);
|
|
26
|
+
#hasStarted = false;
|
|
20
27
|
#interval = $state(0);
|
|
21
28
|
#timeoutId = $state(null);
|
|
22
29
|
#pendingTimeoutIds = [];
|
|
@@ -25,12 +32,17 @@ export class CarouselRootState {
|
|
|
25
32
|
#isHovered = $state(false);
|
|
26
33
|
#isPaused = $state(true);
|
|
27
34
|
#isPointerDown = $state(false);
|
|
28
|
-
#
|
|
35
|
+
#isTouchGesture = false;
|
|
36
|
+
#items = $state([]);
|
|
29
37
|
#nextActiveItemIndex = $state(-1); // Tracks the index of the slide that will become active (-1 when no transition is pending)
|
|
30
38
|
#pauseInteraction = $state(CarouselRootState.PAUSE_DEFAULT);
|
|
31
39
|
#pointerStartX = $state(0);
|
|
40
|
+
#registrationOrderScheduled = false;
|
|
41
|
+
#resumeTimeoutId = null;
|
|
32
42
|
#ride = $state(false);
|
|
33
43
|
#transitionDuration = $state(CarouselRootState.TRANSITION_DURATION_DEFAULT);
|
|
44
|
+
#transitionGeneration = 0;
|
|
45
|
+
#transitionTimeoutId = null;
|
|
34
46
|
#visibilityListener = null;
|
|
35
47
|
// Public readonly properties...
|
|
36
48
|
isSlideAnimation = $derived.by(() => this.animation === 'slide');
|
|
@@ -40,11 +52,13 @@ export class CarouselRootState {
|
|
|
40
52
|
constructor(props) {
|
|
41
53
|
this.props = props;
|
|
42
54
|
this.#animation = this.props.animation || CarouselRootState.ANIMATION_DEFAULT;
|
|
43
|
-
this.#interval = this.props.interval
|
|
55
|
+
this.#interval = CarouselRootState.normalizeInterval(this.props.interval);
|
|
44
56
|
this.#pauseInteraction = this.props.pause !== undefined ? this.props.pause : CarouselRootState.PAUSE_DEFAULT;
|
|
45
57
|
this.#ride = this.props.ride || CarouselRootState.RIDE_DEFAULT;
|
|
46
|
-
this.#transitionDuration = this.props.transitionDuration
|
|
58
|
+
this.#transitionDuration = CarouselRootState.normalizeTransitionDuration(this.props.transitionDuration);
|
|
47
59
|
this.registerItem = this.registerItem.bind(this);
|
|
60
|
+
this.unregisterItem = this.unregisterItem.bind(this);
|
|
61
|
+
this.unregisterIndicator = this.unregisterIndicator.bind(this);
|
|
48
62
|
this.isItemActive = this.isItemActive.bind(this);
|
|
49
63
|
this.next = this.next.bind(this);
|
|
50
64
|
this.prev = this.prev.bind(this);
|
|
@@ -56,6 +70,7 @@ export class CarouselRootState {
|
|
|
56
70
|
this.handlePointerDown = this.handlePointerDown.bind(this);
|
|
57
71
|
this.handlePointerMove = this.handlePointerMove.bind(this);
|
|
58
72
|
this.handlePointerUp = this.handlePointerUp.bind(this);
|
|
73
|
+
this.handlePointerCancel = this.handlePointerCancel.bind(this);
|
|
59
74
|
// Setup Page Visibility API listener
|
|
60
75
|
if (typeof document !== 'undefined' && 'hidden' in document) {
|
|
61
76
|
this.#visibilityListener = () => {
|
|
@@ -68,11 +83,6 @@ export class CarouselRootState {
|
|
|
68
83
|
};
|
|
69
84
|
document.addEventListener('visibilitychange', this.#visibilityListener);
|
|
70
85
|
}
|
|
71
|
-
// Start autoplay immediately in the browser if ride='carousel', otherwise wait for user interaction.
|
|
72
|
-
// During static prerendering there is no browser timer API, so autoplay starts during hydration instead.
|
|
73
|
-
if (typeof window !== 'undefined' && this.#ride === 'carousel' && !this.#timeoutId) {
|
|
74
|
-
this.cycle();
|
|
75
|
-
}
|
|
76
86
|
}
|
|
77
87
|
// Determines the appropriate interval duration, prioritizing the active item's specific interval
|
|
78
88
|
getCurrentItemInterval() {
|
|
@@ -80,8 +90,10 @@ export class CarouselRootState {
|
|
|
80
90
|
return this.#interval;
|
|
81
91
|
}
|
|
82
92
|
const activeItem = this.#items[this.#activeItemIndex];
|
|
83
|
-
if (activeItem
|
|
84
|
-
return activeItem.interval
|
|
93
|
+
if (activeItem?.interval !== undefined) {
|
|
94
|
+
return typeof activeItem.interval === 'number' && Number.isFinite(activeItem.interval) && activeItem.interval > 0
|
|
95
|
+
? activeItem.interval
|
|
96
|
+
: this.#interval;
|
|
85
97
|
}
|
|
86
98
|
return this.#interval;
|
|
87
99
|
}
|
|
@@ -98,6 +110,7 @@ export class CarouselRootState {
|
|
|
98
110
|
callback();
|
|
99
111
|
}, delay);
|
|
100
112
|
this.#pendingTimeoutIds.push(timeoutId);
|
|
113
|
+
return timeoutId;
|
|
101
114
|
}
|
|
102
115
|
get animation() {
|
|
103
116
|
return this.#animation;
|
|
@@ -115,7 +128,7 @@ export class CarouselRootState {
|
|
|
115
128
|
return this.#interval;
|
|
116
129
|
}
|
|
117
130
|
set interval(interval) {
|
|
118
|
-
this.#interval = interval;
|
|
131
|
+
this.#interval = CarouselRootState.normalizeInterval(interval);
|
|
119
132
|
if (this.shouldCycle()) {
|
|
120
133
|
this.cycle();
|
|
121
134
|
}
|
|
@@ -136,7 +149,7 @@ export class CarouselRootState {
|
|
|
136
149
|
return this.#transitionDuration;
|
|
137
150
|
}
|
|
138
151
|
set transitionDuration(duration) {
|
|
139
|
-
this.#transitionDuration = duration;
|
|
152
|
+
this.#transitionDuration = CarouselRootState.normalizeTransitionDuration(duration);
|
|
140
153
|
}
|
|
141
154
|
get isAnimating() {
|
|
142
155
|
return this.#isAnimating;
|
|
@@ -147,10 +160,82 @@ export class CarouselRootState {
|
|
|
147
160
|
if (item.props.isActive && !this.#isAnimating) {
|
|
148
161
|
this.#activeItemIndex = item.index;
|
|
149
162
|
}
|
|
163
|
+
if (this.#hasStarted && this.#items.length === 2 && this.shouldCycle()) {
|
|
164
|
+
this.cycle();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
unregisterItem(item) {
|
|
168
|
+
const removedIndex = this.#items.indexOf(item);
|
|
169
|
+
if (removedIndex === -1) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (removedIndex === this.#activeItemIndex || removedIndex === this.#nextActiveItemIndex) {
|
|
173
|
+
this.#invalidateTransition();
|
|
174
|
+
}
|
|
175
|
+
this.#items.splice(removedIndex, 1);
|
|
176
|
+
if (this.#items.length === 0) {
|
|
177
|
+
this.#activeItemIndex = 0;
|
|
178
|
+
this.#nextActiveItemIndex = -1;
|
|
179
|
+
this.#isAnimating = false;
|
|
180
|
+
this.pause();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (removedIndex < this.#activeItemIndex) {
|
|
184
|
+
this.#activeItemIndex--;
|
|
185
|
+
}
|
|
186
|
+
else if (removedIndex === this.#activeItemIndex) {
|
|
187
|
+
this.#activeItemIndex = Math.min(removedIndex, this.#items.length - 1);
|
|
188
|
+
}
|
|
189
|
+
if (removedIndex < this.#nextActiveItemIndex) {
|
|
190
|
+
this.#nextActiveItemIndex--;
|
|
191
|
+
}
|
|
192
|
+
else if (removedIndex === this.#nextActiveItemIndex) {
|
|
193
|
+
this.#nextActiveItemIndex = -1;
|
|
194
|
+
}
|
|
150
195
|
}
|
|
151
196
|
registerIndicator(indicator) {
|
|
152
197
|
this.#indicators.push(indicator);
|
|
153
198
|
}
|
|
199
|
+
scheduleRegistrationOrder() {
|
|
200
|
+
if (this.#registrationOrderScheduled)
|
|
201
|
+
return;
|
|
202
|
+
this.#registrationOrderScheduled = true;
|
|
203
|
+
queueMicrotask(() => {
|
|
204
|
+
this.#registrationOrderScheduled = false;
|
|
205
|
+
if (this.#isDisposed)
|
|
206
|
+
return;
|
|
207
|
+
this.#reorderRegistrations();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
unregisterIndicator(indicator) {
|
|
211
|
+
const index = this.#indicators.indexOf(indicator);
|
|
212
|
+
if (index !== -1) {
|
|
213
|
+
this.#indicators.splice(index, 1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
getItemIndex(item) {
|
|
217
|
+
return this.#items.indexOf(item);
|
|
218
|
+
}
|
|
219
|
+
getIndicatorIndex(indicator) {
|
|
220
|
+
return this.#indicators.indexOf(indicator);
|
|
221
|
+
}
|
|
222
|
+
#reorderRegistrations() {
|
|
223
|
+
const activeItem = this.#items[this.#activeItemIndex];
|
|
224
|
+
const nextActiveItem = this.#items[this.#nextActiveItemIndex];
|
|
225
|
+
const compareElementOrder = (first, second) => {
|
|
226
|
+
const firstElement = first.props.orderElementRef ?? first.props.elementRef;
|
|
227
|
+
const secondElement = second.props.orderElementRef ?? second.props.elementRef;
|
|
228
|
+
if (!firstElement || !secondElement || firstElement === secondElement) {
|
|
229
|
+
return firstElement ? -1 : secondElement ? 1 : 0;
|
|
230
|
+
}
|
|
231
|
+
const position = firstElement.compareDocumentPosition(secondElement);
|
|
232
|
+
return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : position & Node.DOCUMENT_POSITION_PRECEDING ? 1 : 0;
|
|
233
|
+
};
|
|
234
|
+
this.#items.sort(compareElementOrder);
|
|
235
|
+
this.#indicators.sort(compareElementOrder);
|
|
236
|
+
this.#activeItemIndex = activeItem ? this.#items.indexOf(activeItem) : Math.min(this.#activeItemIndex, this.#items.length - 1);
|
|
237
|
+
this.#nextActiveItemIndex = nextActiveItem ? this.#items.indexOf(nextActiveItem) : -1;
|
|
238
|
+
}
|
|
154
239
|
isItemActive(index) {
|
|
155
240
|
return this.#activeItemIndex === index;
|
|
156
241
|
}
|
|
@@ -169,31 +254,29 @@ export class CarouselRootState {
|
|
|
169
254
|
getIndicatorCount() {
|
|
170
255
|
return this.#indicators.length;
|
|
171
256
|
}
|
|
257
|
+
registerUserInteraction() {
|
|
258
|
+
this.#hasUserInteraction = true;
|
|
259
|
+
if (!this.#isTouchGesture && this.#ride === true && !this.#timeoutId) {
|
|
260
|
+
this.cycle();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
172
263
|
next() {
|
|
173
|
-
if (this.#isAnimating) {
|
|
264
|
+
if (this.#isAnimating || this.#items.length < 2) {
|
|
174
265
|
return;
|
|
175
266
|
}
|
|
176
267
|
const nextIndex = (this.#activeItemIndex + 1) % this.#items.length;
|
|
177
268
|
this.to(nextIndex, 'next');
|
|
178
269
|
// Mark that user interaction has occurred
|
|
179
|
-
this
|
|
180
|
-
// Start cycling if ride is true and this is first interaction
|
|
181
|
-
if (this.#ride === true && !this.#timeoutId) {
|
|
182
|
-
this.cycle();
|
|
183
|
-
}
|
|
270
|
+
this.registerUserInteraction();
|
|
184
271
|
}
|
|
185
272
|
prev() {
|
|
186
|
-
if (this.#isAnimating) {
|
|
273
|
+
if (this.#isAnimating || this.#items.length < 2) {
|
|
187
274
|
return;
|
|
188
275
|
}
|
|
189
276
|
const prevIndex = (this.#activeItemIndex - 1 + this.#items.length) % this.#items.length;
|
|
190
277
|
this.to(prevIndex, 'prev');
|
|
191
278
|
// Mark that user interaction has occurred
|
|
192
|
-
this
|
|
193
|
-
// Start cycling if ride is true and this is first interaction
|
|
194
|
-
if (this.#ride === true && !this.#timeoutId) {
|
|
195
|
-
this.cycle();
|
|
196
|
-
}
|
|
279
|
+
this.registerUserInteraction();
|
|
197
280
|
}
|
|
198
281
|
/**
|
|
199
282
|
* Navigates to the slide at the specified index with the given transition direction.
|
|
@@ -226,6 +309,7 @@ export class CarouselRootState {
|
|
|
226
309
|
}
|
|
227
310
|
this.#isAnimating = true;
|
|
228
311
|
this.#nextActiveItemIndex = index;
|
|
312
|
+
const generation = ++this.#transitionGeneration;
|
|
229
313
|
// For slide and crossfade animations, we set directional classes and clean up after transition
|
|
230
314
|
if (this.#animation === 'slide' || this.#animation === 'crossfade') {
|
|
231
315
|
if (isNext) {
|
|
@@ -238,12 +322,16 @@ export class CarouselRootState {
|
|
|
238
322
|
toItem.direction = 'end';
|
|
239
323
|
toItem.order = 'prev';
|
|
240
324
|
}
|
|
241
|
-
this.#schedule(() => {
|
|
325
|
+
this.#transitionTimeoutId = this.#schedule(() => {
|
|
326
|
+
this.#transitionTimeoutId = null;
|
|
327
|
+
if (generation !== this.#transitionGeneration || !this.#items.includes(fromItem) || !this.#items.includes(toItem)) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
242
330
|
fromItem.order = undefined;
|
|
243
331
|
fromItem.direction = undefined;
|
|
244
332
|
toItem.order = undefined;
|
|
245
333
|
toItem.direction = undefined;
|
|
246
|
-
this.#activeItemIndex =
|
|
334
|
+
this.#activeItemIndex = this.#items.indexOf(toItem);
|
|
247
335
|
this.#nextActiveItemIndex = -1;
|
|
248
336
|
this.#isAnimating = false;
|
|
249
337
|
// Restart cycle with new slide's interval if cycling
|
|
@@ -260,10 +348,14 @@ export class CarouselRootState {
|
|
|
260
348
|
if (isPrev) {
|
|
261
349
|
toItem.order = 'prev';
|
|
262
350
|
}
|
|
263
|
-
this.#schedule(() => {
|
|
351
|
+
this.#transitionTimeoutId = this.#schedule(() => {
|
|
352
|
+
this.#transitionTimeoutId = null;
|
|
353
|
+
if (generation !== this.#transitionGeneration || !this.#items.includes(toItem)) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
264
356
|
toItem.order = undefined;
|
|
265
357
|
this.#isAnimating = false;
|
|
266
|
-
}, this.#transitionDuration
|
|
358
|
+
}, this.#transitionDuration);
|
|
267
359
|
this.#activeItemIndex = index;
|
|
268
360
|
this.#nextActiveItemIndex = -1;
|
|
269
361
|
// Restart cycle with new slide's interval if cycling
|
|
@@ -274,7 +366,10 @@ export class CarouselRootState {
|
|
|
274
366
|
// For no animation, we simply update the active index on the next tick
|
|
275
367
|
if (this.#animation === 'none') {
|
|
276
368
|
tick().then(() => {
|
|
277
|
-
this.#
|
|
369
|
+
if (generation !== this.#transitionGeneration || !this.#items.includes(fromItem) || !this.#items.includes(toItem)) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
this.#activeItemIndex = this.#items.indexOf(toItem);
|
|
278
373
|
this.#nextActiveItemIndex = -1;
|
|
279
374
|
this.#isAnimating = false;
|
|
280
375
|
// Restart cycle with new slide's interval if cycling
|
|
@@ -292,7 +387,12 @@ export class CarouselRootState {
|
|
|
292
387
|
if (typeof window === 'undefined') {
|
|
293
388
|
return;
|
|
294
389
|
}
|
|
295
|
-
if (this.#isDisposed) {
|
|
390
|
+
if (this.#isDisposed || !this.shouldCycle() || this.#items.length < 2) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
// A mount/start or reactive ride update can race with an in-progress touch.
|
|
394
|
+
// Keep the carousel paused until the single delayed touch resume fires.
|
|
395
|
+
if (this.#isPointerDown || this.#resumeTimeoutId !== null) {
|
|
296
396
|
return;
|
|
297
397
|
}
|
|
298
398
|
// Don't start cycling if we're hovered and pause='hover'
|
|
@@ -310,6 +410,7 @@ export class CarouselRootState {
|
|
|
310
410
|
const currentInterval = this.getCurrentItemInterval();
|
|
311
411
|
// Schedule the next slide using setTimeout
|
|
312
412
|
this.#timeoutId = window.setTimeout(() => {
|
|
413
|
+
this.#timeoutId = null;
|
|
313
414
|
if (!this.#isPaused && !this.#isAnimating) {
|
|
314
415
|
this.next();
|
|
315
416
|
}
|
|
@@ -344,6 +445,7 @@ export class CarouselRootState {
|
|
|
344
445
|
handlePointerDown(pointerEvent) {
|
|
345
446
|
// Only track primary touch pointer
|
|
346
447
|
if (pointerEvent.pointerType === 'touch' && pointerEvent.isPrimary) {
|
|
448
|
+
this.#cancelTouchResume();
|
|
347
449
|
this.#isPointerDown = true;
|
|
348
450
|
this.#pointerStartX = pointerEvent.clientX;
|
|
349
451
|
// Pause autoplay during touch interaction
|
|
@@ -366,29 +468,81 @@ export class CarouselRootState {
|
|
|
366
468
|
* Determines swipe direction based on horizontal movement distance.
|
|
367
469
|
*/
|
|
368
470
|
handlePointerUp(pointerEvent) {
|
|
369
|
-
if (!this.#isPointerDown
|
|
471
|
+
if (!this.#isPointerDown) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
this.#isPointerDown = false;
|
|
475
|
+
if (pointerEvent.pointerType !== 'touch' || !pointerEvent.isPrimary) {
|
|
476
|
+
this.#scheduleTouchResume();
|
|
370
477
|
return;
|
|
371
478
|
}
|
|
372
479
|
const deltaX = pointerEvent.clientX - this.#pointerStartX;
|
|
373
480
|
// Consider it a swipe if movement exceeds threshold (50px)
|
|
374
481
|
if (Math.abs(deltaX) > 50) {
|
|
482
|
+
this.#isTouchGesture = true;
|
|
375
483
|
if (deltaX > 0) {
|
|
376
484
|
this.prev(); // Swipe right means go to previous
|
|
377
485
|
}
|
|
378
486
|
else {
|
|
379
487
|
this.next(); // Swipe left means go to next
|
|
380
488
|
}
|
|
489
|
+
this.#isTouchGesture = false;
|
|
381
490
|
}
|
|
382
|
-
this.#isPointerDown = false;
|
|
383
491
|
// Resume cycling after touch with a delay to prevent immediate transition
|
|
384
|
-
|
|
385
|
-
|
|
492
|
+
this.#scheduleTouchResume();
|
|
493
|
+
}
|
|
494
|
+
handlePointerCancel() {
|
|
495
|
+
if (!this.#isPointerDown) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
this.#isPointerDown = false;
|
|
499
|
+
this.#scheduleTouchResume();
|
|
500
|
+
}
|
|
501
|
+
#invalidateTransition() {
|
|
502
|
+
this.#transitionGeneration++;
|
|
503
|
+
if (this.#transitionTimeoutId !== null) {
|
|
504
|
+
window.clearTimeout(this.#transitionTimeoutId);
|
|
505
|
+
this.#pendingTimeoutIds = this.#pendingTimeoutIds.filter((id) => id !== this.#transitionTimeoutId);
|
|
506
|
+
this.#transitionTimeoutId = null;
|
|
507
|
+
}
|
|
508
|
+
for (const item of this.#items) {
|
|
509
|
+
item.order = undefined;
|
|
510
|
+
item.direction = undefined;
|
|
511
|
+
}
|
|
512
|
+
this.#nextActiveItemIndex = -1;
|
|
513
|
+
this.#isAnimating = false;
|
|
514
|
+
}
|
|
515
|
+
#cancelTouchResume() {
|
|
516
|
+
if (this.#resumeTimeoutId === null) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
window.clearTimeout(this.#resumeTimeoutId);
|
|
520
|
+
this.#pendingTimeoutIds = this.#pendingTimeoutIds.filter((id) => id !== this.#resumeTimeoutId);
|
|
521
|
+
this.#resumeTimeoutId = null;
|
|
522
|
+
}
|
|
523
|
+
#scheduleTouchResume() {
|
|
524
|
+
this.#cancelTouchResume();
|
|
525
|
+
if (!this.shouldCycle() || this.#isDisposed) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
this.#schedule(() => {
|
|
529
|
+
this.#resumeTimeoutId = null;
|
|
530
|
+
if (!this.#isDisposed && this.shouldCycle()) {
|
|
386
531
|
this.cycle();
|
|
387
|
-
}
|
|
532
|
+
}
|
|
533
|
+
}, 1000);
|
|
534
|
+
this.#resumeTimeoutId = this.#pendingTimeoutIds.at(-1) ?? null;
|
|
535
|
+
}
|
|
536
|
+
start() {
|
|
537
|
+
this.#hasStarted = true;
|
|
538
|
+
if (this.shouldCycle()) {
|
|
539
|
+
this.cycle();
|
|
388
540
|
}
|
|
389
541
|
}
|
|
390
542
|
dispose() {
|
|
391
543
|
this.#isDisposed = true;
|
|
544
|
+
this.#invalidateTransition();
|
|
545
|
+
this.#cancelTouchResume();
|
|
392
546
|
if (this.#timeoutId) {
|
|
393
547
|
window.clearTimeout(this.#timeoutId);
|
|
394
548
|
this.#timeoutId = null;
|
|
@@ -413,8 +567,12 @@ export class CarouselItemState {
|
|
|
413
567
|
root;
|
|
414
568
|
#order = $state(undefined);
|
|
415
569
|
#direction = $state(undefined);
|
|
416
|
-
index
|
|
417
|
-
|
|
570
|
+
get index() {
|
|
571
|
+
return this.root.getItemIndex(this);
|
|
572
|
+
}
|
|
573
|
+
get interval() {
|
|
574
|
+
return this.props.interval;
|
|
575
|
+
}
|
|
418
576
|
isActive = $derived.by(() => this.root.isItemActive(this.index));
|
|
419
577
|
isNext = $derived(this.#order === 'next');
|
|
420
578
|
isPrev = $derived(this.#order === 'prev');
|
|
@@ -434,8 +592,6 @@ export class CarouselItemState {
|
|
|
434
592
|
constructor(props, root) {
|
|
435
593
|
this.props = props;
|
|
436
594
|
this.root = root;
|
|
437
|
-
this.index = this.root.getItemCount();
|
|
438
|
-
this.interval = this.props.interval || 5000;
|
|
439
595
|
}
|
|
440
596
|
get order() {
|
|
441
597
|
return this.#order;
|
|
@@ -493,15 +649,17 @@ export class CarouselIndicatorButtonState {
|
|
|
493
649
|
root;
|
|
494
650
|
isActive = $derived.by(() => this.root.isItemActive(this.index) && !this.root.isAnimating);
|
|
495
651
|
isNextActive = $derived.by(() => this.root.isNextItemActive(this.index));
|
|
496
|
-
index
|
|
652
|
+
get index() {
|
|
653
|
+
return this.root.getIndicatorIndex(this);
|
|
654
|
+
}
|
|
497
655
|
constructor(props, root) {
|
|
498
656
|
this.props = props;
|
|
499
657
|
this.root = root;
|
|
500
|
-
this.index = this.root.getIndicatorCount();
|
|
501
658
|
this.onclick = this.onclick.bind(this);
|
|
502
659
|
}
|
|
503
660
|
onclick(event) {
|
|
504
661
|
event.preventDefault();
|
|
662
|
+
this.root.registerUserInteraction();
|
|
505
663
|
const order = this.index > this.root.getActiveIndex() ? 'next' : 'prev';
|
|
506
664
|
this.root.to(this.index, order);
|
|
507
665
|
}
|
package/dist/Col/col.svelte
CHANGED
|
@@ -57,29 +57,37 @@ A component that implements Bootstrap's grid column functionality.
|
|
|
57
57
|
-->
|
|
58
58
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
59
59
|
let { children, class: classValues, elementRef = $bindable(null), offset, order, sizing, ...restOfProps } = $props();
|
|
60
|
+
const breakpoints = new Set(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']);
|
|
61
|
+
const isIntegerInRange = (value, min, max) => typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;
|
|
62
|
+
const isSizingValue = (value) => value === 'auto' || value === '*' || isIntegerInRange(value, 1, 12);
|
|
63
|
+
const isOffsetValue = (value) => isIntegerInRange(value, 0, 11);
|
|
64
|
+
const isOrderValue = (value) => value === 'first' || value === 'last' || isIntegerInRange(value, 0, 5);
|
|
60
65
|
// Generate sizing classes based on the sizing prop
|
|
61
66
|
let sizingClasses = $derived.by(() => {
|
|
62
67
|
// If sizing is a string and is 'auto', return a single col-auto class
|
|
63
|
-
if (
|
|
68
|
+
if (sizing === 'auto') {
|
|
64
69
|
return [`col-${sizing}`];
|
|
65
70
|
}
|
|
66
71
|
// If sizing is a number (OneToTwelve type), generate a simple col-n class
|
|
67
|
-
if (
|
|
72
|
+
if (isIntegerInRange(sizing, 1, 12)) {
|
|
68
73
|
return [`col-${sizing}`];
|
|
69
74
|
}
|
|
70
75
|
// If sizing is an object (BreakpointSizingMap type), generate breakpoint-specific classes
|
|
71
76
|
else if (sizing && typeof sizing === 'object') {
|
|
72
|
-
return Object.entries(sizing).
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return `col-${value}`;
|
|
77
|
+
return Object.entries(sizing).flatMap(([breakpoint, value]) => {
|
|
78
|
+
if (!breakpoints.has(breakpoint) || !isSizingValue(value)) {
|
|
79
|
+
return [];
|
|
76
80
|
}
|
|
77
81
|
// If the value is '*', return just 'col-{breakpoint}'
|
|
78
82
|
if (value === '*') {
|
|
79
|
-
return `col-${breakpoint}
|
|
83
|
+
return [breakpoint === 'xs' ? 'col' : `col-${breakpoint}`];
|
|
84
|
+
}
|
|
85
|
+
// If the breakpoint is 'xs', return 'col-{value}'
|
|
86
|
+
if (breakpoint === 'xs') {
|
|
87
|
+
return [`col-${value}`];
|
|
80
88
|
}
|
|
81
89
|
// Otherwise, return 'col-{breakpoint}-{value}'
|
|
82
|
-
return `col-${breakpoint}-${value}
|
|
90
|
+
return [`col-${breakpoint}-${value}`];
|
|
83
91
|
});
|
|
84
92
|
}
|
|
85
93
|
// Default: no sizing classes
|
|
@@ -88,18 +96,16 @@ let sizingClasses = $derived.by(() => {
|
|
|
88
96
|
// Generate offset classes if provided
|
|
89
97
|
let offsetClasses = $derived.by(() => {
|
|
90
98
|
// If offset is a number (ZeroToEleven type), generate a simple offset-n class
|
|
91
|
-
if (
|
|
99
|
+
if (isOffsetValue(offset)) {
|
|
92
100
|
return [`offset-${offset}`];
|
|
93
101
|
}
|
|
94
102
|
// If offset is an object (ColOffsetMap type), generate breakpoint-specific classes
|
|
95
103
|
else if (offset && typeof offset === 'object') {
|
|
96
|
-
return Object.entries(offset).
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// Otherwise, return 'offset-{breakpoint}-{value}'
|
|
102
|
-
return `offset-${breakpoint}-${value}`;
|
|
104
|
+
return Object.entries(offset).flatMap(([breakpoint, value]) => {
|
|
105
|
+
if (!breakpoints.has(breakpoint) || !isOffsetValue(value)) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
return [breakpoint === 'xs' ? `offset-${value}` : `offset-${breakpoint}-${value}`];
|
|
103
109
|
});
|
|
104
110
|
}
|
|
105
111
|
// Default: no offset classes
|
|
@@ -108,24 +114,26 @@ let offsetClasses = $derived.by(() => {
|
|
|
108
114
|
// Generate order classes if provided
|
|
109
115
|
let orderClasses = $derived.by(() => {
|
|
110
116
|
// If order is a string (e.g., 'first' or 'last'), return a single order-first or order-last class
|
|
111
|
-
if (
|
|
117
|
+
if (order === 'first' || order === 'last') {
|
|
112
118
|
return [`order-${order}`];
|
|
113
119
|
}
|
|
114
120
|
// If order is a number (ZeroToEleven type), generate a simple order-n class
|
|
115
|
-
if (
|
|
121
|
+
if (isIntegerInRange(order, 0, 5)) {
|
|
116
122
|
return [`order-${order}`];
|
|
117
123
|
}
|
|
118
124
|
// If order is an object (ColOrderMap type), generate breakpoint-specific classes
|
|
119
125
|
else if (order && typeof order === 'object') {
|
|
120
|
-
return Object.entries(order).
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
return Object.entries(order).flatMap(([breakpoint, value]) => {
|
|
127
|
+
if (!breakpoints.has(breakpoint) || !isOrderValue(value)) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
return [breakpoint === 'xs' ? `order-${value}` : `order-${breakpoint}-${value}`];
|
|
123
131
|
});
|
|
124
132
|
}
|
|
125
133
|
// Default: no order classes
|
|
126
134
|
return [];
|
|
127
135
|
});
|
|
128
|
-
let classes = $derived(uniqueClsx({ col:
|
|
136
|
+
let classes = $derived(uniqueClsx({ col: sizingClasses.length === 0 }, ...sizingClasses, ...offsetClasses, ...orderClasses, classValues));
|
|
129
137
|
</script>
|
|
130
138
|
|
|
131
139
|
<div bind:this={elementRef} class={classes} {...restOfProps}>
|
|
@@ -43,7 +43,7 @@ Supports both vertical and horizontal collapse directions.
|
|
|
43
43
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
44
44
|
import { noop } from '../common/noop.js';
|
|
45
45
|
import { collapseIn, collapseOut } from './transition.js';
|
|
46
|
-
let { children, class: classValues, transitionDuration
|
|
46
|
+
let { children, class: classValues, transitionDuration, elementRef = $bindable(null), isHorizontal = false, isExpanded = false, onCollapse = noop, onCollapsed = noop, onExpand = noop, onExpanded = noop, ...restOfProps } = $props();
|
|
47
47
|
let classes = $derived(uniqueClsx('collapse', { 'collapse-horizontal': isHorizontal }, { show: isExpanded }, classValues));
|
|
48
48
|
</script>
|
|
49
49
|
|
|
@@ -55,8 +55,8 @@ let classes = $derived(uniqueClsx('collapse', { 'collapse-horizontal': isHorizon
|
|
|
55
55
|
onintroend={onExpanded}
|
|
56
56
|
onoutrostart={onCollapse}
|
|
57
57
|
onoutroend={onCollapsed}
|
|
58
|
-
in:collapseIn={{ isHorizontal }}
|
|
59
|
-
out:collapseOut={{ isHorizontal }}
|
|
58
|
+
in:collapseIn={{ isHorizontal, transitionDuration }}
|
|
59
|
+
out:collapseOut={{ isHorizontal, transitionDuration }}
|
|
60
60
|
{...restOfProps}>
|
|
61
61
|
{@render children?.()}
|
|
62
62
|
</div>
|