sortable

Sortable playlist with frame

Sortable playlist with frame

Загрузка превью…

src/sortable/c-sortable-4.vue

<!--
  Расхождение с апстримом: `IconPlaceholder` заменена инлайновым `<svg>`
  (lucide-react v0.545.0 "list-music"/"grip-vertical"/"music") — см.
  docs/PORTING.md §5.
-->
<script setup lang="ts">
import { ref } from "vue"
import { Badge } from "@/components/reui/badge"
import { Frame, FrameDescription, FrameHeader, FramePanel, FrameTitle } from "@/components/reui/frame"
import { Sortable, SortableItem, SortableItemHandle } from "@/components/reui/sortable"

interface Track {
  id: string
  title: string
  artist: string
  album: string
  duration: string
  plays: string
  active?: boolean
}

const tracks = ref<Track[]>([
  { id: "1", title: "Midnight City", artist: "M83", album: "Hurry Up, We're Dreaming", duration: "4:03", plays: "1.2B", active: true },
  { id: "2", title: "Digital Love", artist: "Daft Punk", album: "Discovery", duration: "4:58", plays: "845M" },
  { id: "3", title: "Starlight", artist: "Muse", album: "Black Holes", duration: "3:59", plays: "720M" },
  { id: "4", title: "Take On Me", artist: "a-ha", album: "Hunting High and Low", duration: "3:48", plays: "1.8B" },
  { id: "5", title: "Blue Monday", artist: "New Order", album: "Power, Corruption", duration: "7:29", plays: "530M" },
])

function getItemValue(item: Track) {
  return item.id
}
</script>

<template>
  <div class="mx-auto w-full max-w-md">
    <Frame spacing="xs">
      <FrameHeader>
        <div class="flex items-center justify-between">
          <div>
            <FrameTitle>Queue</FrameTitle>
            <FrameDescription>{{ tracks.length }} tracks</FrameDescription>
          </div>
          <Badge variant="outline" size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-music size-3">
              <path d="M16 5H3" />
              <path d="M11 12H3" />
              <path d="M11 19H3" />
              <path d="M21 16V5" />
              <circle cx="18" cy="16" r="3" />
            </svg>Playlist</Badge>
        </div>
      </FrameHeader>
      <Sortable
        v-model:value="tracks"
        :get-item-value="getItemValue"
        strategy="vertical"
        class="space-y-0.5"
      >
        <SortableItem v-for="track in tracks" :key="track.id" :value="track.id">
          <FramePanel class="p-0!">
            <div class="group flex items-center gap-3 px-3 py-2.5">
              <SortableItemHandle class="text-muted-foreground hover:text-foreground">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-grip-vertical size-4">
                  <circle cx="9" cy="12" r="1" />
                  <circle cx="9" cy="5" r="1" />
                  <circle cx="9" cy="19" r="1" />
                  <circle cx="15" cy="12" r="1" />
                  <circle cx="15" cy="5" r="1" />
                  <circle cx="15" cy="19" r="1" />
                </svg>
              </SortableItemHandle>
              <div class="bg-muted flex size-9 shrink-0 items-center justify-center rounded-md">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-music text-muted-foreground size-4">
                  <path d="M9 18V5l12-2v13" />
                  <circle cx="6" cy="18" r="3" />
                  <circle cx="18" cy="16" r="3" />
                </svg>
              </div>
              <div class="min-w-0 flex-1">
                <p :class="`truncate text-sm font-medium ${track.active ? 'text-primary' : ''}`">{{ track.title }}<Badge v-if="track.active" variant="primary-light" size="xs" class="ms-1.5 align-middle">Playing</Badge></p>
                <p class="text-muted-foreground truncate text-xs">{{ track.artist }} &middot; {{ track.album }}</p>
              </div>
              <div class="flex items-center gap-3">
                <span class="text-muted-foreground hidden text-xs tabular-nums sm:inline">{{ track.plays }}</span>
                <span class="text-muted-foreground text-xs tabular-nums">{{ track.duration }}</span>
              </div>
            </div>
          </FramePanel>
        </SortableItem>
      </Sortable>
    </Frame>
  </div>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-sortable-4.json

Зависимости реестра

Источник: порт из ReUI (Keenthemes, MIT)