@cavicode/uikit-webadmin (1.4.0)

Published 2026-09-13 21:38:00 +03:00 by CaviCode

Installation

@cavicode:registry=
npm install @cavicode/uikit-webadmin@1.4.0
"@cavicode/uikit-webadmin": "1.4.0"

About this package

@cavicode/uikit-webadmin

Vue 3 UI-компоненты административных интерфейсов CaviCode/HVZ.

Установка

npm install @cavicode/uikit-webadmin

.npmrc:

@cavicode:registry=https://git.cavicode.tech/api/packages/cavicode/npm/

@tanstack/vue-table встроен в package bundle. Отдельная установка нужна только consumer, который напрямую импортирует его runtime API или типы.

Nuxt 4

// plugins/uikit.ts
import CavicodeUIKitWebadmin from '@cavicode/uikit-webadmin'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(CavicodeUIKitWebadmin)
})
// nuxt.config.ts
export default defineNuxtConfig({
  css: ['@cavicode/uikit-webadmin/style.css'],
})

Для template types consumer добавляет GlobalComponents augmentation. Nuxt components[] не подходит: опубликованный dist содержит compiled JS и .d.ts, а не каталог исходных .vue.

Лёгкое подключение 1.4

Основной entry сохраняет полный каталог компонентов и строковых Lucide-иконок. Для приложений, которым важен начальный JS, 1.4.0 предоставляет opt-in entry @cavicode/uikit-webadmin/lite. Consumer явно передаёт только используемые компоненты и иконки:

import { UiButton, UiIcon, createUIKitWebadmin } from '@cavicode/uikit-webadmin/lite'
import { CircleHelp, Plus } from '@lucide/vue'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(createUIKitWebadmin(
    { UiButton, UiIcon },
    { CircleHelp, Plus },
  ))
})

@lucide/vue должен быть прямой dependency consumer. CircleHelp сохраняет fallback неизвестной иконки. При добавлении нового Ui* или строкового имени иконки consumer обновляет свой явный registry; переданный Vue-компонент иконки работает без registry. CSS и GlobalComponents подключаются как для полного entry, но типы компонентов импортируются из @cavicode/uikit-webadmin/lite.

Tailwind v4 consumer должен импортировать package CSS в общий pipeline и сканировать compiled dist:

@import "tailwindcss";
@import "@cavicode/uikit-webadmin/style.css";
@source "../../../node_modules/@cavicode/uikit-webadmin/dist";

Путь @source зависит от расположения consumer stylesheet.

Темы и локализация 1.2

Версия 1.2.0 добавляет необязательные semantic CSS tokens --ui-* и реактивный язык встроенных строк ru/en. Без настройки сохраняются прежняя светлая палитра и русский язык. Nuxt/vue-i18n и новый plugin не нужны:

import { ref } from 'vue'
import { uiLocaleKey, type UiLocale } from '@cavicode/uikit-webadmin'

const locale = ref<UiLocale>('ru')
app.provide(uiLocaleKey, locale)
locale.value = 'en'

В Nuxt app здесь соответствует nuxtApp.vueApp; вместо ref можно предоставить getter существующего consumer i18n state. Также экспортируются useUiLocale, uiMessages и UiMessageKey. Явные text props, slots и formatters сохраняют приоритет; тексты бизнес-данных переводит consumer.

Тема задаётся маппингом собственных runtime colors на --ui-surface, --ui-surface-muted, --ui-surface-hover, --ui-text, --ui-text-muted, --ui-text-subtle, --ui-border, --ui-primary и соответствующие variant tokens. Для полного приложения задавайте их на html/body, чтобы охватить Teleport. Выбор и сохранение темы/языка, html.lang и color-scheme остаются у приложения.

Точный API, полный список токенов, совместимость и интеграция SSO. 1.2.0 опубликована: постоянная consumer dependency должна приходить из registry. Source aliases и постоянные file: не нужны.

Компоненты (46)

  • Поля: UiInput, UiTextarea, UiSelect, UiCheckbox, UiRadio, UiColorInput, UiSearchInput.
  • Кнопки и иконки: UiButton, UiIconButton, UiIcon.
  • Формы/layout: UiFormField, UiFormGrid, UiCard, UiMetricCard, UiAlert.
  • Диалоги: UiModal, UiConfirmDialog, UiDrawer.
  • Таблицы: UiDataTable, UiSimpleTable, UiTableSearch, UiTableToolbar.
  • Навигация: UiPagination, UiTabs, UiPageHeader, UiBreadcrumbs, UiNavbar, UiSidebar, UiMenu, UiTooltip.
  • Состояния: UiState, UiStatusBadge, UiToast, UiSpinner, UiSkeleton, UiProgress.
  • Прочее: UiDropdown, UiMediaPicker, UiOrderControls, UiBarChart, UiLineChart, UiPieChart, UiTag, UiDefinitionList, UiDateDivider, UiLogConsole.

Базовый API 0.2

UiInput, UiTextarea, UiSelect, UiSearchInput, UiColorInput и UiDropdown используют размеры sm | md | lg. Поля связывают hint/error/success с контролом через aria-describedby; UiInput, UiTextarea и UiSearchInput поддерживают clearable и событие clear.

UiInput предоставляет slots prefix и suffix:

<UiInput v-model="amount" label="Сумма" clearable>
  <template #suffix></template>
</UiInput>

UiDropdown теперь выводит label визуально и поддерживает hint, error, success, required, loading, а также slots selection, option и empty. Событие search передаёт consumer строку встроенного поиска для server-side загрузки вариантов. Prop placement="auto|top|bottom" управляет направлением popup; по умолчанию компонент выбирает сторону по свободному месту. loading показывает spinner и aria-busy, но не закрывает popup и не блокирует поиск: введённая строка сохраняется до получения server-side вариантов.

Начиная с 1.3.0, filter-mode="server" отключает повторную локальную фильтрацию: Dropdown показывает переданные options, даже если поиск выполнен по телефону или другому полю, отсутствующему в label. По умолчанию filter-mode="local" сохраняет поиск по label/description/value. Загрузка, защита от устаревших ответов и сохранение выбранных options принадлежат consumer. Экспортируется тип UiDropdownFilterMode.

<UiDropdown
  v-model="studentId"
  :options="serverOptions"
  :loading="loading"
  filter-mode="server"
  @search="loadOptions"
/>

UiTooltip в 1.3.0 отменяет все ожидающие таймеры при смене overlay stack, disabled, Escape и unmount. Сочетание hover/focus не оставляет забытый таймер, который способен открыть подсказку поверх нового диалога.

По умолчанию control занимает всю ширину контейнера. Prop fit-content сжимает trigger по выбранному тексту (не шире контейнера), при этом popup остаётся не уже trigger, расширяется под длинные option labels и ограничивается viewport:

<UiDropdown v-model="branchId" :options="branches" fit-content />

UiButton поддерживает loading, loading-text, block, icon, icon-position и href. В loading-state повторный action блокируется:

<UiButton icon="save" :loading="saving" loading-text="Сохраняем">
  Сохранить
</UiButton>

UiCard поддерживает семантический tone="default|info|success|promotion". Promotion-tone предназначен для подтверждённых специальных условий и не заменяет UiAlert для временных сообщений об успехе.

UiTag поддерживает variant="promotion" для компактной постоянной маркировки акций и добавленных ими ценовых условий.

UiIcon полного entry включает весь каталог @lucide/vue; лёгкий entry использует явный consumer registry. Оба принимают имя доступного Lucide-компонента, переданный Vue-компонент или slot. Например: Paperclip, Smile, Mic, Video, Bold, Italic, Underline, Strikethrough, Quote. Все Lucide-компоненты также реэкспортируются из package entry. Старые Material-style имена разрешаются в Lucide через aliases без ручных SVG. Icon font и внешняя загрузка шрифтов не требуются.

Breaking changes 0.2

  • UiFormField рендерит корневой div, а не label. Для связи кастомного контрола передаётся prop for; это исключает невалидные вложенные label.
  • UiRadio без name не создаёт уникальное имя. Для нативной keyboard-группы consumer передаёт одинаковый name всем radio.
  • DOM и CSS-классы базовых полей изменены; использовать внутренние селекторы компонентов как consumer API нельзя.

Навигация и overlays 0.3

UiNavbar и UiSidebar предоставляют структуру приложения и не зависят от router. UiSidebar поддерживает v-model:collapsed; для мобильной панели используется отдельный UiDrawer.

<UiSidebar v-model:collapsed="collapsed" collapsible>
  <template #default="{ collapsed }">...</template>
</UiSidebar>

UiBreadcrumbs принимает массив { label, href?, icon?, disabled? }. UiMenu принимает typed items и предоставляет activator props, которые нужно передать реальной кнопке через v-bind:

<UiMenu :items="actions" @select="runAction">
  <template #activator="{ props }">
    <UiButton v-bind="props">Действия</UiButton>
  </template>
</UiMenu>

Такая же схема activator используется у UiTooltip, чтобы aria-describedby и keyboard handlers находились на фокусируемом элементе.

Общий overlay stack

UiModal и UiDrawer используют общий stack, body lock, focus trap и focus restoration. При открытии нового modal layer нижние слои получают inert. UiDropdown, UiMenu и UiTooltip закрываются при смене stack, поэтому teleport-popover не может остаться активным над неактивным modal/drawer.

Политика модальных окон

UiModal по умолчанию не закрывается кликом по overlay или клавишей Escape. Пользователь закрывает окно только крестиком либо явным consumer action «Отмена»/«Закрыть»; программное закрытие через v-model="false" после успешной операции работает как обычно. Props close-on-overlay и close-on-escape с default false предназначены только для осознанных исключений.

Для дополнительного времени, статуса или другого содержимого справа от заголовка используется typed slot header-extra:

<UiModal v-model="open" title="Занятие">
  <template #header-extra><UiStatusBadge label="Проведено" /></template>
  <!-- Содержимое -->
</UiModal>

На экранах шириной до 768 px все UiModal разворачиваются на весь фактически видимый viewport, в том числе при открытой экранной клавиатуре, с учётом safe area и отдельной прокруткой body. Несколько открытых modal используют общий body lock, располагаются по порядку стека и корректно передают фокус верхнему окну либо внешнему opener после закрытия всего стека. Неактивные слои стека получают inert и скрываются от accessibility tree; только верхнее окно является modal и обрабатывает клавиатуру. UiDrawer по умолчанию закрывается по overlay и Escape. Popover закрываются по Escape/клику снаружи и при изменении общего overlay stack.

UiDataTable автоматически распознаёт строковый заголовок ID и ограничивает колонку диапазоном 72–112 px без переноса цифр. В UiSimpleTable для той же семантики consumer ставит table-id-column на соответствующие th и td.

Для реестров, которые на телефоне должны становиться карточками, используется opt-in prop mobile-cards. Роль колонки задаётся через ColumnDef.meta:

const columns = [
  { accessorKey: 'name', header: 'Название', meta: { mobileRole: 'title' } },
  { accessorKey: 'status', header: 'Статус', meta: { mobileRole: 'badge' } },
  { accessorKey: 'note', header: 'Комментарий', meta: { mobileFullWidth: true } },
  { id: 'actions', header: '', meta: { mobileRole: 'actions' }, cell: renderActions },
]

Поддерживаются роли title, subtitle, badge, field и actions, а также mobileLabel, mobileHidden и mobileFullWidth. По умолчанию колонка выводится как обычное поле. Desktop-представление при этом остаётся таблицей.

Таблицы 0.4

UiDataTable использует manual sorting/filtering: библиотека хранит и эмитит state, но server filtering, sorting и page slice выполняет consumer. Все состояния могут работать controlled через v-model:

<UiDataTable
  v-model:sorting="sorting"
  v-model:row-selection="selection"
  v-model:column-visibility="visibility"
  v-model:column-filters="filters"
  v-model:global-filter="search"
  v-model:page-size="pageSize"
  :data="pageRows"
  :columns="columns"
  :get-row-id="row => String(row.id)"
  toolbar
  searchable
  selectable
  show-column-visibility
/>

Для selection между страницами обязателен стабильный get-row-id; без него TanStack использует индекс строки текущего массива. selection-change содержит только загруженные выбранные rows, а row-selection-change — полный map ID.

Default toolbar поддерживает debounced search, slot filters, slot actions, selection actions, видимость колонок и page size. Его можно заменить slot toolbar или использовать UiTableToolbar отдельно. loading и empty также имеют slots.

UiPagination сохраняет 0-based page, но теперь поддерживает номера страниц, first/last, sibling-count, compact mode и show-jump. На узком viewport номера скрываются, оставляя touch-friendly direction controls.

Media и charts 1.0

UiMediaPicker применяет одну consumer policy к file dialog и drag-and-drop. Форматы задаются через MIME, wildcard или расширение; размеры указываются в bytes. Upload lifecycle остаётся у consumer и передаётся keyed map через file-states:

<UiMediaPicker
  v-model="files"
  :accept="['image/*', '.pdf']"
  :max-files="3"
  :max-file-size="10 * 1024 * 1024"
  :file-states="uploadStates"
  @add="upload"
  @rejected="showValidationErrors"
/>

UiBarChart, UiLineChart и UiPieChart используют общий набор props: loading, loading-text, empty-text, aria-label, show-legend, show-data, format-value и controlled v-model:selected-index. Marks доступны с клавиатуры и эмитят select; loading, empty и legend можно заменить slots. Bar/line дополнительно управляют grid и axes, pie — inner-radius и center value.

<UiLineChart
  v-model:selected-index="selectedPoint"
  :data="points"
  :loading="loading"
  :format-value="formatMoney"
  show-legend
/>

В UiDateDivider режим table рендерит валидную пару tr/td; количество колонок таблицы передаётся через colspan.

Consumer-owned state

UiToast и UiConfirmDialog презентационные. Consumer передаёт props и обрабатывает emits; Nuxt composables в пакет не переносятся.

Charts используют generic data:

type UiBarChartDatum = { label: string; value: number; color?: string }
type UiLineChartPoint = { label?: string; date: string; value: number }
type UiPieChartDatum = { label: string; value: number; color?: string }

Метрики 1.1

UiMetricCard отображает единичный KPI с типизированной иконкой, пояснением, цветовым тоном и slots icon, default, detail, aside. Компонент адаптивен и подходит для dashboard без consumer-копирования рамок, типографики и состояний:

<UiMetricCard
  label="Расчётная зарплата"
  value="42 500 ₽"
  detail="за август 2026"
  icon="payments"
  tone="success"
/>

Миграция на 1.0

Breaking changes и удаляемые consumer workarounds перечислены в docs/solutions/uikit-1-migration.md.

Ограничения версии 1.0.0

  • Upload transport и lifecycle файлов не входят в UiMediaPicker.
  • Charts ожидают неотрицательные значения; отрицательные pie values не строятся.
  • Export ./theme не предоставляет отдельный runtime API: tokens входят в общий style.css.

Разработка

npm ci
npm run typecheck
npm run build
npm test

npm run build также выполняет vue-tsc --noEmit. npm test собирает пакет и проверяет SSR/localization и semantic fallbacks всего каталога через Node test runner. Lint script нет; изменения требуют также consumer typecheck/build.

Dependencies

Dependencies

ID Version
@lucide/vue ^1.39.0

Development dependencies

ID Version
@tanstack/vue-table ^8.21.3
@types/node ^26.2.0
@vitejs/plugin-vue ^5.2.1
@vue/tsconfig ^0.7.0
sass-embedded ^1.100.0
tailwindcss ^4.3.3
typescript ^5.6.0
vite ^6.0.0
vite-plugin-dts ^4.5.0
vue ^3.5.0
vue-tsc ^3.3.8

Peer dependencies

ID Version
vue ^3.5.0
Details
npm
2026-09-13 21:38:00 +03:00
25
UNLICENSED
latest
235 KiB
Assets (1)
Versions (15) View all
1.4.0 2026-09-13
1.3.0 2026-09-13
1.2.0 2026-09-06
1.1.3 2026-09-03
1.1.1 2026-09-03