GrDashboardItem

Пакет: @feugene/granularity-dashboardспутникГруппа: Прочее

Берут, когда любой виджет внутри `GrDashboard`.

Когда брать

  • любой виджет внутри GrDashboard — вне сетки компонент смысла не имеет: перенос, растягивание и клавиатура идут через её контекст;
  • у содержимого есть минимумminW/minH объявляются здесь, а не в раскладке: раскладка знает координаты, а «график ниже трёх строк не читается» знает виджет;
  • шапка нужна не всегда — она появляется, только если есть title, #header или #actions; карте и большой цифре шапка ни к чему;
  • один виджет ведёт себя иначе:draggable="false" или :resizable="false" сужают правило сетки для него одного.

Когда взять другое

НужноБерите
Карточка вне сетки виджетовGrCard
Показатель числом, без рамы виджетаGrStatistic
Сама сеткаGrDashboard
Каталог виджетов для добавленияGrDashboardPalette

`static` и `draggable` — про разное

static — про раскладку: виджет не двигается сам и не даёт соседям себя двигать; столкновение с ним отменяет чужой перенос.

draggable и resizable — про интерфейс, и снимаются по отдельности: :resizable="false" убирает уголок растягивания, оставляя перенос.

Запрет проверяет сама сетка, а не только прячет ручку. Защита, которая держится на «кнопку не отрисовали», снималась бы первым же прямым вызовом из клавиатурного контекста.

Ручка не занимает места в потоке

В режиме редактирования шапка не появляется ради ручки — панель лежит поверх верха содержимого. Поэтому переключение режима ничего не сдвигает: виджет остаётся ровно на своём месте, а не подпрыгивает на высоту шапки.

Скрытая панель остаётся в DOM и в таб-порядке: убрать её значило бы убрать ручку из обхода клавиатурой. Там, где наведения не бывает (палец), она видна всё время редактирования.

Авто-высота: содержимое решает, сколько строк занять

auto-height снимает с приложения задачу угадывать h за пользователя. Виджет замеряет своё содержимое и просит у сетки столько строк, сколько нужно:

<GrDashboardItem item-id="log" title="События" auto-height>
  <ul><li v-for="e in events" :key="e.id">{{ e.text }}</li></ul>
</GrDashboardItem>

Замеряется обёртка вокруг содержимого, а не тело виджета. Высота тела задана ячейкой сетки, и scrollHeight у него равен max(содержимое, контейнер): виджет смог бы вырасти под содержимое, но, став выше него, сообщал бы уже собственную высоту — и не ужался бы обратно никогда. Обёртка появляется в разметке только при включённом пропе: виджеты без него сохраняют прежний DOM.

Округление вверх. Строка сетки целая (model.md, инвариант 1), и содержимое в 131 пиксель при строке в 60 занимает три строки, а не две. Пустота в несколько пикселей внизу заметна и безобидна — обрезанная последняя строка таблицы нет. Этим авто-высота отличается от растягивания уголком: там пользователь тянет и ждёт прилипания к ближайшей ячейке, поэтому там округление к ближайшему.

Уголок у такого виджета меняет только ширину. Заданную рукой высоту затёр бы первый же замер, так что вертикаль у жеста и у стрелок отбирается сразу — вместо того чтобы обещать движение и отменять его.

minH и maxH продолжают действовать. Упёрлись в maxH — содержимое прокручивается по обычным правилам overflow, ради которых он и существует. static авто-высоте не поддаётся вовсе: он про то, что виджет не двигается ни сам, ни под давлением соседей.

Сетка сообщает об этом отдельным событием. Изменение высоты уезжает в update:layout, как любое другое — раскладка остаётся единственной правдой, — но сетка дополнительно эмитит itemAutoResize. Приложение, которое считает раскладку грязной по правкам, иначе спрашивало бы «сохранить изменения?» после загрузки данных в виджет.

На сервере замера нет: действует h из раскладки, а уточнение приходит после монтирования — как и брейкпоинт (см. ../ssr.md).

Действия разведены по времени жизни

#actions — продуктовые кнопки, видны всегда и включают шапку. #editActions — действия режима редактирования (удалить виджет, открыть настройки): появляются только в mode="edit" и едут либо в шапку, либо в панель, если шапки нет.

Настройки в этом ряду — не слот, а проп: showSettings рисует кнопку-шестерёнку там же, где #editActions, и эмитит settings; сетка пересылает это наружу как itemSettings. Место, иконка и доступное имя одинаковы на всех дашбордах, а что показать по нажатию — дело приложения: готовое окно есть, GrDashboardItemSettings.

Шапку кнопка не включает, в отличие от #actions. Иначе переключение режима сдвигало бы содержимое безголового виджета на её высоту — ровно то, ради чего ручка переноса и уехала в панель поверх содержимого.

Установка

npm i @feugene/granularity-dashboard

Импорт

import { GrDashboardItem } from '@feugene/granularity-dashboard/components/GrDashboardItem'

API

API этого компонента ещё не посчитан: генератор витрины пока обходит только ядро. Пока его нет, справочник — в документации пакета.

Примеры 1

Slots

Slots
<script setup lang="ts">
import { ref } from 'vue'

import { GrBadge } from '@feugene/granularity'
import type { GrDashboardResponsiveLayout } from '@feugene/granularity-dashboard'

/**
 * Виджет — не рамка вокруг содержимого, а карточка со своей поверхностью:
 * заголовок, действия рядом с ним, подвал. Плюс собственные границы размера:
 * их знает виджет, а не раскладка.
 */
const breakpoints = { lg: 680, md: 520, sm: 400, xs: 0 }
const cols = { lg: 12, md: 8, sm: 4, xs: 2 }

const layout = ref<GrDashboardResponsiveLayout>({
  lg: [
    { id: 'report', x: 0, y: 0, w: 7, h: 3 },
    { id: 'narrow', x: 7, y: 0, w: 5, h: 3 },
    { id: 'fixed', x: 0, y: 3, w: 6, h: 2 },
    { id: 'pinned', x: 0, y: 5, w: 12, h: 2 },
  ],
})
</script>

<template>
  <GrDashboard
    v-model:layout="layout"
    mode="edit"
    :breakpoints="breakpoints"
    :cols="cols"
    :row-height="72"
  >
    <!-- Шапка, действия и подвал — три слота вокруг содержимого. -->
    <GrDashboardItem item-id="report" title="Отчёт за квартал">
      <template #actions>
        <GrBadge tone="info" size="sm">черновик</GrBadge>
      </template>

      <p class="text-[var(--gr-muted-fg)]">
        Слот <code>#actions</code> держит то, что относится к заголовку: статус, счётчик, кнопку меню.
      </p>

      <template #footer>
        <span class="text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] text-[var(--gr-muted-fg)]">
          Обновлён 14 июля, 09:40
        </span>
      </template>
    </GrDashboardItem>

    <!--
      Границы объявляет сам виджет: раскладка знает координаты, а «ниже двух
      строк я нечитаем» знает только он.
    -->
    <GrDashboardItem item-id="narrow" title="Не сжимается" :min-w="4" :min-h="2">
      <p class="text-[var(--gr-muted-fg)]">
        <code>min-w="4"</code> и <code>min-h="2"</code>: уголок растягивания дальше этих границ не пустит —
        ни мышью, ни с клавиатуры.
      </p>
    </GrDashboardItem>

    <!-- Размер задан вёрсткой содержимого: менять его нечем и незачем. -->
    <GrDashboardItem item-id="fixed" title="Размер фиксирован" :resizable="false">
      <p class="text-[var(--gr-muted-fg)]">
        <code>:resizable="false"</code>: уголка нет вовсе — ни мышью, ни с клавиатуры. Перетащить виджет
        при этом можно.
      </p>
    </GrDashboardItem>

    <!-- Статика: ручек у неё нет вовсе, и соседи её обтекают. -->
    <GrDashboardItem item-id="pinned" title="Плановые работы" static>
      <p class="text-[var(--gr-muted-fg)]">
        Закреплённый виджет не двигается ни сам, ни соседями — перемещение, упёршееся в него, отменяется целиком.
      </p>
    </GrDashboardItem>
  </GrDashboard>
</template>

Документация компонентаВсе компоненты