feat: checkbox group migrated, only tests missing

This commit is contained in:
Junior Garcia 2023-03-04 18:00:18 -03:00
parent 77f2b0950e
commit c302fa6e73
18 changed files with 516 additions and 965 deletions

View File

@ -1,18 +1,22 @@
import type {ButtonProps} from "./index";
import type {ReactRef} from "@nextui-org/shared-utils";
import type {ButtonGroupVariantProps} from "@nextui-org/theme";
import type {AriaButtonProps} from "@react-types/button";
import {buttonGroup} from "@nextui-org/theme";
import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {useMemo, useCallback} from "react";
export interface UseButtonGroupProps
extends HTMLNextUIProps<"div", Omit<ButtonProps, "ref" | "fullWidth">>,
ButtonGroupVariantProps {
interface Props extends HTMLNextUIProps<"div">, ButtonGroupVariantProps {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLDivElement | null>;
/**
* Whether the buttons are disabled.
* @default false
*/
isDisabled?: ButtonProps["isDisabled"];
}
export type ContextType = {
@ -26,6 +30,9 @@ export type ContextType = {
fullWidth?: boolean;
};
export type UseButtonGroupProps = Props &
Omit<ButtonProps, "ref" | "fullWidth" | keyof AriaButtonProps>;
export function useButtonGroup(originalProps: UseButtonGroupProps) {
const [props, variantProps] = mapPropsVariants(originalProps, buttonGroup.variantKeys);

View File

@ -2,15 +2,15 @@ import * as React from "react";
import {render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {Checkbox} from "../src";
import {CheckboxGroup, Checkbox} from "../src";
describe("Checkbox.Group", () => {
it("should render correctly", () => {
const wrapper = render(
<Checkbox.Group defaultValue={[]} label="Select cities">
<CheckboxGroup defaultValue={[]} label="Select cities">
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
expect(() => wrapper.unmount()).not.toThrow();
@ -20,10 +20,10 @@ describe("Checkbox.Group", () => {
const ref = React.createRef<HTMLDivElement>();
render(
<Checkbox.Group ref={ref} defaultValue={[]} label="Select cities">
<CheckboxGroup ref={ref} defaultValue={[]} label="Select cities">
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
expect(ref.current).not.toBeNull();
@ -31,14 +31,14 @@ describe("Checkbox.Group", () => {
it("should work correctly with initial value", () => {
const {container} = render(
<Checkbox.Group defaultValue={["sydney"]} label="Select cities">
<CheckboxGroup defaultValue={["sydney"]} label="Select cities">
<Checkbox data-testid="first-checkbox" value="sydney">
Sydney
</Checkbox>
<Checkbox data-testid="second-checkbox" value="buenos-aires">
Buenos Aires
</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
// check if the first checkbox is checked
@ -51,7 +51,7 @@ describe("Checkbox.Group", () => {
it("should change value after click", () => {
let value = ["sydney"];
const {container} = render(
<Checkbox.Group
<CheckboxGroup
defaultValue={["sydney"]}
label="Select cities"
onChange={(val) => (value = val)}
@ -62,7 +62,7 @@ describe("Checkbox.Group", () => {
<Checkbox data-testid="second-checkbox" value="buenos-aires">
Buenos Aires
</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
const firstCheckbox = container.querySelector("[data-testid=first-checkbox] input");
@ -81,14 +81,14 @@ describe("Checkbox.Group", () => {
it("should ignore events when disabled", () => {
const {container} = render(
<Checkbox.Group isDisabled defaultValue={["sydney"]} label="Select cities">
<CheckboxGroup isDisabled defaultValue={["sydney"]} label="Select cities">
<Checkbox data-testid="first-checkbox" value="sydney">
Sydney
</Checkbox>
<Checkbox data-testid="second-checkbox" value="buenos-aires">
Buenos Aires
</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
const firstCheckbox = container.querySelector("[data-testid=first-checkbox] input");
@ -110,14 +110,14 @@ describe("Checkbox.Group", () => {
});
const {container} = render(
<Checkbox.Group label="Select cities" value={checked} onChange={onChange}>
<CheckboxGroup label="Select cities" value={checked} onChange={onChange}>
<Checkbox data-testid="first-checkbox" value="sydney">
Sydney
</Checkbox>
<Checkbox data-testid="second-checkbox" value="buenos-aires">
Buenos Aires
</Checkbox>
</Checkbox.Group>,
</CheckboxGroup>,
);
const secondCheckbox = container.querySelector("[data-testid=second-checkbox] input");

View File

@ -2,7 +2,7 @@ import * as React from "react";
import {render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {Checkbox} from "../src";
import {Checkbox, CheckboxProps} from "../src";
describe("Checkbox", () => {
it("should render correctly", () => {
@ -14,30 +14,33 @@ describe("Checkbox", () => {
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLLabelElement>();
render(<Checkbox ref={ref} label="checkbox-test" />);
render(<Checkbox ref={ref}>Option</Checkbox>);
expect(ref.current).not.toBeNull();
});
it("should work correctly with initial value", () => {
let {container} = render(<Checkbox isSelected label="checkbox-test" />);
let {container} = render(<Checkbox isSelected>Option</Checkbox>);
expect(container.querySelector("input")?.checked).toBe(true);
container = render(<Checkbox isSelected={false} label="checkbox-test" />).container;
container = render(<Checkbox isSelected={false}>Option</Checkbox>).container;
expect(container.querySelector("input")?.checked).toBe(false);
});
it("should change value after click", () => {
const {container} = render(<Checkbox label="checkbox-test" />);
const wrapper = render(<Checkbox data-testid="checkbox-test">Option</Checkbox>);
const checkbox = wrapper.container.querySelector("input")!;
userEvent.click(container.querySelector("label")!);
expect(checkbox.checked).toBe(false);
expect(container.querySelector("input")?.checked).toBe(true);
wrapper.getByTestId("checkbox-test").click();
expect(checkbox.checked).toBe(true);
});
it("should ignore events when disabled", () => {
const {container} = render(<Checkbox isDisabled label="checkbox-test" />);
const {container} = render(<Checkbox isDisabled>Option</Checkbox>);
userEvent.click(container.querySelector("label")!);
@ -45,59 +48,74 @@ describe("Checkbox", () => {
});
it("should work correctly with indeterminate value", () => {
const {container} = render(<Checkbox isIndeterminate label="checkbox-test" />);
const {container} = render(<Checkbox isIndeterminate>Option</Checkbox>);
expect(container.querySelector("input")?.indeterminate).toBe(true);
});
it('should work correctly with "onChange" prop', () => {
const onChange = jest.fn();
const {container} = render(<Checkbox label="checkbox-test" onChange={onChange} />);
const wrapper = render(
<Checkbox data-testid="checkbox-test" onChange={onChange}>
Option
</Checkbox>,
);
userEvent.click(container.querySelector("label")!);
wrapper.getByTestId("checkbox-test").click();
expect(onChange).toBeCalled();
});
it('should work correctly with "onFocus" prop', () => {
const onFocus = jest.fn();
const {container} = render(<Checkbox label="checkbox-test" onFocus={onFocus} />);
userEvent.click(container.querySelector("label")!);
const wrapper = render(
<Checkbox data-testid="checkbox-test" onFocus={onFocus}>
Option
</Checkbox>,
);
wrapper.getByTestId("checkbox-test").focus();
expect(onFocus).toBeCalled();
});
it('should work correctly with "isRequired" prop', () => {
const {container} = render(<Checkbox isRequired label="checkbox-test" />);
const {container} = render(<Checkbox isRequired>Option</Checkbox>);
expect(container.querySelector("input")?.required).toBe(true);
});
// it("should work correctly with controlled value", () => {
// const onChange = jest.fn();
it("should work correctly with controlled value", () => {
const onChange = jest.fn();
// const Component = (props: CheckboxProps) => {
// const [value, setValue] = React.useState(false);
const Component = (props: CheckboxProps) => {
const [value, setValue] = React.useState(false);
// return (
// <Checkbox
// {...props}
// isSelected={value}
// onChange={(checked) => {
// setValue(checked);
// onChange(checked);
// }}
// />
// );
// };
return (
<Checkbox
{...props}
isSelected={value}
onChange={(checked) => {
setValue(checked);
onChange(checked);
}}
/>
);
};
// const {container} = render(<Component label="checkbox-test" onChange={onChange} />);
const wrapper = render(
<Component data-testid="checkbox-test" onChange={onChange}>
Option
</Component>,
);
// userEvent.click(container.querySelector("label")!);
wrapper.getByTestId("checkbox-test").click();
// expect(onChange).toBeCalled();
const input = wrapper.container.querySelector("input")!;
// expect(container.querySelector("input")?.getAttribute("aria-checked")).toBe("true");
// });
expect(onChange).toBeCalled();
expect(input?.getAttribute("aria-checked")).toBe("true");
});
});

View File

@ -1,64 +0,0 @@
import {styled} from "@nextui-org/system";
import {StyledCheckbox} from "./checkbox.styles";
export const StyledCheckboxGroupContainer = styled("div", {
display: "flex",
flexDirection: "column",
variants: {
isRow: {
true: {
flexDirection: "row",
mt: 0,
[`& ${StyledCheckbox}`]: {
mr: "$$checkboxSize",
},
},
false: {
mr: 0,
flexDirection: "column",
[`& ${StyledCheckbox}:not(:first-child)`]: {
mt: "$$checkboxSize",
},
},
},
},
});
export const StyledCheckboxGroup = styled("div", {
display: "flex",
flexDirection: "column",
"& .nextui-checkbox-group-label": {
d: "block",
fontWeight: "$normal",
fontSize: "calc($$checkboxSize * 0.8)",
color: "$accents7",
mb: "$3",
},
variants: {
size: {
xs: {
$$checkboxSize: "$space$7",
},
sm: {
$$checkboxSize: "$space$8",
},
md: {
$$checkboxSize: "$space$9",
},
lg: {
$$checkboxSize: "$space$10",
},
xl: {
$$checkboxSize: "$space$11",
},
},
isDisabled: {
true: {
"& .nextui-checkbox-group-label": {
color: "$accents5",
},
},
},
},
});

View File

@ -1,39 +1,22 @@
import {forwardRef} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__} from "@nextui-org/shared-utils";
import {mergeProps} from "@react-aria/utils";
import {__DEV__} from "@nextui-org/shared-utils";
import {CheckboxGroupProvider} from "./checkbox-group-context";
import {StyledCheckboxGroup, StyledCheckboxGroupContainer} from "./checkbox-group.styles";
import {UseCheckboxGroupProps, useCheckboxGroup} from "./use-checkbox-group";
export interface CheckboxGroupProps extends UseCheckboxGroupProps {}
export interface CheckboxGroupProps extends Omit<UseCheckboxGroupProps, "ref"> {}
const CheckboxGroup = forwardRef<CheckboxGroupProps, "div">((props, ref) => {
const domRef = useDOMRef(ref);
const {children, orientation, groupProps, labelProps, label, context, className, ...otherProps} =
useCheckboxGroup(props);
const {children, context, label, getGroupProps, getLabelProps, getWrapperProps} =
useCheckboxGroup({ref, ...props});
return (
<StyledCheckboxGroup
ref={domRef}
className={clsx("nextui-checkbox-group", className)}
{...mergeProps(groupProps, otherProps)}
>
{label && (
<label className="nextui-checkbox-group-label" {...labelProps}>
{label}
</label>
)}
<StyledCheckboxGroupContainer
className="nextui-checkbox-group-items"
isRow={orientation === "horizontal"}
role="presentation"
>
<div {...getGroupProps()}>
{label && <label {...getLabelProps()}>{label}</label>}
<div {...getWrapperProps()}>
<CheckboxGroupProvider value={context}>{children}</CheckboxGroupProvider>
</StyledCheckboxGroupContainer>
</StyledCheckboxGroup>
</div>
</div>
);
});
@ -41,6 +24,4 @@ if (__DEV__) {
CheckboxGroup.displayName = "NextUI.CheckboxGroup";
}
CheckboxGroup.toString = () => ".nextui-checkbox-group";
export default CheckboxGroup;

View File

@ -0,0 +1,46 @@
import type {UseCheckboxReturn} from "./use-checkbox";
type CheckboxIconProps = Partial<ReturnType<UseCheckboxReturn["getIconProps"]>>;
function CheckIcon(props: CheckboxIconProps) {
return (
<svg aria-hidden="true" role="presentation" viewBox="0 0 17 18" {...props}>
<polyline
fill="none"
points="1 9 7 14 15 4"
stroke="currentColor"
strokeDasharray={22}
strokeDashoffset={props.isSelected ? 44 : 66}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
style={
!props.disableAnimation
? {
transition: "stroke-dashoffset 250ms ease 0s",
transitionDelay: "250ms",
}
: {}
}
/>
</svg>
);
}
function IndeterminateIcon(props: CheckboxIconProps) {
return (
<svg stroke="currentColor" strokeWidth={3} viewBox="0 0 24 24" {...props}>
<line x1="21" x2="3" y1="12" y2="12" />
</svg>
);
}
/**
* CheckboxIcon is used to visually indicate the checked or indeterminate
* state of a checkbox.
*/
export function CheckboxIcon(props: CheckboxIconProps) {
const BaseIcon = props.isIndeterminate ? IndeterminateIcon : CheckIcon;
return <BaseIcon {...props} />;
}

View File

@ -1,544 +0,0 @@
// import {styled} from "@nextui-org/system";
// import {cssFocusVisible} from "@nextui-org/shared-css";
// export const StyledCheckbox = styled("label", {
// $$checkboxBorderColor: "$colors$border",
// $$checkboxBorderRadius: "$radii$squared",
// d: "inline-flex",
// jc: "flex-start",
// ai: "center",
// position: "relative",
// w: "auto",
// cursor: "pointer",
// zIndex: "$1",
// opacity: 1,
// transition: "opacity 0.25s ease",
// "@motion": {
// transition: "none",
// },
// variants: {
// size: {
// xs: {
// $$checkboxSize: "$space$7",
// },
// sm: {
// $$checkboxSize: "$space$8",
// },
// md: {
// $$checkboxSize: "$space$9",
// },
// lg: {
// $$checkboxSize: "$space$10",
// },
// xl: {
// $$checkboxSize: "$space$11",
// },
// },
// isDisabled: {
// true: {
// opacity: 0.75,
// cursor: "not-allowed",
// },
// },
// disableAnimation: {
// true: {
// transition: "none",
// },
// },
// },
// });
// export const StyledCheckboxMask = styled("div", {
// $$checkboxMaskTransition:
// "transform 0.25s ease 0s, opacity 0.25s ease 0s, background 0.25s ease 0s, border-color 0.25s ease 0s",
// size: "100%",
// position: "absolute",
// pe: "none",
// boxSizing: "border-box",
// dflex: "center",
// zIndex: "-$1",
// br: "inherit",
// color: "$$checkboxBorderColor",
// "&:before": {
// content: "",
// position: "absolute",
// top: "0px",
// left: "0px",
// size: "100%",
// br: "inherit",
// transition: "$$checkboxMaskTransition",
// zIndex: "-$1",
// border: "$borderWeights$normal solid currentColor",
// boxSizing: "border-box",
// },
// "&:after": {
// content: "",
// position: "absolute",
// top: "0px",
// left: "0px",
// size: "100%",
// bg: "$$checkboxColor",
// scale: 0.5,
// br: "inherit",
// opacity: 0,
// transition: "$$checkboxMaskTransition",
// zIndex: "-$1",
// },
// "@motion": {
// "&:before": {
// transition: "none",
// },
// "&:after": {
// transition: "none",
// },
// },
// variants: {
// isChecked: {
// true: {
// "&:before": {
// opacity: 0,
// scale: 1.2,
// },
// "&:after": {
// opacity: 1,
// scale: 1,
// },
// },
// },
// isIndeterminate: {
// true: {
// "&:before": {
// opacity: 0,
// scale: 1.2,
// },
// "&:after": {
// opacity: 1,
// scale: 1,
// },
// },
// },
// disableAnimation: {
// true: {
// "&:before": {
// transition: "none",
// },
// "&:after": {
// transition: "none",
// },
// },
// },
// },
// });
// export const StyledCheckboxText = styled("span", {
// position: "relative",
// dflex: "center",
// color: "$text",
// opacity: 1,
// pl: "calc($$checkboxSize * 0.57)",
// ln: "$$checkboxSize",
// fontSize: "$$checkboxSize",
// us: "none",
// transition: "opacity 0.25s ease 0s",
// "@motion": {
// transition: "none",
// "&:before": {
// transition: "none",
// },
// },
// variants: {
// color: {
// default: {
// color: "$text",
// },
// primary: {
// color: "$primary",
// },
// secondary: {
// color: "$secondary",
// },
// success: {
// color: "$success",
// },
// warning: {
// color: "$warning",
// },
// error: {
// color: "$error",
// },
// },
// lineThrough: {
// true: {
// "&:before": {
// content: "",
// position: "absolute",
// width: "0px",
// height: "2px",
// background: "$text",
// transition: "width 0.25s ease 0s",
// },
// },
// },
// isChecked: {
// true: {
// "&:before": {
// opacity: 0.8,
// },
// },
// },
// isDisabled: {
// true: {
// color: "$accents5",
// },
// },
// disableAnimation: {
// true: {
// transition: "none",
// "&:before": {
// transition: "none",
// },
// },
// },
// },
// compoundVariants: [
// {
// lineThrough: true,
// isChecked: true,
// css: {
// opacity: 0.6,
// "&:before": {
// w: "calc(100% - 10px)",
// },
// },
// },
// ],
// });
// export const StyledCheckboxContainer = styled(
// "div",
// {
// br: "$$checkboxBorderRadius",
// position: "relative",
// sizeMin: "$$checkboxSize",
// transition: "box-shadow 0.25s ease",
// zIndex: "$1",
// ".nextui-checkbox-input": {
// position: "absolute",
// width: "100%",
// height: "100%",
// top: "0px",
// left: "0px",
// margin: "0px",
// padding: "0px",
// opacity: 0,
// zIndex: "$1",
// cursor: "pointer",
// "&:disabled": {
// cursor: "not-allowed",
// },
// },
// "@motion": {
// transition: "none",
// },
// variants: {
// color: {
// default: {
// $$checkboxColor: "$colors$primary",
// $$checkboxColorHover: "$colors$primarySolidHover",
// },
// primary: {
// $$checkboxColor: "$colors$primary",
// $$checkboxColorHover: "$colors$primarySolidHover",
// },
// secondary: {
// $$checkboxColor: "$colors$secondary",
// $$checkboxColorHover: "$colors$secondarySolidHover",
// },
// success: {
// $$checkboxColor: "$colors$success",
// $$checkboxColorHover: "$colors$successSolidHover",
// },
// warning: {
// $$checkboxColor: "$colors$warning",
// $$checkboxColorHover: "$colors$warningSolidHover",
// },
// error: {
// $$checkboxColor: "$colors$error",
// $$checkboxColorHover: "$colors$errorSolidHover",
// },
// gradient: {
// $$checkboxColor: "$colors$gradient",
// $$checkboxColorHover: "$colors$gradient",
// },
// },
// isRounded: {
// true: {
// $$checkboxBorderRadius: "$radii$pill",
// },
// },
// isDisabled: {
// true: {
// opacity: 0.4,
// cursor: "not-allowed",
// },
// },
// disableAnimation: {
// true: {
// transition: "none",
// },
// },
// isHovered: {
// true: {
// [`& ${StyledCheckboxMask}:before`]: {
// bg: "$$checkboxBorderColor",
// border: "2px solid transparent",
// },
// [`& ${StyledCheckboxMask}:after`]: {
// bg: "$$checkboxColorHover",
// },
// },
// },
// },
// },
// cssFocusVisible,
// );
// export const StyledIconCheck = styled("i", {
// size: "$$checkboxSize",
// dflex: "center",
// br: "inherit",
// opacity: 0,
// zIndex: "$2",
// transition: "transform 0.35s ease",
// "&:after": {
// content: "",
// opacity: 0,
// position: "relative",
// width: "10px",
// height: "2px",
// br: "1px",
// background: "$white",
// display: "block",
// },
// "@motion": {
// transition: "none",
// "&:after": {
// transition: "none",
// },
// },
// variants: {
// isIndeterminate: {
// true: {
// opacity: 1,
// transform: "rotate(0deg)",
// width: "auto",
// height: "auto",
// margin: "0px",
// "&:after": {
// opacity: 1,
// },
// },
// false: {
// width: "8px",
// height: "14px",
// display: "block",
// position: "relative",
// marginTop: "-4px",
// },
// },
// size: {
// xs: {
// marginTop: "-2px",
// transform: "rotate(45deg) scale(0.5)",
// },
// sm: {
// marginTop: "-2px",
// transform: "rotate(45deg) scale(0.5)",
// },
// md: {
// transform: "rotate(45deg) scale(0.8)",
// },
// lg: {
// transform: "rotate(45deg)",
// },
// xl: {
// transform: "rotate(45deg)",
// },
// },
// isChecked: {
// true: {
// opacity: 1,
// },
// },
// disableAnimation: {
// true: {
// transition: "none",
// "&:after": {
// transition: "none",
// },
// },
// },
// },
// compoundVariants: [
// // isIndeterminate && xs size
// {
// isIndeterminate: true,
// size: "xs",
// css: {
// scale: "0.5",
// },
// },
// // isIndeterminate && sm size
// {
// isIndeterminate: true,
// size: "sm",
// css: {
// scale: "0.5",
// },
// },
// // isIndeterminate && md size
// {
// isIndeterminate: true,
// size: "md",
// css: {
// scale: "0.8",
// },
// },
// // isIndeterminate && lg size
// {
// isIndeterminate: true,
// size: "lg",
// css: {
// transform: "none",
// },
// },
// // isIndeterminate && xl size
// {
// isIndeterminate: true,
// size: "lg",
// css: {
// transform: "none",
// },
// },
// ],
// });
// export const StyledIconCheckFirstLine = styled("div", {
// content: "",
// background: "transparent",
// position: "absolute",
// width: "8px",
// height: "1px",
// br: "5px",
// zIndex: "$1",
// bottom: "0px",
// "&:after": {
// content: "",
// position: "absolute",
// left: "0px",
// width: "0%",
// height: "2px",
// background: "$white",
// br: "5px 0px 0px 5px",
// },
// "@motion": {
// "&:after": {
// transition: "none",
// },
// },
// variants: {
// isIndeterminate: {
// true: {
// display: "none",
// },
// },
// isChecked: {
// true: {
// "&:after": {
// width: "100%",
// transition: "width 0.25s ease 0.1s",
// },
// },
// },
// disableAnimation: {
// true: {
// "&:after": {
// transition: "none",
// },
// },
// },
// },
// compoundVariants: [
// // checked && disableAnimation
// {
// isChecked: true,
// disableAnimation: true,
// css: {
// "&:after": {
// transition: "none",
// },
// },
// },
// ],
// });
// export const StyledIconCheckSecondLine = styled("div", {
// content: "",
// position: "absolute",
// h: "13px",
// br: "5px",
// bottom: "0",
// right: "0",
// zIndex: "$1",
// background: "transparent",
// width: "2px",
// "&:after": {
// content: "",
// position: "absolute",
// width: "2px",
// height: "0%",
// background: "$white",
// left: "0px",
// bottom: "0px",
// br: "5px 5px 0px 0px",
// },
// "@motion": {
// "&:after": {
// transition: "none",
// },
// },
// variants: {
// isIndeterminate: {
// true: {
// display: "none",
// },
// },
// isChecked: {
// true: {
// "&:after": {
// height: "100%",
// transition: "height 0.2s ease 0.3s",
// },
// },
// },
// disableAnimation: {
// true: {
// "&:after": {
// transition: "none",
// },
// },
// },
// },
// compoundVariants: [
// // checked && disableAnimation
// {
// isChecked: true,
// disableAnimation: true,
// css: {
// "&:after": {
// transition: "none",
// },
// },
// },
// ],
// });

View File

@ -1,8 +1,10 @@
import {forwardRef} from "@nextui-org/system";
import {__DEV__} from "@nextui-org/shared-utils";
import {VisuallyHidden} from "@react-aria/visually-hidden";
import {cloneElement, ReactElement} from "react";
import {UseCheckboxProps, useCheckbox} from "./use-checkbox";
import {CheckboxIcon} from "./checkbox-icon";
export interface CheckboxProps extends Omit<UseCheckboxProps, "ref"> {}
@ -10,10 +12,9 @@ const Checkbox = forwardRef<CheckboxProps, "label">((props, ref) => {
const {
Component,
children,
isChecked,
disableAnimation,
icon = <CheckboxIcon />,
getBaseProps,
getCheckboxProps,
getWrapperProps,
getInputProps,
getIconProps,
getLabelProps,
@ -22,33 +23,14 @@ const Checkbox = forwardRef<CheckboxProps, "label">((props, ref) => {
...props,
});
const clonedIcon = cloneElement(icon as ReactElement, getIconProps());
return (
<Component {...getBaseProps()}>
<VisuallyHidden>
<input {...getInputProps()} />
</VisuallyHidden>
<span {...getCheckboxProps()}>
<svg aria-hidden="true" {...getIconProps()} role="presentation" viewBox="0 0 18 18">
<polyline
fill="none"
points="1 9 7 14 15 4"
stroke="currentColor"
strokeDasharray={22}
strokeDashoffset={isChecked ? 44 : 66}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
style={
!disableAnimation
? {
transition: "all 350ms",
transitionDelay: "200ms",
}
: {}
}
/>
</svg>
</span>
<span {...getWrapperProps()}>{clonedIcon}</span>
{children && <span {...getLabelProps()}>{children}</span>}
</Component>
);
@ -58,6 +40,4 @@ if (__DEV__) {
Checkbox.displayName = "NextUI.Checkbox";
}
Checkbox.toString = () => ".nextui-checkbox";
export default Checkbox;

View File

@ -1,50 +1,49 @@
import type {AriaCheckboxGroupProps} from "@react-types/checkbox";
import type {CheckboxGroupSlots, SlotsToClasses} from "@nextui-org/theme";
import type {AriaCheckboxGroupProps, AriaCheckboxProps} from "@react-types/checkbox";
import type {Orientation} from "@react-types/shared";
import type {HTMLNextUIProps} from "@nextui-org/system";
import type {ReactRef} from "@nextui-org/shared-utils";
import {useMemo} from "react";
import {mergeProps} from "@react-aria/utils";
import {checkboxGroup} from "@nextui-org/theme";
import {useCheckboxGroup as useReactAriaCheckboxGroup} from "@react-aria/checkbox";
import {CheckboxGroupState, useCheckboxGroupState} from "@react-stately/checkbox";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx} from "@nextui-org/shared-utils";
import {CheckboxProps} from "./index";
export interface UseCheckboxGroupProps extends HTMLNextUIProps<"div", AriaCheckboxGroupProps> {
interface Props extends HTMLNextUIProps<"div", AriaCheckboxGroupProps> {
/**
* The color of the checkboxes.
* @default "default"
* Ref to the DOM node.
*/
color?: CheckboxProps["color"];
/**
* The size of the checkboxes.
* @default "md"
*/
size?: CheckboxProps["size"];
/**
* The radius of the checkboxes.
* @default "lg"
*/
radius?: CheckboxProps["radius"];
/**
* Whether the checkboxes should have a line through.
* @default false
*/
lineThrough?: CheckboxProps["lineThrough"];
/**
* Whether the checkboxes are disabled.
* @default false
*/
isDisabled?: CheckboxProps["isDisabled"];
/**
* Whether the animation should be disabled.
* @default false
*/
disableAnimation?: CheckboxProps["disableAnimation"];
ref?: ReactRef<HTMLDivElement | null>;
/**
* The axis the checkbox group items should align with.
* @default "vertical"
*/
orientation?: Orientation;
/**
* 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
* <CheckboxGroup styles={{
* base:"base-classes",
* label: "label-classes",
* wrapper: "wrapper-classes", // checkboxes wrapper
* }} >
* // checkboxes
* </CheckboxGroup>
* ```
*/
styles?: SlotsToClasses<CheckboxGroupSlots>;
}
export type UseCheckboxGroupProps = Props & Omit<CheckboxProps, "ref" | keyof AriaCheckboxProps>;
export type ContextType = {
groupState: CheckboxGroupState;
color?: CheckboxProps["color"];
@ -57,16 +56,26 @@ export type ContextType = {
export function useCheckboxGroup(props: UseCheckboxGroupProps) {
const {
as,
ref,
styles,
children,
label,
size = "md",
color = "neutral",
radius = "lg",
color = "primary",
radius = "md",
orientation = "vertical",
lineThrough = false,
isDisabled = false,
disableAnimation = false,
className,
...otherProps
} = props;
const Component = as || "div";
const domRef = useDOMRef(ref);
const groupState = useCheckboxGroupState(otherProps);
const {labelProps, groupProps} = useReactAriaCheckboxGroup(otherProps, groupState);
@ -81,13 +90,41 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) {
groupState,
};
const slots = useMemo(() => checkboxGroup(), []);
const baseStyles = clsx(styles?.base, className);
const getGroupProps = () => {
return {
ref: domRef,
className: slots.base({class: baseStyles}),
...mergeProps(groupProps, otherProps),
};
};
const getLabelProps = () => {
return {
className: slots.label({class: styles?.label}),
...labelProps,
};
};
const getWrapperProps = () => {
return {
className: slots.wrapper({class: styles?.wrapper}),
role: "presentation",
"data-orientation": orientation,
};
};
return {
size,
orientation,
labelProps,
groupProps,
Component,
children,
label,
context,
...otherProps,
getGroupProps,
getLabelProps,
getWrapperProps,
};
}

View File

@ -6,7 +6,7 @@ import {ReactNode, Ref, useCallback} from "react";
import {useMemo, useRef} from "react";
import {useToggleState} from "@react-stately/toggle";
import {checkbox} from "@nextui-org/theme";
import {useHover, usePress} from "@react-aria/interactions";
import {useHover} from "@react-aria/interactions";
import {useFocusRing} from "@react-aria/focus";
import {mergeProps} from "@react-aria/utils";
import {useFocusableRef} from "@nextui-org/dom-utils";
@ -19,9 +19,7 @@ import {FocusableRef} from "@react-types/shared";
import {useCheckboxGroupContext} from "./checkbox-group-context";
export interface UseCheckboxProps
extends HTMLNextUIProps<"label", Omit<AriaCheckboxProps, keyof CheckboxVariantProps>>,
Omit<CheckboxVariantProps, "isFocusVisible"> {
interface Props extends HTMLNextUIProps<"label"> {
/**
* Ref to the DOM node.
*/
@ -35,6 +33,10 @@ export interface UseCheckboxProps
* The label of the checkbox.
*/
children?: ReactNode;
/**
* The icon to be displayed when the checkbox is checked.
*/
icon?: ReactNode;
/**
* Classname or List of classes to change the styles of the element.
* if `className` is passed, it will be added to the base slot.
@ -52,6 +54,10 @@ export interface UseCheckboxProps
styles?: SlotsToClasses<CheckboxSlots>;
}
export type UseCheckboxProps = Omit<Props, "defaultChecked"> &
Omit<AriaCheckboxProps, keyof CheckboxVariantProps> &
Omit<CheckboxVariantProps, "isFocusVisible">;
export function useCheckbox(props: UseCheckboxProps) {
const groupContext = useCheckboxGroupContext();
const isInGroup = !!groupContext;
@ -62,10 +68,11 @@ export function useCheckbox(props: UseCheckboxProps) {
isSelected,
value = "",
children,
icon,
isRequired = false,
size = groupContext?.size ?? "md",
color = groupContext?.color ?? "neutral",
radius = groupContext?.radius ?? "sm",
color = groupContext?.color ?? "primary",
radius = groupContext?.radius ?? "md",
lineThrough = groupContext?.lineThrough ?? false,
isDisabled = groupContext?.isDisabled ?? false,
disableAnimation = groupContext?.disableAnimation ?? false,
@ -98,16 +105,20 @@ export function useCheckbox(props: UseCheckboxProps) {
const domRef = useFocusableRef(ref as FocusableRef<HTMLLabelElement>, inputRef);
const ariaCheckboxProps = useMemo(() => {
const arialabel =
otherProps["aria-label"] || typeof children === "string" ? (children as string) : undefined;
return {
...otherProps,
value,
defaultSelected,
isSelected,
isDisabled,
isIndeterminate,
isRequired,
onChange,
"aria-label": arialabel,
};
}, [isIndeterminate, otherProps]);
}, [isIndeterminate, isDisabled]);
const {inputProps} = isInGroup
? // eslint-disable-next-line
@ -129,11 +140,6 @@ export function useCheckbox(props: UseCheckboxProps) {
isDisabled: inputProps.disabled,
});
// TODO: Event's propagation wasn't stopped https://github.com/adobe/react-spectrum/issues/2383
const {pressProps} = usePress({
isDisabled: inputProps.disabled,
});
const {focusProps, isFocused, isFocusVisible} = useFocusRing({
autoFocus: inputProps.autoFocus,
});
@ -162,11 +168,11 @@ export function useCheckbox(props: UseCheckboxProps) {
"data-disabled": dataAttr(isDisabled),
"data-checked": dataAttr(inputProps.checked),
"data-invalid": dataAttr(otherProps.validationState === "invalid"),
...mergeProps(hoverProps, pressProps, otherProps),
...mergeProps(hoverProps, otherProps),
};
};
const getCheckboxProps = () => {
const getWrapperProps = () => {
return {
"data-hover": dataAttr(isHovered),
"data-checked": dataAttr(inputProps.checked),
@ -178,7 +184,6 @@ export function useCheckbox(props: UseCheckboxProps) {
"data-readonly": dataAttr(inputProps.readOnly),
"aria-hidden": true,
className: clsx(slots.wrapper({class: styles?.wrapper})),
...mergeProps(hoverProps, pressProps, otherProps),
};
};
@ -202,18 +207,20 @@ export function useCheckbox(props: UseCheckboxProps) {
const getIconProps = useCallback(
() => ({
"data-checked": dataAttr(inputProps.checked),
isSelected: inputProps.checked,
isIndeterminate: !!isIndeterminate,
disableAnimation: !!disableAnimation,
className: slots.icon({class: styles?.icon}),
}),
[slots, inputProps.checked],
[slots, inputProps.checked, isIndeterminate, disableAnimation],
);
return {
Component,
icon,
children,
disableAnimation,
isChecked: inputProps.checked,
getBaseProps,
getCheckboxProps,
getWrapperProps,
getInputProps,
getLabelProps,
getIconProps,

View File

@ -0,0 +1,145 @@
import React from "react";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {checkbox} from "@nextui-org/theme";
import {CheckboxGroup, Checkbox, CheckboxGroupProps} from "../src";
export default {
title: "Inputs/CheckboxGroup",
component: CheckboxGroup,
argTypes: {
color: {
control: {
type: "select",
options: ["neutral", "primary", "secondary", "success", "warning", "danger"],
},
},
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
},
},
size: {
control: {
type: "select",
options: ["xs", "sm", "md", "lg", "xl"],
},
},
lineThrough: {
control: {
type: "boolean",
},
},
isDisabled: {
control: {
type: "boolean",
},
},
},
} as ComponentMeta<typeof Checkbox>;
const defaultProps = {
...checkbox.defaultVariants,
};
const Template: ComponentStory<typeof CheckboxGroup> = (args: CheckboxGroupProps) => (
<CheckboxGroup {...args}>
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="san-francisco">San Francisco</Checkbox>
<Checkbox value="london">London</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</CheckboxGroup>
);
export const Default = Template.bind({});
Default.args = {
...defaultProps,
};
export const WithLabel = Template.bind({});
WithLabel.args = {
label: "Select cities",
};
export const DefaultValue = Template.bind({});
DefaultValue.args = {
...defaultProps,
label: "Select cities",
defaultValue: ["buenos-aires", "london"],
};
export const Horizontal = Template.bind({});
Horizontal.args = {
label: "Select cities",
orientation: "horizontal",
};
export const IsDisabled = Template.bind({});
IsDisabled.args = {
label: "Select cities",
isDisabled: true,
};
export const LineThrough = Template.bind({});
LineThrough.args = {
label: "Select cities",
lineThrough: true,
};
export const DisableAnimation = Template.bind({});
DisableAnimation.args = {
label: "Select cities",
disableAnimation: true,
};
export const Controlled = () => {
const [groupSelected, setGroupSelected] = React.useState<string[]>(["buenos-aires", "sydney"]);
React.useEffect(() => {
// eslint-disable-next-line no-console
console.log("CheckboxGroup ", groupSelected);
}, [groupSelected]);
return (
<div className="flex flex-row gap-2">
<CheckboxGroup
color="warning"
label="Select cities"
value={groupSelected}
onChange={setGroupSelected}
>
<Checkbox color="primary" value="buenos-aires">
Buenos Aires
</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="london">London</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</CheckboxGroup>
</div>
);
};
export const Group = () => {
// eslint-disable-next-line no-console
const handleGroupChange = (value: string[]) => console.log(value);
return (
<CheckboxGroup
color="warning"
defaultValue={["buenos-aires"]}
label="Select cities"
onChange={handleGroupChange}
>
<Checkbox color="primary" value="buenos-aires">
Buenos Aires
</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox isDisabled value="london">
London
</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</CheckboxGroup>
);
};

View File

@ -1,3 +1,4 @@
import React from "react";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {checkbox} from "@nextui-org/theme";
@ -7,12 +8,6 @@ export default {
title: "Inputs/Checkbox",
component: Checkbox,
argTypes: {
variant: {
control: {
type: "select",
options: ["solid", "bordered", "light", "flat", "faded", "shadow", "dot"],
},
},
color: {
control: {
type: "select",
@ -31,6 +26,11 @@ export default {
options: ["xs", "sm", "md", "lg", "xl"],
},
},
lineThrough: {
control: {
type: "boolean",
},
},
isDisabled: {
control: {
type: "boolean",
@ -51,197 +51,83 @@ Default.args = {
...defaultProps,
};
// export const Default = () => {
// return (
// <Checkbox color="default" labelColor="default" size="md">
// Option
// </Checkbox>
// );
// };
export const IsDisabled = Template.bind({});
IsDisabled.args = {
...defaultProps,
isDisabled: true,
};
// export const Label = () => {
// return <Checkbox color="default" label="Option" labelColor="default" size="md" />;
// };
export const DefaultSelected = Template.bind({});
DefaultSelected.args = {
...defaultProps,
defaultSelected: true,
};
// export const Disabled = () => (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected size="xl">
// Enabled
// </Checkbox>
// <br />
// <Checkbox defaultSelected isDisabled size="xl">
// Disabled
// </Checkbox>
// </div>
// );
export const AlwaysSelected = Template.bind({});
AlwaysSelected.args = {
...defaultProps,
isSelected: true,
};
// export const Sizes = () => (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected size="xs">
// mini
// </Checkbox>
// <br />
// <Checkbox defaultSelected size="sm">
// small
// </Checkbox>
// <br />
// <Checkbox defaultSelected size="md">
// medium
// </Checkbox>
// <br />
// <Checkbox defaultSelected size="lg">
// large
// </Checkbox>
// <br />
// <Checkbox defaultSelected size="xl">
// xlarge
// </Checkbox>
// </div>
// );
export const IsIndeterminate = Template.bind({});
IsIndeterminate.args = {
...defaultProps,
isIndeterminate: true,
};
// export const Colors = () => (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected color="primary">
// Primary
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="secondary">
// Secondary
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="success">
// Success
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="warning">
// Warning
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="error">
// Error
// </Checkbox>
// </div>
// );
export const LineThrough = Template.bind({});
LineThrough.args = {
...defaultProps,
lineThrough: true,
};
// export const LabelColors = () => (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected color="primary" labelColor="primary">
// Primary
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="secondary" labelColor="secondary">
// Secondary
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="success" labelColor="success">
// Success
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="warning" labelColor="warning">
// Warning
// </Checkbox>
// <br />
// <Checkbox defaultSelected color="error" labelColor="error">
// Error
// </Checkbox>
// </div>
// );
export const DisableAnimation = Template.bind({});
DisableAnimation.args = {
...defaultProps,
disableAnimation: true,
};
// export const Rounded = () => (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected isRounded color="primary">
// Primary
// </Checkbox>
// <br />
// <Checkbox defaultSelected isRounded color="secondary">
// Secondary
// </Checkbox>
// <br />
// <Checkbox defaultSelected isRounded color="success">
// Success
// </Checkbox>
// <br />
// <Checkbox defaultSelected isRounded color="warning">
// Warning
// </Checkbox>
// <br />
// <Checkbox defaultSelected isRounded color="error">
// Error
// </Checkbox>
// </div>
// );
export const Controlled = () => {
const [selected, setSelected] = React.useState<boolean>(true);
// export const Indeterminate = () => {
// return (
// <Checkbox defaultSelected isIndeterminate color="primary" size="lg">
// Option
// </Checkbox>
// );
// };
// const [groupSelected, setGroupSelected] = React.useState<string[]>(["buenos-aires", "sydney"]);
// export const LineThrough = () => {
// return (
// <Checkbox defaultSelected lineThrough color="primary" size="lg">
// Option
// </Checkbox>
// );
// };
React.useEffect(() => {
// eslint-disable-next-line no-console
console.log("Checkbox ", selected);
}, [selected]);
// export const Controlled = () => {
// const [selected, setSelected] = React.useState<boolean>(true);
// React.useEffect(() => {
// // eslint-disable-next-line no-console
// console.log("CheckboxGroup ", groupSelected);
// }, [groupSelected]);
// const [groupSelected, setGroupSelected] = React.useState<string[]>(["buenos-aires", "sydney"]);
// React.useEffect(() => {
// // eslint-disable-next-line no-console
// console.log("Checkbox ", selected);
// }, [selected]);
// React.useEffect(() => {
// // eslint-disable-next-line no-console
// console.log("CheckboxGroup ", groupSelected);
// }, [groupSelected]);
// return (
// <div style={{display: "flex", flexDirection: "row", gap: 200}}>
// <Checkbox color="success" isSelected={selected} onChange={setSelected}>
// Subscribe (controlled)
// </Checkbox>
// <Checkbox.Group
// color="warning"
// label="Select cities"
// labelColor="primary"
// value={groupSelected}
// onChange={setGroupSelected}
// >
// <Checkbox color="primary" value="buenos-aires">
// Buenos Aires
// </Checkbox>
// <Checkbox labelColor="warning" value="sydney">
// Sydney
// </Checkbox>
// <Checkbox labelColor="error" value="london">
// London
// </Checkbox>
// <Checkbox value="tokyo">Tokyo</Checkbox>
// </Checkbox.Group>
// </div>
// );
// };
// export const NoAnimated = () => {
// return (
// <div style={{display: "flex", flexDirection: "column"}}>
// <Checkbox defaultSelected disableAnimation={true} size="md">
// Option
// </Checkbox>
// <br />
// <Checkbox defaultSelected lineThrough disableAnimation={true} size="md">
// Option
// </Checkbox>
// </div>
// );
// };
return (
<div className="flex flex-row gap-2">
<Checkbox isSelected={selected} onChange={setSelected} {...checkbox.defaultVariants}>
Subscribe (controlled)
</Checkbox>
{/* <Checkbox.Group
color="warning"
label="Select cities"
labelColor="primary"
value={groupSelected}
onChange={setGroupSelected}
>
<Checkbox color="primary" value="buenos-aires">
Buenos Aires
</Checkbox>
<Checkbox labelColor="warning" value="sydney">
Sydney
</Checkbox>
<Checkbox labelColor="error" value="london">
London
</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</Checkbox.Group> */}
</div>
);
};
// export const Group = () => {
// // eslint-disable-next-line no-console

View File

@ -0,0 +1,26 @@
import {tv} from "tailwind-variants";
/**
* CheckboxGroup wrapper **Tailwind Variants** component
*
* const {base, label, wrapper,} = checkboxGroup({...})
*
* @example
* <div className={base())}>
* <label className={label()}>Label</label>
* <div className={wrapper()} data-orientation="vertical/horizontal">
* // checkboxes
* </div>
* </div>
*/
const checkboxGroup = tv({
slots: {
base: "relative flex flex-col gap-2",
label: "relative text-neutral-500",
wrapper: "flex flex-col flex-wrap gap-2 data-[orientation=horizontal]:flex-row ",
},
});
export type CheckboxGroupSlots = keyof ReturnType<typeof checkboxGroup>;
export {checkboxGroup};

View File

@ -20,7 +20,7 @@ import {ringClasses} from "../utils";
*/
const checkbox = tv({
slots: {
base: "relative inline-flex items-center justify-start cursor-pointer",
base: "relative max-w-fit inline-flex items-center justify-start cursor-pointer",
wrapper: [
"relative",
"inline-flex",
@ -50,7 +50,7 @@ const checkbox = tv({
"data-[hover=true]:before:bg-neutral",
],
icon: "z-10 w-4 h-3 opacity-0 data-[checked=true]:opacity-100",
label: "ml-1 text-foreground",
label: "relative ml-1 text-foreground select-none",
},
variants: {
color: {
@ -125,32 +125,48 @@ const checkbox = tv({
},
lineThrough: {
true: {
label: "line-through",
label: [
"inline-flex",
"items-center",
"justify-center",
"before:content-['']",
"before:absolute",
"before:bg-foreground",
"before:w-0",
"before:h-0.5",
"data-[checked=true]:opacity-60",
"data-[checked=true]:before:w-full",
],
},
},
isDisabled: {
true: {
base: "opacity-50 cursor-not-allowed",
base: "opacity-50 pointer-events-none",
},
},
isFocusVisible: {
true: [...ringClasses],
true: {
wrapper: [...ringClasses],
},
},
disableAnimation: {
true: {
wrapper: "transition-none",
},
false: {
wrapper: "before:transition-background after:transition-transform-opacity",
wrapper:
"before:transition-background after:transition-transform-opacity before:!duration-250 after:!duration-250",
icon: "transition-opacity",
label: "transition-opacity before:transition-width",
},
},
},
defaultVariants: {
color: "neutral",
color: "primary",
size: "md",
radius: "md",
isDisabled: false,
lineThrough: false,
disableAnimation: false,
},
});

View File

@ -12,3 +12,4 @@ export * from "./snippet";
export * from "./chip";
export * from "./badge";
export * from "./checkbox";
export * from "./checkbox-group";

View File

@ -167,6 +167,10 @@ const corePlugin = (config: ConfigObject | ConfigFunction = {}) => {
3: "3px",
5: "5px",
},
transitionDuration: {
0: "0ms",
250: "250ms",
},
...animations,
},
},

View File

@ -46,6 +46,11 @@ export const utilities = {
"transition-timing-function": "ease",
"transition-duration": DEFAULT_TRANSITION_DURATION,
},
".transition-width": {
"transition-property": "width",
"transition-timing-function": "ease",
"transition-duration": DEFAULT_TRANSITION_DURATION,
},
".transition-shadow": {
"transition-property": "box-shadow",
"transition-timing-function": "ease",

View File

@ -44,7 +44,7 @@
},
"devDependencies": {
"clean-package": "2.2.0",
"react": "^17.0.2
"react": "^18.0.0"
},
"tsup": {
"clean": true,