mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(radio-group): intial structure
This commit is contained in:
parent
aefad6676a
commit
d57bd1653d
@ -84,7 +84,7 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) {
|
||||
|
||||
const {labelProps, groupProps} = useReactAriaCheckboxGroup(
|
||||
{
|
||||
"aria-label": typeof label === "string" ? label : "Checkbox Group",
|
||||
"aria-label": typeof label === "string" ? label : otherProps["aria-label"],
|
||||
...otherProps,
|
||||
},
|
||||
groupState,
|
||||
|
||||
@ -24,15 +24,15 @@ interface Props extends HTMLNextUIProps<"label"> {
|
||||
* Ref to the DOM node.
|
||||
*/
|
||||
ref?: Ref<HTMLElement>;
|
||||
/**
|
||||
* The label of the checkbox.
|
||||
*/
|
||||
children?: ReactNode;
|
||||
/**
|
||||
* Whether the checkbox is disabled.
|
||||
* @default false
|
||||
*/
|
||||
isDisabled?: boolean;
|
||||
/**
|
||||
* The label of the checkbox.
|
||||
*/
|
||||
children?: ReactNode;
|
||||
/**
|
||||
* The icon to be displayed when the checkbox is checked.
|
||||
*/
|
||||
@ -117,6 +117,7 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
isRequired,
|
||||
onChange,
|
||||
"aria-label": arialabel,
|
||||
"aria-labelledby": otherProps["aria-labelledby"] || arialabel,
|
||||
};
|
||||
}, [isIndeterminate, isDisabled]);
|
||||
|
||||
@ -154,9 +155,8 @@ export function useCheckbox(props: UseCheckboxProps) {
|
||||
isDisabled,
|
||||
isFocusVisible,
|
||||
disableAnimation,
|
||||
className,
|
||||
}),
|
||||
[color, size, radius, lineThrough, isDisabled, isFocusVisible, disableAnimation, className],
|
||||
[color, size, radius, lineThrough, isDisabled, isFocusVisible, disableAnimation],
|
||||
);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
@ -37,8 +37,8 @@
|
||||
"react": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextui-org/theme": "workspace:*",
|
||||
"@nextui-org/dom-utils": "workspace:*",
|
||||
"@nextui-org/shared-css": "workspace:*",
|
||||
"@nextui-org/shared-utils": "workspace:*",
|
||||
"@nextui-org/system": "workspace:*",
|
||||
"@react-aria/focus": "^3.11.0",
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import Radio from "./radio";
|
||||
import RadioGroup from "./radio-group";
|
||||
|
||||
// export types
|
||||
export type {RadioProps} from "./radio";
|
||||
export type {RadioGroupProps} from "./radio-group";
|
||||
|
||||
// export hooks
|
||||
export {useRadio} from "./use-radio";
|
||||
export {useRadioGroup} from "./use-radio-group";
|
||||
|
||||
// export component
|
||||
export {default as Radio} from "./radio";
|
||||
export {Radio, RadioGroup};
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
import {styled} from "@nextui-org/system";
|
||||
|
||||
import {StyledRadio} from "./radio.styles";
|
||||
|
||||
export const StyledRadioGroupContainer = styled("div", {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
variants: {
|
||||
isRow: {
|
||||
true: {
|
||||
mt: 0,
|
||||
flexDirection: "row",
|
||||
[`& ${StyledRadio}:not(:last-child)`]: {
|
||||
mr: "$$radioSize",
|
||||
},
|
||||
},
|
||||
false: {
|
||||
mr: 0,
|
||||
flexDirection: "column",
|
||||
[`& ${StyledRadio}:not(:first-child)`]: {
|
||||
mt: "$$radioSize",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const StyledRadioGroup = styled("div", {
|
||||
border: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
fd: "column",
|
||||
variants: {
|
||||
size: {
|
||||
xs: {
|
||||
$$radioGroupGap: "$space$7",
|
||||
},
|
||||
sm: {
|
||||
$$radioGroupGap: "$space$8",
|
||||
},
|
||||
md: {
|
||||
$$radioGroupGap: "$space$9",
|
||||
},
|
||||
lg: {
|
||||
$$radioGroupGap: "$space$10",
|
||||
},
|
||||
xl: {
|
||||
$$radioGroupGap: "$space$11",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const StyledRadioGroupLabel = styled("label", {
|
||||
d: "block",
|
||||
fontWeight: "$normal",
|
||||
fontSize: "calc($$checkboxSize * 0.9)",
|
||||
color: "$accents8",
|
||||
mb: "$3",
|
||||
});
|
||||
@ -1,43 +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 {RadioGroupProvider} from "./radio-group-context";
|
||||
import {
|
||||
StyledRadioGroup,
|
||||
StyledRadioGroupLabel,
|
||||
StyledRadioGroupContainer,
|
||||
} from "./radio-group.styles";
|
||||
import {UseRadioGroupProps, useRadioGroup} from "./use-radio-group";
|
||||
|
||||
export interface RadioGroupProps extends UseRadioGroupProps {}
|
||||
export interface RadioGroupProps extends Omit<UseRadioGroupProps, "ref"> {}
|
||||
|
||||
const RadioGroup = forwardRef<RadioGroupProps, "div">((props, ref) => {
|
||||
const {className, children, orientation, label, context, groupProps, labelProps, ...otherProps} =
|
||||
useRadioGroup(props);
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
const {Component, children, label, context, getGroupProps, getLabelProps, getWrapperProps} =
|
||||
useRadioGroup({ref, ...props});
|
||||
|
||||
return (
|
||||
<StyledRadioGroup
|
||||
ref={domRef}
|
||||
className={clsx("nextui-radio-group", className)}
|
||||
{...mergeProps(groupProps, otherProps)}
|
||||
>
|
||||
{label && (
|
||||
<StyledRadioGroupLabel className="nextui-radio-group-label" {...labelProps}>
|
||||
{label}
|
||||
</StyledRadioGroupLabel>
|
||||
)}
|
||||
<StyledRadioGroupContainer
|
||||
className="nextui-radio-group-items"
|
||||
isRow={orientation === "horizontal"}
|
||||
role="presentation"
|
||||
>
|
||||
<Component {...getGroupProps()}>
|
||||
{label && <label {...getLabelProps()}>{label}</label>}
|
||||
<div {...getWrapperProps()}>
|
||||
<RadioGroupProvider value={context}>{children}</RadioGroupProvider>
|
||||
</StyledRadioGroupContainer>
|
||||
</StyledRadioGroup>
|
||||
</div>
|
||||
</Component>
|
||||
);
|
||||
});
|
||||
|
||||
@ -45,6 +24,4 @@ if (__DEV__) {
|
||||
RadioGroup.displayName = "NextUI.RadioGroup";
|
||||
}
|
||||
|
||||
RadioGroup.toString = () => ".nextui-radio-group";
|
||||
|
||||
export default RadioGroup;
|
||||
|
||||
@ -1,245 +1,245 @@
|
||||
import {styled} from "@nextui-org/system";
|
||||
import {cssFocusVisible} from "@nextui-org/shared-css";
|
||||
// import {styled} from "@nextui-org/system";
|
||||
// import {cssFocusVisible} from "@nextui-org/shared-css";
|
||||
|
||||
export const StyledRadioText = styled("span", {
|
||||
fontSize: "$$radioSize",
|
||||
us: "none",
|
||||
d: "inline-flex",
|
||||
ai: "center",
|
||||
variants: {
|
||||
color: {
|
||||
default: {
|
||||
color: "$text",
|
||||
},
|
||||
primary: {
|
||||
color: "$primary",
|
||||
},
|
||||
secondary: {
|
||||
color: "$secondary",
|
||||
},
|
||||
success: {
|
||||
color: "$success",
|
||||
},
|
||||
warning: {
|
||||
color: "$warning",
|
||||
},
|
||||
error: {
|
||||
color: "$error",
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
color: "$accents5",
|
||||
},
|
||||
},
|
||||
isInvalid: {
|
||||
true: {
|
||||
color: "$error",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// export const StyledRadioText = styled("span", {
|
||||
// fontSize: "$$radioSize",
|
||||
// us: "none",
|
||||
// d: "inline-flex",
|
||||
// ai: "center",
|
||||
// variants: {
|
||||
// color: {
|
||||
// default: {
|
||||
// color: "$text",
|
||||
// },
|
||||
// primary: {
|
||||
// color: "$primary",
|
||||
// },
|
||||
// secondary: {
|
||||
// color: "$secondary",
|
||||
// },
|
||||
// success: {
|
||||
// color: "$success",
|
||||
// },
|
||||
// warning: {
|
||||
// color: "$warning",
|
||||
// },
|
||||
// error: {
|
||||
// color: "$error",
|
||||
// },
|
||||
// },
|
||||
// isDisabled: {
|
||||
// true: {
|
||||
// color: "$accents5",
|
||||
// },
|
||||
// },
|
||||
// isInvalid: {
|
||||
// true: {
|
||||
// color: "$error",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
export const StyledRadioPoint = styled(
|
||||
"span",
|
||||
{
|
||||
size: "$$radioSize",
|
||||
br: "$$radioRadii",
|
||||
position: "relative",
|
||||
d: "inline-block",
|
||||
mr: "calc($$radioSize * 0.375)",
|
||||
"&:after": {
|
||||
content: "",
|
||||
d: "block",
|
||||
position: "absolute",
|
||||
size: "$$radioSize",
|
||||
br: "$$radioRadii",
|
||||
boxSizing: "border-box",
|
||||
border: "2px solid $border",
|
||||
},
|
||||
},
|
||||
cssFocusVisible,
|
||||
);
|
||||
// export const StyledRadioPoint = styled(
|
||||
// "span",
|
||||
// {
|
||||
// size: "$$radioSize",
|
||||
// br: "$$radioRadii",
|
||||
// position: "relative",
|
||||
// d: "inline-block",
|
||||
// mr: "calc($$radioSize * 0.375)",
|
||||
// "&:after": {
|
||||
// content: "",
|
||||
// d: "block",
|
||||
// position: "absolute",
|
||||
// size: "$$radioSize",
|
||||
// br: "$$radioRadii",
|
||||
// boxSizing: "border-box",
|
||||
// border: "2px solid $border",
|
||||
// },
|
||||
// },
|
||||
// cssFocusVisible,
|
||||
// );
|
||||
|
||||
export const StyledRadio = styled("label", {
|
||||
d: "flex",
|
||||
w: "initial",
|
||||
ai: "flex-start",
|
||||
position: "relative",
|
||||
fd: "column",
|
||||
jc: "flex-start",
|
||||
cursor: "pointer",
|
||||
"@motion": {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
transition: "none",
|
||||
"&:after": {
|
||||
transition: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
default: {
|
||||
$$radioColor: "$colors$primary",
|
||||
$$radioColorHover: "$colors$primarySolidHover",
|
||||
},
|
||||
primary: {
|
||||
$$radioColor: "$colors$primary",
|
||||
$$radioColorHover: "$colors$primarySolidHover",
|
||||
},
|
||||
secondary: {
|
||||
$$radioColor: "$colors$secondary",
|
||||
$$radioColorHover: "$colors$secondarySolidHover",
|
||||
},
|
||||
success: {
|
||||
$$radioColor: "$colors$success",
|
||||
$$radioColorHover: "$colors$successSolidHover",
|
||||
},
|
||||
warning: {
|
||||
$$radioColor: "$colors$warning",
|
||||
$$radioColorHover: "$colors$warningSolidHover",
|
||||
},
|
||||
error: {
|
||||
$$radioColor: "$colors$error",
|
||||
$$radioColorHover: "$colors$errorSolidHover",
|
||||
},
|
||||
},
|
||||
size: {
|
||||
xs: {
|
||||
$$radioSize: "$space$7",
|
||||
},
|
||||
sm: {
|
||||
$$radioSize: "$space$8",
|
||||
},
|
||||
md: {
|
||||
$$radioSize: "$space$9",
|
||||
},
|
||||
lg: {
|
||||
$$radioSize: "$space$10",
|
||||
},
|
||||
xl: {
|
||||
$$radioSize: "$space$11",
|
||||
},
|
||||
},
|
||||
isHovered: {
|
||||
true: {},
|
||||
},
|
||||
isInvalid: {
|
||||
true: {
|
||||
$$radioColor: "$colors$error",
|
||||
$$radioColorHover: "$colors$errorSolidHover",
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
"&:after": {
|
||||
borderColor: "$colors$error",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
cursor: "not-allowed",
|
||||
$$radioColor: "$colors$accents4",
|
||||
},
|
||||
},
|
||||
isSquared: {
|
||||
true: {
|
||||
$$radioRadii: "$radii$squared",
|
||||
},
|
||||
false: {
|
||||
$$radioRadii: "$radii$rounded",
|
||||
},
|
||||
},
|
||||
isChecked: {
|
||||
true: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
"&:after": {
|
||||
border: "calc($$radioSize * 0.34) solid $$radioColor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
disableAnimation: {
|
||||
true: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
transition: "none",
|
||||
"&:after": {
|
||||
transition: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
false: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
transition: "$default",
|
||||
"&:after": {
|
||||
transition: "$default",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
// isChecked && isHovered
|
||||
{
|
||||
isChecked: true,
|
||||
isHovered: true,
|
||||
css: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
"&:after": {
|
||||
border: "calc($$radioSize * 0.34) solid $$radioColorHover",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// isChecked && isDisabled & isHovered
|
||||
{
|
||||
isChecked: true,
|
||||
isDisabled: true,
|
||||
isHovered: true,
|
||||
css: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
"&:after": {
|
||||
border: "calc($$radioSize * 0.34) solid $$radioColor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// !isChecked && !isDisabled && isHovered
|
||||
{
|
||||
isChecked: false,
|
||||
isDisabled: false,
|
||||
isHovered: true,
|
||||
css: {
|
||||
[`& ${StyledRadioPoint}`]: {
|
||||
bg: "$border",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
// export const StyledRadio = styled("label", {
|
||||
// d: "flex",
|
||||
// w: "initial",
|
||||
// ai: "flex-start",
|
||||
// position: "relative",
|
||||
// fd: "column",
|
||||
// jc: "flex-start",
|
||||
// cursor: "pointer",
|
||||
// "@motion": {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// transition: "none",
|
||||
// "&:after": {
|
||||
// transition: "none",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// variants: {
|
||||
// color: {
|
||||
// default: {
|
||||
// $$radioColor: "$colors$primary",
|
||||
// $$radioColorHover: "$colors$primarySolidHover",
|
||||
// },
|
||||
// primary: {
|
||||
// $$radioColor: "$colors$primary",
|
||||
// $$radioColorHover: "$colors$primarySolidHover",
|
||||
// },
|
||||
// secondary: {
|
||||
// $$radioColor: "$colors$secondary",
|
||||
// $$radioColorHover: "$colors$secondarySolidHover",
|
||||
// },
|
||||
// success: {
|
||||
// $$radioColor: "$colors$success",
|
||||
// $$radioColorHover: "$colors$successSolidHover",
|
||||
// },
|
||||
// warning: {
|
||||
// $$radioColor: "$colors$warning",
|
||||
// $$radioColorHover: "$colors$warningSolidHover",
|
||||
// },
|
||||
// error: {
|
||||
// $$radioColor: "$colors$error",
|
||||
// $$radioColorHover: "$colors$errorSolidHover",
|
||||
// },
|
||||
// },
|
||||
// size: {
|
||||
// xs: {
|
||||
// $$radioSize: "$space$7",
|
||||
// },
|
||||
// sm: {
|
||||
// $$radioSize: "$space$8",
|
||||
// },
|
||||
// md: {
|
||||
// $$radioSize: "$space$9",
|
||||
// },
|
||||
// lg: {
|
||||
// $$radioSize: "$space$10",
|
||||
// },
|
||||
// xl: {
|
||||
// $$radioSize: "$space$11",
|
||||
// },
|
||||
// },
|
||||
// isHovered: {
|
||||
// true: {},
|
||||
// },
|
||||
// isInvalid: {
|
||||
// true: {
|
||||
// $$radioColor: "$colors$error",
|
||||
// $$radioColorHover: "$colors$errorSolidHover",
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// "&:after": {
|
||||
// borderColor: "$colors$error",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// isDisabled: {
|
||||
// true: {
|
||||
// cursor: "not-allowed",
|
||||
// $$radioColor: "$colors$accents4",
|
||||
// },
|
||||
// },
|
||||
// isSquared: {
|
||||
// true: {
|
||||
// $$radioRadii: "$radii$squared",
|
||||
// },
|
||||
// false: {
|
||||
// $$radioRadii: "$radii$rounded",
|
||||
// },
|
||||
// },
|
||||
// isChecked: {
|
||||
// true: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// "&:after": {
|
||||
// border: "calc($$radioSize * 0.34) solid $$radioColor",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// disableAnimation: {
|
||||
// true: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// transition: "none",
|
||||
// "&:after": {
|
||||
// transition: "none",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// false: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// transition: "$default",
|
||||
// "&:after": {
|
||||
// transition: "$default",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// compoundVariants: [
|
||||
// // isChecked && isHovered
|
||||
// {
|
||||
// isChecked: true,
|
||||
// isHovered: true,
|
||||
// css: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// "&:after": {
|
||||
// border: "calc($$radioSize * 0.34) solid $$radioColorHover",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// // isChecked && isDisabled & isHovered
|
||||
// {
|
||||
// isChecked: true,
|
||||
// isDisabled: true,
|
||||
// isHovered: true,
|
||||
// css: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// "&:after": {
|
||||
// border: "calc($$radioSize * 0.34) solid $$radioColor",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// // !isChecked && !isDisabled && isHovered
|
||||
// {
|
||||
// isChecked: false,
|
||||
// isDisabled: false,
|
||||
// isHovered: true,
|
||||
// css: {
|
||||
// [`& ${StyledRadioPoint}`]: {
|
||||
// bg: "$border",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
|
||||
export const StyledRadioDescription = styled("span", {
|
||||
color: "$accents7",
|
||||
fontSize: "calc($$radioSize * 0.85)",
|
||||
paddingLeft: "calc($$radioSize + $$radioSize * 0.375)",
|
||||
variants: {
|
||||
isInvalid: {
|
||||
true: {
|
||||
color: "$red500",
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
color: "$accents5",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// export const StyledRadioDescription = styled("span", {
|
||||
// color: "$accents7",
|
||||
// fontSize: "calc($$radioSize * 0.85)",
|
||||
// paddingLeft: "calc($$radioSize + $$radioSize * 0.375)",
|
||||
// variants: {
|
||||
// isInvalid: {
|
||||
// true: {
|
||||
// color: "$red500",
|
||||
// },
|
||||
// },
|
||||
// isDisabled: {
|
||||
// true: {
|
||||
// color: "$accents5",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
export const StyledRadioContainer = styled("div", {
|
||||
w: "initial",
|
||||
position: "relative",
|
||||
d: "flex",
|
||||
fd: "row",
|
||||
ai: "center",
|
||||
jc: "flex-start",
|
||||
});
|
||||
// export const StyledRadioContainer = styled("div", {
|
||||
// w: "initial",
|
||||
// position: "relative",
|
||||
// d: "flex",
|
||||
// fd: "row",
|
||||
// ai: "center",
|
||||
// jc: "flex-start",
|
||||
// });
|
||||
|
||||
@ -1,108 +1,42 @@
|
||||
import {forwardRef} from "@nextui-org/system";
|
||||
import {useFocusableRef} 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 {VisuallyHidden} from "@react-aria/visually-hidden";
|
||||
|
||||
import RadioGroup from "./radio-group";
|
||||
import {
|
||||
StyledRadio,
|
||||
StyledRadioContainer,
|
||||
StyledRadioPoint,
|
||||
StyledRadioText,
|
||||
StyledRadioDescription,
|
||||
} from "./radio.styles";
|
||||
import {UseRadioProps, useRadio} from "./use-radio";
|
||||
|
||||
export interface RadioProps extends UseRadioProps {}
|
||||
|
||||
type CompoundRadio = {
|
||||
Group: typeof RadioGroup;
|
||||
};
|
||||
|
||||
const Radio = forwardRef<RadioProps, "label", CompoundRadio>((props, ref) => {
|
||||
const {className, as, css, children, label, description, ...radioProps} = props;
|
||||
export interface RadioProps extends Omit<UseRadioProps, "ref"> {}
|
||||
|
||||
const Radio = forwardRef<RadioProps, "label">((props, ref) => {
|
||||
const {
|
||||
size,
|
||||
state,
|
||||
color,
|
||||
inputRef,
|
||||
labelColor,
|
||||
isHovered,
|
||||
isSquared,
|
||||
isInvalid,
|
||||
isDisabled,
|
||||
disableAnimation,
|
||||
isFocusVisible,
|
||||
focusProps,
|
||||
hoverProps,
|
||||
inputProps,
|
||||
isRequired,
|
||||
} = useRadio({...radioProps, children: children ?? label});
|
||||
|
||||
const domRef = useFocusableRef(ref, inputRef);
|
||||
Component,
|
||||
children,
|
||||
slots,
|
||||
styles,
|
||||
description,
|
||||
getBaseProps,
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
getLabelProps,
|
||||
} = useRadio({ref, ...props});
|
||||
|
||||
return (
|
||||
<StyledRadio
|
||||
ref={domRef}
|
||||
{...hoverProps}
|
||||
as={as}
|
||||
className={clsx("nextui-radio", className)}
|
||||
color={color}
|
||||
css={css}
|
||||
data-state={state}
|
||||
disableAnimation={disableAnimation}
|
||||
isChecked={inputProps.checked}
|
||||
isDisabled={isDisabled}
|
||||
isHovered={isHovered}
|
||||
isInvalid={isInvalid}
|
||||
isSquared={isSquared}
|
||||
size={size}
|
||||
>
|
||||
<StyledRadioContainer className="nextui-radio-container">
|
||||
<StyledRadioPoint
|
||||
className="nextui-radio-point"
|
||||
isFocusVisible={isFocusVisible}
|
||||
{...focusProps}
|
||||
>
|
||||
<VisuallyHidden>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="nextui-radio-input"
|
||||
required={isRequired}
|
||||
{...mergeProps(inputProps, focusProps)}
|
||||
/>
|
||||
</VisuallyHidden>
|
||||
</StyledRadioPoint>
|
||||
<StyledRadioText
|
||||
className="nextui-radio-label"
|
||||
color={labelColor}
|
||||
isDisabled={isDisabled}
|
||||
isInvalid={isInvalid}
|
||||
>
|
||||
{children}
|
||||
</StyledRadioText>
|
||||
</StyledRadioContainer>
|
||||
<Component {...getBaseProps()}>
|
||||
<VisuallyHidden>
|
||||
<input {...getInputProps()} />
|
||||
</VisuallyHidden>
|
||||
<span {...getWrapperProps()}>
|
||||
<span className={slots.point({class: styles?.point})} />
|
||||
</span>
|
||||
{children && <span {...getLabelProps()}>{children}</span>}
|
||||
{description && (
|
||||
<StyledRadioDescription
|
||||
className="nextui-radio-description"
|
||||
isDisabled={isDisabled}
|
||||
isInvalid={isInvalid}
|
||||
>
|
||||
{description}
|
||||
</StyledRadioDescription>
|
||||
<span className={slots.description({class: styles?.description})}>{description}</span>
|
||||
)}
|
||||
</StyledRadio>
|
||||
</Component>
|
||||
);
|
||||
});
|
||||
|
||||
Radio.Group = RadioGroup;
|
||||
|
||||
if (__DEV__) {
|
||||
Radio.displayName = "NextUI.Radio";
|
||||
}
|
||||
|
||||
Radio.toString = () => ".nextui-radio";
|
||||
|
||||
export default Radio;
|
||||
|
||||
@ -1,67 +1,88 @@
|
||||
import type {AriaRadioGroupProps} from "@react-types/radio";
|
||||
import type {Orientation} from "@react-types/shared";
|
||||
import type {NormalSizes, SimpleColors} from "@nextui-org/shared-utils";
|
||||
import type {ReactRef} from "@nextui-org/shared-utils";
|
||||
import type {RadioGroupSlots, SlotsToClasses} from "@nextui-org/theme";
|
||||
|
||||
import {useMemo, HTMLAttributes} from "react";
|
||||
import {radioGroup} from "@nextui-org/theme";
|
||||
import {useMemo} from "react";
|
||||
import {RadioGroupState, useRadioGroupState} from "@react-stately/radio";
|
||||
import {useRadioGroup as useReactAriaRadioGroup} from "@react-aria/radio";
|
||||
import {HTMLNextUIProps} from "@nextui-org/system";
|
||||
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx} from "@nextui-org/shared-utils";
|
||||
import {mergeProps} from "@react-aria/utils";
|
||||
|
||||
interface Props extends HTMLNextUIProps<"div"> {
|
||||
import {RadioProps} from "./index";
|
||||
|
||||
interface Props extends HTMLNextUIProps<"div", AriaRadioGroupProps> {
|
||||
/**
|
||||
* The color of the radios.
|
||||
* @default "default"
|
||||
* Ref to the DOM node.
|
||||
*/
|
||||
color?: SimpleColors;
|
||||
/**
|
||||
* The color of the radios's label.
|
||||
* @default "default"
|
||||
*/
|
||||
labelColor?: SimpleColors;
|
||||
/**
|
||||
* The size of the radios.
|
||||
* @default "md"
|
||||
*/
|
||||
size?: NormalSizes;
|
||||
ref?: ReactRef<HTMLDivElement | null>;
|
||||
/**
|
||||
* The axis the radio 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
|
||||
* <RadioGroup styles={{
|
||||
* base:"base-classes",
|
||||
* label: "label-classes",
|
||||
* wrapper: "wrapper-classes", // radios wrapper
|
||||
* }} >
|
||||
* // radios
|
||||
* </RadioGroup>
|
||||
* ```
|
||||
*/
|
||||
styles?: SlotsToClasses<RadioGroupSlots>;
|
||||
}
|
||||
|
||||
export type UseRadioGroupProps = AriaRadioGroupProps & Props;
|
||||
|
||||
interface IRadioGroupAria {
|
||||
/** Props for the radio group wrapper element. */
|
||||
radioGroupProps: Omit<HTMLAttributes<HTMLElement>, "css">;
|
||||
/** Props for the radio group's visible label (if any). */
|
||||
labelProps: Omit<HTMLAttributes<HTMLElement>, "css">;
|
||||
}
|
||||
export type UseRadioGroupProps = Props &
|
||||
Pick<RadioProps, "color" | "size" | "radius" | "isDisabled" | "disableAnimation">;
|
||||
|
||||
export type ContextType = {
|
||||
groupState: RadioGroupState;
|
||||
isRequired?: UseRadioGroupProps["isRequired"];
|
||||
color?: UseRadioGroupProps["color"];
|
||||
labelColor?: UseRadioGroupProps["labelColor"];
|
||||
size?: UseRadioGroupProps["size"];
|
||||
validationState?: UseRadioGroupProps["validationState"];
|
||||
color?: RadioProps["color"];
|
||||
size?: RadioProps["size"];
|
||||
radius?: RadioProps["radius"];
|
||||
isDisabled?: RadioProps["isDisabled"];
|
||||
disableAnimation?: RadioProps["disableAnimation"];
|
||||
};
|
||||
|
||||
export function useRadioGroup(props: UseRadioGroupProps) {
|
||||
const {
|
||||
as,
|
||||
ref,
|
||||
styles,
|
||||
children,
|
||||
label,
|
||||
size = "md",
|
||||
color = "default",
|
||||
labelColor = "default",
|
||||
color = "primary",
|
||||
radius = "full",
|
||||
isDisabled = false,
|
||||
disableAnimation = false,
|
||||
orientation = "vertical",
|
||||
isRequired,
|
||||
validationState,
|
||||
className,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const Component = as || "div";
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
const otherPropsWithOrientation = useMemo<AriaRadioGroupProps>(() => {
|
||||
return {
|
||||
...otherProps,
|
||||
"aria-label": typeof label === "string" ? label : otherProps["aria-label"],
|
||||
isRequired,
|
||||
orientation,
|
||||
};
|
||||
@ -69,21 +90,61 @@ export function useRadioGroup(props: UseRadioGroupProps) {
|
||||
|
||||
const groupState = useRadioGroupState(otherPropsWithOrientation);
|
||||
|
||||
const {labelProps, radioGroupProps: groupProps}: IRadioGroupAria = useReactAriaRadioGroup(
|
||||
const {labelProps, radioGroupProps: groupProps} = useReactAriaRadioGroup(
|
||||
otherPropsWithOrientation,
|
||||
groupState,
|
||||
);
|
||||
|
||||
const context: ContextType = {
|
||||
size,
|
||||
color,
|
||||
labelColor,
|
||||
groupState,
|
||||
isRequired,
|
||||
validationState,
|
||||
const context: ContextType = useMemo(
|
||||
() => ({
|
||||
size,
|
||||
color,
|
||||
radius,
|
||||
groupState,
|
||||
isRequired,
|
||||
validationState,
|
||||
isDisabled,
|
||||
disableAnimation,
|
||||
}),
|
||||
[size, color, radius, groupState, isRequired, validationState, isDisabled, disableAnimation],
|
||||
);
|
||||
|
||||
const slots = useMemo(() => radioGroup(), []);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
const getGroupProps: PropGetter = () => {
|
||||
return {
|
||||
ref: domRef,
|
||||
className: slots.base({class: baseStyles}),
|
||||
...mergeProps(groupProps, otherProps),
|
||||
};
|
||||
};
|
||||
|
||||
return {size, orientation, labelProps, groupProps, context, ...otherProps};
|
||||
const getLabelProps: PropGetter = () => {
|
||||
return {
|
||||
className: slots.label({class: styles?.label}),
|
||||
...labelProps,
|
||||
};
|
||||
};
|
||||
|
||||
const getWrapperProps: PropGetter = () => {
|
||||
return {
|
||||
className: slots.wrapper({class: styles?.wrapper}),
|
||||
role: "presentation",
|
||||
"data-orientation": orientation,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
Component,
|
||||
children,
|
||||
label,
|
||||
context,
|
||||
getGroupProps,
|
||||
getLabelProps,
|
||||
getWrapperProps,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseRadioGroupReturn = ReturnType<typeof useRadioGroup>;
|
||||
|
||||
@ -1,63 +1,69 @@
|
||||
import type {AriaRadioProps} from "@react-types/radio";
|
||||
|
||||
import {RadioVariantProps, RadioSlots, SlotsToClasses, radio} from "@nextui-org/theme";
|
||||
import {Ref, ReactNode, useCallback} from "react";
|
||||
import {useMemo, useRef} from "react";
|
||||
import {useFocusRing} from "@react-aria/focus";
|
||||
import {useHover} from "@react-aria/interactions";
|
||||
import {useRadio as useReactAriaRadio} from "@react-aria/radio";
|
||||
import {HTMLNextUIProps} from "@nextui-org/system";
|
||||
import {NormalSizes, SimpleColors, __DEV__, warn} from "@nextui-org/shared-utils";
|
||||
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system";
|
||||
import {__DEV__, warn, clsx, dataAttr} from "@nextui-org/shared-utils";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {mergeProps} from "@react-aria/utils";
|
||||
|
||||
import {useRadioGroupContext} from "./radio-group-context";
|
||||
|
||||
interface Props extends HTMLNextUIProps<"label"> {
|
||||
/**
|
||||
* The content to display as the label.
|
||||
* Ref to the DOM node.
|
||||
*/
|
||||
label?: string;
|
||||
ref?: Ref<HTMLElement>;
|
||||
/**
|
||||
* The color of the radio.
|
||||
* @default "default"
|
||||
* The label of the checkbox.
|
||||
*/
|
||||
color?: SimpleColors;
|
||||
/**
|
||||
* The color of the label.
|
||||
* @default "default"
|
||||
*/
|
||||
labelColor?: SimpleColors;
|
||||
/**
|
||||
* The size of the radio.
|
||||
* @default "md"
|
||||
*/
|
||||
size?: NormalSizes;
|
||||
children?: ReactNode;
|
||||
/**
|
||||
* The radio description text.
|
||||
*/
|
||||
description?: string;
|
||||
description?: string | ReactNode;
|
||||
/**
|
||||
* Whether the radio is squared.
|
||||
* @default false
|
||||
* 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
|
||||
* <Radio styles={{
|
||||
* base:"base-classes",
|
||||
* wrapper: "wrapper-classes",
|
||||
* point: "control-classes", // inner circle
|
||||
* label: "label-classes",
|
||||
* description: "description-classes",
|
||||
* }} />
|
||||
* ```
|
||||
*/
|
||||
isSquared?: boolean;
|
||||
/**
|
||||
* Whether the radio has animations.
|
||||
* @default false
|
||||
*/
|
||||
disableAnimation?: boolean;
|
||||
styles?: SlotsToClasses<RadioSlots>;
|
||||
}
|
||||
|
||||
export type UseRadioProps = Props & AriaRadioProps;
|
||||
export type UseRadioProps = Props &
|
||||
Omit<AriaRadioProps, keyof RadioVariantProps> &
|
||||
Omit<RadioVariantProps, "isFocusVisible">;
|
||||
|
||||
export function useRadio(props: UseRadioProps) {
|
||||
const groupContext = useRadioGroupContext();
|
||||
|
||||
const {
|
||||
as,
|
||||
ref,
|
||||
styles,
|
||||
children,
|
||||
description,
|
||||
size = groupContext?.size ?? "md",
|
||||
color = groupContext?.color ?? "default",
|
||||
labelColor = groupContext?.labelColor ?? "default",
|
||||
color = groupContext?.color ?? "primary",
|
||||
radius = groupContext?.radius ?? "full",
|
||||
isDisabled: isDisabledProp = groupContext?.isDisabled ?? false,
|
||||
disableAnimation = groupContext?.disableAnimation ?? false,
|
||||
autoFocus = false,
|
||||
isSquared = false,
|
||||
isDisabled: isDisabledProp = false,
|
||||
disableAnimation = false,
|
||||
className,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
@ -70,55 +76,121 @@ export function useRadio(props: UseRadioProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const Component = as || "label";
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const {inputProps} = useReactAriaRadio(
|
||||
{
|
||||
...otherProps,
|
||||
...groupContext,
|
||||
isDisabled: isDisabledProp,
|
||||
},
|
||||
groupContext.groupState,
|
||||
inputRef,
|
||||
);
|
||||
|
||||
const isDisabled = useMemo(() => inputProps.disabled ?? false, [inputProps.disabled]);
|
||||
const isDisabled = useMemo(() => !!isDisabledProp, [isDisabledProp]);
|
||||
const isRequired = useMemo(() => groupContext.isRequired ?? false, [groupContext.isRequired]);
|
||||
const isInvalid = useMemo(
|
||||
() => groupContext.validationState === "invalid",
|
||||
[groupContext.validationState],
|
||||
);
|
||||
|
||||
const ariaRadioProps = useMemo(() => {
|
||||
const arialabel =
|
||||
otherProps["aria-label"] || typeof children === "string" ? (children as string) : undefined;
|
||||
const ariaDescribedBy =
|
||||
otherProps["aria-describedby"] || typeof description === "string"
|
||||
? (description as string)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
isDisabled,
|
||||
isRequired,
|
||||
"aria-label": arialabel,
|
||||
"aria-labelledby": otherProps["aria-labelledby"] || arialabel,
|
||||
"aria-describedby": otherProps["aria-describedby"] || ariaDescribedBy,
|
||||
};
|
||||
}, [isDisabled, isRequired]);
|
||||
|
||||
const {inputProps} = useReactAriaRadio(
|
||||
{
|
||||
...otherProps,
|
||||
...groupContext,
|
||||
...ariaRadioProps,
|
||||
},
|
||||
groupContext.groupState,
|
||||
inputRef,
|
||||
);
|
||||
|
||||
const {hoverProps, isHovered} = useHover({isDisabled});
|
||||
|
||||
const {focusProps, isFocusVisible} = useFocusRing({
|
||||
const {focusProps, isFocused, isFocusVisible} = useFocusRing({
|
||||
autoFocus,
|
||||
});
|
||||
|
||||
const state = useMemo(() => {
|
||||
if (isHovered) return "hovered";
|
||||
if (isDisabled) return "disabled";
|
||||
const slots = useMemo(
|
||||
() =>
|
||||
radio({
|
||||
color,
|
||||
size,
|
||||
radius,
|
||||
isDisabled,
|
||||
isFocusVisible,
|
||||
disableAnimation,
|
||||
}),
|
||||
[color, size, radius, isDisabled, isFocusVisible, disableAnimation],
|
||||
);
|
||||
|
||||
return inputProps.checked ? "checked" : "uncheked";
|
||||
}, [isDisabled, inputProps.checked, isHovered]);
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
const getBaseProps: PropGetter = () => {
|
||||
return {
|
||||
ref: domRef,
|
||||
className: slots.base({class: baseStyles}),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
...mergeProps(hoverProps, otherProps),
|
||||
};
|
||||
};
|
||||
|
||||
const getWrapperProps: PropGetter = () => {
|
||||
return {
|
||||
"data-active": dataAttr(inputProps.checked),
|
||||
"data-hover": dataAttr(isHovered),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-focus": dataAttr(isFocused),
|
||||
"data-focus-visible": dataAttr(isFocused && isFocusVisible),
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
"data-readonly": dataAttr(inputProps.readOnly),
|
||||
"aria-required": dataAttr(isRequired),
|
||||
"aria-hidden": true,
|
||||
className: clsx(slots.wrapper({class: styles?.wrapper})),
|
||||
};
|
||||
};
|
||||
|
||||
const getInputProps: PropGetter = () => {
|
||||
return {
|
||||
ref: inputRef,
|
||||
"data-readonly": dataAttr(inputProps.readOnly),
|
||||
...mergeProps(inputProps, focusProps),
|
||||
};
|
||||
};
|
||||
|
||||
const getLabelProps: PropGetter = useCallback(
|
||||
() => ({
|
||||
"data-disabled": dataAttr(isDisabled),
|
||||
"data-checked": dataAttr(inputProps.checked),
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
className: slots.label({class: styles?.label}),
|
||||
}),
|
||||
[slots, isDisabled, inputProps.checked, isInvalid],
|
||||
);
|
||||
|
||||
return {
|
||||
state,
|
||||
size,
|
||||
color,
|
||||
labelColor,
|
||||
inputRef,
|
||||
isDisabled,
|
||||
isRequired,
|
||||
isInvalid,
|
||||
isHovered,
|
||||
isSquared,
|
||||
isFocusVisible,
|
||||
disableAnimation,
|
||||
hoverProps,
|
||||
focusProps,
|
||||
inputProps,
|
||||
...otherProps,
|
||||
Component,
|
||||
children,
|
||||
slots,
|
||||
styles,
|
||||
description,
|
||||
getBaseProps,
|
||||
getWrapperProps,
|
||||
getInputProps,
|
||||
getLabelProps,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,247 +1,303 @@
|
||||
import React from "react";
|
||||
import {Meta} from "@storybook/react";
|
||||
import {Button} from "@nextui-org/button";
|
||||
import {Spacer} from "@nextui-org/spacer";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {radio} from "@nextui-org/theme";
|
||||
|
||||
import {Radio} from "../src";
|
||||
import {RadioGroup, Radio, RadioGroupProps} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Inputs/Radio",
|
||||
component: Radio,
|
||||
title: "Inputs/RadioGroup",
|
||||
component: RadioGroup,
|
||||
onChange: {action: "changed"},
|
||||
} as Meta;
|
||||
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"],
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof RadioGroup>;
|
||||
|
||||
export const Default = () => (
|
||||
<Radio.Group label="Options">
|
||||
const defaultProps = {
|
||||
...radio.defaultVariants,
|
||||
label: "Options",
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof RadioGroup> = (args: RadioGroupProps) => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="A">Option A</Radio>
|
||||
<Radio value="B">Option B</Radio>
|
||||
<Radio value="C">Option C</Radio>
|
||||
<Radio value="D">Option D</Radio>
|
||||
</Radio.Group>
|
||||
</RadioGroup>
|
||||
);
|
||||
|
||||
const handleSubmit = (e: any) => {
|
||||
e.preventDefault();
|
||||
alert("Submitted!");
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const Required = () => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Radio.Group isRequired label="Options">
|
||||
<Radio value="A">Option A</Radio>
|
||||
<Radio value="B">Option B</Radio>
|
||||
<Radio value="C">Option C</Radio>
|
||||
<Radio value="D">Option D</Radio>
|
||||
</Radio.Group>
|
||||
<Spacer y={1} />
|
||||
<Button type="submit">Submit</Button>
|
||||
</form>
|
||||
);
|
||||
// import React from "react";
|
||||
// import {Meta} from "@storybook/react";
|
||||
// import {Button} from "@nextui-org/button";
|
||||
// import {Spacer} from "@nextui-org/spacer";
|
||||
|
||||
export const Disabled = () => (
|
||||
<Radio.Group isDisabled defaultValue="A" label="Options">
|
||||
<Radio description="Description for Option A" value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio value="B">Option B</Radio>
|
||||
<Radio value="C">Option C</Radio>
|
||||
<Radio value="D">Option D</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
// import {Radio} from "../src";
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "row", gap: 200}}>
|
||||
<Radio.Group defaultValue="md" label="Sizes">
|
||||
<Radio size="xs" value="xs">
|
||||
mini
|
||||
</Radio>
|
||||
<Radio size="sm" value="sm">
|
||||
small
|
||||
</Radio>
|
||||
<Radio size="md" value="md">
|
||||
medium
|
||||
</Radio>
|
||||
<Radio size="lg" value="lg">
|
||||
large
|
||||
</Radio>
|
||||
<Radio size="xl" value="xl">
|
||||
xlarge
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
<Radio.Group defaultValue="md" label="Sizes">
|
||||
<Radio description="Description for Option mini" size="xs" value="xs">
|
||||
mini
|
||||
</Radio>
|
||||
<Radio description="Description for Option small" size="sm" value="sm">
|
||||
small
|
||||
</Radio>
|
||||
<Radio description="Description for Option medium" size="md" value="md">
|
||||
medium
|
||||
</Radio>
|
||||
<Radio description="Description for Option large" size="lg" value="lg">
|
||||
large
|
||||
</Radio>
|
||||
<Radio description="Description for Option xlarge" size="xl" value="xl">
|
||||
xlarge
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// export default {
|
||||
// title: "Inputs/Radio",
|
||||
// component: Radio,
|
||||
// onChange: {action: "changed"},
|
||||
// } as Meta;
|
||||
|
||||
export const Colors = () => {
|
||||
return (
|
||||
<Radio.Group defaultValue="primary" label="Colors">
|
||||
<Radio color="primary" value="primary">
|
||||
primary
|
||||
</Radio>
|
||||
<Radio color="secondary" value="secondary">
|
||||
secondary
|
||||
</Radio>
|
||||
<Radio color="success" value="success">
|
||||
success
|
||||
</Radio>
|
||||
<Radio color="warning" value="warning">
|
||||
warning
|
||||
</Radio>
|
||||
<Radio color="error" value="error">
|
||||
error
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
};
|
||||
// export const Default = () => (
|
||||
// <Radio.Group label="Options">
|
||||
// <Radio value="A">Option A</Radio>
|
||||
// <Radio value="B">Option B</Radio>
|
||||
// <Radio value="C">Option C</Radio>
|
||||
// <Radio value="D">Option D</Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
|
||||
export const LabelColors = () => {
|
||||
return (
|
||||
<Radio.Group defaultValue="primary" label="Label colors">
|
||||
<Radio color="primary" labelColor="primary" value="primary">
|
||||
primary
|
||||
</Radio>
|
||||
<Radio color="secondary" labelColor="secondary" value="secondary">
|
||||
secondary
|
||||
</Radio>
|
||||
<Radio color="success" labelColor="success" value="success">
|
||||
success
|
||||
</Radio>
|
||||
<Radio color="warning" labelColor="warning" value="warning">
|
||||
warning
|
||||
</Radio>
|
||||
<Radio color="error" labelColor="error" value="error">
|
||||
error
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
};
|
||||
// const handleSubmit = (e: any) => {
|
||||
// e.preventDefault();
|
||||
// alert("Submitted!");
|
||||
// };
|
||||
|
||||
export const Squared = () => (
|
||||
<Radio.Group defaultValue="A" label="Options">
|
||||
<Radio isSquared value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio isSquared value="B">
|
||||
Option B
|
||||
</Radio>
|
||||
<Radio isSquared value="C">
|
||||
Option C
|
||||
</Radio>
|
||||
<Radio isSquared value="D">
|
||||
Option D
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
// export const Required = () => (
|
||||
// <form onSubmit={handleSubmit}>
|
||||
// <Radio.Group isRequired label="Options">
|
||||
// <Radio value="A">Option A</Radio>
|
||||
// <Radio value="B">Option B</Radio>
|
||||
// <Radio value="C">Option C</Radio>
|
||||
// <Radio value="D">Option D</Radio>
|
||||
// </Radio.Group>
|
||||
// <Spacer y={1} />
|
||||
// <Button type="submit">Submit</Button>
|
||||
// </form>
|
||||
// );
|
||||
|
||||
export const Description = () => (
|
||||
<Radio.Group defaultValue="A" label="Options">
|
||||
<Radio description="Description for Option A" value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio description="Description for Option B" value="B">
|
||||
Option B
|
||||
</Radio>
|
||||
<Radio description="Description for Option C" value="C">
|
||||
Option C
|
||||
</Radio>
|
||||
<Radio description="Description for Option D" value="D">
|
||||
Option D
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
// export const Disabled = () => (
|
||||
// <Radio.Group isDisabled defaultValue="A" label="Options">
|
||||
// <Radio description="Description for Option A" value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio value="B">Option B</Radio>
|
||||
// <Radio value="C">Option C</Radio>
|
||||
// <Radio value="D">Option D</Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
|
||||
export const Invalid = () => (
|
||||
<Radio.Group defaultValue="A" label="Options" validationState="invalid">
|
||||
<Radio description="Description for Option A" value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio description="Description for Option B" value="B">
|
||||
Option B
|
||||
</Radio>
|
||||
<Radio description="Description for Option C" value="C">
|
||||
Option C
|
||||
</Radio>
|
||||
<Radio description="Description for Option D" value="D">
|
||||
Option D
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
// export const Sizes = () => {
|
||||
// return (
|
||||
// <div style={{display: "flex", flexDirection: "row", gap: 200}}>
|
||||
// <Radio.Group defaultValue="md" label="Sizes">
|
||||
// <Radio size="xs" value="xs">
|
||||
// mini
|
||||
// </Radio>
|
||||
// <Radio size="sm" value="sm">
|
||||
// small
|
||||
// </Radio>
|
||||
// <Radio size="md" value="md">
|
||||
// medium
|
||||
// </Radio>
|
||||
// <Radio size="lg" value="lg">
|
||||
// large
|
||||
// </Radio>
|
||||
// <Radio size="xl" value="xl">
|
||||
// xlarge
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// <Radio.Group defaultValue="md" label="Sizes">
|
||||
// <Radio description="Description for Option mini" size="xs" value="xs">
|
||||
// mini
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option small" size="sm" value="sm">
|
||||
// small
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option medium" size="md" value="md">
|
||||
// medium
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option large" size="lg" value="lg">
|
||||
// large
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option xlarge" size="xl" value="xl">
|
||||
// xlarge
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
export const Row = () => (
|
||||
<div style={{display: "flex", flexDirection: "column", gap: 100}}>
|
||||
<Radio.Group defaultValue="A" label="Options" orientation="horizontal">
|
||||
<Radio value="A">Option A</Radio>
|
||||
<Radio value="B">Option B</Radio>
|
||||
<Radio value="C">Option C</Radio>
|
||||
<Radio value="D">Option D</Radio>
|
||||
</Radio.Group>
|
||||
<Radio.Group defaultValue="A" label="Options" orientation="horizontal">
|
||||
<Radio description="Description for Option A" value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio description="Description for Option B" value="B">
|
||||
Option B
|
||||
</Radio>
|
||||
<Radio description="Description for Option C" value="C">
|
||||
Option C
|
||||
</Radio>
|
||||
<Radio description="Description for Option D" value="D">
|
||||
Option D
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
);
|
||||
// export const Colors = () => {
|
||||
// return (
|
||||
// <Radio.Group defaultValue="primary" label="Colors">
|
||||
// <Radio color="primary" value="primary">
|
||||
// primary
|
||||
// </Radio>
|
||||
// <Radio color="secondary" value="secondary">
|
||||
// secondary
|
||||
// </Radio>
|
||||
// <Radio color="success" value="success">
|
||||
// success
|
||||
// </Radio>
|
||||
// <Radio color="warning" value="warning">
|
||||
// warning
|
||||
// </Radio>
|
||||
// <Radio color="error" value="error">
|
||||
// error
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
// };
|
||||
|
||||
export const Controlled = () => {
|
||||
const [checked, setChecked] = React.useState<string>("london");
|
||||
// export const LabelColors = () => {
|
||||
// return (
|
||||
// <Radio.Group defaultValue="primary" label="Label colors">
|
||||
// <Radio color="primary" labelColor="primary" value="primary">
|
||||
// primary
|
||||
// </Radio>
|
||||
// <Radio color="secondary" labelColor="secondary" value="secondary">
|
||||
// secondary
|
||||
// </Radio>
|
||||
// <Radio color="success" labelColor="success" value="success">
|
||||
// success
|
||||
// </Radio>
|
||||
// <Radio color="warning" labelColor="warning" value="warning">
|
||||
// warning
|
||||
// </Radio>
|
||||
// <Radio color="error" labelColor="error" value="error">
|
||||
// error
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
// };
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("checked:", checked);
|
||||
}, [checked]);
|
||||
// export const Squared = () => (
|
||||
// <Radio.Group defaultValue="A" label="Options">
|
||||
// <Radio isSquared value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio isSquared value="B">
|
||||
// Option B
|
||||
// </Radio>
|
||||
// <Radio isSquared value="C">
|
||||
// Option C
|
||||
// </Radio>
|
||||
// <Radio isSquared value="D">
|
||||
// Option D
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
|
||||
return (
|
||||
<Radio.Group label="Check cities" value={checked} onChange={(value) => setChecked(value)}>
|
||||
<Radio value="buenos-aires">Buenos Aires</Radio>
|
||||
<Radio value="sydney">Sydney</Radio>
|
||||
<Radio value="london">London</Radio>
|
||||
<Radio value="tokyo">Tokyo</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
};
|
||||
// export const Description = () => (
|
||||
// <Radio.Group defaultValue="A" label="Options">
|
||||
// <Radio description="Description for Option A" value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option B" value="B">
|
||||
// Option B
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option C" value="C">
|
||||
// Option C
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option D" value="D">
|
||||
// Option D
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
|
||||
export const DisableAnimation = () => {
|
||||
return (
|
||||
<Radio.Group defaultValue="A" label="Options">
|
||||
<Radio disableAnimation value="A">
|
||||
Option A
|
||||
</Radio>
|
||||
<Radio disableAnimation value="B">
|
||||
Option B
|
||||
</Radio>
|
||||
<Radio disableAnimation value="C">
|
||||
Option C
|
||||
</Radio>
|
||||
<Radio disableAnimation value="D">
|
||||
Option D
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
);
|
||||
};
|
||||
// export const Invalid = () => (
|
||||
// <Radio.Group defaultValue="A" label="Options" validationState="invalid">
|
||||
// <Radio description="Description for Option A" value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option B" value="B">
|
||||
// Option B
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option C" value="C">
|
||||
// Option C
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option D" value="D">
|
||||
// Option D
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
|
||||
// export const Row = () => (
|
||||
// <div style={{display: "flex", flexDirection: "column", gap: 100}}>
|
||||
// <Radio.Group defaultValue="A" label="Options" orientation="horizontal">
|
||||
// <Radio value="A">Option A</Radio>
|
||||
// <Radio value="B">Option B</Radio>
|
||||
// <Radio value="C">Option C</Radio>
|
||||
// <Radio value="D">Option D</Radio>
|
||||
// </Radio.Group>
|
||||
// <Radio.Group defaultValue="A" label="Options" orientation="horizontal">
|
||||
// <Radio description="Description for Option A" value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option B" value="B">
|
||||
// Option B
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option C" value="C">
|
||||
// Option C
|
||||
// </Radio>
|
||||
// <Radio description="Description for Option D" value="D">
|
||||
// Option D
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// </div>
|
||||
// );
|
||||
|
||||
// export const Controlled = () => {
|
||||
// const [checked, setChecked] = React.useState<string>("london");
|
||||
|
||||
// React.useEffect(() => {
|
||||
// console.log("checked:", checked);
|
||||
// }, [checked]);
|
||||
|
||||
// return (
|
||||
// <Radio.Group label="Check cities" value={checked} onChange={(value) => setChecked(value)}>
|
||||
// <Radio value="buenos-aires">Buenos Aires</Radio>
|
||||
// <Radio value="sydney">Sydney</Radio>
|
||||
// <Radio value="london">London</Radio>
|
||||
// <Radio value="tokyo">Tokyo</Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export const DisableAnimation = () => {
|
||||
// return (
|
||||
// <Radio.Group defaultValue="A" label="Options">
|
||||
// <Radio disableAnimation value="A">
|
||||
// Option A
|
||||
// </Radio>
|
||||
// <Radio disableAnimation value="B">
|
||||
// Option B
|
||||
// </Radio>
|
||||
// <Radio disableAnimation value="C">
|
||||
// Option C
|
||||
// </Radio>
|
||||
// <Radio disableAnimation value="D">
|
||||
// Option D
|
||||
// </Radio>
|
||||
// </Radio.Group>
|
||||
// );
|
||||
// };
|
||||
|
||||
@ -2,11 +2,7 @@ import type {As, RightJoinProps, PropsOf, ComponentWithAs} from "./types";
|
||||
|
||||
import {forwardRef as baseForwardRef} from "react";
|
||||
|
||||
export function forwardRef<
|
||||
Props extends object,
|
||||
Component extends As,
|
||||
CompoundComponents extends object = {},
|
||||
>(
|
||||
export function forwardRef<Props extends object, Component extends As>(
|
||||
component: React.ForwardRefRenderFunction<
|
||||
any,
|
||||
RightJoinProps<PropsOf<Component>, Props> & {
|
||||
@ -14,8 +10,7 @@ export function forwardRef<
|
||||
}
|
||||
>,
|
||||
) {
|
||||
return baseForwardRef(component) as unknown as ComponentWithAs<Component, Props> &
|
||||
CompoundComponents;
|
||||
return baseForwardRef(component) as unknown as ComponentWithAs<Component, Props>;
|
||||
}
|
||||
|
||||
export const toIterator = (obj: any) => {
|
||||
|
||||
@ -3,7 +3,7 @@ import {tv} from "tailwind-variants";
|
||||
/**
|
||||
* CheckboxGroup wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, label, wrapper,} = checkboxGroup({...})
|
||||
* const {base, label, wrapper} = checkboxGroup({...})
|
||||
*
|
||||
* @example
|
||||
* <div className={base())}>
|
||||
|
||||
@ -13,3 +13,5 @@ export * from "./chip";
|
||||
export * from "./badge";
|
||||
export * from "./checkbox";
|
||||
export * from "./checkbox-group";
|
||||
export * from "./radio";
|
||||
export * from "./radio-group";
|
||||
|
||||
26
packages/core/theme/src/components/radio-group.ts
Normal file
26
packages/core/theme/src/components/radio-group.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* RadioGroup wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, label, wrapper} = radioGroup({...})
|
||||
*
|
||||
* @example
|
||||
* <div className={base())}>
|
||||
* <label className={label()}>Label</label>
|
||||
* <div className={wrapper()} data-orientation="vertical/horizontal">
|
||||
* // radios
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
const radioGroup = 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 RadioGroupSlots = keyof ReturnType<typeof radioGroup>;
|
||||
|
||||
export {radioGroup};
|
||||
80
packages/core/theme/src/components/radio.ts
Normal file
80
packages/core/theme/src/components/radio.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
/**
|
||||
* Radio wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, wrapper, point, label, description} = radio({...})
|
||||
*
|
||||
* @example
|
||||
* <label className={base())}>
|
||||
* // input
|
||||
* <span className={wrapper()} aria-hidden="true" data-checked={checked}>
|
||||
* <span className={point()}/>
|
||||
* </span>
|
||||
* <span className={label()}>Label</span>
|
||||
* <span className={description()}>Description</span>
|
||||
* </label>
|
||||
*/
|
||||
const radio = tv({
|
||||
slots: {
|
||||
base: "relative max-w-fit inline-flex items-center justify-start cursor-pointer",
|
||||
wrapper: "",
|
||||
point: "",
|
||||
label: "relative ml-1 text-foreground select-none",
|
||||
description: "relative ml-1 text-neutral-500 select-none",
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
neutral: {},
|
||||
primary: {},
|
||||
secondary: {},
|
||||
success: {},
|
||||
warning: {},
|
||||
danger: {},
|
||||
},
|
||||
size: {
|
||||
xs: {},
|
||||
sm: {},
|
||||
md: {},
|
||||
lg: {},
|
||||
xl: {},
|
||||
},
|
||||
radius: {
|
||||
none: {},
|
||||
base: {},
|
||||
sm: {},
|
||||
md: {},
|
||||
lg: {},
|
||||
xl: {},
|
||||
full: {},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
base: "opacity-50 pointer-events-none",
|
||||
},
|
||||
},
|
||||
isFocusVisible: {
|
||||
true: {
|
||||
wrapper: [...ringClasses],
|
||||
},
|
||||
},
|
||||
disableAnimation: {
|
||||
true: {},
|
||||
false: {},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "primary",
|
||||
size: "md",
|
||||
radius: "md",
|
||||
isDisabled: false,
|
||||
disableAnimation: false,
|
||||
},
|
||||
});
|
||||
|
||||
export type RadioVariantProps = VariantProps<typeof radio>;
|
||||
export type RadioSlots = keyof ReturnType<typeof radio>;
|
||||
|
||||
export {radio};
|
||||
@ -12,6 +12,7 @@ module.exports = {
|
||||
"../../components/chip/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../components/badge/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../components/checkbox/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../components/radio/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
staticDirs: ["../public"],
|
||||
addons: [
|
||||
|
||||
4
pnpm-lock.yaml
generated
4
pnpm-lock.yaml
generated
@ -621,9 +621,9 @@ importers:
|
||||
specifiers:
|
||||
'@nextui-org/button': workspace:*
|
||||
'@nextui-org/dom-utils': workspace:*
|
||||
'@nextui-org/shared-css': workspace:*
|
||||
'@nextui-org/shared-utils': workspace:*
|
||||
'@nextui-org/system': workspace:*
|
||||
'@nextui-org/theme': workspace:*
|
||||
'@react-aria/focus': ^3.11.0
|
||||
'@react-aria/interactions': ^3.14.0
|
||||
'@react-aria/radio': ^3.5.0
|
||||
@ -636,9 +636,9 @@ importers:
|
||||
react: ^18.2.0
|
||||
dependencies:
|
||||
'@nextui-org/dom-utils': link:../../utilities/dom-utils
|
||||
'@nextui-org/shared-css': link:../../utilities/shared-css
|
||||
'@nextui-org/shared-utils': link:../../utilities/shared-utils
|
||||
'@nextui-org/system': link:../../core/system
|
||||
'@nextui-org/theme': link:../../core/theme
|
||||
'@react-aria/focus': 3.11.0_react@18.2.0
|
||||
'@react-aria/interactions': 3.14.0_react@18.2.0
|
||||
'@react-aria/radio': 3.5.0_react@18.2.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user