@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.
@@ -122,8 +122,8 @@ const loaderSizeMap = {
122
122
  md: 'size-8',
123
123
  lg: 'size-10',
124
124
  };
125
- const Loader = ({ className = 'text-blue-600 dark:text-white', size = 'md', ...props }) => {
126
- return (jsx("svg", { className: buildClassName(loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
125
+ const Loader = ({ className = '', size = 'md', ...props }) => {
126
+ return (jsx("svg", { className: buildClassName('text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
127
127
  };
128
128
 
129
129
  const TextContent = forwardRef((props, ref) => {
@@ -156,6 +156,23 @@ const THEMED_BTN_CLASS = {
156
156
  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',
157
157
  },
158
158
  };
159
+ const LOADER_TEXT_CLASS = {
160
+ primary: {
161
+ default: 'text-white',
162
+ outlined: 'text-[var(--ui-primary-text)]',
163
+ plain: 'text-[var(--ui-primary-text)]',
164
+ },
165
+ danger: {
166
+ default: 'text-white',
167
+ outlined: 'text-[var(--ui-danger-text)]',
168
+ plain: 'text-[var(--ui-danger-text)]',
169
+ },
170
+ secondary: {
171
+ default: 'text-gray-700 dark:text-white',
172
+ outlined: 'text-gray-700 dark:text-gray-200',
173
+ plain: 'text-gray-700 dark:text-gray-200',
174
+ },
175
+ };
159
176
  /**
160
177
  * Generate size classes.
161
178
  */
@@ -205,7 +222,7 @@ const Button = forwardRef(({ as, type, ...props }, ref) => {
205
222
  if (loading && iconOnly) {
206
223
  return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
207
224
  }
208
- return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }), loadingText && jsx(TextContent, { children: loadingText })] })) : (children) }));
225
+ return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }), loadingText && (jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText }))] })) : (children) }));
209
226
  });
210
227
  Button.displayName = 'Button';
211
228
 
@@ -785,11 +802,11 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
785
802
  }
786
803
  DataTable.displayName = 'DataTable';
787
804
 
788
- const Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
789
- const SubTitle = ({ children, className }) => (jsx("h2", { className: buildClassName('text-2xl font-semibold', className), children: children }));
790
- const SubTitle2 = ({ children, className }) => (jsx("h3", { className: buildClassName('text-xl font-semibold', className), children: children }));
791
- const SubTitle3 = ({ children, className }) => (jsx("h4", { className: buildClassName('text-lg font-medium', className), children: children }));
792
- const SubTitle4 = ({ children, className }) => (jsx("h5", { className: buildClassName('text-base font-medium', className), children: children }));
805
+ const Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold text-gray-900 dark:text-white', className), children: children }));
806
+ const SubTitle = ({ children, className }) => (jsx("h2", { className: buildClassName('text-2xl font-semibold text-gray-900 dark:text-white', className), children: children }));
807
+ const SubTitle2 = ({ children, className }) => (jsx("h3", { className: buildClassName('text-xl font-semibold text-gray-900 dark:text-white', className), children: children }));
808
+ const SubTitle3 = ({ children, className }) => (jsx("h4", { className: buildClassName('text-lg font-medium text-gray-900 dark:text-white', className), children: children }));
809
+ const SubTitle4 = ({ children, className }) => (jsx("h5", { className: buildClassName('text-base font-medium text-gray-900 dark:text-white', className), children: children }));
793
810
  const HeadingText = {
794
811
  Title,
795
812
  SubTitle,
@@ -1185,7 +1202,7 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
1185
1202
 
1186
1203
  const constructInputClass = (disabled) => buildInputClass('', { disabled });
1187
1204
  const SelectBox = forwardRef((props, forwardedRef) => {
1188
- 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;
1205
+ 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;
1189
1206
  const hasError = Boolean(error);
1190
1207
  const [query, setQuery] = useState('');
1191
1208
  const debouncedQuery = useDebounce(query, 500);
@@ -1280,11 +1297,18 @@ const SelectBox = forwardRef((props, forwardedRef) => {
1280
1297
  console.error('onAdd handler error', err);
1281
1298
  }
1282
1299
  }, [onAdd, handleOnChangeInternal]);
1300
+ // Kept in a ref so `handleSearch` stays referentially stable even when the
1301
+ // caller passes a non-memoized `onSearch` — otherwise the effect below would
1302
+ // re-fire (and re-search) on every render instead of only on query changes.
1303
+ const onSearchRef = useRef(props.onSearch);
1304
+ useEffect(() => {
1305
+ onSearchRef.current = props.onSearch;
1306
+ }, [props.onSearch]);
1283
1307
  const handleSearch = useCallback(async (searchString) => {
1284
1308
  var _a;
1285
1309
  try {
1286
1310
  setOptionsLoading(true);
1287
- await ((_a = props.onSearch) === null || _a === void 0 ? void 0 : _a.call(props, searchString));
1311
+ await ((_a = onSearchRef.current) === null || _a === void 0 ? void 0 : _a.call(onSearchRef, searchString));
1288
1312
  }
1289
1313
  catch (err) {
1290
1314
  console.error('Error loading options:', err);
@@ -1292,7 +1316,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
1292
1316
  finally {
1293
1317
  setOptionsLoading(false);
1294
1318
  }
1295
- }, [props.onSearch]);
1319
+ }, []);
1296
1320
  useEffect(() => {
1297
1321
  if (async && debouncedQuery)
1298
1322
  handleSearch(debouncedQuery);
@@ -1305,7 +1329,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
1305
1329
  removeSelection(selected.length - 1);
1306
1330
  }
1307
1331
  }
1308
- }, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [jsx(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) && (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" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsx(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 ? (jsx("div", { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx("div", { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [filteredOptions.map((option) => (jsx(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: jsxs("div", { className: "flex items-center justify-between gap-2 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" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (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 ", jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsx(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: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1332
+ }, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [jsx(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) && (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", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsx(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 ? (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: [filteredOptions.map((option) => (jsx(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: jsxs("div", { className: "flex items-center justify-between gap-2 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" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (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 ", jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsx(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: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1309
1333
  });
1310
1334
 
1311
1335
  const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {