@carbon/utilities 0.17.0 → 0.18.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 (63) hide show
  1. package/README.md +37 -0
  2. package/es/carousel/index.d.ts +2 -0
  3. package/es/carousel/index.js +3 -1
  4. package/es/chunk-BYypO7fO.js +18 -0
  5. package/es/chunk-Bzxox1sl.d.ts +48 -0
  6. package/es/chunk-CYAX4TFi.js +80 -0
  7. package/es/chunk-Ci2WlFE4.js +437 -0
  8. package/es/chunk-DPUOAfLB.d.ts +55 -0
  9. package/{types/datePartsOrder/datePartsOrder.d.ts → es/datePartsOrder/index.d.ts} +6 -4
  10. package/es/datePartsOrder/index.js +21 -1
  11. package/es/dateTimeFormat/index.d.ts +9 -0
  12. package/es/dateTimeFormat/index.js +16 -1
  13. package/{types/documentLang/documentLang.d.ts → es/documentLang/index.d.ts} +5 -3
  14. package/es/documentLang/index.js +69 -1
  15. package/es/index.d.ts +8 -0
  16. package/es/index.js +10 -1
  17. package/es/makeDraggable/index.d.ts +47 -0
  18. package/es/makeDraggable/index.js +148 -1
  19. package/es/overflowHandler/index.d.ts +104 -0
  20. package/es/overflowHandler/index.js +95 -1
  21. package/lib/carousel/index.js +4 -1
  22. package/lib/chunk-C5VBwGBG.js +443 -0
  23. package/lib/chunk-Dc0mzKQx.js +107 -0
  24. package/lib/datePartsOrder/index.js +23 -1
  25. package/lib/dateTimeFormat/index.js +17 -1
  26. package/lib/documentLang/index.js +72 -1
  27. package/lib/index.js +24 -1
  28. package/lib/makeDraggable/index.js +150 -1
  29. package/lib/overflowHandler/index.js +99 -1
  30. package/package.json +29 -12
  31. package/scss/carousel/_carousel.scss +34 -2
  32. package/es/carousel/carousel.js +0 -1
  33. package/es/carousel/swipeEvents.js +0 -1
  34. package/es/carousel/types.js +0 -0
  35. package/es/datePartsOrder/datePartsOrder.js +0 -1
  36. package/es/dateTimeFormat/absolute.js +0 -1
  37. package/es/dateTimeFormat/relative.js +0 -1
  38. package/es/documentLang/documentLang.js +0 -1
  39. package/es/makeDraggable/makeDraggable.js +0 -1
  40. package/es/overflowHandler/overflowHandler.js +0 -1
  41. package/lib/carousel/carousel.js +0 -1
  42. package/lib/carousel/swipeEvents.js +0 -1
  43. package/lib/carousel/types.js +0 -1
  44. package/lib/datePartsOrder/datePartsOrder.js +0 -1
  45. package/lib/dateTimeFormat/absolute.js +0 -1
  46. package/lib/dateTimeFormat/relative.js +0 -1
  47. package/lib/documentLang/documentLang.js +0 -1
  48. package/lib/makeDraggable/makeDraggable.js +0 -1
  49. package/lib/overflowHandler/overflowHandler.js +0 -1
  50. package/types/carousel/carousel.d.ts +0 -14
  51. package/types/carousel/index.d.ts +0 -8
  52. package/types/carousel/swipeEvents.d.ts +0 -9
  53. package/types/carousel/types.d.ts +0 -36
  54. package/types/datePartsOrder/index.d.ts +0 -7
  55. package/types/dateTimeFormat/absolute.d.ts +0 -30
  56. package/types/dateTimeFormat/index.d.ts +0 -12
  57. package/types/dateTimeFormat/relative.d.ts +0 -10
  58. package/types/documentLang/index.d.ts +0 -7
  59. package/types/index.d.ts +0 -13
  60. package/types/makeDraggable/index.d.ts +0 -7
  61. package/types/makeDraggable/makeDraggable.d.ts +0 -38
  62. package/types/overflowHandler/index.d.ts +0 -7
  63. package/types/overflowHandler/overflowHandler.d.ts +0 -85
package/README.md CHANGED
@@ -19,6 +19,43 @@ instead:
19
19
  yarn add @carbon/utilities
20
20
  ```
21
21
 
22
+ ## Maintainer information
23
+
24
+ This package uses a convention-based multi-entry build. The supported public API
25
+ consists of two parts.
26
+
27
+ 1. The root entrypoint
28
+
29
+ ```js
30
+ import { utility } from '@carbon/utilities';
31
+ ```
32
+
33
+ 2. Any top-level module with a `src/<name>/index.ts` file
34
+
35
+ ```js
36
+ import { utility } from '@carbon/utilities/utility';
37
+ ```
38
+
39
+ Review the
40
+ [architecture decision record (ADR)](../../docs/decisions/0004-adopt-explicit-package-entrypoints-for-utilities.md)
41
+ for more detail on how and why this approach is used.
42
+
43
+ ### Adding a utility
44
+
45
+ 1. Create a new top-level directory under `src/`
46
+ 2. Export the public API for that utility from `src/<name>/index.ts`
47
+ 3. Re-export it from `src/index.ts` if it should also be available from the
48
+ package root
49
+ 4. Build the package and verify the generated outputs under `es/<name>/index.*`
50
+ and `lib/<name>/index.js`
51
+
52
+ ```text
53
+ src/
54
+ someUtility/
55
+ index.ts
56
+ someUtility.ts
57
+ ```
58
+
22
59
  ## 🙌 Contributing
23
60
 
24
61
  We're always looking for contributors to help us fix bugs, build new features,
@@ -0,0 +1,2 @@
1
+ import { a as InitCarousel, i as Config, n as CarouselResponse, o as TranslationKey, r as CarouselStackHistory, t as initCarousel } from "../chunk-DPUOAfLB.js";
2
+ export { CarouselResponse, CarouselStackHistory, Config, InitCarousel, TranslationKey, initCarousel };
@@ -1 +1,3 @@
1
- export*from"./carousel";export*from"./types";
1
+ import { t as initCarousel } from "../chunk-Ci2WlFE4.js";
2
+
3
+ export { initCarousel };
@@ -0,0 +1,18 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ }
11
+ if (!no_symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
15
+ };
16
+
17
+ //#endregion
18
+ export { __exportAll as t };
@@ -0,0 +1,48 @@
1
+ declare namespace relative_d_exports {
2
+ export { format$1 as format };
3
+ }
4
+ /**
5
+ * Copyright IBM Corp. 2024
6
+ *
7
+ * This source code is licensed under the Apache-2.0 license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ declare function format$1(date: Date | number, options?: Partial<{
11
+ locale: string;
12
+ style: Intl.RelativeTimeFormatStyle;
13
+ }>): string;
14
+ declare namespace absolute_d_exports {
15
+ export { format, formatDate, formatRange, formatTime };
16
+ }
17
+ /**
18
+ * Copyright IBM Corp. 2024, 2025
19
+ *
20
+ * This source code is licensed under the Apache-2.0 license found in the
21
+ * LICENSE file in the root directory of this source tree.
22
+ */
23
+ declare function formatTime(date: Date | number, options?: Partial<{
24
+ locale: string;
25
+ style: Intl.DateTimeFormatOptions['timeStyle'];
26
+ timeZone: Intl.DateTimeFormatOptions['timeZone'];
27
+ }>): string;
28
+ declare function formatDate(date: Date | number, options?: Partial<{
29
+ locale: string;
30
+ style: Intl.DateTimeFormatOptions['dateStyle'];
31
+ timeZone: Intl.DateTimeFormatOptions['timeZone'];
32
+ }>): string;
33
+ declare function format(date: Date | number, options?: Partial<{
34
+ locale: string;
35
+ style: Intl.DateTimeFormatOptions['timeStyle'] | 'tooltip';
36
+ timeStyle: Intl.DateTimeFormatOptions['timeStyle'];
37
+ dateStyle: Intl.DateTimeFormatOptions['dateStyle'];
38
+ timeZone: Intl.DateTimeFormatOptions['timeZone'];
39
+ }>): string;
40
+ declare function formatRange(startDate: Date | number, endDate: Date | number, options?: Partial<{
41
+ locale: string;
42
+ style: Intl.DateTimeFormatOptions['timeStyle'];
43
+ timeStyle: Intl.DateTimeFormatOptions['timeStyle'] | null;
44
+ dateStyle: Intl.DateTimeFormatOptions['dateStyle'] | null;
45
+ timeZone: Intl.DateTimeFormatOptions['timeZone'];
46
+ }>): string;
47
+ //#endregion
48
+ export { relative_d_exports as n, absolute_d_exports as t };
@@ -0,0 +1,80 @@
1
+ import { t as __exportAll } from "./chunk-BYypO7fO.js";
2
+
3
+ //#region src/dateTimeFormat/relative.ts
4
+ var relative_exports = /* @__PURE__ */ __exportAll({ format: () => format$1 });
5
+ /**
6
+ * Copyright IBM Corp. 2024
7
+ *
8
+ * This source code is licensed under the Apache-2.0 license found in the
9
+ * LICENSE file in the root directory of this source tree.
10
+ */
11
+ function format$1(date, options) {
12
+ const rtf = new Intl.RelativeTimeFormat(options?.locale, { style: options?.style ?? "long" });
13
+ const d = typeof date === "number" ? new Date(date) : date;
14
+ const now = Date.now();
15
+ const seconds = Math.floor((now - d.getTime()) / 1e3);
16
+ const minutes = Math.floor(seconds / 60);
17
+ const hours = Math.floor(minutes / 60);
18
+ const days = Math.floor(hours / 24);
19
+ const weeks = Math.floor(days / 7);
20
+ const months = Math.floor(weeks / 4);
21
+ const years = Math.floor(days / 365);
22
+ if (Math.abs(seconds) < 60) return new Intl.RelativeTimeFormat(options?.locale, {
23
+ numeric: "auto",
24
+ style: options?.style ?? "long"
25
+ }).format(0, "seconds");
26
+ if (Math.abs(minutes) < 60) return rtf.format(minutes * -1, "minutes");
27
+ if (Math.abs(hours) < 24) return rtf.format(hours * -1, "hours");
28
+ if (Math.abs(days) < 7) return rtf.format(days * -1, "days");
29
+ if (Math.abs(weeks) < 4) return rtf.format(weeks * -1, "weeks");
30
+ if (Math.abs(days) < 365) return rtf.format(months * -1, "months");
31
+ return rtf.format(years * -1, "years");
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/dateTimeFormat/absolute.ts
36
+ var absolute_exports = /* @__PURE__ */ __exportAll({
37
+ format: () => format,
38
+ formatDate: () => formatDate,
39
+ formatRange: () => formatRange,
40
+ formatTime: () => formatTime
41
+ });
42
+ /**
43
+ * Copyright IBM Corp. 2024, 2025
44
+ *
45
+ * This source code is licensed under the Apache-2.0 license found in the
46
+ * LICENSE file in the root directory of this source tree.
47
+ */
48
+ function formatTime(date, options) {
49
+ return new Intl.DateTimeFormat(options?.locale, {
50
+ timeStyle: options?.style ?? "short",
51
+ timeZone: options?.timeZone
52
+ }).format(date);
53
+ }
54
+ function formatDate(date, options) {
55
+ return new Intl.DateTimeFormat(options?.locale, {
56
+ dateStyle: options?.style ?? "medium",
57
+ timeZone: options?.timeZone
58
+ }).format(date);
59
+ }
60
+ function format(date, options) {
61
+ const timeStyle = options?.timeStyle ?? (options?.style === "tooltip" ? "long" : options?.style) ?? "short";
62
+ const dateStyle = options?.dateStyle ?? (options?.style === "tooltip" ? "full" : options?.style) ?? "medium";
63
+ return new Intl.DateTimeFormat(options?.locale, {
64
+ timeStyle,
65
+ dateStyle,
66
+ timeZone: options?.timeZone
67
+ }).format(date);
68
+ }
69
+ function formatRange(startDate, endDate, options) {
70
+ const timeStyle = options?.timeStyle === null ? void 0 : options?.timeStyle ?? options?.style ?? "short";
71
+ const dateStyle = options?.dateStyle === null ? void 0 : options?.dateStyle ?? options?.style ?? "medium";
72
+ return new Intl.DateTimeFormat(options?.locale, {
73
+ timeStyle,
74
+ dateStyle,
75
+ timeZone: options?.timeZone
76
+ }).formatRange(startDate, endDate);
77
+ }
78
+
79
+ //#endregion
80
+ export { relative_exports as n, absolute_exports as t };
@@ -0,0 +1,437 @@
1
+ //#region src/carousel/defs.ts
2
+ /**
3
+ * Copyright IBM Corp. 2026
4
+ *
5
+ * This source code is licensed under the Apache-2.0 license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ const translationIds = {
9
+ "carbon.carousel.item": "carbon.carousel.item",
10
+ "carbon.carousel.of": "carbon.carousel.of"
11
+ };
12
+
13
+ //#endregion
14
+ //#region src/carousel/swipeEvents.ts
15
+ /**
16
+ * Registers swipe event handlers for a carousel element.
17
+ * Handles touch, mouse, and wheel events for navigation.
18
+ * @param {HTMLElement} carousel - The carousel element to attach event listeners to.
19
+ * @param {() => void} next - Callback function to execute when swiping right.
20
+ * @param {() => void} prev - Callback function to execute when swiping left.
21
+ * @param {boolean} destroy - If true, removes existing event listeners before adding new ones.
22
+ */
23
+ const registerSwipeEvents = (carousel, next, prev, destroy) => {
24
+ const minSwipeDistance = 50;
25
+ let touchStartX = null;
26
+ let touchEndX = null;
27
+ let lastScrollTime = 0;
28
+ const scrollCooldown = 400;
29
+ let isMouseDown = false;
30
+ let mouseStartX = null;
31
+ let mouseEndX = null;
32
+ const touchStartHandler = (e) => {
33
+ touchStartX = e.touches[0].clientX;
34
+ };
35
+ const touchMoveHandler = (e) => {
36
+ touchEndX = e.touches[0].clientX;
37
+ };
38
+ const touchEndHandler = () => {
39
+ if (touchStartX !== null && touchEndX !== null) {
40
+ const distance = touchStartX - touchEndX;
41
+ if (Math.abs(distance) > minSwipeDistance) if (distance > 0) next();
42
+ else prev();
43
+ }
44
+ touchStartX = null;
45
+ touchEndX = null;
46
+ };
47
+ const mouseDownHandler = (e) => {
48
+ isMouseDown = true;
49
+ mouseStartX = e.clientX;
50
+ };
51
+ const mouseMoveHandler = (e) => {
52
+ if (!isMouseDown) return;
53
+ mouseEndX = e.clientX;
54
+ };
55
+ const mouseUpHandler = () => {
56
+ if (isMouseDown && mouseStartX !== null && mouseEndX !== null) {
57
+ const distance = mouseStartX - mouseEndX;
58
+ if (Math.abs(distance) > minSwipeDistance) if (distance > 0) next();
59
+ else prev();
60
+ }
61
+ isMouseDown = false;
62
+ mouseStartX = null;
63
+ mouseEndX = null;
64
+ };
65
+ const wheelHandler = (e) => {
66
+ const now = Date.now();
67
+ if (Math.abs(e.deltaX) > Math.abs(e.deltaY) && Math.abs(e.deltaX) > 20) {
68
+ e.preventDefault();
69
+ if (now - lastScrollTime < scrollCooldown) return;
70
+ if (e.deltaX > 0) next();
71
+ else prev();
72
+ lastScrollTime = now;
73
+ }
74
+ };
75
+ if (destroy) {
76
+ carousel.removeEventListener("touchstart", touchStartHandler);
77
+ carousel.removeEventListener("touchmove", touchMoveHandler);
78
+ carousel.removeEventListener("touchend", touchEndHandler);
79
+ carousel.removeEventListener("mousedown", mouseDownHandler);
80
+ carousel.removeEventListener("mousemove", mouseMoveHandler);
81
+ carousel.removeEventListener("mouseup", mouseUpHandler);
82
+ carousel.removeEventListener("wheel", wheelHandler);
83
+ }
84
+ carousel.addEventListener("touchstart", touchStartHandler);
85
+ carousel.addEventListener("touchmove", touchMoveHandler);
86
+ carousel.addEventListener("touchend", touchEndHandler);
87
+ carousel.addEventListener("mousedown", mouseDownHandler);
88
+ carousel.addEventListener("mousemove", mouseMoveHandler);
89
+ carousel.addEventListener("mouseup", mouseUpHandler);
90
+ carousel.addEventListener("wheel", wheelHandler);
91
+ };
92
+
93
+ //#endregion
94
+ //#region src/carousel/carousel.ts
95
+ const defaultTranslations = {
96
+ [translationIds["carbon.carousel.item"]]: "Item",
97
+ [translationIds["carbon.carousel.of"]]: "of"
98
+ };
99
+ /**
100
+ * Default translation function
101
+ */
102
+ const defaultTranslateWithId = (messageId) => {
103
+ return defaultTranslations[messageId];
104
+ };
105
+ /**
106
+ * Initializes a carousel with the given configuration.
107
+ * @param carouselContainer - The HTMLElement representing the carousel container.
108
+ * @param config - Optional configuration object.
109
+ * @returns An object containing methods to control the carousel.
110
+ */
111
+ const initCarousel = (carouselContainer, config) => {
112
+ const prefix = "carousel";
113
+ let viewIndexStack = [0];
114
+ let previousViewIndexStack = [0];
115
+ const refs = {};
116
+ const carouselListeners = /* @__PURE__ */ new WeakMap();
117
+ const minHeight = 4;
118
+ const { onViewChangeStart, onViewChangeEnd, excludeSwipeSupport, useMaxHeight, translateWithId: t = defaultTranslateWithId } = config || {};
119
+ /**
120
+ * Registers an HTMLElement at a specific index in the refs array.
121
+ *
122
+ * @param index - The index at which to register the HTMLElement.
123
+ * @param ref - The HTMLElement to register.
124
+ *
125
+ * @example
126
+ * registerRef(0, document.getElementById('myElement'));
127
+ */
128
+ const registerRef = (index, ref) => {
129
+ refs[index] = ref;
130
+ };
131
+ const getLiveRegion = () => carouselContainer.querySelector(`.${prefix}__live-region`);
132
+ const getWrapper = () => carouselContainer.querySelector(`.${prefix}__itemsWrapper`);
133
+ const updateLiveRegion = (idx) => {
134
+ const div = getLiveRegion();
135
+ if (div) div.textContent = `${t("carbon.carousel.item")} ${idx} ${t("carbon.carousel.of")} ${getCarouselItems()?.length}`;
136
+ };
137
+ const syncLiveRegionWithActiveView = () => {
138
+ updateLiveRegion(viewIndexStack[0] + 1);
139
+ };
140
+ const createLiveRegion = () => {
141
+ const childCount = getCarouselItems()?.length;
142
+ if (getLiveRegion() || !childCount) return;
143
+ const div = document.createElement("div");
144
+ div.setAttribute("aria-live", "polite");
145
+ div.setAttribute("aria-atomic", "true");
146
+ div.setAttribute("class", `${prefix}__live-region`);
147
+ div.textContent = `${t("carbon.carousel.item")} 1 ${t("carbon.carousel.of")} ${childCount}`;
148
+ carouselContainer.appendChild(div);
149
+ };
150
+ /**
151
+ * Wraps all child elements of a given container into a new div with the specified class.
152
+ * If an element with the specified class already exists as a child of the container, the function does nothing.
153
+ */
154
+ const wrapAllItems = () => {
155
+ if (getWrapper()) return;
156
+ const wrapper = document.createElement("div");
157
+ wrapper.classList.add(`${prefix}__itemsWrapper`);
158
+ while (carouselContainer.firstChild) wrapper.appendChild(carouselContainer.firstChild);
159
+ carouselContainer.appendChild(wrapper);
160
+ };
161
+ const getHistory = () => {
162
+ return viewIndexStack.reduce((history, id) => {
163
+ const elem = refs[id];
164
+ if (elem) history.push({
165
+ id,
166
+ elem
167
+ });
168
+ return history;
169
+ }, []);
170
+ };
171
+ /**
172
+ * Retrieves the current carousel response based on the view index stack and reference objects.
173
+ * @returns {CarouselResponse} - An object containing carousel response details.
174
+ */
175
+ const getCallbackResponse = () => {
176
+ const totalRefs = Object.keys(refs).length;
177
+ const lastElementRef = refs[totalRefs - 1];
178
+ const historicalData = getHistory();
179
+ return {
180
+ currentIndex: viewIndexStack[0],
181
+ lastIndex: parseInt(lastElementRef?.dataset.index || viewIndexStack[0].toString(), 10),
182
+ totalViews: totalRefs,
183
+ historyStack: historicalData
184
+ };
185
+ };
186
+ /**
187
+ * Handles the start of a transition in the application.
188
+ * This function is responsible for capturing the current state of the view index stack
189
+ * and invoking a callback function if it exists.
190
+ *
191
+ * @function handleTransitionStart
192
+ * @returns {void}
193
+ */
194
+ const handleTransitionStart = () => {
195
+ previousViewIndexStack = [...viewIndexStack];
196
+ const callbackData = getCallbackResponse();
197
+ onViewChangeStart?.(callbackData);
198
+ };
199
+ /**
200
+ * Handles the 'transitionend' event for a given element.
201
+ * This function checks if the element has a 'data-index' attribute and if its value matches the current view index.
202
+ * If both conditions are met, it calls the 'onViewChangeEnd' callback with the response from 'getCallbackResponse'.
203
+ *
204
+ * @param el - The element to handle the 'transitionend' event for.
205
+ */
206
+ const handleTransitionEnd = (el) => {
207
+ if (!el) return;
208
+ const tmpElementIndex = el.dataset.index;
209
+ if (tmpElementIndex && viewIndexStack[0] === parseInt(tmpElementIndex, 10)) {
210
+ const callbackData = getCallbackResponse();
211
+ onViewChangeEnd?.(callbackData);
212
+ }
213
+ };
214
+ /**
215
+ * A utility function to sanitize an index value.
216
+ * This function ensures the index stays within the bounds of the refs array.
217
+ *
218
+ * @param idx - The index to be sanitized.
219
+ * @returns - The sanitized index.
220
+ */
221
+ const sanitizeIndex = (idx) => {
222
+ const floorVal = 0;
223
+ const ceilVal = Object.keys(refs).length - 1;
224
+ return Math.max(floorVal, Math.min(idx, ceilVal));
225
+ };
226
+ /**
227
+ * Handles the 'transitionend' event for a given element.
228
+ * This function checks if the element has a 'data-index' attribute and if its value matches the current view index.
229
+ * If both conditions are met, it calls the 'onViewChangeEnd' callback with the response from 'getCallbackResponse'.
230
+ * @returns {void}
231
+ */
232
+ const transitionToViewIndex = (idx) => {
233
+ const sanitizedIndex = sanitizeIndex(idx);
234
+ if (viewIndexStack[0] !== sanitizedIndex) {
235
+ handleTransitionStart();
236
+ viewIndexStack = [sanitizedIndex, ...viewIndexStack];
237
+ syncLiveRegionWithActiveView();
238
+ performAnimation(false);
239
+ }
240
+ };
241
+ const transitionComplete = (ref) => {
242
+ handleTransitionEnd(ref);
243
+ };
244
+ /**
245
+ * Attaches class names to an HTMLElement based on given conditions.
246
+ *
247
+ * @param viewItem - The HTML element to which class names will be added.
248
+ * @param isInViewStack - Indicates if the view item is in the view stack.
249
+ * @param isActive - Indicates if the view item is active.
250
+ * @param isBeingRecycledOut - Indicates if the view item is being recycled out.
251
+ * @param isBeingRecycledIn - Indicates if the view item is being recycled in.
252
+ */
253
+ const attachClassNames = (viewItem, isInViewStack, isActive, isBeingRecycledOut, isBeingRecycledIn) => {
254
+ viewItem.classList.add(`${prefix}__view`);
255
+ viewItem.classList.toggle(`${prefix}__view-in-stack`, isInViewStack && !isActive);
256
+ viewItem.classList.toggle(`${prefix}__view-active`, isInViewStack && isActive);
257
+ if (isBeingRecycledIn && !isBeingRecycledOut) viewItem.classList.add(`${prefix}__view-recycle-in`);
258
+ if (!isBeingRecycledIn && isBeingRecycledOut) viewItem.classList.add(`${prefix}__view-recycle-out`);
259
+ if (isActive) {
260
+ viewItem.removeAttribute("aria-hidden");
261
+ viewItem.removeAttribute("inert");
262
+ } else {
263
+ viewItem.setAttribute("aria-hidden", "true");
264
+ viewItem.setAttribute("inert", "");
265
+ }
266
+ };
267
+ const removeReCycleClasses = (viewItem) => {
268
+ viewItem.classList.remove(`${prefix}__view-recycle-in`, `${prefix}__view-recycle-out`);
269
+ };
270
+ const remToPx = (rem) => {
271
+ return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
272
+ };
273
+ /**
274
+ * Updates the height of the items wrapper in a carousel based on the smallest item height and a threshold height.
275
+ * This function ensures that the items wrapper does not have a height smaller than the threshold, adjusting the item height if necessary.
276
+ *
277
+ * @param itemHeightSmallest - The smallest height of an item in pixels.
278
+ */
279
+ const updateHeightForWrapper = (itemHeightSmallest) => {
280
+ const thresholdHeight = remToPx(minHeight);
281
+ if (carouselContainer.clientHeight < thresholdHeight) {
282
+ if (itemHeightSmallest < thresholdHeight) itemHeightSmallest = thresholdHeight;
283
+ const itemsWrapper = getWrapper();
284
+ if (itemsWrapper) itemsWrapper.style.blockSize = `${itemHeightSmallest}px`;
285
+ }
286
+ };
287
+ /**
288
+ * Performs animation on view items based on their state in the view index stack.
289
+ * @param isInitial - A flag indicating if this is the initial animation.
290
+ */
291
+ const performAnimation = (isInitial) => {
292
+ const viewItems = getCarouselItems();
293
+ if (!viewItems) return;
294
+ let itemHeightSmallest = 0;
295
+ let itemHeightMaximum = 0;
296
+ Array.from(viewItems).forEach((viewItem, index) => {
297
+ const stackIndex = viewIndexStack.findIndex((idx) => idx === index);
298
+ const stackIndexInstanceCount = previousViewIndexStack.filter((viIdx) => viIdx === index).length;
299
+ const isBeingRecycledOut = previousViewIndexStack.length > viewIndexStack.length && previousViewIndexStack[0] === index && stackIndexInstanceCount > 0;
300
+ const isBeingRecycledIn = previousViewIndexStack.length < viewIndexStack.length && previousViewIndexStack[0] === index && stackIndexInstanceCount > 0;
301
+ attachClassNames(viewItem, stackIndex > -1, index === viewIndexStack[0], isBeingRecycledOut, isBeingRecycledIn);
302
+ if (isInitial) {
303
+ registerRef(index, viewItem);
304
+ setTimeout(() => {
305
+ if (useMaxHeight) {
306
+ const heights = Array.from(viewItems).map((viewItem) => viewItem.scrollHeight);
307
+ itemHeightMaximum = Math.max(...heights);
308
+ viewItem.style.position = "absolute";
309
+ updateHeightForWrapper(itemHeightMaximum);
310
+ } else {
311
+ if (!itemHeightSmallest || viewItem.offsetHeight < itemHeightSmallest && itemHeightSmallest > remToPx(minHeight)) itemHeightSmallest = viewItem.offsetHeight;
312
+ viewItem.style.position = "absolute";
313
+ updateHeightForWrapper(itemHeightSmallest);
314
+ }
315
+ });
316
+ const listener = (e) => {
317
+ removeReCycleClasses(viewItem);
318
+ if (e.target === refs[viewIndexStack[0]]) transitionComplete(viewItem);
319
+ };
320
+ carouselListeners.set(viewItem, listener);
321
+ viewItem.addEventListener("animationend", listener);
322
+ viewItem.addEventListener("transitionend", listener);
323
+ viewItem.setAttribute("data-index", index.toString());
324
+ }
325
+ });
326
+ if (isInitial) handleTransitionEnd(Array.from(viewItems)[0]);
327
+ };
328
+ /**
329
+ * A utility function to navigate to the next view in the stack.
330
+ * This function increments the current view index and transitions to the new index.
331
+ *
332
+ * @returns {void} - This function does not return any value.
333
+ */
334
+ const navigateNext = () => {
335
+ transitionToViewIndex(viewIndexStack[0] + 1);
336
+ };
337
+ /**
338
+ * Navigates to the previous view in the view stack.
339
+ * @function navigatePrev
340
+ * @description This function checks if there is a previous view in the stack. If so, it triggers a transition start, removes the current view from the stack, and performs an animation to transition to the previous view.
341
+ * @returns {void} - This function does not return a value.
342
+ */
343
+ const navigatePrev = () => {
344
+ if (viewIndexStack.length - 1 >= 1) {
345
+ handleTransitionStart();
346
+ viewIndexStack = viewIndexStack.slice(1);
347
+ syncLiveRegionWithActiveView();
348
+ performAnimation(false);
349
+ }
350
+ };
351
+ /**
352
+ * A function that transitions the view to a specified index.
353
+ *
354
+ * @param index - The index to transition to.
355
+ * @returns - This function does not return a value.
356
+ */
357
+ const goToIndex = (index) => {
358
+ transitionToViewIndex(index);
359
+ };
360
+ /**
361
+ * Retrieves the currently active item and its index from the view index stack and references.
362
+ * @returns An object containing the index and the corresponding item reference.
363
+ */
364
+ const getActiveItem = () => {
365
+ return {
366
+ index: viewIndexStack[0],
367
+ item: refs[viewIndexStack[0]]
368
+ };
369
+ };
370
+ /**
371
+ * Resets the view index stack and performs an animation.
372
+ *
373
+ * @returns {void}
374
+ */
375
+ const reset = () => {
376
+ const viewItems = getCarouselItems();
377
+ if (!viewItems) return;
378
+ Array.from(viewItems).forEach((viewItem) => {
379
+ removeReCycleClasses(viewItem);
380
+ });
381
+ previousViewIndexStack = [0];
382
+ viewIndexStack = [0];
383
+ performAnimation(false);
384
+ syncLiveRegionWithActiveView();
385
+ };
386
+ /**
387
+ * Removes event listeners for 'animationend' and 'transitionend' events from all elements with references stored in the `refs` object.
388
+ * Also registers swipe events if `excludeSwipeSupport` is false.
389
+ */
390
+ const destroyEvents = () => {
391
+ Object.values(refs).forEach((el) => {
392
+ if (!el) return;
393
+ const listener = carouselListeners.get(el);
394
+ if (listener) {
395
+ el.removeEventListener("animationend", listener);
396
+ el.removeEventListener("transitionend", listener);
397
+ }
398
+ carouselListeners.delete(el);
399
+ });
400
+ if (!excludeSwipeSupport) registerSwipeEvents(carouselContainer, navigateNext, navigatePrev, true);
401
+ };
402
+ /**
403
+ * Retrieves carousel items from a given container element.
404
+ * If the container has a 'slot' element, it fetches all elements assigned to that slot.
405
+ * Otherwise, it fetches all direct children of the container.
406
+ *
407
+ * @returns An array of HTMLElements representing the carousel items or nothing if a container is not found.
408
+ *
409
+ * @example
410
+ * const carouselItems = getCarouselItems();
411
+ * console.log(carouselItems); // Logs the carousel items as HTMLElements
412
+ */
413
+ const getCarouselItems = () => {
414
+ const container = getWrapper();
415
+ if (!container) return;
416
+ const slot = container.querySelector("slot");
417
+ if (slot instanceof HTMLSlotElement) return slot.assignedElements({ flatten: true }).filter((item) => item instanceof HTMLElement);
418
+ return Array.from(container.children).filter((item) => item instanceof HTMLElement);
419
+ };
420
+ wrapAllItems();
421
+ createLiveRegion();
422
+ carouselContainer.classList.add(`${prefix}__view-stack`);
423
+ performAnimation(true);
424
+ if (!excludeSwipeSupport) registerSwipeEvents(carouselContainer, navigateNext, navigatePrev, false);
425
+ return {
426
+ next: navigateNext,
427
+ prev: navigatePrev,
428
+ reset,
429
+ goToIndex,
430
+ getActiveItem,
431
+ destroyEvents,
432
+ allViews: refs
433
+ };
434
+ };
435
+
436
+ //#endregion
437
+ export { initCarousel as t };
@@ -0,0 +1,55 @@
1
+ //#region src/carousel/defs.d.ts
2
+ /**
3
+ * Copyright IBM Corp. 2026
4
+ *
5
+ * This source code is licensed under the Apache-2.0 license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ declare const translationIds: {
9
+ readonly 'carbon.carousel.item': "carbon.carousel.item";
10
+ readonly 'carbon.carousel.of': "carbon.carousel.of";
11
+ };
12
+ //#endregion
13
+ //#region src/carousel/types.d.ts
14
+ type TranslationKey = keyof typeof translationIds;
15
+ interface CarouselStackHistory {
16
+ id: number;
17
+ elem: HTMLElement;
18
+ }
19
+ type CarouselResponse = {
20
+ currentIndex: number;
21
+ lastIndex: number;
22
+ totalViews: number;
23
+ historyStack: CarouselStackHistory[];
24
+ };
25
+ type ActiveItem = {
26
+ index: number;
27
+ item: HTMLElement | null;
28
+ };
29
+ type Config = {
30
+ onViewChangeStart?: (args: CarouselResponse) => void;
31
+ onViewChangeEnd?: (args: CarouselResponse) => void;
32
+ excludeSwipeSupport?: boolean;
33
+ useMaxHeight?: boolean;
34
+ translateWithId?: (messageId: TranslationKey) => string;
35
+ };
36
+ interface InitCarousel {
37
+ next: () => void;
38
+ prev: () => void;
39
+ reset: () => void;
40
+ goToIndex: (index: number) => void;
41
+ getActiveItem: () => ActiveItem;
42
+ destroyEvents: (() => void) | null;
43
+ allViews: Record<number, HTMLElement | null>;
44
+ }
45
+ //#endregion
46
+ //#region src/carousel/carousel.d.ts
47
+ /**
48
+ * Initializes a carousel with the given configuration.
49
+ * @param carouselContainer - The HTMLElement representing the carousel container.
50
+ * @param config - Optional configuration object.
51
+ * @returns An object containing methods to control the carousel.
52
+ */
53
+ declare const initCarousel: (carouselContainer: HTMLElement, config?: Config) => InitCarousel;
54
+ //#endregion
55
+ export { InitCarousel as a, Config as i, CarouselResponse as n, TranslationKey as o, CarouselStackHistory as r, initCarousel as t };