Skip to content

Commit 2abd8dd

Browse files
fix: qrl's inside computed (#996)
1 parent 88ed151 commit 2abd8dd

File tree

6 files changed

+237
-32
lines changed

6 files changed

+237
-32
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
export const api = {
2+
combobox: [
3+
{
4+
'combobox-context': [],
5+
},
6+
{
7+
'combobox-control': [],
8+
},
9+
{
10+
'combobox-description': [],
11+
},
12+
{
13+
'combobox-empty': [],
14+
},
15+
{
16+
'combobox-error-message': [],
17+
},
18+
{
19+
'combobox-group-label': [],
20+
},
21+
{
22+
'combobox-group': [],
23+
},
24+
{
25+
'combobox-hidden-option': [],
26+
},
27+
{
28+
'combobox-hidden-select': [],
29+
},
30+
{
31+
'combobox-inline': [],
32+
},
33+
{
34+
'combobox-input': [],
35+
},
36+
{
37+
'combobox-item-indicator': [],
38+
},
39+
{
40+
'combobox-item-label': [],
41+
},
42+
{
43+
'combobox-item': [
44+
{
45+
HComboboxItemProps: [
46+
{
47+
comment:
48+
' Internal index we get from the inline component. Please see combobox-inline.tsx ',
49+
prop: '_index',
50+
type: 'number',
51+
},
52+
{
53+
comment: ' If true, item is not selectable or focusable. ',
54+
prop: 'disabled',
55+
type: 'boolean',
56+
},
57+
{
58+
comment: ' Selected value associated with the item. ',
59+
prop: 'value',
60+
type: 'string',
61+
},
62+
],
63+
},
64+
],
65+
},
66+
{
67+
'combobox-label': [],
68+
},
69+
{
70+
'combobox-listbox': [],
71+
},
72+
{
73+
'combobox-popover': [],
74+
},
75+
{
76+
'combobox-root': [],
77+
},
78+
{
79+
'combobox-trigger': [],
80+
},
81+
{
82+
'use-combobox': [],
83+
},
84+
],
85+
};
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
export const api = {
2+
dropdown: [
3+
{
4+
'dropdown-checkbox-item': [
5+
{
6+
DropdownCheckboxItemProps: [
7+
{
8+
comment:
9+
'\n A signal that controls the current checked value (controlled).\n ',
10+
prop: "'bind:checked'",
11+
type: 'Signal<boolean>',
12+
},
13+
{
14+
comment: '\n QRL handler that runs when the checked value changes.\n ',
15+
prop: 'onChange$',
16+
type: 'QRL<(checked: boolean) => void>',
17+
},
18+
],
19+
},
20+
],
21+
},
22+
{
23+
'dropdown-content': [],
24+
},
25+
{
26+
'dropdown-group-label': [],
27+
},
28+
{
29+
'dropdown-group': [],
30+
},
31+
{
32+
'dropdown-inline': [],
33+
},
34+
{
35+
'dropdown-item-indicator': [],
36+
},
37+
{
38+
'dropdown-item': [
39+
{
40+
DropdownItemProps: [
41+
{
42+
comment:
43+
' Internal index we get from the inline component. Please see dropdown-inline.tsx ',
44+
prop: '_index',
45+
type: 'number',
46+
},
47+
{
48+
comment: ' If true, item is not selectable or focusable. ',
49+
prop: 'disabled',
50+
type: 'boolean',
51+
},
52+
{
53+
comment: ' If true, dropdown will close after selecting the item. ',
54+
prop: 'closeOnSelect',
55+
type: 'boolean',
56+
},
57+
{
58+
comment: '\n QRL handler that runs when the user selects an item.\n ',
59+
prop: 'onClick$',
60+
type: 'QRL<() => void>',
61+
},
62+
],
63+
},
64+
],
65+
},
66+
{
67+
'dropdown-popover': [],
68+
},
69+
{
70+
'dropdown-radio-group': [],
71+
},
72+
{
73+
'dropdown-radio-item': [],
74+
},
75+
{
76+
'dropdown-root': [
77+
{
78+
DropdownProps: [
79+
{
80+
comment: ' A signal that controls the current open state (controlled). ',
81+
prop: "'bind:open'",
82+
type: 'Signal<boolean>',
83+
},
84+
{
85+
comment:
86+
'\n QRL handler that runs when the dropdown opens or closes.\n @param open The new state of the dropdown.\n \n ',
87+
prop: 'onOpenChange$',
88+
type: 'QRL<(open: boolean) => void>',
89+
},
90+
{
91+
comment:
92+
'\n The native scrollIntoView method is used to scroll the options into view when the user highlights an option. This allows customization of the scroll behavior.\n ',
93+
prop: 'scrollOptions',
94+
type: 'ScrollIntoViewOptions',
95+
},
96+
{
97+
comment:
98+
'\n Enables looped behavior when the user navigates through the options using the arrow keys.\n ',
99+
prop: 'loop',
100+
type: 'boolean',
101+
},
102+
],
103+
},
104+
],
105+
},
106+
{
107+
'dropdown-separator': [],
108+
},
109+
{
110+
'dropdown-trigger': [],
111+
},
112+
{
113+
'use-dropdown-item': [],
114+
},
115+
{
116+
'use-dropdown': [],
117+
},
118+
],
119+
};

packages/kit-headless/src/components/combobox/combobox-input.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,27 @@ export const HComboboxInput = component$(
2929
const wasEmptyBeforeBackspaceSig = useSignal(false);
3030
const isInputResetSig = useSignal(false);
3131

32-
const {
33-
selectionManager$,
34-
getNextEnabledItemIndex$,
35-
getPrevEnabledItemIndex$,
36-
getActiveDescendant$,
37-
} = useCombobox();
32+
const { selectionManager$, getNextEnabledItemIndex$, getPrevEnabledItemIndex$ } =
33+
useCombobox();
3834

3935
const activeDescendantSig = useComputed$(() => {
40-
if (context.isListboxOpenSig.value) {
41-
return getActiveDescendant$(context.highlightedIndexSig.value ?? -1);
42-
} else {
36+
if (!context.isListboxOpenSig.value) {
37+
return '';
38+
}
39+
40+
const highlightedIndex = context.highlightedIndexSig.value ?? -1;
41+
const highlightedItem = context.itemsMapSig.value.get(highlightedIndex);
42+
43+
if (
44+
highlightedIndex === null ||
45+
highlightedIndex === -1 ||
46+
highlightedItem?.disabled
47+
) {
4348
return '';
4449
}
50+
51+
// highlighted item id
52+
return `${context.localId}-${highlightedIndex}`;
4553
});
4654

4755
const handleKeyDownSync$ = sync$((e: KeyboardEvent) => {

packages/kit-headless/src/components/combobox/use-combobox.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,9 @@ export function useCombobox() {
153153
return context.loop || prevIndex < index ? prevIndex : index;
154154
});
155155

156-
const getActiveDescendant$ = $((index: number) => {
157-
if (index === -1 || context.disabledIndexSetSig.value.has(index)) {
158-
return '';
159-
}
160-
return `${context.localId}-${index}`;
161-
});
162-
163156
return {
164157
getNextEnabledItemIndex$,
165158
getPrevEnabledItemIndex$,
166-
getActiveDescendant$,
167159
selectionManager$,
168160
filterManager$,
169161
};

packages/kit-headless/src/components/dropdown/dropdown-root.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
useComputed$,
1212
} from '@builder.io/qwik';
1313
import { dropdownContextId, type DropdownContext } from './dropdown-context';
14-
import { useDropdown } from './use-dropdown';
1514

1615
export type TItemsMap = Map<
1716
number,
@@ -102,8 +101,6 @@ export const HDropdownImpl = component$<DropdownProps & InternalDropdownProps>(
102101

103102
useContextProvider(dropdownContextId, context);
104103

105-
const { getActiveDescendant$ } = useDropdown();
106-
107104
useTask$(function reactiveUserOpen({ track }) {
108105
const bindOpenSig = props['bind:open'];
109106
if (!bindOpenSig) return;
@@ -120,12 +117,25 @@ export const HDropdownImpl = component$<DropdownProps & InternalDropdownProps>(
120117
}
121118
});
122119

120+
// only evaluate when open
123121
const activeDescendantSig = useComputed$(() => {
124-
if (isOpenSig.value) {
125-
return getActiveDescendant$(highlightedIndexSig.value ?? -1);
126-
} else {
122+
if (!isOpenSig.value) {
123+
return '';
124+
}
125+
126+
const highlightedIndex = context.highlightedIndexSig.value ?? -1;
127+
const highlightedItem = context.itemsMapSig.value.get(highlightedIndex);
128+
129+
if (
130+
highlightedIndex === null ||
131+
highlightedIndex === -1 ||
132+
highlightedItem?.disabled
133+
) {
127134
return '';
128135
}
136+
137+
// highlighted item id
138+
return `${context.localId}-${highlightedIndex}`;
129139
});
130140

131141
useTask$(() => {

packages/kit-headless/src/components/dropdown/use-dropdown.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,8 @@ export function useDropdown() {
5353
return index;
5454
});
5555

56-
const getActiveDescendant$ = $((index: number) => {
57-
if (index === -1 || context.itemsMapSig.value.get(index)?.disabled) {
58-
return '';
59-
}
60-
61-
return `${context.localId}-${index}`;
62-
});
63-
6456
return {
6557
getNextEnabledItemIndex$,
6658
getPrevEnabledItemIndex$,
67-
getActiveDescendant$,
6859
};
6960
}

0 commit comments

Comments
 (0)