Skip to content

Commit d0babd3

Browse files
get the first item focused
1 parent 3086c0f commit d0babd3

File tree

4 files changed

+25
-5
lines changed

4 files changed

+25
-5
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type SelectContext = {
1414
listboxRef: Signal<HTMLUListElement | undefined>;
1515
groupRef: Signal<HTMLDivElement | undefined>;
1616
labelRef: Signal<HTMLDivElement | undefined>;
17+
firstEnabledItemRef: Signal<HTMLLIElement | undefined>;
1718

1819
// core state
1920
itemsMapSig: Readonly<Signal<TItemsMap>>;

packages/kit-headless/src/components/select/select-item.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ export const HSelectItem = component$<SelectItemProps>((props) => {
3434
const itemRef = useSignal<HTMLLIElement>();
3535
const localIndexSig = useSignal<number | null>(null);
3636
const itemId = `${context.localId}-${_index}`;
37+
const isInitialFocusSig = useSignal<boolean>(true);
3738

38-
const { selectionManager$ } = useSelect();
39+
const { selectionManager$, getNextEnabledItemIndex$ } = useSelect();
3940

4041
const isSelectedSig = useComputed$(() => {
4142
const index = _index ?? null;
@@ -46,14 +47,20 @@ export const HSelectItem = component$<SelectItemProps>((props) => {
4647
return !disabled && context.highlightedIndexSig.value === _index;
4748
});
4849

49-
useTask$(function getIndexTask() {
50+
useTask$(async function getIndexTask() {
5051
if (_index === undefined)
5152
throw Error('Qwik UI: Select component item cannot find its proper index.');
5253

5354
localIndexSig.value = _index;
55+
56+
// update the context with the first enabled item ref
57+
const firstEnabledIndex = await getNextEnabledItemIndex$(-1);
58+
if (localIndexSig.value === firstEnabledIndex) {
59+
context.firstEnabledItemRef = itemRef;
60+
}
5461
});
5562

56-
useTask$(function scrollableTask({ track, cleanup }) {
63+
useTask$(async function scrollableTask({ track, cleanup }) {
5764
track(() => context.highlightedIndexSig.value);
5865

5966
if (isServer) return;
@@ -81,6 +88,8 @@ export const HSelectItem = component$<SelectItemProps>((props) => {
8188
observer.observe(itemRef.value);
8289
}
8390
}
91+
92+
if (!isInitialFocusSig.value) return;
8493
});
8594

8695
const handleClick$ = $(async () => {
@@ -124,7 +133,7 @@ export const HSelectItem = component$<SelectItemProps>((props) => {
124133
data-selected={isSelectedSig.value ? '' : undefined}
125134
data-highlighted={isHighlightedSig.value ? '' : undefined}
126135
data-disabled={disabled ? '' : undefined}
127-
data-item
136+
data-item={_index}
128137
role="option"
129138
>
130139
<Slot />

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export const HSelectImpl = component$<SelectProps<boolean> & InternalSelectProps
161161
const currDisplayValueSig = useSignal<string | string[]>();
162162

163163
const initialLoadSig = useSignal<boolean>(true);
164+
const firstEnabledItemRef = useSignal<HTMLLIElement>();
164165

165166
const context: SelectContext = {
166167
itemsMapSig,
@@ -170,6 +171,7 @@ export const HSelectImpl = component$<SelectProps<boolean> & InternalSelectProps
170171
listboxRef,
171172
labelRef,
172173
groupRef,
174+
firstEnabledItemRef,
173175
localId,
174176
highlightedIndexSig,
175177
selectedIndexSetSig,

packages/kit-headless/src/components/select/select-trigger.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,15 @@ export const HSelectTrigger = component$<SelectTriggerProps>((props) => {
134134

135135
/** When initially opening the listbox, we want to grab the first enabled option index */
136136
if (context.highlightedIndexSig.value === null) {
137-
context.highlightedIndexSig.value = await getNextEnabledItemIndex$(-1);
137+
const firstItem = await getNextEnabledItemIndex$(-1);
138+
context.highlightedIndexSig.value = firstItem;
139+
140+
// Wait for the popover code to be executed
141+
while (!(context.firstEnabledItemRef.value === document.activeElement)) {
142+
await new Promise((resolve) => setTimeout(resolve, 5));
143+
context.firstEnabledItemRef.value?.focus();
144+
}
145+
138146
return;
139147
}
140148
});

0 commit comments

Comments
 (0)