UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

289 lines (285 loc) • 9.44 kB
"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;