@pk-design/react-tailwind 1.7.2 → 1.8.1
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/Button/Button.js +46 -28
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/Checkbox/Checkbox.js +10 -4
- package/dist/cjs/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js +19 -13
- package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/cjs/components/DataList/DataList.js +2 -2
- package/dist/cjs/components/DataList/DataList.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTable.js +72 -40
- package/dist/cjs/components/DataTable/DataTable.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTable.utils.js +2 -1
- package/dist/cjs/components/DataTable/DataTable.utils.js.map +1 -1
- package/dist/cjs/components/Input/Input.js +11 -8
- package/dist/cjs/components/Input/Input.js.map +1 -1
- package/dist/cjs/components/Pagination/Pagination.js +27 -15
- package/dist/cjs/components/Pagination/Pagination.js.map +1 -1
- package/dist/cjs/components/Radio/Radio.js +9 -5
- package/dist/cjs/components/Radio/Radio.js.map +1 -1
- package/dist/cjs/components/RadioGroup/RadioGroup.js +13 -6
- package/dist/cjs/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/cjs/components/SelectBox/SelectBox.js +24 -25
- package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/cjs/components/Textarea/Textarea.js +8 -5
- package/dist/cjs/components/Textarea/Textarea.js.map +1 -1
- package/dist/es/components/Button/Button.js +46 -28
- package/dist/es/components/Button/Button.js.map +1 -1
- package/dist/es/components/Checkbox/Checkbox.js +10 -4
- package/dist/es/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/es/components/CheckboxGroup/CheckboxGroup.js +20 -14
- package/dist/es/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/es/components/DataList/DataList.js +2 -2
- package/dist/es/components/DataList/DataList.js.map +1 -1
- package/dist/es/components/DataTable/DataTable.js +74 -42
- package/dist/es/components/DataTable/DataTable.js.map +1 -1
- package/dist/es/components/DataTable/DataTable.utils.js +2 -1
- package/dist/es/components/DataTable/DataTable.utils.js.map +1 -1
- package/dist/es/components/Input/Input.js +12 -9
- package/dist/es/components/Input/Input.js.map +1 -1
- package/dist/es/components/Pagination/Pagination.js +27 -15
- package/dist/es/components/Pagination/Pagination.js.map +1 -1
- package/dist/es/components/Radio/Radio.js +9 -5
- package/dist/es/components/Radio/Radio.js.map +1 -1
- package/dist/es/components/RadioGroup/RadioGroup.js +14 -7
- package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/es/components/SelectBox/SelectBox.js +24 -25
- package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/es/components/Textarea/Textarea.js +9 -6
- package/dist/es/components/Textarea/Textarea.js.map +1 -1
- package/dist/react-tailwind.cjs.js +269 -180
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +269 -180
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Button/Button.types.d.ts +5 -0
- package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +9 -1
- package/dist/types/components/DataList/DataList.d.ts +1 -1
- package/dist/types/components/DataList/DataList.types.d.ts +8 -0
- package/dist/types/components/DataTable/DataTable.d.ts +1 -1
- package/dist/types/components/DataTable/DataTable.types.d.ts +46 -0
- package/dist/types/components/Pagination/Pagination.types.d.ts +21 -24
- package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -156,17 +156,17 @@ const THEMED_BTN_CLASS = {
|
|
|
156
156
|
primary: {
|
|
157
157
|
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)]',
|
|
158
158
|
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)]',
|
|
159
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
159
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-blue-50/80 dark:hover:bg-blue-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
|
|
160
160
|
},
|
|
161
161
|
danger: {
|
|
162
162
|
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)]',
|
|
163
163
|
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)]',
|
|
164
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
164
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-red-50/80 dark:hover:bg-red-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
|
|
165
165
|
},
|
|
166
166
|
secondary: {
|
|
167
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:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:bg-gray-200 dark:disabled:bg-gray-700',
|
|
168
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:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400 disabled:border-[var(--ui-border-muted)]',
|
|
169
|
-
plain: 'text-gray-700 hover:bg-
|
|
169
|
+
plain: 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400',
|
|
170
170
|
},
|
|
171
171
|
success: {
|
|
172
172
|
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',
|
|
@@ -206,54 +206,72 @@ const LOADER_TEXT_CLASS = {
|
|
|
206
206
|
plain: 'text-amber-600 dark:text-amber-400',
|
|
207
207
|
},
|
|
208
208
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
: 'h-10 px-5 rounded-lg gap-2 text-base';
|
|
224
|
-
}
|
|
209
|
+
const SIZE_CLASSES = {
|
|
210
|
+
xs: { normal: 'h-7 px-2.5 rounded-sm gap-1 text-xs', iconOnly: 'h-7 w-7 p-1 rounded-sm text-xs' },
|
|
211
|
+
sm: { normal: 'h-8 px-3 rounded gap-1.5 text-sm', iconOnly: 'h-8 w-8 p-1.5 rounded text-sm' },
|
|
212
|
+
md: { normal: 'h-9 px-4 rounded-md gap-2 text-sm', iconOnly: 'h-9 w-9 p-2 rounded-md text-sm' },
|
|
213
|
+
lg: {
|
|
214
|
+
normal: 'h-10 px-5 rounded-lg gap-2 text-base',
|
|
215
|
+
iconOnly: 'h-10 w-10 p-2.5 rounded-lg text-base',
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
const LOADER_SIZE = {
|
|
219
|
+
xs: 'xs',
|
|
220
|
+
sm: 'xs',
|
|
221
|
+
md: 'sm',
|
|
222
|
+
lg: 'sm',
|
|
225
223
|
};
|
|
226
224
|
/**
|
|
227
225
|
* Base shared tailwind classes.
|
|
228
226
|
*/
|
|
229
|
-
const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
|
|
227
|
+
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
230
228
|
'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
|
|
231
229
|
'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
|
|
232
230
|
const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
233
|
-
|
|
231
|
+
var _a, _b, _c, _d;
|
|
232
|
+
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;
|
|
234
233
|
const Component = as || 'button';
|
|
234
|
+
const isNativeButton = Component === 'button';
|
|
235
235
|
// Accessibility guard for icon-only buttons
|
|
236
236
|
if (iconOnly && !restProps['aria-label']) {
|
|
237
237
|
console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
|
|
238
238
|
}
|
|
239
|
-
const resolvedDisabled = disabled || loading;
|
|
239
|
+
const resolvedDisabled = Boolean(disabled || loading);
|
|
240
240
|
// Clean invalid props when not rendering a real button
|
|
241
241
|
const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
|
|
242
|
-
const
|
|
242
|
+
const handleClick = (e) => {
|
|
243
|
+
if (resolvedDisabled) {
|
|
244
|
+
e.preventDefault();
|
|
245
|
+
e.stopPropagation();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
249
|
+
};
|
|
250
|
+
const elementProps = isNativeButton
|
|
243
251
|
? {
|
|
244
252
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
245
253
|
disabled: resolvedDisabled,
|
|
254
|
+
onClick: handleClick,
|
|
246
255
|
}
|
|
247
|
-
: {
|
|
248
|
-
|
|
256
|
+
: {
|
|
257
|
+
role: (_a = cleanRest.role) !== null && _a !== void 0 ? _a : 'button',
|
|
258
|
+
tabIndex: resolvedDisabled ? -1 : ((_b = cleanRest.tabIndex) !== null && _b !== void 0 ? _b : 0),
|
|
259
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
260
|
+
onClick: handleClick,
|
|
261
|
+
};
|
|
262
|
+
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
263
|
+
const classes = React.useMemo(() => {
|
|
264
|
+
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, !noOutlineOnFocus && 'focus-visible:ring-2 focus-visible:ring-offset-2', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', active && 'ring-2 ring-offset-2 ring-current', className);
|
|
266
|
+
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
267
|
+
const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: buildClassName((_c = LOADER_TEXT_CLASS[theme]) === null || _c === void 0 ? void 0 : _c[variant]) }));
|
|
249
268
|
/**
|
|
250
269
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
251
270
|
*/
|
|
252
271
|
if (loading && iconOnly) {
|
|
253
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes,
|
|
272
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loaderIcon }));
|
|
254
273
|
}
|
|
255
|
-
|
|
256
|
-
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 })] })) }));
|
|
274
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsxRuntime.jsx(TextContent, { className: buildClassName((_d = LOADER_TEXT_CLASS[theme]) === null || _d === void 0 ? void 0 : _d[variant]), 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 })] })) }));
|
|
257
275
|
});
|
|
258
276
|
Button.displayName = 'Button';
|
|
259
277
|
|
|
@@ -900,6 +918,34 @@ const Label = React.forwardRef((props, ref) => {
|
|
|
900
918
|
});
|
|
901
919
|
Label.displayName = 'Label';
|
|
902
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Checks if a value is an array.
|
|
923
|
+
* Equivalent to lodash's _.isArray.
|
|
924
|
+
*/
|
|
925
|
+
function isArray(value) {
|
|
926
|
+
return Array.isArray(value);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Checks if a value is empty.
|
|
931
|
+
* Works for arrays, objects, strings, Maps, and Sets.
|
|
932
|
+
* Equivalent to lodash's _.isEmpty.
|
|
933
|
+
*/
|
|
934
|
+
function isEmpty(value) {
|
|
935
|
+
if (value == null)
|
|
936
|
+
return true; // null or undefined
|
|
937
|
+
if (typeof value === 'string' || isArray(value)) {
|
|
938
|
+
return value.length === 0;
|
|
939
|
+
}
|
|
940
|
+
if (value instanceof Map || value instanceof Set) {
|
|
941
|
+
return value.size === 0;
|
|
942
|
+
}
|
|
943
|
+
if (typeof value === 'object') {
|
|
944
|
+
return Object.keys(value).length === 0;
|
|
945
|
+
}
|
|
946
|
+
return false; // for numbers, booleans, functions, etc.
|
|
947
|
+
}
|
|
948
|
+
|
|
903
949
|
const sizeStyles = {
|
|
904
950
|
sm: {
|
|
905
951
|
box: 'size-3.5',
|
|
@@ -922,7 +968,7 @@ const sizeStyles = {
|
|
|
922
968
|
};
|
|
923
969
|
const Checkbox = React.forwardRef((props, ref) => {
|
|
924
970
|
var _a;
|
|
925
|
-
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
|
|
971
|
+
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
|
|
926
972
|
const generatedId = React.useId();
|
|
927
973
|
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
928
974
|
const innerRef = React.useRef(null);
|
|
@@ -934,6 +980,11 @@ const Checkbox = React.forwardRef((props, ref) => {
|
|
|
934
980
|
}, [indeterminate]);
|
|
935
981
|
const currentSize = sizeStyles[size] || sizeStyles.md;
|
|
936
982
|
const isCard = variant === 'card';
|
|
983
|
+
const hasError = !isEmpty(error);
|
|
984
|
+
const errorId = checkboxId && hasError ? `${checkboxId}-error` : undefined;
|
|
985
|
+
const helperId = checkboxId && helperText && !hasError ? `${checkboxId}-helper` : undefined;
|
|
986
|
+
const descId = checkboxId && description ? `${checkboxId}-desc` : undefined;
|
|
987
|
+
const describedBy = [ariaDescribedBy, errorId, helperId, descId].filter(Boolean).join(' ') || undefined;
|
|
937
988
|
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName(isCard
|
|
938
989
|
? 'rounded-xl border p-3.5 transition-all duration-150 relative'
|
|
939
990
|
: 'flex items-center', isCard &&
|
|
@@ -941,16 +992,16 @@ const Checkbox = React.forwardRef((props, ref) => {
|
|
|
941
992
|
? 'border-[var(--ui-border-muted)] bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
|
|
942
993
|
: checked || indeterminate
|
|
943
994
|
? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
|
|
944
|
-
: 'border-[var(--ui-border)] 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 relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", 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
|
|
995
|
+
: 'border-[var(--ui-border)] 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 relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [jsxRuntime.jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "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
|
|
945
996
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
946
997
|
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500', indeterminate &&
|
|
947
998
|
'!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsxRuntime.jsx("span", { className: buildClassName('absolute inset-0 flex items-center justify-center text-white pointer-events-none transition-opacity', checked !== undefined
|
|
948
999
|
? checked
|
|
949
1000
|
? 'opacity-100'
|
|
950
1001
|
: 'opacity-0'
|
|
951
|
-
: 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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 inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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
|
|
1002
|
+
: 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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 inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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, "aria-required": required, className: buildClassName(disabled
|
|
952
1003
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
953
|
-
: '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 })) }))] }));
|
|
1004
|
+
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { id: descId, 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, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
954
1005
|
});
|
|
955
1006
|
Checkbox.displayName = 'Checkbox';
|
|
956
1007
|
|
|
@@ -961,25 +1012,26 @@ const columnClasses = {
|
|
|
961
1012
|
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
|
|
962
1013
|
};
|
|
963
1014
|
const CheckboxGroup = (props) => {
|
|
964
|
-
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;
|
|
1015
|
+
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, required, 'aria-describedby': ariaDescribedBy, } = props;
|
|
965
1016
|
const groupId = React.useId();
|
|
966
1017
|
const [internalValue, setInternalValue] = React.useState(defaultValue || []);
|
|
967
1018
|
const isControlled = controlledValue !== undefined;
|
|
968
|
-
const currentValue = isControlled ? controlledValue : internalValue;
|
|
1019
|
+
const currentValue = isControlled ? controlledValue || [] : internalValue;
|
|
969
1020
|
const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
|
|
970
1021
|
const enabledItems = React.useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
|
|
1022
|
+
const currentValueKeySet = React.useMemo(() => new Set(currentValue), [currentValue]);
|
|
971
1023
|
// Select All State calculation
|
|
972
1024
|
const allSelected = React.useMemo(() => {
|
|
973
1025
|
if (!enabledItems.length)
|
|
974
1026
|
return false;
|
|
975
|
-
return enabledItems.every((item) =>
|
|
976
|
-
}, [enabledItems,
|
|
1027
|
+
return enabledItems.every((item) => currentValueKeySet.has(item.value));
|
|
1028
|
+
}, [enabledItems, currentValueKeySet]);
|
|
977
1029
|
const isIndeterminate = React.useMemo(() => {
|
|
978
1030
|
if (allSelected)
|
|
979
1031
|
return false;
|
|
980
|
-
return enabledItems.some((item) =>
|
|
981
|
-
}, [allSelected, enabledItems,
|
|
982
|
-
const handleSelectAllChange = (e) => {
|
|
1032
|
+
return enabledItems.some((item) => currentValueKeySet.has(item.value));
|
|
1033
|
+
}, [allSelected, enabledItems, currentValueKeySet]);
|
|
1034
|
+
const handleSelectAllChange = React.useCallback((e) => {
|
|
983
1035
|
let updated;
|
|
984
1036
|
if (allSelected || isIndeterminate) {
|
|
985
1037
|
const enabledValues = new Set(enabledItems.map((i) => i.value));
|
|
@@ -994,8 +1046,8 @@ const CheckboxGroup = (props) => {
|
|
|
994
1046
|
setInternalValue(updated);
|
|
995
1047
|
}
|
|
996
1048
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
997
|
-
};
|
|
998
|
-
const handleGroupChange = (e) => {
|
|
1049
|
+
}, [allSelected, isIndeterminate, enabledItems, currentValue, isControlled, onChange]);
|
|
1050
|
+
const handleGroupChange = React.useCallback((e) => {
|
|
999
1051
|
const raw = e.target.value;
|
|
1000
1052
|
const checked = e.target.checked;
|
|
1001
1053
|
const matched = items.find((i) => String(i.value) === raw);
|
|
@@ -1011,16 +1063,21 @@ const CheckboxGroup = (props) => {
|
|
|
1011
1063
|
setInternalValue(updated);
|
|
1012
1064
|
}
|
|
1013
1065
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
1014
|
-
};
|
|
1066
|
+
}, [items, currentValue, isControlled, onChange]);
|
|
1067
|
+
const hasError = Boolean(error);
|
|
1068
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
1069
|
+
const errorId = groupId && hasError ? `${groupId}-error` : undefined;
|
|
1070
|
+
const helperId = groupId && helperText && !hasError ? `${groupId}-helper` : undefined;
|
|
1071
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
1015
1072
|
const layoutClass = columns
|
|
1016
1073
|
? columnClasses[columns]
|
|
1017
1074
|
: inline
|
|
1018
1075
|
? 'flex flex-wrap gap-3'
|
|
1019
1076
|
: 'flex flex-col gap-3';
|
|
1020
|
-
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-[var(--ui-border)]", 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) => {
|
|
1077
|
+
return (jsxRuntime.jsxs("div", { role: "group", "aria-labelledby": labelId, "aria-describedby": describedBy, 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, { id: labelId, className: labelClass, "aria-required": required, 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-[var(--ui-border)]", 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) => {
|
|
1021
1078
|
const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
|
|
1022
|
-
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:
|
|
1023
|
-
}) }), (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 })) }))] }));
|
|
1079
|
+
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: currentValueKeySet.has(item.value), onChange: handleGroupChange }, key));
|
|
1080
|
+
}) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
1024
1081
|
};
|
|
1025
1082
|
CheckboxGroup.displayName = 'CheckboxGroup';
|
|
1026
1083
|
|
|
@@ -1114,27 +1171,39 @@ var SvgChevronRight = function SvgChevronRight(props) {
|
|
|
1114
1171
|
})));
|
|
1115
1172
|
};
|
|
1116
1173
|
|
|
1117
|
-
const Pagination = (
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
const
|
|
1121
|
-
const
|
|
1122
|
-
const
|
|
1174
|
+
const Pagination = (props) => {
|
|
1175
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1176
|
+
const { className, ellipsis = true, onChange, isMobile, showSummary = true, showPageNumbers = true, } = props;
|
|
1177
|
+
const currentPage = (_c = (_b = (_a = props.page) !== null && _a !== void 0 ? _a : props.current) !== null && _b !== void 0 ? _b : props.currentPage) !== null && _c !== void 0 ? _c : 1;
|
|
1178
|
+
const limit = (_e = (_d = props.limit) !== null && _d !== void 0 ? _d : props.pageSize) !== null && _e !== void 0 ? _e : 10;
|
|
1179
|
+
const total = (_f = props.total) !== null && _f !== void 0 ? _f : 0;
|
|
1180
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
1181
|
+
const isFirstPage = currentPage <= 1;
|
|
1182
|
+
const isLastPage = total === 0 || currentPage >= totalPages;
|
|
1183
|
+
const range = usePagination({ totalPages, currentPage });
|
|
1123
1184
|
const handlePageChange = (nextVal) => {
|
|
1124
|
-
if (nextVal === currentPage)
|
|
1185
|
+
if (nextVal === currentPage || nextVal < 1 || nextVal > totalPages)
|
|
1125
1186
|
return;
|
|
1126
1187
|
onChange({ page: nextVal });
|
|
1127
1188
|
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: page }) }, `${page}-${idx}`));
|
|
1134
|
-
}), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-next-btn", children: "Next" }) })] })] })] }));
|
|
1189
|
+
const startResult = total === 0 ? 0 : Math.max((currentPage - 1) * limit + 1, 1);
|
|
1190
|
+
const endResult = Math.min(currentPage * limit, total);
|
|
1191
|
+
// Explicit Mobile UX (when isMobile=true)
|
|
1192
|
+
if (isMobile) {
|
|
1193
|
+
return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }));
|
|
1135
1194
|
}
|
|
1136
|
-
|
|
1195
|
+
// Standard Responsive View
|
|
1196
|
+
return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 items-center justify-between sm:hidden", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxRuntime.jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [showSummary && (jsxRuntime.jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsxRuntime.jsx("span", { className: "font-medium", children: startResult }), "to", jsxRuntime.jsx("span", { className: "font-medium", children: endResult }), "of", jsxRuntime.jsx("span", { className: "font-medium", children: total }), "results"] })), showPageNumbers && (jsxRuntime.jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-prev-btn' : 'prev-btn', children: ellipsis ? 'Prev' : 'Previous' }) }), ellipsis
|
|
1197
|
+
? range.map((pageItem, idx) => {
|
|
1198
|
+
if (pageItem === DOTS) {
|
|
1199
|
+
return (jsxRuntime.jsx("li", { "data-testid": `page-${pageItem}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${pageItem}-${idx}`));
|
|
1200
|
+
}
|
|
1201
|
+
const pageNum = Number(pageItem);
|
|
1202
|
+
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: pageNum }) }, `${pageItem}-${idx}`));
|
|
1203
|
+
})
|
|
1204
|
+
: Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: jsxRuntime.jsx("span", { className: "size-4 block", children: pageNum }) }) }, pageNum))), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-next-btn' : 'next-btn', children: ellipsis ? 'Next' : 'Next' }) })] }))] })] }));
|
|
1137
1205
|
};
|
|
1206
|
+
Pagination.displayName = 'Pagination';
|
|
1138
1207
|
|
|
1139
1208
|
function get(obj, path, defaultValue) {
|
|
1140
1209
|
var _a;
|
|
@@ -1221,9 +1290,10 @@ const useRowSelection = ({ items, getKey, selectedRowKeys, defaultSelectedRowKey
|
|
|
1221
1290
|
emit(Array.from(new Set([...keys, ...selectableKeySet])));
|
|
1222
1291
|
}
|
|
1223
1292
|
}, [allSelected, selectableEntries, keys, emit]);
|
|
1293
|
+
const isSelected = React.useCallback((key) => keySet.has(key), [keySet]);
|
|
1224
1294
|
return {
|
|
1225
1295
|
selectedKeys: keys,
|
|
1226
|
-
isSelected
|
|
1296
|
+
isSelected,
|
|
1227
1297
|
toggleRow,
|
|
1228
1298
|
toggleAll,
|
|
1229
1299
|
allSelected,
|
|
@@ -1334,6 +1404,8 @@ const SortIconMap = {
|
|
|
1334
1404
|
};
|
|
1335
1405
|
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsxRuntime.jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
1336
1406
|
|
|
1407
|
+
const OVERFLOW_REGEX = /\boverflow-(x-|y-)?(auto|hidden|scroll|visible|clip)\b/;
|
|
1408
|
+
const NOOP$1 = () => { };
|
|
1337
1409
|
const DENSITY_CELL_CLASS$2 = {
|
|
1338
1410
|
compact: 'px-2 py-1.5',
|
|
1339
1411
|
default: '',
|
|
@@ -1363,41 +1435,33 @@ const autoSortItems = (items, column, sort) => {
|
|
|
1363
1435
|
return 0;
|
|
1364
1436
|
});
|
|
1365
1437
|
};
|
|
1366
|
-
function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination =
|
|
1367
|
-
const
|
|
1368
|
-
const
|
|
1369
|
-
React.useEffect(() => {
|
|
1370
|
-
setSortedItems(items);
|
|
1371
|
-
}, [items]);
|
|
1372
|
-
const doAutoSort = (column) => {
|
|
1373
|
-
internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
|
|
1374
|
-
setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
|
|
1375
|
-
};
|
|
1376
|
-
const handleSort = (column) => {
|
|
1438
|
+
function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination = NOOP$1, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', tableClass = '', headClass = '', footerRow, rowKey = 'id', onRowClick, rowClass, density = 'default', striped = false, layout = 'auto', selectable = false, selectedRowKeys, defaultSelectedRowKeys, onSelectionChange, isRowSelectable, stickyHeader = true, stickyPagination = true, rowHoverActions, renderRowHoverActions, rowHoverActionHeader, rowHoverActionMode = 'overlay', rowHoverActionClass, paginationClass, paginationContainerClass, paginationProps, renderPagination, }) {
|
|
1439
|
+
const [internalSortingState, setInternalSortingState] = React.useState({});
|
|
1440
|
+
const handleSort = React.useCallback((column) => {
|
|
1377
1441
|
if (!column.sortable)
|
|
1378
1442
|
return;
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
};
|
|
1387
|
-
const
|
|
1388
|
-
if (
|
|
1389
|
-
return
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1443
|
+
if (onSort) {
|
|
1444
|
+
onSort(column);
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
setInternalSortingState((prev) => updateSortQuery(prev, column.name, column.type));
|
|
1448
|
+
}
|
|
1449
|
+
}, [onSort]);
|
|
1450
|
+
const activeSorting = React.useMemo(() => ({ ...sorting, ...internalSortingState }), [sorting, internalSortingState]);
|
|
1451
|
+
const sortedItems = React.useMemo(() => {
|
|
1452
|
+
if (onSort)
|
|
1453
|
+
return items;
|
|
1454
|
+
const activeSortKey = Object.keys(activeSorting)[0];
|
|
1455
|
+
if (!activeSortKey)
|
|
1456
|
+
return items;
|
|
1457
|
+
const column = columns.find((c) => c.name === activeSortKey);
|
|
1458
|
+
if (!column)
|
|
1459
|
+
return items;
|
|
1460
|
+
return autoSortItems(items, column, activeSorting[activeSortKey]);
|
|
1461
|
+
}, [items, columns, onSort, activeSorting]);
|
|
1462
|
+
const getKey = React.useCallback((item, idx) => { var _a; return typeof rowKey === 'function' ? rowKey(item) : ((_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx); }, [rowKey]);
|
|
1463
|
+
const getRowClass = React.useCallback((item, idx) => typeof rowClass === 'function' ? rowClass(item, idx) : rowClass || '', [rowClass]);
|
|
1464
|
+
const getCellClass = React.useCallback((column, item) => typeof column.cellClass === 'function' ? column.cellClass(item) : column.cellClass || '', []);
|
|
1401
1465
|
const { isSelected, toggleRow, toggleAll, allSelected, someSelected } = useRowSelection({
|
|
1402
1466
|
items: sortedItems,
|
|
1403
1467
|
getKey,
|
|
@@ -1406,15 +1470,50 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1406
1470
|
onSelectionChange,
|
|
1407
1471
|
isRowSelectable,
|
|
1408
1472
|
});
|
|
1473
|
+
const hasRowHoverActions = Boolean(rowHoverActions || renderRowHoverActions);
|
|
1474
|
+
const isInlineMode = hasRowHoverActions && rowHoverActionMode === 'inline';
|
|
1475
|
+
const isOverlayMode = hasRowHoverActions && rowHoverActionMode !== 'inline';
|
|
1409
1476
|
const densityCellClass = DENSITY_CELL_CLASS$2[density];
|
|
1410
|
-
const colSize = columns.length + (selectable ? 1 : 0);
|
|
1411
|
-
const wrapperHasOverflow =
|
|
1477
|
+
const colSize = columns.length + (selectable ? 1 : 0) + (isInlineMode ? 1 : 0);
|
|
1478
|
+
const wrapperHasOverflow = OVERFLOW_REGEX.test(wrapperClass);
|
|
1479
|
+
const renderActionsContent = React.useCallback((item, idx) => {
|
|
1480
|
+
if (renderRowHoverActions)
|
|
1481
|
+
return renderRowHoverActions(item, idx);
|
|
1482
|
+
if (!rowHoverActions)
|
|
1483
|
+
return null;
|
|
1484
|
+
return rowHoverActions(item, idx).map((act) => {
|
|
1485
|
+
const isDisabled = typeof act.disabled === 'function' ? act.disabled(item) : act.disabled;
|
|
1486
|
+
const isIconOnly = Boolean(act.icon && !act.label);
|
|
1487
|
+
return (jsxRuntime.jsxs(Button, { size: "xs", theme: act.theme || 'secondary', variant: act.variant || 'plain', disabled: isDisabled, title: act.title || (typeof act.label === 'string' ? act.label : undefined), className: buildClassName(isIconOnly
|
|
1488
|
+
? 'p-1.5 rounded-full hover:bg-black/5 dark:hover:bg-white/10'
|
|
1489
|
+
: 'px-2 py-1', act.className), onClick: (e) => {
|
|
1490
|
+
e.stopPropagation();
|
|
1491
|
+
act.onClick(item, idx, e);
|
|
1492
|
+
}, children: [act.icon && jsxRuntime.jsx("span", { className: "shrink-0", children: act.icon }), act.label && jsxRuntime.jsx("span", { children: act.label })] }, act.id));
|
|
1493
|
+
});
|
|
1494
|
+
}, [renderRowHoverActions, rowHoverActions]);
|
|
1412
1495
|
return (jsxRuntime.jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsxRuntime.jsx("div", { className: buildClassName(!wrapperHasOverflow && 'overflow-x-auto', 'rounded-lg border border-[var(--ui-border)]', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxRuntime.jsxs(Table, { className: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsxRuntime.jsxs(TableHead, { className: headClass, children: [selectable && (jsxRuntime.jsx(TableHeaderCell, { className: buildClassName('bg-gray-50 dark:bg-gray-800 w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select all rows", checked: allSelected, ref: (el) => {
|
|
1413
1496
|
if (el)
|
|
1414
1497
|
el.indeterminate = someSelected;
|
|
1415
|
-
}, onChange: toggleAll }) })), columns.map((column) =>
|
|
1416
|
-
|
|
1417
|
-
|
|
1498
|
+
}, onChange: toggleAll }) })), columns.map((column) => {
|
|
1499
|
+
const sortState = activeSorting[column.name];
|
|
1500
|
+
const ariaSortValue = column.sortable
|
|
1501
|
+
? sortState === 'asc' || sortState === '1'
|
|
1502
|
+
? 'ascending'
|
|
1503
|
+
: sortState === 'desc' || sortState === '-1'
|
|
1504
|
+
? 'descending'
|
|
1505
|
+
: 'none'
|
|
1506
|
+
: undefined;
|
|
1507
|
+
return (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, "aria-sort": ariaSortValue, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]', column.sticky === 'left' &&
|
|
1508
|
+
'sticky left-0 z-30 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
1509
|
+
'sticky right-0 z-30 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: jsxRuntime.jsxs(TextContent, { role: column.sortable ? 'button' : undefined, tabIndex: column.sortable ? 0 : undefined, "aria-label": column.sortable ? `Sort by ${column.label}` : undefined, className: buildClassName('group', column.sortable &&
|
|
1510
|
+
'inline-flex items-center gap-2 cursor-pointer select-none focus:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 rounded'), onClick: () => handleSort(column), onKeyDown: (e) => {
|
|
1511
|
+
if (column.sortable && (e.key === 'Enter' || e.key === ' ')) {
|
|
1512
|
+
e.preventDefault();
|
|
1513
|
+
handleSort(column);
|
|
1514
|
+
}
|
|
1515
|
+
}, children: [jsxRuntime.jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(activeSorting[column.name]) : null] }) }, column.name));
|
|
1516
|
+
}), isInlineMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", className: buildClassName('bg-gray-50 dark:bg-gray-800 text-right w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: rowHoverActionHeader !== null && rowHoverActionHeader !== void 0 ? rowHoverActionHeader : '' })), isOverlayMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: buildClassName('sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 bg-gray-50 dark:bg-gray-800', stickyHeader && 'top-0'), children: null }))] }), jsxRuntime.jsx(TableBody, { loading: loading, colSize: colSize, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: colSize, children: emptyMessage })) : (sortedItems.map((item, idx) => {
|
|
1418
1517
|
const key = getKey(item, idx);
|
|
1419
1518
|
const rowSelectable = !isRowSelectable || isRowSelectable(item);
|
|
1420
1519
|
return (jsxRuntime.jsxs(TableRow, { className: buildClassName('group', onRowClick && 'cursor-pointer', striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40', getRowClass(item, idx)), onClick: onRowClick ? () => onRowClick(item, idx) : undefined, children: [selectable && (jsxRuntime.jsx(TableCell, { className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), children: jsxRuntime.jsx("span", { onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select row", checked: isSelected(key), disabled: !rowSelectable, onChange: () => toggleRow(key) }) }) })), columns.map((column) => {
|
|
@@ -1424,8 +1523,10 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1424
1523
|
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
|
|
1425
1524
|
? column.render(item)
|
|
1426
1525
|
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
1427
|
-
})
|
|
1428
|
-
|
|
1526
|
+
}), 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
|
+
? 'bg-gray-100 dark:bg-gray-800'
|
|
1528
|
+
: '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', 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 })) }))] }));
|
|
1429
1530
|
}
|
|
1430
1531
|
DataTable.displayName = 'DataTable';
|
|
1431
1532
|
|
|
@@ -2054,20 +2155,20 @@ function buildInputClass(className, { isFocusWithin = false, disabled = false, h
|
|
|
2054
2155
|
: 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
|
|
2055
2156
|
}
|
|
2056
2157
|
const Input = React.forwardRef((props, ref) => {
|
|
2057
|
-
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;
|
|
2158
|
+
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, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
|
|
2058
2159
|
const innerRef = React.useRef(null);
|
|
2059
2160
|
React.useImperativeHandle(ref, () => innerRef.current);
|
|
2060
2161
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
2061
2162
|
const isControlled = value !== undefined;
|
|
2062
2163
|
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
2063
2164
|
const charLength = currentVal.length;
|
|
2064
|
-
const handleInputChange = (e) => {
|
|
2165
|
+
const handleInputChange = React.useCallback((e) => {
|
|
2065
2166
|
if (!isControlled) {
|
|
2066
2167
|
setUncontrolledValue(e.target.value);
|
|
2067
2168
|
}
|
|
2068
2169
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
2069
|
-
};
|
|
2070
|
-
const handleClear = () => {
|
|
2170
|
+
}, [isControlled, onChange]);
|
|
2171
|
+
const handleClear = React.useCallback(() => {
|
|
2071
2172
|
if (innerRef.current) {
|
|
2072
2173
|
innerRef.current.value = '';
|
|
2073
2174
|
const event = new Event('input', {
|
|
@@ -2080,18 +2181,21 @@ const Input = React.forwardRef((props, ref) => {
|
|
|
2080
2181
|
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
2081
2182
|
}
|
|
2082
2183
|
onClear === null || onClear === void 0 ? void 0 : onClear();
|
|
2083
|
-
};
|
|
2184
|
+
}, [isControlled, onChange, onClear]);
|
|
2084
2185
|
const hasError = Boolean(error);
|
|
2085
2186
|
const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
|
|
2187
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
2188
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
2189
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
2086
2190
|
const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
|
|
2087
|
-
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);
|
|
2191
|
+
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", "aria-label": "Clear input text", 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);
|
|
2088
2192
|
const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
|
|
2089
2193
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
2090
|
-
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-[var(--ui-border)] 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, {
|
|
2194
|
+
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, "aria-required": required, 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-[var(--ui-border)] 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, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, className: buildClassName(buildInputClass(className, {
|
|
2091
2195
|
disabled: inputProps.disabled,
|
|
2092
2196
|
hasError,
|
|
2093
2197
|
size,
|
|
2094
|
-
}), 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-[var(--ui-border)] 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
|
|
2198
|
+
}), 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-[var(--ui-border)] 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, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2095
2199
|
? 'text-red-500 font-semibold'
|
|
2096
2200
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
2097
2201
|
});
|
|
@@ -2316,16 +2420,20 @@ const THEME_MAP = {
|
|
|
2316
2420
|
danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
|
|
2317
2421
|
};
|
|
2318
2422
|
const Radio = React.forwardRef((props, ref) => {
|
|
2319
|
-
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
|
|
2423
|
+
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
|
|
2320
2424
|
const generatedId = React.useId();
|
|
2321
2425
|
const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
|
|
2322
|
-
|
|
2426
|
+
const hasError = Boolean(error);
|
|
2427
|
+
const errorId = radioId && hasError ? `${radioId}-error` : undefined;
|
|
2428
|
+
const descId = radioId && description ? `${radioId}-desc` : undefined;
|
|
2429
|
+
const describedBy = [ariaDescribedBy, errorId, descId].filter(Boolean).join(' ') || undefined;
|
|
2430
|
+
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, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "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
|
|
2323
2431
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
2324
|
-
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] 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
|
|
2432
|
+
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] 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, "aria-required": required, className: buildClassName(disabled
|
|
2325
2433
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
2326
|
-
: '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
|
|
2434
|
+
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { id: descId, className: buildClassName('text-xs leading-normal', disabled
|
|
2327
2435
|
? 'text-gray-300 dark:text-gray-600'
|
|
2328
|
-
: '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 }) }))] }));
|
|
2436
|
+
: '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, id: errorId, children: error }) }))] }));
|
|
2329
2437
|
});
|
|
2330
2438
|
Radio.displayName = 'Radio';
|
|
2331
2439
|
|
|
@@ -2336,7 +2444,9 @@ const COLUMN_GRID_MAP = {
|
|
|
2336
2444
|
4: 'grid-cols-1 md:grid-cols-4',
|
|
2337
2445
|
};
|
|
2338
2446
|
const RadioGroup = (props) => {
|
|
2339
|
-
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
|
|
2447
|
+
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
|
+
const groupId = React.useId();
|
|
2449
|
+
const groupName = name || groupId;
|
|
2340
2450
|
const items = React.useMemo(() => {
|
|
2341
2451
|
var _a;
|
|
2342
2452
|
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
@@ -2350,21 +2460,26 @@ const RadioGroup = (props) => {
|
|
|
2350
2460
|
: option)) !== null && _a !== void 0 ? _a : [];
|
|
2351
2461
|
}, [options]);
|
|
2352
2462
|
const isCards = variant === 'cards';
|
|
2353
|
-
|
|
2463
|
+
const hasError = Boolean(error);
|
|
2464
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
2465
|
+
const errorId = groupId && hasError && showErrorMessage ? `${groupId}-error` : undefined;
|
|
2466
|
+
const describedBy = [ariaDescribedBy, errorId].filter(Boolean).join(' ') || undefined;
|
|
2467
|
+
return (jsxRuntime.jsxs("div", { role: "radiogroup", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsxRuntime.jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName(columns
|
|
2354
2468
|
? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
|
|
2355
2469
|
: row || isCards
|
|
2356
2470
|
? 'flex flex-wrap gap-3'
|
|
2357
2471
|
: 'flex flex-col gap-3'), children: items.map((item) => {
|
|
2358
2472
|
const isSelected = value === item.value;
|
|
2359
2473
|
const isItemDisabled = disabled || Boolean(item.disabled);
|
|
2474
|
+
const radioItemId = `${groupName}-${item.value}`;
|
|
2360
2475
|
if (isCards) {
|
|
2361
|
-
return (jsxRuntime.jsxs("label", { htmlFor:
|
|
2476
|
+
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
|
|
2362
2477
|
? 'border-[var(--ui-primary)] bg-blue-50/40 dark:bg-blue-950/30 ring-2 ring-[var(--ui-primary-ring)]/30'
|
|
2363
2478
|
: 'border-[var(--ui-border)] bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600', isItemDisabled &&
|
|
2364
|
-
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.jsx(Radio, { name:
|
|
2479
|
+
'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));
|
|
2365
2480
|
}
|
|
2366
|
-
return (jsxRuntime.jsx(Radio, { name:
|
|
2367
|
-
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
2481
|
+
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));
|
|
2482
|
+
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2368
2483
|
};
|
|
2369
2484
|
RadioGroup.displayName = 'RadioGroup';
|
|
2370
2485
|
|
|
@@ -2476,34 +2591,6 @@ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', d
|
|
|
2476
2591
|
};
|
|
2477
2592
|
RadioSwitch.displayName = 'RadioSwitch';
|
|
2478
2593
|
|
|
2479
|
-
/**
|
|
2480
|
-
* Checks if a value is an array.
|
|
2481
|
-
* Equivalent to lodash's _.isArray.
|
|
2482
|
-
*/
|
|
2483
|
-
function isArray(value) {
|
|
2484
|
-
return Array.isArray(value);
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
/**
|
|
2488
|
-
* Checks if a value is empty.
|
|
2489
|
-
* Works for arrays, objects, strings, Maps, and Sets.
|
|
2490
|
-
* Equivalent to lodash's _.isEmpty.
|
|
2491
|
-
*/
|
|
2492
|
-
function isEmpty(value) {
|
|
2493
|
-
if (value == null)
|
|
2494
|
-
return true; // null or undefined
|
|
2495
|
-
if (typeof value === 'string' || isArray(value)) {
|
|
2496
|
-
return value.length === 0;
|
|
2497
|
-
}
|
|
2498
|
-
if (value instanceof Map || value instanceof Set) {
|
|
2499
|
-
return value.size === 0;
|
|
2500
|
-
}
|
|
2501
|
-
if (typeof value === 'object') {
|
|
2502
|
-
return Object.keys(value).length === 0;
|
|
2503
|
-
}
|
|
2504
|
-
return false; // for numbers, booleans, functions, etc.
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
2594
|
const sizeClasses$3 = {
|
|
2508
2595
|
sm: {
|
|
2509
2596
|
track: 'h-1.5',
|
|
@@ -3486,7 +3573,7 @@ const parseNumericValue = (val) => {
|
|
|
3486
3573
|
const cleaned = String(val).replace(/[^0-9.-]/g, '');
|
|
3487
3574
|
return cleaned !== '' ? Number(cleaned) : NaN;
|
|
3488
3575
|
};
|
|
3489
|
-
function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, containerClass = '', itemClass = '', }) {
|
|
3576
|
+
function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, paginationClass, paginationContainerClass, paginationProps, renderPagination, containerClass = '', itemClass = '', }) {
|
|
3490
3577
|
// Internal Search State
|
|
3491
3578
|
const [internalSearchQuery, setInternalSearchQuery] = React.useState('');
|
|
3492
3579
|
const searchQuery = externalSearchQuery !== undefined ? externalSearchQuery : internalSearchQuery;
|
|
@@ -3734,10 +3821,12 @@ function DataList({ items, columns, renderItem, searchable = false, searchQuery:
|
|
|
3734
3821
|
return (jsxRuntime.jsx(SwipeableRow, { leftActions: actions.leftActions, rightActions: actions.rightActions, children: contentNode }, key));
|
|
3735
3822
|
}
|
|
3736
3823
|
return jsxRuntime.jsx("div", { children: contentNode }, key);
|
|
3737
|
-
})) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className:
|
|
3824
|
+
})) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className: buildClassName('pt-2 flex justify-center w-full', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, onChange: (newPag) => setPagination === null || setPagination === void 0 ? void 0 : setPagination(newPag) })) }))] }));
|
|
3738
3825
|
}
|
|
3739
3826
|
DataList.displayName = 'DataList';
|
|
3740
3827
|
|
|
3828
|
+
const NOOP = () => { };
|
|
3829
|
+
const EMPTY_ARRAY = [];
|
|
3741
3830
|
const sizeClasses$1 = {
|
|
3742
3831
|
sm: {
|
|
3743
3832
|
input: 'px-2.5 py-1.5 text-xs leading-5 min-h-[32px]',
|
|
@@ -3759,7 +3848,7 @@ const sizeClasses$1 = {
|
|
|
3759
3848
|
},
|
|
3760
3849
|
};
|
|
3761
3850
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
3762
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options =
|
|
3851
|
+
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;
|
|
3763
3852
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3764
3853
|
return jsxRuntime.jsx(MobilePicker, { ...props });
|
|
3765
3854
|
}
|
|
@@ -3777,7 +3866,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3777
3866
|
const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
|
|
3778
3867
|
const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
|
|
3779
3868
|
const baseInputClass = React.useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
|
|
3780
|
-
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');
|
|
3869
|
+
const chevronIconClass = React.useMemo(() => 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'), [currentSize.icon, disabled]);
|
|
3781
3870
|
const inputRef = React.useRef(null);
|
|
3782
3871
|
const containerRef = React.useRef(null);
|
|
3783
3872
|
React.useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
|
|
@@ -3853,7 +3942,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3853
3942
|
maxSelection,
|
|
3854
3943
|
onChange,
|
|
3855
3944
|
]);
|
|
3856
|
-
const removeSelection = (idx) => {
|
|
3945
|
+
const removeSelection = React.useCallback((idx) => {
|
|
3857
3946
|
var _a;
|
|
3858
3947
|
if (!isArray(selected))
|
|
3859
3948
|
return;
|
|
@@ -3861,7 +3950,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3861
3950
|
next.splice(idx, 1);
|
|
3862
3951
|
onChange(next);
|
|
3863
3952
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
3864
|
-
};
|
|
3953
|
+
}, [selected, onChange]);
|
|
3865
3954
|
const handleOnChangeInternal = React.useCallback((value) => {
|
|
3866
3955
|
if (value === null) {
|
|
3867
3956
|
onChange(multiple ? [] : null);
|
|
@@ -3905,9 +3994,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3905
3994
|
}
|
|
3906
3995
|
}, [onAdd, handleOnChangeInternal]);
|
|
3907
3996
|
const onSearchRef = React.useRef(props.onSearch);
|
|
3908
|
-
|
|
3909
|
-
onSearchRef.current = props.onSearch;
|
|
3910
|
-
}, [props.onSearch]);
|
|
3997
|
+
onSearchRef.current = props.onSearch;
|
|
3911
3998
|
const handleSearch = React.useCallback(async (searchString) => {
|
|
3912
3999
|
var _a;
|
|
3913
4000
|
try {
|
|
@@ -3939,19 +4026,27 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3939
4026
|
return selected !== null && selected !== void 0 ? selected : null;
|
|
3940
4027
|
}
|
|
3941
4028
|
}, [multiple, selected]);
|
|
4029
|
+
const selectedValueSet = React.useMemo(() => {
|
|
4030
|
+
if (!normalizedSelected)
|
|
4031
|
+
return new Set();
|
|
4032
|
+
if (Array.isArray(normalizedSelected)) {
|
|
4033
|
+
return new Set(normalizedSelected.map((s) => getValue(s)));
|
|
4034
|
+
}
|
|
4035
|
+
return new Set([getValue(normalizedSelected)]);
|
|
4036
|
+
}, [normalizedSelected, getValue]);
|
|
3942
4037
|
const isAllSelected = React.useMemo(() => {
|
|
3943
4038
|
if (!multiple || !Array.isArray(normalizedSelected) || !allOptions.length)
|
|
3944
4039
|
return false;
|
|
3945
4040
|
return allOptions.length === normalizedSelected.length;
|
|
3946
4041
|
}, [multiple, normalizedSelected, allOptions]);
|
|
3947
|
-
const handleToggleSelectAll = () => {
|
|
4042
|
+
const handleToggleSelectAll = React.useCallback(() => {
|
|
3948
4043
|
if (isAllSelected) {
|
|
3949
4044
|
onChange([]);
|
|
3950
4045
|
}
|
|
3951
4046
|
else {
|
|
3952
4047
|
onChange(allOptions);
|
|
3953
4048
|
}
|
|
3954
|
-
};
|
|
4049
|
+
}, [isAllSelected, onChange, allOptions]);
|
|
3955
4050
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
3956
4051
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
3957
4052
|
query.trim() &&
|
|
@@ -3960,6 +4055,11 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3960
4055
|
const hasGroupOptions = Boolean(filteredGroups && filteredGroups.length > 0);
|
|
3961
4056
|
const hasFlatOptions = filteredOptions.length > 0;
|
|
3962
4057
|
const hasAnyOptionsToDisplay = hasFlatOptions || hasGroupOptions || hasFreeTextOption || hasAddOption;
|
|
4058
|
+
const renderOptionItem = (option) => {
|
|
4059
|
+
const optVal = getValue(option);
|
|
4060
|
+
const isOptSelected = selectedValueSet.has(optVal);
|
|
4061
|
+
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));
|
|
4062
|
+
};
|
|
3963
4063
|
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) &&
|
|
3964
4064
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
3965
4065
|
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) => {
|
|
@@ -4003,23 +4103,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4003
4103
|
}, 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(normalizedSelected) && (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-[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 &&
|
|
4004
4104
|
query.trim() &&
|
|
4005
4105
|
!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
|
|
4006
|
-
? 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(
|
|
4007
|
-
const optVal = getValue(option);
|
|
4008
|
-
const isOptSelected = multiple
|
|
4009
|
-
? isArray(normalizedSelected) &&
|
|
4010
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
4011
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
4012
|
-
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));
|
|
4013
|
-
})] }, gIdx)))
|
|
4106
|
+
? 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)))
|
|
4014
4107
|
: /* Render Standard Flat Options */
|
|
4015
|
-
filteredOptions.map((
|
|
4016
|
-
const optVal = getValue(option);
|
|
4017
|
-
const isOptSelected = multiple
|
|
4018
|
-
? isArray(normalizedSelected) &&
|
|
4019
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
4020
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
4021
|
-
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));
|
|
4022
|
-
}), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxRuntime.jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]: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, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), 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 }) }))] }));
|
|
4108
|
+
filteredOptions.map(renderOptionItem), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxRuntime.jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]: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, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), 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 }) }))] }));
|
|
4023
4109
|
});
|
|
4024
4110
|
SelectBox.displayName = 'SelectBox';
|
|
4025
4111
|
|
|
@@ -4373,7 +4459,7 @@ const resizeClasses = {
|
|
|
4373
4459
|
both: 'resize',
|
|
4374
4460
|
};
|
|
4375
4461
|
const Textarea = React.forwardRef((props, ref) => {
|
|
4376
|
-
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;
|
|
4462
|
+
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, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
|
|
4377
4463
|
const innerRef = React.useRef(null);
|
|
4378
4464
|
React.useImperativeHandle(ref, () => innerRef.current);
|
|
4379
4465
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
@@ -4387,19 +4473,22 @@ const Textarea = React.forwardRef((props, ref) => {
|
|
|
4387
4473
|
innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
|
|
4388
4474
|
}
|
|
4389
4475
|
}, [autoSize, currentVal]);
|
|
4390
|
-
const handleTextareaChange = (e) => {
|
|
4476
|
+
const handleTextareaChange = React.useCallback((e) => {
|
|
4391
4477
|
if (!isControlled) {
|
|
4392
4478
|
setUncontrolledValue(e.target.value);
|
|
4393
4479
|
}
|
|
4394
4480
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
4395
|
-
};
|
|
4481
|
+
}, [isControlled, onChange]);
|
|
4396
4482
|
const hasError = !isEmpty(error);
|
|
4397
4483
|
const hasRightGroup = rightGroup != null && rightGroup !== undefined;
|
|
4398
4484
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4399
4485
|
const currentSize = sizeClasses[size] || sizeClasses.md;
|
|
4400
|
-
|
|
4486
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
4487
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
4488
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
4489
|
+
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, "aria-required": required, 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, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, 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
|
|
4401
4490
|
? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
|
|
4402
|
-
: 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', 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
|
|
4491
|
+
: 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', 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, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
4403
4492
|
? 'text-red-500 font-semibold'
|
|
4404
4493
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
4405
4494
|
});
|