From 245ef00ed9fad7b9ac3c027d34e0ad00da9b9108 Mon Sep 17 00:00:00 2001 From: SameerAther Date: Tue, 5 Aug 2025 14:57:42 +0500 Subject: [PATCH 1/2] feat(checkbox): add checked/unchecked class overrides and renderIcon --- .../reusables/src/components/ui/checkbox.tsx | 91 ++++++++++++++----- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/packages/reusables/src/components/ui/checkbox.tsx b/packages/reusables/src/components/ui/checkbox.tsx index 576438ef..2e85c63e 100644 --- a/packages/reusables/src/components/ui/checkbox.tsx +++ b/packages/reusables/src/components/ui/checkbox.tsx @@ -4,30 +4,79 @@ import { Platform } from 'react-native'; import { Check } from '../../lib/icons/Check'; import { cn } from '../../lib/utils'; -function Checkbox({ - className, - ...props -}: CheckboxPrimitive.RootProps & { - ref?: React.RefObject; -}) { +export interface CheckboxProps extends CheckboxPrimitive.RootProps { + checkedClassName?: string; + uncheckedClassName?: string; + indicatorClassName?: string; + iconClassName?: string; + iconCheckedClassName?: string; + iconUncheckedClassName?: string; + renderIcon?: (opts: { checked: boolean }) => React.ReactNode; +} + +/** Internal implementation (normal function). */ +function CheckboxImpl( + { + className, + checkedClassName, + uncheckedClassName, + indicatorClassName, + iconClassName, + iconCheckedClassName, + iconUncheckedClassName, + renderIcon, + checked, + ...props + }: CheckboxProps, + ref: React.ForwardedRef +) { + const baseRoot = + 'web:peer h-4 w-4 native:h-[20] native:w-[20] shrink-0 rounded-sm native:rounded ' + + 'border web:ring-offset-background web:focus-visible:outline-none ' + + 'web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2 ' + + 'disabled:cursor-not-allowed disabled:opacity-50'; + + // Defaults so "just checked/onCheckedChange" works + const defaultUnchecked = 'border-primary'; + const defaultChecked = ['bg-primary', 'border-primary']; + const defaultIconChecked = 'text-primary-foreground'; + + const rootClasses = cn( + baseRoot, + defaultUnchecked, + checked && defaultChecked, + checked ? checkedClassName : uncheckedClassName, + className + ); + + const iconClasses = cn( + checked && defaultIconChecked, + iconClassName, + checked ? iconCheckedClassName : iconUncheckedClassName + ); + return ( - - - + + + {renderIcon ? ( + renderIcon({ checked: !!checked }) + ) : ( + + )} ); } -export { Checkbox }; +/** Make a forwardRef component… */ +const ForwardedCheckbox = React.forwardRef(CheckboxImpl); +ForwardedCheckbox.displayName = 'Checkbox'; +export function Checkbox(props: CheckboxProps) { + return ; +} From 628e4d81967a05736b9be3362b70d2a5d4627744 Mon Sep 17 00:00:00 2001 From: SameerAther Date: Tue, 5 Aug 2025 15:12:17 +0500 Subject: [PATCH 2/2] fix(docs): update checkbox documentation and improve examples --- .../src/content/docs/components/checkbox.mdx | 137 ++++++++++++++++-- .../reusables/src/components/ui/checkbox.tsx | 2 - 2 files changed, 123 insertions(+), 16 deletions(-) diff --git a/apps/docs/src/content/docs/components/checkbox.mdx b/apps/docs/src/content/docs/components/checkbox.mdx index bf56a182..7f4f9383 100644 --- a/apps/docs/src/content/docs/components/checkbox.mdx +++ b/apps/docs/src/content/docs/components/checkbox.mdx @@ -11,10 +11,8 @@ import { LinkButton } from '@/components/react/LinkButton'; import { Aside, Tabs, TabItem } from '@astrojs/starlight/components'; import importedCode from '@rnr/reusables/components/ui/checkbox?raw'; - - Checkbox Primitive - - +Checkbox Primitive + Demo @@ -23,6 +21,7 @@ import importedCode from '@rnr/reusables/components/ui/checkbox?raw'; A box that is a checked (ticked) indicator when activated. ### Installation + ```bash @@ -40,28 +39,138 @@ A box that is a checked (ticked) indicator when activated. + ### Usage ```tsx +import * as React from 'react'; import { Checkbox } from '~/components/ui/checkbox'; function Example() { - const [checked, setChecked] = React.useState(false); - return ( - - ); + const [checked, setChecked] = React.useState(false); + return ; } ``` + ## Props ### Checkbox -Extends [`Pressable`](https://reactnative.dev/docs/pressable#props) props +Extends [`Pressable`](https://reactnative.dev/docs/pressable#props) props. + +| Prop | Type | Default | Notes | +| ------------------------ | ------------------------------------- | --------------------------- | ---------------------------------------------------- | +| `checked` **\*** | `boolean` | — | Controlled state. | +| `onCheckedChange` **\*** | `(checked: boolean) => void` | — | Change handler. | +| `disabled` | `boolean` | `false` | Disables interactions. | +| `className` | `string` | — | Base classes always applied (shape/size/etc.). | +| `checkedClassName` | `string` | `bg-primary border-primary` | Added **only** when checked. | +| `uncheckedClassName` | `string` | `border-primary` | Added **only** when not checked. | +| `indicatorClassName` | `string` | — | Container for the icon (fills the box). | +| `iconClassName` | `string` | — | Applied to the default icon always. | +| `iconCheckedClassName` | `string` | `text-primary-foreground` | Extra classes for the default icon when checked. | +| `iconUncheckedClassName` | `string` | — | Extra classes for the default icon when not checked. | +| `renderIcon` | `({ checked: boolean }) => ReactNode` | default check icon | Render a custom icon; overrides `icon*ClassName`. | + +**Theme defaults** (when you don’t pass overrides): + +- Unchecked border: `border-primary` +- Checked: `bg-primary border-primary` +- Checked icon: `text-primary-foreground` + +--- + +## Customizing styles (no presets) + +Use state-aware class props to color only when checked (and keep defaults otherwise). + +```tsx + +``` + +**Web-only selectors:** you can also do: + +```tsx + +``` + +> React Native doesn’t support `data-*` selectors—prefer `checkedClassName` on native. + +--- + +## Custom icon with `renderIcon` + +When you supply `renderIcon`, you fully control the icon node. + +```tsx +import { Platform, View } from 'react-native'; +import { Check } from '~/lib/icons/Check'; + + + checked ? ( + + ) : null + } +/>; +``` + +Show a subtle dot when unchecked: + +```tsx +renderIcon={({ checked }) => + checked ? ( + + ) : ( + + ) +} +``` -| Prop | Type | Note | -| :---------------: | :------------------------: | :----------: | -| checked\* | boolean | | -| onCheckedChange\* | (checked: boolean) => void | | -| disabled | boolean | _(optional)_ | \ No newline at end of file +--- + +## Examples + +**Square, red when checked** + +```tsx + +``` + +**Subtle unchecked, bold checked** + +```tsx + +``` diff --git a/packages/reusables/src/components/ui/checkbox.tsx b/packages/reusables/src/components/ui/checkbox.tsx index 2e85c63e..ceb491c0 100644 --- a/packages/reusables/src/components/ui/checkbox.tsx +++ b/packages/reusables/src/components/ui/checkbox.tsx @@ -14,7 +14,6 @@ export interface CheckboxProps extends CheckboxPrimitive.RootProps { renderIcon?: (opts: { checked: boolean }) => React.ReactNode; } -/** Internal implementation (normal function). */ function CheckboxImpl( { className, @@ -74,7 +73,6 @@ function CheckboxImpl( ); } -/** Make a forwardRef component… */ const ForwardedCheckbox = React.forwardRef(CheckboxImpl); ForwardedCheckbox.displayName = 'Checkbox'; export function Checkbox(props: CheckboxProps) {