Agent Skills by ALSEL
Anthropic Claudeソフトウェア開発⭐ リポ 0品質スコア 50/100

tanstack-ranger

TS/JS、React、Vue、Solid、Svelte、Angularに対応した、レンジスライダーおよびマルチレンジスライダーを構築するためのヘッドレスユーティリティです。UIを自由にカスタマイズしながら、スライダーのロジックを手軽に実装できます。

description の原文を見る

Headless utilities for building range and multi-range sliders in TS/JS, React, Vue, Solid, Svelte & Angular.

SKILL.md 本文

Overview

TanStack Ranger は、完全にアクセシブルなレンジスライダーおよびマルチレンジスライダーコンポーネントを構築するためのヘッドレスユーティリティを提供します。単一値、レンジ、マルチサムスライダーのすべての複雑なロジックを処理しながら、スタイリングとマークアップの完全なコントロールを提供します。

Package: @tanstack/react-ranger Core: @tanstack/ranger-core (フレームワーク非依存) Status: Stable

Installation

npm install @tanstack/react-ranger

Core Pattern

import { useRanger } from '@tanstack/react-ranger'

function RangeSlider() {
  const [values, setValues] = useState([25, 75])

  const rangerInstance = useRanger({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 1,
    onChange: (instance) => setValues(instance.sortedValues),
  })

  const rangerRef = useRef<HTMLDivElement>(null)

  return (
    <div
      ref={rangerRef}
      style={{
        position: 'relative',
        height: '8px',
        background: '#ddd',
        borderRadius: '4px',
        width: '100%',
      }}
    >
      {/* Track segments */}
      {rangerInstance.getSteps().map(({ left, width }, i) => (
        <div
          key={i}
          style={{
            position: 'absolute',
            left: `${left}%`,
            width: `${width}%`,
            height: '100%',
            background: i === 1 ? '#3b82f6' : '#ddd',
            borderRadius: '4px',
          }}
        />
      ))}

      {/* Thumbs */}
      {rangerInstance.handles.map((handle, i) => (
        <button
          key={i}
          {...handle.getHandleProps()}
          style={{
            position: 'absolute',
            left: `${handle.getPercentage()}%`,
            transform: 'translateX(-50%)',
            width: '20px',
            height: '20px',
            borderRadius: '50%',
            background: '#3b82f6',
            border: '2px solid white',
            cursor: 'grab',
          }}
        />
      ))}
    </div>
  )
}

Ranger Options

Required

OptionTypeDescription
getRangerElement() => Element | nullスライダートラック要素を返す
valuesnumber[]現在のサムの値
minnumber最小値
maxnumber最大値
onChange(instance) => void値が変更されたときに呼び出される

Optional

OptionTypeDefaultDescription
stepSizenumber1値間のステップインクリメント
stepsnumber[]-カスタムステップ位置 (stepSize を上書き)
tickSizenumber-目盛りのサイズ
ticksnumber[]-カスタム目盛り位置
interpolatorInterpolatorlinear値補間関数
onDrag(instance) => void-ドラッグ操作中に呼び出される

Ranger Instance API

// ソート済みの値を取得 (常に昇順)
rangerInstance.sortedValues: number[]

// レンダリング用のハンドルを取得
rangerInstance.handles: Handle[]

// ハンドル間のトラックセグメントを取得
rangerInstance.getSteps(): { left: number; width: number }[]

// 目盛りを取得
rangerInstance.getTicks(): { value: number; percentage: number }[]

// プログラマティックに値を設定
rangerInstance.setValues(newValues: number[])

Handle API

interface Handle {
  // トラック上のパーセンテージ位置を取得 (0-100)
  getPercentage(): number

  // 現在の値を取得
  getValue(): number

  // ハンドル要素に適用するプロップを取得
  getHandleProps(): {
    role: 'slider'
    tabIndex: number
    'aria-valuemin': number
    'aria-valuemax': number
    'aria-valuenow': number
    onKeyDown: (e: KeyboardEvent) => void
    onMouseDown: (e: MouseEvent) => void
    onTouchStart: (e: TouchEvent) => void
  }
}

Single Value Slider

function SingleSlider() {
  const [values, setValues] = useState([50])

  const rangerInstance = useRanger({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 1,
    onChange: (instance) => setValues(instance.sortedValues),
  })

  const rangerRef = useRef<HTMLDivElement>(null)

  return (
    <div ref={rangerRef} className="slider-track">
      {rangerInstance.handles.map((handle, i) => (
        <button key={i} {...handle.getHandleProps()} className="slider-thumb">
          {handle.getValue()}
        </button>
      ))}
    </div>
  )
}

Multi-Range Slider

function MultiRangeSlider() {
  const [values, setValues] = useState([10, 40, 60, 90])

  const rangerInstance = useRanger({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 5,
    onChange: (instance) => setValues(instance.sortedValues),
  })

  const rangerRef = useRef<HTMLDivElement>(null)

  return (
    <div ref={rangerRef} className="slider-track">
      {rangerInstance.getSteps().map(({ left, width }, i) => (
        <div
          key={i}
          className={`segment ${i % 2 === 1 ? 'active' : ''}`}
          style={{ left: `${left}%`, width: `${width}%` }}
        />
      ))}
      {rangerInstance.handles.map((handle, i) => (
        <button key={i} {...handle.getHandleProps()} className="slider-thumb" />
      ))}
    </div>
  )
}

Custom Steps

const rangerInstance = useRanger({
  getRangerElement: () => rangerRef.current,
  values,
  min: 0,
  max: 100,
  steps: [0, 10, 25, 50, 75, 100], // これらの値のみが許可される
  onChange: (instance) => setValues(instance.sortedValues),
})

Tick Marks

function SliderWithTicks() {
  const rangerInstance = useRanger({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 10,
    ticks: [0, 25, 50, 75, 100],
    onChange: (instance) => setValues(instance.sortedValues),
  })

  return (
    <div>
      <div ref={rangerRef} className="slider-track">
        {/* Handles */}
      </div>
      <div className="tick-container">
        {rangerInstance.getTicks().map((tick, i) => (
          <div
            key={i}
            style={{ left: `${tick.percentage}%` }}
            className="tick"
          >
            <span className="tick-label">{tick.value}</span>
          </div>
        ))}
      </div>
    </div>
  )
}

Logarithmic Scale

import { logarithmicInterpolator } from '@tanstack/react-ranger'

const rangerInstance = useRanger({
  getRangerElement: () => rangerRef.current,
  values,
  min: 1,
  max: 1000,
  interpolator: logarithmicInterpolator,
  onChange: (instance) => setValues(instance.sortedValues),
})

Accessibility

TanStack Ranger は組み込みのアクセシビリティ機能を提供します:

  • ハンドルに role="slider" を指定
  • aria-valuemin, aria-valuemax, aria-valuenow 属性
  • キーボードナビゲーション (矢印キー、Home、End、Page Up/Down)
  • フォーカス管理
// スクリーンリーダー用の aria-label を追加
<button
  {...handle.getHandleProps()}
  aria-label={`Value: ${handle.getValue()}`}
/>

Controlled vs Uncontrolled

// Controlled (推奨)
const [values, setValues] = useState([50])
const ranger = useRanger({
  values,
  onChange: (instance) => setValues(instance.sortedValues),
  // ...
})

// バリデーション付き
const handleChange = (instance) => {
  const [min, max] = instance.sortedValues
  // 最小ギャップが10であることを確認
  if (max - min >= 10) {
    setValues(instance.sortedValues)
  }
}

Styling Tips

/* Track */
.slider-track {
  position: relative;
  height: 8px;
  background: #e5e7eb;
  border-radius: 4px;
  width: 100%;
}

/* Active segment */
.segment.active {
  background: #3b82f6;
}

/* Thumb */
.slider-thumb {
  position: absolute;
  transform: translateX(-50%);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #3b82f6;
  border: 2px solid white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  cursor: grab;
}

.slider-thumb:active {
  cursor: grabbing;
}

.slider-thumb:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
}

Framework Adapters

FrameworkPackageStatus
React@tanstack/react-rangerStable
Vue@tanstack/vue-rangerStable
Solid@tanstack/solid-rangerStable
Svelte@tanstack/svelte-rangerStable
Angular@tanstack/angular-rangerStable
Core@tanstack/ranger-coreStable

Best Practices

  1. 常に onChange から sortedValues を使用する - ドラッグ中にハンドルが交差する場合がある
  2. getRangerElement コールバックをメモ化する - 不要な再レンダリングを防ぐ
  3. セマンティック HTML を使用する - アクセシビリティのためにハンドルを <button> 要素として レンダリング
  4. aria-label を追加する - 各ハンドルの目的を説明する
  5. CSS transforms を使用する (translateX) - パフォーマンス向上のため left の代わりに使用
  6. onChange でバリデーションする - 制約 (最小ギャップ、最大レンジなど) を強制する
  7. onDrag を使用する - ドラッグ操作中のリアルタイムフィードバック用
  8. タッチターゲットを検討する - モバイルでは少なくとも 44x44px のハンドルにする

Common Pitfalls

  • トラックコンテナに position: relative を忘れる
  • values を使用する (sortedValues の代わりに) - ハンドルが位置を交換する可能性がある
  • getRangerElement をコールバックとして提供しない
  • left でサムの位置を設定する (transform: translateX() の代わりに)
  • キーボードナビゲーションの処理を忘れる (getHandleProps 経由で組み込み済み)
  • 位置を計算するときにサムの幅を考慮しない

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
tanstack-skills
リポジトリ
tanstack-skills/tanstack-skills
ライセンス
MIT
最終更新
不明

Source: https://github.com/tanstack-skills/tanstack-skills / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: tanstack-skills · tanstack-skills/tanstack-skills · ライセンス: MIT