From ff1245414ea2a8457a66bb8f74bcfced52ac1d95 Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Fri, 12 Apr 2024 23:51:23 -0400 Subject: [PATCH] checkbox stuff --- .../src/lib/bits/checkbox/checkbox.svelte.ts | 10 +- .../components/checkbox-indicator.svelte | 4 +- .../checkbox/components/checkbox-input.svelte | 2 +- .../bits/checkbox/components/checkbox.svelte | 9 +- .../bits-ui/src/lib/bits/checkbox/types.ts | 98 ++++++++++--------- packages/bits-ui/src/lib/internal/types.ts | 6 +- .../lib/components/demos/checkbox-demo.svelte | 15 ++- 7 files changed, 82 insertions(+), 62 deletions(-) diff --git a/packages/bits-ui/src/lib/bits/checkbox/checkbox.svelte.ts b/packages/bits-ui/src/lib/bits/checkbox/checkbox.svelte.ts index 8379984af..043924a76 100644 --- a/packages/bits-ui/src/lib/bits/checkbox/checkbox.svelte.ts +++ b/packages/bits-ui/src/lib/bits/checkbox/checkbox.svelte.ts @@ -36,11 +36,12 @@ class CheckboxRootState { #attrs = $derived({ "data-disabled": getDataDisabled(this.disabled.value), "data-state": getCheckboxDataState(this.checked.value), - type: "button", role: "checkbox", + type: "button", "aria-checked": getAriaChecked(this.checked.value), "aria-required": getAriaRequired(this.required.value), "data-checkbox-root": "", + disabled: this.disabled.value, } as const); constructor(props: CheckboxRootStateProps) { @@ -59,8 +60,11 @@ class CheckboxRootState { onclick = composeHandlers(this.onclickProp.value, () => { if (this.disabled.value) return; - if (this.checked.value === "indeterminate") return true; - return !this.checked.value; + if (this.checked.value === "indeterminate") { + this.checked.value = true; + return; + } + this.checked.value = !this.checked.value; }); createIndicator() { diff --git a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-indicator.svelte b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-indicator.svelte index 2589e1e7f..0d84ff403 100644 --- a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-indicator.svelte +++ b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-indicator.svelte @@ -7,7 +7,7 @@ const indicatorState = getCheckboxIndicatorState(); const mergedProps = $derived({ - ...indicatorState.attrs, + ...indicatorState.props, ...restProps, }); @@ -16,6 +16,6 @@ {@render child?.(mergedProps)} {:else}
- {@render children?.()} + {@render children?.({ checked: indicatorState.root.checked })}
{/if} diff --git a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-input.svelte b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-input.svelte index 452c38df6..57724f734 100644 --- a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-input.svelte +++ b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox-input.svelte @@ -5,5 +5,5 @@ {#if inputState.shouldRender} - + {/if} diff --git a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox.svelte b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox.svelte index 7039dbff2..77aa05072 100644 --- a/packages/bits-ui/src/lib/bits/checkbox/components/checkbox.svelte +++ b/packages/bits-ui/src/lib/bits/checkbox/components/checkbox.svelte @@ -1,6 +1,7 @@ {#if asChild} - {@render child?.(mergedProps)} + {@render child?.({ props: mergedProps, checked: checkboxState.checked.value })} {:else} {/if} + + diff --git a/packages/bits-ui/src/lib/bits/checkbox/types.ts b/packages/bits-ui/src/lib/bits/checkbox/types.ts index a7a0b08a1..a71c2fc66 100644 --- a/packages/bits-ui/src/lib/bits/checkbox/types.ts +++ b/packages/bits-ui/src/lib/bits/checkbox/types.ts @@ -1,4 +1,5 @@ import type { HTMLButtonAttributes } from "svelte/elements"; +import type { Snippet } from "svelte"; import type { EventCallback, HTMLDivAttributes, @@ -6,51 +7,56 @@ import type { WithAsChild, } from "$lib/internal/index.js"; -export type CheckboxRootPropsWithoutHTML = WithAsChild<{ - /** - * Whether the checkbox is disabled. - * - * @defaultValue false - */ - disabled?: boolean; - - /** - * Whether the checkbox is required (for form validation). - * - * @defaultValue false - */ - required?: boolean; - - /** - * The name of the checkbox used in form submission. - * If not provided, the hidden input will not be rendered. - * - * @defaultValue undefined - */ - name?: string; - - /** - * The value of the checkbox used in form submission. - * - * @defaultValue undefined - */ - value?: string; - - /** - * The checked state of the checkbox. It can be one of: - * - `true` for checked - * - `false` for unchecked - * - `"indeterminate"` for indeterminate - * - * @defaultValue false - */ - checked?: boolean | "indeterminate"; - - /** - * A callback function called when the checked state changes. - */ - onCheckedChange?: OnChangeFn; -}>; +export type CheckboxRootPropsWithoutHTML = WithAsChild< + { + /** + * Whether the checkbox is disabled. + * + * @defaultValue false + */ + disabled?: boolean; + + /** + * Whether the checkbox is required (for form validation). + * + * @defaultValue false + */ + required?: boolean; + + /** + * The name of the checkbox used in form submission. + * If not provided, the hidden input will not be rendered. + * + * @defaultValue undefined + */ + name?: string; + + /** + * The value of the checkbox used in form submission. + * + * @defaultValue undefined + */ + value?: string; + + /** + * The checked state of the checkbox. It can be one of: + * - `true` for checked + * - `false` for unchecked + * - `"indeterminate"` for indeterminate + * + * @defaultValue false + */ + checked?: boolean | "indeterminate"; + + /** + * A callback function called when the checked state changes. + */ + onCheckedChange?: OnChangeFn; + + indicator?: Snippet<[{ checked: boolean | "indeterminate" }]>; + }, + { props: Record; checked: boolean | "indeterminate" } +>; export type CheckboxRootProps = CheckboxRootPropsWithoutHTML & Omit & { @@ -58,6 +64,6 @@ export type CheckboxRootProps = CheckboxRootPropsWithoutHTML & onkeydown?: EventCallback; }; -export type CheckboxIndicatorPropsWithoutHTML = WithAsChild; +export type CheckboxIndicatorPropsWithoutHTML = WithAsChild<{ checked: boolean | "indeterminate" }>; export type CheckboxIndicatorProps = CheckboxIndicatorPropsWithoutHTML & HTMLDivAttributes; diff --git a/packages/bits-ui/src/lib/internal/types.ts b/packages/bits-ui/src/lib/internal/types.ts index ab3105fd0..bef71399c 100644 --- a/packages/bits-ui/src/lib/internal/types.ts +++ b/packages/bits-ui/src/lib/internal/types.ts @@ -135,16 +135,16 @@ export type AsChildProps = { el?: HTMLElement; } & Omit; -export type DefaultProps = { +export type DefaultProps = { asChild?: never; child?: never; - children?: Snippet; + children?: Snippet<[U]>; el?: HTMLElement; } & Omit; // eslint-disable-next-line ts/ban-types export type WithAsChild = {}> = - | DefaultProps + | DefaultProps | AsChildProps; /** diff --git a/sites/docs/src/lib/components/demos/checkbox-demo.svelte b/sites/docs/src/lib/components/demos/checkbox-demo.svelte index 9845cd16a..be9924563 100644 --- a/sites/docs/src/lib/components/demos/checkbox-demo.svelte +++ b/sites/docs/src/lib/components/demos/checkbox-demo.svelte @@ -1,5 +1,6 @@
@@ -7,11 +8,17 @@ id="terms" aria-labelledby="terms-label" class="peer inline-flex size-[25px] items-center justify-center rounded-md border border-muted bg-foreground transition-all duration-150 ease-in-out active:scale-98 data-[state=unchecked]:border-border-input data-[state=unchecked]:bg-background data-[state=unchecked]:hover:border-dark-40" - checked="indeterminate" + name="hello" > - - check - + {#snippet indicator({ checked })} +
+ {#if checked === true} + + {:else if checked === "indeterminate"} + + {/if} +
+ {/snippet}