@pk-design/react-tailwind 1.4.2 → 1.4.4

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.
@@ -142,8 +142,8 @@ const loaderSizeMap = {
142
142
  md: 'size-8',
143
143
  lg: 'size-10',
144
144
  };
145
- const Loader = ({ className = 'text-blue-600 dark:text-white', size = 'md', ...props }) => {
146
- return (jsxRuntime.jsx("svg", { className: buildClassName(loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsxRuntime.jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
145
+ const Loader = ({ className = '', size = 'md', ...props }) => {
146
+ return (jsxRuntime.jsx("svg", { className: buildClassName('text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsxRuntime.jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
147
147
  };
148
148
 
149
149
  const TextContent = React.forwardRef((props, ref) => {
@@ -176,6 +176,23 @@ const THEMED_BTN_CLASS = {
176
176
  plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus:outline-[var(--ui-focus-ring)] disabled:text-gray-400',
177
177
  },
178
178
  };
179
+ const LOADER_TEXT_CLASS = {
180
+ primary: {
181
+ default: 'text-white',
182
+ outlined: 'text-[var(--ui-primary-text)]',
183
+ plain: 'text-[var(--ui-primary-text)]',
184
+ },
185
+ danger: {
186
+ default: 'text-white',
187
+ outlined: 'text-[var(--ui-danger-text)]',
188
+ plain: 'text-[var(--ui-danger-text)]',
189
+ },
190
+ secondary: {
191
+ default: 'text-gray-700 dark:text-white',
192
+ outlined: 'text-gray-700 dark:text-gray-200',
193
+ plain: 'text-gray-700 dark:text-gray-200',
194
+ },
195
+ };
179
196
  /**
180
197
  * Generate size classes.
181
198
  */
@@ -225,7 +242,7 @@ const Button = React.forwardRef(({ as, type, ...props }, ref) => {
225
242
  if (loading && iconOnly) {
226
243
  return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
227
244
  }
228
- return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }), loadingText && jsxRuntime.jsx(TextContent, { children: loadingText })] })) : (children) }));
245
+ return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }), loadingText && (jsxRuntime.jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText }))] })) : (children) }));
229
246
  });
230
247
  Button.displayName = 'Button';
231
248
 
@@ -805,11 +822,11 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
805
822
  }
806
823
  DataTable.displayName = 'DataTable';
807
824
 
808
- const Title = ({ children, className }) => (jsxRuntime.jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
809
- const SubTitle = ({ children, className }) => (jsxRuntime.jsx("h2", { className: buildClassName('text-2xl font-semibold', className), children: children }));
810
- const SubTitle2 = ({ children, className }) => (jsxRuntime.jsx("h3", { className: buildClassName('text-xl font-semibold', className), children: children }));
811
- const SubTitle3 = ({ children, className }) => (jsxRuntime.jsx("h4", { className: buildClassName('text-lg font-medium', className), children: children }));
812
- const SubTitle4 = ({ children, className }) => (jsxRuntime.jsx("h5", { className: buildClassName('text-base font-medium', className), children: children }));
825
+ const Title = ({ children, className }) => (jsxRuntime.jsx("h1", { className: buildClassName('text-3xl font-bold text-gray-900 dark:text-white', className), children: children }));
826
+ const SubTitle = ({ children, className }) => (jsxRuntime.jsx("h2", { className: buildClassName('text-2xl font-semibold text-gray-900 dark:text-white', className), children: children }));
827
+ const SubTitle2 = ({ children, className }) => (jsxRuntime.jsx("h3", { className: buildClassName('text-xl font-semibold text-gray-900 dark:text-white', className), children: children }));
828
+ const SubTitle3 = ({ children, className }) => (jsxRuntime.jsx("h4", { className: buildClassName('text-lg font-medium text-gray-900 dark:text-white', className), children: children }));
829
+ const SubTitle4 = ({ children, className }) => (jsxRuntime.jsx("h5", { className: buildClassName('text-base font-medium text-gray-900 dark:text-white', className), children: children }));
813
830
  const HeadingText = {
814
831
  Title,
815
832
  SubTitle,
@@ -1205,7 +1222,7 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
1205
1222
 
1206
1223
  const constructInputClass = (disabled) => buildInputClass('', { disabled });
1207
1224
  const SelectBox = React.forwardRef((props, forwardedRef) => {
1208
- const { labelKey = 'label', valueKey = 'value', name, id = name, options, selected, label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, immediate = true, showErrorMessage = true, error = null, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, } = props;
1225
+ const { labelKey = 'label', valueKey = 'value', name, id = name, options, selected, label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, immediate = true, showErrorMessage = true, error = null, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, modalDropdown = false, } = props;
1209
1226
  const hasError = Boolean(error);
1210
1227
  const [query, setQuery] = React.useState('');
1211
1228
  const debouncedQuery = useDebounce(query, 500);
@@ -1300,11 +1317,18 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1300
1317
  console.error('onAdd handler error', err);
1301
1318
  }
1302
1319
  }, [onAdd, handleOnChangeInternal]);
1320
+ // Kept in a ref so `handleSearch` stays referentially stable even when the
1321
+ // caller passes a non-memoized `onSearch` — otherwise the effect below would
1322
+ // re-fire (and re-search) on every render instead of only on query changes.
1323
+ const onSearchRef = React.useRef(props.onSearch);
1324
+ React.useEffect(() => {
1325
+ onSearchRef.current = props.onSearch;
1326
+ }, [props.onSearch]);
1303
1327
  const handleSearch = React.useCallback(async (searchString) => {
1304
1328
  var _a;
1305
1329
  try {
1306
1330
  setOptionsLoading(true);
1307
- await ((_a = props.onSearch) === null || _a === void 0 ? void 0 : _a.call(props, searchString));
1331
+ await ((_a = onSearchRef.current) === null || _a === void 0 ? void 0 : _a.call(onSearchRef, searchString));
1308
1332
  }
1309
1333
  catch (err) {
1310
1334
  console.error('Error loading options:', err);
@@ -1312,7 +1336,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1312
1336
  finally {
1313
1337
  setOptionsLoading(false);
1314
1338
  }
1315
- }, [props.onSearch]);
1339
+ }, []);
1316
1340
  React.useEffect(() => {
1317
1341
  if (async && debouncedQuery)
1318
1342
  handleSearch(debouncedQuery);
@@ -1325,7 +1349,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1325
1349
  removeSelection(selected.length - 1);
1326
1350
  }
1327
1351
  }
1328
- }, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [jsxRuntime.jsx(react.ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsxRuntime.jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsx(react.ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsxRuntime.jsx("div", { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx("div", { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredOptions.map((option) => (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: ["No results found for ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1352
+ }, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [jsxRuntime.jsx(react.ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsxRuntime.jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsx(react.ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", modal: modalDropdown, className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsxRuntime.jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredOptions.map((option) => (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: ["No results found for ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1329
1353
  });
1330
1354
 
1331
1355
  const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {