@pk-design/react-tailwind 1.5.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react-tailwind.cjs.js +1429 -289
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +1420 -292
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/BodyText/BodyText.d.ts +1 -1
- package/dist/types/components/BodyText/BodyText.types.d.ts +47 -8
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +2 -3
- package/dist/types/components/Breadcrumb/Breadcrumb.types.d.ts +43 -10
- package/dist/types/components/Button/Button.types.d.ts +17 -63
- package/dist/types/components/ButtonGroup/ButtonGroup.types.d.ts +16 -0
- package/dist/types/components/Card/Card.d.ts +7 -1
- package/dist/types/components/Card/Card.types.d.ts +48 -42
- package/dist/types/components/Checkbox/Checkbox.types.d.ts +28 -1
- package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +53 -1
- package/dist/types/components/DetailedInformation/DetailedInformation.d.ts +1 -1
- package/dist/types/components/DetailedInformation/DetailedInformation.types.d.ts +47 -3
- package/dist/types/components/Drawer/Drawer.types.d.ts +37 -13
- package/dist/types/components/Dropdown/Dropdown.types.d.ts +17 -1
- package/dist/types/components/HeadingText/HeadingText.d.ts +7 -7
- package/dist/types/components/HeadingText/HeadingText.test.d.ts +1 -0
- package/dist/types/components/HeadingText/HeadingText.types.d.ts +56 -3
- package/dist/types/components/Input/Input.d.ts +4 -2
- package/dist/types/components/Input/Input.types.d.ts +36 -3
- package/dist/types/components/Loader/index.d.ts +1 -1
- package/dist/types/components/PasswordInput/PasswordInput.d.ts +8 -4
- package/dist/types/components/PasswordInput/PasswordInput.types.d.ts +8 -0
- package/dist/types/components/QuantityStepper/QuantityStepper.d.ts +3 -0
- package/dist/types/components/QuantityStepper/QuantityStepper.test.d.ts +1 -0
- package/dist/types/components/QuantityStepper/QuantityStepper.types.d.ts +49 -0
- package/dist/types/components/QuantityStepper/index.d.ts +2 -0
- package/dist/types/components/Radio/Radio.d.ts +1 -1
- package/dist/types/components/Radio/Radio.types.d.ts +12 -2
- package/dist/types/components/RadioGroup/RadioGroup.d.ts +1 -1
- package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +41 -13
- package/dist/types/components/RadioSwitch/RadioSwitch.d.ts +0 -4
- package/dist/types/components/RadioSwitch/RadioSwitch.types.d.ts +6 -31
- package/dist/types/components/RangeInput/RangeInput.d.ts +1 -1
- package/dist/types/components/RangeInput/RangeInput.types.d.ts +14 -1
- package/dist/types/components/RangeSlider/RangeSlider.d.ts +1 -1
- package/dist/types/components/RangeSlider/RangeSlider.types.d.ts +28 -13
- package/dist/types/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/dist/types/components/SegmentedControl/SegmentedControl.test.d.ts +1 -0
- package/dist/types/components/SegmentedControl/SegmentedControl.types.d.ts +53 -0
- package/dist/types/components/SegmentedControl/index.d.ts +2 -0
- package/dist/types/components/SelectBox/SelectBox.types.d.ts +23 -14
- package/dist/types/components/Skeleton/Skeleton.d.ts +2 -0
- package/dist/types/components/Skeleton/Skeleton.test.d.ts +1 -0
- package/dist/types/components/Skeleton/Skeleton.types.d.ts +38 -0
- package/dist/types/components/Skeleton/index.d.ts +2 -0
- package/dist/types/components/StatusPill/StatusPill.d.ts +2 -0
- package/dist/types/components/StatusPill/StatusPill.test.d.ts +1 -0
- package/dist/types/components/StatusPill/StatusPill.types.d.ts +29 -0
- package/dist/types/components/StatusPill/index.d.ts +2 -0
- package/dist/types/components/StickyActionBar/StickyActionBar.d.ts +2 -0
- package/dist/types/components/StickyActionBar/StickyActionBar.test.d.ts +1 -0
- package/dist/types/components/StickyActionBar/StickyActionBar.types.d.ts +30 -0
- package/dist/types/components/StickyActionBar/index.d.ts +2 -0
- package/dist/types/components/TextContent/TextContent.d.ts +2 -2
- package/dist/types/components/TextContent/TextContent.types.d.ts +47 -7
- package/dist/types/components/Textarea/Textarea.types.d.ts +37 -3
- package/dist/types/components/Toast/Toast.d.ts +1 -1
- package/dist/types/components/Toast/Toast.types.d.ts +117 -43
- package/dist/types/components/Toast/ToastProvider.d.ts +3 -6
- package/dist/types/index.d.ts +5 -0
- package/package.json +1 -1
|
@@ -214,15 +214,46 @@ const loaderSizeMap = {
|
|
|
214
214
|
lg: 'size-10',
|
|
215
215
|
};
|
|
216
216
|
const Loader = ({ className = '', size = 'md', ...props }) => {
|
|
217
|
-
|
|
217
|
+
const hasColorClass = /\btext-/.test(className);
|
|
218
|
+
return (jsxRuntime.jsx("svg", { className: buildClassName(!hasColorClass && 'text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsxRuntime.jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
|
|
218
219
|
};
|
|
219
220
|
|
|
221
|
+
const sizeClassMap$2 = {
|
|
222
|
+
xs: 'text-xs/5',
|
|
223
|
+
sm: 'text-sm/6',
|
|
224
|
+
md: 'text-base/7',
|
|
225
|
+
lg: 'text-lg/8',
|
|
226
|
+
xl: 'text-xl/9',
|
|
227
|
+
};
|
|
228
|
+
const weightClassMap$2 = {
|
|
229
|
+
light: 'font-light',
|
|
230
|
+
normal: 'font-normal',
|
|
231
|
+
medium: 'font-medium',
|
|
232
|
+
semibold: 'font-semibold',
|
|
233
|
+
bold: 'font-bold',
|
|
234
|
+
};
|
|
220
235
|
const TextContent = React.forwardRef((props, ref) => {
|
|
221
|
-
const { xsmall = false, small = false, strong = false, muted = false, error = false, className, ...rest } = props;
|
|
222
|
-
|
|
223
|
-
|
|
236
|
+
const { as = 'span', size, weight, xsmall = false, small = false, strong = false, muted = false, error = false, success = false, warning = false, info = false, invert = false, monospace = false, truncate = false, className, children, ...rest } = props;
|
|
237
|
+
const textColorClass = error
|
|
238
|
+
? 'text-[var(--ui-text-danger)]'
|
|
239
|
+
: success
|
|
240
|
+
? 'text-[var(--ui-success)]'
|
|
241
|
+
: warning
|
|
242
|
+
? 'text-[var(--ui-warning)]'
|
|
243
|
+
: info
|
|
244
|
+
? 'text-[var(--ui-info)]'
|
|
245
|
+
: muted
|
|
246
|
+
? invert
|
|
247
|
+
? 'text-gray-300 dark:text-gray-600'
|
|
248
|
+
: 'text-[var(--ui-text-muted)]'
|
|
249
|
+
: invert
|
|
250
|
+
? 'text-white dark:text-black'
|
|
251
|
+
: 'text-[var(--ui-text)]';
|
|
252
|
+
return React.createElement(as, {
|
|
224
253
|
ref,
|
|
225
|
-
className: buildClassName(
|
|
254
|
+
className: buildClassName(textColorClass, size ? sizeClassMap$2[size] : xsmall ? 'text-xs/5' : small ? 'text-sm/6' : undefined, weight ? weightClassMap$2[weight] : strong ? 'font-semibold' : undefined, monospace && 'font-mono', truncate && 'truncate inline-block max-w-full', className),
|
|
255
|
+
...rest,
|
|
256
|
+
children,
|
|
226
257
|
});
|
|
227
258
|
});
|
|
228
259
|
TextContent.displayName = 'TextContent';
|
|
@@ -232,19 +263,29 @@ TextContent.displayName = 'TextContent';
|
|
|
232
263
|
*/
|
|
233
264
|
const THEMED_BTN_CLASS = {
|
|
234
265
|
primary: {
|
|
235
|
-
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus:
|
|
236
|
-
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus:
|
|
237
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus:
|
|
266
|
+
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:bg-[var(--ui-primary-disabled)]',
|
|
267
|
+
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)] disabled:border-[var(--ui-primary-disabled)]',
|
|
268
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
|
|
238
269
|
},
|
|
239
270
|
danger: {
|
|
240
|
-
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus:
|
|
241
|
-
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus:
|
|
242
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus:
|
|
271
|
+
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:bg-[var(--ui-danger-disabled)]',
|
|
272
|
+
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)] disabled:border-[var(--ui-danger-disabled)]',
|
|
273
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
|
|
243
274
|
},
|
|
244
275
|
secondary: {
|
|
245
|
-
default: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 focus:
|
|
246
|
-
outlined: 'text-gray-700 hover:text-white border border-gray-500 hover:bg-gray-700 dark:border-gray-500 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus:
|
|
247
|
-
plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus:
|
|
276
|
+
default: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:bg-gray-200 dark:disabled:bg-gray-700',
|
|
277
|
+
outlined: 'text-gray-700 hover:text-white border border-gray-500 hover:bg-gray-700 dark:border-gray-500 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400 disabled:border-gray-400',
|
|
278
|
+
plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400',
|
|
279
|
+
},
|
|
280
|
+
success: {
|
|
281
|
+
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:bg-emerald-300 dark:disabled:bg-emerald-900',
|
|
282
|
+
outlined: 'text-emerald-600 hover:text-white border border-emerald-600 hover:bg-emerald-600 dark:text-emerald-400 dark:border-emerald-500 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:opacity-50',
|
|
283
|
+
plain: 'text-emerald-600 hover:bg-emerald-50 dark:text-emerald-400 dark:hover:bg-emerald-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:opacity-50',
|
|
284
|
+
},
|
|
285
|
+
warning: {
|
|
286
|
+
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:bg-amber-300 dark:disabled:bg-amber-900',
|
|
287
|
+
outlined: 'text-amber-600 hover:text-white border border-amber-500 hover:bg-amber-500 dark:text-amber-400 dark:border-amber-500 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:opacity-50',
|
|
288
|
+
plain: 'text-amber-600 hover:bg-amber-50 dark:text-amber-400 dark:hover:bg-amber-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:opacity-50',
|
|
248
289
|
},
|
|
249
290
|
};
|
|
250
291
|
const LOADER_TEXT_CLASS = {
|
|
@@ -263,6 +304,16 @@ const LOADER_TEXT_CLASS = {
|
|
|
263
304
|
outlined: 'text-gray-700 dark:text-gray-200',
|
|
264
305
|
plain: 'text-gray-700 dark:text-gray-200',
|
|
265
306
|
},
|
|
307
|
+
success: {
|
|
308
|
+
default: 'text-white',
|
|
309
|
+
outlined: 'text-emerald-600 dark:text-emerald-400',
|
|
310
|
+
plain: 'text-emerald-600 dark:text-emerald-400',
|
|
311
|
+
},
|
|
312
|
+
warning: {
|
|
313
|
+
default: 'text-white',
|
|
314
|
+
outlined: 'text-amber-600 dark:text-amber-400',
|
|
315
|
+
plain: 'text-amber-600 dark:text-amber-400',
|
|
316
|
+
},
|
|
266
317
|
};
|
|
267
318
|
/**
|
|
268
319
|
* Generate size classes.
|
|
@@ -270,13 +321,15 @@ const LOADER_TEXT_CLASS = {
|
|
|
270
321
|
const getBtnSizeClasses = (size, iconOnly = false) => {
|
|
271
322
|
switch (size) {
|
|
272
323
|
case 'xs':
|
|
273
|
-
return iconOnly ? 'p-1 rounded-sm text-xs' : 'px-2
|
|
324
|
+
return iconOnly ? 'h-7 w-7 p-1 rounded-sm text-xs' : 'h-7 px-2.5 rounded-sm gap-1 text-xs';
|
|
274
325
|
case 'sm':
|
|
275
|
-
return iconOnly ? 'p-1.5 rounded text-sm' : 'px-3
|
|
326
|
+
return iconOnly ? 'h-8 w-8 p-1.5 rounded text-sm' : 'h-8 px-3 rounded gap-1.5 text-sm';
|
|
276
327
|
case 'md':
|
|
277
|
-
return iconOnly ? 'p-2 rounded-md text-sm' : 'px-4
|
|
328
|
+
return iconOnly ? 'h-9 w-9 p-2 rounded-md text-sm' : 'h-9 px-4 rounded-md gap-2 text-sm';
|
|
278
329
|
case 'lg':
|
|
279
|
-
return iconOnly
|
|
330
|
+
return iconOnly
|
|
331
|
+
? 'h-10 w-10 p-2.5 rounded-lg text-base'
|
|
332
|
+
: 'h-10 px-5 rounded-lg gap-2 text-base';
|
|
280
333
|
}
|
|
281
334
|
};
|
|
282
335
|
/**
|
|
@@ -284,15 +337,14 @@ const getBtnSizeClasses = (size, iconOnly = false) => {
|
|
|
284
337
|
*/
|
|
285
338
|
const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
|
|
286
339
|
'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
|
|
287
|
-
'transition duration-
|
|
340
|
+
'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
|
|
288
341
|
const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
289
|
-
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
|
|
342
|
+
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
|
|
290
343
|
const Component = as || 'button';
|
|
291
344
|
// Accessibility guard for icon-only buttons
|
|
292
345
|
if (iconOnly && !restProps['aria-label']) {
|
|
293
346
|
console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
|
|
294
347
|
}
|
|
295
|
-
// Disable interaction when loading
|
|
296
348
|
const resolvedDisabled = disabled || loading;
|
|
297
349
|
// Clean invalid props when not rendering a real button
|
|
298
350
|
const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
|
|
@@ -302,18 +354,15 @@ const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
|
302
354
|
disabled: resolvedDisabled,
|
|
303
355
|
}
|
|
304
356
|
: {};
|
|
357
|
+
const classes = React.useMemo(() => buildClassName(BASE_BTN_CLASS, THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), !noOutlineOnFocus && 'focus:outline focus:outline-2 focus:outline-offset-2', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), [theme, variant, size, iconOnly, rounded, fullWidth, noOutlineOnFocus, className]);
|
|
305
358
|
/**
|
|
306
|
-
*
|
|
307
|
-
*/
|
|
308
|
-
const classes = React.useMemo(() => buildClassName(BASE_BTN_CLASS, THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), !noOutlineOnFocus && 'focus:outline focus:outline-2 focus:outline-offset-2', rounded && 'rounded-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), [theme, variant, size, iconOnly, rounded, noOutlineOnFocus, className]);
|
|
309
|
-
/**
|
|
310
|
-
* SPECIAL CASE:
|
|
311
|
-
* iconOnly + loading → show spinner only
|
|
359
|
+
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
312
360
|
*/
|
|
313
361
|
if (loading && iconOnly) {
|
|
314
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
|
|
362
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }) }));
|
|
315
363
|
}
|
|
316
|
-
|
|
364
|
+
const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }));
|
|
365
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading && !loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, children] })) : loading && loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, jsxRuntime.jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: rightIcon })] })) }));
|
|
317
366
|
});
|
|
318
367
|
Button.displayName = 'Button';
|
|
319
368
|
|
|
@@ -344,13 +393,13 @@ const ThemeClassMap = {
|
|
|
344
393
|
info: 'bg-[var(--ui-info-light)] text-[var(--ui-info-text)] ring-[var(--ui-info-ring-subtle)]',
|
|
345
394
|
secondary: 'bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 ring-[var(--ui-secondary-ring-subtle)]',
|
|
346
395
|
};
|
|
347
|
-
const SIZE_CLASS = {
|
|
396
|
+
const SIZE_CLASS$1 = {
|
|
348
397
|
sm: 'text-xs py-0.5 px-2',
|
|
349
398
|
md: 'text-xs py-1 px-3',
|
|
350
399
|
};
|
|
351
400
|
const Badge = React.forwardRef((props, ref) => {
|
|
352
401
|
const { theme = 'info', className, children, removable = false, rounded = false, onRemove, size = 'md', icon, ...restProps } = props;
|
|
353
|
-
return (jsxRuntime.jsxs(TextContent, { ref: ref, className: buildClassName('inline-flex items-center gap-1 font-medium ring-1 ring-inset', SIZE_CLASS[size], ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md', icon ? 'pl-2' : '', removable ? 'pr-1' : '', className), "data-testid": `badge-${theme}`, ...restProps, children: [icon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsxRuntime.jsx(SvgX, { className: "size-4 shrink-0 hover:bg-black/10 dark:hover:bg-white/10 rounded-sm cursor-pointer", role: "button", tabIndex: 0, "aria-label": "Remove", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), onKeyDown: (e) => {
|
|
402
|
+
return (jsxRuntime.jsxs(TextContent, { ref: ref, className: buildClassName('inline-flex items-center gap-1 font-medium ring-1 ring-inset', SIZE_CLASS$1[size], ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md', icon ? 'pl-2' : '', removable ? 'pr-1' : '', className), "data-testid": `badge-${theme}`, ...restProps, children: [icon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsxRuntime.jsx(SvgX, { className: "size-4 shrink-0 hover:bg-black/10 dark:hover:bg-white/10 rounded-sm cursor-pointer", role: "button", tabIndex: 0, "aria-label": "Remove", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), onKeyDown: (e) => {
|
|
354
403
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
355
404
|
e.preventDefault();
|
|
356
405
|
onRemove === null || onRemove === void 0 ? void 0 : onRemove();
|
|
@@ -428,21 +477,54 @@ const Banner = React.forwardRef((props, ref) => {
|
|
|
428
477
|
});
|
|
429
478
|
Banner.displayName = 'Banner';
|
|
430
479
|
|
|
480
|
+
const sizeClassMap$1 = {
|
|
481
|
+
xs: 'text-xs/5',
|
|
482
|
+
sm: 'text-sm/6',
|
|
483
|
+
md: 'text-base/7',
|
|
484
|
+
lg: 'text-lg/8',
|
|
485
|
+
xl: 'text-xl/9',
|
|
486
|
+
};
|
|
487
|
+
const weightClassMap$1 = {
|
|
488
|
+
light: 'font-light',
|
|
489
|
+
normal: 'font-normal',
|
|
490
|
+
medium: 'font-medium',
|
|
491
|
+
semibold: 'font-semibold',
|
|
492
|
+
bold: 'font-bold',
|
|
493
|
+
};
|
|
494
|
+
const alignClassMap$1 = {
|
|
495
|
+
left: 'text-left',
|
|
496
|
+
center: 'text-center',
|
|
497
|
+
right: 'text-right',
|
|
498
|
+
justify: 'text-justify',
|
|
499
|
+
};
|
|
500
|
+
const clampClassMap = {
|
|
501
|
+
1: 'line-clamp-1',
|
|
502
|
+
2: 'line-clamp-2',
|
|
503
|
+
3: 'line-clamp-3',
|
|
504
|
+
4: 'line-clamp-4',
|
|
505
|
+
};
|
|
431
506
|
const BodyText = React.forwardRef((props, ref) => {
|
|
432
|
-
const { as = 'p', small = false, strong = false, muted = false, error = false, inline = false, className,
|
|
507
|
+
const { as = 'p', size, weight, small = false, strong = false, muted = false, error = false, success = false, warning = false, info = false, inline = false, invert = false, align, truncate = false, clamp, className, children, ...rest } = props;
|
|
433
508
|
const textColorClass = error
|
|
434
509
|
? 'text-[var(--ui-text-danger)]'
|
|
435
|
-
:
|
|
436
|
-
?
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
510
|
+
: success
|
|
511
|
+
? 'text-[var(--ui-success)]'
|
|
512
|
+
: warning
|
|
513
|
+
? 'text-[var(--ui-warning)]'
|
|
514
|
+
: info
|
|
515
|
+
? 'text-[var(--ui-info)]'
|
|
516
|
+
: muted
|
|
517
|
+
? invert
|
|
518
|
+
? 'text-gray-300 dark:text-gray-600'
|
|
519
|
+
: 'text-[var(--ui-text-muted)]'
|
|
520
|
+
: invert
|
|
521
|
+
? 'text-white dark:text-black'
|
|
522
|
+
: 'text-[var(--ui-text)]';
|
|
442
523
|
return React.createElement(as, {
|
|
443
|
-
ref
|
|
444
|
-
className: buildClassName(textColorClass, small && 'text-sm/6', strong && 'font-semibold', inline && 'inline-flex items-center gap-2', className),
|
|
524
|
+
ref,
|
|
525
|
+
className: buildClassName(textColorClass, size ? sizeClassMap$1[size] : small && 'text-sm/6', weight ? weightClassMap$1[weight] : strong && 'font-semibold', align && alignClassMap$1[align], inline && 'inline-flex items-center gap-2', truncate && 'truncate', clamp && clampClassMap[clamp], className),
|
|
445
526
|
...rest,
|
|
527
|
+
children,
|
|
446
528
|
});
|
|
447
529
|
});
|
|
448
530
|
BodyText.displayName = 'BodyText';
|
|
@@ -466,24 +548,60 @@ var SvgSlash = function SvgSlash(props) {
|
|
|
466
548
|
})));
|
|
467
549
|
};
|
|
468
550
|
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
551
|
+
const SIZE_CONTAINER_CLASS$2 = {
|
|
552
|
+
sm: 'text-xs gap-1.5',
|
|
553
|
+
md: 'text-sm gap-2',
|
|
554
|
+
lg: 'text-base gap-2.5',
|
|
555
|
+
};
|
|
556
|
+
const SIZE_ICON_CLASS$1 = {
|
|
557
|
+
sm: 'size-3',
|
|
558
|
+
md: 'size-3.5',
|
|
559
|
+
lg: 'size-4',
|
|
473
560
|
};
|
|
561
|
+
function RenderSeparator({ separator, size, index, className, }) {
|
|
562
|
+
const testId = `breadcrumb-divider-${index}`;
|
|
563
|
+
if (separator === 'slash' || !separator) {
|
|
564
|
+
return (jsxRuntime.jsx(SvgSlash, { "data-testid": testId, className: buildClassName(SIZE_ICON_CLASS$1[size] || 'size-3.5', 'text-gray-400 dark:text-gray-500 shrink-0 select-none', className) }));
|
|
565
|
+
}
|
|
566
|
+
if (separator === 'chevron') {
|
|
567
|
+
return (jsxRuntime.jsx("svg", { "data-testid": testId, className: buildClassName(SIZE_ICON_CLASS$1[size] || 'size-3.5', 'text-gray-400 dark:text-gray-500 shrink-0 select-none', className), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) }));
|
|
568
|
+
}
|
|
569
|
+
if (separator === 'dot') {
|
|
570
|
+
return (jsxRuntime.jsx("span", { "data-testid": testId, className: buildClassName('size-1 rounded-full bg-gray-400 dark:bg-gray-500 shrink-0 select-none mx-0.5', className) }));
|
|
571
|
+
}
|
|
572
|
+
return (jsxRuntime.jsx("span", { "data-testid": testId, className: buildClassName('shrink-0 select-none', className), children: separator }));
|
|
573
|
+
}
|
|
574
|
+
const Breadcrumb = React.forwardRef((props, ref) => {
|
|
575
|
+
const { items = [], render, separator = 'slash', size = 'md', maxItems, className, itemClass, activeItemClass, separatorClass, ...navProps } = props;
|
|
576
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
577
|
+
const count = items.length;
|
|
578
|
+
const shouldTruncate = maxItems !== undefined && maxItems > 1 && count > maxItems && !expanded;
|
|
579
|
+
const getVisibleItems = () => {
|
|
580
|
+
if (!shouldTruncate)
|
|
581
|
+
return items;
|
|
582
|
+
// Always keep 1st item and last 1-2 items
|
|
583
|
+
const firstItem = items[0];
|
|
584
|
+
const lastItems = items.slice(count - (maxItems - 1));
|
|
585
|
+
return [firstItem, ...lastItems];
|
|
586
|
+
};
|
|
587
|
+
const hiddenItems = shouldTruncate ? items.slice(1, count - (maxItems - 1)) : [];
|
|
588
|
+
getVisibleItems();
|
|
589
|
+
return (jsxRuntime.jsx("nav", { ref: ref, "aria-label": "Breadcrumb", "data-testid": "breadcrumb-container", className: buildClassName('flex flex-wrap items-center text-gray-600 dark:text-gray-300 font-medium', SIZE_CONTAINER_CLASS$2[size] || SIZE_CONTAINER_CLASS$2.md, className), ...navProps, children: items.map((item, index) => {
|
|
590
|
+
const isLast = index === count - 1;
|
|
591
|
+
const isCollapsedMiddle = shouldTruncate && index > 0 && index < count - (maxItems - 1);
|
|
592
|
+
if (isCollapsedMiddle) {
|
|
593
|
+
if (index === 1) {
|
|
594
|
+
// Render the single '...' collapse pill
|
|
595
|
+
return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 relative group/ellipsis", children: [jsxRuntime.jsx("button", { type: "button", onClick: () => setExpanded(true), className: "px-1.5 py-0.5 rounded-md text-xs font-semibold bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300 transition-colors cursor-pointer", "aria-label": `Expand ${hiddenItems.length} hidden breadcrumbs`, title: `Show ${hiddenItems.length} hidden levels`, children: "\u2022\u2022\u2022" }), jsxRuntime.jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass })] }, "breadcrumb-collapsed"));
|
|
596
|
+
}
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
const defaultContent = isLast ? (jsxRuntime.jsx("span", { className: buildClassName('text-gray-900 dark:text-white font-semibold', activeItemClass, item.className), children: item.text })) : item.to ? (jsxRuntime.jsx("a", { href: item.to, className: buildClassName('hover:text-blue-600 dark:hover:text-blue-400 transition-colors', itemClass, item.className), children: item.text })) : (jsxRuntime.jsx("span", { className: buildClassName(itemClass, item.className), children: item.text }));
|
|
600
|
+
return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", ...(isLast ? { 'aria-current': 'page' } : {}), children: [item.icon && jsxRuntime.jsx("span", { className: "shrink-0 text-gray-400", children: item.icon }), isLast ? (defaultContent) : render ? (jsxRuntime.jsx("span", { className: buildClassName(itemClass, item.className), children: render(item) })) : (defaultContent)] }), !isLast && (jsxRuntime.jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass }))] }, item.key));
|
|
601
|
+
}) }));
|
|
602
|
+
});
|
|
474
603
|
Breadcrumb.displayName = 'Breadcrumb';
|
|
475
604
|
|
|
476
|
-
/**
|
|
477
|
-
* Corner-rounding classes for the group's first/last button, keyed by
|
|
478
|
-
* orientation + size, matching Button's own size -> radius mapping
|
|
479
|
-
* (xs: rounded-sm, sm: rounded, md: rounded-md, lg: rounded-lg).
|
|
480
|
-
*
|
|
481
|
-
* These must stay fully static string literals (no template-literal
|
|
482
|
-
* interpolation) — Tailwind's JIT scanner only picks up class names that
|
|
483
|
-
* appear verbatim in source, and the docs site scans the library's
|
|
484
|
-
* *compiled* output, where interpolated fragments never appear as a
|
|
485
|
-
* complete class name.
|
|
486
|
-
*/
|
|
487
605
|
const EDGE_ROUNDING = {
|
|
488
606
|
horizontal: {
|
|
489
607
|
xs: { first: 'rounded-l-sm rounded-r-none', last: 'rounded-r-sm rounded-l-none' },
|
|
@@ -502,11 +620,6 @@ const PILL_EDGE_ROUNDING = {
|
|
|
502
620
|
horizontal: { first: 'rounded-l-full rounded-r-none', last: 'rounded-r-full rounded-l-none' },
|
|
503
621
|
vertical: { first: 'rounded-t-full rounded-b-none', last: 'rounded-b-full rounded-t-none' },
|
|
504
622
|
};
|
|
505
|
-
/**
|
|
506
|
-
* Resolves the corner-rounding classes for a single button at `index`
|
|
507
|
-
* out of `count` inside the group, so only the group's outer edges stay
|
|
508
|
-
* rounded and interior seams go square.
|
|
509
|
-
*/
|
|
510
623
|
const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
|
|
511
624
|
if (count <= 1)
|
|
512
625
|
return '';
|
|
@@ -517,18 +630,46 @@ const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
|
|
|
517
630
|
? PILL_EDGE_ROUNDING[orientation][position]
|
|
518
631
|
: EDGE_ROUNDING[orientation][size][position];
|
|
519
632
|
};
|
|
520
|
-
const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', rounded = false, fullWidth = false, label, className = '', }) => {
|
|
633
|
+
const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', value, onChange, disabled: groupDisabled = false, rounded = false, fullWidth = false, label, className = '', }) => {
|
|
521
634
|
const isVertical = orientation === 'vertical';
|
|
522
635
|
const items = React.useMemo(() => React.Children.toArray(children).filter(React.isValidElement), [children]);
|
|
523
636
|
const count = items.length;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
637
|
+
const isToggleMode = value !== undefined;
|
|
638
|
+
return (jsxRuntime.jsx("div", { role: "group", "aria-label": label, className: buildClassName('inline-flex items-stretch', isVertical ? 'flex-col' : 'flex-row', fullWidth && 'flex w-full', className), children: items.map((child, index) => {
|
|
639
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
640
|
+
const childProps = child.props;
|
|
641
|
+
const childSize = (_a = childProps.size) !== null && _a !== void 0 ? _a : size;
|
|
642
|
+
const childDisabled = (_b = childProps.disabled) !== null && _b !== void 0 ? _b : groupDisabled;
|
|
643
|
+
// Resolve item value for toggle mode
|
|
644
|
+
const itemVal = (_d = (_c = childProps.value) !== null && _c !== void 0 ? _c : childProps.id) !== null && _d !== void 0 ? _d : (typeof childProps.children === 'string' || typeof childProps.children === 'number'
|
|
645
|
+
? childProps.children
|
|
646
|
+
: index);
|
|
647
|
+
const isSelected = isToggleMode
|
|
648
|
+
? Array.isArray(value)
|
|
649
|
+
? value.includes(itemVal)
|
|
650
|
+
: value === itemVal
|
|
651
|
+
: false;
|
|
652
|
+
const resolvedTheme = isToggleMode
|
|
653
|
+
? isSelected
|
|
654
|
+
? ((_e = childProps.theme) !== null && _e !== void 0 ? _e : 'primary')
|
|
655
|
+
: ((_f = childProps.theme) !== null && _f !== void 0 ? _f : 'secondary')
|
|
656
|
+
: ((_g = childProps.theme) !== null && _g !== void 0 ? _g : theme);
|
|
657
|
+
const resolvedVariant = (_h = childProps.variant) !== null && _h !== void 0 ? _h : variant;
|
|
658
|
+
const handleClick = (e) => {
|
|
659
|
+
var _a;
|
|
660
|
+
(_a = childProps.onClick) === null || _a === void 0 ? void 0 : _a.call(childProps, e);
|
|
661
|
+
if (isToggleMode && !childDisabled) {
|
|
662
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(itemVal);
|
|
663
|
+
}
|
|
664
|
+
};
|
|
527
665
|
return React.cloneElement(child, {
|
|
528
|
-
theme:
|
|
529
|
-
variant:
|
|
666
|
+
theme: resolvedTheme,
|
|
667
|
+
variant: resolvedVariant,
|
|
530
668
|
size: childSize,
|
|
531
|
-
|
|
669
|
+
disabled: childDisabled,
|
|
670
|
+
...(isToggleMode ? { 'aria-pressed': isSelected } : {}),
|
|
671
|
+
onClick: handleClick,
|
|
672
|
+
className: buildClassName(childProps.className, getEdgeRoundingClass({
|
|
532
673
|
index,
|
|
533
674
|
count,
|
|
534
675
|
size: childSize,
|
|
@@ -536,23 +677,66 @@ const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary'
|
|
|
536
677
|
pill: rounded,
|
|
537
678
|
}), count > 1 && [
|
|
538
679
|
'relative shadow-none hover:z-10 focus:z-10 focus-visible:z-10',
|
|
680
|
+
isSelected && 'z-10',
|
|
539
681
|
index > 0 && (isVertical ? '-mt-px' : '-ml-px'),
|
|
682
|
+
index > 0 &&
|
|
683
|
+
resolvedVariant === 'default' &&
|
|
684
|
+
(isVertical
|
|
685
|
+
? resolvedTheme === 'primary' || resolvedTheme === 'danger'
|
|
686
|
+
? 'border-t border-white/25 dark:border-white/20'
|
|
687
|
+
: 'border-t border-gray-300/70 dark:border-gray-700/70'
|
|
688
|
+
: resolvedTheme === 'primary' || resolvedTheme === 'danger'
|
|
689
|
+
? 'border-l border-white/25 dark:border-white/20'
|
|
690
|
+
: 'border-l border-gray-300/70 dark:border-gray-700/70'),
|
|
540
691
|
], fullWidth && 'flex-1'),
|
|
541
692
|
});
|
|
542
693
|
}) }));
|
|
543
694
|
};
|
|
544
695
|
ButtonGroup.displayName = 'ButtonGroup';
|
|
545
696
|
|
|
546
|
-
const
|
|
697
|
+
const RADIUS_CLASS = {
|
|
698
|
+
none: 'rounded-none',
|
|
699
|
+
sm: 'rounded-sm',
|
|
700
|
+
md: 'rounded-md',
|
|
701
|
+
lg: 'rounded-lg',
|
|
702
|
+
xl: 'rounded-xl',
|
|
703
|
+
'2xl': 'rounded-2xl',
|
|
704
|
+
};
|
|
705
|
+
const ASPECT_RATIO_CLASS = {
|
|
706
|
+
auto: 'aspect-auto',
|
|
707
|
+
square: 'aspect-square',
|
|
708
|
+
video: 'aspect-video',
|
|
709
|
+
wide: 'aspect-[21/9]',
|
|
710
|
+
};
|
|
547
711
|
const Card = React.forwardRef(({ as, ...props }, ref) => {
|
|
548
|
-
|
|
549
|
-
const { hoverable = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
|
|
712
|
+
const { variant = 'outlined', radius = 'xl', hoverable = false, clickable = false, selected = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
|
|
550
713
|
const Component = as || 'div';
|
|
551
|
-
// Extract className safely from rest
|
|
552
714
|
const { className, ...cleanRest } = rest;
|
|
553
|
-
const
|
|
715
|
+
const isOutlined = variant === 'outlined' && bordered;
|
|
716
|
+
const isElevated = variant === 'elevated';
|
|
717
|
+
const isFilled = variant === 'filled';
|
|
718
|
+
const isGhost = variant === 'ghost';
|
|
719
|
+
const classes = buildClassName('block transition-all duration-200', RADIUS_CLASS[radius] || RADIUS_CLASS.xl, zeroPadding ? '' : compact ? 'p-3 sm:p-4' : 'p-5 sm:p-6',
|
|
720
|
+
// Surface variants
|
|
721
|
+
isOutlined && 'bg-white dark:bg-gray-900 border border-[var(--ui-border)]', isElevated &&
|
|
722
|
+
'bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 shadow-md shadow-gray-200/50 dark:shadow-none', isFilled && 'bg-gray-50/80 dark:bg-gray-800/60 border border-transparent', isGhost && 'bg-transparent border border-transparent',
|
|
723
|
+
// Hoverable & Clickable states
|
|
724
|
+
hoverable && 'hover:bg-gray-100/80 dark:hover:bg-gray-800/80', clickable && 'cursor-pointer hover:-translate-y-0.5 hover:shadow-lg', selected && 'ring-2 ring-[var(--ui-primary)] border-[var(--ui-primary)]', className);
|
|
554
725
|
return jsxRuntime.jsx(Component, { ref: ref, className: classes, ...cleanRest });
|
|
555
726
|
});
|
|
727
|
+
const CardHeader = ({ className, bordered = false, children, ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('flex items-center justify-between gap-4 mb-4', bordered && 'pb-4 border-b border-gray-200 dark:border-gray-800', className), ...props, children: children }));
|
|
728
|
+
const CardTitle = ({ className, as: Component = 'h3', children, ...props }) => (jsxRuntime.jsx(Component, { className: buildClassName('text-lg font-semibold text-gray-900 dark:text-white tracking-tight', className), ...props, children: children }));
|
|
729
|
+
const CardDescription = ({ className, children, ...props }) => (jsxRuntime.jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal mt-1', className), ...props, children: children }));
|
|
730
|
+
const CardContent = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('space-y-3', className), ...props, children: children }));
|
|
731
|
+
const CardFooter = ({ className, bordered = false, children, ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('flex items-center justify-end gap-3 mt-4', bordered && 'pt-4 border-t border-gray-200 dark:border-gray-800', className), ...props, children: children }));
|
|
732
|
+
const CardMedia = ({ className, aspectRatio = 'video', alt = '', ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('overflow-hidden -mx-5 -mt-5 mb-4 sm:-mx-6 sm:-mt-6', className), children: jsxRuntime.jsx("img", { alt: alt, className: buildClassName('w-full object-cover', ASPECT_RATIO_CLASS[aspectRatio] || ASPECT_RATIO_CLASS.video), ...props }) }));
|
|
733
|
+
Card.Header = CardHeader;
|
|
734
|
+
Card.Title = CardTitle;
|
|
735
|
+
Card.Description = CardDescription;
|
|
736
|
+
Card.Content = CardContent;
|
|
737
|
+
Card.Body = CardContent;
|
|
738
|
+
Card.Footer = CardFooter;
|
|
739
|
+
Card.Media = CardMedia;
|
|
556
740
|
Card.displayName = 'Card';
|
|
557
741
|
|
|
558
742
|
const Label = React.forwardRef((props, ref) => {
|
|
@@ -561,49 +745,127 @@ const Label = React.forwardRef((props, ref) => {
|
|
|
561
745
|
});
|
|
562
746
|
Label.displayName = 'Label';
|
|
563
747
|
|
|
748
|
+
const sizeStyles = {
|
|
749
|
+
sm: {
|
|
750
|
+
box: 'size-3.5',
|
|
751
|
+
svg: 'h-2.5 w-2.5',
|
|
752
|
+
text: 'text-xs',
|
|
753
|
+
subtext: 'text-[11px]',
|
|
754
|
+
},
|
|
755
|
+
md: {
|
|
756
|
+
box: 'size-4.5',
|
|
757
|
+
svg: 'h-3.25 w-3.25',
|
|
758
|
+
text: 'text-sm',
|
|
759
|
+
subtext: 'text-xs',
|
|
760
|
+
},
|
|
761
|
+
lg: {
|
|
762
|
+
box: 'size-5.5',
|
|
763
|
+
svg: 'h-4 w-4',
|
|
764
|
+
text: 'text-base',
|
|
765
|
+
subtext: 'text-sm',
|
|
766
|
+
},
|
|
767
|
+
};
|
|
564
768
|
const Checkbox = React.forwardRef((props, ref) => {
|
|
565
769
|
var _a;
|
|
566
|
-
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
567
|
-
// Checkbox-only props removed from rest
|
|
568
|
-
...rest } = props;
|
|
770
|
+
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
|
|
569
771
|
const generatedId = React.useId();
|
|
570
772
|
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
773
|
+
const innerRef = React.useRef(null);
|
|
774
|
+
React.useImperativeHandle(ref, () => innerRef.current);
|
|
775
|
+
React.useEffect(() => {
|
|
776
|
+
if (innerRef.current) {
|
|
777
|
+
innerRef.current.indeterminate = Boolean(indeterminate);
|
|
778
|
+
}
|
|
779
|
+
}, [indeterminate]);
|
|
780
|
+
const currentSize = sizeStyles[size] || sizeStyles.md;
|
|
781
|
+
const isCard = variant === 'card';
|
|
782
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName(isCard
|
|
783
|
+
? 'rounded-xl border p-3.5 transition-all duration-150 relative'
|
|
784
|
+
: 'flex items-start', isCard &&
|
|
785
|
+
(disabled
|
|
786
|
+
? 'border-gray-200 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
|
|
787
|
+
: checked || indeterminate
|
|
788
|
+
? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
|
|
789
|
+
: 'border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-start relative select-none w-full', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center shrink-0 pt-0.5", children: [jsxRuntime.jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, "aria-checked": indeterminate ? 'mixed' : checked, className: buildClassName('peer cursor-pointer transition-all appearance-none rounded', currentSize.box, 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600 dark:checked:bg-blue-600 dark:checked:border-blue-600', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-900', disabled
|
|
790
|
+
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
791
|
+
: 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-xs', indeterminate &&
|
|
792
|
+
'!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsxRuntime.jsx("span", { className: buildClassName('absolute text-white pointer-events-none transition-opacity top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', checked !== undefined
|
|
793
|
+
? checked
|
|
794
|
+
? 'opacity-100'
|
|
795
|
+
: 'opacity-0'
|
|
796
|
+
: 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })), indeterminate && (jsxRuntime.jsx("span", { className: "absolute text-white pointer-events-none top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-100 z-10", children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxRuntime.jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
|
|
797
|
+
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
798
|
+
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
576
799
|
});
|
|
577
800
|
Checkbox.displayName = 'Checkbox';
|
|
578
801
|
|
|
802
|
+
const columnClasses = {
|
|
803
|
+
1: 'grid grid-cols-1 gap-3',
|
|
804
|
+
2: 'grid grid-cols-1 sm:grid-cols-2 gap-3',
|
|
805
|
+
3: 'grid grid-cols-1 sm:grid-cols-3 gap-3',
|
|
806
|
+
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
|
|
807
|
+
};
|
|
579
808
|
const CheckboxGroup = (props) => {
|
|
580
|
-
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value
|
|
809
|
+
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value: controlledValue, defaultValue, onChange, inline = false, columns, size = 'md', variant = 'default', showSelectAll = false, selectAllLabel = 'Select All', error, helperText, disabled = false, } = props;
|
|
581
810
|
const groupId = React.useId();
|
|
811
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue || []);
|
|
812
|
+
const isControlled = controlledValue !== undefined;
|
|
813
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
582
814
|
const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
815
|
+
const enabledItems = React.useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
|
|
816
|
+
// Select All State calculation
|
|
817
|
+
const allSelected = React.useMemo(() => {
|
|
818
|
+
if (!enabledItems.length)
|
|
819
|
+
return false;
|
|
820
|
+
return enabledItems.every((item) => currentValue.includes(item.value));
|
|
821
|
+
}, [enabledItems, currentValue]);
|
|
822
|
+
const isIndeterminate = React.useMemo(() => {
|
|
823
|
+
if (allSelected)
|
|
824
|
+
return false;
|
|
825
|
+
return enabledItems.some((item) => currentValue.includes(item.value));
|
|
826
|
+
}, [allSelected, enabledItems, currentValue]);
|
|
827
|
+
const handleSelectAllChange = (e) => {
|
|
828
|
+
let updated;
|
|
829
|
+
if (allSelected || isIndeterminate) {
|
|
830
|
+
const enabledValues = new Set(enabledItems.map((i) => i.value));
|
|
831
|
+
updated = currentValue.filter((v) => !enabledValues.has(v));
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
const enabledValues = enabledItems.map((i) => i.value);
|
|
835
|
+
const uniqueSet = new Set([...currentValue, ...enabledValues]);
|
|
836
|
+
updated = Array.from(uniqueSet);
|
|
837
|
+
}
|
|
838
|
+
if (!isControlled) {
|
|
839
|
+
setInternalValue(updated);
|
|
840
|
+
}
|
|
841
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
842
|
+
};
|
|
587
843
|
const handleGroupChange = (e) => {
|
|
588
844
|
const raw = e.target.value;
|
|
589
845
|
const checked = e.target.checked;
|
|
590
|
-
// Convert HTML string value → typed T value
|
|
591
|
-
// Locate in normalized items
|
|
592
846
|
const matched = items.find((i) => String(i.value) === raw);
|
|
593
|
-
const itemValue = matched
|
|
847
|
+
const itemValue = matched ? matched.value : raw;
|
|
594
848
|
let updated;
|
|
595
849
|
if (checked) {
|
|
596
|
-
updated = [...
|
|
850
|
+
updated = [...currentValue, itemValue];
|
|
597
851
|
}
|
|
598
852
|
else {
|
|
599
|
-
updated =
|
|
853
|
+
updated = currentValue.filter((v) => v !== itemValue);
|
|
854
|
+
}
|
|
855
|
+
if (!isControlled) {
|
|
856
|
+
setInternalValue(updated);
|
|
600
857
|
}
|
|
601
858
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
602
859
|
};
|
|
603
|
-
|
|
860
|
+
const layoutClass = columns
|
|
861
|
+
? columnClasses[columns]
|
|
862
|
+
: inline
|
|
863
|
+
? 'flex flex-wrap gap-3'
|
|
864
|
+
: 'flex flex-col gap-3';
|
|
865
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint && (jsxRuntime.jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsxRuntime.jsx("div", { className: "pb-1 border-b border-gray-200 dark:border-gray-800", children: jsxRuntime.jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsxRuntime.jsx("div", { className: layoutClass, children: items.map((item, index) => {
|
|
604
866
|
const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
|
|
605
|
-
return (jsxRuntime.jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, value: String(item.value), checked:
|
|
606
|
-
}) })] }));
|
|
867
|
+
return (jsxRuntime.jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, description: item.description, value: String(item.value), size: size, variant: variant, disabled: disabled || item.disabled, checked: currentValue.includes(item.value), onChange: handleGroupChange }, key));
|
|
868
|
+
}) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
607
869
|
};
|
|
608
870
|
CheckboxGroup.displayName = 'CheckboxGroup';
|
|
609
871
|
|
|
@@ -1030,24 +1292,142 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1030
1292
|
}
|
|
1031
1293
|
DataTable.displayName = 'DataTable';
|
|
1032
1294
|
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1295
|
+
const defaultLevelSizeMap = {
|
|
1296
|
+
1: 'text-3xl font-bold',
|
|
1297
|
+
2: 'text-2xl font-semibold',
|
|
1298
|
+
3: 'text-xl font-semibold',
|
|
1299
|
+
4: 'text-lg font-medium',
|
|
1300
|
+
5: 'text-base font-medium',
|
|
1301
|
+
6: 'text-sm font-medium',
|
|
1302
|
+
};
|
|
1303
|
+
const sizeClassMap = {
|
|
1304
|
+
xs: 'text-xs',
|
|
1305
|
+
sm: 'text-sm',
|
|
1306
|
+
md: 'text-base',
|
|
1307
|
+
lg: 'text-lg',
|
|
1308
|
+
xl: 'text-xl',
|
|
1309
|
+
'2xl': 'text-2xl',
|
|
1310
|
+
'3xl': 'text-3xl',
|
|
1311
|
+
'4xl': 'text-4xl',
|
|
1312
|
+
'5xl': 'text-5xl',
|
|
1313
|
+
};
|
|
1314
|
+
const weightClassMap = {
|
|
1315
|
+
normal: 'font-normal',
|
|
1316
|
+
medium: 'font-medium',
|
|
1317
|
+
semibold: 'font-semibold',
|
|
1318
|
+
bold: 'font-bold',
|
|
1319
|
+
extrabold: 'font-extrabold',
|
|
1320
|
+
};
|
|
1321
|
+
const alignClassMap = {
|
|
1322
|
+
left: 'text-left',
|
|
1323
|
+
center: 'text-center',
|
|
1324
|
+
right: 'text-right',
|
|
1325
|
+
};
|
|
1326
|
+
const trackingClassMap = {
|
|
1327
|
+
tighter: 'tracking-tighter',
|
|
1328
|
+
tight: 'tracking-tight',
|
|
1329
|
+
normal: 'tracking-normal',
|
|
1330
|
+
wide: 'tracking-wide',
|
|
1331
|
+
};
|
|
1332
|
+
const HeadingTextComponent = React.forwardRef((props, ref) => {
|
|
1333
|
+
const { level = 1, size, weight, muted = false, error = false, success = false, warning = false, info = false, invert = false, align, tracking, truncate = false, className, children, ...rest } = props;
|
|
1334
|
+
const tag = `h${Math.min(6, Math.max(1, level))}`;
|
|
1335
|
+
const colorClass = error
|
|
1336
|
+
? 'text-[var(--ui-text-danger)]'
|
|
1337
|
+
: success
|
|
1338
|
+
? 'text-[var(--ui-success)]'
|
|
1339
|
+
: warning
|
|
1340
|
+
? 'text-[var(--ui-warning)]'
|
|
1341
|
+
: info
|
|
1342
|
+
? 'text-[var(--ui-info)]'
|
|
1343
|
+
: muted
|
|
1344
|
+
? invert
|
|
1345
|
+
? 'text-gray-300 dark:text-gray-600'
|
|
1346
|
+
: 'text-[var(--ui-text-muted)]'
|
|
1347
|
+
: invert
|
|
1348
|
+
? 'text-white dark:text-black'
|
|
1349
|
+
: 'text-[var(--ui-text)]';
|
|
1350
|
+
return React.createElement(tag, {
|
|
1351
|
+
ref,
|
|
1352
|
+
className: buildClassName(colorClass, size ? sizeClassMap[size] : defaultLevelSizeMap[level], weight && weightClassMap[weight], align && alignClassMap[align], tracking && trackingClassMap[tracking], truncate && 'truncate', className),
|
|
1353
|
+
...rest,
|
|
1354
|
+
children,
|
|
1355
|
+
});
|
|
1356
|
+
});
|
|
1357
|
+
HeadingTextComponent.displayName = 'HeadingText';
|
|
1358
|
+
const Title = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 1, ...props })));
|
|
1359
|
+
Title.displayName = 'HeadingText.Title';
|
|
1360
|
+
const SubTitle = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 2, ...props })));
|
|
1361
|
+
SubTitle.displayName = 'HeadingText.SubTitle';
|
|
1362
|
+
const SubTitle2 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 3, ...props })));
|
|
1363
|
+
SubTitle2.displayName = 'HeadingText.SubTitle2';
|
|
1364
|
+
const SubTitle3 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 4, ...props })));
|
|
1365
|
+
SubTitle3.displayName = 'HeadingText.SubTitle3';
|
|
1366
|
+
const SubTitle4 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 5, ...props })));
|
|
1367
|
+
SubTitle4.displayName = 'HeadingText.SubTitle4';
|
|
1368
|
+
const HeadingText = Object.assign(HeadingTextComponent, {
|
|
1039
1369
|
Title,
|
|
1040
1370
|
SubTitle,
|
|
1041
1371
|
SubTitle2,
|
|
1042
1372
|
SubTitle3,
|
|
1043
1373
|
SubTitle4,
|
|
1044
|
-
};
|
|
1374
|
+
});
|
|
1045
1375
|
|
|
1376
|
+
const COLUMN_GRID_CLASS = {
|
|
1377
|
+
1: 'grid-cols-1',
|
|
1378
|
+
2: 'grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3',
|
|
1379
|
+
3: 'grid grid-cols-1 md:grid-cols-3 gap-x-6 gap-y-3',
|
|
1380
|
+
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-x-6 gap-y-3',
|
|
1381
|
+
};
|
|
1382
|
+
function CopyableValue({ text, children }) {
|
|
1383
|
+
const [copied, setCopied] = React.useState(false);
|
|
1384
|
+
const handleCopy = async (e) => {
|
|
1385
|
+
e.stopPropagation();
|
|
1386
|
+
try {
|
|
1387
|
+
await navigator.clipboard.writeText(text);
|
|
1388
|
+
setCopied(true);
|
|
1389
|
+
setTimeout(() => setCopied(false), 2000);
|
|
1390
|
+
}
|
|
1391
|
+
catch {
|
|
1392
|
+
// Ignore clipboard error
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 group/copy", children: [jsxRuntime.jsx("span", { children: children }), jsxRuntime.jsx("button", { type: "button", onClick: handleCopy, className: "opacity-0 group-hover/copy:opacity-100 focus:opacity-100 transition-opacity p-0.5 text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 cursor-pointer", title: "Copy to clipboard", "aria-label": "Copy to clipboard", children: copied ? (jsxRuntime.jsx("svg", { className: "size-3.5 text-emerald-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })) : (jsxRuntime.jsx("svg", { className: "size-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })) })] }));
|
|
1396
|
+
}
|
|
1046
1397
|
const DetailedInformation = React.forwardRef((props, ref) => {
|
|
1047
|
-
const { className, title, subTitle, details, compact = false, divider = true, titleClass, detailsClass,
|
|
1048
|
-
const
|
|
1398
|
+
const { className, title, subTitle, action, details = [], columns = 1, layout = 'horizontal', variant = 'flat', compact = false, bordered = false, divider = true, emptyValue = '- -', titleClass, detailsClass, labelClass, ...divProps } = props;
|
|
1399
|
+
const isCard = variant === 'card';
|
|
1400
|
+
const isBordered = bordered || variant === 'bordered' || isCard;
|
|
1401
|
+
const hasTitles = title || subTitle || action;
|
|
1049
1402
|
const filteredDetails = details.filter((detail) => !detail.hidden);
|
|
1050
|
-
|
|
1403
|
+
const isMultiColumn = columns > 1;
|
|
1404
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: buildClassName(isCard
|
|
1405
|
+
? compact
|
|
1406
|
+
? 'p-3 sm:p-4 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
|
|
1407
|
+
: 'p-4 sm:p-6 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
|
|
1408
|
+
: isBordered
|
|
1409
|
+
? compact
|
|
1410
|
+
? 'p-3 sm:p-4 border border-[var(--ui-border)] rounded-2xl'
|
|
1411
|
+
: 'p-4 sm:p-6 border border-[var(--ui-border)] rounded-2xl'
|
|
1412
|
+
: compact
|
|
1413
|
+
? 'p-2 md:p-3'
|
|
1414
|
+
: 'p-3 md:p-4', className), ...divProps, children: [hasTitles && (jsxRuntime.jsxs("div", { className: "mb-4 flex items-start justify-between gap-4", children: [jsxRuntime.jsxs("div", { children: [title && (jsxRuntime.jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsxRuntime.jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-400 font-normal", children: subTitle }))] }), action && jsxRuntime.jsx("div", { className: "shrink-0 flex items-center", children: action })] })), filteredDetails.length ? (jsxRuntime.jsx("div", { className: buildClassName('border-[var(--ui-border-muted)]', hasTitles && !isMultiColumn && 'border-t pt-2', detailsClass), children: jsxRuntime.jsx("dl", { className: buildClassName(COLUMN_GRID_CLASS[columns] || COLUMN_GRID_CLASS[1], !isMultiColumn && divider && 'divide-y divide-[var(--ui-border-muted)]'), children: filteredDetails.map((detail, index) => {
|
|
1415
|
+
var _a;
|
|
1416
|
+
const itemFallback = (_a = detail.emptyValue) !== null && _a !== void 0 ? _a : emptyValue;
|
|
1417
|
+
const hasValue = detail.value !== undefined && detail.value !== null && detail.value !== '';
|
|
1418
|
+
const displayValue = hasValue ? detail.value : itemFallback;
|
|
1419
|
+
const copyText = typeof detail.value === 'string' || typeof detail.value === 'number'
|
|
1420
|
+
? String(detail.value)
|
|
1421
|
+
: '';
|
|
1422
|
+
const isVertical = layout === 'vertical' || isMultiColumn;
|
|
1423
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName(isMultiColumn && isCard
|
|
1424
|
+
? compact
|
|
1425
|
+
? 'p-3 bg-white dark:bg-gray-900 rounded-xl border border-gray-200/80 dark:border-gray-700/80 shadow-2xs flex flex-col gap-1'
|
|
1426
|
+
: 'p-4 bg-white dark:bg-gray-900 rounded-xl border border-gray-200/80 dark:border-gray-700/80 shadow-2xs flex flex-col gap-1'
|
|
1427
|
+
: isVertical
|
|
1428
|
+
? 'flex flex-col gap-1 py-2 px-1'
|
|
1429
|
+
: 'grid grid-cols-3 gap-4 items-center py-2.5 px-1', compact && !isMultiColumn ? 'py-1.5' : ''), children: [jsxRuntime.jsxs("dt", { className: buildClassName('text-sm/6 font-medium text-gray-900 dark:text-gray-100 flex items-center gap-1.5', labelClass, detail.labelClass), children: [detail.icon && jsxRuntime.jsx("span", { className: "shrink-0 text-gray-400", children: detail.icon }), jsxRuntime.jsx("span", { children: detail.label }), detail.helpText && (jsxRuntime.jsx("span", { className: "text-xs text-gray-400 font-normal cursor-help", title: detail.helpText, children: "(?)" }))] }), jsxRuntime.jsx("dd", { className: buildClassName('text-sm/6 text-gray-700 dark:text-gray-300', !isVertical && 'sm:col-span-2 sm:mt-0', detail.contentClass), children: detail.copyable && copyText ? (jsxRuntime.jsx(CopyableValue, { text: copyText, children: displayValue })) : (displayValue) })] }, detail.label || index));
|
|
1430
|
+
}) }) })) : null] }));
|
|
1051
1431
|
});
|
|
1052
1432
|
DetailedInformation.displayName = 'DetailedInformation';
|
|
1053
1433
|
|
|
@@ -1060,15 +1440,15 @@ const alignmentMap = {
|
|
|
1060
1440
|
center: 'items-center justify-center h-full',
|
|
1061
1441
|
start: 'justify-start h-full',
|
|
1062
1442
|
end: 'justify-end h-full',
|
|
1063
|
-
top: 'items-
|
|
1443
|
+
top: 'items-start justify-center w-full',
|
|
1064
1444
|
bottom: 'items-end justify-center h-full',
|
|
1065
1445
|
};
|
|
1066
1446
|
const panelAlignmentMap = {
|
|
1067
|
-
center: 'data-[closed]:scale-95 rounded-
|
|
1068
|
-
start: 'data-[closed]:-translate-x-1/2 rounded-r-
|
|
1069
|
-
end: 'data-[closed]:translate-x-1/2 rounded-l-
|
|
1070
|
-
top: 'data-[closed]:-translate-y-1/2 rounded-b-
|
|
1071
|
-
bottom: 'data-[closed]:translate-y-1/2 rounded-t-
|
|
1447
|
+
center: 'data-[closed]:scale-95 rounded-2xl',
|
|
1448
|
+
start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',
|
|
1449
|
+
end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',
|
|
1450
|
+
top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',
|
|
1451
|
+
bottom: 'data-[closed]:translate-y-1/2 rounded-t-2xl md:rounded-t-3xl max-h-[92svh]',
|
|
1072
1452
|
};
|
|
1073
1453
|
const panelSizeMap = {
|
|
1074
1454
|
xs: 'max-w-xs',
|
|
@@ -1079,15 +1459,56 @@ const panelSizeMap = {
|
|
|
1079
1459
|
'2xl': 'max-w-2xl',
|
|
1080
1460
|
full: 'w-full',
|
|
1081
1461
|
};
|
|
1082
|
-
const Drawer = ({ isOpen, onClose = () => { }, align = 'end', size = 'md', backdrop = true, title, description, children, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, }) => {
|
|
1083
|
-
const showTitleSection = Boolean(title || description);
|
|
1084
|
-
|
|
1462
|
+
const Drawer = ({ isOpen, onClose = () => { }, closeOnOutsideClick = true, align = 'end', size = 'md', backdrop = true, title, description, children, footer, footerSticky = false, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, showCloseButton = true, dragHandle, }) => {
|
|
1463
|
+
const showTitleSection = Boolean(title || description || showBackButton || showCloseButton);
|
|
1464
|
+
const shouldShowDragHandle = dragHandle !== null && dragHandle !== void 0 ? dragHandle : align === 'bottom';
|
|
1465
|
+
const handleDialogClose = () => {
|
|
1466
|
+
if (closeOnOutsideClick) {
|
|
1467
|
+
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
return (jsxRuntime.jsxs(react.Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: handleDialogClose, transition: true, children: [backdrop && (jsxRuntime.jsx(react.DialogBackdrop, { className: "fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200" })), jsxRuntime.jsx("div", { className: "fixed inset-0 w-screen h-[100svh]", children: jsxRuntime.jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxRuntime.jsxs(react.DialogPanel, { transition: true, className: buildClassName('w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0', 'overflow-y-auto overflow-x-hidden duration-200 ease-out data-[closed]:opacity-0', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [shouldShowDragHandle && (jsxRuntime.jsx("div", { className: "w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab", "data-testid": "drawer-drag-handle" })), showTitleSection && (jsxRuntime.jsxs("div", { className: buildClassName('space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-gray-100 dark:border-gray-700/60 shrink-0', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [showBackButton && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Back", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, children: jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }) })), title && (jsxRuntime.jsx(react.DialogTitle, { as: "h3", className: buildClassName('text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0', titleClass), children: title }))] }), showCloseButton && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, className: "rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0", children: jsxRuntime.jsx(SvgX, { className: "size-4" }) }))] }), description && (jsxRuntime.jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal truncate', descriptionClass), children: description }))] })), jsxRuntime.jsx("div", { className: buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass), children: children }), footer && (jsxRuntime.jsx("div", { className: buildClassName('p-4 border-t border-gray-100 dark:border-gray-700/60 bg-gray-50/50 dark:bg-gray-800/80 shrink-0 min-w-0', footerSticky && 'sticky bottom-0 z-10'), children: footer }))] }) }) })] }));
|
|
1085
1471
|
};
|
|
1086
1472
|
Drawer.displayName = 'Drawer';
|
|
1087
1473
|
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1474
|
+
const WIDTH_CLASS = {
|
|
1475
|
+
auto: 'w-auto min-w-[120px]',
|
|
1476
|
+
sm: 'w-44',
|
|
1477
|
+
md: 'w-56',
|
|
1478
|
+
lg: 'w-64',
|
|
1479
|
+
xl: 'w-72',
|
|
1480
|
+
};
|
|
1481
|
+
const Dropdown = ({ triggerButton, renderTriggerButton, className, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', width = 'md', menuItemClass, onMenuClick, ...restProps }) => {
|
|
1482
|
+
var _a, _b, _c, _d;
|
|
1483
|
+
const widthClass = WIDTH_CLASS[width] || width || WIDTH_CLASS.md;
|
|
1484
|
+
const { theme, variant, size, disabled } = restProps;
|
|
1485
|
+
return (jsxRuntime.jsxs(react.Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: (state) => {
|
|
1486
|
+
const rendered = renderTriggerButton(state);
|
|
1487
|
+
if (React.isValidElement(rendered) && className) {
|
|
1488
|
+
return React.cloneElement(rendered, {
|
|
1489
|
+
className: buildClassName(rendered.props.className, className),
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
return rendered;
|
|
1493
|
+
} })) : React.isValidElement(triggerButton) ? (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: React.cloneElement(triggerButton, {
|
|
1494
|
+
className: buildClassName(triggerButton.props.className, className),
|
|
1495
|
+
theme: (_a = triggerButton.props.theme) !== null && _a !== void 0 ? _a : theme,
|
|
1496
|
+
variant: (_b = triggerButton.props.variant) !== null && _b !== void 0 ? _b : variant,
|
|
1497
|
+
size: (_c = triggerButton.props.size) !== null && _c !== void 0 ? _c : size,
|
|
1498
|
+
disabled: (_d = triggerButton.props.disabled) !== null && _d !== void 0 ? _d : disabled,
|
|
1499
|
+
}) })) : (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: triggerButton })), jsxRuntime.jsx(react.MenuItems, { transition: transition, anchor: anchor, className: buildClassName('origin-top-right rounded-xl shadow-lg p-1.5 text-sm transition duration-100 ease-out z-50', 'border border-gray-200/80 dark:border-gray-700/80 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200', 'focus:outline-none data-[closed]:scale-95 data-[closed]:opacity-0', '[--anchor-gap:--spacing(1)]', widthClass, itemsContainerClass), "data-testid": "dropdown-items", as: "ul", children: items.map((item, idx) => (jsxRuntime.jsxs(React.Fragment, { children: [item.groupTitle && (jsxRuntime.jsx("li", { className: "px-2.5 pt-2 pb-1 text-[11px] font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider select-none", "data-testid": `dropdown-group-${item.id}`, children: item.groupTitle }, `group-${item.id}`)), jsxRuntime.jsx(react.MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled, active }) => (jsxRuntime.jsx("div", { onClick: () => {
|
|
1500
|
+
var _a;
|
|
1501
|
+
if (!disabled) {
|
|
1502
|
+
(_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, item);
|
|
1503
|
+
onMenuClick === null || onMenuClick === void 0 ? void 0 : onMenuClick(item, idx);
|
|
1504
|
+
}
|
|
1505
|
+
}, className: buildClassName('flex items-center justify-between gap-3 w-full px-2.5 py-1.5 rounded-lg text-sm transition-colors cursor-pointer select-none', item.danger
|
|
1506
|
+
? buildClassName('text-rose-600 dark:text-rose-400', active
|
|
1507
|
+
? 'bg-rose-50 dark:bg-rose-950/40'
|
|
1508
|
+
: 'hover:bg-rose-50 dark:hover:bg-rose-950/40')
|
|
1509
|
+
: buildClassName('text-gray-700 dark:text-gray-200', active
|
|
1510
|
+
? 'bg-gray-100 dark:bg-gray-700/80'
|
|
1511
|
+
: 'hover:bg-gray-100/80 dark:hover:bg-gray-700/80'), disabled && 'opacity-40 cursor-not-allowed pointer-events-none', menuItemClass), "data-testid": `dropdown-item-${item.id}`, children: renderItem ? (renderItem(item)) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 min-w-0 flex-1", children: [item.icon && (jsxRuntime.jsx("span", { className: buildClassName('shrink-0', item.danger ? 'text-rose-500' : 'text-gray-400 dark:text-gray-500'), children: item.icon })), jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0", children: [item.label && jsxRuntime.jsx("span", { className: "font-medium truncate", children: item.label }), item.description && (jsxRuntime.jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500 font-normal truncate", children: item.description }))] })] }), item.shortcut && (jsxRuntime.jsx("kbd", { className: "shrink-0 text-[10px] font-medium font-mono px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-700/80 border border-gray-200/60 dark:border-gray-600/60 text-gray-400 dark:text-gray-400", children: item.shortcut }))] })) })) }), item.divider && idx < items.length - 1 && (jsxRuntime.jsx("li", { className: "my-1 h-px bg-gray-200/80 dark:bg-gray-700/80", "data-testid": "dropdown-divider" }, `divider-${item.id}`))] }, item.id))) })] }));
|
|
1091
1512
|
};
|
|
1092
1513
|
Dropdown.displayName = 'Dropdown';
|
|
1093
1514
|
|
|
@@ -1114,90 +1535,294 @@ const EmptyState = ({ title, description, className, action = null, }) => {
|
|
|
1114
1535
|
return (jsxRuntime.jsx("div", { className: buildClassName('w-full flex justify-center px-4 pt-16', className), children: jsxRuntime.jsxs("div", { className: "max-w-md flex flex-col items-center gap-4", children: [jsxRuntime.jsx(SvgFolderPlus, { className: "size-8 text-[var(--ui-text)]" }), jsxRuntime.jsx(HeadingText.SubTitle, { children: title }), description && (jsxRuntime.jsx(BodyText, { as: "p", className: "text-center", muted: true, small: true, children: description })), action] }) }));
|
|
1115
1536
|
};
|
|
1116
1537
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1538
|
+
const sizeClasses$4 = {
|
|
1539
|
+
sm: {
|
|
1540
|
+
input: 'px-2.5 py-1 text-xs leading-5',
|
|
1541
|
+
group: 'w-8 text-xs',
|
|
1542
|
+
icon: 'size-3.5',
|
|
1543
|
+
text: 'text-xs',
|
|
1544
|
+
},
|
|
1545
|
+
md: {
|
|
1546
|
+
input: 'px-3.5 py-2 text-sm leading-6',
|
|
1547
|
+
group: 'w-10 text-sm',
|
|
1548
|
+
icon: 'size-4',
|
|
1549
|
+
text: 'text-sm',
|
|
1550
|
+
},
|
|
1551
|
+
lg: {
|
|
1552
|
+
input: 'px-4 py-2.5 text-base leading-7',
|
|
1553
|
+
group: 'w-12 text-base',
|
|
1554
|
+
icon: 'size-5',
|
|
1555
|
+
text: 'text-base',
|
|
1556
|
+
},
|
|
1557
|
+
};
|
|
1558
|
+
function buildInputClass(className, { isFocusWithin = false, disabled = false, hasError = false, size = 'md', } = {}) {
|
|
1559
|
+
const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
|
|
1560
|
+
return buildClassName('transition-all duration-150', 'block w-full rounded-lg bg-white', currentSize.input, 'text-gray-900 outline outline-1 -outline-offset-1', hasError
|
|
1561
|
+
? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
|
|
1562
|
+
: 'outline-gray-300 dark:outline-gray-600', disabled
|
|
1119
1563
|
? 'pointer-events-none bg-gray-100 dark:bg-gray-800 dark:text-gray-600 dark:placeholder-gray-600 placeholder-gray-400 outline-gray-200 dark:outline-gray-700'
|
|
1120
1564
|
: 'bg-white dark:bg-gray-700 dark:text-white outline-gray-300 dark:outline-gray-600 dark:placeholder-gray-400 placeholder-gray-400', isFocusWithin
|
|
1121
1565
|
? 'focus-within:outline-[var(--ui-focus-ring)] focus-within:outline-2 focus-within:-outline-offset-2'
|
|
1122
1566
|
: 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
|
|
1123
1567
|
}
|
|
1124
1568
|
const Input = React.forwardRef((props, ref) => {
|
|
1125
|
-
const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, ...inputProps } = props;
|
|
1569
|
+
const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, prefix, suffix, clearable = false, onClear, showCount = false, helperText, size = 'md', value, defaultValue, onChange, maxLength, ...inputProps } = props;
|
|
1570
|
+
const innerRef = React.useRef(null);
|
|
1571
|
+
React.useImperativeHandle(ref, () => innerRef.current);
|
|
1572
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
1573
|
+
const isControlled = value !== undefined;
|
|
1574
|
+
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
1575
|
+
const charLength = currentVal.length;
|
|
1576
|
+
const handleInputChange = (e) => {
|
|
1577
|
+
if (!isControlled) {
|
|
1578
|
+
setUncontrolledValue(e.target.value);
|
|
1579
|
+
}
|
|
1580
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
1581
|
+
};
|
|
1582
|
+
const handleClear = () => {
|
|
1583
|
+
if (innerRef.current) {
|
|
1584
|
+
innerRef.current.value = '';
|
|
1585
|
+
const event = new Event('input', {
|
|
1586
|
+
bubbles: true,
|
|
1587
|
+
});
|
|
1588
|
+
Object.defineProperty(event, 'target', { writable: true, value: { value: '' } });
|
|
1589
|
+
if (!isControlled) {
|
|
1590
|
+
setUncontrolledValue('');
|
|
1591
|
+
}
|
|
1592
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
1593
|
+
}
|
|
1594
|
+
onClear === null || onClear === void 0 ? void 0 : onClear();
|
|
1595
|
+
};
|
|
1126
1596
|
const hasError = Boolean(error);
|
|
1127
|
-
const
|
|
1597
|
+
const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
|
|
1598
|
+
const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
|
|
1599
|
+
const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors cursor-pointer", title: "Clear text", tabIndex: -1, children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: jsxRuntime.jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) }) })) : null);
|
|
1600
|
+
const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
|
|
1128
1601
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
1129
|
-
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-
|
|
1602
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-l-lg border border-r-0 border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: prefix })), jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), jsxRuntime.jsx("input", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, maxLength: maxLength, name: name, id: id, type: type, className: buildClassName(buildInputClass(className, {
|
|
1603
|
+
disabled: inputProps.disabled,
|
|
1604
|
+
hasError,
|
|
1605
|
+
size,
|
|
1606
|
+
}), prefix ? '!rounded-l-none' : '', suffix ? '!rounded-r-none' : '', hasRightGroup ? (size === 'lg' ? 'pe-12' : size === 'sm' ? 'pe-8' : 'pe-10') : '', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : '') }), hasRightGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute end-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: actualRightGroup }))] }), suffix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-r-lg border border-l-0 border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: suffix }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
1607
|
+
? 'text-red-500 font-semibold'
|
|
1608
|
+
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
1130
1609
|
});
|
|
1131
1610
|
Input.displayName = 'Input';
|
|
1132
1611
|
|
|
1133
1612
|
const PASSWORD_TYPE = 'password';
|
|
1134
1613
|
const PASSWORD_PLACEHOLDER = '••••••••••';
|
|
1135
|
-
|
|
1136
|
-
|
|
1614
|
+
function evaluatePasswordStrength(val) {
|
|
1615
|
+
if (!val)
|
|
1616
|
+
return { score: 0, label: 'Weak', colorClass: 'bg-gray-200 dark:bg-gray-700', percent: 0 };
|
|
1617
|
+
let score = 0;
|
|
1618
|
+
if (val.length >= 8)
|
|
1619
|
+
score += 1;
|
|
1620
|
+
if (val.length >= 12)
|
|
1621
|
+
score += 1;
|
|
1622
|
+
if (/[0-9]/.test(val))
|
|
1623
|
+
score += 1;
|
|
1624
|
+
if (/[a-z]/.test(val) && /[A-Z]/.test(val))
|
|
1625
|
+
score += 1;
|
|
1626
|
+
if (/[^A-Za-z0-9]/.test(val))
|
|
1627
|
+
score += 1;
|
|
1628
|
+
if (score <= 1) {
|
|
1629
|
+
return { score: 1, label: 'Weak', colorClass: 'bg-red-500', percent: 25 };
|
|
1630
|
+
}
|
|
1631
|
+
if (score === 2) {
|
|
1632
|
+
return { score: 2, label: 'Fair', colorClass: 'bg-amber-500', percent: 50 };
|
|
1633
|
+
}
|
|
1634
|
+
if (score === 3) {
|
|
1635
|
+
return { score: 3, label: 'Good', colorClass: 'bg-blue-500', percent: 75 };
|
|
1636
|
+
}
|
|
1637
|
+
return { score: 4, label: 'Strong', colorClass: 'bg-emerald-500', percent: 100 };
|
|
1638
|
+
}
|
|
1639
|
+
const PasswordInput = React.forwardRef((props, ref) => {
|
|
1640
|
+
const { showStrength = false, placeholder = PASSWORD_PLACEHOLDER, onChange, value, defaultValue, ...inputProps } = props;
|
|
1137
1641
|
const [showPassword, setShowPassword] = React.useState(false);
|
|
1138
|
-
|
|
1139
|
-
|
|
1642
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
1643
|
+
const isControlled = value !== undefined;
|
|
1644
|
+
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
1645
|
+
const handleInputChange = (e) => {
|
|
1646
|
+
if (!isControlled) {
|
|
1647
|
+
setUncontrolledValue(e.target.value);
|
|
1648
|
+
}
|
|
1649
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
1650
|
+
};
|
|
1651
|
+
const strength = evaluatePasswordStrength(currentVal);
|
|
1652
|
+
return (jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [jsxRuntime.jsx(Input, { ref: ref, ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: placeholder, rightGroup: jsxRuntime.jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1.5 mr-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200", type: "button", onClick: () => setShowPassword((prev) => !prev), tabIndex: -1, "aria-label": showPassword ? 'Hide password' : 'Show password', children: showPassword ? (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }), showStrength && currentVal.length > 0 && (jsxRuntime.jsxs("div", { className: "pt-0.5 space-y-1", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-[11px] font-medium text-gray-500 dark:text-gray-400", children: [jsxRuntime.jsx("span", { children: "Password Strength" }), jsxRuntime.jsx("span", { className: "font-semibold text-gray-700 dark:text-gray-200", children: strength.label })] }), jsxRuntime.jsx("div", { className: "h-1.5 w-full bg-gray-200 dark:bg-gray-800 rounded-full overflow-hidden flex gap-1 p-0.5", children: jsxRuntime.jsx("div", { className: `h-full transition-all duration-300 rounded-full ${strength.colorClass}`, style: { width: `${strength.percent}%` } }) })] }))] }));
|
|
1653
|
+
});
|
|
1654
|
+
PasswordInput.displayName = 'PasswordInput';
|
|
1140
1655
|
|
|
1656
|
+
const SIZE_MAP = {
|
|
1657
|
+
sm: 'size-4',
|
|
1658
|
+
md: 'size-4.5',
|
|
1659
|
+
lg: 'size-5.5',
|
|
1660
|
+
};
|
|
1661
|
+
const THEME_MAP = {
|
|
1662
|
+
primary: 'checked:border-[var(--ui-primary)] checked:hover:border-[var(--ui-primary)] focus:ring-[var(--ui-primary-ring)]',
|
|
1663
|
+
success: 'checked:border-emerald-600 checked:hover:border-emerald-600 focus:ring-emerald-500',
|
|
1664
|
+
danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
|
|
1665
|
+
};
|
|
1141
1666
|
const Radio = React.forwardRef((props, ref) => {
|
|
1142
|
-
|
|
1143
|
-
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
1144
|
-
// RadioButton-only props removed from rest
|
|
1145
|
-
...rest } = props;
|
|
1667
|
+
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
|
|
1146
1668
|
const generatedId = React.useId();
|
|
1147
|
-
const radioId =
|
|
1148
|
-
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-
|
|
1669
|
+
const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
|
|
1670
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-start gap-2.5', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center shrink-0 pt-0.5", children: jsxRuntime.jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'dark:focus:ring-offset-gray-800', SIZE_MAP[size], THEME_MAP[theme], disabled
|
|
1149
1671
|
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
1150
|
-
:
|
|
1151
|
-
|
|
1152
|
-
|
|
1672
|
+
: 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsxRuntime.jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
|
|
1673
|
+
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
1674
|
+
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { className: buildClassName('text-xs leading-normal', disabled
|
|
1675
|
+
? 'text-gray-300 dark:text-gray-600'
|
|
1676
|
+
: 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1153
1677
|
});
|
|
1678
|
+
Radio.displayName = 'Radio';
|
|
1154
1679
|
|
|
1680
|
+
const COLUMN_GRID_MAP = {
|
|
1681
|
+
1: 'grid-cols-1',
|
|
1682
|
+
2: 'grid-cols-1 md:grid-cols-2',
|
|
1683
|
+
3: 'grid-cols-1 md:grid-cols-3',
|
|
1684
|
+
4: 'grid-cols-1 md:grid-cols-4',
|
|
1685
|
+
};
|
|
1155
1686
|
const RadioGroup = (props) => {
|
|
1156
|
-
const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
|
|
1687
|
+
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
|
|
1157
1688
|
const items = React.useMemo(() => {
|
|
1158
1689
|
var _a;
|
|
1159
|
-
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
1690
|
+
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
1691
|
+
? {
|
|
1692
|
+
label: option,
|
|
1693
|
+
value: option,
|
|
1694
|
+
description: undefined,
|
|
1695
|
+
icon: undefined,
|
|
1696
|
+
disabled: undefined,
|
|
1697
|
+
}
|
|
1698
|
+
: option)) !== null && _a !== void 0 ? _a : [];
|
|
1160
1699
|
}, [options]);
|
|
1161
|
-
|
|
1700
|
+
const isCards = variant === 'cards';
|
|
1701
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName(columns
|
|
1702
|
+
? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
|
|
1703
|
+
: row || isCards
|
|
1704
|
+
? 'flex flex-wrap gap-3'
|
|
1705
|
+
: 'flex flex-col gap-3'), children: items.map((item) => {
|
|
1706
|
+
const isSelected = value === item.value;
|
|
1707
|
+
const isItemDisabled = disabled || Boolean(item.disabled);
|
|
1708
|
+
if (isCards) {
|
|
1709
|
+
return (jsxRuntime.jsxs("label", { htmlFor: `${name}-${item.value}`, className: buildClassName('relative flex items-start gap-3 p-4 rounded-xl border transition-all cursor-pointer select-none min-w-[200px] flex-1', isSelected
|
|
1710
|
+
? 'border-[var(--ui-primary)] bg-blue-50/40 dark:bg-blue-950/30 ring-2 ring-[var(--ui-primary-ring)]/30'
|
|
1711
|
+
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600', isItemDisabled &&
|
|
1712
|
+
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, value: item.value, "aria-label": item.label, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange, containerClass: "pt-0.5" }), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [item.icon && (jsxRuntime.jsx("span", { className: "shrink-0 text-gray-500 dark:text-gray-400", children: item.icon })), jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: item.label })] }), item.description && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400 font-normal leading-relaxed", children: item.description }))] })] }, item.value));
|
|
1713
|
+
}
|
|
1714
|
+
return (jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, description: item.description, value: item.value, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange }, item.value));
|
|
1715
|
+
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1162
1716
|
};
|
|
1717
|
+
RadioGroup.displayName = 'RadioGroup';
|
|
1163
1718
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1719
|
+
const SIZE_CONTAINER_CLASS$1 = {
|
|
1720
|
+
sm: 'p-1.5 gap-1 rounded-lg text-xs',
|
|
1721
|
+
md: 'p-2 gap-1.5 rounded-xl text-sm',
|
|
1722
|
+
lg: 'p-2 gap-2 rounded-2xl text-base',
|
|
1723
|
+
};
|
|
1724
|
+
const SIZE_ITEM_CLASS = {
|
|
1725
|
+
sm: 'px-4 py-2 text-xs rounded-md min-w-[64px]',
|
|
1726
|
+
md: 'px-5 py-2.5 text-sm rounded-lg min-w-[76px]',
|
|
1727
|
+
lg: 'px-7 py-3 text-base rounded-xl min-w-[92px]',
|
|
1728
|
+
};
|
|
1729
|
+
const SIZE_ITEM_DESC_CLASS = {
|
|
1730
|
+
sm: 'px-4 py-2.5 text-xs rounded-md min-w-[80px]',
|
|
1731
|
+
md: 'px-5 py-3 text-sm rounded-lg min-w-[96px]',
|
|
1732
|
+
lg: 'px-7 py-3.5 text-base rounded-xl min-w-[112px]',
|
|
1733
|
+
};
|
|
1734
|
+
const THEME_SWITCH_CLASS = {
|
|
1735
|
+
primary: 'bg-[var(--ui-primary)] text-white shadow-xs',
|
|
1736
|
+
secondary: 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-xs',
|
|
1737
|
+
dark: 'bg-gray-900 dark:bg-black text-white shadow-xs',
|
|
1738
|
+
};
|
|
1739
|
+
const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', disabled = false, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
|
|
1169
1740
|
const containerRef = React.useRef(null);
|
|
1170
|
-
const [style, setStyle] = React.useState({ left: 0, top: 0, width: 0, height: 0 });
|
|
1741
|
+
const [style, setStyle] = React.useState({ left: 0, top: 0, width: 0, height: 0, ready: false });
|
|
1171
1742
|
const getLabel = (i) => (typeof i === 'string' ? i : i.label);
|
|
1172
1743
|
const getValue = (i) => (typeof i === 'string' ? i : i.value);
|
|
1173
1744
|
const getDescription = (i) => { var _a; return typeof i === 'string' ? '' : ((_a = i.description) !== null && _a !== void 0 ? _a : ''); };
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
React.
|
|
1745
|
+
const isItemDisabled = (i) => disabled || (typeof i === 'string' ? false : Boolean(i.disabled));
|
|
1746
|
+
const hasDescription = items.some((i) => typeof i !== 'string' && Boolean(i.description));
|
|
1747
|
+
// Position calculation for GPU sliding highlight
|
|
1748
|
+
React.useLayoutEffect(() => {
|
|
1178
1749
|
if (!containerRef.current)
|
|
1179
1750
|
return;
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1751
|
+
const updatePosition = () => {
|
|
1752
|
+
if (!containerRef.current)
|
|
1753
|
+
return;
|
|
1754
|
+
const activeEl = containerRef.current.querySelector('[data-selected="true"]');
|
|
1755
|
+
if (!activeEl)
|
|
1756
|
+
return;
|
|
1757
|
+
const parentRect = containerRef.current.getBoundingClientRect();
|
|
1758
|
+
const rect = activeEl.getBoundingClientRect();
|
|
1759
|
+
const left = activeEl.offsetLeft || rect.left - parentRect.left;
|
|
1760
|
+
const top = activeEl.offsetTop || rect.top - parentRect.top;
|
|
1761
|
+
const width = activeEl.offsetWidth || rect.width;
|
|
1762
|
+
const height = activeEl.offsetHeight || rect.height;
|
|
1763
|
+
setStyle({
|
|
1764
|
+
left,
|
|
1765
|
+
top,
|
|
1766
|
+
width,
|
|
1767
|
+
height,
|
|
1768
|
+
ready: true,
|
|
1769
|
+
});
|
|
1770
|
+
};
|
|
1771
|
+
updatePosition();
|
|
1772
|
+
const resizeObserver = new ResizeObserver(updatePosition);
|
|
1773
|
+
resizeObserver.observe(containerRef.current);
|
|
1774
|
+
return () => resizeObserver.disconnect();
|
|
1775
|
+
}, [selected, items, size]);
|
|
1776
|
+
// ARIA Keyboard navigation handler
|
|
1777
|
+
const handleGroupKeyDown = (e) => {
|
|
1778
|
+
const enabledItems = items.filter((it) => !isItemDisabled(it));
|
|
1779
|
+
if (enabledItems.length === 0)
|
|
1183
1780
|
return;
|
|
1184
|
-
const
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1781
|
+
const currentVal = selected;
|
|
1782
|
+
const currentEnabledIndex = enabledItems.findIndex((it) => getValue(it) === currentVal);
|
|
1783
|
+
let nextItem = null;
|
|
1784
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
1785
|
+
e.preventDefault();
|
|
1786
|
+
const nextIdx = (currentEnabledIndex + 1) % enabledItems.length;
|
|
1787
|
+
nextItem = enabledItems[nextIdx];
|
|
1788
|
+
}
|
|
1789
|
+
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
1790
|
+
e.preventDefault();
|
|
1791
|
+
const prevIdx = (currentEnabledIndex - 1 + enabledItems.length) % enabledItems.length;
|
|
1792
|
+
nextItem = enabledItems[prevIdx];
|
|
1793
|
+
}
|
|
1794
|
+
else if (e.key === 'Home') {
|
|
1795
|
+
e.preventDefault();
|
|
1796
|
+
nextItem = enabledItems[0];
|
|
1797
|
+
}
|
|
1798
|
+
else if (e.key === 'End') {
|
|
1799
|
+
e.preventDefault();
|
|
1800
|
+
nextItem = enabledItems[enabledItems.length - 1];
|
|
1801
|
+
}
|
|
1802
|
+
if (nextItem) {
|
|
1803
|
+
onChange(getValue(nextItem));
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
return (jsxRuntime.jsxs("div", { ref: containerRef, role: "radiogroup", tabIndex: 0, "aria-label": label, onKeyDown: handleGroupKeyDown, className: buildClassName('relative inline-grid grid-flow-col auto-cols-fr items-stretch bg-gray-100 dark:bg-gray-800/80 border border-gray-200/80 dark:border-gray-700/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)]', SIZE_CONTAINER_CLASS$1[size], disabled && 'opacity-60 pointer-events-none', wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName('absolute transition-all duration-200 ease-[cubic-bezier(.25,.8,.25,1)] pointer-events-none', THEME_SWITCH_CLASS[theme], size === 'sm' ? 'rounded-md' : 'rounded-lg', style.ready ? 'opacity-100' : 'opacity-0', switchClass), style: {
|
|
1196
1807
|
transform: `translate(${style.left}px, ${style.top}px)`,
|
|
1197
1808
|
width: style.width,
|
|
1198
1809
|
height: style.height,
|
|
1199
|
-
} }), items.map((item) =>
|
|
1810
|
+
} }), items.map((item) => {
|
|
1811
|
+
const itemVal = getValue(item);
|
|
1812
|
+
const isSelected = selected === itemVal;
|
|
1813
|
+
const disabledState = isItemDisabled(item);
|
|
1814
|
+
return (jsxRuntime.jsx("button", { type: "button", role: "radio", "aria-checked": isSelected, "aria-disabled": disabledState, tabIndex: isSelected && !disabledState ? 0 : -1, disabled: disabledState, "data-radio-item": true, "data-value": itemVal, "data-selected": isSelected ? 'true' : undefined, onClick: () => {
|
|
1815
|
+
if (!disabledState) {
|
|
1816
|
+
onChange(itemVal);
|
|
1817
|
+
}
|
|
1818
|
+
}, className: buildClassName('relative z-10 appearance-none m-0 select-none group transition-colors flex flex-col items-center justify-center w-full text-center font-semibold focus:outline-none', hasDescription ? SIZE_ITEM_DESC_CLASS[size] : SIZE_ITEM_CLASS[size], disabledState ? 'cursor-not-allowed opacity-50' : 'cursor-pointer', isSelected
|
|
1819
|
+
? theme === 'secondary'
|
|
1820
|
+
? 'text-gray-900 dark:text-white'
|
|
1821
|
+
: 'text-white'
|
|
1822
|
+
: buildClassName('text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white', !disabledState && 'hover:bg-black/[0.04] dark:hover:bg-white/[0.06]')), children: getDescription(item) ? (jsxRuntime.jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsxRuntime.jsx("span", { className: "font-semibold leading-tight text-center truncate", children: getLabel(item) }), jsxRuntime.jsx("span", { className: buildClassName('text-[12px] leading-tight opacity-80 font-normal text-center truncate', descriptionClass), children: getDescription(item) })] })) : (jsxRuntime.jsx("span", { className: buildClassName('truncate leading-tight text-center', contentClass), children: getLabel(item) })) }, itemVal));
|
|
1823
|
+
})] }));
|
|
1200
1824
|
};
|
|
1825
|
+
RadioSwitch.displayName = 'RadioSwitch';
|
|
1201
1826
|
|
|
1202
1827
|
/**
|
|
1203
1828
|
* Checks if a value is an array.
|
|
@@ -1227,30 +1852,86 @@ function isEmpty(value) {
|
|
|
1227
1852
|
return false; // for numbers, booleans, functions, etc.
|
|
1228
1853
|
}
|
|
1229
1854
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1855
|
+
const sizeClasses$3 = {
|
|
1856
|
+
sm: {
|
|
1857
|
+
track: 'h-1.5',
|
|
1858
|
+
thumb: '[&::-webkit-slider-thumb]:h-3.5 [&::-webkit-slider-thumb]:w-3.5 [&::-moz-range-thumb]:h-3.5 [&::-moz-range-thumb]:w-3.5',
|
|
1859
|
+
text: 'text-xs',
|
|
1860
|
+
},
|
|
1861
|
+
md: {
|
|
1862
|
+
track: 'h-2',
|
|
1863
|
+
thumb: '[&::-webkit-slider-thumb]:h-4.5 [&::-webkit-slider-thumb]:w-4.5 [&::-moz-range-thumb]:h-4.5 [&::-moz-range-thumb]:w-4.5',
|
|
1864
|
+
text: 'text-sm',
|
|
1865
|
+
},
|
|
1866
|
+
lg: {
|
|
1867
|
+
track: 'h-3',
|
|
1868
|
+
thumb: '[&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:w-6 [&::-moz-range-thumb]:h-6 [&::-moz-range-thumb]:w-6',
|
|
1869
|
+
text: 'text-base',
|
|
1870
|
+
},
|
|
1871
|
+
};
|
|
1240
1872
|
const RangeInput = React.forwardRef((props, ref) => {
|
|
1241
|
-
const { label, labelClass = '', labelHint = null, containerClass = '', className = '', showValue = true, valueSuffix = '', error = null, showErrorMessage = true, id, min = 0, max = 100, value, ...inputProps } = props;
|
|
1873
|
+
const { label, labelClass = '', labelHint = null, containerClass = '', className = '', size = 'md', showValue = true, valueSuffix = '', showTooltip = false, marks, error = null, helperText, showErrorMessage = true, id, min = 0, max = 100, value, defaultValue, onChange, ...inputProps } = props;
|
|
1874
|
+
const [hovering, setHovering] = React.useState(false);
|
|
1875
|
+
const [internalValue, setInternalValue] = React.useState(Number(defaultValue !== null && defaultValue !== void 0 ? defaultValue : min));
|
|
1876
|
+
const isControlled = value !== undefined;
|
|
1877
|
+
const currentVal = Number(isControlled ? value : internalValue);
|
|
1878
|
+
const numMin = Number(min);
|
|
1879
|
+
const numMax = Number(max);
|
|
1880
|
+
const percent = numMax > numMin ? ((currentVal - numMin) / (numMax - numMin)) * 100 : 0;
|
|
1881
|
+
const currentSize = sizeClasses$3[size] || sizeClasses$3.md;
|
|
1242
1882
|
const hasError = !isEmpty(error);
|
|
1243
|
-
|
|
1244
|
-
|
|
1883
|
+
const handleSliderChange = (e) => {
|
|
1884
|
+
if (!isControlled) {
|
|
1885
|
+
setInternalValue(Number(e.target.value));
|
|
1886
|
+
}
|
|
1887
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
1888
|
+
};
|
|
1889
|
+
// Parse custom marks if object provided
|
|
1890
|
+
const markEntries = typeof marks === 'object' && marks !== null
|
|
1891
|
+
? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
|
|
1892
|
+
: typeof marks === 'boolean' && marks
|
|
1893
|
+
? [
|
|
1894
|
+
{ value: numMin, label: String(numMin) },
|
|
1895
|
+
{ value: (numMin + numMax) / 2, label: String(Math.round((numMin + numMax) / 2)) },
|
|
1896
|
+
{ value: numMax, label: String(numMax) },
|
|
1897
|
+
]
|
|
1898
|
+
: null;
|
|
1899
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { className: "relative flex items-center gap-3", children: [jsxRuntime.jsxs("div", { className: "relative flex-1 py-2", children: [showTooltip && hovering && (jsxRuntime.jsxs("div", { className: "absolute -top-7 transform -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none transition-all z-20", style: { left: `${percent}%` }, children: [currentVal, valueSuffix, jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })), jsxRuntime.jsx("input", { ref: ref, type: "range", id: id, min: min, max: max, value: currentVal, onChange: handleSliderChange, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false), ...inputProps, style: {
|
|
1900
|
+
background: `linear-gradient(to right, ${hasError ? '#ef4444' : '#2563eb'} 0%, ${hasError ? '#ef4444' : '#2563eb'} ${percent}%, ${document.documentElement.classList.contains('dark') ? '#374151' : '#e5e7eb'} ${percent}%, ${document.documentElement.classList.contains('dark') ? '#374151' : '#e5e7eb'} 100%)`,
|
|
1901
|
+
}, className: buildClassName('w-full rounded-lg appearance-none cursor-pointer transition-all', currentSize.track, currentSize.thumb, '[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full', '[&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-md', hasError
|
|
1902
|
+
? '[&::-webkit-slider-thumb]:border-red-600 [&::-moz-range-thumb]:border-red-600'
|
|
1903
|
+
: '[&::-webkit-slider-thumb]:border-blue-600 [&::-moz-range-thumb]:border-blue-600', '[&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:shadow-md', className) }), markEntries && (jsxRuntime.jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
|
|
1904
|
+
const markPercent = ((m.value - numMin) / (numMax - numMin)) * 100;
|
|
1905
|
+
return (jsxRuntime.jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsxRuntime.jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
|
|
1906
|
+
}) }))] }), showValue && (jsxRuntime.jsxs("span", { className: buildClassName('font-medium text-gray-700 dark:text-gray-300 min-w-[3rem] text-right shrink-0', currentSize.text), children: [currentVal, valueSuffix] }))] }), (showErrorMessage && hasError) || helperText ? (jsxRuntime.jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
|
|
1245
1907
|
});
|
|
1246
|
-
|
|
1247
|
-
|
|
1908
|
+
RangeInput.displayName = 'RangeInput';
|
|
1909
|
+
|
|
1910
|
+
const sizeClasses$2 = {
|
|
1911
|
+
sm: {
|
|
1912
|
+
track: 'h-1.5',
|
|
1913
|
+
thumb: 'h-4 w-4',
|
|
1914
|
+
thumbPx: 16,
|
|
1915
|
+
text: 'text-xs',
|
|
1916
|
+
},
|
|
1917
|
+
md: {
|
|
1918
|
+
track: 'h-2',
|
|
1919
|
+
thumb: 'h-5 w-5',
|
|
1920
|
+
thumbPx: 20,
|
|
1921
|
+
text: 'text-sm',
|
|
1922
|
+
},
|
|
1923
|
+
lg: {
|
|
1924
|
+
track: 'h-3',
|
|
1925
|
+
thumb: 'h-6 w-6',
|
|
1926
|
+
thumbPx: 24,
|
|
1927
|
+
text: 'text-base',
|
|
1928
|
+
},
|
|
1929
|
+
};
|
|
1930
|
+
const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', className = '', size = 'md', min, max, step = 1, valueMin, valueMax, onChange, valueSuffix = '', showTooltips = false, marks, error = null, helperText, showErrorMessage = true, }) => {
|
|
1248
1931
|
const trackRef = React.useRef(null);
|
|
1249
|
-
// Refs for drag logic
|
|
1250
1932
|
const activeThumbRef = React.useRef(null);
|
|
1251
|
-
// State for UI styling
|
|
1252
1933
|
const [activeThumbState, setActiveThumbState] = React.useState(null);
|
|
1253
|
-
|
|
1934
|
+
const [hoveringThumb, setHoveringThumb] = React.useState(null);
|
|
1254
1935
|
const valueMinRef = React.useRef(valueMin);
|
|
1255
1936
|
const valueMaxRef = React.useRef(valueMax);
|
|
1256
1937
|
const onChangeRef = React.useRef(onChange);
|
|
@@ -1258,20 +1939,28 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
|
|
|
1258
1939
|
valueMaxRef.current = valueMax;
|
|
1259
1940
|
onChangeRef.current = onChange;
|
|
1260
1941
|
const hasError = !isEmpty(error);
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1942
|
+
const currentSize = sizeClasses$2[size] || sizeClasses$2.md;
|
|
1943
|
+
const clampValue = (val) => Math.min(Math.max(val, min), max);
|
|
1944
|
+
const snapToStep = (val) => {
|
|
1945
|
+
const clamped = clampValue(val);
|
|
1946
|
+
const steps = Math.round((clamped - min) / step);
|
|
1947
|
+
return Math.min(Math.max(min + steps * step, min), max);
|
|
1948
|
+
};
|
|
1949
|
+
const percent = (v) => ((clampValue(v) - min) / (max - min)) * 100;
|
|
1264
1950
|
const stablePointerMoveRef = React.useRef((e) => {
|
|
1265
1951
|
if (!trackRef.current || !activeThumbRef.current)
|
|
1266
1952
|
return;
|
|
1267
1953
|
const rect = trackRef.current.getBoundingClientRect();
|
|
1268
1954
|
const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
|
|
1269
1955
|
const rawVal = min + (x / rect.width) * (max - min);
|
|
1956
|
+
const snappedVal = snapToStep(rawVal);
|
|
1270
1957
|
if (activeThumbRef.current === 'min') {
|
|
1271
|
-
|
|
1958
|
+
const nextMin = Math.min(snappedVal, valueMaxRef.current - step);
|
|
1959
|
+
onChangeRef.current(nextMin, valueMaxRef.current);
|
|
1272
1960
|
}
|
|
1273
1961
|
else {
|
|
1274
|
-
|
|
1962
|
+
const nextMax = Math.max(snappedVal, valueMinRef.current + step);
|
|
1963
|
+
onChangeRef.current(valueMinRef.current, nextMax);
|
|
1275
1964
|
}
|
|
1276
1965
|
});
|
|
1277
1966
|
const stablePointerUpRef = React.useRef(() => {
|
|
@@ -1280,7 +1969,6 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
|
|
|
1280
1969
|
window.removeEventListener('pointermove', stablePointerMoveRef.current);
|
|
1281
1970
|
window.removeEventListener('pointerup', stablePointerUpRef.current);
|
|
1282
1971
|
});
|
|
1283
|
-
// Remove listeners on unmount in case a drag is in progress
|
|
1284
1972
|
React.useEffect(() => {
|
|
1285
1973
|
const move = stablePointerMoveRef.current;
|
|
1286
1974
|
const up = stablePointerUpRef.current;
|
|
@@ -1289,59 +1977,91 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
|
|
|
1289
1977
|
window.removeEventListener('pointerup', up);
|
|
1290
1978
|
};
|
|
1291
1979
|
}, []);
|
|
1292
|
-
// ---------------------------------------------
|
|
1293
|
-
// Pointer Move (drag)
|
|
1294
|
-
// ---------------------------------------------
|
|
1295
1980
|
const startDrag = (thumb) => (_e) => {
|
|
1296
1981
|
activeThumbRef.current = thumb;
|
|
1297
1982
|
setActiveThumbState(thumb);
|
|
1298
1983
|
window.addEventListener('pointermove', stablePointerMoveRef.current);
|
|
1299
1984
|
window.addEventListener('pointerup', stablePointerUpRef.current);
|
|
1300
1985
|
};
|
|
1301
|
-
// ---------------------------------------------
|
|
1302
|
-
// Click Track to move closest thumb
|
|
1303
|
-
// ---------------------------------------------
|
|
1304
1986
|
const handleTrackClick = (e) => {
|
|
1305
1987
|
if (!trackRef.current)
|
|
1306
1988
|
return;
|
|
1307
1989
|
const rect = trackRef.current.getBoundingClientRect();
|
|
1308
1990
|
const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
|
|
1309
1991
|
const rawVal = min + (x / rect.width) * (max - min);
|
|
1992
|
+
const snappedVal = snapToStep(rawVal);
|
|
1310
1993
|
const pxMin = (percent(valueMin) / 100) * rect.width;
|
|
1311
1994
|
const pxMax = (percent(valueMax) / 100) * rect.width;
|
|
1312
|
-
// If click is left of min thumb → move min thumb
|
|
1313
1995
|
if (x <= pxMin) {
|
|
1314
|
-
onChange(
|
|
1996
|
+
onChange(Math.min(snappedVal, valueMax - step), valueMax);
|
|
1315
1997
|
return;
|
|
1316
1998
|
}
|
|
1317
|
-
// If click is right of max thumb → move max thumb
|
|
1318
1999
|
if (x >= pxMax) {
|
|
1319
|
-
onChange(valueMin,
|
|
2000
|
+
onChange(valueMin, Math.max(snappedVal, valueMin + step));
|
|
1320
2001
|
return;
|
|
1321
2002
|
}
|
|
1322
|
-
|
|
1323
|
-
const
|
|
1324
|
-
const distMax = Math.abs(rawVal - valueMax);
|
|
2003
|
+
const distMin = Math.abs(snappedVal - valueMin);
|
|
2004
|
+
const distMax = Math.abs(snappedVal - valueMax);
|
|
1325
2005
|
if (distMin < distMax) {
|
|
1326
|
-
onChange(
|
|
2006
|
+
onChange(Math.min(snappedVal, valueMax - step), valueMax);
|
|
1327
2007
|
}
|
|
1328
2008
|
else {
|
|
1329
|
-
onChange(valueMin,
|
|
2009
|
+
onChange(valueMin, Math.max(snappedVal, valueMin + step));
|
|
1330
2010
|
}
|
|
1331
2011
|
};
|
|
1332
|
-
|
|
2012
|
+
// Keyboard accessibility handler for thumbs
|
|
2013
|
+
const handleKeyDown = (thumb) => (e) => {
|
|
2014
|
+
let delta = 0;
|
|
2015
|
+
if (e.key === 'ArrowLeft' || e.key === 'ArrowDown')
|
|
2016
|
+
delta = -step;
|
|
2017
|
+
else if (e.key === 'ArrowRight' || e.key === 'ArrowUp')
|
|
2018
|
+
delta = step;
|
|
2019
|
+
else if (e.key === 'Home') {
|
|
2020
|
+
if (thumb === 'min')
|
|
2021
|
+
onChange(min, valueMax);
|
|
2022
|
+
else
|
|
2023
|
+
onChange(valueMin, valueMin + step);
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
else if (e.key === 'End') {
|
|
2027
|
+
if (thumb === 'min')
|
|
2028
|
+
onChange(valueMax - step, valueMax);
|
|
2029
|
+
else
|
|
2030
|
+
onChange(valueMin, max);
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
if (delta !== 0) {
|
|
2034
|
+
e.preventDefault();
|
|
2035
|
+
if (thumb === 'min') {
|
|
2036
|
+
const next = snapToStep(valueMin + delta);
|
|
2037
|
+
onChange(Math.min(next, valueMax - step), valueMax);
|
|
2038
|
+
}
|
|
2039
|
+
else {
|
|
2040
|
+
const next = snapToStep(valueMax + delta);
|
|
2041
|
+
onChange(valueMin, Math.max(next, valueMin + step));
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
const markEntries = marks
|
|
2046
|
+
? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
|
|
2047
|
+
: null;
|
|
2048
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full flex items-center cursor-pointer", "data-testid": "range-track", children: [jsxRuntime.jsx("div", { className: buildClassName('absolute top-1/2 left-0 right-0 rounded-full bg-gray-200 dark:bg-gray-700 -translate-y-1/2', currentSize.track) }), jsxRuntime.jsx("div", { className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full transition-all duration-150 ease-out', hasError ? 'bg-red-500' : 'bg-blue-600 dark:bg-blue-500', currentSize.track), style: {
|
|
1333
2049
|
left: `${percent(valueMin)}%`,
|
|
1334
2050
|
width: `${percent(valueMax) - percent(valueMin)}%`,
|
|
1335
|
-
} }), jsxRuntime.jsx("div", { onPointerDown: startDrag('min'), className: buildClassName('absolute top-1/2 -translate-y-1/2
|
|
1336
|
-
|
|
1337
|
-
left: `calc(${percent(valueMin)}% -
|
|
2051
|
+
} }), jsxRuntime.jsx("div", { role: "slider", tabIndex: 0, "aria-label": "Minimum value", "aria-valuenow": valueMin, "aria-valuemin": min, "aria-valuemax": valueMax - step, onPointerDown: startDrag('min'), onMouseEnter: () => setHoveringThumb('min'), onMouseLeave: () => setHoveringThumb(null), onKeyDown: handleKeyDown('min'), className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full focus:outline-none', 'bg-white border-2 shadow-md cursor-pointer touch-none', hasError ? 'border-red-600' : 'border-blue-600', 'transition-all duration-150 ease-out', currentSize.thumb, (activeThumbState === 'min' || hoveringThumb === 'min') &&
|
|
2052
|
+
(hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
|
|
2053
|
+
left: `calc(${percent(valueMin)}% - ${currentSize.thumbPx / 2}px)`,
|
|
1338
2054
|
zIndex: activeThumbState === 'min' ? 50 : 30,
|
|
1339
|
-
}, "data-testid": "thumb-min" }), jsxRuntime.jsx("div", { onPointerDown: startDrag('max'), className: buildClassName('absolute top-1/2 -translate-y-1/2
|
|
1340
|
-
|
|
1341
|
-
left: `calc(${percent(valueMax)}% -
|
|
2055
|
+
}, "data-testid": "thumb-min", children: showTooltips && (activeThumbState === 'min' || hoveringThumb === 'min') && (jsxRuntime.jsxs("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none z-50 whitespace-nowrap", children: [Math.round(valueMin), valueSuffix, jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })) }), jsxRuntime.jsx("div", { role: "slider", tabIndex: 0, "aria-label": "Maximum value", "aria-valuenow": valueMax, "aria-valuemin": valueMin + step, "aria-valuemax": max, onPointerDown: startDrag('max'), onMouseEnter: () => setHoveringThumb('max'), onMouseLeave: () => setHoveringThumb(null), onKeyDown: handleKeyDown('max'), className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full focus:outline-none', 'bg-white border-2 shadow-md cursor-pointer touch-none', hasError ? 'border-red-600' : 'border-blue-600', 'transition-all duration-150 ease-out', currentSize.thumb, (activeThumbState === 'max' || hoveringThumb === 'max') &&
|
|
2056
|
+
(hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
|
|
2057
|
+
left: `calc(${percent(valueMax)}% - ${currentSize.thumbPx / 2}px)`,
|
|
1342
2058
|
zIndex: activeThumbState === 'max' ? 50 : 40,
|
|
1343
|
-
}, "data-testid": "thumb-max"
|
|
2059
|
+
}, "data-testid": "thumb-max", children: showTooltips && (activeThumbState === 'max' || hoveringThumb === 'max') && (jsxRuntime.jsxs("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none z-50 whitespace-nowrap", children: [Math.round(valueMax), valueSuffix, jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })) })] }), markEntries && (jsxRuntime.jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
|
|
2060
|
+
const markPercent = percent(m.value);
|
|
2061
|
+
return (jsxRuntime.jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsxRuntime.jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
|
|
2062
|
+
}) })), jsxRuntime.jsxs("div", { className: buildClassName('flex justify-between font-medium text-gray-700 dark:text-gray-300', currentSize.text), children: [jsxRuntime.jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxRuntime.jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), (showErrorMessage && hasError) || helperText ? (jsxRuntime.jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
|
|
1344
2063
|
};
|
|
2064
|
+
RangeSlider.displayName = 'RangeSlider';
|
|
1345
2065
|
|
|
1346
2066
|
const useDebounce = (value, delay) => {
|
|
1347
2067
|
const [debouncedValue, setDebouncedValue] = React.useState(value);
|
|
@@ -1409,28 +2129,69 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
|
|
|
1409
2129
|
})));
|
|
1410
2130
|
};
|
|
1411
2131
|
|
|
1412
|
-
const
|
|
2132
|
+
const sizeClasses$1 = {
|
|
2133
|
+
sm: {
|
|
2134
|
+
input: 'px-2.5 py-1 text-xs leading-5 min-h-[30px]',
|
|
2135
|
+
group: 'w-8 text-xs',
|
|
2136
|
+
icon: 'size-3.5',
|
|
2137
|
+
badgeSize: 'sm',
|
|
2138
|
+
},
|
|
2139
|
+
md: {
|
|
2140
|
+
input: 'px-3.5 py-2 text-sm leading-6 min-h-[38px]',
|
|
2141
|
+
group: 'w-10 text-sm',
|
|
2142
|
+
icon: 'size-4',
|
|
2143
|
+
badgeSize: 'md',
|
|
2144
|
+
},
|
|
2145
|
+
lg: {
|
|
2146
|
+
input: 'px-4 py-2.5 text-base leading-7 min-h-[46px]',
|
|
2147
|
+
group: 'w-12 text-base',
|
|
2148
|
+
icon: 'size-5',
|
|
2149
|
+
badgeSize: 'md',
|
|
2150
|
+
},
|
|
2151
|
+
};
|
|
1413
2152
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
1414
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options, selected, label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, immediate = true, showErrorMessage = true, error = null, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
2153
|
+
const { labelKey = 'label', valueKey = 'value', name, id = name, options = [], groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
1415
2154
|
const hasError = Boolean(error);
|
|
1416
2155
|
const [query, setQuery] = React.useState('');
|
|
1417
2156
|
const debouncedQuery = useDebounce(query, 500);
|
|
1418
2157
|
const [optionsLoading, setOptionsLoading] = React.useState(false);
|
|
1419
|
-
|
|
1420
|
-
const
|
|
1421
|
-
|
|
2158
|
+
// Normalize options from options prop or groups prop
|
|
2159
|
+
const allOptions = React.useMemo(() => {
|
|
2160
|
+
if (groups === null || groups === void 0 ? void 0 : groups.length) {
|
|
2161
|
+
return groups.flatMap((g) => g.options);
|
|
2162
|
+
}
|
|
2163
|
+
return options;
|
|
2164
|
+
}, [groups, options]);
|
|
2165
|
+
const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
|
|
2166
|
+
const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
|
|
2167
|
+
const baseInputClass = React.useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
|
|
2168
|
+
const chevronIconClass = buildClassName(currentSize.icon, 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400');
|
|
1422
2169
|
const inputRef = React.useRef(null);
|
|
1423
2170
|
const containerRef = React.useRef(null);
|
|
1424
|
-
// Expose the input element to parent
|
|
1425
2171
|
React.useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
|
|
1426
2172
|
const filteredOptions = React.useMemo(() => {
|
|
1427
2173
|
if (query === '' || async)
|
|
1428
|
-
return
|
|
1429
|
-
return
|
|
1430
|
-
const
|
|
1431
|
-
return
|
|
2174
|
+
return allOptions;
|
|
2175
|
+
return allOptions.filter((opt) => {
|
|
2176
|
+
const lbl = typeof opt === 'string' ? opt : opt[labelKey];
|
|
2177
|
+
return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
|
|
1432
2178
|
});
|
|
1433
|
-
}, [
|
|
2179
|
+
}, [allOptions, query, async, labelKey]);
|
|
2180
|
+
const filteredGroups = React.useMemo(() => {
|
|
2181
|
+
if (!(groups === null || groups === void 0 ? void 0 : groups.length))
|
|
2182
|
+
return null;
|
|
2183
|
+
if (query === '' || async)
|
|
2184
|
+
return groups;
|
|
2185
|
+
return groups
|
|
2186
|
+
.map((g) => ({
|
|
2187
|
+
...g,
|
|
2188
|
+
options: g.options.filter((opt) => {
|
|
2189
|
+
const lbl = typeof opt === 'string' ? opt : opt[labelKey];
|
|
2190
|
+
return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
|
|
2191
|
+
}),
|
|
2192
|
+
}))
|
|
2193
|
+
.filter((g) => g.options.length > 0);
|
|
2194
|
+
}, [groups, query, async, labelKey]);
|
|
1434
2195
|
const getDisplayValue = React.useCallback((opt) => {
|
|
1435
2196
|
return typeof opt === 'string' ? opt : opt[labelKey];
|
|
1436
2197
|
}, [labelKey]);
|
|
@@ -1446,69 +2207,48 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
1446
2207
|
onChange(next);
|
|
1447
2208
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1448
2209
|
};
|
|
1449
|
-
// A strongly-typed internal handler (what you expect callers to receive)
|
|
1450
2210
|
const handleOnChangeInternal = React.useCallback((value) => {
|
|
1451
|
-
// normal empty value
|
|
1452
2211
|
if (value === null) {
|
|
1453
2212
|
onChange(multiple ? [] : null);
|
|
1454
2213
|
setQuery('');
|
|
1455
2214
|
return;
|
|
1456
2215
|
}
|
|
1457
|
-
// enforce maxSelection
|
|
1458
2216
|
if (multiple && Array.isArray(value) && maxSelection && value.length > maxSelection) {
|
|
1459
|
-
// ignore the change
|
|
1460
2217
|
return;
|
|
1461
2218
|
}
|
|
1462
2219
|
onChange(value);
|
|
1463
2220
|
setQuery('');
|
|
1464
2221
|
}, [onChange, multiple, maxSelection]);
|
|
1465
|
-
/**
|
|
1466
|
-
* Headless UI typings are overly broad (they include null[] and EnsureArray<T> etc).
|
|
1467
|
-
* We therefore pass a wrapper to the Combobox with a cast (only at the call site),
|
|
1468
|
-
* while keeping our internal logic strongly typed.
|
|
1469
|
-
*/
|
|
1470
2222
|
const comboboxOnChangeWrapper = React.useCallback((raw) => {
|
|
1471
|
-
// normalize actual runtime shapes Headless UI uses: T | T[] | null
|
|
1472
2223
|
if (raw === null || raw === undefined) {
|
|
1473
2224
|
handleOnChangeInternal(null);
|
|
1474
2225
|
return;
|
|
1475
2226
|
}
|
|
1476
2227
|
if (Array.isArray(raw)) {
|
|
1477
|
-
// sometimes union typing suggests arrays of nulls — guard it
|
|
1478
2228
|
const allNulls = raw.length > 0 && raw.every((v) => v === null);
|
|
1479
2229
|
if (allNulls) {
|
|
1480
2230
|
handleOnChangeInternal([]);
|
|
1481
2231
|
return;
|
|
1482
2232
|
}
|
|
1483
|
-
// cast array to T[]
|
|
1484
2233
|
handleOnChangeInternal(raw);
|
|
1485
2234
|
return;
|
|
1486
2235
|
}
|
|
1487
|
-
// single value
|
|
1488
2236
|
handleOnChangeInternal(raw);
|
|
1489
2237
|
}, [handleOnChangeInternal]);
|
|
1490
|
-
// support create (allowAdd) as a real ComboboxOption
|
|
1491
2238
|
const handleAddAndSelect = React.useCallback(async (valueToAdd) => {
|
|
1492
2239
|
try {
|
|
1493
2240
|
const result = await onAdd(valueToAdd);
|
|
1494
|
-
// if the parent returned a new option (string or object), select it
|
|
1495
2241
|
if (result) {
|
|
1496
|
-
// If parent returned raw string or object, call onChange with that
|
|
1497
2242
|
handleOnChangeInternal(result);
|
|
1498
2243
|
}
|
|
1499
2244
|
else {
|
|
1500
|
-
// if parent didn't return anything, select the raw string value
|
|
1501
2245
|
handleOnChangeInternal(valueToAdd);
|
|
1502
2246
|
}
|
|
1503
2247
|
}
|
|
1504
2248
|
catch (err) {
|
|
1505
|
-
// eslint-disable-next-line no-console
|
|
1506
2249
|
console.error('onAdd handler error', err);
|
|
1507
2250
|
}
|
|
1508
2251
|
}, [onAdd, handleOnChangeInternal]);
|
|
1509
|
-
// Kept in a ref so `handleSearch` stays referentially stable even when the
|
|
1510
|
-
// caller passes a non-memoized `onSearch` — otherwise the effect below would
|
|
1511
|
-
// re-fire (and re-search) on every render instead of only on query changes.
|
|
1512
2252
|
const onSearchRef = React.useRef(props.onSearch);
|
|
1513
2253
|
React.useEffect(() => {
|
|
1514
2254
|
onSearchRef.current = props.onSearch;
|
|
@@ -1530,16 +2270,232 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
1530
2270
|
if (async && debouncedQuery)
|
|
1531
2271
|
handleSearch(debouncedQuery);
|
|
1532
2272
|
}, [debouncedQuery, async, handleSearch]);
|
|
1533
|
-
|
|
2273
|
+
const isAllSelected = React.useMemo(() => {
|
|
2274
|
+
if (!multiple || !Array.isArray(selected) || !allOptions.length)
|
|
2275
|
+
return false;
|
|
2276
|
+
return allOptions.length === selected.length;
|
|
2277
|
+
}, [multiple, selected, allOptions]);
|
|
2278
|
+
const handleToggleSelectAll = () => {
|
|
2279
|
+
if (isAllSelected) {
|
|
2280
|
+
onChange([]);
|
|
2281
|
+
}
|
|
2282
|
+
else {
|
|
2283
|
+
onChange(allOptions);
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
2287
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray(selected) &&
|
|
1534
2288
|
(selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
|
|
1535
|
-
selected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
|
|
2289
|
+
selected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400 bg-transparent", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
|
|
1536
2290
|
if (multiple && e.key === 'Backspace' && !query) {
|
|
1537
2291
|
if (Array.isArray(selected) && selected.length > 0) {
|
|
1538
2292
|
removeSelection(selected.length - 1);
|
|
1539
2293
|
}
|
|
1540
2294
|
}
|
|
1541
|
-
}, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [jsxRuntime.jsx(react.ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className:
|
|
2295
|
+
}, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500 z-10', currentSize.group), children: leftGroup })), jsxRuntime.jsx(react.ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: buildClassName(baseInputClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsxRuntime.jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsxs(react.ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", modal: modalDropdown, className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: [multiple && showSelectAll && allOptions.length > 0 && (jsxRuntime.jsxs("div", { className: "pb-1 mb-1 border-b border-gray-200 dark:border-gray-700/80 px-3 pt-1 flex items-center justify-between", children: [jsxRuntime.jsx("button", { type: "button", onClick: handleToggleSelectAll, className: "text-xs font-semibold text-blue-600 dark:text-blue-400 hover:underline cursor-pointer", children: isAllSelected ? 'Deselect All' : 'Select All' }), jsxRuntime.jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(selected) ? selected.length : 0, " of ", allOptions.length] })] })), optionsLoading ? (jsxRuntime.jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredGroups
|
|
2296
|
+
? filteredGroups.map((grp, gIdx) => (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-[11px] font-bold uppercase tracking-wider text-gray-400 dark:text-gray-500 px-3 py-1 select-none", children: grp.group }), grp.options.map((option) => {
|
|
2297
|
+
const optVal = getValue(option);
|
|
2298
|
+
const isOptSelected = multiple
|
|
2299
|
+
? isArray(selected) && selected.some((s) => getValue(s) === optVal)
|
|
2300
|
+
: selected && getValue(selected) === optVal;
|
|
2301
|
+
return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
2302
|
+
})] }, gIdx)))
|
|
2303
|
+
: /* Render Standard Flat Options */
|
|
2304
|
+
filteredOptions.map((option) => {
|
|
2305
|
+
const optVal = getValue(option);
|
|
2306
|
+
const isOptSelected = multiple
|
|
2307
|
+
? isArray(selected) && selected.some((s) => getValue(s) === optVal)
|
|
2308
|
+
: selected && getValue(selected) === optVal;
|
|
2309
|
+
return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
2310
|
+
}), filteredOptions.length === 0 && !(filteredGroups === null || filteredGroups === void 0 ? void 0 : filteredGroups.length) && (jsxRuntime.jsx("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: query ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["No results found for ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })) : (noOptionsText) })), allowAdd && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] }))] })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
|
|
2311
|
+
});
|
|
2312
|
+
SelectBox.displayName = 'SelectBox';
|
|
2313
|
+
|
|
2314
|
+
const VARIANT_CLASS = {
|
|
2315
|
+
text: 'rounded-md h-4 w-full my-1',
|
|
2316
|
+
circular: 'rounded-full shrink-0',
|
|
2317
|
+
rectangular: 'rounded-none w-full h-24',
|
|
2318
|
+
rounded: 'rounded-xl w-full h-24',
|
|
2319
|
+
};
|
|
2320
|
+
const ANIMATION_CLASS = {
|
|
2321
|
+
pulse: 'animate-pulse bg-gray-200 dark:bg-gray-700',
|
|
2322
|
+
wave: 'bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 dark:from-gray-800 dark:via-gray-700 dark:to-gray-800 bg-[length:200%_100%] animate-[shimmer_1.5s_infinite]',
|
|
2323
|
+
none: 'bg-gray-200 dark:bg-gray-700',
|
|
2324
|
+
};
|
|
2325
|
+
const Skeleton = React.forwardRef((props, ref) => {
|
|
2326
|
+
const { as, variant = 'text', animation = 'pulse', width, height, borderRadius, className, style: customStyle, ...restProps } = props;
|
|
2327
|
+
const Component = as || 'div';
|
|
2328
|
+
const inlineStyles = {
|
|
2329
|
+
...(width !== undefined ? { width: typeof width === 'number' ? `${width}px` : width } : {}),
|
|
2330
|
+
...(height !== undefined
|
|
2331
|
+
? { height: typeof height === 'number' ? `${height}px` : height }
|
|
2332
|
+
: {}),
|
|
2333
|
+
...(borderRadius !== undefined
|
|
2334
|
+
? { borderRadius: typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius }
|
|
2335
|
+
: {}),
|
|
2336
|
+
...customStyle,
|
|
2337
|
+
};
|
|
2338
|
+
return (jsxRuntime.jsx(Component, { ref: ref, "aria-hidden": "true", style: inlineStyles, className: buildClassName('block select-none pointer-events-none', VARIANT_CLASS[variant], ANIMATION_CLASS[animation], className), "data-testid": "skeleton", ...restProps }));
|
|
2339
|
+
});
|
|
2340
|
+
Skeleton.displayName = 'Skeleton';
|
|
2341
|
+
|
|
2342
|
+
const MinusIcon = ({ className }) => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) }));
|
|
2343
|
+
const PlusIcon = ({ className }) => (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: [jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
|
|
2344
|
+
const BUTTON_SIZE_CLASS = {
|
|
2345
|
+
sm: 'size-7',
|
|
2346
|
+
md: 'size-9',
|
|
2347
|
+
lg: 'size-10',
|
|
2348
|
+
};
|
|
2349
|
+
const ICON_SIZE_CLASS = {
|
|
2350
|
+
sm: 'size-3.5',
|
|
2351
|
+
md: 'size-4',
|
|
2352
|
+
lg: 'size-4.5',
|
|
2353
|
+
};
|
|
2354
|
+
const INPUT_TEXT_CLASS = {
|
|
2355
|
+
sm: 'w-12 text-xs py-1',
|
|
2356
|
+
md: 'w-16 text-sm py-1.5',
|
|
2357
|
+
lg: 'w-20 text-base py-2',
|
|
2358
|
+
};
|
|
2359
|
+
const QuantityStepper = React.forwardRef((props, ref) => {
|
|
2360
|
+
const { value, onChange, min = 0, max = 99999, step = 1, label, size = 'md', prefix, suffix, disabled = false, className, onFocus, ...restProps } = props;
|
|
2361
|
+
const handleDecrement = () => {
|
|
2362
|
+
if (disabled)
|
|
2363
|
+
return;
|
|
2364
|
+
const next = Math.max(min, Number((value - step).toFixed(4)));
|
|
2365
|
+
onChange(next);
|
|
2366
|
+
};
|
|
2367
|
+
const handleIncrement = () => {
|
|
2368
|
+
if (disabled)
|
|
2369
|
+
return;
|
|
2370
|
+
const next = Math.min(max, Number((value + step).toFixed(4)));
|
|
2371
|
+
onChange(next);
|
|
2372
|
+
};
|
|
2373
|
+
const handleInputChange = (e) => {
|
|
2374
|
+
const raw = e.target.value;
|
|
2375
|
+
if (raw === '') {
|
|
2376
|
+
onChange(0);
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
const parsed = parseFloat(raw);
|
|
2380
|
+
if (!isNaN(parsed)) {
|
|
2381
|
+
onChange(Math.max(min, Math.min(max, parsed)));
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
const isAtMin = disabled || value <= min;
|
|
2385
|
+
const isAtMax = disabled || value >= max;
|
|
2386
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('flex flex-col gap-1 inline-flex', className), children: [label && (jsxRuntime.jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300", children: label })), jsxRuntime.jsxs("div", { className: buildClassName('inline-flex items-center rounded-lg border border-gray-300 bg-white dark:border-gray-600 dark:bg-gray-800 shadow-xs focus-within:ring-2 focus-within:ring-[var(--ui-primary-ring)] focus-within:border-[var(--ui-primary)] transition-all', disabled ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-900' : ''), "data-testid": "quantity-stepper", children: [jsxRuntime.jsx("button", { type: "button", disabled: isAtMin, onClick: handleDecrement, tabIndex: -1, "aria-label": "Decrease quantity", className: buildClassName('flex items-center justify-center rounded-l-md text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 transition-colors', BUTTON_SIZE_CLASS[size], isAtMin ? 'opacity-30 hover:bg-transparent cursor-not-allowed' : 'cursor-pointer'), "data-testid": "stepper-decrement", children: jsxRuntime.jsx(MinusIcon, { className: ICON_SIZE_CLASS[size] }) }), prefix && jsxRuntime.jsx("span", { className: "pl-1 text-gray-400 dark:text-gray-500", children: prefix }), jsxRuntime.jsx("input", { ref: ref, type: "number", min: min, max: max, step: step, value: value === 0 ? '' : value, placeholder: "0", disabled: disabled, onFocus: (e) => {
|
|
2387
|
+
e.target.select();
|
|
2388
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
|
|
2389
|
+
}, onChange: handleInputChange, className: buildClassName('text-center font-semibold text-gray-900 dark:text-white bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', INPUT_TEXT_CLASS[size]), "data-testid": "stepper-input", ...restProps }), suffix && jsxRuntime.jsx("span", { className: "pr-1 text-xs text-gray-500 dark:text-gray-400", children: suffix }), jsxRuntime.jsx("button", { type: "button", disabled: isAtMax, onClick: handleIncrement, tabIndex: -1, "aria-label": "Increase quantity", className: buildClassName('flex items-center justify-center rounded-r-md text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 transition-colors', BUTTON_SIZE_CLASS[size], isAtMax ? 'opacity-30 hover:bg-transparent cursor-not-allowed' : 'cursor-pointer'), "data-testid": "stepper-increment", children: jsxRuntime.jsx(PlusIcon, { className: ICON_SIZE_CLASS[size] }) })] })] }));
|
|
2390
|
+
});
|
|
2391
|
+
QuantityStepper.displayName = 'QuantityStepper';
|
|
2392
|
+
|
|
2393
|
+
const SIZE_CONTAINER_CLASS = {
|
|
2394
|
+
sm: 'p-0.5 gap-0.5 rounded-lg text-xs',
|
|
2395
|
+
md: 'p-1 gap-1 rounded-xl text-sm',
|
|
2396
|
+
lg: 'p-1.5 gap-1.5 rounded-xl text-base',
|
|
2397
|
+
};
|
|
2398
|
+
const SIZE_OPTION_CLASS = {
|
|
2399
|
+
sm: 'px-2 py-1 text-xs font-medium rounded-md',
|
|
2400
|
+
md: 'px-3 py-1.5 text-sm font-semibold rounded-lg',
|
|
2401
|
+
lg: 'px-4 py-2 text-base font-semibold rounded-lg',
|
|
2402
|
+
};
|
|
2403
|
+
const SIZE_ICON_CLASS = {
|
|
2404
|
+
sm: 'size-3.5',
|
|
2405
|
+
md: 'size-4',
|
|
2406
|
+
lg: 'size-5',
|
|
2407
|
+
};
|
|
2408
|
+
const SegmentedControl = React.forwardRef((props, ref) => {
|
|
2409
|
+
const { options, value, onChange, disabled = false, size = 'md', fullWidth = false, className, ...restProps } = props;
|
|
2410
|
+
const handleKeyDown = (e, currentIndex) => {
|
|
2411
|
+
const enabledOptions = options.filter((opt) => !opt.disabled && !disabled);
|
|
2412
|
+
if (enabledOptions.length === 0)
|
|
2413
|
+
return;
|
|
2414
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
2415
|
+
e.preventDefault();
|
|
2416
|
+
const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
|
|
2417
|
+
const nextPos = (currentPos + 1) % enabledOptions.length;
|
|
2418
|
+
onChange(enabledOptions[nextPos].value);
|
|
2419
|
+
}
|
|
2420
|
+
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
2421
|
+
e.preventDefault();
|
|
2422
|
+
const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
|
|
2423
|
+
const prevPos = (currentPos - 1 + enabledOptions.length) % enabledOptions.length;
|
|
2424
|
+
onChange(enabledOptions[prevPos].value);
|
|
2425
|
+
}
|
|
2426
|
+
else if (e.key === 'Home') {
|
|
2427
|
+
e.preventDefault();
|
|
2428
|
+
onChange(enabledOptions[0].value);
|
|
2429
|
+
}
|
|
2430
|
+
else if (e.key === 'End') {
|
|
2431
|
+
e.preventDefault();
|
|
2432
|
+
onChange(enabledOptions[enabledOptions.length - 1].value);
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
return (jsxRuntime.jsx("div", { ref: ref, role: "tablist", "aria-orientation": "horizontal", className: buildClassName('inline-flex items-center border border-gray-200 bg-gray-100/80 dark:border-gray-700 dark:bg-gray-800/80', SIZE_CONTAINER_CLASS[size], fullWidth ? 'w-full flex' : '', disabled ? 'opacity-60 cursor-not-allowed' : '', className), "data-testid": "segmented-control", ...restProps, children: options.map((option, index) => {
|
|
2436
|
+
const isSelected = value === option.value;
|
|
2437
|
+
const isOptionDisabled = disabled || option.disabled;
|
|
2438
|
+
return (jsxRuntime.jsxs("button", { type: "button", role: "tab", "aria-selected": isSelected, "aria-disabled": isOptionDisabled, tabIndex: isSelected && !isOptionDisabled ? 0 : -1, disabled: isOptionDisabled, title: option.title, onClick: () => {
|
|
2439
|
+
if (!isOptionDisabled) {
|
|
2440
|
+
onChange(option.value);
|
|
2441
|
+
}
|
|
2442
|
+
}, onKeyDown: (e) => handleKeyDown(e, index), className: buildClassName('inline-flex items-center justify-center gap-1.5 transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)]', SIZE_OPTION_CLASS[size], fullWidth ? 'flex-1' : '', isOptionDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer', isSelected
|
|
2443
|
+
? 'bg-white text-[var(--ui-primary)] dark:bg-gray-700 dark:text-blue-400 shadow-xs font-semibold'
|
|
2444
|
+
: 'text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100'), "data-testid": `segmented-option-${option.value}`, children: [option.icon && (jsxRuntime.jsx("span", { className: buildClassName('shrink-0 inline-flex items-center justify-center', SIZE_ICON_CLASS[size]), children: option.icon })), jsxRuntime.jsx("span", { children: option.label })] }, option.value));
|
|
2445
|
+
}) }));
|
|
1542
2446
|
});
|
|
2447
|
+
SegmentedControl.displayName = 'SegmentedControl';
|
|
2448
|
+
|
|
2449
|
+
const THEME_PILL_CLASS = {
|
|
2450
|
+
success: {
|
|
2451
|
+
bg: 'bg-emerald-500/10 text-emerald-700 dark:text-emerald-300 border-emerald-500/20 dark:border-emerald-500/30',
|
|
2452
|
+
dot: 'bg-emerald-500',
|
|
2453
|
+
},
|
|
2454
|
+
warning: {
|
|
2455
|
+
bg: 'bg-amber-500/10 text-amber-700 dark:text-amber-300 border-amber-500/20 dark:border-amber-500/30',
|
|
2456
|
+
dot: 'bg-amber-500',
|
|
2457
|
+
},
|
|
2458
|
+
danger: {
|
|
2459
|
+
bg: 'bg-rose-500/10 text-rose-700 dark:text-rose-300 border-rose-500/20 dark:border-rose-500/30',
|
|
2460
|
+
dot: 'bg-rose-500',
|
|
2461
|
+
},
|
|
2462
|
+
info: {
|
|
2463
|
+
bg: 'bg-blue-500/10 text-blue-700 dark:text-blue-300 border-blue-500/20 dark:border-blue-500/30',
|
|
2464
|
+
dot: 'bg-blue-500',
|
|
2465
|
+
},
|
|
2466
|
+
secondary: {
|
|
2467
|
+
bg: 'bg-gray-500/10 text-gray-600 dark:text-gray-400 border-gray-500/20 dark:border-gray-500/30',
|
|
2468
|
+
dot: 'bg-gray-400',
|
|
2469
|
+
},
|
|
2470
|
+
};
|
|
2471
|
+
const SIZE_CLASS = {
|
|
2472
|
+
sm: 'px-2.5 py-0.5 text-xs',
|
|
2473
|
+
md: 'px-3 py-1 text-sm',
|
|
2474
|
+
};
|
|
2475
|
+
const DOT_SIZE_CLASS = {
|
|
2476
|
+
sm: 'size-1.5',
|
|
2477
|
+
md: 'size-2',
|
|
2478
|
+
};
|
|
2479
|
+
const StatusPill = React.forwardRef((props, ref) => {
|
|
2480
|
+
var _a;
|
|
2481
|
+
const { theme = 'secondary', size = 'sm', dot = true, pulse = false, className, children, ...restProps } = props;
|
|
2482
|
+
const style = (_a = THEME_PILL_CLASS[theme]) !== null && _a !== void 0 ? _a : THEME_PILL_CLASS.secondary;
|
|
2483
|
+
return (jsxRuntime.jsxs("span", { ref: ref, className: buildClassName('inline-flex items-center gap-1.5 rounded-full border font-semibold whitespace-nowrap transition-colors', SIZE_CLASS[size], style.bg, className), "data-testid": `status-pill-${theme}`, ...restProps, children: [dot && (jsxRuntime.jsx("span", { className: buildClassName('shrink-0 rounded-full', DOT_SIZE_CLASS[size], style.dot, pulse ? 'animate-pulse' : ''), "data-testid": "status-pill-dot", "aria-hidden": "true" })), jsxRuntime.jsx("span", { children: children })] }));
|
|
2484
|
+
});
|
|
2485
|
+
StatusPill.displayName = 'StatusPill';
|
|
2486
|
+
|
|
2487
|
+
const ChevronUpIcon = ({ className }) => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" }) }));
|
|
2488
|
+
const StickyActionBar = React.forwardRef((props, ref) => {
|
|
2489
|
+
const { summaryContent, actionsContent, drawerContent, drawerTitle = 'Summary Details', position = 'fixed', containerMaxWidth = 'max-w-5xl', className, ...restProps } = props;
|
|
2490
|
+
const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
|
|
2491
|
+
const positionClass = position === 'fixed'
|
|
2492
|
+
? 'fixed bottom-0 left-0 right-0 z-30'
|
|
2493
|
+
: position === 'sticky'
|
|
2494
|
+
? 'sticky bottom-0 z-30'
|
|
2495
|
+
: 'relative z-10';
|
|
2496
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: ref, className: buildClassName(positionClass, 'border-t border-gray-200/80 bg-white/90 p-3.5 backdrop-blur-md shadow-lg dark:border-gray-800 dark:bg-gray-900/90 transition-all', className), "data-testid": "sticky-action-bar", ...restProps, children: jsxRuntime.jsxs("div", { className: buildClassName('mx-auto flex items-center justify-between gap-3', containerMaxWidth), children: [summaryContent && (jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: drawerContent ? (jsxRuntime.jsxs("button", { type: "button", onClick: () => setIsDrawerOpen(true), className: "flex items-center gap-2 text-left transition-opacity hover:opacity-80 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)] rounded-lg p-1 -m-1", "aria-label": "Expand summary details", "data-testid": "summary-trigger", children: [jsxRuntime.jsx("div", { children: summaryContent }), jsxRuntime.jsx(ChevronUpIcon, { className: "size-4 text-[var(--ui-primary)] dark:text-blue-400 shrink-0" })] })) : (jsxRuntime.jsx("div", { children: summaryContent })) })), actionsContent && (jsxRuntime.jsx("div", { className: "flex items-center gap-2 shrink-0 ml-auto", "data-testid": "actions-stack", children: actionsContent }))] }) }), drawerContent && (jsxRuntime.jsx(Drawer, { isOpen: isDrawerOpen, onClose: () => setIsDrawerOpen(false), align: "bottom", size: "full", title: drawerTitle, showBackButton: true, contentClass: "px-4 pb-6 max-h-[calc(100svh-6rem)] overflow-y-auto", children: drawerContent }))] }));
|
|
2497
|
+
});
|
|
2498
|
+
StickyActionBar.displayName = 'StickyActionBar';
|
|
1543
2499
|
|
|
1544
2500
|
const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {
|
|
1545
2501
|
const isVertical = orientation === 'vertical';
|
|
@@ -1560,7 +2516,7 @@ const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'unde
|
|
|
1560
2516
|
}) })] }));
|
|
1561
2517
|
};
|
|
1562
2518
|
function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
|
|
1563
|
-
const base = buildClassName('inline-flex items-center gap-1.5 text-sm font-medium transition focus-visible:outline-none', 'disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none', stretch && !isVertical && 'flex-1 justify-center');
|
|
2519
|
+
const base = buildClassName('inline-flex items-center gap-1.5 text-sm font-medium transition focus:outline-none focus-visible:outline-none focus:ring-0 select-none', 'disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none', stretch && !isVertical && 'flex-1 justify-center');
|
|
1564
2520
|
let variantClass;
|
|
1565
2521
|
switch (variant) {
|
|
1566
2522
|
case 'underline':
|
|
@@ -1585,13 +2541,58 @@ function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
|
|
|
1585
2541
|
return buildClassName(base, variantClass, tabClass);
|
|
1586
2542
|
}
|
|
1587
2543
|
|
|
2544
|
+
const sizeClasses = {
|
|
2545
|
+
sm: {
|
|
2546
|
+
textarea: 'px-2.5 py-1.5 text-xs leading-5 min-h-[60px]',
|
|
2547
|
+
text: 'text-xs',
|
|
2548
|
+
},
|
|
2549
|
+
md: {
|
|
2550
|
+
textarea: 'px-3.5 py-2 text-sm leading-6 min-h-[80px]',
|
|
2551
|
+
text: 'text-sm',
|
|
2552
|
+
},
|
|
2553
|
+
lg: {
|
|
2554
|
+
textarea: 'px-4 py-2.5 text-base leading-7 min-h-[100px]',
|
|
2555
|
+
text: 'text-base',
|
|
2556
|
+
},
|
|
2557
|
+
};
|
|
2558
|
+
const resizeClasses = {
|
|
2559
|
+
none: 'resize-none',
|
|
2560
|
+
vertical: 'resize-y',
|
|
2561
|
+
horizontal: 'resize-x',
|
|
2562
|
+
both: 'resize',
|
|
2563
|
+
};
|
|
1588
2564
|
const Textarea = React.forwardRef((props, ref) => {
|
|
1589
|
-
const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, ...inputProps } = props;
|
|
2565
|
+
const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, size = 'md', autoSize = false, showCount = false, helperText, resize = 'vertical', value, defaultValue, onChange, maxLength, ...inputProps } = props;
|
|
2566
|
+
const innerRef = React.useRef(null);
|
|
2567
|
+
React.useImperativeHandle(ref, () => innerRef.current);
|
|
2568
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
2569
|
+
const isControlled = value !== undefined;
|
|
2570
|
+
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
2571
|
+
const charLength = currentVal.length;
|
|
2572
|
+
// Handle Auto-Resize behavior
|
|
2573
|
+
React.useEffect(() => {
|
|
2574
|
+
if (autoSize && innerRef.current) {
|
|
2575
|
+
innerRef.current.style.height = 'auto';
|
|
2576
|
+
innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
|
|
2577
|
+
}
|
|
2578
|
+
}, [autoSize, currentVal]);
|
|
2579
|
+
const handleTextareaChange = (e) => {
|
|
2580
|
+
if (!isControlled) {
|
|
2581
|
+
setUncontrolledValue(e.target.value);
|
|
2582
|
+
}
|
|
2583
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
2584
|
+
};
|
|
1590
2585
|
const hasError = !isEmpty(error);
|
|
1591
2586
|
const hasRightGroup = rightGroup != null && rightGroup !== undefined;
|
|
1592
2587
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
1593
|
-
|
|
2588
|
+
const currentSize = sizeClasses[size] || sizeClasses.md;
|
|
2589
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "relative", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: "absolute top-2.5 left-3 flex items-center justify-center text-gray-400 dark:text-gray-500", children: leftGroup })), jsxRuntime.jsx("textarea", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleTextareaChange, maxLength: maxLength, name: name, id: id, className: buildClassName('transition-all duration-150', 'block w-full rounded-lg bg-white', currentSize.textarea, autoSize ? 'resize-none overflow-hidden' : resizeClasses[resize], 'text-gray-900 outline outline-1 -outline-offset-1', hasError
|
|
2590
|
+
? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
|
|
2591
|
+
: 'outline-gray-300 dark:outline-gray-600', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-gray-200', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-gray-700', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsxRuntime.jsx("span", { className: "absolute end-3 top-2.5 flex items-center justify-center text-gray-400 dark:text-gray-500", children: rightGroup }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2592
|
+
? 'text-red-500 font-semibold'
|
|
2593
|
+
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
1594
2594
|
});
|
|
2595
|
+
Textarea.displayName = 'Textarea';
|
|
1595
2596
|
|
|
1596
2597
|
/**
|
|
1597
2598
|
* Icons for each toast type
|
|
@@ -1602,23 +2603,76 @@ const iconMap = {
|
|
|
1602
2603
|
info: jsxRuntime.jsx(SvgInfo, { className: "size-5 text-[var(--ui-info)]" }),
|
|
1603
2604
|
warning: jsxRuntime.jsx(SvgTriangleAlert, { className: "size-5 text-[var(--ui-warning)]" }),
|
|
1604
2605
|
};
|
|
2606
|
+
const filledIconMap = {
|
|
2607
|
+
success: jsxRuntime.jsx(SvgCircleCheck, { className: "size-5 text-white" }),
|
|
2608
|
+
error: jsxRuntime.jsx(SvgCircleAlert, { className: "size-5 text-white" }),
|
|
2609
|
+
info: jsxRuntime.jsx(SvgInfo, { className: "size-5 text-white" }),
|
|
2610
|
+
warning: jsxRuntime.jsx(SvgTriangleAlert, { className: "size-5 text-white" }),
|
|
2611
|
+
};
|
|
1605
2612
|
const textColorMap = {
|
|
1606
2613
|
success: 'text-[var(--ui-success-text)]',
|
|
1607
2614
|
error: 'text-[var(--ui-danger-text)]',
|
|
1608
2615
|
info: 'text-[var(--ui-info-text)]',
|
|
1609
2616
|
warning: 'text-[var(--ui-warning-text)]',
|
|
1610
2617
|
};
|
|
1611
|
-
const
|
|
1612
|
-
success: '
|
|
1613
|
-
error: '
|
|
1614
|
-
info: '
|
|
1615
|
-
warning: '
|
|
2618
|
+
const progressBarColorMap = {
|
|
2619
|
+
success: 'bg-[var(--ui-success)]',
|
|
2620
|
+
error: 'bg-[var(--ui-danger)]',
|
|
2621
|
+
info: 'bg-[var(--ui-info)]',
|
|
2622
|
+
warning: 'bg-[var(--ui-warning)]',
|
|
2623
|
+
};
|
|
2624
|
+
const variantStyleMap = {
|
|
2625
|
+
accent: {
|
|
2626
|
+
success: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
|
|
2627
|
+
error: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
|
|
2628
|
+
info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
|
|
2629
|
+
warning: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
|
|
2630
|
+
},
|
|
2631
|
+
filled: {
|
|
2632
|
+
success: 'bg-[var(--ui-success)] text-white shadow-lg',
|
|
2633
|
+
error: 'bg-[var(--ui-danger)] text-white shadow-lg',
|
|
2634
|
+
info: 'bg-[var(--ui-info)] text-white shadow-lg',
|
|
2635
|
+
warning: 'bg-[var(--ui-warning)] text-white shadow-lg',
|
|
2636
|
+
},
|
|
2637
|
+
outlined: {
|
|
2638
|
+
success: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',
|
|
2639
|
+
error: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',
|
|
2640
|
+
info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',
|
|
2641
|
+
warning: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',
|
|
2642
|
+
},
|
|
2643
|
+
glass: {
|
|
2644
|
+
success: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
|
|
2645
|
+
error: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
|
|
2646
|
+
info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
|
|
2647
|
+
warning: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
|
|
2648
|
+
},
|
|
1616
2649
|
};
|
|
1617
|
-
const Toast = ({ message, type, onClose, className,
|
|
2650
|
+
const Toast = ({ id, title, message, type, variant = 'accent', action, icon, showProgressBar = true, duration = 4000, isPaused = false, onClose, className, isTop = true, }) => {
|
|
2651
|
+
const [progress, setProgress] = React.useState(100);
|
|
2652
|
+
React.useEffect(() => {
|
|
2653
|
+
if (!showProgressBar || !duration || isPaused)
|
|
2654
|
+
return;
|
|
2655
|
+
const intervalTime = 50;
|
|
2656
|
+
const step = (intervalTime / duration) * 100;
|
|
2657
|
+
const timer = setInterval(() => {
|
|
2658
|
+
setProgress((prev) => {
|
|
2659
|
+
if (prev <= 0) {
|
|
2660
|
+
clearInterval(timer);
|
|
2661
|
+
return 0;
|
|
2662
|
+
}
|
|
2663
|
+
return prev - step;
|
|
2664
|
+
});
|
|
2665
|
+
}, intervalTime);
|
|
2666
|
+
return () => clearInterval(timer);
|
|
2667
|
+
}, [showProgressBar, duration, isPaused]);
|
|
1618
2668
|
const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
|
|
1619
2669
|
const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
|
|
1620
|
-
|
|
2670
|
+
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
2671
|
+
return (jsxRuntime.jsx(react.Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxRuntime.jsxs("div", { role: "status", id: id.toString(), className: buildClassName('relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[280px] max-w-md transition-all', variantStyleMap[variant][type], className), children: [jsxRuntime.jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsxRuntime.jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsxRuntime.jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsxRuntime.jsx("div", { className: buildClassName('text-sm font-medium leading-relaxed', variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300'), children: message }), action && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
|
|
2672
|
+
? 'text-white hover:opacity-85'
|
|
2673
|
+
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsxRuntime.jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', noOutlineOnFocus: true, children: jsxRuntime.jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsxRuntime.jsx("div", { className: buildClassName('h-full transition-all duration-75 ease-linear', variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type]), style: { width: `${Math.max(0, progress)}%` } }) }))] }) }));
|
|
1621
2674
|
};
|
|
2675
|
+
Toast.displayName = 'Toast';
|
|
1622
2676
|
|
|
1623
2677
|
const ToastContext = React.createContext(undefined);
|
|
1624
2678
|
let toastCounter = 0;
|
|
@@ -1628,64 +2682,138 @@ const useToast = () => {
|
|
|
1628
2682
|
throw new Error('useToast must be used within ToastProvider');
|
|
1629
2683
|
return context;
|
|
1630
2684
|
};
|
|
1631
|
-
// Maximum visible toasts at once
|
|
1632
|
-
const MAX_TOASTS = 5;
|
|
1633
2685
|
const placementClass = {
|
|
1634
|
-
'top-right': 'top-
|
|
1635
|
-
'top-left': 'top-
|
|
1636
|
-
'top-center': 'top-
|
|
1637
|
-
'bottom-right': 'bottom-
|
|
1638
|
-
'bottom-left': 'bottom-
|
|
1639
|
-
'bottom-center': 'bottom-
|
|
1640
|
-
};
|
|
1641
|
-
const ToastProvider = ({ children, placement = 'top-
|
|
2686
|
+
'top-right': 'top-20 right-6 items-end',
|
|
2687
|
+
'top-left': 'top-20 left-6 items-start',
|
|
2688
|
+
'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',
|
|
2689
|
+
'bottom-right': 'bottom-6 right-6 items-end',
|
|
2690
|
+
'bottom-left': 'bottom-6 left-6 items-start',
|
|
2691
|
+
'bottom-center': 'bottom-6 left-1/2 -translate-x-1/2 items-center',
|
|
2692
|
+
};
|
|
2693
|
+
const ToastProvider = ({ children, placement = 'top-right', maxToasts = 5, className, }) => {
|
|
1642
2694
|
const [toasts, setToasts] = React.useState([]);
|
|
1643
|
-
const
|
|
2695
|
+
const [pausedIds, setPausedIds] = React.useState(new Set());
|
|
2696
|
+
const metaRefs = React.useRef(new Map());
|
|
1644
2697
|
const clearTimer = (id) => {
|
|
1645
|
-
const
|
|
1646
|
-
if (
|
|
1647
|
-
clearTimeout(
|
|
1648
|
-
|
|
2698
|
+
const meta = metaRefs.current.get(id);
|
|
2699
|
+
if (meta === null || meta === void 0 ? void 0 : meta.timerId) {
|
|
2700
|
+
clearTimeout(meta.timerId);
|
|
2701
|
+
meta.timerId = null;
|
|
1649
2702
|
}
|
|
1650
2703
|
};
|
|
1651
2704
|
const closeToast = React.useCallback((id) => {
|
|
1652
2705
|
clearTimer(id);
|
|
2706
|
+
metaRefs.current.delete(id);
|
|
2707
|
+
setPausedIds((prev) => {
|
|
2708
|
+
const next = new Set(prev);
|
|
2709
|
+
next.delete(id);
|
|
2710
|
+
return next;
|
|
2711
|
+
});
|
|
1653
2712
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1654
2713
|
}, []);
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
2714
|
+
const dismissAll = React.useCallback(() => {
|
|
2715
|
+
metaRefs.current.forEach((meta) => {
|
|
2716
|
+
if (meta.timerId)
|
|
2717
|
+
clearTimeout(meta.timerId);
|
|
2718
|
+
});
|
|
2719
|
+
metaRefs.current.clear();
|
|
2720
|
+
setPausedIds(new Set());
|
|
2721
|
+
setToasts([]);
|
|
2722
|
+
}, []);
|
|
2723
|
+
const startAutoClose = React.useCallback((toastId, delay) => {
|
|
2724
|
+
clearTimer(toastId);
|
|
2725
|
+
const timerId = setTimeout(() => {
|
|
2726
|
+
closeToast(toastId);
|
|
2727
|
+
}, delay);
|
|
2728
|
+
const meta = metaRefs.current.get(toastId);
|
|
2729
|
+
if (meta) {
|
|
2730
|
+
meta.timerId = timerId;
|
|
2731
|
+
meta.startTime = Date.now();
|
|
2732
|
+
meta.remainingTime = delay;
|
|
2733
|
+
}
|
|
2734
|
+
else {
|
|
2735
|
+
metaRefs.current.set(toastId, {
|
|
2736
|
+
timerId,
|
|
2737
|
+
startTime: Date.now(),
|
|
2738
|
+
remainingTime: delay,
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
1663
2741
|
}, [closeToast]);
|
|
1664
2742
|
React.useEffect(() => {
|
|
1665
|
-
const refs =
|
|
1666
|
-
return () => refs.forEach((
|
|
2743
|
+
const refs = metaRefs.current;
|
|
2744
|
+
return () => refs.forEach((meta) => {
|
|
2745
|
+
if (meta.timerId)
|
|
2746
|
+
clearTimeout(meta.timerId);
|
|
2747
|
+
});
|
|
1667
2748
|
}, []);
|
|
1668
2749
|
const showToast = React.useCallback((message, options) => {
|
|
1669
|
-
var _a, _b, _c, _d;
|
|
2750
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1670
2751
|
const id = ++toastCounter;
|
|
1671
2752
|
const newToast = {
|
|
1672
2753
|
id,
|
|
2754
|
+
title: options === null || options === void 0 ? void 0 : options.title,
|
|
1673
2755
|
message,
|
|
1674
2756
|
type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'info',
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
2757
|
+
variant: (_b = options === null || options === void 0 ? void 0 : options.variant) !== null && _b !== void 0 ? _b : 'accent',
|
|
2758
|
+
duration: (_c = options === null || options === void 0 ? void 0 : options.duration) !== null && _c !== void 0 ? _c : 4000,
|
|
2759
|
+
autoClose: (_d = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _d !== void 0 ? _d : true,
|
|
2760
|
+
pauseOnHover: (_e = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _e !== void 0 ? _e : true,
|
|
2761
|
+
showProgressBar: (_f = options === null || options === void 0 ? void 0 : options.showProgressBar) !== null && _f !== void 0 ? _f : (options === null || options === void 0 ? void 0 : options.autoClose) !== false,
|
|
2762
|
+
action: options === null || options === void 0 ? void 0 : options.action,
|
|
2763
|
+
icon: options === null || options === void 0 ? void 0 : options.icon,
|
|
1678
2764
|
};
|
|
1679
2765
|
setToasts((prev) => {
|
|
1680
2766
|
const updated = [...prev, newToast];
|
|
1681
|
-
if (updated.length >
|
|
2767
|
+
if (updated.length > maxToasts)
|
|
1682
2768
|
updated.shift();
|
|
1683
2769
|
return updated;
|
|
1684
2770
|
});
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
2771
|
+
if (newToast.autoClose) {
|
|
2772
|
+
startAutoClose(id, newToast.duration);
|
|
2773
|
+
}
|
|
2774
|
+
return id;
|
|
2775
|
+
}, [maxToasts, startAutoClose]);
|
|
2776
|
+
// Hover pauses timer and tracks exact remaining milliseconds
|
|
2777
|
+
const handleMouseEnter = (toast) => {
|
|
2778
|
+
if (!toast.pauseOnHover || !toast.autoClose)
|
|
2779
|
+
return;
|
|
2780
|
+
const meta = metaRefs.current.get(toast.id);
|
|
2781
|
+
if (meta) {
|
|
2782
|
+
const elapsed = Date.now() - meta.startTime;
|
|
2783
|
+
meta.remainingTime = Math.max(0, meta.remainingTime - elapsed);
|
|
2784
|
+
clearTimer(toast.id);
|
|
2785
|
+
setPausedIds((prev) => new Set(prev).add(toast.id));
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
// Mouse leave resumes timer with exact remaining milliseconds
|
|
2789
|
+
const handleMouseLeave = (toast) => {
|
|
2790
|
+
if (!toast.pauseOnHover || !toast.autoClose)
|
|
2791
|
+
return;
|
|
2792
|
+
setPausedIds((prev) => {
|
|
2793
|
+
const next = new Set(prev);
|
|
2794
|
+
next.delete(toast.id);
|
|
2795
|
+
return next;
|
|
2796
|
+
});
|
|
2797
|
+
const meta = metaRefs.current.get(toast.id);
|
|
2798
|
+
if (meta && meta.remainingTime > 0) {
|
|
2799
|
+
startAutoClose(toast.id, meta.remainingTime);
|
|
2800
|
+
}
|
|
2801
|
+
else if (meta && meta.remainingTime <= 0) {
|
|
2802
|
+
closeToast(toast.id);
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
const toastHelpers = React.useMemo(() => ({
|
|
2806
|
+
show: (message, options) => showToast(message, options),
|
|
2807
|
+
success: (message, options) => showToast(message, { ...options, type: 'success' }),
|
|
2808
|
+
error: (message, options) => showToast(message, { ...options, type: 'error' }),
|
|
2809
|
+
info: (message, options) => showToast(message, { ...options, type: 'info' }),
|
|
2810
|
+
warning: (message, options) => showToast(message, { ...options, type: 'warning' }),
|
|
2811
|
+
dismiss: (id) => closeToast(id),
|
|
2812
|
+
dismissAll: () => dismissAll(),
|
|
2813
|
+
}), [showToast, closeToast, dismissAll]);
|
|
2814
|
+
return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast, toast: toastHelpers }, children: [children, jsxRuntime.jsx("div", { className: buildClassName('fixed z-[9999] flex flex-col gap-3 pointer-events-none', placementClass[placement], className), children: toasts.map((toastItem) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => handleMouseEnter(toastItem), onMouseLeave: () => handleMouseLeave(toastItem), children: jsxRuntime.jsx(Toast, { ...toastItem, isPaused: pausedIds.has(toastItem.id), isTop: placement.startsWith('top'), onClose: () => closeToast(toastItem.id) }) }, toastItem.id))) })] }));
|
|
2815
|
+
};
|
|
2816
|
+
ToastProvider.displayName = 'ToastProvider';
|
|
1689
2817
|
|
|
1690
2818
|
const DENSITY_CELL_CLASS$1 = {
|
|
1691
2819
|
compact: 'px-2 py-1.5',
|
|
@@ -1878,6 +3006,12 @@ exports.Breadcrumb = Breadcrumb;
|
|
|
1878
3006
|
exports.Button = Button;
|
|
1879
3007
|
exports.ButtonGroup = ButtonGroup;
|
|
1880
3008
|
exports.Card = Card;
|
|
3009
|
+
exports.CardContent = CardContent;
|
|
3010
|
+
exports.CardDescription = CardDescription;
|
|
3011
|
+
exports.CardFooter = CardFooter;
|
|
3012
|
+
exports.CardHeader = CardHeader;
|
|
3013
|
+
exports.CardMedia = CardMedia;
|
|
3014
|
+
exports.CardTitle = CardTitle;
|
|
1881
3015
|
exports.Checkbox = Checkbox;
|
|
1882
3016
|
exports.CheckboxGroup = CheckboxGroup;
|
|
1883
3017
|
exports.DOTS = DOTS;
|
|
@@ -1896,13 +3030,18 @@ exports.PASSWORD_PLACEHOLDER = PASSWORD_PLACEHOLDER;
|
|
|
1896
3030
|
exports.PASSWORD_TYPE = PASSWORD_TYPE;
|
|
1897
3031
|
exports.Pagination = Pagination;
|
|
1898
3032
|
exports.PasswordInput = PasswordInput;
|
|
3033
|
+
exports.QuantityStepper = QuantityStepper;
|
|
1899
3034
|
exports.Radio = Radio;
|
|
1900
3035
|
exports.RadioGroup = RadioGroup;
|
|
1901
3036
|
exports.RadioSwitch = RadioSwitch;
|
|
1902
3037
|
exports.RangeInput = RangeInput;
|
|
1903
3038
|
exports.RangeSlider = RangeSlider;
|
|
3039
|
+
exports.SegmentedControl = SegmentedControl;
|
|
1904
3040
|
exports.SelectBox = SelectBox;
|
|
3041
|
+
exports.Skeleton = Skeleton;
|
|
1905
3042
|
exports.SortIconMap = SortIconMap;
|
|
3043
|
+
exports.StatusPill = StatusPill;
|
|
3044
|
+
exports.StickyActionBar = StickyActionBar;
|
|
1906
3045
|
exports.Table = Table;
|
|
1907
3046
|
exports.TableBody = TableBody;
|
|
1908
3047
|
exports.TableCell = TableCell;
|
|
@@ -1918,6 +3057,7 @@ exports.ToastProvider = ToastProvider;
|
|
|
1918
3057
|
exports.VirtualizedDataTable = VirtualizedDataTable;
|
|
1919
3058
|
exports.buildClassName = buildClassName;
|
|
1920
3059
|
exports.buildInputClass = buildInputClass;
|
|
3060
|
+
exports.evaluatePasswordStrength = evaluatePasswordStrength;
|
|
1921
3061
|
exports.get = get;
|
|
1922
3062
|
exports.getSortIcon = getSortIcon;
|
|
1923
3063
|
exports.isArray = isArray;
|