Svelecte

Various examples

This page gives you some examples what can be done and how.

Multiselect

Example how multiselect can be implemented.

3 selected
<script>
  const svg_checked = `<svg>...</svg>`;
  const svg_empty = `<svg>...</svg>`;
  function svgRenderer(opt, isSelected, _input) {
    return `<div class="inlined">${opt.$selected ? svg_checked : svg_empty}<span>${opt.text}</span></div>`;
  }
</script>

<Svelecte
  multiple
  renderer={svgRenderer}
  collapseSelection="always"
  clearable
  keepSelectionInList={true}
  searchProps={{skipSort: true}}
></Svelecte>
<script>
  const svg_checked = `<svg>...</svg>`;
  const svg_empty = `<svg>...</svg>`;
  function svgRenderer(opt, isSelected, _input) {
    return `<div class="inlined">${opt.$selected ? svg_checked : svg_empty}<span>${opt.text}</span></div>`;
  }
</script>

<Svelecte
  multiple
  renderer={svgRenderer}
  collapseSelection="always"
  clearable
  keepSelectionInList={true}
  searchProps={{skipSort: true}}
></Svelecte>

Custom option snippet

Simple example of using snippet instead of render function for dropdown item rendering. Default snippet implementation correctly handles highlighted search. Custom snippet has no ability to do that.

If you need to keep highlighting feature use render function for custom option rendering.

option X
<script>
  import Svelecte from 'svelecte'

  let options = [
    {id: '1', text: 'option X'},
    {id: '2', text: 'option Y'},
    {id: '3', text: 'option Z'}
  ];
</script>

{#snippet option(item)}
<div>
  {item.$selected ? 'πŸ‘Œ' : 'πŸ‘‰'} {item.text} {item.$selected ? 'βœ…' : 'β˜‘οΈ'}
</div>
{/snippet}

<Svelecte {options} closeAfterSelect={false} value={'1'} {option} />
<script>
  import Svelecte from 'svelecte'

  let options = [
    {id: '1', text: 'option X'},
    {id: '2', text: 'option Y'},
    {id: '3', text: 'option Z'}
  ];
</script>

{#snippet option(item)}
<div>
  {item.$selected ? 'πŸ‘Œ' : 'πŸ‘‰'} {item.text} {item.$selected ? 'βœ…' : 'β˜‘οΈ'}
</div>
{/snippet}

<Svelecte {options} closeAfterSelect={false} value={'1'} {option} />

πŸ’‘ Replicate "search highlighting" with custom option


<script>
  import Svelecte from 'svelecte'
  import { highlightSearch } from 'svelecte';

  let options = [
    {id: '1', text: 'option X'},
    {id: '2', text: 'option Y'},
    {id: '3', text: 'option Z'}
  ];
</script>

{#snippet optionWithSearch(item, inputValue)}
<div>
  <b>Item:</b> {@html highlightSearch(item, false, inputValue, opt => opt.text)} {item.$selected ? 'πŸ’₯' : ''}
</div>
{/snippet}

<Svelecte options={render_options} closeAfterSelect={false} value={'1'} />
<script>
  import Svelecte from 'svelecte'
  import { highlightSearch } from 'svelecte';

  let options = [
    {id: '1', text: 'option X'},
    {id: '2', text: 'option Y'},
    {id: '3', text: 'option Z'}
  ];
</script>

{#snippet optionWithSearch(item, inputValue)}
<div>
  <b>Item:</b> {@html highlightSearch(item, false, inputValue, opt => opt.text)} {item.$selected ? 'πŸ’₯' : ''}
</div>
{/snippet}

<Svelecte options={render_options} closeAfterSelect={false} value={'1'} />

Dependent selects

This functionality is not strictly related to remote fetch, but it’s typical how it’s used. Just set parentValue and you’re done. If you require parentValue in your fetch URL, just use [parent] placeholder.

<script>
  import Svelecte from 'svelecte';

  let parentValue = $state(null);
  let value = $state();

  // ...
</script>

<Svelecte {options} bind:value={parentValue} clearable />
<Svelecte {parentValue} bind:value fetch="/api/[parent]?search=[query]" />
<script>
  import Svelecte from 'svelecte';

  let parentValue = $state(null);
  let value = $state();

  // ...
</script>

<Svelecte {options} bind:value={parentValue} clearable />
<Svelecte {parentValue} bind:value fetch="/api/[parent]?search=[query]" />

Drag & Drop

You can add support for drag & drop reordering by adding svelte-dnd-action library

Reorder selection by dragging: red,blue,purple

Red
Blue
Purple
<script>
  import Svelecte from 'svelecte';
  import { dndzone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode } from 'svelte-dnd-action';

  /** my example has no 'id' property */
  overrideItemIdKeyNameBeforeInitialisingDndZones('value');

  // rest of code ...
</script>

<Svelecte {options} bind:value={value} multiple {dndzone} placeholder="Re-order selected items by dragging" />
<script>
  import Svelecte from 'svelecte';
  import { dndzone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode } from 'svelte-dnd-action';

  /** my example has no 'id' property */
  overrideItemIdKeyNameBeforeInitialisingDndZones('value');

  // rest of code ...
</script>

<Svelecte {options} bind:value={value} multiple {dndzone} placeholder="Re-order selected items by dragging" />

Custom dropdown positioning

When Svelecte is placed inside container with overflow: hidden, dropdown gets clipped. You can fix that by providing positionResolver property. It’s a svelte action which receives dropdown element, so you can move it to document.body and position it manually.

Inside overflow (with positionResolver)
Inside overflow (without positionResolver)
<script>
  import Svelecte from 'svelecte';

  function bodyPositionResolver(node) {
    const svelecteEl = node.parentElement;
    document.body.appendChild(node);

    function reposition() {
      const rect = svelecteEl.getBoundingClientRect();
      Object.assign(node.style, {
        top: `${rect.bottom + window.scrollY}px`,
        left: `${rect.left + window.scrollX}px`,
        minWidth: `${rect.width}px`
      });
    }
    reposition();

    // re-calculate position every time dropdown gets opened
    const observer = new MutationObserver(() => node.classList.contains('is-open') && reposition());
    observer.observe(node, { attributes: true, attributeFilter: ['class'] });

    return {
      destroy() {
        observer.disconnect();
        node.remove();
      }
    }
  }
</script>

<div class="in-overflow">
  <Svelecte {options} positionResolver={bodyPositionResolver} />
</div>

<style>
  .in-overflow {
    position: relative;
    overflow: hidden;
  }
</style>
<script>
  import Svelecte from 'svelecte';

  function bodyPositionResolver(node) {
    const svelecteEl = node.parentElement;
    document.body.appendChild(node);

    function reposition() {
      const rect = svelecteEl.getBoundingClientRect();
      Object.assign(node.style, {
        top: `${rect.bottom + window.scrollY}px`,
        left: `${rect.left + window.scrollX}px`,
        minWidth: `${rect.width}px`
      });
    }
    reposition();

    // re-calculate position every time dropdown gets opened
    const observer = new MutationObserver(() => node.classList.contains('is-open') && reposition());
    observer.observe(node, { attributes: true, attributeFilter: ['class'] });

    return {
      destroy() {
        observer.disconnect();
        node.remove();
      }
    }
  }
</script>

<div class="in-overflow">
  <Svelecte {options} positionResolver={bodyPositionResolver} />
</div>

<style>
  .in-overflow {
    position: relative;
    overflow: hidden;
  }
</style>

Made with Svelte ❀ by Martin Skocik.
♦
You can support me through GitHub.