mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(input): data management improved
This commit is contained in:
parent
da5aa53a6c
commit
8ce412c508
@ -5,7 +5,7 @@ import {AriaTextFieldProps} from "@react-types/textfield";
|
|||||||
import {useFocusRing} from "@react-aria/focus";
|
import {useFocusRing} from "@react-aria/focus";
|
||||||
import {input} from "@nextui-org/theme";
|
import {input} from "@nextui-org/theme";
|
||||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||||
import {usePress} from "@react-aria/interactions";
|
import {useHover, usePress} from "@react-aria/interactions";
|
||||||
import {clsx, dataAttr} from "@nextui-org/shared-utils";
|
import {clsx, dataAttr} from "@nextui-org/shared-utils";
|
||||||
import {useControlledState} from "@react-stately/utils";
|
import {useControlledState} from "@react-stately/utils";
|
||||||
import {useMemo, Ref, RefObject} from "react";
|
import {useMemo, Ref, RefObject} from "react";
|
||||||
@ -115,6 +115,8 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
isTextInput: true,
|
isTextInput: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {isHovered, hoverProps} = useHover({isDisabled: !!originalProps?.isDisabled});
|
||||||
|
|
||||||
const {focusProps: clearFocusProps, isFocusVisible: isClearButtonFocusVisible} = useFocusRing();
|
const {focusProps: clearFocusProps, isFocusVisible: isClearButtonFocusVisible} = useFocusRing();
|
||||||
|
|
||||||
const {pressProps: clearPressProps} = usePress({
|
const {pressProps: clearPressProps} = usePress({
|
||||||
@ -138,19 +140,9 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
...variantProps,
|
...variantProps,
|
||||||
isInvalid,
|
isInvalid,
|
||||||
isClearable,
|
isClearable,
|
||||||
isFocusVisible,
|
|
||||||
isLabelPlaceholder: isLabelPlaceholder && !hasStartContent,
|
isLabelPlaceholder: isLabelPlaceholder && !hasStartContent,
|
||||||
isClearButtonFocusVisible,
|
|
||||||
}),
|
}),
|
||||||
[
|
[...Object.values(variantProps), isInvalid, isClearable, isLabelPlaceholder, hasStartContent],
|
||||||
...Object.values(variantProps),
|
|
||||||
isInvalid,
|
|
||||||
isClearable,
|
|
||||||
isClearButtonFocusVisible,
|
|
||||||
isLabelPlaceholder,
|
|
||||||
hasStartContent,
|
|
||||||
isFocusVisible,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getBaseProps: PropGetter = (props = {}) => {
|
const getBaseProps: PropGetter = (props = {}) => {
|
||||||
@ -159,6 +151,7 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
"data-focus-visible": dataAttr(isFocusVisible),
|
"data-focus-visible": dataAttr(isFocusVisible),
|
||||||
"data-focused": dataAttr(isFocused),
|
"data-focused": dataAttr(isFocused),
|
||||||
"data-invalid": dataAttr(isInvalid),
|
"data-invalid": dataAttr(isInvalid),
|
||||||
|
|
||||||
...props,
|
...props,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -175,9 +168,6 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
return {
|
return {
|
||||||
ref: domRef,
|
ref: domRef,
|
||||||
className: slots.input({class: clsx(classNames?.input, !!inputValue ? "is-filled" : "")}),
|
className: slots.input({class: clsx(classNames?.input, !!inputValue ? "is-filled" : "")}),
|
||||||
"data-focus-visible": dataAttr(isFocusVisible),
|
|
||||||
"data-focused": dataAttr(isFocused),
|
|
||||||
"data-invalid": dataAttr(isInvalid),
|
|
||||||
...mergeProps(focusProps, inputProps, filterDOMProps(otherProps, {labelable: true}), props),
|
...mergeProps(focusProps, inputProps, filterDOMProps(otherProps, {labelable: true}), props),
|
||||||
onChange: chain(inputProps.onChange, onChange),
|
onChange: chain(inputProps.onChange, onChange),
|
||||||
};
|
};
|
||||||
@ -185,6 +175,7 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
|
|
||||||
const getInputWrapperProps: PropGetter = (props = {}) => {
|
const getInputWrapperProps: PropGetter = (props = {}) => {
|
||||||
return {
|
return {
|
||||||
|
"data-hover": dataAttr(isHovered),
|
||||||
className: slots.inputWrapper({
|
className: slots.inputWrapper({
|
||||||
class: clsx(classNames?.inputWrapper, !!inputValue ? "is-filled" : ""),
|
class: clsx(classNames?.inputWrapper, !!inputValue ? "is-filled" : ""),
|
||||||
}),
|
}),
|
||||||
@ -193,7 +184,7 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
domRef.current?.focus();
|
domRef.current?.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...props,
|
...mergeProps(props, hoverProps),
|
||||||
style: {
|
style: {
|
||||||
cursor: "text",
|
cursor: "text",
|
||||||
...props.style,
|
...props.style,
|
||||||
@ -231,6 +222,7 @@ export function useInput(originalProps: UseInputProps) {
|
|||||||
role: "button",
|
role: "button",
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
className: slots.clearButton({class: classNames?.clearButton}),
|
className: slots.clearButton({class: classNames?.clearButton}),
|
||||||
|
"data-focus-visible": dataAttr(isClearButtonFocusVisible),
|
||||||
...mergeProps(clearPressProps, clearFocusProps),
|
...mergeProps(clearPressProps, clearFocusProps),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {modal} from "@nextui-org/theme";
|
|||||||
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
|
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
|
||||||
import {useAriaButton} from "@nextui-org/use-aria-button";
|
import {useAriaButton} from "@nextui-org/use-aria-button";
|
||||||
import {useFocusRing} from "@react-aria/focus";
|
import {useFocusRing} from "@react-aria/focus";
|
||||||
import {clsx, ReactRef} from "@nextui-org/shared-utils";
|
import {clsx, dataAttr, ReactRef} from "@nextui-org/shared-utils";
|
||||||
import {useOverlayTrigger} from "@react-aria/overlays";
|
import {useOverlayTrigger} from "@react-aria/overlays";
|
||||||
import {createDOMRef} from "@nextui-org/dom-utils";
|
import {createDOMRef} from "@nextui-org/dom-utils";
|
||||||
import {useOverlayTriggerState} from "@react-stately/overlays";
|
import {useOverlayTriggerState} from "@react-stately/overlays";
|
||||||
@ -148,9 +148,8 @@ export function useModal(originalProps: UseModalProps) {
|
|||||||
() =>
|
() =>
|
||||||
modal({
|
modal({
|
||||||
...variantProps,
|
...variantProps,
|
||||||
isCloseButtonFocusVisible,
|
|
||||||
}),
|
}),
|
||||||
[...Object.values(variantProps), isCloseButtonFocusVisible],
|
[...Object.values(variantProps)],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDialogProps: PropGetter = (props = {}, ref = null) => ({
|
const getDialogProps: PropGetter = (props = {}, ref = null) => ({
|
||||||
@ -158,6 +157,7 @@ export function useModal(originalProps: UseModalProps) {
|
|||||||
...mergeProps(modalProps, otherProps, props),
|
...mergeProps(modalProps, otherProps, props),
|
||||||
className: slots.base({class: clsx(baseStyles, props.className)}),
|
className: slots.base({class: clsx(baseStyles, props.className)}),
|
||||||
id: dialogId,
|
id: dialogId,
|
||||||
|
"data-open": dataAttr(state.isOpen),
|
||||||
"aria-modal": true,
|
"aria-modal": true,
|
||||||
"aria-labelledby": headerMounted ? headerId : undefined,
|
"aria-labelledby": headerMounted ? headerId : undefined,
|
||||||
"aria-describedby": bodyMounted ? bodyId : undefined,
|
"aria-describedby": bodyMounted ? bodyId : undefined,
|
||||||
@ -186,6 +186,7 @@ export function useModal(originalProps: UseModalProps) {
|
|||||||
role: "button",
|
role: "button",
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
"aria-label": "Close",
|
"aria-label": "Close",
|
||||||
|
"data-focus-visible": dataAttr(isCloseButtonFocusVisible),
|
||||||
className: slots.closeButton({class: classNames?.closeButton}),
|
className: slots.closeButton({class: classNames?.closeButton}),
|
||||||
...mergeProps(closeButtonProps, closeButtonFocusProps),
|
...mergeProps(closeButtonProps, closeButtonFocusProps),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import type {VariantProps} from "tailwind-variants";
|
|||||||
|
|
||||||
import {tv} from "tailwind-variants";
|
import {tv} from "tailwind-variants";
|
||||||
|
|
||||||
import {ringClasses} from "../utils";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input wrapper **Tailwind Variants** component
|
* Input wrapper **Tailwind Variants** component
|
||||||
*
|
*
|
||||||
@ -24,7 +22,7 @@ import {ringClasses} from "../utils";
|
|||||||
*/
|
*/
|
||||||
const input = tv({
|
const input = tv({
|
||||||
slots: {
|
slots: {
|
||||||
base: "flex flex-col gap-2",
|
base: "group flex flex-col gap-2",
|
||||||
label: "block text-sm font-medium text-neutral-600",
|
label: "block text-sm font-medium text-neutral-600",
|
||||||
inputWrapper: "relative w-full inline-flex flex-row items-center shadow-sm px-3 gap-3",
|
inputWrapper: "relative w-full inline-flex flex-row items-center shadow-sm px-3 gap-3",
|
||||||
innerWrapper: "inline-flex h-full items-center w-full gap-1.5 box-border",
|
innerWrapper: "inline-flex h-full items-center w-full gap-1.5 box-border",
|
||||||
@ -42,6 +40,13 @@ const input = tv({
|
|||||||
"cursor-pointer",
|
"cursor-pointer",
|
||||||
"active:!opacity-70",
|
"active:!opacity-70",
|
||||||
"rounded-full",
|
"rounded-full",
|
||||||
|
// focus ring
|
||||||
|
"data-[focus-visible]:outline-none",
|
||||||
|
"data-[focus-visible]:ring-2",
|
||||||
|
"data-[focus-visible]:!ring-primary",
|
||||||
|
"data-[focus-visible]:ring-offset-2",
|
||||||
|
"data-[focus-visible]:ring-offset-background",
|
||||||
|
"data-[focus-visible]:dark:ring-offset-background-dark",
|
||||||
],
|
],
|
||||||
description: "text-xs text-neutral-500",
|
description: "text-xs text-neutral-500",
|
||||||
errorMessage: "text-xs text-danger",
|
errorMessage: "text-xs text-danger",
|
||||||
@ -49,21 +54,25 @@ const input = tv({
|
|||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
flat: {
|
flat: {
|
||||||
inputWrapper: ["bg-neutral-100", "hover:bg-neutral-200", "focus-within:!bg-neutral-100"],
|
inputWrapper: [
|
||||||
|
"bg-neutral-100",
|
||||||
|
"data-[hover=true]:bg-neutral-200",
|
||||||
|
"focus-within:!bg-neutral-100",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
faded: {
|
faded: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-neutral-100",
|
"bg-neutral-100",
|
||||||
"border",
|
"border",
|
||||||
"border-neutral-200",
|
"border-neutral-200",
|
||||||
"hover:border-neutral-400",
|
"data-[hover=true]:border-neutral-400",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
bordered: {
|
bordered: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"border-2",
|
"border-2",
|
||||||
"border-neutral-200",
|
"border-neutral-200",
|
||||||
"hover:border-neutral-400",
|
"data-[hover=true]:border-neutral-400",
|
||||||
"focus-within:!border-foreground",
|
"focus-within:!border-foreground",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -197,14 +206,6 @@ const input = tv({
|
|||||||
base: "opacity-50 pointer-events-none",
|
base: "opacity-50 pointer-events-none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isFocusVisible: {
|
|
||||||
true: {},
|
|
||||||
},
|
|
||||||
isClearButtonFocusVisible: {
|
|
||||||
true: {
|
|
||||||
clearButton: [...ringClasses],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isInvalid: {
|
isInvalid: {
|
||||||
true: {
|
true: {
|
||||||
label: "!text-danger",
|
label: "!text-danger",
|
||||||
@ -259,7 +260,7 @@ const input = tv({
|
|||||||
class: {
|
class: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-primary-50",
|
"bg-primary-50",
|
||||||
"hover:bg-primary-100",
|
"data-[hover=true]:bg-primary-100",
|
||||||
"text-primary",
|
"text-primary",
|
||||||
"focus-within:!bg-primary-50",
|
"focus-within:!bg-primary-50",
|
||||||
"placeholder:text-primary",
|
"placeholder:text-primary",
|
||||||
@ -273,7 +274,7 @@ const input = tv({
|
|||||||
class: {
|
class: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-secondary-50",
|
"bg-secondary-50",
|
||||||
"hover:bg-secondary-100",
|
"data-[hover=true]:bg-secondary-100",
|
||||||
"text-secondary",
|
"text-secondary",
|
||||||
"focus-within:!bg-secondary-50",
|
"focus-within:!bg-secondary-50",
|
||||||
"placeholder:text-secondary",
|
"placeholder:text-secondary",
|
||||||
@ -287,7 +288,7 @@ const input = tv({
|
|||||||
class: {
|
class: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-success-50",
|
"bg-success-50",
|
||||||
"hover:bg-success-100",
|
"data-[hover=true]:bg-success-100",
|
||||||
"text-success",
|
"text-success",
|
||||||
"focus-within:!bg-success-50",
|
"focus-within:!bg-success-50",
|
||||||
"placeholder:text-success",
|
"placeholder:text-success",
|
||||||
@ -301,7 +302,7 @@ const input = tv({
|
|||||||
class: {
|
class: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-warning-50",
|
"bg-warning-50",
|
||||||
"hover:bg-warning-100",
|
"data-[hover=true]:bg-warning-100",
|
||||||
"text-warning",
|
"text-warning",
|
||||||
"focus-within:!bg-warning-50",
|
"focus-within:!bg-warning-50",
|
||||||
"placeholder:text-warning",
|
"placeholder:text-warning",
|
||||||
@ -315,7 +316,7 @@ const input = tv({
|
|||||||
class: {
|
class: {
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"bg-danger-50",
|
"bg-danger-50",
|
||||||
"hover:bg-danger-100",
|
"data-[hover=true]:bg-danger-100",
|
||||||
"text-danger",
|
"text-danger",
|
||||||
"focus-within:!bg-danger-50",
|
"focus-within:!bg-danger-50",
|
||||||
"placeholder:text-danger",
|
"placeholder:text-danger",
|
||||||
@ -329,7 +330,7 @@ const input = tv({
|
|||||||
color: "primary",
|
color: "primary",
|
||||||
class: {
|
class: {
|
||||||
label: "text-primary",
|
label: "text-primary",
|
||||||
inputWrapper: "hover:border-primary focus-within:border-primary",
|
inputWrapper: "data-[hover=true]:border-primary focus-within:border-primary",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -337,7 +338,7 @@ const input = tv({
|
|||||||
color: "secondary",
|
color: "secondary",
|
||||||
class: {
|
class: {
|
||||||
label: "text-secondary",
|
label: "text-secondary",
|
||||||
inputWrapper: "hover:border-secondary focus-within:border-secondary",
|
inputWrapper: "data-[hover=true]:border-secondary focus-within:border-secondary",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -345,7 +346,7 @@ const input = tv({
|
|||||||
color: "success",
|
color: "success",
|
||||||
class: {
|
class: {
|
||||||
label: "text-success",
|
label: "text-success",
|
||||||
inputWrapper: "hover:border-success focus-within:border-success",
|
inputWrapper: "data-[hover=true]:border-success focus-within:border-success",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -353,7 +354,7 @@ const input = tv({
|
|||||||
color: "warning",
|
color: "warning",
|
||||||
class: {
|
class: {
|
||||||
label: "text-warning",
|
label: "text-warning",
|
||||||
inputWrapper: "hover:border-warning focus-within:border-warning",
|
inputWrapper: "data-[hover=true]:border-warning focus-within:border-warning",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -361,7 +362,7 @@ const input = tv({
|
|||||||
color: "danger",
|
color: "danger",
|
||||||
class: {
|
class: {
|
||||||
label: "text-danger",
|
label: "text-danger",
|
||||||
inputWrapper: "hover:border-danger focus-within:border-danger",
|
inputWrapper: "data-[hover=true]:border-danger focus-within:border-danger",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// underlined & color
|
// underlined & color
|
||||||
@ -480,12 +481,19 @@ const input = tv({
|
|||||||
inputWrapper: "after:transition-width motion-reduce:after:transition-none",
|
inputWrapper: "after:transition-width motion-reduce:after:transition-none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// isFocusVisible & variant
|
// flat & faded
|
||||||
{
|
{
|
||||||
isFocusVisible: true,
|
|
||||||
variant: ["flat", "faded"],
|
variant: ["flat", "faded"],
|
||||||
class: {
|
class: {
|
||||||
inputWrapper: [...ringClasses],
|
inputWrapper: [
|
||||||
|
// focus ring
|
||||||
|
"group-data-[focus-visible]:outline-none",
|
||||||
|
"group-data-[focus-visible]:ring-2",
|
||||||
|
"group-data-[focus-visible]:!ring-primary",
|
||||||
|
"group-data-[focus-visible]:ring-offset-2",
|
||||||
|
"group-data-[focus-visible]:ring-offset-background",
|
||||||
|
"group-data-[focus-visible]:dark:ring-offset-background-dark",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// isInvalid & variant
|
// isInvalid & variant
|
||||||
@ -493,7 +501,11 @@ const input = tv({
|
|||||||
isInvalid: true,
|
isInvalid: true,
|
||||||
variant: "flat",
|
variant: "flat",
|
||||||
class: {
|
class: {
|
||||||
inputWrapper: ["bg-danger-50", "hover:bg-danger-100", "focus-within:!bg-danger-50"],
|
inputWrapper: [
|
||||||
|
"bg-danger-50",
|
||||||
|
"data-[hover=true]:bg-danger-100",
|
||||||
|
"focus-within:!bg-danger-50",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import type {VariantProps} from "tailwind-variants";
|
|||||||
|
|
||||||
import {tv} from "tailwind-variants";
|
import {tv} from "tailwind-variants";
|
||||||
|
|
||||||
import {ringClasses} from "../utils";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Card **Tailwind Variants** component
|
* Card **Tailwind Variants** component
|
||||||
*
|
*
|
||||||
@ -66,6 +64,13 @@ const modal = tv({
|
|||||||
"rounded-full",
|
"rounded-full",
|
||||||
"hover:bg-neutral-100",
|
"hover:bg-neutral-100",
|
||||||
"active:bg-neutral-200",
|
"active:bg-neutral-200",
|
||||||
|
// focus ring
|
||||||
|
"data-[focus-visible]:outline-none",
|
||||||
|
"data-[focus-visible]:ring-2",
|
||||||
|
"data-[focus-visible]:!ring-primary",
|
||||||
|
"data-[focus-visible]:ring-offset-2",
|
||||||
|
"data-[focus-visible]:ring-offset-background",
|
||||||
|
"data-[focus-visible]:dark:ring-offset-background-dark",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
@ -130,11 +135,6 @@ const modal = tv({
|
|||||||
base: "my-16",
|
base: "my-16",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isCloseButtonFocusVisible: {
|
|
||||||
true: {
|
|
||||||
closeButton: [...ringClasses],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
size: "md",
|
size: "md",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user