Skip to content

Commit bda241a

Browse files
updates to use new checkbox component
1 parent a8ef928 commit bda241a

File tree

9 files changed

+328
-209
lines changed

9 files changed

+328
-209
lines changed
Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
import { $, QRL, Signal, Slot, component$, useSignal, useTask$ } from '@builder.io/qwik';
2-
import { DropdownItemProps, HDropdownItem } from './dropdown-item';
1+
import {
2+
$,
3+
QRL,
4+
Signal,
5+
Slot,
6+
component$,
7+
sync$,
8+
useSignal,
9+
useTask$,
10+
} from '@builder.io/qwik';
311

4-
type DropdownCheckboxItemProps = {
5-
/** A signal that controls the current checked state (controlled). */
12+
import { CheckboxRoot } from '../checkbox/checkbox';
13+
import { DropdownItemProps } from './dropdown-item';
14+
import { useDropdownItem } from './use-dropdown-item';
15+
16+
export type DropdownCheckboxItemProps = {
17+
/**
18+
* A signal that controls the current checked value (controlled).
19+
*/
620
'bind:checked'?: Signal<boolean>;
721

822
/**
9-
* QRL handler that runs when the user selects an item.
23+
* QRL handler that runs when the checked value changes.
1024
*/
1125
onChange$?: QRL<(checked: boolean) => void>;
1226
} & Omit<DropdownItemProps, 'onChange$'>;
1327

1428
export const HDropdownCheckboxItem = component$((props: DropdownCheckboxItemProps) => {
15-
const { disabled = false, closeOnSelect = false, onChange$, ...rest } = props;
29+
const { disabled, onChange$, closeOnSelect = false, ...rest } = props;
1630

1731
const checkedSig = useSignal<boolean>(false);
32+
const checkboxRef = useSignal<HTMLDivElement>();
1833

1934
useTask$(function reactiveUserChecked({ track }) {
2035
const bindCheckedSig = props['bind:checked'];
@@ -30,26 +45,51 @@ export const HDropdownCheckboxItem = component$((props: DropdownCheckboxItemProp
3045
onChange$?.(checkedSig.value);
3146
});
3247

33-
const onSelect = $(() => {
48+
// Handle the toggle of the checked state when the item is selected trough the keyboard or click.
49+
const toggleChecked$ = $(() => {
3450
checkedSig.value = !checkedSig.value;
35-
props.onSelect$?.();
51+
});
52+
53+
const {
54+
handleClick$,
55+
handleKeyDown$,
56+
handlePointerOver$,
57+
itemId,
58+
itemRef,
59+
isHighlightedSig,
60+
} = useDropdownItem({ ...props, onItemSelect: toggleChecked$, closeOnSelect });
61+
62+
//Prevent default behavior for certain keys. This needs to be sync to prevent default behavior and can't be implemented in useDropdownItem.
63+
const handleKeyDownSync$ = sync$((e: KeyboardEvent) => {
64+
const keys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'Home', 'End', 'Enter', ' '];
65+
if (keys.includes(e.key)) {
66+
e.preventDefault();
67+
}
3668
});
3769

3870
return (
39-
<HDropdownItem
40-
{...rest}
41-
onSelect$={onSelect}
42-
role="menuitemcheckbox"
43-
closeOnSelect={closeOnSelect}
44-
disabled={disabled}
45-
aria-checked={checkedSig.value ? 'true' : 'false'}
46-
aria-disabled={disabled}
47-
style={{ display: 'flex', alignItems: 'center' }}
71+
<div
72+
onClick$={[handleClick$, props.onClick$]}
73+
tabIndex={-1}
74+
id={itemId}
75+
onKeyDown$={[handleKeyDownSync$, handleKeyDown$, props.onKeyDown$]}
76+
onPointerOver$={[handlePointerOver$, props.onPointerOver$]}
77+
ref={itemRef}
78+
aria-disabled={disabled === true ? 'true' : 'false'}
4879
data-disabled={disabled}
80+
data-highlighted={isHighlightedSig.value}
4981
data-checked={checkedSig.value}
82+
data-menu-item
5083
>
51-
<Slot name="dropdown-item-indicator" />
52-
<Slot />
53-
</HDropdownItem>
84+
<CheckboxRoot
85+
bind:checked={checkedSig}
86+
preventdefault:click
87+
ref={checkboxRef}
88+
style={{ pointerEvents: 'none' }}
89+
{...rest}
90+
>
91+
<Slot />
92+
</CheckboxRoot>
93+
</div>
5494
);
5595
});

packages/kit-headless/src/components/dropdown/dropdown-context.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,10 @@ export type DropdownRadioGroupContext = {
3131
valueSig: Signal<string>;
3232
disabled: boolean;
3333
};
34+
35+
export const dropdownGroupContextId =
36+
createContextId<DropdownGroupContext>('qui-dropdown-group');
37+
38+
export type DropdownGroupContext = {
39+
groupLabelId: string;
40+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { PropsOf, Slot, component$, useContext } from '@builder.io/qwik';
2+
3+
import { dropdownGroupContextId } from './dropdown-context';
4+
5+
type DropdownLabelProps = PropsOf<'span'>;
6+
7+
export const HDropdownGroupLabel = component$<DropdownLabelProps>((props) => {
8+
const groupContext = useContext(dropdownGroupContextId);
9+
10+
return (
11+
<span id={groupContext.groupLabelId} {...props}>
12+
<Slot />
13+
</span>
14+
);
15+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
DropdownGroupContext,
3+
dropdownContextId,
4+
dropdownGroupContextId,
5+
} from './dropdown-context';
6+
import {
7+
PropsOf,
8+
Slot,
9+
component$,
10+
useContext,
11+
useContextProvider,
12+
} from '@builder.io/qwik';
13+
14+
type DropdownGroupProps = PropsOf<'div'>;
15+
16+
export const HDropdownGroup = component$((props: DropdownGroupProps) => {
17+
const context = useContext(dropdownContextId);
18+
const groupLabelId = `${context.localId}-group-label`;
19+
20+
const dropdownGroupContext: DropdownGroupContext = {
21+
groupLabelId,
22+
};
23+
24+
useContextProvider(dropdownGroupContextId, dropdownGroupContext);
25+
26+
return (
27+
<div aria-labelledby={groupLabelId} role="group" {...props}>
28+
<Slot />
29+
</div>
30+
);
31+
});
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { PropsOf, Slot, component$ } from '@builder.io/qwik';
1+
import {
2+
CheckboxIndicator,
3+
CheckboxIndicatorProps,
4+
} from '../checkbox/checkbox-indicator';
5+
import { Slot, component$ } from '@builder.io/qwik';
26

3-
type DropdownItemIndicatorProps = PropsOf<'div'>;
7+
type DropdownItemIndicatorProps = CheckboxIndicatorProps;
48

59
export const HDropdownItemIndicator = component$((props: DropdownItemIndicatorProps) => {
610
return (
7-
<div data-indicator {...props} q:slot="dropdown-item-indicator">
11+
<CheckboxIndicator data-indicator {...props}>
812
<Slot />
9-
</div>
13+
</CheckboxIndicator>
1014
);
1115
});

0 commit comments

Comments
 (0)