Skip to content

Commit 899a56d

Browse files
fix(fixed enter key not properly hiding listbox): enter key now hides the listbow
1 parent 44dca74 commit 899a56d

File tree

5 files changed

+204
-8
lines changed

5 files changed

+204
-8
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { component$, Slot } from '@builder.io/qwik';
2+
import {
3+
AutocompleteRoot,
4+
AutocompleteLabel,
5+
AutocompleteTrigger,
6+
AutocompleteInput,
7+
AutocompleteButton,
8+
AutocompleteListbox,
9+
AutocompleteOption,
10+
} from '@qwik-ui/headless';
11+
import { PreviewCodeExample } from '../../../../../components/preview-code-example/preview-code-example';
12+
13+
const trainers = [
14+
'Caleb',
15+
'Olivia',
16+
'James',
17+
'Ava',
18+
'Noah',
19+
'Emma',
20+
'Oliver',
21+
'Amelia',
22+
'Theodore',
23+
'Elizabeth',
24+
];
25+
26+
export const Example01 = component$(() => {
27+
return (
28+
<PreviewCodeExample>
29+
<div q:slot="actualComponent">
30+
<AutocompleteRoot style="width: fit-content">
31+
<AutocompleteLabel>Personal Trainers ⚡</AutocompleteLabel>
32+
<AutocompleteTrigger>
33+
<AutocompleteInput />
34+
<AutocompleteButton>
35+
<svg
36+
xmlns="http://www.w3.org/2000/svg"
37+
viewBox="0 0 24 24"
38+
fill="none"
39+
stroke="currentColor"
40+
stroke-width="2"
41+
stroke-linecap="round"
42+
stroke-linejoin="round"
43+
style="width: 20px; height: 20px;"
44+
>
45+
<polyline points="6 9 12 15 18 9"></polyline>
46+
</svg>
47+
</AutocompleteButton>
48+
</AutocompleteTrigger>
49+
<AutocompleteListbox class="listboxStyle">
50+
{trainers.map((trainer, index) => (
51+
<AutocompleteOption optionValue={trainer} key={index}>
52+
{trainer}
53+
</AutocompleteOption>
54+
))}
55+
</AutocompleteListbox>
56+
</AutocompleteRoot>
57+
</div>
58+
59+
<div q:slot="codeExample">
60+
<Slot />
61+
</div>
62+
</PreviewCodeExample>
63+
);
64+
});
65+
66+
export const Example02 = component$(() => {
67+
return <PreviewCodeExample></PreviewCodeExample>;
68+
});
69+
70+
export const Example03 = component$(() => {
71+
return <PreviewCodeExample></PreviewCodeExample>;
72+
});
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
title: Qwik UI | Autocomplete
3+
---
4+
5+
import {
6+
AutocompleteRoot,
7+
AutocompleteLabel,
8+
AutocompleteTrigger,
9+
AutocompleteInput,
10+
AutocompleteButton,
11+
AutocompleteListbox,
12+
AutocompleteOption,
13+
} from '@qwik-ui/headless';
14+
15+
import { Example01, Example02, Example03 } from './examples';
16+
import { CodeExample } from '../../../../../components/code-example/code-example';
17+
import { KeyboardInteractionTable } from '../../../../../components/keyboard-interaction-table/keyboard-interaction-table';
18+
import { APITable } from '../../../../../components/api-table/api-table';
19+
20+
# Autocomplete
21+
22+
#### A combobox pattern that allows users to select from a list of previously searched terms, ensuring that the listbox popup is not automatically triggered but only when a character is typed in or an open command is given.
23+
24+
{' '}
25+
26+
<Example01>```tsx ```</Example01>
27+
28+
## Building blocks
29+
30+
<CodeExample>
31+
```tsx
32+
import { component$ } from '@builder.io/qwik';
33+
import { AutocompleteRoot, AutocompleteLabel, AutocompleteTrigger, AutocompleteInput, AutocompleteButton, AutocompleteListbox, AutocompleteOption } from '@qwik-ui/headless';
34+
35+
export default component$(() => (
36+
<AutocompleteRoot>
37+
<AutocompleteLabel>Label</AutocompleteLabel>
38+
<AutocompleteTrigger>
39+
<AutocompleteInput />
40+
<AutocompleteButton>
41+
Button Marker
42+
</AutocompleteButton>
43+
</AutocompleteTrigger>
44+
<AutocompleteListbox>
45+
<AutocompleteOption />
46+
</AutocompleteListbox>
47+
</AutocompleteRoot>
48+
))
49+
```
50+
51+
</CodeExample>
52+
53+
## Examples
54+
55+
### EXAMPLE: Frequently Asked Questions
56+
57+
<Example02>```tsx ```</Example02>
58+
59+
## Accessibility
60+
61+
### Keyboard interaction
62+
63+
<KeyboardInteractionTable keyDescriptors={
64+
[
65+
{
66+
keyTitle: 'Space',
67+
description: 'Expand or collapse the `AccordionItem`.',
68+
},
69+
{
70+
keyTitle: 'Enter',
71+
description: 'Expand or collapse the `AccordionItem`.',
72+
},
73+
{
74+
keyTitle: 'Tab',
75+
description: 'Moves focus to the next focusable element.',
76+
},
77+
{
78+
keyTitle: 'Shift + Tab',
79+
description: 'Moves focus to the previous focusable element.',
80+
},
81+
{
82+
keyTitle: 'Home',
83+
description: 'When on `AccordionItem`, Moves focus to the first `AccordionItem`.',
84+
},
85+
{
86+
keyTitle: 'End',
87+
description: 'When on `AccordionItem`, Moves focus to the last `AccordionItem`.',
88+
},
89+
90+
]
91+
}/>
92+
93+
## API
94+
95+
### AccordionItem
96+
97+
<APITable
98+
propDescriptors={[
99+
{
100+
name: 'class',
101+
type: 'string',
102+
description: 'CSS classes to apply to the accordion container.',
103+
},
104+
{
105+
name: 'style',
106+
type: 'string',
107+
description: 'CSS styles to apply to the accordion container.',
108+
},
109+
{
110+
name: 'label',
111+
type: 'string',
112+
description: 'The label to give to the accordion item.',
113+
},
114+
{
115+
name: 'onClick$',
116+
type: 'PropFunction<() => void>',
117+
description: 'A custom click handler',
118+
},
119+
]}
120+
/>

apps/website/src/routes/docs/headless/menu.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
## Components
1010

1111
- [Accordion](/docs/headless/accordion)
12+
- [Autocomplete](/docs/headless/autocomplete)
1213
- [Carousel](/docs/headless/carousel)
1314
- [Popover](/docs/headless/popover)
1415
- [Select](/docs/headless/select)

packages/kit-headless/src/components/Autocomplete/autocomplete.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
$,
1313
useId,
1414
useOnWindow,
15+
useTask$,
1516
} from '@builder.io/qwik';
1617

1718
import { computePosition, flip } from '@floating-ui/dom';
@@ -287,16 +288,16 @@ export const AutocompleteInput = component$((props: InputProps) => {
287288
const inputValue = contextService.inputValue.value;
288289

289290
if (
290-
contextService.inputValue.value.length > 0 &&
291+
contextService.inputValue.value.length >= 0 &&
291292
document.activeElement === ref.value
292293
) {
293-
// issue we need to look at. Still gives an array if any keyword is a match.
294-
//For example, apple returns 3 options after hitting enter.
295-
if (optionValue.match(new RegExp(inputValue, 'i'))) {
296-
contextService.isExpanded.value = true;
297-
} else {
294+
if (optionValue === inputValue) {
298295
contextService.isExpanded.value = false;
296+
} else if (optionValue.match(new RegExp(inputValue, 'i'))) {
297+
contextService.isExpanded.value = true;
299298
}
299+
} else {
300+
contextService.isExpanded.value = false;
300301
}
301302

302303
return optionValue.match(new RegExp(inputValue, 'i'));
@@ -328,8 +329,9 @@ export const AutocompleteInput = component$((props: InputProps) => {
328329
aria-controls={contextService.listBoxId}
329330
bind:value={contextService.inputValue}
330331
onKeyDown$={(e) => {
331-
if (e.key === 'ArrowDown' && contextService.options?.[0]?.value) {
332-
contextService.filteredOptions[0].value?.focus();
332+
if (e.key === 'ArrowDown') {
333+
contextService.isExpanded.value = true;
334+
contextService.filteredOptions[0]?.value?.focus();
333335
}
334336
}}
335337
{...props}

packages/kit-headless/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from './components/tooltip/tooltip';
1818
export * as Checkbox from './components/checkbox/checkbox';
1919
export * as CheckboxProps from './components/checkbox/checkbox';
2020
export * from './components/select/select';
21+
export * from './components/autocomplete/autocomplete';
2122
export * from './components/slider';
2223
export * from './components/breadcrumb';
2324
export * from './components/navigation-bar/navigation-bar';

0 commit comments

Comments
 (0)