Select
Allow users to choose one or more options from a dropdown list.
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption } from '@holistics/design-system'
const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
] as const satisfies SelectOption[]
const value = ref<typeof options[number]['value']>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
/>
</template>Examples
Option Types & Grouped Options
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption, type SelectValue } from '@holistics/design-system'
const options = [
{
stickyTop: true, value: 101, label: 'Top Option 1', tooltip: 'Lorem ipsum donor',
},
{
stickyTop: true,
value: 102,
label: 'Top Option 2',
tooltip: 'Some tips at a group',
children: [
{ stickyTop: true, value: '102.1', label: 'Top Option 2.1' },
{
stickyTop: true, value: '102.2', label: 'Inner-Bell', icon: 'bell', action: () => { console.log('Inner-bell logged!') },
},
],
initialExpanded: true,
},
{
stickyTop: true,
value: 103,
label: 'Top Action Option',
icon: 'function',
action: (option) => { alert(`You clicked an option with value: "${option.value}" and label: "${option.label}"`) },
tooltip: { content: '<em>I can be HTML, too!</em>', html: true },
},
{ value: 1, label: 'Option 1', tooltip: 'I can be anywhere!' },
{ value: 2, label: 'Option 2 (with icon)', icon: 'favorite' },
{ value: 3, label: 'Option 3', description: 'Option with description' },
{
value: 4, label: 'Option 4 has a VERY Very very long label', icon: 'data-set', description: 'Option with description & icon, and a very long long long long long long long long long long long description',
},
{
value: 5, label: 'Option 5 (disabled)', disabled: true, tooltip: 'Disabled option only shows tooltip on hovering (not keyboad)!',
},
{
value: 6,
label: 'Option 6',
children: [
{ label: 'Option a.1', value: '6.1', icon: 'type/number' },
{ label: 'Option b.2', value: '6.2', icon: 'type/string' },
{
label: 'Option x.3',
value: '6.3',
children: [
{ label: 'Option d.3.1', value: '6.3.1' },
{ label: 'Option e.3.2', value: '6.3.2' },
{
label: 'Option x.3.3',
value: '6.3.3',
children: [
{ label: 'Option g.3.3.1', value: '6.3.3.1' },
{ label: 'Option h.3.3.2', value: '6.3.3.2' },
{ label: 'Option i.3.3.3', value: '6.3.3.3' },
{
label: 'Option x.3.3.4',
value: '6.3.3.4',
children: [
{ label: 'Option k.3.3.4.1', value: '6.3.3.4.1' },
{ label: 'Option l.3.3.4.2', value: '6.3.3.4.2' },
{ label: 'Option 6.3.3.4.3', value: '6.3.3.4.3' },
{ label: 'Option m.3.3.4.4', value: '6.3.3.4.4' },
],
},
{ label: 'Option j.3.3.3', value: '6.3.3.5' },
],
},
{ label: 'Option f.3.4', value: '6.3.4' },
],
initialExpanded: true,
},
{ label: 'Option c.4', value: '6.4' },
],
},
{ value: 7, label: 'Option 7', icon: 'user' },
{ value: 8, label: 'Option 8' },
{ value: 9, label: 'Option 9' },
{ value: 10, label: 'Option 10' },
{ value: null, label: 'Option null' },
{ stickyBottom: true, value: 901, label: 'Bottom Option 1' },
{
stickyBottom: true,
value: 902,
label: 'Bottom Option 2',
children: [
{ stickyBottom: true, value: '901.1', label: 'Bottom Option 2.1' },
{ stickyBottom: true, value: '901.2', label: 'Bottom Option 2.2' },
],
},
{
stickyBottom: true,
value: 903,
label: 'Async Refresh (done after 2s)',
icon: 'refresh',
action: async () => {
await new Promise((res) => { setTimeout(res, 2000) })
alert('Done refreshing!')
},
},
] as const satisfies SelectOption[]
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
/>
</template>By default, group options are not selectable. To alter this, simply set groupSelectable to true. Be aware that enabling this feature will consequently affect the following behaviors:
- To select/deslect a group option: Click/Enter
- To expand a group option: Click on chevron icon
>/Double-click/Shift + Enter
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { OPTIONS } from './options'
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="OPTIONS"
group-selectable
class="w-80"
/>
</template>import type { SelectOption } from '@holistics/design-system'
export const OPTIONS = [
{
stickyTop: true, value: 101, label: 'Top Option 1', tooltip: 'Lorem ipsum donor',
},
{
stickyTop: true,
value: 102,
label: 'Top Option 2',
tooltip: 'Some tips at a group',
children: [
{ stickyTop: true, value: '102.1', label: 'Top Option 2.1' },
{
stickyTop: true, value: '102.2', label: 'Inner-Bell', icon: 'bell', action: () => { console.log('Inner-bell logged!') },
},
],
initialExpanded: true,
},
{
stickyTop: true,
value: 103,
label: 'Top Action Option',
icon: 'function',
action: (option) => { alert(`You clicked an option with value: "${option.value}" and label: "${option.label}"`) },
tooltip: { content: '<em>I can be HTML, too!</em>', html: true },
},
{ value: 1, label: 'Option 1', tooltip: 'I can be anywhere!' },
{ value: 2, label: 'Option 2 (with icon)', icon: 'favorite' },
{ value: 3, label: 'Option 3', description: 'Option with description' },
{
value: 4, label: 'Option 4 has a VERY Very very long label', icon: 'data-set', description: 'Option with description & icon, and a very long long long long long long long long long long long description',
},
{
value: 5, label: 'Option 5 (disabled)', disabled: true, tooltip: 'Disabled option only shows tooltip on hovering (not keyboad)!',
},
{
value: 6,
label: 'Option 6',
children: [
{ label: 'Option a.1', value: '6.1', icon: 'type/number' },
{ label: 'Option b.2', value: '6.2', icon: 'type/string' },
{
label: 'Option x.3',
value: '6.3',
children: [
{ label: 'Option d.3.1', value: '6.3.1' },
{ label: 'Option e.3.2', value: '6.3.2' },
{
label: 'Option x.3.3',
value: '6.3.3',
children: [
{ label: 'Option g.3.3.1', value: '6.3.3.1' },
{ label: 'Option h.3.3.2', value: '6.3.3.2' },
{ label: 'Option i.3.3.3', value: '6.3.3.3' },
{
label: 'Option x.3.3.4',
value: '6.3.3.4',
children: [
{ label: 'Option k.3.3.4.1', value: '6.3.3.4.1' },
{ label: 'Option l.3.3.4.2', value: '6.3.3.4.2' },
{ label: 'Option 6.3.3.4.3', value: '6.3.3.4.3' },
{ label: 'Option m.3.3.4.4', value: '6.3.3.4.4' },
],
},
{ label: 'Option j.3.3.3', value: '6.3.3.5' },
],
},
{ label: 'Option f.3.4', value: '6.3.4' },
],
initialExpanded: true,
},
{ label: 'Option c.4', value: '6.4' },
],
},
{ value: 7, label: 'Option 7', icon: 'user' },
{ value: 8, label: 'Option 8' },
{ value: 9, label: 'Option 9' },
{ value: 10, label: 'Option 10' },
{ value: null, label: 'Option null' },
{ stickyBottom: true, value: 901, label: 'Bottom Option 1' },
{
stickyBottom: true,
value: 902,
label: 'Bottom Option 2',
children: [
{ stickyBottom: true, value: '901.1', label: 'Bottom Option 2.1' },
{ stickyBottom: true, value: '901.2', label: 'Bottom Option 2.2' },
],
},
{
stickyBottom: true,
value: 903,
label: 'Async Refresh (done after 2s)',
icon: 'refresh',
action: async () => {
await new Promise((res) => { setTimeout(res, 2000) })
alert('Done refreshing!')
},
},
] as const satisfies SelectOption[]
export const ADVANCED_SEARCH_OPTIONS = [
{
value: 'users',
label: 'Users',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'countries',
label: 'Countries',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'cities',
label: 'Cities',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'beautiful-stories', label: 'Beautiful Stories' },
{ value: 'large-libraries', label: 'Large Libraries' },
],
},
{
value: 'families',
label: 'Families',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'with-stories', label: 'With Stories' },
{ value: 'like-parties', label: 'Like Parties' },
],
},
],
},
],
},
{
value: 'A',
label: 'City Dwellers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'B',
label: 'Urban City',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'C',
label: 'Analyze Data',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'D',
label: 'Analytical Reports',
},
],
},
{
value: 'E',
label: 'Childhood Memories',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'F',
label: "Children's Games",
},
],
},
],
},
{
value: 'G',
label: 'Tooth Brushing',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'H',
label: 'Teeth Cleaning',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'I',
label: 'Mouse Clicks',
},
{
value: 'J',
label: 'Mice Running',
},
],
},
{
value: 'K',
label: 'Foot Prints',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'L',
label: 'Feet Walking',
},
],
},
],
},
],
},
{
value: 'M',
label: 'Person Interview',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'N',
label: 'People Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'O',
label: 'Woman Power',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'P',
label: 'Women Rights',
},
],
},
{
value: 'Q',
label: 'Leaf Fall',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'R',
label: 'Leaves Turning',
},
],
},
],
},
{
value: 'S',
label: 'Knife Sharp',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'T',
label: 'Knives Set',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'U',
label: 'Create Content',
},
{
value: 'V',
label: 'Creative Writing',
},
],
},
{
value: 'W',
label: 'Develop Software',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'X',
label: 'Development Tools',
},
],
},
],
},
],
},
{
value: 'Y',
label: 'Organize Events',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'Z',
label: 'Organizational Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AA',
label: 'Apply Pressure',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AB',
label: 'Application Forms',
},
],
},
{
value: 'AC',
label: 'Multiply Numbers',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AD',
label: 'Multiplication Tables',
},
],
},
],
},
{
value: 'AE',
label: 'Identify Problems',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AF',
label: 'Identification Cards',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AG',
label: 'Rectify Errors',
},
{
value: 'AH',
label: 'Rectification Process',
},
],
},
{
value: 'AI',
label: 'Classify Items',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AJ',
label: 'Classification System',
},
],
},
],
},
],
},
{
value: 'AK',
label: 'Beautiful Flowers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AL',
label: 'Beauty Products',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AM',
label: 'Quick Action',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AN',
label: 'Quickly Done',
},
],
},
{
value: 'AO',
label: 'Heavy Lifting',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AP',
label: 'Heavily Loaded',
},
],
},
],
},
{
value: 'AQ',
label: 'Simple Solution',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AR',
label: 'Simply Put',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AS',
label: 'Possible outcome',
},
{
value: 'AT',
label: 'Possibly true',
},
],
},
{
value: 'AU',
label: 'Electric cars',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AV',
label: 'electricity bills',
},
],
},
],
},
],
},
{
value: 'AW',
label: 'Dramatic plays',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AX',
label: 'dramatically changed',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AY',
label: 'Historic buildings',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AZ',
label: 'historically accurate',
},
],
},
],
},
],
},
] as const satisfies SelectOption[]Multiple Select
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect } from '@holistics/design-system'
import { options } from './options'
const value = ref<typeof options[number]['value']>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
multiple
placeholder="Default..."
class="w-80"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
import { HSelect } from '@holistics/design-system'
import { options } from './options'
const value = ref<typeof options[number]['value']>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
multiple="counter"
placeholder="With Counter..."
class="w-80"
/>
</template>import type { SelectOption } from '@holistics/design-system'
export const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4 (longer)' },
{ value: 5, label: 'Option 5 (even longgggeeerrr)' },
{
value: 6,
label: 'Option 6',
children: [
{ value: '6.1', label: 'Child of Option 6' },
{ value: '6.2', label: 'Another child of Option 6', disabled: true },
{ value: '6.3', label: 'Last child of Option 6' },
],
},
{ value: 7, label: 'Option 7' },
{ value: 8, label: '8' },
{ value: 9, label: 'Option 9', description: 'Some explanation for Option 9...' },
{ value: null, label: 'Option with null value' },
] as const satisfies SelectOption[]Filterable
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { OPTIONS } from '../options'
const value = ref<SelectValue | SelectValue[]>()
</script>
<template>
<div class="flex flex-col items-center gap-x-4 gap-y-2 md:flex-row lg:flex-col 2xl:flex-row">
<HSelect
v-model="value"
:options="OPTIONS"
filterable
placeholder="Select..."
class="w-80"
/>
<HSelect
v-model="value"
:options="OPTIONS"
multiple
filterable
placeholder="Select multiple..."
class="w-80"
/>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { OPTIONS } from '../options'
const value = ref<SelectValue | SelectValue[]>()
</script>
<template>
<div class="flex flex-col items-center gap-x-4 gap-y-2 md:flex-row lg:flex-col 2xl:flex-row">
<HSelect
v-model="value"
:options="OPTIONS"
filterable
filter-include-direct-children
placeholder="Select (include direct children)..."
class="w-80"
/>
<HSelect
v-model="value"
:options="OPTIONS"
multiple
filterable
filter-include-direct-children
placeholder="Select multiple (include direct children)..."
class="w-80"
/>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { OPTIONS } from '../options'
const value = ref<SelectValue | SelectValue[]>()
</script>
<template>
<div class="flex flex-col items-center gap-x-4 gap-y-2 md:flex-row lg:flex-col 2xl:flex-row">
<HSelect
v-model="value"
:options="OPTIONS"
filterable
filter-include-sticky-options
placeholder="Select (include sticky options)..."
class="w-80"
/>
<HSelect
v-model="value"
:options="OPTIONS"
multiple
filterable
filter-include-sticky-options
placeholder="Select multiple (include sticky options)..."
class="w-80"
/>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { ADVANCED_SEARCH_OPTIONS } from '../options'
const value = ref<SelectValue | SelectValue[]>()
</script>
<template>
<div class="flex flex-col items-center gap-x-4 gap-y-2 md:flex-row lg:flex-col 2xl:flex-row">
<HSelect
v-model="value"
:options="ADVANCED_SEARCH_OPTIONS"
filterable
filter-advanced-search
placeholder="Select (advanced search)..."
class="w-80"
/>
<HSelect
v-model="value"
:options="ADVANCED_SEARCH_OPTIONS"
multiple
filterable
filter-advanced-search
placeholder="Select multiple (advanced search)..."
class="w-80"
/>
</div>
</template>import type { SelectOption } from '@holistics/design-system'
export const OPTIONS = [
{
stickyTop: true, value: 101, label: 'Top Option 1', tooltip: 'Lorem ipsum donor',
},
{
stickyTop: true,
value: 102,
label: 'Top Option 2',
tooltip: 'Some tips at a group',
children: [
{ stickyTop: true, value: '102.1', label: 'Top Option 2.1' },
{
stickyTop: true, value: '102.2', label: 'Inner-Bell', icon: 'bell', action: () => { console.log('Inner-bell logged!') },
},
],
initialExpanded: true,
},
{
stickyTop: true,
value: 103,
label: 'Top Action Option',
icon: 'function',
action: (option) => { alert(`You clicked an option with value: "${option.value}" and label: "${option.label}"`) },
tooltip: { content: '<em>I can be HTML, too!</em>', html: true },
},
{ value: 1, label: 'Option 1', tooltip: 'I can be anywhere!' },
{ value: 2, label: 'Option 2 (with icon)', icon: 'favorite' },
{ value: 3, label: 'Option 3', description: 'Option with description' },
{
value: 4, label: 'Option 4 has a VERY Very very long label', icon: 'data-set', description: 'Option with description & icon, and a very long long long long long long long long long long long description',
},
{
value: 5, label: 'Option 5 (disabled)', disabled: true, tooltip: 'Disabled option only shows tooltip on hovering (not keyboad)!',
},
{
value: 6,
label: 'Option 6',
children: [
{ label: 'Option a.1', value: '6.1', icon: 'type/number' },
{ label: 'Option b.2', value: '6.2', icon: 'type/string' },
{
label: 'Option x.3',
value: '6.3',
children: [
{ label: 'Option d.3.1', value: '6.3.1' },
{ label: 'Option e.3.2', value: '6.3.2' },
{
label: 'Option x.3.3',
value: '6.3.3',
children: [
{ label: 'Option g.3.3.1', value: '6.3.3.1' },
{ label: 'Option h.3.3.2', value: '6.3.3.2' },
{ label: 'Option i.3.3.3', value: '6.3.3.3' },
{
label: 'Option x.3.3.4',
value: '6.3.3.4',
children: [
{ label: 'Option k.3.3.4.1', value: '6.3.3.4.1' },
{ label: 'Option l.3.3.4.2', value: '6.3.3.4.2' },
{ label: 'Option 6.3.3.4.3', value: '6.3.3.4.3' },
{ label: 'Option m.3.3.4.4', value: '6.3.3.4.4' },
],
},
{ label: 'Option j.3.3.3', value: '6.3.3.5' },
],
},
{ label: 'Option f.3.4', value: '6.3.4' },
],
initialExpanded: true,
},
{ label: 'Option c.4', value: '6.4' },
],
},
{ value: 7, label: 'Option 7', icon: 'user' },
{ value: 8, label: 'Option 8' },
{ value: 9, label: 'Option 9' },
{ value: 10, label: 'Option 10' },
{ value: null, label: 'Option null' },
{ stickyBottom: true, value: 901, label: 'Bottom Option 1' },
{
stickyBottom: true,
value: 902,
label: 'Bottom Option 2',
children: [
{ stickyBottom: true, value: '901.1', label: 'Bottom Option 2.1' },
{ stickyBottom: true, value: '901.2', label: 'Bottom Option 2.2' },
],
},
{
stickyBottom: true,
value: 903,
label: 'Async Refresh (done after 2s)',
icon: 'refresh',
action: async () => {
await new Promise((res) => { setTimeout(res, 2000) })
alert('Done refreshing!')
},
},
] as const satisfies SelectOption[]
export const ADVANCED_SEARCH_OPTIONS = [
{
value: 'users',
label: 'Users',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'countries',
label: 'Countries',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'cities',
label: 'Cities',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'beautiful-stories', label: 'Beautiful Stories' },
{ value: 'large-libraries', label: 'Large Libraries' },
],
},
{
value: 'families',
label: 'Families',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'with-stories', label: 'With Stories' },
{ value: 'like-parties', label: 'Like Parties' },
],
},
],
},
],
},
{
value: 'A',
label: 'City Dwellers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'B',
label: 'Urban City',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'C',
label: 'Analyze Data',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'D',
label: 'Analytical Reports',
},
],
},
{
value: 'E',
label: 'Childhood Memories',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'F',
label: "Children's Games",
},
],
},
],
},
{
value: 'G',
label: 'Tooth Brushing',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'H',
label: 'Teeth Cleaning',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'I',
label: 'Mouse Clicks',
},
{
value: 'J',
label: 'Mice Running',
},
],
},
{
value: 'K',
label: 'Foot Prints',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'L',
label: 'Feet Walking',
},
],
},
],
},
],
},
{
value: 'M',
label: 'Person Interview',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'N',
label: 'People Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'O',
label: 'Woman Power',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'P',
label: 'Women Rights',
},
],
},
{
value: 'Q',
label: 'Leaf Fall',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'R',
label: 'Leaves Turning',
},
],
},
],
},
{
value: 'S',
label: 'Knife Sharp',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'T',
label: 'Knives Set',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'U',
label: 'Create Content',
},
{
value: 'V',
label: 'Creative Writing',
},
],
},
{
value: 'W',
label: 'Develop Software',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'X',
label: 'Development Tools',
},
],
},
],
},
],
},
{
value: 'Y',
label: 'Organize Events',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'Z',
label: 'Organizational Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AA',
label: 'Apply Pressure',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AB',
label: 'Application Forms',
},
],
},
{
value: 'AC',
label: 'Multiply Numbers',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AD',
label: 'Multiplication Tables',
},
],
},
],
},
{
value: 'AE',
label: 'Identify Problems',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AF',
label: 'Identification Cards',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AG',
label: 'Rectify Errors',
},
{
value: 'AH',
label: 'Rectification Process',
},
],
},
{
value: 'AI',
label: 'Classify Items',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AJ',
label: 'Classification System',
},
],
},
],
},
],
},
{
value: 'AK',
label: 'Beautiful Flowers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AL',
label: 'Beauty Products',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AM',
label: 'Quick Action',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AN',
label: 'Quickly Done',
},
],
},
{
value: 'AO',
label: 'Heavy Lifting',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AP',
label: 'Heavily Loaded',
},
],
},
],
},
{
value: 'AQ',
label: 'Simple Solution',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AR',
label: 'Simply Put',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AS',
label: 'Possible outcome',
},
{
value: 'AT',
label: 'Possibly true',
},
],
},
{
value: 'AU',
label: 'Electric cars',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AV',
label: 'electricity bills',
},
],
},
],
},
],
},
{
value: 'AW',
label: 'Dramatic plays',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AX',
label: 'dramatically changed',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AY',
label: 'Historic buildings',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AZ',
label: 'historically accurate',
},
],
},
],
},
],
},
] as const satisfies SelectOption[]Filterable (async)
You can listen on the search text and decide what options are rendered accordingly with the @search event.
WARNING
When using @search, the default searching behavior of the filterable* and creatable props won't be applied anymore! This means you need to specify the searching mechanism on your own!
<script setup lang="ts">
import { type Ref, ref } from 'vue'
import { HSelect, type SelectValue, type SelectOption } from '@holistics/design-system'
import { OPTIONS as baseOptions } from './options'
const options = ref(baseOptions) as Ref<SelectOption[]>
const value = ref<SelectValue | SelectValue[]>()
async function onSearch (text: string) {
if (!text) {
options.value = baseOptions
return
}
await new Promise((res) => { setTimeout(res, 3000) })
options.value = baseOptions.filter((o) => o.label.includes(text)) // [!code warning] // 🗃️️ Should be a list returned from the server
}
</script>
<template>
<div class="space-y-4">
<HSelect
v-model="value"
:options="options"
filterable
class="w-80"
@search="onSearch"
/>
<HSelect
v-model="value"
:options="options"
mutliple
filterable
placeholder="Select multiple..."
class="w-80"
@search="onSearch"
/>
</div>
</template>import type { SelectOption } from '@holistics/design-system'
export const OPTIONS = [
{
stickyTop: true, value: 101, label: 'Top Option 1', tooltip: 'Lorem ipsum donor',
},
{
stickyTop: true,
value: 102,
label: 'Top Option 2',
tooltip: 'Some tips at a group',
children: [
{ stickyTop: true, value: '102.1', label: 'Top Option 2.1' },
{
stickyTop: true, value: '102.2', label: 'Inner-Bell', icon: 'bell', action: () => { console.log('Inner-bell logged!') },
},
],
initialExpanded: true,
},
{
stickyTop: true,
value: 103,
label: 'Top Action Option',
icon: 'function',
action: (option) => { alert(`You clicked an option with value: "${option.value}" and label: "${option.label}"`) },
tooltip: { content: '<em>I can be HTML, too!</em>', html: true },
},
{ value: 1, label: 'Option 1', tooltip: 'I can be anywhere!' },
{ value: 2, label: 'Option 2 (with icon)', icon: 'favorite' },
{ value: 3, label: 'Option 3', description: 'Option with description' },
{
value: 4, label: 'Option 4 has a VERY Very very long label', icon: 'data-set', description: 'Option with description & icon, and a very long long long long long long long long long long long description',
},
{
value: 5, label: 'Option 5 (disabled)', disabled: true, tooltip: 'Disabled option only shows tooltip on hovering (not keyboad)!',
},
{
value: 6,
label: 'Option 6',
children: [
{ label: 'Option a.1', value: '6.1', icon: 'type/number' },
{ label: 'Option b.2', value: '6.2', icon: 'type/string' },
{
label: 'Option x.3',
value: '6.3',
children: [
{ label: 'Option d.3.1', value: '6.3.1' },
{ label: 'Option e.3.2', value: '6.3.2' },
{
label: 'Option x.3.3',
value: '6.3.3',
children: [
{ label: 'Option g.3.3.1', value: '6.3.3.1' },
{ label: 'Option h.3.3.2', value: '6.3.3.2' },
{ label: 'Option i.3.3.3', value: '6.3.3.3' },
{
label: 'Option x.3.3.4',
value: '6.3.3.4',
children: [
{ label: 'Option k.3.3.4.1', value: '6.3.3.4.1' },
{ label: 'Option l.3.3.4.2', value: '6.3.3.4.2' },
{ label: 'Option 6.3.3.4.3', value: '6.3.3.4.3' },
{ label: 'Option m.3.3.4.4', value: '6.3.3.4.4' },
],
},
{ label: 'Option j.3.3.3', value: '6.3.3.5' },
],
},
{ label: 'Option f.3.4', value: '6.3.4' },
],
initialExpanded: true,
},
{ label: 'Option c.4', value: '6.4' },
],
},
{ value: 7, label: 'Option 7', icon: 'user' },
{ value: 8, label: 'Option 8' },
{ value: 9, label: 'Option 9' },
{ value: 10, label: 'Option 10' },
{ value: null, label: 'Option null' },
{ stickyBottom: true, value: 901, label: 'Bottom Option 1' },
{
stickyBottom: true,
value: 902,
label: 'Bottom Option 2',
children: [
{ stickyBottom: true, value: '901.1', label: 'Bottom Option 2.1' },
{ stickyBottom: true, value: '901.2', label: 'Bottom Option 2.2' },
],
},
{
stickyBottom: true,
value: 903,
label: 'Async Refresh (done after 2s)',
icon: 'refresh',
action: async () => {
await new Promise((res) => { setTimeout(res, 2000) })
alert('Done refreshing!')
},
},
] as const satisfies SelectOption[]
export const ADVANCED_SEARCH_OPTIONS = [
{
value: 'users',
label: 'Users',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'countries',
label: 'Countries',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'cities',
label: 'Cities',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'beautiful-stories', label: 'Beautiful Stories' },
{ value: 'large-libraries', label: 'Large Libraries' },
],
},
{
value: 'families',
label: 'Families',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'with-stories', label: 'With Stories' },
{ value: 'like-parties', label: 'Like Parties' },
],
},
],
},
],
},
{
value: 'A',
label: 'City Dwellers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'B',
label: 'Urban City',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'C',
label: 'Analyze Data',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'D',
label: 'Analytical Reports',
},
],
},
{
value: 'E',
label: 'Childhood Memories',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'F',
label: "Children's Games",
},
],
},
],
},
{
value: 'G',
label: 'Tooth Brushing',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'H',
label: 'Teeth Cleaning',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'I',
label: 'Mouse Clicks',
},
{
value: 'J',
label: 'Mice Running',
},
],
},
{
value: 'K',
label: 'Foot Prints',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'L',
label: 'Feet Walking',
},
],
},
],
},
],
},
{
value: 'M',
label: 'Person Interview',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'N',
label: 'People Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'O',
label: 'Woman Power',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'P',
label: 'Women Rights',
},
],
},
{
value: 'Q',
label: 'Leaf Fall',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'R',
label: 'Leaves Turning',
},
],
},
],
},
{
value: 'S',
label: 'Knife Sharp',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'T',
label: 'Knives Set',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'U',
label: 'Create Content',
},
{
value: 'V',
label: 'Creative Writing',
},
],
},
{
value: 'W',
label: 'Develop Software',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'X',
label: 'Development Tools',
},
],
},
],
},
],
},
{
value: 'Y',
label: 'Organize Events',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'Z',
label: 'Organizational Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AA',
label: 'Apply Pressure',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AB',
label: 'Application Forms',
},
],
},
{
value: 'AC',
label: 'Multiply Numbers',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AD',
label: 'Multiplication Tables',
},
],
},
],
},
{
value: 'AE',
label: 'Identify Problems',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AF',
label: 'Identification Cards',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AG',
label: 'Rectify Errors',
},
{
value: 'AH',
label: 'Rectification Process',
},
],
},
{
value: 'AI',
label: 'Classify Items',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AJ',
label: 'Classification System',
},
],
},
],
},
],
},
{
value: 'AK',
label: 'Beautiful Flowers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AL',
label: 'Beauty Products',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AM',
label: 'Quick Action',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AN',
label: 'Quickly Done',
},
],
},
{
value: 'AO',
label: 'Heavy Lifting',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AP',
label: 'Heavily Loaded',
},
],
},
],
},
{
value: 'AQ',
label: 'Simple Solution',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AR',
label: 'Simply Put',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AS',
label: 'Possible outcome',
},
{
value: 'AT',
label: 'Possibly true',
},
],
},
{
value: 'AU',
label: 'Electric cars',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AV',
label: 'electricity bills',
},
],
},
],
},
],
},
{
value: 'AW',
label: 'Dramatic plays',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AX',
label: 'dramatically changed',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AY',
label: 'Historic buildings',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AZ',
label: 'historically accurate',
},
],
},
],
},
],
},
] as const satisfies SelectOption[]Create Options Dynamically
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption } from '@holistics/design-system'
const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
] as const satisfies SelectOption[]
const value = ref<typeof options[number]['value']>()
</script>
<template>
<div class="space-y-4">
<HSelect
v-model="value"
:options="options"
filterable
createable
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
filterable
createable
placeholder="Select multiple..."
class="w-80"
/>
</div>
</template>Clearable
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption } from '@holistics/design-system'
const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
] as const satisfies SelectOption[]
const value = ref<typeof options[number]['value']>()
</script>
<template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-1 2xl:grid-cols-2">
<HSelect
v-model="value"
:options="options"
clearable
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
filterable
clearable
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
clearable
placeholder="Select multiple..."
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
filterable
clearable
placeholder="Select multiple..."
class="w-80"
/>
</div>
</template>Inlined
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { OPTIONS } from './options'
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="OPTIONS"
inline
filterable
class="w-80"
/>
</template>import type { SelectOption } from '@holistics/design-system'
export const OPTIONS = [
{
stickyTop: true, value: 101, label: 'Top Option 1', tooltip: 'Lorem ipsum donor',
},
{
stickyTop: true,
value: 102,
label: 'Top Option 2',
tooltip: 'Some tips at a group',
children: [
{ stickyTop: true, value: '102.1', label: 'Top Option 2.1' },
{
stickyTop: true, value: '102.2', label: 'Inner-Bell', icon: 'bell', action: () => { console.log('Inner-bell logged!') },
},
],
initialExpanded: true,
},
{
stickyTop: true,
value: 103,
label: 'Top Action Option',
icon: 'function',
action: (option) => { alert(`You clicked an option with value: "${option.value}" and label: "${option.label}"`) },
tooltip: { content: '<em>I can be HTML, too!</em>', html: true },
},
{ value: 1, label: 'Option 1', tooltip: 'I can be anywhere!' },
{ value: 2, label: 'Option 2 (with icon)', icon: 'favorite' },
{ value: 3, label: 'Option 3', description: 'Option with description' },
{
value: 4, label: 'Option 4 has a VERY Very very long label', icon: 'data-set', description: 'Option with description & icon, and a very long long long long long long long long long long long description',
},
{
value: 5, label: 'Option 5 (disabled)', disabled: true, tooltip: 'Disabled option only shows tooltip on hovering (not keyboad)!',
},
{
value: 6,
label: 'Option 6',
children: [
{ label: 'Option a.1', value: '6.1', icon: 'type/number' },
{ label: 'Option b.2', value: '6.2', icon: 'type/string' },
{
label: 'Option x.3',
value: '6.3',
children: [
{ label: 'Option d.3.1', value: '6.3.1' },
{ label: 'Option e.3.2', value: '6.3.2' },
{
label: 'Option x.3.3',
value: '6.3.3',
children: [
{ label: 'Option g.3.3.1', value: '6.3.3.1' },
{ label: 'Option h.3.3.2', value: '6.3.3.2' },
{ label: 'Option i.3.3.3', value: '6.3.3.3' },
{
label: 'Option x.3.3.4',
value: '6.3.3.4',
children: [
{ label: 'Option k.3.3.4.1', value: '6.3.3.4.1' },
{ label: 'Option l.3.3.4.2', value: '6.3.3.4.2' },
{ label: 'Option 6.3.3.4.3', value: '6.3.3.4.3' },
{ label: 'Option m.3.3.4.4', value: '6.3.3.4.4' },
],
},
{ label: 'Option j.3.3.3', value: '6.3.3.5' },
],
},
{ label: 'Option f.3.4', value: '6.3.4' },
],
initialExpanded: true,
},
{ label: 'Option c.4', value: '6.4' },
],
},
{ value: 7, label: 'Option 7', icon: 'user' },
{ value: 8, label: 'Option 8' },
{ value: 9, label: 'Option 9' },
{ value: 10, label: 'Option 10' },
{ value: null, label: 'Option null' },
{ stickyBottom: true, value: 901, label: 'Bottom Option 1' },
{
stickyBottom: true,
value: 902,
label: 'Bottom Option 2',
children: [
{ stickyBottom: true, value: '901.1', label: 'Bottom Option 2.1' },
{ stickyBottom: true, value: '901.2', label: 'Bottom Option 2.2' },
],
},
{
stickyBottom: true,
value: 903,
label: 'Async Refresh (done after 2s)',
icon: 'refresh',
action: async () => {
await new Promise((res) => { setTimeout(res, 2000) })
alert('Done refreshing!')
},
},
] as const satisfies SelectOption[]
export const ADVANCED_SEARCH_OPTIONS = [
{
value: 'users',
label: 'Users',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'countries',
label: 'Countries',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'cities',
label: 'Cities',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'beautiful-stories', label: 'Beautiful Stories' },
{ value: 'large-libraries', label: 'Large Libraries' },
],
},
{
value: 'families',
label: 'Families',
initialExpanded: true,
icon: 'data-model',
children: [
{ value: 'with-stories', label: 'With Stories' },
{ value: 'like-parties', label: 'Like Parties' },
],
},
],
},
],
},
{
value: 'A',
label: 'City Dwellers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'B',
label: 'Urban City',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'C',
label: 'Analyze Data',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'D',
label: 'Analytical Reports',
},
],
},
{
value: 'E',
label: 'Childhood Memories',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'F',
label: "Children's Games",
},
],
},
],
},
{
value: 'G',
label: 'Tooth Brushing',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'H',
label: 'Teeth Cleaning',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'I',
label: 'Mouse Clicks',
},
{
value: 'J',
label: 'Mice Running',
},
],
},
{
value: 'K',
label: 'Foot Prints',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'L',
label: 'Feet Walking',
},
],
},
],
},
],
},
{
value: 'M',
label: 'Person Interview',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'N',
label: 'People Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'O',
label: 'Woman Power',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'P',
label: 'Women Rights',
},
],
},
{
value: 'Q',
label: 'Leaf Fall',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'R',
label: 'Leaves Turning',
},
],
},
],
},
{
value: 'S',
label: 'Knife Sharp',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'T',
label: 'Knives Set',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'U',
label: 'Create Content',
},
{
value: 'V',
label: 'Creative Writing',
},
],
},
{
value: 'W',
label: 'Develop Software',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'X',
label: 'Development Tools',
},
],
},
],
},
],
},
{
value: 'Y',
label: 'Organize Events',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'Z',
label: 'Organizational Skills',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AA',
label: 'Apply Pressure',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AB',
label: 'Application Forms',
},
],
},
{
value: 'AC',
label: 'Multiply Numbers',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AD',
label: 'Multiplication Tables',
},
],
},
],
},
{
value: 'AE',
label: 'Identify Problems',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AF',
label: 'Identification Cards',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AG',
label: 'Rectify Errors',
},
{
value: 'AH',
label: 'Rectification Process',
},
],
},
{
value: 'AI',
label: 'Classify Items',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AJ',
label: 'Classification System',
},
],
},
],
},
],
},
{
value: 'AK',
label: 'Beautiful Flowers',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AL',
label: 'Beauty Products',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AM',
label: 'Quick Action',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AN',
label: 'Quickly Done',
},
],
},
{
value: 'AO',
label: 'Heavy Lifting',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AP',
label: 'Heavily Loaded',
},
],
},
],
},
{
value: 'AQ',
label: 'Simple Solution',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AR',
label: 'Simply Put',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AS',
label: 'Possible outcome',
},
{
value: 'AT',
label: 'Possibly true',
},
],
},
{
value: 'AU',
label: 'Electric cars',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AV',
label: 'electricity bills',
},
],
},
],
},
],
},
{
value: 'AW',
label: 'Dramatic plays',
initialExpanded: true,
icon: 'canvas',
children: [
{
value: 'AX',
label: 'dramatically changed',
initialExpanded: true,
icon: 'data-set',
children: [
{
value: 'AY',
label: 'Historic buildings',
initialExpanded: true,
icon: 'data-model',
children: [
{
value: 'AZ',
label: 'historically accurate',
},
],
},
],
},
],
},
] as const satisfies SelectOption[]With Refresh Button
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption } from '@holistics/design-system'
const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
] as const satisfies SelectOption[]
const value = ref<typeof options[number]['value']>()
async function onRefresh () {
await new Promise((res) => { setTimeout(res, 2000) })
}
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
@refresh="onRefresh"
/>
</template>Infinite Scroll
<script setup lang="ts">
import { type Ref, ref } from 'vue'
import { HSelect, type SelectOption, type SelectValue } from '@holistics/design-system'
const options = ref([
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
{ value: 5, label: 'Option 5' },
{ value: 6, label: 'Option 6' },
{ value: 7, label: 'Option 7' },
{ value: 8, label: 'Option 8' },
]) as Ref<SelectOption[]>
const value = ref<SelectValue>()
const page = ref(0)
async function onScrollBottom () {
await new Promise((res) => { setTimeout(res, 3000) })
const temp = options.value.slice()
// [!code warning] // 🗃️ Should be a list returned from the server
temp.push(...Array.from({ length: 5 }, (_, i) => {
const num = page.value * 5 + (i + 1)
return {
value: `appended-${num}`,
label: `Appended Option ${num}`,
}
}))
options.value = temp
page.value += 1
}
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
preserve-focus
@scroll-bottom="onScrollBottom"
/>
</template>Themes
<script setup lang="ts">
import { type Ref, ref } from 'vue'
import { HSelect, type SelectOption, type SelectValue } from '@holistics/design-system'
const options = ref([
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3', disabled: true },
{ value: 4, label: 'Option 4' },
]) as Ref<SelectOption[]>
const value = ref<SelectValue>()
</script>
<template>
<div class="flex flex-col items-center gap-4 md:flex-row lg:flex-col 2xl:flex-row">
<HSelect
v-model="value"
:options="options"
placeholder="Default..."
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
placeholder="Underline..."
theme="underline"
class="w-80"
/>
</div>
</template>Custom CSS & Styles for Options
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption, type SelectValue } from '@holistics/design-system'
const options = [
{
stickyTop: true, value: '1', label: 'A big padding option!', style: { padding: '1rem 1.5rem' },
},
{
value: '2',
label: 'Open me',
children: [
{ value: '2-1', label: 'A little bit italic', class: 'italic' },
{ value: '2-2', label: 'And super large text!', style: { 'font-size': '2rem', 'line-height': '1.5' } },
],
},
] as const satisfies SelectOption[]
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
/>
</template>Customize Options individually
<script setup lang="ts">
import { ref } from 'vue'
import {
HSelect, HTextHighlight, type SelectOption, type SelectValue,
} from '@holistics/design-system'
const options = [
{ value: 'default-option', label: 'Default Option' },
{
value: 'mimic-default-option',
label: 'Mimic Default Option',
slot: 'mimic-default-option',
tooltip: 'This slotted option is trying to mimic all styles and behaviors of default Option!',
},
{
stickyBottom: true,
value: 'footer',
label: 'Footer',
slot: 'footer',
disabled: true,
},
] as const satisfies SelectOption[]
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
>
<template #mimic-default-option="{ option, focused, selected, searchText, onClick, onMouseover }">
<div
class="hui-select-option"
:class="[
{ focused, selected },
!selected && 'text-green-600',
]"
:data-value="option.value"
@click="onClick"
@mouseover="onMouseover"
>
<HTextHighlight
:text="option.label"
:highlight="searchText"
/>
</div>
</template>
<template #footer>
<div class="p-2">
A footer that has a <a
href="https://holistics.io"
target="_blank"
class="text-blue-600 underline"
>link</a>!
</div>
</template>
</HSelect>
</template>Submenu 🔮
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectValue } from '@holistics/design-system'
import { options } from './optionsSubmenu'
const value = ref<SelectValue>()
</script>
<template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-1 2xl:grid-cols-2">
<HSelect
v-model="value"
:options="options"
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
placeholder="Select multiple..."
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
filterable
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
filterable
placeholder="Select multiple..."
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
filterable
filter-include-direct-children
placeholder="Select (include direct children)..."
class="w-80"
/>
<HSelect
v-model="value"
:options="options"
multiple
filterable
filter-include-direct-children
placeholder="Select multiple (include direct children)..."
class="w-80"
/>
</div>
</template>import type { SelectOption } from '@holistics/design-system'
export const options = [
{ value: 1, label: 'Option 1' },
{ value: 2, label: 'Option 2' },
{
value: 'more-options',
label: 'More Options',
childrenAsSubmenu: true,
children: [
{ value: '3.1', label: 'Option 3.1' },
{ value: '3.2', label: 'Option 3.2' },
{
value: '3.3-group',
label: 'Option 3.3 (Group)',
children: [
{ value: '3.x.1', label: 'Option 3.x.1' },
{ value: '3.x.2', label: 'Option 3.x.2' },
{ value: '3.x.3', label: 'Option 3.x.3' },
{
value: '3.3.4-more-options',
label: 'More Options',
childrenAsSubmenu: true,
children: [
{ value: '3.3.4.1', label: 'Option 3.3.4.1' },
{
value: '3.3.4.2-more-options',
label: 'More Options',
childrenAsSubmenu: true,
children: [
{ value: '3.3.4.2.1', label: 'Option 3.3.4.2.1' },
{ value: '3.3.4.2.2', label: 'Option 3.3.4.2.2' },
],
},
{
value: '3.3.4.3',
label: 'Option 3.3.4.3 (Group)',
children: [
{ value: '3.3.4.x.1', label: 'Option 3.3.4.x.1' },
{ value: '3.3.4.x.2', label: 'Option 3.3.4.x.2' },
{ value: '3.3.4.x.3', label: 'Option 3.3.4.x.3' },
{ value: '3.3.4.x.4', label: 'Option 3.3.4.x.4' },
],
},
],
},
{
value: '3.3.5',
label: 'Option 3.3.5 (Group)',
children: [
{ value: '3.3.x.1', label: 'Option 3.3.x.1' },
{ value: '3.3.x.2', label: 'Option 3.3.x.2' },
{
value: '3.3.5.3 (Group)',
label: 'Option 3.3.5.3',
children: [
{ value: '3.3.5.x.1', label: 'Option 3.3.5.x.1' },
{ value: '3.3.5.x.2', label: 'Option 3.3.5.x.2' },
{ value: '3.3.5.x.3', label: 'Option 3.3.5.x.3' },
],
},
],
},
],
},
],
},
{ value: 4, label: 'Option 4' },
] as const satisfies SelectOption[]⚠️ Unsupported features for individual submenu
- Create options dynamically: cannot create options in the currently focused opening submenu (i.e. all creating options are placed at root menu)
- Persist sticky options when filtering: unlike in root menu, sticky options within submenu will be filtered out if not matched
Virtual Scroll
Virtual Scroll is enabled by default and cannot be opt-outed.
<script setup lang="ts">
import { ref } from 'vue'
import { HSelect, type SelectOption, type SelectValue } from '@holistics/design-system'
const options = Array.from(
{ length: 1000 },
(_, i) => ({ value: `opt-${i + 1}`, label: `Option ${i + 1}` }),
) satisfies SelectOption[]
const value = ref<SelectValue>()
</script>
<template>
<HSelect
v-model="value"
:options="options"
class="w-80"
/>
</template>API
Pass-through: <HPopper>
What does this mean?
All props, events, and attrs that are not specified in the tables below will be passed to the element/component described above.
Props
| Name | Type | Description |
|---|---|---|
options * | readonly SelectOption[] | An array of select options. Option properties:
|
modelValue | SelectValue | SelectValue[] | Primitive value:
or Array of primitive values if |
icon | "function" | "cancel" | "copy" | "cut" | "error" | "pause" | "play" | "add-block" | "add-circle" | "add-filter" | "add-tag" | "add-user" | "add" | "address-card" | "adhoc-query" | ... 466 more ... | Icon to display on the left side of the select. |
iconSpin | boolean | Whether the icon should spin. |
placeholder | string= "Select..." | Placeholder text. |
optionIndent | number= 20 | [Tree] The distance (in |
disabled | boolean | |
clearable | boolean | If true, select can be cleared. |
multiple | SelectMultiple | If true, multiple values can be selected. |
indeterminateKeys | Key[] | |
checkable | boolean | Whether to display the selection checkbox. When NOTE: Due to performance reasons, |
checkCascade | boolean | Whether to cascade checkboxes. |
checkStrategy | CheckStrategy= "all" | |
showCheckmark | boolean | Whether to show a check mark at the selected options. |
filterable | boolean | If true, options are filterable/searchable. |
filterAdvancedSearch | boolean | When |
filterIncludeDirectChildren | boolean | Whether to also include all direct (first-level) children of matched groups when filtering/searching. |
filterIncludeStickyOptions | boolean | Whether to include Sticky Options when filtering/searching:
|
searchText | string | Search text used for filtering option(s). |
searchDebounce | number= 200 | Debounce time for searching (ms). |
searchLoading | boolean | Whether the |
inputId | string | The id of the input element. |
preserveSearchText | boolean | Whether to preseve search text when blurring, after selecting option, or when pressing |
createable | boolean | If true, new options can be created. |
createFn | CreateOptFn | An array of select options. Interface: |
createAtTop | boolean | When |
refreshFn | RefreshFn | Function to refresh the options. Interface: |
refreshLoading | boolean | Whether the |
matchAnchorSize | boolean | "min" | "max"= true | Set the size of the options menu depending on the size of the select trigger element.
|
theme | "normal" | "underline"= "normal" | The theme of the select component. |
inline | boolean | If true, display the options menu as inlined instead of floating. |
groupSelectable | boolean | Whether to allow selecting group option (best when using with |
floatingClass | HTMLAttributeClass | Custom class for the options menu. |
maxHeight | string= "15.5rem" | Max height of the filterable options for scrolling. |
scrollBottomLoading | boolean | Whether the |
preserveFocus | boolean | Whether to preserve current focused element while the menu is opening and |
Events
| Name | Parameters | Description |
|---|---|---|
@blur | [event: FocusEvent] | |
@focus | [event: FocusEvent] | |
@select | [value: SelectValue, option: SelectOption] | |
@update:modelValue | [value: SelectValue | SelectValue[] | undefined] | |
@update:indeterminateKeys | [keys: Key[]] | |
@update:searchText | [value: string] | |
@update:searchLoading | [loading: boolean] | |
@update:refreshLoading | [loading: boolean] | |
@update:scrollBottomLoading | [loading: boolean] | |
@deselect | [value: SelectValue, option: SelectOption] | |
@focusOption | [option?: SelectOption | undefined] | |
@search | SearchFn | Function triggered when the search text is changed. If this is specified, searching from Interface: |
@refresh | DefinePropEmit | Function to refresh the options. Interface: |
@scrollBottom | DefinePropEmit<[menuHolder?: SelectOption]> | Fired when scrolling to bottom of a menu. Interface: |
Slots
| Name | Scoped | Description |
|---|---|---|
#placeholder | { placeholder: string; } | |
#trigger-selected-options | { selectedOptions: SelectOption[]; disabled?: boolean | undefined; multiple?: SelectMultiple | undefined; searchText: string; searchLoading?: boolean | undefined; ... 4 more ...; deselect: (option: SelectOption) => void; } | |
#option-prepend | { option: SelectOption; } | |
#option | SelectOptionSlotProps | |
#options-empty | any | |
#options-filter-empty | any |
Exposed
| Name | Type | Description |
|---|---|---|
focusAndOpen | () => void | |
close | (keepFocus?: boolean | undefined) => void | |
updatePositions | () => void | |
handleCheck | (option: SelectOption, assumeChecked?: boolean | undefined) => void | Check/uncheck a single option based on its current checked state (can be overridden by |
setCheckedStates | (checkedKeys: Key[] | null | undefined) => void | Calculate, then set correct checked and indeterminate keys based on a list of checked keys. |