feat(input): initial structure and styles created

This commit is contained in:
Junior Garcia 2023-04-02 01:13:02 -03:00
parent 3a7579dbf5
commit 1ee0293bb3
9 changed files with 1007 additions and 40 deletions

View File

@ -41,9 +41,13 @@
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/system": "workspace:*",
"@nextui-org/theme": "workspace:*",
"@react-aria/textfield": "^3.9.0"
"@nextui-org/use-aria-field": "workspace:*",
"@react-aria/focus": "^3.11.0",
"@react-aria/utils": "^3.15.0",
"@react-stately/utils": "^3.6.0"
},
"devDependencies": {
"@react-types/shared": "^3.15.0",
"@react-types/textfield": "^3.7.0",
"clean-package": "2.2.0",
"react": "^18.0.0"

View File

@ -4,12 +4,33 @@ import {UseInputProps, useInput} from "./use-input";
export interface InputProps extends Omit<UseInputProps, "ref"> {}
const Input = forwardRef<InputProps, "div">((props, ref) => {
const {Component, domRef, children, styles, ...otherProps} = useInput({ref, ...props});
const Input = forwardRef<InputProps, "input">((props, ref) => {
const {
Component,
label,
description,
shouldLabelBeOutside,
shouldLabelBeInside,
errorMessage,
getBaseProps,
getLabelProps,
getInputProps,
getInputWrapperProps,
getDescriptionProps,
getErrorMessageProps,
} = useInput({ref, ...props});
const labelContent = <label {...getLabelProps()}>{label}</label>;
return (
<Component ref={domRef} className={styles} {...otherProps}>
{children}
<Component {...getBaseProps()}>
{shouldLabelBeOutside ? labelContent : null}
<div {...getInputWrapperProps()}>
{shouldLabelBeInside ? labelContent : null}
<input {...getInputProps()} />
</div>
{description && <div {...getDescriptionProps()}>{description}</div>}
{errorMessage && <div {...getErrorMessageProps()}>{errorMessage}</div>}
</Component>
);
});

View File

@ -0,0 +1,166 @@
// based on @react-aria/use-textfield hook, but with useId from react 18
// thanks to @adobe/react-spectrum for the great work ❤️
import {AriaTextFieldProps} from "@react-types/textfield";
import {
ChangeEvent,
DOMFactory,
HTMLAttributes,
LabelHTMLAttributes,
ReactDOM,
RefObject,
} from "react";
import {DOMAttributes} from "@react-types/shared";
import {filterDOMProps, mergeProps} from "@react-aria/utils";
import {useAriaField} from "@nextui-org/use-aria-field";
import {useFocusable} from "@react-aria/focus";
/**
* A map of HTML element names and their interface types.
* For example `'a'` -> `HTMLAnchorElement`.
*/
type IntrinsicHTMLElements = {
[K in keyof IntrinsicHTMLAttributes]: IntrinsicHTMLAttributes[K] extends HTMLAttributes<infer T>
? T
: never;
};
/**
* A map of HTML element names and their attribute interface types.
* For example `'a'` -> `AnchorHTMLAttributes<HTMLAnchorElement>`.
*/
type IntrinsicHTMLAttributes = {
[K in keyof ReactDOM]: ReactDOM[K] extends DOMFactory<infer T, any> ? T : never;
};
type DefaultElementType = "input";
/**
* The intrinsic HTML element names that `useTextField` supports; e.g. `input`,
* `textarea`.
*/
type TextFieldIntrinsicElements = keyof Pick<IntrinsicHTMLElements, "input" | "textarea">;
/**
* The HTML element interfaces that `useTextField` supports based on what is
* defined for `TextFieldIntrinsicElements`; e.g. `HTMLInputElement`,
* `HTMLTextAreaElement`.
*/
type TextFieldHTMLElementType = Pick<IntrinsicHTMLElements, TextFieldIntrinsicElements>;
/**
* The HTML attributes interfaces that `useTextField` supports based on what
* is defined for `TextFieldIntrinsicElements`; e.g. `InputHTMLAttributes`,
* `TextareaHTMLAttributes`.
*/
type TextFieldHTMLAttributesType = Pick<IntrinsicHTMLAttributes, TextFieldIntrinsicElements>;
/**
* The type of `inputProps` returned by `useTextField`; e.g. `InputHTMLAttributes`,
* `TextareaHTMLAttributes`.
*/
type TextFieldInputProps<T extends TextFieldIntrinsicElements> = TextFieldHTMLAttributesType[T];
export interface AriaTextFieldOptions<T extends TextFieldIntrinsicElements>
extends AriaTextFieldProps {
/**
* The HTML element used to render the input, e.g. 'input', or 'textarea'.
* It determines whether certain HTML attributes will be included in `inputProps`.
* For example, [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type).
* @default 'input'
*/
inputElementType?: T;
}
/**
* The type of `ref` object that can be passed to `useTextField` based on the given
* intrinsic HTML element name; e.g.`RefObject<HTMLInputElement>`,
* `RefObject<HTMLTextAreaElement>`.
*/
type TextFieldRefObject<T extends TextFieldIntrinsicElements> = RefObject<
TextFieldHTMLElementType[T]
>;
export interface TextFieldAria<T extends TextFieldIntrinsicElements = DefaultElementType> {
/** Props for the input element. */
inputProps: TextFieldInputProps<T>;
/** Props for the text field's visible label element, if any. */
labelProps: DOMAttributes | LabelHTMLAttributes<HTMLLabelElement>;
/** Props for the text field's description element, if any. */
descriptionProps: DOMAttributes;
/** Props for the text field's error message element, if any. */
errorMessageProps: DOMAttributes;
}
/**
* Provides the behavior and accessibility implementation for a text field.
* @param props - Props for the text field.
* @param ref - Ref to the HTML input or textarea element.
*/
export function useAriaTextField<T extends TextFieldIntrinsicElements = DefaultElementType>(
props: AriaTextFieldOptions<T>,
ref: TextFieldRefObject<T>,
): TextFieldAria<T> {
let {
inputElementType = "input",
isDisabled = false,
isRequired = false,
isReadOnly = false,
validationState,
type = "text",
onChange = () => {},
}: AriaTextFieldOptions<TextFieldIntrinsicElements> = props;
let {focusableProps} = useFocusable(props, ref);
let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useAriaField(props);
let domProps = filterDOMProps(props, {labelable: true});
const inputOnlyProps = {
type,
pattern: props.pattern,
};
return {
labelProps,
// @ts-ignore
inputProps: mergeProps(domProps, inputElementType === "input" && inputOnlyProps, {
disabled: isDisabled,
readOnly: isReadOnly,
"aria-required": isRequired || undefined,
"aria-invalid": validationState === "invalid" || undefined,
"aria-errormessage": props["aria-errormessage"],
"aria-activedescendant": props["aria-activedescendant"],
"aria-autocomplete": props["aria-autocomplete"],
"aria-haspopup": props["aria-haspopup"],
value: props.value,
defaultValue: props.value ? undefined : props.defaultValue,
onChange: (e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
autoComplete: props.autoComplete,
maxLength: props.maxLength,
minLength: props.minLength,
name: props.name,
placeholder: props.placeholder,
inputMode: props.inputMode,
// Clipboard events
onCopy: props.onCopy,
onCut: props.onCut,
onPaste: props.onPaste,
// Composition events
onCompositionEnd: props.onCompositionEnd,
onCompositionStart: props.onCompositionStart,
onCompositionUpdate: props.onCompositionUpdate,
// Selection events
onSelect: props.onSelect,
// Input events
onBeforeInput: props.onBeforeInput,
onInput: props.onInput,
...focusableProps,
...fieldProps,
}),
descriptionProps,
errorMessageProps,
};
}

View File

@ -1,37 +1,167 @@
import type {InputVariantProps} from "@nextui-org/theme";
import type {InputVariantProps, SlotsToClasses, InputSlots} from "@nextui-org/theme";
import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {AriaTextFieldProps} from "@react-types/textfield";
import {useFocusRing} from "@react-aria/focus";
import {input} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/dom-utils";
import {ReactRef} from "@nextui-org/shared-utils";
import {useMemo} from "react";
import {clsx, dataAttr} from "@nextui-org/shared-utils";
import {useControlledState} from "@react-stately/utils";
import {useMemo, Ref} from "react";
import {chain, mergeProps} from "@react-aria/utils";
export interface UseInputProps extends HTMLNextUIProps<"div", InputVariantProps> {
import {useAriaTextField} from "./use-aria-text-field";
export interface Props extends HTMLNextUIProps<"input"> {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLElement | null>;
ref?: Ref<HTMLInputElement>;
/**
* Classname or List of classes to change the styles of the element.
* if `className` is passed, it will be added to the base slot.
*
* @example
* ```ts
* <Chip styles={{
* base:"base-classes",
* label: "label-classes",
* inputWrapper: "input-wrapper-classes",
* input: "input-classes",
* description: "description-classes",
* helperText: "helper-text-classes",
* }} />
* ```
*/
styles?: SlotsToClasses<InputSlots>;
}
export type UseInputProps = Props & AriaTextFieldProps & InputVariantProps;
export function useInput(originalProps: UseInputProps) {
const [props, variantProps] = mapPropsVariants(originalProps, input.variantKeys);
const {ref, as, className, ...otherProps} = props;
const {
ref,
as,
label,
description,
errorMessage,
className,
styles,
autoFocus,
...otherProps
} = props;
const Component = as || "div";
const baseStyles = clsx(styles?.base, className);
const domRef = useDOMRef(ref);
const domRef = useDOMRef<HTMLInputElement>(ref);
const styles = useMemo(
const [inputValue, setInputValue] = useControlledState(props.value, props.defaultValue, () => {});
const {labelProps, inputProps, descriptionProps, errorMessageProps} = useAriaTextField(
{
...originalProps,
onChange: chain(props.onChange, setInputValue),
},
domRef,
);
const {isFocusVisible, isFocused, focusProps} = useFocusRing({
autoFocus,
isTextInput: true,
});
const isInvalid = props.validationState === "invalid";
const labelPosition = originalProps.labelPosition || "outside";
const isLabelPlaceholder = !props.placeholder;
const shouldLabelBeOutside = labelPosition === "outside" || labelPosition === "outside-left";
const shouldLabelBeInside = labelPosition === "inside";
const slots = useMemo(
() =>
input({
...variantProps,
className,
isInvalid,
isLabelPlaceholder,
isFocusVisible,
}),
[...Object.values(variantProps), className],
[...Object.values(variantProps), isInvalid, isLabelPlaceholder, isFocusVisible],
);
return {Component, styles, domRef, ...otherProps};
const getBaseProps: PropGetter = (props = {}) => {
return {
className: slots.base({class: baseStyles}),
"data-focus-visible": dataAttr(isFocusVisible),
"data-focused": dataAttr(isFocused),
"data-invalid": dataAttr(isInvalid),
...props,
};
};
const getLabelProps: PropGetter = (props = {}) => {
return {
className: slots.label({class: styles?.label}),
...labelProps,
...props,
};
};
const getInputProps: PropGetter = (props = {}) => {
return {
ref: domRef,
className: slots.input({class: styles?.input}),
"data-focus-visible": dataAttr(isFocusVisible),
"data-focused": dataAttr(isFocused),
"data-invalid": dataAttr(isInvalid),
...mergeProps(focusProps, inputProps, otherProps, props),
};
};
const getInputWrapperProps: PropGetter = (props = {}) => {
return {
className: slots.inputWrapper({
class: clsx(styles?.inputWrapper, !!inputValue ? "is-filled" : ""),
}),
...props,
};
};
const getDescriptionProps: PropGetter = (props = {}) => {
return {
className: slots.description({class: styles?.description}),
...descriptionProps,
...props,
};
};
const getErrorMessageProps: PropGetter = (props = {}) => {
return {
className: slots.errorMessage({class: styles?.errorMessage}),
...errorMessageProps,
...props,
};
};
return {
Component,
styles,
domRef,
label,
description,
labelPosition,
shouldLabelBeOutside,
shouldLabelBeInside,
errorMessage,
getBaseProps,
getLabelProps,
getInputProps,
getInputWrapperProps,
getDescriptionProps,
getErrorMessageProps,
};
}
export type UseInputReturn = ReturnType<typeof useInput>;

View File

@ -8,6 +8,12 @@ export default {
title: "Components/Input",
component: Input,
argTypes: {
variant: {
control: {
type: "select",
options: ["flat", "faded", "bordered", "underlined"],
},
},
color: {
control: {
type: "select",
@ -26,6 +32,12 @@ export default {
options: ["xs", "sm", "md", "lg", "xl"],
},
},
labelPosition: {
control: {
type: "select",
options: ["outside", "outside-left", "inside"],
},
},
isDisabled: {
control: {
type: "boolean",
@ -36,11 +48,37 @@ export default {
const defaultProps = {
...input.defaultVariants,
label: "Email",
};
const Template: ComponentStory<typeof Input> = (args: InputProps) => <Input {...args} />;
const Template: ComponentStory<typeof Input> = (args: InputProps) => (
<div className="max-w-md flex flex-row gap-4">
<Input {...args} />
<Input {...args} placeholder="Enter your email" />
</div>
);
export const Default = Template.bind({});
Default.args = {
...defaultProps,
};
export const WithPlaceholder = Template.bind({});
WithPlaceholder.args = {
...defaultProps,
placeholder: "Enter your email",
};
export const WithErrorMessage = Template.bind({});
WithErrorMessage.args = {
...defaultProps,
errorMessage: "Please enter a valid email address",
};
export const InvalidValidationState = Template.bind({});
InvalidValidationState.args = {
...defaultProps,
variant: "bordered",
validationState: "invalid",
errorMessage: "Please enter a valid email address",
};

View File

@ -53,7 +53,7 @@
"lodash.foreach": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.isempty": "^4.4.0",
"tailwind-variants": "^0.1.1",
"tailwind-variants": "^0.1.2",
"tailwindcss": "^3.2.7"
},
"peerDependencies": {

View File

@ -22,3 +22,4 @@ export * from "./accordion-item";
export * from "./accordion";
export * from "./progress";
export * from "./circular-progress";
export * from "./input";

View File

@ -0,0 +1,608 @@
import type {VariantProps} from "tailwind-variants";
import {tv} from "tailwind-variants";
import {ringClasses} from "../utils";
/**
* Input wrapper **Tailwind Variants** component
*
* @example
* ```js
* const {base, label, inputWrapper, input, description, helperText} = input({...})
*
* <div className={base())}>
* <label className={label()}>Label</label>
* <div className={inputWrapper()}>
* <input className={input()}/>
* </div>
* <span className={description()}>Description</span>
* <span className={helperText()}>Helper text</span>
* </div>
* ```
*/
const input = tv({
slots: {
base: "flex flex-col gap-2",
label: "block text-sm font-medium text-neutral-600",
inputWrapper: "w-full flex flex-row items-center shadow-sm px-3 gap-3",
input: "w-full h-full bg-transparent outline-none placeholder:text-neutral-500",
description: "text-xs text-neutral-500",
errorMessage: "text-xs text-danger",
},
variants: {
variant: {
flat: {
inputWrapper: ["bg-neutral-100", "hover:bg-neutral-200", "focus-within:!bg-neutral-100"],
},
faded: {
inputWrapper: [
"bg-neutral-100",
"border",
"border-neutral-200",
"hover:border-neutral-400",
],
},
bordered: {
inputWrapper: [
"border-2",
"border-neutral-200",
"hover:border-neutral-400",
"focus-within:!border-foreground",
],
},
underlined: {
inputWrapper: [
"!px-1",
"relative",
"box-border",
"border-b-2",
"shadow-[0_1px_0px_0_rgba(0,0,0,0.05)]",
"border-neutral-200",
"!rounded-none",
"hover:border-neutral-300",
"after:content-['']",
"after:w-0",
"after:origin-center",
"after:bg-foreground",
"after:absolute",
"after:left-1/2",
"after:-translate-x-1/2",
"after:-bottom-[2px]",
"after:h-[2px]",
"focus-within:after:w-full",
],
},
},
color: {
neutral: {},
primary: {
label: "text-primary",
},
secondary: {
label: "text-secondary",
},
success: {
label: "text-success",
},
warning: {
label: "text-warning",
},
danger: {
label: "text-danger",
},
},
size: {
xs: {
label: "text-xs",
inputWrapper: "h-6 px-1",
input: "text-xs",
},
sm: {
label: "text-xs",
inputWrapper: "h-8 px-2",
input: "text-xs",
},
md: {
inputWrapper: "h-10",
input: "text-sm",
},
lg: {
inputWrapper: "h-12",
input: "text-base",
},
xl: {
inputWrapper: "h-14",
input: "text-md",
},
},
radius: {
none: {
inputWrapper: "rounded-none",
},
base: {
inputWrapper: "rounded",
},
sm: {
inputWrapper: "rounded-sm",
},
md: {
inputWrapper: "rounded-md",
},
lg: {
inputWrapper: "rounded-lg",
},
xl: {
inputWrapper: "rounded-xl",
},
full: {
inputWrapper: "rounded-full",
},
},
labelPosition: {
outside: {},
"outside-left": {
base: "flex-row items-center",
},
inside: {
label: "text-xs",
inputWrapper: "flex-col items-start justify-center gap-0",
},
},
fullWidth: {
true: {
base: "w-full",
},
},
isLabelPlaceholder: {
true: {
label: "absolute",
},
},
isDisabled: {
true: {
base: "opacity-50 pointer-events-none",
},
},
isFocusVisible: {
true: {},
},
isInvalid: {
true: {
label: "text-danger",
input: "placeholder:text-danger",
},
},
disableAnimation: {
true: {
inputWrapper: "transition-none",
label: "transition-none",
},
false: {
inputWrapper: "transition-background motion-reduce:transition-none",
label: [
"will-change-auto",
"transition-all",
"!duration-200",
"motion-reduce:transition-none",
],
},
},
},
defaultVariants: {
variant: "flat",
color: "neutral",
size: "md",
radius: "xl",
fullWidth: true,
labelPosition: "outside",
isDisabled: false,
disableAnimation: false,
},
compoundVariants: [
// flat & faded & color
{
variant: "flat",
color: "primary",
class: {
inputWrapper: [
"bg-primary-50",
"hover:bg-primary-100",
"text-primary",
"focus-within:!bg-primary-50",
"placeholder:text-primary",
],
input: "placeholder:text-primary",
},
},
{
variant: "flat",
color: "secondary",
class: {
inputWrapper: [
"bg-secondary-50",
"hover:bg-secondary-100",
"text-secondary",
"focus-within:!bg-secondary-50",
"placeholder:text-secondary",
],
input: "placeholder:text-secondary",
},
},
{
variant: "flat",
color: "success",
class: {
inputWrapper: [
"bg-success-50",
"hover:bg-success-100",
"text-success",
"focus-within:!bg-success-50",
"placeholder:text-success",
],
input: "placeholder:text-success",
},
},
{
variant: "flat",
color: "warning",
class: {
inputWrapper: [
"bg-warning-50",
"hover:bg-warning-100",
"text-warning",
"focus-within:!bg-warning-50",
"placeholder:text-warning",
],
input: "placeholder:text-warning",
},
},
{
variant: "flat",
color: "danger",
class: {
inputWrapper: [
"bg-danger-50",
"hover:bg-danger-100",
"text-danger",
"focus-within:!bg-danger-50",
"placeholder:text-danger",
],
input: "placeholder:text-danger",
},
},
// faded & color
{
variant: "faded",
color: "primary",
class: {
inputWrapper: "text-primary",
input: "placeholder:text-primary",
},
},
{
variant: "faded",
color: "secondary",
class: {
inputWrapper: "text-secondary",
input: "placeholder:text-secondary",
},
},
{
variant: "faded",
color: "success",
class: {
inputWrapper: "text-success",
input: "placeholder:text-success",
},
},
{
variant: "faded",
color: "warning",
class: {
inputWrapper: "text-warning",
input: "placeholder:text-warning",
},
},
{
variant: "faded",
color: "danger",
class: {
inputWrapper: "text-danger",
input: "placeholder:text-danger",
},
},
// underlined & color
{
variant: "underlined",
color: "primary",
class: {
inputWrapper: "after:bg-primary",
},
},
{
variant: "underlined",
color: "secondary",
class: {
inputWrapper: "after:bg-secondary",
},
},
{
variant: "underlined",
color: "success",
class: {
inputWrapper: "after:bg-success",
},
},
{
variant: "underlined",
color: "warning",
class: {
inputWrapper: "after:bg-warning",
},
},
{
variant: "underlined",
color: "danger",
class: {
inputWrapper: "after:bg-danger",
},
},
// bordered & color
{
variant: "bordered",
color: "primary",
class: {
inputWrapper: "focus-within:!border-primary",
},
},
{
variant: "bordered",
color: "secondary",
class: {
inputWrapper: "focus-within:!border-secondary",
},
},
{
variant: "bordered",
color: "success",
class: {
inputWrapper: "focus-within:!border-success",
},
},
{
variant: "bordered",
color: "warning",
class: {
inputWrapper: "focus-within:!border-warning",
},
},
{
variant: "bordered",
color: "danger",
class: {
inputWrapper: "focus-within:!border-danger",
},
},
// radius-full & size
{
radius: "full",
size: ["xs", "sm"],
class: {
inputWrapper: "px-3",
},
},
{
radius: "full",
size: "md",
class: {
inputWrapper: "px-4",
},
},
{
radius: "full",
size: "lg",
class: {
inputWrapper: "px-5",
},
},
{
radius: "full",
size: "xl",
class: {
inputWrapper: "px-6",
},
},
// !disableAnimation & variant
{
disableAnimation: false,
variant: ["faded", "bordered"],
class: {
inputWrapper: "transition-colors motion-reduce:transition-none",
},
},
{
disableAnimation: false,
variant: "underlined",
class: {
inputWrapper: "after:transition-width motion-reduce:after:transition-none",
},
},
// isFocusVisible & variant
{
isFocusVisible: true,
variant: ["flat", "faded"],
class: {
inputWrapper: [...ringClasses],
},
},
// isInvalid & variant
{
isInvalid: true,
variant: "flat",
class: {
inputWrapper: [
"bg-danger-50",
"hover:bg-danger-100",
"text-danger",
"focus-within:!bg-danger-50",
"placeholder:text-danger",
],
},
},
{
isInvalid: true,
variant: "faded",
class: {
inputWrapper: "text-danger",
},
},
{
isInvalid: true,
variant: "bordered",
class: {
inputWrapper: "!border-danger focus-within:!border-danger",
},
},
{
isInvalid: true,
variant: "underlined",
class: {
inputWrapper: "after:bg-danger",
},
},
// size & labelPosition
{
labelPosition: "inside",
size: "xs",
class: {
label: "text-[0.6rem]",
inputWrapper: "h-10 py-1 px-2",
},
},
{
labelPosition: "inside",
size: "sm",
class: {
inputWrapper: "h-12 py-1.5 px-3",
},
},
{
labelPosition: "inside",
size: "md",
class: {
inputWrapper: "h-14 py-2",
},
},
{
labelPosition: "inside",
size: "lg",
class: {
label: "text-sm",
inputWrapper: "h-16 py-2.5 gap-1",
},
},
{
labelPosition: "inside",
size: "xl",
class: {
label: "text-sm",
inputWrapper: "h-20 p-4 gap-2",
},
},
// !isLabelPlaceholder & labelPosition
{
isLabelPlaceholder: true,
labelPosition: "inside",
class: {
inputWrapper: "group",
label: [
"font-normal",
"text-neutral-500",
"group-focus-within:font-medium",
"group-focus-within:text-neutral-600",
"group-[.is-filled]:font-medium",
"group-[.is-filled]:text-neutral-600",
],
},
},
// isLabelPlaceholder & labelPosition & size
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: "xs",
class: {
label: [
"text-xs",
"group-focus-within:text-[0.6rem]",
"group-focus-within:-translate-y-2",
"group-[.is-filled]:text-[0.6rem]",
"group-[.is-filled]:-translate-y-2",
],
input: "pt-4",
},
},
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: ["sm", "md"],
class: {
label: ["text-sm", "group-focus-within:text-xs", "group-[.is-filled]:text-xs"],
input: "pt-4",
},
},
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: "sm",
class: {
label: ["group-focus-within:-translate-y-2.5", "group-[.is-filled]:-translate-y-2.5"],
input: "pt-4",
},
},
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: "md",
class: {
label: ["group-focus-within:-translate-y-3", "group-[.is-filled]:-translate-y-3"],
input: "pt-4",
},
},
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: "lg",
class: {
label: [
"text-base",
"group-focus-within:text-sm",
"group-focus-within:-translate-y-3",
"group-[.is-filled]:text-sm",
"group-[.is-filled]:-translate-y-3",
],
input: "pt-6",
},
},
{
isLabelPlaceholder: true,
labelPosition: "inside",
size: "xl",
class: {
label: [
"text-md",
"group-focus-within:text-sm",
"group-focus-within:-translate-y-3",
"group-[.is-filled]:text-sm",
"group-[.is-filled]:-translate-y-3",
],
input: "pt-8",
},
},
],
});
export type InputVariantProps = VariantProps<typeof input>;
export type InputSlots = keyof ReturnType<typeof input>;
export {input};

41
pnpm-lock.yaml generated
View File

@ -831,10 +831,22 @@ importers:
'@nextui-org/theme':
specifier: workspace:*
version: link:../../core/theme
'@react-aria/textfield':
specifier: ^3.9.0
version: 3.9.0(react@18.2.0)
'@nextui-org/use-aria-field':
specifier: workspace:*
version: link:../../hooks/use-aria-field
'@react-aria/focus':
specifier: ^3.11.0
version: 3.11.0(react@18.2.0)
'@react-aria/utils':
specifier: ^3.15.0
version: 3.15.0(react@18.2.0)
'@react-stately/utils':
specifier: ^3.6.0
version: 3.6.0(react@18.2.0)
devDependencies:
'@react-types/shared':
specifier: ^3.15.0
version: 3.17.0(react@18.2.0)
'@react-types/textfield':
specifier: ^3.7.0
version: 3.7.0(react@18.2.0)
@ -1303,8 +1315,8 @@ importers:
specifier: ^4.4.0
version: 4.4.0
tailwind-variants:
specifier: ^0.1.1
version: 0.1.1(tailwindcss@3.2.7)
specifier: ^0.1.2
version: 0.1.2(tailwindcss@3.2.7)
tailwindcss:
specifier: ^3.2.7
version: 3.2.7(postcss@8.4.21)(ts-node@10.9.1)
@ -5910,20 +5922,6 @@ packages:
react: 18.2.0
dev: false
/@react-aria/textfield@3.9.0(react@18.2.0):
resolution: {integrity: sha512-plX+/RDidTpz4kfQni2mnH10g9iARC5P7oi4XBXqwrVCIqpTUNoyGLUH952wObYOI9k7lG2QG0+b+3GyrV159g==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-aria/focus': 3.11.0(react@18.2.0)
'@react-aria/label': 3.5.0(react@18.2.0)
'@react-aria/utils': 3.15.0(react@18.2.0)
'@react-types/shared': 3.17.0(react@18.2.0)
'@react-types/textfield': 3.7.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
dev: false
/@react-aria/toggle@3.5.0(react@18.2.0):
resolution: {integrity: sha512-K49OmHBmYW8pk0rXJU1TNRzR+PxLVvfL/ni6ifV5gcxoxV6DmFsNFj+5B/U3AMnCEQeyKQeiY6z9X7EBVX6j9Q==}
peerDependencies:
@ -6220,6 +6218,7 @@ packages:
dependencies:
'@react-types/shared': 3.17.0(react@18.2.0)
react: 18.2.0
dev: true
/@react-types/tooltip@3.3.0(react@18.2.0):
resolution: {integrity: sha512-TMaKkjYbysZbMnY8zjI2Djh8QMHvB8LoN9EjOZX++3ZsO74CeCeOoGARh6+4c0Bu5rg4BXhNyi+y1JL3jtUEBg==}
@ -21550,8 +21549,8 @@ packages:
resolution: {integrity: sha512-oG3/328Y7LrfPMfkgMNxoqEk1ZQdXBxdphf9FFrreo8q0EtVIHt3bQf2IyFhQuVt8puB57lCRBNbazGhYAyz9w==}
dev: false
/tailwind-variants@0.1.1(tailwindcss@3.2.7):
resolution: {integrity: sha512-kW9kXmVv9ankGydv36pg9M7fwgQfN/DZ3JlqFfmuIDPRa1/psm3KbTcB4vgEBiku7VtGQBm6oy3EsTJUYJKsLg==}
/tailwind-variants@0.1.2(tailwindcss@3.2.7):
resolution: {integrity: sha512-MOTaBS5Hg7RN/M0tlMZCUD1crDGPlHe7XsFS7+I5bc4hfwQPVuTiPiRqivW9xH/0QvtuVo8AlEwvR2uxSC3phw==}
engines: {node: '>=16.x', pnpm: '>=7.x'}
peerDependencies:
tailwindcss: '*'