diff --git a/apps/www/content/primitives/components/checkbox.mdx b/apps/www/content/primitives/components/checkbox.mdx index e796ac17..baf65655 100644 --- a/apps/www/content/primitives/components/checkbox.mdx +++ b/apps/www/content/primitives/components/checkbox.mdx @@ -1,6 +1,6 @@ --- title: Checkbox -description: Checkbox is a user interface control that enables users to toggle between a checked and unchecked state +description: Checkbox is a user interface control that enables users to toggle between checked, unchecked, and indeterminate states radix: link: https://www.radix-ui.com/docs/primitives/components/checkbox api: https://www.radix-ui.com/docs/primitives/components/checkbox#api-reference @@ -10,14 +10,14 @@ radix: - - + + + + @@ -29,40 +29,92 @@ Install the component from your command line. +## Usage + +### Controlled Component +You can control the checkbox state using the `checked` and `onCheckedChange` props: + + + setChecked(value)} +/> +`} border /> + + +### Indeterminate State +The indeterminate state is useful for representing partial selection, commonly used in parent checkboxes: + + + ('indeterminate'); + + setChecked(value)} +/> +`} border /> + + +## Props + +The `Checkbox` component accepts the following props: + +- `checked`: The controlled state of the checkbox (`boolean | "indeterminate"`) +- `defaultChecked`: The default state when initially rendered (`boolean | "indeterminate"`) +- `onCheckedChange`: Event handler called when the state changes (`(checked: boolean | "indeterminate") => void`) +- `disabled`: When true, prevents the user from interacting with the checkbox (`boolean`) ## Anatomy Import all parts and piece them together. - -`} border /> - +// Checked state + -## Scale -The Checkbox component offers different size options to suit various design needs. By selecting the "Medium" size, you can create a larger Checkbox that provides enhanced visibility and ease of interaction within the user interface. Additionally, the Checkbox component also supports the following size options: +// Indeterminate state + -- Small -- Medium +// Disabled state + +`} border /> + + +## States +The Checkbox component supports multiple states to represent different selection conditions: -These different size options allow you to customize the appearance of the Checkbox to match your specific design preferences and ensure a seamless user experience. +- Unchecked: Default state +- Checked: Selected state +- Indeterminate: Partial selection state +- Disabled: Disabled state `, + name: "Unchecked", + code: ``, + }, + { + name: "Checked", + code: ``, + }, + { + name: "Indeterminate", + code: ``, }, { - name: "Medium", - code: ``, + name: "Disabled", + code: ``, }, - ]} -/> \ No newline at end of file +/> diff --git a/apps/www/examples/shield-ts/assets.tsx b/apps/www/examples/shield-ts/assets.tsx index c74beff8..386cd3c3 100644 --- a/apps/www/examples/shield-ts/assets.tsx +++ b/apps/www/examples/shield-ts/assets.tsx @@ -2,15 +2,12 @@ import React, { useState, useCallback, useEffect } from "react"; import dayjs from "dayjs"; import { PlusIcon, BlendingModeIcon, HomeIcon, ChevronDownIcon } from "@radix-ui/react-icons"; import { - Checkbox, DataTable, - Flex, - Text, Title, useTable } from "@raystack/apsara"; -import { toast, ToastContainer, Avatar, AvatarGroup, Button, Spinner, DropdownMenu, Breadcrumb } from "@raystack/apsara/v1"; +import { toast, ToastContainer, Avatar, AvatarGroup, Button, Spinner, DropdownMenu, Breadcrumb, Flex, Text, Checkbox} from "@raystack/apsara/v1"; import { getData, Payment } from "./data"; import { ApsaraColumnDef } from "@raystack/apsara/table/datatables.types"; const TOTAL_PAGES = 100; @@ -181,6 +178,12 @@ export const Assets = () => { const AssetsHeader = () => { const { filteredColumns } = useTable(); + const [checked, setChecked] = useState('indeterminate'); + const handleCheckedChange = (newChecked: boolean | 'indeterminate') => { + if (newChecked !== 'indeterminate') { + setChecked(newChecked); + } + }; const isFiltered = filteredColumns.length > 0; const items = [ { label: 'Home', href: '/', icon: }, @@ -206,6 +209,15 @@ const AssetsHeader = () => { Assets +
+ { + setChecked(value); + console.log('New value:', value); + }} + /> +
diff --git a/apps/www/utils/routes.ts b/apps/www/utils/routes.ts index 9ae4e352..a3840ce9 100644 --- a/apps/www/utils/routes.ts +++ b/apps/www/utils/routes.ts @@ -30,7 +30,7 @@ export const primitivesRoutes = [ }, { title: "Calendar", slug: "docs/primitives/components/calendar" }, { title: "Command", slug: "docs/primitives/components/command" }, - { title: "Checkbox", slug: "docs/primitives/components/checkbox" }, + { title: "Checkbox", slug: "docs/primitives/components/checkbox", newBadge: true }, { title: "Container", slug: "docs/primitives/components/container" }, { title: "Datatable", slug: "docs/primitives/components/datatable" }, { title: "Dialog", slug: "docs/primitives/components/dialog" }, diff --git a/packages/raystack/checkbox/checkbox.tsx b/packages/raystack/checkbox/checkbox.tsx index 2acdd64e..cac4124a 100644 --- a/packages/raystack/checkbox/checkbox.tsx +++ b/packages/raystack/checkbox/checkbox.tsx @@ -23,6 +23,9 @@ export interface CheckboxProps VariantProps, CheckboxPrimitive.CheckboxProps {} +/** + * @deprecated Use Checkbox from '@raystack/apsara/v1' instead. + */ export const Checkbox = forwardRef< ElementRef, CheckboxProps diff --git a/packages/raystack/v1/components/checkbox/checkbox.module.css b/packages/raystack/v1/components/checkbox/checkbox.module.css new file mode 100644 index 00000000..d57826fe --- /dev/null +++ b/packages/raystack/v1/components/checkbox/checkbox.module.css @@ -0,0 +1,66 @@ +.checkbox { + all: unset; + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--rs-space-5); + height: var(--rs-space-5); + min-width: var(--rs-space-5); + min-height: var(--rs-space-5); + border-radius: var(--rs-radius-1); + background: var(--rs-color-background-base-primary); + border: 1px solid var(--rs-color-border-base-secondary); + cursor: pointer; + flex-shrink: 0; + } + + .checkbox:hover { + background: var(--rs-color-background-base-primary-hover); + border-color: var(--rs-color-border-base-focus); + } + + .checkbox[data-state="checked"] { + background: var(--rs-color-background-accent-emphasis); + border: none; + } + + .checkbox[data-state="checked"]:hover { + background: var(--rs-color-background-accent-emphasis-hover); + } + + /* Indeterminate state */ + .checkbox-indeterminate[data-state="checked"] { + background: var(--rs-color-background-neutral-tertiary); + border: none; + } + + .checkbox-indeterminate[data-state="checked"]:hover { + background: var(--rs-color-background-neutral-tertiary); + border: none; + } + + .checkbox-disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .checkbox-disabled:hover { + background: var(--rs-color-background-base-primary); + border-color: var(--rs-color-border-base-primary); + } + + .indicator { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + color: var(--rs-color-text-accent-emphasis); + } + + .icon { + width: var(--rs-space-5); + height: var(--rs-space-5); + } + \ No newline at end of file diff --git a/packages/raystack/v1/components/checkbox/checkbox.tsx b/packages/raystack/v1/components/checkbox/checkbox.tsx new file mode 100644 index 00000000..403bf7b5 --- /dev/null +++ b/packages/raystack/v1/components/checkbox/checkbox.tsx @@ -0,0 +1,94 @@ +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import clsx from 'clsx'; +import { cva, VariantProps } from "class-variance-authority"; +import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react"; +import styles from "./checkbox.module.css"; + + +const CheckMarkIcon = () => ( + + + +); + +const IndeterminateIcon = () => ( + + + +); + +const checkbox = cva(styles.checkbox); + +type CheckboxVariants = VariantProps; +type CheckedState = boolean | 'indeterminate'; + +export interface CheckboxProps + extends Omit, keyof CheckboxVariants>, + CheckboxVariants { + checked?: CheckedState; + defaultChecked?: CheckedState; + onCheckedChange?: (checked: CheckedState) => void; +} + +export const Checkbox = forwardRef< + ElementRef, + CheckboxProps +>(({ className, disabled, checked, defaultChecked, onCheckedChange, ...props }, forwardedRef) => { + const isIndeterminate = checked === 'indeterminate' || defaultChecked === 'indeterminate'; + + return ( + { + if (onCheckedChange) { + // If it's currently indeterminate, next state will be unchecked + if (checked === 'indeterminate') { + onCheckedChange(false); + } else { + onCheckedChange(value); + } + } + }} + disabled={disabled} + ref={forwardedRef} + {...props} + > + + {isIndeterminate ? : } + + + ); +}); + +Checkbox.displayName = "Checkbox"; \ No newline at end of file diff --git a/packages/raystack/v1/components/checkbox/index.tsx b/packages/raystack/v1/components/checkbox/index.tsx new file mode 100644 index 00000000..2d4f967d --- /dev/null +++ b/packages/raystack/v1/components/checkbox/index.tsx @@ -0,0 +1 @@ +export { Checkbox } from "./checkbox"; diff --git a/packages/raystack/v1/index.tsx b/packages/raystack/v1/index.tsx index 4ccb6780..bffad7d6 100644 --- a/packages/raystack/v1/index.tsx +++ b/packages/raystack/v1/index.tsx @@ -9,6 +9,7 @@ export { DropdownMenu } from "./components/dropdownMenu"; export { Text } from "./components/text"; export { Flex } from "./components/flex"; export { EmptyState } from "./components/emptystate"; +export { Checkbox } from "./components/checkbox"; export { ThemeProvider,