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.
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
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
Planet | Moons | Discovery | Notes |
---|---|---|---|
Mercury | 0 | ancient | |
Venus | 0 | ancient | Very bright |
Earth | 1 | ancient | Leads with zeros |
Mars | 2 | 1610 | Phobos/Deimos |
Jupiter | 95 | 1610 | Gas giant |
<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>