Slider

Sliderは、指定された数値の範囲から値を選択するためのコンポーネントです。

プロパティ

Sliderは、以下のプロパティをサポートしています。

名前 デフォルト値 説明
max number 100 最大値を指定します。
min number 0 最小値を指定します。
step number 1 stepを指定します。
value number 0 初期値を指定します。
trackClass string 0 sliderのtrackのクラスを指定します。

インストールの手順

以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。

atoms/Slider.svelte
        <!--
@component

## 概要
- Sliderは、指定された数値の範囲から値を選択するためのコンポーネントです。

## 機能
- 範囲制限(min, max)とステップ刻み(step)に対応
- キーボード操作(Arrowキー)で値の増減が可能
- カスタムラベル(minContent, maxContent)のスロット対応

## Props
- min: スライダーの最小値を指定します(デフォルト: 0)
- max: スライダーの最大値を指定します(デフォルト: 100)
- step: 値の増減単位を指定します(デフォルト: 1)
- value: スライダーの現在の値(bind:value 可能、デフォルト: 50)
- class: 外部から渡されるクラス名を指定します
- minContent: 最小値のラベルに表示するスロットコンテンツ
- maxContent: 最大値のラベルに表示するスロットコンテンツ

## Usage
```svelte
<Slider min={0} max={100} bind:value={current} step={5}>
  {#snippet minContent()}
    <span>Low</span>
  {/snippet}
  {#snippet maxContent()}
    <span>High</span>
  {/snippet}
</Slider>
```
-->

<script module lang="ts">
  import type { Snippet } from 'svelte';
  import type { ClassValue } from 'svelte/elements';
  import { cva } from 'class-variance-authority';

  export const sliderTrackVariants = cva('relative flex items-center min-w-64 w-full h-5');

  export const sliderThumbVariants = cva('absolute bg-primary rounded-full outline-none touch-none transition-shadow cursor-pointer focus-visible:ring-2 hover:ring-[0.25rem] focus-visible:ring-offset-2 focus-visible:ring-primary hover:ring-primary/20');

  export interface SliderProps {
    /** スライダーの最小値 */
    min: number;
    /** スライダーの最大値 */
    max: number;
    /** スライダーのステップ値 */
    step?: number;
    /** 現在の値 */
    value?: number;
    /** クラス */
    class?: ClassValue;
    /** スライダーのトラックのクラス名 */
    trackClass?: ClassValue;
    /** 最小値のsnippet */
    minContent?: Snippet<[]>;
    /** 最大値のsnippet */
    maxContent?: Snippet<[]>;
  }
</script>

<script lang="ts">
  let { class: className, min = 0, max = 100, step = 1, value = $bindable(0), trackClass, minContent, maxContent }: SliderProps = $props();

  let thumbSize = 20;

  let trackWidth = $state(0);

  let thumbLeft = $derived.by(() => {
    const range = max - min;
    const ratio = (value - min) / range;
    const usable_width = trackWidth - thumbSize;
    return ratio * usable_width;
  });

  function onKeyDown(e: KeyboardEvent) {
    if (e.key === 'ArrowRight') {
      e.preventDefault();
      value = Math.min(value + step, max);
    }
    else if (e.key === 'ArrowLeft') {
      e.preventDefault();
      value = Math.max(value - step, min);
    }
  }

  function onPointerDown(e: PointerEvent) {
    e.preventDefault();
    if (!(e.currentTarget instanceof HTMLElement)) return;
    const track = e.currentTarget;
    const rect = track.getBoundingClientRect();

    function update_value(clientX: number) {
      const pos = Math.min(Math.max(clientX - rect.left, 0), rect.width);
      const new_percent = pos / rect.width;
      const raw_value = min + (max - min) * new_percent;
      value = Math.round(raw_value / step) * step;
    }

    update_value(e.clientX);

    function onMove(e: PointerEvent) {
      update_value(e.clientX);
    }

    function onUp() {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerup', onUp);
    }

    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  }
</script>

<div class={[className, 'flex items-center justify-center w-fit gap-x-2 mx-auto']}>
  {@render minContent?.()}

  <div class={[sliderTrackVariants(), trackClass]} onpointerdown={onPointerDown} bind:clientWidth={trackWidth}>
    <!-- 背景バー -->
    <div class="absolute top-1/2 w-full h-2 bg-base-container-muted rounded-full -translate-y-1/2 cursor-pointer"></div>

    <!-- アクティブバー -->
    <div class="absolute top-1/2 h-2 bg-primary rounded-full -translate-y-1/2 cursor-pointer" style={`width: ${thumbLeft + thumbSize / 2}px;`}></div>

    <!-- サム -->
    <div class={sliderThumbVariants()} style={`width: ${thumbSize}px; height: ${thumbSize}px; left: ${thumbLeft}px;`} tabindex="0" role="slider" aria-valuemin={min} aria-valuemax={max} aria-valuenow={value} onkeydown={onKeyDown}></div>
  </div>

  {@render maxContent?.()}
</div>

      

使い方


サンプル

Default

デフォルトの状態です。

With RangeLabel

最小値と最大値を数値で表示するスライダーです。

With Icon

最小値と最大値をアイコンで表示するスライダーです。

Step

stepの数値を指定できます。