@sqrzro/ui 4.0.0-alpha.4 → 4.0.0-alpha.6

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.
Files changed (29) hide show
  1. package/dist/components/collections/Collection/index.d.ts +4 -2
  2. package/dist/components/collections/List/index.d.ts +2 -3
  3. package/dist/components/collections/Table/index.d.ts +2 -2
  4. package/dist/filters/interfaces.d.ts +2 -2
  5. package/dist/filters/utility/parse-filters.d.ts +1 -1
  6. package/dist/filters/utility/parse-filters.js +3 -2
  7. package/dist/filters/utility/transform-boolean.d.ts +1 -1
  8. package/dist/filters/utility/transform-date.d.ts +1 -1
  9. package/dist/filters/utility/transform-multi.d.ts +1 -1
  10. package/dist/forms/components/Autocomplete/index.d.ts +11 -0
  11. package/dist/forms/components/Autocomplete/index.js +37 -0
  12. package/dist/forms/components/Dropdown/index.js +1 -1
  13. package/dist/forms/components/DropdownList/index.js +3 -1
  14. package/dist/forms/components/FormFields/index.d.ts +9 -0
  15. package/dist/forms/components/FormFields/index.js +18 -0
  16. package/dist/forms/components/MoneyInput/index.d.ts +5 -0
  17. package/dist/forms/components/MoneyInput/index.js +22 -0
  18. package/dist/forms/components/NumberInput/index.d.ts +7 -0
  19. package/dist/forms/components/NumberInput/index.js +27 -0
  20. package/dist/forms/components/StaticTextInput/index.d.ts +1 -2
  21. package/dist/forms/components/StaticTextInput/index.js +2 -2
  22. package/dist/forms/components/TextInput/index.d.ts +1 -1
  23. package/dist/forms/components/TextInput/index.js +2 -2
  24. package/dist/forms/hooks/useAutocomplete.d.ts +8 -0
  25. package/dist/forms/hooks/useAutocomplete.js +31 -0
  26. package/dist/forms/index.d.ts +9 -2
  27. package/dist/forms/index.js +5 -1
  28. package/dist/forms/interfaces.d.ts +1 -0
  29. package/package.json +1 -1
@@ -2,14 +2,16 @@ import type { FilterMap } from '../../../filters/interfaces';
2
2
  import type { NextPageProps } from '../../../utility/interfaces';
3
3
  import type { EmptyMessageProps } from '../EmptyMessage';
4
4
  import type { ListFunctionConfig, Paginated } from '../interfaces';
5
- export interface CollectionProps<Item, Transformed, Filters = null> {
5
+ export interface CollectionComponentProps<Item, Transformed, Filters = null> {
6
6
  readonly data?: Item[] | Paginated<Item>;
7
7
  readonly emptyMessageProps?: EmptyMessageProps;
8
8
  readonly filterMap?: FilterMap<Filters>;
9
9
  readonly fn?: (config: ListFunctionConfig<Filters>) => Promise<Item[]> | Promise<Paginated<Item>>;
10
10
  readonly pageProps?: NextPageProps;
11
- readonly render: (data: Transformed[]) => React.ReactElement;
12
11
  readonly transformer: (item: Item) => Transformed;
13
12
  }
13
+ export interface CollectionProps<Item, Transformed, Filters = null> extends CollectionComponentProps<Item, Transformed, Filters> {
14
+ readonly render: (data: Transformed[]) => React.ReactElement;
15
+ }
14
16
  declare function Collection<Item extends object, Transformed, Filters>({ filterMap, ...props }: CollectionProps<Item, Transformed, Filters>): React.ReactElement;
15
17
  export default Collection;
@@ -1,6 +1,5 @@
1
- import { CollectionProps } from '../Collection';
1
+ import { CollectionComponentProps } from '../Collection';
2
2
  import type { ListItemObject } from '../interfaces';
3
- export interface ListProps<Item, Filters = null, Data extends object | null = null> extends Omit<CollectionProps<Item, ListItemObject<Data>, Filters>, 'render'> {
4
- }
3
+ export type ListProps<Item, Filters = null, Data extends object | null = null> = CollectionComponentProps<Item, ListItemObject<Data>, Filters>;
5
4
  declare function List<Item extends object, Filters, Data extends object | null = null>(props: ListProps<Item, Filters, Data>): React.ReactElement;
6
5
  export default List;
@@ -1,7 +1,7 @@
1
- import { CollectionProps } from '../Collection';
1
+ import { CollectionComponentProps } from '../Collection';
2
2
  import type { TableColumnObject, TableItemObject } from '../interfaces';
3
3
  export type { TableClassNames } from '../TableClientComponent';
4
- export interface TableProps<Item, Filters = null> extends CollectionProps<Item, TableItemObject, Filters> {
4
+ export interface TableProps<Item, Filters = null> extends CollectionComponentProps<Item, TableItemObject, Filters> {
5
5
  readonly columns: TableColumnObject[];
6
6
  readonly isSelectable?: boolean;
7
7
  }
@@ -21,8 +21,8 @@ export type ClientFilterMap<T> = {
21
21
  [K in keyof T]?: ClientFilterObject<FilterType>;
22
22
  };
23
23
  export interface FilterObject<T, F extends FilterType> extends ClientFilterObject<F> {
24
- transformer: (value: string) => T;
24
+ transformer: (value: string | null) => T | null;
25
25
  }
26
26
  export type FilterMap<T> = {
27
- [K in keyof T]?: FilterObject<T[K], FilterType>;
27
+ [K in keyof T]?: FilterObject<T[K] | null, FilterType>;
28
28
  };
@@ -1,3 +1,3 @@
1
- import type { FilterMap } from "../interfaces";
1
+ import type { FilterMap } from '../interfaces';
2
2
  declare function parseFilters<T>(searchParams: Record<string, string> | null, map?: FilterMap<T> | null): Partial<T> | null;
3
3
  export default parseFilters;
@@ -6,8 +6,9 @@ function parseFilters(searchParams, map) {
6
6
  for (const key in map) {
7
7
  if (Object.hasOwn(map, key) && typeof map[key] !== 'undefined') {
8
8
  const value = searchParams[key];
9
- if (value) {
10
- out[key] = map[key].transformer(value);
9
+ const transformed = map[key].transformer(value);
10
+ if (transformed) {
11
+ out[key] = transformed;
11
12
  }
12
13
  }
13
14
  }
@@ -1,2 +1,2 @@
1
1
  export declare function transformFromBoolean(value?: boolean): string;
2
- export declare function transformToBoolean(value?: string): boolean;
2
+ export declare function transformToBoolean(value?: string | null): boolean;
@@ -1,2 +1,2 @@
1
1
  export declare function transformFromDate(value?: [Date, Date] | null): string;
2
- export declare function transformToDate(value?: string): [Date, Date] | null;
2
+ export declare function transformToDate(value?: string | null): [Date, Date] | null;
@@ -1,2 +1,2 @@
1
1
  export declare function transformFromMulti(value?: string[] | null): string;
2
- export declare function transformToMulti(value?: string): string[] | null;
2
+ export declare function transformToMulti(value?: string | null): string[] | null;
@@ -0,0 +1,11 @@
1
+ import { ClassNameProps } from '../../../styles/interfaces';
2
+ import type { DropdownObject, InputProps } from '../../interfaces';
3
+ import { DropdownClassNames, DropdownComponentProps } from '../Dropdown';
4
+ export interface AutocompleteComponentProps<T> extends DropdownComponentProps<T> {
5
+ isLoading?: boolean;
6
+ onSearch?: (search: string) => void;
7
+ selected?: DropdownObject<T> | null;
8
+ }
9
+ export type AutocompleteProps<T> = ClassNameProps<DropdownClassNames> & InputProps<T | null> & AutocompleteComponentProps<T>;
10
+ declare function Autocomplete<T>({ hasError, isDisabled, isLoading, isOptional, name, onChange, onSearch, options, selected, placeholder, value, }: Readonly<AutocompleteProps<T>>): React.ReactElement;
11
+ export default Autocomplete;
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Fragment, useRef, useState } from 'react';
4
+ import Popover from '../../../components/utility/Popover';
5
+ import useClickOutside from '../../../hooks/useClickOutside';
6
+ import DropdownList from '../DropdownList';
7
+ import StaticTextInput from '../StaticTextInput';
8
+ import TextInput from '../TextInput';
9
+ function Autocomplete({ hasError, isDisabled, isLoading, isOptional, name, onChange, onSearch, options, selected, placeholder, value, }) {
10
+ const inputRef = useRef(null);
11
+ const [search, setSearch] = useState('');
12
+ const [isOpen, setIsOpen, ref] = useClickOutside();
13
+ function handleChange(event) {
14
+ if (onChange) {
15
+ onChange(event);
16
+ }
17
+ setIsOpen(false);
18
+ setSearch('');
19
+ }
20
+ function handleSearch(event) {
21
+ setSearch(event.target.value ?? '');
22
+ setIsOpen(true);
23
+ if (onSearch) {
24
+ onSearch(event.target.value ?? '');
25
+ }
26
+ }
27
+ function handleReset() {
28
+ if (onChange) {
29
+ onChange({ target: { name, value: null } });
30
+ }
31
+ setTimeout(() => {
32
+ inputRef.current?.focus();
33
+ }, 10);
34
+ }
35
+ return (_jsx("div", { ref: ref, className: "relative", children: selected ? (_jsx(StaticTextInput, { details: selected.details, hasError: hasError, isDisabled: isDisabled, isOptional: isOptional, label: selected.label, name: name, onClick: handleReset, placeholder: placeholder || 'Select...', value: value ? String(value) : '' })) : (_jsxs(Fragment, { children: [_jsx(TextInput, { ref: inputRef, hasError: hasError, isDisabled: isDisabled, name: `${name}_search`, onChange: handleSearch, onFocus: () => setIsOpen(Boolean(options?.length)), placeholder: "Start typing...", value: search }), _jsx(Popover, { align: "center", isOpen: isOpen, isScrollable: true, children: options?.length ? (_jsx(DropdownList, { name: name, onChange: handleChange, options: options ?? [], value: value })) : isLoading ? (_jsx("div", { children: "Loading..." })) : (_jsx("div", { children: "No results" })) })] })) }));
36
+ }
37
+ export default Autocomplete;
@@ -36,6 +36,6 @@ function Dropdown({ classNames, classNameProps, hasError, isDisabled, isOptional
36
36
  return (_jsxs("div", { ref: node, className: tw('relative', componentClassNames?.root?.default, hasError ? componentClassNames?.root?.error : null, isOpen && !isDisabled ? componentClassNames?.root?.focused : null), children: [_jsx(StaticTextInput, { classNames: {
37
37
  clear: componentClassNames?.clear,
38
38
  icon: componentClassNames?.icon,
39
- }, details: renderDetails(options, value), hasError: hasError, isDisabled: isDisabled || !options?.length, isOpen: isOpen, isOptional: isOptional, label: renderLabel(options, value), name: name, onClear: handleClear, onClick: toggleIsOpen, placeholder: placeholder || 'Select...', value: value ? String(value) : '' }), _jsx(Popover, { align: "center", isOpen: isOpen ? !isDisabled : false, isScrollable: true, children: _jsx(DropdownList, { name: name, onChange: handleChange, options: options, value: value }) })] }));
39
+ }, details: renderDetails(options, value), hasError: hasError, isDisabled: isDisabled || !options?.length, isOptional: isOptional, label: renderLabel(options, value), name: name, onClear: handleClear, onClick: toggleIsOpen, placeholder: placeholder || 'Select...', value: value ? String(value) : '' }), _jsx(Popover, { align: "center", isOpen: isOpen ? !isDisabled : false, isScrollable: true, children: _jsx(DropdownList, { name: name, onChange: handleChange, options: options, value: value }) })] }));
40
40
  }
41
41
  export default Dropdown;
@@ -15,6 +15,8 @@ function DropdownList({ classNames, classNameProps, name, onChange, options, val
15
15
  });
16
16
  };
17
17
  }
18
- return (_jsx("ul", { className: componentClassNames?.list, role: "listbox", children: options.map((item, index) => (_jsx("li", { "aria-selected": isSelected(item.value, value), className: tw('relative', item.isDisabled ? 'pointer-events-none opacity-30' : null, componentClassNames?.item?.default, isSelected(item.value, value) ? componentClassNames?.item?.selected : null), role: "option", children: _jsxs("button", { onClick: handleChange(item), tabIndex: -1, type: "button", children: [_jsx("span", { className: tw('whitespace-nowrap' /*componentClassNames?.label*/), children: item.label }), item.details ? (_jsx("small", { className: tw(componentClassNames?.details), children: item.details })) : null] }) }, index))) }));
18
+ return (_jsx("ul", { className: componentClassNames?.list, role: "listbox", children: options.map((item, index) => (_jsx("li", { "aria-selected": isSelected(item.value, value), role: "option", children: _jsxs("button", { className: tw('relative w-full text-left', item.isDisabled ? 'pointer-events-none opacity-30' : null, componentClassNames?.item?.default, isSelected(item.value, value)
19
+ ? componentClassNames?.item?.selected
20
+ : null), onClick: handleChange(item), tabIndex: -1, type: "button", children: [_jsx("span", { className: tw('whitespace-nowrap' /*componentClassNames?.label*/), children: item.label }), item.details ? (_jsx("small", { className: tw(componentClassNames?.details), children: item.details })) : null] }) }, index))) }));
19
21
  }
20
22
  export default DropdownList;
@@ -1,9 +1,18 @@
1
1
  import type { FormFieldComponentProps } from '../../interfaces';
2
+ import type { AutocompleteComponentProps } from '../Autocomplete';
2
3
  import type { DropdownComponentProps } from '../Dropdown';
4
+ import type { MoneyInputComponentProps } from '../MoneyInput';
5
+ import type { NumberInputComponentProps } from '../NumberInput';
3
6
  import type { PasswordInputComponentProps } from '../PasswordInput';
4
7
  import type { TextInputComponentProps } from '../TextInput';
8
+ export type AutocompleteFormFieldProps<T> = FormFieldComponentProps<T | null> & AutocompleteComponentProps<T>;
9
+ export declare function AutocompleteFormField<T>(props: Readonly<AutocompleteFormFieldProps<T>>): React.ReactElement;
5
10
  export type DropdownFormFieldProps<T> = FormFieldComponentProps<T | null> & DropdownComponentProps<T>;
6
11
  export declare function DropdownFormField<T>(props: Readonly<DropdownFormFieldProps<T>>): React.ReactElement;
12
+ export type MoneyFormFieldProps = FormFieldComponentProps<number> & MoneyInputComponentProps;
13
+ export declare function MoneyFormField(props: Readonly<MoneyFormFieldProps>): React.ReactElement;
14
+ export type NumberFormFieldProps = FormFieldComponentProps<number> & NumberInputComponentProps;
15
+ export declare function NumberFormField(props: Readonly<NumberFormFieldProps>): React.ReactElement;
7
16
  export type PasswordFormFieldProps = FormFieldComponentProps<string> & PasswordInputComponentProps;
8
17
  export declare function PasswordFormField(props: Readonly<PasswordFormFieldProps>): React.ReactElement;
9
18
  export type TextFormFieldProps = FormFieldComponentProps<string> & TextInputComponentProps;
@@ -1,15 +1,33 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback } from 'react';
3
3
  import extractInputProps from '../../utility/extract-input-props';
4
+ import Autocomplete from '../Autocomplete';
4
5
  import Dropdown from '../Dropdown';
5
6
  import FormField from '../FormField';
7
+ import MoneyInput from '../MoneyInput';
8
+ import NumberInput from '../NumberInput';
6
9
  import PasswordInput from '../PasswordInput';
7
10
  import TextInput from '../TextInput';
11
+ export function AutocompleteFormField(props) {
12
+ const { fieldProps, inputProps } = extractInputProps(props);
13
+ const renderInput = useCallback((renderProps) => (_jsx(Autocomplete, { ...renderProps, ...inputProps })), [inputProps]);
14
+ return _jsx(FormField, { ...fieldProps, render: renderInput });
15
+ }
8
16
  export function DropdownFormField(props) {
9
17
  const { fieldProps, inputProps } = extractInputProps(props);
10
18
  const renderInput = useCallback((renderProps) => (_jsx(Dropdown, { ...renderProps, ...inputProps })), [inputProps]);
11
19
  return _jsx(FormField, { ...fieldProps, render: renderInput });
12
20
  }
21
+ export function MoneyFormField(props) {
22
+ const { fieldProps, inputProps } = extractInputProps(props);
23
+ const renderInput = useCallback((renderProps) => (_jsx(MoneyInput, { ...renderProps, ...inputProps })), [inputProps]);
24
+ return _jsx(FormField, { ...fieldProps, render: renderInput });
25
+ }
26
+ export function NumberFormField(props) {
27
+ const { fieldProps, inputProps } = extractInputProps(props);
28
+ const renderInput = useCallback((renderProps) => (_jsx(NumberInput, { ...renderProps, ...inputProps })), [inputProps]);
29
+ return _jsx(FormField, { ...fieldProps, render: renderInput });
30
+ }
13
31
  export function PasswordFormField(props) {
14
32
  const { fieldProps, inputProps } = extractInputProps(props);
15
33
  const renderInput = useCallback((renderProps) => (_jsx(PasswordInput, { ...renderProps, ...inputProps })), [inputProps]);
@@ -0,0 +1,5 @@
1
+ import type { NumberInputComponentProps, NumberInputProps } from '../NumberInput';
2
+ export type MoneyInputComponentProps = NumberInputComponentProps;
3
+ export type MoneyInputProps = NumberInputProps;
4
+ declare function MoneyInput({ name, onChange, prefix, value, ...props }: Readonly<MoneyInputProps>): React.ReactElement;
5
+ export default MoneyInput;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import NumberInput from '../NumberInput';
3
+ const FACTOR = 100;
4
+ function setValue(value) {
5
+ if (typeof value === 'undefined') {
6
+ return value;
7
+ }
8
+ return Math.round(value * FACTOR);
9
+ }
10
+ function getValue(value) {
11
+ if (typeof value === 'undefined') {
12
+ return value;
13
+ }
14
+ return value / FACTOR;
15
+ }
16
+ function MoneyInput({ name, onChange, prefix = '£', value, ...props }) {
17
+ function handleChange(event) {
18
+ onChange?.({ target: { name, value: setValue(event.target.value) } });
19
+ }
20
+ return (_jsx(NumberInput, { prefix: prefix, ...props, name: name, onChange: handleChange, value: getValue(value) }));
21
+ }
22
+ export default MoneyInput;
@@ -0,0 +1,7 @@
1
+ import { ClassNameProps } from '../../../styles/interfaces';
2
+ import type { InputProps } from '../../interfaces';
3
+ import type { TextInputClassNames, TextInputComponentProps } from '../TextInput';
4
+ export type NumberInputComponentProps = TextInputComponentProps;
5
+ export type NumberInputProps = ClassNameProps<TextInputClassNames> & InputProps<number> & NumberInputComponentProps;
6
+ declare function NumberInput({ onChange, value, ...props }: Readonly<NumberInputProps>): React.ReactElement;
7
+ export default NumberInput;
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from 'react';
4
+ import TextInput from '../TextInput';
5
+ function NumberInput({ onChange, value, ...props }) {
6
+ const ref = useRef(null);
7
+ function handleChange(event) {
8
+ onChange?.({ target: { name: event.target.name, value: Number(event.target.value) } });
9
+ }
10
+ function disableScroll() {
11
+ if (ref.current) {
12
+ ref.current.blur();
13
+ }
14
+ }
15
+ useEffect(() => {
16
+ if (ref.current) {
17
+ ref.current.addEventListener('wheel', disableScroll, { passive: false });
18
+ }
19
+ return () => {
20
+ if (ref.current) {
21
+ ref.current.removeEventListener('wheel', disableScroll);
22
+ }
23
+ };
24
+ }, []);
25
+ return (_jsx(TextInput, { ...props, ref: ref, onChange: handleChange, type: "number", value: value ? String(value) : '' }));
26
+ }
27
+ export default NumberInput;
@@ -12,11 +12,10 @@ export interface StaticTextInputProps extends ClassNameProps<StaticTextInputClas
12
12
  details?: string | null;
13
13
  label: string;
14
14
  icon?: React.ReactNode;
15
- isOpen?: boolean;
16
15
  isOptional?: boolean;
17
16
  onClear?: () => void;
18
17
  onClick?: React.MouseEventHandler<HTMLButtonElement>;
19
18
  placeholder?: string;
20
19
  }
21
- declare function StaticTextInput({ classNames, classNameProps, details, hasError, icon, isDisabled, isOpen, isOptional, label, name, onClear, onClick, onKeyDown, placeholder, value, }: Readonly<StaticTextInputProps>): React.ReactElement;
20
+ declare function StaticTextInput({ classNames, classNameProps, details, hasError, icon, isDisabled, isOptional, label, name, onClear, onClick, onKeyDown, placeholder, value, }: Readonly<StaticTextInputProps>): React.ReactElement;
22
21
  export default StaticTextInput;
@@ -9,7 +9,7 @@ function hasValue(value) {
9
9
  }
10
10
  return Boolean(value);
11
11
  }
12
- function StaticTextInput({ classNames, classNameProps, details, hasError, icon, isDisabled, isOpen, isOptional, label, name, onClear, onClick, onKeyDown, placeholder = 'Select...', value, }) {
12
+ function StaticTextInput({ classNames, classNameProps, details, hasError, icon, isDisabled, isOptional, label, name, onClear, onClick, onKeyDown, placeholder = 'Select...', value, }) {
13
13
  const componentClassNames = {
14
14
  ...getClassNames('staticTextInput')?.(classNameProps),
15
15
  ...classNames,
@@ -17,6 +17,6 @@ function StaticTextInput({ classNames, classNameProps, details, hasError, icon,
17
17
  function handleClear() {
18
18
  onClear?.();
19
19
  }
20
- return (_jsxs("div", { className: tw('relative', isDisabled ? 'pointer-events-none opacity-30' : null, componentClassNames.root?.default, hasError ? componentClassNames.root?.error : null), children: [_jsx("input", { name: name, type: "hidden", value: value ? value.toString() : '' }), label ? (_jsxs(Fragment, { children: [_jsx("span", { className: tw(componentClassNames.label), children: label }), details ? (_jsx("small", { className: tw(componentClassNames.details), children: details })) : null] })) : (_jsx("span", { className: tw(componentClassNames.placeholder), children: placeholder })), onClick ? (_jsx("button", { className: "absolute -bottom-px -left-px -right-px -top-px", disabled: isDisabled, onClick: onClick, onKeyDown: onKeyDown, tabIndex: 0, type: "button", children: _jsx(Assistive, { children: isOpen ? 'Close' : 'Open' }) })) : null, _jsxs("div", { className: "absolute bottom-0 right-0 top-0 flex w-0 items-center justify-end", children: [isOptional && hasValue(value) ? (_jsx("button", { className: tw('flex-none', componentClassNames.clear), onClick: handleClear, type: "button", children: _jsx(Assistive, { children: "Clear" }) })) : null, icon && !isDisabled ? (_jsx("i", { className: tw('pointer-events-none flex-none', componentClassNames.icon) })) : null] })] }));
20
+ return (_jsxs("div", { className: tw('relative', isDisabled ? 'pointer-events-none opacity-30' : null, componentClassNames.root?.default, hasError ? componentClassNames.root?.error : null), children: [_jsx("input", { name: name, type: "hidden", value: value ? value.toString() : '' }), label ? (_jsxs(Fragment, { children: [_jsx("span", { className: tw(componentClassNames.label), children: label }), details ? (_jsx("small", { className: tw(componentClassNames.details), children: details })) : null] })) : (_jsx("span", { className: tw(componentClassNames.placeholder), children: placeholder })), onClick ? (_jsx("button", { className: "absolute -bottom-px -left-px -right-px -top-px", disabled: isDisabled, onClick: onClick, onKeyDown: onKeyDown, tabIndex: 0, type: "button", children: _jsx(Assistive, { children: "Toggle" }) })) : null, _jsxs("div", { className: "absolute bottom-0 right-0 top-0 flex w-0 items-center justify-end", children: [isOptional && hasValue(value) ? (_jsx("button", { className: tw('flex-none', componentClassNames.clear), onClick: handleClear, type: "button", children: _jsx(Assistive, { children: "Clear" }) })) : null, icon && !isDisabled ? (_jsx("i", { className: tw('pointer-events-none flex-none', componentClassNames.icon) })) : null] })] }));
21
21
  }
22
22
  export default StaticTextInput;
@@ -30,5 +30,5 @@ export type TextInputProps = ClassNameProps<TextInputClassNames> & InputProps<st
30
30
  * | clear | The wrapper around the 'clear' button of the input, if one exists. The style of the
31
31
  * button itself is handled by the `renderClear` prop. | `string` |
32
32
  */
33
- declare function TextInput({ classNames, classNameProps, hasError, id, isAutocomplete, isDisabled, name, onBlur, onChange, onFocus, onKeyDown, placeholder, prefix, suffix, type, value, }: Readonly<TextInputProps>): React.ReactElement;
33
+ declare function TextInput({ classNames, classNameProps, hasError, id, isAutocomplete, isDisabled, name, onBlur, onChange, onFocus, onKeyDown, placeholder, prefix, ref, suffix, type, value, }: Readonly<TextInputProps>): React.ReactElement;
34
34
  export default TextInput;
@@ -13,7 +13,7 @@ import tw from '../../../styles/tw';
13
13
  * | clear | The wrapper around the 'clear' button of the input, if one exists. The style of the
14
14
  * button itself is handled by the `renderClear` prop. | `string` |
15
15
  */
16
- function TextInput({ classNames, classNameProps, hasError, id, isAutocomplete, isDisabled, name, onBlur, onChange, onFocus, onKeyDown, placeholder, prefix, suffix, type, value, }) {
16
+ function TextInput({ classNames, classNameProps, hasError, id, isAutocomplete, isDisabled, name, onBlur, onChange, onFocus, onKeyDown, placeholder, prefix, ref, suffix, type, value, }) {
17
17
  const componentClassNames = classNames || getClassNames('textInput')?.(classNameProps);
18
18
  function handleChange(event) {
19
19
  if (onChange) {
@@ -21,7 +21,7 @@ function TextInput({ classNames, classNameProps, hasError, id, isAutocomplete, i
21
21
  onChange({ target });
22
22
  }
23
23
  }
24
- return (_jsxs("div", { className: tw('relative', prefix ? 'flex flex-row-reverse' : null, (!prefix && suffix) || value ? 'flex' : null, isDisabled ? 'pointer-events-none opacity-30' : null), children: [prefix ? _jsx("div", { children: prefix }) : null, _jsx("input", { "aria-invalid": hasError, "aria-labelledby": hasError
24
+ return (_jsxs("div", { className: tw('relative', prefix ? 'flex flex-row-reverse' : null, (!prefix && suffix) || value ? 'flex' : null, isDisabled ? 'pointer-events-none opacity-30' : null), children: [prefix ? _jsx("div", { children: prefix }) : null, _jsx("input", { ref: ref, "aria-invalid": hasError, "aria-labelledby": hasError
25
25
  ? `${id || name}_err`
26
26
  : undefined /* eslint-disable-line no-undefined */, autoComplete: isAutocomplete === false
27
27
  ? 'one-time-code'
@@ -0,0 +1,8 @@
1
+ import { DropdownObject } from '../interfaces';
2
+ import { AutocompleteFormFieldProps } from '../components/FormFields';
3
+ interface UseAutocompleteArgs<T extends string> {
4
+ searchFn: (search: string) => Promise<DropdownObject<T>[]>;
5
+ selectFn: (id: T) => Promise<DropdownObject<T> | null>;
6
+ }
7
+ declare function useAutocomplete<T extends string>(props: AutocompleteFormFieldProps<T>, { searchFn, selectFn }: UseAutocompleteArgs<T>): AutocompleteFormFieldProps<T>;
8
+ export default useAutocomplete;
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { useEffect, useState } from 'react';
3
+ function useAutocomplete(props, { searchFn, selectFn }) {
4
+ const [isLoading, setIsLoading] = useState(false);
5
+ const [options, setOptions] = useState([]);
6
+ const [selected, setSelected] = useState(null);
7
+ async function handleSelect(value) {
8
+ if (!value) {
9
+ setSelected(null);
10
+ return;
11
+ }
12
+ const response = await selectFn(value);
13
+ setOptions([]);
14
+ setSelected(response);
15
+ }
16
+ async function handleSearch(search) {
17
+ if (search.length < 3) {
18
+ setOptions([]);
19
+ return;
20
+ }
21
+ setIsLoading(true);
22
+ const response = await searchFn(search);
23
+ setOptions(response);
24
+ setIsLoading(false);
25
+ }
26
+ useEffect(() => {
27
+ void handleSelect(props.value);
28
+ }, [props.value]);
29
+ return { ...props, isLoading, onSearch: handleSearch, options, selected };
30
+ }
31
+ export default useAutocomplete;
@@ -1,4 +1,6 @@
1
1
  export * from './interfaces';
2
+ export type { AutocompleteProps } from './components/Autocomplete';
3
+ export { default as Autocomplete } from './components/Autocomplete';
2
4
  export type { DropdownClassNames, DropdownProps } from './components/Dropdown';
3
5
  export { default as Dropdown } from './components/Dropdown';
4
6
  export type { EditableFormClassNames, EditableFormProps } from './components/EditableForm';
@@ -13,14 +15,19 @@ export type { FormSubmitProps } from './components/FormSubmit';
13
15
  export { default as FormSubmit } from './components/FormSubmit';
14
16
  export type { ModalFormProps } from './components/ModalForm';
15
17
  export { default as ModalForm } from './components/ModalForm';
18
+ export type { MoneyInputProps } from './components/MoneyInput';
19
+ export { default as MoneyInput } from './components/MoneyInput';
20
+ export type { NumberInputProps } from './components/NumberInput';
21
+ export { default as NumberInput } from './components/NumberInput';
16
22
  export type { PasswordInputClassNames, PasswordInputProps } from './components/PasswordInput';
17
23
  export { default as PasswordInput } from './components/PasswordInput';
18
24
  export type { TextInputClassNames, TextInputProps } from './components/TextInput';
19
25
  export { default as TextInput } from './components/TextInput';
20
26
  export type { EditableDropdownFormFieldProps, EditableTextFormFieldProps, } from './components/EditableFormFields';
21
27
  export { EditableDropdownFormField, EditableTextFormField } from './components/EditableFormFields';
22
- export type { DropdownFormFieldProps, PasswordFormFieldProps, TextFormFieldProps, } from './components/FormFields';
23
- export { DropdownFormField, PasswordFormField, TextFormField } from './components/FormFields';
28
+ export type { AutocompleteFormFieldProps, DropdownFormFieldProps, MoneyFormFieldProps, NumberFormFieldProps, PasswordFormFieldProps, TextFormFieldProps, } from './components/FormFields';
29
+ export { AutocompleteFormField, DropdownFormField, MoneyFormField, NumberFormField, PasswordFormField, TextFormField, } from './components/FormFields';
30
+ export { default as useAutocomplete } from './hooks/useAutocomplete';
24
31
  export { default as useDropdown } from './hooks/useDropdown';
25
32
  export { default as useEditableForm } from './hooks/useEditableForm';
26
33
  export { default as useForm } from './hooks/useForm';
@@ -1,4 +1,5 @@
1
1
  export * from './interfaces';
2
+ export { default as Autocomplete } from './components/Autocomplete';
2
3
  export { default as Dropdown } from './components/Dropdown';
3
4
  export { default as EditableForm } from './components/EditableForm';
4
5
  export { default as EditableFormField } from './components/EditableFormField';
@@ -6,11 +7,14 @@ export { default as Form } from './components/Form';
6
7
  export { default as FormField } from './components/FormField';
7
8
  export { default as FormSubmit } from './components/FormSubmit';
8
9
  export { default as ModalForm } from './components/ModalForm';
10
+ export { default as MoneyInput } from './components/MoneyInput';
11
+ export { default as NumberInput } from './components/NumberInput';
9
12
  export { default as PasswordInput } from './components/PasswordInput';
10
13
  export { default as TextInput } from './components/TextInput';
11
14
  export { EditableDropdownFormField, EditableTextFormField } from './components/EditableFormFields';
12
- export { DropdownFormField, PasswordFormField, TextFormField } from './components/FormFields';
15
+ export { AutocompleteFormField, DropdownFormField, MoneyFormField, NumberFormField, PasswordFormField, TextFormField, } from './components/FormFields';
13
16
  // Hooks
17
+ export { default as useAutocomplete } from './hooks/useAutocomplete';
14
18
  export { default as useDropdown } from './hooks/useDropdown';
15
19
  export { default as useEditableForm } from './hooks/useEditableForm';
16
20
  export { default as useForm } from './hooks/useForm';
@@ -26,6 +26,7 @@ export interface InputProps<V, T extends V = V> {
26
26
  id?: string;
27
27
  isDisabled?: boolean;
28
28
  name: string;
29
+ ref?: React.Ref<HTMLInputElement>;
29
30
  onChange?: (event: InputEvent<T>) => void;
30
31
  onKeyDown?: React.KeyboardEventHandler;
31
32
  value?: V;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sqrzro/ui",
3
3
  "type": "module",
4
- "version": "4.0.0-alpha.4",
4
+ "version": "4.0.0-alpha.6",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "ISC",