@1337exp/taoui 1.1.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.RU.md ADDED
@@ -0,0 +1,492 @@
1
+ # Tao UI
2
+
3
+ [English](README.md) · **Русский**
4
+
5
+ Библиотека Vue 3 компонентов для Nuxt/Vue проектов с системой дизайн-токенов:
6
+ поменял несколько CSS-переменных — поменялась тема всего набора компонентов.
7
+
8
+ ## Компоненты
9
+
10
+ ### Базовые
11
+
12
+ - **TaoBlock** — базовый контейнер с настраиваемыми отступами и скруглением
13
+ - **TaoButton** — кнопка с вариантами (primary, secondary, ghost, danger), размерами и состояниями
14
+ - **TaoCard** — карточка со слотами cover / header / footer. В `#cover` — фото или `TaoCarousel`
15
+ - **TaoCopy** — иконка копирования в буфер. После клика сверху тултип «Скопировано»; если кнопку убрали — тултип тоже
16
+ - **TaoInput** — поле ввода с поддержкой v-model, валидации и слотов `#prefix` / `#suffix` (иконка или текст внутри рамки)
17
+ - **TaoInputGroup** — склейка снаружи: `#before` | поле | `#after` (протокол, единица, кнопка)
18
+ - **TaoModal** — модальное окно с анимацией и слотами
19
+ - **TaoDrawer** — боковая панель (фильтры, настройки). Esc, клик по фону, ловушка фокуса — как у модалки
20
+ - **TaoSpoiler** — раскрывающийся блок. `v-model` и `aria-expanded`. Несколько подряд сами друг друга не закрывают
21
+ - **TaoSpoilerGroup** — аккордеон: `v-model` — ключ открытой панели (`null` — все закрыты), у спойлера `name`
22
+ - **TaoTabs** — вкладки для переключения между секциями контента
23
+ - **TaoTooltip** — всплывающая подсказка при наведении
24
+ - **TaoPopover** — панель по клику: слот с любым контентом, Esc и клик снаружи. Не тултип и не меню действий
25
+
26
+ ### Layout
27
+
28
+ - **TaoContainer** — контейнер с ограничением ширины (wide/slim/ultra-slim) и авто-центровкой
29
+ - **TaoFlex** — обёртка над flexbox с пропами justify/align/direction/wrap/gap
30
+ - **TaoSpace** — авто-расстановка дочерних элементов с равномерным gap
31
+ - **TaoDivider** — разделитель: line (тонкая линия), text (линия с текстом), gap (пустой отступ, size: small/medium/large)
32
+ - **TaoFieldset** — рамка-группа: `legend` сидит на верхней границе, `align` — start / center / end
33
+ - **TaoAnimatedBorder** — контейнер с анимированной радужной рамкой (декоративный, не завязан на тему)
34
+
35
+ ### Формы и ввод
36
+
37
+ - **TaoCheckbox** — чекбокс с v-model, слотами pre/post и `indeterminate` (частично выбран)
38
+ - **TaoSwitch** — переключатель для булевых настроек (`role="switch"`)
39
+ - **TaoRadio** / **TaoRadioGroup** — взаимоисключающий выбор из 2–5 вариантов
40
+ - **TaoSelect** — выпадающий список с клавиатурой, тот же визуал, что у Input. Сброс — «Очистить» в списке или Delete
41
+ - **TaoCombobox** — Select с полем: фильтр по вводу, `allow-create` пишет своё в `v-model`, `create` — чтобы родитель дополнил список
42
+ - **TaoDate** — один день: `YYYY-MM-DD`, без часов и пояса. Календарь как у Select
43
+ - **TaoDateRange** — период: `v-model` — `{ start, end }` или `null`. Первый клик — начало, второй — конец (если конец раньше — меняются местами). Неполный выбор в модель не пишется
44
+ - **TaoFormField** — общие label / hint / error для полей формы
45
+ - **TaoInputNumber** — число с min/max/step и кнопками ± справа. Пустое значение — `null`
46
+ - **TaoQuantity** — количество в корзине: `− | поле | +`, целые штуки, сток, минус на минимуме может убрать строку
47
+ - **TaoTextarea** — многострочное поле с авто-высотой. `maxlength` ограничивает ввод, `count` показывает «введено / максимум»
48
+ - **TaoPinCode** — пин-код из N полей. По умолчанию клик стирает ячейку и все справа (`clear-on="focus"`); `clear-on="input"` заменяет цифру только при вводе. Последняя ячейка снимает фокус и эмитит `complete`
49
+ - **TaoSlider** — слайдер: мышь, тач и стрелки. Опциональный точный ввод по правому клику
50
+ - **TaoFileDrop** — зона загрузки (drag & drop + клик). Список имён под зоной, `v-model` — `File[]`. Очистку всей пачки решает родитель (`clear-request`)
51
+
52
+ ### Отображение данных
53
+
54
+ - **TaoTable** — простая таблица: колонки, empty, loading, сортировка через `v-model:sort`. Не datagrid.
55
+ - **TaoPagination** — страницы с многоточием. Клик по «…» прыгает на `jump` страниц (5 по умолчанию)
56
+ - **TaoEmpty** — пустой список, нет результатов, нет прав
57
+ - **TaoStages** — список стадий: `wait` / `work` / `ok` / `bad`. Не линейный степпер
58
+ - **TaoSkeleton** — плейсхолдер загрузки (text / title / circle / rect)
59
+ - **TaoCounter** — витрина числа с переворотом цифр (не инпут)
60
+ - **TaoCarousel** — лента: целый слайд с `autoplay`, карточка с `peek`, полоса с `per-view`. Стрелки через `controls` или `#prev` / `#next`, точки — `dots`
61
+ - **TaoAvatar** — фото или инициалы, размеры `small` / `medium` / `large`. Точка `dot` и счётчик `count`
62
+ - **TaoTag** — тег/бейдж со статусами (neutral, success, danger, warning, info)
63
+ - **TaoAlert** — инлайн-баннер страницы или ошибки формы (success / error / warning / info)
64
+ - **TaoProgress** — статичная полоса (`progress` в %). Проценты сверху по центру или `showPercentage="right"` справа; слоты `#left` / `#right` — подписи, тогда процент справа в скобках. `false` прячет. Ширина — `minWidth` / `maxWidth`, не диапазон значения
65
+ - **TaoLoader** — анимированный лоадер (четыре точки)
66
+ - **TaoImage** — обёртка над `<img>`: fade-in и плейсхолдер. `lazy` включает нативный `loading="lazy"`
67
+ - **TaoIcon** — обёртка для icon-шрифта (см. раздел «Иконки» ниже)
68
+
69
+ ### Навигация и взаимодействие
70
+
71
+ - **TaoLink** — ссылка, использующая `<NuxtLink>` в Nuxt-проекте и обычный `<a>` вне его — без дополнительной настройки
72
+ - **TaoBreadcrumb** — крошки: последний пункт — текущая страница
73
+ - **TaoDropdownMenu** — выпадающее меню с авто-позиционированием у края экрана
74
+ - **TaoScrollTop** — плавающая кнопка «наверх» с ripple. Появляется при обратном скролле после `boundary`
75
+
76
+ ### Обратная связь
77
+
78
+ - **toast()** — fluent-уведомления: `toast().success().message('Сохранено')`
79
+ - **confirm()** — вопрос с оверлеем: тот же fluent + `await`. Не тост — ждёт ответ, не исчезает сам.
80
+ - **TaoToastViewport** / **TaoConfirmHost** — контейнеры; если их нет в разметке, первый вызов сам монтирует их в `body`
81
+
82
+ ## Соглашения
83
+
84
+ Тоны визуала: `success` | `danger` | `warning` | `info` | `neutral`.
85
+ `error` у Alert, Tag и `toast()` — синоним `danger`. Button уже `danger`.
86
+ Stages — это процесс, не тон: `wait` / `work` / `ok` / `bad` (`danger` принимается как `bad`).
87
+
88
+ Размеры контролов: `small` | `medium` | `large`.
89
+ Avatar понимает и короткие `s` / `m` / `l`. Divider `gap` раньше называл крупный размер `big` — это алиас `large`.
90
+ Container живёт на другой оси: `wide` / `slim` / `ultra-slim`.
91
+
92
+ ## Иконки
93
+
94
+ `TaoIcon` не поставляет собственный набор иконок — он рендерит класс `icon-<name>`
95
+ по соглашению, рассчитанному на подключение внешнего icon-шрифта (например,
96
+ собранного через IcoMoon/Fontello). Чтобы иконки отображались, подключите
97
+ свой шрифт с классами вида `.icon-arrow-up { ... }` в проекте.
98
+
99
+ ## Установка
100
+
101
+ ```bash
102
+ npm install @1337exp/taoui
103
+ ```
104
+
105
+ Или локально из папки:
106
+
107
+ ```bash
108
+ npm install ../path-to-tao-ui
109
+ ```
110
+
111
+ ## Использование
112
+
113
+ ### Подключение в Nuxt 3
114
+
115
+ Создайте плагин `plugins/tao-ui.ts`:
116
+
117
+ ```typescript
118
+ import { defineNuxtPlugin } from '#app'
119
+ import TaoUI from '@1337exp/taoui'
120
+
121
+ export default defineNuxtPlugin((nuxtApp) => {
122
+ nuxtApp.vueApp.use(TaoUI)
123
+ })
124
+ ```
125
+
126
+ Импортируйте стили в `app.vue` или layout:
127
+
128
+ ```vue
129
+ <script setup>
130
+ import '@1337exp/taoui/styles.css'
131
+ </script>
132
+ ```
133
+
134
+ ### Подключение в Vue 3 проекте
135
+
136
+ ```javascript
137
+ // main.js
138
+ import { createApp } from 'vue'
139
+ import App from './App.vue'
140
+ import TaoUI from '@1337exp/taoui'
141
+ import '@1337exp/taoui/styles.css'
142
+
143
+ const app = createApp(App)
144
+ app.use(TaoUI)
145
+ app.mount('#app')
146
+ ```
147
+
148
+ ### Использование отдельных компонентов
149
+
150
+ ```vue
151
+ <script setup>
152
+ import { TaoButton, TaoInput, TaoModal } from '@1337exp/taoui'
153
+ import '@1337exp/taoui/styles.css'
154
+ </script>
155
+
156
+ <template>
157
+ <TaoButton variant="primary">Нажми меня</TaoButton>
158
+ <TaoInput v-model="text" label="Введите текст" />
159
+ <TaoModal v-model="isOpen" title="Заголовок">
160
+ Содержимое модального окна
161
+ </TaoModal>
162
+ </template>
163
+ ```
164
+
165
+ ## Toast
166
+
167
+ Вызов — цепочка в одном тике, `setTimeout(0)` отправляет уведомление.
168
+
169
+ ```javascript
170
+ import { toast } from '@1337exp/taoui'
171
+
172
+ toast().success().message('Сохранено')
173
+ toast().error().title('Сеть').timeout(5000).message('Нет соединения')
174
+ toast.success('Сохранено')
175
+ toast.warning('Черновик', { timeout: 1000 })
176
+ ```
177
+
178
+ Позиция по умолчанию — `topCenter`. Глобально:
179
+
180
+ ```javascript
181
+ toast.defaults({ position: 'bottomRight' })
182
+ toast.byBottomRight()
183
+ ```
184
+
185
+ `question` с оверлеем в kit нет — это confirm, не toast. Кнопки в тосте, если нужны:
186
+
187
+ ```javascript
188
+ toast().info().message('Удалить файл?').action('Удалить', onRemove)
189
+ ```
190
+
191
+ Viewport можно не вставлять: первый вызов сам создаст его. Если нужен явный контроль (тесты, свой layout):
192
+
193
+ ```vue
194
+ <TaoToastViewport />
195
+ ```
196
+
197
+ ## Confirm
198
+
199
+ Вопрос с оверлеем: всплывает по центру и спрашивает. Это не toast — нет прогресс-бара и автозакрытия, есть `await`, Esc и клик по оверлею = отмена.
200
+
201
+ ```javascript
202
+ import { confirm } from '@1337exp/taoui'
203
+
204
+ if (await confirm().title('Удалить файл?').message('Это нельзя отменить').danger()) {
205
+ remove()
206
+ }
207
+
208
+ const ok = await confirm('Выйти без сохранения?', {
209
+ ok: 'Выйти',
210
+ cancel: 'Остаться',
211
+ })
212
+ ```
213
+
214
+ Очередь: второй `confirm()`, пока открыт первый, подождёт. Подписи по умолчанию — «ОК» / «Отмена»:
215
+
216
+ ```javascript
217
+ confirm.defaults({ ok: 'Yes', cancel: 'No' })
218
+ ```
219
+
220
+ ## Формы
221
+
222
+ `TaoFormField` вешает общий label / hint / error и прокидывает `id` во вложенный контрол.
223
+
224
+ ```vue
225
+ <TaoFormField label="Город" hint="Необязательно">
226
+ <TaoSelect v-model="city" :options="cities" />
227
+ </TaoFormField>
228
+
229
+ <TaoSwitch v-model="dark" label="Тёмная тема" />
230
+
231
+ <TaoRadioGroup v-model="plan" legend="Тариф">
232
+ <TaoRadio value="free" label="Free" />
233
+ <TaoRadio value="pro" label="Pro" />
234
+ </TaoRadioGroup>
235
+
236
+ <TaoAlert type="warning" title="Черновик">Сохраните, прежде чем уйти.</TaoAlert>
237
+ ```
238
+
239
+ `TaoSelect` открывается с клавиатуры (стрелки, Enter, Esc), список переворачивается у края экрана. Delete или «Очистить» внизу списка сбрасывает значение в `null`.
240
+
241
+ `TaoCombobox` — то же меню, но с полем. Печатаете — варианты режутся. `allow-create` пишет введённое в `v-model`, если точного совпадения нет. Список сам не меняется: слушайте `create` и добавьте option снаружи.
242
+
243
+ `TaoFormField` также оборачивает `TaoTextarea` и `TaoCheckbox` — id, hint и error прокидываются сами.
244
+
245
+ ## Таблица и пагинация
246
+
247
+ Таблица рендерит то, что передали: сама данные не режет и не сортирует. Это удобно и для серверной выборки, и для клиентской — страница режется снаружи.
248
+
249
+ ```vue
250
+ <TaoTable
251
+ :columns="columns"
252
+ :rows="pageRows"
253
+ v-model:sort="sort"
254
+ empty-text="Пока нет записей"
255
+ >
256
+ <template #cell-status="{ row }">
257
+ <TaoTag :type="row.status === 'active' ? 'success' : 'neutral'">
258
+ {{ row.status }}
259
+ </TaoTag>
260
+ </template>
261
+ </TaoTable>
262
+
263
+ <TaoPagination v-model:page="page" :total="rows.length" :page-size="8" />
264
+ ```
265
+
266
+ ```javascript
267
+ const columns = [
268
+ { key: 'name', label: 'Имя', sortable: true },
269
+ { key: 'city', label: 'Город' },
270
+ { key: 'status', label: 'Статус' },
271
+ ]
272
+ ```
273
+
274
+ Слот `cell-<ключ>` перекрывает ячейку. `loading` показывает оверлей, пустой список — текст или слот `#empty`. Клик по шапке с `sortable` крутит `asc → desc → сброс`.
275
+
276
+ ## Пустые состояния и загрузка
277
+
278
+ ```vue
279
+ <TaoEmpty title="Ничего не нашлось">
280
+ Измените фильтр или сбросьте поиск.
281
+ <template #action>
282
+ <TaoButton variant="secondary">Сбросить</TaoButton>
283
+ </template>
284
+ </TaoEmpty>
285
+
286
+ <TaoSkeleton variant="title" />
287
+ <TaoSkeleton :lines="3" />
288
+ <TaoSkeleton variant="circle" />
289
+
290
+ <TaoStages :items="[
291
+ { key: 'pay', label: 'Оплата', status: 'ok' },
292
+ { key: 'pack', label: 'Сборка', status: 'work' },
293
+ { key: 'ship', label: 'Доставка', status: 'wait' },
294
+ ]" />
295
+ ```
296
+
297
+ ```vue
298
+ <TaoInput v-model="q" placeholder="Найти…">
299
+ <template #prefix>⌕</template>
300
+ </TaoInput>
301
+
302
+ <TaoInputGroup>
303
+ <template #before>https://</template>
304
+ <TaoInput v-model="host" placeholder="example.com" />
305
+ <template #after>
306
+ <TaoButton>Проверить</TaoButton>
307
+ </template>
308
+ </TaoInputGroup>
309
+
310
+ <form @submit.prevent="onSave">
311
+ <TaoFormField label="Email">
312
+ <TaoInput v-model="email" type="email" />
313
+ </TaoFormField>
314
+ <TaoButton type="submit">Сохранить</TaoButton>
315
+ </form>
316
+
317
+ <TaoFormField label="Количество">
318
+ <TaoInputNumber v-model="qty" :min="1" :max="99" />
319
+ </TaoFormField>
320
+
321
+ <TaoQuantity v-model="qty" :max="12" @dec="onDec" />
322
+
323
+ <TaoFormField label="День доставки" hint="В модели всегда YYYY-MM-DD">
324
+ <TaoDate v-model="day" min="2026-09-01" max="2026-09-30" />
325
+ </TaoFormField>
326
+
327
+ <TaoFormField label="Даты поездки">
328
+ <TaoDateRange v-model="stay" min="2026-09-01" max="2026-09-30" />
329
+ </TaoFormField>
330
+
331
+ <TaoCounter :value="score" :max-digits="6" />
332
+
333
+ <TaoCarousel :autoplay="4000" loop dots>
334
+ <article v-for="item in banners" :key="item.id" class="hero">
335
+ <strong>{{ item.title }}</strong>
336
+ <span>{{ item.text }}</span>
337
+ </article>
338
+ </TaoCarousel>
339
+ <TaoCarousel :peek="72" :controls="false" dots>…</TaoCarousel>
340
+ <TaoCarousel v-model="slide" :per-view="5" :peek="28">
341
+ <template #prev></template>
342
+ <template #next="{ go, disabled }">
343
+ <button type="button" :disabled="disabled" @click="go">вперёд</button>
344
+ </template>
345
+ <article v-for="item in products" :key="item.id">…</article>
346
+ </TaoCarousel>
347
+ ```
348
+
349
+ `TaoDrawer` — лист сбоку, не модалка по центру:
350
+
351
+ ```vue
352
+ <TaoDrawer v-model="open" title="Фильтры">
353
+ ...
354
+ </TaoDrawer>
355
+ ```
356
+
357
+ ## Дизайн-токены и темизация
358
+
359
+ Стили библиотеки построены в три слоя. Компоненты **никогда** не обращаются
360
+ к цветам/размерам напрямую — только через переменные из семантического слоя.
361
+ Это и есть механизм темизации: переопределяете 20-30 переменных — меняется
362
+ внешний вид всех компонентов сразу, без правок самих компонентов.
363
+
364
+ ```
365
+ primitives.css → semantic.css → themes/*.css (опционально)
366
+ сырые цвета публичный API готовые темы поверх
367
+ и размеры, темизации: semantic — пример: light
368
+ не трогать вот что менять
369
+ ```
370
+
371
+ ### Слои
372
+
373
+ | Файл | Назначение | Трогать? |
374
+ |---|---|---|
375
+ | `primitives.css` | Сырая палитра, шкалы отступов/радиусов/теней (`--tao-orange-400`, `--tao-space-3`...) | Обычно нет |
376
+ | `semantic.css` | Токены темы по умолчанию (dark): `--tao-color-accent`, `--tao-color-surface`, `--tao-color-danger` и т.д. | **Да — это и есть API темизации** |
377
+ | `themes/light.css` | Пример альтернативной темы через `[data-tao-theme="light"]` | Да, как образец |
378
+ | `base.css` | Глобальный reset (скроллбар, ссылки, box-sizing) | Редко |
379
+
380
+ Все четыре подключаются одним файлом `@1337exp/taoui/styles.css`.
381
+
382
+ ### Как сделать свою тему
383
+
384
+ Тема — это просто переопределение семантических токенов под своим селектором.
385
+ Ничего в компонентах менять не нужно:
386
+
387
+ ```css
388
+ /* my-brand-theme.css */
389
+ [data-tao-theme="my-brand"] {
390
+ --tao-color-accent: #6c5ce7;
391
+ --tao-color-accent-hover: #5a4bd1;
392
+ --tao-color-bg: #0f0f1a;
393
+ --tao-color-surface: #0f0f1a;
394
+ --tao-color-surface-raised: #1a1a2e;
395
+ --tao-color-text: #d6d6e8;
396
+ --tao-color-text-strong: #ffffff;
397
+ /* остальные токены см. semantic.css */
398
+ }
399
+ ```
400
+
401
+ ```vue
402
+ <!-- app.vue -->
403
+ <template>
404
+ <div data-tao-theme="my-brand">
405
+ <NuxtPage />
406
+ </div>
407
+ </template>
408
+ ```
409
+
410
+ Или переключайте тему целиком на `<html>`, чтобы стилизовать модалки/тултипы,
411
+ которые телепортируются в `body`:
412
+
413
+ ```javascript
414
+ document.documentElement.setAttribute('data-tao-theme', 'light')
415
+ ```
416
+
417
+ ### Ключевые группы токенов
418
+
419
+ - `--tao-color-accent*` — акцентный/брендовый цвет (кнопки primary, активные вкладки, ссылки)
420
+ - `--tao-color-on-accent` — текст на акцентном фоне (`--tao-color-text-on-accent` — алиас)
421
+ - `--tao-color-selection`, `--tao-color-selection-text` — выделение текста (`::selection`)
422
+ - `--tao-color-surface*` — фоны контейнеров (обычный / приподнятый / утопленный / hover)
423
+ - `--tao-color-border*` — границы
424
+ - `--tao-color-text*` — текст (обычный / усиленный / приглушённый / disabled)
425
+ - `--tao-color-danger*`, `--tao-color-success*`, `--tao-color-warning*`, `--tao-color-info*` — статусные цвета
426
+ - `--tao-color-input-*` — поля ввода отдельно, т.к. фон/текст полей часто не совпадают с surface/text
427
+ - `--tao-radius-control`, `--tao-radius-panel` — скругления (мелкие контролы vs крупные панели)
428
+ - `--tao-shadow-panel`, `--tao-shadow-overlay` — тени
429
+
430
+ Полный список — в `src/styles/semantic.css`, он же единственный файл,
431
+ который стоит открыть, чтобы понять весь доступный API темизации.
432
+
433
+ ### Если нужна тема с нуля, без primitives
434
+
435
+ ```javascript
436
+ import '@1337exp/taoui/styles/primitives.css' // шкалы можно оставить
437
+ import './my-semantic.css' // свой семантический слой вместо @1337exp/taoui/styles/semantic.css
438
+ import '@1337exp/taoui/styles/base.css'
439
+ ```
440
+
441
+ ## Демонстрация
442
+
443
+ Для просмотра всех компонентов запустите showcase:
444
+
445
+ ```bash
446
+ cd showcase
447
+ npm install
448
+ npm run dev
449
+ ```
450
+
451
+ В showcase есть переключатель темы (dark / light) в шапке — так виден
452
+ эффект переопределения токенов без правок кода компонентов.
453
+
454
+ Showcase смотрит прямо в `src/`, поэтому правки компонентов видны без
455
+ `npm run build`. Сборка `dist/` нужна для публикации и для чужих проектов,
456
+ которые ставят пакет.
457
+
458
+ ## Разработка
459
+
460
+ ```bash
461
+ # Сборка библиотеки
462
+ npm run build
463
+
464
+ # Режим разработки (watch)
465
+ npm run dev
466
+
467
+ # Проверка типов
468
+ npm run type-check
469
+ ```
470
+
471
+ ## Публикация в npm
472
+
473
+ Публикуется только `dist/` (собранная библиотека) и `src/styles/` (сырые
474
+ токены — чтобы можно было подключить `primitives.css` отдельно при
475
+ кастомной теме). Исходники компонентов (`src/components/*.vue`),
476
+ конфиги сборки и `showcase/` в пакет не попадают — они остаются в
477
+ репозитории; чтобы их получить, репозиторий клонируют.
478
+
479
+ `npm publish` **сам** прогоняет проверку типов и полную пересборку перед
480
+ публикацией (хук `prepublishOnly` в `package.json`) — так в реестр не
481
+ уедет пакет со старым или отсутствующим `dist/`. Собирать вручную перед
482
+ `npm publish` не нужно.
483
+
484
+ Проверить, что именно попадёт в пакет, без реальной публикации:
485
+
486
+ ```bash
487
+ npm pack --dry-run
488
+ ```
489
+
490
+ ## Лицензия
491
+
492
+ MIT