Skip to content

Commit

Permalink
Merge pull request #1132 from nsbno/add-radio-card
Browse files Browse the repository at this point in the history
Add RadioCard with RadioCardGroup to spor
  • Loading branch information
alicemacl authored May 10, 2024
2 parents 205b033 + 114ae22 commit af7e657
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .changeset/breezy-roses-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@vygruppen/spor-react": minor
---

New component: RadioCard & RadioCardGroup

- Should be used togehter for optimal functionality
1 change: 1 addition & 0 deletions apps/docs/app/routes/playground/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default function PlaygroundPage() {
setPlaygroundData(newCode);
localStorage.setItem("playgroundData", newCode);
};

return (
<LiveProvider code={playgroundData}>
<Stack spacing={2} id="content">
Expand Down
95 changes: 95 additions & 0 deletions packages/spor-react/src/layout/RadioCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
BoxProps,
UseRadioProps,
chakra,
forwardRef,
useRadio,
useStyleConfig,
} from "@chakra-ui/react";
import { dataAttr } from "@chakra-ui/utils";
import React, { useId } from "react";

type RadioCardProps = UseRadioProps &
BoxProps & {
children: React.ReactNode;
variant: "floating" | "base";
};

/**
* Renders a radio card.
*
* The most basic version looks like this:
*
* ```tsx
* <RadioCard>
* Content
* </RadioCard>
* ```
*
* In order to use RadioCard, you typically want to place these components in a group with several other RadioCards.
*
* ```tsx
* <RadioCardGroup name="ticket">
* <RadioCard value="economy">Economy</RadioCard>
* <RadioCard value="business">Business</RadioCard>
* <RadioCard value="first-class">First Class</RadioCard>
* </RadioCardGroup>
* ```
*
* You can add styling to each card seperately, or you can add a common style to the group.
* Group styling overrides single styling if both are present.
*
* This example shows how to style all cards in a group:
*
* ```tsx
* <RadioCardGroup name="ticket" variant="floating" padding={3}>
* <RadioCard value="economy">Economy</RadioCard>
* <RadioCard value="business">Business</RadioCard>
* <RadioCard value="first-class">First Class</RadioCard>
* </RadioCardGroup>
* ```
*
* This example shows how to style a single card:
*
* ```tsx
* <RadioCard variant="floating" padding={3}>
* Economy
* </RadioCard>
* ```
*/

export const RadioCard = forwardRef<RadioCardProps, "div">(
({ children, variant = "base", ...props }, ref) => {
const { getInputProps, getRadioProps, getRootProps, state } =
useRadio(props);

const styles = useStyleConfig("RadioCard", { variant });

const input = getInputProps({}, ref);
const radio = getRadioProps();

const id = `radio-card-${useId()}`;

return (
<chakra.label
htmlFor={id}
{...getRootProps()}
aria-label={String(children)}
>
<chakra.input {...input} id={id} disabled={state.isDisabled} />
<chakra.div
__css={styles}
{...radio}
data-checked={dataAttr(state.isChecked)}
data-hover={dataAttr(state.isHovered)}
data-focus={dataAttr(state.isFocused)}
data-active={dataAttr(state.isActive)}
data-disabled={dataAttr(state.isDisabled)}
{...props}
>
{children}
</chakra.div>
</chakra.label>
);
},
);
81 changes: 81 additions & 0 deletions packages/spor-react/src/layout/RadioCardGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
useRadioGroup,
RadioGroupProps,
StackDirection,
Stack,
} from "@chakra-ui/react";
import React, { Children } from "react";

type RadioCardGroupProps = RadioGroupProps & {
children: React.ReactNode;
props?: RadioGroupProps;
direction?: StackDirection;
};

/**
* Radio card groups are used to group several radio cards together.
*
* You can and should pass the common `name` prop to the `RadioGroup`, instead of to each `Radio` component.
*
* ```tsx
* <RadioCardGroup name="ticket">
* <RadioCard>Economy</RadioCard>
* <RadioCard>Business</RadioCard>
* <RadioCard>First Class</RadioCard>
* </RadioCardGroup>
* ```
*
* By default, radio cards show up horizontally. If you want them to show up vertically, please specify the `direction="column"` prop.
*
* ```tsx
* <RadioCardGroup name="ticket" direction="column">
* <RadioCard>Economy</RadioCard>
* <RadioCard>Business</RadioCard>
* <RadioCard>First Class</RadioCard>
* </RadioCardGroup>
* ```
*
* You can also specify the `defaultValue` prop to set the default value of the radio group.
*
* ```tsx
* <RadioCardGroup name="ticket" defaultValue="Economy">
* <RadioCard>Economy</RadioCard>
* <RadioCard>Business</RadioCard>
* <RadioCard>First Class</RadioCard>
* </RadioCardGroup>
* ```
*
* Check out RadioCard for more information on how to style the radio cards.
* @see RadioCard
*/

export const RadioCardGroup = ({
children,
name,
direction = "row",
onChange,
defaultValue,
variant = "base",
...props
}: RadioCardGroupProps) => {
const { getRootProps, getRadioProps } = useRadioGroup({
defaultValue: defaultValue,
name: name,
onChange: onChange,
...props,
});

const group = getRootProps();

return (
<Stack direction={direction} {...group}>
{Children.map(
children as React.ReactElement[],
(child: React.ReactElement) => {
const radio = getRadioProps({ value: child.props.value });
return React.cloneElement(child, { ...radio, variant, ...props });
},
)}
</Stack>
);
};
2 changes: 2 additions & 0 deletions packages/spor-react/src/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ export type {
} from "@chakra-ui/react";
export * from "./Divider";
export * from "./Stack";
export * from "./RadioCard";
export * from "./RadioCardGroup";
1 change: 1 addition & 0 deletions packages/spor-react/src/theme/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export { default as Pagination } from "./pagination";
export { default as Popover } from "./popover";
export { default as ProgressBar } from "./progress-bar";
export { default as ProgressIndicator } from "./progress-indicator";
export { default as RadioCard } from "./radio-card";
export { default as Radio } from "./radio";
export { default as Select } from "./select";
export { default as Skeleton } from "./skeleton";
Expand Down
64 changes: 64 additions & 0 deletions packages/spor-react/src/theme/components/radio-card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { defineStyleConfig } from "@chakra-ui/react";
import { baseBackground, baseBorder, baseText } from "../utils/base-utils";
import { floatingBackground, floatingBorder } from "../utils/floating-utils";
import { focusVisibleStyles } from "../utils/focus-utils";

const config = defineStyleConfig({
baseStyle: (props: any) => ({
appearance: "none",
border: "none",
overflow: "hidden",
fontSize: "inherit",
display: "block",
borderRadius: "sm",
...focusVisibleStyles(props),
_checked: {
outline: "1px solid",
outlineColor: "greenHaze",
...floatingBackground("active", props),
_hover: {
...floatingBackground("active", props),
},
},
_disabled: {
pointerEvents: "none",
...baseBackground("disabled", props),
...baseBorder("disabled", props),
...baseText("disabled", props),
},
}),
variants: {
base: (props) => ({
...baseBackground("default", props),
...baseBorder("default", props),
_hover: {
...baseBackground("hover", props),
...baseBorder("hover", props),
},
_active: {
...baseBackground("active", props),
},
}),
floating: (props) => ({
...floatingBackground("default", props),
boxShadow: "sm",
_hover: {
...floatingBackground("hover", props),
...floatingBorder("hover", props),
boxShadow: "md",
},
_active: {
...floatingBackground("active", props),
...floatingBorder("active", props),
},
_checked: {
_hover: {
outline: "1px solid",
outlineColor: "silver",
},
},
}),
},
});

export default config;
2 changes: 2 additions & 0 deletions packages/spor-react/src/theme/components/static-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineStyleConfig } from "@chakra-ui/react";
import { mode } from "@chakra-ui/theme-tools";
import { colors } from "../foundations";
import { baseBorder, baseText } from "../utils/base-utils";
import { focusVisibleStyles } from "../utils/focus-utils";

const config = defineStyleConfig({
baseStyle: (props: any) => ({
Expand All @@ -13,6 +14,7 @@ const config = defineStyleConfig({
borderRadius: "md",
// Except for white cards, all cards are light mode always
color: "text.default.light",
...focusVisibleStyles(props),
...getColorSchemeBaseProps(props),
}),
});
Expand Down
18 changes: 17 additions & 1 deletion packages/spor-react/src/theme/utils/floating-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export function floatingBackground(
}
}

type FloatingBorderState = Subset<State, "default" | "hover" | "active">;
type FloatingBorderState = Subset<
State,
"default" | "hover" | "active" | "selected"
>;
export function floatingBorder(
state: FloatingBorderState,
props: StyleFunctionProps,
Expand All @@ -52,6 +55,19 @@ export function floatingBorder(
"floating.outline.hover.dark",
)(props),
};
case "selected":
return {
outline: "1px solid",
outlineColor: mode("outline.focus.light", "outline.focus.dark")(props),
};
case "active":
return {
outline: "1px solid",
outlineColor: mode(
"floating.outline.active.light",
"floating.outline.active.dark",
)(props),
};
default:
return {
outline: "1px solid",
Expand Down

0 comments on commit af7e657

Please sign in to comment.