@pgcorp/ui-kit 0.1.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/LICENSE +21 -0
- package/README.md +157 -0
- package/docs/accessibility.md +27 -0
- package/docs/getting-started.md +80 -0
- package/docs/licensing.md +30 -0
- package/docs/public-api.md +39 -0
- package/docs/security.md +21 -0
- package/docs/theming.md +35 -0
- package/docs/versioning.md +23 -0
- package/index.ts +3 -0
- package/package.json +592 -0
- package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
- package/src/components/icons/sputnigUiIcons.ts +530 -0
- package/src/components/layout/SAppShell.vue +232 -0
- package/src/components/layout/SDockRegion.vue +200 -0
- package/src/components/layout/SResizeHandle.vue +251 -0
- package/src/components/layout/SSideMenuButton.vue +104 -0
- package/src/components/layout/SSideMenuRail.vue +47 -0
- package/src/components/layout/SStack.css +25 -0
- package/src/components/layout/SStack.vue +57 -0
- package/src/components/layout/SStatusBarShell.css +15 -0
- package/src/components/layout/SStatusBarShell.vue +26 -0
- package/src/components/layout/SWorkbenchLayout.vue +53 -0
- package/src/components/layout/SWorkbenchPanelShell.css +161 -0
- package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
- package/src/components/layout/_internal/SAppBrand.css +45 -0
- package/src/components/layout/_internal/SAppBrand.vue +37 -0
- package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
- package/src/components/layout/types.ts +28 -0
- package/src/components/shared/_internal/SDialogSurface.css +127 -0
- package/src/components/shared/_internal/SDialogSurface.vue +117 -0
- package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
- package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
- package/src/components/shared/_internal/SListboxGroup.css +18 -0
- package/src/components/shared/_internal/SListboxGroup.vue +24 -0
- package/src/components/shared/_internal/SMenuItem.css +112 -0
- package/src/components/shared/_internal/SMenuItem.vue +213 -0
- package/src/components/shared/_internal/SMenuLayer.css +21 -0
- package/src/components/shared/_internal/SMenuLayer.vue +48 -0
- package/src/components/shared/_internal/SMenuSeparator.css +5 -0
- package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
- package/src/components/shared/_internal/SToastItem.css +65 -0
- package/src/components/shared/_internal/SToastItem.vue +58 -0
- package/src/components/shared/_internal/STreeNode.vue +761 -0
- package/src/components/shared/_internal/dialogSurface.ts +105 -0
- package/src/components/shared/_internal/focusTrap.ts +40 -0
- package/src/components/shared/_internal/listboxContext.ts +16 -0
- package/src/components/shared/_internal/markerContract.ts +10 -0
- package/src/components/shared/_internal/menuContext.ts +23 -0
- package/src/components/shared/_internal/selectionContract.ts +239 -0
- package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
- package/src/components/shared/_internal/tabsContext.ts +57 -0
- package/src/components/shared/_internal/treeRuntime.ts +203 -0
- package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
- package/src/components/shared/codeLanguages.ts +168 -0
- package/src/components/shared/complex/SKanbanCard.css +8 -0
- package/src/components/shared/complex/SKanbanCard.vue +76 -0
- package/src/components/shared/complex/SKanbanLane.vue +82 -0
- package/src/components/shared/complex/STree.css +24 -0
- package/src/components/shared/complex/STree.vue +288 -0
- package/src/components/shared/complex/treeAdapter.ts +210 -0
- package/src/components/shared/complex/types.ts +102 -0
- package/src/components/shared/containers/SCloudChrome.css +103 -0
- package/src/components/shared/containers/SCloudChrome.vue +87 -0
- package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
- package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
- package/src/components/shared/containers/SCloudChromeRow.css +78 -0
- package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
- package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
- package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
- package/src/components/shared/containers/SDrawer.css +60 -0
- package/src/components/shared/containers/SDrawer.vue +86 -0
- package/src/components/shared/containers/SFormGrid.css +26 -0
- package/src/components/shared/containers/SFormGrid.vue +46 -0
- package/src/components/shared/containers/SFormGridItem.css +18 -0
- package/src/components/shared/containers/SFormGridItem.vue +29 -0
- package/src/components/shared/containers/SLeftSidebar.vue +74 -0
- package/src/components/shared/containers/SModal.css +27 -0
- package/src/components/shared/containers/SModal.vue +105 -0
- package/src/components/shared/containers/SPageHeader.css +66 -0
- package/src/components/shared/containers/SPageHeader.vue +45 -0
- package/src/components/shared/containers/SPanel.css +134 -0
- package/src/components/shared/containers/SPanel.vue +169 -0
- package/src/components/shared/containers/SPopover.css +35 -0
- package/src/components/shared/containers/SPopover.vue +551 -0
- package/src/components/shared/containers/SSidebarGroup.css +12 -0
- package/src/components/shared/containers/SSidebarGroup.vue +443 -0
- package/src/components/shared/containers/SSidebarSection.css +41 -0
- package/src/components/shared/containers/SSidebarSection.vue +268 -0
- package/src/components/shared/containers/disclosureSummary.ts +48 -0
- package/src/components/shared/containers/popover.ts +72 -0
- package/src/components/shared/containers/sidebar.ts +206 -0
- package/src/components/shared/controls/SButton.css +274 -0
- package/src/components/shared/controls/SButton.vue +211 -0
- package/src/components/shared/controls/SCheckbox.css +65 -0
- package/src/components/shared/controls/SCheckbox.vue +130 -0
- package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
- package/src/components/shared/controls/SChoiceCards.css +29 -0
- package/src/components/shared/controls/SChoiceCards.vue +145 -0
- package/src/components/shared/controls/SColorSelect.vue +146 -0
- package/src/components/shared/controls/SComboboxTrigger.css +35 -0
- package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
- package/src/components/shared/controls/SContextMenu.css +11 -0
- package/src/components/shared/controls/SContextMenu.vue +186 -0
- package/src/components/shared/controls/SContextToggleButton.vue +64 -0
- package/src/components/shared/controls/SDragHandle.vue +100 -0
- package/src/components/shared/controls/SDropdownMenu.css +5 -0
- package/src/components/shared/controls/SDropdownMenu.vue +188 -0
- package/src/components/shared/controls/SField.css +79 -0
- package/src/components/shared/controls/SField.vue +89 -0
- package/src/components/shared/controls/SFieldGroup.css +58 -0
- package/src/components/shared/controls/SFieldGroup.vue +117 -0
- package/src/components/shared/controls/SFieldLabel.css +12 -0
- package/src/components/shared/controls/SFieldLabel.vue +51 -0
- package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
- package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
- package/src/components/shared/controls/SInputText.css +66 -0
- package/src/components/shared/controls/SInputText.vue +321 -0
- package/src/components/shared/controls/SInteractiveSurface.css +72 -0
- package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
- package/src/components/shared/controls/SLink.css +114 -0
- package/src/components/shared/controls/SLink.ts +85 -0
- package/src/components/shared/controls/SLink.vue +114 -0
- package/src/components/shared/controls/SListbox.css +31 -0
- package/src/components/shared/controls/SListbox.vue +388 -0
- package/src/components/shared/controls/SListboxOption.css +63 -0
- package/src/components/shared/controls/SListboxOption.vue +85 -0
- package/src/components/shared/controls/SMarker.css +19 -0
- package/src/components/shared/controls/SMarker.vue +33 -0
- package/src/components/shared/controls/SMenuSurface.css +20 -0
- package/src/components/shared/controls/SMenuSurface.vue +333 -0
- package/src/components/shared/controls/SSegmentedControl.css +38 -0
- package/src/components/shared/controls/SSegmentedControl.vue +134 -0
- package/src/components/shared/controls/SSelect.css +12 -0
- package/src/components/shared/controls/SSelect.vue +338 -0
- package/src/components/shared/controls/STextarea.css +31 -0
- package/src/components/shared/controls/STextarea.vue +316 -0
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
- package/src/components/shared/controls/menu.ts +178 -0
- package/src/components/shared/data-display/SActionCard.css +6 -0
- package/src/components/shared/data-display/SActionCard.vue +49 -0
- package/src/components/shared/data-display/SActionList.css +15 -0
- package/src/components/shared/data-display/SActionList.vue +34 -0
- package/src/components/shared/data-display/SActionListItem.css +114 -0
- package/src/components/shared/data-display/SActionListItem.vue +230 -0
- package/src/components/shared/data-display/SBadge.css +73 -0
- package/src/components/shared/data-display/SBadge.vue +52 -0
- package/src/components/shared/data-display/SCodeBlock.css +56 -0
- package/src/components/shared/data-display/SCodeBlock.vue +188 -0
- package/src/components/shared/data-display/SCodeEditor.css +178 -0
- package/src/components/shared/data-display/SCodeEditor.vue +274 -0
- package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
- package/src/components/shared/data-display/SCopyField.vue +77 -0
- package/src/components/shared/data-display/SDocBlock.css +136 -0
- package/src/components/shared/data-display/SDocBlock.vue +358 -0
- package/src/components/shared/data-display/SEmptyState.css +42 -0
- package/src/components/shared/data-display/SEmptyState.vue +79 -0
- package/src/components/shared/data-display/SExpandableText.vue +314 -0
- package/src/components/shared/data-display/SJsonTree.css +6 -0
- package/src/components/shared/data-display/SJsonTree.vue +111 -0
- package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
- package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
- package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
- package/src/components/shared/data-display/SMessage.css +88 -0
- package/src/components/shared/data-display/SMessage.vue +152 -0
- package/src/components/shared/data-display/SMetricCard.css +32 -0
- package/src/components/shared/data-display/SMetricCard.vue +117 -0
- package/src/components/shared/data-display/SProgressBar.css +84 -0
- package/src/components/shared/data-display/SProgressBar.vue +114 -0
- package/src/components/shared/data-display/SProgressIndicator.css +59 -0
- package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
- package/src/components/shared/data-display/SSectionHeader.css +84 -0
- package/src/components/shared/data-display/SSectionHeader.vue +58 -0
- package/src/components/shared/data-display/SStatus.css +90 -0
- package/src/components/shared/data-display/SStatus.vue +79 -0
- package/src/components/shared/data-display/STable.css +83 -0
- package/src/components/shared/data-display/STable.vue +339 -0
- package/src/components/shared/data-display/SToastContainer.css +53 -0
- package/src/components/shared/data-display/SToastContainer.vue +31 -0
- package/src/components/shared/data-display/STooltip.css +114 -0
- package/src/components/shared/data-display/STooltip.vue +550 -0
- package/src/components/shared/data-display/STooltipAnchor.css +21 -0
- package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
- package/src/components/shared/data-display/SVirtualList.css +24 -0
- package/src/components/shared/data-display/SVirtualList.ts +19 -0
- package/src/components/shared/data-display/SVirtualList.vue +361 -0
- package/src/components/shared/data-display/json.ts +136 -0
- package/src/components/shared/data-display/table.ts +76 -0
- package/src/components/shared/database/SDataGrid.css +3 -0
- package/src/components/shared/database/SDataGrid.vue +464 -0
- package/src/components/shared/database/SSqlEditor.vue +363 -0
- package/src/components/shared/database/dataGrid.ts +61 -0
- package/src/components/shared/feedback/SAsyncState.css +26 -0
- package/src/components/shared/feedback/SAsyncState.vue +190 -0
- package/src/components/shared/graph/SGraphViewport.ts +1 -0
- package/src/components/shared/graph/SGraphViewport.vue +858 -0
- package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
- package/src/components/shared/markdown.ts +169 -0
- package/src/components/shared/navigation/SBottomNav.vue +692 -0
- package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
- package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
- package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
- package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
- package/src/components/shared/navigation/STab.vue +363 -0
- package/src/components/shared/navigation/STabList.vue +442 -0
- package/src/components/shared/navigation/STabPanel.vue +165 -0
- package/src/components/shared/navigation/STabPanels.vue +33 -0
- package/src/components/shared/navigation/STabs.vue +617 -0
- package/src/components/shared/navigation/SWizardSteps.css +69 -0
- package/src/components/shared/navigation/SWizardSteps.vue +171 -0
- package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
- package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
- package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
- package/src/components/shared/persona/_internal/inventory.ts +110 -0
- package/src/components/shared/persona/_internal/labels.ts +79 -0
- package/src/components/shared/persona/presentation.ts +371 -0
- package/src/components/shared/persona/types.ts +256 -0
- package/src/composables/uiPreferencesReset.ts +52 -0
- package/src/composables/useClipboard.ts +37 -0
- package/src/composables/useDialogOverlay.ts +85 -0
- package/src/composables/useDisclosure.ts +40 -0
- package/src/composables/useFloatingPosition.ts +245 -0
- package/src/composables/useNotifier.ts +26 -0
- package/src/composables/useSidebarPanelState.ts +610 -0
- package/src/internal/codeEditorContract.ts +110 -0
- package/src/internal/codeEditorExtensions.ts +27 -0
- package/src/internal/feedbackPresentation.ts +136 -0
- package/src/internal/fieldSurface.ts +59 -0
- package/src/internal/focusNavigation.ts +69 -0
- package/src/internal/inlineTokenEditorContract.ts +136 -0
- package/src/internal/interactiveElement.ts +33 -0
- package/src/internal/layerStack.ts +202 -0
- package/src/internal/linkTarget.ts +256 -0
- package/src/internal/ownedAttrs.ts +464 -0
- package/src/internal/passiveContentContract.ts +129 -0
- package/src/internal/pointerInteractionLease.ts +81 -0
- package/src/internal/runtimeContract.ts +194 -0
- package/src/internal/runtimeTheme.ts +159 -0
- package/src/internal/semanticSizing.ts +60 -0
- package/src/internal/useMenuLayer.ts +129 -0
- package/src/stores/useNotifierStore.ts +69 -0
- package/src/styles/reference.css +4 -0
- package/src/styles/style.css +151 -0
- package/src/styles/tokens.css +836 -0
- package/src/theme.ts +449 -0
- package/src/types/highlight-js-lib.d.ts +76 -0
- package/src/variants.ts +15 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="placement === 'top'"
|
|
4
|
+
v-bind="ownedAttrs.bindings()"
|
|
5
|
+
class="s-tabs-header"
|
|
6
|
+
data-tabs-part="header"
|
|
7
|
+
:data-placement="placement"
|
|
8
|
+
:data-position="headerPosition"
|
|
9
|
+
:data-inset="inset"
|
|
10
|
+
:data-divider="divider"
|
|
11
|
+
:class="{
|
|
12
|
+
's-tabs-header--dividerless': !divider,
|
|
13
|
+
}"
|
|
14
|
+
>
|
|
15
|
+
<div class="s-tabs-header__content" :style="{ paddingRight: controlsPadding }">
|
|
16
|
+
<div
|
|
17
|
+
ref="tabListWrapperRef"
|
|
18
|
+
class="s-tab-list-wrapper"
|
|
19
|
+
:class="scrollClasses"
|
|
20
|
+
@wheel.passive="onWheelTabList"
|
|
21
|
+
>
|
|
22
|
+
<div
|
|
23
|
+
ref="tabListRef"
|
|
24
|
+
class="s-tab-list"
|
|
25
|
+
@scroll.passive="updateScrollState"
|
|
26
|
+
>
|
|
27
|
+
<div
|
|
28
|
+
v-if="$slots.leading"
|
|
29
|
+
class="s-tab-list__leading"
|
|
30
|
+
:role="leadingAriaLabel ? 'group' : undefined"
|
|
31
|
+
:aria-label="leadingAriaLabel"
|
|
32
|
+
>
|
|
33
|
+
<slot name="leading" />
|
|
34
|
+
</div>
|
|
35
|
+
<div
|
|
36
|
+
class="s-tab-list__tabs"
|
|
37
|
+
:id="tabListId"
|
|
38
|
+
role="tablist"
|
|
39
|
+
:aria-label="ariaLabel"
|
|
40
|
+
aria-orientation="horizontal"
|
|
41
|
+
>
|
|
42
|
+
<slot />
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
v-if="$slots.controls"
|
|
49
|
+
ref="controlsRef"
|
|
50
|
+
class="s-tabs-header__controls"
|
|
51
|
+
role="group"
|
|
52
|
+
:aria-label="controlsAriaLabel"
|
|
53
|
+
>
|
|
54
|
+
<slot name="controls" />
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div
|
|
58
|
+
v-else
|
|
59
|
+
v-bind="ownedAttrs.bindings()"
|
|
60
|
+
ref="tabListWrapperRef"
|
|
61
|
+
class="s-tab-list-wrapper"
|
|
62
|
+
:data-placement="placement"
|
|
63
|
+
:data-inset="inset"
|
|
64
|
+
:data-divider="divider"
|
|
65
|
+
:class="scrollClasses"
|
|
66
|
+
@wheel.passive="onWheelTabList"
|
|
67
|
+
>
|
|
68
|
+
<div
|
|
69
|
+
ref="tabListRef"
|
|
70
|
+
class="s-tab-list s-tab-list--bottom"
|
|
71
|
+
:style="{ paddingRight: controlsPadding }"
|
|
72
|
+
@scroll.passive="updateScrollState"
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
v-if="$slots.leading"
|
|
76
|
+
class="s-tab-list__leading"
|
|
77
|
+
:role="leadingAriaLabel ? 'group' : undefined"
|
|
78
|
+
:aria-label="leadingAriaLabel"
|
|
79
|
+
>
|
|
80
|
+
<slot name="leading" />
|
|
81
|
+
</div>
|
|
82
|
+
<div
|
|
83
|
+
class="s-tab-list__tabs"
|
|
84
|
+
:id="tabListId"
|
|
85
|
+
role="tablist"
|
|
86
|
+
:aria-label="ariaLabel"
|
|
87
|
+
aria-orientation="horizontal"
|
|
88
|
+
>
|
|
89
|
+
<slot />
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div
|
|
93
|
+
v-if="$slots.controls"
|
|
94
|
+
ref="controlsRef"
|
|
95
|
+
class="s-tabs-header__controls"
|
|
96
|
+
role="group"
|
|
97
|
+
:aria-label="controlsAriaLabel"
|
|
98
|
+
>
|
|
99
|
+
<slot name="controls" />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</template>
|
|
103
|
+
|
|
104
|
+
<script setup lang="ts">
|
|
105
|
+
import {
|
|
106
|
+
computed,
|
|
107
|
+
inject,
|
|
108
|
+
nextTick,
|
|
109
|
+
onBeforeUnmount,
|
|
110
|
+
onMounted,
|
|
111
|
+
onUpdated,
|
|
112
|
+
ref,
|
|
113
|
+
watch,
|
|
114
|
+
type VNodeChild,
|
|
115
|
+
} from 'vue'
|
|
116
|
+
import {
|
|
117
|
+
tabsContextKey,
|
|
118
|
+
type STabsContext,
|
|
119
|
+
} from '../_internal/tabsContext'
|
|
120
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
121
|
+
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
122
|
+
|
|
123
|
+
defineOptions({
|
|
124
|
+
inheritAttrs: false,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Публичный layout/overflow owner для tablist и внешних action regions.
|
|
129
|
+
* Public layout and overflow owner for the tablist and external action regions.
|
|
130
|
+
*/
|
|
131
|
+
export interface Props {
|
|
132
|
+
/** Размещение list относительно panels. / List placement relative to panels. */
|
|
133
|
+
placement?: 'top' | 'bottom'
|
|
134
|
+
/** Показывает разделитель header. / Shows the header divider. */
|
|
135
|
+
divider?: boolean
|
|
136
|
+
/** Добавляет внутренний inline inset для scroll row. / Adds an inline inset to the scroll row. */
|
|
137
|
+
inset?: 'none' | 'compact' | 'content'
|
|
138
|
+
/** Sticky фиксирует header в scroll owner, static оставляет его в потоке. / Sticky pins the header in its scroll owner; static keeps it in flow. */
|
|
139
|
+
headerPosition?: 'sticky' | 'static'
|
|
140
|
+
/** Accessible name role=tablist. / Accessible name for role=tablist. */
|
|
141
|
+
ariaLabel?: string
|
|
142
|
+
/** Стабильный id tablist для aria-controls внешних owners. / Stable tablist id for external aria-controls owners. */
|
|
143
|
+
tabListId?: string
|
|
144
|
+
/** Accessible name leading controls; включает role=group. / Accessible name for leading controls; enables role=group. */
|
|
145
|
+
leadingAriaLabel?: string
|
|
146
|
+
/** Accessible name header actions. / Accessible name for header actions. */
|
|
147
|
+
controlsAriaLabel?: string
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
151
|
+
placement: 'top',
|
|
152
|
+
divider: true,
|
|
153
|
+
inset: 'none',
|
|
154
|
+
headerPosition: 'sticky',
|
|
155
|
+
ariaLabel: 'Вкладки',
|
|
156
|
+
tabListId: undefined,
|
|
157
|
+
leadingAriaLabel: undefined,
|
|
158
|
+
controlsAriaLabel: 'Действия вкладок',
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
watch(
|
|
162
|
+
() => props.tabListId,
|
|
163
|
+
(tabListId) => {
|
|
164
|
+
if (tabListId !== undefined && (!tabListId.trim() || tabListId !== tabListId.trim())) {
|
|
165
|
+
throw new TypeError('STabList: tabListId must be a non-empty string without surrounding whitespace')
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{ immediate: true },
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
defineSlots<{
|
|
172
|
+
/** STab compositions, зарегистрированные текущим owner. / STab compositions registered by the current owner. */
|
|
173
|
+
default?: () => VNodeChild
|
|
174
|
+
/** Controls before tablist semantics. / Controls before tablist semantics. */
|
|
175
|
+
leading?: () => VNodeChild
|
|
176
|
+
/** Header actions outside tablist semantics. / Header actions outside tablist semantics. */
|
|
177
|
+
controls?: () => VNodeChild
|
|
178
|
+
}>()
|
|
179
|
+
const ownedAttrs = useOwnedAttrs({ component: 'STabList', owner: 'active tab-list layout root' })
|
|
180
|
+
useInteractiveLeafRegistration({ owner: 'STabList' })
|
|
181
|
+
|
|
182
|
+
const maybeCtx = inject<STabsContext>(tabsContextKey)
|
|
183
|
+
if (!maybeCtx) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
'STabList должен использоваться внутри STabs. / STabList must be used inside STabs.',
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
const ctx = maybeCtx
|
|
189
|
+
|
|
190
|
+
const controlsRef = ref<HTMLElement | null>(null)
|
|
191
|
+
const controlsWidth = ref(0)
|
|
192
|
+
const tabListWrapperRef = ref<HTMLElement | null>(null)
|
|
193
|
+
const tabListRef = ref<HTMLElement | null>(null)
|
|
194
|
+
const isScrollable = ref(false)
|
|
195
|
+
const isScrolledToStart = ref(true)
|
|
196
|
+
const isScrolledToEnd = ref(false)
|
|
197
|
+
|
|
198
|
+
let resizeObserver: ResizeObserver | null = null
|
|
199
|
+
let mutationObserver: MutationObserver | null = null
|
|
200
|
+
let controlsResizeObserver: ResizeObserver | null = null
|
|
201
|
+
let observedControlsElement: HTMLElement | null = null
|
|
202
|
+
const animationFrameIds = new Set<number>()
|
|
203
|
+
|
|
204
|
+
const controlsPadding = computed(() => {
|
|
205
|
+
if (!controlsWidth.value) return '0px'
|
|
206
|
+
return `${controlsWidth.value + 8}px`
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const scrollClasses = computed(() => ({
|
|
210
|
+
'is-scrollable': isScrollable.value,
|
|
211
|
+
'is-scrolled-to-start': isScrolledToStart.value,
|
|
212
|
+
'is-scrolled-to-end': isScrolledToEnd.value,
|
|
213
|
+
}))
|
|
214
|
+
|
|
215
|
+
const getListEl = (): HTMLElement | null => tabListRef.value
|
|
216
|
+
|
|
217
|
+
const updateControlsWidth = (): void => {
|
|
218
|
+
controlsWidth.value = controlsRef.value?.offsetWidth ?? 0
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const syncControlsResizeObserver = (): void => {
|
|
222
|
+
const nextElement = controlsRef.value
|
|
223
|
+
if (nextElement === observedControlsElement) return
|
|
224
|
+
controlsResizeObserver?.disconnect()
|
|
225
|
+
controlsResizeObserver = null
|
|
226
|
+
observedControlsElement = nextElement
|
|
227
|
+
if (typeof ResizeObserver !== 'undefined' && nextElement) {
|
|
228
|
+
controlsResizeObserver = new ResizeObserver(updateControlsWidth)
|
|
229
|
+
controlsResizeObserver.observe(nextElement)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const scheduleAnimationFrame = (callback: () => void): void => {
|
|
234
|
+
if (typeof requestAnimationFrame === 'undefined') {
|
|
235
|
+
callback()
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
let frameId = -1
|
|
239
|
+
let completedSynchronously = false
|
|
240
|
+
frameId = requestAnimationFrame(() => {
|
|
241
|
+
completedSynchronously = true
|
|
242
|
+
if (frameId >= 0) animationFrameIds.delete(frameId)
|
|
243
|
+
callback()
|
|
244
|
+
})
|
|
245
|
+
if (!completedSynchronously) animationFrameIds.add(frameId)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const updateScrollState = (): void => {
|
|
249
|
+
const element = getListEl()
|
|
250
|
+
if (!element) return
|
|
251
|
+
|
|
252
|
+
const scrollable = element.scrollWidth > element.clientWidth
|
|
253
|
+
isScrollable.value = scrollable
|
|
254
|
+
if (scrollable) {
|
|
255
|
+
isScrolledToStart.value = element.scrollLeft <= 1
|
|
256
|
+
isScrolledToEnd.value = element.scrollWidth - element.scrollLeft - element.clientWidth <= 1
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
isScrolledToStart.value = true
|
|
261
|
+
isScrolledToEnd.value = true
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const scrollActiveIntoCenterDom = (): void => {
|
|
265
|
+
const listElement = getListEl()
|
|
266
|
+
if (!listElement) return
|
|
267
|
+
const activeTabElement = ctx.getActiveTabElement()
|
|
268
|
+
if (!activeTabElement) return
|
|
269
|
+
const tabCenter = activeTabElement.offsetLeft + activeTabElement.offsetWidth / 2
|
|
270
|
+
const targetLeft = Math.max(0, tabCenter - listElement.clientWidth / 2)
|
|
271
|
+
if (typeof listElement.scrollTo === 'function') {
|
|
272
|
+
listElement.scrollTo({ left: targetLeft, behavior: 'smooth' })
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
listElement.scrollLeft = targetLeft
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const tabListRegistrationKey = ctx.registerTabList(scrollActiveIntoCenterDom)
|
|
279
|
+
|
|
280
|
+
const onWheelTabList = (event: WheelEvent): void => {
|
|
281
|
+
const element = getListEl()
|
|
282
|
+
if (!element) return
|
|
283
|
+
const delta = Math.abs(event.deltaY) >= Math.abs(event.deltaX) ? event.deltaY : event.deltaX
|
|
284
|
+
if (delta === 0) return
|
|
285
|
+
if (typeof element.scrollBy === 'function') {
|
|
286
|
+
element.scrollBy({ left: delta, behavior: 'auto' })
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
element.scrollLeft += delta
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
onMounted(() => {
|
|
293
|
+
updateControlsWidth()
|
|
294
|
+
syncControlsResizeObserver()
|
|
295
|
+
void nextTick(() => {
|
|
296
|
+
updateScrollState()
|
|
297
|
+
const listElement = getListEl()
|
|
298
|
+
if (!listElement) return
|
|
299
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
300
|
+
resizeObserver = new ResizeObserver(updateScrollState)
|
|
301
|
+
resizeObserver.observe(listElement)
|
|
302
|
+
}
|
|
303
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
304
|
+
mutationObserver = new MutationObserver(() => {
|
|
305
|
+
updateScrollState()
|
|
306
|
+
scheduleAnimationFrame(scrollActiveIntoCenterDom)
|
|
307
|
+
})
|
|
308
|
+
mutationObserver.observe(listElement, {
|
|
309
|
+
characterData: true,
|
|
310
|
+
childList: true,
|
|
311
|
+
subtree: true,
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
scheduleAnimationFrame(scrollActiveIntoCenterDom)
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
onUpdated(() => {
|
|
319
|
+
updateControlsWidth()
|
|
320
|
+
syncControlsResizeObserver()
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
watch(() => ctx.activeKey.value, () => {
|
|
324
|
+
void nextTick(() => scheduleAnimationFrame(scrollActiveIntoCenterDom))
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
defineExpose({
|
|
328
|
+
scrollActiveIntoCenter: scrollActiveIntoCenterDom,
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
onBeforeUnmount(() => {
|
|
332
|
+
resizeObserver?.disconnect()
|
|
333
|
+
mutationObserver?.disconnect()
|
|
334
|
+
controlsResizeObserver?.disconnect()
|
|
335
|
+
observedControlsElement = null
|
|
336
|
+
if (typeof cancelAnimationFrame !== 'undefined') {
|
|
337
|
+
for (const frameId of animationFrameIds) cancelAnimationFrame(frameId)
|
|
338
|
+
}
|
|
339
|
+
animationFrameIds.clear()
|
|
340
|
+
ctx.unregisterTabList(tabListRegistrationKey)
|
|
341
|
+
})
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<style lang="postcss" scoped>
|
|
345
|
+
@reference "../../../styles/reference.css";
|
|
346
|
+
|
|
347
|
+
.s-tabs-header {
|
|
348
|
+
@apply sticky top-0 border-b border-surface-200 dark:border-surface-700;
|
|
349
|
+
z-index: var(--s-layer-affordance);
|
|
350
|
+
height: var(--s-tab-row-height);
|
|
351
|
+
min-height: var(--s-tab-row-height);
|
|
352
|
+
max-height: var(--s-tab-row-height);
|
|
353
|
+
background: inherit;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.s-tabs-header--dividerless { border-bottom: 0; }
|
|
357
|
+
.s-tabs-header[data-position='static'] { position: relative; }
|
|
358
|
+
|
|
359
|
+
[data-inset='compact'] { --s-tab-list-inset-inline: 0.75rem; }
|
|
360
|
+
[data-inset='content'] { --s-tab-list-inset-inline: 1rem; }
|
|
361
|
+
|
|
362
|
+
@media (min-width: 1024px) {
|
|
363
|
+
[data-inset='content'] { --s-tab-list-inset-inline: 1.25rem; }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.s-tabs-header__content { @apply h-full; }
|
|
367
|
+
|
|
368
|
+
.s-tabs-header__controls {
|
|
369
|
+
@apply absolute inset-y-0 right-0 flex items-center gap-1 px-2;
|
|
370
|
+
z-index: var(--s-layer-affordance);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.s-tab-list-wrapper {
|
|
374
|
+
@apply relative h-full shrink-0 overflow-hidden;
|
|
375
|
+
overscroll-behavior: contain;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.s-tab-list-wrapper[data-placement='bottom'] {
|
|
379
|
+
height: var(--s-tab-row-height);
|
|
380
|
+
min-height: var(--s-tab-row-height);
|
|
381
|
+
max-height: var(--s-tab-row-height);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.s-tab-list-wrapper::before,
|
|
385
|
+
.s-tab-list-wrapper::after {
|
|
386
|
+
@apply pointer-events-none absolute inset-y-0 opacity-0 transition-opacity;
|
|
387
|
+
z-index: var(--s-layer-sticky);
|
|
388
|
+
transition-duration: var(--s-motion-standard);
|
|
389
|
+
content: '';
|
|
390
|
+
width: var(--s-tab-overflow-fade-width);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.s-tab-list-wrapper::before {
|
|
394
|
+
left: 0;
|
|
395
|
+
background: linear-gradient(to right, var(--s-tab-list-edge-surface), transparent);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.s-tab-list-wrapper::after {
|
|
399
|
+
right: 0;
|
|
400
|
+
background: linear-gradient(to left, var(--s-tab-list-edge-surface), transparent);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.s-tab-list-wrapper.is-scrollable:not(.is-scrolled-to-start)::before,
|
|
404
|
+
.s-tab-list-wrapper.is-scrollable:not(.is-scrolled-to-end)::after { opacity: var(--s-tab-overflow-edge-opacity); }
|
|
405
|
+
|
|
406
|
+
.s-tab-list {
|
|
407
|
+
@apply flex h-full min-w-0 flex-nowrap items-center overflow-x-auto overflow-y-hidden;
|
|
408
|
+
padding-right: var(--s-tab-list-inset-inline);
|
|
409
|
+
padding-left: var(--s-tab-list-inset-inline);
|
|
410
|
+
scroll-padding-inline: var(--s-tab-list-inset-inline);
|
|
411
|
+
scrollbar-width: none;
|
|
412
|
+
-ms-overflow-style: none;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.s-tab-list::-webkit-scrollbar { display: none; }
|
|
416
|
+
.s-tab-list--bottom { @apply justify-between px-2; }
|
|
417
|
+
|
|
418
|
+
.s-tab-list__leading,
|
|
419
|
+
.s-tab-list__tabs {
|
|
420
|
+
@apply flex h-full min-w-max flex-none items-center;
|
|
421
|
+
width: var(--s-tab-list-tabs-width);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.s-tab-list--bottom .s-tab-list__tabs {
|
|
425
|
+
@apply min-w-full flex-1 justify-between;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.is-scrollable.is-scrolled-to-start:not(.is-scrolled-to-end) .s-tab-list {
|
|
429
|
+
-webkit-mask-image: linear-gradient(to right, var(--s-tab-overflow-mask-opaque) calc(100% - var(--s-tab-overflow-fade-width)), transparent 100%);
|
|
430
|
+
mask-image: linear-gradient(to right, var(--s-tab-overflow-mask-opaque) calc(100% - var(--s-tab-overflow-fade-width)), transparent 100%);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.is-scrollable:not(.is-scrolled-to-start).is-scrolled-to-end .s-tab-list {
|
|
434
|
+
-webkit-mask-image: linear-gradient(to right, transparent 0, var(--s-tab-overflow-mask-opaque) var(--s-tab-overflow-fade-width));
|
|
435
|
+
mask-image: linear-gradient(to right, transparent 0, var(--s-tab-overflow-mask-opaque) var(--s-tab-overflow-fade-width));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.is-scrollable:not(.is-scrolled-to-start):not(.is-scrolled-to-end) .s-tab-list {
|
|
439
|
+
-webkit-mask-image: linear-gradient(to right, transparent 0, var(--s-tab-overflow-mask-opaque) var(--s-tab-overflow-fade-width), var(--s-tab-overflow-mask-opaque) calc(100% - var(--s-tab-overflow-fade-width)), transparent 100%);
|
|
440
|
+
mask-image: linear-gradient(to right, transparent 0, var(--s-tab-overflow-mask-opaque) var(--s-tab-overflow-fade-width), var(--s-tab-overflow-mask-opaque) calc(100% - var(--s-tab-overflow-fade-width)), transparent 100%);
|
|
441
|
+
}
|
|
442
|
+
</style>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="unmount ? state.active : true"
|
|
4
|
+
v-bind="ownedAttrs.bindings()"
|
|
5
|
+
ref="panelElement"
|
|
6
|
+
v-show="unmount ? true : state.active"
|
|
7
|
+
class="s-tab-panel"
|
|
8
|
+
:id="state.panelId"
|
|
9
|
+
:aria-labelledby="state.tabId"
|
|
10
|
+
:aria-hidden="state.active ? undefined : 'true'"
|
|
11
|
+
:data-surface="surface"
|
|
12
|
+
:data-content-gap="contentGap"
|
|
13
|
+
:data-align="align"
|
|
14
|
+
:data-padding="padding"
|
|
15
|
+
:data-overflow="overflow"
|
|
16
|
+
role="tabpanel"
|
|
17
|
+
tabindex="0"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import {
|
|
25
|
+
computed,
|
|
26
|
+
inject,
|
|
27
|
+
onBeforeUnmount,
|
|
28
|
+
onMounted,
|
|
29
|
+
onUpdated,
|
|
30
|
+
ref,
|
|
31
|
+
watchEffect,
|
|
32
|
+
type VNodeChild,
|
|
33
|
+
} from 'vue'
|
|
34
|
+
import { tabsContextKey, type STabsContext } from '../_internal/tabsContext'
|
|
35
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
36
|
+
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
37
|
+
|
|
38
|
+
defineOptions({
|
|
39
|
+
inheritAttrs: false,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Публичная panel-часть STabs со стабильной ARIA-связью с одноимённой вкладкой.
|
|
44
|
+
* Public STabs panel part with a stable ARIA relationship to the matching tab.
|
|
45
|
+
*/
|
|
46
|
+
export interface Props {
|
|
47
|
+
/** Точная stable identity соответствующего STab. / Exact stable identity of the matching STab. */
|
|
48
|
+
name: string
|
|
49
|
+
/** Удаляет неактивное содержимое из DOM вместо v-show. / Removes inactive content from the DOM instead of using v-show. */
|
|
50
|
+
unmount?: boolean
|
|
51
|
+
/** Владеющая layout-поверхность панели. / Layout surface owned by the panel. */
|
|
52
|
+
surface?: 'default' | 'workbench-right-drawer' | 'workbench-bottom-dock'
|
|
53
|
+
/** Семантический интервал между непосредственными участниками. / Semantic gap between direct participants. */
|
|
54
|
+
contentGap?: 'none' | 'control' | 'content' | 'section' | 'panel'
|
|
55
|
+
/** Поперечное выравнивание участников панели. / Cross-axis alignment of panel participants. */
|
|
56
|
+
align?: 'stretch' | 'start' | 'center' | 'end'
|
|
57
|
+
/** Семантический внутренний отступ panel surface. / Semantic panel-surface padding. */
|
|
58
|
+
padding?: 'none' | 'control' | 'content' | 'section' | 'panel'
|
|
59
|
+
/** Политика переполнения panel surface. / Panel-surface overflow policy. */
|
|
60
|
+
overflow?: 'visible' | 'hidden' | 'auto'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
64
|
+
unmount: false,
|
|
65
|
+
surface: 'default',
|
|
66
|
+
contentGap: 'none',
|
|
67
|
+
align: 'stretch',
|
|
68
|
+
padding: 'none',
|
|
69
|
+
overflow: 'visible',
|
|
70
|
+
})
|
|
71
|
+
const ownedAttrs = useOwnedAttrs({ component: 'STabPanel', owner: 'mounted role=tabpanel root' })
|
|
72
|
+
defineSlots<{
|
|
73
|
+
/** Содержимое панели. / Panel content. */
|
|
74
|
+
default?: () => VNodeChild
|
|
75
|
+
}>()
|
|
76
|
+
|
|
77
|
+
const maybeCtx = inject<STabsContext>(tabsContextKey)
|
|
78
|
+
if (!maybeCtx) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
'STabPanel должен использоваться внутри STabs. / STabPanel must be used inside STabs.',
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
const ctx = maybeCtx
|
|
84
|
+
const panelElement = ref<HTMLElement | null>(null)
|
|
85
|
+
const registrationKey = ctx.registerPanel({
|
|
86
|
+
tabKey: props.name,
|
|
87
|
+
element: () => panelElement.value,
|
|
88
|
+
})
|
|
89
|
+
const state = computed(() => ctx.getPanelState(registrationKey))
|
|
90
|
+
useInteractiveLeafRegistration({ owner: 'STabPanel', active: () => state.value.active })
|
|
91
|
+
|
|
92
|
+
watchEffect(() => {
|
|
93
|
+
if (props.unmount && !state.value.active) {
|
|
94
|
+
ownedAttrs.reject('inactive unmounted tab panel has no DOM owner')
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
ownedAttrs.bindings()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
onMounted(ctx.notifyPanelOrderChanged)
|
|
101
|
+
onUpdated(() => {
|
|
102
|
+
if (props.name !== registrationKey) {
|
|
103
|
+
throw new Error('STabPanel: name is immutable for the lifetime of a mounted panel')
|
|
104
|
+
}
|
|
105
|
+
ctx.notifyPanelOrderChanged()
|
|
106
|
+
})
|
|
107
|
+
onBeforeUnmount(() => ctx.unregisterPanel(registrationKey))
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="postcss" scoped>
|
|
111
|
+
@reference "../../../styles/reference.css";
|
|
112
|
+
|
|
113
|
+
.s-tab-panel {
|
|
114
|
+
@apply flex h-full min-h-0 min-w-0 w-full flex-col text-surface-700 focus:outline-none;
|
|
115
|
+
@apply dark:text-surface-300;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.s-tab-panel[data-align='start'] { @apply items-start; }
|
|
119
|
+
.s-tab-panel[data-align='center'] { @apply items-center; }
|
|
120
|
+
.s-tab-panel[data-align='end'] { @apply items-end; }
|
|
121
|
+
.s-tab-panel[data-align='stretch'] { @apply items-stretch; }
|
|
122
|
+
|
|
123
|
+
.s-tab-panel[data-content-gap='control'] { gap: var(--s-space-control-gap); }
|
|
124
|
+
.s-tab-panel[data-content-gap='content'] { gap: var(--s-space-content-gap); }
|
|
125
|
+
.s-tab-panel[data-content-gap='section'] { gap: var(--s-space-section-gap); }
|
|
126
|
+
.s-tab-panel[data-content-gap='panel'] { gap: var(--s-space-panel-gap); }
|
|
127
|
+
|
|
128
|
+
.s-tab-panel[data-padding='control'] { padding: var(--s-space-control-gap); }
|
|
129
|
+
.s-tab-panel[data-padding='content'] { padding: var(--s-space-content-gap); }
|
|
130
|
+
.s-tab-panel[data-padding='section'] { padding: var(--s-space-section-gap); }
|
|
131
|
+
.s-tab-panel[data-padding='panel'] { padding: var(--s-space-panel-gap); }
|
|
132
|
+
|
|
133
|
+
.s-tab-panel[data-overflow='visible'] { overflow: visible; }
|
|
134
|
+
.s-tab-panel[data-overflow='hidden'] { overflow: hidden; }
|
|
135
|
+
.s-tab-panel[data-overflow='auto'] {
|
|
136
|
+
overflow: auto;
|
|
137
|
+
overscroll-behavior: contain;
|
|
138
|
+
scrollbar-gutter: stable;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.s-tab-panel[data-surface='workbench-right-drawer'],
|
|
142
|
+
.s-tab-panel[data-surface='workbench-bottom-dock'] {
|
|
143
|
+
@apply flex-1 overflow-y-auto px-5;
|
|
144
|
+
overscroll-behavior: contain;
|
|
145
|
+
scrollbar-gutter: stable;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.s-tab-panel[data-surface='workbench-right-drawer'] {
|
|
149
|
+
@apply pb-7 pt-4;
|
|
150
|
+
scroll-padding-block-start: 1rem;
|
|
151
|
+
scroll-padding-block-end: calc(var(--workbench-scroll-end-padding, 1rem) + 1rem);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.s-tab-panel[data-surface='workbench-bottom-dock'] {
|
|
155
|
+
@apply pb-6 pt-5;
|
|
156
|
+
scroll-padding-block-start: 1.25rem;
|
|
157
|
+
scroll-padding-block-end: calc(var(--workbench-scroll-end-padding, 1rem) + 1rem);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@media (max-width: 1023px) {
|
|
161
|
+
.s-tab-panel[data-surface='workbench-bottom-dock'] {
|
|
162
|
+
scroll-padding-block-end: calc(var(--workbench-scroll-end-padding, 1rem) + 1.5rem);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-bind="ownedAttrs.bindings()" class="s-tab-panels">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { inject, onBeforeUnmount, type VNodeChild } from 'vue'
|
|
9
|
+
import { tabsContextKey, type STabsContext } from '../_internal/tabsContext'
|
|
10
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
11
|
+
|
|
12
|
+
defineOptions({ inheritAttrs: false })
|
|
13
|
+
|
|
14
|
+
defineSlots<{
|
|
15
|
+
/** Набор STabPanel одного owner. / STabPanel collection owned by one STabs. */
|
|
16
|
+
default?: () => VNodeChild
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
const ownedAttrs = useOwnedAttrs({ component: 'STabPanels', owner: 'tab panels stack root' })
|
|
20
|
+
const context = inject<STabsContext>(tabsContextKey)
|
|
21
|
+
if (!context) throw new Error('STabPanels must be used inside STabs')
|
|
22
|
+
const registrationKey = context.registerPanels()
|
|
23
|
+
onBeforeUnmount(() => context.unregisterPanels(registrationKey))
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<style lang="postcss" scoped>
|
|
27
|
+
@reference "../../../styles/reference.css";
|
|
28
|
+
|
|
29
|
+
.s-tab-panels {
|
|
30
|
+
@apply flex min-h-0 min-w-0 w-full flex-col;
|
|
31
|
+
flex: var(--s-tab-panels-flex);
|
|
32
|
+
}
|
|
33
|
+
</style>
|