@nex-ui/react
Version:
🎉 A beautiful, modern, and reliable React component library.
289 lines (285 loc) • 9.44 kB
JavaScript
"use client";
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
var utils = require('@nex-ui/utils');
var hooks = require('@nex-ui/hooks');
var icons = require('@nex-ui/icons');
var Context = require('../provider/Context.cjs');
var useDefaultProps = require('../utils/useDefaultProps.cjs');
var useStyles = require('../utils/useStyles.cjs');
var useSlot = require('../utils/useSlot.cjs');
var ButtonBase = require('../buttonBase/ButtonBase.cjs');
var composeClasses = require('../utils/composeClasses.cjs');
var input = require('../../theme/recipes/input.cjs');
var getUtilityClass = require('../utils/getUtilityClass.cjs');
const useSlotClasses = (ownerState)=>{
const { prefix } = Context.useNexUI();
const { variant, radius, size, color, disabled, fullWidth, invalid, classes, labelPlacement } = ownerState;
return react.useMemo(()=>{
const inputRoot = `${prefix}-input`;
const slots = {
root: [
'root',
`variant-${variant}`,
`radius-${radius}`,
`size-${size}`,
`color-${color}`,
disabled && 'disabled',
fullWidth && 'full-width',
invalid && 'invalid',
labelPlacement && `label-placement-${labelPlacement}`
],
input: [
'input'
],
clearButton: [
'clear-btn'
],
prefix: [
'prefix'
],
suffix: [
'suffix'
],
label: [
'label'
]
};
return composeClasses.composeClasses(slots, getUtilityClass.getUtilityClass(inputRoot), classes);
}, [
classes,
color,
disabled,
fullWidth,
invalid,
labelPlacement,
prefix,
radius,
size,
variant
]);
};
const useSlotAriaProps = (ownerState)=>{
const { disabled, invalid, type, value, placeholder, slotProps, label: inputLabel, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, id: idProp, as = 'input', tabIndex = 0 } = ownerState;
const id = react.useId();
return react.useMemo(()=>{
const labelProps = slotProps?.label ?? {};
const clearButtonProps = slotProps?.clearButton ?? {};
const stringLabel = utils.isString(inputLabel);
const inputId = idProp ?? (stringLabel ? `input-${id}` : undefined);
const labelId = labelProps['id'] ?? (stringLabel ? `label-${id}` : undefined);
let input = {
id: inputId,
tabIndex: disabled ? -1 : tabIndex,
'aria-invalid': invalid,
'aria-labelledby': ariaLabelledBy ?? labelId,
'aria-label': ariaLabel ?? (stringLabel ? inputLabel : undefined)
};
if (as === 'input' || utils.isFunction(as)) {
input = {
...input,
value,
disabled,
placeholder,
type
};
} else {
input = {
...input,
role: 'textbox',
'aria-disabled': disabled
};
}
const label = {
id: labelId,
htmlFor: inputId
};
const clearButton = {
'aria-label': clearButtonProps['aria-label'] ?? 'Clear input'
};
return {
input,
label,
clearButton
};
}, [
ariaLabel,
ariaLabelledBy,
as,
disabled,
id,
idProp,
inputLabel,
invalid,
placeholder,
tabIndex,
type,
value,
slotProps?.clearButton,
slotProps?.label
]);
};
const Input = (inProps)=>{
const { primaryThemeColor } = Context.useNexUI();
const props = useDefaultProps.useDefaultProps({
name: 'Input',
props: inProps
});
const { sx, label, className, prefix, suffix, onClear, slotProps, onValueChange, placeholder, autoFocus, defaultValue = '', value: valueProp, color = primaryThemeColor, type = 'text', disabled = false, variant = 'outlined', fullWidth = false, invalid = false, size = 'md', radius = size, clearable = false, labelPlacement: labelPlacementProp = 'float-outside', ...remainingProps } = props;
const [value, setValue] = hooks.useControlledState(valueProp, defaultValue, onValueChange);
const inputRef = react.useRef(null);
const hasLabel = !!label;
const hasValue = !!value;
const hasPlaceholder = !!placeholder;
const hasPrefix = !!prefix;
let labelPlacement = labelPlacementProp;
const floatLabel = labelPlacement === 'float-outside' || labelPlacement === 'float-inside';
const hasDefaultPlaceholder = [
'date',
'datetime-local',
'time',
'week',
'month',
'range'
].includes(type);
if (!hasLabel) {
labelPlacement = undefined;
} else if (floatLabel && (hasPlaceholder || hasValue || hasDefaultPlaceholder || hasPrefix)) {
if (labelPlacementProp === 'float-outside') {
labelPlacement = 'outside';
} else if (labelPlacementProp === 'float-inside') {
labelPlacement = 'inside';
}
}
const ownerState = {
...props,
color,
disabled,
variant,
fullWidth,
size,
radius,
invalid,
type,
clearable,
value,
labelPlacement,
autoFocus
};
const { focusVisible, focusProps } = hooks.useFocusRing({
autoFocus,
input: true
});
const styles = useStyles.useStyles({
ownerState,
name: 'Input',
recipe: input.inputRecipe
});
const classes = useSlotClasses(ownerState);
const slotAriaProps = useSlotAriaProps(ownerState);
const handleClearValue = hooks.useEvent(()=>{
setValue('');
onClear?.();
inputRef.current?.focus();
});
const handleChange = hooks.useEvent((e)=>{
setValue(e.target.value);
});
const handleFocusInput = hooks.useEvent((e)=>{
if (inputRef.current && e.target === e.currentTarget) {
inputRef.current.focus();
}
});
const [InputRoot, getInputRootProps] = useSlot.useSlot({
ownerState,
elementType: 'div',
externalSlotProps: slotProps?.root,
style: styles.root,
classNames: classes.root,
additionalProps: {
sx,
className,
onClick: handleFocusInput,
['data-focus-visible']: focusVisible || undefined
}
});
const [InputLabel, getInputLabelProps] = useSlot.useSlot({
ownerState,
elementType: 'label',
externalSlotProps: slotProps?.label,
style: styles.label,
classNames: classes.label,
a11y: slotAriaProps.label
});
const [InputControl, getInputControlProps] = useSlot.useSlot({
ownerState,
elementType: 'input',
externalForwardedProps: remainingProps,
style: styles.input,
classNames: classes.input,
a11y: slotAriaProps.input,
additionalProps: {
ref: inputRef,
autoFocus,
onChange: handleChange,
...focusProps
}
});
const [InputClearButton, getClearButtonProps] = useSlot.useSlot({
ownerState,
elementType: ButtonBase.ButtonBase,
style: styles.clearButton,
externalSlotProps: slotProps?.clearButton,
classNames: classes.clearButton,
a11y: slotAriaProps.clearButton,
shouldForwardComponent: false,
additionalProps: {
onClick: handleClearValue,
disabled: disabled,
sx: {
visibility: value ? 'visible' : 'hidden'
}
}
});
const [InputPrefix, getInputPrefixProps] = useSlot.useSlot({
ownerState,
elementType: 'span',
externalSlotProps: slotProps?.prefix,
style: styles.prefix,
classNames: classes.prefix
});
const [InputSuffix, getInputSuffixProps] = useSlot.useSlot({
ownerState,
elementType: 'span',
externalSlotProps: slotProps?.suffix,
style: styles.suffix,
classNames: classes.suffix
});
return /*#__PURE__*/ jsxRuntime.jsxs(InputRoot, {
...getInputRootProps(),
children: [
prefix && /*#__PURE__*/ jsxRuntime.jsx(InputPrefix, {
...getInputPrefixProps(),
children: prefix
}),
label && /*#__PURE__*/ jsxRuntime.jsx(InputLabel, {
...getInputLabelProps(),
children: label
}),
/*#__PURE__*/ jsxRuntime.jsx(InputControl, {
...getInputControlProps()
}),
clearable && /*#__PURE__*/ jsxRuntime.jsx(InputClearButton, {
...getClearButtonProps(),
children: /*#__PURE__*/ jsxRuntime.jsx(icons.CloseCircleFilled, {})
}),
suffix && /*#__PURE__*/ jsxRuntime.jsx(InputSuffix, {
...getInputSuffixProps(),
children: suffix
})
]
});
};
Input.displayName = 'Input';
exports.Input = Input;