Skip to content

Commit 2ab6912

Browse files
committed
feat(primitives): add Checkbox component
1 parent 81fe055 commit 2ab6912

File tree

22 files changed

+2058
-0
lines changed

22 files changed

+2058
-0
lines changed

packages/primitives/src/components/checkbox/README.md

Lines changed: 299 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { CheckboxContextValue } from './checkbox-context.types';
2+
import { createContextId } from '@builder.io/qwik';
3+
4+
export const CheckboxContext = createContextId<CheckboxContextValue>('qwik-primitives-checkbox-context');
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { ReadonlySignal, QRL, Signal } from '@builder.io/qwik';
2+
3+
export interface CheckboxContextValue {
4+
/**
5+
* The controlled checked state of the checkbox.
6+
*/
7+
isChecked: ReadonlySignal<boolean | 'indeterminate'>;
8+
9+
/**
10+
* The function that allow change the controlled checked state of the checkbox.
11+
*/
12+
setIsChecked$: QRL<(value: boolean | 'indeterminate') => void>;
13+
14+
/**
15+
* The initila checked state of the checkbox.
16+
*/
17+
initilaChecked: boolean | 'indeterminate';
18+
19+
/**
20+
* The name of the text field.
21+
* Submitted with its owning form as part of a name/value pair.
22+
*/
23+
inputName: ReadonlySignal<string | undefined>;
24+
25+
/**
26+
* The value given as data when submitted with a `name`.
27+
*/
28+
inputValue: ReadonlySignal<string | undefined>;
29+
30+
/**
31+
* When `true`, prevents the user from interacting with the checkbox.
32+
*/
33+
isDisabled: ReadonlySignal<boolean | undefined>;
34+
35+
/**
36+
* Whether the checkbox is invalid.
37+
*/
38+
isInvalid: ReadonlySignal<boolean | undefined>;
39+
40+
/**
41+
* When `true`, indicates that the user must check the checkbox before the owning form can be submitted.
42+
*/
43+
isRequired: ReadonlySignal<boolean | undefined>;
44+
45+
/**
46+
* The unique id of the checkbox label.
47+
*/
48+
labelId: Signal<string | undefined>;
49+
50+
/**
51+
* The unique id of the checkbox input.
52+
*/
53+
inputId: Signal<string | undefined>;
54+
55+
/**
56+
* The unique id of the checkbox description.
57+
*/
58+
descriptionId: Signal<string | undefined>;
59+
60+
/**
61+
* The unique id of the checkbox error message.
62+
*/
63+
errorMessageId: Signal<string | undefined>;
64+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { CheckboxContextValue } from './checkbox-context.types';
2+
export { CheckboxContext } from './checkbox-context';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { CheckboxDescriptionProps } from './checkbox-description.types';
2+
import { component$, useContext, useId, useTask$, Slot } from '@builder.io/qwik';
3+
import { CheckboxContext } from '../checkbox-context';
4+
5+
/**
6+
* The description that gives the user more information on the checkbox.
7+
* This component is based on the `span` element.
8+
*/
9+
export const CheckboxDescription = component$<CheckboxDescriptionProps>((props) => {
10+
const { as, id, ...others } = props;
11+
12+
const { isChecked, descriptionId, isDisabled, isInvalid, isRequired } = useContext(CheckboxContext);
13+
14+
const autoId = useId();
15+
16+
// This asynchronous function is required.
17+
// This trick allows you to use the value coming from the context
18+
// when this value is initialized or assigned in another component during SSR.
19+
useTask$(async () => undefined);
20+
21+
useTask$(() => {
22+
// During SSR we give the user the option to provide their own id.
23+
descriptionId.value = id || `qwik-primitives-checkbox-description-${autoId}`;
24+
});
25+
26+
const Component = as || 'span';
27+
28+
return (
29+
<Component
30+
id={descriptionId.value}
31+
data-qwik-primitives-checkbox-description=""
32+
data-scope="checkbox"
33+
data-part="description"
34+
data-state={isChecked.value === 'indeterminate' ? 'indeterminate' : isChecked.value ? 'checked' : 'unchecked'}
35+
data-disabled={isDisabled.value ? '' : undefined}
36+
data-invalid={isInvalid.value ? '' : undefined}
37+
data-required={isRequired.value ? '' : undefined}
38+
{...others}
39+
>
40+
<Slot />
41+
</Component>
42+
);
43+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { PropsOf, FunctionComponent, CSSProperties } from '@builder.io/qwik';
2+
3+
export interface CheckboxDescriptionProps extends PropsOf<'span'> {
4+
/**
5+
* Change the default rendered element for the one passed as, merging their props and behavior.
6+
*
7+
* Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details.
8+
*/
9+
as?: FunctionComponent;
10+
11+
/**
12+
* The inline style for the element.
13+
*/
14+
style?: CSSProperties;
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { CheckboxDescriptionProps } from './checkbox-description.types';
2+
export { CheckboxDescription } from './checkbox-description';
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { CheckboxErrorMessageProps } from './checkbox-error-message.types';
2+
import { component$, useContext, useId, useTask$, Slot } from '@builder.io/qwik';
3+
import { CheckboxContext } from '../checkbox-context';
4+
5+
/**
6+
* The error message that gives the user information about how to fix a validation error on the checkbox.
7+
* By default, it will render only when the `invalid` prop is set to `true` on `Checkbox.Root` component,
8+
* use the `forceMount` prop to always render the error message.
9+
* This component is based on the `span` element.
10+
*/
11+
export const CheckboxErrorMessage = component$<CheckboxErrorMessageProps>((props) => {
12+
const { as, id, forceMount, ...others } = props;
13+
14+
const { isChecked, errorMessageId, isDisabled, isInvalid, isRequired } = useContext(CheckboxContext);
15+
16+
const autoId = useId();
17+
18+
// This asynchronous function is required.
19+
// This trick allows you to use the value coming from the context
20+
// when this value is initialized or assigned in another component during SSR.
21+
useTask$(async () => undefined);
22+
23+
useTask$(() => {
24+
// During SSR we give the user the option to provide their own id.
25+
errorMessageId.value = id || `qwik-primitives-checkbox-error-message-${autoId}`;
26+
});
27+
28+
const Component = as || 'span';
29+
30+
return (
31+
(forceMount || isInvalid.value) && (
32+
<Component
33+
id={errorMessageId.value}
34+
aria-hidden={forceMount && !isInvalid.value ? 'true' : undefined}
35+
data-qwik-primitives-checkbox-error-message=""
36+
data-scope="checkbox"
37+
data-part="error-message"
38+
data-state={isChecked.value === 'indeterminate' ? 'indeterminate' : isChecked.value ? 'checked' : 'unchecked'}
39+
data-disabled={isDisabled.value ? '' : undefined}
40+
data-invalid={isInvalid.value ? '' : undefined}
41+
data-required={isRequired.value ? '' : undefined}
42+
{...others}
43+
>
44+
<Slot />
45+
</Component>
46+
)
47+
);
48+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { PropsOf, FunctionComponent, CSSProperties } from '@builder.io/qwik';
2+
3+
export interface CheckboxErrorMessageProps extends PropsOf<'span'> {
4+
/**
5+
* Change the default rendered element for the one passed as, merging their props and behavior.
6+
*
7+
* Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details.
8+
*/
9+
as?: FunctionComponent;
10+
11+
/**
12+
* Used to force mounting when more control is needed.
13+
*/
14+
forceMount?: boolean;
15+
16+
/**
17+
* The inline style for the element.
18+
*/
19+
style?: CSSProperties;
20+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { CheckboxErrorMessageProps } from './checkbox-error-message.types';
2+
export { CheckboxErrorMessage } from './checkbox-error-message';

0 commit comments

Comments
 (0)