Skip to content

Commit a993289

Browse files
select: proper CSR fix
1 parent 8fe9baa commit a993289

File tree

4 files changed

+21
-25
lines changed

4 files changed

+21
-25
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ export const HiddenNativeSelect = component$(
5151
onFocus$={() => context.triggerRef.value?.focus()}
5252
ref={(element: HTMLSelectElement) => {
5353
nativeSelectRef.value = element;
54-
55-
// @ts-expect-error ref
54+
// @ts-expect-error modular forms ref function
5655
ref?.(element);
5756
}}
5857
multiple={context.multiple}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,30 @@ import { PopoverPanel } from '../popover/popover-panel';
1111

1212
import SelectContextId from './select-context';
1313
import { PopoverRoot } from '../popover/popover-root';
14+
import { isServer } from '@builder.io/qwik/build';
1415

1516
export const SelectPopover = component$<PropsOf<typeof PopoverRoot>>((props) => {
1617
const context = useContext(SelectContextId);
1718
const { showPopover, hidePopover } = usePopover(context.localId);
18-
const initialLoadSig = useSignal<boolean>(true);
1919

2020
const { floating, flip, hover, gutter, ...rest } = props;
21+
const initialLoadSig = useSignal<boolean>(true);
2122

2223
useTask$(async ({ track }) => {
2324
track(() => context.isListboxOpenSig.value);
2425

26+
if (isServer) return;
27+
2528
if (!initialLoadSig.value) {
2629
if (context.isListboxOpenSig.value) {
2730
await showPopover();
2831
} else {
2932
await hidePopover();
3033
}
3134
}
35+
});
3236

37+
useTask$(() => {
3338
initialLoadSig.value = false;
3439
});
3540

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

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
useId,
1111
useComputed$,
1212
} from '@builder.io/qwik';
13-
import { isBrowser, isServer } from '@builder.io/qwik/build';
1413
import SelectContextId, { type SelectContext } from './select-context';
1514
import { useSelect } from './use-select';
1615

@@ -154,7 +153,6 @@ export const SelectImpl = component$<SelectProps<boolean> & InternalSelectProps>
154153
const highlightedIndexSig = useSignal<number | null>(givenValuePropIndex ?? null);
155154

156155
const isListboxOpenSig = useSignal<boolean>(false);
157-
const initialLoadSig = useSignal<boolean>(true);
158156
const scrollOptions = givenScrollOptions ?? {
159157
behavior: 'instant',
160158
block: 'nearest',
@@ -187,20 +185,17 @@ export const SelectImpl = component$<SelectProps<boolean> & InternalSelectProps>
187185
const { getActiveDescendant$, extractedStrOrArrFromMap$, selectionManager$ } =
188186
useSelect();
189187

190-
useTask$(function reactiveUserValue({ track }) {
188+
useTask$(async function reactiveUserValue({ track }) {
191189
const bindValueSig = props['bind:value'];
192190
if (!bindValueSig) return;
193191
track(() => bindValueSig.value);
194192

195193
for (const [index, item] of itemsMapSig.value) {
196194
if (bindValueSig.value.includes(item.value)) {
197-
selectionManager$(index, 'add');
195+
await selectionManager$(index, 'add');
198196

199-
if (initialLoadSig.value) {
200-
// for both SSR and CSR, we need to set the initial index
201-
context.highlightedIndexSig.value = index;
202-
}
203-
initialLoadSig.value = false;
197+
// for both SSR and CSR, we need to set the initial index
198+
context.highlightedIndexSig.value = index;
204199
}
205200
}
206201
});
@@ -215,9 +210,7 @@ export const SelectImpl = component$<SelectProps<boolean> & InternalSelectProps>
215210

216211
useTask$(function onOpenChangeTask({ track }) {
217212
track(() => isListboxOpenSig.value);
218-
if (isBrowser) {
219-
onOpenChange$?.(isListboxOpenSig.value);
220-
}
213+
onOpenChange$?.(isListboxOpenSig.value);
221214
});
222215

223216
const activeDescendantSig = useComputed$(() => {
@@ -233,8 +226,6 @@ export const SelectImpl = component$<SelectProps<boolean> & InternalSelectProps>
233226
const bindDisplayTextSig = props['bind:displayText'];
234227
track(() => selectedIndexSetSig.value);
235228

236-
if (isServer) return;
237-
238229
const currValue = await extractedStrOrArrFromMap$('value');
239230
const currDisplayValue = await extractedStrOrArrFromMap$('displayValue');
240231

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,21 @@ export function useTypeahead() {
124124
const inputStrSig = useSignal('');
125125
const indexDiffSig = useSignal<number | undefined>(undefined);
126126
const prevTimeoutSig = useSignal<undefined | NodeJS.Timeout>(undefined);
127+
const { selectionManager$ } = useSelect();
127128

128129
const firstCharItemSig = useComputed$(() => {
129130
return Array.from(context.itemsMapSig.value.values()).map((item) =>
130131
item.displayValue?.slice(0, 1).toLowerCase(),
131132
);
132133
});
133134

134-
const typeahead$ = $((key: string): void => {
135+
const typeahead$ = $(async (key: string): Promise<void> => {
135136
inputStrSig.value += key;
136137
if (key.length > 1) {
137138
return;
138139
}
139140

140-
const firstCharOnly$ = $(() => {
141+
const firstCharOnly$ = $(async () => {
141142
// First opens the listbox if it is not already displayed and then moves visual focus to the first option that matches the typed character.
142143
const singleInputChar = key.toLowerCase();
143144

@@ -152,7 +153,7 @@ export function useTypeahead() {
152153
context.highlightedIndexSig.value = firstCharIndex;
153154

154155
if (!context.isListboxOpenSig.value) {
155-
context.selectedIndexSetSig.value = new Set([firstCharIndex]);
156+
await selectionManager$(firstCharIndex, 'add');
156157
}
157158

158159
return;
@@ -175,7 +176,7 @@ export function useTypeahead() {
175176

176177
context.highlightedIndexSig.value = nextIndex;
177178
if (!context.isListboxOpenSig.value) {
178-
context.selectedIndexSetSig.value = new Set([nextIndex]);
179+
await selectionManager$(nextIndex, 'add');
179180
}
180181
indexDiffSig.value = nextIndex + 1;
181182
return;
@@ -184,20 +185,20 @@ export function useTypeahead() {
184185
indexDiffSig.value = undefined;
185186
context.highlightedIndexSig.value = firstCharIndex;
186187
if (!context.isListboxOpenSig.value) {
187-
context.selectedIndexSetSig.value = new Set([firstCharIndex]);
188+
await selectionManager$(firstCharIndex, 'add');
188189
}
189190
return;
190191
}
191192
indexDiffSig.value = firstCharIndex + 1;
192193
context.highlightedIndexSig.value = firstCharIndex;
193194
if (!context.isListboxOpenSig.value) {
194-
context.selectedIndexSetSig.value = new Set([firstCharIndex]);
195+
await selectionManager$(firstCharIndex, 'add');
195196
}
196197

197198
return;
198199
});
199200

200-
const multipleChars$ = $(() => {
201+
const multipleChars$ = $(async () => {
201202
// If multiple keys are typed in quick succession, visual focus moves to the first option that matches the full string.
202203
clearTimeout(prevTimeoutSig.value);
203204
prevTimeoutSig.value = setTimeout(() => {
@@ -219,7 +220,7 @@ export function useTypeahead() {
219220
if (firstPossibleOpt !== -1) {
220221
context.highlightedIndexSig.value = firstPossibleOpt;
221222
if (!context.isListboxOpenSig.value) {
222-
context.selectedIndexSetSig.value = new Set([firstPossibleOpt]);
223+
await selectionManager$(firstPossibleOpt, 'add');
223224
}
224225
return;
225226
}

0 commit comments

Comments
 (0)