@pk-design/react-tailwind 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/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 +8 -3
- 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 +9 -4
- 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 +264 -177
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +264 -177
- 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
|
@@ -136,17 +136,17 @@ const THEMED_BTN_CLASS = {
|
|
|
136
136
|
primary: {
|
|
137
137
|
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)]',
|
|
138
138
|
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)]',
|
|
139
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
139
|
+
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)]',
|
|
140
140
|
},
|
|
141
141
|
danger: {
|
|
142
142
|
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)]',
|
|
143
143
|
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)]',
|
|
144
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
144
|
+
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)]',
|
|
145
145
|
},
|
|
146
146
|
secondary: {
|
|
147
147
|
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',
|
|
148
148
|
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)]',
|
|
149
|
-
plain: 'text-gray-700 hover:bg-
|
|
149
|
+
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',
|
|
150
150
|
},
|
|
151
151
|
success: {
|
|
152
152
|
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',
|
|
@@ -186,54 +186,72 @@ const LOADER_TEXT_CLASS = {
|
|
|
186
186
|
plain: 'text-amber-600 dark:text-amber-400',
|
|
187
187
|
},
|
|
188
188
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
: 'h-10 px-5 rounded-lg gap-2 text-base';
|
|
204
|
-
}
|
|
189
|
+
const SIZE_CLASSES = {
|
|
190
|
+
xs: { normal: 'h-7 px-2.5 rounded-sm gap-1 text-xs', iconOnly: 'h-7 w-7 p-1 rounded-sm text-xs' },
|
|
191
|
+
sm: { normal: 'h-8 px-3 rounded gap-1.5 text-sm', iconOnly: 'h-8 w-8 p-1.5 rounded text-sm' },
|
|
192
|
+
md: { normal: 'h-9 px-4 rounded-md gap-2 text-sm', iconOnly: 'h-9 w-9 p-2 rounded-md text-sm' },
|
|
193
|
+
lg: {
|
|
194
|
+
normal: 'h-10 px-5 rounded-lg gap-2 text-base',
|
|
195
|
+
iconOnly: 'h-10 w-10 p-2.5 rounded-lg text-base',
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
const LOADER_SIZE = {
|
|
199
|
+
xs: 'xs',
|
|
200
|
+
sm: 'xs',
|
|
201
|
+
md: 'sm',
|
|
202
|
+
lg: 'sm',
|
|
205
203
|
};
|
|
206
204
|
/**
|
|
207
205
|
* Base shared tailwind classes.
|
|
208
206
|
*/
|
|
209
|
-
const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
|
|
207
|
+
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
210
208
|
'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
|
|
211
209
|
'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
|
|
212
210
|
const Button = forwardRef(({ as, type, ...props }, ref) => {
|
|
213
|
-
|
|
211
|
+
var _a, _b, _c, _d;
|
|
212
|
+
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, active, onClick, ...restProps } = props;
|
|
214
213
|
const Component = as || 'button';
|
|
214
|
+
const isNativeButton = Component === 'button';
|
|
215
215
|
// Accessibility guard for icon-only buttons
|
|
216
216
|
if (iconOnly && !restProps['aria-label']) {
|
|
217
217
|
console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
|
|
218
218
|
}
|
|
219
|
-
const resolvedDisabled = disabled || loading;
|
|
219
|
+
const resolvedDisabled = Boolean(disabled || loading);
|
|
220
220
|
// Clean invalid props when not rendering a real button
|
|
221
221
|
const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
|
|
222
|
-
const
|
|
222
|
+
const handleClick = (e) => {
|
|
223
|
+
if (resolvedDisabled) {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
e.stopPropagation();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
229
|
+
};
|
|
230
|
+
const elementProps = isNativeButton
|
|
223
231
|
? {
|
|
224
232
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
225
233
|
disabled: resolvedDisabled,
|
|
234
|
+
onClick: handleClick,
|
|
226
235
|
}
|
|
227
|
-
: {
|
|
228
|
-
|
|
236
|
+
: {
|
|
237
|
+
role: (_a = cleanRest.role) !== null && _a !== void 0 ? _a : 'button',
|
|
238
|
+
tabIndex: resolvedDisabled ? -1 : ((_b = cleanRest.tabIndex) !== null && _b !== void 0 ? _b : 0),
|
|
239
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
240
|
+
onClick: handleClick,
|
|
241
|
+
};
|
|
242
|
+
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
243
|
+
const classes = useMemo(() => {
|
|
244
|
+
var _a;
|
|
245
|
+
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);
|
|
246
|
+
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
247
|
+
const loaderIcon = (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]) }));
|
|
229
248
|
/**
|
|
230
249
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
231
250
|
*/
|
|
232
251
|
if (loading && iconOnly) {
|
|
233
|
-
return (jsx(Component, { ref: ref, className: classes,
|
|
252
|
+
return (jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loaderIcon }));
|
|
234
253
|
}
|
|
235
|
-
|
|
236
|
-
return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading && !loadingText ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, children] })) : loading && loadingText ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText })] })) : (jsxs(Fragment, { children: [leftIcon && jsx("span", { className: "shrink-0 flex items-center", children: leftIcon }), children, rightIcon && jsx("span", { className: "shrink-0 flex items-center", children: rightIcon })] })) }));
|
|
254
|
+
return (jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loading ? (jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsx(TextContent, { className: buildClassName((_d = LOADER_TEXT_CLASS[theme]) === null || _d === void 0 ? void 0 : _d[variant]), children: loadingText })) : (children)] })) : (jsxs(Fragment, { children: [leftIcon && jsx("span", { className: "shrink-0 inline-flex items-center", children: leftIcon }), children, rightIcon && jsx("span", { className: "shrink-0 inline-flex items-center", children: rightIcon })] })) }));
|
|
237
255
|
});
|
|
238
256
|
Button.displayName = 'Button';
|
|
239
257
|
|
|
@@ -880,6 +898,34 @@ const Label = forwardRef((props, ref) => {
|
|
|
880
898
|
});
|
|
881
899
|
Label.displayName = 'Label';
|
|
882
900
|
|
|
901
|
+
/**
|
|
902
|
+
* Checks if a value is an array.
|
|
903
|
+
* Equivalent to lodash's _.isArray.
|
|
904
|
+
*/
|
|
905
|
+
function isArray(value) {
|
|
906
|
+
return Array.isArray(value);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Checks if a value is empty.
|
|
911
|
+
* Works for arrays, objects, strings, Maps, and Sets.
|
|
912
|
+
* Equivalent to lodash's _.isEmpty.
|
|
913
|
+
*/
|
|
914
|
+
function isEmpty(value) {
|
|
915
|
+
if (value == null)
|
|
916
|
+
return true; // null or undefined
|
|
917
|
+
if (typeof value === 'string' || isArray(value)) {
|
|
918
|
+
return value.length === 0;
|
|
919
|
+
}
|
|
920
|
+
if (value instanceof Map || value instanceof Set) {
|
|
921
|
+
return value.size === 0;
|
|
922
|
+
}
|
|
923
|
+
if (typeof value === 'object') {
|
|
924
|
+
return Object.keys(value).length === 0;
|
|
925
|
+
}
|
|
926
|
+
return false; // for numbers, booleans, functions, etc.
|
|
927
|
+
}
|
|
928
|
+
|
|
883
929
|
const sizeStyles = {
|
|
884
930
|
sm: {
|
|
885
931
|
box: 'size-3.5',
|
|
@@ -902,7 +948,7 @@ const sizeStyles = {
|
|
|
902
948
|
};
|
|
903
949
|
const Checkbox = forwardRef((props, ref) => {
|
|
904
950
|
var _a;
|
|
905
|
-
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
|
|
951
|
+
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;
|
|
906
952
|
const generatedId = useId();
|
|
907
953
|
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
908
954
|
const innerRef = useRef(null);
|
|
@@ -914,6 +960,11 @@ const Checkbox = forwardRef((props, ref) => {
|
|
|
914
960
|
}, [indeterminate]);
|
|
915
961
|
const currentSize = sizeStyles[size] || sizeStyles.md;
|
|
916
962
|
const isCard = variant === 'card';
|
|
963
|
+
const hasError = !isEmpty(error);
|
|
964
|
+
const errorId = checkboxId && hasError ? `${checkboxId}-error` : undefined;
|
|
965
|
+
const helperId = checkboxId && helperText && !hasError ? `${checkboxId}-helper` : undefined;
|
|
966
|
+
const descId = checkboxId && description ? `${checkboxId}-desc` : undefined;
|
|
967
|
+
const describedBy = [ariaDescribedBy, errorId, helperId, descId].filter(Boolean).join(' ') || undefined;
|
|
917
968
|
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsx("div", { className: buildClassName(isCard
|
|
918
969
|
? 'rounded-xl border p-3.5 transition-all duration-150 relative'
|
|
919
970
|
: 'flex items-center', isCard &&
|
|
@@ -921,16 +972,16 @@ const Checkbox = forwardRef((props, ref) => {
|
|
|
921
972
|
? 'border-[var(--ui-border-muted)] bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
|
|
922
973
|
: checked || indeterminate
|
|
923
974
|
? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
|
|
924
|
-
: 'border-[var(--ui-border)] bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: 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: [jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [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
|
|
975
|
+
: 'border-[var(--ui-border)] bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: 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: [jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [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
|
|
925
976
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
926
977
|
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500', indeterminate &&
|
|
927
978
|
'!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsx("span", { className: buildClassName('absolute inset-0 flex items-center justify-center text-white pointer-events-none transition-opacity', checked !== undefined
|
|
928
979
|
? checked
|
|
929
980
|
? 'opacity-100'
|
|
930
981
|
: 'opacity-0'
|
|
931
|
-
: 'opacity-0 peer-checked:opacity-100'), children: jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: 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 && (jsx("span", { className: "absolute inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
|
|
982
|
+
: 'opacity-0 peer-checked:opacity-100'), children: jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: 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 && (jsx("span", { className: "absolute inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsx(Label, { htmlFor: checkboxId, "aria-required": required, className: buildClassName(disabled
|
|
932
983
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
933
|
-
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsx("span", { className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, children: error })) : (jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
984
|
+
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (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) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
934
985
|
});
|
|
935
986
|
Checkbox.displayName = 'Checkbox';
|
|
936
987
|
|
|
@@ -941,25 +992,26 @@ const columnClasses = {
|
|
|
941
992
|
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
|
|
942
993
|
};
|
|
943
994
|
const CheckboxGroup = (props) => {
|
|
944
|
-
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;
|
|
995
|
+
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;
|
|
945
996
|
const groupId = useId();
|
|
946
997
|
const [internalValue, setInternalValue] = useState(defaultValue || []);
|
|
947
998
|
const isControlled = controlledValue !== undefined;
|
|
948
|
-
const currentValue = isControlled ? controlledValue : internalValue;
|
|
999
|
+
const currentValue = isControlled ? controlledValue || [] : internalValue;
|
|
949
1000
|
const items = useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
|
|
950
1001
|
const enabledItems = useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
|
|
1002
|
+
const currentValueKeySet = useMemo(() => new Set(currentValue), [currentValue]);
|
|
951
1003
|
// Select All State calculation
|
|
952
1004
|
const allSelected = useMemo(() => {
|
|
953
1005
|
if (!enabledItems.length)
|
|
954
1006
|
return false;
|
|
955
|
-
return enabledItems.every((item) =>
|
|
956
|
-
}, [enabledItems,
|
|
1007
|
+
return enabledItems.every((item) => currentValueKeySet.has(item.value));
|
|
1008
|
+
}, [enabledItems, currentValueKeySet]);
|
|
957
1009
|
const isIndeterminate = useMemo(() => {
|
|
958
1010
|
if (allSelected)
|
|
959
1011
|
return false;
|
|
960
|
-
return enabledItems.some((item) =>
|
|
961
|
-
}, [allSelected, enabledItems,
|
|
962
|
-
const handleSelectAllChange = (e) => {
|
|
1012
|
+
return enabledItems.some((item) => currentValueKeySet.has(item.value));
|
|
1013
|
+
}, [allSelected, enabledItems, currentValueKeySet]);
|
|
1014
|
+
const handleSelectAllChange = useCallback((e) => {
|
|
963
1015
|
let updated;
|
|
964
1016
|
if (allSelected || isIndeterminate) {
|
|
965
1017
|
const enabledValues = new Set(enabledItems.map((i) => i.value));
|
|
@@ -974,8 +1026,8 @@ const CheckboxGroup = (props) => {
|
|
|
974
1026
|
setInternalValue(updated);
|
|
975
1027
|
}
|
|
976
1028
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
977
|
-
};
|
|
978
|
-
const handleGroupChange = (e) => {
|
|
1029
|
+
}, [allSelected, isIndeterminate, enabledItems, currentValue, isControlled, onChange]);
|
|
1030
|
+
const handleGroupChange = useCallback((e) => {
|
|
979
1031
|
const raw = e.target.value;
|
|
980
1032
|
const checked = e.target.checked;
|
|
981
1033
|
const matched = items.find((i) => String(i.value) === raw);
|
|
@@ -991,16 +1043,21 @@ const CheckboxGroup = (props) => {
|
|
|
991
1043
|
setInternalValue(updated);
|
|
992
1044
|
}
|
|
993
1045
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
994
|
-
};
|
|
1046
|
+
}, [items, currentValue, isControlled, onChange]);
|
|
1047
|
+
const hasError = Boolean(error);
|
|
1048
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
1049
|
+
const errorId = groupId && hasError ? `${groupId}-error` : undefined;
|
|
1050
|
+
const helperId = groupId && helperText && !hasError ? `${groupId}-helper` : undefined;
|
|
1051
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
995
1052
|
const layoutClass = columns
|
|
996
1053
|
? columnClasses[columns]
|
|
997
1054
|
: inline
|
|
998
1055
|
? 'flex flex-wrap gap-3'
|
|
999
1056
|
: 'flex flex-col gap-3';
|
|
1000
|
-
return (jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint && (jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsx("div", { className: "pb-1 border-b border-[var(--ui-border)]", children: jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsx("div", { className: layoutClass, children: items.map((item, index) => {
|
|
1057
|
+
return (jsxs("div", { role: "group", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint && (jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsx("div", { className: "pb-1 border-b border-[var(--ui-border)]", children: jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsx("div", { className: layoutClass, children: items.map((item, index) => {
|
|
1001
1058
|
const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
|
|
1002
|
-
return (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:
|
|
1003
|
-
}) }), (error || helperText) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, children: error })) : (jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
1059
|
+
return (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));
|
|
1060
|
+
}) }), (error || helperText) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
1004
1061
|
};
|
|
1005
1062
|
CheckboxGroup.displayName = 'CheckboxGroup';
|
|
1006
1063
|
|
|
@@ -1094,27 +1151,39 @@ var SvgChevronRight = function SvgChevronRight(props) {
|
|
|
1094
1151
|
})));
|
|
1095
1152
|
};
|
|
1096
1153
|
|
|
1097
|
-
const Pagination = (
|
|
1098
|
-
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
const
|
|
1102
|
-
const
|
|
1154
|
+
const Pagination = (props) => {
|
|
1155
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1156
|
+
const { className, ellipsis = true, onChange, isMobile, showSummary = true, showPageNumbers = true, } = props;
|
|
1157
|
+
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;
|
|
1158
|
+
const limit = (_e = (_d = props.limit) !== null && _d !== void 0 ? _d : props.pageSize) !== null && _e !== void 0 ? _e : 10;
|
|
1159
|
+
const total = (_f = props.total) !== null && _f !== void 0 ? _f : 0;
|
|
1160
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
1161
|
+
const isFirstPage = currentPage <= 1;
|
|
1162
|
+
const isLastPage = total === 0 || currentPage >= totalPages;
|
|
1163
|
+
const range = usePagination({ totalPages, currentPage });
|
|
1103
1164
|
const handlePageChange = (nextVal) => {
|
|
1104
|
-
if (nextVal === currentPage)
|
|
1165
|
+
if (nextVal === currentPage || nextVal < 1 || nextVal > totalPages)
|
|
1105
1166
|
return;
|
|
1106
1167
|
onChange({ page: nextVal });
|
|
1107
1168
|
};
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
return (jsx("li", { children: 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}`));
|
|
1114
|
-
}), jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-next-btn", children: "Next" }) })] })] })] }));
|
|
1169
|
+
const startResult = total === 0 ? 0 : Math.max((currentPage - 1) * limit + 1, 1);
|
|
1170
|
+
const endResult = Math.min(currentPage * limit, total);
|
|
1171
|
+
// Explicit Mobile UX (when isMobile=true)
|
|
1172
|
+
if (isMobile) {
|
|
1173
|
+
return (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: [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: [jsx(SvgChevronLeft, { className: "size-4" }), jsx("span", { children: "Previous" })] }), jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), 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: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }));
|
|
1115
1174
|
}
|
|
1116
|
-
|
|
1175
|
+
// Standard Responsive View
|
|
1176
|
+
return (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: [jsxs("div", { className: "flex flex-1 items-center justify-between sm:hidden", children: [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: [jsx(SvgChevronLeft, { className: "size-4" }), jsx("span", { children: "Previous" })] }), jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), 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: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [showSummary && (jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: startResult }), "to", jsx("span", { className: "font-medium", children: endResult }), "of", jsx("span", { className: "font-medium", children: total }), "results"] })), showPageNumbers && (jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsx("li", { children: 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
|
|
1177
|
+
? range.map((pageItem, idx) => {
|
|
1178
|
+
if (pageItem === DOTS) {
|
|
1179
|
+
return (jsx("li", { "data-testid": `page-${pageItem}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${pageItem}-${idx}`));
|
|
1180
|
+
}
|
|
1181
|
+
const pageNum = Number(pageItem);
|
|
1182
|
+
return (jsx("li", { children: 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}`));
|
|
1183
|
+
})
|
|
1184
|
+
: Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => (jsx("li", { children: jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: jsx("span", { className: "size-4 block", children: pageNum }) }) }, pageNum))), jsx("li", { children: 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' }) })] }))] })] }));
|
|
1117
1185
|
};
|
|
1186
|
+
Pagination.displayName = 'Pagination';
|
|
1118
1187
|
|
|
1119
1188
|
function get(obj, path, defaultValue) {
|
|
1120
1189
|
var _a;
|
|
@@ -1201,9 +1270,10 @@ const useRowSelection = ({ items, getKey, selectedRowKeys, defaultSelectedRowKey
|
|
|
1201
1270
|
emit(Array.from(new Set([...keys, ...selectableKeySet])));
|
|
1202
1271
|
}
|
|
1203
1272
|
}, [allSelected, selectableEntries, keys, emit]);
|
|
1273
|
+
const isSelected = useCallback((key) => keySet.has(key), [keySet]);
|
|
1204
1274
|
return {
|
|
1205
1275
|
selectedKeys: keys,
|
|
1206
|
-
isSelected
|
|
1276
|
+
isSelected,
|
|
1207
1277
|
toggleRow,
|
|
1208
1278
|
toggleAll,
|
|
1209
1279
|
allSelected,
|
|
@@ -1314,6 +1384,8 @@ const SortIconMap = {
|
|
|
1314
1384
|
};
|
|
1315
1385
|
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
1316
1386
|
|
|
1387
|
+
const OVERFLOW_REGEX = /\boverflow-(x-|y-)?(auto|hidden|scroll|visible|clip)\b/;
|
|
1388
|
+
const NOOP$1 = () => { };
|
|
1317
1389
|
const DENSITY_CELL_CLASS$2 = {
|
|
1318
1390
|
compact: 'px-2 py-1.5',
|
|
1319
1391
|
default: '',
|
|
@@ -1343,41 +1415,33 @@ const autoSortItems = (items, column, sort) => {
|
|
|
1343
1415
|
return 0;
|
|
1344
1416
|
});
|
|
1345
1417
|
};
|
|
1346
|
-
function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination =
|
|
1347
|
-
const
|
|
1348
|
-
const
|
|
1349
|
-
useEffect(() => {
|
|
1350
|
-
setSortedItems(items);
|
|
1351
|
-
}, [items]);
|
|
1352
|
-
const doAutoSort = (column) => {
|
|
1353
|
-
internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
|
|
1354
|
-
setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
|
|
1355
|
-
};
|
|
1356
|
-
const handleSort = (column) => {
|
|
1418
|
+
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, }) {
|
|
1419
|
+
const [internalSortingState, setInternalSortingState] = useState({});
|
|
1420
|
+
const handleSort = useCallback((column) => {
|
|
1357
1421
|
if (!column.sortable)
|
|
1358
1422
|
return;
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
};
|
|
1367
|
-
const
|
|
1368
|
-
if (
|
|
1369
|
-
return
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1423
|
+
if (onSort) {
|
|
1424
|
+
onSort(column);
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
setInternalSortingState((prev) => updateSortQuery(prev, column.name, column.type));
|
|
1428
|
+
}
|
|
1429
|
+
}, [onSort]);
|
|
1430
|
+
const activeSorting = useMemo(() => ({ ...sorting, ...internalSortingState }), [sorting, internalSortingState]);
|
|
1431
|
+
const sortedItems = useMemo(() => {
|
|
1432
|
+
if (onSort)
|
|
1433
|
+
return items;
|
|
1434
|
+
const activeSortKey = Object.keys(activeSorting)[0];
|
|
1435
|
+
if (!activeSortKey)
|
|
1436
|
+
return items;
|
|
1437
|
+
const column = columns.find((c) => c.name === activeSortKey);
|
|
1438
|
+
if (!column)
|
|
1439
|
+
return items;
|
|
1440
|
+
return autoSortItems(items, column, activeSorting[activeSortKey]);
|
|
1441
|
+
}, [items, columns, onSort, activeSorting]);
|
|
1442
|
+
const getKey = useCallback((item, idx) => { var _a; return typeof rowKey === 'function' ? rowKey(item) : ((_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx); }, [rowKey]);
|
|
1443
|
+
const getRowClass = useCallback((item, idx) => typeof rowClass === 'function' ? rowClass(item, idx) : rowClass || '', [rowClass]);
|
|
1444
|
+
const getCellClass = useCallback((column, item) => typeof column.cellClass === 'function' ? column.cellClass(item) : column.cellClass || '', []);
|
|
1381
1445
|
const { isSelected, toggleRow, toggleAll, allSelected, someSelected } = useRowSelection({
|
|
1382
1446
|
items: sortedItems,
|
|
1383
1447
|
getKey,
|
|
@@ -1386,15 +1450,50 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1386
1450
|
onSelectionChange,
|
|
1387
1451
|
isRowSelectable,
|
|
1388
1452
|
});
|
|
1453
|
+
const hasRowHoverActions = Boolean(rowHoverActions || renderRowHoverActions);
|
|
1454
|
+
const isInlineMode = hasRowHoverActions && rowHoverActionMode === 'inline';
|
|
1455
|
+
const isOverlayMode = hasRowHoverActions && rowHoverActionMode !== 'inline';
|
|
1389
1456
|
const densityCellClass = DENSITY_CELL_CLASS$2[density];
|
|
1390
|
-
const colSize = columns.length + (selectable ? 1 : 0);
|
|
1391
|
-
const wrapperHasOverflow =
|
|
1457
|
+
const colSize = columns.length + (selectable ? 1 : 0) + (isInlineMode ? 1 : 0);
|
|
1458
|
+
const wrapperHasOverflow = OVERFLOW_REGEX.test(wrapperClass);
|
|
1459
|
+
const renderActionsContent = useCallback((item, idx) => {
|
|
1460
|
+
if (renderRowHoverActions)
|
|
1461
|
+
return renderRowHoverActions(item, idx);
|
|
1462
|
+
if (!rowHoverActions)
|
|
1463
|
+
return null;
|
|
1464
|
+
return rowHoverActions(item, idx).map((act) => {
|
|
1465
|
+
const isDisabled = typeof act.disabled === 'function' ? act.disabled(item) : act.disabled;
|
|
1466
|
+
const isIconOnly = Boolean(act.icon && !act.label);
|
|
1467
|
+
return (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
|
|
1468
|
+
? 'p-1.5 rounded-full hover:bg-black/5 dark:hover:bg-white/10'
|
|
1469
|
+
: 'px-2 py-1', act.className), onClick: (e) => {
|
|
1470
|
+
e.stopPropagation();
|
|
1471
|
+
act.onClick(item, idx, e);
|
|
1472
|
+
}, children: [act.icon && jsx("span", { className: "shrink-0", children: act.icon }), act.label && jsx("span", { children: act.label })] }, act.id));
|
|
1473
|
+
});
|
|
1474
|
+
}, [renderRowHoverActions, rowHoverActions]);
|
|
1392
1475
|
return (jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsx("div", { className: buildClassName(!wrapperHasOverflow && 'overflow-x-auto', 'rounded-lg border border-[var(--ui-border)]', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxs(Table, { className: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsxs(TableHead, { className: headClass, children: [selectable && (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: jsx(Checkbox, { "aria-label": "Select all rows", checked: allSelected, ref: (el) => {
|
|
1393
1476
|
if (el)
|
|
1394
1477
|
el.indeterminate = someSelected;
|
|
1395
|
-
}, onChange: toggleAll }) })), columns.map((column) =>
|
|
1396
|
-
|
|
1397
|
-
|
|
1478
|
+
}, onChange: toggleAll }) })), columns.map((column) => {
|
|
1479
|
+
const sortState = activeSorting[column.name];
|
|
1480
|
+
const ariaSortValue = column.sortable
|
|
1481
|
+
? sortState === 'asc' || sortState === '1'
|
|
1482
|
+
? 'ascending'
|
|
1483
|
+
: sortState === 'desc' || sortState === '-1'
|
|
1484
|
+
? 'descending'
|
|
1485
|
+
: 'none'
|
|
1486
|
+
: undefined;
|
|
1487
|
+
return (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' &&
|
|
1488
|
+
'sticky left-0 z-30 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
1489
|
+
'sticky right-0 z-30 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: 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 &&
|
|
1490
|
+
'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) => {
|
|
1491
|
+
if (column.sortable && (e.key === 'Enter' || e.key === ' ')) {
|
|
1492
|
+
e.preventDefault();
|
|
1493
|
+
handleSort(column);
|
|
1494
|
+
}
|
|
1495
|
+
}, children: [jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(activeSorting[column.name]) : null] }) }, column.name));
|
|
1496
|
+
}), isInlineMode && (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 && (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 }))] }), jsx(TableBody, { loading: loading, colSize: colSize, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsx(EmptyTableRow, { colSpan: colSize, children: emptyMessage })) : (sortedItems.map((item, idx) => {
|
|
1398
1497
|
const key = getKey(item, idx);
|
|
1399
1498
|
const rowSelectable = !isRowSelectable || isRowSelectable(item);
|
|
1400
1499
|
return (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 && (jsx(TableCell, { className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), children: jsx("span", { onClick: (e) => e.stopPropagation(), children: jsx(Checkbox, { "aria-label": "Select row", checked: isSelected(key), disabled: !rowSelectable, onChange: () => toggleRow(key) }) }) })), columns.map((column) => {
|
|
@@ -1404,8 +1503,10 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1404
1503
|
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
|
|
1405
1504
|
? column.render(item)
|
|
1406
1505
|
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
1407
|
-
})
|
|
1408
|
-
|
|
1506
|
+
}), isInlineMode && (jsx(TableCell, { align: "right", className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), onClick: (e) => e.stopPropagation(), children: 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 && (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: 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
|
|
1507
|
+
? 'bg-gray-100 dark:bg-gray-800'
|
|
1508
|
+
: '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));
|
|
1509
|
+
})) }), footerRow && jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsx("div", { className: buildClassName(stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsx(Pagination, { ...pagination, ...paginationProps, className: buildClassName('rounded-t-none rounded-b-lg border border-[var(--ui-border)]', stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationProps === null || paginationProps === void 0 ? void 0 : paginationProps.className), onChange: setPagination })) }))] }));
|
|
1409
1510
|
}
|
|
1410
1511
|
DataTable.displayName = 'DataTable';
|
|
1411
1512
|
|
|
@@ -2034,20 +2135,20 @@ function buildInputClass(className, { isFocusWithin = false, disabled = false, h
|
|
|
2034
2135
|
: 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
|
|
2035
2136
|
}
|
|
2036
2137
|
const Input = forwardRef((props, ref) => {
|
|
2037
|
-
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;
|
|
2138
|
+
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;
|
|
2038
2139
|
const innerRef = useRef(null);
|
|
2039
2140
|
useImperativeHandle(ref, () => innerRef.current);
|
|
2040
2141
|
const [uncontrolledValue, setUncontrolledValue] = useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
2041
2142
|
const isControlled = value !== undefined;
|
|
2042
2143
|
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
2043
2144
|
const charLength = currentVal.length;
|
|
2044
|
-
const handleInputChange = (e) => {
|
|
2145
|
+
const handleInputChange = useCallback((e) => {
|
|
2045
2146
|
if (!isControlled) {
|
|
2046
2147
|
setUncontrolledValue(e.target.value);
|
|
2047
2148
|
}
|
|
2048
2149
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
2049
|
-
};
|
|
2050
|
-
const handleClear = () => {
|
|
2150
|
+
}, [isControlled, onChange]);
|
|
2151
|
+
const handleClear = useCallback(() => {
|
|
2051
2152
|
if (innerRef.current) {
|
|
2052
2153
|
innerRef.current.value = '';
|
|
2053
2154
|
const event = new Event('input', {
|
|
@@ -2060,18 +2161,21 @@ const Input = forwardRef((props, ref) => {
|
|
|
2060
2161
|
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
2061
2162
|
}
|
|
2062
2163
|
onClear === null || onClear === void 0 ? void 0 : onClear();
|
|
2063
|
-
};
|
|
2164
|
+
}, [isControlled, onChange, onClear]);
|
|
2064
2165
|
const hasError = Boolean(error);
|
|
2065
2166
|
const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
|
|
2167
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
2168
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
2169
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
2066
2170
|
const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
|
|
2067
|
-
const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (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: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: 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);
|
|
2171
|
+
const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (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: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: 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);
|
|
2068
2172
|
const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
|
|
2069
2173
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
2070
|
-
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (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 })), jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (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 })), 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, {
|
|
2174
|
+
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (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 })), jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (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 })), 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, {
|
|
2071
2175
|
disabled: inputProps.disabled,
|
|
2072
2176
|
hasError,
|
|
2073
2177
|
size,
|
|
2074
|
-
}), 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 && (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 && (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 ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2178
|
+
}), 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 && (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 && (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 ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2075
2179
|
? 'text-red-500 font-semibold'
|
|
2076
2180
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
2077
2181
|
});
|
|
@@ -2296,16 +2400,20 @@ const THEME_MAP = {
|
|
|
2296
2400
|
danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
|
|
2297
2401
|
};
|
|
2298
2402
|
const Radio = forwardRef((props, ref) => {
|
|
2299
|
-
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
|
|
2403
|
+
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
|
|
2300
2404
|
const generatedId = useId();
|
|
2301
2405
|
const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
|
|
2302
|
-
|
|
2406
|
+
const hasError = Boolean(error);
|
|
2407
|
+
const errorId = radioId && hasError ? `${radioId}-error` : undefined;
|
|
2408
|
+
const descId = radioId && description ? `${radioId}-desc` : undefined;
|
|
2409
|
+
const describedBy = [ariaDescribedBy, errorId, descId].filter(Boolean).join(' ') || undefined;
|
|
2410
|
+
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-start gap-2.5', containerClass), children: [jsx("div", { className: "grid place-items-center shrink-0 pt-0.5", children: 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
|
|
2303
2411
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
2304
|
-
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
|
|
2412
|
+
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsx(Label, { htmlFor: radioId, "aria-required": required, className: buildClassName(disabled
|
|
2305
2413
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
2306
|
-
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsx("p", { className: buildClassName('text-xs leading-normal', disabled
|
|
2414
|
+
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsx("p", { id: descId, className: buildClassName('text-xs leading-normal', disabled
|
|
2307
2415
|
? 'text-gray-300 dark:text-gray-600'
|
|
2308
|
-
: 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
2416
|
+
: 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2309
2417
|
});
|
|
2310
2418
|
Radio.displayName = 'Radio';
|
|
2311
2419
|
|
|
@@ -2316,7 +2424,8 @@ const COLUMN_GRID_MAP = {
|
|
|
2316
2424
|
4: 'grid-cols-1 md:grid-cols-4',
|
|
2317
2425
|
};
|
|
2318
2426
|
const RadioGroup = (props) => {
|
|
2319
|
-
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
|
|
2427
|
+
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;
|
|
2428
|
+
const groupId = useId();
|
|
2320
2429
|
const items = useMemo(() => {
|
|
2321
2430
|
var _a;
|
|
2322
2431
|
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
@@ -2330,7 +2439,11 @@ const RadioGroup = (props) => {
|
|
|
2330
2439
|
: option)) !== null && _a !== void 0 ? _a : [];
|
|
2331
2440
|
}, [options]);
|
|
2332
2441
|
const isCards = variant === 'cards';
|
|
2333
|
-
|
|
2442
|
+
const hasError = Boolean(error);
|
|
2443
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
2444
|
+
const errorId = groupId && hasError && showErrorMessage ? `${groupId}-error` : undefined;
|
|
2445
|
+
const describedBy = [ariaDescribedBy, errorId].filter(Boolean).join(' ') || undefined;
|
|
2446
|
+
return (jsxs("div", { role: "radiogroup", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint] })), jsx("div", { className: buildClassName(columns
|
|
2334
2447
|
? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
|
|
2335
2448
|
: row || isCards
|
|
2336
2449
|
? 'flex flex-wrap gap-3'
|
|
@@ -2344,7 +2457,7 @@ const RadioGroup = (props) => {
|
|
|
2344
2457
|
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsx(Radio, { name: name, id: `${name}-${item.value}`, value: item.value, "aria-label": item.label, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange, containerClass: "pt-0.5" }), jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2", children: [item.icon && (jsx("span", { className: "shrink-0 text-gray-500 dark:text-gray-400", children: item.icon })), jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: item.label })] }), item.description && (jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400 font-normal leading-relaxed", children: item.description }))] })] }, item.value));
|
|
2345
2458
|
}
|
|
2346
2459
|
return (jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, description: item.description, value: item.value, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange }, item.value));
|
|
2347
|
-
}) }), showErrorMessage && error && (jsx("div", { className: "mt-1.5", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
2460
|
+
}) }), showErrorMessage && error && (jsx("div", { className: "mt-1.5", children: jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2348
2461
|
};
|
|
2349
2462
|
RadioGroup.displayName = 'RadioGroup';
|
|
2350
2463
|
|
|
@@ -2456,34 +2569,6 @@ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', d
|
|
|
2456
2569
|
};
|
|
2457
2570
|
RadioSwitch.displayName = 'RadioSwitch';
|
|
2458
2571
|
|
|
2459
|
-
/**
|
|
2460
|
-
* Checks if a value is an array.
|
|
2461
|
-
* Equivalent to lodash's _.isArray.
|
|
2462
|
-
*/
|
|
2463
|
-
function isArray(value) {
|
|
2464
|
-
return Array.isArray(value);
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
/**
|
|
2468
|
-
* Checks if a value is empty.
|
|
2469
|
-
* Works for arrays, objects, strings, Maps, and Sets.
|
|
2470
|
-
* Equivalent to lodash's _.isEmpty.
|
|
2471
|
-
*/
|
|
2472
|
-
function isEmpty(value) {
|
|
2473
|
-
if (value == null)
|
|
2474
|
-
return true; // null or undefined
|
|
2475
|
-
if (typeof value === 'string' || isArray(value)) {
|
|
2476
|
-
return value.length === 0;
|
|
2477
|
-
}
|
|
2478
|
-
if (value instanceof Map || value instanceof Set) {
|
|
2479
|
-
return value.size === 0;
|
|
2480
|
-
}
|
|
2481
|
-
if (typeof value === 'object') {
|
|
2482
|
-
return Object.keys(value).length === 0;
|
|
2483
|
-
}
|
|
2484
|
-
return false; // for numbers, booleans, functions, etc.
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
2572
|
const sizeClasses$3 = {
|
|
2488
2573
|
sm: {
|
|
2489
2574
|
track: 'h-1.5',
|
|
@@ -3466,7 +3551,7 @@ const parseNumericValue = (val) => {
|
|
|
3466
3551
|
const cleaned = String(val).replace(/[^0-9.-]/g, '');
|
|
3467
3552
|
return cleaned !== '' ? Number(cleaned) : NaN;
|
|
3468
3553
|
};
|
|
3469
|
-
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 = '', }) {
|
|
3554
|
+
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 = '', }) {
|
|
3470
3555
|
// Internal Search State
|
|
3471
3556
|
const [internalSearchQuery, setInternalSearchQuery] = useState('');
|
|
3472
3557
|
const searchQuery = externalSearchQuery !== undefined ? externalSearchQuery : internalSearchQuery;
|
|
@@ -3714,10 +3799,12 @@ function DataList({ items, columns, renderItem, searchable = false, searchQuery:
|
|
|
3714
3799
|
return (jsx(SwipeableRow, { leftActions: actions.leftActions, rightActions: actions.rightActions, children: contentNode }, key));
|
|
3715
3800
|
}
|
|
3716
3801
|
return jsx("div", { children: contentNode }, key);
|
|
3717
|
-
})) }), pagination && pagination.total > 0 && (jsx("div", { className:
|
|
3802
|
+
})) }), pagination && pagination.total > 0 && (jsx("div", { className: buildClassName('pt-2 flex justify-center w-full', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsx(Pagination, { ...pagination, ...paginationProps, onChange: (newPag) => setPagination === null || setPagination === void 0 ? void 0 : setPagination(newPag) })) }))] }));
|
|
3718
3803
|
}
|
|
3719
3804
|
DataList.displayName = 'DataList';
|
|
3720
3805
|
|
|
3806
|
+
const NOOP = () => { };
|
|
3807
|
+
const EMPTY_ARRAY = [];
|
|
3721
3808
|
const sizeClasses$1 = {
|
|
3722
3809
|
sm: {
|
|
3723
3810
|
input: 'px-2.5 py-1.5 text-xs leading-5 min-h-[32px]',
|
|
@@ -3739,7 +3826,7 @@ const sizeClasses$1 = {
|
|
|
3739
3826
|
},
|
|
3740
3827
|
};
|
|
3741
3828
|
const SelectBox = forwardRef((props, forwardedRef) => {
|
|
3742
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options =
|
|
3829
|
+
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;
|
|
3743
3830
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3744
3831
|
return jsx(MobilePicker, { ...props });
|
|
3745
3832
|
}
|
|
@@ -3757,7 +3844,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3757
3844
|
const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
|
|
3758
3845
|
const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
|
|
3759
3846
|
const baseInputClass = useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
|
|
3760
|
-
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');
|
|
3847
|
+
const chevronIconClass = 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]);
|
|
3761
3848
|
const inputRef = useRef(null);
|
|
3762
3849
|
const containerRef = useRef(null);
|
|
3763
3850
|
useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
|
|
@@ -3833,7 +3920,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3833
3920
|
maxSelection,
|
|
3834
3921
|
onChange,
|
|
3835
3922
|
]);
|
|
3836
|
-
const removeSelection = (idx) => {
|
|
3923
|
+
const removeSelection = useCallback((idx) => {
|
|
3837
3924
|
var _a;
|
|
3838
3925
|
if (!isArray(selected))
|
|
3839
3926
|
return;
|
|
@@ -3841,7 +3928,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3841
3928
|
next.splice(idx, 1);
|
|
3842
3929
|
onChange(next);
|
|
3843
3930
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
3844
|
-
};
|
|
3931
|
+
}, [selected, onChange]);
|
|
3845
3932
|
const handleOnChangeInternal = useCallback((value) => {
|
|
3846
3933
|
if (value === null) {
|
|
3847
3934
|
onChange(multiple ? [] : null);
|
|
@@ -3885,9 +3972,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3885
3972
|
}
|
|
3886
3973
|
}, [onAdd, handleOnChangeInternal]);
|
|
3887
3974
|
const onSearchRef = useRef(props.onSearch);
|
|
3888
|
-
|
|
3889
|
-
onSearchRef.current = props.onSearch;
|
|
3890
|
-
}, [props.onSearch]);
|
|
3975
|
+
onSearchRef.current = props.onSearch;
|
|
3891
3976
|
const handleSearch = useCallback(async (searchString) => {
|
|
3892
3977
|
var _a;
|
|
3893
3978
|
try {
|
|
@@ -3919,19 +4004,27 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3919
4004
|
return selected !== null && selected !== void 0 ? selected : null;
|
|
3920
4005
|
}
|
|
3921
4006
|
}, [multiple, selected]);
|
|
4007
|
+
const selectedValueSet = useMemo(() => {
|
|
4008
|
+
if (!normalizedSelected)
|
|
4009
|
+
return new Set();
|
|
4010
|
+
if (Array.isArray(normalizedSelected)) {
|
|
4011
|
+
return new Set(normalizedSelected.map((s) => getValue(s)));
|
|
4012
|
+
}
|
|
4013
|
+
return new Set([getValue(normalizedSelected)]);
|
|
4014
|
+
}, [normalizedSelected, getValue]);
|
|
3922
4015
|
const isAllSelected = useMemo(() => {
|
|
3923
4016
|
if (!multiple || !Array.isArray(normalizedSelected) || !allOptions.length)
|
|
3924
4017
|
return false;
|
|
3925
4018
|
return allOptions.length === normalizedSelected.length;
|
|
3926
4019
|
}, [multiple, normalizedSelected, allOptions]);
|
|
3927
|
-
const handleToggleSelectAll = () => {
|
|
4020
|
+
const handleToggleSelectAll = useCallback(() => {
|
|
3928
4021
|
if (isAllSelected) {
|
|
3929
4022
|
onChange([]);
|
|
3930
4023
|
}
|
|
3931
4024
|
else {
|
|
3932
4025
|
onChange(allOptions);
|
|
3933
4026
|
}
|
|
3934
|
-
};
|
|
4027
|
+
}, [isAllSelected, onChange, allOptions]);
|
|
3935
4028
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
3936
4029
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
3937
4030
|
query.trim() &&
|
|
@@ -3940,6 +4033,11 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3940
4033
|
const hasGroupOptions = Boolean(filteredGroups && filteredGroups.length > 0);
|
|
3941
4034
|
const hasFlatOptions = filteredOptions.length > 0;
|
|
3942
4035
|
const hasAnyOptionsToDisplay = hasFlatOptions || hasGroupOptions || hasFreeTextOption || hasAddOption;
|
|
4036
|
+
const renderOptionItem = (option) => {
|
|
4037
|
+
const optVal = getValue(option);
|
|
4038
|
+
const isOptSelected = selectedValueSet.has(optVal);
|
|
4039
|
+
return (jsx(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))) : (jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
4040
|
+
};
|
|
3943
4041
|
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs(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 ? (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 && (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) &&
|
|
3944
4042
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
3945
4043
|
normalizedSelected.map((option, idx) => (jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(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) => {
|
|
@@ -3983,23 +4081,9 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
3983
4081
|
}, 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) && (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: jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxs(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 && (jsxs("div", { className: "pb-1 mb-1 border-b border-[var(--ui-border-muted)] px-3 pt-1 flex items-center justify-between", children: [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' }), jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(normalizedSelected) ? normalizedSelected.length : 0, " of", ' ', allOptions.length] })] })), optionsLoading ? (jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [allowFreeText &&
|
|
3984
4082
|
query.trim() &&
|
|
3985
4083
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsx(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: jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
|
|
3986
|
-
? filteredGroups.map((grp, gIdx) => (jsxs("div", { children: [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(
|
|
3987
|
-
const optVal = getValue(option);
|
|
3988
|
-
const isOptSelected = multiple
|
|
3989
|
-
? isArray(normalizedSelected) &&
|
|
3990
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
3991
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
3992
|
-
return (jsx(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))) : (jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
3993
|
-
})] }, gIdx)))
|
|
4084
|
+
? filteredGroups.map((grp, gIdx) => (jsxs("div", { children: [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)))
|
|
3994
4085
|
: /* Render Standard Flat Options */
|
|
3995
|
-
filteredOptions.map((
|
|
3996
|
-
const optVal = getValue(option);
|
|
3997
|
-
const isOptSelected = multiple
|
|
3998
|
-
? isArray(normalizedSelected) &&
|
|
3999
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
4000
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
4001
|
-
return (jsx(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))) : (jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
4002
|
-
}), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsx(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: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsx("div", { className: "mt-1", children: jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
|
|
4086
|
+
filteredOptions.map(renderOptionItem), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsx(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: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsx("div", { className: "mt-1", children: jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
|
|
4003
4087
|
});
|
|
4004
4088
|
SelectBox.displayName = 'SelectBox';
|
|
4005
4089
|
|
|
@@ -4353,7 +4437,7 @@ const resizeClasses = {
|
|
|
4353
4437
|
both: 'resize',
|
|
4354
4438
|
};
|
|
4355
4439
|
const Textarea = forwardRef((props, ref) => {
|
|
4356
|
-
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;
|
|
4440
|
+
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;
|
|
4357
4441
|
const innerRef = useRef(null);
|
|
4358
4442
|
useImperativeHandle(ref, () => innerRef.current);
|
|
4359
4443
|
const [uncontrolledValue, setUncontrolledValue] = useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
@@ -4367,19 +4451,22 @@ const Textarea = forwardRef((props, ref) => {
|
|
|
4367
4451
|
innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
|
|
4368
4452
|
}
|
|
4369
4453
|
}, [autoSize, currentVal]);
|
|
4370
|
-
const handleTextareaChange = (e) => {
|
|
4454
|
+
const handleTextareaChange = useCallback((e) => {
|
|
4371
4455
|
if (!isControlled) {
|
|
4372
4456
|
setUncontrolledValue(e.target.value);
|
|
4373
4457
|
}
|
|
4374
4458
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
4375
|
-
};
|
|
4459
|
+
}, [isControlled, onChange]);
|
|
4376
4460
|
const hasError = !isEmpty(error);
|
|
4377
4461
|
const hasRightGroup = rightGroup != null && rightGroup !== undefined;
|
|
4378
4462
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4379
4463
|
const currentSize = sizeClasses[size] || sizeClasses.md;
|
|
4380
|
-
|
|
4464
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
4465
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
4466
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
4467
|
+
return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-2.5 left-3 flex items-center justify-center text-gray-400 dark:text-gray-500", children: leftGroup })), 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
|
|
4381
4468
|
? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
|
|
4382
|
-
: '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 && (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 ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
4469
|
+
: '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 && (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 ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
4383
4470
|
? 'text-red-500 font-semibold'
|
|
4384
4471
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
4385
4472
|
});
|