« home

svelte-multiselect Svelte MultiSelect

Attachments

Exported from svelte-multiselect/attachments:

tooltip

  <script>
  import { Icon, tooltip } from '$lib'

  let custom_delay = $state(0)
</script>

<div style="display: flex; gap: 3em">
  <button
    aria-label="More info"
    style="padding: 0.4em 0.8em"
    {@attach tooltip({
      content: `<strong>Custom</strong> <em>HTML</em> tooltip`,
      placement: `right`,
      delay: custom_delay,
    })}
  >
    Hover me
  </button>

  <label style="display: inline-flex; gap: 0.5em; align-items: center">
    Delay (ms)
    <input type="number" min="0" step="50" bind:value={custom_delay} style="width: 6em" />
  </label>
</div>

<!-- Placement showcase -->
<div style="display: flex; gap: 1em; margin: 2em 0">
  <button {@attach tooltip({ content: `Top`, placement: `top` })}>
    Top
  </button>
  <button {@attach tooltip({ content: `Right`, placement: `right` })}>
    Right
  </button>
  <button {@attach tooltip({ content: `Bottom (default)`, placement: `bottom` })}>
    Bottom
  </button>
  <button {@attach tooltip({ content: `Left`, placement: `left` })}>
    Left
  </button>
</div>

<!-- Style variations via CSS variables to demonstrate customization -->
<div
  style="display: flex; gap: 1em; margin: 2em 0"
>
  <button
    style="--tooltip-bg: white; --text-color: #111; --tooltip-border: 1px solid rgba(0, 0, 0, 0.18); --tooltip-font-size: 12px; --tooltip-arrow-size: 5; --tooltip-opacity: 0.95"
    {@attach tooltip({ content: `Light tooltip`, placement: `top` })}
  >
    Light style
  </button>
  <button
    style="--tooltip-bg: #0f2a43; --text-color: #d7ecff; --tooltip-border: 1px solid rgba(0, 128, 255, 0.4); --tooltip-shadow: drop-shadow(0 4px 12px rgba(0, 128, 255, 0.25)); --tooltip-font-size: 14px; --tooltip-arrow-size: 8; --tooltip-opacity: 1"
    {@attach tooltip({ content: `Info tooltip`, placement: `right` })}
  >
    Info style
  </button>
  <button
    style="--tooltip-bg: rgba(255, 50, 50, 0.9); --text-color: white; --tooltip-border: 1px solid rgba(255, 50, 50, 0.9); --tooltip-radius: 3px; --tooltip-font-size: 12px; --tooltip-arrow-size: 10; --tooltip-opacity: 0.9"
    {@attach tooltip({ content: `Warning tooltip`, placement: `bottom` })}
  >
    Warning style
  </button>
  <button
    style="--tooltip-bg: white; --text-color: #111; --tooltip-border: 1px solid rgba(255, 255, 255, 0.15); --tooltip-font-size: 16px; --tooltip-arrow-size: 12; --tooltip-padding: 10px 12px"
    {@attach tooltip({ content: `Large text + big arrow`, placement: `left` })}
  >
    Large text
  </button>
</div>
<!-- Attach once to a container: children with title/aria-label/data-title get their own tooltip -->
<div style="display: flex; gap: 1em; margin: 2em 0" {@attach tooltip()}>
  <button title="Added via title attribute">Title-based</button>
  <button aria-label="Added via aria-label">aria-label</button>
  <button data-title="Added via data-title">data-title</button>
</div>

draggable

Drag me this text is also draggable
Drag with custom callbacks
this text is not draggable

last pointer: —

  <script>
  import { draggable } from '$lib'

  let last_drag = $state('')
</script>

<div class="drag-area">
  <!-- Absolute positioned box → default handle is the node itself -->
  <div
    class="drag-box"
    style="position: absolute; left: 1rem; top: 1rem"
    {@attach draggable({
      on_drag: (event) => last_drag = `${event.clientX}, ${event.clientY}`,
    })}
  >
    Drag me
    <small style="display: block; opacity: 0.7">this text is also draggable</small>
  </div>

  <!-- Second draggable with custom handle and callbacks -->
  <div
    class="drag-box"
    style="position: absolute; left: 12rem; top: 8rem; width: 14rem"
    {@attach draggable({
      handle_selector: `.drag-handle`,
      on_drag_start: () => last_drag = `start`,
      on_drag: (event) => last_drag = `${event.clientX}, ${event.clientY}`,
      on_drag_end: () => last_drag = `end`,
    })}
  >
    <div class="drag-handle">Drag with custom callbacks</div>
    <small style="display: block; opacity: 0.7">this text is not draggable</small>
  </div>
</div>

<p>last pointer: {last_drag || '—'}</p>

highlight_matches

This paragraph will highlight occurrences of the query across element boundaries. Try words like ancient, giant, or split- word matches.

This line is excluded via node_filter.

  <script>
  import { highlight_matches } from '$lib'

  let search_text = $state('')
  let disabled = $state(false)

  // Only highlight inside .target; skip any node inside .no-hl
  const node_filter = (node) =>
    node.parentElement?.closest('.no-hl')
      ? NodeFilter.FILTER_REJECT
      : NodeFilter.FILTER_ACCEPT
</script>

<label style="display: inline-flex; gap: 0.6em; align-items: center">
  Search
  <input
    placeholder="type to highlight..."
    bind:value={search_text}
    style="min-width: 16ch"
  />
  <input id="toggle-disabled" type="checkbox" bind:checked={disabled} />
  <label for="toggle-disabled">disabled</label>
</label>

<article
  class="target"
  {@attach highlight_matches({ query: search_text.toLowerCase(), disabled, node_filter })}
>
  <p>
    This paragraph will highlight occurrences of the query across element boundaries. Try
    words like <em>ancient</em>, <strong>giant</strong>, or split-
    <span>word</span> matches.
  </p>
  <p class="no-hl" style="opacity: 0.7">
    This line is excluded via node_filter.
  </p>
</article>

click_outside

  <script>
  import { click_outside, tooltip } from '$lib'

  let open_menu = $state(false)
</script>

<div class="menu">
  <button
    class="toggle"
    onclick={() => open_menu = !open_menu}
    {@attach tooltip({ content: 'Toggle menu', placement: 'top' })}
  >
    Menu
  </button>

  {#if open_menu}
    <div
      class="dropdown"
      {@attach click_outside({ exclude: ['.toggle'], callback: () => (open_menu = false) })}
    >
      <ul style="list-style: none; padding: 0; margin: 0">
        <li><a href="#one">First</a></li>
        <li><a href="#two">Second</a></li>
        <li>
          <a href="#noop" class="toggle">Clicking me won’t close (excluded)</a>
        </li>
      </ul>
    </div>
  {/if}
</div>

sortable

PlanetMoonsDiscoveryNotes
Mercury0ancient
Venus0ancientVery bright
Earth1ancientLeads with zeros
Mars21610Phobos/Deimos
Jupiter951610Gas giant
Click headers to sort; click again to reverse
  <script>
  import { sortable } from '$lib'
</script>

<table {@attach sortable()} class="demo-table">
  <thead>
    <tr>
      <th>Planet</th>
      <th>Moons</th>
      <th>Discovery</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    {#each [
        { planet: `Mercury`, moons: 0, discovery: `ancient`, notes: `` },
        { planet: `Venus`, moons: 0, discovery: `ancient`, notes: `Very bright` },
        {
          planet: `Earth`,
          moons: 1,
          discovery: `ancient`,
          notes: `Leads with zeros`,
        },
        { planet: `Mars`, moons: 2, discovery: `1610`, notes: `Phobos/Deimos` },
        { planet: `Jupiter`, moons: 95, discovery: `1610`, notes: `Gas giant` },
      ] as
      { planet, moons, discovery, notes }
    }
      <tr>
        <td>{planet}</td>
        <td>{moons}</td>
        <td>{discovery}</td>
        <td>{notes}</td>
      </tr>
    {/each}
  </tbody>
  <caption style="caption-side: bottom; padding-top: 0.5em">
    Click headers to sort; click again to reverse
  </caption>
</table>