mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: add shadcn checkbox component (#17248)
contributes to coder/preview#55 Add shadcn checkbox component matching Figma styles from Coder Kit: https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=489-4187&t=Zx137ETWsQZtaCku-1 <img width="52" alt="Screenshot 2025-04-03 at 21 15 52" src="https://github.com/user-attachments/assets/ff2de95c-cb12-46ed-af31-a6d230e52a31" />
This commit is contained in:
@ -51,6 +51,7 @@
|
||||
"@mui/utils": "5.16.14",
|
||||
"@mui/x-tree-view": "7.25.0",
|
||||
"@radix-ui/react-avatar": "1.1.2",
|
||||
"@radix-ui/react-checkbox": "1.1.4",
|
||||
"@radix-ui/react-collapsible": "1.1.2",
|
||||
"@radix-ui/react-dialog": "1.1.4",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.4",
|
||||
|
32
site/pnpm-lock.yaml
generated
32
site/pnpm-lock.yaml
generated
@ -67,6 +67,9 @@ importers:
|
||||
'@radix-ui/react-avatar':
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@radix-ui/react-checkbox':
|
||||
specifier: 1.1.4
|
||||
version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@radix-ui/react-collapsible':
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -1482,6 +1485,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-checkbox@1.1.4':
|
||||
resolution: {integrity: sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==, tarball: https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-collapsible@1.1.2':
|
||||
resolution: {integrity: sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==, tarball: https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz}
|
||||
peerDependencies:
|
||||
@ -7509,6 +7525,22 @@ snapshots:
|
||||
'@types/react': 18.3.12
|
||||
'@types/react-dom': 18.3.1
|
||||
|
||||
'@radix-ui/react-checkbox@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
|
||||
'@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1)
|
||||
'@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1)
|
||||
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1)
|
||||
'@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.12
|
||||
'@types/react-dom': 18.3.1
|
||||
|
||||
'@radix-ui/react-collapsible@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
|
89
site/src/components/Checkbox/Checkbox.stories.tsx
Normal file
89
site/src/components/Checkbox/Checkbox.stories.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import React from "react";
|
||||
import { Checkbox } from "./Checkbox";
|
||||
|
||||
const meta: Meta<typeof Checkbox> = {
|
||||
title: "components/Checkbox",
|
||||
component: Checkbox,
|
||||
args: {},
|
||||
argTypes: {
|
||||
checked: {
|
||||
control: "boolean",
|
||||
description: "The controlled checked state of the checkbox",
|
||||
},
|
||||
defaultChecked: {
|
||||
control: "boolean",
|
||||
description: "The default checked state when initially rendered",
|
||||
},
|
||||
disabled: {
|
||||
control: "boolean",
|
||||
description:
|
||||
"When true, prevents the user from interacting with the checkbox",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Checkbox>;
|
||||
|
||||
export const Unchecked: Story = {};
|
||||
|
||||
export const Checked: Story = {
|
||||
args: {
|
||||
defaultChecked: true,
|
||||
checked: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledChecked: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
defaultChecked: true,
|
||||
checked: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomStyling: Story = {
|
||||
args: {
|
||||
className: "h-6 w-6 rounded-full",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLabel: Story = {
|
||||
render: () => (
|
||||
<div className="flex gap-3">
|
||||
<Checkbox id="terms" />
|
||||
<div className="grid">
|
||||
<label
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium peer-disabled:cursor-not-allowed peer-disabled:text-content-disabled"
|
||||
>
|
||||
Accept terms and conditions
|
||||
</label>
|
||||
<p className="text-sm text-content-secondary mt-1">
|
||||
You agree to our Terms of Service and Privacy Policy.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const Indeterminate: Story = {
|
||||
render: () => {
|
||||
const [checked, setChecked] = React.useState<boolean | "indeterminate">(
|
||||
"indeterminate",
|
||||
);
|
||||
return (
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onCheckedChange={(value) => setChecked(value)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
42
site/src/components/Checkbox/Checkbox.tsx
Normal file
42
site/src/components/Checkbox/Checkbox.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copied from shadc/ui on 04/03/2025
|
||||
* @see {@link https://ui.shadcn.com/docs/components/checkbox}
|
||||
*/
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||
import { Check, Minus } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "utils/cn";
|
||||
|
||||
export const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
`peer h-6 w-6 shrink-0 rounded-sm border border-border border-solid
|
||||
focus-visible:outline-none focus-visible:ring-2
|
||||
focus-visible:ring-content-link focus-visible:ring-offset-4 focus-visible:ring-offset-surface-primary
|
||||
disabled:cursor-not-allowed disabled:bg-surface-primary disabled:data-[state=checked]:bg-surface-tertiary
|
||||
data-[state=unchecked]:bg-surface-primary
|
||||
data-[state=checked]:bg-surface-invert-primary data-[state=checked]:text-content-invert
|
||||
hover:border-border-hover hover:data-[state=checked]:bg-surface-invert-secondary`,
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current relative")}
|
||||
>
|
||||
<div className="flex">
|
||||
{(props.checked === true || props.defaultChecked === true) && (
|
||||
<Check className="w-5 h-5" strokeWidth={2.5} />
|
||||
)}
|
||||
{props.checked === "indeterminate" && (
|
||||
<Minus className="w-5 h-5" strokeWidth={2.5} />
|
||||
)}
|
||||
</div>
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
@ -31,6 +31,7 @@
|
||||
--border-default: 240 6% 90%;
|
||||
--border-success: 142 76% 36%;
|
||||
--border-destructive: 0 84% 60%;
|
||||
--border-hover: 240, 5%, 34%;
|
||||
--overlay-default: 240 5% 84% / 80%;
|
||||
--radius: 0.5rem;
|
||||
--highlight-purple: 262 83% 58%;
|
||||
@ -67,6 +68,7 @@
|
||||
--border-default: 240 4% 16%;
|
||||
--border-success: 142 76% 36%;
|
||||
--border-destructive: 0 91% 71%;
|
||||
--border-hover: 240, 5%, 34%;
|
||||
--overlay-default: 240 10% 4% / 80%;
|
||||
--highlight-purple: 252 95% 85%;
|
||||
--highlight-green: 141 79% 85%;
|
||||
|
@ -53,6 +53,8 @@ module.exports = {
|
||||
border: {
|
||||
DEFAULT: "hsl(var(--border-default))",
|
||||
destructive: "hsl(var(--border-destructive))",
|
||||
success: "hsl(var(--border-success))",
|
||||
hover: "hsl(var(--border-hover))",
|
||||
},
|
||||
overlay: "hsla(var(--overlay-default))",
|
||||
input: "hsl(var(--input))",
|
||||
|
Reference in New Issue
Block a user