Skip to content

Commit

Permalink
feat(svelte): add checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
Omikorin committed Jan 6, 2025
1 parent a33c038 commit ac99c6e
Show file tree
Hide file tree
Showing 21 changed files with 474 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script module lang="ts">
import type { Snippet } from 'svelte'
import type { UseCheckboxContext } from './use-checkbox-context'
export interface CheckboxContextProps {
api?: Snippet<[UseCheckboxContext]>
}
</script>

<script lang="ts">
import { useCheckboxContext } from './use-checkbox-context'
const { api }: CheckboxContextProps = $props()
const checkbox = useCheckboxContext()
</script>

{@render api?.(checkbox)}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script module lang="ts">
import type { HTMLProps, PolymorphicProps } from '$lib/types'
export interface CheckboxControlBaseProps extends PolymorphicProps<'div'> {}
export interface CheckboxControlProps extends HTMLProps<'div'>, CheckboxControlBaseProps {}
</script>

<script lang="ts">
import { useCheckboxContext } from './use-checkbox-context'
import { mergeProps } from '@zag-js/svelte'
import { Ark } from '../factory'
const props: CheckboxControlProps = $props()
const checkbox = useCheckboxContext()
const mergedProps = $derived(mergeProps(checkbox().getControlProps(), props))
</script>

<Ark as="div" {...mergedProps} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script module lang="ts">
import type { HTMLProps, PolymorphicProps } from '$lib/types'
export interface CheckboxHiddenInputBaseProps extends PolymorphicProps<'input'> {}
export interface CheckboxHiddenInputProps
extends HTMLProps<'input'>,
CheckboxHiddenInputBaseProps {}
</script>

<script lang="ts">
import { useCheckboxContext } from './use-checkbox-context'
import { mergeProps } from '@zag-js/svelte'
import { Ark } from '../factory'
// TODO: use Field component
const props: CheckboxHiddenInputProps = $props()
const checkbox = useCheckboxContext()
const mergedProps = $derived(mergeProps(checkbox().getHiddenInputProps(), props))
</script>

<Ark as="input" {...mergedProps} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script module lang="ts">
import type { HTMLProps, PolymorphicProps } from '$lib/types'
interface IndicatorProps {
indeterminate?: boolean
}
export interface CheckboxIndicatorBaseProps extends IndicatorProps, PolymorphicProps<'div'> {}
export interface CheckboxIndicatorProps extends HTMLProps<'div'>, CheckboxIndicatorBaseProps {}
</script>

<script lang="ts">
import { mergeProps } from '@zag-js/svelte'
import { Ark } from '../factory'
import { useCheckboxContext } from './use-checkbox-context'
const { indeterminate, ...rest }: CheckboxIndicatorProps = $props()
// const props: CheckboxIndicatorProps = $props()
$inspect('props', indeterminate)
const checkbox = useCheckboxContext()
$inspect('checkbox', checkbox())
const isVisible = $derived(indeterminate ? checkbox().indeterminate : checkbox().checked)
// const isVisible = $derived(props.indeterminate ? checkbox().indeterminate : checkbox().checked)
// const isVisible = $derived(checkbox().checked)
$inspect('isVisible', isVisible)
const mergedProps = $derived(mergeProps(checkbox().getIndicatorProps(), rest))
// const mergedProps = $derived(mergeProps(checkbox().getIndicatorProps(), props))
console.log('--------------')
</script>

<Ark as="div" {...mergedProps} hidden={!isVisible} />
19 changes: 19 additions & 0 deletions packages/svelte/src/lib/components/checkbox/checkbox-label.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script module lang="ts">
import type { HTMLProps, PolymorphicProps } from '$lib/types'
export interface CheckboxLabelBaseProps extends PolymorphicProps<'span'> {}
export interface CheckboxLabelProps extends HTMLProps<'span'>, CheckboxLabelBaseProps {}
</script>

<script lang="ts">
import { useCheckboxContext } from './use-checkbox-context'
import { mergeProps } from '@zag-js/svelte'
import { Ark } from '../factory'
const props: CheckboxLabelProps = $props()
const checkbox = useCheckboxContext()
const mergedProps = $derived(mergeProps(checkbox().getLabelProps(), props))
</script>

<Ark as="span" {...mergedProps} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script module lang="ts">
import type { Assign, HTMLProps } from '$lib/types'
import type { UseCheckboxReturn } from './use-checkbox.svelte'
interface RootProviderProps {
value: UseCheckboxReturn
}
export interface CheckboxRootProviderBaseProps extends RootProviderProps {}
export interface CheckboxRootProviderProps
extends Assign<HTMLProps<'div'>, CheckboxRootProviderBaseProps> {}
</script>

<script lang="ts">
import { mergeProps } from '@zag-js/svelte'
import { Ark } from '../factory'
import { CheckboxProvider } from './use-checkbox-context'
const props: CheckboxRootProviderProps = $props()
const { value: checkbox, ...localProps } = props
const mergedProps = $derived(mergeProps(checkbox().getRootProps(), localProps))
CheckboxProvider(checkbox)
</script>

<Ark as="div" {...mergedProps} />
40 changes: 40 additions & 0 deletions packages/svelte/src/lib/components/checkbox/checkbox-root.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script module lang="ts">
import type { Assign, HTMLProps, PolymorphicProps } from '$lib/types'
import type { UseCheckboxProps } from './use-checkbox.svelte'
export interface CheckboxRootBaseProps extends UseCheckboxProps, PolymorphicProps<'label'> {}
export interface CheckboxRootProps extends Assign<HTMLProps<'label'>, CheckboxRootBaseProps> {}
</script>

<script lang="ts">
import { mergeProps, reflect } from '@zag-js/svelte'
import { createSplitProps } from '../../utils/create-split-props'
import { Ark } from '../factory'
import { CheckboxProvider } from './use-checkbox-context'
import { useCheckbox } from './use-checkbox.svelte'
const props: CheckboxRootProps = $props()
const [useCheckboxProps, localProps] = $derived(
createSplitProps<UseCheckboxProps>()(props, [
'checked',
'defaultChecked',
'disabled',
'form',
'id',
'ids',
'invalid',
'name',
'onCheckedChange',
'readOnly',
'required',
'value',
]),
)
const checkbox = useCheckbox(reflect(() => useCheckboxProps))
const mergedProps = $derived(mergeProps(checkbox().getRootProps(), localProps))
CheckboxProvider(checkbox)
</script>

<Ark as="label" {...mergedProps} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { anatomy } from '@zag-js/checkbox'

export const checkboxAnatomy = anatomy.extendWith('group')
53 changes: 53 additions & 0 deletions packages/svelte/src/lib/components/checkbox/checkbox.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Meta } from '@storybook/svelte'
import BasicExample from './examples/basic.svelte'
import ContextExample from './examples/context.svelte'
import IndeterminateExample from './examples/indeterminate.svelte'
import RootProviderExample from './examples/root-provider.svelte'

const meta = {
title: 'Components / Checkbox',
} as Meta

export default meta

export const Basic = {
render: () => ({
Component: BasicExample,
}),
}

export const Indeterminate = {
render: () => ({
Component: IndeterminateExample,
}),
}

// export const RenderProp = {
// render: () => ({
// Component: RenderPropExample,
// }),
// }

// export const Group = {
// render: () => ({
// Component: GroupExample,
// }),
// }

// export const WithField = {
// render: () => ({
// Component: WithFieldExample,
// }),
// }

export const RootProvider = {
render: () => ({
Component: RootProviderExample,
}),
}

export const Context = {
render: () => ({
Component: ContextExample,
}),
}
10 changes: 10 additions & 0 deletions packages/svelte/src/lib/components/checkbox/checkbox.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { render, screen } from '@testing-library/svelte'
import { describe, expect, it } from 'vitest'
import ComponentUnderTest from './examples/basic.svelte'

describe('Checkbox', async () => {
it('should render', async () => {
render(ComponentUnderTest)
expect(screen.getByText('Checkbox')).toBeInTheDocument()
})
})
35 changes: 35 additions & 0 deletions packages/svelte/src/lib/components/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type { CheckedChangeDetails, CheckedState } from '@zag-js/checkbox'
export {
default as Context,
type CheckboxContextProps as ContextProps,
} from './checkbox-context.svelte'
export {
default as Control,
type CheckboxControlBaseProps as ControlBaseProps,
type CheckboxControlProps as ControlProps,
} from './checkbox-control.svelte'
export {
default as HiddenInput,
type CheckboxHiddenInputBaseProps as HiddenInputBaseProps,
type CheckboxHiddenInputProps as HiddenInputProps,
} from './checkbox-hidden-input.svelte'
export {
default as Indicator,
type CheckboxIndicatorBaseProps as IndicatorBaseProps,
type CheckboxIndicatorProps as IndicatorProps,
} from './checkbox-indicator.svelte'
export {
default as Label,
type CheckboxLabelBaseProps as LabelBaseProps,
type CheckboxLabelProps as LabelProps,
} from './checkbox-label.svelte'
export {
default as RootProvider,
type CheckboxRootProviderBaseProps as RootProviderBaseProps,
type CheckboxRootProviderProps as RootProviderProps,
} from './checkbox-root-provider.svelte'
export {
default as Root,
type CheckboxRootBaseProps as RootBaseProps,
type CheckboxRootProps as RootProps,
} from './checkbox-root.svelte'
17 changes: 17 additions & 0 deletions packages/svelte/src/lib/components/checkbox/examples/basic.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
import { Checkbox } from '..'
import type { RootProps } from '../checkbox'
import CheckIcon from './check-icon.svelte'
const props: RootProps = $props()
</script>

<Checkbox.Root {...props}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-check"
>
<title>Check Icon</title>
<path d="M20 6 9 17l-5-5" />
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { Checkbox } from '..'
import CheckIcon from './check-icon.svelte'
</script>

<Checkbox.Root>
<Checkbox.Context>
{#snippet api(checkbox)}
<Checkbox.Label>Checkbox {checkbox().checked.toString()}</Checkbox.Label>
{/snippet}
</Checkbox.Context>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import CheckIcon from './check-icon.svelte'
import MinusIcon from './minus-icon.svelte'
</script>

<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-minus"
>
<title>Minus Icon</title>
<path d="M5 12h14" />
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import { Checkbox, useCheckbox } from '..'
import CheckIcon from './check-icon.svelte'
const checkbox = useCheckbox({
onCheckedChange: (details) => {
console.log('checked', details.checked)
},
})
</script>

<button onclick={() => checkbox().toggleChecked()}>Toggle Checkbox</button>

<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
Loading

0 comments on commit ac99c6e

Please sign in to comment.