@pgcorp/ui-kit 0.8.1 → 0.10.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.
- package/README.md +332 -9
- package/docs/getting-started.md +135 -0
- package/docs/public-api.md +53 -4
- package/docs/rich-document.md +216 -0
- package/docs/scroll-viewport.md +167 -0
- package/docs/theming.md +17 -0
- package/package.json +108 -50
- package/src/components/layout/SAppShell.vue +3 -1
- package/src/components/layout/SDockRegion.vue +5 -3
- package/src/components/layout/SResizeHandle.vue +45 -5
- package/src/components/layout/SWorkbenchPanelShell.vue +4 -2
- package/src/components/shared/_internal/SDialogSurface.css +23 -0
- package/src/components/shared/_internal/SDialogSurface.vue +9 -2
- package/src/components/shared/_internal/SToastItem.css +4 -0
- package/src/components/shared/_internal/SToastItem.vue +44 -8
- package/src/components/shared/_internal/STreeNode.vue +24 -11
- package/src/components/shared/_internal/dialogSurface.ts +7 -1
- package/src/components/shared/complex/SKanbanLane.vue +4 -2
- package/src/components/shared/containers/SCloudChromeRow.vue +3 -2
- package/src/components/shared/containers/SDrawer.css +11 -2
- package/src/components/shared/containers/SDrawer.vue +48 -9
- package/src/components/shared/containers/SModal.vue +5 -2
- package/src/components/shared/containers/SPanel.vue +4 -2
- package/src/components/shared/containers/SPopover.vue +7 -3
- package/src/components/shared/containers/SUiKitLocaleProvider.vue +57 -0
- package/src/components/shared/controls/SButton.css +14 -10
- package/src/components/shared/controls/SCombobox.vue +8 -6
- package/src/components/shared/controls/SComboboxTrigger.css +7 -4
- package/src/components/shared/controls/SComboboxTrigger.vue +5 -2
- package/src/components/shared/controls/SContextToggleButton.vue +6 -4
- package/src/components/shared/controls/SDragHandle.vue +8 -4
- package/src/components/shared/controls/SDropdownMenu.vue +7 -4
- package/src/components/shared/controls/SInputText.css +5 -4
- package/src/components/shared/controls/SListbox.vue +4 -2
- package/src/components/shared/controls/SSelect.vue +4 -2
- package/src/components/shared/controls/STextarea.ts +13 -0
- package/src/components/shared/controls/STextarea.vue +391 -37
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +4 -2
- package/src/components/shared/data-display/SChart.vue +7 -5
- package/src/components/shared/data-display/SChip.vue +3 -1
- package/src/components/shared/data-display/SCodeBlock.css +4 -0
- package/src/components/shared/data-display/SCodeBlock.vue +46 -22
- package/src/components/shared/data-display/SCodeEditor.css +17 -0
- package/src/components/shared/data-display/SCodeEditor.vue +173 -8
- package/src/components/shared/data-display/SCodeSearchPanel.vue +48 -33
- package/src/components/shared/data-display/SCodeWorkspaceEditor.css +13 -0
- package/src/components/shared/data-display/SCodeWorkspaceEditor.vue +355 -0
- package/src/components/shared/data-display/SControlledToastViewport.css +89 -0
- package/src/components/shared/data-display/SControlledToastViewport.vue +205 -0
- package/src/components/shared/data-display/SCopyField.vue +12 -10
- package/src/components/shared/data-display/SDiffViewer.css +122 -0
- package/src/components/shared/data-display/SDiffViewer.vue +499 -0
- package/src/components/shared/data-display/SDocBlock.vue +99 -71
- package/src/components/shared/data-display/SExpandableText.vue +10 -8
- package/src/components/shared/data-display/SJsonTree.vue +4 -2
- package/src/components/shared/data-display/SLinkedSystemsList.vue +4 -2
- package/src/components/shared/data-display/SMessage.vue +6 -6
- package/src/components/shared/data-display/SRichDocument.css +8 -0
- package/src/components/shared/data-display/SRichDocument.vue +65 -0
- package/src/components/shared/data-display/SScrollViewport.css +53 -0
- package/src/components/shared/data-display/SScrollViewport.vue +470 -0
- package/src/components/shared/data-display/STable.vue +38 -19
- package/src/components/shared/data-display/SToastContainer.vue +22 -17
- package/src/components/shared/data-display/SVirtualList.ts +33 -0
- package/src/components/shared/data-display/SVirtualList.vue +321 -36
- package/src/components/shared/data-display/_internal/SCodeTokenLine.css +72 -0
- package/src/components/shared/data-display/_internal/SCodeTokenLine.vue +79 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.css +85 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.vue +282 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.css +6 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.vue +76 -0
- package/src/components/shared/data-display/_internal/SRichMath.css +42 -0
- package/src/components/shared/data-display/_internal/SRichMath.vue +102 -0
- package/src/components/shared/data-display/codeEditor.ts +200 -0
- package/src/components/shared/data-display/codeWorkspace.ts +233 -0
- package/src/components/shared/data-display/diff.ts +541 -0
- package/src/components/shared/data-display/richDocument.ts +876 -0
- package/src/components/shared/data-display/scrollViewport.ts +47 -0
- package/src/components/shared/data-display/toast.ts +194 -0
- package/src/components/shared/database/SDataGrid.vue +7 -4
- package/src/components/shared/database/SSqlEditor.vue +39 -39
- package/src/components/shared/feedback/SAsyncState.vue +3 -1
- package/src/components/shared/graph/SGraphViewport.vue +11 -9
- package/src/components/shared/navigation/SBreadcrumbs.vue +8 -5
- package/src/components/shared/navigation/SCatalogNavigator.vue +26 -20
- package/src/components/shared/navigation/STabList.vue +10 -6
- package/src/components/shared/navigation/STabs.vue +15 -11
- package/src/components/shared/navigation/SWizardSteps.vue +4 -2
- package/src/components/shared/persona/SPersonaProjectPicker.vue +31 -27
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +39 -33
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +8 -6
- package/src/components/shared/persona/_internal/labels.ts +66 -57
- package/src/components/shared/persona/presentation.ts +140 -110
- package/src/composables/useFloatingPosition.ts +39 -2
- package/src/composables/useUiKitLocale.ts +17 -0
- package/src/internal/codeHighlight.ts +110 -0
- package/src/internal/diffEngine.ts +464 -0
- package/src/internal/diffRuntime.ts +189 -0
- package/src/internal/feedbackPresentation.ts +33 -14
- package/src/internal/localeContext.ts +22 -0
- package/src/internal/ownedAttrs.ts +8 -0
- package/src/internal/runtimeContract.ts +11 -9
- package/src/internal/semanticSizing.ts +13 -0
- package/src/locales/catalog.ts +634 -0
- package/src/locales/locale.ts +472 -0
- package/src/locales/messages.generated.ts +1826 -0
- package/src/locales/messages.ts +150 -0
- package/src/stores/useNotifierStore.ts +61 -44
- package/src/styles/tokens.css +23 -2
- package/src/theme.ts +59 -32
- package/src/workers/diff.worker.ts +73 -0
- package/src/workers/diffProtocol.ts +155 -0
- package/src/components/shared/data-display/SToastContainer.css +0 -53
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Безопасные rich documents / Safe rich documents
|
|
2
|
+
|
|
3
|
+
## Назначение / Purpose
|
|
4
|
+
|
|
5
|
+
`SRichDocument` отображает сериализуемый документ по закрытому контракту
|
|
6
|
+
`SRichDocumentV1`. Компонент предназначен для содержимого, которое приходит из
|
|
7
|
+
worker, backend или persisted state и должно содержать типографику, ссылки,
|
|
8
|
+
таблицы, код, формулы, изображения, файлы и типизированные действия.
|
|
9
|
+
|
|
10
|
+
`SRichDocument` renders a serializable document described by the closed
|
|
11
|
+
`SRichDocumentV1` contract. It is intended for content received from a worker,
|
|
12
|
+
backend, or persisted state that needs typography, links, tables, code, math,
|
|
13
|
+
images, files, and typed actions.
|
|
14
|
+
|
|
15
|
+
Markdown и HTML strings относятся к контракту `SDocBlock`. `SRichDocument` не
|
|
16
|
+
принимает raw HTML, VNodes, callbacks, component names или trust flags.
|
|
17
|
+
|
|
18
|
+
Markdown and HTML strings belong to the `SDocBlock` contract. `SRichDocument`
|
|
19
|
+
does not accept raw HTML, VNodes, callbacks, component names, or trust flags.
|
|
20
|
+
|
|
21
|
+
## Импорты / Imports
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import SRichDocument from '@pgcorp/ui-kit/shared/data-display/SRichDocument.vue'
|
|
25
|
+
import {
|
|
26
|
+
SRichDocumentContractError,
|
|
27
|
+
validateRichDocument,
|
|
28
|
+
validateRichDocumentResources,
|
|
29
|
+
type SRichDocumentActionIntent,
|
|
30
|
+
type SRichDocumentResource,
|
|
31
|
+
type SRichDocumentResourceActivate,
|
|
32
|
+
type SRichDocumentV1,
|
|
33
|
+
} from '@pgcorp/ui-kit/shared/data-display/richDocument'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Граница данных / Data boundary
|
|
37
|
+
|
|
38
|
+
Внешний JSON проверяется в месте получения. Валидатор возвращает новый
|
|
39
|
+
типизированный результат только после проверки версии, структуры, полей,
|
|
40
|
+
лимитов, идентификаторов, ссылок на resources и URL policy. Renderer повторяет
|
|
41
|
+
валидацию перед созданием DOM; это защищает прямое использование компонента, но
|
|
42
|
+
не заменяет проверку на application boundary.
|
|
43
|
+
|
|
44
|
+
Validate external JSON at the receiving boundary. The validator returns a
|
|
45
|
+
typed result only after checking the version, structure, fields, limits, IDs,
|
|
46
|
+
resource references, and URL policy. The renderer validates again before
|
|
47
|
+
creating DOM; this protects direct component use but does not replace validation
|
|
48
|
+
at the application boundary.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const document = validateRichDocument(workerMessage.document)
|
|
52
|
+
const resources = validateRichDocumentResources(apiResponse.resources)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`SRichDocumentContractError` содержит стабильный `code`, точный `path` и
|
|
56
|
+
диагностическое сообщение. Не исправляйте payload downstream и не отбрасывайте
|
|
57
|
+
неизвестные nodes: ошибка означает несовместимый или опасный вход.
|
|
58
|
+
|
|
59
|
+
`SRichDocumentContractError` exposes a stable `code`, an exact `path`, and a
|
|
60
|
+
diagnostic message. Do not repair the payload downstream or drop unknown nodes:
|
|
61
|
+
the error represents incompatible or unsafe input.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
try {
|
|
65
|
+
const document = validateRichDocument(payload)
|
|
66
|
+
useDocument(document)
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (error instanceof SRichDocumentContractError) {
|
|
69
|
+
reportContractFailure(error.code, error.path)
|
|
70
|
+
}
|
|
71
|
+
throw error
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Links принимают относительные application paths и `http:`, `https:`, `mailto:`,
|
|
76
|
+
`tel:`. Resource records принимают относительные paths, `http:`, `https:` и
|
|
77
|
+
`blob:`. Скриптовые, data, scheme-relative и неизвестные protocols отклоняются.
|
|
78
|
+
Resources передаются отдельными consumer-owned records и связываются с AST по
|
|
79
|
+
stable ID; UI-kit не читает filesystem и не выполняет URL самостоятельно.
|
|
80
|
+
|
|
81
|
+
Links accept relative application paths plus `http:`, `https:`, `mailto:`, and
|
|
82
|
+
`tel:`. Resource records accept relative paths, `http:`, `https:`, and `blob:`.
|
|
83
|
+
Script, data, scheme-relative, and unknown protocols are rejected. Resources
|
|
84
|
+
are supplied as separate consumer-owned records and joined to the AST by stable
|
|
85
|
+
ID; the UI kit does not read the filesystem or activate URLs itself.
|
|
86
|
+
|
|
87
|
+
## Модель документа / Document model
|
|
88
|
+
|
|
89
|
+
`SRichDocumentV1` содержит `version: 1`, стабильный `id` и readonly `nodes`.
|
|
90
|
+
Закрытая модель поддерживает:
|
|
91
|
+
|
|
92
|
+
`SRichDocumentV1` contains `version: 1`, a stable `id`, and readonly `nodes`.
|
|
93
|
+
The closed model supports:
|
|
94
|
+
|
|
95
|
+
- paragraph, heading, strong, emphasis, inline code и links с optional
|
|
96
|
+
`current`-состоянием;
|
|
97
|
+
- ordered/unordered lists, quote и divider;
|
|
98
|
+
- code blocks с optional language, line numbers и label;
|
|
99
|
+
- inline/block math;
|
|
100
|
+
- semantic tables и callouts;
|
|
101
|
+
- image/file resources по ID;
|
|
102
|
+
- action groups с serializable action IDs.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const document = {
|
|
106
|
+
version: 1,
|
|
107
|
+
id: 'release-notes',
|
|
108
|
+
nodes: [
|
|
109
|
+
{
|
|
110
|
+
type: 'heading',
|
|
111
|
+
level: 1,
|
|
112
|
+
children: [{ type: 'text', value: 'Release notes' }],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'paragraph',
|
|
116
|
+
children: [{
|
|
117
|
+
type: 'link',
|
|
118
|
+
href: '/releases/current',
|
|
119
|
+
current: true,
|
|
120
|
+
children: [{ type: 'text', value: 'Current release' }],
|
|
121
|
+
}],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: 'resource',
|
|
125
|
+
resourceId: 'report',
|
|
126
|
+
label: 'Open report',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
type: 'actionGroup',
|
|
130
|
+
label: 'Review actions',
|
|
131
|
+
actions: [{ type: 'action', id: 'approve', label: 'Approve' }],
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
} as const satisfies SRichDocumentV1
|
|
135
|
+
|
|
136
|
+
const resources = [{
|
|
137
|
+
id: 'report',
|
|
138
|
+
kind: 'file',
|
|
139
|
+
url: '/reports/release.pdf',
|
|
140
|
+
label: 'Release report',
|
|
141
|
+
mediaType: 'application/pdf',
|
|
142
|
+
}] as const satisfies readonly SRichDocumentResource[]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Controlled intents
|
|
146
|
+
|
|
147
|
+
Ссылки, resources и actions не выполняют application effects. Компонент эмитит
|
|
148
|
+
`link-activate`, `resource-activate` и `action` с `documentId`, точным node
|
|
149
|
+
`path` и целевым ID/URL. Consumer проверяет разрешения, выполняет навигацию,
|
|
150
|
+
скачивание или доменное действие.
|
|
151
|
+
|
|
152
|
+
Links, resources, and actions never execute application effects. The component
|
|
153
|
+
emits `link-activate`, `resource-activate`, and `action` with the `documentId`,
|
|
154
|
+
exact node `path`, and target ID or URL. The consumer authorizes and performs
|
|
155
|
+
navigation, download, or domain actions.
|
|
156
|
+
|
|
157
|
+
`current: true` у link-node передаёт каноническому `SLink` состояние текущего
|
|
158
|
+
назначения и публикует `aria-current="page"`. Поле описывает состояние
|
|
159
|
+
навигации, но не запускает переход и не меняет typed intent.
|
|
160
|
+
|
|
161
|
+
`current: true` on a link node delegates the current-destination state to the
|
|
162
|
+
canonical `SLink` and publishes `aria-current="page"`. The field describes
|
|
163
|
+
navigation state without triggering navigation or changing the typed intent.
|
|
164
|
+
|
|
165
|
+
```vue
|
|
166
|
+
<SRichDocument
|
|
167
|
+
:document="document"
|
|
168
|
+
:resources="resources"
|
|
169
|
+
accessible-label="Release notes"
|
|
170
|
+
@link-activate="openLink"
|
|
171
|
+
@resource-activate="openResource"
|
|
172
|
+
@action="runDocumentAction"
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`accessible-label` обязателен и задаёт имя root article. Consumer-owned
|
|
177
|
+
`data-*` разрешены как test/tour hooks; private classes и `data-s-*` не являются
|
|
178
|
+
selector API.
|
|
179
|
+
|
|
180
|
+
`accessible-label` is required and names the root article. Consumer-owned
|
|
181
|
+
`data-*` are supported as test and tour hooks; private classes and `data-s-*`
|
|
182
|
+
are not selector APIs.
|
|
183
|
+
|
|
184
|
+
## Code, math, SSR и производительность
|
|
185
|
+
|
|
186
|
+
Подсветка кода и KaTeX загружаются отдельными lazy chunks. Code block использует
|
|
187
|
+
read-only token presentation, а не CodeMirror editor runtime. KaTeX работает с
|
|
188
|
+
`trust: false` и создаёт HTML+MathML внутри leaf renderer. Невалидная формула
|
|
189
|
+
показывает локализованную диагностируемую ошибку и не превращается в trusted
|
|
190
|
+
markup.
|
|
191
|
+
|
|
192
|
+
Syntax highlighting and KaTeX load as separate lazy chunks. Code blocks use a
|
|
193
|
+
read-only token presentation rather than the CodeMirror editor runtime. KaTeX
|
|
194
|
+
runs with `trust: false` and produces HTML+MathML inside a leaf renderer. An
|
|
195
|
+
invalid formula produces a localized, diagnosable error and never becomes
|
|
196
|
+
trusted markup.
|
|
197
|
+
|
|
198
|
+
SSR выводит стабильный безопасный fallback для code/math. Client runtime после
|
|
199
|
+
mount заменяет его подсвеченным кодом и формулой. Package не использует CDN и
|
|
200
|
+
не выполняет document parser в main-thread component.
|
|
201
|
+
|
|
202
|
+
SSR renders stable safe fallbacks for code and math. After mount, the client
|
|
203
|
+
runtime replaces them with highlighted code and rendered math. The package uses
|
|
204
|
+
no CDN and runs no document parser inside the main-thread component.
|
|
205
|
+
|
|
206
|
+
## Версионирование / Versioning
|
|
207
|
+
|
|
208
|
+
`version` является частью persisted/transport contract. Не добавляйте
|
|
209
|
+
неизвестные поля и не подменяйте версию. Новая несовместимая схема получает
|
|
210
|
+
новую public version и явную migration policy; silent compatibility branches
|
|
211
|
+
не используются.
|
|
212
|
+
|
|
213
|
+
`version` is part of the persisted and transport contract. Do not add unknown
|
|
214
|
+
fields or rewrite the version. A new incompatible schema receives a new public
|
|
215
|
+
version and an explicit migration policy; silent compatibility branches are not
|
|
216
|
+
used.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Область прокрутки / Scroll viewport
|
|
2
|
+
|
|
3
|
+
## Назначение / Purpose
|
|
4
|
+
|
|
5
|
+
`SScrollViewport` является semantic owner прокручиваемой области. Компонент
|
|
6
|
+
задаёт доступное имя, разрешённые оси, размер, focus/keyboard contract,
|
|
7
|
+
overscroll policy и сериализуемое состояние границ. Consumer передаёт контент,
|
|
8
|
+
но не создаёт собственный `overflow: auto` container для этого паттерна.
|
|
9
|
+
|
|
10
|
+
`SScrollViewport` is the semantic owner of a scrollable region. It defines the
|
|
11
|
+
accessible name, enabled axes, size, focus and keyboard contract, overscroll
|
|
12
|
+
policy, and serializable boundary state. Consumers supply content instead of
|
|
13
|
+
creating an application-owned `overflow: auto` container for this pattern.
|
|
14
|
+
|
|
15
|
+
## Импорты / Imports
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import SScrollViewport from '@pgcorp/ui-kit/shared/data-display/SScrollViewport.vue'
|
|
19
|
+
import type {
|
|
20
|
+
SScrollViewportApi,
|
|
21
|
+
SScrollViewportState,
|
|
22
|
+
} from '@pgcorp/ui-kit/shared/data-display/scrollViewport'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop | Contract |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `accessibleLabel` | Обязательное непустое имя `region`. / Required non-empty name of the `region`. |
|
|
30
|
+
| `axis` | `'x' \| 'y' \| 'both'`; default `y`. Запрещённая ось не прокручивается и недоступна API. / A disabled axis neither scrolls nor accepts API commands. |
|
|
31
|
+
| `blockSize` | `'sm' \| 'md' \| 'lg' \| 'fill'`; semantic token, raw CSS length запрещён. / Semantic token; raw CSS lengths are rejected. |
|
|
32
|
+
| `focusable` | Включает root tab stop и `focus()`; default `true`. / Enables the root tab stop and `focus()`; defaults to `true`. |
|
|
33
|
+
| `keyboardNavigation` | Управление корнем с клавиатуры; требует `focusable=true`. / Root keyboard control; requires `focusable=true`. |
|
|
34
|
+
| `overscrollBehavior` | `'auto' \| 'contain' \| 'none'`; default `contain`. |
|
|
35
|
+
|
|
36
|
+
Consumer-owned `data-*` разрешены на root region для tour/test hooks. Internal
|
|
37
|
+
classes и `data-s-*` не являются selector API.
|
|
38
|
+
|
|
39
|
+
Consumer-owned `data-*` are supported on the root region for tour and test
|
|
40
|
+
hooks. Internal classes and `data-s-*` are not selector APIs.
|
|
41
|
+
|
|
42
|
+
## Состояние / State
|
|
43
|
+
|
|
44
|
+
`scroll-state` эмитится после mount, прокрутки, resize viewport/content и
|
|
45
|
+
изменения presentation props. Payload и вложенные записи immutable и
|
|
46
|
+
serializable:
|
|
47
|
+
|
|
48
|
+
`scroll-state` is emitted after mount, scrolling, viewport/content resize, and
|
|
49
|
+
presentation prop changes. The payload and nested records are immutable and
|
|
50
|
+
serializable:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
interface SScrollViewportState {
|
|
54
|
+
readonly physical: {
|
|
55
|
+
readonly x: number
|
|
56
|
+
readonly y: number
|
|
57
|
+
readonly maxX: number
|
|
58
|
+
readonly maxY: number
|
|
59
|
+
}
|
|
60
|
+
readonly semantic: {
|
|
61
|
+
readonly inline: number
|
|
62
|
+
readonly block: number
|
|
63
|
+
readonly maxInline: number
|
|
64
|
+
readonly maxBlock: number
|
|
65
|
+
}
|
|
66
|
+
readonly atInlineStart: boolean
|
|
67
|
+
readonly atInlineEnd: boolean
|
|
68
|
+
readonly atBlockStart: boolean
|
|
69
|
+
readonly atBlockEnd: boolean
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`physical.x` измеряется от физического левого края и нормализован между
|
|
74
|
+
browser RTL scroll models; `physical.y` — от верхнего края. Semantic offsets
|
|
75
|
+
учитывают `direction` и `writing-mode`. Boundary flags используют semantic
|
|
76
|
+
inline/block directions.
|
|
77
|
+
|
|
78
|
+
`physical.x` is measured from the physical left edge and normalized across
|
|
79
|
+
browser RTL scroll models; `physical.y` is measured from the top edge. Semantic
|
|
80
|
+
offsets follow `direction` and `writing-mode`. Boundary flags use semantic
|
|
81
|
+
inline and block directions.
|
|
82
|
+
|
|
83
|
+
## Exposed API
|
|
84
|
+
|
|
85
|
+
Component ref реализует только `SScrollViewportApi`; DOM owner не экспортируется:
|
|
86
|
+
|
|
87
|
+
The component ref implements `SScrollViewportApi` only; its DOM owner is not
|
|
88
|
+
exposed:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
interface SScrollViewportApi {
|
|
92
|
+
focus(): void
|
|
93
|
+
scrollToBoundary(axis: 'x' | 'y', boundary: 'start' | 'end'): void
|
|
94
|
+
reveal(options: { x?: number; y?: number; behavior?: 'auto' | 'smooth' }): void
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`scrollToBoundary` и `reveal` проверяют разрешённые оси. Координаты `reveal`
|
|
99
|
+
задаются в physical left/top model и ограничиваются фактическим scroll range.
|
|
100
|
+
Вызов до mount, обращение к запрещённой оси и пустой reveal payload завершаются
|
|
101
|
+
явной диагностической ошибкой.
|
|
102
|
+
|
|
103
|
+
`scrollToBoundary` and `reveal` validate the enabled axes. `reveal` coordinates
|
|
104
|
+
use the physical left/top model and are clamped to the actual scroll range.
|
|
105
|
+
Calls before mount, commands for a disabled axis, and an empty reveal payload
|
|
106
|
+
fail with explicit diagnostics.
|
|
107
|
+
|
|
108
|
+
## Клавиатура и вложенные controls / Keyboard and nested controls
|
|
109
|
+
|
|
110
|
+
При фокусе на root доступны:
|
|
111
|
+
|
|
112
|
+
When the root owns focus:
|
|
113
|
+
|
|
114
|
+
- `ArrowLeft`/`ArrowRight` — горизонтальная ось;
|
|
115
|
+
- `ArrowUp`/`ArrowDown` — вертикальная ось;
|
|
116
|
+
- `PageUp`/`PageDown` — вертикальная страница, либо горизонтальная при `axis="x"`;
|
|
117
|
+
- `Home`/`End` — начало/конец всех разрешённых осей.
|
|
118
|
+
|
|
119
|
+
Сочетания с `Alt`, `Control`, `Meta` или `Shift` компонент не перехватывает.
|
|
120
|
+
Обработчик действует только когда event target совпадает с root region.
|
|
121
|
+
Вложенные UI-kit inputs, editors, terminals и другие controls сохраняют свои
|
|
122
|
+
клавиши, selection и native copy. Перевод фокуса на вложенный control может
|
|
123
|
+
штатно прокрутить его в видимую область браузером.
|
|
124
|
+
|
|
125
|
+
Combinations with `Alt`, `Control`, `Meta`, or `Shift` are not intercepted. The
|
|
126
|
+
handler runs only when the event target is the root region. Nested UI-kit
|
|
127
|
+
inputs, editors, terminals, and other controls retain their keys, selection,
|
|
128
|
+
and native copy. Moving focus to a nested control may let the browser scroll it
|
|
129
|
+
into view.
|
|
130
|
+
|
|
131
|
+
## Lifecycle
|
|
132
|
+
|
|
133
|
+
Один `ResizeObserver` отслеживает root viewport и content owner. Passive scroll
|
|
134
|
+
listener и observer освобождаются при unmount; callback после teardown не
|
|
135
|
+
публикует state. Компонент не создаёт global listener и не использует polling.
|
|
136
|
+
|
|
137
|
+
One `ResizeObserver` tracks the root viewport and content owner. The passive
|
|
138
|
+
scroll listener and observer are released on unmount, and callbacks after
|
|
139
|
+
teardown publish no state. The component creates no global listener and uses no
|
|
140
|
+
polling.
|
|
141
|
+
|
|
142
|
+
## Пример / Example
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<script setup lang="ts">
|
|
146
|
+
import { ref } from 'vue'
|
|
147
|
+
import SInputText from '@pgcorp/ui-kit/shared/controls/SInputText.vue'
|
|
148
|
+
import SScrollViewport from '@pgcorp/ui-kit/shared/data-display/SScrollViewport.vue'
|
|
149
|
+
import type { SScrollViewportState } from '@pgcorp/ui-kit/shared/data-display/scrollViewport'
|
|
150
|
+
|
|
151
|
+
const query = ref('')
|
|
152
|
+
const position = ref<SScrollViewportState | null>(null)
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<template>
|
|
156
|
+
<SScrollViewport
|
|
157
|
+
accessible-label="Результаты поиска"
|
|
158
|
+
axis="both"
|
|
159
|
+
block-size="lg"
|
|
160
|
+
@scroll-state="position = $event"
|
|
161
|
+
>
|
|
162
|
+
<SInputText v-model="query" label="Поиск" />
|
|
163
|
+
<ResultsMatrix :query="query" />
|
|
164
|
+
</SScrollViewport>
|
|
165
|
+
</template>
|
|
166
|
+
```
|
|
167
|
+
|
package/docs/theming.md
CHANGED
|
@@ -17,6 +17,14 @@ applyThemeIdToDocument(document, 'classic-light')
|
|
|
17
17
|
theme-контейнере. Не стилизуйте внутренний DOM компонентов через `:deep`,
|
|
18
18
|
private selectors или зависимость от структуры template.
|
|
19
19
|
|
|
20
|
+
### Геометрия separator
|
|
21
|
+
|
|
22
|
+
`SResizeHandle` использует `--s-resize-handle-hitbox-compact` (6 px),
|
|
23
|
+
`--s-resize-handle-hitbox-standard` (12 px) и
|
|
24
|
+
`--s-resize-handle-hitbox-spacious` (32 px). Выбирайте ступень через публичный
|
|
25
|
+
prop `hitbox`; переопределяйте токены только как часть общей темы, а не для
|
|
26
|
+
отдельного экземпляра компонента. Толщина визуальной линии от hitbox не зависит.
|
|
27
|
+
|
|
20
28
|
### Интеграция шкалы слоёв host-приложения
|
|
21
29
|
|
|
22
30
|
Floating surfaces (`STooltip`, menu, select, combobox и popover), modal и
|
|
@@ -80,6 +88,15 @@ Override documented CSS custom properties on the root theme container only. Do
|
|
|
80
88
|
not style component internals through `:deep`, private selectors, or template
|
|
81
89
|
structure dependencies.
|
|
82
90
|
|
|
91
|
+
### Separator geometry
|
|
92
|
+
|
|
93
|
+
`SResizeHandle` uses `--s-resize-handle-hitbox-compact` (6 px),
|
|
94
|
+
`--s-resize-handle-hitbox-standard` (12 px), and
|
|
95
|
+
`--s-resize-handle-hitbox-spacious` (32 px). Select a step with the public
|
|
96
|
+
`hitbox` prop. Override the tokens only as part of the host theme, not for an
|
|
97
|
+
individual component instance. The visual divider thickness is independent of
|
|
98
|
+
the hitbox.
|
|
99
|
+
|
|
83
100
|
### Host application layer-scale integration
|
|
84
101
|
|
|
85
102
|
Floating surfaces (`STooltip`, menus, selects, comboboxes, and popovers), modals,
|