Skip to content

Commit 6149b6d

Browse files
feat(combobox): search Bar configuration
1 parent 54c8330 commit 6149b6d

File tree

8 files changed

+378
-67
lines changed

8 files changed

+378
-67
lines changed

apps/website/src/routes/_components/header/header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default component$(
5555
]}
5656
>
5757
<section class="flex flex-col md:flex-row gap-1 md:gap-8 mr-auto">
58-
<a href="/" class="lg:ml-8">
58+
<a href="/" aria-label="Qwik UI Logo" class="lg:ml-8">
5959
<Logo />
6060
</a>
6161

apps/website/src/routes/docs/_components/api-table/api-table.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ export const APITable = component$(({ propDescriptors }: APITableProps) => {
1515
<table class="w-full min-w-[540px] border-b border-slate-300 dark:border-slate-600 text-left sm:min-w-full mb-6">
1616
<tbody class="divide-y divide-slate-300 dark:divide-slate-600">
1717
<tr class="w-1/4 dark:text-white ">
18-
<td class="w-1/6 whitespace-nowrap py-2 pl-4 text-base font-medium sm:pl-0 text-slate-700 dark:text-slate-300 font-[600]">
18+
<th class="w-1/6 whitespace-nowrap py-2 pl-4 text-base font-medium sm:pl-0 text-slate-700 dark:text-slate-300 font-[600]">
1919
Prop
20-
</td>
21-
<td class="w-1/6 whitespace-nowrap py-2 text-base text-slate-700 dark:text-slate-300 font-[600]">
20+
</th>
21+
<th class="w-1/6 whitespace-nowrap py-2 text-base text-slate-700 dark:text-slate-300 font-[600]">
2222
Type
23-
</td>
24-
<td class="w-2/3 whitespace-nowrap p-2 text-base text-slate-700 dark:text-slate-300 font-[600]">
23+
</th>
24+
<th class="w-2/3 whitespace-nowrap p-2 text-base text-slate-700 dark:text-slate-300 font-[600]">
2525
Description
26-
</td>
26+
</th>
2727
</tr>
2828
{propDescriptors?.map((propDescriptor) => {
2929
return (

apps/website/src/routes/docs/_components/status-banner/status-banner.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const StatusBanner = component$((props: StatusBannerProps) => {
116116
>
117117
<span class="pr-2">{getMessageByStatus(props.status)}</span>
118118
<button
119+
aria-label="close status banner"
119120
onClick$={() => {
120121
// we need the margin as a variable rather than a static class.
121122
ref.value?.style.setProperty(

apps/website/src/routes/docs/headless/(components)/combobox/examples.tsx

Lines changed: 200 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { component$, Slot, useSignal, useVisibleTask$ } from '@builder.io/qwik';
1+
import {
2+
component$,
3+
QwikIntrinsicElements,
4+
Slot,
5+
useSignal,
6+
useVisibleTask$,
7+
} from '@builder.io/qwik';
28
import {
39
Combobox,
410
ComboboxControl,
@@ -1107,60 +1113,205 @@ export const HighlightedExample = component$(() => {
11071113

11081114
// // Using context example.
11091115

1110-
// import { createContextId, useContext, useContextProvider } from '@builder.io/qwik';
1116+
import { createContextId, useContext, useContextProvider } from '@builder.io/qwik';
1117+
import { StatusBadge } from '../../../_components/component-status-badge/component-status-badge';
1118+
import { statusByComponent } from 'apps/website/src/_state/component-statuses';
11111119

1112-
// // Create a context ID
1113-
// export const AnimalContext = createContextId<string[]>('animal-context');
1120+
// Create a context ID
1121+
export const AnimalContext = createContextId<string[]>('animal-context');
11141122

1115-
// export const ContextExample = component$(() => {
1116-
// const animals = ['Armadillo', 'Donkey', 'Baboon', 'Badger', 'Barracuda', 'Bat', 'Bear'];
1117-
// // Provide the animals array to the context under the context ID
1118-
// useContextProvider(AnimalContext, animals);
1123+
export const ContextExample = component$(() => {
1124+
const animals = ['Armadillo', 'Donkey', 'Baboon', 'Badger', 'Barracuda', 'Bat', 'Bear'];
1125+
// Provide the animals array to the context under the context ID
1126+
useContextProvider(AnimalContext, animals);
11191127

1120-
// return <ContextChild />;
1121-
// });
1128+
return <ContextChild />;
1129+
});
11221130

1123-
// export const ContextChild = component$(() => {
1124-
// const animals = useContext(AnimalContext);
1131+
export const ContextChild = component$(() => {
1132+
const animals = useContext(AnimalContext);
11251133

1126-
// return (
1127-
// <PreviewCodeExample>
1128-
// <div class="flex flex-col gap-4" q:slot="actualComponent">
1129-
// <Combobox options={animals} class="relative">
1130-
// <ComboboxLabel class=" font-semibold text-white">Animals 🐖</ComboboxLabel>
1131-
// <ComboboxControl class="bg-[#1f2532] flex items-center rounded-sm border-slate-400 border-[1px] relative">
1132-
// <ComboboxInput class="px-2 w-44 bg-slate-900 px-d2 pr-6 text-white placeholder:text-slate-500" />
1133-
// <ComboboxTrigger class="w-6 h-6 group absolute right-0">
1134-
// <ComboboxIcon class="stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]" />
1135-
// </ComboboxTrigger>
1136-
// </ComboboxControl>
1137-
// <ComboboxPortal>
1138-
// <ComboboxListbox
1139-
// flip={true}
1140-
// gutter={8}
1141-
// class="w-44 bg-slate-900 px-4 py-2 rounded-sm border-slate-400 border-[1px]"
1142-
// optionRenderer$={(option: ResolvedOption, index: number) => (
1143-
// <ComboboxOption
1144-
// index={index}
1145-
// resolved={option}
1146-
// class="aria-disabled:text-slate-600 aria-disabled:hover:bg-slate-700 rounded-sm px-2 hover:bg-slate-500 aria-selected:bg-slate-500 text-white border-2 border-transparent aria-selected:border-slate-200 group"
1147-
// >
1148-
// <span class="block group-aria-selected:translate-x-[3px] transition-transform duration-350">
1149-
// <span>{option.label}</span>
1150-
// </span>
1151-
// </ComboboxOption>
1152-
// )}
1153-
// />
1154-
// </ComboboxPortal>
1155-
// </Combobox>
1156-
// </div>
1134+
return (
1135+
<PreviewCodeExample>
1136+
<div class="flex flex-col gap-4" q:slot="actualComponent">
1137+
<Combobox options={animals} class="relative">
1138+
<ComboboxLabel class=" font-semibold text-white">Animals 🐖</ComboboxLabel>
1139+
<ComboboxControl class="bg-[#1f2532] flex items-center rounded-sm border-slate-400 border-[1px] relative">
1140+
<ComboboxInput class="px-2 w-44 bg-slate-900 px-d2 pr-6 text-white placeholder:text-slate-500" />
1141+
<ComboboxTrigger class="w-6 h-6 group absolute right-0">
1142+
<ComboboxIcon class="stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]" />
1143+
</ComboboxTrigger>
1144+
</ComboboxControl>
1145+
<ComboboxPortal>
1146+
<ComboboxListbox
1147+
flip={true}
1148+
gutter={8}
1149+
class="w-44 bg-slate-900 px-4 py-2 rounded-sm border-slate-400 border-[1px]"
1150+
optionRenderer$={(option: ResolvedOption, index: number) => (
1151+
<ComboboxOption
1152+
index={index}
1153+
resolved={option}
1154+
class="aria-disabled:text-slate-600 aria-disabled:hover:bg-slate-700 rounded-sm px-2 hover:bg-slate-500 aria-selected:bg-slate-500 text-white border-2 border-transparent aria-selected:border-slate-200 group"
1155+
>
1156+
<span class="block group-aria-selected:translate-x-[3px] transition-transform duration-350">
1157+
<span>{option.label}</span>
1158+
</span>
1159+
</ComboboxOption>
1160+
)}
1161+
/>
1162+
</ComboboxPortal>
1163+
</Combobox>
1164+
</div>
11571165

1158-
// <div q:slot="codeExample">
1159-
// <Slot />
1160-
// </div>
1161-
// </PreviewCodeExample>
1162-
// );
1163-
// });
1166+
<div q:slot="codeExample">
1167+
<Slot />
1168+
</div>
1169+
</PreviewCodeExample>
1170+
);
1171+
});
1172+
1173+
export const SearchBar = component$(() => {
1174+
const inputValueSig = useSignal('');
1175+
const highlightedIndexSig = useSignal(0);
1176+
const inputRef = useSignal<HTMLInputElement>();
1177+
const isListboxOpenSig = useSignal(false);
1178+
1179+
type MyComponents = {
1180+
component: string;
1181+
label: string;
1182+
};
1183+
1184+
const docsPrefix = '/docs/headless';
1185+
const components = [
1186+
{ component: 'accordion', label: 'Accordion' },
1187+
{ component: 'combobox', label: 'Combobox' },
1188+
{ component: 'popover', label: 'Popover' },
1189+
{ component: 'select', label: 'Select' },
1190+
{ component: 'separator', label: 'Separator' },
1191+
{ component: 'tabs', label: 'Tabs' },
1192+
{ component: 'toggle', label: 'Toggle' },
1193+
{ component: 'tooltip', label: 'Tooltip' },
1194+
];
1195+
1196+
return (
1197+
<PreviewCodeExample>
1198+
<div class="flex flex-col items-center gap-4 p-4" q:slot="actualComponent">
1199+
<div>
1200+
<Combobox
1201+
bind:inputValueSig={inputValueSig}
1202+
bind:inputRef={inputRef}
1203+
bind:highlightedIndexSig={highlightedIndexSig}
1204+
bind:isListboxOpenSig={isListboxOpenSig}
1205+
optionValueKey="component"
1206+
class="w-fit"
1207+
options={components}
1208+
>
1209+
<ComboboxLabel class="text-white">Qwik UI ⚡</ComboboxLabel>
1210+
<ComboboxControl class="bg-[#1f2532] rounded-sm border-slate-400 border-[1px] relative">
1211+
<ComboboxInput
1212+
onClick$={() => (isListboxOpenSig.value = !isListboxOpenSig.value)}
1213+
class="pl-6 w-44 bg-slate-900 px-d2 pr-6 text-white placeholder:text-slate-500"
1214+
onKeyDown$={(e) => {
1215+
if (e.key === 'Enter') {
1216+
const inputElement = e.target as HTMLInputElement;
1217+
window.location.href = `${docsPrefix}/${inputElement.value.toLowerCase()}`;
1218+
}
1219+
}}
1220+
/>
1221+
<ComboboxTrigger class="w-6 h-6 group absolute left-[4px]">
1222+
<SearchIcon />
1223+
</ComboboxTrigger>
1224+
{inputValueSig.value.length > 0 && (
1225+
// give separate id if two triggers
1226+
<button
1227+
id="close-button"
1228+
aria-label="clear search"
1229+
onMouseDown$={() => {
1230+
inputValueSig.value = '';
1231+
inputRef.value?.focus();
1232+
}}
1233+
class="w-6 h-6 flex justify-center items-center absolute top-0 right-0"
1234+
>
1235+
<ClearIcon class="w-4 h-4" />
1236+
</button>
1237+
)}
1238+
</ComboboxControl>
1239+
<ComboboxPortal>
1240+
<ComboboxListbox
1241+
gutter={8}
1242+
class="w-44 bg-slate-900 px-1 py-2 rounded-sm border-slate-400 border-[1px]"
1243+
hide="escaped"
1244+
optionRenderer$={(option: ResolvedOption, index: number) => {
1245+
const searchOption = option.option as MyComponents;
1246+
return (
1247+
<a
1248+
href={`${docsPrefix}/${searchOption.component}`}
1249+
aria-label={option.label}
1250+
>
1251+
<ComboboxOption
1252+
key={option.key}
1253+
class="aria-disabled:text-slate-600 aria-disabled:hover:bg-slate-700 rounded-sm px-2 hover:bg-slate-500 aria-selected:bg-slate-500 text-white border-2 border-transparent aria-selected:border-slate-200 group flex justify-between items-start gap-4"
1254+
index={index}
1255+
resolved={option}
1256+
>
1257+
<span>{searchOption.label}</span>
1258+
<span class="scale-[0.9]">
1259+
<StatusBadge
1260+
status={statusByComponent.headless[option.label]}
1261+
/>
1262+
</span>
1263+
</ComboboxOption>
1264+
</a>
1265+
);
1266+
}}
1267+
/>
1268+
</ComboboxPortal>
1269+
</Combobox>
1270+
</div>
1271+
</div>
1272+
1273+
<div q:slot="codeExample">
1274+
<Slot />
1275+
</div>
1276+
</PreviewCodeExample>
1277+
);
1278+
});
1279+
1280+
export function SearchIcon(props: QwikIntrinsicElements['svg'], key: string) {
1281+
return (
1282+
<svg
1283+
xmlns="http://www.w3.org/2000/svg"
1284+
width="1em"
1285+
height="1em"
1286+
viewBox="0 0 256 256"
1287+
{...props}
1288+
key={key}
1289+
>
1290+
<path
1291+
fill="white"
1292+
d="m228.24 219.76l-51.38-51.38a86.15 86.15 0 1 0-8.48 8.48l51.38 51.38a6 6 0 0 0 8.48-8.48ZM38 112a74 74 0 1 1 74 74a74.09 74.09 0 0 1-74-74Z"
1293+
></path>
1294+
</svg>
1295+
);
1296+
}
1297+
1298+
export function ClearIcon(props: QwikIntrinsicElements['svg'], key: string) {
1299+
return (
1300+
<svg
1301+
xmlns="http://www.w3.org/2000/svg"
1302+
width="1em"
1303+
height="1em"
1304+
viewBox="0 0 256 256"
1305+
{...props}
1306+
key={key}
1307+
>
1308+
<path
1309+
fill="white"
1310+
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm37.66 130.34a8 8 0 0 1-11.32 11.32L128 139.31l-26.34 26.35a8 8 0 0 1-11.32-11.32L116.69 128l-26.35-26.34a8 8 0 0 1 11.32-11.32L128 116.69l26.34-26.35a8 8 0 0 1 11.32 11.32L139.31 128Z"
1311+
></path>
1312+
</svg>
1313+
);
1314+
}
11641315

11651316
export const AutoPlacementExample = component$(() => {
11661317
const isListboxOpenSig = useSignal(true);

0 commit comments

Comments
 (0)