Select

The select component provides a way to select one or many options from a list of options.

WAI-ARIA Select Pattern

Example

You have selected the value: nothing

Sources

+page.svelte

<script lang="ts">
  import Select from './Select.svelte'
  import Option from './Option.svelte'

  let value: string = ''
</script>

<p class="mb-4 text-sm opacity-60">
  You have selected the value: <strong>{value || 'nothing'}</strong>
</p>

<Select bind:value placeholder="Select a transportation method">
  <Option value="πŸš—">Car</Option>
  <Option value="🚲">Bike</Option>
  <Option value="🚌">Bus</Option>
  <Option value="✈️">Plane</Option>
  <Option value="πŸš‚">Train</Option>
  <Option value="πŸšΆβ€β™‚οΈ">Walk</Option>
</Select>

Option.svelte

<script lang="ts">
  import type { Select } from 'louisette'
  import { getContext } from 'svelte'

  export let value: string
  export let selected: boolean = false
  export let disabled: boolean = false

  const {
    optionAttrs,
    select,
    selected: selectedList,
    activeDescendant,
  } = getContext<Select>('select')

  if (selected) select(value)
</script>

<div
  {...$optionAttrs(value)}
  class="flex cursor-pointer items-center justify-between rounded-md p-2 hover:bg-neutral-200 dark:hover:bg-neutral-600"
  class:is-active-descendant={$activeDescendant === value}
>
  <span>
    <slot />
  </span>
  {#if $selectedList.includes(value)}
    <svg
      class="text-primary-500 dark:text-primary-400 ml-2 h-4 w-4"
      fill="currentColor"
      viewBox="0 0 20 20"
      aria-hidden="true"
    >
      <path
        clip-rule="evenodd"
        d="M10.707 14.707a1 1 0 01-1.414 0L5 10.414A1 1 0 016.414 9l3.293 3.293 6.293-6.293a1 1 0 111.414 1.414l-7 7z"
        fill-rule="evenodd"
      />
    </svg>
  {/if}
</div>

<style lang="postcss">
  .is-active-descendant {
    @apply bg-neutral-200 dark:bg-neutral-600;
  }
</style>

Select.svelte

<script lang="ts">
  import { createSelect } from 'louisette'
  import { setContext } from 'svelte'

  export let multiple: boolean = false
  export let value: string | string[] = ''
  export let placeholder: string = 'Select an option'

  // TODO: implement disabled
  export let disabled: boolean = false

  const selectContext = createSelect()
  setContext('select', selectContext)

  const {
    opened,
    selectAttrs,
    buttonAttrs,
    listboxAttrs,
    selectedLabel,
    selected: selectedList,
  } = selectContext

  $: value = multiple ? $selectedList : $selectedList[0]
</script>

<div {...$selectAttrs} class="relative min-w-[1rem]">
  <button
    {...$buttonAttrs}
    class="flex w-full items-center justify-between gap-4 rounded bg-white px-4 py-2 shadow dark:bg-neutral-700 dark:text-neutral-100"
  >
    <span class:opacity-50={!$selectedLabel}>
      {$selectedLabel || placeholder}
    </span>
    <span class="ml-auto" class:rotate-180={$opened}>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        stroke-width="1.5"
        stroke="currentColor"
        class="h-4 w-4"
      >
        <path
          stroke-linecap="round"
          stroke-linejoin="round"
          d="M19.5 8.25l-7.5 7.5-7.5-7.5"
        />
      </svg>
    </span>
  </button>

  <div
    {...$listboxAttrs}
    class:hidden={!$opened}
    class="absolute z-10 mt-1 w-full space-y-2 rounded-b bg-white p-2 text-sm shadow-lg dark:bg-neutral-700 dark:text-neutral-100"
  >
    <slot />
  </div>
</div>