GrDashboardItemSettings

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

Берут, когда у виджета есть что настраивать.

Когда брать

  • у виджета есть что настраивать — период, источник, порог тревоги: окно даёт им место, не заставляя приложение собирать диалог заново на каждом дашборде;
  • размер задаётся числом, а не жестом — с клавиатуры уголок растягивания требует счёта нажатий, а тут ширина и высота вводятся прямо;
  • виджет нельзя растянуть мышью — узкий экран, сенсорный ввод, resizable снят у соседей: поля остаются единственным способом изменить размер;
  • отказ должен быть виден — «на сетке нет места» окно показывает на месте, а не закрывается над несделанным.

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

НужноБерите
Своё окно со своей шапкой и подваломGrDialog
Спросить «точно?» перед разрушительным действиемGrConfirmDialog
Продуктовые кнопки прямо в шапке виджетаGrDashboardItem
Форму из схемы бэкендаGrSchemaForm

Кнопку открывает виджет, окно рисует пакет

Шестерёнка живёт у GrDashboardItem под пропом showSettings и появляется там же, где #editActions: в шапке, если она есть, и в панели поверх содержимого, если шапки нет. Шапку она, в отличие от #actions, не включает — иначе вход в режим редактирования сдвигал бы содержимое на её высоту.

Нажатие эмитит settings у виджета и itemSettings у сетки. Второе нужно затем, чтобы приложение слушало одно место, а не подписывалось на каждый виджет отдельно.

Размер коммитит сетка, а не окно

Ширина и высота уходят в раскладку через контекст сетки — с её компактизацией, её preventCollision и её проверкой static. Своей арифметики у окна нет намеренно: посчитай оно размер само, на дашборде появились бы две раскладки, расходящиеся на первом же столкновении.

Отсюда и границы полей. Верхний предел ширины — не число колонок и не maxW, а меньшее из них и cols - x: виджет растёт вправо и за правый край не выйдет. Поле, предлагающее двенадцать колонок виджету, стоящему на восьмой, молча отдало бы четыре.

Применение по кнопке, а не вживую

Изменение размера двигает соседей, и живой предпросмотр потребовал бы снимка раскладки и отката по Esc. Поэтому одно нажатие «Применить» — один коммит, а «Отмена» не оставляет следов: черновик набирается заново при каждом открытии.

Вне сетки окно работает без размера

Контекст GrDashboard — единственный источник и виджета, и числа колонок. Стоит окно снаружи, itemId равен null или сетка в режиме просмотра — блока размера просто нет, а слот приложения продолжает работать. Проп hideSize убирает его и там, где сетка есть: бывает, что размер приложение считает само.

Границы

Заголовок виджета, его static и границы minW/maxW окно не редактирует. Первый не входит в раскладку намеренно — уехав в хранилище, он протух бы при первой смене языка; остальные объявляет разметка виджета, и редактор для них завёл бы вторую правду о том же самом.

Установка

npm i @feugene/granularity-dashboard

Импорт

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

API

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

Примеры 1

Basic

Basic
<script setup lang="ts">
import { ref } from 'vue'
import type { GrDashboardResponsiveLayout } from '@feugene/granularity-dashboard'

/**
 * Настройки виджета: размер в ячейках даёт пакет, период — приложение.
 *
 * Кнопку-шестерёнку рисует сам виджет (`show-settings`), окно приложение
 * открывает по `item-settings` от сетки — так подписка одна на всю сетку, а не
 * по одной на каждый виджет.
 */
const layout = ref<GrDashboardResponsiveLayout>({
  lg: [
    { id: 'revenue', x: 0, y: 0, w: 6, h: 2 },
    { id: 'orders', x: 6, y: 0, w: 6, h: 2, minW: 3 },
  ],
})

const titles: Record<string, string> = { revenue: 'Выручка', orders: 'Заказы' }
const periods = [
  { value: 'week', label: 'Неделя' },
  { value: 'month', label: 'Месяц' },
  { value: 'quarter', label: 'Квартал' },
]

const period = ref<Record<string, string>>({ revenue: 'month', orders: 'week' })
const draft = ref('month')

const open = ref(false)
const editing = ref<string | null>(null)

function openSettings(id: string): void {
  editing.value = id
  draft.value = period.value[id] ?? 'month'
  open.value = true
}

function apply(id: string): void {
  period.value = { ...period.value, [id]: draft.value }
}

const breakpoints = { lg: 680, md: 520, sm: 400, xs: 0 }
const cols = { lg: 12, md: 8, sm: 4, xs: 2 }
</script>

<template>
  <GrDashboard
    v-model:layout="layout"
    mode="edit"
    :breakpoints="breakpoints"
    :cols="cols"
    :row-height="72"
    aria-label="Настройки виджета"
    @item-settings="openSettings"
  >
    <GrDashboardItem
      v-for="item in layout.lg"
      :key="item.id"
      :item-id="item.id"
      :title="titles[item.id]"
      show-settings
      overflow="hidden"
    >
      <p class="text-[var(--gr-muted-fg)]">
        Период: {{ periods.find(entry => entry.value === period[item.id])?.label }}
      </p>
    </GrDashboardItem>

    <GrDashboardItemSettings v-model="open" :item-id="editing" @apply="apply">
      <GrFormField label="Период">
        <GrSelect v-model="draft" :options="periods" />
      </GrFormField>
    </GrDashboardItemSettings>
  </GrDashboard>
</template>

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