remix/ui/runtime · Demo

View Source

Keyed List

Exercises keyed DOM reconciliation with shuffling, reversing, and per-item controls.

import { on, type Handle } from 'remix/ui'

type ListItem = {
  id: string
  label: string
}

export default function App(handle: Handle) {
  let items: ListItem[] = [
    { id: 'a', label: 'Item A' },
    { id: 'b', label: 'Item B' },
    { id: 'c', label: 'Item C' },
    { id: 'd', label: 'Item D' },
  ]

  let shuffleInterval: ReturnType<typeof setInterval> | null = null

  let moveUp = (index: number) => {
    if (index === 0) return
    let newItems = [...items]
    ;[newItems[index - 1], newItems[index]] = [
      newItems[index],
      newItems[index - 1],
    ]
    items = newItems
    handle.update()
  }

  let moveDown = (index: number) => {
    if (index === items.length - 1) return
    let newItems = [...items]
    ;[newItems[index], newItems[index + 1]] = [
      newItems[index + 1],
      newItems[index],
    ]
    items = newItems
    handle.update()
  }

  let reverse = () => {
    items = [...items].reverse()
    handle.update()
  }

  let shuffle = () => {
    let newItems = [...items]
    for (let i = newItems.length - 1; i > 0; i--) {
      let j = Math.floor(Math.random() * (i + 1))
      ;[newItems[i], newItems[j]] = [newItems[j], newItems[i]]
    }
    items = newItems
    handle.update()
  }

  let toggleAutoShuffle = () => {
    if (shuffleInterval !== null) {
      clearInterval(shuffleInterval)
      shuffleInterval = null
    } else {
      shuffleInterval = setInterval(() => {
        shuffle()
      }, 1000)
    }
    handle.update()
  }

  return () => (
    <div>
      <div className="controls">
        <button mix={[on('click', reverse)]}>Reverse List</button>
        <button mix={[on('click', shuffle)]}>Shuffle List</button>
        <button mix={[on('click', toggleAutoShuffle)]}>
          {shuffleInterval !== null
            ? 'Stop Auto-Shuffle'
            : 'Start Auto-Shuffle'}
        </button>
      </div>

      {items.map((item, index) => (
        <div key={item.id} className="list-item">
          <input
            type="text"
            placeholder={item.label}
            defaultValue={item.label}
          />
          <button
            // disabled={index === 0}
            mix={[on('click', () => moveUp(index))]}
          >

          </button>
          <button
            // disabled={index === items.length - 1}
            mix={[on('click', () => moveDown(index))]}
          >

          </button>
        </div>
      ))}
    </div>
  )
}