AGENT SKILL

dnd-kit

Add drag-and-drop functionality with @dnd-kit. Use whenever the user wants to reorder items, sort a list, build a kanban board, or any drag-to-rearrange interaction. For reordering/sorting use dnd-kit; for free-drag/swipe gestures (drag a card around, swipe-to-dismiss) use the animations skill.

License 许可
AGPL-3.0
最近更新
2026-11-05
Stars
★ 42
Forks
9

01 前言 Front Matter

name
dnd-kit
description
Add drag-and-drop functionality with @dnd-kit. Use whenever the user wants to reorder items, sort a list, build a kanban board, or any drag-to-rearrange interaction. For reordering/sorting use dnd-kit; for free-drag/swipe gestures (drag a card around, swipe-to-dismiss) use the animations skill.

03 SKILL.md 正文

dnd-kit — Drag and Drop

Sortable list (most common pattern)

import { DndContext, closestCenter, type DragEndEvent } from "@dnd-kit/core"
import { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from "@dnd-kit/sortable"
import { CSS } from "@dnd-kit/utilities"
import { GripVertical } from "lucide-react"

interface Item { id: string; title: string }

function SortableItem({ item }: { item: Item }) {
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: item.id })
  const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1 }

  return (
    <div ref={setNodeRef} style={style} className="flex items-center gap-2 rounded-lg border bg-card p-3">
      <button {...attributes} {...listeners} className="cursor-grab active:cursor-grabbing text-muted-foreground">
        <GripVertical className="h-4 w-4" />
      </button>
      <span>{item.title}</span>
    </div>
  )
}

function SortableList() {
  const [items, setItems] = useState<Item[]>([
    { id: "1", title: "First" },
    { id: "2", title: "Second" },
    { id: "3", title: "Third" },
  ])

  function handleDragEnd(event: DragEndEvent) {
    const { active, over } = event
    if (over && active.id !== over.id) {
      setItems((prev) => {
        const oldIndex = prev.findIndex((i) => i.id === active.id)
        const newIndex = prev.findIndex((i) => i.id === over.id)
        return arrayMove(prev, oldIndex, newIndex)
      })
    }
  }

  return (
    <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        <div className="space-y-2">
          {items.map((item) => <SortableItem key={item.id} item={item} />)}
        </div>
      </SortableContext>
    </DndContext>
  )
}

Kanban board (multiple columns)

Columns are droppable zones (useDroppable); cards are the SortableItem from above. over.id on drop is either a column id (dropped on empty space) or a card id (dropped on another card) — resolve both to a column, then move the card.

import { DndContext, type DragEndEvent, DragOverlay, type DragStartEvent, useDroppable } from "@dnd-kit/core"
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"

type ColumnId = "todo" | "doing" | "done"
type Columns = Record<ColumnId, Item[]>

const COLUMN_TITLES: Record<ColumnId, string> = { todo: "To Do", doing: "In Progress", done: "Done" }

function Column({ id, items }: { id: ColumnId; items: Item[] }) {
  const { setNodeRef, isOver } = useDroppable({ id })
  return (
    <div className="flex-1">
      <h3 className="mb-2 text-sm font-medium text-muted-foreground">{COLUMN_TITLES[id]}</h3>
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        <div
          ref={setNodeRef}
          className={cn("min-h-24 space-y-2 rounded-lg border p-2", isOver && "bg-muted/50")}
        >
          {items.map((item) => <SortableItem key={item.id} item={item} />)}
        </div>
      </SortableContext>
    </div>
  )
}

function KanbanBoard() {
  const [columns, setColumns] = useState<Columns>({
    todo: [{ id: "1", title: "First" }, { id: "2", title: "Second" }],
    doing: [{ id: "3", title: "Third" }],
    done: [],
  })
  const [activeId, setActiveId] = useState<string | null>(null)

  const columnIds = Object.keys(columns) as ColumnId[]
  const findColumn = (id: string): ColumnId | undefined =>
    columnIds.find((col) => columns[col].some((item) => item.id === id))
  const findItem = (id: string): Item | undefined =>
    columnIds.flatMap((col) => columns[col]).find((item) => item.id === id)

  function handleDragStart(event: DragStartEvent) {
    setActiveId(event.active.id as string)
  }

  function handleDragEnd(event: DragEndEvent) {
    const { active, over } = event
    setActiveId(null)
    if (!over) return

    const activeItemId = active.id as string
    const overId = over.id as string
    const from = findColumn(activeItemId)
    const to = columnIds.includes(overId as ColumnId) ? (overId as ColumnId) : findColumn(overId)
    if (!from || !to || from === to) return

    setColumns((prev) => {
      const item = prev[from].find((i) => i.id === activeItemId)
      if (!item) return prev
      return {
        ...prev,
        [from]: prev[from].filter((i) => i.id !== activeItemId),
        [to]: [...prev[to], item],
      }
    })
  }

  const activeItem = activeId ? findItem(activeId) : undefined

  return (
    <DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
      <div className="flex gap-4">
        {columnIds.map((id) => <Column key={id} id={id} items={columns[id]} />)}
      </div>
      <DragOverlay>{activeItem ? <SortableItem item={activeItem} /> : null}</DragOverlay>
    </DndContext>
  )
}

This moves a card only between different columns; to also reorder within a column, add an arrayMove branch when from === to (see the sortable list above). useDroppable, cn, and useState are imported from @dnd-kit/core, @/lib/utils, and react respectively.

Key rules

  • Every draggable item needs a unique string id
  • Use arrayMove from @dnd-kit/sortable to reorder arrays
  • useSortable hook provides attributes, listeners, setNodeRef, transform, transition
  • Apply CSS.Transform.toString(transform) as inline style
  • Use DragOverlay for a floating preview during drag

04 同仓库更多 Skills

SimaDevelopment
SimaDevelopment/opsiforce

figma-use

**MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `use_figma` tool call. NEVER call `use_figma` directly without loading this skill first. Skipping it causes common, hard-to-debug failures. Trigger whenever the user wants to perform a write action or a unique read action that requires JavaScript execution in the Figma file context — e.g. create/edit/delete nodes, set up variables or tokens, build components and variants, modify auto-layout or fills, bind variables to properties, or inspect file structure programmatically.

开发工程师设计师Skill与智能体开发
★ 42
SimaDevelopment
SimaDevelopment/opsiforce

figma-use-slides

This skill helps agents use Figma's use_figma MCP tool in the Slides context. Can be used alongside figma-use which has foundational context for using the use_figma tool.

设计师演示文稿Skill与智能体开发
★ 42
SimaDevelopment
SimaDevelopment/opsiforce

figma-use-motion

Motion / animation context for the `use_figma` MCP tool — animating Figma nodes via manual keyframes, animation styles, easing, and timeline duration. Load alongside figma-use whenever a task involves adding, editing, or inspecting animation on a node.

设计师Skill与智能体开发
★ 42
SimaDevelopment
SimaDevelopment/opsiforce

figma-use-figjam

This skill helps agents use Figma's use_figma MCP tool in the FigJam context. Can be used alongside figma-use which has foundational context for using the use_figma tool.

设计师Skill与智能体开发
★ 42
SimaDevelopment
SimaDevelopment/opsiforce

figma-swiftui

SwiftUI ↔ Figma translation. Use whenever the user mentions Swift, SwiftUI, iOS, iPhone, or iPad — in EITHER direction — translating a Figma design into SwiftUI (design → code), or pushing SwiftUI views / screens / tokens back into a Figma file (code → design). Triggers on phrases like 'implement this Figma design in SwiftUI', 'build this screen in Swift', 'push this SwiftUI view to Figma', 'mirror my Swift code in a Figma file', or whenever a Figma URL appears alongside `.swift` files / an `.xcodeproj`. Routes to a direction-specific reference doc; loads alongside `figma-use` for the code → design path.

设计师
★ 42
SimaDevelopment
SimaDevelopment/opsiforce

figma-shaders

**MANDATORY prerequisite** — load this skill before calling `create_shader` or `update_shader`. Use when the user asks to create, author, change, fix, or iterate on a shader effect, shader fill, custom effect, custom fill, or procedural shader in Figma.

设计师
★ 42