Files
My-KOPKB/fe/src/docs/pages/combobox.vue
T
ISMAIL MASSERAN 94ecbe5887 first init
2026-06-08 11:37:14 +08:00

1112 lines
36 KiB
Vue

<script lang="ts" setup>
import {
ComboboxRoot,
ComboboxLabel,
ComboboxControl,
ComboboxInput,
ComboboxTrigger,
ComboboxContent,
ComboboxItemGroup,
ComboboxItemGroupLabel,
ComboboxItem,
ComboboxItemText,
} from "@/components/ui/combobox";
import * as combobox from "@zag-js/combobox";
import {
Preview,
SectionTitle,
SectionContent,
InstallPackage,
PreviewCode,
} from "@/components/docs";
import { ref, computed } from "vue";
const comboboxData = [
{ label: "React", code: "react" },
{ label: "Solid", code: "solid" },
{ label: "Vue", code: "vue" },
{ label: "Svelte", code: "svelte" },
];
const timezoneData = [
{
label: "North America",
items: [
{ value: "est", label: "Eastern Standard Time (EST)" },
{ value: "cst", label: "Central Standard Time (CST)" },
{ value: "mst", label: "Mountain Standard Time (MST)" },
{ value: "pst", label: "Pacific Standard Time (PST)" },
{ value: "akst", label: "Alaska Standard Time (AKST)" },
{ value: "hst", label: "Hawaii Standard Time (HST)" },
],
},
{
label: "Europe & Africa",
items: [
{ value: "gmt", label: "Greenwich Mean Time (GMT)" },
{ value: "cet", label: "Central European Time (CET)" },
{ value: "eet", label: "Eastern European Time (EET)" },
{ value: "west", label: "Western European Summer Time (WEST)" },
{ value: "cat", label: "Central Africa Time (CAT)" },
{ value: "eat", label: "East Africa Time (EAT)" },
],
},
{
label: "Asia",
items: [
{ value: "msk", label: "Moscow Time (MSK)" },
{ value: "ist", label: "India Standard Time (IST)" },
{ value: "cst_china", label: "China Standard Time (CST)" },
{ value: "jst", label: "Japan Standard Time (JST)" },
{ value: "kst", label: "Korea Standard Time (KST)" },
{
value: "ist_indonesia",
label: "Indonesia Central Standard Time (WITA)",
},
],
},
{
label: "Australia & Pacific",
items: [
{ value: "awst", label: "Australian Western Standard Time (AWST)" },
{ value: "acst", label: "Australian Central Standard Time (ACST)" },
{ value: "aest", label: "Australian Eastern Standard Time (AEST)" },
{ value: "nzst", label: "New Zealand Standard Time (NZST)" },
{ value: "fjt", label: "Fiji Time (FJT)" },
],
},
{
label: "South America",
items: [
{ value: "art", label: "Argentina Time (ART)" },
{ value: "bot", label: "Bolivia Time (BOT)" },
{ value: "brt", label: "Brasilia Time (BRT)" },
{ value: "clt", label: "Chile Standard Time (CLT)" },
],
},
];
const stateSingle = ref([""]);
const stateMultiple = ref([""]);
const stateTimezone = ref([""]);
const options = ref(comboboxData);
const timezoneOptions = ref(timezoneData);
const collection = computed(() =>
combobox.collection({
items: options.value,
itemToValue: (item) => item.label,
})
);
const collectionTimezone = computed(() =>
combobox.collection({
items: timezoneOptions.value.flatMap((region) =>
region.items.map((item) => ({
region: region.label,
value: item.value,
label: item.label,
}))
),
})
);
</script>
<template>
<div id="preview" class="-mt-20">
<Preview>
<template #preview>
<ComboboxRoot :collection="collection" :value="stateSingle"
@value-change="(details) => (stateSingle = details.value)" @input-value-change="
({ inputValue }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
options = filtered.length > 0 ? filtered : comboboxData;
}
" class="w-56">
<ComboboxLabel>Single</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search frameworks..." />
<ComboboxItemGroup>
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel>
<ComboboxItem v-for="item in collection.items" :key="item.code" :item="item">
<ComboboxItemText>{{ item.label }}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
</template>
<template #code>
<PreviewCode>
{{`
const comboboxData = [
{ label: "React", code: "react" },
{ label: "Solid", code: "solid" },
{ label: "Vue", code: "vue" },
{ label: "Svelte", code: "svelte" },
];
const timezoneData = [
{
label: "North America",
items: [
{ value: "est", label: "Eastern Standard Time (EST)" },
{ value: "cst", label: "Central Standard Time (CST)" },
{ value: "mst", label: "Mountain Standard Time (MST)" },
{ value: "pst", label: "Pacific Standard Time (PST)" },
{ value: "akst", label: "Alaska Standard Time (AKST)" },
{ value: "hst", label: "Hawaii Standard Time (HST)" },
],
},
{
label: "Europe & Africa",
items: [
{ value: "gmt", label: "Greenwich Mean Time (GMT)" },
{ value: "cet", label: "Central European Time (CET)" },
{ value: "eet", label: "Eastern European Time (EET)" },
{ value: "west", label: "Western European Summer Time (WEST)" },
{ value: "cat", label: "Central Africa Time (CAT)" },
{ value: "eat", label: "East Africa Time (EAT)" },
],
},
{
label: "Asia",
items: [
{ value: "msk", label: "Moscow Time (MSK)" },
{ value: "ist", label: "India Standard Time (IST)" },
{ value: "cst_china", label: "China Standard Time (CST)" },
{ value: "jst", label: "Japan Standard Time (JST)" },
{ value: "kst", label: "Korea Standard Time (KST)" },
{
value: "ist_indonesia",
label: "Indonesia Central Standard Time (WITA)",
},
],
},
{
label: "Australia & Pacific",
items: [
{ value: "awst", label: "Australian Western Standard Time (AWST)" },
{ value: "acst", label: "Australian Central Standard Time (ACST)" },
{ value: "aest", label: "Australian Eastern Standard Time (AEST)" },
{ value: "nzst", label: "New Zealand Standard Time (NZST)" },
{ value: "fjt", label: "Fiji Time (FJT)" },
],
},
{
label: "South America",
items: [
{ value: "art", label: "Argentina Time (ART)" },
{ value: "bot", label: "Bolivia Time (BOT)" },
{ value: "brt", label: "Brasilia Time (BRT)" },
{ value: "clt", label: "Chile Standard Time (CLT)" },
],
},
];
const stateSingle = ref([""]);
const stateMultiple = ref([""]);
const stateTimezone = ref([""]);
const options = ref(comboboxData);
const timezoneOptions = ref(timezoneData);
const collection = computed(() =>
combobox.collection({
items: options.value,
itemToValue: (item) => item.label,
})
);
const collectionTimezone = computed(() =>
combobox.collection({
items: timezoneOptions.value.flatMap((region) =>
region.items.map((item) => ({
region: region.label,
value: item.value,
label: item.label,
}))
),
})
);
<ComboboxRoot :collection="collection" :value="stateSingle"
@value-change="(details) => (stateSingle = details.value)" @input-value-change="
({ inputValue }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
options = filtered.length > 0 ? filtered : comboboxData;
}
" class="w-56">
<ComboboxLabel>Single</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search frameworks..." />
<ComboboxItemGroup>
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel>
<ComboboxItem v-for="item in collection.items" :key="item.code" :item="item">
<ComboboxItemText>\{\{ item.label \}\}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
` }}
</PreviewCode>
</template>
</Preview>
</div>
<div id="installation">
<SectionTitle>Installation</SectionTitle>
<SectionContent>Install the following dependencies:</SectionContent>
<InstallPackage>add @zag-js/vue @zag-js/combobox</InstallPackage>
<SectionContent>
Copy and paste the following code into your project.
</SectionContent>
<PreviewCode title="components/ui/combobox/ComboboxRoot.vue">
{{`
<script lang="ts" setup>
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxRoot } from "@mykopkb/core/styles/combobox.styles";
import { provide, computed } from "vue";
import * as combobox from "@zag-js/combobox";
import { useMachine, normalizeProps } from "@zag-js/vue";
import type { Props } from "@zag-js/combobox";
import { Slot } from "@/components/ui/slot";
const {
class: className,
multiple = false,
selectionBehavior = "clear",
value,
asChild = false,
onOpenChange,
onInputValueChange,
onValueChange,
open = undefined,
...props
} = defineProps<Partial<Props> & { asChild?: boolean; class?: string }>();
const service = useMachine(combobox.machine, {
multiple,
selectionBehavior,
onOpenChange,
onInputValueChange,
onValueChange,
open,
...props,
id: crypto.randomUUID(),
});
const api = computed(() => combobox.connect(service, normalizeProps));
provide("comboboxApi", api);
</script>
<template>
<Slot :class="cn(comboboxRoot, className)" :data-multiple="multiple"
v-bind="{ ...props, ...$attrs, ...api.getRootProps() }">
<slot v-if="asChild" />
<div v-else>
<slot />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxLabel.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxLabel } from "@mykopkb/core/styles/combobox.styles";
import { Label } from "@/components/ui/label";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Slot v-bind="{ ...props, ...$attrs, ...api?.getLabelProps() }">
<slot v-if="asChild" />
<Label v-else :class="cn(comboboxLabel, className)">
<slot />
</Label>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxControl.vue">
{{ `
<script lang="ts" setup>
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxControl } from "@mykopkb/core/styles/combobox.styles";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Slot :class="cn(comboboxControl, className)" v-bind="{ ...props, ...$attrs, ...api?.getLabelProps() }">
<slot v-if="asChild" />
<div v-else>
<slot />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxInput.vue">
{{ `
<script lang="ts" setup>
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxInput } from "@mykopkb/core/styles/combobox.styles";
import { Input } from "@/components/ui/input";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
const { class: className, ...props } = defineProps<{
class?: string;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Input :class="cn(comboboxInput, className)" v-bind="{ ...props, ...$attrs, ...api?.getInputProps() }" />
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxTrigger.vue">
{{ `
<script lang="ts" setup>
import { cn } from "@mykopkb/core/utils/cn";
import { Button } from "@/components/ui/button";
import { ChevronsUpDownIcon } from "@lucide/vue";
import { comboboxTrigger } from "@mykopkb/core/styles/combobox.styles";
import { ComboboxClearTrigger } from ".";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
import { Slot } from "@/components/ui/slot";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Slot v-bind="{ ...props, ...$attrs, ...api?.getTriggerProps() }">
<Button variant="ghost" v-if="!asChild" :class="cn(comboboxTrigger, className)">
<div>\{\{ api?.valueAsString || "Select Options..." \}\}</div>
<ComboboxClearTrigger>Clear</ComboboxClearTrigger>
<ChevronsUpDownIcon />
</Button>
<slot v-else />
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxClearTrigger.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxClearTrigger } from "@mykopkb/core/styles/combobox.styles";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Slot v-bind="{ ...props, ...$attrs, ...api?.getClearTriggerProps() }">
<slot v-if="asChild" />
<span :class="cn(comboboxClearTrigger, className)" v-else>
<slot />
</span>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxPositioner.vue">
{{ `
<script lang="ts" setup>
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxPositioner } from "@mykopkb/core/styles/combobox.styles";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
import { Slot } from "@/components/ui/slot";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Slot :class="cn(comboboxPositioner, className)" v-bind="{ ...props, ...$attrs, ...api?.getPositionerProps() }">
<slot v-if="asChild" />
<div v-else>
<slot />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxContent.vue">
{{ `
<script lang="ts" setup>
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxContent } from "@mykopkb/core/styles/combobox.styles";
import { Box } from "@/components/ui/box";
import { ComboboxPositioner } from ".";
import type { Api } from "@zag-js/combobox";
import { inject } from "vue";
import { Slot } from "@/components/ui/slot";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
</script>
<template>
<Teleport to="body">
<ComboboxPositioner>
<Slot v-bind="{ ...props, ...$attrs, ...api?.getContentProps() }">
<slot v-if="asChild" />
<Box v-else raised="single" :class="cn(comboboxContent, className)">
<div>
<slot />
</div>
</Box>
</Slot>
</ComboboxPositioner>
</Teleport>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxItemGroup.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxItemGroup } from "@mykopkb/core/styles/combobox.styles";
import type { Api } from "@zag-js/combobox";
import { provide, inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
const itemGroupId = { id: crypto.randomUUID() };
provide("comboboxItemGroupId", itemGroupId);
</script>
<template>
<Slot :class="cn(comboboxItemGroup, className)"
v-bind="{ ...props, ...$attrs, ...api?.getItemGroupProps(itemGroupId) }">
<slot v-if="asChild" />
<div v-else>
<slot />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxItemGroupLabel.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxItemGroupLabel } from "@mykopkb/core/styles/combobox.styles";
import type { Api, ItemGroupProps } from "@zag-js/combobox";
import { inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
const itemGroupId = inject<ItemGroupProps>("comboboxItemGroupId");
</script>
<template>
<Slot :class="cn(comboboxItemGroupLabel, className)" v-bind="{ ...props, ...$attrs, ...api?.getItemGroupLabelProps({
htmlFor: itemGroupId?.id!,
}) }">
<slot v-if="asChild" />
<label v-else>
<slot />
</label>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxItem.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxItem } from "@mykopkb/core/styles/combobox.styles";
import type { ItemProps } from "@zag-js/combobox";
import type { Api } from "@zag-js/combobox";
import { provide, inject } from "vue";
import { ComboboxItemIndicator } from ".";
const {
class: className,
asChild = false,
...props
} = defineProps<ItemProps & { class?: string; asChild?: boolean }>();
const api = inject<Api>("comboboxApi");
provide("comboboxItem", props);
</script>
<template>
<Slot :class="cn(comboboxItem, className)" v-bind="{ ...props, ...$attrs, ...api?.getItemProps(props) }">
<slot v-if="asChild" />
<div v-else>
<slot />
<ComboboxItemIndicator />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxItemText.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxItemText } from "@mykopkb/core/styles/combobox.styles";
import type { Api, ItemProps } from "@zag-js/combobox";
import { inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
const item = inject<ItemProps>("comboboxItem");
</script>
<template>
<Slot :class="cn(comboboxItemText, className)"
v-bind="{ ...props, ...$attrs, ...api?.getItemTextProps(item!) }">
<slot v-if="asChild" />
<div v-else>
<slot />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/ComboboxItemIndicator.vue">
{{ `
<script lang="ts" setup>
import { Slot } from "@/components/ui/slot";
import { cn } from "@mykopkb/core/utils/cn";
import { comboboxItemIndicator } from "@mykopkb/core/styles/combobox.styles";
import { Check } from "@lucide/vue";
import type { Api, ItemProps } from "@zag-js/combobox";
import { inject } from "vue";
const {
class: className,
asChild = false,
...props
} = defineProps<{
class?: string;
asChild?: boolean;
}>();
const api = inject<Api>("comboboxApi");
const item = inject<ItemProps>("comboboxItem");
</script>
<template>
<Slot :class="cn(comboboxItemIndicator, className)"
v-bind="{ ...props, ...$attrs, ...api?.getItemIndicatorProps(item!) }">
<slot v-if="asChild" />
<div v-else>
<slot v-if="$slots.default" />
<Check v-else class="size-3.5" />
</div>
</Slot>
</template>
` }}
</PreviewCode>
<PreviewCode title="components/ui/combobox/index.ts">
{{ `
export { default as ComboboxRoot } from "./ComboboxRoot.vue";
export { default as ComboboxLabel } from "./ComboboxLabel.vue";
export { default as ComboboxControl } from "./ComboboxControl.vue";
export { default as ComboboxInput } from "./ComboboxInput.vue";
export { default as ComboboxTrigger } from "./ComboboxTrigger.vue";
export { default as ComboboxClearTrigger } from "./ComboboxClearTrigger.vue";
export { default as ComboboxPositioner } from "./ComboboxPositioner.vue";
export { default as ComboboxContent } from "./ComboboxContent.vue";
export { default as ComboboxItemGroup } from "./ComboboxItemGroup.vue";
export { default as ComboboxItemGroupLabel } from "./ComboboxItemGroupLabel.vue";
export { default as ComboboxItem } from "./ComboboxItem.vue";
export { default as ComboboxItemText } from "./ComboboxItemText.vue";
export { default as ComboboxItemIndicator } from "./ComboboxItemIndicator.vue";
` }}
</PreviewCode>
<SectionContent>
Update the import paths to match your project setup.
</SectionContent>
</div>
<div id="usage">
<SectionTitle>Usage</SectionTitle>
<PreviewCode>
{{ `
import {
ComboboxRoot,
ComboboxLabel,
ComboboxControl,
ComboboxInput,
ComboboxTrigger,
ComboboxContent,
ComboboxItemGroup,
ComboboxItemGroupLabel,
ComboboxItem,
ComboboxItemText,
} from "@/components/ui/combobox";
` }}
</PreviewCode>
<PreviewCode>
{{`
<ComboboxRoot :collection="collection" :value="stateSingle"
@value-change="(details) => (stateSingle = details.value)" @input-value-change="
({ inputValue }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
options = filtered.length > 0 ? filtered : comboboxData;
}
" class="w-56">
<ComboboxLabel>Single</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search frameworks..." />
<ComboboxItemGroup>
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel>
<ComboboxItem v-for="item in collection.items" :key="item.code" :item="item">
<ComboboxItemText>\{\{ item.label \}\}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
` }}
</PreviewCode>
</div>
<div id="variants">
<SectionTitle>Variants</SectionTitle>
<SectionContent>A collection of components you can use.</SectionContent>
<Preview>
<template #preview>
<ComboboxRoot :collection="collection" :value="stateMultiple"
@value-change="(details) => (stateMultiple = details.value)" @input-value-change="
({ inputValue }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
options = filtered.length > 0 ? filtered : comboboxData;
}
" class="w-56" multiple>
<ComboboxLabel>Multiple</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search frameworks..." />
<ComboboxItemGroup>
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel>
<ComboboxItem v-for="item in collection.items" :key="item.code" :item="item">
<ComboboxItemText>{{ item.label }}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
</template>
<template #code>
<PreviewCode>
{{`
const comboboxData = [
{ label: "React", code: "react" },
{ label: "Solid", code: "solid" },
{ label: "Vue", code: "vue" },
{ label: "Svelte", code: "svelte" },
];
const timezoneData = [
{
label: "North America",
items: [
{ value: "est", label: "Eastern Standard Time (EST)" },
{ value: "cst", label: "Central Standard Time (CST)" },
{ value: "mst", label: "Mountain Standard Time (MST)" },
{ value: "pst", label: "Pacific Standard Time (PST)" },
{ value: "akst", label: "Alaska Standard Time (AKST)" },
{ value: "hst", label: "Hawaii Standard Time (HST)" },
],
},
{
label: "Europe & Africa",
items: [
{ value: "gmt", label: "Greenwich Mean Time (GMT)" },
{ value: "cet", label: "Central European Time (CET)" },
{ value: "eet", label: "Eastern European Time (EET)" },
{ value: "west", label: "Western European Summer Time (WEST)" },
{ value: "cat", label: "Central Africa Time (CAT)" },
{ value: "eat", label: "East Africa Time (EAT)" },
],
},
{
label: "Asia",
items: [
{ value: "msk", label: "Moscow Time (MSK)" },
{ value: "ist", label: "India Standard Time (IST)" },
{ value: "cst_china", label: "China Standard Time (CST)" },
{ value: "jst", label: "Japan Standard Time (JST)" },
{ value: "kst", label: "Korea Standard Time (KST)" },
{
value: "ist_indonesia",
label: "Indonesia Central Standard Time (WITA)",
},
],
},
{
label: "Australia & Pacific",
items: [
{ value: "awst", label: "Australian Western Standard Time (AWST)" },
{ value: "acst", label: "Australian Central Standard Time (ACST)" },
{ value: "aest", label: "Australian Eastern Standard Time (AEST)" },
{ value: "nzst", label: "New Zealand Standard Time (NZST)" },
{ value: "fjt", label: "Fiji Time (FJT)" },
],
},
{
label: "South America",
items: [
{ value: "art", label: "Argentina Time (ART)" },
{ value: "bot", label: "Bolivia Time (BOT)" },
{ value: "brt", label: "Brasilia Time (BRT)" },
{ value: "clt", label: "Chile Standard Time (CLT)" },
],
},
];
const stateSingle = ref([""]);
const stateMultiple = ref([""]);
const stateTimezone = ref([""]);
const options = ref(comboboxData);
const timezoneOptions = ref(timezoneData);
const collection = computed(() =>
combobox.collection({
items: options.value,
itemToValue: (item) => item.label,
})
);
const collectionTimezone = computed(() =>
combobox.collection({
items: timezoneOptions.value.flatMap((region) =>
region.items.map((item) => ({
region: region.label,
value: item.value,
label: item.label,
}))
),
})
);
<ComboboxRoot :collection="collection" :value="stateMultiple"
@value-change="(details) => (stateMultiple = details.value)" @input-value-change="
({ inputValue }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
options = filtered.length > 0 ? filtered : comboboxData;
}
" class="w-56" multiple>
<ComboboxLabel>Multiple</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search frameworks..." />
<ComboboxItemGroup>
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel>
<ComboboxItem v-for="item in collection.items" :key="item.code" :item="item">
<ComboboxItemText>\{\{ item.label \}\}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
` }}
</PreviewCode>
</template>
</Preview>
<Preview>
<template #preview>
<ComboboxRoot :collection="collection" @input-value-change="
({ inputValue }) => {
const filtered = timezoneData
.map((group) => {
const matchedItems = group.items.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
return {
...group,
items: matchedItems,
};
})
.filter((group) => group.items.length > 0);
timezoneOptions = filtered.length > 0 ? filtered : timezoneData;
}
" :value="stateTimezone" @value-change="(details) => (stateTimezone = details.value)" class="w-56" multiple>
<ComboboxLabel>Scrollable</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search region..." />
<ComboboxItemGroup v-for="item in timezoneData
.map((region) => ({
...region,
items: region.items.filter((item) =>
collectionTimezone.items.some((c) => c.value === item.value)
),
}))
.filter((region) => region.items.length > 0)" :key="item.label">
<ComboboxItemGroupLabel>{{ item.label }}</ComboboxItemGroupLabel>
<ComboboxItem v-for="timezoneItem in item.items" :key="timezoneItem.value" :item="timezoneItem.value">
<ComboboxItemText>{{ timezoneItem.label }}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
</template>
<template #code>
<PreviewCode>
{{`
const comboboxData = [
{ label: "React", code: "react" },
{ label: "Solid", code: "solid" },
{ label: "Vue", code: "vue" },
{ label: "Svelte", code: "svelte" },
];
const timezoneData = [
{
label: "North America",
items: [
{ value: "est", label: "Eastern Standard Time (EST)" },
{ value: "cst", label: "Central Standard Time (CST)" },
{ value: "mst", label: "Mountain Standard Time (MST)" },
{ value: "pst", label: "Pacific Standard Time (PST)" },
{ value: "akst", label: "Alaska Standard Time (AKST)" },
{ value: "hst", label: "Hawaii Standard Time (HST)" },
],
},
{
label: "Europe & Africa",
items: [
{ value: "gmt", label: "Greenwich Mean Time (GMT)" },
{ value: "cet", label: "Central European Time (CET)" },
{ value: "eet", label: "Eastern European Time (EET)" },
{ value: "west", label: "Western European Summer Time (WEST)" },
{ value: "cat", label: "Central Africa Time (CAT)" },
{ value: "eat", label: "East Africa Time (EAT)" },
],
},
{
label: "Asia",
items: [
{ value: "msk", label: "Moscow Time (MSK)" },
{ value: "ist", label: "India Standard Time (IST)" },
{ value: "cst_china", label: "China Standard Time (CST)" },
{ value: "jst", label: "Japan Standard Time (JST)" },
{ value: "kst", label: "Korea Standard Time (KST)" },
{
value: "ist_indonesia",
label: "Indonesia Central Standard Time (WITA)",
},
],
},
{
label: "Australia & Pacific",
items: [
{ value: "awst", label: "Australian Western Standard Time (AWST)" },
{ value: "acst", label: "Australian Central Standard Time (ACST)" },
{ value: "aest", label: "Australian Eastern Standard Time (AEST)" },
{ value: "nzst", label: "New Zealand Standard Time (NZST)" },
{ value: "fjt", label: "Fiji Time (FJT)" },
],
},
{
label: "South America",
items: [
{ value: "art", label: "Argentina Time (ART)" },
{ value: "bot", label: "Bolivia Time (BOT)" },
{ value: "brt", label: "Brasilia Time (BRT)" },
{ value: "clt", label: "Chile Standard Time (CLT)" },
],
},
];
const stateSingle = ref([""]);
const stateMultiple = ref([""]);
const stateTimezone = ref([""]);
const options = ref(comboboxData);
const timezoneOptions = ref(timezoneData);
const collection = computed(() =>
combobox.collection({
items: options.value,
itemToValue: (item) => item.label,
})
);
const collectionTimezone = computed(() =>
combobox.collection({
items: timezoneOptions.value.flatMap((region) =>
region.items.map((item) => ({
region: region.label,
value: item.value,
label: item.label,
}))
),
})
);
<ComboboxRoot :collection="collection" @input-value-change="
({ inputValue }) => {
const filtered = timezoneData
.map((group) => {
const matchedItems = group.items.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
return {
...group,
items: matchedItems,
};
})
.filter((group) => group.items.length > 0);
timezoneOptions = filtered.length > 0 ? filtered : timezoneData;
}
" :value="stateTimezone" @value-change="(details) => (stateTimezone = details.value)" class="w-56" multiple>
<ComboboxLabel>Scrollable</ComboboxLabel>
<ComboboxControl>
<ComboboxTrigger />
</ComboboxControl>
<ComboboxContent>
<ComboboxInput placeholder="Search region..." />
<ComboboxItemGroup v-for="item in timezoneData
.map((region) => ({
...region,
items: region.items.filter((item) =>
collectionTimezone.items.some((c) => c.value === item.value)
),
}))
.filter((region) => region.items.length > 0)" :key="item.label">
<ComboboxItemGroupLabel>\{\{ item.label \}\}</ComboboxItemGroupLabel>
<ComboboxItem v-for="timezoneItem in item.items" :key="timezoneItem.value" :item="timezoneItem.value">
<ComboboxItemText>\{\{ timezoneItem.label \}\}</ComboboxItemText>
</ComboboxItem>
</ComboboxItemGroup>
</ComboboxContent>
</ComboboxRoot>
` }}
</PreviewCode>
</template>
</Preview>
</div>
</template>