mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(components): radio & checkbox improved, custom examples added to the stories
This commit is contained in:
parent
2545eb7898
commit
aba2e459d6
@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import {render} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {act, render} from "@testing-library/react";
|
||||
|
||||
import {Button} from "../src";
|
||||
|
||||
@ -22,7 +21,10 @@ describe("Button", () => {
|
||||
const onPress = jest.fn();
|
||||
const {getByRole} = render(<Button onPress={onPress} />);
|
||||
|
||||
act(() => {
|
||||
getByRole("button").click();
|
||||
});
|
||||
|
||||
expect(onPress).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -32,9 +34,9 @@ describe("Button", () => {
|
||||
|
||||
const wrapper = render(<Button onClick={onClick} />);
|
||||
|
||||
let button = wrapper.getByRole("button");
|
||||
|
||||
userEvent.click(button);
|
||||
act(() => {
|
||||
wrapper.getByRole("button").click();
|
||||
});
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
spy.mockRestore();
|
||||
@ -44,7 +46,10 @@ describe("Button", () => {
|
||||
const onPress = jest.fn();
|
||||
const {getByRole} = render(<Button disabled onPress={onPress} />);
|
||||
|
||||
act(() => {
|
||||
getByRole("button").click();
|
||||
});
|
||||
|
||||
expect(onPress).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@ -50,6 +50,10 @@
|
||||
"@react-aria/utils": "^3.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nextui-org/shared-icons": "workspace:*",
|
||||
"@nextui-org/chip": "workspace:*",
|
||||
"@nextui-org/user": "workspace:*",
|
||||
"@nextui-org/link": "workspace:*",
|
||||
"@react-types/checkbox": "^3.4.2",
|
||||
"@react-types/shared": "^3.17.0",
|
||||
"clean-package": "2.2.0",
|
||||
|
||||
@ -23,7 +23,10 @@ const Checkbox = forwardRef<CheckboxProps, "label">((props, ref) => {
|
||||
...props,
|
||||
});
|
||||
|
||||
const clonedIcon = cloneElement(icon as ReactElement, getIconProps());
|
||||
const clonedIcon =
|
||||
typeof icon === "function"
|
||||
? icon(getIconProps())
|
||||
: cloneElement(icon as ReactElement, getIconProps());
|
||||
|
||||
return (
|
||||
<Component {...getBaseProps()}>
|
||||
|
||||
@ -8,6 +8,10 @@ export {useCheckboxGroup} from "./use-checkbox-group";
|
||||
// export types
|
||||
export type {CheckboxProps} from "./checkbox";
|
||||
export type {CheckboxGroupProps} from "./checkbox-group";
|
||||
export type {CheckboxIconProps} from "./use-checkbox";
|
||||
|
||||
// export context
|
||||
export * from "./checkbox-group-context";
|
||||
|
||||
// export components
|
||||
export {Checkbox, CheckboxGroup};
|
||||
|
||||
@ -19,6 +19,14 @@ import {FocusableRef} from "@react-types/shared";
|
||||
|
||||
import {useCheckboxGroupContext} from "./checkbox-group-context";
|
||||
|
||||
export type CheckboxIconProps = {
|
||||
"data-checked": string;
|
||||
isSelected: boolean;
|
||||
isIndeterminate: boolean;
|
||||
disableAnimation: boolean;
|
||||
className: string;
|
||||
};
|
||||
|
||||
interface Props extends HTMLNextUIProps<"label"> {
|
||||
/**
|
||||
* Ref to the DOM node.
|
||||
@ -36,7 +44,7 @@ interface Props extends HTMLNextUIProps<"label"> {
|
||||
/**
|
||||
* The icon to be displayed when the checkbox is checked.
|
||||
*/
|
||||
icon?: ReactNode;
|
||||
icon?: ReactNode | ((props: CheckboxIconProps) => 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.
|
||||
@ -54,7 +62,7 @@ interface Props extends HTMLNextUIProps<"label"> {
|
||||
styles?: SlotsToClasses<CheckboxSlots>;
|
||||
}
|
||||
|
||||
export type UseCheckboxProps = Omit<Props, "defaultChecked"> &
|
||||
export type UseCheckboxProps = Omit<Props, "defaultChecked" | "onChange"> &
|
||||
Omit<AriaCheckboxProps, keyof CheckboxVariantProps> &
|
||||
Omit<CheckboxVariantProps, "isFocusVisible">;
|
||||
|
||||
@ -65,11 +73,13 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
const {
|
||||
as,
|
||||
ref,
|
||||
isSelected,
|
||||
value = "",
|
||||
children,
|
||||
icon,
|
||||
name,
|
||||
isRequired = false,
|
||||
isReadOnly = false,
|
||||
isSelected: isSelectedProp,
|
||||
size = groupContext?.size ?? "md",
|
||||
color = groupContext?.color ?? "primary",
|
||||
radius = groupContext?.radius ?? "md",
|
||||
@ -77,6 +87,7 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
isDisabled = groupContext?.isDisabled ?? false,
|
||||
disableAnimation = groupContext?.disableAnimation ?? false,
|
||||
isIndeterminate = false,
|
||||
validationState,
|
||||
defaultSelected,
|
||||
styles,
|
||||
onChange,
|
||||
@ -85,7 +96,7 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
} = props;
|
||||
|
||||
if (groupContext && __DEV__) {
|
||||
if (isSelected) {
|
||||
if (isSelectedProp) {
|
||||
warn(
|
||||
"The Checkbox.Group is being used, `isSelected` will be ignored. Use the `value` of the Checkbox.Group instead.",
|
||||
"Checkbox",
|
||||
@ -105,34 +116,50 @@ 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 {
|
||||
name,
|
||||
value,
|
||||
children,
|
||||
defaultSelected,
|
||||
isSelected,
|
||||
isSelected: isSelectedProp,
|
||||
isDisabled,
|
||||
isIndeterminate,
|
||||
isRequired,
|
||||
isReadOnly,
|
||||
validationState,
|
||||
"aria-label": otherProps["aria-label"],
|
||||
"aria-labelledby": otherProps["aria-labelledby"],
|
||||
onChange,
|
||||
"aria-label": arialabel,
|
||||
"aria-labelledby": otherProps["aria-labelledby"] || arialabel,
|
||||
};
|
||||
}, [isIndeterminate, isDisabled]);
|
||||
}, [
|
||||
value,
|
||||
name,
|
||||
children,
|
||||
isIndeterminate,
|
||||
isDisabled,
|
||||
isSelectedProp,
|
||||
defaultSelected,
|
||||
validationState,
|
||||
otherProps["aria-label"],
|
||||
otherProps["aria-labelledby"],
|
||||
onChange,
|
||||
]);
|
||||
|
||||
const {inputProps} = isInGroup
|
||||
? // eslint-disable-next-line
|
||||
useReactAriaCheckboxGroupItem(
|
||||
{
|
||||
...ariaCheckboxProps,
|
||||
validationState: otherProps.validationState,
|
||||
validationState,
|
||||
},
|
||||
groupContext.groupState,
|
||||
inputRef,
|
||||
)
|
||||
: useReactAriaCheckbox(ariaCheckboxProps, useToggleState(ariaCheckboxProps), inputRef); // eslint-disable-line
|
||||
|
||||
const isSelected = inputProps.checked;
|
||||
const isInvalid = useMemo(() => validationState === "invalid", [validationState]);
|
||||
|
||||
if (isRequired) {
|
||||
inputProps.required = true;
|
||||
}
|
||||
@ -166,8 +193,8 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
ref: domRef,
|
||||
className: slots.base({class: baseStyles}),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-invalid": dataAttr(otherProps.validationState === "invalid"),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
...mergeProps(hoverProps, otherProps),
|
||||
};
|
||||
};
|
||||
@ -175,12 +202,12 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
const getWrapperProps: PropGetter = () => {
|
||||
return {
|
||||
"data-hover": dataAttr(isHovered),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-focus": dataAttr(isFocused),
|
||||
"data-focus-visible": dataAttr(isFocused && isFocusVisible),
|
||||
"data-indeterminate": dataAttr(isIndeterminate),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-invalid": dataAttr(otherProps.validationState === "invalid"),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
"data-readonly": dataAttr(inputProps.readOnly),
|
||||
"aria-hidden": true,
|
||||
className: clsx(slots.wrapper({class: styles?.wrapper})),
|
||||
@ -197,28 +224,32 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
const getLabelProps: PropGetter = useCallback(
|
||||
() => ({
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-invalid": dataAttr(otherProps.validationState === "invalid"),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
className: slots.label({class: styles?.label}),
|
||||
}),
|
||||
[slots, isDisabled, inputProps.checked, otherProps.validationState],
|
||||
[slots, styles?.label, isDisabled, isSelected, isInvalid],
|
||||
);
|
||||
|
||||
const getIconProps: PropGetter = useCallback(
|
||||
() => ({
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
isSelected: inputProps.checked,
|
||||
const getIconProps = useCallback(
|
||||
() =>
|
||||
({
|
||||
"data-checked": dataAttr(isSelected),
|
||||
isSelected: isSelected,
|
||||
isIndeterminate: !!isIndeterminate,
|
||||
disableAnimation: !!disableAnimation,
|
||||
className: slots.icon({class: styles?.icon}),
|
||||
}),
|
||||
[slots, inputProps.checked, isIndeterminate, disableAnimation],
|
||||
} as CheckboxIconProps),
|
||||
[slots, styles?.icon, isSelected, isIndeterminate, disableAnimation],
|
||||
);
|
||||
|
||||
return {
|
||||
Component,
|
||||
icon,
|
||||
children,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
isInvalid,
|
||||
getBaseProps,
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
|
||||
@ -4,6 +4,11 @@ import {checkbox} from "@nextui-org/theme";
|
||||
|
||||
import {CheckboxGroup, Checkbox, CheckboxGroupProps} from "../src";
|
||||
|
||||
import {
|
||||
CustomWithStyles as CheckboxItemWithStyles,
|
||||
CustomWithHooks as CheckboxItemWithHooks,
|
||||
} from "./checkbox.stories";
|
||||
|
||||
export default {
|
||||
title: "Inputs/CheckboxGroup",
|
||||
component: CheckboxGroup,
|
||||
@ -121,25 +126,58 @@ export const Controlled = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Group = () => {
|
||||
// eslint-disable-next-line no-console
|
||||
const handleGroupChange = (value: string[]) => console.log(value);
|
||||
export const CustomWithStyles = () => {
|
||||
const [groupSelected, setGroupSelected] = React.useState<string[]>([]);
|
||||
|
||||
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 label="Select employees" value={groupSelected} onChange={setGroupSelected}>
|
||||
<CheckboxItemWithStyles value="junior" />
|
||||
<CheckboxItemWithStyles
|
||||
userName="John Doe"
|
||||
userProfile={{
|
||||
avatar: "https://i.pravatar.cc/300?u=a042581f4e29026707d",
|
||||
username: "johndoe",
|
||||
url: "#",
|
||||
}}
|
||||
userRole="Product Designer"
|
||||
value="johndoe"
|
||||
/>
|
||||
<CheckboxItemWithStyles
|
||||
userName="Zoey Lang"
|
||||
userProfile={{
|
||||
avatar: "https://i.pravatar.cc/150?u=a042581f4e29026704d",
|
||||
username: "zoeylang",
|
||||
url: "#",
|
||||
}}
|
||||
userRole="Technical Writer"
|
||||
value="zoeylang"
|
||||
/>
|
||||
<CheckboxItemWithStyles
|
||||
userName="William Howard"
|
||||
userProfile={{
|
||||
avatar: "https://i.pravatar.cc/150?u=a048581f4e29026701d",
|
||||
username: "william",
|
||||
url: "#",
|
||||
}}
|
||||
userRole="Sales Manager"
|
||||
value="william"
|
||||
/>
|
||||
</CheckboxGroup>
|
||||
<p className="mt-4 ml-1 text-neutral-500">Selected: {groupSelected.join(", ")}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomWithHooks = () => {
|
||||
return (
|
||||
<CheckboxGroup className="gap-1" label="Select ammenities" orientation="horizontal">
|
||||
<CheckboxItemWithHooks value="wifi">Wifi</CheckboxItemWithHooks>
|
||||
<CheckboxItemWithHooks value="tv">TV</CheckboxItemWithHooks>
|
||||
<CheckboxItemWithHooks value="kitchen">Kitchen</CheckboxItemWithHooks>
|
||||
<CheckboxItemWithHooks value="parking">Parking</CheckboxItemWithHooks>
|
||||
<CheckboxItemWithHooks value="pool">Pool</CheckboxItemWithHooks>
|
||||
<CheckboxItemWithHooks value="gym">Gym</CheckboxItemWithHooks>
|
||||
</CheckboxGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,20 @@
|
||||
import React from "react";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {checkbox} from "@nextui-org/theme";
|
||||
import {checkbox, colors} from "@nextui-org/theme";
|
||||
import {CheckIcon, CloseIcon} from "@nextui-org/shared-icons";
|
||||
import {User} from "@nextui-org/user";
|
||||
import {Link} from "@nextui-org/link";
|
||||
import {Chip, ChipProps} from "@nextui-org/chip";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
import {VisuallyHidden} from "@react-aria/visually-hidden";
|
||||
|
||||
import {Checkbox, CheckboxProps} from "../src";
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxIconProps,
|
||||
CheckboxProps,
|
||||
useCheckbox,
|
||||
useCheckboxGroupContext,
|
||||
} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Inputs/Checkbox",
|
||||
@ -39,7 +51,7 @@ export default {
|
||||
},
|
||||
} as ComponentMeta<typeof Checkbox>;
|
||||
|
||||
const defaultProps = {
|
||||
const defaultProps: CheckboxProps = {
|
||||
...checkbox.defaultVariants,
|
||||
children: "Option",
|
||||
};
|
||||
@ -63,6 +75,18 @@ DefaultSelected.args = {
|
||||
defaultSelected: true,
|
||||
};
|
||||
|
||||
export const CustomIconNode = Template.bind({});
|
||||
CustomIconNode.args = {
|
||||
...defaultProps,
|
||||
icon: <CloseIcon />,
|
||||
};
|
||||
|
||||
export const CustomIconFunction = Template.bind({});
|
||||
CustomIconFunction.args = {
|
||||
...defaultProps,
|
||||
icon: (props: CheckboxIconProps) => <CloseIcon {...props} />,
|
||||
};
|
||||
|
||||
export const AlwaysSelected = Template.bind({});
|
||||
AlwaysSelected.args = {
|
||||
...defaultProps,
|
||||
@ -103,3 +127,112 @@ export const Controlled = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface CustomCheckboxProps extends CheckboxProps {
|
||||
userName?: string;
|
||||
userProfile?: {
|
||||
username?: string;
|
||||
avatar?: string;
|
||||
url?: string;
|
||||
};
|
||||
userRole?: string;
|
||||
status?: string;
|
||||
statusColor?: ChipProps["color"];
|
||||
}
|
||||
|
||||
export const CustomWithStyles = (props: CustomCheckboxProps) => {
|
||||
const {
|
||||
value,
|
||||
userName = "Junior Garcia",
|
||||
userProfile = {
|
||||
avatar: "https://avatars.githubusercontent.com/u/30373425?v=4",
|
||||
username: "jrgarciadev",
|
||||
url: "https://twitter.com/jrgarciadev",
|
||||
},
|
||||
userRole = "Software Developer",
|
||||
status = "Active",
|
||||
statusColor = "secondary",
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const groupContext = useCheckboxGroupContext();
|
||||
const isInGroup = !!groupContext;
|
||||
|
||||
const [isSelected, setIsSelected] = React.useState<boolean>(false);
|
||||
|
||||
const checkboxProps = !isInGroup
|
||||
? {
|
||||
isSelected,
|
||||
onChange: setIsSelected,
|
||||
}
|
||||
: {};
|
||||
|
||||
const isChecked = isInGroup && value ? groupContext?.groupState.isSelected(value) : isSelected;
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
{...otherProps}
|
||||
aria-label={userName}
|
||||
styles={{
|
||||
base: clsx(
|
||||
"inline-flex w-full max-w-md bg-content1 hover:bg-content2 items-center justify-start cursor-pointer rounded-lg gap-2 p-4 border-2 border-transparent",
|
||||
{
|
||||
"border-primary": isChecked,
|
||||
},
|
||||
),
|
||||
label: "w-full",
|
||||
}}
|
||||
value={value}
|
||||
{...checkboxProps}
|
||||
>
|
||||
<div className="w-full flex justify-between gap-2">
|
||||
<User
|
||||
avatarProps={{size: "sm", src: userProfile.avatar}}
|
||||
description={
|
||||
<Link href={userProfile.url} size="xs">
|
||||
@{userProfile.username}
|
||||
</Link>
|
||||
}
|
||||
name={userName}
|
||||
/>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<span className="text-xs text-neutral-500">{userRole}</span>
|
||||
<Chip color={statusColor} size="xs" variant="flat">
|
||||
{status}
|
||||
</Chip>
|
||||
</div>
|
||||
</div>
|
||||
</Checkbox>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomWithHooks = (props: CheckboxProps) => {
|
||||
const {children, isSelected, getBaseProps, getLabelProps, getInputProps} = useCheckbox({
|
||||
"aria-label": props["aria-label"] || "Toggle status",
|
||||
...props,
|
||||
});
|
||||
|
||||
return (
|
||||
<label {...getBaseProps()}>
|
||||
<VisuallyHidden>
|
||||
<input {...getInputProps()} />
|
||||
</VisuallyHidden>
|
||||
<Chip
|
||||
color="primary"
|
||||
leftContent={isSelected ? <CheckIcon className="ml-1" color={colors.white} /> : null}
|
||||
styles={{
|
||||
base: clsx("border-neutral hover:bg-neutral-200", {
|
||||
"border-primary bg-primary hover:bg-primary-600 hover:border-primary-600": isSelected,
|
||||
}),
|
||||
content: clsx("text-primary", {
|
||||
"text-primary-contrastText pl-1": isSelected,
|
||||
}),
|
||||
}}
|
||||
variant="faded"
|
||||
{...getLabelProps()}
|
||||
>
|
||||
{children ? children : isSelected ? "Enabled" : "Disabled"}
|
||||
</Chip>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
@ -46,7 +46,7 @@ const Chip = forwardRef<ChipProps, "div">((props, ref) => {
|
||||
return (
|
||||
<Component {...getChipProps()}>
|
||||
{left}
|
||||
<label className={slots.label({class: styles?.label})}>{children}</label>
|
||||
<span className={slots.content({class: styles?.content})}>{children}</span>
|
||||
{right}
|
||||
</Component>
|
||||
);
|
||||
|
||||
@ -37,7 +37,7 @@ export interface UseChipProps extends HTMLNextUIProps<"div">, ChipVariantProps {
|
||||
* <Chip styles={{
|
||||
* base:"base-classes",
|
||||
* dot: "dot-classes",
|
||||
* label: "label-classes",
|
||||
* content: "content-classes",
|
||||
* avatar: "avatar-classes",
|
||||
* closeButton: "close-button-classes",
|
||||
* }} />
|
||||
|
||||
@ -9,5 +9,8 @@ export type {RadioGroupProps} from "./radio-group";
|
||||
export {useRadio} from "./use-radio";
|
||||
export {useRadioGroup} from "./use-radio-group";
|
||||
|
||||
// export context
|
||||
export * from "./radio-group-context";
|
||||
|
||||
// export component
|
||||
export {Radio, RadioGroup};
|
||||
|
||||
@ -17,6 +17,7 @@ const Radio = forwardRef<RadioProps, "label">((props, ref) => {
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
getLabelProps,
|
||||
getLabelWrapperProps,
|
||||
getControlProps,
|
||||
} = useRadio({ref, ...props});
|
||||
|
||||
@ -28,7 +29,7 @@ const Radio = forwardRef<RadioProps, "label">((props, ref) => {
|
||||
<span {...getWrapperProps()}>
|
||||
<span {...getControlProps()} />
|
||||
</span>
|
||||
<div className={slots.labelWrapper({class: styles?.labelWrapper})}>
|
||||
<div {...getLabelWrapperProps()}>
|
||||
{children && <span {...getLabelProps()}>{children}</span>}
|
||||
{description && (
|
||||
<span className={slots.description({class: styles?.description})}>{description}</span>
|
||||
|
||||
@ -120,6 +120,8 @@ export function useRadio(props: UseRadioProps) {
|
||||
inputRef,
|
||||
);
|
||||
|
||||
const isSelected = useMemo(() => inputProps.checked, [inputProps.checked]);
|
||||
|
||||
const {hoverProps, isHovered} = useHover({isDisabled});
|
||||
|
||||
const {focusProps, isFocused, isFocusVisible} = useFocusRing({
|
||||
@ -142,8 +144,9 @@ export function useRadio(props: UseRadioProps) {
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
const getBaseProps: PropGetter = () => {
|
||||
const getBaseProps: PropGetter = (props = {}) => {
|
||||
return {
|
||||
...props,
|
||||
ref: domRef,
|
||||
className: slots.base({class: baseStyles}),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
@ -153,12 +156,13 @@ export function useRadio(props: UseRadioProps) {
|
||||
};
|
||||
};
|
||||
|
||||
const getWrapperProps: PropGetter = () => {
|
||||
const getWrapperProps: PropGetter = (props = {}) => {
|
||||
return {
|
||||
"data-active": dataAttr(inputProps.checked),
|
||||
...props,
|
||||
"data-active": dataAttr(isSelected),
|
||||
"data-hover": dataAttr(isHovered),
|
||||
"data-hover-unchecked": dataAttr(isHovered && !inputProps.checked),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-hover-unchecked": dataAttr(isHovered && !isSelected),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-focus": dataAttr(isFocused),
|
||||
"data-focus-visible": dataAttr(isFocused && isFocusVisible),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
@ -170,8 +174,9 @@ export function useRadio(props: UseRadioProps) {
|
||||
};
|
||||
};
|
||||
|
||||
const getInputProps: PropGetter = () => {
|
||||
const getInputProps: PropGetter = (props = {}) => {
|
||||
return {
|
||||
...props,
|
||||
ref: inputRef,
|
||||
required: isRequired,
|
||||
"aria-required": dataAttr(isRequired),
|
||||
@ -182,23 +187,33 @@ export function useRadio(props: UseRadioProps) {
|
||||
};
|
||||
|
||||
const getLabelProps: PropGetter = useCallback(
|
||||
() => ({
|
||||
(props = {}) => ({
|
||||
...props,
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
className: slots.label({class: styles?.label}),
|
||||
}),
|
||||
[slots, isDisabled, inputProps.checked, isInvalid],
|
||||
[slots, styles, isDisabled, isSelected, isInvalid],
|
||||
);
|
||||
|
||||
const getLabelWrapperProps: PropGetter = useCallback(
|
||||
(props = {}) => ({
|
||||
...props,
|
||||
className: slots.labelWrapper({class: styles?.labelWrapper}),
|
||||
}),
|
||||
[slots, styles],
|
||||
);
|
||||
|
||||
const getControlProps: PropGetter = useCallback(
|
||||
() => ({
|
||||
(props = {}) => ({
|
||||
...props,
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-checked": dataAttr(isSelected),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
className: slots.control({class: styles?.control}),
|
||||
}),
|
||||
[slots, isDisabled, inputProps.checked, isInvalid],
|
||||
[slots, isDisabled, isSelected, isInvalid],
|
||||
);
|
||||
|
||||
return {
|
||||
@ -207,10 +222,15 @@ export function useRadio(props: UseRadioProps) {
|
||||
slots,
|
||||
styles,
|
||||
description,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
isInvalid,
|
||||
isFocusVisible,
|
||||
getBaseProps,
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
getLabelProps,
|
||||
getLabelWrapperProps,
|
||||
getControlProps,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
import React from "react";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {VisuallyHidden} from "@react-aria/visually-hidden";
|
||||
import {radio, button} from "@nextui-org/theme";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
|
||||
import {RadioGroup, Radio, RadioGroupProps} from "../src";
|
||||
import {
|
||||
RadioGroup,
|
||||
Radio,
|
||||
RadioProps,
|
||||
RadioGroupProps,
|
||||
useRadio,
|
||||
useRadioGroupContext,
|
||||
} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Inputs/RadioGroup",
|
||||
@ -141,16 +150,22 @@ Row.args = {
|
||||
description: "for",
|
||||
};
|
||||
|
||||
export const DisableAnimation = Template.bind({});
|
||||
DisableAnimation.args = {
|
||||
...defaultProps,
|
||||
disableAnimation: true,
|
||||
};
|
||||
|
||||
export const Controlled = () => {
|
||||
const [checked, setChecked] = React.useState<string>("london");
|
||||
const [isSelected, setIsSelected] = React.useState<string>("london");
|
||||
|
||||
React.useEffect(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("checked:", checked);
|
||||
}, [checked]);
|
||||
console.log("isSelected:", isSelected);
|
||||
}, [isSelected]);
|
||||
|
||||
return (
|
||||
<RadioGroup label="Select city" value={checked} onChange={setChecked}>
|
||||
<RadioGroup label="Select city" value={isSelected} onChange={setIsSelected}>
|
||||
<Radio value="buenos-aires">Buenos Aires</Radio>
|
||||
<Radio value="sydney">Sydney</Radio>
|
||||
<Radio value="london">London</Radio>
|
||||
@ -159,8 +174,98 @@ export const Controlled = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const DisableAnimation = Template.bind({});
|
||||
DisableAnimation.args = {
|
||||
...defaultProps,
|
||||
disableAnimation: true,
|
||||
const CustomRadio = (props: RadioProps) => {
|
||||
const {children, ...otherProps} = props;
|
||||
|
||||
const {groupState} = useRadioGroupContext();
|
||||
|
||||
const isSelected = groupState.selectedValue === otherProps.value;
|
||||
|
||||
return (
|
||||
<Radio
|
||||
{...otherProps}
|
||||
styles={{
|
||||
base: clsx(
|
||||
"inline-flex bg-content1 hover:bg-content2 items-center justify-between flex-row-reverse max-w-[300px] cursor-pointer rounded-lg gap-4 p-4 border-2 border-transparent",
|
||||
{
|
||||
"border-primary": isSelected,
|
||||
},
|
||||
),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Radio>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomWithStyles = () => {
|
||||
return (
|
||||
<RadioGroup label="Plans">
|
||||
<CustomRadio description="Up to 20 items" value="free">
|
||||
Free
|
||||
</CustomRadio>
|
||||
<CustomRadio description="Unlimited items. $10 per month." value="pro">
|
||||
Pro
|
||||
</CustomRadio>
|
||||
<CustomRadio description="24/7 support. Contact us for pricing." value="enterprise">
|
||||
Enterprise
|
||||
</CustomRadio>
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const RadioCard = (props: RadioProps) => {
|
||||
const {
|
||||
Component,
|
||||
children,
|
||||
isSelected,
|
||||
description,
|
||||
getBaseProps,
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
getLabelProps,
|
||||
getLabelWrapperProps,
|
||||
getControlProps,
|
||||
} = useRadio(props);
|
||||
|
||||
return (
|
||||
<Component
|
||||
{...getBaseProps()}
|
||||
className={clsx(
|
||||
"inline-flex items-center justify-between hover:bg-content2 flex-row-reverse max-w-[300px] cursor-pointer border-2 border-neutral rounded-lg gap-4 p-4",
|
||||
{
|
||||
"border-primary": isSelected,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<VisuallyHidden>
|
||||
<input {...getInputProps()} />
|
||||
</VisuallyHidden>
|
||||
<span {...getWrapperProps()}>
|
||||
<span {...getControlProps()} />
|
||||
</span>
|
||||
<div {...getLabelWrapperProps()}>
|
||||
{children && <span {...getLabelProps()}>{children}</span>}
|
||||
{description && (
|
||||
<span className={clsx("text-sm text-foreground opacity-70")}>{description}</span>
|
||||
)}
|
||||
</div>
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomWithHooks = () => {
|
||||
return (
|
||||
<RadioGroup label="Plans">
|
||||
<RadioCard description="Up to 20 items" value="free">
|
||||
Free
|
||||
</RadioCard>
|
||||
<RadioCard description="Unlimited items. $10 per month." value="pro">
|
||||
Pro
|
||||
</RadioCard>
|
||||
<RadioCard description="24/7 support. Contact us for pricing." value="enterprise">
|
||||
Enterprise
|
||||
</RadioCard>
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@ -73,7 +73,8 @@ describe("Snippet - Clipboard", () => {
|
||||
|
||||
act(() => {
|
||||
wrapper.getByRole("button").click();
|
||||
});
|
||||
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,16 @@
|
||||
export * from "./common";
|
||||
export * from "./semantic";
|
||||
import {commonColors} from "./common";
|
||||
import {semanticColorsLight, semanticColorsDark} from "./semantic";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
const colors = {
|
||||
...commonColors,
|
||||
light: {
|
||||
...semanticColorsLight,
|
||||
},
|
||||
dark: {
|
||||
...semanticColorsDark,
|
||||
},
|
||||
};
|
||||
|
||||
export {colors, commonColors, semanticColorsLight, semanticColorsDark};
|
||||
|
||||
@ -50,16 +50,16 @@ const base: SemanticBaseColors = {
|
||||
contrastText: readableColor(twColors.zinc[900]),
|
||||
},
|
||||
content2: {
|
||||
DEFAULT: twColors.zinc[800],
|
||||
contrastText: readableColor(twColors.zinc[800]),
|
||||
},
|
||||
content3: {
|
||||
DEFAULT: twColors.zinc[700],
|
||||
contrastText: readableColor(twColors.zinc[700]),
|
||||
},
|
||||
content3: {
|
||||
DEFAULT: twColors.zinc[500],
|
||||
contrastText: readableColor(twColors.zinc[500]),
|
||||
},
|
||||
content4: {
|
||||
DEFAULT: twColors.zinc[400],
|
||||
contrastText: readableColor(twColors.zinc[400]),
|
||||
DEFAULT: twColors.zinc[600],
|
||||
contrastText: readableColor(twColors.zinc[600]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -40,7 +40,7 @@ const badge = tv({
|
||||
solid: {},
|
||||
flat: {},
|
||||
faded: {
|
||||
badge: "border-2",
|
||||
badge: "border-1.5",
|
||||
},
|
||||
shadow: {},
|
||||
},
|
||||
|
||||
@ -34,7 +34,7 @@ const button = tv({
|
||||
bordered: "border-2 !bg-transparent",
|
||||
light: "!bg-transparent",
|
||||
flat: "",
|
||||
faded: "border-2",
|
||||
faded: "border-1.5",
|
||||
shadow: "",
|
||||
ghost: "border-2 !bg-transparent",
|
||||
},
|
||||
|
||||
@ -50,7 +50,7 @@ const checkbox = tv({
|
||||
"data-[hover=true]:before:bg-neutral-100",
|
||||
],
|
||||
icon: "z-10 w-4 h-3 opacity-0 data-[checked=true]:opacity-100",
|
||||
label: "relative ml-1 text-foreground select-none",
|
||||
label: "relative text-foreground select-none",
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
@ -75,28 +75,28 @@ const checkbox = tv({
|
||||
},
|
||||
size: {
|
||||
xs: {
|
||||
wrapper: "w-3.5 h-3.5",
|
||||
label: "ml-1 text-xs",
|
||||
wrapper: "w-3.5 h-3.5 mr-1",
|
||||
label: "text-xs",
|
||||
icon: "w-3 h-2",
|
||||
},
|
||||
sm: {
|
||||
wrapper: "w-4 h-4",
|
||||
label: "ml-1 text-sm",
|
||||
wrapper: "w-4 h-4 mr-1",
|
||||
label: "text-sm",
|
||||
icon: "w-3 h-2",
|
||||
},
|
||||
md: {
|
||||
wrapper: "w-5 h-5",
|
||||
label: "ml-2 text-base",
|
||||
wrapper: "w-5 h-5 mr-2",
|
||||
label: "text-base",
|
||||
icon: "w-4 h-3",
|
||||
},
|
||||
lg: {
|
||||
wrapper: "w-6 h-6",
|
||||
label: "ml-2 text-lg",
|
||||
wrapper: "w-6 h-6 mr-2",
|
||||
label: "text-lg",
|
||||
icon: "w-5 h-4",
|
||||
},
|
||||
xl: {
|
||||
wrapper: "w-7 h-7",
|
||||
label: "ml-2 text-xl",
|
||||
wrapper: "w-7 h-7 mr-2",
|
||||
label: "text-xl",
|
||||
icon: "w-6 h-5",
|
||||
},
|
||||
},
|
||||
|
||||
@ -5,22 +5,22 @@ import {ringClasses, colorVariants} from "../utils";
|
||||
/**
|
||||
* Chip wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, label, dot, avatar, closeButton} = chip({...})
|
||||
* const {base, content, dot, avatar, closeButton} = chip({...})
|
||||
*
|
||||
* @example
|
||||
* <div className={base())}>
|
||||
* // left content
|
||||
* <span className={avatar()}/>
|
||||
* <svg className={dot()}/>
|
||||
* <label className={label()}>Default</label>
|
||||
* <span className={content()}>Default</span>
|
||||
* <svg className={closeButton()}>close button</svg>
|
||||
* // right content
|
||||
* </div>
|
||||
*/
|
||||
const chip = tv({
|
||||
slots: {
|
||||
base: ["relative", "inline-flex", "items-center", "justify-between", "box-border"],
|
||||
label: "flex-1 text-inherit select-none font-regular",
|
||||
base: ["relative", "max-w-fit", "inline-flex", "items-center", "justify-between", "box-border"],
|
||||
content: "flex-1 text-inherit select-none font-regular",
|
||||
dot: ["w-2", "h-2", "mx-1", "rounded-full"],
|
||||
avatar: "flex-shrink-0",
|
||||
closeButton: [
|
||||
@ -38,18 +38,18 @@ const chip = tv({
|
||||
variant: {
|
||||
solid: {},
|
||||
bordered: {
|
||||
base: "border-2 !bg-transparent",
|
||||
base: "border-1.5 !bg-transparent",
|
||||
},
|
||||
light: {
|
||||
base: "!bg-transparent",
|
||||
},
|
||||
flat: {},
|
||||
faded: {
|
||||
base: "border-2",
|
||||
base: "border-1.5",
|
||||
},
|
||||
shadow: {},
|
||||
dot: {
|
||||
base: "border-2 border-neutral text-foreground !bg-transparent",
|
||||
base: "border-1.5 border-neutral text-foreground !bg-transparent",
|
||||
},
|
||||
},
|
||||
color: {
|
||||
@ -75,31 +75,31 @@ const chip = tv({
|
||||
size: {
|
||||
xs: {
|
||||
base: "px-0.5 h-5 text-xs",
|
||||
label: "px-0.5",
|
||||
content: "px-1",
|
||||
closeButton: "text-sm",
|
||||
avatar: "w-3.5 h-3.5",
|
||||
},
|
||||
sm: {
|
||||
base: "px-1 h-6 text-sm",
|
||||
label: "px-1",
|
||||
content: "px-1",
|
||||
closeButton: "text-base",
|
||||
avatar: "w-4 h-4",
|
||||
},
|
||||
md: {
|
||||
base: "px-1 h-7 text-base",
|
||||
label: "px-1",
|
||||
content: "px-2",
|
||||
closeButton: "text-lg",
|
||||
avatar: "w-5 h-5",
|
||||
},
|
||||
lg: {
|
||||
base: "px-2 h-8 text-lg",
|
||||
label: "px-1",
|
||||
content: "px-2",
|
||||
closeButton: "text-xl",
|
||||
avatar: "w-6 h-6",
|
||||
},
|
||||
xl: {
|
||||
base: "px-2 h-9 text-xl",
|
||||
label: "px-1",
|
||||
content: "px-2",
|
||||
closeButton: "text-2xl",
|
||||
avatar: "w-7 h-7",
|
||||
},
|
||||
@ -118,7 +118,7 @@ const chip = tv({
|
||||
isOneChar: {
|
||||
true: {
|
||||
base: "px-0 justify-center",
|
||||
label: "px-0 flex-none",
|
||||
content: "px-0 flex-none",
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
|
||||
@ -29,7 +29,7 @@ const tooltip = tv({
|
||||
solid: "",
|
||||
bordered: "border-2 !bg-transparent",
|
||||
light: "!bg-transparent",
|
||||
faded: "border-2",
|
||||
faded: "border-1.5",
|
||||
flat: "",
|
||||
shadow: "",
|
||||
},
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export * from "./components";
|
||||
export * from "./utils";
|
||||
export * from "./colors";
|
||||
|
||||
export {tv} from "tailwind-variants";
|
||||
export type {VariantProps} from "tailwind-variants";
|
||||
|
||||
@ -5,12 +5,10 @@ module.exports = {
|
||||
content: [
|
||||
"../components/**/src/**/*.{js,jsx,ts,tsx}",
|
||||
"../components/**/stories/**/*.{js,jsx,ts,tsx}",
|
||||
"../core/theme/src/components/**/*.{js,jsx,ts,tsx}",
|
||||
"../core/theme/src/utils/**/*.{js,jsx,ts,tsx}",
|
||||
"../core/theme/stories/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
darkMode: "class",
|
||||
plugins: [nextui(
|
||||
{
|
||||
defaultTheme: 'dark'
|
||||
}
|
||||
)],
|
||||
plugins: [nextui()],
|
||||
};
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export const AvatarIcon = () => (
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export const AvatarIcon = (props: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
@ -7,6 +9,7 @@ export const AvatarIcon = () => (
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M12 2C9.38 2 7.25 4.13 7.25 6.75C7.25 9.32 9.26 11.4 11.88 11.49C11.96 11.48 12.04 11.48 12.1 11.49C12.12 11.49 12.13 11.49 12.15 11.49C12.16 11.49 12.16 11.49 12.17 11.49C14.73 11.4 16.74 9.32 16.75 6.75C16.75 4.13 14.62 2 12 2Z"
|
||||
|
||||
@ -1,5 +1,28 @@
|
||||
export const CheckIcon = () => (
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export interface CheckIconProps extends IconSvgProps {
|
||||
filled?: boolean;
|
||||
}
|
||||
|
||||
export const CheckIcon = ({filled = false, ...props}: CheckIconProps) =>
|
||||
filled ? (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM16.78 9.7L11.11 15.37C10.97 15.51 10.78 15.59 10.58 15.59C10.38 15.59 10.19 15.51 10.05 15.37L7.22 12.54C6.93 12.25 6.93 11.77 7.22 11.48C7.51 11.19 7.99 11.19 8.28 11.48L10.58 13.78L15.72 8.64C16.01 8.35 16.49 8.35 16.78 8.64C17.07 8.93 17.07 9.4 16.78 9.7Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
@ -9,6 +32,7 @@ export const CheckIcon = () => (
|
||||
strokeWidth={2}
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export const CloseFilledIcon = () => (
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export const CloseFilledIcon = (props: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
@ -6,6 +8,7 @@ export const CloseFilledIcon = () => (
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M12 2a10 10 0 1010 10A10.016 10.016 0 0012 2zm3.36 12.3a.754.754 0 010 1.06.748.748 0 01-1.06 0l-2.3-2.3-2.3 2.3a.748.748 0 01-1.06 0 .754.754 0 010-1.06l2.3-2.3-2.3-2.3A.75.75 0 019.7 8.64l2.3 2.3 2.3-2.3a.75.75 0 011.06 1.06l-2.3 2.3z"
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export const CloseIcon = () => (
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export const CloseIcon = (props: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
@ -11,6 +13,7 @@ export const CloseIcon = () => (
|
||||
strokeWidth={2}
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path d="M18 6L6 18M6 6l12 12" />
|
||||
</svg>
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export const CopyIcon = () => (
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export const CopyIcon = (props: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
@ -6,6 +8,7 @@ export const CopyIcon = () => (
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M20 2H10c-1.103 0-2 .897-2 2v4H4c-1.103 0-2 .897-2 2v10c0 1.103.897 2 2 2h10c1.103 0 2-.897 2-2v-4h4c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2zM4 20V10h10l.002 10H4zm16-6h-4v-4c0-1.103-.897-2-2-2h-4V4h10v10z"
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import * as React from "react";
|
||||
|
||||
interface IconProps {
|
||||
import {IconSvgProps} from "./types";
|
||||
interface IconProps extends IconSvgProps {
|
||||
fill?: string;
|
||||
filled?: boolean;
|
||||
size?: string | number;
|
||||
height?: string | number;
|
||||
width?: string | number;
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Sun: React.FC<IconProps> = ({fill, filled, size, height, width, ...props}) => {
|
||||
|
||||
3
packages/utilities/shared-icons/src/types.ts
Normal file
3
packages/utilities/shared-icons/src/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import {SVGProps} from "react";
|
||||
|
||||
export type IconSvgProps = SVGProps<SVGSVGElement>;
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -421,10 +421,14 @@ importers:
|
||||
|
||||
packages/components/checkbox:
|
||||
specifiers:
|
||||
'@nextui-org/chip': workspace:*
|
||||
'@nextui-org/dom-utils': workspace:*
|
||||
'@nextui-org/link': workspace:*
|
||||
'@nextui-org/shared-icons': workspace:*
|
||||
'@nextui-org/shared-utils': workspace:*
|
||||
'@nextui-org/system': workspace:*
|
||||
'@nextui-org/theme': workspace:*
|
||||
'@nextui-org/user': workspace:*
|
||||
'@react-aria/checkbox': ^3.8.0
|
||||
'@react-aria/focus': ^3.11.0
|
||||
'@react-aria/interactions': ^3.14.0
|
||||
@ -449,6 +453,10 @@ importers:
|
||||
'@react-stately/checkbox': 3.4.0_react@18.2.0
|
||||
'@react-stately/toggle': 3.5.0_react@18.2.0
|
||||
devDependencies:
|
||||
'@nextui-org/chip': link:../chip
|
||||
'@nextui-org/link': link:../link
|
||||
'@nextui-org/shared-icons': link:../../utilities/shared-icons
|
||||
'@nextui-org/user': link:../user
|
||||
'@react-types/checkbox': 3.4.2_react@18.2.0
|
||||
'@react-types/shared': 3.17.0_react@18.2.0
|
||||
clean-package: 2.2.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user