@pgcorp/ui-kit 0.14.0 → 0.15.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 +330 -12
- package/dist/assets/src/components/layout/{SResizeHandle.vue_vue_type_style_index_0_scoped_13b9b27a_lang.css → SResizeHandle.vue_vue_type_style_index_0_scoped_dc65aba1_lang.css} +1 -1
- package/dist/assets/src/components/shared/_internal/SDialogSurface.css_vue_type_style_index_0_src_a108e8f1_scoped_a108e8f1_lang.css +2 -0
- package/dist/assets/src/components/shared/containers/{SCollapsiblePanel.css_vue_type_style_index_0_src_71824088_scoped_71824088_lang.css → SCollapsiblePanel.css_vue_type_style_index_0_src_db85261d_scoped_db85261d_lang.css} +1 -1
- package/dist/assets/src/components/shared/containers/{SOverlayHost.css_vue_type_style_index_0_src_8f9be94e_scoped_8f9be94e_lang.css → SOverlayHost.css_vue_type_style_index_0_src_09215628_scoped_09215628_lang.css} +1 -1
- package/dist/assets/src/components/shared/containers/{SPopover.css_vue_type_style_index_0_src_4091d007_scoped_4091d007_lang.css → SPopover.css_vue_type_style_index_0_src_a4f1f64a_scoped_a4f1f64a_lang.css} +1 -1
- package/dist/assets/src/components/shared/containers/SSidebarSection.css_vue_type_style_index_0_src_52396e42_scoped_52396e42_lang.css +2 -0
- package/dist/assets/src/components/shared/controls/SButton.css_vue_type_style_index_0_src_7a5acda9_scoped_7a5acda9_lang.css +1 -1
- package/dist/assets/src/components/shared/controls/SCheckbox.css_vue_type_style_index_0_src_2562f916_scoped_2562f916_lang.css +2 -0
- package/dist/assets/src/components/shared/controls/SCombobox.css_vue_type_style_index_0_src_d630c922_scoped_d630c922_lang.css +2 -0
- package/dist/assets/src/components/shared/controls/SContextMenu.css_vue_type_style_index_0_src_43b6e378_scoped_43b6e378_lang.css +1 -0
- package/dist/assets/src/components/shared/controls/{SDropdownMenu.css_vue_type_style_index_0_src_857e2649_scoped_857e2649_lang.css → SDropdownMenu.css_vue_type_style_index_0_src_40040ae5_scoped_40040ae5_lang.css} +1 -1
- package/dist/assets/src/components/shared/controls/{SMenuSurface.css_vue_type_style_index_0_src_84109ad9_scoped_84109ad9_lang.css → SMenuSurface.css_vue_type_style_index_0_src_d2b7d71b_scoped_d2b7d71b_lang.css} +1 -1
- package/dist/assets/src/components/shared/controls/SSelect.css_vue_type_style_index_0_src_d1ead6c0_scoped_d1ead6c0_lang.css +2 -0
- package/dist/assets/src/components/shared/data-display/SControlledToastViewport.css_vue_type_style_index_0_src_a69aa84b_scoped_a69aa84b_lang.css +2 -0
- package/dist/assets/src/components/shared/data-display/SScrollViewport.css_vue_type_style_index_0_src_d94fcf8c_scoped_d94fcf8c_lang.css +2 -0
- package/dist/assets/src/components/shared/data-display/STooltip.css_vue_type_style_index_0_src_true_lang.css +1 -1
- package/dist/assets/src/components/shared/data-display/_internal/STableFrame.css_vue_type_style_index_0_src_914bad28_scoped_914bad28_lang.css +2 -0
- package/dist/assets/src/components/shared/navigation/{SBottomNav.vue_vue_type_style_index_0_scoped_24be0f2b_lang.css → SBottomNav.vue_vue_type_style_index_0_scoped_68cf84b3_lang.css} +1 -1
- package/dist/src/components/layout/SResizeHandle.vue.js +2 -2
- package/dist/src/components/layout/SResizeHandle.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/src/components/shared/_internal/SDialogSurface.vue.d.ts +9 -1
- package/dist/src/components/shared/_internal/SDialogSurface.vue.js +2 -2
- package/dist/src/components/shared/_internal/SDialogSurface.vue_vue_type_script_setup_true_lang.js +174 -82
- package/dist/src/components/shared/_internal/dialogSurface.d.ts +2 -0
- package/dist/src/components/shared/_internal/dialogSurface.js +30 -27
- package/dist/src/components/shared/_internal/dialogSurfaceMotion.d.ts +14 -0
- package/dist/src/components/shared/_internal/dialogSurfaceMotion.js +84 -0
- package/dist/src/components/shared/_internal/focusTrap.js +15 -13
- package/dist/src/components/shared/containers/SCollapsiblePanel.vue.d.ts +5 -0
- package/dist/src/components/shared/containers/SCollapsiblePanel.vue.js +2 -2
- package/dist/src/components/shared/containers/SCollapsiblePanel.vue_vue_type_script_setup_true_lang.js +123 -121
- package/dist/src/components/shared/containers/SDrawer.vue.d.ts +4 -4
- package/dist/src/components/shared/containers/SDrawer.vue.js +3 -6
- package/dist/src/components/shared/containers/SDrawer.vue_vue_type_script_setup_true_lang.js +104 -128
- package/dist/src/components/shared/containers/SModal.vue.d.ts +8 -8
- package/dist/src/components/shared/containers/SModal.vue.js +3 -6
- package/dist/src/components/shared/containers/SModal.vue_vue_type_script_setup_true_lang.js +87 -103
- package/dist/src/components/shared/containers/SOverlayHost.vue.js +2 -2
- package/dist/src/components/shared/containers/SOverlayHost.vue_vue_type_script_setup_true_lang.js +32 -31
- package/dist/src/components/shared/containers/SPanel.vue.d.ts +1 -1
- package/dist/src/components/shared/containers/SPopover.vue.js +2 -2
- package/dist/src/components/shared/containers/SPopover.vue_vue_type_script_setup_true_lang.js +225 -200
- package/dist/src/components/shared/containers/SSidebarSection.vue.js +2 -2
- package/dist/src/components/shared/containers/SSidebarSection.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/src/components/shared/controls/SCheckbox.vue.js +2 -2
- package/dist/src/components/shared/controls/SCheckbox.vue_vue_type_script_setup_true_lang.js +13 -7
- package/dist/src/components/shared/controls/SCombobox.vue.js +2 -2
- package/dist/src/components/shared/controls/SCombobox.vue_vue_type_script_setup_true_lang.js +104 -94
- package/dist/src/components/shared/controls/SContextMenu.vue.js +2 -2
- package/dist/src/components/shared/controls/SContextMenu.vue_vue_type_script_setup_true_lang.js +86 -81
- package/dist/src/components/shared/controls/SDropdownMenu.vue.js +2 -2
- package/dist/src/components/shared/controls/SDropdownMenu.vue_vue_type_script_setup_true_lang.js +77 -72
- package/dist/src/components/shared/controls/SMenuSurface.vue.js +2 -2
- package/dist/src/components/shared/controls/SMenuSurface.vue_vue_type_script_setup_true_lang.js +81 -77
- package/dist/src/components/shared/controls/SSelect.vue.js +2 -2
- package/dist/src/components/shared/controls/SSelect.vue_vue_type_script_setup_true_lang.js +85 -77
- package/dist/src/components/shared/data-display/SControlledToastViewport.vue.js +2 -2
- package/dist/src/components/shared/data-display/SControlledToastViewport.vue_vue_type_script_setup_true_lang.js +58 -49
- package/dist/src/components/shared/data-display/SScrollViewport.vue.d.ts +10 -5
- package/dist/src/components/shared/data-display/SScrollViewport.vue.js +2 -2
- package/dist/src/components/shared/data-display/SScrollViewport.vue_vue_type_script_setup_true_lang.js +162 -111
- package/dist/src/components/shared/data-display/STooltip.vue_vue_type_script_setup_true_lang.js +276 -224
- package/dist/src/components/shared/data-display/_internal/STableFrame.vue.d.ts +3 -3
- package/dist/src/components/shared/data-display/_internal/STableFrame.vue.js +2 -2
- package/dist/src/components/shared/data-display/_internal/STableFrame.vue_vue_type_script_setup_true_lang.js +62 -46
- package/dist/src/components/shared/data-display/scrollViewport.d.ts +2 -0
- package/dist/src/components/shared/navigation/SBottomNav.vue.js +2 -2
- package/dist/src/components/shared/navigation/SBottomNav.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/src/components/shared/navigation/STabList.vue.d.ts +1 -1
- package/dist/src/composables/useDialogOverlay.d.ts +12 -2
- package/dist/src/composables/useDialogOverlay.js +192 -119
- package/dist/src/internal/flatTree.d.ts +8 -0
- package/dist/src/internal/flatTree.js +23 -0
- package/dist/src/internal/focusNavigation.d.ts +3 -0
- package/dist/src/internal/focusNavigation.js +28 -15
- package/dist/src/internal/inertLease.d.ts +8 -0
- package/dist/src/internal/inertLease.js +33 -0
- package/dist/src/internal/layerStack.d.ts +40 -0
- package/dist/src/internal/layerStack.js +301 -94
- package/dist/src/internal/nativeFocus.d.ts +4 -0
- package/dist/src/internal/nativeFocus.js +12 -0
- package/dist/src/internal/overlayPortal.d.ts +17 -0
- package/dist/src/internal/overlayPortal.js +23 -0
- package/dist/src/internal/semanticSizing.d.ts +2 -2
- package/dist/src/internal/semanticSizing.js +2 -1
- package/dist/src/internal/useMenuLayer.d.ts +1 -0
- package/dist/src/internal/useMenuLayer.js +63 -57
- package/dist/src/internal/viewportModality.d.ts +27 -0
- package/dist/src/internal/viewportModality.js +132 -0
- package/dist/typography.js +17 -62
- package/docs/overlay-lifecycle.md +195 -0
- package/docs/public-api.md +141 -4
- package/docs/resize.md +34 -5
- package/docs/scroll-viewport.md +30 -10
- package/docs/theming.md +42 -8
- package/package.json +1 -1
- package/src/components/shared/_internal/SDialogSurface.css +185 -40
- package/src/components/shared/controls/SButton.css +0 -3
- package/src/components/shared/controls/SCheckbox.css +19 -7
- package/src/components/shared/data-display/SControlledToastViewport.css +2 -10
- package/src/components/shared/data-display/SScrollViewport.css +17 -1
- package/src/components/shared/data-display/STooltip.css +1 -1
- package/src/components/shared/data-display/_internal/STableFrame.css +1 -5
- package/dist/assets/src/components/shared/_internal/SDialogSurface.css_vue_type_style_index_0_src_0e58afdc_scoped_0e58afdc_lang.css +0 -2
- package/dist/assets/src/components/shared/containers/SDrawer.css_vue_type_style_index_0_src_61cf20dd_scoped_61cf20dd_lang.css +0 -2
- package/dist/assets/src/components/shared/containers/SModal.css_vue_type_style_index_0_src_42e1bd5f_scoped_42e1bd5f_lang.css +0 -2
- package/dist/assets/src/components/shared/containers/SSidebarSection.css_vue_type_style_index_0_src_21f71ae8_scoped_21f71ae8_lang.css +0 -2
- package/dist/assets/src/components/shared/controls/SCheckbox.css_vue_type_style_index_0_src_55d4826e_scoped_55d4826e_lang.css +0 -2
- package/dist/assets/src/components/shared/controls/SCombobox.css_vue_type_style_index_0_src_537f5744_scoped_537f5744_lang.css +0 -2
- package/dist/assets/src/components/shared/controls/SContextMenu.css_vue_type_style_index_0_src_03921269_scoped_03921269_lang.css +0 -1
- package/dist/assets/src/components/shared/controls/SSelect.css_vue_type_style_index_0_src_df3d6688_scoped_df3d6688_lang.css +0 -2
- package/dist/assets/src/components/shared/data-display/SControlledToastViewport.css_vue_type_style_index_0_src_5864b6c6_scoped_5864b6c6_lang.css +0 -2
- package/dist/assets/src/components/shared/data-display/SScrollViewport.css_vue_type_style_index_0_src_e5d11342_scoped_e5d11342_lang.css +0 -2
- package/dist/assets/src/components/shared/data-display/_internal/STableFrame.css_vue_type_style_index_0_src_52b5825e_scoped_52b5825e_lang.css +0 -2
- package/src/components/shared/containers/SDrawer.css +0 -87
- package/src/components/shared/containers/SModal.css +0 -27
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Жизненный цикл overlay / Overlay lifecycle
|
|
2
|
+
|
|
3
|
+
## Русский
|
|
4
|
+
|
|
5
|
+
`SModal` и viewport `SDrawer` используют один native dialog owner. Публичные
|
|
6
|
+
props, slots и events этих компонентов не требуют consumer DOM API.
|
|
7
|
+
Составные окна, меню, поля выбора, popover и tooltip используют принадлежащий
|
|
8
|
+
окну portal. Global toast остаётся background notification owner; modal-local
|
|
9
|
+
controlled toast принадлежит окну, в котором он объявлен.
|
|
10
|
+
|
|
11
|
+
### Custom leaf через `useDialogOverlay`
|
|
12
|
+
|
|
13
|
+
Контракт `composables/useDialogOverlay` предназначен для leaf, который владеет
|
|
14
|
+
полной semantic, visual и accessibility поверхностью своего диалога.
|
|
15
|
+
Приложения используют готовые `SModal`/`SDrawer`, если их props и slots выражают
|
|
16
|
+
нужную композицию.
|
|
17
|
+
|
|
18
|
+
Миграция `0.15` относится к собственным viewport leaf, использующим
|
|
19
|
+
экспортируемый composable: semantic surface — `HTMLDialogElement`, а portal —
|
|
20
|
+
его непосредственный дочерний HTMLElement. Публичные props, slots и events
|
|
21
|
+
готовых `SModal`/`SDrawer` сохраняются; contained leaf сохраняет HTMLElement
|
|
22
|
+
surface и `SOverlayHost`.
|
|
23
|
+
|
|
24
|
+
Для viewport leaf:
|
|
25
|
+
|
|
26
|
+
1. Передать controlled boolean `open`, boolean `closable` и callback
|
|
27
|
+
`requestClose`. Callback формирует намерение, а состояние задаёт parent.
|
|
28
|
+
2. Рендерить один подключённый `HTMLDialogElement` через `renderOpen` и связать
|
|
29
|
+
его ref с `setPanelElement`. Не задавать `open`, `autofocus` или `tabindex`
|
|
30
|
+
semantic root. Lifecycle native methods принадлежит composable.
|
|
31
|
+
3. Создать прямой дочерний HTMLElement portal, связать его ref с
|
|
32
|
+
`setPortalElement` и применить `portalStyle`. Portal находится вне transform,
|
|
33
|
+
overflow clipping и visual scrolling frame.
|
|
34
|
+
4. Связать visible frame с `setDismissalElement`; применить `backdropStyle` и
|
|
35
|
+
`surfaceStyle` к соответствующим принадлежащим leaf visual surfaces.
|
|
36
|
+
5. Установить доступное имя диалога через heading/`aria-labelledby` либо
|
|
37
|
+
`aria-label`. Footer/submit completion запрашивает controlled close.
|
|
38
|
+
|
|
39
|
+
### Исполняемый typed binding
|
|
40
|
+
|
|
41
|
+
Полная композиция — export `NativeOwnerContract` в
|
|
42
|
+
[`SModal.stories.ts`](https://github.com/pglyanenko/sputnig/blob/master/packages/ui-kit/src/components/shared/containers/SModal.stories.ts).
|
|
43
|
+
В Storybook она называется **«Публичный владелец нативного диалога»**,
|
|
44
|
+
story ID — `ui-kit-контейнеры-smodal--native-owner-contract`.
|
|
45
|
+
Тип `DialogOverlayController` импортируется из публичного
|
|
46
|
+
`@pgcorp/ui-kit/composables/useDialogOverlay`; native ref binding типизирован
|
|
47
|
+
как `DialogOverlayController['setPanelElement']`.
|
|
48
|
+
|
|
49
|
+
| Владелец в story | Binding | Контракт |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| Родительское состояние | `open`, `closable`, `requestClose` | Callback формирует close intent; parent подтверждает его изменением `open` |
|
|
52
|
+
| Native root | `controller.renderOpen.value`, `bindSubject` → `controller.setPanelElement` | Единственный подключённый `<dialog>`; root без `open`, `autofocus` и `tabindex` |
|
|
53
|
+
| Доступное имя | `aria-labelledby="native-owner-title"`, `<h2 id="native-owner-title">` | Видимый heading именует semantic root |
|
|
54
|
+
| Backdrop | `controller.backdropStyle.value` | Собственная поверхность leaf, `aria-hidden="true"`, отдельная от content |
|
|
55
|
+
| Контейнер размещения и прокрутки | `controller.surfaceStyle.value` | Paint band visual frame; portal не является потомком scroll/transform owner |
|
|
56
|
+
| Видимая frame | `controller.setDismissalElement` | Точная граница pointer dismissal; semantic focus scope остаётся native root |
|
|
57
|
+
| Native portal | `controller.setPortalElement`, `controller.portalStyle.value` | Прямой дочерний HTMLElement dialog, sibling visual surfaces, `display: contents` |
|
|
58
|
+
| Кнопка завершения | `SButton` → `requestClose` | Обычное UI-kit действие без native `dialog.close()` |
|
|
59
|
+
| Явное освобождение | `controller.dispose()` | Retirement без запроса возврата focus |
|
|
60
|
+
|
|
61
|
+
В source приведены полные стили semantic root, backdrop, positioner и frame,
|
|
62
|
+
включая focus-visible frame. Native methods вызываются только shared owner.
|
|
63
|
+
Кнопки изменения admission, `beforetoggle` probes и неподдержанное завершение
|
|
64
|
+
формы — диагностические действия regression story, а не требования к consumer.
|
|
65
|
+
Этот же export исполняется в
|
|
66
|
+
[`ui-kit-native-dialog-isolation.visual.spec.ts`](https://github.com/pglyanenko/sputnig/blob/master/lvs/frontend/tests/storybook/ui-kit-native-dialog-isolation.visual.spec.ts):
|
|
67
|
+
проверяются подтверждённое закрытие, no-ACK, `closable=false`, explicit dispose,
|
|
68
|
+
отменённый focus intent, reentrant retirement и сохранение source attributes в
|
|
69
|
+
Chromium, Firefox и WebKit.
|
|
70
|
+
|
|
71
|
+
`opened` вызывается после подтверждённого initial focus claim самого semantic
|
|
72
|
+
root. Initial controlled child menu/popover имеет самостоятельного focus owner;
|
|
73
|
+
его доступность не требует initial focus permission родителя.
|
|
74
|
+
`dispose()` явно освобождает cycle без запроса возврата фокуса. Обычный unmount
|
|
75
|
+
использует shared guarded return к доступному launcher, захваченному из
|
|
76
|
+
фактического native focus. Pointer activation сама по себе не гарантирует
|
|
77
|
+
фокус кнопки во всех браузерах. Скрытый retained dock
|
|
78
|
+
отменяет admission и не возвращает focus скрытому trigger. Возврат видимости
|
|
79
|
+
не открывает отменённое окно: требуется новое явное изменение `open` с `false`
|
|
80
|
+
на `true`. Содержимое retained dock сохраняет свой экземпляр и состояние.
|
|
81
|
+
|
|
82
|
+
`scope="contained"` сохраняет HTMLElement surface внутри exact `SOverlayHost`
|
|
83
|
+
portal и локальный HTML inert lease. Native `showModal()` в этом режиме не
|
|
84
|
+
вызывается; внешние controls остаются доступными.
|
|
85
|
+
|
|
86
|
+
### Граница lifecycle
|
|
87
|
+
|
|
88
|
+
Consumer не вызывает native `showModal()`/`close()`, не меняет `open` attribute
|
|
89
|
+
и не использует `form method="dialog"` как обход controlled состояния.
|
|
90
|
+
Для завершения формы предусмотрены обычный submit и явное намерение parent.
|
|
91
|
+
Уже отменённый submit не создаёт close intent. Native `cancel` не закрывает
|
|
92
|
+
controlled окно без обновления `open`; `closable=false` запрещает dismissal.
|
|
93
|
+
|
|
94
|
+
Native Document blocking охватывает HTML, SVG, MathML и background sources
|
|
95
|
+
внутри closed shadow root. Открытый дочерний native dialog изолирует предыдущие
|
|
96
|
+
native owners и их portal-содержимое на весь свой lifecycle. Их временные inert
|
|
97
|
+
leases освобождаются после native close до shared guarded focus return;
|
|
98
|
+
исходные атрибуты и независимые изменения host сохраняются.
|
|
99
|
+
Сторонний native modal внутри opaque closed shadow
|
|
100
|
+
root не входит в поддерживаемую nesting-композицию без exact host-owner
|
|
101
|
+
capability. Прямое вмешательство host в принадлежащий cycle или его временные
|
|
102
|
+
modality guards нарушает owner boundary.
|
|
103
|
+
|
|
104
|
+
### Браузерная проверка
|
|
105
|
+
|
|
106
|
+
`make -C lvs frontend-browser-install` устанавливает browser revisions из
|
|
107
|
+
зафиксированного Playwright workspace. Команда не запускает quality gates.
|
|
108
|
+
`LVS_STORYBOOK_NATIVE_DIALOG_ENGINES=1 make -C lvs check-frontend-storybook-proof
|
|
109
|
+
PLAYWRIGHT_STORYBOOK_ARGS="tests/storybook/ui-kit-native-dialog-isolation.visual.spec.ts"`
|
|
110
|
+
проверяет native lifecycle в Chromium, Firefox и WebKit.
|
|
111
|
+
|
|
112
|
+
## English
|
|
113
|
+
|
|
114
|
+
`SModal` and viewport `SDrawer` share one native dialog owner. Their public
|
|
115
|
+
props, slots and events require no consumer DOM lifecycle calls. Owned floating
|
|
116
|
+
surfaces use the dialog's portal; global notifications remain background-owned.
|
|
117
|
+
|
|
118
|
+
The `0.15` migration applies to custom viewport leaves using the exported
|
|
119
|
+
composable: the semantic surface is an `HTMLDialogElement`, and its portal is a
|
|
120
|
+
direct-child HTMLElement. The ready-made `SModal`/`SDrawer` retain their public
|
|
121
|
+
props, slots, and events. Contained leaves retain their HTMLElement surface and
|
|
122
|
+
`SOverlayHost` contract.
|
|
123
|
+
|
|
124
|
+
Custom `useDialogOverlay` viewport leaves provide one connected
|
|
125
|
+
`HTMLDialogElement` through `setPanelElement`, an exact direct-child portal
|
|
126
|
+
through `setPortalElement`, and the visible dismissal frame through
|
|
127
|
+
`setDismissalElement`. Render from `renderOpen`; apply the controller's
|
|
128
|
+
`backdropStyle`, `surfaceStyle` and `portalStyle` to their exact visual owners.
|
|
129
|
+
The semantic root has an accessible name and no `open`, `autofocus` or
|
|
130
|
+
`tabindex` assignment. Controlled `open` owns the lifecycle; `requestClose`
|
|
131
|
+
is an intent, not permission to mutate native state.
|
|
132
|
+
|
|
133
|
+
### Executable typed binding
|
|
134
|
+
|
|
135
|
+
The complete composition is the `NativeOwnerContract` export in
|
|
136
|
+
[`SModal.stories.ts`](https://github.com/pglyanenko/sputnig/blob/master/packages/ui-kit/src/components/shared/containers/SModal.stories.ts).
|
|
137
|
+
Its Storybook name is **«Публичный владелец нативного диалога»** and its story ID
|
|
138
|
+
is `ui-kit-контейнеры-smodal--native-owner-contract`. `DialogOverlayController`
|
|
139
|
+
is imported from the public `@pgcorp/ui-kit/composables/useDialogOverlay`; the
|
|
140
|
+
native ref binding uses `DialogOverlayController['setPanelElement']`.
|
|
141
|
+
|
|
142
|
+
| Story owner | Binding | Contract |
|
|
143
|
+
| --- | --- | --- |
|
|
144
|
+
| Parent state | `open`, `closable`, `requestClose` | The callback produces a close intent; the parent acknowledges it by updating `open` |
|
|
145
|
+
| Native root | `controller.renderOpen.value`, `bindSubject` → `controller.setPanelElement` | One connected `<dialog>` without `open`, `autofocus`, or `tabindex` assignments |
|
|
146
|
+
| Accessible name | `aria-labelledby="native-owner-title"`, `<h2 id="native-owner-title">` | The visible heading names the semantic root |
|
|
147
|
+
| Backdrop | `controller.backdropStyle.value` | An owned leaf surface with `aria-hidden="true"`, separate from content |
|
|
148
|
+
| Placement and scrolling container | `controller.surfaceStyle.value` | The visual frame's paint band; the portal is outside scroll/transform ownership |
|
|
149
|
+
| Visible frame | `controller.setDismissalElement` | The exact pointer dismissal boundary; native root retains semantic focus scope |
|
|
150
|
+
| Native portal | `controller.setPortalElement`, `controller.portalStyle.value` | A direct-child HTMLElement dialog portal, sibling to visual surfaces, with `display: contents` |
|
|
151
|
+
| Completion button | `SButton` → `requestClose` | A regular UI-kit action without native `dialog.close()` |
|
|
152
|
+
| Explicit disposal | `controller.dispose()` | Retirement without a focus-return request |
|
|
153
|
+
|
|
154
|
+
The source includes complete semantic-root, backdrop, positioner, and frame
|
|
155
|
+
styles, including the focus-visible frame. Only the shared owner calls native
|
|
156
|
+
methods. Admission controls, `beforetoggle` probes, and unsupported form
|
|
157
|
+
completion are regression-story diagnostics, not consumer requirements.
|
|
158
|
+
The same export is exercised by
|
|
159
|
+
[`ui-kit-native-dialog-isolation.visual.spec.ts`](https://github.com/pglyanenko/sputnig/blob/master/lvs/frontend/tests/storybook/ui-kit-native-dialog-isolation.visual.spec.ts):
|
|
160
|
+
acknowledged close, no-ACK, `closable=false`, explicit disposal, cancelled focus
|
|
161
|
+
intent, reentrant retirement, and preserved source attributes are verified in
|
|
162
|
+
Chromium, Firefox, and WebKit.
|
|
163
|
+
|
|
164
|
+
`opened` follows the root's successful initial focus claim. Initially open
|
|
165
|
+
child layers retain their own focus ownership. Explicit `dispose()` requests
|
|
166
|
+
no return; ordinary unmount uses the shared guarded return to the available
|
|
167
|
+
launcher captured from actual native focus. Pointer activation does not
|
|
168
|
+
guarantee button focus in every browser. Hiding a retained dock revokes admission
|
|
169
|
+
without returning focus to its hidden trigger. Revealing the dock does not revive
|
|
170
|
+
the cancelled window: a fresh explicit `open` change from `false` to `true` is
|
|
171
|
+
required. The retained dock keeps its content instance and state.
|
|
172
|
+
Contained scope retains its local HTMLElement/`SOverlayHost` contract and does
|
|
173
|
+
not call native `showModal()`.
|
|
174
|
+
|
|
175
|
+
Direct `showModal()`, `close()`, `open` writes and `form method="dialog"` exits
|
|
176
|
+
are outside the controlled API. Use regular form submission and an explicit
|
|
177
|
+
parent close intent. Native cancel respects controlled state and dismissal
|
|
178
|
+
permission. Already cancelled submissions produce no close intent.
|
|
179
|
+
|
|
180
|
+
Native Document blocking covers HTML, SVG, MathML and ordinary closed-shadow
|
|
181
|
+
background sources. An open child native dialog isolates previous native owners
|
|
182
|
+
and their portal content for its entire lifecycle. Temporary inert leases release
|
|
183
|
+
after native close and before the shared guarded focus return; original attributes
|
|
184
|
+
and independent host writes are preserved.
|
|
185
|
+
Nesting inside a foreign native modal in an opaque closed
|
|
186
|
+
shadow root requires a separate exact host-owner capability. Host mutation of
|
|
187
|
+
the owned native cycle or its temporary modality guards violates ownership.
|
|
188
|
+
|
|
189
|
+
### Browser verification
|
|
190
|
+
|
|
191
|
+
`make -C lvs frontend-browser-install` installs the browser revisions required
|
|
192
|
+
by the locked Playwright workspace; it does not run quality gates.
|
|
193
|
+
`LVS_STORYBOOK_NATIVE_DIALOG_ENGINES=1 make -C lvs check-frontend-storybook-proof
|
|
194
|
+
PLAYWRIGHT_STORYBOOK_ARGS="tests/storybook/ui-kit-native-dialog-isolation.visual.spec.ts"`
|
|
195
|
+
verifies the native lifecycle in Chromium, Firefox and WebKit.
|
package/docs/public-api.md
CHANGED
|
@@ -27,6 +27,9 @@ UI-kit-owned атрибуты `data-s-*` относятся к внутренн
|
|
|
27
27
|
runtime-зависимости через layout конкретного package manager. Сборка использует
|
|
28
28
|
`target/lib: ES2020`, совпадающий с baseline `@vue/tsconfig/tsconfig.dom.json`,
|
|
29
29
|
и не требует скрытых полифиллов.
|
|
30
|
+
Сборка Tailwind entrypoints использует Node ESM `dist/typography.js` с native
|
|
31
|
+
`node:util`, полным upstream prose contract и options `className`/`target`
|
|
32
|
+
для `modern | legacy`; browser Web Storage и DOM globals не требуются.
|
|
30
33
|
`icons` публикует именованные tree-shakeable компоненты, включая `HardDrive`,
|
|
31
34
|
`Usb`, `Disc`, `CalendarDays`, `ArrowUpRight`, `Paperclip`, `GitFork`, `ZoomIn`,
|
|
32
35
|
`ZoomOut`, `RotateCw`, `Volume2`, `Radio`, `Pause`, `ChevronLeft` и `Zap`. `Zap`
|
|
@@ -74,10 +77,20 @@ Link surface требует ровно один target и доступное и
|
|
|
74
77
|
подключённого Vue Router. Static surface отклоняет tooltip binding.
|
|
75
78
|
Sibling `#actions` сохраняет независимые focus/activation. Подсказка
|
|
76
79
|
открывается по pointer/focus и закрывается через Escape.
|
|
77
|
-
|
|
80
|
+
Пассивный visual portal исключён из самостоятельного AX-чтения через
|
|
81
|
+
`aria-hidden`; полный rich content доступен как `aria-describedby` описание
|
|
82
|
+
focus owner. Внешние description references сохраняются; имя, роль и Tab
|
|
83
|
+
порядок owner не изменяются. Tooltip не принимает интерактивный контент.
|
|
84
|
+
Пассивная tooltip panel поддерживает геометрический hover без focus или Tab stop
|
|
85
|
+
и не перехватывает указатель у расположенных под ней действий. Выход из
|
|
78
86
|
target и панели закрывает её через `hideDelay`. Escape отменяет pending
|
|
79
87
|
`showDelay` без перехвата события у другого overlay. Blur, скрытие документа,
|
|
80
88
|
disabled и освобождение target завершают activation lease и его слушатели.
|
|
89
|
+
Binding допускает canonical focus owner в сохранённой скрытой секции.
|
|
90
|
+
Активация требует подключённого, видимого и доступного владельца. Скрытие
|
|
91
|
+
его ancestor отменяет pending-таймеры, сразу исключает visual portal из
|
|
92
|
+
взаимодействия и снимает tooltip description; последующий reveal сохраняет
|
|
93
|
+
тот же owner и допускает новое pointer/focus открытие.
|
|
81
94
|
`SUiKitLocaleProvider` задаёт `en | ru | zh-CN | zh-TW` для одного Vue subtree,
|
|
82
95
|
а `composables/useUiKitLocale` экспортирует readonly context, полный список
|
|
83
96
|
локалей и типы messages/overrides/ключей. Provider не создаёт DOM owner и
|
|
@@ -97,6 +110,10 @@ callbacks и trust flags не входят в API.
|
|
|
97
110
|
`sm/md/lg = 32/36/40 px` и label typography `12/14/14 px`; тот же
|
|
98
111
|
`density="compact"` у `SInputText` даёт совместимую геометрию поля без
|
|
99
112
|
consumer CSS.
|
|
113
|
+
Foreground/background `SButton` применяются атомарно без color interpolation
|
|
114
|
+
при disabled/loading → enabled и смене визуального состояния. Native
|
|
115
|
+
activation, `href` и Tab availability следуют текущим props без ожидания
|
|
116
|
+
декоративных shadow/transform transitions.
|
|
100
117
|
`SInputText.minLength`/`maxLength` и `STextarea.minLength`/`maxLength` передают строгие
|
|
101
118
|
неотрицательные safe integers нативному textual input; `minLength <= maxLength`
|
|
102
119
|
проверяется до рендера, а numeric/temporal families отвергают этот контракт.
|
|
@@ -128,9 +145,32 @@ retired controlled `open=true` получает один close intent и тре
|
|
|
128
145
|
sibling overlays и consumer-owned arbitrary portals не затрагиваются.
|
|
129
146
|
`SModal.headingLevel` и `SDrawer.headingLevel` принимают только `1 | 2 | 3 | 4 |
|
|
130
147
|
5 | 6`, используют `2` по умолчанию и сохраняют связь видимого заголовка с
|
|
131
|
-
dialog surface через `aria-labelledby`.
|
|
148
|
+
dialog surface через `aria-labelledby`. Локальная шапка диалога содержит
|
|
149
|
+
семантический heading и не создаёт page-level `banner`.
|
|
132
150
|
`SModal.size="4xl"` задаёт responsive maximum `56rem`; при ошибке дочернего
|
|
133
151
|
mount modal освобождает body lock, layer registration и owned teleport nodes.
|
|
152
|
+
Viewport `SModal`/`SDrawer` владеют единственным нативным `dialog` в modal top
|
|
153
|
+
layer, без `tabindex` на semantic root. Фон всех DOM namespaces исключён из
|
|
154
|
+
pointer/focus interaction и accessibility tree. Дочерние UI-kit portals
|
|
155
|
+
принадлежат actual dialog вне transform/scroll clipping; вложенный viewport
|
|
156
|
+
dialog изолирует родителя. Retirement синхронно освобождает native blocking,
|
|
157
|
+
сохраняя отдельный shared focus-return и visual leave lifecycle. Contained
|
|
158
|
+
drawer ограничивает HTML inert lease своим host и не вызывает `showModal()`.
|
|
159
|
+
Экспортируемый `useDialogOverlay` в viewport-режиме требует actual connected
|
|
160
|
+
`HTMLDialogElement` через `setPanelElement` и exact direct-child portal через
|
|
161
|
+
`setPortalElement`; contained-режим сохраняет HTMLElement surface внутри
|
|
162
|
+
SOverlayHost. Native methods принадлежат composable, не consumer.
|
|
163
|
+
Для custom viewport leaf `dialog` и portal находятся в одном Document;
|
|
164
|
+
portal — непосредственный дочерний HTMLElement semantic root. Visible frame
|
|
165
|
+
передаётся через `setDismissalElement`, его расположение не меняет native focus
|
|
166
|
+
scope. Controlled `open` является единственным запросом открытия/закрытия;
|
|
167
|
+
`showModal()`, `close()`, запись `open` и native `form method="dialog"` не являются
|
|
168
|
+
consumer API. Завершение формы использует обычный submit и явное controlled
|
|
169
|
+
намерение закрытия. Native `cancel` обрабатывается shared owner без потери
|
|
170
|
+
контроля над `open`. Композиция в стороннем native modal внутри opaque closed
|
|
171
|
+
shadow root требует отдельного host-owner контракта и не поддерживается этим API.
|
|
172
|
+
Обычные background sources внутри closed shadow root покрывает native Document
|
|
173
|
+
blocking. Контракт миграции custom leaf описан в [overlay-lifecycle.md](./overlay-lifecycle.md).
|
|
134
174
|
`SDrawer.placement` принимает `left | right | bottom` с default `right`.
|
|
135
175
|
Боковые размещения физические, не зависят от text direction и используют
|
|
136
176
|
общий size/scroll/focus owner; `left` зеркалирует сторону панели и её chrome.
|
|
@@ -143,9 +183,20 @@ placement. `extent` запрещён для viewport-режима. `scrollBehavi
|
|
|
143
183
|
делегирует прокрутку дочернему viewport без второго scroll owner/полей.
|
|
144
184
|
Закрытие/retirement завершают дочерние overlay leases в общем stack; consumer
|
|
145
185
|
обрабатывает `update:open`, повторное открытие требует явного нового open-cycle.
|
|
186
|
+
Открытие через `SDropdownMenu` переносит exact current focus intent от
|
|
187
|
+
временного menuitem к dialog и сохраняет устойчивый menu trigger как цель
|
|
188
|
+
возврата, включая trigger вне contained host. Внешний focus отменяет pending
|
|
189
|
+
admission и stale return; contained Escape boundary остаётся локальной.
|
|
146
190
|
`SPopover` сохраняет native anchor и исходного focus owner trigger-запроса до
|
|
147
191
|
подтверждения controlled `open`, без перехвата последующего внешнего фокуса.
|
|
148
192
|
Программный `close()` отменяет ожидающий запрос через `update:open=false`.
|
|
193
|
+
Удаление или disabled текущего focused action не является outside-переходом:
|
|
194
|
+
панель остаётся открытой, принятое закрытие через Escape возвращает focus
|
|
195
|
+
доступному registered trigger.
|
|
196
|
+
Явный blur доступного элемента и внешний focus сохраняют outside-политику.
|
|
197
|
+
`SDrawer`, `SPopover`, `STooltip`, `SContextMenu` и
|
|
198
|
+
`SControlledToastViewport` сохраняют `opacity: 1` content при enter/leave:
|
|
199
|
+
transform motion не меняет прозрачность выбранной surface, backdrop анимируется отдельно.
|
|
149
200
|
Ширина `trigger` является минимальной шириной anchor, `content` — intrinsic,
|
|
150
201
|
`sm/md/lg` — viewport-bounded `20/28/36rem`. Chrome и focus owner используют
|
|
151
202
|
единые bounds; прозрачная поверхность задаёт всему dialog максимум `90vw`,
|
|
@@ -163,6 +214,20 @@ inline-сторонам вложенное меню использует дос
|
|
|
163
214
|
collision owner.
|
|
164
215
|
`SCollapsiblePanel` связывает compact/dense density с
|
|
165
216
|
32-пиксельной disclosure geometry через публичный API `SButton`.
|
|
217
|
+
`contentRole="region"` обозначает самостоятельный landmark и является значением
|
|
218
|
+
по умолчанию; `contentRole="group"` задаёт именованную локальную группу для
|
|
219
|
+
повторяемых или вложенных сведений. Обе роли связаны с видимым summary через
|
|
220
|
+
`aria-labelledby`; trigger сохраняет `aria-expanded` и `aria-controls`.
|
|
221
|
+
Raw `role` attrs не разрешены, неизвестный `contentRole` отклоняется без coercion.
|
|
222
|
+
`lazy` монтирует slot при первом принятом раскрытии и сохраняет его после
|
|
223
|
+
collapse/reopen в controlled и uncontrolled режимах, включая анимацию.
|
|
224
|
+
Controlled `open` эмитит `update:open` intent до prop acknowledgement;
|
|
225
|
+
uncontrolled режим изменяет собственное состояние без этого события.
|
|
226
|
+
Принятое сворачивание сразу исключает content из Tab/AX через inert и
|
|
227
|
+
aria-hidden, сохраняя mounted slot и завершающую анимацию. Owned focus
|
|
228
|
+
возвращается доступному trigger; внешний focus не перехватывается.
|
|
229
|
+
Отсутствие height transition завершает motion сразу; stale completion
|
|
230
|
+
не влияет на повторное раскрытие или изменённую высоту.
|
|
166
231
|
`SStatus` использует общую feedback tone-пару; `neutral` соответствует
|
|
167
232
|
`secondary`. Текстовый статус в passive content `SButton` использует foreground
|
|
168
233
|
интерактивной поверхности во всех доступных состояниях. `appearance="soft"`
|
|
@@ -174,6 +239,13 @@ semantics сохраняются; `SCollapsiblePanel.summaryStatus` исполь
|
|
|
174
239
|
ResizeObserver lifecycle. `shared/data-display/scrollViewport` экспортирует
|
|
175
240
|
сериализуемое physical/semantic state и exposed API `focus`,
|
|
176
241
|
`scrollToBoundary`, `reveal` без доступа к внутреннему DOM.
|
|
242
|
+
`focusPolicy="always"` — default; `overflow` добавляет region и Tab stop только
|
|
243
|
+
при переполнении разрешённой оси. Ровно один из `accessibleLabel` и
|
|
244
|
+
`accessibleLabelledby` задаёт имя. `blockSize="content"` сохраняет естественную
|
|
245
|
+
высоту, `maxBlockSize="none|sm|md|lg"` задаёт semantic limit. Канонический table
|
|
246
|
+
frame композирует этот owner, сохраняя native table semantics и nested controls.
|
|
247
|
+
При исчезновении overflow focused root сохраняет `tabindex="-1"` до native blur
|
|
248
|
+
без дополнительной остановки Tab или программного переноса focus.
|
|
177
249
|
|
|
178
250
|
## English
|
|
179
251
|
|
|
@@ -202,6 +274,9 @@ Consumer type checkers do not compile internal SFCs or resolve their runtime
|
|
|
202
274
|
dependencies through a package-manager-specific layout. The build uses the
|
|
203
275
|
`target/lib: ES2020` baseline from `@vue/tsconfig/tsconfig.dom.json` and does not
|
|
204
276
|
require implicit polyfills.
|
|
277
|
+
Tailwind entrypoints use the Node ESM `dist/typography.js` artifact with
|
|
278
|
+
native `node:util`, the complete upstream prose contract, and `className`/
|
|
279
|
+
`target` options for `modern | legacy`, without browser Web Storage or DOM globals.
|
|
205
280
|
`icons` exports named tree-shakeable components, including `HardDrive`, `Usb`,
|
|
206
281
|
`Disc`, `CalendarDays`, `ArrowUpRight`, `Paperclip`, `GitFork`, `ZoomIn`, `ZoomOut`,
|
|
207
282
|
`RotateCw`, `Volume2`, `Radio`, `Pause`, `ChevronLeft`, and `Zap`. `Zap` represents
|
|
@@ -250,10 +325,16 @@ Router `to`. A link surface requires exactly one target and an accessible name;
|
|
|
250
325
|
`to` requires Vue Router installed in the application. A static surface rejects
|
|
251
326
|
the tooltip binding. Sibling `#actions` retain independent focus and activation.
|
|
252
327
|
The tooltip opens on pointer/focus and closes with Escape.
|
|
253
|
-
The passive tooltip panel supports hover without focus or a Tab stop
|
|
328
|
+
The passive tooltip panel supports geometric hover without focus or a Tab stop
|
|
329
|
+
and does not intercept pointer activation of underlying actions. Leaving
|
|
254
330
|
the target and panel closes it after `hideDelay`. Escape cancels a pending
|
|
255
331
|
`showDelay` without consuming another overlay's event. Blur, a hidden document,
|
|
256
332
|
disabled state, and target release terminate the activation lease and listeners.
|
|
333
|
+
Binding accepts a canonical focus owner in a retained hidden section.
|
|
334
|
+
Activation requires a connected, visible, available owner. Hiding its
|
|
335
|
+
ancestor cancels pending timers, immediately retires the visual portal from
|
|
336
|
+
interaction and removes the tooltip description. Revealing the section
|
|
337
|
+
retains the same owner and permits fresh pointer/focus activation.
|
|
257
338
|
`SUiKitLocaleProvider` selects `en | ru | zh-CN | zh-TW` for one Vue subtree,
|
|
258
339
|
while `composables/useUiKitLocale` exports the readonly context, the complete
|
|
259
340
|
locale list, and message/override/key types. The provider renders no DOM owner
|
|
@@ -274,6 +355,10 @@ and trust flags are not part of the API.
|
|
|
274
355
|
`sm/md/lg = 32/36/40 px` with `12/14/14 px` label typography. The matching
|
|
275
356
|
`SInputText.density="compact"` exposes the same field geometry without consumer
|
|
276
357
|
CSS.
|
|
358
|
+
The `SButton` foreground/background pair changes atomically, without color
|
|
359
|
+
interpolation, when disabled/loading actions become enabled or presentation
|
|
360
|
+
changes. Native activation, `href`, and Tab availability follow current props
|
|
361
|
+
without waiting for decorative shadow/transform transitions.
|
|
277
362
|
`SInputText.minLength`/`maxLength` and `STextarea.minLength`/`maxLength` pass strict non-negative safe
|
|
278
363
|
integers to the native textual input. `minLength <= maxLength` is validated
|
|
279
364
|
before rendering, and numeric/temporal families reject this contract.
|
|
@@ -306,9 +391,29 @@ request. Nested docks compose admission with AND; independent sibling overlays
|
|
|
306
391
|
and arbitrary consumer-owned portals are unaffected.
|
|
307
392
|
`SModal.headingLevel` and `SDrawer.headingLevel` accept `1 | 2 | 3 | 4 | 5 | 6`
|
|
308
393
|
only, default to `2`, and preserve the visible-title relationship with the
|
|
309
|
-
dialog surface through `aria-labelledby`.
|
|
394
|
+
dialog surface through `aria-labelledby`. Local dialog chrome contains the
|
|
395
|
+
semantic heading without creating a page-level `banner` landmark.
|
|
310
396
|
`SModal.size="4xl"` provides a responsive `56rem` maximum; a failed child mount
|
|
311
397
|
releases body lock, layer registration, and owned teleport nodes.
|
|
398
|
+
Viewport `SModal`/`SDrawer` own one native `dialog` in the modal top layer, with
|
|
399
|
+
no `tabindex` on the semantic root. All background DOM namespaces leave
|
|
400
|
+
pointer/focus interaction and the accessibility tree. Descendant UI-kit portals
|
|
401
|
+
belong to the actual dialog outside transforms and scroll clipping; a nested
|
|
402
|
+
viewport dialog isolates its parent. Retirement releases native blocking
|
|
403
|
+
synchronously while preserving the shared focus-return and visual leave
|
|
404
|
+
lifecycles. A contained drawer limits an HTML inert lease to its host and does
|
|
405
|
+
not call `showModal()`.
|
|
406
|
+
The exported `useDialogOverlay` requires an actual connected `HTMLDialogElement`
|
|
407
|
+
through `setPanelElement` and its exact direct-child portal through
|
|
408
|
+
`setPortalElement` in viewport mode. Contained mode retains an HTMLElement
|
|
409
|
+
surface inside SOverlayHost. The composable owns native methods, not consumers.
|
|
410
|
+
The custom viewport leaf binds its visible frame through `setDismissalElement`
|
|
411
|
+
and applies `portalStyle` to its direct-child portal. Controlled `open` is the
|
|
412
|
+
only opening/closing request; direct native lifecycle calls, `open` writes and
|
|
413
|
+
`form method="dialog"` exits are not consumer APIs. Regular form submission
|
|
414
|
+
uses an explicit parent close intent. Nesting inside a foreign native modal in
|
|
415
|
+
an opaque closed shadow root requires a separate host-owner contract.
|
|
416
|
+
See [overlay-lifecycle.md](./overlay-lifecycle.md) for the custom-leaf migration.
|
|
312
417
|
`SDrawer.placement` accepts `left | right | bottom`, defaulting to `right`.
|
|
313
418
|
Side placements are physical, independent of text direction, and share the
|
|
314
419
|
size/scroll/focus owner; `left` mirrors the panel edge and its chrome.
|
|
@@ -321,10 +426,21 @@ the placement axis. Viewport mode rejects `extent`. `scrollBehavior="content"`
|
|
|
321
426
|
delegates scrolling to a child viewport without a second scroll owner/padding.
|
|
322
427
|
Close/retirement retires descendant overlay leases in the shared stack; the
|
|
323
428
|
consumer handles `update:open` and an explicit new open-cycle controls reopening.
|
|
429
|
+
Opening through `SDropdownMenu` transfers the exact current focus intent
|
|
430
|
+
from a transient menu item to the dialog and retains its stable menu trigger
|
|
431
|
+
for restoration, including triggers outside a contained host. External focus
|
|
432
|
+
cancels pending admission and stale restoration; contained Escape remains local.
|
|
324
433
|
`SPopover` preserves the native anchor and original focus owner of a trigger
|
|
325
434
|
request through controlled `open` acknowledgement without taking subsequent
|
|
326
435
|
external focus. Programmatic `close()` cancels a pending request with
|
|
327
436
|
`update:open=false`.
|
|
437
|
+
Removing or disabling the currently focused action is not an outside move:
|
|
438
|
+
the panel stays open, and an accepted Escape close restores the available
|
|
439
|
+
registered trigger.
|
|
440
|
+
Explicit blur of an available element and external focus retain the outside policy.
|
|
441
|
+
`SDrawer`, `SPopover`, `STooltip`, `SContextMenu`, and
|
|
442
|
+
`SControlledToastViewport` keep content at `opacity: 1` during entry/exit:
|
|
443
|
+
transform motion preserves the selected surface translucency, and backdrop animation is independent.
|
|
328
444
|
The `trigger` width is the anchor minimum, `content` is intrinsic, and
|
|
329
445
|
`sm/md/lg` are viewport-bounded `20/28/36rem`. Chrome and focus owner share
|
|
330
446
|
the same bounds; a transparent surface sets the entire dialog's maximum to `90vw`.
|
|
@@ -342,6 +458,20 @@ side fits a submenu, it uses the available top/bottom side of the shared
|
|
|
342
458
|
collision owner.
|
|
343
459
|
`SCollapsiblePanel` maps compact/dense density to
|
|
344
460
|
the 32px disclosure geometry through the public `SButton` API.
|
|
461
|
+
`contentRole="region"` marks an independently navigable landmark and is the
|
|
462
|
+
default; `contentRole="group"` provides a named local group for repeated or
|
|
463
|
+
nested details. Both roles are labelled by the visible summary through
|
|
464
|
+
`aria-labelledby`; the trigger retains `aria-expanded` and `aria-controls`.
|
|
465
|
+
Raw `role` attrs are not admitted; unknown `contentRole` values fail without
|
|
466
|
+
coercion. `lazy` mounts its slot on the first accepted opening and retains it
|
|
467
|
+
across collapse/reopen in controlled and uncontrolled modes, including animation.
|
|
468
|
+
Controlled `open` emits `update:open` intent before prop acknowledgement;
|
|
469
|
+
uncontrolled mode updates its owned state without that event.
|
|
470
|
+
Accepted collapse immediately excludes content from Tab/AX using inert
|
|
471
|
+
and aria-hidden, retaining the mounted slot and closing visual transition.
|
|
472
|
+
Owned focus returns to an available trigger; external focus is not taken.
|
|
473
|
+
An absent height transition settles motion immediately; stale completion
|
|
474
|
+
does not affect reopening or an updated content height.
|
|
345
475
|
`SStatus` uses the shared feedback tone pair; `neutral` maps to `secondary`.
|
|
346
476
|
A text status in `SButton` passive content uses the interactive surface's
|
|
347
477
|
foreground across its available states. `appearance="soft"` owns its
|
|
@@ -353,3 +483,10 @@ enabled axes, semantic block size, root-only keyboard navigation, and
|
|
|
353
483
|
ResizeObserver lifecycle. `shared/data-display/scrollViewport` exports
|
|
354
484
|
serializable physical and semantic state plus the `focus`, `scrollToBoundary`,
|
|
355
485
|
and `reveal` exposed API without access to internal DOM.
|
|
486
|
+
`focusPolicy="always"` is the default; `overflow` adds a region and Tab stop
|
|
487
|
+
only when an enabled axis overflows. Exactly one of `accessibleLabel` and
|
|
488
|
+
`accessibleLabelledby` supplies the name. `blockSize="content"` retains intrinsic
|
|
489
|
+
height, and `maxBlockSize="none|sm|md|lg"` supplies a semantic limit. The canonical
|
|
490
|
+
table frame composes this owner, retaining native table semantics and nested controls.
|
|
491
|
+
After overflow disappears, a focused root retains `tabindex="-1"` until native
|
|
492
|
+
blur, without an additional Tab stop or programmatic focus move.
|
package/docs/resize.md
CHANGED
|
@@ -52,8 +52,36 @@ Consumer применяет `resize` к preview. В `resize-end` он прини
|
|
|
52
52
|
актуальной host geometry; persistence preference восстанавливается из его
|
|
53
53
|
собственного transaction snapshot, не из `initialValue`. Отменённый preview
|
|
54
54
|
не сохраняется. На время pointer transaction persistent watcher должен быть
|
|
55
|
-
отделён от preview state.
|
|
56
|
-
|
|
55
|
+
отделён от preview state.
|
|
56
|
+
|
|
57
|
+
## Клавиатурное завершение
|
|
58
|
+
|
|
59
|
+
Arrow/Home/End применяют одно дискретное изменение: `resize(value)`, затем
|
|
60
|
+
`resize-end(value)` без `SResizeHandleCompletion`. Отсутствие completion
|
|
61
|
+
обозначает клавиатурный источник; pointer completion всегда содержит
|
|
62
|
+
`source: 'pointer'`. Клавиатура не открывает pointer transaction и не выдаёт
|
|
63
|
+
`resize-start` или `resize-cancel`.
|
|
64
|
+
|
|
65
|
+
Значение ограничивается текущими `min/max`. На границе диапазона, для
|
|
66
|
+
ортогональной стрелки, при `disabled` или во время активного pointer gesture
|
|
67
|
+
события изменения и завершения не выдаются. Если обработчик `resize` удаляет
|
|
68
|
+
native owner, завершение после его disposal не выдаётся.
|
|
69
|
+
|
|
70
|
+
Consumer может хранить preview в `resize` и сохранять preference в
|
|
71
|
+
`resize-end` для обоих источников. Если составной owner уже атомарно сохраняет
|
|
72
|
+
клавиатурный `resize`, terminal notification не выполняет повторную запись:
|
|
73
|
+
обработчик явно разделяет источники по completion. Pointer commit требует
|
|
74
|
+
собственной живой transaction, а не разрешается при её отсутствии.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
function finishSizeChange(value: number, completion?: SResizeHandleCompletion): void {
|
|
78
|
+
if (completion === undefined) {
|
|
79
|
+
commitKeyboardSize(value)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
commitPointerSize(value, completion)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
57
85
|
|
|
58
86
|
```vue
|
|
59
87
|
<SDockRegion
|
|
@@ -65,7 +93,7 @@ Consumer применяет `resize` к preview. В `resize-end` он прини
|
|
|
65
93
|
hitbox="standard"
|
|
66
94
|
@resize-start="beginSizeTransaction"
|
|
67
95
|
@resize="previewSize"
|
|
68
|
-
@resize-end="
|
|
96
|
+
@resize-end="finishSizeChange"
|
|
69
97
|
@resize-cancel="cancelSizeTransaction"
|
|
70
98
|
>
|
|
71
99
|
<ProjectPanel />
|
|
@@ -102,7 +130,7 @@ Pointer cancellation не вызывает `collapse`. Keyboard Home устан
|
|
|
102
130
|
hitbox="standard"
|
|
103
131
|
@resize-start="beginSizeTransaction"
|
|
104
132
|
@resize="previewSize"
|
|
105
|
-
@resize-end="
|
|
133
|
+
@resize-end="finishSizeChange"
|
|
106
134
|
@resize-cancel="cancelSizeTransaction"
|
|
107
135
|
@collapse="collapseSizeTransaction"
|
|
108
136
|
>
|
|
@@ -125,5 +153,6 @@ Pointer cancellation не вызывает `collapse`. Keyboard Home устан
|
|
|
125
153
|
payload; его диапазон принадлежит текущей рабочей области. Consumers используют
|
|
126
154
|
эти owners, а не создают собственный drag engine.
|
|
127
155
|
|
|
128
|
-
Storybook: `SResizeHandle/
|
|
156
|
+
Storybook: `SResizeHandle/KeyboardCommitPersistence`,
|
|
157
|
+
`SResizeHandle/PointerTouchAndConcurrentLifecycle` и
|
|
129
158
|
`SDockRegion/CompanionDocks`, `CompanionDocksDark`, `CompanionDocksNarrow`.
|
package/docs/scroll-viewport.md
CHANGED
|
@@ -26,9 +26,11 @@ import type {
|
|
|
26
26
|
|
|
27
27
|
| Prop | Contract |
|
|
28
28
|
| --- | --- |
|
|
29
|
-
| `accessibleLabel` |
|
|
29
|
+
| `accessibleLabel` / `accessibleLabelledby` | Ровно один источник имени: непустая строка либо exact single-space DOM IDREF-list. / Exactly one name source: non-empty text or an exact single-space DOM IDREF list. |
|
|
30
30
|
| `axis` | `'x' \| 'y' \| 'both'`; default `y`. Запрещённая ось не прокручивается и недоступна API. / A disabled axis neither scrolls nor accepts API commands. |
|
|
31
|
-
| `blockSize` | `'sm' \| 'md' \| 'lg' \| 'fill'`;
|
|
31
|
+
| `blockSize` | `'sm' \| 'md' \| 'lg' \| 'fill' \| 'content'`; default `md`. `content` сохраняет естественный flow. / Semantic size; `content` preserves natural flow. Raw CSS lengths are rejected. |
|
|
32
|
+
| `maxBlockSize` | `'none' \| 'sm' \| 'md' \| 'lg'`; default `none`. Семантический верхний предел высоты. / Semantic maximum block size. |
|
|
33
|
+
| `focusPolicy` | `'always' \| 'overflow'`; default `always`. `overflow` добавляет named region и остановку Tab только при переполнении разрешённой оси. / `overflow` adds a named region and Tab stop only during enabled-axis overflow. |
|
|
32
34
|
| `focusable` | Включает root tab stop и `focus()`; default `true`. / Enables the root tab stop and `focus()`; defaults to `true`. |
|
|
33
35
|
| `keyboardNavigation` | Управление корнем с клавиатуры; требует `focusable=true`. / Root keyboard control; requires `focusable=true`. |
|
|
34
36
|
| `overscrollBehavior` | `'auto' \| 'contain' \| 'none'`; default `contain`. |
|
|
@@ -39,6 +41,21 @@ classes и `data-s-*` не являются selector API.
|
|
|
39
41
|
Consumer-owned `data-*` are supported on the root region for tour and test
|
|
40
42
|
hooks. Internal classes and `data-s-*` are not selector APIs.
|
|
41
43
|
|
|
44
|
+
`focusable` разрешает root focus; `focusPolicy="overflow"` учитывает фактический
|
|
45
|
+
scroll range только разрешённых осей. При исчезновении переполнения component
|
|
46
|
+
удаляет Tab stop и named region, не перемещая существующий focus: focused root
|
|
47
|
+
сохраняет `tabindex="-1"` до native blur, после которого атрибут снимается. `focus()` в
|
|
48
|
+
этом состоянии завершается явной ошибкой. Содержимое таблиц использует такой
|
|
49
|
+
контракт через общий canonical table owner; native table name и ElementApi
|
|
50
|
+
сохраняются.
|
|
51
|
+
|
|
52
|
+
`focusable` permits root focus; `focusPolicy="overflow"` checks the actual range
|
|
53
|
+
of enabled axes. When overflow disappears, the component removes the Tab stop
|
|
54
|
+
and named region without moving existing focus: a focused root retains
|
|
55
|
+
`tabindex="-1"` until native blur removes the attribute. `focus()` fails explicitly in
|
|
56
|
+
that state. Canonical table composition uses this policy while retaining the
|
|
57
|
+
native table name and ElementApi.
|
|
58
|
+
|
|
42
59
|
## Состояние / State
|
|
43
60
|
|
|
44
61
|
`scroll-state` эмитится после mount, прокрутки, resize viewport/content и
|
|
@@ -130,14 +147,18 @@ into view.
|
|
|
130
147
|
|
|
131
148
|
## Lifecycle
|
|
132
149
|
|
|
133
|
-
Один `ResizeObserver` отслеживает
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
Один `ResizeObserver` отслеживает viewport, content owner и его непосредственные
|
|
151
|
+
дочерние элементы. Scoped content `MutationObserver` обновляет intrinsic
|
|
152
|
+
геометрию и наблюдаемый набор; capture load listener учитывает размеры ресурсов.
|
|
153
|
+
Observers/listeners освобождаются при unmount; callback после teardown не
|
|
154
|
+
публикует state. Global document observer/listener и polling не используются.
|
|
136
155
|
|
|
137
|
-
One `ResizeObserver` tracks the
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
156
|
+
One `ResizeObserver` tracks the viewport, content owner and direct content
|
|
157
|
+
element children. A scoped content `MutationObserver` updates intrinsic
|
|
158
|
+
geometry and the observed child set; a content capture load listener covers
|
|
159
|
+
resource dimensions. Observers/listeners are released on unmount, and callbacks
|
|
160
|
+
after teardown publish no state. The component creates no document-global
|
|
161
|
+
observer/listener and uses no polling.
|
|
141
162
|
|
|
142
163
|
## Пример / Example
|
|
143
164
|
|
|
@@ -164,4 +185,3 @@ const position = ref<SScrollViewportState | null>(null)
|
|
|
164
185
|
</SScrollViewport>
|
|
165
186
|
</template>
|
|
166
187
|
```
|
|
167
|
-
|