Blocks

List View

A block for listing of custom items.

Create your own custom list while using the block's features.

Usage

MeListView does not provide a default view of your data. Use the cell scoped slot ({ cell, column, getValue, renderValue, row, table }) to compose each row. To enable sticky header and footer behavior, the block must have an explicit height set. See Table for more information.

Loading preview...
<template>
<MeListView
  v-model:row-selection="rowSelection"
  v-model:active-row="activeRow"
  v-model:pagination="pagination"
  :data="data"
  active-behavior
  selectable
>
  <template #header>
    Header action
  </template>

  <template #cell="{ row }">
    <MeForehead
      :text-banner="{ label: row.original.label, code: row.original.code }"
      banner-variant="text"
    >
      <template #leading>
        <div class="flex flex-col gap-y-[6px]">
          <span class="text-base text-default font-semibold">
            {{ row.original.code }} - {{ row.original.name }}
          </span>

          <span class="text-xs text-muted font-normal">
            {{ row.original.description }}
          </span>
        </div>
      </template>

      <template #trailing>
        <div class="flex flex-col justify-between items-end h-full">
          <span class="text-base text-default font-semibold">
            {{ row.original.price }}
          </span>

          <MeForeheadActionBar
            :actions="[
              { icon: 'i-lucide-heart' },
              { icon: 'i-lucide-wand-sparkles' }
            ]"
            :dropdown-items="[
              { label: 'Duplicate', icon: 'i-lucide-copy', onSelect: () => {} },
              { label: 'Delete', icon: 'i-lucide-trash', onSelect: () => {} }
            ]"
            see-more
          />
        </div>
      </template>
    </MeForehead>
  </template>

  <template #footer>
    Footer action
  </template>
</MeListView>
</template>

<script setup>
const rowSelection = ref({ item2: true })
const pagination = ref({ pageIndex: 0, pageSize: 2 })
const activeRow = ref(undefined)

const data = [
  {
    id: 'item1',
    label: 'Item 1',
    code: '123',
    name: 'Requisição',
    price: 'BRL 100,00',
    description: 'Descrição 1'
  },
  {
    id: 'item2',
    label: 'Item 2',
    code: '456',
    name: 'Cotação',
    price: 'BRL 200,00',
    description: 'Descrição 2'
  },
  {
    id: 'item3',
    label: 'Item 3',
    code: '789',
    name: 'Pedido',
    price: 'BRL 300,00',
    description: 'Descrição 3'
  },
  {
    id: 'item4',
    label: 'Item 4',
    code: '101',
    name: 'Mercado Eletronico',
    price: 'BRL 400,00',
    description: 'Descrição 4'
  }
]

watch(rowSelection, () => console.log('Selected rows: ', rowSelection.value))
watch(pagination, () => console.log('Pagination state: ', pagination.value))
watch(activeRow, () => console.log('Active row: ', activeRow.value))
</script>

Data

Use the data prop to create your listing. It could be an array of objects or a function that returns a Promise that resolves with an array.

type ListViewDataRow = {
  //...any other properties you may need
  id: string,
  avatar?: {
    alt?: string,
    icon?: string,
    src?: string,
    text?: string
  }
}
<template>
  <MeListView
    :data="provider"
    selectable
  >
    <template #cell="{ row }">
      <MeForehead>
        <template #leading>
          <div class="flex flex-col gap-y-[6px]">
            <span class="text-base text-default font-semibold">
              {{ row.original.code }} - {{ row.original.name }}
            </span>

            <span class="text-xs text-muted font-normal">
              {{ row.original.description }}
            </span>
          </div>
        </template>
      </MeForehead>
    </template>
  </MeListView>
</template>

<script setup>
function provider() {
  const data = [
    {
      id: '1',
      code: '123',
      name: 'Requisição',
      description: 'Descrição 1'
    },
    {
      id: '2',
      code: '456',
      name: 'Cotação',
      description: 'Descrição 2'
    },
    {
      id: '3',
      code: '789',
      name: 'Pedido',
      description: 'Descrição 3'
    }
  ]

  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(data)
    }, 1500)
  })
}
</script>

Pagination

Bound pagination with v-model to control the displayed page index and page size. Pagination is provided by TanStack Table, see Table for more information.

type PaginationState = {
  pageIndex: number,
  pageSize: number
}
123 - RequisiçãoDescrição 1
456 - CotaçãoDescrição 2
<template>
  <MeListView
    v-model:pagination="pagination"
    :data="data"
    selectable
  >
    <template #cell="{ row }">
      <MeForehead>
        <template #leading>
          <div class="flex flex-col gap-y-[6px]">
            <span class="text-base text-default font-semibold">
              {{ row.original.code }} - {{ row.original.name }}
            </span>

            <span class="text-xs text-muted font-normal">
              {{ row.original.description }}
            </span>
          </div>
        </template>
      </MeForehead>
    </template>
  </MeListView>
</template>

<script setup>
const pagination = ref({ pageIndex: 0, pageSize: 2 })

const data = [
  {
    id: 'item1',
    code: '123',
    name: 'Requisição',
    description: 'Descrição 1'
  },
  {
    id: 'item2',
    code: '456',
    name: 'Cotação',
    description: 'Descrição 2'
  },
  {
    id: 'item3',
    code: '789',
    name: 'Pedido',
    description: 'Descrição 3'
  },
  {
    id: 'item4',
    code: '101',
    name: 'Mercado Eletronico',
    description: 'Descrição 4'
  }
]
</script>

Selected and active rows

Bound rowSelection and activeRow with v-model to control which rows are selected or activated. Selection and activation are only enabled when selectable and active-behavior props are true, respectively.

type RowSelectionState = Record<string, boolean> // { rowId: boolean (whether or not row is selected) }

const activeRow: string // id of the current active row
123 - RequisiçãoDescrição 1
456 - CotaçãoDescrição 2
789 - PedidoDescrição 3
<template>
  <MeListView
    v-model:active-row="activeRow"
    v-model:row-selection="rowSelection"
    :data="data"
    selectable
    active-behavior
  >
    <template #cell="{ row }">
      <MeForehead>
        <template #leading>
          <div class="flex flex-col gap-y-[6px]">
            <span class="text-base text-default font-semibold">
              {{ row.original.code }} - {{ row.original.name }}
            </span>

            <span class="text-xs text-muted font-normal">
              {{ row.original.description }}
            </span>
          </div>
        </template>
      </MeForehead>
    </template>
  </MeListView>
</template>

<script setup>
const activeRow = ref('item2')
const rowSelection = ref({ item1: true })

const data = [
  {
    id: 'item1',
    code: '123',
    name: 'Requisição',
    description: 'Descrição 1'
  },
  {
    id: 'item2',
    code: '456',
    name: 'Cotação',
    description: 'Descrição 2'
  },
  {
    id: 'item3',
    code: '789',
    name: 'Pedido',
    description: 'Descrição 3'
  }
]
</script>

Empty

Use the empty prop to customize the empty state when no data is provided. It accepts all props from Empty (except variant), plus a src field to customize the image.

const empty: Omit<EmptyProps, 'variant' & { src?: string }>

Title

Description
<template>
  <MeListView
    :empty="{
      title: 'Title',
      description: 'Description',
      actions: [{ label: 'Action' }]
    }"
    selectable
  />
</template>

Preview

Combine MePreview with MeListView to create a custom preview for each list item.

RE
123 - RequisiçãoDescrição 1
BRL 100,00
CO
456 - CotaçãoDescrição 2
BRL 200,00
PE
789 - PedidoDescrição 3
BRL 300,00
101 - Mercado EletronicoDescrição 4
BRL 400,00
Loading...
<template>
  <MePreview :preview-config="{ src: '/' }">
    <MeListView
      :data="data"
      :pagination="{ pageIndex: 0, pageSize: 4 }"
      active-row="item1"
      active-behavior
      selectable
    >
      <template #cell="{ row }">
        <MeForehead>
          <template #leading>
            <div class="flex flex-col gap-y-[6px]">
              <span class="text-base text-default font-semibold">
                {{ row.original.code }} - {{ row.original.name }}
              </span>

              <span class="text-xs text-muted font-normal">
                {{ row.original.description }}
              </span>
            </div>
          </template>

          <template #trailing>
            <div class="flex flex-col justify-between items-end h-full">
              <span class="text-base text-default font-semibold">
                {{ row.original.price }}
              </span>

              <MeForeheadActionBar
                :actions="[
                  { icon: 'i-lucide-heart' },
                  { icon: 'i-lucide-wand-sparkles' }
                ]"
                :dropdown-items="[
                  { label: 'Duplicate', icon: 'i-lucide-copy', onSelect: () => {} },
                  { label: 'Delete', icon: 'i-lucide-trash', onSelect: () => {} }
                ]"
                see-more
              />
            </div>
          </template>
        </MeForehead>
      </template>
    </MeListView>
  </MePreview>
</template>

<script setup>
const data = [
  {
    id: 'item1',
    avatar: { text: 'RE' },
    code: '123',
    name: 'Requisição',
    description: 'Descrição 1',
    price: 'BRL 100,00'
  },
  {
    id: 'item2',
    avatar: { text: 'CO' },
    code: '456',
    name: 'Cotação',
    description: 'Descrição 2',
    price: 'BRL 200,00'
  },
  {
    id: 'item3',
    avatar: { text: 'PE' },
    code: '789',
    name: 'Pedido',
    description: 'Descrição 3',
    price: 'BRL 300,00'
  },
  {
    id: 'item4',
    avatar: { alt: 'Default avatar' },
    code: '101',
    name: 'Mercado Eletronico',
    description: 'Descrição 4',
    price: 'BRL 400,00'
  }
]
</script>

Slots

The block provides the header (scoped { column, header, table }), cell (scoped ({ cell, column, getValue, renderValue, row, table })) and footer slots.

Header slot
Row item1 cell slot
Row item2 cell slot
Row item3 cell slot
Footer slot
<template>
  <MeListView
    :data="data"
    selectable
  >
    <template #header>
      Header slot
    </template>

    <template #cell="{ row }">
      Row {{ row.original.id }} cell slot
    </template>

    <template #footer>
      Footer slot
    </template>
  </MeListView>
</template>

<script setup>
const data = [
  {
    id: 'item1',
    code: '123',
    name: 'Requisição',
    description: 'Descrição 1'
  },
  {
    id: 'item2',
    code: '456',
    name: 'Cotação',
    description: 'Descrição 2'
  },
  {
    id: 'item3',
    code: '789',
    name: 'Pedido',
    description: 'Descrição 3'
  }
]
</script>

API

Props

PropDefaultType
activeBehaviorfalseBoolean
Enables row activation
activeRow''String
Sets active row, bound with v-model
dataT[] | () => Promise<T[]>
List rows data
emptyOmit<EmptyProps, 'variant'> & { src?: string }
Empty state configuration. See Empty
loadingfalseBoolean
Sets list to loading state
loadingColor'primary''primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral'
Loading bar color
loadingAnimation'carousel''carousel' | 'carousel-inverse' | 'swing' | 'elastic'
Loading bar animation
pagination{ pageIndex: number, pageSize: number }
Pagination state, bound with v-model
rowSelectionRecord<string, boolean>
Selected rows, bound with v-model
selectablefalseBoolean
Enables row selection

Slots

SlotType
cell{ cell, column, getValue, renderValue, row, table }
empty{}
footer{}
header{ column, header, table }