@pk-design/react-tailwind 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/Banner/Banner.js +1 -1
- package/dist/cjs/components/Banner/Banner.js.map +1 -1
- package/dist/cjs/components/Button/Button.js +88 -60
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTable.js +1 -1
- package/dist/cjs/components/DataTable/DataTable.js.map +1 -1
- package/dist/cjs/components/RadioGroup/RadioGroup.js +5 -3
- package/dist/cjs/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/cjs/components/SelectBox/SelectBox.js +25 -4
- package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/cjs/components/Toast/Toast.js +1 -1
- package/dist/cjs/components/Toast/Toast.js.map +1 -1
- package/dist/es/components/Banner/Banner.js +1 -1
- package/dist/es/components/Banner/Banner.js.map +1 -1
- package/dist/es/components/Button/Button.js +89 -61
- package/dist/es/components/Button/Button.js.map +1 -1
- package/dist/es/components/DataTable/DataTable.js +1 -1
- package/dist/es/components/DataTable/DataTable.js.map +1 -1
- package/dist/es/components/RadioGroup/RadioGroup.js +5 -3
- package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/es/components/SelectBox/SelectBox.js +26 -5
- package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/es/components/Toast/Toast.js +1 -1
- package/dist/es/components/Toast/Toast.js.map +1 -1
- package/dist/react-tailwind.cjs.js +121 -70
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +122 -71
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Button/Button.types.d.ts +12 -6
- package/dist/types/components/DataTable/DataTable.types.d.ts +2 -2
- package/dist/types/components/SelectBox/SelectBox.types.d.ts +18 -1
- package/package.json +1 -1
|
@@ -151,59 +151,35 @@ TextContent.displayName = 'TextContent';
|
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* Tailwind CSS classes for theme + variant combinations.
|
|
154
|
+
*
|
|
155
|
+
* Only colours live here. Focus outline width/offset and disabled styling are
|
|
156
|
+
* applied uniformly in the component so they can be toggled in one place.
|
|
154
157
|
*/
|
|
155
158
|
const THEMED_BTN_CLASS = {
|
|
156
159
|
primary: {
|
|
157
|
-
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:
|
|
158
|
-
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:
|
|
159
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
160
|
+
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:outline-[var(--ui-primary-ring)]',
|
|
161
|
+
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:outline-[var(--ui-primary-ring)]',
|
|
162
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-primary-ring)]',
|
|
160
163
|
},
|
|
161
164
|
danger: {
|
|
162
|
-
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:
|
|
163
|
-
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:
|
|
164
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
165
|
+
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:outline-[var(--ui-danger-ring)]',
|
|
166
|
+
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:outline-[var(--ui-danger-ring)]',
|
|
167
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-danger-ring)]',
|
|
165
168
|
},
|
|
166
169
|
secondary: {
|
|
167
|
-
default: 'text-gray-700 bg-white border border-[var(--ui-border)] hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 focus-visible:
|
|
168
|
-
outlined: 'text-gray-700 hover:text-white border border-[var(--ui-border)] hover:bg-gray-700 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:
|
|
169
|
-
plain: 'text-gray-700 hover:bg-
|
|
170
|
+
default: 'text-gray-700 bg-white border border-[var(--ui-border)] hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
171
|
+
outlined: 'text-gray-700 hover:text-white border border-[var(--ui-border)] hover:bg-gray-700 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
172
|
+
plain: 'text-gray-700 hover:bg-current/10 active:bg-current/15 dark:text-gray-200 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
170
173
|
},
|
|
171
174
|
success: {
|
|
172
|
-
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:
|
|
173
|
-
outlined: 'text-emerald-
|
|
174
|
-
plain: 'text-emerald-
|
|
175
|
+
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:outline-emerald-500',
|
|
176
|
+
outlined: 'text-emerald-700 hover:text-white border border-emerald-600 hover:bg-emerald-600 dark:text-emerald-400 dark:border-emerald-500 focus-visible:outline-emerald-500',
|
|
177
|
+
plain: 'text-emerald-700 hover:bg-current/10 active:bg-current/15 dark:text-emerald-400 focus-visible:outline-emerald-500',
|
|
175
178
|
},
|
|
176
179
|
warning: {
|
|
177
|
-
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:
|
|
178
|
-
outlined: 'text-amber-
|
|
179
|
-
plain: 'text-amber-
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
const LOADER_TEXT_CLASS = {
|
|
183
|
-
primary: {
|
|
184
|
-
default: 'text-white',
|
|
185
|
-
outlined: 'text-[var(--ui-primary-text)]',
|
|
186
|
-
plain: 'text-[var(--ui-primary-text)]',
|
|
187
|
-
},
|
|
188
|
-
danger: {
|
|
189
|
-
default: 'text-white',
|
|
190
|
-
outlined: 'text-[var(--ui-danger-text)]',
|
|
191
|
-
plain: 'text-[var(--ui-danger-text)]',
|
|
192
|
-
},
|
|
193
|
-
secondary: {
|
|
194
|
-
default: 'text-gray-700 dark:text-white',
|
|
195
|
-
outlined: 'text-gray-700 dark:text-gray-200',
|
|
196
|
-
plain: 'text-gray-700 dark:text-gray-200',
|
|
197
|
-
},
|
|
198
|
-
success: {
|
|
199
|
-
default: 'text-white',
|
|
200
|
-
outlined: 'text-emerald-600 dark:text-emerald-400',
|
|
201
|
-
plain: 'text-emerald-600 dark:text-emerald-400',
|
|
202
|
-
},
|
|
203
|
-
warning: {
|
|
204
|
-
default: 'text-white',
|
|
205
|
-
outlined: 'text-amber-600 dark:text-amber-400',
|
|
206
|
-
plain: 'text-amber-600 dark:text-amber-400',
|
|
180
|
+
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:outline-amber-500',
|
|
181
|
+
outlined: 'text-amber-700 hover:text-white border border-amber-500 hover:bg-amber-500 dark:text-amber-400 dark:border-amber-500 focus-visible:outline-amber-500',
|
|
182
|
+
plain: 'text-amber-700 hover:bg-current/10 active:bg-current/15 dark:text-amber-400 focus-visible:outline-amber-500',
|
|
207
183
|
},
|
|
208
184
|
};
|
|
209
185
|
const SIZE_CLASSES = {
|
|
@@ -223,22 +199,32 @@ const LOADER_SIZE = {
|
|
|
223
199
|
};
|
|
224
200
|
/**
|
|
225
201
|
* Base shared tailwind classes.
|
|
202
|
+
*
|
|
203
|
+
* Disabled styling is keyed off both the native `disabled` attribute and
|
|
204
|
+
* `aria-disabled`, so non-button elements rendered via `as` look disabled too.
|
|
226
205
|
*/
|
|
227
206
|
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
228
|
-
'
|
|
229
|
-
'active:scale-[0.98]
|
|
207
|
+
'transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out ' +
|
|
208
|
+
'active:scale-[0.98] focus:outline-none ' +
|
|
209
|
+
'disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none ' +
|
|
210
|
+
'aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:shadow-none';
|
|
230
211
|
const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
231
|
-
var _a, _b, _c, _d;
|
|
232
212
|
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, active, onClick, ...restProps } = props;
|
|
233
213
|
const Component = as || 'button';
|
|
234
214
|
const isNativeButton = Component === 'button';
|
|
215
|
+
// Clean invalid props when not rendering a real button
|
|
216
|
+
const { type: _ignoreType, disabled: _ignoreDisabled, role, tabIndex, onKeyDown, onKeyUp, ...domProps } = restProps;
|
|
217
|
+
const ariaLabel = domProps['aria-label'];
|
|
218
|
+
const ariaLabelledBy = domProps['aria-labelledby'];
|
|
235
219
|
// Accessibility guard for icon-only buttons
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
220
|
+
React.useEffect(() => {
|
|
221
|
+
if (iconOnly && !ariaLabel && !ariaLabelledBy) {
|
|
222
|
+
console.warn('[ui] <Button iconOnly> requires aria-label or aria-labelledby for accessibility.');
|
|
223
|
+
}
|
|
224
|
+
}, [iconOnly, ariaLabel, ariaLabelledBy]);
|
|
239
225
|
const resolvedDisabled = Boolean(disabled || loading);
|
|
240
|
-
//
|
|
241
|
-
const
|
|
226
|
+
// Links (`href`, or `to` for router Link components) keep their native semantics.
|
|
227
|
+
const isLink = !isNativeButton && (domProps.href != null || domProps.to != null);
|
|
242
228
|
const handleClick = (e) => {
|
|
243
229
|
if (resolvedDisabled) {
|
|
244
230
|
e.preventDefault();
|
|
@@ -247,31 +233,73 @@ const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
|
247
233
|
}
|
|
248
234
|
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
249
235
|
};
|
|
236
|
+
// Mirror native <button> keyboard activation for non-button elements:
|
|
237
|
+
// Enter activates on keydown, Space on keyup (and must not scroll the page).
|
|
238
|
+
const handleKeyDown = (e) => {
|
|
239
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
|
|
240
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
241
|
+
return;
|
|
242
|
+
if (e.key === 'Enter') {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
e.currentTarget.click();
|
|
245
|
+
}
|
|
246
|
+
else if (e.key === ' ') {
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const handleKeyUp = (e) => {
|
|
251
|
+
onKeyUp === null || onKeyUp === void 0 ? void 0 : onKeyUp(e);
|
|
252
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
253
|
+
return;
|
|
254
|
+
if (e.key === ' ') {
|
|
255
|
+
e.preventDefault();
|
|
256
|
+
e.currentTarget.click();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
250
259
|
const elementProps = isNativeButton
|
|
251
260
|
? {
|
|
252
261
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
253
262
|
disabled: resolvedDisabled,
|
|
263
|
+
role,
|
|
264
|
+
tabIndex,
|
|
254
265
|
onClick: handleClick,
|
|
266
|
+
onKeyDown,
|
|
267
|
+
onKeyUp,
|
|
255
268
|
}
|
|
256
|
-
:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
: isLink
|
|
270
|
+
? {
|
|
271
|
+
role,
|
|
272
|
+
tabIndex: resolvedDisabled ? -1 : tabIndex,
|
|
273
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
274
|
+
onClick: handleClick,
|
|
275
|
+
onKeyDown,
|
|
276
|
+
onKeyUp,
|
|
277
|
+
}
|
|
278
|
+
: {
|
|
279
|
+
role: role !== null && role !== void 0 ? role : 'button',
|
|
280
|
+
tabIndex: resolvedDisabled ? -1 : (tabIndex !== null && tabIndex !== void 0 ? tabIndex : 0),
|
|
281
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
282
|
+
onClick: handleClick,
|
|
283
|
+
onKeyDown: handleKeyDown,
|
|
284
|
+
onKeyUp: handleKeyUp,
|
|
285
|
+
};
|
|
262
286
|
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
263
287
|
const classes = React.useMemo(() => {
|
|
264
288
|
var _a;
|
|
265
|
-
return buildClassName(BASE_BTN_CLASS, ((_a = THEMED_BTN_CLASS[theme]) === null || _a === void 0 ? void 0 : _a[variant]) || THEMED_BTN_CLASS.primary.default, iconOnly ? sizeObj.iconOnly : sizeObj.normal,
|
|
289
|
+
return buildClassName(BASE_BTN_CLASS, ((_a = THEMED_BTN_CLASS[theme]) === null || _a === void 0 ? void 0 : _a[variant]) || THEMED_BTN_CLASS.primary.default, iconOnly ? sizeObj.iconOnly : sizeObj.normal,
|
|
290
|
+
// An outline (not a ring) so the offset gap stays transparent on any surface/mode.
|
|
291
|
+
!noOutlineOnFocus &&
|
|
292
|
+
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none', active && 'ring-2 ring-current', className);
|
|
266
293
|
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
267
|
-
|
|
294
|
+
// The spinner and loading text inherit the button's text colour.
|
|
295
|
+
const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: "text-current" }));
|
|
268
296
|
/**
|
|
269
297
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
270
298
|
*/
|
|
271
299
|
if (loading && iconOnly) {
|
|
272
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...
|
|
300
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loaderIcon }));
|
|
273
301
|
}
|
|
274
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...
|
|
302
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loading ? (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsxRuntime.jsx(TextContent, { className: "text-current", children: loadingText })) : (children)] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: rightIcon })] })) }));
|
|
275
303
|
});
|
|
276
304
|
Button.displayName = 'Button';
|
|
277
305
|
|
|
@@ -595,7 +623,7 @@ const Banner = React.forwardRef((props, ref) => {
|
|
|
595
623
|
const { type = 'info', iconSize = 'sm', children, icon, removable = false, onRemove, className, ...restProps } = props;
|
|
596
624
|
const DefaultIcon = IconMap[type];
|
|
597
625
|
const iconNode = icon !== undefined ? (icon) : (jsxRuntime.jsx(DefaultIcon, { "data-testid": `banner-${type}-icon`, className: buildClassName('shrink-0', sizeMap[iconSize]) }));
|
|
598
|
-
return (jsxRuntime.jsxs("div", { ref: ref, className: buildClassName('text-sm rounded-md flex w-full p-3 items-center gap-3 ring-1 ring-inset', TypeClassMap[type], className), "data-testid": `banner-${type}`, ...restProps, children: [iconNode, jsxRuntime.jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Dismiss banner", size: "xs", variant: "plain",
|
|
626
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: buildClassName('text-sm rounded-md flex w-full p-3 items-center gap-3 ring-1 ring-inset', TypeClassMap[type], className), "data-testid": `banner-${type}`, ...restProps, children: [iconNode, jsxRuntime.jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Dismiss banner", size: "xs", variant: "plain", className: "text-current focus-visible:outline-current shrink-0", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), "data-testid": "banner-remove-btn", children: jsxRuntime.jsx(SvgX, { className: "size-4.5", "data-testid": "banner-remove-icon" }) }))] }));
|
|
599
627
|
});
|
|
600
628
|
Banner.displayName = 'Banner';
|
|
601
629
|
|
|
@@ -1526,7 +1554,7 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1526
1554
|
}), isInlineMode && (jsxRuntime.jsx(TableCell, { align: "right", className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1.5', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) })), isOverlayMode && (jsxRuntime.jsx(TableCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: "sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 overflow-visible text-right", onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('absolute right-0 top-0 bottom-0 h-full opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1 px-3 z-30 pointer-events-none group-hover:pointer-events-auto whitespace-nowrap', striped && idx % 2 === 1
|
|
1527
1555
|
? 'bg-gray-100 dark:bg-gray-800'
|
|
1528
1556
|
: 'bg-white group-hover:bg-gray-50 dark:bg-gray-900 dark:group-hover:bg-gray-800', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) }))] }, key));
|
|
1529
|
-
})) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx("div", { className: buildClassName(stickyPagination && 'sticky bottom-0 z-20
|
|
1557
|
+
})) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx("div", { className: buildClassName(stickyPagination && 'sticky bottom-0 z-20', paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, className: buildClassName('rounded-t-none rounded-b-lg border border-[var(--ui-border)] bg-white dark:bg-gray-800', paginationClass, paginationProps === null || paginationProps === void 0 ? void 0 : paginationProps.className), onChange: setPagination })) }))] }));
|
|
1530
1558
|
}
|
|
1531
1559
|
DataTable.displayName = 'DataTable';
|
|
1532
1560
|
|
|
@@ -2446,6 +2474,7 @@ const COLUMN_GRID_MAP = {
|
|
|
2446
2474
|
const RadioGroup = (props) => {
|
|
2447
2475
|
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, required, 'aria-describedby': ariaDescribedBy, } = props;
|
|
2448
2476
|
const groupId = React.useId();
|
|
2477
|
+
const groupName = name || groupId;
|
|
2449
2478
|
const items = React.useMemo(() => {
|
|
2450
2479
|
var _a;
|
|
2451
2480
|
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
@@ -2470,13 +2499,14 @@ const RadioGroup = (props) => {
|
|
|
2470
2499
|
: 'flex flex-col gap-3'), children: items.map((item) => {
|
|
2471
2500
|
const isSelected = value === item.value;
|
|
2472
2501
|
const isItemDisabled = disabled || Boolean(item.disabled);
|
|
2502
|
+
const radioItemId = `${groupName}-${item.value}`;
|
|
2473
2503
|
if (isCards) {
|
|
2474
|
-
return (jsxRuntime.jsxs("label", { htmlFor:
|
|
2504
|
+
return (jsxRuntime.jsxs("label", { htmlFor: radioItemId, className: buildClassName('relative flex items-start gap-3 p-4 rounded-xl border transition-all cursor-pointer select-none min-w-[200px] flex-1', 'focus-within:ring-2 focus-within:ring-[var(--ui-primary-ring)] focus-within:border-[var(--ui-primary)]', isSelected
|
|
2475
2505
|
? 'border-[var(--ui-primary)] bg-blue-50/40 dark:bg-blue-950/30 ring-2 ring-[var(--ui-primary-ring)]/30'
|
|
2476
2506
|
: 'border-[var(--ui-border)] bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600', isItemDisabled &&
|
|
2477
|
-
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.jsx(Radio, { name:
|
|
2507
|
+
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.jsx(Radio, { name: groupName, id: radioItemId, 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));
|
|
2478
2508
|
}
|
|
2479
|
-
return (jsxRuntime.jsx(Radio, { name:
|
|
2509
|
+
return (jsxRuntime.jsx(Radio, { name: groupName, id: radioItemId, label: item.label, description: item.description, value: item.value, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange }, item.value));
|
|
2480
2510
|
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2481
2511
|
};
|
|
2482
2512
|
RadioGroup.displayName = 'RadioGroup';
|
|
@@ -3846,9 +3876,18 @@ const sizeClasses$1 = {
|
|
|
3846
3876
|
},
|
|
3847
3877
|
};
|
|
3848
3878
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
3849
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = NOOP, 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 = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
3879
|
+
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected: selectedProp, defaultSelected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange: onChangeProp = NOOP, placeholder, dropdownContainerClass = 'z-20', dropdownMinWidth, 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 = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
3880
|
+
// Uncontrolled when `selected` is not passed: keep the selection internally.
|
|
3881
|
+
const isControlled = selectedProp !== undefined;
|
|
3882
|
+
const [internalSelected, setInternalSelected] = React.useState(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : null);
|
|
3883
|
+
const selected = isControlled ? selectedProp : internalSelected;
|
|
3884
|
+
const onChange = React.useCallback((value) => {
|
|
3885
|
+
if (!isControlled)
|
|
3886
|
+
setInternalSelected(value);
|
|
3887
|
+
onChangeProp(value);
|
|
3888
|
+
}, [isControlled, onChangeProp]);
|
|
3850
3889
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3851
|
-
return jsxRuntime.jsx(MobilePicker, { ...props });
|
|
3890
|
+
return jsxRuntime.jsx(MobilePicker, { ...props, selected: selected, onChange: onChange });
|
|
3852
3891
|
}
|
|
3853
3892
|
const hasError = Boolean(error);
|
|
3854
3893
|
const [query, setQuery] = React.useState('');
|
|
@@ -4046,6 +4085,16 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4046
4085
|
}
|
|
4047
4086
|
}, [isAllSelected, onChange, allOptions]);
|
|
4048
4087
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4088
|
+
// Reserve room for the chevron (and the clear button when shown) so long values
|
|
4089
|
+
// don't run underneath them.
|
|
4090
|
+
const showClearButton = allowClear && !multiple && !isEmpty(normalizedSelected);
|
|
4091
|
+
const rightPaddingClass = showClearButton
|
|
4092
|
+
? 'pe-16'
|
|
4093
|
+
: size === 'lg'
|
|
4094
|
+
? 'pe-11'
|
|
4095
|
+
: size === 'sm'
|
|
4096
|
+
? 'pe-9'
|
|
4097
|
+
: 'pe-10';
|
|
4049
4098
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
4050
4099
|
query.trim() &&
|
|
4051
4100
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
|
|
@@ -4058,7 +4107,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4058
4107
|
const isOptSelected = selectedValueSet.has(optVal);
|
|
4059
4108
|
return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 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 text-gray-900 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));
|
|
4060
4109
|
};
|
|
4061
|
-
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: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), 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(normalizedSelected) &&
|
|
4110
|
+
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: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', rightPaddingClass, 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(normalizedSelected) &&
|
|
4062
4111
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
4063
4112
|
normalizedSelected.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: (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) ? '' : placeholder, onChange: (e) => {
|
|
4064
4113
|
const val = e.target.value;
|
|
@@ -4098,7 +4147,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4098
4147
|
}
|
|
4099
4148
|
}, onFocus: () => {
|
|
4100
4149
|
setQuery('');
|
|
4101
|
-
}, 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" }),
|
|
4150
|
+
}, className: buildClassName(baseInputClass, 'text-ellipsis', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), showClearButton && (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", 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 start', gap: 5, padding: 8 }, as: "div", modal: modalDropdown,
|
|
4151
|
+
// Matches the input width by default; min-width lets the panel grow wider than a narrow input.
|
|
4152
|
+
style: dropdownMinWidth != null ? { minWidth: dropdownMinWidth } : undefined, className: buildClassName('w-[var(--input-width)] rounded-lg border border-[var(--ui-border)] bg-white dark:bg-gray-800 p-1 shadow-lg max-h-80 overflow-y-auto z-50 [--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-[var(--ui-border-muted)] 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(normalizedSelected) ? normalizedSelected.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: [allowFreeText &&
|
|
4102
4153
|
query.trim() &&
|
|
4103
4154
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsxRuntime.jsx(react.ComboboxOption, { value: createFreeTextOption(query.trim()), className: "group flex items-center gap-2 rounded-lg py-1.5 px-3 select-none text-sm data-[focus]:bg-blue-50 dark:data-[focus]:bg-blue-950/40 text-blue-600 dark:text-blue-400 font-medium cursor-pointer w-full", "data-testid": "freetext-option", children: jsxRuntime.jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
|
|
4104
4155
|
? 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(renderOptionItem)] }, gIdx)))
|
|
@@ -4568,7 +4619,7 @@ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, sho
|
|
|
4568
4619
|
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
4569
4620
|
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'
|
|
4570
4621
|
? 'text-white hover:opacity-85'
|
|
4571
|
-
: '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',
|
|
4622
|
+
: '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 focus-visible:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', 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)}%` } }) }))] }) }));
|
|
4572
4623
|
};
|
|
4573
4624
|
Toast.displayName = 'Toast';
|
|
4574
4625
|
|