diff --git a/packages/@luke-ui/react/src/combobox-field/index.tsx b/packages/@luke-ui/react/src/combobox-field/index.tsx index e4fb4607..9398da2b 100644 --- a/packages/@luke-ui/react/src/combobox-field/index.tsx +++ b/packages/@luke-ui/react/src/combobox-field/index.tsx @@ -1,5 +1,6 @@ import type { CSSProperties, JSX, ReactNode } from 'react'; import type { ComboBoxProps as RacComboBoxProps } from 'react-aria-components/ComboBox'; +import { composeField } from '../field/compose-field.js'; import type { FieldErrorProps } from '../field/primitive/error.js'; import { Field } from '../field/primitive/index.js'; import type { FieldNecessityIndicator } from '../field/primitive/label.js'; @@ -79,33 +80,25 @@ export interface ComboboxFieldProps /** Composes `ComboboxInput` with label, description, and error slots. */ export function ComboboxField(props: ComboboxFieldProps): JSX.Element { + const [fieldSlotProps, restProps] = composeField(props); const { children, - description, - errorMessage, - label, listBoxProps, loadMoreItem, loadingState, menuWidth, - necessityIndicator, onLoadMore, placeholder, popoverProps, size = 'medium', ...comboboxInputProps - } = props; + } = restProps; const isAsync = loadingState != null; return ( size={size} {...comboboxInputProps}> - + diff --git a/packages/@luke-ui/react/src/field/compose-field.ts b/packages/@luke-ui/react/src/field/compose-field.ts new file mode 100644 index 00000000..678dbbff --- /dev/null +++ b/packages/@luke-ui/react/src/field/compose-field.ts @@ -0,0 +1,23 @@ +import type { ReactNode } from 'react'; +import type { FieldErrorProps } from './primitive/error.js'; +import type { FieldNecessityIndicator } from './primitive/label.js'; + +export interface FieldSlotProps { + /** Optional helper text shown below the control. */ + description?: ReactNode; + /** Error content passed to `FieldError`. */ + errorMessage?: FieldErrorProps['children']; + /** Label content shown above the control. */ + label?: ReactNode; + /** Label necessity style. */ + necessityIndicator?: FieldNecessityIndicator; +} + +/** Splits the `Field` slot props (label/description/errorMessage/necessityIndicator) off a Composed field's props. */ +export function composeField( + props: T, +): [FieldSlotProps, Omit] { + const { description, errorMessage, label, necessityIndicator, ...restProps } = props; + + return [{ description, errorMessage, label, necessityIndicator }, restProps]; +} diff --git a/packages/@luke-ui/react/src/text-field/index.tsx b/packages/@luke-ui/react/src/text-field/index.tsx index 2f56467b..ecbc7b94 100644 --- a/packages/@luke-ui/react/src/text-field/index.tsx +++ b/packages/@luke-ui/react/src/text-field/index.tsx @@ -4,6 +4,7 @@ import type { TextFieldProps as RacTextFieldProps, } from 'react-aria-components/TextField'; import { TextField as RacTextField } from 'react-aria-components/TextField'; +import { composeField } from '../field/compose-field.js'; import type { FieldErrorProps } from '../field/primitive/error.js'; import { Field } from '../field/primitive/index.js'; import type { FieldNecessityIndicator } from '../field/primitive/label.js'; @@ -56,27 +57,19 @@ export interface TextFieldProps /** Composes `TextInput` with label, description, and error slots. */ export function TextField(props: TextFieldProps): JSX.Element { + const [fieldSlotProps, restProps] = composeField(props); const { adornmentEnd, adornmentStart, - description, - errorMessage, inputClassName, - label, - necessityIndicator, placeholder, size = 'medium', ...textFieldProps - } = props; + } = restProps; return ( - +