@mario_hahn/jumpy-ui 0.14.0 → 0.14.2

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.
@@ -0,0 +1,4331 @@
1
+ import {
2
+ JumpyAnimationDuration,
3
+ JumpyBorderWidth,
4
+ JumpyOpacity,
5
+ JumpyRadius,
6
+ JumpyShadowStyle,
7
+ JumpySize,
8
+ JumpySpacing,
9
+ JumpyTextStyle,
10
+ JumpyThemeScopeContext,
11
+ animationDurationValue,
12
+ borderWidthValue,
13
+ opacityValue,
14
+ radiusValue,
15
+ shadowValue,
16
+ sizeValue,
17
+ spacingValue,
18
+ textStyleValue,
19
+ themeToCSSVariables,
20
+ useJumpyTheme
21
+ } from "./chunk-2XOYK2JS.js";
22
+ import {
23
+ cn
24
+ } from "./chunk-3DBKYKP2.js";
25
+
26
+ // components.js
27
+ import { autoUpdate, flip, FloatingFocusManager, FloatingOverlay, FloatingPortal, offset, shift, size, useFloating } from "@floating-ui/react";
28
+ import { Children, cloneElement, createContext as createContext2, forwardRef, isValidElement, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
29
+
30
+ // field-context.js
31
+ import { createContext } from "react";
32
+ var JumpyFieldContext = createContext(null);
33
+
34
+ // components.js
35
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
36
+ var JumpyTableContext = createContext2(null);
37
+ var JumpyExpandableContext = createContext2(null);
38
+ var JumpySideMenuContext = createContext2(null);
39
+ var JumpyAppShellNavigationContext = createContext2(false);
40
+ var JumpyToggleButtonGroupContext = createContext2(null);
41
+ var JumpySnackbarContext = createContext2({ addSnackbar: () => "", removeSnackbar: () => {
42
+ }, clearAll: () => {
43
+ } });
44
+ var KeyboardKeys = {
45
+ ARROW_DOWN: "ArrowDown",
46
+ ARROW_LEFT: "ArrowLeft",
47
+ ARROW_RIGHT: "ArrowRight",
48
+ ARROW_UP: "ArrowUp",
49
+ END: "End",
50
+ ENTER: "Enter",
51
+ ESCAPE: "Escape",
52
+ HOME: "Home",
53
+ SPACE: " ",
54
+ TAB: "Tab"
55
+ };
56
+ function ChevronDownIcon(props) {
57
+ const theme = useJumpyTheme();
58
+ return /* @__PURE__ */ jsx("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, style: { display: "block", fontSize: sizeValue(theme, JumpySize.small), ...props.style || {} }, children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M7.41 8.295 6 9.705l6 6 6-6-1.41-1.41-4.59 4.58z", clipRule: "evenodd", fill: "currentColor" }) });
59
+ }
60
+ function ChevronUpIcon(props) {
61
+ const theme = useJumpyTheme();
62
+ return /* @__PURE__ */ jsx("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, style: { display: "block", fontSize: sizeValue(theme, JumpySize.small), ...props.style || {} }, children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "m16.59 15.705 1.41-1.41-6-6-6 6 1.41 1.41 4.59-4.58z", clipRule: "evenodd", fill: "currentColor" }) });
63
+ }
64
+ function CircleConfirmIcon(props) {
65
+ return /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M10 1.667a8.333 8.333 0 1 0 0 16.666 8.333 8.333 0 0 0 0-16.666Zm3.775 6.441-4.167 4.167a.833.833 0 0 1-1.178 0l-2.083-2.083a.833.833 0 1 1 1.178-1.179l1.494 1.494 3.577-3.577a.833.833 0 0 1 1.179 1.178Z" }) });
66
+ }
67
+ function CircleErrorIcon(props) {
68
+ return /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M10 1.667a8.333 8.333 0 1 0 0 16.666A8.333 8.333 0 0 0 10 1.667Zm0 4.166c.46 0 .833.373.833.834v3.75a.833.833 0 0 1-1.666 0v-3.75c0-.46.373-.834.833-.834Zm0 8.75a1.042 1.042 0 1 1 0-2.083 1.042 1.042 0 0 1 0 2.083Z" }) });
69
+ }
70
+ function CircleClearIcon(props) {
71
+ return /* @__PURE__ */ jsxs("svg", { "data-jumpy-stroke-icon": "true", width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, style: { display: "block", fill: "none", ...props.style || {} }, children: [
72
+ /* @__PURE__ */ jsx("path", { d: "M10 17.5a7.5 7.5 0 1 0 0-15 7.5 7.5 0 0 0 0 15Z", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }),
73
+ /* @__PURE__ */ jsx("path", { d: "m7.5 7.5 5 5M12.5 7.5l-5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
74
+ ] });
75
+ }
76
+ function supportsDisabledAttribute(tag) {
77
+ return ["button", "input", "select", "textarea", "option", "optgroup", "fieldset"].includes(tag);
78
+ }
79
+ function responsiveValue(value, defaults) {
80
+ if (value && typeof value === "object" && !Array.isArray(value)) {
81
+ return {
82
+ small: value.small ?? defaults.small,
83
+ medium: value.medium ?? value.small ?? defaults.medium,
84
+ large: value.large ?? value.medium ?? value.small ?? defaults.large,
85
+ xLarge: value.xLarge ?? value.large ?? value.medium ?? value.small ?? defaults.xLarge
86
+ };
87
+ }
88
+ return { small: value, medium: value, large: value, xLarge: value };
89
+ }
90
+ function gridSpacingValue(spacing, fallback = "1rem") {
91
+ const length = (value2) => typeof value2 === "number" ? `${value2}px` : value2;
92
+ if (Array.isArray(spacing)) return [length(spacing[0] ?? fallback), length(spacing[1] ?? fallback)];
93
+ const value = length(spacing ?? fallback);
94
+ return [value, value];
95
+ }
96
+ function responsiveGridSpacing(spacing) {
97
+ const defaults = { small: ["1rem", "1rem"], medium: ["1rem", "1rem"], large: ["1.5rem", "1.5rem"], xLarge: ["1.5rem", "1.5rem"] };
98
+ if (spacing == null || typeof spacing !== "object" || Array.isArray(spacing)) {
99
+ return {
100
+ small: gridSpacingValue(spacing ?? defaults.small, defaults.small[0]),
101
+ medium: gridSpacingValue(spacing ?? defaults.medium, defaults.medium[0]),
102
+ large: gridSpacingValue(spacing ?? defaults.large, defaults.large[0]),
103
+ xLarge: gridSpacingValue(spacing ?? defaults.xLarge, defaults.xLarge[0])
104
+ };
105
+ }
106
+ return {
107
+ small: gridSpacingValue(spacing.small ?? defaults.small, defaults.small[0]),
108
+ medium: gridSpacingValue(spacing.medium ?? spacing.small ?? defaults.medium, defaults.medium[0]),
109
+ large: gridSpacingValue(spacing.large ?? spacing.medium ?? spacing.small ?? defaults.large, defaults.large[0]),
110
+ xLarge: gridSpacingValue(spacing.xLarge ?? spacing.large ?? spacing.medium ?? spacing.small ?? defaults.xLarge, defaults.xLarge[0])
111
+ };
112
+ }
113
+ function isValueSelected(selectedValues, value) {
114
+ return value !== void 0 && Array.isArray(selectedValues) && selectedValues.includes(value);
115
+ }
116
+ function hasSelectedNestedItem(node, selectedValues) {
117
+ if (!Array.isArray(selectedValues) || !node) return false;
118
+ let found = false;
119
+ Children.forEach(node, (child) => {
120
+ if (found || !isValidElement(child)) return;
121
+ if (isValueSelected(selectedValues, child.props.value)) {
122
+ found = true;
123
+ return;
124
+ }
125
+ const nestedMenu = child.props.slots?.nestedMenu ?? child.props.nestedMenu;
126
+ if (hasSelectedNestedItem(nestedMenu ?? child.props.children, selectedValues)) found = true;
127
+ });
128
+ return found;
129
+ }
130
+ function focusSideMenuItem(itemRefs, direction) {
131
+ const items = itemRefs.map((ref) => ref?.current).filter((item) => item && item.dataset.disabled !== "true");
132
+ if (!items.length) return;
133
+ const currentIndex = Math.max(0, items.findIndex((item) => item === document.activeElement || item.contains(document.activeElement)));
134
+ const nextIndex = direction === "first" ? 0 : direction === "last" ? items.length - 1 : (currentIndex + direction + items.length) % items.length;
135
+ items[nextIndex]?.focus();
136
+ }
137
+ function transitionTimeToMs(value) {
138
+ return value.split(",").map((part) => part.trim()).reduce((max, part) => {
139
+ if (!part) return max;
140
+ const ms = part.endsWith("ms") ? Number.parseFloat(part) : Number.parseFloat(part) * 1e3;
141
+ return Number.isFinite(ms) ? Math.max(max, ms) : max;
142
+ }, 0);
143
+ }
144
+ function getWidthTransitionMs(element) {
145
+ const styles = window.getComputedStyle(element);
146
+ const properties = styles.transitionProperty.split(",").map((part) => part.trim());
147
+ const widthIndex = properties.findIndex((property) => property === "width" || property === "all");
148
+ if (widthIndex === -1) return 0;
149
+ return transitionTimeToMs(styles.transitionDuration) + transitionTimeToMs(styles.transitionDelay);
150
+ }
151
+ function splitNavigationItemProps(props) {
152
+ const linkKeys = /* @__PURE__ */ new Set(["href", "target", "rel", "download", "hrefLang", "ping", "referrerPolicy", "aria-current", "aria-disabled"]);
153
+ const wrapperProps = {};
154
+ const linkProps = {};
155
+ Object.entries(props).forEach(([key, value]) => {
156
+ if (linkKeys.has(key)) {
157
+ linkProps[key] = value;
158
+ return;
159
+ }
160
+ wrapperProps[key] = value;
161
+ });
162
+ return { wrapperProps, linkProps };
163
+ }
164
+ function clampNumber(value, min, max) {
165
+ return Math.min(Math.max(value, min), max);
166
+ }
167
+ function getButtonVariantStyles(theme, variant, status = "regular") {
168
+ const variantTokens = theme.actions[variant] || theme.actions.primary;
169
+ const tokens = variantTokens[status] || variantTokens.regular;
170
+ const elevated = variant === "primary";
171
+ const token = (state, slot, fallback) => variant === "primary" && status === "regular" ? `var(--jumpy-actions-primary-regular-${state}-${slot}, ${fallback})` : fallback;
172
+ return {
173
+ "--jumpy-action-default-surface": token("default", "surface", tokens.default.surface),
174
+ "--jumpy-action-default-text": token("default", "text", tokens.default.text),
175
+ "--jumpy-action-default-border": token("default", "border", tokens.default.border),
176
+ "--jumpy-action-default-icon": token("default", "icon", tokens.default.icon),
177
+ "--jumpy-action-hover-surface": token("hover", "surface", tokens.hover.surface),
178
+ "--jumpy-action-hover-text": token("hover", "text", tokens.hover.text),
179
+ "--jumpy-action-hover-icon": token("hover", "icon", tokens.hover.icon),
180
+ "--jumpy-action-hover-border": token("hover", "border", tokens.hover.border),
181
+ "--jumpy-action-pressed-surface": token("pressed", "surface", tokens.pressed.surface),
182
+ "--jumpy-action-pressed-text": token("pressed", "text", tokens.pressed.text),
183
+ "--jumpy-action-pressed-icon": token("pressed", "icon", tokens.pressed.icon),
184
+ "--jumpy-action-pressed-border": token("pressed", "border", tokens.pressed.border),
185
+ "--jumpy-action-focus-surface": token("focus", "surface", tokens.focus.surface),
186
+ "--jumpy-action-focus-text": token("focus", "text", tokens.focus.text),
187
+ "--jumpy-action-focus-icon": token("focus", "icon", tokens.focus.icon),
188
+ "--jumpy-action-focus-border": token("focus", "border", tokens.focus.border),
189
+ "--jumpy-action-focus-ring": token("focus", "focus-ring", tokens.focus.focusRing),
190
+ "--jumpy-action-disabled-surface": token("disabled", "surface", tokens.disabled.surface),
191
+ "--jumpy-action-disabled-text": token("disabled", "text", tokens.disabled.text),
192
+ "--jumpy-action-disabled-icon": token("disabled", "icon", tokens.disabled.icon),
193
+ "--jumpy-action-disabled-border": token("disabled", "border", tokens.disabled.border),
194
+ "--jumpy-action-hover-shadow": "none",
195
+ "--jumpy-action-focus-shadow": elevated ? shadowValue(theme, JumpyShadowStyle.positiveSmall) : "none",
196
+ "--jumpy-action-pressed-shadow": "none",
197
+ "--jumpy-action-default-shadow": elevated ? shadowValue(theme, JumpyShadowStyle.positiveMedium) : "none"
198
+ };
199
+ }
200
+ function getNoticeToneStyles(theme, tone) {
201
+ switch (tone) {
202
+ case "success":
203
+ return {
204
+ backgroundColor: theme.color.successSurface,
205
+ borderColor: theme.color.successBorder,
206
+ color: theme.color.successText
207
+ };
208
+ case "warning":
209
+ return {
210
+ backgroundColor: theme.color.warningSurface,
211
+ borderColor: theme.color.warningBorder,
212
+ color: theme.color.warningText
213
+ };
214
+ case "danger":
215
+ return {
216
+ backgroundColor: theme.color.dangerSurface,
217
+ borderColor: theme.color.dangerBorder,
218
+ color: theme.color.dangerText
219
+ };
220
+ default:
221
+ return {
222
+ backgroundColor: theme.color.infoSurface,
223
+ borderColor: theme.color.infoBorder,
224
+ color: theme.color.textPrimary
225
+ };
226
+ }
227
+ }
228
+ function getBannerStatusStyles(theme, status) {
229
+ switch (status) {
230
+ case "positive":
231
+ return { indicator: theme.color.positiveBase, indicatorSurface: theme.color.positiveUltralight, inlineSurface: theme.color.positiveExtralight };
232
+ case "warning":
233
+ return { indicator: theme.color.warningBase, indicatorSurface: theme.color.warningUltralight, inlineSurface: theme.color.warningExtralight };
234
+ case "alarm":
235
+ return { indicator: theme.color.alarmBase, indicatorSurface: theme.color.alarmUltralight, inlineSurface: theme.color.alarmExtralight };
236
+ case "info":
237
+ return { indicator: theme.color.neutralBase, indicatorSurface: theme.color.neutralMegalight, inlineSurface: `rgba(244, 247, 245, ${opacityValue(theme, JumpyOpacity.medium)})` };
238
+ default:
239
+ return { indicator: theme.color.neutralBase, indicatorSurface: "transparent", inlineSurface: "transparent" };
240
+ }
241
+ }
242
+ function JumpSeverityIcon({ status = "other", icon, style }) {
243
+ if (status === "other" && icon) return icon;
244
+ const common = {
245
+ viewBox: "0 0 24 24",
246
+ width: "100%",
247
+ height: "100%",
248
+ fill: "currentColor",
249
+ xmlns: "http://www.w3.org/2000/svg",
250
+ "aria-hidden": true,
251
+ focusable: false,
252
+ style
253
+ };
254
+ switch (status) {
255
+ case "positive":
256
+ return /* @__PURE__ */ jsx("svg", { ...common, children: /* @__PURE__ */ jsx("path", { d: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm4.3 7.8-5 5a1 1 0 0 1-1.4 0l-2.2-2.2a1 1 0 1 1 1.4-1.4l1.5 1.5 4.3-4.3a1 1 0 1 1 1.4 1.4Z" }) });
257
+ case "warning":
258
+ return /* @__PURE__ */ jsx("svg", { ...common, children: /* @__PURE__ */ jsx("path", { d: "M11.1 3.5a1 1 0 0 1 1.8 0l8.2 15A1 1 0 0 1 20.2 20H3.8a1 1 0 0 1-.9-1.5l8.2-15ZM12 8a1 1 0 0 0-1 1v4a1 1 0 1 0 2 0V9a1 1 0 0 0-1-1Zm0 9.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z" }) });
259
+ case "alarm":
260
+ return /* @__PURE__ */ jsx("svg", { ...common, children: /* @__PURE__ */ jsx("path", { d: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm1 13a1 1 0 1 1-2 0V7a1 1 0 1 1 2 0v8Zm-1 4a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5Z" }) });
261
+ case "info":
262
+ return /* @__PURE__ */ jsx("svg", { ...common, children: /* @__PURE__ */ jsx("path", { d: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm1 15a1 1 0 1 1-2 0v-6a1 1 0 1 1 2 0v6Zm-1-8.5A1.25 1.25 0 1 1 12 6a1.25 1.25 0 0 1 0 2.5Z" }) });
263
+ default:
264
+ return /* @__PURE__ */ jsx("svg", { ...common, children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }) });
265
+ }
266
+ }
267
+ function renderBannerSlot(slot, fallbackTag = "span", className, style) {
268
+ if (!slot) return null;
269
+ if (typeof slot === "string" || typeof slot === "number") {
270
+ const Tag = fallbackTag;
271
+ return /* @__PURE__ */ jsx(Tag, { className, style, children: slot });
272
+ }
273
+ if (isValidElement(slot)) {
274
+ return cloneElement(slot, {
275
+ className: cn(className, slot.props.className),
276
+ style: slot.props.style || style ? { ...style, ...slot.props.style } : void 0
277
+ });
278
+ }
279
+ return slot;
280
+ }
281
+ function getBadgeStatusStyles(theme, status) {
282
+ const alwaysLight = "var(--jumpy-color-non-color-always-ultralight, #FFFFFF)";
283
+ switch (status) {
284
+ case "positive":
285
+ return { backgroundColor: theme.color.positiveBase, color: alwaysLight };
286
+ case "warning":
287
+ return { backgroundColor: theme.color.warningBase, color: theme.color.nonColorAlwaysUltradark };
288
+ case "alarm":
289
+ return { backgroundColor: theme.color.alarmBase, color: alwaysLight };
290
+ case "active":
291
+ return { backgroundColor: "var(--jumpy-color-primary-base)", color: theme.color.textOnPrimary };
292
+ default:
293
+ return { backgroundColor: theme.color.neutralBase, color: alwaysLight };
294
+ }
295
+ }
296
+ function getBadgeSizeStyles(theme, size2) {
297
+ const labelSmall = textStyleValue(theme, JumpyTextStyle.labelSmallEmphasized);
298
+ const labelMedium = textStyleValue(theme, JumpyTextStyle.labelMediumEmphasized);
299
+ switch (size2) {
300
+ case "xSmall":
301
+ return {
302
+ width: sizeValue(theme, JumpySize.small3XS),
303
+ height: sizeValue(theme, JumpySize.small3XS),
304
+ minWidth: sizeValue(theme, JumpySize.small3XS),
305
+ padding: spacingValue(theme, JumpySpacing.zero),
306
+ fontSize: 0,
307
+ lineHeight: 0
308
+ };
309
+ case "medium":
310
+ return {
311
+ minWidth: sizeValue(theme, JumpySize.small),
312
+ height: sizeValue(theme, JumpySize.small),
313
+ padding: `${spacingValue(theme, JumpySpacing.small3XS)}px ${spacingValue(theme, JumpySpacing.smallXS)}px`,
314
+ fontFamily: theme.fontFamily.familyBase,
315
+ fontSize: labelMedium.fontSize,
316
+ // A line box filling the whole-pixel content height keeps the baseline from rounding the digits upward.
317
+ lineHeight: `${sizeValue(theme, JumpySize.small) - spacingValue(theme, JumpySpacing.small3XS) * 2}px`,
318
+ fontWeight: labelMedium.fontWeight
319
+ };
320
+ default:
321
+ return {
322
+ minWidth: sizeValue(theme, JumpySize.small2XS),
323
+ height: sizeValue(theme, JumpySize.small2XS),
324
+ padding: `${spacingValue(theme, JumpySpacing.zero)}px ${spacingValue(theme, JumpySpacing.small3XS)}px`,
325
+ fontFamily: theme.fontFamily.familyBase,
326
+ fontSize: labelSmall.fontSize,
327
+ lineHeight: `${sizeValue(theme, JumpySize.small2XS)}px`,
328
+ fontWeight: labelSmall.fontWeight
329
+ };
330
+ }
331
+ }
332
+ function getTagStatusStyles(theme, status) {
333
+ switch (status) {
334
+ case "positive":
335
+ return { backgroundColor: theme.color.successSurface, color: theme.color.successText, borderColor: theme.color.successBorder };
336
+ case "warning":
337
+ return { backgroundColor: theme.color.warningSurface, color: theme.color.warningText, borderColor: theme.color.warningBorder };
338
+ case "alarm":
339
+ return { backgroundColor: theme.color.dangerSurface, color: theme.color.dangerText, borderColor: theme.color.dangerBorder };
340
+ default:
341
+ return { backgroundColor: theme.color.surfaceField, color: theme.color.textPrimary, borderColor: theme.color.borderSubtle };
342
+ }
343
+ }
344
+ function getTypographyStyle(theme, variant = "bodyMedium") {
345
+ const style = textStyleValue(theme, JumpyTextStyle[variant] || JumpyTextStyle.bodyMedium);
346
+ return {
347
+ fontFamily: theme.fontFamily.familyBase,
348
+ fontSize: style.fontSize,
349
+ lineHeight: style.lineHeight,
350
+ fontWeight: style.fontWeight,
351
+ letterSpacing: style.letterSpacing
352
+ };
353
+ }
354
+ function getCardStateStyles(theme, selected = false) {
355
+ const inactive = theme.card.inactive;
356
+ const active = theme.card.active;
357
+ const tokens = selected ? active : inactive;
358
+ const state = selected ? "active" : "inactive";
359
+ const cardToken = (interaction, slot, fallback) => `var(--jumpy-card-${state}-${interaction}-${slot}, ${fallback})`;
360
+ return {
361
+ "--jumpy-card-default-surface": cardToken("default", "surface", tokens.default.surface),
362
+ "--jumpy-card-default-border": cardToken("default", "border", tokens.default.border),
363
+ "--jumpy-card-default-text": cardToken("default", "text", tokens.default.text),
364
+ "--jumpy-card-default-icon": cardToken("default", "icon", tokens.default.icon),
365
+ "--jumpy-card-hover-surface": cardToken("hover", "surface", tokens.hover.surface),
366
+ "--jumpy-card-hover-border": cardToken("hover", "border", tokens.hover.border),
367
+ "--jumpy-card-hover-text": cardToken("hover", "text", tokens.hover.text),
368
+ "--jumpy-card-hover-icon": cardToken("hover", "icon", tokens.hover.icon),
369
+ "--jumpy-card-pressed-surface": cardToken("pressed", "surface", tokens.pressed.surface),
370
+ "--jumpy-card-pressed-border": cardToken("pressed", "border", tokens.pressed.border),
371
+ "--jumpy-card-pressed-text": cardToken("pressed", "text", tokens.pressed.text),
372
+ "--jumpy-card-pressed-icon": cardToken("pressed", "icon", tokens.pressed.icon),
373
+ "--jumpy-card-focus-surface": cardToken("focus", "surface", tokens.focus.surface),
374
+ "--jumpy-card-focus-border": cardToken("focus", "border", tokens.focus.border),
375
+ "--jumpy-card-focus-ring": cardToken("focus-focus", "ring", tokens.focus.focusRing),
376
+ "--jumpy-card-focus-text": cardToken("focus", "text", tokens.focus.text),
377
+ "--jumpy-card-focus-icon": cardToken("focus", "icon", tokens.focus.icon),
378
+ "--jumpy-card-disabled-surface": cardToken("disabled", "surface", tokens.disabled.surface),
379
+ "--jumpy-card-disabled-border": cardToken("disabled", "border", tokens.disabled.border),
380
+ "--jumpy-card-disabled-text": cardToken("disabled", "text", tokens.disabled.text),
381
+ "--jumpy-card-disabled-icon": cardToken("disabled", "icon", tokens.disabled.icon)
382
+ };
383
+ }
384
+ function getListItemStateStyles(theme, selected = false) {
385
+ const tokens = selected ? theme.listItem.active : theme.listItem.inactive;
386
+ return {
387
+ "--jumpy-list-item-default-surface": tokens.default.surface,
388
+ "--jumpy-list-item-default-text": tokens.default.text,
389
+ "--jumpy-list-item-default-subtext": tokens.default.subtext,
390
+ "--jumpy-list-item-default-icon": tokens.default.icon,
391
+ "--jumpy-list-item-hover-surface": tokens.hover.surface,
392
+ "--jumpy-list-item-hover-text": tokens.hover.text,
393
+ "--jumpy-list-item-hover-subtext": tokens.hover.subtext,
394
+ "--jumpy-list-item-hover-icon": tokens.hover.icon,
395
+ "--jumpy-list-item-pressed-surface": tokens.pressed.surface,
396
+ "--jumpy-list-item-pressed-text": tokens.pressed.text,
397
+ "--jumpy-list-item-pressed-subtext": tokens.pressed.subtext,
398
+ "--jumpy-list-item-pressed-icon": tokens.pressed.icon,
399
+ "--jumpy-list-item-focus-surface": tokens.focus.surface,
400
+ "--jumpy-list-item-focus-text": tokens.focus.text,
401
+ "--jumpy-list-item-focus-subtext": tokens.focus.subtext,
402
+ "--jumpy-list-item-focus-icon": tokens.focus.icon,
403
+ "--jumpy-list-item-focus-ring": tokens.focus.focusRing,
404
+ "--jumpy-list-item-disabled-surface": tokens.disabled.surface,
405
+ "--jumpy-list-item-disabled-text": tokens.disabled.text,
406
+ "--jumpy-list-item-disabled-subtext": tokens.disabled.subtext,
407
+ "--jumpy-list-item-disabled-icon": tokens.disabled.icon
408
+ };
409
+ }
410
+ function getButtonSpacing(theme, variant, hasLeading, hasTrailing) {
411
+ const gap = spacingValue(theme, JumpySpacing.smallXS);
412
+ const verticalPadding = spacingValue(theme, JumpySpacing.small2XS);
413
+ if (variant === "borderless") {
414
+ return {
415
+ gap,
416
+ paddingLeft: spacingValue(theme, JumpySpacing.zero),
417
+ paddingRight: spacingValue(theme, JumpySpacing.zero),
418
+ paddingTop: verticalPadding,
419
+ paddingBottom: verticalPadding
420
+ };
421
+ }
422
+ return {
423
+ gap,
424
+ paddingLeft: hasLeading ? spacingValue(theme, JumpySpacing.medium) : spacingValue(theme, JumpySpacing.medium) + spacingValue(theme, JumpySpacing.small3XS),
425
+ paddingRight: hasTrailing ? spacingValue(theme, JumpySpacing.medium) : spacingValue(theme, JumpySpacing.medium) + spacingValue(theme, JumpySpacing.small3XS),
426
+ paddingTop: verticalPadding,
427
+ paddingBottom: verticalPadding
428
+ };
429
+ }
430
+ function getFieldInputStyles(theme, disabled) {
431
+ const bodyMedium = textStyleValue(theme, JumpyTextStyle.bodyMedium);
432
+ return {
433
+ display: "block",
434
+ width: "100%",
435
+ boxSizing: "border-box",
436
+ color: "var(--jumpy-cmp-base-input-value-color)",
437
+ padding: 0,
438
+ margin: 0,
439
+ border: 0,
440
+ outline: 0,
441
+ background: "transparent",
442
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid),
443
+ fontFamily: theme.fontFamily.familyBase,
444
+ fontSize: bodyMedium.fontSize,
445
+ lineHeight: `${bodyMedium.fontSize * bodyMedium.lineHeight}px`,
446
+ transition: `color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${theme.animation.timeEasingRelease}`
447
+ };
448
+ }
449
+ function getFieldShellStyles(theme, disabled, status = "regular", focused = false, readOnly = false) {
450
+ const outlineWidth = borderWidthValue(theme, focused && !readOnly ? JumpyBorderWidth.medium : JumpyBorderWidth.small);
451
+ const easing = focused && !readOnly ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease;
452
+ return {
453
+ display: "flex",
454
+ alignItems: "center",
455
+ width: "100%",
456
+ minHeight: sizeValue(theme, JumpySize.largeXL),
457
+ height: sizeValue(theme, JumpySize.largeXL),
458
+ boxSizing: "border-box",
459
+ borderRadius: radiusValue(theme, JumpyRadius.small),
460
+ border: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid transparent`,
461
+ outline: readOnly ? "none" : `${outlineWidth}px solid var(--jumpy-cmp-base-input-border-color)`,
462
+ outlineOffset: readOnly ? void 0 : -outlineWidth,
463
+ backgroundColor: "var(--jumpy-cmp-base-input-surface-color)",
464
+ paddingLeft: spacingValue(theme, JumpySpacing.large),
465
+ paddingRight: spacingValue(theme, JumpySpacing.small3XS),
466
+ paddingTop: spacingValue(theme, JumpySpacing.small3XS),
467
+ paddingBottom: spacingValue(theme, JumpySpacing.small3XS),
468
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid),
469
+ gap: spacingValue(theme, JumpySpacing.medium),
470
+ cursor: disabled || readOnly ? "default" : "text",
471
+ pointerEvents: disabled ? "none" : void 0,
472
+ transition: `background-color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${easing}, outline-color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${easing}, outline-width ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${easing}`
473
+ };
474
+ }
475
+ function renderStandaloneField(theme, disabled, className, style, inputElement, status = "regular", readOnly = false) {
476
+ return /* @__PURE__ */ jsx(
477
+ "div",
478
+ {
479
+ "data-jumpy-component": "field-control",
480
+ "data-disabled": disabled ? "true" : "false",
481
+ "data-readonly": readOnly ? "true" : "false",
482
+ "data-status": status,
483
+ className: cn(className),
484
+ style: {
485
+ ...getFieldShellStyles(theme, disabled, status, false, readOnly),
486
+ ...style || {}
487
+ },
488
+ children: inputElement
489
+ }
490
+ );
491
+ }
492
+ function JumpBackground({ snow = true }) {
493
+ const theme = useJumpyTheme();
494
+ return /* @__PURE__ */ jsxs("div", { className: "pointer-events-none fixed inset-0 z-0 overflow-hidden", style: { pointerEvents: "none" }, children: [
495
+ /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 top-[-18rem] h-[34rem] w-[34rem] -translate-x-1/2 rounded-full blur-[120px]", style: { backgroundColor: theme.color.infoSurface } }),
496
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-24 right-[-10rem] h-[28rem] w-[28rem] rounded-full blur-[110px]", style: { backgroundColor: theme.color.borderSubtle } }),
497
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(rgba(26,28,31,0.16)_1px,transparent_1px)] bg-[size:22px_22px]", style: { opacity: theme.opacity.subtle } }),
498
+ snow && /* @__PURE__ */ jsx("div", { className: "snowfall", "aria-hidden": "true" })
499
+ ] });
500
+ }
501
+ function JumpBadge({ as, size: size2 = "medium", status = "regular", hidden = false, children, className = "", ...props }) {
502
+ const theme = useJumpyTheme();
503
+ const visible = !hidden;
504
+ const Root = as || "div";
505
+ return /* @__PURE__ */ jsx(
506
+ Root,
507
+ {
508
+ ...props,
509
+ "data-jumpy-component": "badge",
510
+ "data-size": size2,
511
+ "data-status": status,
512
+ "data-visible": visible ? "true" : "false",
513
+ role: props.role || "status",
514
+ "aria-live": props["aria-live"] || "polite",
515
+ "aria-hidden": hidden || void 0,
516
+ className: cn("inline-flex items-center justify-center box-border", className),
517
+ style: {
518
+ display: "inline-flex",
519
+ alignItems: "center",
520
+ justifyContent: "center",
521
+ boxSizing: "border-box",
522
+ ...getBadgeSizeStyles(theme, size2),
523
+ ...getBadgeStatusStyles(theme, status),
524
+ borderRadius: radiusValue(theme, JumpyRadius.full),
525
+ letterSpacing: "normal",
526
+ opacity: visible ? opacityValue(theme, JumpyOpacity.solid) : opacityValue(theme, JumpyOpacity.transparent),
527
+ scale: visible ? 1 : 0.25,
528
+ width: size2 === "xSmall" ? sizeValue(theme, JumpySize.small3XS) : "fit-content",
529
+ transition: `width ${animationDurationValue(theme, JumpyAnimationDuration.medium)}s ${theme.animation.timeEasingDrift}, opacity ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${visible ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease}, scale ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${visible ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease}`,
530
+ ...props.style || {}
531
+ },
532
+ children: size2 === "xSmall" ? null : /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "badge-content", style: { display: "inline-block", lineHeight: "inherit", fontVariantNumeric: "tabular-nums" }, children })
533
+ }
534
+ );
535
+ }
536
+ function BrandMark({ compact = false, label = "Jumpy UI" }) {
537
+ const theme = useJumpyTheme();
538
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-3", !compact && "justify-center"), children: [
539
+ /* @__PURE__ */ jsx("div", { className: "grid h-10 w-10 place-items-center rounded-xl text-xl font-black", style: { backgroundColor: theme.color.surfaceCard, color: theme.color.surfacePrimary, borderRadius: theme.radius.medium }, children: "J" }),
540
+ /* @__PURE__ */ jsx("span", { className: "block text-lg font-extrabold tracking-tight", style: { color: theme.color.textPrimary, fontFamily: theme.typography.fontFamily }, children: label })
541
+ ] });
542
+ }
543
+ function JumpCard({ as, ref, children, className = "", variant = "filled", interactive = false, selected = false, disabled = false, dragged = false, innerClasses = {}, ...props }) {
544
+ const theme = useJumpyTheme();
545
+ const Component = as || "div";
546
+ const cardRef = useRef(null);
547
+ const [pressScaleLimit, setPressScaleLimit] = useState(1);
548
+ const pressDistance = spacingValue(theme, JumpySpacing.small3XS);
549
+ useLayoutEffect(() => {
550
+ const element = cardRef.current;
551
+ if (!element || !interactive || disabled) return;
552
+ const measure = () => {
553
+ const longestSide = Math.max(element.offsetWidth, element.offsetHeight);
554
+ setPressScaleLimit(longestSide > 0 ? Math.max(0, 1 - pressDistance / longestSide) : 1);
555
+ };
556
+ measure();
557
+ const observer = new ResizeObserver(measure);
558
+ observer.observe(element);
559
+ return () => observer.disconnect();
560
+ }, [Component, interactive, disabled, pressDistance]);
561
+ const isButton = Component === "button";
562
+ const activity = selected ? "active" : "inactive";
563
+ const draggedStyles = dragged && !disabled ? {
564
+ borderColor: `var(--jumpy-card-${activity}-dragged-border)`,
565
+ backgroundColor: `var(--jumpy-card-${activity}-dragged-surface)`
566
+ } : {};
567
+ const hasPaddingUtility = /(?:^|\s)(?:[a-z]+:)*p(?:[trblxy])?-/.test(className);
568
+ return /* @__PURE__ */ jsxs(
569
+ Component,
570
+ {
571
+ ...props,
572
+ ref: (node) => {
573
+ cardRef.current = node;
574
+ if (typeof ref === "function") return ref(node);
575
+ if (ref) ref.current = node;
576
+ },
577
+ type: isButton ? props.type || "button" : props.type,
578
+ disabled: isButton ? disabled : void 0,
579
+ "data-jumpy-component": "card",
580
+ "data-variant": variant,
581
+ "data-interactive": interactive ? "true" : "false",
582
+ "data-selected": selected ? "true" : "false",
583
+ "data-dragged": dragged ? "true" : "false",
584
+ "data-disabled": disabled ? "true" : "false",
585
+ tabIndex: interactive && !disabled ? props.tabIndex ?? 0 : props.tabIndex,
586
+ "aria-disabled": disabled || void 0,
587
+ className: cn(className),
588
+ style: {
589
+ ...getCardStateStyles(theme, selected),
590
+ "--jumpy-card-press-scale-limit": pressScaleLimit,
591
+ display: "block",
592
+ position: "relative",
593
+ boxSizing: "border-box",
594
+ overflow: "hidden",
595
+ appearance: isButton ? "none" : void 0,
596
+ WebkitAppearance: isButton ? "none" : void 0,
597
+ outline: "none",
598
+ border: 0,
599
+ borderRadius: theme.sizing.cardRadius,
600
+ padding: props.style?.padding === void 0 && !hasPaddingUtility ? spacingValue(theme, JumpySpacing.large) : void 0,
601
+ backgroundColor: "var(--jumpy-card-default-surface)",
602
+ boxShadow: variant === "filled" ? theme.shadow.card : "none",
603
+ color: "var(--jumpy-card-default-text, var(--jumpy-color-text-primary))",
604
+ ...draggedStyles,
605
+ ...props.style || {}
606
+ },
607
+ children: [
608
+ interactive && selected ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "checkmark", className: innerClasses.checkmark?.className, style: { position: "absolute", right: "var(--jumpy-cmp-card-checkmark-right)", top: "var(--jumpy-cmp-card-checkmark-top)", display: "flex", color: "var(--jumpy-card-default-icon)", ...innerClasses.checkmark?.style || {} }, children: /* @__PURE__ */ jsx(CircleConfirmIcon, { "data-jumpy-slot": "checkmark-icon", className: innerClasses.checkmarkIcon?.className, style: { width: sizeValue(theme, JumpySize.small), height: sizeValue(theme, JumpySize.small), ...innerClasses.checkmarkIcon?.style || {} } }) }) : null,
609
+ children
610
+ ]
611
+ }
612
+ );
613
+ }
614
+ function JumpGrid({ as, columns = { small: 8, medium: 12, large: 12, xLarge: 12 }, spacing = { small: ["1rem", "1rem"], medium: ["1rem", "1rem"], large: ["1.5rem", "1.5rem"], xLarge: ["1.5rem", "1.5rem"] }, children, className = "", ...props }) {
615
+ const Component = as || "div";
616
+ const resolvedColumns = responsiveValue(columns, { small: 8, medium: 12, large: 12, xLarge: 12 });
617
+ const resolvedSpacing = responsiveGridSpacing(spacing);
618
+ return /* @__PURE__ */ jsx(
619
+ Component,
620
+ {
621
+ ...props,
622
+ "data-jumpy-component": "grid",
623
+ className: cn(className),
624
+ style: {
625
+ "--jumpy-cmp-grid-columns-small": resolvedColumns.small,
626
+ "--jumpy-cmp-grid-columns-medium": resolvedColumns.medium,
627
+ "--jumpy-cmp-grid-columns-large": resolvedColumns.large,
628
+ "--jumpy-cmp-grid-columns-xLarge": resolvedColumns.xLarge,
629
+ "--jumpy-cmp-grid-spacing-row-small": resolvedSpacing.small[0],
630
+ "--jumpy-cmp-grid-spacing-column-small": resolvedSpacing.small[1],
631
+ "--jumpy-cmp-grid-spacing-row-medium": resolvedSpacing.medium[0],
632
+ "--jumpy-cmp-grid-spacing-column-medium": resolvedSpacing.medium[1],
633
+ "--jumpy-cmp-grid-spacing-row-large": resolvedSpacing.large[0],
634
+ "--jumpy-cmp-grid-spacing-column-large": resolvedSpacing.large[1],
635
+ "--jumpy-cmp-grid-spacing-row-xLarge": resolvedSpacing.xLarge[0],
636
+ "--jumpy-cmp-grid-spacing-column-xLarge": resolvedSpacing.xLarge[1],
637
+ ...props.style || {}
638
+ },
639
+ children
640
+ }
641
+ );
642
+ }
643
+ function JumpGridItem({ as, span = 1, horizontalPlacement = "stretch", verticalPlacement = "stretch", children, className = "", ...props }) {
644
+ const Component = as || "div";
645
+ const resolvedSpan = responsiveValue(span, { small: 1, medium: 1, large: 1, xLarge: 1 });
646
+ return /* @__PURE__ */ jsx(
647
+ Component,
648
+ {
649
+ ...props,
650
+ "data-jumpy-component": "grid-item",
651
+ "data-horizontal-placement": horizontalPlacement,
652
+ "data-vertical-placement": verticalPlacement,
653
+ className: cn(className),
654
+ style: {
655
+ "--jumpy-cmp-grid-item-span-small": resolvedSpan.small,
656
+ "--jumpy-cmp-grid-item-span-medium": resolvedSpan.medium,
657
+ "--jumpy-cmp-grid-item-span-large": resolvedSpan.large,
658
+ "--jumpy-cmp-grid-item-span-xLarge": resolvedSpan.xLarge,
659
+ ...props.style || {}
660
+ },
661
+ children
662
+ }
663
+ );
664
+ }
665
+ function JumpList({ as, children, className = "", ...props }) {
666
+ const Component = as || "div";
667
+ return /* @__PURE__ */ jsx(
668
+ Component,
669
+ {
670
+ ...props,
671
+ "data-jumpy-component": "list",
672
+ role: props.role || "list",
673
+ className: cn(className),
674
+ style: {
675
+ display: "block",
676
+ width: "100%",
677
+ height: "fit-content",
678
+ overflowX: "hidden",
679
+ overflowY: "auto",
680
+ ...props.style || {}
681
+ },
682
+ children
683
+ }
684
+ );
685
+ }
686
+ function JumpListItem({ as, children, className = "", selected = false, disabled = false, readOnly = false, focusedView = false, leading, trailing, metadata, description, slots = {}, nestedCollapsed, defaultNestedCollapsed = true, onNestedCollapsedChange, truncateTexts = false, innerClasses = {}, ref, ...props }) {
687
+ const theme = useJumpyTheme();
688
+ const Component = as || "button";
689
+ const isButton = Component === "button";
690
+ const componentName = props["data-jumpy-component"] || "list-item";
691
+ const resolvedLeading = slots.leading ?? leading;
692
+ const resolvedTrailing = slots.trailing ?? trailing;
693
+ const resolvedMetadata = slots.metadata ?? metadata;
694
+ const resolvedDescription = slots.description ?? description;
695
+ const resolvedSelector = slots.selector;
696
+ const resolvedNestedList = slots.nestedList;
697
+ const [internalNestedCollapsed, setInternalNestedCollapsed] = useState(defaultNestedCollapsed);
698
+ const isNestedCollapsed = nestedCollapsed ?? internalNestedCollapsed;
699
+ const hasNestedList = Boolean(resolvedNestedList);
700
+ const isDisabled = disabled || readOnly;
701
+ const bodyMedium = textStyleValue(theme, JumpyTextStyle.bodyMedium);
702
+ const bodySmall = textStyleValue(theme, JumpyTextStyle.bodySmall);
703
+ const iconSize = sizeValue(theme, JumpySize.small2XS);
704
+ const handleExpandedChange = () => {
705
+ const nextCollapsed = !isNestedCollapsed;
706
+ if (nestedCollapsed === void 0) setInternalNestedCollapsed(nextCollapsed);
707
+ onNestedCollapsedChange?.(nextCollapsed);
708
+ };
709
+ const handleClick = (event) => {
710
+ if (isDisabled) return;
711
+ if (hasNestedList) handleExpandedChange();
712
+ props.onClick?.(event);
713
+ };
714
+ const handleKeyDown = (event) => {
715
+ if ((event.key === KeyboardKeys.ENTER || event.key === KeyboardKeys.SPACE) && hasNestedList && !isDisabled) {
716
+ event.preventDefault();
717
+ handleExpandedChange();
718
+ }
719
+ props.onKeyDown?.(event);
720
+ };
721
+ return /* @__PURE__ */ jsxs(
722
+ Component,
723
+ {
724
+ ...props,
725
+ ref,
726
+ type: isButton ? props.type || "button" : props.type,
727
+ disabled: isButton ? disabled : void 0,
728
+ "aria-disabled": !isButton && isDisabled ? true : void 0,
729
+ "aria-current": selected ? props["aria-current"] || "true" : props["aria-current"],
730
+ "aria-selected": props.role === "option" || props.role === "treeitem" ? selected : props["aria-selected"],
731
+ "aria-expanded": hasNestedList ? !isNestedCollapsed : props["aria-expanded"],
732
+ onClick: handleClick,
733
+ onKeyDown: handleKeyDown,
734
+ "data-jumpy-component": componentName,
735
+ "data-selected": selected ? "true" : "false",
736
+ "data-disabled": disabled ? "true" : "false",
737
+ "data-readonly": readOnly ? "true" : "false",
738
+ "data-focused-view": focusedView ? "true" : void 0,
739
+ role: props.role || "listitem",
740
+ className: cn("text-left", className),
741
+ style: {
742
+ ...getListItemStateStyles(theme, selected),
743
+ display: "grid",
744
+ textAlign: "start",
745
+ gridTemplateColumns: `${resolvedSelector ? "min-content " : ""}${resolvedLeading ? "min-content " : ""}minmax(0, 1fr) ${resolvedMetadata ? "minmax(0, min-content) " : ""}${resolvedTrailing || hasNestedList ? "min-content" : ""}`,
746
+ gridTemplateRows: resolvedDescription ? "auto auto" : "auto",
747
+ alignItems: "center",
748
+ gap: spacingValue(theme, JumpySpacing.large),
749
+ width: "100%",
750
+ minHeight: sizeValue(theme, JumpySize.large) + spacingValue(theme, JumpySpacing.large),
751
+ boxSizing: "border-box",
752
+ padding: `${spacingValue(theme, JumpySpacing.medium)}px ${spacingValue(theme, JumpySpacing.large)}px`,
753
+ border: 0,
754
+ borderRadius: radiusValue(theme, JumpyRadius.small),
755
+ backgroundColor: componentName === "side-menu-item" ? "var(--jumpy-list-item-surface, var(--jumpy-list-item-default-surface))" : "transparent",
756
+ color: "var(--jumpy-list-item-color, var(--jumpy-list-item-default-text))",
757
+ cursor: disabled ? "not-allowed" : readOnly ? "default" : "pointer",
758
+ fontFamily: theme.fontFamily.familyBase,
759
+ fontSize: bodyMedium.fontSize,
760
+ lineHeight: bodyMedium.lineHeight,
761
+ fontWeight: bodyMedium.fontWeight,
762
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid),
763
+ pointerEvents: readOnly ? "none" : void 0,
764
+ position: "relative",
765
+ zIndex: 1,
766
+ ...props.style || {}
767
+ },
768
+ children: [
769
+ resolvedSelector ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "selector", "aria-hidden": props.role === "option" || props.role === "treeitem" ? "true" : void 0, className: innerClasses.selector?.className, style: { display: "inline-flex", pointerEvents: "none", color: "var(--jumpy-list-item-icon-color, var(--jumpy-list-item-default-icon))", ...innerClasses.selector?.style || {} }, children: resolvedSelector }) : null,
770
+ resolvedLeading ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "leading", "aria-hidden": "true", className: innerClasses.leading?.className, style: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, width: iconSize, height: iconSize, color: "var(--jumpy-list-item-icon-color, var(--jumpy-list-item-default-icon))", ...innerClasses.leading?.style || {} }, children: resolvedLeading }) : null,
771
+ /* @__PURE__ */ jsxs("span", { style: { display: "grid", minWidth: 0 }, children: [
772
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "label", "data-truncate": truncateTexts ? "true" : void 0, "data-text": typeof children === "string" ? children : void 0, className: innerClasses.label?.className, style: { minWidth: 0, maxWidth: "100%", overflowWrap: "anywhere", wordBreak: "break-word", fontWeight: bodyMedium.fontWeight, ...innerClasses.label?.style || {} }, children }),
773
+ resolvedDescription ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "description", className: innerClasses.description?.className, style: { color: selected ? theme.listItem.active.default.subtext : theme.listItem.inactive.default.subtext, fontSize: bodySmall.fontSize, lineHeight: bodySmall.lineHeight, ...innerClasses.description?.style || {} }, children: resolvedDescription }) : null
774
+ ] }),
775
+ resolvedMetadata ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "metadata", className: innerClasses.metadata?.className, style: { color: selected ? theme.listItem.active.default.subtext : theme.listItem.inactive.default.subtext, whiteSpace: "nowrap", fontSize: bodySmall.fontSize, lineHeight: bodySmall.lineHeight, ...innerClasses.metadata?.style || {} }, children: resolvedMetadata }) : null,
776
+ resolvedTrailing || hasNestedList ? /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "trailing", "aria-hidden": "true", className: innerClasses.trailing?.className, style: { display: "inline-flex", color: "var(--jumpy-list-item-icon-color, var(--jumpy-list-item-default-icon))", ...innerClasses.trailing?.style || {} }, children: [
777
+ resolvedTrailing,
778
+ hasNestedList ? /* @__PURE__ */ jsx(JumpIconButton, { as: "span", variant: "secondary", alternateHover: true, tabIndex: -1, "aria-hidden": "true", children: isNestedCollapsed ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronUpIcon, {}) }) : null
779
+ ] }) : null,
780
+ hasNestedList ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "nested-list", "aria-hidden": isNestedCollapsed ? "true" : void 0, style: { gridColumn: "1 / -1", display: isNestedCollapsed ? "none" : "block" }, children: resolvedNestedList }) : null
781
+ ]
782
+ }
783
+ );
784
+ }
785
+ function JumpSideMenu({ as, children, className = "", collapsed = false, selected, disabled = false, disableKeyboardNavigation = false, renderMode, getItemProps, onChange, onKeyDown, onTransitionEnd, ...props }) {
786
+ const Component = as || "ul";
787
+ const theme = useJumpyTheme();
788
+ const elementRef = useRef(null);
789
+ const itemRefs = useRef([]);
790
+ const previousCollapsed = useRef(collapsed);
791
+ const releaseTimer = useRef(void 0);
792
+ const [isResizing, setIsResizing] = useState(false);
793
+ const fillsNavigation = useContext(JumpyAppShellNavigationContext) && !collapsed;
794
+ useLayoutEffect(() => {
795
+ if (previousCollapsed.current === collapsed) return void 0;
796
+ previousCollapsed.current = collapsed;
797
+ const element = elementRef.current;
798
+ if (!element) return void 0;
799
+ const transitionMs = getWidthTransitionMs(element);
800
+ if (!transitionMs) return void 0;
801
+ setIsResizing(true);
802
+ window.clearTimeout(releaseTimer.current);
803
+ releaseTimer.current = window.setTimeout(() => setIsResizing(false), transitionMs + 100);
804
+ return void 0;
805
+ }, [collapsed]);
806
+ useEffect(() => () => window.clearTimeout(releaseTimer.current), []);
807
+ const selectItem = (value) => {
808
+ if (disabled || value === void 0) return;
809
+ onChange?.([value]);
810
+ };
811
+ const handleKeyDown = (event) => {
812
+ if (!disableKeyboardNavigation) {
813
+ if (event.key === KeyboardKeys.ARROW_DOWN || event.key === KeyboardKeys.ARROW_RIGHT) {
814
+ event.preventDefault();
815
+ focusSideMenuItem(itemRefs.current, 1);
816
+ } else if (event.key === KeyboardKeys.ARROW_UP || event.key === KeyboardKeys.ARROW_LEFT) {
817
+ event.preventDefault();
818
+ focusSideMenuItem(itemRefs.current, -1);
819
+ } else if (event.key === KeyboardKeys.HOME) {
820
+ event.preventDefault();
821
+ focusSideMenuItem(itemRefs.current, "first");
822
+ } else if (event.key === KeyboardKeys.END) {
823
+ event.preventDefault();
824
+ focusSideMenuItem(itemRefs.current, "last");
825
+ }
826
+ }
827
+ onKeyDown?.(event);
828
+ };
829
+ const handleTransitionEnd = (event) => {
830
+ onTransitionEnd?.(event);
831
+ if (event.target === event.currentTarget && event.propertyName === "width") {
832
+ window.clearTimeout(releaseTimer.current);
833
+ setIsResizing(false);
834
+ }
835
+ };
836
+ return /* @__PURE__ */ jsx(JumpySideMenuContext.Provider, { value: { collapsed, disabled, selected, onSelect: selectItem, containerExpanded: isResizing || !collapsed, renderMode, getItemProps, disableKeyboardNavigation, itemRefs }, children: /* @__PURE__ */ jsx(
837
+ Component,
838
+ {
839
+ ...props,
840
+ ref: elementRef,
841
+ "data-jumpy-component": "side-menu",
842
+ "data-menu-collapsed": collapsed ? "true" : void 0,
843
+ "data-menu-resizing": isResizing || props["data-menu-resizing"] === "true" ? "true" : void 0,
844
+ "aria-disabled": disabled || void 0,
845
+ onKeyDown: handleKeyDown,
846
+ onTransitionEnd: handleTransitionEnd,
847
+ className: cn(className),
848
+ style: {
849
+ display: "block",
850
+ width: fillsNavigation ? "100%" : "fit-content",
851
+ height: "fit-content",
852
+ overflowX: "hidden",
853
+ overflowY: "auto",
854
+ margin: 0,
855
+ padding: 0,
856
+ listStyle: "none",
857
+ transition: `width ${animationDurationValue(theme, collapsed ? JumpyAnimationDuration.small2XS : JumpyAnimationDuration.smallXS)}s ${theme.animation.timeEasingDrift}`,
858
+ ...collapsed && !isResizing ? { minWidth: "min-content" } : {},
859
+ ...props.style || {}
860
+ },
861
+ children
862
+ }
863
+ ) });
864
+ }
865
+ function JumpSideMenuItem({ as, children, value, selected = false, disabled = false, className = "", icon, badge, collapsed, slots, metadata, description, nestedMenu, nestedCollapsed, defaultNestedCollapsed = false, onNestedCollapsedChange, renderMode, focusedView, truncateTexts = false, tooltipProps, innerClasses = {}, wrapperProps = {}, expanderIconProps = {}, onClick, onKeyDown, ...props }) {
866
+ const theme = useJumpyTheme();
867
+ const sideMenu = useContext(JumpySideMenuContext);
868
+ const itemRef = useRef(null);
869
+ const wrapperItemRef = useRef(null);
870
+ const isCollapsed = sideMenu ? !sideMenu.containerExpanded : collapsed ?? false;
871
+ const resolvedRenderMode = sideMenu?.renderMode ?? renderMode ?? "navigation";
872
+ const selectedValues = sideMenu?.selected;
873
+ const resolvedNestedMenu = slots?.nestedMenu ?? nestedMenu;
874
+ const resolvedMetadata = slots?.metadata ?? metadata;
875
+ const isSelected = isValueSelected(selectedValues, value) || selected;
876
+ const isDisabled = disabled || sideMenu?.disabled;
877
+ const resolvedTabIndex = focusedView ? 0 : sideMenu && !sideMenu.disableKeyboardNavigation && Array.isArray(selectedValues) ? isSelected ? 0 : -1 : props.tabIndex;
878
+ const hasNested = Boolean(resolvedNestedMenu);
879
+ const hasSelectedChildren = hasSelectedNestedItem(resolvedNestedMenu, selectedValues);
880
+ const [internalNestedCollapsed, setInternalNestedCollapsed] = useState(defaultNestedCollapsed);
881
+ const isNestedCollapsed = nestedCollapsed ?? internalNestedCollapsed;
882
+ const iconNode = typeof icon === "function" ? icon({ selected: isSelected }) : icon;
883
+ const badgeNode = typeof badge === "function" ? badge({ selected: isSelected, collapsed: isCollapsed }) : badge;
884
+ const handleItemClick = (event) => {
885
+ if (isDisabled) return;
886
+ if (hasNested) {
887
+ const nextCollapsed = !isNestedCollapsed;
888
+ onNestedCollapsedChange?.(nextCollapsed);
889
+ if (nestedCollapsed === void 0) setInternalNestedCollapsed(nextCollapsed);
890
+ } else {
891
+ sideMenu?.onSelect?.(value);
892
+ }
893
+ onClick?.(event);
894
+ };
895
+ const handleItemKeyDown = (event) => {
896
+ if ((event.key === KeyboardKeys.ENTER || event.key === KeyboardKeys.SPACE) && !isDisabled) {
897
+ event.preventDefault();
898
+ handleItemClick(event);
899
+ }
900
+ onKeyDown?.(event);
901
+ };
902
+ const itemInteractionProps = sideMenu?.getItemProps?.({ onClick: handleItemClick, onKeyDown: handleItemKeyDown }) ?? {};
903
+ const { onClick: interactionOnClick, onKeyDown: interactionOnKeyDown, ...restItemInteractionProps } = itemInteractionProps;
904
+ const { wrapperProps: splitWrapperProps, linkProps } = resolvedRenderMode === "navigation" ? splitNavigationItemProps(props) : { wrapperProps: {}, linkProps: props };
905
+ useEffect(() => {
906
+ if (!sideMenu?.itemRefs) return void 0;
907
+ const registryItem = isCollapsed && resolvedRenderMode === "navigation" ? wrapperItemRef : itemRef;
908
+ sideMenu.itemRefs.current.push(registryItem);
909
+ return () => {
910
+ sideMenu.itemRefs.current = sideMenu.itemRefs.current.filter((item) => item !== registryItem);
911
+ };
912
+ }, [isCollapsed, resolvedRenderMode, sideMenu?.itemRefs]);
913
+ const withTooltip = (node) => {
914
+ if (!tooltipProps?.content) return node;
915
+ const { content, ...restTooltipProps } = tooltipProps;
916
+ return /* @__PURE__ */ jsx(JumpTooltip, { ...restTooltipProps, content, children: node });
917
+ };
918
+ const trailingNode = hasNested ? /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "expander", "aria-hidden": "true", className: innerClasses.expander?.className, style: { display: "inline-flex", alignItems: "center", gap: spacingValue(theme, JumpySpacing.small), ...innerClasses.expander?.style || {} }, children: [
919
+ badgeNode,
920
+ /* @__PURE__ */ jsx("span", { ...expanderIconProps, "data-jumpy-slot": "expander-icon", className: cn(innerClasses.expanderIcon?.className, expanderIconProps.className), style: { display: "inline-flex", transform: isNestedCollapsed ? "rotate(0deg)" : "rotate(180deg)", transition: `transform ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${theme.animation.timeEasingDrift}`, ...innerClasses.expanderIcon?.style || {}, ...expanderIconProps.style || {} }, children: /* @__PURE__ */ jsx(ChevronDownIcon, {}) })
921
+ ] }) : badgeNode;
922
+ if (isCollapsed) {
923
+ const Component = as || (resolvedRenderMode === "navigation" ? "a" : "button");
924
+ const isButton = Component === "button";
925
+ const tokens = isSelected ? theme.listItem.active.default : theme.listItem.inactive.default;
926
+ const labelStyle = textStyleValue(theme, isSelected ? JumpyTextStyle.labelMediumEmphasized : JumpyTextStyle.labelMedium);
927
+ const collapsedWrapperInteractiveProps = resolvedRenderMode === "navigation" ? {
928
+ ref: wrapperItemRef,
929
+ tabIndex: resolvedTabIndex,
930
+ onClick: (event) => {
931
+ splitWrapperProps.onClick?.(event);
932
+ wrapperProps.onClick?.(event);
933
+ if (!event.defaultPrevented && event.target === event.currentTarget) itemRef.current?.click();
934
+ },
935
+ onKeyDown: (event) => {
936
+ splitWrapperProps.onKeyDown?.(event);
937
+ wrapperProps.onKeyDown?.(event);
938
+ if (!event.defaultPrevented && (event.key === KeyboardKeys.ENTER || event.key === KeyboardKeys.SPACE)) {
939
+ event.preventDefault();
940
+ itemRef.current?.click();
941
+ }
942
+ }
943
+ } : {};
944
+ return /* @__PURE__ */ jsxs("li", { ...splitWrapperProps, ...wrapperProps, ...collapsedWrapperInteractiveProps, "data-jumpy-component": "side-menu-item-wrapper", "aria-expanded": hasNested ? !isNestedCollapsed : void 0, className: cn(innerClasses.selector?.className, splitWrapperProps.className, wrapperProps.className), style: { display: "block", margin: 0, padding: 0, ...splitWrapperProps.style || {}, ...innerClasses.selector?.style || {}, ...wrapperProps.style || {} }, children: [
945
+ withTooltip(
946
+ /* @__PURE__ */ jsxs(
947
+ Component,
948
+ {
949
+ ...linkProps,
950
+ ref: itemRef,
951
+ ...restItemInteractionProps,
952
+ onClick: interactionOnClick ?? handleItemClick,
953
+ onKeyDown: interactionOnKeyDown ?? handleItemKeyDown,
954
+ type: isButton ? linkProps.type || "button" : linkProps.type,
955
+ disabled: isButton ? isDisabled : void 0,
956
+ "aria-disabled": !isButton && isDisabled ? true : void 0,
957
+ "aria-current": isSelected ? linkProps["aria-current"] || "page" : linkProps["aria-current"],
958
+ role: !isButton && !linkProps.href ? linkProps.role || "link" : linkProps.role,
959
+ tabIndex: resolvedRenderMode === "navigation" ? -1 : resolvedTabIndex,
960
+ "data-jumpy-component": "side-menu-item",
961
+ "data-collapsed": "true",
962
+ "data-selected": isSelected ? "true" : "false",
963
+ "data-disabled": isDisabled ? "true" : "false",
964
+ "data-value": !hasNested ? value : void 0,
965
+ "data-has-selected-children": hasSelectedChildren ? "true" : void 0,
966
+ "data-focused-view": focusedView ? "true" : void 0,
967
+ className: cn(className, innerClasses.collapsedLabel?.className),
968
+ style: {
969
+ position: "relative",
970
+ display: "flex",
971
+ flexDirection: "column",
972
+ alignItems: "center",
973
+ width: "100%",
974
+ minWidth: sizeValue(theme, JumpySize.large8XL) - spacingValue(theme, JumpySpacing.large3XL),
975
+ boxSizing: "border-box",
976
+ border: 0,
977
+ borderRadius: 0,
978
+ backgroundColor: "transparent",
979
+ color: tokens.text,
980
+ cursor: isDisabled ? "not-allowed" : "pointer",
981
+ padding: `${spacingValue(theme, JumpySpacing.smallXS)}px 0`,
982
+ opacity: isDisabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid),
983
+ ...linkProps.style || {}
984
+ },
985
+ children: [
986
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "side-menu-decorator", className: innerClasses.decoratorContainer?.className, style: { position: "relative", display: "inline-flex", alignItems: "center", justifyContent: "center", padding: `${spacingValue(theme, JumpySpacing.smallXS)}px ${spacingValue(theme, JumpySpacing.large2XL)}px ${spacingValue(theme, JumpySpacing.small3XS)}px`, color: tokens.icon, ...innerClasses.decoratorContainer?.style || {} }, children: [
987
+ iconNode,
988
+ badgeNode ? /* @__PURE__ */ jsx("span", { style: { position: "absolute", top: 0, right: spacingValue(theme, JumpySpacing.large), display: "inline-flex" }, children: badgeNode }) : null
989
+ ] }),
990
+ children ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "label", "data-text": typeof children === "string" ? children : void 0, className: innerClasses.label?.className, style: { boxSizing: "border-box", paddingInline: spacingValue(theme, JumpySpacing.large), overflowWrap: "anywhere", textAlign: "center", fontFamily: theme.fontFamily.familyBase, fontSize: labelStyle.fontSize, lineHeight: labelStyle.lineHeight, fontWeight: labelStyle.fontWeight, ...truncateTexts ? { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: "100%" } : {}, ...innerClasses.label?.style || {} }, children }) : null
991
+ ]
992
+ }
993
+ )
994
+ ),
995
+ hasNested && !isNestedCollapsed ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "nested-menu", children: resolvedNestedMenu }) : null
996
+ ] });
997
+ }
998
+ const expandedItem = /* @__PURE__ */ jsx(
999
+ JumpListItem,
1000
+ {
1001
+ ...linkProps,
1002
+ ref: itemRef,
1003
+ ...restItemInteractionProps,
1004
+ as: as || (resolvedRenderMode === "navigation" ? "a" : "li"),
1005
+ onClick: interactionOnClick ?? handleItemClick,
1006
+ onKeyDown: interactionOnKeyDown ?? handleItemKeyDown,
1007
+ selected: isSelected,
1008
+ disabled: isDisabled,
1009
+ leading: iconNode,
1010
+ trailing: trailingNode,
1011
+ metadata: resolvedMetadata,
1012
+ description,
1013
+ truncateTexts,
1014
+ innerClasses,
1015
+ "aria-expanded": hasNested ? !isNestedCollapsed : void 0,
1016
+ role: linkProps.role || (resolvedRenderMode === "navigation" ? "link" : void 0),
1017
+ tabIndex: resolvedTabIndex,
1018
+ "data-jumpy-component": "side-menu-item",
1019
+ "data-value": !hasNested ? value : void 0,
1020
+ "data-has-selected-children": hasSelectedChildren ? "true" : void 0,
1021
+ "data-focused-view": focusedView ? "true" : void 0,
1022
+ className: cn(className, innerClasses.selector?.className),
1023
+ style: {
1024
+ "--jumpy-side-menu-item-indicator-scale": isSelected ? 1 : 0.5,
1025
+ paddingLeft: spacingValue(theme, JumpySpacing.large2XL),
1026
+ paddingRight: spacingValue(theme, JumpySpacing.large2XL),
1027
+ paddingTop: spacingValue(theme, JumpySpacing.large),
1028
+ paddingBottom: spacingValue(theme, JumpySpacing.large),
1029
+ ...truncateTexts ? { overflow: "hidden" } : {},
1030
+ ...innerClasses.selector?.style || {},
1031
+ ...linkProps.style || {}
1032
+ },
1033
+ children
1034
+ }
1035
+ );
1036
+ const expandedNestedMenu = hasNested && !isNestedCollapsed ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "nested-menu", children: resolvedNestedMenu }) : null;
1037
+ if (resolvedRenderMode === "navigation") {
1038
+ return withTooltip(
1039
+ /* @__PURE__ */ jsxs("li", { ...splitWrapperProps, ...wrapperProps, "data-jumpy-component": "side-menu-item-wrapper", "aria-expanded": hasNested ? !isNestedCollapsed : void 0, className: cn(splitWrapperProps.className, wrapperProps.className), style: { display: "block", margin: 0, padding: 0, ...splitWrapperProps.style || {}, ...wrapperProps.style || {} }, children: [
1040
+ withTooltip(expandedItem),
1041
+ expandedNestedMenu
1042
+ ] })
1043
+ );
1044
+ }
1045
+ return withTooltip(
1046
+ /* @__PURE__ */ jsxs(Fragment, { children: [
1047
+ expandedItem,
1048
+ expandedNestedMenu
1049
+ ] })
1050
+ );
1051
+ }
1052
+ function JumpTypography({ as, variant = "bodyMedium", color = "primary", className = "", children, ...props }) {
1053
+ const theme = useJumpyTheme();
1054
+ const Component = as || "p";
1055
+ const colorValue = color === "secondary" ? theme.color.textSecondary : color === "primary" ? theme.color.textPrimary : color;
1056
+ return /* @__PURE__ */ jsx(
1057
+ Component,
1058
+ {
1059
+ ...props,
1060
+ "data-jumpy-component": "typography",
1061
+ className: cn(className),
1062
+ style: {
1063
+ ...getTypographyStyle(theme, variant),
1064
+ color: colorValue,
1065
+ margin: 0,
1066
+ ...props.style || {}
1067
+ },
1068
+ children
1069
+ }
1070
+ );
1071
+ }
1072
+ function JumpHyperlink({ as, variant = "inline", disabled = false, className = "", children, ...props }) {
1073
+ const theme = useJumpyTheme();
1074
+ const Component = as || "a";
1075
+ if (variant !== "inline") {
1076
+ return /* @__PURE__ */ jsx(JumpLinkButton, { ...props, href: props.href || "#", variant: "borderless", className, children });
1077
+ }
1078
+ return /* @__PURE__ */ jsx(
1079
+ Component,
1080
+ {
1081
+ ...props,
1082
+ "data-jumpy-component": "hyperlink",
1083
+ "aria-disabled": disabled || void 0,
1084
+ tabIndex: disabled ? -1 : props.tabIndex,
1085
+ className: cn(className),
1086
+ style: {
1087
+ display: "inline",
1088
+ borderRadius: radiusValue(theme, JumpyRadius.small),
1089
+ backgroundColor: "transparent",
1090
+ color: disabled ? theme.actions.tertiary.regular.disabled.text : theme.actions.tertiary.regular.default.text,
1091
+ font: "inherit",
1092
+ textDecoration: "none",
1093
+ cursor: disabled ? "default" : "pointer",
1094
+ pointerEvents: disabled ? "none" : void 0,
1095
+ ...props.style || {}
1096
+ },
1097
+ children
1098
+ }
1099
+ );
1100
+ }
1101
+ function JumpDivider({ as, direction = "horizontal", captionPosition = "center", className = "", children, ...props }) {
1102
+ const theme = useJumpyTheme();
1103
+ const Component = as || "div";
1104
+ const isVertical = direction === "vertical";
1105
+ return /* @__PURE__ */ jsx(
1106
+ Component,
1107
+ {
1108
+ ...props,
1109
+ "data-jumpy-component": "divider",
1110
+ role: "separator",
1111
+ "aria-orientation": direction,
1112
+ "aria-label": children ? String(children) : props["aria-label"],
1113
+ className: cn(className),
1114
+ style: {
1115
+ display: isVertical ? "inline-flex" : "flex",
1116
+ alignItems: "center",
1117
+ justifyContent: captionPosition === "start" ? "flex-start" : captionPosition === "end" ? "flex-end" : "center",
1118
+ alignSelf: isVertical ? "stretch" : void 0,
1119
+ width: isVertical ? borderWidthValue(theme, JumpyBorderWidth.small) : "100%",
1120
+ minHeight: isVertical ? sizeValue(theme, JumpySize.large) : void 0,
1121
+ border: 0,
1122
+ borderTop: isVertical ? 0 : `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}`,
1123
+ borderLeft: isVertical ? `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}` : 0,
1124
+ color: theme.color.textSecondary,
1125
+ ...props.style || {}
1126
+ },
1127
+ children: children ? /* @__PURE__ */ jsx("span", { style: { backgroundColor: theme.color.surfaceCard, padding: `0 ${spacingValue(theme, JumpySpacing.smallXS)}px`, transform: isVertical ? "rotate(-90deg)" : void 0 }, children }) : null
1128
+ }
1129
+ );
1130
+ }
1131
+ function JumpAvatar({ as, size: size2 = "medium", disableInteraction = false, disabled = false, className = "", children, ...props }) {
1132
+ const theme = useJumpyTheme();
1133
+ const Component = as || (disableInteraction ? "span" : "button");
1134
+ const isButton = Component === "button";
1135
+ const sizes = { small: JumpySize.large, medium: JumpySize.large2XL, large: JumpySize.large4XL };
1136
+ const avatarSize = sizeValue(theme, sizes[size2] || JumpySize.large2XL);
1137
+ return /* @__PURE__ */ jsx(
1138
+ Component,
1139
+ {
1140
+ ...props,
1141
+ type: isButton ? props.type || "button" : props.type,
1142
+ disabled: isButton ? disabled : void 0,
1143
+ "aria-disabled": !isButton && disabled ? true : void 0,
1144
+ "data-jumpy-component": "avatar",
1145
+ className: cn(className),
1146
+ style: {
1147
+ display: "inline-grid",
1148
+ placeItems: "center",
1149
+ width: avatarSize,
1150
+ height: avatarSize,
1151
+ borderRadius: radiusValue(theme, JumpyRadius.full),
1152
+ border: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}`,
1153
+ backgroundColor: theme.color.infoSurface,
1154
+ color: theme.color.surfacePrimary,
1155
+ fontFamily: theme.fontFamily.familyBase,
1156
+ fontWeight: theme.fontFamily.bodyMediumEmphasized.fontWeight,
1157
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.medium) : opacityValue(theme, JumpyOpacity.solid),
1158
+ cursor: disableInteraction || disabled ? "default" : "pointer",
1159
+ overflow: "hidden",
1160
+ ...props.style || {}
1161
+ },
1162
+ children
1163
+ }
1164
+ );
1165
+ }
1166
+ function JumpBanner({ as, status: statusProp = "other", variant = "default", direction = "horizontal", hideStatus = false, floating = false, title, actions, slots, innerClasses = {}, className = "", children, ...props }) {
1167
+ const theme = useJumpyTheme();
1168
+ const Component = as || "div";
1169
+ const status = variant === "inline" && statusProp === "other" ? "info" : statusProp;
1170
+ const statusTone = getBannerStatusStyles(theme, status);
1171
+ const urgent = status === "alarm";
1172
+ const bannerTitle = slots?.title ?? title;
1173
+ const bannerActions = slots?.actions ?? actions;
1174
+ const bannerIcon = slots?.icon;
1175
+ const contentContainerVertical = direction === "vertical";
1176
+ const contentTitle = textStyleValue(theme, JumpyTextStyle.titleMediumEmphasized);
1177
+ const contentDescription = textStyleValue(theme, JumpyTextStyle.bodyMedium);
1178
+ return /* @__PURE__ */ jsxs(
1179
+ Component,
1180
+ {
1181
+ ...props,
1182
+ "data-jumpy-component": "banner",
1183
+ "data-variant": variant,
1184
+ "data-status": status,
1185
+ "data-floating": floating ? "true" : "false",
1186
+ role: urgent ? "alert" : "status",
1187
+ "aria-live": urgent ? "assertive" : "polite",
1188
+ className: cn(className),
1189
+ style: {
1190
+ "--jumpy-banner-status-icon": statusTone.indicator,
1191
+ "--jumpy-banner-status-surface": statusTone.indicatorSurface,
1192
+ "--jumpy-banner-inline-surface": statusTone.inlineSurface,
1193
+ ...props.style || {}
1194
+ },
1195
+ children: [
1196
+ !hideStatus ? /* @__PURE__ */ jsx(
1197
+ "span",
1198
+ {
1199
+ "data-jumpy-slot": "banner-status",
1200
+ className: innerClasses.status?.className,
1201
+ "aria-hidden": "true",
1202
+ style: {
1203
+ ...innerClasses.status?.style || {}
1204
+ },
1205
+ children: /* @__PURE__ */ jsx(JumpSeverityIcon, { status, icon: bannerIcon })
1206
+ }
1207
+ ) : null,
1208
+ /* @__PURE__ */ jsxs(
1209
+ "div",
1210
+ {
1211
+ "data-jumpy-slot": "banner-content-container",
1212
+ "data-direction": direction,
1213
+ className: innerClasses.contentContainer?.className,
1214
+ style: {
1215
+ ...innerClasses.contentContainer?.style || {}
1216
+ },
1217
+ children: [
1218
+ /* @__PURE__ */ jsxs(
1219
+ "div",
1220
+ {
1221
+ "data-jumpy-slot": "banner-content",
1222
+ className: innerClasses.content?.className,
1223
+ style: {
1224
+ ...innerClasses.content?.style || {}
1225
+ },
1226
+ children: [
1227
+ renderBannerSlot(bannerTitle, "span", innerClasses.contentTitle?.className, {
1228
+ margin: 0,
1229
+ fontFamily: theme.fontFamily.familyBase,
1230
+ fontSize: contentTitle.fontSize,
1231
+ lineHeight: contentTitle.lineHeight,
1232
+ fontWeight: contentTitle.fontWeight,
1233
+ letterSpacing: contentTitle.letterSpacing,
1234
+ color: "inherit",
1235
+ ...innerClasses.contentTitle?.style || {}
1236
+ }),
1237
+ renderBannerSlot(children, "p", innerClasses.contentDescription?.className, {
1238
+ margin: 0,
1239
+ fontFamily: theme.fontFamily.familyBase,
1240
+ fontSize: contentDescription.fontSize,
1241
+ lineHeight: contentDescription.lineHeight,
1242
+ fontWeight: contentDescription.fontWeight,
1243
+ letterSpacing: contentDescription.letterSpacing,
1244
+ color: "inherit",
1245
+ ...innerClasses.contentDescription?.style || {}
1246
+ })
1247
+ ]
1248
+ }
1249
+ ),
1250
+ contentContainerVertical && bannerActions ? /* @__PURE__ */ jsx(
1251
+ "div",
1252
+ {
1253
+ "data-jumpy-slot": "banner-actions-container",
1254
+ className: innerClasses.actionsContainer?.className,
1255
+ style: {
1256
+ ...innerClasses.actionsContainer?.style || {}
1257
+ },
1258
+ children: bannerActions
1259
+ }
1260
+ ) : bannerActions
1261
+ ]
1262
+ }
1263
+ )
1264
+ ]
1265
+ }
1266
+ );
1267
+ }
1268
+ function JumpSkeletonLoader({ as, variant = "text", lines = 1, width, height, disableAnimation = false, className = "", children, ...props }) {
1269
+ const theme = useJumpyTheme();
1270
+ const Component = as || "div";
1271
+ if (children) return children;
1272
+ const defaultHeight = variant === "text" ? theme.fontFamily.bodyMedium.fontSize * theme.fontFamily.bodyMedium.lineHeight : variant === "icon" || variant === "avatar" ? sizeValue(theme, JumpySize.large) : sizeValue(theme, JumpySize.large2XL);
1273
+ const radius = variant === "avatar" ? radiusValue(theme, JumpyRadius.full) : radiusValue(theme, JumpyRadius.small);
1274
+ const items = Array.from({ length: variant === "text" ? lines : 1 });
1275
+ return /* @__PURE__ */ jsx("div", { "data-jumpy-component": "skeleton-loader-group", className: cn(className), style: { display: "grid", gap: spacingValue(theme, JumpySpacing.smallXS), width: width || "100%", ...props.style || {} }, children: items.map((_, index) => /* @__PURE__ */ jsx(
1276
+ Component,
1277
+ {
1278
+ "aria-hidden": "true",
1279
+ "data-jumpy-component": "skeleton-loader",
1280
+ style: {
1281
+ width: index === items.length - 1 && items.length > 1 ? "72%" : width || "100%",
1282
+ height: height || defaultHeight,
1283
+ borderRadius: radius,
1284
+ backgroundColor: theme.color.borderSubtle,
1285
+ opacity: disableAnimation ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.medium)
1286
+ }
1287
+ },
1288
+ `skeleton-${lines}-${index}`
1289
+ )) });
1290
+ }
1291
+ var JumpSearchField = forwardRef(function JumpSearchField2({ label = "Search", placeholder = "Search", className = "", ...props }, ref) {
1292
+ const ariaLabelledBy = props["aria-labelledby"];
1293
+ const ariaLabel = ariaLabelledBy ? void 0 : props["aria-label"] || label || placeholder;
1294
+ return /* @__PURE__ */ jsx(JumpBaseInput, { className: cn("jumpy-search-field", className), leading: /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M16.071 14.5q1.509-1.95 1.508-4.5c0-4.1-3.418-7.5-7.54-7.5C5.919 2.5 2.5 5.9 2.5 10s3.418 7.5 7.54 7.5c1.709 0 3.317-.6 4.524-1.5l5.529 5.5 1.407-1.4zm-6.031 1c-3.016 0-5.53-2.5-5.53-5.5s2.515-5.5 5.53-5.5S15.569 7 15.569 10s-2.513 5.5-5.53 5.5z" }) }), children: /* @__PURE__ */ jsx(JumpInput, { ...props, ref, type: "search", placeholder, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }) });
1295
+ });
1296
+ function JumpTooltip({ content, children, className = "", ...props }) {
1297
+ const theme = useJumpyTheme();
1298
+ const [visible, setVisible] = useState(false);
1299
+ return /* @__PURE__ */ jsxs("span", { ...props, "data-jumpy-component": "tooltip", className: cn(className), style: { position: "relative", display: "inline-flex", ...props.style || {} }, onMouseEnter: () => setVisible(true), onMouseLeave: () => setVisible(false), onFocus: () => setVisible(true), onBlur: () => setVisible(false), children: [
1300
+ children,
1301
+ visible ? /* @__PURE__ */ jsx("span", { role: "tooltip", style: { position: "absolute", left: "50%", bottom: `calc(100% + ${spacingValue(theme, JumpySpacing.smallXS)}px)`, transform: "translateX(-50%)", zIndex: 30, width: "max-content", maxWidth: 240, borderRadius: radiusValue(theme, JumpyRadius.small), backgroundColor: theme.color.textPrimary, color: theme.color.surfaceCard, padding: `${spacingValue(theme, JumpySpacing.small3XS)}px ${spacingValue(theme, JumpySpacing.smallXS)}px`, boxShadow: theme.shadow.positiveSmall, fontSize: theme.fontFamily.bodySmall.fontSize, lineHeight: theme.fontFamily.bodySmall.lineHeight }, children: content }) : null
1302
+ ] });
1303
+ }
1304
+ function JumpPopover({ open, defaultOpen = false, onOpenChange, trigger, children, className = "", ...props }) {
1305
+ const theme = useJumpyTheme();
1306
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
1307
+ const visible = open ?? internalOpen;
1308
+ const setVisible = (next) => {
1309
+ setInternalOpen(next);
1310
+ onOpenChange?.(next);
1311
+ };
1312
+ return /* @__PURE__ */ jsxs("span", { "data-jumpy-component": "popover-root", className: cn(className), style: { position: "relative", display: "inline-flex", ...props.style || {} }, children: [
1313
+ /* @__PURE__ */ jsx("span", { onClick: () => setVisible(!visible), children: trigger }),
1314
+ visible ? /* @__PURE__ */ jsx("div", { role: "dialog", "data-jumpy-component": "popover", style: { position: "absolute", top: `calc(100% + ${spacingValue(theme, JumpySpacing.smallXS)}px)`, left: 0, zIndex: 30, minWidth: 220, borderRadius: radiusValue(theme, JumpyRadius.large), border: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}`, backgroundColor: theme.color.surfaceCard, color: theme.color.textPrimary, boxShadow: theme.shadow.positiveMedium, padding: spacingValue(theme, JumpySpacing.large) }, children }) : null
1315
+ ] });
1316
+ }
1317
+ function JumpMenu({ trigger, triggerProps = {}, open, defaultOpen = false, onOpenChange, header, footer, children, className = "", placement = "bottom-start", closeOnSelect = true, ...props }) {
1318
+ const theme = useJumpyTheme();
1319
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
1320
+ const isOpen = open ?? internalOpen;
1321
+ const menuRef = useRef(null);
1322
+ const insideEventRef = useRef(null);
1323
+ const menuOffset = spacingValue(theme, JumpySpacing.small3XS);
1324
+ const menuPadding = spacingValue(theme, JumpySpacing.medium);
1325
+ const menuMinWidth = sizeValue(theme, JumpySize.large8XL);
1326
+ const { refs, floatingStyles } = useFloating({
1327
+ open: isOpen,
1328
+ onOpenChange: setOpen,
1329
+ placement,
1330
+ strategy: "fixed",
1331
+ whileElementsMounted: autoUpdate,
1332
+ middleware: [
1333
+ offset(menuOffset),
1334
+ flip({ padding: menuPadding }),
1335
+ size({
1336
+ padding: menuPadding,
1337
+ apply({ rects, availableHeight, elements }) {
1338
+ Object.assign(elements.floating.style, {
1339
+ minWidth: `${Math.max(menuMinWidth, rects.reference.width)}px`,
1340
+ maxHeight: `${Math.max(sizeValue(theme, JumpySize.large2XL), availableHeight)}px`
1341
+ });
1342
+ }
1343
+ })
1344
+ ]
1345
+ });
1346
+ function setOpen(next) {
1347
+ setInternalOpen(next);
1348
+ onOpenChange?.(next);
1349
+ }
1350
+ function getMenuItems() {
1351
+ return Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') || []).filter((item) => item.getAttribute("aria-disabled") !== "true" && !item.disabled);
1352
+ }
1353
+ function focusMenuItem(direction) {
1354
+ const menuItems = getMenuItems();
1355
+ if (menuItems.length === 0) return;
1356
+ const activeIndex = menuItems.indexOf(document.activeElement);
1357
+ const nextIndex = direction === "first" ? 0 : direction === "last" ? menuItems.length - 1 : (activeIndex + direction + menuItems.length) % menuItems.length;
1358
+ menuItems[nextIndex]?.focus();
1359
+ }
1360
+ function openAndFocusFirst() {
1361
+ setOpen(true);
1362
+ window.requestAnimationFrame(() => focusMenuItem("first"));
1363
+ }
1364
+ function handleTriggerKeyDown(event) {
1365
+ if (event.key === KeyboardKeys.ARROW_DOWN || event.key === KeyboardKeys.ENTER || event.key === KeyboardKeys.SPACE) {
1366
+ event.preventDefault();
1367
+ openAndFocusFirst();
1368
+ }
1369
+ if (event.key === KeyboardKeys.ARROW_UP) {
1370
+ event.preventDefault();
1371
+ setOpen(true);
1372
+ window.requestAnimationFrame(() => focusMenuItem("last"));
1373
+ }
1374
+ }
1375
+ function handleMenuKeyDown(event) {
1376
+ if (event.key === KeyboardKeys.ESCAPE) {
1377
+ event.preventDefault();
1378
+ setOpen(false);
1379
+ refs.reference.current?.focus?.();
1380
+ }
1381
+ if (event.key === KeyboardKeys.ARROW_DOWN) {
1382
+ event.preventDefault();
1383
+ focusMenuItem(1);
1384
+ }
1385
+ if (event.key === KeyboardKeys.ARROW_UP) {
1386
+ event.preventDefault();
1387
+ focusMenuItem(-1);
1388
+ }
1389
+ if (event.key === KeyboardKeys.HOME) {
1390
+ event.preventDefault();
1391
+ focusMenuItem("first");
1392
+ }
1393
+ if (event.key === KeyboardKeys.END) {
1394
+ event.preventDefault();
1395
+ focusMenuItem("last");
1396
+ }
1397
+ props.onKeyDown?.(event);
1398
+ }
1399
+ useEffect(() => {
1400
+ if (!isOpen) return void 0;
1401
+ function dismissOutside(event) {
1402
+ if (insideEventRef.current !== event) setOpen(false);
1403
+ }
1404
+ document.addEventListener("pointerdown", dismissOutside);
1405
+ document.addEventListener("focusin", dismissOutside);
1406
+ return () => {
1407
+ document.removeEventListener("pointerdown", dismissOutside);
1408
+ document.removeEventListener("focusin", dismissOutside);
1409
+ };
1410
+ });
1411
+ function markInteractionInside(event) {
1412
+ insideEventRef.current = event.nativeEvent;
1413
+ }
1414
+ function handleMenuClick(event) {
1415
+ props.onClick?.(event);
1416
+ if (!closeOnSelect || event.defaultPrevented) return;
1417
+ const item = event.target.closest?.('[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"]');
1418
+ if (!item || item.disabled || item.getAttribute("aria-disabled") === "true" || item.closest("[aria-haspopup]")) return;
1419
+ setOpen(false);
1420
+ refs.domReference.current?.focus?.();
1421
+ }
1422
+ return /* @__PURE__ */ jsxs("span", { "data-jumpy-component": "menu-root", className: cn(className), onPointerDown: markInteractionInside, onFocus: markInteractionInside, style: { display: "inline-flex", ...props.style || {} }, children: [
1423
+ /* @__PURE__ */ jsx("span", { ...triggerProps, ref: refs.setReference, "aria-haspopup": "menu", "aria-expanded": isOpen, tabIndex: 0, onClick: () => setOpen(!isOpen), onKeyDown: handleTriggerKeyDown, children: trigger }),
1424
+ isOpen ? /* @__PURE__ */ jsx(JumpyPortal, { children: /* @__PURE__ */ jsxs(
1425
+ "div",
1426
+ {
1427
+ ...props,
1428
+ ref: (node) => {
1429
+ refs.setFloating(node);
1430
+ menuRef.current = node;
1431
+ },
1432
+ "data-jumpy-component": "menu",
1433
+ role: "menu",
1434
+ onClick: handleMenuClick,
1435
+ onKeyDown: handleMenuKeyDown,
1436
+ style: {
1437
+ ...floatingStyles,
1438
+ zIndex: JUMPY_POPUP_Z_INDEX,
1439
+ maxWidth: "min(420px, calc(100vw - 32px))",
1440
+ overflowY: "auto",
1441
+ ...props.style || {}
1442
+ },
1443
+ children: [
1444
+ header ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "menu-header", children: header }) : null,
1445
+ children,
1446
+ footer ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "menu-footer", children: footer }) : null
1447
+ ]
1448
+ }
1449
+ ) }) : null
1450
+ ] });
1451
+ }
1452
+ function JumpMenuItem({ as, children, selected = false, disabled = false, readOnly = false, focusedView = false, leading, trailing, metadata, description, slots = {}, nestedMenu, onClick, ...props }) {
1453
+ const Component = as || "button";
1454
+ const resolvedSlots = {
1455
+ ...slots,
1456
+ leading: slots.leading ?? leading,
1457
+ trailing: nestedMenu ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u203A" }) : slots.trailing ?? trailing,
1458
+ metadata: slots.metadata ?? metadata,
1459
+ description: slots.description ?? description
1460
+ };
1461
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1462
+ /* @__PURE__ */ jsx(
1463
+ JumpListItem,
1464
+ {
1465
+ ...props,
1466
+ as: Component,
1467
+ role: props.role || "menuitem",
1468
+ selected,
1469
+ disabled,
1470
+ readOnly,
1471
+ focusedView,
1472
+ slots: resolvedSlots,
1473
+ "data-jumpy-component": "menu-item",
1474
+ onClick: (event) => {
1475
+ if (disabled || readOnly) return;
1476
+ onClick?.(event);
1477
+ },
1478
+ children
1479
+ }
1480
+ ),
1481
+ nestedMenu || null
1482
+ ] });
1483
+ }
1484
+ function JumpToggleButton({ as, selected, value, disabled = false, onChange, leading, children, className = "", ...props }) {
1485
+ const theme = useJumpyTheme();
1486
+ const group = useContext(JumpyToggleButtonGroupContext);
1487
+ const Component = as || "button";
1488
+ const isButton = Component === "button";
1489
+ const isDisabled = disabled || group?.disabled || false;
1490
+ const isSelected = group && value !== void 0 ? group.value.includes(value) : Boolean(selected);
1491
+ const textStyle = textStyleValue(theme, JumpyTextStyle.bodyMediumEmphasized);
1492
+ function handleClick(event) {
1493
+ if (isDisabled) return;
1494
+ if (group && value !== void 0) group.onChange(value);
1495
+ onChange?.(!isSelected);
1496
+ props.onClick?.(event);
1497
+ }
1498
+ return /* @__PURE__ */ jsxs(
1499
+ Component,
1500
+ {
1501
+ ...props,
1502
+ type: isButton ? props.type || "button" : props.type,
1503
+ disabled: isButton ? isDisabled : void 0,
1504
+ "aria-disabled": !isButton && isDisabled ? true : void 0,
1505
+ "aria-pressed": isSelected,
1506
+ "data-jumpy-component": "toggle-button",
1507
+ "data-selected": isSelected ? "true" : "false",
1508
+ "data-disabled": isDisabled ? "true" : "false",
1509
+ className: cn(className),
1510
+ onClick: handleClick,
1511
+ style: {
1512
+ boxSizing: "border-box",
1513
+ position: "relative",
1514
+ display: "inline-flex",
1515
+ alignItems: "center",
1516
+ justifyContent: "center",
1517
+ width: "fit-content",
1518
+ height: sizeValue(theme, JumpySize.large),
1519
+ gap: spacingValue(theme, JumpySpacing.small3XS),
1520
+ padding: `${spacingValue(theme, JumpySpacing.smallXS)}px ${spacingValue(theme, JumpySpacing.large)}px`,
1521
+ outline: "none",
1522
+ border: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-cmp-toggle-button-border-color)`,
1523
+ borderRadius: "var(--jumpy-cmp-toggle-button-border-radius)",
1524
+ backgroundColor: "var(--jumpy-cmp-toggle-button-surface)",
1525
+ color: "var(--jumpy-cmp-toggle-button-text)",
1526
+ cursor: isDisabled ? "not-allowed" : "pointer",
1527
+ userSelect: "none",
1528
+ fontFamily: theme.fontFamily.familyBase,
1529
+ fontSize: textStyle.fontSize,
1530
+ lineHeight: textStyle.lineHeight,
1531
+ fontWeight: textStyle.fontWeight,
1532
+ opacity: isDisabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid),
1533
+ ...props.style || {}
1534
+ },
1535
+ children: [
1536
+ leading ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: "inline-flex", color: "var(--jumpy-cmp-toggle-button-icon-fill)" }, children: typeof leading === "function" ? leading({ selected: isSelected }) : leading }) : null,
1537
+ children ? /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", alignItems: "center", justifyContent: "center", margin: `0 ${spacingValue(theme, JumpySpacing.small3XS)}px`, whiteSpace: "nowrap" }, children }) : null
1538
+ ]
1539
+ }
1540
+ );
1541
+ }
1542
+ function JumpToggleButtonGroup({ as, selected, defaultSelected = [], onChange, exclusive = false, disabled = false, children, className = "", ...props }) {
1543
+ const theme = useJumpyTheme();
1544
+ const Component = as || "div";
1545
+ const [internalSelected, setInternalSelected] = useState(defaultSelected);
1546
+ const currentSelected = selected ?? internalSelected;
1547
+ function handleToggle(toggledValue) {
1548
+ const nextSelected = currentSelected.includes(toggledValue) ? currentSelected.filter((item) => item !== toggledValue) : exclusive ? [toggledValue] : [...currentSelected, toggledValue];
1549
+ setInternalSelected(nextSelected);
1550
+ onChange?.(nextSelected);
1551
+ }
1552
+ return /* @__PURE__ */ jsx(JumpyToggleButtonGroupContext.Provider, { value: { value: currentSelected, onChange: handleToggle, exclusive, disabled }, children: /* @__PURE__ */ jsx(
1553
+ Component,
1554
+ {
1555
+ ...props,
1556
+ "data-jumpy-component": "toggle-button-group",
1557
+ role: props.role || "toolbar",
1558
+ className: cn(className),
1559
+ style: { display: "inline-flex", flexDirection: "row", alignItems: "center", width: "fit-content", gap: spacingValue(theme, JumpySpacing.small3XS), flexWrap: "wrap", userSelect: "none", ...props.style || {} },
1560
+ children
1561
+ }
1562
+ ) });
1563
+ }
1564
+ function JumpPinInput({ length = 6, value, defaultValue = "", onChange, onComplete, name, mask = false, type = "numeric", error = false, disabled = false, helperText, ariaLabels = {}, className = "", innerClasses = {}, ...props }) {
1565
+ const theme = useJumpyTheme();
1566
+ const normalizedLength = clampNumber(length, 4, 8);
1567
+ const [internalValue, setInternalValue] = useState(defaultValue);
1568
+ const currentValue = String(value ?? internalValue).slice(0, normalizedLength);
1569
+ const refs = useRef([]);
1570
+ const pattern = type === "alphanumeric" ? /^[a-z0-9]$/i : /^\d$/;
1571
+ const helperId = helperText ? `${name || props.id || "jumpy-pin-input"}-helper` : void 0;
1572
+ function commit(nextValue, focusIndex) {
1573
+ const compact = nextValue.slice(0, normalizedLength);
1574
+ setInternalValue(compact);
1575
+ onChange?.(compact);
1576
+ if (compact.length === normalizedLength) onComplete?.(compact);
1577
+ if (focusIndex !== void 0) refs.current[clampNumber(focusIndex, 0, normalizedLength - 1)]?.focus();
1578
+ }
1579
+ function replaceAt(index, char) {
1580
+ const cells = Array.from({ length: normalizedLength }, (_, cellIndex) => currentValue[cellIndex] || "");
1581
+ cells[index] = char;
1582
+ return cells.join("");
1583
+ }
1584
+ const supportingColor = disabled ? theme.color["jumpy-input-regular-disabled-supporting"] : error ? theme.color["jumpy-input-alarm-default-supporting"] : theme.color["jumpy-input-regular-default-supporting"];
1585
+ const textStyle = textStyleValue(theme, JumpyTextStyle.bodyLarge);
1586
+ return /* @__PURE__ */ jsxs("fieldset", { ...props, disabled, "data-jumpy-component": "pin-input", className: cn(className), style: { display: "inline-flex", flexDirection: "column", alignItems: "flex-start", border: 0, margin: 0, padding: 0, ...props.style || {} }, children: [
1587
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }) : null,
1588
+ /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "pin-input-cells", className: cn(innerClasses.cells?.className), style: { display: "flex", gap: spacingValue(theme, JumpySpacing.small2XS), cursor: disabled ? "not-allowed" : "text", ...innerClasses.cells?.style || {} }, onClick: (event) => {
1589
+ if (event.target === event.currentTarget) refs.current[Math.min(currentValue.length, normalizedLength - 1)]?.focus();
1590
+ }, children: Array.from({ length: normalizedLength }, (_, index) => /* @__PURE__ */ jsx(
1591
+ "input",
1592
+ {
1593
+ ref: (node) => {
1594
+ refs.current[index] = node;
1595
+ },
1596
+ type: mask ? "password" : "text",
1597
+ inputMode: type === "alphanumeric" ? "text" : "numeric",
1598
+ autoComplete: index === 0 ? "one-time-code" : "off",
1599
+ autoCapitalize: "characters",
1600
+ "aria-label": ariaLabels.cell ? ariaLabels.cell(index + 1, normalizedLength) : `PIN digit ${index + 1} of ${normalizedLength}`,
1601
+ "aria-invalid": error || void 0,
1602
+ "aria-describedby": helperId,
1603
+ disabled,
1604
+ "data-jumpy-slot": "pin-input-cell",
1605
+ "data-status": error ? "alarm" : "regular",
1606
+ "data-disabled": disabled ? "true" : "false",
1607
+ maxLength: 1,
1608
+ value: currentValue[index] || "",
1609
+ onChange: (event) => {
1610
+ const nextRaw = event.target.value.slice(-1);
1611
+ if (!nextRaw) return;
1612
+ if (!pattern.test(nextRaw)) return;
1613
+ commit(replaceAt(index, nextRaw.toUpperCase()), index + 1);
1614
+ },
1615
+ onKeyDown: (event) => {
1616
+ if (event.key === "Backspace") {
1617
+ event.preventDefault();
1618
+ if (currentValue[index]) commit(replaceAt(index, ""), index);
1619
+ else commit(replaceAt(Math.max(index - 1, 0), ""), index - 1);
1620
+ }
1621
+ if (event.key === "ArrowLeft") refs.current[Math.max(index - 1, 0)]?.focus();
1622
+ if (event.key === "ArrowRight") refs.current[Math.min(index + 1, normalizedLength - 1)]?.focus();
1623
+ },
1624
+ onPaste: (event) => {
1625
+ event.preventDefault();
1626
+ const pasted = event.clipboardData.getData("text").split("").filter((char) => pattern.test(char)).join("").slice(0, normalizedLength).toUpperCase();
1627
+ if (pasted) commit(pasted, pasted.length - 1);
1628
+ },
1629
+ className: cn(innerClasses.cell?.className),
1630
+ style: { width: sizeValue(theme, JumpySize.largeXL), height: sizeValue(theme, JumpySize.largeXL), boxSizing: "border-box", appearance: "none", WebkitAppearance: "none", border: 0, outlineWidth: "var(--jumpy-cmp-pin-input-cell-outline-width)", outlineStyle: "solid", outlineColor: "var(--jumpy-cmp-pin-input-cell-border)", outlineOffset: `calc(var(--jumpy-cmp-pin-input-cell-outline-width) * -1)`, borderRadius: radiusValue(theme, JumpyRadius.small), backgroundColor: "var(--jumpy-cmp-pin-input-cell-surface)", color: "var(--jumpy-cmp-pin-input-cell-value)", textAlign: "center", overflow: "hidden", fontFamily: theme.fontFamily.familyBase, fontSize: textStyle.fontSize, lineHeight: textStyle.lineHeight, fontWeight: textStyle.fontWeight, ...innerClasses.cell?.style || {} }
1631
+ },
1632
+ index
1633
+ )) }),
1634
+ helperText ? /* @__PURE__ */ jsx("div", { id: helperId, "data-jumpy-slot": "pin-input-helper", className: cn(innerClasses.supportingText?.className), style: { paddingTop: spacingValue(theme, JumpySpacing.small3XS), color: supportingColor, fontFamily: theme.fontFamily.familyBase, fontSize: textStyleValue(theme, JumpyTextStyle.labelSmall).fontSize, ...innerClasses.supportingText?.style || {} }, children: helperText }) : null
1635
+ ] });
1636
+ }
1637
+ function JumpPageControl({ totalPages = 1, currentPage, defaultPage = 0, maxVisibleDots = 7, disabled = false, disabledPages = [], disableInteraction = false, onPageChange, pageAriaLabels = [], className = "", ...props }) {
1638
+ const theme = useJumpyTheme();
1639
+ const [internalPage, setInternalPage] = useState(defaultPage);
1640
+ const selectedPage = clampNumber(currentPage ?? internalPage, 0, Math.max(totalPages - 1, 0));
1641
+ const visibleCount = clampNumber(maxVisibleDots, 3, Math.max(totalPages, 3));
1642
+ const half = Math.floor(visibleCount / 2);
1643
+ const start = clampNumber(selectedPage - half, 0, Math.max(totalPages - visibleCount, 0));
1644
+ const pages = Array.from({ length: Math.min(totalPages, visibleCount) }, (_, index) => start + index);
1645
+ const showLeft = start > 0;
1646
+ const showRight = start + visibleCount < totalPages;
1647
+ function selectPage(page, pageDisabled) {
1648
+ if (disabled || disableInteraction || pageDisabled) return;
1649
+ setInternalPage(page);
1650
+ onPageChange?.(page);
1651
+ }
1652
+ function renderDot(page, truncation = false) {
1653
+ const selected = page === selectedPage;
1654
+ const pageDisabled = disabledPages.includes(page);
1655
+ const dotSize = truncation ? sizeValue(theme, JumpySize.small5XS) : sizeValue(theme, JumpySize.small4XS);
1656
+ return /* @__PURE__ */ jsx("button", { type: "button", disabled: disabled || pageDisabled || truncation || disableInteraction, "aria-label": truncation ? "More pages" : pageAriaLabels[page] || `Page ${page + 1}`, "aria-current": selected ? "page" : void 0, "data-jumpy-component": "page-control-dot", "data-selected": selected ? "true" : "false", "data-truncation": truncation ? "true" : "false", onClick: () => selectPage(page, pageDisabled), style: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, width: truncation ? sizeValue(theme, JumpySize.small3XS) : sizeValue(theme, JumpySize.small), height: truncation ? sizeValue(theme, JumpySize.small3XS) : sizeValue(theme, JumpySize.small), border: `${borderWidthValue(theme, JumpyBorderWidth.medium)}px solid transparent`, borderRadius: radiusValue(theme, JumpyRadius.small), background: "transparent", cursor: disabled || pageDisabled || truncation || disableInteraction ? "default" : "pointer", opacity: disabled || pageDisabled ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid) }, children: /* @__PURE__ */ jsx("span", { style: { display: "block", width: dotSize, height: dotSize, boxSizing: "border-box", borderRadius: selected || truncation ? radiusValue(theme, JumpyRadius.full) : radiusValue(theme, JumpyRadius.small), border: truncation ? 0 : `${borderWidthValue(theme, JumpyBorderWidth.medium)}px solid ${selected ? theme.listItem.active.default.icon : theme.color.borderStrong}`, backgroundColor: selected ? theme.listItem.active.default.icon : truncation ? theme.color.borderStrong : "transparent" } }) }, truncation ? `truncate-${page}` : page);
1657
+ }
1658
+ return /* @__PURE__ */ jsxs("div", { ...props, "data-jumpy-component": "page-control", className: cn(className), style: { position: "relative", overflow: "clip", width: "100%", height: sizeValue(theme, JumpySize.small), display: "flex", justifyContent: "center", alignItems: "center", boxSizing: "border-box", ...props.style || {} }, children: [
1659
+ showLeft ? renderDot(start - 1, true) : null,
1660
+ pages.map((page) => renderDot(page)),
1661
+ showRight ? renderDot(start + visibleCount, true) : null
1662
+ ] });
1663
+ }
1664
+ function textFromNode(node) {
1665
+ if (typeof node === "string" || typeof node === "number") return String(node);
1666
+ if (Array.isArray(node)) return node.map(textFromNode).filter(Boolean).join(" ");
1667
+ if (isValidElement(node)) return textFromNode(node.props.children);
1668
+ return "";
1669
+ }
1670
+ function useJumpSnackbar() {
1671
+ return useContext(JumpySnackbarContext);
1672
+ }
1673
+ function JumpSnackbarProvider({ children, position = "bottom-center", maxSnackbars = 3, offset: offset2 = {}, className = "", ...props }) {
1674
+ const theme = useJumpyTheme();
1675
+ const [snackbars, setSnackbars] = useState([]);
1676
+ const top = position.startsWith("top");
1677
+ const align = position.endsWith("start") ? "flex-start" : position.endsWith("end") ? "flex-end" : "center";
1678
+ const horizontalOffset = offset2.left ?? offset2.right ?? spacingValue(theme, JumpySpacing.large);
1679
+ const verticalOffset = top ? offset2.top ?? spacingValue(theme, JumpySpacing.smallXS) : offset2.bottom ?? spacingValue(theme, JumpySpacing.smallXS);
1680
+ function removeSnackbar(id) {
1681
+ setSnackbars((items) => items.filter((item) => item.id !== id));
1682
+ }
1683
+ function addSnackbar(contentOrData, options = {}) {
1684
+ const id = options.id || `jumpy-snackbar-${Date.now()}-${Math.random().toString(36).slice(2)}`;
1685
+ const isData = contentOrData && typeof contentOrData === "object" && !isValidElement(contentOrData) && ("title" in contentOrData || "description" in contentOrData || "status" in contentOrData);
1686
+ const duration = options.persistent || contentOrData?.persistent ? null : options.duration ?? contentOrData?.duration ?? 4e3;
1687
+ const snackbar = { id, content: contentOrData, isData, createdAt: Date.now() };
1688
+ setSnackbars((items) => [snackbar, ...items.filter((item) => item.id !== id)].slice(0, maxSnackbars));
1689
+ if (duration) window.setTimeout(() => removeSnackbar(id), duration);
1690
+ return id;
1691
+ }
1692
+ const contextValue = { addSnackbar, removeSnackbar, clearAll: () => setSnackbars([]), snackbars };
1693
+ return /* @__PURE__ */ jsxs(JumpySnackbarContext.Provider, { value: contextValue, children: [
1694
+ children,
1695
+ /* @__PURE__ */ jsx("output", { "aria-live": "assertive", "aria-atomic": "true", style: { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", border: 0 }, children: snackbars[0] ? textFromNode(snackbars[0].content?.title || snackbars[0].content?.description || snackbars[0].content) : "" }),
1696
+ /* @__PURE__ */ jsx("div", { ...props, "data-jumpy-component": "snackbar-provider", className: cn(className), style: { position: "fixed", zIndex: 1100, left: position.endsWith("start") ? horizontalOffset : position.endsWith("end") ? "auto" : "50%", right: position.endsWith("end") ? horizontalOffset : "auto", top: top ? verticalOffset : "auto", bottom: top ? "auto" : verticalOffset, transform: position.endsWith("center") ? "translateX(-50%)" : void 0, display: "flex", flexDirection: "column", alignItems: align, gap: spacingValue(theme, JumpySpacing.smallXS), width: "min(487px, calc(100vw - 32px))", maxWidth: "50vw", pointerEvents: "none", ...props.style || {} }, children: snackbars.map((snackbar) => snackbar.isData ? /* @__PURE__ */ jsx(JumpBanner, { floating: true, status: snackbar.content.status || "info", title: snackbar.content.title, actions: snackbar.content.slots?.actions, style: { width: "100%", pointerEvents: "auto", boxShadow: theme.shadow.positiveLarge }, children: snackbar.content.description }, snackbar.id) : /* @__PURE__ */ jsx("div", { "data-jumpy-component": "snackbar", style: { width: "100%", pointerEvents: "auto", borderRadius: radiusValue(theme, JumpyRadius.small), boxShadow: theme.shadow.positiveLarge }, children: snackbar.content }, snackbar.id)) })
1697
+ ] });
1698
+ }
1699
+ function JumpTable({ children, showHorizontalBorders = false, disabled = false, stickyHeader = false, density = "regular", containerProps, innerClasses = {}, className = "", ...props }) {
1700
+ const { className: containerClassName, style: containerStyle, ...restContainerProps } = containerProps || {};
1701
+ const contextValue = { showHorizontalBorders, disabled };
1702
+ return /* @__PURE__ */ jsx(JumpyTableContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "table-wrapper", className: innerClasses.wrapper?.className, style: innerClasses.wrapper?.style, children: /* @__PURE__ */ jsx("div", { ...restContainerProps, "data-jumpy-slot": "table-container", "data-sticky-header": stickyHeader ? "true" : "false", className: cn(containerClassName), style: containerStyle, children: /* @__PURE__ */ jsx("table", { ...props, "data-jumpy-component": "table", "data-horizontal-borders": showHorizontalBorders ? "true" : "false", "data-density": density === "condensed" ? "condensed" : void 0, "data-disabled": disabled ? "true" : void 0, role: props.role || "grid", className: cn(className), children }) }) }) });
1703
+ }
1704
+ function JumpTableHead({ as, children, className = "", ...props }) {
1705
+ const Component = as || "thead";
1706
+ return /* @__PURE__ */ jsx(Component, { ...props, "data-jumpy-component": "table-head", role: props.role || "rowgroup", className: cn(className), children });
1707
+ }
1708
+ function JumpTableBody({ as, children, className = "", ...props }) {
1709
+ const Component = as || "tbody";
1710
+ return /* @__PURE__ */ jsx(Component, { ...props, "data-jumpy-component": "table-body", role: props.role || "rowgroup", className: cn(className), children });
1711
+ }
1712
+ function JumpTableFooter({ as, children, className = "", ...props }) {
1713
+ const Component = as || "tfoot";
1714
+ return /* @__PURE__ */ jsx(Component, { ...props, "data-jumpy-component": "table-footer", role: props.role || "rowgroup", className: cn(className), children });
1715
+ }
1716
+ function JumpTableRow({ as, children, selected = false, disabled = false, disableHover = false, verticalAlign = "middle", dragging = false, className = "", onClick, ...props }) {
1717
+ const Component = as || "tr";
1718
+ const tableContext = useContext(JumpyTableContext);
1719
+ const isDisabled = disabled || tableContext?.disabled;
1720
+ const isInteractive = !disableHover && !isDisabled && Boolean(onClick);
1721
+ return /* @__PURE__ */ jsx(
1722
+ Component,
1723
+ {
1724
+ ...props,
1725
+ "data-jumpy-component": "table-row",
1726
+ "data-selectable": isInteractive ? "true" : void 0,
1727
+ "data-disable-hover": disableHover ? "true" : void 0,
1728
+ "data-disabled": isDisabled ? "true" : void 0,
1729
+ "data-state": selected ? "selected" : void 0,
1730
+ "data-vertical-align": verticalAlign === "top" ? "top" : void 0,
1731
+ "data-dragging": dragging ? "true" : void 0,
1732
+ role: props.role || "row",
1733
+ "aria-selected": selected || void 0,
1734
+ "aria-disabled": isDisabled || void 0,
1735
+ tabIndex: isInteractive ? props.tabIndex ?? 0 : props.tabIndex,
1736
+ className: cn(className),
1737
+ onClick: isDisabled ? void 0 : onClick,
1738
+ children
1739
+ }
1740
+ );
1741
+ }
1742
+ function getTableCellTypography(theme, emphasized = false) {
1743
+ return textStyleValue(theme, emphasized ? JumpyTextStyle.bodyMediumEmphasized : JumpyTextStyle.bodyMedium);
1744
+ }
1745
+ function tableCellTextStyle(theme, emphasized = false, style) {
1746
+ const textStyle = getTableCellTypography(theme, emphasized);
1747
+ return {
1748
+ fontFamily: theme.fontFamily.familyBase,
1749
+ fontSize: textStyle.fontSize,
1750
+ lineHeight: textStyle.lineHeight,
1751
+ fontWeight: textStyle.fontWeight,
1752
+ letterSpacing: textStyle.letterSpacing,
1753
+ ...style
1754
+ };
1755
+ }
1756
+ function JumpTableCell({ as, children, selected = false, disabled = false, align, variant = "default", padding = "default", showHorizontalBorder, pinned, pinnedEdge = false, leading, trailing, description, slots, innerClasses = {}, className = "", onClick, ...props }) {
1757
+ const theme = useJumpyTheme();
1758
+ const tableContext = useContext(JumpyTableContext);
1759
+ const Component = as || "td";
1760
+ const isControl = variant === "control" || padding === "checkbox";
1761
+ const resolvedAlign = align || (isControl ? "center" : "start");
1762
+ const cellLeading = slots?.leading ?? leading;
1763
+ const cellTrailing = slots?.trailing ?? trailing;
1764
+ const cellDescription = slots?.description ?? description;
1765
+ const isDisabled = disabled || tableContext?.disabled;
1766
+ const resolvedHorizontalBorder = showHorizontalBorder ?? tableContext?.showHorizontalBorders ?? false;
1767
+ const isInteractive = !isDisabled && Boolean(onClick);
1768
+ return /* @__PURE__ */ jsxs(
1769
+ Component,
1770
+ {
1771
+ ...props,
1772
+ "data-jumpy-component": "table-cell",
1773
+ "data-pinned": pinned || void 0,
1774
+ "data-pinned-edge": pinnedEdge ? "true" : void 0,
1775
+ "data-horizontal-border": resolvedHorizontalBorder ? "true" : "false",
1776
+ "data-state": selected ? "selected" : "default",
1777
+ "data-disabled": isDisabled ? "true" : void 0,
1778
+ "data-align": resolvedAlign,
1779
+ "data-padding": padding,
1780
+ "data-variant": isControl ? "control" : void 0,
1781
+ "data-interactive": isInteractive ? "true" : void 0,
1782
+ role: props.role || "gridcell",
1783
+ "aria-disabled": isDisabled || void 0,
1784
+ "aria-selected": selected || void 0,
1785
+ tabIndex: props.tabIndex ?? (isInteractive ? 0 : void 0),
1786
+ className: cn(className),
1787
+ onClick: isDisabled ? void 0 : onClick,
1788
+ children: [
1789
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-jumpy-slot": "table-cell-surface" }),
1790
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "table-cell-inner", "data-align": resolvedAlign, className: innerClasses.inner?.className, style: innerClasses.inner?.style, children: [
1791
+ cellLeading ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-leading", className: innerClasses.leading?.className, style: innerClasses.leading?.style, children: cellLeading }) : null,
1792
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "table-cell-content", className: innerClasses.content?.className, style: innerClasses.content?.style, children: [
1793
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-text", className: innerClasses.text?.className, style: tableCellTextStyle(theme, false, innerClasses.text?.style), children }),
1794
+ cellDescription ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-description", className: innerClasses.description?.className, style: tableCellTextStyle(theme, false, innerClasses.description?.style), children: cellDescription }) : null
1795
+ ] }),
1796
+ cellTrailing ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-trailing", className: innerClasses.trailing?.className, style: innerClasses.trailing?.style, children: cellTrailing }) : null
1797
+ ] })
1798
+ ]
1799
+ }
1800
+ );
1801
+ }
1802
+ function getSortMapping(currentOrder, ascValue, descValue, defaultValue) {
1803
+ if (currentOrder === "ascending" || currentOrder === "asc") return ascValue;
1804
+ if (currentOrder === "descending" || currentOrder === "desc") return descValue;
1805
+ return defaultValue;
1806
+ }
1807
+ function getNextSortOrder(order) {
1808
+ if (order === "ascending" || order === "asc") return "desc";
1809
+ if (order === "descending" || order === "desc") return "default";
1810
+ return "asc";
1811
+ }
1812
+ function getSortOrder(sortDirection) {
1813
+ return getSortMapping(sortDirection, "asc", "desc", "default");
1814
+ }
1815
+ function getSortActionLabel(currentOrder, columnName) {
1816
+ if (columnName) {
1817
+ return getSortMapping(currentOrder, `Sort ${columnName} descending`, `Clear sort on ${columnName}`, `Sort ${columnName} ascending`);
1818
+ }
1819
+ return getSortMapping(currentOrder, "Sort descending", "Clear sort", "Sort ascending");
1820
+ }
1821
+ function participatesInSelection(row) {
1822
+ return row.getCanSelect() || (row.subRows?.some(participatesInSelection) ?? false);
1823
+ }
1824
+ function getSelectionChildren(row) {
1825
+ const children = row.subRows?.filter(participatesInSelection);
1826
+ return children?.length ? children : void 0;
1827
+ }
1828
+ function isRowFullySelected(row) {
1829
+ const children = getSelectionChildren(row);
1830
+ if (children) return children.every(isRowFullySelected);
1831
+ return row.getCanSelect() && row.getIsSelected();
1832
+ }
1833
+ function applySubtreeSelection(row, selected) {
1834
+ if (row.getCanSelect() && row.getIsSelected() !== selected) {
1835
+ row.toggleSelected(selected, { selectChildren: false });
1836
+ }
1837
+ row.subRows?.forEach((subRow) => applySubtreeSelection(subRow, selected));
1838
+ }
1839
+ function getRowCheckboxProps(row, disabled) {
1840
+ const checked = isRowFullySelected(row);
1841
+ return {
1842
+ checked,
1843
+ indeterminate: !checked && row.getIsSomeSelected(),
1844
+ disabled: disabled || !row.getCanSelect(),
1845
+ "aria-label": `Select row ${row.id}`,
1846
+ onChange: () => applySubtreeSelection(row, !isRowFullySelected(row))
1847
+ };
1848
+ }
1849
+ function getHeaderCheckboxProps(table, disabled) {
1850
+ const topRows = table.getRowModel?.().rows.filter((row) => (row.depth ?? 0) === 0 && participatesInSelection(row));
1851
+ const isTree = topRows?.some((row) => getSelectionChildren(row)) ?? false;
1852
+ const checked = isTree && topRows ? topRows.every(isRowFullySelected) : table.getIsAllRowsSelected();
1853
+ return {
1854
+ checked,
1855
+ indeterminate: !checked && table.getIsSomeRowsSelected(),
1856
+ disabled,
1857
+ "aria-label": "Select all rows",
1858
+ onChange: table.getToggleAllRowsSelectedHandler()
1859
+ };
1860
+ }
1861
+ function sortSymbol(order) {
1862
+ if (order === "ascending" || order === "asc") return "\u2191";
1863
+ if (order === "descending" || order === "desc") return "\u2193";
1864
+ return "\u2195";
1865
+ }
1866
+ function ariaSortOrder(order) {
1867
+ if (order === "ascending" || order === "asc") return "ascending";
1868
+ if (order === "descending" || order === "desc") return "descending";
1869
+ if (order === "default" || order === "none" || order === false) return "none";
1870
+ return void 0;
1871
+ }
1872
+ function JumpTableHeaderCell({ as, children, selected = false, disabled = false, align, variant = "default", padding = "default", showHorizontalBorder, pinned, pinnedEdge = false, sortable = false, sortOrder = "default", onSortChange, ariaLabels, leading, trailing, slots, innerClasses = {}, className = "", ...props }) {
1873
+ const theme = useJumpyTheme();
1874
+ const tableContext = useContext(JumpyTableContext);
1875
+ const Component = as || "th";
1876
+ const isControl = variant === "control" || padding === "checkbox";
1877
+ const resolvedAlign = align || (isControl ? "center" : "start");
1878
+ const cellLeading = slots?.leading ?? leading;
1879
+ const cellTrailing = slots?.trailing ?? trailing;
1880
+ const isDisabled = disabled || tableContext?.disabled;
1881
+ const resolvedHorizontalBorder = showHorizontalBorder ?? tableContext?.showHorizontalBorders ?? false;
1882
+ const participatesInSort = sortable || sortOrder !== "default" && sortOrder !== "none";
1883
+ const ariaSort = participatesInSort ? ariaSortOrder(sortOrder) : void 0;
1884
+ return /* @__PURE__ */ jsxs(
1885
+ Component,
1886
+ {
1887
+ ...props,
1888
+ "data-jumpy-component": "table-header-cell",
1889
+ "data-pinned": pinned || void 0,
1890
+ "data-pinned-edge": pinnedEdge ? "true" : void 0,
1891
+ "data-horizontal-border": resolvedHorizontalBorder ? "true" : "false",
1892
+ "data-sortable": sortable ? "true" : "false",
1893
+ "data-sort-order": sortOrder,
1894
+ "data-disabled": isDisabled ? "true" : void 0,
1895
+ "data-state": selected ? "selected" : "default",
1896
+ "data-align": resolvedAlign,
1897
+ "data-padding": padding,
1898
+ "data-variant": isControl ? "control" : void 0,
1899
+ role: props.role || "columnheader",
1900
+ "aria-sort": ariaSort,
1901
+ className: cn(className),
1902
+ children: [
1903
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-jumpy-slot": "table-cell-surface" }),
1904
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "table-cell-inner", "data-align": resolvedAlign, className: innerClasses.inner?.className, style: innerClasses.inner?.style, children: [
1905
+ cellLeading ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-leading", className: innerClasses.leading?.className, style: innerClasses.leading?.style, children: cellLeading }) : null,
1906
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-content", className: innerClasses.label?.className, style: innerClasses.label?.style, children: /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "table-cell-text", className: innerClasses.labelText?.className, style: tableCellTextStyle(theme, true, innerClasses.labelText?.style), children }) }),
1907
+ cellTrailing || sortable ? /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "table-cell-trailing", className: innerClasses.trailing?.className, style: innerClasses.trailing?.style, children: [
1908
+ sortable ? /* @__PURE__ */ jsx(JumpIconButton, { variant: "secondary", disabled: isDisabled, "aria-label": typeof ariaLabels?.sort === "function" ? ariaLabels.sort(sortOrder) : ariaLabels?.sort || getSortActionLabel(sortOrder, typeof children === "string" ? children : void 0), onClick: () => onSortChange?.(getNextSortOrder(sortOrder)), children: sortSymbol(sortOrder) }) : null,
1909
+ cellTrailing
1910
+ ] }) : null
1911
+ ] })
1912
+ ]
1913
+ }
1914
+ );
1915
+ }
1916
+ function ChevronIcon({ expanded }) {
1917
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: expanded ? "M7.4 15.4 12 10.8l4.6 4.6 1.4-1.4-6-6-6 6 1.4 1.4Z" : "M7.4 8.6 12 13.2l4.6-4.6 1.4 1.4-6 6-6-6 1.4-1.4Z" }) });
1918
+ }
1919
+ function JumpExpandable({ as, variant = "filled", disabled = false, expanded, defaultExpanded = false, onChange, slots, innerClasses = {}, className = "", children, ...props }) {
1920
+ const Component = as || "div";
1921
+ const context = useContext(JumpyExpandableContext);
1922
+ const panelId = useId();
1923
+ const isDisabled = disabled || context?.disabled;
1924
+ const isControlled = expanded !== void 0 || context?.expanded !== void 0;
1925
+ const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);
1926
+ const isExpanded = isControlled ? Boolean(expanded ?? context?.expanded) : uncontrolledExpanded;
1927
+ const label = slots?.label;
1928
+ const description = slots?.description;
1929
+ const leading = slots?.leading;
1930
+ const trailing = slots?.trailing;
1931
+ function handleToggle() {
1932
+ if (isDisabled) return;
1933
+ const nextExpanded = !isExpanded;
1934
+ context?.onExpandedChange?.(nextExpanded);
1935
+ if (!isControlled) setUncontrolledExpanded(nextExpanded);
1936
+ onChange?.(nextExpanded);
1937
+ }
1938
+ const header = /* @__PURE__ */ jsxs(
1939
+ "button",
1940
+ {
1941
+ ref: context?.buttonRef,
1942
+ type: "button",
1943
+ "data-jumpy-slot": "expandable-header",
1944
+ className: innerClasses.header?.className,
1945
+ style: innerClasses.header?.style,
1946
+ onClick: handleToggle,
1947
+ disabled: isDisabled,
1948
+ "aria-controls": panelId,
1949
+ "aria-expanded": isExpanded,
1950
+ children: [
1951
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "expandable-slots-container", children: [
1952
+ leading,
1953
+ label || description ? /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "expandable-header-content", className: innerClasses.headerContent?.className, style: innerClasses.headerContent?.style, children: [
1954
+ label ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "expandable-label", className: innerClasses.label?.className, style: innerClasses.label?.style, children: label }) : null,
1955
+ description ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "expandable-description", className: innerClasses.description?.className, style: innerClasses.description?.style, children: description }) : null
1956
+ ] }) : null
1957
+ ] }),
1958
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "expandable-slots-container", children: [
1959
+ trailing,
1960
+ !isDisabled ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "expandable-icon", className: innerClasses.icon?.className, style: innerClasses.icon?.style, children: /* @__PURE__ */ jsx(ChevronIcon, { expanded: isExpanded }) }) : null
1961
+ ] })
1962
+ ]
1963
+ }
1964
+ );
1965
+ return /* @__PURE__ */ jsxs(Component, { ...props, "data-jumpy-component": "expandable", "data-variant": variant, className: cn(className), children: [
1966
+ context?.headingLevel ? /* @__PURE__ */ jsx(context.headingLevel, { "data-jumpy-slot": "expandable-header-wrapper", children: header }) : header,
1967
+ /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "expandable-content-wrapper", "data-expanded": isExpanded ? "true" : "false", children: /* @__PURE__ */ jsx("div", { id: panelId, "data-jumpy-slot": "expandable-panel", className: innerClasses.panel?.className, style: innerClasses.panel?.style, children }) })
1968
+ ] });
1969
+ }
1970
+ function headingTag(level) {
1971
+ if (!level) return void 0;
1972
+ return `h${Math.min(6, Math.max(1, Number(level)))}`;
1973
+ }
1974
+ function focusExpandableButton(buttonRefs, index) {
1975
+ const nextButton = buttonRefs.current[index];
1976
+ nextButton?.focus();
1977
+ }
1978
+ function JumpExpandableGroup({ as, multiple = false, disabled = false, headingLevel, onChange, innerClasses = {}, className = "", children, ...props }) {
1979
+ const Component = as || "div";
1980
+ const buttonRefs = useRef([]);
1981
+ const childArray = Children.toArray(children);
1982
+ const [expandedIndices, setExpandedIndices] = useState(() => {
1983
+ const initial = /* @__PURE__ */ new Set();
1984
+ childArray.forEach((child, index) => {
1985
+ if (isValidElement(child) && (child.props.expanded || child.props.defaultExpanded)) initial.add(index);
1986
+ });
1987
+ return initial;
1988
+ });
1989
+ function updateExpanded(index, nextExpanded) {
1990
+ setExpandedIndices((current) => {
1991
+ const next = new Set(current);
1992
+ if (nextExpanded) {
1993
+ if (!multiple) next.clear();
1994
+ next.add(index);
1995
+ } else {
1996
+ next.delete(index);
1997
+ }
1998
+ onChange?.(Array.from(next).sort((a, b) => a - b));
1999
+ return next;
2000
+ });
2001
+ }
2002
+ function handleKeyDown(event) {
2003
+ const currentIndex = buttonRefs.current.findIndex((button) => button === event.target);
2004
+ if (currentIndex === -1) {
2005
+ props.onKeyDown?.(event);
2006
+ return;
2007
+ }
2008
+ if (event.key === KeyboardKeys.ARROW_DOWN) {
2009
+ event.preventDefault();
2010
+ focusExpandableButton(buttonRefs, (currentIndex + 1) % buttonRefs.current.length);
2011
+ } else if (event.key === KeyboardKeys.ARROW_UP) {
2012
+ event.preventDefault();
2013
+ focusExpandableButton(buttonRefs, (currentIndex - 1 + buttonRefs.current.length) % buttonRefs.current.length);
2014
+ } else if (event.key === KeyboardKeys.HOME) {
2015
+ event.preventDefault();
2016
+ focusExpandableButton(buttonRefs, 0);
2017
+ } else if (event.key === KeyboardKeys.END) {
2018
+ event.preventDefault();
2019
+ focusExpandableButton(buttonRefs, buttonRefs.current.length - 1);
2020
+ }
2021
+ props.onKeyDown?.(event);
2022
+ }
2023
+ return /* @__PURE__ */ jsx(Component, { ...props, "data-jumpy-component": "expandable-group", "aria-disabled": disabled || void 0, className: cn(className), onKeyDown: handleKeyDown, children: childArray.map((child, index) => {
2024
+ const contextValue = {
2025
+ headingLevel: headingTag(headingLevel),
2026
+ disabled,
2027
+ expanded: expandedIndices.has(index),
2028
+ onExpandedChange: (nextExpanded) => updateExpanded(index, nextExpanded),
2029
+ buttonRef: (element) => {
2030
+ buttonRefs.current[index] = element;
2031
+ }
2032
+ };
2033
+ return /* @__PURE__ */ jsxs(JumpyExpandableContext.Provider, { value: contextValue, children: [
2034
+ child,
2035
+ index < childArray.length - 1 ? /* @__PURE__ */ jsx(JumpDivider, { direction: "horizontal", "data-jumpy-slot": "expandable-group-divider", className: innerClasses.divider?.className, style: innerClasses.divider?.style }) : null
2036
+ ] }, child.key || index);
2037
+ }) });
2038
+ }
2039
+ var JUMPY_POPUP_Z_INDEX = 1200;
2040
+ function JumpyPortal({ children }) {
2041
+ const theme = useJumpyTheme();
2042
+ const scope = useContext(JumpyThemeScopeContext);
2043
+ return /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx("div", { "data-jumpy-theme": "true", "data-jumpy-scope": scope || void 0, "data-jumpy-portal": "true", style: { ...!scope ? themeToCSSVariables(theme) : {}, fontFamily: theme.fontFamily.familyBase }, children }) });
2044
+ }
2045
+ function JumpyModalLayer({ children, onVisibleChange, style, onPointerDown, ...props }) {
2046
+ const theme = useJumpyTheme();
2047
+ const { refs, context } = useFloating({ open: true, onOpenChange: onVisibleChange });
2048
+ return /* @__PURE__ */ jsx(JumpyPortal, { children: /* @__PURE__ */ jsx(FloatingOverlay, { ...props, lockScroll: true, onPointerDown: (event) => {
2049
+ onPointerDown?.(event);
2050
+ if (event.target === event.currentTarget) event.preventDefault();
2051
+ }, style, children: /* @__PURE__ */ jsx(FloatingFocusManager, { context, outsideElementsInert: true, restoreFocus: true, children: cloneElement(children, {
2052
+ ref: refs.setFloating,
2053
+ tabIndex: -1,
2054
+ onKeyDown(event) {
2055
+ children.props.onKeyDown?.(event);
2056
+ const nestedPopup = !event.currentTarget.contains(event.target) || event.target.closest('[aria-haspopup][aria-expanded="true"]');
2057
+ if (event.key === KeyboardKeys.ESCAPE && !event.defaultPrevented && !nestedPopup) {
2058
+ event.preventDefault();
2059
+ event.stopPropagation();
2060
+ onVisibleChange?.(false);
2061
+ }
2062
+ }
2063
+ }) }) }) });
2064
+ }
2065
+ function JumpSheet({ visible = false, onVisibleChange, position = "right", title, subtitle, footer, children, className = "", ...props }) {
2066
+ const theme = useJumpyTheme();
2067
+ const vertical = position === "left" || position === "right";
2068
+ if (!visible) return null;
2069
+ return /* @__PURE__ */ jsx(JumpyModalLayer, { onVisibleChange, "data-jumpy-component": "sheet-overlay", role: "presentation", onClick: (event) => {
2070
+ if (event.target === event.currentTarget) onVisibleChange?.(false);
2071
+ }, style: { position: "fixed", inset: 0, zIndex: 50, backgroundColor: theme.color.surfaceOverlay }, children: /* @__PURE__ */ jsxs("aside", { ...props, "data-jumpy-component": "sheet", className: cn(className), role: "dialog", "aria-modal": "true", "aria-label": title || props["aria-label"], tabIndex: -1, onClick: (event) => {
2072
+ event.stopPropagation();
2073
+ props.onClick?.(event);
2074
+ }, style: { position: "fixed", top: position === "bottom" ? "auto" : 0, bottom: position === "top" ? "auto" : 0, left: position === "right" ? "auto" : 0, right: position === "left" ? "auto" : 0, width: vertical ? "min(420px, 100vw)" : "100vw", height: vertical ? "100dvh" : "min(420px, 100dvh)", backgroundColor: theme.color.surfaceCard, color: theme.color.textPrimary, boxShadow: theme.shadow.positiveMedium, display: "flex", flexDirection: "column", ...props.style || {} }, children: [
2075
+ /* @__PURE__ */ jsxs("div", { style: { padding: spacingValue(theme, JumpySpacing.large), borderBottom: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}` }, children: [
2076
+ title ? /* @__PURE__ */ jsx(JumpTypography, { variant: "titleMediumEmphasized", children: title }) : null,
2077
+ subtitle ? /* @__PURE__ */ jsx(JumpTypography, { variant: "bodySmall", color: "secondary", children: subtitle }) : null
2078
+ ] }),
2079
+ /* @__PURE__ */ jsx("div", { style: { padding: spacingValue(theme, JumpySpacing.large), flex: 1, overflow: "auto" }, children }),
2080
+ footer ? /* @__PURE__ */ jsx("div", { style: { padding: spacingValue(theme, JumpySpacing.large), borderTop: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid ${theme.color.borderSubtle}` }, children: footer }) : null
2081
+ ] }) });
2082
+ }
2083
+ function JumpyNavigationIcon({ type, size: size2 = 24 }) {
2084
+ const paths = {
2085
+ menu: "M4 6h16M4 12h16M4 18h16",
2086
+ close: "M6 6l12 12M18 6 6 18",
2087
+ back: "M15 18l-6-6 6-6",
2088
+ more: "M5 12h.01M12 12h.01M19 12h.01"
2089
+ };
2090
+ return /* @__PURE__ */ jsx("svg", { width: size2, height: size2, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: type === "more" ? 3 : 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", "data-jumpy-stroke-icon": "true", style: { display: "block" }, children: /* @__PURE__ */ jsx("path", { d: paths[type] }) });
2091
+ }
2092
+ function JumpTopBar({ leading, onMenu, onBack, menuOpen = false, title, subtitle, trailing, sticky = true, titleAlign = "start", titleHidden = false, ariaLabels = {}, className = "", ...props }) {
2093
+ const theme = useJumpyTheme();
2094
+ const [scrolled, setScrolled] = useState(false);
2095
+ const titleStyle = textStyleValue(theme, JumpyTextStyle.bodyLargeEmphasized);
2096
+ const subtitleStyle = textStyleValue(theme, JumpyTextStyle.bodySmall);
2097
+ useEffect(() => {
2098
+ if (!sticky) return void 0;
2099
+ const update = () => setScrolled(window.scrollY > 0);
2100
+ update();
2101
+ window.addEventListener("scroll", update, { passive: true });
2102
+ return () => window.removeEventListener("scroll", update);
2103
+ }, [sticky]);
2104
+ const leadingType = leading === "menu" || leading === "back" ? leading : void 0;
2105
+ const leadingNode = leading === "menu" ? /* @__PURE__ */ jsx(JumpIconButton, { variant: "borderless", "aria-label": ariaLabels.menu || "Open navigation", "aria-haspopup": "dialog", "aria-expanded": menuOpen, onClick: onMenu, children: /* @__PURE__ */ jsx(JumpyNavigationIcon, { type: "menu" }) }) : leading === "back" ? /* @__PURE__ */ jsx(JumpIconButton, { variant: "borderless", "aria-label": ariaLabels.back || "Back", onClick: onBack, children: /* @__PURE__ */ jsx(JumpyNavigationIcon, { type: "back" }) }) : leading;
2106
+ return /* @__PURE__ */ jsxs(
2107
+ "header",
2108
+ {
2109
+ ...props,
2110
+ "data-jumpy-component": "top-bar",
2111
+ "data-sticky": sticky ? "true" : "false",
2112
+ "data-scrolled": scrolled ? "true" : "false",
2113
+ "data-title-align": titleAlign,
2114
+ "data-title-hidden": titleHidden ? "true" : "false",
2115
+ className: cn(className),
2116
+ style: {
2117
+ boxSizing: "border-box",
2118
+ position: sticky ? "sticky" : "relative",
2119
+ top: 0,
2120
+ zIndex: 40,
2121
+ alignItems: "center",
2122
+ gap: spacingValue(theme, JumpySpacing.medium),
2123
+ minHeight: `calc(${sizeValue(theme, JumpySize.large2XL)}px + env(safe-area-inset-top, 0px))`,
2124
+ padding: `calc(${spacingValue(theme, JumpySpacing.smallXS)}px + env(safe-area-inset-top, 0px)) calc(${spacingValue(theme, JumpySpacing.large)}px + env(safe-area-inset-right, 0px)) ${spacingValue(theme, JumpySpacing.smallXS)}px calc(${spacingValue(theme, JumpySpacing.large)}px + env(safe-area-inset-left, 0px))`,
2125
+ color: "var(--jumpy-color-text-primary)",
2126
+ fontFamily: theme.fontFamily.familyBase,
2127
+ ...props.style || {}
2128
+ },
2129
+ children: [
2130
+ leadingNode ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "top-bar-leading", "data-leading": leadingType, style: { minWidth: 0, maxWidth: "100%", marginLeft: -spacingValue(theme, JumpySpacing.smallXS) }, children: leadingNode }) : null,
2131
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "top-bar-title", "aria-hidden": titleHidden || void 0, style: titleAlign === "center" ? {
2132
+ display: "flex",
2133
+ flexDirection: "column",
2134
+ alignItems: "center",
2135
+ textAlign: "center",
2136
+ minWidth: 0,
2137
+ opacity: titleHidden ? 0 : 1
2138
+ } : { display: "flex", flexDirection: "column", flex: "1 1 auto", minWidth: 0, opacity: titleHidden ? 0 : 1 }, children: [
2139
+ title ? /* @__PURE__ */ jsx("span", { style: { maxWidth: "100%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: titleStyle.fontSize, lineHeight: titleStyle.lineHeight, fontWeight: titleStyle.fontWeight }, children: title }) : null,
2140
+ subtitle ? /* @__PURE__ */ jsx("span", { style: { maxWidth: "100%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: "var(--jumpy-color-text-secondary)", fontSize: subtitleStyle.fontSize, lineHeight: subtitleStyle.lineHeight }, children: subtitle }) : null
2141
+ ] }),
2142
+ trailing ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "top-bar-trailing", style: { display: "inline-flex", flex: "0 0 auto", marginLeft: "auto", alignItems: "center", gap: spacingValue(theme, JumpySpacing.smallXS) }, children: trailing }) : null
2143
+ ]
2144
+ }
2145
+ );
2146
+ }
2147
+ function JumpTabBar({ children, className = "", "aria-label": ariaLabel = "Main navigation", ...props }) {
2148
+ const theme = useJumpyTheme();
2149
+ return /* @__PURE__ */ jsx(
2150
+ "div",
2151
+ {
2152
+ "data-jumpy-component": "tab-bar",
2153
+ className: cn(className),
2154
+ style: {
2155
+ boxSizing: "border-box",
2156
+ position: "sticky",
2157
+ bottom: 0,
2158
+ zIndex: 40,
2159
+ padding: `0 calc(${spacingValue(theme, JumpySpacing.medium)}px + env(safe-area-inset-right, 0px)) calc(${spacingValue(theme, JumpySpacing.medium)}px + env(safe-area-inset-bottom, 0px)) calc(${spacingValue(theme, JumpySpacing.medium)}px + env(safe-area-inset-left, 0px))`,
2160
+ pointerEvents: "none"
2161
+ },
2162
+ children: /* @__PURE__ */ jsx(
2163
+ "nav",
2164
+ {
2165
+ ...props,
2166
+ "aria-label": ariaLabel,
2167
+ "data-jumpy-slot": "tab-bar-surface",
2168
+ style: {
2169
+ boxSizing: "border-box",
2170
+ display: "flex",
2171
+ gap: spacingValue(theme, JumpySpacing.small4XS),
2172
+ padding: spacingValue(theme, JumpySpacing.small3XS),
2173
+ borderRadius: radiusValue(theme, JumpyRadius.large2XL),
2174
+ border: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-color-border-subtle)`,
2175
+ boxShadow: theme.shadow.positiveMedium,
2176
+ pointerEvents: "auto",
2177
+ fontFamily: theme.fontFamily.familyBase,
2178
+ ...props.style || {}
2179
+ },
2180
+ children
2181
+ }
2182
+ )
2183
+ }
2184
+ );
2185
+ }
2186
+ function JumpTabBarItem({ as, icon, badge, selected = false, children, className = "", ...props }) {
2187
+ const theme = useJumpyTheme();
2188
+ const Component = as || (props.href ? "a" : "button");
2189
+ const labelStyle = textStyleValue(theme, JumpyTextStyle.labelSmall);
2190
+ const badgeNode = typeof badge === "number" ? badge > 0 ? /* @__PURE__ */ jsx(JumpBadge, { size: "small", status: "active", "aria-label": void 0, role: "presentation", "aria-live": "off", children: badge > 99 ? "99+" : badge }) : null : badge;
2191
+ return /* @__PURE__ */ jsxs(
2192
+ Component,
2193
+ {
2194
+ ...props,
2195
+ type: Component === "button" ? props.type || "button" : props.type,
2196
+ "aria-current": selected ? props["aria-current"] || "page" : props["aria-current"],
2197
+ "data-jumpy-component": "tab-bar-item",
2198
+ "data-selected": selected ? "true" : "false",
2199
+ className: cn(className),
2200
+ style: {
2201
+ boxSizing: "border-box",
2202
+ flex: "1 1 0",
2203
+ minWidth: 0,
2204
+ display: "flex",
2205
+ flexDirection: "column",
2206
+ alignItems: "center",
2207
+ justifyContent: "center",
2208
+ gap: spacingValue(theme, JumpySpacing.small4XS),
2209
+ minHeight: sizeValue(theme, JumpySize.large2XL),
2210
+ padding: `${spacingValue(theme, JumpySpacing.smallXS)}px ${spacingValue(theme, JumpySpacing.small3XS)}px`,
2211
+ border: 0,
2212
+ borderRadius: radiusValue(theme, JumpyRadius.largeXL),
2213
+ backgroundColor: selected ? "var(--jumpy-interactive-active-regular-default-surface)" : "transparent",
2214
+ color: selected ? "var(--jumpy-interactive-active-regular-default-text)" : "var(--jumpy-color-text-secondary)",
2215
+ textDecoration: "none",
2216
+ cursor: "pointer",
2217
+ fontFamily: theme.fontFamily.familyBase,
2218
+ fontSize: labelStyle.fontSize,
2219
+ lineHeight: labelStyle.lineHeight,
2220
+ fontWeight: labelStyle.fontWeight,
2221
+ ...props.style || {}
2222
+ },
2223
+ children: [
2224
+ icon ? /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "tab-bar-item-icon", "aria-hidden": "true", style: { position: "relative", display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, width: sizeValue(theme, JumpySize.smallXS), height: sizeValue(theme, JumpySize.smallXS) }, children: [
2225
+ icon,
2226
+ badgeNode ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "tab-bar-item-badge", style: { position: "absolute", top: -spacingValue(theme, JumpySpacing.smallXS), left: "50%", marginLeft: spacingValue(theme, JumpySpacing.small3XS), display: "inline-flex" }, children: badgeNode }) : null
2227
+ ] }) : null,
2228
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "tab-bar-item-label", style: { maxWidth: "100%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children })
2229
+ ]
2230
+ }
2231
+ );
2232
+ }
2233
+ var jumpyLocations = { current: null, previous: null };
2234
+ function jumpyNormalizeUrl(url) {
2235
+ const resolved = new URL(url, window.location.href);
2236
+ const path = resolved.pathname.length > 1 ? resolved.pathname.replace(/\/+$/, "") : resolved.pathname;
2237
+ return `${resolved.origin}${path}${resolved.search === "?" ? "" : resolved.search}`;
2238
+ }
2239
+ function jumpyPreviousEntryIs(href) {
2240
+ const target = jumpyNormalizeUrl(href);
2241
+ const navigation = window.navigation;
2242
+ if (navigation && typeof navigation.entries === "function" && navigation.currentEntry) {
2243
+ if (!navigation.canGoBack) return false;
2244
+ const previous = navigation.entries()[navigation.currentEntry.index - 1];
2245
+ return Boolean(previous?.url) && jumpyNormalizeUrl(previous.url) === target;
2246
+ }
2247
+ return Boolean(jumpyLocations.previous) && jumpyNormalizeUrl(jumpyLocations.previous) === target && window.history.length > 1;
2248
+ }
2249
+ function JumpyAppShellBack({ back, variant, ariaLabels }) {
2250
+ const theme = useJumpyTheme();
2251
+ const Component = back.as || (back.href ? "a" : "button");
2252
+ const textStyle = textStyleValue(theme, variant === "inline" ? JumpyTextStyle.bodySmallEmphasized : JumpyTextStyle.bodyMedium);
2253
+ const accessibleName = ariaLabels.backTo ? ariaLabels.backTo(back.label) : `Back to ${back.label}`;
2254
+ function handleClick(event) {
2255
+ back.onClick?.(event);
2256
+ if (event.defaultPrevented || !back.href || back.history === false) return;
2257
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
2258
+ if (!jumpyPreviousEntryIs(back.href)) return;
2259
+ event.preventDefault();
2260
+ window.history.back();
2261
+ }
2262
+ return /* @__PURE__ */ jsxs(
2263
+ Component,
2264
+ {
2265
+ href: back.href,
2266
+ type: Component === "button" ? "button" : void 0,
2267
+ "aria-label": accessibleName,
2268
+ "data-jumpy-slot": "app-shell-back",
2269
+ "data-variant": variant,
2270
+ onClick: handleClick,
2271
+ style: {
2272
+ boxSizing: "border-box",
2273
+ alignItems: "center",
2274
+ gap: spacingValue(theme, JumpySpacing.small4XS),
2275
+ maxWidth: "100%",
2276
+ minWidth: 0,
2277
+ minHeight: variant === "inline" ? void 0 : sizeValue(theme, JumpySize.largeXL),
2278
+ margin: variant === "inline" ? `0 0 ${spacingValue(theme, JumpySpacing.small3XS)}px ${-spacingValue(theme, JumpySpacing.small3XS)}px` : 0,
2279
+ padding: variant === "inline" ? 0 : `0 ${spacingValue(theme, JumpySpacing.smallXS)}px 0 0`,
2280
+ border: 0,
2281
+ borderRadius: radiusValue(theme, JumpyRadius.small),
2282
+ background: "transparent",
2283
+ color: "var(--jumpy-interactive-active-regular-default-text)",
2284
+ textDecoration: "none",
2285
+ cursor: "pointer",
2286
+ fontFamily: theme.fontFamily.familyBase,
2287
+ fontSize: textStyle.fontSize,
2288
+ lineHeight: textStyle.lineHeight,
2289
+ fontWeight: textStyle.fontWeight
2290
+ },
2291
+ children: [
2292
+ /* @__PURE__ */ jsx(JumpyNavigationIcon, { type: "back", size: variant === "inline" ? 18 : 24 }),
2293
+ /* @__PURE__ */ jsx("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: back.label })
2294
+ ]
2295
+ }
2296
+ );
2297
+ }
2298
+ function JumpyAppShellActions({ actions, variant, ariaLabels }) {
2299
+ const theme = useJumpyTheme();
2300
+ if (!actions.length) return null;
2301
+ const gap = spacingValue(theme, JumpySpacing.smallXS);
2302
+ if (variant === "full") {
2303
+ return /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "app-shell-actions", "data-variant": "full", style: { alignItems: "center", gap, flexWrap: "nowrap", justifyContent: "flex-end" }, children: actions.map((action, index) => /* @__PURE__ */ jsx(JumpButton, { as: action.href ? "a" : void 0, href: action.href, variant: action.variant || "secondary", leadingIcon: action.icon, disabled: action.disabled, onClick: action.onClick, children: action.label }, action.id ?? index)) });
2304
+ }
2305
+ const visible = actions.length > 2 ? actions.slice(0, 1) : actions;
2306
+ const overflow = actions.length > 2 ? actions.slice(1) : [];
2307
+ return /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "app-shell-actions", "data-variant": "compact", style: { alignItems: "center", gap }, children: [
2308
+ visible.map((action, index) => /* @__PURE__ */ jsx(JumpIconButton, { as: action.href ? "a" : void 0, href: action.href, variant: action.variant === "primary" ? "primary" : "borderless", "aria-label": action.label, disabled: action.disabled, onClick: action.onClick, children: action.icon }, action.id ?? index)),
2309
+ overflow.length ? /* @__PURE__ */ jsx(
2310
+ JumpMenu,
2311
+ {
2312
+ placement: "bottom-end",
2313
+ triggerProps: { role: "button", "aria-label": ariaLabels.moreActions || "More actions", "data-jumpy-slot": "app-shell-actions-more" },
2314
+ trigger: /* @__PURE__ */ jsx(JumpIconButton, { as: "span", variant: "borderless", tabIndex: -1, "aria-hidden": "true", children: /* @__PURE__ */ jsx(JumpyNavigationIcon, { type: "more" }) }),
2315
+ children: overflow.map((action, index) => /* @__PURE__ */ jsx(JumpMenuItem, { as: action.href ? "a" : void 0, href: action.href, leading: action.icon, disabled: action.disabled, onClick: action.onClick, children: action.label }, action.id ?? index))
2316
+ }
2317
+ ) : null
2318
+ ] });
2319
+ }
2320
+ function JumpAppShell({ brand, navigation, sidebarFooter, topBar, tabBar, hideTabBar = false, breakpoint = 1024, sidebarWidth = 256, navigationOpen, defaultNavigationOpen = false, onNavigationOpenChange, ariaLabels = {}, children, className = "", ...props }) {
2321
+ const theme = useJumpyTheme();
2322
+ const shellId = useId();
2323
+ const [internalOpen, setInternalOpen] = useState(defaultNavigationOpen);
2324
+ const isOpen = Boolean(navigation) && (navigationOpen ?? internalOpen);
2325
+ const swipeStart = useRef(null);
2326
+ const rootRef = useRef(null);
2327
+ const [drawerMounted, setDrawerMounted] = useState(isOpen);
2328
+ const showDrawer = isOpen || drawerMounted;
2329
+ const closing = drawerMounted && !isOpen;
2330
+ const wasShown = useRef(showDrawer);
2331
+ const { desktop: topBarDesktop = false, leading, largeTitle = false, actions = [], back, ...topBarProps } = topBar || {};
2332
+ const showLargeTitle = largeTitle && Boolean(topBarProps.title);
2333
+ const hasBack = Boolean(back?.label);
2334
+ const topBarOnDesktop = topBarDesktop || !showLargeTitle && (actions.length > 0 || hasBack);
2335
+ const largeTitleRef = useRef(null);
2336
+ const [largeTitleCovered, setLargeTitleCovered] = useState(false);
2337
+ const resolvedLeading = hasBack ? /* @__PURE__ */ jsx(JumpyAppShellBack, { back, variant: "bar", ariaLabels }) : leading ?? (navigation ? "menu" : void 0);
2338
+ useEffect(() => {
2339
+ const href = window.location.href;
2340
+ if (jumpyLocations.current === href) return;
2341
+ jumpyLocations.previous = jumpyLocations.current;
2342
+ jumpyLocations.current = href;
2343
+ });
2344
+ const shell = `[data-jumpy-app-shell="${shellId}"]`;
2345
+ const width = typeof sidebarWidth === "number" ? `${sidebarWidth}px` : sidebarWidth;
2346
+ function setOpen(next) {
2347
+ if (navigationOpen === void 0) setInternalOpen(next);
2348
+ onNavigationOpenChange?.(next);
2349
+ }
2350
+ useEffect(() => {
2351
+ if (!isOpen) return void 0;
2352
+ const desktop = window.matchMedia(`(min-width: ${breakpoint}px)`);
2353
+ const closeOnDesktop = () => {
2354
+ if (desktop.matches) setOpen(false);
2355
+ };
2356
+ closeOnDesktop();
2357
+ desktop.addEventListener("change", closeOnDesktop);
2358
+ return () => desktop.removeEventListener("change", closeOnDesktop);
2359
+ });
2360
+ useEffect(() => {
2361
+ if (isOpen) {
2362
+ setDrawerMounted(true);
2363
+ return void 0;
2364
+ }
2365
+ if (!drawerMounted) return void 0;
2366
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
2367
+ setDrawerMounted(false);
2368
+ return void 0;
2369
+ }
2370
+ const timer = window.setTimeout(() => setDrawerMounted(false), animationDurationValue(theme, JumpyAnimationDuration.small) * 1e3 + 100);
2371
+ return () => window.clearTimeout(timer);
2372
+ }, [isOpen, drawerMounted]);
2373
+ useEffect(() => {
2374
+ const closed = wasShown.current && !showDrawer;
2375
+ wasShown.current = showDrawer;
2376
+ if (!closed) return void 0;
2377
+ const frame = window.requestAnimationFrame(() => {
2378
+ if (document.activeElement && document.activeElement !== document.body) return;
2379
+ rootRef.current?.querySelector(':scope > [data-jumpy-slot="app-shell-body"] > [data-jumpy-component="top-bar"] > [data-leading="menu"] button')?.focus();
2380
+ });
2381
+ return () => window.cancelAnimationFrame(frame);
2382
+ }, [showDrawer]);
2383
+ useEffect(() => {
2384
+ const element = largeTitleRef.current;
2385
+ if (!showLargeTitle || !element || typeof IntersectionObserver === "undefined") return void 0;
2386
+ const bar = rootRef.current?.querySelector(':scope > [data-jumpy-slot="app-shell-body"] > [data-jumpy-component="top-bar"]');
2387
+ const barHeight = bar && bar.getBoundingClientRect().height > 0 ? bar.getBoundingClientRect().height : 0;
2388
+ const observer = new IntersectionObserver(([entry]) => setLargeTitleCovered(!entry.isIntersecting), { rootMargin: `-${Math.round(barHeight)}px 0px 0px 0px` });
2389
+ observer.observe(element);
2390
+ return () => observer.disconnect();
2391
+ }, [showLargeTitle, topBarProps.title]);
2392
+ function closeOnNavigate(event) {
2393
+ const item = event.target.closest?.('a[href], [data-jumpy-component="side-menu-item"]');
2394
+ if (!item || item.hasAttribute("aria-expanded") || item.getAttribute("aria-disabled") === "true" || item.disabled) return;
2395
+ setOpen(false);
2396
+ }
2397
+ const shellStyles = `
2398
+ ${shell} { grid-template-columns: minmax(0, 1fr); }
2399
+ ${shell} > [data-jumpy-slot='app-shell-sidebar'] { display: none; }
2400
+ ${shell} [data-jumpy-slot='app-shell-actions'][data-variant='compact'] { display: inline-flex; }
2401
+ ${shell} [data-jumpy-slot='app-shell-actions'][data-variant='full'] { display: none; }
2402
+ ${shell} [data-jumpy-slot='app-shell-back'][data-variant='inline'] { display: none; }
2403
+ @media (min-width: ${breakpoint}px) {
2404
+ ${shell} { grid-template-columns: ${width} minmax(0, 1fr); }
2405
+ ${shell} > [data-jumpy-slot='app-shell-sidebar'] { display: flex; }
2406
+ ${shell} [data-jumpy-slot='app-shell-actions'][data-variant='compact'] { display: none; }
2407
+ ${shell} [data-jumpy-slot='app-shell-actions'][data-variant='full'] { display: inline-flex; }
2408
+ ${shell} [data-jumpy-slot='app-shell-back'][data-variant='inline'] { display: flex; width: fit-content; }
2409
+ ${shell} > [data-jumpy-slot='app-shell-body'] > [data-jumpy-component='tab-bar'] { display: none; }
2410
+ ${shell} > [data-jumpy-slot='app-shell-body'] > [data-jumpy-component='top-bar'][data-mobile-only='true'] { display: none; }
2411
+ ${shell} > [data-jumpy-slot='app-shell-body'] > [data-jumpy-component='top-bar'] > [data-leading='menu'] { display: none; }
2412
+ ${shell} > [data-jumpy-slot='app-shell-body'] > [data-jumpy-component='top-bar'][data-large-title='true'] [data-jumpy-slot='app-shell-back'][data-variant='bar'] { display: none; }
2413
+ }`;
2414
+ const topBarNode = /* @__PURE__ */ jsx(
2415
+ JumpTopBar,
2416
+ {
2417
+ ...topBarProps,
2418
+ "data-mobile-only": topBarOnDesktop ? "false" : "true",
2419
+ leading: resolvedLeading,
2420
+ menuOpen: isOpen,
2421
+ onMenu: () => setOpen(true),
2422
+ title: topBarProps.title ?? brand,
2423
+ subtitle: showLargeTitle ? void 0 : topBarProps.subtitle,
2424
+ titleAlign: showLargeTitle || hasBack ? "center" : topBarProps.titleAlign,
2425
+ titleHidden: showLargeTitle ? !largeTitleCovered : topBarProps.titleHidden,
2426
+ "data-large-title": showLargeTitle ? "true" : void 0,
2427
+ trailing: actions.length || topBarProps.trailing ? /* @__PURE__ */ jsxs(Fragment, { children: [
2428
+ /* @__PURE__ */ jsx(JumpyAppShellActions, { actions, variant: "compact", ariaLabels }),
2429
+ showLargeTitle ? null : /* @__PURE__ */ jsx(JumpyAppShellActions, { actions, variant: "full", ariaLabels }),
2430
+ topBarProps.trailing
2431
+ ] }) : void 0,
2432
+ ariaLabels: { menu: ariaLabels.openNavigation, back: ariaLabels.back, ...topBarProps.ariaLabels || {} }
2433
+ }
2434
+ );
2435
+ return /* @__PURE__ */ jsxs(
2436
+ "div",
2437
+ {
2438
+ ...props,
2439
+ ref: rootRef,
2440
+ "data-jumpy-component": "app-shell",
2441
+ "data-jumpy-app-shell": shellId,
2442
+ "data-navigation-open": isOpen ? "true" : "false",
2443
+ className: cn(className),
2444
+ style: {
2445
+ display: "grid",
2446
+ minHeight: "100dvh",
2447
+ backgroundColor: "var(--jumpy-color-surface-canvas)",
2448
+ color: "var(--jumpy-color-text-primary)",
2449
+ fontFamily: theme.fontFamily.familyBase,
2450
+ ...props.style || {}
2451
+ },
2452
+ children: [
2453
+ /* @__PURE__ */ jsx("style", { children: shellStyles }),
2454
+ /* @__PURE__ */ jsxs(
2455
+ "aside",
2456
+ {
2457
+ "data-jumpy-slot": "app-shell-sidebar",
2458
+ "aria-label": ariaLabels.navigation || "Navigation",
2459
+ style: {
2460
+ boxSizing: "border-box",
2461
+ position: "sticky",
2462
+ top: 0,
2463
+ height: "100dvh",
2464
+ flexDirection: "column",
2465
+ overflow: "hidden",
2466
+ borderRight: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-color-border-subtle)`,
2467
+ backgroundColor: "var(--jumpy-color-surface-card)"
2468
+ },
2469
+ children: [
2470
+ brand ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-brand", style: { flex: "0 0 auto", padding: spacingValue(theme, JumpySpacing.large2XL), borderBottom: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-color-border-subtle)` }, children: brand }) : null,
2471
+ navigation ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-navigation", style: { flex: "1 1 auto", minHeight: 0, overflowY: "auto", overscrollBehavior: "contain", padding: spacingValue(theme, JumpySpacing.large) }, children: /* @__PURE__ */ jsx(JumpyAppShellNavigationContext.Provider, { value: true, children: navigation }) }) : null,
2472
+ sidebarFooter ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-sidebar-footer", style: { flex: "0 0 auto", padding: spacingValue(theme, JumpySpacing.large), borderTop: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-color-border-subtle)` }, children: sidebarFooter }) : null
2473
+ ]
2474
+ }
2475
+ ),
2476
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "app-shell-body", style: { display: "flex", flexDirection: "column", minWidth: 0, minHeight: "100dvh" }, children: [
2477
+ topBar !== null ? topBarNode : null,
2478
+ /* @__PURE__ */ jsxs("main", { "data-jumpy-slot": "app-shell-content", style: { flex: 1, minWidth: 0 }, children: [
2479
+ showLargeTitle ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-large-title", style: { padding: `${spacingValue(theme, JumpySpacing.smallXS)}px calc(var(--jumpy-app-shell-gutter, ${spacingValue(theme, JumpySpacing.large)}px) + env(safe-area-inset-right, 0px)) 0 calc(var(--jumpy-app-shell-gutter, ${spacingValue(theme, JumpySpacing.large)}px) + env(safe-area-inset-left, 0px))` }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: spacingValue(theme, JumpySpacing.large), flexWrap: "wrap" }, children: [
2480
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
2481
+ hasBack ? /* @__PURE__ */ jsx(JumpyAppShellBack, { back, variant: "inline", ariaLabels }) : null,
2482
+ /* @__PURE__ */ jsx("h1", { ref: largeTitleRef, style: { margin: 0, ...textStyleValue(theme, JumpyTextStyle.displaySmallEmphasized), overflowWrap: "anywhere" }, children: topBarProps.title }),
2483
+ topBarProps.subtitle ? /* @__PURE__ */ jsx("p", { style: { margin: `${spacingValue(theme, JumpySpacing.small4XS)}px 0 0`, color: "var(--jumpy-color-text-secondary)", ...textStyleValue(theme, JumpyTextStyle.bodyMedium) }, children: topBarProps.subtitle }) : null
2484
+ ] }),
2485
+ /* @__PURE__ */ jsx(JumpyAppShellActions, { actions, variant: "full", ariaLabels })
2486
+ ] }) }) : null,
2487
+ children
2488
+ ] }),
2489
+ tabBar && !hideTabBar ? tabBar : null
2490
+ ] }),
2491
+ showDrawer ? /* @__PURE__ */ jsx(JumpyModalLayer, { onVisibleChange: setOpen, "data-jumpy-component": "app-shell-drawer-overlay", "data-state": closing ? "closing" : "open", style: { position: "fixed", inset: 0, zIndex: 60, backgroundColor: "var(--jumpy-color-surface-overlay)" }, children: /* @__PURE__ */ jsxs(
2492
+ "div",
2493
+ {
2494
+ role: "dialog",
2495
+ "aria-modal": "true",
2496
+ "aria-label": ariaLabels.navigation || "Navigation",
2497
+ "data-jumpy-component": "app-shell-drawer",
2498
+ "data-state": closing ? "closing" : "open",
2499
+ onAnimationEnd: (event) => {
2500
+ if (closing && event.target === event.currentTarget) setDrawerMounted(false);
2501
+ },
2502
+ onClick: closeOnNavigate,
2503
+ onTouchStart: (event) => {
2504
+ const touch = event.touches[0];
2505
+ swipeStart.current = touch ? { x: touch.clientX, y: touch.clientY } : null;
2506
+ },
2507
+ onTouchEnd: (event) => {
2508
+ const start = swipeStart.current;
2509
+ const touch = event.changedTouches[0];
2510
+ swipeStart.current = null;
2511
+ if (!start || !touch) return;
2512
+ const dx = touch.clientX - start.x;
2513
+ const dy = touch.clientY - start.y;
2514
+ if (dx < -spacingValue(theme, JumpySpacing.large4XL) && Math.abs(dx) > Math.abs(dy) * 1.5) setOpen(false);
2515
+ },
2516
+ style: {
2517
+ boxSizing: "border-box",
2518
+ position: "fixed",
2519
+ inset: 0,
2520
+ display: "flex",
2521
+ flexDirection: "column",
2522
+ width: "100vw",
2523
+ height: "100dvh",
2524
+ overflow: "hidden",
2525
+ outline: "none",
2526
+ backgroundColor: "var(--jumpy-color-surface-card)",
2527
+ color: "var(--jumpy-color-text-primary)",
2528
+ fontFamily: theme.fontFamily.familyBase
2529
+ },
2530
+ children: [
2531
+ /* @__PURE__ */ jsx(
2532
+ JumpTopBar,
2533
+ {
2534
+ "data-jumpy-slot": "app-shell-drawer-header",
2535
+ sticky: false,
2536
+ style: { flex: "0 0 auto" },
2537
+ leading: /* @__PURE__ */ jsx(JumpIconButton, { variant: "borderless", "aria-label": ariaLabels.closeNavigation || "Close navigation", onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(JumpyNavigationIcon, { type: "close" }) }),
2538
+ title: brand
2539
+ }
2540
+ ),
2541
+ /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-drawer-navigation", style: { flex: "1 1 auto", minHeight: 0, overflowY: "auto", overscrollBehavior: "contain", padding: spacingValue(theme, JumpySpacing.large) }, children: /* @__PURE__ */ jsx(JumpyAppShellNavigationContext.Provider, { value: true, children: navigation }) }),
2542
+ sidebarFooter ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "app-shell-drawer-footer", style: { flex: "0 0 auto", padding: `${spacingValue(theme, JumpySpacing.large)}px calc(${spacingValue(theme, JumpySpacing.large)}px + env(safe-area-inset-right, 0px)) calc(${spacingValue(theme, JumpySpacing.large)}px + env(safe-area-inset-bottom, 0px)) calc(${spacingValue(theme, JumpySpacing.large)}px + env(safe-area-inset-left, 0px))`, borderTop: `${borderWidthValue(theme, JumpyBorderWidth.small)}px solid var(--jumpy-color-border-subtle)`, backgroundColor: "var(--jumpy-color-surface-card)" }, children: sidebarFooter }) : null
2543
+ ]
2544
+ }
2545
+ ) }) : null
2546
+ ]
2547
+ }
2548
+ );
2549
+ }
2550
+ function JumpSectionHeader({ as, variant = "regular", description, className = "", children, ...props }) {
2551
+ const theme = useJumpyTheme();
2552
+ const Component = as || "li";
2553
+ return /* @__PURE__ */ jsxs(Component, { ...props, "data-jumpy-component": "section-header", className: cn(className), style: { display: "grid", gap: variant === "prominent" ? spacingValue(theme, JumpySpacing.small3XS) : 0, padding: `${spacingValue(theme, JumpySpacing.smallXS)}px ${spacingValue(theme, JumpySpacing.large)}px`, color: theme.color.textSecondary, ...props.style || {} }, children: [
2554
+ /* @__PURE__ */ jsx(JumpTypography, { variant: variant === "prominent" ? "bodyMediumEmphasized" : "labelSmallEmphasized", color: "secondary", children }),
2555
+ variant === "prominent" && description ? /* @__PURE__ */ jsx(JumpTypography, { variant: "bodySmall", color: "secondary", children: description }) : null
2556
+ ] });
2557
+ }
2558
+ function JumpSectionFooter({ as, className = "", children, ...props }) {
2559
+ const theme = useJumpyTheme();
2560
+ const Component = as || "li";
2561
+ return /* @__PURE__ */ jsx(Component, { ...props, "data-jumpy-component": "section-footer", className: cn(className), style: { padding: `${spacingValue(theme, JumpySpacing.smallXS)}px ${spacingValue(theme, JumpySpacing.large)}px`, color: theme.color.textSecondary, ...props.style || {} }, children: /* @__PURE__ */ jsx(JumpTypography, { variant: "bodySmall", color: "secondary", children }) });
2562
+ }
2563
+ function JumpTag({ size: size2 = "small", status = "regular", disabled = false, className = "", children, ...props }) {
2564
+ const theme = useJumpyTheme();
2565
+ const labelStyle = textStyleValue(theme, size2 === "medium" ? JumpyTextStyle.labelMediumEmphasized : JumpyTextStyle.labelSmallEmphasized);
2566
+ return /* @__PURE__ */ jsx(
2567
+ "span",
2568
+ {
2569
+ ...props,
2570
+ "data-jumpy-component": "tag",
2571
+ "data-size": size2,
2572
+ "data-status": status,
2573
+ "data-disabled": disabled ? "true" : "false",
2574
+ className: cn(className),
2575
+ style: {
2576
+ display: "inline-flex",
2577
+ alignItems: "center",
2578
+ width: "fit-content",
2579
+ minHeight: size2 === "medium" ? sizeValue(theme, JumpySize.small) : sizeValue(theme, JumpySize.small2XS),
2580
+ padding: `${spacingValue(theme, JumpySpacing.small3XS)}px ${spacingValue(theme, JumpySpacing.smallXS)}px`,
2581
+ borderRadius: radiusValue(theme, JumpyRadius.full),
2582
+ borderWidth: borderWidthValue(theme, JumpyBorderWidth.small),
2583
+ borderStyle: "solid",
2584
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.medium) : opacityValue(theme, JumpyOpacity.solid),
2585
+ fontFamily: theme.fontFamily.familyBase,
2586
+ fontSize: labelStyle.fontSize,
2587
+ lineHeight: labelStyle.lineHeight,
2588
+ fontWeight: labelStyle.fontWeight,
2589
+ letterSpacing: labelStyle.letterSpacing,
2590
+ ...getTagStatusStyles(theme, status),
2591
+ ...props.style || {}
2592
+ },
2593
+ children
2594
+ }
2595
+ );
2596
+ }
2597
+ function JumpChip({ selected = false, disabled = false, className = "", children, ...props }) {
2598
+ const theme = useJumpyTheme();
2599
+ const bodyMediumEmphasized = textStyleValue(theme, JumpyTextStyle.bodyMediumEmphasized);
2600
+ return /* @__PURE__ */ jsx(
2601
+ "button",
2602
+ {
2603
+ ...props,
2604
+ type: props.type || "button",
2605
+ "data-jumpy-component": "chip",
2606
+ "data-selected": selected ? "true" : "false",
2607
+ disabled,
2608
+ className: cn(className),
2609
+ style: {
2610
+ display: "inline-flex",
2611
+ alignItems: "center",
2612
+ justifyContent: "center",
2613
+ minHeight: sizeValue(theme, JumpySize.large),
2614
+ padding: `${spacingValue(theme, JumpySpacing.small2XS)}px ${spacingValue(theme, JumpySpacing.large)}px`,
2615
+ borderRadius: radiusValue(theme, JumpyRadius.full),
2616
+ borderWidth: borderWidthValue(theme, JumpyBorderWidth.small),
2617
+ borderStyle: "solid",
2618
+ borderColor: selected ? theme.color.surfacePrimary : theme.color.borderStrong,
2619
+ backgroundColor: selected ? theme.color.infoSurface : theme.color.surfaceField,
2620
+ color: selected ? theme.color.surfacePrimary : theme.color.textPrimary,
2621
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.medium) : opacityValue(theme, JumpyOpacity.solid),
2622
+ fontFamily: theme.fontFamily.familyBase,
2623
+ fontSize: bodyMediumEmphasized.fontSize,
2624
+ lineHeight: bodyMediumEmphasized.lineHeight,
2625
+ fontWeight: bodyMediumEmphasized.fontWeight,
2626
+ letterSpacing: bodyMediumEmphasized.letterSpacing,
2627
+ cursor: disabled ? "not-allowed" : "pointer",
2628
+ ...props.style || {}
2629
+ },
2630
+ children
2631
+ }
2632
+ );
2633
+ }
2634
+ function JumpCheckbox({ label, children, checked, defaultChecked = false, indeterminate = false, onCheckedChange, onIndeterminateChange, labelPlacement = "end", innerClasses = {}, className = "", disabled = false, readOnly = false, style, ...props }) {
2635
+ const theme = useJumpyTheme();
2636
+ const labelLarge = textStyleValue(theme, JumpyTextStyle.labelLarge);
2637
+ const inputRef = useRef(null);
2638
+ const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);
2639
+ const isControlled = checked !== void 0;
2640
+ const isChecked = Boolean(isControlled ? checked : uncontrolledChecked);
2641
+ const isReadonly = readOnly && !disabled;
2642
+ const activeDefault = theme.selectors.active.default;
2643
+ const activeDisabled = theme.selectors.active.disabled;
2644
+ const activeReadOnly = theme.selectors.active.readOnly;
2645
+ const inactiveDefault = theme.selectors.inactive.default;
2646
+ const inactiveDisabled = theme.selectors.inactive.disabled;
2647
+ const inactiveReadOnly = theme.selectors.inactive.readOnly;
2648
+ const labelContent = children ?? label;
2649
+ useEffect(() => {
2650
+ if (inputRef.current) inputRef.current.indeterminate = indeterminate;
2651
+ }, [indeterminate]);
2652
+ const stateTokens = isChecked || indeterminate ? disabled ? activeDisabled : isReadonly ? activeReadOnly : activeDefault : disabled ? inactiveDisabled : isReadonly ? inactiveReadOnly : inactiveDefault;
2653
+ function handleChange(event) {
2654
+ if (isReadonly) {
2655
+ if (inputRef.current) inputRef.current.indeterminate = indeterminate;
2656
+ return;
2657
+ }
2658
+ if (indeterminate) onIndeterminateChange?.(false);
2659
+ if (!isControlled) setUncontrolledChecked(event.target.checked);
2660
+ onCheckedChange?.(event.target.checked);
2661
+ props.onChange?.(event);
2662
+ }
2663
+ return /* @__PURE__ */ jsxs(
2664
+ "label",
2665
+ {
2666
+ "data-jumpy-component": "checkbox",
2667
+ "data-checked": isChecked || indeterminate ? "true" : "false",
2668
+ "data-disabled": disabled ? "true" : void 0,
2669
+ "data-readonly": isReadonly ? "true" : void 0,
2670
+ "data-label-placement": labelPlacement,
2671
+ className: cn(className),
2672
+ style: {
2673
+ "--jumpy-cmp-checkbox-container-size": `${sizeValue(theme, JumpySize.large)}px`,
2674
+ "--jumpy-cmp-checkbox-container-radius": `${radiusValue(theme, JumpyRadius.medium)}px`,
2675
+ "--jumpy-cmp-checkbox-input-size": `${sizeValue(theme, JumpySize.smallXS)}px`,
2676
+ "--jumpy-cmp-checkbox-input-radius": `${radiusValue(theme, JumpyRadius.small2XS)}px`,
2677
+ "--jumpy-cmp-checkbox-label-gap-size": `${spacingValue(theme, JumpySpacing.smallXS)}px`,
2678
+ "--jumpy-cmp-checkbox-label-color": stateTokens.text,
2679
+ "--jumpy-cmp-checkbox-container-color": stateTokens.surface || activeDefault.surface,
2680
+ "--jumpy-cmp-checkbox-container-bg": "transparent",
2681
+ "--jumpy-cmp-checkbox-icon-fill": stateTokens.icon || activeDefault.icon || theme.color.textOnPrimary,
2682
+ "--jumpy-cmp-checkbox-input-border-color": stateTokens.border || inactiveDefault.border,
2683
+ "--jumpy-cmp-checkbox-hover-ring-color": "var(--jumpy-selectors-inactive-hover-hover-ring, var(--jumpy-color-feedback-info-surface))",
2684
+ "--jumpy-cmp-checkbox-hover-border-color": "var(--jumpy-selectors-inactive-hover-border, var(--jumpy-color-focus-border))",
2685
+ "--jumpy-cmp-checkbox-hover-surface-color": "var(--jumpy-selectors-active-hover-surface, var(--jumpy-color-surface-primary))",
2686
+ "--jumpy-cmp-checkbox-focus-border-color": "var(--jumpy-selectors-inactive-focus-border, var(--jumpy-color-focus-border))",
2687
+ "--jumpy-cmp-checkbox-focus-ring-color": "var(--jumpy-selectors-inactive-focus-focus-ring, var(--jumpy-color-focus-ring))",
2688
+ "--jumpy-cmp-checkbox-active-focus-ring-color": "var(--jumpy-selectors-active-focus-focus-ring, var(--jumpy-color-focus-ring))",
2689
+ fontFamily: theme.fontFamily.familyBase,
2690
+ fontSize: labelLarge.fontSize,
2691
+ lineHeight: labelLarge.lineHeight,
2692
+ fontWeight: labelLarge.fontWeight,
2693
+ letterSpacing: labelLarge.letterSpacing,
2694
+ ...style || {}
2695
+ },
2696
+ children: [
2697
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "checkbox-container", className: innerClasses.container?.className, style: innerClasses.container?.style, children: [
2698
+ /* @__PURE__ */ jsx(
2699
+ "input",
2700
+ {
2701
+ ...props,
2702
+ ref: (node) => {
2703
+ inputRef.current = node;
2704
+ if (typeof props.ref === "function") props.ref(node);
2705
+ else if (props.ref) props.ref.current = node;
2706
+ },
2707
+ type: "checkbox",
2708
+ "data-jumpy-slot": "checkbox-input",
2709
+ className: innerClasses.input?.className,
2710
+ checked: isChecked,
2711
+ disabled,
2712
+ readOnly: isReadonly,
2713
+ "aria-readonly": isReadonly || void 0,
2714
+ onChange: handleChange
2715
+ }
2716
+ ),
2717
+ isChecked || indeterminate ? /* @__PURE__ */ jsx("svg", { "data-jumpy-slot": "checkbox-icon", className: innerClasses.icons?.className, style: innerClasses.icons?.style, viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: indeterminate ? /* @__PURE__ */ jsx("path", { d: "M6 11h12v2H6z" }) : /* @__PURE__ */ jsx("path", { d: "M9.4 16.6 4.8 12l1.4-1.4 3.2 3.2 8.4-8.4L19.2 6.8z" }) }) : null
2718
+ ] }),
2719
+ labelContent ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "checkbox-label", children: labelContent }) : null
2720
+ ]
2721
+ }
2722
+ );
2723
+ }
2724
+ function JumpSwitch({ label, children, checked, defaultChecked = false, onCheckedChange, labelPlacement = "end", innerClasses = {}, className = "", disabled = false, readOnly = false, style, ...props }) {
2725
+ const theme = useJumpyTheme();
2726
+ const bodyMedium = textStyleValue(theme, JumpyTextStyle.bodyMedium);
2727
+ const [uncontrolledChecked, setUncontrolledChecked] = useState(Boolean(defaultChecked));
2728
+ const isControlled = checked !== void 0;
2729
+ const isChecked = Boolean(isControlled ? checked : uncontrolledChecked);
2730
+ const isReadonly = readOnly && !disabled;
2731
+ const selectorState = disabled ? "disabled" : isReadonly ? "readOnly" : "default";
2732
+ const selectorTokens = isChecked ? theme.selectors.active[selectorState] : theme.selectors.inactive[selectorState];
2733
+ const labelContent = children ?? label;
2734
+ return /* @__PURE__ */ jsxs(
2735
+ "label",
2736
+ {
2737
+ "data-jumpy-component": "switch",
2738
+ "data-checked": isChecked ? "true" : "false",
2739
+ "data-disabled": disabled ? "true" : void 0,
2740
+ "data-readonly": isReadonly ? "true" : void 0,
2741
+ "data-label-placement": labelPlacement,
2742
+ className: cn(className),
2743
+ style: {
2744
+ "--jumpy-cmp-switch-thumb-size": `${sizeValue(theme, JumpySize.small2XS)}px`,
2745
+ "--jumpy-cmp-switch-thumb-size-checked": `${sizeValue(theme, JumpySize.small)}px`,
2746
+ "--jumpy-cmp-switch-track-width": `${sizeValue(theme, JumpySize.large2XL)}px`,
2747
+ "--jumpy-cmp-switch-track-bg": selectorTokens.surface,
2748
+ "--jumpy-cmp-switch-track-border": selectorTokens.border,
2749
+ "--jumpy-cmp-switch-thumb-bg": selectorTokens.handle || selectorTokens.icon || theme.color.textOnPrimary,
2750
+ "--jumpy-cmp-switch-icon-fill": selectorTokens.surface,
2751
+ "--jumpy-cmp-switch-label-color": selectorTokens.text,
2752
+ "--jumpy-cmp-switch-hover-track-bg": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-hover-surface)`,
2753
+ "--jumpy-cmp-switch-hover-track-border": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-hover-border)`,
2754
+ "--jumpy-cmp-switch-hover-thumb-bg": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-hover-handle)`,
2755
+ "--jumpy-cmp-switch-focus-ring": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-focus-focus-ring)`,
2756
+ "--jumpy-cmp-switch-focus-track-bg": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-focus-surface)`,
2757
+ "--jumpy-cmp-switch-focus-track-border": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-focus-border)`,
2758
+ "--jumpy-cmp-switch-focus-thumb-bg": `var(--jumpy-selectors-${isChecked ? "active" : "inactive"}-focus-handle)`,
2759
+ fontFamily: theme.fontFamily.familyBase,
2760
+ fontSize: bodyMedium.fontSize,
2761
+ lineHeight: bodyMedium.lineHeight,
2762
+ fontWeight: bodyMedium.fontWeight,
2763
+ ...style || {}
2764
+ },
2765
+ children: [
2766
+ /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "switch-container", className: innerClasses.container?.className, style: innerClasses.container?.style, children: [
2767
+ /* @__PURE__ */ jsx(
2768
+ "input",
2769
+ {
2770
+ ...props,
2771
+ type: "checkbox",
2772
+ role: "switch",
2773
+ checked: isChecked,
2774
+ disabled,
2775
+ readOnly: isReadonly,
2776
+ "aria-checked": isChecked,
2777
+ "aria-readonly": isReadonly || void 0,
2778
+ "data-jumpy-slot": "switch-input",
2779
+ className: innerClasses.input?.className,
2780
+ onChange: (event) => {
2781
+ if (isReadonly) return;
2782
+ if (!isControlled) setUncontrolledChecked(event.target.checked);
2783
+ onCheckedChange?.(event.target.checked);
2784
+ props.onChange?.(event);
2785
+ }
2786
+ }
2787
+ ),
2788
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "switch-track", className: innerClasses.track?.className, style: innerClasses.track?.style, children: /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "switch-thumb", className: innerClasses.thumb?.className, style: innerClasses.thumb?.style, children: /* @__PURE__ */ jsx("svg", { "data-jumpy-slot": "switch-icon", className: innerClasses.icon?.className, style: innerClasses.icon?.style, viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M6 10.25 8.6 12.85 14 7.45", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })
2789
+ ] }),
2790
+ labelContent ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "switch-label", children: labelContent }) : null
2791
+ ]
2792
+ }
2793
+ );
2794
+ }
2795
+ function JumpRadioGroup({ name, value, defaultValue, onValueChange, options, className = "", disabled = false, readOnly = false, direction = "vertical", innerClasses = {}, ...props }) {
2796
+ const theme = useJumpyTheme();
2797
+ const labelLarge = textStyleValue(theme, JumpyTextStyle.labelLarge);
2798
+ const isReadonly = readOnly && !disabled;
2799
+ const generatedName = useId();
2800
+ const validName = name || generatedName;
2801
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? "");
2802
+ const isControlled = value !== void 0;
2803
+ const selectedValue = isControlled ? value : uncontrolledValue;
2804
+ return /* @__PURE__ */ jsx("div", { ...props, role: "radiogroup", "aria-readonly": isReadonly || void 0, "aria-orientation": direction === "horizontal" ? "horizontal" : "vertical", "data-jumpy-component": "radio-group", "data-direction": direction, className: cn(className), style: { display: "flex", flexDirection: direction === "horizontal" ? "row" : "column", gap: spacingValue(theme, JumpySpacing.medium), ...props.style || {} }, onChange: (event) => {
2805
+ if (isReadonly || event.defaultPrevented) return;
2806
+ const target = event.target;
2807
+ if (target?.type === "radio" && target.name === validName) {
2808
+ if (!isControlled) setUncontrolledValue(target.value);
2809
+ onValueChange?.(target.value);
2810
+ }
2811
+ props.onChange?.(event);
2812
+ }, children: options.map((option) => {
2813
+ const optionDisabled = disabled || option.disabled;
2814
+ const optionSelected = selectedValue === option.value;
2815
+ const selectorState = optionDisabled ? "disabled" : isReadonly ? "readOnly" : "default";
2816
+ const selectorTokens = optionSelected ? theme.selectors.active[selectorState] : theme.selectors.inactive[selectorState];
2817
+ return /* @__PURE__ */ jsxs(
2818
+ "label",
2819
+ {
2820
+ "data-jumpy-component": "radio",
2821
+ "data-checked": optionSelected ? "true" : "false",
2822
+ "data-disabled": optionDisabled ? "true" : void 0,
2823
+ "data-readonly": isReadonly ? "true" : void 0,
2824
+ style: {
2825
+ "--jumpy-cmp-radio-label-color": selectorTokens.text,
2826
+ "--jumpy-cmp-radio-input-border-color": selectorTokens.border,
2827
+ "--jumpy-cmp-radio-dot-color": optionSelected ? selectorTokens.surface : "transparent",
2828
+ "--jumpy-cmp-radio-container-bg": "transparent",
2829
+ "--jumpy-cmp-radio-hover-ring-color": `var(--jumpy-selectors-${optionSelected ? "active" : "inactive"}-hover-hover-ring)`,
2830
+ "--jumpy-cmp-radio-hover-border-color": `var(--jumpy-selectors-${optionSelected ? "active" : "inactive"}-hover-border)`,
2831
+ "--jumpy-cmp-radio-hover-dot-color": `var(--jumpy-selectors-${optionSelected ? "active" : "inactive"}-hover-surface)`,
2832
+ "--jumpy-cmp-radio-focus-ring-color": `var(--jumpy-selectors-${optionSelected ? "active" : "inactive"}-focus-focus-ring)`,
2833
+ "--jumpy-cmp-radio-focus-border-color": `var(--jumpy-selectors-${optionSelected ? "active" : "inactive"}-focus-border)`,
2834
+ fontFamily: theme.fontFamily.familyBase,
2835
+ fontSize: labelLarge.fontSize,
2836
+ lineHeight: labelLarge.lineHeight,
2837
+ fontWeight: labelLarge.fontWeight,
2838
+ letterSpacing: labelLarge.letterSpacing
2839
+ },
2840
+ children: [
2841
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "radio-container", className: innerClasses.container?.className, style: innerClasses.container?.style, children: /* @__PURE__ */ jsx("input", { type: "radio", name: validName, value: option.value, checked: optionSelected, disabled: optionDisabled, "aria-readonly": isReadonly || void 0, readOnly: isReadonly || void 0, "data-jumpy-slot": "radio-input", className: innerClasses.input?.className, style: innerClasses.input?.style, onChange: () => {
2842
+ } }) }),
2843
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "radio-label", className: innerClasses.label?.className, style: innerClasses.label?.style, children: option.label })
2844
+ ]
2845
+ },
2846
+ option.value
2847
+ );
2848
+ }) });
2849
+ }
2850
+ function toDateValue(value) {
2851
+ if (value === null || value === void 0 || value === "") return null;
2852
+ const date = value instanceof Date ? value : new Date(value);
2853
+ return Number.isNaN(date.getTime()) ? null : date;
2854
+ }
2855
+ function sameDay(a, b) {
2856
+ if (!a || !b) return false;
2857
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2858
+ }
2859
+ function formatDateValue(value, format = "MM/dd/yyyy") {
2860
+ const date = toDateValue(value);
2861
+ if (!date) return "";
2862
+ const parts = {
2863
+ dd: String(date.getDate()).padStart(2, "0"),
2864
+ MM: String(date.getMonth() + 1).padStart(2, "0"),
2865
+ yyyy: String(date.getFullYear())
2866
+ };
2867
+ return format.replace(/yyyy|MM|dd/g, (token) => parts[token]);
2868
+ }
2869
+ function formatTimeValue(value, format = "HH:mm") {
2870
+ const date = toDateValue(value);
2871
+ if (!date) return "";
2872
+ const hours24 = date.getHours();
2873
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2874
+ const period = hours24 >= 12 ? "PM" : "AM";
2875
+ const hours12 = hours24 % 12 || 12;
2876
+ return format.includes("aa") ? format.replace("hh", String(hours12).padStart(2, "0")).replace("mm", minutes).replace("aa", period) : format.replace("HH", String(hours24).padStart(2, "0")).replace("mm", minutes);
2877
+ }
2878
+ function parseDateValue(text, format = "MM/dd/yyyy") {
2879
+ const value = text.trim();
2880
+ if (!value) return null;
2881
+ const activeFormat = Array.isArray(format) ? format[0] : format;
2882
+ const escaped = activeFormat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2883
+ const pattern = escaped.replace("dd", "(?<dd>\\d{1,2})").replace("MM", "(?<MM>\\d{1,2})").replace("yyyy", "(?<yyyy>\\d{4})");
2884
+ const match = value.match(new RegExp(`^${pattern}$`));
2885
+ if (!match?.groups) return null;
2886
+ const day = Number(match.groups.dd);
2887
+ const month = Number(match.groups.MM) - 1;
2888
+ const year = Number(match.groups.yyyy);
2889
+ const date = new Date(year, month, day);
2890
+ return date.getFullYear() === year && date.getMonth() === month && date.getDate() === day ? date : null;
2891
+ }
2892
+ function parseTimeValue(text, format = "HH:mm") {
2893
+ const value = text.trim();
2894
+ if (!value) return null;
2895
+ const usesPeriod = format.includes("aa");
2896
+ const match = usesPeriod ? value.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i) : value.match(/^(\d{1,2}):(\d{2})$/);
2897
+ if (!match) return null;
2898
+ let hour = Number(match[1]);
2899
+ const minute = Number(match[2]);
2900
+ if (usesPeriod) {
2901
+ if (hour < 1 || hour > 12) return null;
2902
+ const period = match[3].toUpperCase();
2903
+ hour = period === "PM" ? hour % 12 + 12 : hour % 12;
2904
+ }
2905
+ if (hour > 23 || minute > 59) return null;
2906
+ const date = /* @__PURE__ */ new Date();
2907
+ date.setHours(hour, minute, 0, 0);
2908
+ return date;
2909
+ }
2910
+ function coerceDateOutput(date, sample) {
2911
+ return typeof sample === "number" ? date?.getTime() ?? null : date;
2912
+ }
2913
+ function PickerTrailingIcon({ type, open }) {
2914
+ if (type === "time") {
2915
+ return /* @__PURE__ */ jsxs("svg", { "data-jumpy-stroke-icon": "true", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", style: { display: "block", fill: "none" }, children: [
2916
+ /* @__PURE__ */ jsx("path", { d: "M8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12Z", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }),
2917
+ /* @__PURE__ */ jsx("path", { d: "M8 4.75V8l2.25 1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
2918
+ ] });
2919
+ }
2920
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", style: { display: "block" }, children: [
2921
+ /* @__PURE__ */ jsx("path", { d: "M4.25 2.5v2M11.75 2.5v2M3.25 6.25h9.5M3.75 3.75h8.5c.69 0 1.25.56 1.25 1.25v7.25c0 .69-.56 1.25-1.25 1.25h-8.5c-.69 0-1.25-.56-1.25-1.25V5c0-.69.56-1.25 1.25-1.25Z", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
2922
+ open ? /* @__PURE__ */ jsx("path", { d: "M6 10.5 7.25 11.75 10.25 8.75", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) : null
2923
+ ] });
2924
+ }
2925
+ function getMonthDays(viewDate) {
2926
+ const year = viewDate.getFullYear();
2927
+ const month = viewDate.getMonth();
2928
+ const firstDay = new Date(year, month, 1);
2929
+ const leading = (firstDay.getDay() + 6) % 7;
2930
+ const start = new Date(year, month, 1 - leading);
2931
+ return Array.from({ length: 42 }, (_, index) => new Date(start.getFullYear(), start.getMonth(), start.getDate() + index));
2932
+ }
2933
+ function dateIsUnavailable(date, { minDate, maxDate, disabledDates = [], isDateDisabled }) {
2934
+ const min = toDateValue(minDate);
2935
+ const max = toDateValue(maxDate);
2936
+ if (min && date < new Date(min.getFullYear(), min.getMonth(), min.getDate())) return true;
2937
+ if (max && date > new Date(max.getFullYear(), max.getMonth(), max.getDate())) return true;
2938
+ if (disabledDates.some((disabledDate) => sameDay(date, toDateValue(disabledDate)))) return true;
2939
+ return Boolean(isDateDisabled?.(date));
2940
+ }
2941
+ function JumpDatePicker({ label = "Date", hint, value, defaultValue = null, onChange, open, onOpenChange, format = "MM/dd/yyyy", minDate, maxDate, disabledDates = [], isDateDisabled, labels = {}, disabled = false, readOnly = false, className = "", status = "regular", error = false, hideClearIcon = false, onClear, ...props }) {
2942
+ const theme = useJumpyTheme();
2943
+ const [internalValue, setInternalValue] = useState(defaultValue);
2944
+ const [internalOpen, setInternalOpen] = useState(false);
2945
+ const isControlled = value !== void 0;
2946
+ const selectedDate = toDateValue(isControlled ? value : internalValue);
2947
+ const selectedTime = selectedDate?.getTime() ?? null;
2948
+ const [viewDate, setViewDate] = useState(selectedDate || /* @__PURE__ */ new Date());
2949
+ const isOpen = open ?? internalOpen;
2950
+ const resolvedFormat = Array.isArray(format) ? format[0] : format;
2951
+ const inputValue = formatDateValue(selectedDate, resolvedFormat);
2952
+ const [draft, setDraft] = useState(inputValue);
2953
+ const typedValue = useRef(void 0);
2954
+ useEffect(() => {
2955
+ if (typedValue.current !== selectedTime) setDraft(inputValue);
2956
+ typedValue.current = void 0;
2957
+ if (selectedTime !== null) setViewDate(new Date(selectedTime));
2958
+ }, [selectedTime, inputValue, resolvedFormat]);
2959
+ const weekdays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
2960
+ const dayCells = getMonthDays(viewDate);
2961
+ const { refs, floatingStyles } = useFloating({
2962
+ open: isOpen,
2963
+ onOpenChange: setOpen,
2964
+ placement: "bottom-start",
2965
+ strategy: "fixed",
2966
+ whileElementsMounted: autoUpdate,
2967
+ middleware: [offset(spacingValue(theme, JumpySpacing.smallXS)), flip({ padding: spacingValue(theme, JumpySpacing.medium) })]
2968
+ });
2969
+ function setOpen(next) {
2970
+ setInternalOpen(next);
2971
+ onOpenChange?.(next);
2972
+ }
2973
+ useEffect(() => {
2974
+ if (!isOpen) return void 0;
2975
+ function dismissOutside(event) {
2976
+ if (!refs.reference.current?.contains(event.target) && !refs.floating.current?.contains(event.target)) setOpen(false);
2977
+ }
2978
+ document.addEventListener("pointerdown", dismissOutside);
2979
+ document.addEventListener("focusin", dismissOutside);
2980
+ return () => {
2981
+ document.removeEventListener("pointerdown", dismissOutside);
2982
+ document.removeEventListener("focusin", dismissOutside);
2983
+ };
2984
+ });
2985
+ function commit(date) {
2986
+ if (date && dateIsUnavailable(date, { minDate, maxDate, disabledDates, isDateDisabled })) return;
2987
+ const next = coerceDateOutput(date, value ?? defaultValue);
2988
+ typedValue.current = void 0;
2989
+ if (!isControlled) setInternalValue(next);
2990
+ setDraft(isControlled ? inputValue : formatDateValue(date, resolvedFormat));
2991
+ onChange?.(next);
2992
+ setOpen(false);
2993
+ }
2994
+ const trailing = readOnly ? void 0 : /* @__PURE__ */ jsx(PickerTrailingIcon, { type: "date", open: isOpen });
2995
+ return /* @__PURE__ */ jsxs("div", { ref: refs.setReference, "data-jumpy-component": "date-picker", style: { position: "relative", width: "100%", maxWidth: "100%" }, children: [
2996
+ /* @__PURE__ */ jsx(JumpField, { label, hint, status, error, hideClearIcon, onClear, className, trailing, children: /* @__PURE__ */ jsx(
2997
+ JumpInput,
2998
+ {
2999
+ ...props,
3000
+ role: "combobox",
3001
+ "aria-expanded": isOpen,
3002
+ "aria-haspopup": "dialog",
3003
+ readOnly,
3004
+ disabled,
3005
+ "data-has-value": selectedTime !== null,
3006
+ value: draft,
3007
+ placeholder: props.placeholder || (Array.isArray(format) ? format[0] : format),
3008
+ autoComplete: "off",
3009
+ inputMode: "numeric",
3010
+ onChange: (event) => {
3011
+ const text = event.target.value;
3012
+ setDraft(text);
3013
+ const parsedDate = parseDateValue(text, format);
3014
+ typedValue.current = void 0;
3015
+ if (!text.trim() || parsedDate && !dateIsUnavailable(parsedDate, { minDate, maxDate, disabledDates, isDateDisabled })) {
3016
+ const next = coerceDateOutput(parsedDate, value ?? defaultValue);
3017
+ const nextTime = parsedDate?.getTime() ?? null;
3018
+ if (nextTime !== selectedTime) typedValue.current = nextTime;
3019
+ if (!isControlled) setInternalValue(next);
3020
+ onChange?.(next);
3021
+ }
3022
+ },
3023
+ onBlur: (event) => {
3024
+ typedValue.current = void 0;
3025
+ setDraft(inputValue);
3026
+ props.onBlur?.(event);
3027
+ },
3028
+ onClick: (event) => {
3029
+ if (!readOnly) setOpen(!isOpen);
3030
+ props.onClick?.(event);
3031
+ },
3032
+ onKeyDown: (event) => {
3033
+ if ((event.key === "ArrowDown" || event.key === "Enter") && !readOnly) {
3034
+ event.preventDefault();
3035
+ setOpen(true);
3036
+ }
3037
+ if (event.key === "Escape") setOpen(false);
3038
+ props.onKeyDown?.(event);
3039
+ }
3040
+ }
3041
+ ) }),
3042
+ isOpen && !disabled && !readOnly ? /* @__PURE__ */ jsx(JumpyPortal, { children: /* @__PURE__ */ jsxs("div", { ref: refs.setFloating, role: "dialog", "aria-label": label, style: { ...floatingStyles, zIndex: JUMPY_POPUP_Z_INDEX, width: sizeValue(theme, JumpySize.large8XL) * 2.5, maxWidth: "calc(100vw - 32px)", overflow: "visible", borderRadius: radiusValue(theme, JumpyRadius.smallXS), backgroundColor: "var(--jumpy-card-inactive-default-surface, var(--jumpy-color-surface-card))", color: "var(--jumpy-card-inactive-default-text, var(--jumpy-color-text-primary))", boxShadow: theme.shadow.positiveMedium, padding: `${spacingValue(theme, JumpySpacing.small3XS)}px 0` }, children: [
3043
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spacingValue(theme, JumpySpacing.medium), padding: spacingValue(theme, JumpySpacing.small3XS) }, children: [
3044
+ /* @__PURE__ */ jsx(JumpIconButton, { variant: "borderless", "aria-label": "Previous", onClick: () => setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1)), children: "\u2039" }),
3045
+ /* @__PURE__ */ jsx("strong", { style: { fontFamily: theme.fontFamily.familyBase, color: "inherit" }, children: viewDate.toLocaleDateString(void 0, { month: "long", year: "numeric" }) }),
3046
+ /* @__PURE__ */ jsx(JumpIconButton, { variant: "borderless", "aria-label": "Next", onClick: () => setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1)), children: "\u203A" })
3047
+ ] }),
3048
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: spacingValue(theme, JumpySpacing.small3XS), padding: `0 ${spacingValue(theme, JumpySpacing.small3XS)}px` }, children: [
3049
+ weekdays.map((weekday) => /* @__PURE__ */ jsx("span", { style: { color: theme.color.textSecondary, textAlign: "center", fontSize: textStyleValue(theme, JumpyTextStyle.labelSmall).fontSize, fontWeight: theme.fontFamily.labelSmall.fontWeight }, children: weekday }, weekday)),
3050
+ dayCells.map((date) => {
3051
+ const outside = date.getMonth() !== viewDate.getMonth();
3052
+ const selected = sameDay(date, selectedDate);
3053
+ const unavailable = dateIsUnavailable(date, { minDate, maxDate, disabledDates, isDateDisabled });
3054
+ return /* @__PURE__ */ jsx("button", { type: "button", disabled: unavailable, onClick: () => commit(date), style: { minHeight: sizeValue(theme, JumpySize.large), border: 0, borderRadius: radiusValue(theme, JumpyRadius.small), backgroundColor: selected ? "var(--jumpy-interactive-active-regular-default-surface, var(--jumpy-color-surface-primary))" : "transparent", color: selected ? "var(--jumpy-interactive-active-regular-default-text, var(--jumpy-color-text-on-primary))" : outside ? "var(--jumpy-card-inactive-default-subtext, var(--jumpy-color-text-secondary))" : "var(--jumpy-card-inactive-default-text, var(--jumpy-color-text-primary))", opacity: unavailable ? opacityValue(theme, JumpyOpacity.medium) : outside ? opacityValue(theme, JumpyOpacity.strong) : opacityValue(theme, JumpyOpacity.solid), cursor: unavailable ? "not-allowed" : "pointer", fontFamily: theme.fontFamily.familyBase }, children: date.getDate() }, date.toISOString());
3055
+ })
3056
+ ] }),
3057
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: spacingValue(theme, JumpySpacing.smallXS), padding: spacingValue(theme, JumpySpacing.large) }, children: [
3058
+ /* @__PURE__ */ jsx(JumpButton, { variant: "tertiary", onClick: () => setOpen(false), children: labels.cancelButton || "Cancel" }),
3059
+ /* @__PURE__ */ jsx(JumpButton, { variant: "borderless", onClick: () => commit(/* @__PURE__ */ new Date()), children: "Today" })
3060
+ ] })
3061
+ ] }) }) : null
3062
+ ] });
3063
+ }
3064
+ function JumpTimePicker({ label = "Time", hint, value, defaultValue = null, onChange, open, onOpenChange, format, twentyFourHours = true, labels = {}, disabled = false, readOnly = false, className = "", status = "regular", error = false, hideClearIcon = false, onClear, ...props }) {
3065
+ const theme = useJumpyTheme();
3066
+ const resolvedFormat = format || (twentyFourHours ? "HH:mm" : "hh:mm aa");
3067
+ const [internalValue, setInternalValue] = useState(defaultValue);
3068
+ const [internalOpen, setInternalOpen] = useState(false);
3069
+ const isControlled = value !== void 0;
3070
+ const selectedDate = toDateValue(isControlled ? value : internalValue);
3071
+ const selectedTime = selectedDate?.getTime() ?? null;
3072
+ const inputValue = formatTimeValue(selectedDate, resolvedFormat);
3073
+ const [draft, setDraft] = useState(inputValue);
3074
+ const typedValue = useRef(void 0);
3075
+ useEffect(() => {
3076
+ if (typedValue.current !== selectedTime) setDraft(inputValue);
3077
+ typedValue.current = void 0;
3078
+ }, [selectedTime, inputValue, resolvedFormat]);
3079
+ const isOpen = open ?? internalOpen;
3080
+ const hours = Array.from({ length: twentyFourHours ? 24 : 12 }, (_, index) => String(twentyFourHours ? index : index + 1).padStart(2, "0"));
3081
+ const minutes = Array.from({ length: 60 }, (_, index) => String(index).padStart(2, "0"));
3082
+ const currentHour = selectedDate ? selectedDate.getHours() : 0;
3083
+ const currentMinute = selectedDate ? selectedDate.getMinutes() : 0;
3084
+ const displayHour = twentyFourHours ? currentHour : currentHour % 12 || 12;
3085
+ const currentPeriod = currentHour >= 12 ? "PM" : "AM";
3086
+ const { refs, floatingStyles } = useFloating({
3087
+ open: isOpen,
3088
+ onOpenChange: setOpen,
3089
+ placement: "bottom-start",
3090
+ strategy: "fixed",
3091
+ whileElementsMounted: autoUpdate,
3092
+ middleware: [offset(spacingValue(theme, JumpySpacing.smallXS)), flip({ padding: spacingValue(theme, JumpySpacing.medium) })]
3093
+ });
3094
+ function setOpen(next) {
3095
+ setInternalOpen(next);
3096
+ onOpenChange?.(next);
3097
+ }
3098
+ useEffect(() => {
3099
+ if (!isOpen) return void 0;
3100
+ function dismissOutside(event) {
3101
+ if (!refs.reference.current?.contains(event.target) && !refs.floating.current?.contains(event.target)) setOpen(false);
3102
+ }
3103
+ document.addEventListener("pointerdown", dismissOutside);
3104
+ document.addEventListener("focusin", dismissOutside);
3105
+ return () => {
3106
+ document.removeEventListener("pointerdown", dismissOutside);
3107
+ document.removeEventListener("focusin", dismissOutside);
3108
+ };
3109
+ });
3110
+ function commit(part, rawValue) {
3111
+ const next = selectedDate ? new Date(selectedDate) : /* @__PURE__ */ new Date();
3112
+ if (part === "hour") {
3113
+ const hour = Number(rawValue);
3114
+ next.setHours(twentyFourHours ? hour : currentPeriod === "PM" ? hour % 12 + 12 : hour % 12);
3115
+ }
3116
+ if (part === "minute") next.setMinutes(Number(rawValue));
3117
+ if (part === "period") {
3118
+ const base = next.getHours() % 12;
3119
+ next.setHours(rawValue === "PM" ? base + 12 : base);
3120
+ }
3121
+ next.setSeconds(0, 0);
3122
+ const output = coerceDateOutput(next, value ?? defaultValue);
3123
+ typedValue.current = void 0;
3124
+ if (!isControlled) setInternalValue(output);
3125
+ setDraft(isControlled ? inputValue : formatTimeValue(next, resolvedFormat));
3126
+ onChange?.(output);
3127
+ }
3128
+ const trailing = readOnly ? void 0 : /* @__PURE__ */ jsx(PickerTrailingIcon, { type: "time", open: isOpen });
3129
+ return /* @__PURE__ */ jsxs("div", { ref: refs.setReference, "data-jumpy-component": "time-picker", style: { position: "relative", width: "100%", maxWidth: "100%" }, children: [
3130
+ /* @__PURE__ */ jsx(JumpField, { label, hint, status, error, hideClearIcon, onClear, className, trailing, children: /* @__PURE__ */ jsx(
3131
+ JumpInput,
3132
+ {
3133
+ ...props,
3134
+ role: "combobox",
3135
+ "aria-expanded": isOpen,
3136
+ "aria-haspopup": "dialog",
3137
+ readOnly,
3138
+ disabled,
3139
+ "data-has-value": selectedTime !== null,
3140
+ value: draft,
3141
+ placeholder: props.placeholder || resolvedFormat,
3142
+ autoComplete: "off",
3143
+ inputMode: resolvedFormat.includes("aa") ? "text" : "numeric",
3144
+ onChange: (event) => {
3145
+ const text = event.target.value;
3146
+ setDraft(text);
3147
+ const parsedDate = parseTimeValue(text, resolvedFormat);
3148
+ typedValue.current = void 0;
3149
+ if (!text.trim() || parsedDate) {
3150
+ const output = coerceDateOutput(parsedDate, value ?? defaultValue);
3151
+ const nextTime = parsedDate?.getTime() ?? null;
3152
+ if (nextTime !== selectedTime) typedValue.current = nextTime;
3153
+ if (!isControlled) setInternalValue(output);
3154
+ onChange?.(output);
3155
+ }
3156
+ },
3157
+ onBlur: (event) => {
3158
+ typedValue.current = void 0;
3159
+ setDraft(inputValue);
3160
+ props.onBlur?.(event);
3161
+ },
3162
+ onClick: (event) => {
3163
+ if (!readOnly) setOpen(!isOpen);
3164
+ props.onClick?.(event);
3165
+ },
3166
+ onKeyDown: (event) => {
3167
+ if ((event.key === "ArrowDown" || event.key === "Enter") && !readOnly) {
3168
+ event.preventDefault();
3169
+ setOpen(true);
3170
+ }
3171
+ if (event.key === "Escape") setOpen(false);
3172
+ props.onKeyDown?.(event);
3173
+ }
3174
+ }
3175
+ ) }),
3176
+ isOpen && !disabled && !readOnly ? /* @__PURE__ */ jsx(JumpyPortal, { children: /* @__PURE__ */ jsxs("div", { ref: refs.setFloating, role: "dialog", "aria-label": label, style: { ...floatingStyles, zIndex: JUMPY_POPUP_Z_INDEX, overflow: "visible", borderRadius: radiusValue(theme, JumpyRadius.smallXS), backgroundColor: `var(--jumpy-non-interactive-background-alternative, var(--jumpy-color-non-interactive-background-alternative, ${theme.color.surfaceCard}))`, color: "var(--jumpy-card-inactive-default-text, var(--jumpy-color-text-primary))", boxShadow: theme.shadow.positiveMedium, padding: `${spacingValue(theme, JumpySpacing.small3XS)}px 0`, minWidth: 260 }, children: [
3177
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", position: "relative", justifyContent: "center", alignItems: "center", gap: spacingValue(theme, JumpySpacing.small), padding: `${spacingValue(theme, JumpySpacing.zero)}px ${spacingValue(theme, JumpySpacing.zero)}px`, overflow: "hidden" }, children: [
3178
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", left: 0, right: 0, top: "50%", height: sizeValue(theme, JumpySize.medium), transform: "translateY(-50%)", backgroundColor: "var(--jumpy-interactive-active-regular-default-surface)", pointerEvents: "none" } }),
3179
+ /* @__PURE__ */ jsx(TimeColumn, { label: "Hour", values: hours, selected: String(displayHour).padStart(2, "0"), onSelect: (next) => commit("hour", next), theme }),
3180
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { zIndex: 1, display: "flex", alignItems: "center", justifyContent: "center", transform: "translateY(-0.1em)", width: sizeValue(theme, JumpySize.smallXS), height: sizeValue(theme, JumpySize.large), padding: `${spacingValue(theme, JumpySpacing.small3XS)}px 0`, color: "var(--jumpy-interactive-active-regular-default-text)", fontFamily: theme.fontFamily.familyBase, fontSize: textStyleValue(theme, JumpyTextStyle.bodyLargeEmphasized).fontSize, fontWeight: textStyleValue(theme, JumpyTextStyle.bodyLargeEmphasized).fontWeight }, children: ":" }),
3181
+ /* @__PURE__ */ jsx(TimeColumn, { label: "Minute", values: minutes, selected: String(currentMinute).padStart(2, "0"), onSelect: (next) => commit("minute", next), theme }),
3182
+ !twentyFourHours ? /* @__PURE__ */ jsx(TimeColumn, { label: "Period", values: ["AM", "PM"], selected: currentPeriod, onSelect: (next) => commit("period", next), theme }) : null
3183
+ ] }),
3184
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: spacingValue(theme, JumpySpacing.smallXS), padding: spacingValue(theme, JumpySpacing.large) }, children: [
3185
+ /* @__PURE__ */ jsx(JumpButton, { variant: "tertiary", onClick: () => setOpen(false), children: labels.cancelButton || "Cancel" }),
3186
+ /* @__PURE__ */ jsx(JumpButton, { onClick: () => setOpen(false), children: labels.applyButton || "Apply" })
3187
+ ] })
3188
+ ] }) }) : null
3189
+ ] });
3190
+ }
3191
+ function TimeColumn({ label, values, selected, onSelect, theme }) {
3192
+ const selectedIndex = Math.max(0, values.indexOf(selected));
3193
+ const wraps = values.length >= 5;
3194
+ const visibleValues = Array.from({ length: 5 }, (_, offsetIndex) => {
3195
+ const index = selectedIndex + offsetIndex - 2;
3196
+ if (wraps) return values[(index + values.length) % values.length];
3197
+ return index >= 0 && index < values.length ? values[index] : null;
3198
+ });
3199
+ const rowHeight = sizeValue(theme, JumpySize.large) + spacingValue(theme, JumpySpacing.small3XS) * 2;
3200
+ const bodyMedium = textStyleValue(theme, JumpyTextStyle.bodyMedium);
3201
+ const bodyLargeEmphasized = textStyleValue(theme, JumpyTextStyle.bodyLargeEmphasized);
3202
+ return /* @__PURE__ */ jsx("div", { "aria-label": label, style: { position: "relative", zIndex: 1, display: "flex", flexDirection: "column", overflow: "hidden", padding: `0 ${spacingValue(theme, JumpySpacing.small3XS)}px`, margin: `0 ${-spacingValue(theme, JumpySpacing.small3XS)}px`, height: rowHeight * 5 }, children: /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column" }, children: visibleValues.map((value, offsetIndex) => {
3203
+ if (value === null) return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { flex: `0 0 ${rowHeight}px` } }, `${label}-empty-${offsetIndex}`);
3204
+ const active = value === selected;
3205
+ return /* @__PURE__ */ jsx("button", { type: "button", "data-jumpy-component": "time-picker-wheel-button", onClick: () => onSelect(value), "data-selected": active ? "true" : "false", style: { border: 0, width: sizeValue(theme, JumpySize.large), height: rowHeight, flex: `0 0 ${rowHeight}px`, backgroundColor: "transparent", color: active ? "var(--jumpy-interactive-active-regular-default-text, var(--jumpy-color-text-on-primary))" : "var(--jumpy-interactive-inactive-regular-default-text, var(--jumpy-color-text-primary))", padding: `${spacingValue(theme, JumpySpacing.small3XS)}px 0`, cursor: "pointer", fontFamily: theme.fontFamily.familyBase, fontSize: active ? bodyLargeEmphasized.fontSize : bodyMedium.fontSize, lineHeight: active ? bodyLargeEmphasized.lineHeight : bodyMedium.lineHeight, fontWeight: active ? bodyLargeEmphasized.fontWeight : bodyMedium.fontWeight }, children: value }, `${label}-${value}`);
3206
+ }) }) });
3207
+ }
3208
+ function flattenDropdownItems(items) {
3209
+ return items.flatMap((item) => [item, ...flattenDropdownItems(item.children || [])]);
3210
+ }
3211
+ function JumpDropdown({ label, hint, value, defaultValue = [], onChange, open, onOpenChange, items = [], placeholder, popupMinWidth = 0, disabled = false, readOnly = false, multiple = false, searchable = false, renderTrigger, renderItem, inputRef, className = "", status = "regular", error = false, hideClearIcon = false, onClear, ...props }) {
3212
+ const theme = useJumpyTheme();
3213
+ const fieldContext = useContext(JumpyFieldContext);
3214
+ const generatedId = useId();
3215
+ const triggerId = props.id || `jumpy-dropdown-${generatedId}`;
3216
+ const [internalValue, setInternalValue] = useState(defaultValue);
3217
+ const [internalOpen, setInternalOpen] = useState(false);
3218
+ const [searchValue, setSearchValue] = useState("");
3219
+ const [focusedIndex, setFocusedIndex] = useState(-1);
3220
+ const dropdownRef = useRef(null);
3221
+ const triggerRef = useRef(null);
3222
+ const selectedValues = value ?? internalValue;
3223
+ const isOpen = open ?? internalOpen;
3224
+ const flattenedItems = flattenDropdownItems(items);
3225
+ const visibleItems = searchValue ? flattenedItems.filter((item) => item.label.toLowerCase().includes(searchValue.toLowerCase())) : flattenedItems;
3226
+ const enabledVisibleItems = visibleItems.filter((item) => !item.disabled);
3227
+ const selectedItems = flattenedItems.filter((item) => selectedValues.includes(item.value));
3228
+ const displayText = selectedItems.map((item) => item.label).join(", ");
3229
+ const dropdownOffset = spacingValue(theme, JumpySpacing.small3XS);
3230
+ const dropdownPadding = spacingValue(theme, JumpySpacing.medium);
3231
+ const dropdownMinHeight = sizeValue(theme, JumpySize.large2XL);
3232
+ const dropdownMaxHeight = sizeValue(theme, JumpySize.large8XL) * 2;
3233
+ const { refs, floatingStyles } = useFloating({
3234
+ open: isOpen,
3235
+ onOpenChange: setOpen,
3236
+ placement: "bottom-start",
3237
+ strategy: "fixed",
3238
+ whileElementsMounted: autoUpdate,
3239
+ middleware: [
3240
+ offset(dropdownOffset),
3241
+ flip({ padding: dropdownPadding }),
3242
+ shift({ padding: dropdownPadding }),
3243
+ size({
3244
+ padding: dropdownPadding,
3245
+ apply({ rects, availableWidth, availableHeight, elements }) {
3246
+ Object.assign(elements.floating.style, {
3247
+ width: `${Math.max(0, Math.min(Math.max(rects.reference.width, popupMinWidth), availableWidth))}px`,
3248
+ maxHeight: `${Math.max(dropdownMinHeight, Math.min(dropdownMaxHeight, availableHeight))}px`
3249
+ });
3250
+ }
3251
+ })
3252
+ ]
3253
+ });
3254
+ const triggerStyle = fieldContext && !label && !hint ? {
3255
+ paddingRight: sizeValue(theme, JumpySize.small2XS) + spacingValue(theme, JumpySpacing.medium),
3256
+ ...props.style || {}
3257
+ } : props.style;
3258
+ const triggerInputStyle = {
3259
+ ...triggerStyle || {},
3260
+ caretColor: "transparent"
3261
+ };
3262
+ function setOpen(next) {
3263
+ setInternalOpen(next);
3264
+ onOpenChange?.(next);
3265
+ if (!next) {
3266
+ setSearchValue("");
3267
+ setFocusedIndex(-1);
3268
+ } else if (enabledVisibleItems.length > 0 && focusedIndex < 0) {
3269
+ setFocusedIndex(visibleItems.findIndex((item) => !item.disabled));
3270
+ }
3271
+ }
3272
+ function commit(nextValues) {
3273
+ setInternalValue(nextValues);
3274
+ onChange?.(nextValues);
3275
+ if (!multiple) {
3276
+ setOpen(false);
3277
+ triggerRef.current?.focus();
3278
+ }
3279
+ }
3280
+ useEffect(() => {
3281
+ if (!isOpen) return void 0;
3282
+ function dismissOutside(event) {
3283
+ if (!dropdownRef.current?.contains(event.target) && !refs.floating.current?.contains(event.target)) setOpen(false);
3284
+ }
3285
+ document.addEventListener("pointerdown", dismissOutside);
3286
+ document.addEventListener("focusin", dismissOutside);
3287
+ return () => {
3288
+ document.removeEventListener("pointerdown", dismissOutside);
3289
+ document.removeEventListener("focusin", dismissOutside);
3290
+ };
3291
+ });
3292
+ function selectItem(item) {
3293
+ if (!item || item.disabled) return;
3294
+ if (multiple) {
3295
+ commit(selectedValues.includes(item.value) ? selectedValues.filter((v) => v !== item.value) : [...selectedValues, item.value]);
3296
+ return;
3297
+ }
3298
+ commit([item.value]);
3299
+ }
3300
+ function moveFocus(direction) {
3301
+ if (enabledVisibleItems.length === 0) return;
3302
+ const currentEnabledIndex = enabledVisibleItems.findIndex((item) => item.value === visibleItems[focusedIndex]?.value);
3303
+ const nextEnabledIndex = direction === "start" ? 0 : direction === "end" ? enabledVisibleItems.length - 1 : (currentEnabledIndex + direction + enabledVisibleItems.length) % enabledVisibleItems.length;
3304
+ setFocusedIndex(visibleItems.findIndex((item) => item.value === enabledVisibleItems[nextEnabledIndex].value));
3305
+ }
3306
+ const triggerInput = /* @__PURE__ */ jsx(
3307
+ JumpInput,
3308
+ {
3309
+ ...props,
3310
+ ref: (node) => {
3311
+ triggerRef.current = node;
3312
+ refs.setReference(node?.closest('[data-jumpy-component="field-control"]') || node);
3313
+ if (typeof inputRef === "function") inputRef(node);
3314
+ else if (inputRef) inputRef.current = node;
3315
+ },
3316
+ id: triggerId,
3317
+ style: triggerInputStyle,
3318
+ "data-jumpy-dropdown-trigger": "true",
3319
+ onChange: () => {
3320
+ },
3321
+ "data-has-value": Boolean(displayText),
3322
+ role: "combobox",
3323
+ "aria-expanded": isOpen,
3324
+ "aria-haspopup": "listbox",
3325
+ "aria-controls": isOpen ? `${triggerId}-listbox` : void 0,
3326
+ "aria-activedescendant": isOpen && focusedIndex >= 0 ? `${triggerId}-option-${focusedIndex}` : void 0,
3327
+ readOnly,
3328
+ disabled,
3329
+ value: displayText,
3330
+ placeholder: displayText ? void 0 : placeholder,
3331
+ autoComplete: "off",
3332
+ spellCheck: false,
3333
+ inputMode: "none",
3334
+ onClick: (event) => {
3335
+ if (!readOnly) setOpen(!isOpen);
3336
+ props.onClick?.(event);
3337
+ },
3338
+ onBlur: (event) => {
3339
+ props.onBlur?.(event);
3340
+ },
3341
+ onKeyDown: (event) => {
3342
+ if (event.key !== "Tab" && event.key !== "Escape") event.preventDefault();
3343
+ if (readOnly) {
3344
+ props.onKeyDown?.(event);
3345
+ return;
3346
+ }
3347
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
3348
+ setOpen(true);
3349
+ }
3350
+ if (event.key === "ArrowDown") moveFocus(1);
3351
+ if (event.key === "ArrowUp") moveFocus(-1);
3352
+ if (event.key === "Home") moveFocus("start");
3353
+ if (event.key === "End") moveFocus("end");
3354
+ if ((event.key === "Enter" || event.key === " ") && isOpen) selectItem(visibleItems[focusedIndex]);
3355
+ props.onKeyDown?.(event);
3356
+ },
3357
+ onPaste: (event) => event.preventDefault()
3358
+ }
3359
+ );
3360
+ const trailing = readOnly ? void 0 : /* @__PURE__ */ jsx("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", style: { display: "block", fontSize: sizeValue(theme, JumpySize.small) }, children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: isOpen ? "m16.59 15.705 1.41-1.41-6-6-6 6 1.41 1.41 4.59-4.58z" : "M7.41 8.295 6 9.705l6 6 6-6-1.41-1.41-4.59 4.58z", clipRule: "evenodd", fill: "currentColor" }) });
3361
+ const trigger = renderTrigger ? renderTrigger(selectedValues, isOpen) : fieldContext && !label && !hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
3362
+ triggerInput,
3363
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { pointerEvents: "none", position: "absolute", right: spacingValue(theme, JumpySpacing.medium), top: "50%", transform: "translateY(-50%)", color: theme.color.textSecondary, display: "inline-flex", alignItems: "center", justifyContent: "center", lineHeight: 1 }, children: trailing })
3364
+ ] }) : /* @__PURE__ */ jsx(JumpField, { label, hint, status, error, hideClearIcon, onClear: () => {
3365
+ commit([]);
3366
+ setSearchValue("");
3367
+ onClear?.();
3368
+ }, className, trailing, children: triggerInput });
3369
+ const listbox = isOpen && !disabled ? /* @__PURE__ */ jsx(JumpyPortal, { children: /* @__PURE__ */ jsxs(
3370
+ "div",
3371
+ {
3372
+ ref: refs.setFloating,
3373
+ id: `${triggerId}-listbox`,
3374
+ role: "listbox",
3375
+ "aria-multiselectable": multiple || void 0,
3376
+ "data-jumpy-component": "dropdown-popover",
3377
+ style: {
3378
+ ...floatingStyles,
3379
+ zIndex: JUMPY_POPUP_Z_INDEX,
3380
+ overflowX: "hidden",
3381
+ overflowY: "auto",
3382
+ borderRadius: radiusValue(theme, JumpyRadius.large),
3383
+ backgroundColor: `var(--jumpy-non-interactive-background-alternative, var(--jumpy-color-non-interactive-background-alternative, ${theme.color.surfaceCard}))`,
3384
+ boxShadow: theme.shadow.positiveMedium,
3385
+ padding: `${spacingValue(theme, JumpySpacing.small3XS)}px 0`
3386
+ },
3387
+ children: [
3388
+ searchable ? /* @__PURE__ */ jsx("div", { style: { margin: `${spacingValue(theme, JumpySpacing.small3XS)}px ${spacingValue(theme, JumpySpacing.smallXS)}px` }, children: /* @__PURE__ */ jsx(JumpSearchField, { label: "Search", placeholder: "Search", value: searchValue, onChange: (event) => {
3389
+ setSearchValue(event.target.value);
3390
+ setFocusedIndex(-1);
3391
+ } }) }) : null,
3392
+ visibleItems.length === 0 ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "dropdown-empty-state", style: { padding: `${spacingValue(theme, JumpySpacing.medium)}px ${spacingValue(theme, JumpySpacing.large)}px`, color: "var(--jumpy-interactive-inactive-regular-default-text)", fontFamily: theme.fontFamily.familyBase, fontSize: textStyleValue(theme, JumpyTextStyle.bodyMedium).fontSize }, children: "No options available" }) : null,
3393
+ visibleItems.map((item, index) => {
3394
+ const selected = selectedValues.includes(item.value);
3395
+ const listItemProps = {
3396
+ id: `${triggerId}-option-${index}`,
3397
+ role: "option",
3398
+ "aria-selected": selected,
3399
+ disabled: item.disabled,
3400
+ onMouseDown: (event) => event.preventDefault(),
3401
+ onMouseEnter: () => setFocusedIndex(index),
3402
+ onClick: () => {
3403
+ selectItem(item);
3404
+ }
3405
+ };
3406
+ return renderItem ? renderItem(item, selected, listItemProps) : /* @__PURE__ */ jsx(JumpListItem, { selected, ...listItemProps, children: item.label }, String(item.value));
3407
+ })
3408
+ ]
3409
+ }
3410
+ ) }) : null;
3411
+ return /* @__PURE__ */ jsxs("div", { ref: dropdownRef, "data-jumpy-component": "dropdown", onKeyDown: (event) => {
3412
+ if (event.key === KeyboardKeys.ESCAPE && isOpen && !event.defaultPrevented) {
3413
+ event.preventDefault();
3414
+ event.stopPropagation();
3415
+ setOpen(false);
3416
+ triggerRef.current?.focus();
3417
+ }
3418
+ }, style: { position: "relative", width: "100%" }, children: [
3419
+ trigger,
3420
+ listbox
3421
+ ] });
3422
+ }
3423
+ function JumpSpinner({ size: size2 = "medium", mode = "indeterminate", value = 0, showTrack = true, inverse = false, className = "", ...props }) {
3424
+ const theme = useJumpyTheme();
3425
+ const sizeMap = {
3426
+ xSmall: { diameter: 20, strokeWidth: 2 },
3427
+ small: { diameter: 24, strokeWidth: 2 },
3428
+ medium: { diameter: 32, strokeWidth: 2 },
3429
+ large: { diameter: 48, strokeWidth: 4 },
3430
+ xLarge: { diameter: 56, strokeWidth: 4 },
3431
+ xxLarge: { diameter: 72, strokeWidth: 4 }
3432
+ };
3433
+ const { diameter, strokeWidth } = sizeMap[size2] || sizeMap.medium;
3434
+ const radius = (diameter - strokeWidth) / 2;
3435
+ const center = diameter / 2;
3436
+ const circumference = 2 * Math.PI * radius;
3437
+ const clampedValue = Math.min(Math.max(value, 0), 100);
3438
+ const dash = mode === "determinate" ? clampedValue / 100 * circumference : circumference * 0.72;
3439
+ const gap = circumference - dash;
3440
+ return /* @__PURE__ */ jsxs("svg", { ...props, "data-jumpy-component": "spinner", className: cn(className), width: diameter, height: diameter, viewBox: `0 0 ${diameter} ${diameter}`, role: "progressbar", "aria-valuemin": mode === "determinate" ? 0 : void 0, "aria-valuemax": mode === "determinate" ? 100 : void 0, "aria-valuenow": mode === "determinate" ? clampedValue : void 0, "aria-label": props["aria-label"] || (mode === "determinate" ? `Loading progress: ${clampedValue}%` : "Loading in progress"), style: { color: inverse ? theme.color.textOnPrimary : theme.color.surfacePrimary, ...props.style || {} }, children: [
3441
+ showTrack ? /* @__PURE__ */ jsx("circle", { cx: center, cy: center, r: radius, fill: "none", stroke: theme.color.borderSubtle, strokeWidth }) : null,
3442
+ /* @__PURE__ */ jsx("circle", { cx: center, cy: center, r: radius, fill: "none", stroke: "currentColor", strokeWidth, strokeLinecap: "round", strokeDasharray: `${dash} ${gap}`, transform: `rotate(-90 ${center} ${center})`, children: mode === "indeterminate" ? /* @__PURE__ */ jsx("animateTransform", { attributeName: "transform", type: "rotate", from: `0 ${center} ${center}`, to: `360 ${center} ${center}`, dur: "1s", repeatCount: "indefinite" }) : null })
3443
+ ] });
3444
+ }
3445
+ function JumpDialog({ as, visible = false, onVisibleChange, title, description, footer, children, className = "", disableBackdropClick = false, disableContentScroll = false, fullScreen = false, overlayProps, slots, innerClasses = {}, ...props }) {
3446
+ const theme = useJumpyTheme();
3447
+ const Component = as || "div";
3448
+ const labelId = useId();
3449
+ const descriptionId = useId();
3450
+ const dialogTitle = slots?.title ?? title;
3451
+ const dialogDescription = slots?.description ?? description;
3452
+ const dialogFooter = slots?.footer ?? footer;
3453
+ const dialogIcon = slots?.icon;
3454
+ const dialogIllustration = slots?.illustration;
3455
+ const titleStyle = textStyleValue(theme, JumpyTextStyle.titleLarge);
3456
+ const descriptionStyle = textStyleValue(theme, JumpyTextStyle.bodyMedium);
3457
+ const { className: overlayClassName, style: overlayStyle, onClick: overlayOnClick, ...restOverlayProps } = overlayProps || {};
3458
+ if (!visible) return null;
3459
+ return /* @__PURE__ */ jsx(
3460
+ JumpyModalLayer,
3461
+ {
3462
+ ...restOverlayProps,
3463
+ onVisibleChange,
3464
+ "data-jumpy-component": "dialog-overlay",
3465
+ "data-disable-content-scroll": disableContentScroll ? "true" : "false",
3466
+ role: "presentation",
3467
+ className: cn(overlayClassName),
3468
+ onClick: (event) => {
3469
+ overlayOnClick?.(event);
3470
+ if (event.target === event.currentTarget && !event.defaultPrevented && !disableBackdropClick) onVisibleChange?.(false);
3471
+ },
3472
+ style: overlayStyle,
3473
+ children: /* @__PURE__ */ jsxs(
3474
+ Component,
3475
+ {
3476
+ ...props,
3477
+ "data-jumpy-component": "dialog",
3478
+ "data-full-screen": fullScreen ? "true" : "false",
3479
+ "data-no-footer": dialogFooter ? "false" : "true",
3480
+ role: "dialog",
3481
+ "aria-modal": "true",
3482
+ "aria-labelledby": dialogTitle ? labelId : void 0,
3483
+ "aria-describedby": dialogDescription ? descriptionId : void 0,
3484
+ "aria-label": !dialogTitle ? props["aria-label"] || title : props["aria-label"],
3485
+ className: cn(className),
3486
+ onClick: (event) => {
3487
+ event.stopPropagation();
3488
+ props.onClick?.(event);
3489
+ },
3490
+ style: {
3491
+ "--jumpy-dialog-width": props.style?.width || "472px",
3492
+ ...props.style || {}
3493
+ },
3494
+ children: [
3495
+ dialogIllustration ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "dialog-illustration", className: innerClasses.illustration?.className, style: innerClasses.illustration?.style, children: dialogIllustration }) : null,
3496
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "dialog-body", className: innerClasses.body?.className, style: innerClasses.body?.style, children: [
3497
+ dialogIcon ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "dialog-icon", className: innerClasses.icon?.className, style: innerClasses.icon?.style, children: dialogIcon }) : null,
3498
+ dialogTitle ? /* @__PURE__ */ jsx(
3499
+ "div",
3500
+ {
3501
+ id: labelId,
3502
+ "data-jumpy-slot": "dialog-title",
3503
+ className: innerClasses.title?.className,
3504
+ style: {
3505
+ margin: 0,
3506
+ fontFamily: theme.fontFamily.familyBase,
3507
+ fontSize: titleStyle.fontSize,
3508
+ lineHeight: titleStyle.lineHeight,
3509
+ fontWeight: titleStyle.fontWeight,
3510
+ letterSpacing: titleStyle.letterSpacing,
3511
+ ...innerClasses.title?.style || {}
3512
+ },
3513
+ children: dialogTitle
3514
+ }
3515
+ ) : null,
3516
+ /* @__PURE__ */ jsxs("div", { "data-jumpy-slot": "dialog-scrollable-content", className: innerClasses.scrollableContent?.className, style: innerClasses.scrollableContent?.style, children: [
3517
+ dialogDescription ? /* @__PURE__ */ jsx(
3518
+ "div",
3519
+ {
3520
+ id: descriptionId,
3521
+ "data-jumpy-slot": "dialog-description",
3522
+ className: innerClasses.description?.className,
3523
+ style: {
3524
+ margin: 0,
3525
+ fontFamily: theme.fontFamily.familyBase,
3526
+ fontSize: descriptionStyle.fontSize,
3527
+ lineHeight: descriptionStyle.lineHeight,
3528
+ fontWeight: descriptionStyle.fontWeight,
3529
+ letterSpacing: descriptionStyle.letterSpacing,
3530
+ ...innerClasses.description?.style || {}
3531
+ },
3532
+ children: dialogDescription
3533
+ }
3534
+ ) : null,
3535
+ children
3536
+ ] })
3537
+ ] }),
3538
+ dialogFooter ? /* @__PURE__ */ jsx("div", { "data-jumpy-slot": "dialog-footer", className: innerClasses.footer?.className, style: innerClasses.footer?.style, children: dialogFooter }) : null
3539
+ ]
3540
+ }
3541
+ )
3542
+ }
3543
+ );
3544
+ }
3545
+ function JumpLoader({ label = "Jumpy UI wird geladen", fullScreen = false, className = "", decorativeBackground = false }) {
3546
+ const theme = useJumpyTheme();
3547
+ const content = /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center text-center", className), role: "status", "aria-live": "polite", children: [
3548
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", style: { height: theme.sizing.loaderSize, width: theme.sizing.loaderSize }, children: [
3549
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: { borderRadius: theme.radius.large2XL, backgroundColor: theme.color.infoSurface } }),
3550
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-2", style: { borderRadius: theme.radius.largeXL, border: `1px solid ${theme.color.infoBorder}`, backgroundColor: theme.color.surfaceCard, boxShadow: theme.shadow.loader } }),
3551
+ /* @__PURE__ */ jsx("span", { className: "relative grid place-items-center", style: { color: theme.color.surfacePrimary }, children: /* @__PURE__ */ jsx(JumpSpinner, { size: "large", "aria-label": label }) })
3552
+ ] }),
3553
+ /* @__PURE__ */ jsx("p", { className: "mt-5 text-[11px] font-bold uppercase tracking-[0.18em]", style: { color: theme.color.surfacePrimary }, children: label }),
3554
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-1.5", "aria-hidden": "true", children: [
3555
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full [animation-delay:-0.2s]", style: { backgroundColor: theme.color.surfacePrimary } }),
3556
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full [animation-delay:-0.1s]", style: { backgroundColor: theme.color.successBorder } }),
3557
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full", style: { backgroundColor: theme.color.infoBorder } })
3558
+ ] }),
3559
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Laden" })
3560
+ ] });
3561
+ if (!fullScreen) return content;
3562
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-screen items-center justify-center overflow-hidden p-6", style: { backgroundColor: theme.color.surfaceCanvas, color: theme.color.textPrimary }, children: [
3563
+ decorativeBackground && /* @__PURE__ */ jsx(JumpBackground, { snow: false }),
3564
+ /* @__PURE__ */ jsx(JumpCard, { className: cn("px-10 py-9", decorativeBackground && "relative z-10"), children: content })
3565
+ ] });
3566
+ }
3567
+ function JumpButton({ as, size: size2, variant = "primary", status = "regular", className = "", isLoading = false, disabled, elevated = false, slots, leadingIcon, trailingIcon, children, ...props }) {
3568
+ const theme = useJumpyTheme();
3569
+ const variantStyles = getButtonVariantStyles(theme, variant, status);
3570
+ const height = sizeValue(theme, size2 ?? JumpySize.large);
3571
+ if (!Number.isFinite(height) || height < 0) throw new Error(`Invalid JumpButton size token: ${size2}`);
3572
+ const compact = height < sizeValue(theme, JumpySize.large);
3573
+ const textStyle = textStyleValue(theme, compact ? JumpyTextStyle.bodySmallEmphasized : JumpyTextStyle.bodyMediumEmphasized);
3574
+ const iconSize = sizeValue(theme, compact ? JumpySize.small3XS : JumpySize.small2XS);
3575
+ const [pressed, setPressed] = useState(false);
3576
+ const leading = slots?.leading ?? leadingIcon;
3577
+ const trailing = slots?.trailing ?? trailingIcon;
3578
+ const Component = as || "button";
3579
+ const isButton = Component === "button";
3580
+ const isDisabled = disabled || isLoading;
3581
+ const displayedLeading = isLoading ? /* @__PURE__ */ jsx(JumpSpinner, { size: "xSmall", showTrack: false, "aria-hidden": "true", style: { display: "block", width: iconSize, height: iconSize, color: "inherit" } }) : leading;
3582
+ const spacing = getButtonSpacing(theme, variant, Boolean(displayedLeading), Boolean(trailing));
3583
+ const hasBorder = variant === "secondary" || variant === "tertiary";
3584
+ if (size2 !== void 0) {
3585
+ const border = hasBorder ? borderWidthValue(theme, JumpyBorderWidth.small) : 0;
3586
+ const contentHeight = Math.max(textStyle.fontSize * textStyle.lineHeight, displayedLeading || trailing ? iconSize : 0);
3587
+ spacing.paddingTop = spacing.paddingBottom = Math.max(0, (height - contentHeight - border * 2) / 2);
3588
+ if (compact) {
3589
+ spacing.gap = spacingValue(theme, JumpySpacing.small3XS);
3590
+ spacing.paddingLeft = spacing.paddingRight = spacingValue(theme, variant === "borderless" ? JumpySpacing.zero : JumpySpacing.medium);
3591
+ }
3592
+ }
3593
+ const content = /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", flexDirection: "row", alignItems: "center", justifyContent: "center", gap: spacing.gap, whiteSpace: "nowrap" }, children: [
3594
+ displayedLeading ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "button-leading", "aria-hidden": "true", style: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, width: iconSize, height: iconSize, lineHeight: 0, fontSize: iconSize }, children: displayedLeading }) : null,
3595
+ /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "button-label", children }),
3596
+ trailing ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "button-trailing", "aria-hidden": "true", style: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, width: iconSize, height: iconSize, lineHeight: 0, fontSize: iconSize }, children: trailing }) : null
3597
+ ] });
3598
+ return /* @__PURE__ */ jsx(
3599
+ Component,
3600
+ {
3601
+ ...props,
3602
+ type: isButton ? props.type || "button" : props.type,
3603
+ "data-jumpy-component": "icon-button",
3604
+ "data-variant": variant,
3605
+ "data-status": status,
3606
+ "data-elevated": elevated ? "true" : "false",
3607
+ "data-loading": isLoading ? "true" : "false",
3608
+ "data-size": size2,
3609
+ "data-pressed": pressed ? "true" : "false",
3610
+ "aria-busy": isLoading || void 0,
3611
+ "aria-disabled": !isButton && isDisabled ? true : props["aria-disabled"],
3612
+ tabIndex: !isButton && isDisabled ? -1 : props.tabIndex,
3613
+ disabled: isButton ? isDisabled : void 0,
3614
+ onClick: (event) => {
3615
+ if (isDisabled) {
3616
+ event.preventDefault();
3617
+ return;
3618
+ }
3619
+ props.onClick?.(event);
3620
+ },
3621
+ onPointerDown: (event) => {
3622
+ setPressed(true);
3623
+ props.onPointerDown?.(event);
3624
+ },
3625
+ onPointerUp: (event) => {
3626
+ setPressed(false);
3627
+ props.onPointerUp?.(event);
3628
+ },
3629
+ onPointerLeave: (event) => {
3630
+ setPressed(false);
3631
+ props.onPointerLeave?.(event);
3632
+ },
3633
+ onBlur: (event) => {
3634
+ setPressed(false);
3635
+ props.onBlur?.(event);
3636
+ },
3637
+ className: cn("disabled:cursor-not-allowed", className),
3638
+ style: {
3639
+ display: "inline-flex",
3640
+ alignItems: "center",
3641
+ justifyContent: "center",
3642
+ minHeight: height,
3643
+ appearance: "none",
3644
+ WebkitAppearance: "none",
3645
+ boxSizing: "border-box",
3646
+ paddingLeft: spacing.paddingLeft,
3647
+ paddingRight: spacing.paddingRight,
3648
+ paddingTop: spacing.paddingTop,
3649
+ paddingBottom: spacing.paddingBottom,
3650
+ borderRadius: radiusValue(theme, JumpyRadius.small),
3651
+ letterSpacing: textStyle.letterSpacing,
3652
+ fontFamily: theme.fontFamily.familyBase,
3653
+ fontSize: textStyle.fontSize,
3654
+ lineHeight: textStyle.lineHeight,
3655
+ fontWeight: textStyle.fontWeight,
3656
+ // Primary regular disabled tokens already encode the subdued appearance.
3657
+ opacity: isDisabled && !(variant === "primary" && status === "regular") ? opacityValue(theme, JumpyOpacity.medium) : opacityValue(theme, JumpyOpacity.solid),
3658
+ borderWidth: hasBorder ? borderWidthValue(theme, JumpyBorderWidth.small) : 0,
3659
+ borderStyle: hasBorder ? "solid" : "none",
3660
+ ...variantStyles,
3661
+ ...elevated && !isDisabled ? { "--jumpy-action-default-shadow": shadowValue(theme, JumpyShadowStyle.positiveMedium) } : {},
3662
+ ...props.style || {}
3663
+ },
3664
+ children: /* @__PURE__ */ jsx("span", { style: { position: "relative", display: "inline-flex", alignItems: "center", justifyContent: "center", whiteSpace: "nowrap" }, children: content })
3665
+ }
3666
+ );
3667
+ }
3668
+ function JumpIconButton({ as, variant = "primary", status = "regular", alternateHover = false, elevated = false, className = "", disabled, children, "aria-label": ariaLabel, ...props }) {
3669
+ const theme = useJumpyTheme();
3670
+ const actionVariant = variant === "primary" ? "secondary" : variant === "secondary" ? "tertiary" : variant;
3671
+ const variantStyles = getButtonVariantStyles(theme, actionVariant, status);
3672
+ const actionTokenPrefix = `--jumpy-actions-${actionVariant}-${status}`;
3673
+ const [pressed, setPressed] = useState(false);
3674
+ const Component = as || "button";
3675
+ const isButton = Component === "button";
3676
+ const supportsDisabled = typeof Component === "string" && supportsDisabledAttribute(Component);
3677
+ const hasBorder = variant === "primary";
3678
+ return /* @__PURE__ */ jsx(
3679
+ Component,
3680
+ {
3681
+ ...props,
3682
+ type: isButton ? props.type || "button" : props.type,
3683
+ "aria-label": ariaLabel,
3684
+ "data-jumpy-component": "button",
3685
+ "data-variant": variant,
3686
+ "data-status": status,
3687
+ "data-elevated": elevated ? "true" : "false",
3688
+ "data-pressed": pressed ? "true" : "false",
3689
+ "aria-disabled": !supportsDisabled && disabled ? true : props["aria-disabled"],
3690
+ tabIndex: !supportsDisabled && disabled ? -1 : props.tabIndex,
3691
+ disabled: supportsDisabled ? disabled : void 0,
3692
+ onPointerDown: (event) => {
3693
+ setPressed(true);
3694
+ props.onPointerDown?.(event);
3695
+ },
3696
+ onPointerUp: (event) => {
3697
+ setPressed(false);
3698
+ props.onPointerUp?.(event);
3699
+ },
3700
+ onPointerLeave: (event) => {
3701
+ setPressed(false);
3702
+ props.onPointerLeave?.(event);
3703
+ },
3704
+ onBlur: (event) => {
3705
+ setPressed(false);
3706
+ props.onBlur?.(event);
3707
+ },
3708
+ className: cn("disabled:cursor-not-allowed", className),
3709
+ style: {
3710
+ display: "inline-grid",
3711
+ placeItems: "center",
3712
+ width: sizeValue(theme, JumpySize.large),
3713
+ height: sizeValue(theme, JumpySize.large),
3714
+ minWidth: sizeValue(theme, JumpySize.large),
3715
+ minHeight: sizeValue(theme, JumpySize.large),
3716
+ appearance: "none",
3717
+ WebkitAppearance: "none",
3718
+ boxSizing: "border-box",
3719
+ padding: spacingValue(theme, JumpySpacing.small2XS),
3720
+ borderRadius: radiusValue(theme, JumpyRadius.small),
3721
+ borderWidth: hasBorder ? borderWidthValue(theme, JumpyBorderWidth.small) : 0,
3722
+ borderStyle: hasBorder ? "solid" : "none",
3723
+ opacity: disabled ? opacityValue(theme, JumpyOpacity.medium) : opacityValue(theme, JumpyOpacity.solid),
3724
+ ...variantStyles,
3725
+ "--jumpy-action-default-icon": `var(${actionTokenPrefix}-default-action, ${variantStyles["--jumpy-action-default-icon"]})`,
3726
+ "--jumpy-action-hover-icon": `var(${actionTokenPrefix}-hover-action, ${variantStyles["--jumpy-action-hover-icon"]})`,
3727
+ "--jumpy-action-focus-icon": `var(${actionTokenPrefix}-focus-action, ${variantStyles["--jumpy-action-focus-icon"]})`,
3728
+ "--jumpy-action-pressed-icon": `var(${actionTokenPrefix}-pressed-action, ${variantStyles["--jumpy-action-pressed-icon"]})`,
3729
+ "--jumpy-action-disabled-icon": `var(${actionTokenPrefix}-disabled-action, ${variantStyles["--jumpy-action-disabled-icon"]})`,
3730
+ ...elevated && !disabled ? { "--jumpy-action-default-shadow": shadowValue(theme, JumpyShadowStyle.positiveMedium) } : {},
3731
+ ...alternateHover ? {
3732
+ "--jumpy-action-hover-surface": `var(--jumpy-interactive-inactive-${status}-hover-surface-alternate, ${variantStyles["--jumpy-action-hover-surface"]})`,
3733
+ "--jumpy-action-pressed-surface": `var(--jumpy-interactive-inactive-${status}-pressed-surface-alternate, ${variantStyles["--jumpy-action-pressed-surface"]})`
3734
+ } : {},
3735
+ ...props.style || {}
3736
+ },
3737
+ children: /* @__PURE__ */ jsx("span", { style: { display: "grid", placeItems: "center", width: sizeValue(theme, JumpySize.small), height: sizeValue(theme, JumpySize.small), lineHeight: 0, fontSize: sizeValue(theme, JumpySize.small) }, "aria-hidden": "true", children })
3738
+ }
3739
+ );
3740
+ }
3741
+ function JumpLinkButton({ href, variant = "borderless", status = "regular", className = "", leadingIcon, trailingIcon, children, ...props }) {
3742
+ const theme = useJumpyTheme();
3743
+ const variantStyles = getButtonVariantStyles(theme, variant, status);
3744
+ const bodyMediumEmphasized = textStyleValue(theme, JumpyTextStyle.bodyMediumEmphasized);
3745
+ const iconSize = sizeValue(theme, JumpySize.small2XS);
3746
+ const [pressed, setPressed] = useState(false);
3747
+ const spacing = getButtonSpacing(theme, variant, Boolean(leadingIcon), Boolean(trailingIcon));
3748
+ const hasBorder = variant === "secondary" || variant === "tertiary";
3749
+ return /* @__PURE__ */ jsx(
3750
+ "a",
3751
+ {
3752
+ href,
3753
+ ...props,
3754
+ "data-jumpy-component": "link-button",
3755
+ "data-variant": variant,
3756
+ "data-status": status,
3757
+ "data-pressed": pressed ? "true" : "false",
3758
+ onPointerDown: (event) => {
3759
+ setPressed(true);
3760
+ props.onPointerDown?.(event);
3761
+ },
3762
+ onPointerUp: (event) => {
3763
+ setPressed(false);
3764
+ props.onPointerUp?.(event);
3765
+ },
3766
+ onPointerLeave: (event) => {
3767
+ setPressed(false);
3768
+ props.onPointerLeave?.(event);
3769
+ },
3770
+ onBlur: (event) => {
3771
+ setPressed(false);
3772
+ props.onBlur?.(event);
3773
+ },
3774
+ className: cn(className),
3775
+ style: {
3776
+ display: "inline-flex",
3777
+ alignItems: "center",
3778
+ justifyContent: "center",
3779
+ minHeight: sizeValue(theme, JumpySize.large),
3780
+ appearance: "none",
3781
+ WebkitAppearance: "none",
3782
+ boxSizing: "border-box",
3783
+ paddingLeft: spacing.paddingLeft,
3784
+ paddingRight: spacing.paddingRight,
3785
+ paddingTop: spacing.paddingTop,
3786
+ paddingBottom: spacing.paddingBottom,
3787
+ borderRadius: radiusValue(theme, JumpyRadius.small),
3788
+ letterSpacing: bodyMediumEmphasized.letterSpacing,
3789
+ fontFamily: theme.fontFamily.familyBase,
3790
+ fontSize: bodyMediumEmphasized.fontSize,
3791
+ lineHeight: bodyMediumEmphasized.lineHeight,
3792
+ fontWeight: bodyMediumEmphasized.fontWeight,
3793
+ borderWidth: hasBorder ? borderWidthValue(theme, JumpyBorderWidth.small) : 0,
3794
+ borderStyle: hasBorder ? "solid" : "none",
3795
+ ...variantStyles,
3796
+ ...props.style || {}
3797
+ },
3798
+ children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", flexDirection: "row", alignItems: "center", justifyContent: "center", gap: spacing.gap, whiteSpace: "nowrap" }, children: [
3799
+ leadingIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex shrink-0 items-center justify-center", style: { width: iconSize, height: iconSize, lineHeight: 0, fontSize: iconSize }, children: leadingIcon }) : null,
3800
+ /* @__PURE__ */ jsx("span", { children }),
3801
+ trailingIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex shrink-0 items-center justify-center", style: { width: iconSize, height: iconSize, lineHeight: 0, fontSize: iconSize }, children: trailingIcon }) : null
3802
+ ] })
3803
+ }
3804
+ );
3805
+ }
3806
+ var JumpInput = forwardRef(function JumpInput2({ className = "", ...props }, ref) {
3807
+ const theme = useJumpyTheme();
3808
+ const fieldContext = useContext(JumpyFieldContext);
3809
+ const inputOffset = fieldContext?.hasLabel ? spacingValue(theme, JumpySpacing.large) : spacingValue(theme, JumpySpacing.zero);
3810
+ const inputMinHeight = fieldContext ? void 0 : sizeValue(theme, JumpySize.largeXL) - spacingValue(theme, JumpySpacing.small3XS) * 2;
3811
+ const inputElement = /* @__PURE__ */ jsx(
3812
+ "input",
3813
+ {
3814
+ ...props,
3815
+ ref,
3816
+ "data-jumpy-component": "input",
3817
+ className: cn("w-full text-sm disabled:cursor-not-allowed", className),
3818
+ style: {
3819
+ ...getFieldInputStyles(theme, props.disabled),
3820
+ minHeight: inputMinHeight,
3821
+ marginTop: inputOffset,
3822
+ ...props.style || {}
3823
+ }
3824
+ }
3825
+ );
3826
+ if (fieldContext) {
3827
+ return inputElement;
3828
+ }
3829
+ return renderStandaloneField(theme, props.disabled, "", null, inputElement, props.status || "regular", props.readOnly);
3830
+ });
3831
+ function JumpTextarea({ className = "", ...props }) {
3832
+ const theme = useJumpyTheme();
3833
+ const fieldContext = useContext(JumpyFieldContext);
3834
+ const inputOffset = fieldContext?.hasLabel ? spacingValue(theme, JumpySpacing.large) : spacingValue(theme, JumpySpacing.zero);
3835
+ const textareaElement = /* @__PURE__ */ jsx(
3836
+ "textarea",
3837
+ {
3838
+ ...props,
3839
+ "data-jumpy-component": "textarea",
3840
+ className: cn("w-full text-sm disabled:cursor-not-allowed", className),
3841
+ style: {
3842
+ ...getFieldInputStyles(theme, props.disabled),
3843
+ minHeight: props.rows ? Number(props.rows) * 24 : sizeValue(theme, JumpySize.large2XL),
3844
+ marginTop: inputOffset,
3845
+ resize: "none",
3846
+ ...props.style || {}
3847
+ }
3848
+ }
3849
+ );
3850
+ if (fieldContext) {
3851
+ return textareaElement;
3852
+ }
3853
+ return renderStandaloneField(theme, props.disabled, "", { alignItems: "flex-start", height: "auto", maxHeight: "none", paddingRight: spacingValue(theme, JumpySpacing.small3XS) }, textareaElement, props.status || "regular", props.readOnly);
3854
+ }
3855
+ function JumpBaseInput({ label, hint, children, className = "", error = false, hideClearIcon = false, showCharCount = false, onClear, ...props }) {
3856
+ const theme = useJumpyTheme();
3857
+ const fieldId = useId();
3858
+ const labelId = useId();
3859
+ const helperId = useId();
3860
+ const counterId = useId();
3861
+ const child = Children.only(children);
3862
+ const childValue = isValidElement(child) ? child.props.value ?? child.props.defaultValue ?? "" : "";
3863
+ const childHasValue = isValidElement(child) && (child.props["data-has-value"] === true || child.props["data-has-value"] === "true");
3864
+ const [focused, setFocused] = useState(false);
3865
+ const [hasValue, setHasValue] = useState(childHasValue || Boolean(String(childValue).length));
3866
+ const [inputValue, setInputValue] = useState(String(childValue ?? ""));
3867
+ const bodySmall = textStyleValue(theme, JumpyTextStyle.bodySmall);
3868
+ const bodyMedium = textStyleValue(theme, JumpyTextStyle.bodyMedium);
3869
+ const labelSmall = textStyleValue(theme, JumpyTextStyle.labelSmall);
3870
+ useEffect(() => {
3871
+ setHasValue(childHasValue || Boolean(String(childValue).length));
3872
+ setInputValue(String(childValue ?? ""));
3873
+ }, [childHasValue, childValue]);
3874
+ if (!isValidElement(child)) {
3875
+ return /* @__PURE__ */ jsx("div", { className: cn("block w-full", className), children });
3876
+ }
3877
+ const originalOnFocus = child.props.onFocus;
3878
+ const originalOnBlur = child.props.onBlur;
3879
+ const originalOnChange = child.props.onChange;
3880
+ const disabled = Boolean(child.props.disabled);
3881
+ const isDropdownTrigger = child.props["data-jumpy-dropdown-trigger"] === true || child.props["data-jumpy-dropdown-trigger"] === "true";
3882
+ const readOnly = Boolean(child.props.readOnly) && !isDropdownTrigger;
3883
+ const floating = focused && !readOnly || hasValue || Boolean(props.slots?.content);
3884
+ const isTextarea = child.type === JumpTextarea || child.props["data-jumpy-token-editor"] && child.props.multiline;
3885
+ const isDropdown = isDropdownTrigger || child.props["data-jumpy-component"] === "dropdown";
3886
+ const status = error ? "alarm" : props.status || "regular";
3887
+ const leading = props.slots?.leading ?? props.leading;
3888
+ const trailing = props.slots?.trailing ?? props.trailing;
3889
+ const content = props.slots?.content;
3890
+ const helperText = props.slots?.helperText ?? hint;
3891
+ const hasHelperText = Boolean(helperText);
3892
+ const maxLength = child.props.maxLength;
3893
+ const hasCounter = showCharCount && maxLength !== void 0 && maxLength !== null;
3894
+ const showClearButton = hasValue && !hideClearIcon && !disabled && !readOnly;
3895
+ const showErrorIcon = Boolean(error) && !disabled && !readOnly;
3896
+ const showTrailing = showClearButton || trailing || showErrorIcon;
3897
+ const describedBy = [child.props["aria-describedby"], hasHelperText ? helperId : void 0, hasCounter ? counterId : void 0].filter(Boolean).join(" ") || void 0;
3898
+ const labelScale = labelSmall.fontSize / bodyMedium.fontSize;
3899
+ const fieldInnerHeight = sizeValue(theme, JumpySize.largeXL) - spacingValue(theme, JumpySpacing.small3XS) * 2;
3900
+ const labelRestingY = fieldInnerHeight / 2;
3901
+ const labelFloatingY = (labelSmall.fontSize * labelSmall.lineHeight - labelSmall.fontSize) / 2;
3902
+ const labelTiming = focused && !readOnly ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease;
3903
+ const enhancedChild = cloneElement(child, {
3904
+ id: child.props.id || fieldId,
3905
+ "aria-labelledby": child.props["aria-labelledby"] || (!child.props["aria-label"] && label ? labelId : void 0),
3906
+ "aria-invalid": error ? true : child.props["aria-invalid"],
3907
+ "aria-describedby": describedBy,
3908
+ placeholder: !label || isDropdown || floating ? child.props.placeholder : " ",
3909
+ style: content ? {
3910
+ ...child.props.style || {},
3911
+ position: "absolute",
3912
+ inset: 0,
3913
+ zIndex: 2,
3914
+ width: "100%",
3915
+ height: "100%",
3916
+ margin: 0,
3917
+ opacity: 0,
3918
+ cursor: disabled || readOnly ? "default" : "pointer"
3919
+ } : child.props.style,
3920
+ onFocus: (event) => {
3921
+ setFocused(true);
3922
+ originalOnFocus?.(event);
3923
+ },
3924
+ onBlur: (event) => {
3925
+ setFocused(false);
3926
+ setHasValue(childHasValue || Boolean(String(event.target?.value ?? "").length));
3927
+ originalOnBlur?.(event);
3928
+ },
3929
+ onChange: (event) => {
3930
+ setHasValue(childHasValue || Boolean(String(event.target?.value ?? "").length));
3931
+ setInputValue(String(event.target?.value ?? ""));
3932
+ originalOnChange?.(event);
3933
+ }
3934
+ });
3935
+ const Container = label ? "label" : "div";
3936
+ const containerProps = label ? { htmlFor: child.props.id || fieldId } : {
3937
+ onClick: () => {
3938
+ if (!disabled) document.getElementById(child.props.id || fieldId)?.focus();
3939
+ }
3940
+ };
3941
+ return /* @__PURE__ */ jsxs("div", { "data-jumpy-component": "base-input-root", className: cn("inline-flex w-full max-w-full flex-col", className), style: { color: "var(--jumpy-cmp-base-input-helper-color)" }, children: [
3942
+ /* @__PURE__ */ jsx(
3943
+ Container,
3944
+ {
3945
+ ...containerProps,
3946
+ "data-jumpy-component": "base-input",
3947
+ "data-disabled": disabled ? "true" : "false",
3948
+ "data-readonly": readOnly ? "true" : "false",
3949
+ "data-status": status,
3950
+ style: { color: "var(--jumpy-cmp-base-input-helper-color)" },
3951
+ children: /* @__PURE__ */ jsxs(
3952
+ "div",
3953
+ {
3954
+ "data-jumpy-component": "field-control",
3955
+ "data-disabled": disabled ? "true" : "false",
3956
+ "data-readonly": readOnly ? "true" : "false",
3957
+ "data-status": status,
3958
+ "data-testid": props["data-testid"],
3959
+ onMouseDown: (event) => {
3960
+ const control = document.getElementById(child.props.id || fieldId);
3961
+ const interactive = event.target.closest("input, textarea, select, button, a, [tabindex]");
3962
+ if (disabled || !control || event.button !== 0 || interactive && event.currentTarget.contains(interactive)) return;
3963
+ event.preventDefault();
3964
+ control.focus();
3965
+ },
3966
+ style: {
3967
+ ...getFieldShellStyles(theme, disabled, status, focused, readOnly),
3968
+ alignItems: isTextarea ? "flex-start" : "center",
3969
+ position: "relative",
3970
+ height: isTextarea ? "auto" : sizeValue(theme, JumpySize.largeXL),
3971
+ minHeight: isTextarea ? sizeValue(theme, JumpySize.large2XL) : sizeValue(theme, JumpySize.largeXL)
3972
+ },
3973
+ children: [
3974
+ leading ? /* @__PURE__ */ jsx(
3975
+ "span",
3976
+ {
3977
+ "aria-hidden": "true",
3978
+ className: "inline-flex shrink-0 items-center justify-center",
3979
+ style: {
3980
+ width: sizeValue(theme, JumpySize.small),
3981
+ height: sizeValue(theme, JumpySize.small),
3982
+ color: "var(--jumpy-cmp-base-input-icon-fill)",
3983
+ transition: `color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${focused && !readOnly ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease}`
3984
+ },
3985
+ children: leading
3986
+ }
3987
+ ) : null,
3988
+ /* @__PURE__ */ jsxs(
3989
+ "div",
3990
+ {
3991
+ style: {
3992
+ position: "relative",
3993
+ display: "flex",
3994
+ alignItems: isTextarea ? "initial" : "center",
3995
+ alignSelf: "stretch",
3996
+ flex: 1,
3997
+ minWidth: 0,
3998
+ overflow: "hidden"
3999
+ },
4000
+ children: [
4001
+ label && /* @__PURE__ */ jsx(
4002
+ "span",
4003
+ {
4004
+ id: labelId,
4005
+ "data-testid": props.labelTestId,
4006
+ style: {
4007
+ position: "absolute",
4008
+ left: spacingValue(theme, JumpySpacing.zero),
4009
+ top: spacingValue(theme, JumpySpacing.zero),
4010
+ transform: floating ? `translateY(${labelFloatingY}px) scale(${labelScale})` : `translateY(calc(${labelRestingY}px - 50%)) scale(1)`,
4011
+ transformOrigin: "left top",
4012
+ color: "var(--jumpy-cmp-base-input-label-color)",
4013
+ fontFamily: theme.fontFamily.familyBase,
4014
+ fontSize: bodyMedium.fontSize,
4015
+ lineHeight: floating ? labelSmall.lineHeight : bodyMedium.lineHeight,
4016
+ fontWeight: floating ? labelSmall.fontWeight : bodyMedium.fontWeight,
4017
+ letterSpacing: floating ? labelSmall.letterSpacing : bodyMedium.letterSpacing,
4018
+ pointerEvents: "none",
4019
+ transition: `transform ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${labelTiming}, color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${labelTiming}`,
4020
+ maxWidth: "100%",
4021
+ overflow: "hidden",
4022
+ textOverflow: "ellipsis",
4023
+ whiteSpace: "nowrap"
4024
+ },
4025
+ children: label
4026
+ }
4027
+ ),
4028
+ /* @__PURE__ */ jsx(JumpyFieldContext.Provider, { value: { fieldId, hasLabel: Boolean(label), floating }, children: enhancedChild }),
4029
+ content ? /* @__PURE__ */ jsx(
4030
+ "div",
4031
+ {
4032
+ "data-jumpy-slot": "content",
4033
+ style: {
4034
+ width: "100%",
4035
+ minWidth: 0,
4036
+ paddingTop: label ? spacingValue(theme, JumpySpacing.large) : spacingValue(theme, JumpySpacing.zero),
4037
+ color: "var(--jumpy-cmp-base-input-value-color)",
4038
+ fontFamily: theme.fontFamily.familyBase,
4039
+ fontSize: bodyMedium.fontSize,
4040
+ lineHeight: `${bodyMedium.fontSize * bodyMedium.lineHeight}px`,
4041
+ overflow: "hidden",
4042
+ textOverflow: "ellipsis",
4043
+ whiteSpace: "nowrap"
4044
+ },
4045
+ children: content
4046
+ }
4047
+ ) : null
4048
+ ]
4049
+ }
4050
+ ),
4051
+ showTrailing ? /* @__PURE__ */ jsxs(
4052
+ "span",
4053
+ {
4054
+ "data-jumpy-slot": "trailing-container",
4055
+ className: "inline-flex shrink-0 items-center justify-center",
4056
+ style: {
4057
+ display: "inline-flex",
4058
+ alignItems: isTextarea ? "flex-start" : "center",
4059
+ justifyContent: "center",
4060
+ alignSelf: isTextarea ? "flex-start" : void 0,
4061
+ minHeight: isTextarea ? sizeValue(theme, JumpySize.large) : void 0,
4062
+ flexShrink: 0,
4063
+ gap: spacingValue(theme, JumpySpacing.smallXS),
4064
+ color: "var(--jumpy-cmp-base-input-trailing-color)",
4065
+ transition: `color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${focused && !readOnly ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease}`
4066
+ },
4067
+ children: [
4068
+ showClearButton ? /* @__PURE__ */ jsx(
4069
+ JumpIconButton,
4070
+ {
4071
+ variant: "secondary",
4072
+ "aria-label": "Clear",
4073
+ "data-jumpy-slot": "clear-button",
4074
+ onClick: (event) => {
4075
+ event.preventDefault();
4076
+ event.stopPropagation();
4077
+ const input = document.getElementById(child.props.id || fieldId);
4078
+ if (input && child.props.value === void 0) input.value = "";
4079
+ setHasValue(false);
4080
+ setInputValue("");
4081
+ originalOnChange?.({ target: { value: "" }, currentTarget: { value: "" } });
4082
+ onClear?.();
4083
+ input?.focus();
4084
+ },
4085
+ style: {
4086
+ width: sizeValue(theme, JumpySize.large),
4087
+ height: sizeValue(theme, JumpySize.large),
4088
+ minWidth: sizeValue(theme, JumpySize.large),
4089
+ minHeight: sizeValue(theme, JumpySize.large),
4090
+ padding: spacingValue(theme, JumpySpacing.small2XS),
4091
+ borderRadius: radiusValue(theme, JumpyRadius.small)
4092
+ },
4093
+ children: /* @__PURE__ */ jsx(CircleClearIcon, {})
4094
+ }
4095
+ ) : null,
4096
+ showErrorIcon || trailing ? /* @__PURE__ */ jsxs("span", { "data-jumpy-slot": "trailing", "aria-hidden": "true", style: { display: "inline-flex", alignItems: "center", justifyContent: "center", gap: spacingValue(theme, JumpySpacing.smallXS), paddingRight: spacingValue(theme, JumpySpacing.medium), color: "var(--jumpy-cmp-base-input-trailing-color)", fontSize: bodyMedium.fontSize, lineHeight: bodyMedium.lineHeight }, children: [
4097
+ showErrorIcon ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "trailing-error", style: { display: "inline-flex", alignItems: "center", color: "var(--jumpy-cmp-base-input-trailing-error-fill, var(--jumpy-cmp-base-input-icon-fill))", lineHeight: 1 }, children: /* @__PURE__ */ jsx(CircleErrorIcon, { style: { width: sizeValue(theme, JumpySize.small2XS), height: sizeValue(theme, JumpySize.small2XS) } }) }) : null,
4098
+ trailing ? /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "trailing-text", style: { display: "inline-flex", alignItems: "center", maxWidth: `calc(30 * ${spacingValue(theme, JumpySpacing.smallXS)}px)`, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", lineHeight: 1 }, children: trailing }) : null
4099
+ ] }) : null
4100
+ ]
4101
+ }
4102
+ ) : null
4103
+ ]
4104
+ }
4105
+ )
4106
+ }
4107
+ ),
4108
+ (hasHelperText || hasCounter) && /* @__PURE__ */ jsxs(
4109
+ "div",
4110
+ {
4111
+ className: "flex justify-end",
4112
+ style: {
4113
+ gap: spacingValue(theme, JumpySpacing.smallXS),
4114
+ paddingTop: spacingValue(theme, JumpySpacing.small3XS),
4115
+ paddingLeft: spacingValue(theme, JumpySpacing.large),
4116
+ paddingRight: spacingValue(theme, JumpySpacing.large),
4117
+ color: "var(--jumpy-cmp-base-input-helper-color)",
4118
+ fontSize: bodySmall.fontSize,
4119
+ lineHeight: `${bodySmall.fontSize * bodySmall.lineHeight}px`,
4120
+ transition: `color ${animationDurationValue(theme, JumpyAnimationDuration.smallXS)}s ${focused && !readOnly ? theme.animation.timeEasingImpulse : theme.animation.timeEasingRelease}`
4121
+ },
4122
+ children: [
4123
+ hasHelperText ? /* @__PURE__ */ jsx("span", { id: helperId, style: { flex: 1 }, children: helperText }) : null,
4124
+ hasCounter ? /* @__PURE__ */ jsxs("span", { id: counterId, style: { flex: 0, whiteSpace: "nowrap" }, children: [
4125
+ inputValue.length,
4126
+ "/",
4127
+ maxLength
4128
+ ] }) : null
4129
+ ]
4130
+ }
4131
+ )
4132
+ ] });
4133
+ }
4134
+ function JumpField(props) {
4135
+ return /* @__PURE__ */ jsx(JumpBaseInput, { ...props });
4136
+ }
4137
+ function JumpChoice({ selected = false, title, description, className = "", ...props }) {
4138
+ const theme = useJumpyTheme();
4139
+ return /* @__PURE__ */ jsxs(
4140
+ "button",
4141
+ {
4142
+ type: "button",
4143
+ ...props,
4144
+ "data-jumpy-component": "choice",
4145
+ className: cn("w-full p-4 text-left transition active:scale-[0.99] disabled:cursor-not-allowed", className),
4146
+ style: {
4147
+ borderRadius: theme.sizing.choiceRadius,
4148
+ border: `1px solid ${selected ? theme.color.surfacePrimary : theme.color.borderSubtle}`,
4149
+ backgroundColor: selected ? theme.color.infoSurface : theme.color.surfaceCard,
4150
+ color: theme.color.textPrimary,
4151
+ opacity: props.disabled ? theme.opacity.disabled : 1
4152
+ },
4153
+ children: [
4154
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-bold", children: title }),
4155
+ description && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs leading-5", style: { color: theme.color.textSecondary }, children: description })
4156
+ ]
4157
+ }
4158
+ );
4159
+ }
4160
+ function JumpNotice({ tone = "info", title, children, className = "" }) {
4161
+ const theme = useJumpyTheme();
4162
+ const toneStyles = getNoticeToneStyles(theme, tone);
4163
+ return /* @__PURE__ */ jsxs("div", { className: cn("p-4", className), style: { borderRadius: theme.sizing.choiceRadius, border: `1px solid ${toneStyles.borderColor}`, backgroundColor: toneStyles.backgroundColor, color: toneStyles.color }, children: [
4164
+ title && /* @__PURE__ */ jsx("p", { className: "text-sm font-bold", children: title }),
4165
+ /* @__PURE__ */ jsx("div", { className: cn("text-xs leading-5", title && "mt-1"), children })
4166
+ ] });
4167
+ }
4168
+ function JumpTabs({ items, value, onChange, className = "" }) {
4169
+ const scrollRef = useRef(null);
4170
+ const indicatorRef = useRef(null);
4171
+ const tabRefs = useRef(/* @__PURE__ */ new Map());
4172
+ const placeIndicatorRef = useRef(null);
4173
+ placeIndicatorRef.current = (animate) => {
4174
+ const selectedTab = tabRefs.current.get(value);
4175
+ const scrollContainer = scrollRef.current;
4176
+ const indicator = indicatorRef.current;
4177
+ if (!selectedTab || !scrollContainer || !indicator) return;
4178
+ const tabBounds = selectedTab.getBoundingClientRect();
4179
+ const containerBounds = scrollContainer.getBoundingClientRect();
4180
+ const tabStyles = getComputedStyle(selectedTab);
4181
+ const content = selectedTab.firstElementChild;
4182
+ const contentStyles = content ? getComputedStyle(content) : null;
4183
+ const insetLeft = Number.parseFloat(tabStyles.paddingLeft) + (contentStyles ? Number.parseFloat(contentStyles.paddingLeft) : 0);
4184
+ const insetRight = Number.parseFloat(tabStyles.paddingRight) + (contentStyles ? Number.parseFloat(contentStyles.paddingRight) : 0);
4185
+ const x = tabBounds.left - containerBounds.left + scrollContainer.scrollLeft + insetLeft;
4186
+ const width = tabBounds.width - insetLeft - insetRight;
4187
+ if (!animate) indicator.style.transition = "none";
4188
+ indicator.style.setProperty("--jumpy-cmp-tabs-indicator-x", `${x}px`);
4189
+ indicator.style.setProperty("--jumpy-cmp-tabs-indicator-width", `${width}px`);
4190
+ indicator.style.setProperty("--jumpy-cmp-tabs-indicator-opacity", "1");
4191
+ if (!animate) {
4192
+ indicator.getBoundingClientRect();
4193
+ indicator.style.transition = "";
4194
+ }
4195
+ };
4196
+ const indicatorPlacedRef = useRef(false);
4197
+ useLayoutEffect(() => {
4198
+ placeIndicatorRef.current(indicatorPlacedRef.current);
4199
+ indicatorPlacedRef.current = true;
4200
+ }, [items, value]);
4201
+ useEffect(() => {
4202
+ const scrollContainer = scrollRef.current;
4203
+ if (!scrollContainer || typeof ResizeObserver === "undefined") return void 0;
4204
+ const sizes = /* @__PURE__ */ new WeakMap();
4205
+ const observer = new ResizeObserver((entries) => {
4206
+ let resized = false;
4207
+ entries.forEach(({ target, contentRect }) => {
4208
+ const size2 = `${contentRect.width}x${contentRect.height}`;
4209
+ if (sizes.has(target) && sizes.get(target) !== size2) resized = true;
4210
+ sizes.set(target, size2);
4211
+ });
4212
+ if (resized) placeIndicatorRef.current(false);
4213
+ });
4214
+ observer.observe(scrollContainer);
4215
+ tabRefs.current.forEach((tab) => observer.observe(tab));
4216
+ return () => observer.disconnect();
4217
+ }, [items]);
4218
+ return /* @__PURE__ */ jsx("div", { className, "data-jumpy-component": "tabs", children: /* @__PURE__ */ jsxs("div", { ref: scrollRef, "data-jumpy-slot": "tabs-scroll-container", role: "tablist", "aria-orientation": "horizontal", tabIndex: -1, children: [
4219
+ items.map((item) => {
4220
+ const active = item.key === value;
4221
+ return /* @__PURE__ */ jsx(
4222
+ "button",
4223
+ {
4224
+ ref: (node) => {
4225
+ if (node) tabRefs.current.set(item.key, node);
4226
+ else tabRefs.current.delete(item.key);
4227
+ },
4228
+ type: "button",
4229
+ role: "tab",
4230
+ "aria-selected": active,
4231
+ onClick: (event) => {
4232
+ onChange(item.key);
4233
+ event.currentTarget.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
4234
+ },
4235
+ "data-jumpy-component": "tab",
4236
+ children: /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "tab-content", children: /* @__PURE__ */ jsx("span", { "data-jumpy-slot": "tab-label", "data-text": item.label, children: item.label }) })
4237
+ },
4238
+ item.key
4239
+ );
4240
+ }),
4241
+ /* @__PURE__ */ jsx(
4242
+ "div",
4243
+ {
4244
+ ref: indicatorRef,
4245
+ "aria-hidden": "true",
4246
+ "data-jumpy-slot": "tabs-indicator"
4247
+ }
4248
+ )
4249
+ ] }) });
4250
+ }
4251
+ function JumpModal({ children }) {
4252
+ return /* @__PURE__ */ jsx(JumpDialog, { visible: true, children });
4253
+ }
4254
+
4255
+ export {
4256
+ JumpyFieldContext,
4257
+ KeyboardKeys,
4258
+ JumpBackground,
4259
+ JumpBadge,
4260
+ BrandMark,
4261
+ JumpCard,
4262
+ JumpGrid,
4263
+ JumpGridItem,
4264
+ JumpList,
4265
+ JumpListItem,
4266
+ JumpSideMenu,
4267
+ JumpSideMenuItem,
4268
+ JumpTypography,
4269
+ JumpHyperlink,
4270
+ JumpDivider,
4271
+ JumpAvatar,
4272
+ JumpBanner,
4273
+ JumpSkeletonLoader,
4274
+ JumpSearchField,
4275
+ JumpTooltip,
4276
+ JumpPopover,
4277
+ JumpMenu,
4278
+ JumpMenuItem,
4279
+ JumpToggleButton,
4280
+ JumpToggleButtonGroup,
4281
+ JumpPinInput,
4282
+ JumpPageControl,
4283
+ useJumpSnackbar,
4284
+ JumpSnackbarProvider,
4285
+ JumpTable,
4286
+ JumpTableHead,
4287
+ JumpTableBody,
4288
+ JumpTableFooter,
4289
+ JumpTableRow,
4290
+ JumpTableCell,
4291
+ getSortMapping,
4292
+ getNextSortOrder,
4293
+ getSortOrder,
4294
+ getSortActionLabel,
4295
+ isRowFullySelected,
4296
+ applySubtreeSelection,
4297
+ getRowCheckboxProps,
4298
+ getHeaderCheckboxProps,
4299
+ JumpTableHeaderCell,
4300
+ JumpExpandable,
4301
+ JumpExpandableGroup,
4302
+ JumpSheet,
4303
+ JumpTopBar,
4304
+ JumpTabBar,
4305
+ JumpTabBarItem,
4306
+ JumpAppShell,
4307
+ JumpSectionHeader,
4308
+ JumpSectionFooter,
4309
+ JumpTag,
4310
+ JumpChip,
4311
+ JumpCheckbox,
4312
+ JumpSwitch,
4313
+ JumpRadioGroup,
4314
+ JumpDatePicker,
4315
+ JumpTimePicker,
4316
+ JumpDropdown,
4317
+ JumpSpinner,
4318
+ JumpDialog,
4319
+ JumpLoader,
4320
+ JumpButton,
4321
+ JumpIconButton,
4322
+ JumpLinkButton,
4323
+ JumpInput,
4324
+ JumpTextarea,
4325
+ JumpBaseInput,
4326
+ JumpField,
4327
+ JumpChoice,
4328
+ JumpNotice,
4329
+ JumpTabs,
4330
+ JumpModal
4331
+ };