@pgcorp/ui-kit 0.7.2 → 0.8.0

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.
Files changed (33) hide show
  1. package/README.md +135 -8
  2. package/docs/accessibility.md +12 -0
  3. package/docs/getting-started.md +32 -2
  4. package/docs/public-api.md +19 -2
  5. package/docs/theming.md +16 -0
  6. package/package.json +18 -3
  7. package/src/components/shared/codeLanguages.ts +2 -107
  8. package/src/components/shared/containers/SPanel.css +32 -1
  9. package/src/components/shared/containers/SPanel.vue +4 -0
  10. package/src/components/shared/controls/SCheckbox.vue +2 -2
  11. package/src/components/shared/controls/SInteractiveSurface.css +4 -0
  12. package/src/components/shared/controls/SInteractiveSurface.vue +1 -1
  13. package/src/components/shared/controls/SListbox.vue +4 -4
  14. package/src/components/shared/controls/SSwitch.vue +2 -2
  15. package/src/components/shared/data-display/SChart.css +149 -0
  16. package/src/components/shared/data-display/SChart.vue +493 -0
  17. package/src/components/shared/data-display/SChip.css +19 -0
  18. package/src/components/shared/data-display/SChip.vue +30 -6
  19. package/src/components/shared/data-display/SCodeBlock.css +68 -0
  20. package/src/components/shared/data-display/SCodeBlock.vue +102 -16
  21. package/src/components/shared/data-display/SMetricCard.css +1 -0
  22. package/src/components/shared/data-display/SMetricCard.vue +1 -0
  23. package/src/components/shared/data-display/STable.vue +120 -145
  24. package/src/components/shared/data-display/chart.ts +54 -0
  25. package/src/components/shared/navigation/STabList.vue +0 -3
  26. package/src/internal/chartGeometry.ts +748 -0
  27. package/src/internal/codeLanguageIdentity.ts +89 -0
  28. package/src/internal/lazyCodeEditor.ts +1 -0
  29. package/src/internal/linkTarget.ts +1 -3
  30. package/src/internal/ownedAttrs.ts +1 -0
  31. package/src/internal/useBinaryInput.ts +9 -2
  32. package/src/styles/tailwind.css +3 -0
  33. package/src/styles/tokens.css +33 -0
package/README.md CHANGED
@@ -46,14 +46,32 @@ owns their single runtime instances.
46
46
 
47
47
  ## Быстрый старт / Quick start
48
48
 
49
- Подключите Pinia, router и базовые стили в entrypoint приложения:
49
+ Создайте корневой CSS entrypoint приложения. Интеграционный stylesheet подключает
50
+ стили и typography plugin, а явный `@source` регистрирует source-SFC пакета:
50
51
 
51
- Register Pinia, the router, and the base stylesheet in the application entrypoint:
52
+ Create the application's root CSS entrypoint. The integration stylesheet includes
53
+ the styles and typography plugin, while an explicit `@source` registers the package SFCs:
54
+
55
+ ```css
56
+ @import "tailwindcss";
57
+ @import "@pgcorp/ui-kit/tailwind.css";
58
+ @source "../node_modules/@pgcorp/ui-kit";
59
+ ```
60
+
61
+ `@source` обязателен: Tailwind 4 не сканирует dependencies автоматически, а путь
62
+ разрешается относительно CSS-файла приложения. Runtime-импорт `reference.css`
63
+ не требуется. / `@source` is required because Tailwind 4 does not scan
64
+ dependencies automatically; the path resolves relative to the application CSS.
65
+ No runtime `reference.css` import is required.
66
+
67
+ Подключите CSS, Pinia и router в entrypoint приложения:
68
+
69
+ Import the CSS, Pinia, and router from the application entrypoint:
52
70
 
53
71
  ```ts
54
72
  import { createPinia } from 'pinia'
55
73
  import { createApp } from 'vue'
56
- import '@pgcorp/ui-kit/style.css'
74
+ import './styles.css'
57
75
 
58
76
  import App from './App.vue'
59
77
  import router from './router'
@@ -102,7 +120,7 @@ monorepo alias or copied UI-kit files.
102
120
  | Layout | App shell, dock regions, stacks, workbench layout | `layout/SAppShell.vue`, `layout/SStack.vue`, `layout/SWorkbenchLayout.vue` |
103
121
  | Controls | Buttons, fields, inputs, switches, selects, comboboxes, menus | `shared/controls/SButton.vue`, `shared/controls/SSwitch.vue`, `shared/controls/SCombobox.vue` |
104
122
  | Containers | Panels, modal, drawer, popover, sidebars | `shared/containers/SPanel.vue`, `shared/containers/SModal.vue`, `shared/containers/SPopover.vue` |
105
- | Data display | Tables, chips, status, progress, code, JSON, tooltips | `shared/data-display/STable.vue`, `shared/data-display/SChip.vue`, `shared/data-display/SStatus.vue` |
123
+ | Data display | Tables, charts, metric cards, chips, status, progress, code, JSON, tooltips | `shared/data-display/STable.vue`, `shared/data-display/SChart.vue`, `shared/data-display/SMetricCard.vue` |
106
124
  | Navigation | Tabs, breadcrumbs, catalog navigation, wizard | `shared/navigation/STabs.vue`, `shared/navigation/SBreadcrumbs.vue`, `shared/navigation/SWizardSteps.vue` |
107
125
  | Complex surfaces | Tree, Kanban, data grid, SQL editor, graph | `shared/complex/STree.vue`, `shared/database/SDataGrid.vue`, `shared/graph/SGraphViewport.vue` |
108
126
  | Runtime | Themes, variants, icons, composables, stores | `theme`, `variants`, `icons`, `composables/useNotifier`, `stores/useNotifierStore` |
@@ -121,6 +139,13 @@ controlled keys и доменный контент, не копирует checkb
121
139
  `STable` owns selection and disclosure columns. Consumers provide controlled
122
140
  keys and domain content instead of copying checkboxes, disclosures, or CSS:
123
141
 
142
+ ```ts
143
+ import type {
144
+ STableColumn,
145
+ STableSort,
146
+ } from '@pgcorp/ui-kit/shared/data-display/table'
147
+ ```
148
+
124
149
  ```vue
125
150
  <STable
126
151
  v-model:selected-row-keys="selectedRowKeys"
@@ -180,6 +205,94 @@ authoritative prop update becomes the base for subsequent actions.
180
205
  />
181
206
  ```
182
207
 
208
+ ### Графики и метрики / Charts and metrics
209
+
210
+ `SChart` является единым semantic owner для line, area, grouped/stacked bar,
211
+ pie и donut. Размеры `compact`, `sm`, `md`, `lg`, оси, сетка, легенда,
212
+ маркеры, подписи, auto/fixed domain и таблица данных задаются typed props.
213
+ Компонент пересчитывает SVG-геометрию по фактическому контейнеру, сохраняет
214
+ читаемую типографику и переносит длинные подписи категорий на узкой ширине.
215
+
216
+ `SChart` is the single semantic owner for line, area, grouped/stacked bar, pie,
217
+ and donut charts. Typed props control `compact`, `sm`, `md`, and `lg` sizes,
218
+ axes, grid, legend, markers, labels, auto/fixed domains, and the data table. The
219
+ component recomputes SVG geometry for its actual container, preserves readable
220
+ typography, and wraps long category labels at narrow widths.
221
+
222
+ ```ts
223
+ import type {
224
+ SChartCategory,
225
+ SChartSeries,
226
+ } from '@pgcorp/ui-kit/shared/data-display/chart'
227
+ ```
228
+
229
+ ```vue
230
+ <SChart
231
+ type="area"
232
+ :categories="categories"
233
+ :series="series"
234
+ :accessibility="{
235
+ mode: 'label',
236
+ label: 'Нагрузка VPN по часам',
237
+ description: 'Входящий и исходящий трафик',
238
+ }"
239
+ x-axis-label="Час"
240
+ y-axis-label="Мбит/с"
241
+ legend="top"
242
+ />
243
+ ```
244
+
245
+ Для компактного тренда `SMetricCard` предоставляет слот `#chart`. Декоративный
246
+ график внутри уже озвученной карточки использует
247
+ `accessibility.mode="decorative"` и `data-table-presentation="none"`;
248
+ содержательный самостоятельный график сохраняет default assistive data table.
249
+
250
+ `SMetricCard` exposes `#chart` for a compact trend. A decorative chart inside an
251
+ already labelled card uses `accessibility.mode="decorative"` with
252
+ `data-table-presentation="none"`; a meaningful standalone chart keeps the
253
+ default assistive data table.
254
+
255
+ ### Действия chip и адаптивный header панели / Chip actions and responsive panel header
256
+
257
+ `SChip` принимает typed `surface` contract для статичного, action и link
258
+ режимов и эмитит `activate`. Закрытие остаётся отдельным sibling-действием, не
259
+ вложенной кнопкой. `SPanel` использует `header-layout="auto"` для переноса
260
+ действий под заголовок по container width; `inline` и `stacked` доступны как
261
+ явные варианты композиции.
262
+
263
+ `SChip` accepts the typed `surface` contract for static, action, and link modes
264
+ and emits `activate`. Closing remains a separate sibling action rather than a
265
+ nested button. `SPanel` uses `header-layout="auto"` to move actions below the
266
+ title based on container width; `inline` and `stacked` remain explicit layout
267
+ options.
268
+
269
+ ### Лёгкий и редактируемый код / Lightweight and editable code
270
+
271
+ `SCodeBlock` загружает CodeMirror отдельным async chunk только для
272
+ `presentation="editor"`. Для read-only текста, логов и конфигурации используйте
273
+ `presentation="plain"`: header, copy action, accessible name, line numbers и
274
+ semantic viewport сохраняются, а editor/language runtime не загружается.
275
+
276
+ `SCodeBlock` loads CodeMirror in a separate async chunk only for
277
+ `presentation="editor"`. Use `presentation="plain"` for read-only text, logs,
278
+ and configuration: the header, copy action, accessible name, line numbers, and
279
+ semantic viewport remain available without loading the editor/language runtime.
280
+
281
+ ```vue
282
+ <SCodeBlock
283
+ :code="diagnostic"
284
+ :language="{ mode: 'explicit', id: 'text' }"
285
+ presentation="plain"
286
+ aria-label="Диагностика подключения"
287
+ />
288
+ ```
289
+
290
+ Plain presentation допускает только `readOnly=true` и явно отклоняет
291
+ editor-only `interceptKeys`, `nextSelectionPos` и `applySelection`.
292
+
293
+ Plain presentation accepts `readOnly=true` only and explicitly rejects the
294
+ editor-only `interceptKeys`, `nextSelectionPos`, and `applySelection` contracts.
295
+
183
296
  ### Подсказка на части действия / Tooltip for part of an action
184
297
 
185
298
  Обычный tooltip использует combined `trigger`. Для пассивного фрагмента внутри
@@ -207,14 +320,24 @@ button and owns ARIA/focus, while `pointerTarget` is applied to a passive
207
320
 
208
321
  ## Темы и стили / Themes and styles
209
322
 
210
- Для обычного приложения подключайте полный style entrypoint один раз:
323
+ Для приложения на Tailwind 4 подключайте integration entrypoint после собственного
324
+ `@import "tailwindcss"`:
211
325
 
212
- For a regular application, import the complete style entrypoint once:
326
+ For a Tailwind 4 application, import the integration entrypoint after the
327
+ application's own `@import "tailwindcss"`:
213
328
 
214
- ```ts
215
- import '@pgcorp/ui-kit/style.css'
329
+ ```css
330
+ @import "tailwindcss";
331
+ @import "@pgcorp/ui-kit/tailwind.css";
332
+ @source "../node_modules/@pgcorp/ui-kit";
216
333
  ```
217
334
 
335
+ `tailwind.css` включает `style.css`, dark variant и required typography plugin.
336
+ `reference.css` предназначен для compile-time `@reference` в component styles.
337
+ / `tailwind.css` includes `style.css`, the dark variant, and the required
338
+ typography plugin. `reference.css` is a compile-time `@reference` target for
339
+ component styles.
340
+
218
341
  Для token-only интеграции доступен отдельный entrypoint:
219
342
 
220
343
  A separate entrypoint is available for token-only integration:
@@ -276,6 +399,10 @@ Do not combine it with manual shifts of individual layer tokens. The complete
276
399
  owner map, dynamic-stack contract, and verification checklist are published in
277
400
  `docs/theming.md` inside the npm package.
278
401
 
402
+ CodeMirror autocomplete in `SCodeEditor` and the overlay `SResizeHandle` stay
403
+ inside their positioned component owners. They intentionally use the local
404
+ `--s-layer-floating` anchor and are not shifted by `--s-layer-host-offset`.
405
+
279
406
  ## Публичный контракт / Public contract
280
407
 
281
408
  `package.json#exports` — источник истины для импортов. Публичный контракт
@@ -13,6 +13,12 @@
13
13
  Consumer обязан передавать человекочитаемые labels и не заменять семантику
14
14
  компонента локальными DOM listeners или CSS.
15
15
 
16
+ `SChart` требует явный `accessibility` contract. Содержательный график получает
17
+ accessible name через `mode: 'label'` или `mode: 'labelledby'` и по умолчанию
18
+ рендерит assistive data table. `mode: 'decorative'` допустим только когда те же
19
+ данные уже выражены ближайшим владельцем; для него задаётся
20
+ `dataTablePresentation: 'none'` либо видимая таблица.
21
+
16
22
  ## English
17
23
 
18
24
  Interactive semantics belong to UI-kit leaf components. They own:
@@ -25,3 +31,9 @@ Interactive semantics belong to UI-kit leaf components. They own:
25
31
 
26
32
  Consumers must provide human-readable labels and must not replace component
27
33
  semantics with local DOM listeners or CSS.
34
+
35
+ `SChart` requires an explicit `accessibility` contract. A meaningful chart gets
36
+ an accessible name through `mode: 'label'` or `mode: 'labelledby'` and renders
37
+ an assistive data table by default. `mode: 'decorative'` is valid only when a
38
+ nearby owner already expresses the same data; it uses
39
+ `dataTablePresentation: 'none'` or a visible table.
@@ -13,11 +13,26 @@ Consumer отвечает за единственные экземпляры `vu
13
13
 
14
14
  ### Подключение
15
15
 
16
+ UI-kit публикует source-SFC и использует Tailwind 4 utilities. Подключите Tailwind
17
+ и package integration entrypoint в корневом CSS приложения:
18
+
19
+ ```css
20
+ @import "tailwindcss";
21
+ @import "@pgcorp/ui-kit/tailwind.css";
22
+ @source "../node_modules/@pgcorp/ui-kit";
23
+ ```
24
+
25
+ `tailwind.css` подключает полные стили UI-kit и typography plugin. Tailwind 4 не
26
+ сканирует зависимости автоматически, поэтому `@source` обязателен и задаётся
27
+ относительно этого CSS-файла. Импорт `reference.css` consumer-у не нужен. Если
28
+ source root приложения задаётся явно, сохраните его на собственном
29
+ `@import "tailwindcss" source("…")`.
30
+
16
31
  ```ts
17
32
  import { createApp } from 'vue'
18
33
  import { createPinia } from 'pinia'
19
34
  import router from './router'
20
- import '@pgcorp/ui-kit/style.css'
35
+ import './styles.css'
21
36
 
22
37
  import App from './App.vue'
23
38
 
@@ -53,11 +68,26 @@ instances.
53
68
 
54
69
  ### Setup
55
70
 
71
+ The UI kit publishes source SFCs and uses Tailwind 4 utilities. Import Tailwind
72
+ and the package integration entrypoint from the application's root CSS:
73
+
74
+ ```css
75
+ @import "tailwindcss";
76
+ @import "@pgcorp/ui-kit/tailwind.css";
77
+ @source "../node_modules/@pgcorp/ui-kit";
78
+ ```
79
+
80
+ `tailwind.css` includes the complete UI-kit styles and enables the typography
81
+ plugin. Tailwind 4 does not scan dependencies automatically, so the `@source`
82
+ line is required and resolves relative to this CSS file. Consumers do not need
83
+ a `reference.css` import. If the application uses an explicit source root, keep
84
+ it on its own `@import "tailwindcss" source("…")`.
85
+
56
86
  ```ts
57
87
  import { createApp } from 'vue'
58
88
  import { createPinia } from 'pinia'
59
89
  import router from './router'
60
- import '@pgcorp/ui-kit/style.css'
90
+ import './styles.css'
61
91
 
62
92
  import App from './App.vue'
63
93
 
@@ -14,7 +14,8 @@
14
14
  - `shared/navigation/*` — tabs, breadcrumbs, catalog navigation и wizard steps;
15
15
  - `shared/database/*`, `shared/graph/*`, `shared/persona/*` — доменные сложные поверхности;
16
16
  - `composables/*`, `stores/*`, `theme`, `variants`, `icons` — typed runtime modules;
17
- - `style.css`, `tokens.css`, `reference.css` документированные style entrypoints.
17
+ - `tailwind.css` — полный Tailwind 4 integration entrypoint;
18
+ - `style.css`, `tokens.css`, `reference.css` — global styles, token-only и compile-time reference entrypoints.
18
19
 
19
20
  Компонентный contract выражается через typed props, slots, events и exposed
20
21
  methods. Внутренний DOM, CSS-классы и private modules не являются API.
@@ -25,6 +26,13 @@ UI-kit-owned атрибуты `data-s-*` относятся к внутренн
25
26
  compiler регистрирует их без межфайлового filesystem type resolver.
26
27
  Публикуемый production-source компилируется с `target/lib: ES2020`, совпадающим
27
28
  с baseline `@vue/tsconfig/tsconfig.dom.json`, и не требует скрытых полифиллов.
29
+ Типы таблицы импортируются из `shared/data-display/table`, типы графиков — из
30
+ `shared/data-display/chart`. `SChart` объединяет line/area/bar/pie/donut,
31
+ responsive geometry, semantic palette и доступную таблицу данных. `SMetricCard`
32
+ принимает компактный график через `#chart`. `SCodeBlock` сохраняет CodeMirror за
33
+ async boundary; `presentation="plain"` является lightweight read-only контрактом
34
+ без загрузки editor runtime. `SChip.surface` владеет action/link семантикой, а
35
+ `SPanel.headerLayout` — inline/stacked/auto компоновкой заголовка и действий.
28
36
 
29
37
  ## English
30
38
 
@@ -40,7 +48,8 @@ Primary groups:
40
48
  - `shared/navigation/*` — tabs, breadcrumbs, catalog navigation, and wizard steps;
41
49
  - `shared/database/*`, `shared/graph/*`, `shared/persona/*` — complex domain surfaces;
42
50
  - `composables/*`, `stores/*`, `theme`, `variants`, `icons` — typed runtime modules;
43
- - `style.css`, `tokens.css`, `reference.css` documented style entrypoints.
51
+ - `tailwind.css` — the complete Tailwind 4 integration entrypoint;
52
+ - `style.css`, `tokens.css`, `reference.css` — global styles, token-only, and compile-time reference entrypoints.
44
53
 
45
54
  Component contracts are expressed through typed props, slots, events, and exposed
46
55
  methods. Internal DOM, CSS classes, and private modules are not API.
@@ -51,3 +60,11 @@ Public source SFCs declare runtime props and events locally, so the consumer Vue
51
60
  compiler registers them without a cross-file filesystem type resolver.
52
61
  Published production source compiles with the `target/lib: ES2020` baseline from
53
62
  `@vue/tsconfig/tsconfig.dom.json` and does not require implicit polyfills.
63
+ Table types are imported from `shared/data-display/table`; chart types are
64
+ imported from `shared/data-display/chart`. `SChart` unifies
65
+ line/area/bar/pie/donut charts, responsive geometry, the semantic palette, and
66
+ an accessible data table. `SMetricCard` accepts a compact chart through
67
+ `#chart`. `SCodeBlock` keeps CodeMirror behind an async boundary;
68
+ `presentation="plain"` is the lightweight read-only contract that does not load
69
+ the editor runtime. `SChip.surface` owns action/link semantics, while
70
+ `SPanel.headerLayout` owns inline/stacked/auto title and action composition.
package/docs/theming.md CHANGED
@@ -55,6 +55,14 @@ drawer телепортируются в `document.body`. `SToastContainer` та
55
55
  интеграции проверьте tooltip/menu/select, toast, drag, modal/drawer и floating
56
56
  surface внутри modal. Не меняйте `z-index` внутренних элементов компонентов.
57
57
 
58
+ Локальные floating-элементы не входят в root-level bridge. Autocomplete-tooltip
59
+ `SCodeEditor` остаётся потомком `.cm-editor`, а overlay-вариант
60
+ `SResizeHandle` — абсолютным потомком своего positioned owner. Они используют
61
+ `--s-layer-floating` внутри локального stacking context и не получают
62
+ `--s-layer-host-offset`. Конфигурация CodeMirror с переносом tooltip в
63
+ `document.body` не входит в публичный контракт `SCodeEditor`; root-level surface
64
+ обязана использовать зарегистрированный root layer UI-kit.
65
+
58
66
  ## English
59
67
 
60
68
  `tokens.css` contains primitive and semantic tokens. `style.css` includes tokens,
@@ -108,3 +116,11 @@ sticky, affordance, and navigation anchors are not shifted.
108
116
  `--s-layer-host-offset` with manual shifts of individual UI-kit layer tokens.
109
117
  Verify tooltip/menu/select, toast, drag, modal/drawer, and a floating surface
110
118
  inside a modal. Do not override internal component `z-index` values.
119
+
120
+ Local floating elements are outside the root-level bridge. The `SCodeEditor`
121
+ autocomplete tooltip remains a descendant of `.cm-editor`, and the overlay
122
+ `SResizeHandle` remains an absolutely positioned child of its positioned owner.
123
+ They use `--s-layer-floating` inside a local stacking context and do not receive
124
+ `--s-layer-host-offset`. A CodeMirror configuration that reparents tooltips into
125
+ `document.body` is outside the public `SCodeEditor` contract; a root-level
126
+ surface must use a registered UI-kit root layer.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgcorp/ui-kit",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Typed Vue 3 design system with accessible components, semantic themes, and workbench patterns.",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
@@ -270,6 +270,9 @@
270
270
  "shared/controls/STextarea.vue": [
271
271
  "src/components/shared/controls/STextarea.vue"
272
272
  ],
273
+ "shared/data-display/chart": [
274
+ "src/components/shared/data-display/chart.ts"
275
+ ],
273
276
  "shared/data-display/json": [
274
277
  "src/components/shared/data-display/json.ts"
275
278
  ],
@@ -285,6 +288,9 @@
285
288
  "shared/data-display/SBadge.vue": [
286
289
  "src/components/shared/data-display/SBadge.vue"
287
290
  ],
291
+ "shared/data-display/SChart.vue": [
292
+ "src/components/shared/data-display/SChart.vue"
293
+ ],
288
294
  "shared/data-display/SChip.vue": [
289
295
  "src/components/shared/data-display/SChip.vue"
290
296
  ],
@@ -357,6 +363,9 @@
357
363
  "shared/data-display/SVirtualList.vue": [
358
364
  "src/components/shared/data-display/SVirtualList.vue"
359
365
  ],
366
+ "shared/data-display/table": [
367
+ "src/components/shared/data-display/table.ts"
368
+ ],
360
369
  "shared/database/SDataGrid.vue": [
361
370
  "src/components/shared/database/SDataGrid.vue"
362
371
  ],
@@ -428,7 +437,8 @@
428
437
  "sideEffects": [
429
438
  "./src/styles/style.css",
430
439
  "./src/styles/tokens.css",
431
- "./src/styles/reference.css"
440
+ "./src/styles/reference.css",
441
+ "./src/styles/tailwind.css"
432
442
  ],
433
443
  "exports": {
434
444
  ".": "./index.ts",
@@ -501,11 +511,13 @@
501
511
  "./shared/controls/SSelect.vue": "./src/components/shared/controls/SSelect.vue",
502
512
  "./shared/controls/SSwitch.vue": "./src/components/shared/controls/SSwitch.vue",
503
513
  "./shared/controls/STextarea.vue": "./src/components/shared/controls/STextarea.vue",
514
+ "./shared/data-display/chart": "./src/components/shared/data-display/chart.ts",
504
515
  "./shared/data-display/json": "./src/components/shared/data-display/json.ts",
505
516
  "./shared/data-display/SActionCard.vue": "./src/components/shared/data-display/SActionCard.vue",
506
517
  "./shared/data-display/SActionList.vue": "./src/components/shared/data-display/SActionList.vue",
507
518
  "./shared/data-display/SActionListItem.vue": "./src/components/shared/data-display/SActionListItem.vue",
508
519
  "./shared/data-display/SBadge.vue": "./src/components/shared/data-display/SBadge.vue",
520
+ "./shared/data-display/SChart.vue": "./src/components/shared/data-display/SChart.vue",
509
521
  "./shared/data-display/SChip.vue": "./src/components/shared/data-display/SChip.vue",
510
522
  "./shared/data-display/SCodeBlock.vue": "./src/components/shared/data-display/SCodeBlock.vue",
511
523
  "./shared/data-display/SCodeEditor.vue": "./src/components/shared/data-display/SCodeEditor.vue",
@@ -530,6 +542,7 @@
530
542
  "./shared/data-display/STooltipTarget.vue": "./src/components/shared/data-display/STooltipTarget.vue",
531
543
  "./shared/data-display/SVirtualList": "./src/components/shared/data-display/SVirtualList.ts",
532
544
  "./shared/data-display/SVirtualList.vue": "./src/components/shared/data-display/SVirtualList.vue",
545
+ "./shared/data-display/table": "./src/components/shared/data-display/table.ts",
533
546
  "./shared/database/SDataGrid.vue": "./src/components/shared/database/SDataGrid.vue",
534
547
  "./shared/database/SSqlEditor.vue": "./src/components/shared/database/SSqlEditor.vue",
535
548
  "./shared/feedback/SAsyncState.vue": "./src/components/shared/feedback/SAsyncState.vue",
@@ -551,6 +564,7 @@
551
564
  "./shared/persona/types": "./src/components/shared/persona/types.ts",
552
565
  "./stores/useNotifierStore": "./src/stores/useNotifierStore.ts",
553
566
  "./style.css": "./src/styles/style.css",
567
+ "./tailwind.css": "./src/styles/tailwind.css",
554
568
  "./theme": "./src/theme.ts",
555
569
  "./tokens.css": "./src/styles/tokens.css",
556
570
  "./variants": "./src/variants.ts"
@@ -575,6 +589,7 @@
575
589
  "graphology": "^0.26.0",
576
590
  "highlight.js": "^11.11.1",
577
591
  "@lucide/vue": "^1.16.0",
592
+ "@tailwindcss/typography": "^0.5.19",
578
593
  "marked": "^17.0.3",
579
594
  "sigma": "^3.0.3",
580
595
  "tailwindcss": "^4.2.0",
@@ -583,7 +598,7 @@
583
598
  },
584
599
  "devDependencies": {
585
600
  "@storybook/vue3-vite": "10.5.3",
586
- "@tailwindcss/typography": "^0.5.19",
601
+ "@tailwindcss/postcss": "^4.2.0",
587
602
  "@types/node": "^25.3.0",
588
603
  "@vitejs/plugin-vue": "^6.0.4",
589
604
  "@vitest/coverage-v8": "^4.0.18",
@@ -7,60 +7,10 @@ import { nginx } from '@codemirror/legacy-modes/mode/nginx'
7
7
  import { shell } from '@codemirror/legacy-modes/mode/shell'
8
8
  import {
9
9
  type CodeEditorLanguageContract,
10
- validateCodeEditorLanguage,
11
10
  } from '../../internal/codeEditorContract'
11
+ import { resolveCodeLanguageKey } from '../../internal/codeLanguageIdentity'
12
12
 
13
- type NullableText = string | null | undefined
14
-
15
- const SPECIAL_FILENAMES: Record<string, string> = {
16
- 'cmakelists.txt': 'cmake',
17
- 'dockerfile': 'dockerfile',
18
- 'docker-compose.yml': 'yaml',
19
- 'docker-compose.yaml': 'yaml',
20
- 'compose.yml': 'yaml',
21
- 'compose.yaml': 'yaml',
22
- '.env': 'properties',
23
- '.env.example': 'properties',
24
- '.env.local': 'properties',
25
- '.env.development': 'properties',
26
- '.env.production': 'properties',
27
- 'makefile': 'makefile',
28
- 'nginx.conf': 'nginx',
29
- }
30
-
31
- const LANGUAGE_ALIASES: Record<string, string> = {
32
- bash: 'shell',
33
- 'c#': 'cs',
34
- csharp: 'cs',
35
- env: 'properties',
36
- fish: 'shell',
37
- handlebars: 'handlebars',
38
- htm: 'html',
39
- js: 'javascript',
40
- jsonc: 'json',
41
- json5: 'json',
42
- log: 'text',
43
- make: 'makefile',
44
- md: 'markdown',
45
- mk: 'makefile',
46
- plaintext: 'text',
47
- proto3: 'proto',
48
- ps1: 'powershell',
49
- psm1: 'powershell',
50
- psd1: 'powershell',
51
- py: 'python',
52
- pyw: 'python',
53
- pyi: 'python',
54
- rbw: 'ruby',
55
- sh: 'shell',
56
- shellsession: 'shell',
57
- ts: 'typescript',
58
- text: 'text',
59
- txt: 'text',
60
- yaml: 'yaml',
61
- yml: 'yaml',
62
- zsh: 'shell',
63
- }
13
+ export { resolveCodeLanguageKey } from '../../internal/codeLanguageIdentity'
64
14
 
65
15
  const LEGACY_CODEMIRROR_LANGUAGES: Record<string, Extension> = {
66
16
  dockerfile: StreamLanguage.define(dockerFile),
@@ -88,65 +38,10 @@ type SupportedCodeMirrorLanguage = Parameters<typeof loadLanguage>[0]
88
38
 
89
39
  const SUPPORTED_CODEMIRROR_LANGUAGES = new Set<SupportedCodeMirrorLanguage>(langNames)
90
40
 
91
- function normalizeDetectedToken(value: NullableText): string | null {
92
- if (!value) {
93
- return null
94
- }
95
-
96
- const normalized = value.trim().toLowerCase().replace(/^\./, '')
97
- if (!normalized) {
98
- return null
99
- }
100
-
101
- return LANGUAGE_ALIASES[normalized] ?? normalized
102
- }
103
-
104
- function resolveLanguageFromFilePath(filePath: NullableText): string | null {
105
- if (!filePath) {
106
- return null
107
- }
108
-
109
- const normalizedPath = filePath.trim().toLowerCase()
110
- if (!normalizedPath) {
111
- return null
112
- }
113
-
114
- const basename = normalizedPath.split('/').pop() ?? normalizedPath
115
- if (SPECIAL_FILENAMES[basename]) {
116
- return SPECIAL_FILENAMES[basename]
117
- }
118
-
119
- if (basename.startsWith('dockerfile.')) {
120
- return 'dockerfile'
121
- }
122
-
123
- if (!basename.includes('.')) {
124
- return null
125
- }
126
-
127
- const extension = basename.split('.').pop()
128
- return normalizeDetectedToken(extension)
129
- }
130
-
131
41
  function isSupportedCodeMirrorLanguage(value: string): value is SupportedCodeMirrorLanguage {
132
42
  return SUPPORTED_CODEMIRROR_LANGUAGES.has(value as SupportedCodeMirrorLanguage)
133
43
  }
134
44
 
135
- export function resolveCodeLanguageKey(input: CodeEditorLanguageContract): string {
136
- const language = validateCodeEditorLanguage('codeLanguages', input)
137
- if (language.mode === 'explicit') {
138
- return LANGUAGE_ALIASES[language.id] ?? language.id
139
- }
140
- const detected = resolveLanguageFromFilePath(language.filePath)
141
- if (!detected) {
142
- throw new RangeError(
143
- `codeLanguages: невозможно определить язык по filePath ${JSON.stringify(language.filePath)}. `
144
- + `/ codeLanguages: cannot detect a language from filePath ${JSON.stringify(language.filePath)}.`,
145
- )
146
- }
147
- return detected
148
- }
149
-
150
45
  export function getCodeMirrorLanguageExtension(input: CodeEditorLanguageContract): Extension | null {
151
46
  const languageKey = resolveCodeLanguageKey(input)
152
47
 
@@ -4,6 +4,7 @@
4
4
  @apply relative flex flex-col rounded-lg border bg-surface-0 dark:bg-surface-800 border-surface-200 dark:border-surface-700;
5
5
  @apply text-surface-700 dark:text-surface-300;
6
6
  @apply min-h-0 min-w-0 w-full flex-none; /* предотвращаем горизонтальное распирание, панель заполняет доступную ширину; flex-none исключает сжатие панелей в flex-контейнерах */
7
+ container-type: inline-size;
7
8
  }
8
9
 
9
10
  .s-panel[data-grow='true'] {
@@ -70,10 +71,40 @@
70
71
  }
71
72
 
72
73
  .s-panel-header-right {
73
- @apply flex items-center min-w-0 shrink-0;
74
+ @apply flex min-w-0 shrink-0 flex-wrap items-center;
74
75
  gap: var(--s-space-content-gap);
75
76
  }
76
77
 
78
+ .s-panel-header[data-header-layout='stacked'] {
79
+ @apply flex-col items-stretch;
80
+ gap: var(--s-space-content-gap);
81
+ }
82
+
83
+ .s-panel-header[data-header-layout='stacked'] .s-panel-header-right {
84
+ @apply w-full shrink justify-start;
85
+ }
86
+
87
+ .s-panel-header[data-header-layout='stacked'] .s-panel-title {
88
+ @apply whitespace-normal;
89
+ overflow-wrap: anywhere;
90
+ }
91
+
92
+ @container (max-width: 30rem) {
93
+ .s-panel-header[data-header-layout='auto']:has(.s-panel-header-right > *) {
94
+ @apply flex-col items-stretch;
95
+ gap: var(--s-space-content-gap);
96
+ }
97
+
98
+ .s-panel-header[data-header-layout='auto']:has(.s-panel-header-right > *) .s-panel-header-right {
99
+ @apply w-full shrink justify-start;
100
+ }
101
+
102
+ .s-panel-header[data-header-layout='auto']:has(.s-panel-header-right > *) .s-panel-title {
103
+ @apply whitespace-normal;
104
+ overflow-wrap: anywhere;
105
+ }
106
+ }
107
+
77
108
  .s-panel-content {
78
109
  /* Базово без внутренних скроллов; прокрутка включается только для grow-панелей */
79
110
  @apply min-h-0 min-w-0 w-full; /* безопасная ширина для любых сеток/инпутов внутри */
@@ -18,6 +18,7 @@
18
18
  <div
19
19
  v-if="title || $slots.actions || $slots.header"
20
20
  class="s-panel-header"
21
+ :data-header-layout="headerLayout"
21
22
  >
22
23
  <div class="s-panel-header-left">
23
24
  <slot name="header">
@@ -119,6 +120,8 @@ export interface Props {
119
120
  surfaceOverflow?: 'visible' | 'clip'
120
121
  /** DOM id общего footer/status region. / DOM id of the shared footer/status region. */
121
122
  footerId?: string
123
+ /** Адаптация heading/actions: auto складывает actions под heading при нехватке места. / Heading/actions adaptation: auto stacks actions below the heading when space is constrained. */
124
+ headerLayout?: 'auto' | 'inline' | 'stacked'
122
125
  }
123
126
 
124
127
  const props = withDefaults(defineProps<Props>(), {
@@ -141,6 +144,7 @@ const props = withDefaults(defineProps<Props>(), {
141
144
  surfaceElevation: 'flat',
142
145
  surfaceOverflow: 'visible',
143
146
  footerId: undefined,
147
+ headerLayout: 'auto',
144
148
  })
145
149
 
146
150
  const slots = useSlots()
@@ -13,7 +13,7 @@
13
13
  <template #default="field">
14
14
  <span class="s-checkbox-control">
15
15
  <input
16
- ref="inputEl"
16
+ :ref="setInputElement"
17
17
  v-bind="ownedAttrs.bindings()"
18
18
  :id="field.controlId"
19
19
  type="checkbox"
@@ -84,10 +84,10 @@ const ownedAttrs = useOwnedAttrs({ component: 'SCheckbox', owner: 'native checkb
84
84
  useInteractiveLeafRegistration({ owner: 'SCheckbox' })
85
85
  const {
86
86
  checkedValue,
87
- inputEl,
88
87
  inputId,
89
88
  onChange,
90
89
  onClick,
90
+ setInputElement,
91
91
  } = useBinaryInput({
92
92
  owner: 'SCheckbox',
93
93
  inputName: 'checkbox',