@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.
Files changed (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/docs/accessibility.md +27 -0
  4. package/docs/getting-started.md +80 -0
  5. package/docs/licensing.md +30 -0
  6. package/docs/public-api.md +39 -0
  7. package/docs/security.md +21 -0
  8. package/docs/theming.md +35 -0
  9. package/docs/versioning.md +23 -0
  10. package/index.ts +3 -0
  11. package/package.json +592 -0
  12. package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
  13. package/src/components/icons/sputnigUiIcons.ts +530 -0
  14. package/src/components/layout/SAppShell.vue +232 -0
  15. package/src/components/layout/SDockRegion.vue +200 -0
  16. package/src/components/layout/SResizeHandle.vue +251 -0
  17. package/src/components/layout/SSideMenuButton.vue +104 -0
  18. package/src/components/layout/SSideMenuRail.vue +47 -0
  19. package/src/components/layout/SStack.css +25 -0
  20. package/src/components/layout/SStack.vue +57 -0
  21. package/src/components/layout/SStatusBarShell.css +15 -0
  22. package/src/components/layout/SStatusBarShell.vue +26 -0
  23. package/src/components/layout/SWorkbenchLayout.vue +53 -0
  24. package/src/components/layout/SWorkbenchPanelShell.css +161 -0
  25. package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
  26. package/src/components/layout/_internal/SAppBrand.css +45 -0
  27. package/src/components/layout/_internal/SAppBrand.vue +37 -0
  28. package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
  29. package/src/components/layout/types.ts +28 -0
  30. package/src/components/shared/_internal/SDialogSurface.css +127 -0
  31. package/src/components/shared/_internal/SDialogSurface.vue +117 -0
  32. package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
  33. package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
  34. package/src/components/shared/_internal/SListboxGroup.css +18 -0
  35. package/src/components/shared/_internal/SListboxGroup.vue +24 -0
  36. package/src/components/shared/_internal/SMenuItem.css +112 -0
  37. package/src/components/shared/_internal/SMenuItem.vue +213 -0
  38. package/src/components/shared/_internal/SMenuLayer.css +21 -0
  39. package/src/components/shared/_internal/SMenuLayer.vue +48 -0
  40. package/src/components/shared/_internal/SMenuSeparator.css +5 -0
  41. package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
  42. package/src/components/shared/_internal/SToastItem.css +65 -0
  43. package/src/components/shared/_internal/SToastItem.vue +58 -0
  44. package/src/components/shared/_internal/STreeNode.vue +761 -0
  45. package/src/components/shared/_internal/dialogSurface.ts +105 -0
  46. package/src/components/shared/_internal/focusTrap.ts +40 -0
  47. package/src/components/shared/_internal/listboxContext.ts +16 -0
  48. package/src/components/shared/_internal/markerContract.ts +10 -0
  49. package/src/components/shared/_internal/menuContext.ts +23 -0
  50. package/src/components/shared/_internal/selectionContract.ts +239 -0
  51. package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
  52. package/src/components/shared/_internal/tabsContext.ts +57 -0
  53. package/src/components/shared/_internal/treeRuntime.ts +203 -0
  54. package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
  55. package/src/components/shared/codeLanguages.ts +168 -0
  56. package/src/components/shared/complex/SKanbanCard.css +8 -0
  57. package/src/components/shared/complex/SKanbanCard.vue +76 -0
  58. package/src/components/shared/complex/SKanbanLane.vue +82 -0
  59. package/src/components/shared/complex/STree.css +24 -0
  60. package/src/components/shared/complex/STree.vue +288 -0
  61. package/src/components/shared/complex/treeAdapter.ts +210 -0
  62. package/src/components/shared/complex/types.ts +102 -0
  63. package/src/components/shared/containers/SCloudChrome.css +103 -0
  64. package/src/components/shared/containers/SCloudChrome.vue +87 -0
  65. package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
  66. package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
  67. package/src/components/shared/containers/SCloudChromeRow.css +78 -0
  68. package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
  69. package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
  70. package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
  71. package/src/components/shared/containers/SDrawer.css +60 -0
  72. package/src/components/shared/containers/SDrawer.vue +86 -0
  73. package/src/components/shared/containers/SFormGrid.css +26 -0
  74. package/src/components/shared/containers/SFormGrid.vue +46 -0
  75. package/src/components/shared/containers/SFormGridItem.css +18 -0
  76. package/src/components/shared/containers/SFormGridItem.vue +29 -0
  77. package/src/components/shared/containers/SLeftSidebar.vue +74 -0
  78. package/src/components/shared/containers/SModal.css +27 -0
  79. package/src/components/shared/containers/SModal.vue +105 -0
  80. package/src/components/shared/containers/SPageHeader.css +66 -0
  81. package/src/components/shared/containers/SPageHeader.vue +45 -0
  82. package/src/components/shared/containers/SPanel.css +134 -0
  83. package/src/components/shared/containers/SPanel.vue +169 -0
  84. package/src/components/shared/containers/SPopover.css +35 -0
  85. package/src/components/shared/containers/SPopover.vue +551 -0
  86. package/src/components/shared/containers/SSidebarGroup.css +12 -0
  87. package/src/components/shared/containers/SSidebarGroup.vue +443 -0
  88. package/src/components/shared/containers/SSidebarSection.css +41 -0
  89. package/src/components/shared/containers/SSidebarSection.vue +268 -0
  90. package/src/components/shared/containers/disclosureSummary.ts +48 -0
  91. package/src/components/shared/containers/popover.ts +72 -0
  92. package/src/components/shared/containers/sidebar.ts +206 -0
  93. package/src/components/shared/controls/SButton.css +274 -0
  94. package/src/components/shared/controls/SButton.vue +211 -0
  95. package/src/components/shared/controls/SCheckbox.css +65 -0
  96. package/src/components/shared/controls/SCheckbox.vue +130 -0
  97. package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
  98. package/src/components/shared/controls/SChoiceCards.css +29 -0
  99. package/src/components/shared/controls/SChoiceCards.vue +145 -0
  100. package/src/components/shared/controls/SColorSelect.vue +146 -0
  101. package/src/components/shared/controls/SComboboxTrigger.css +35 -0
  102. package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
  103. package/src/components/shared/controls/SContextMenu.css +11 -0
  104. package/src/components/shared/controls/SContextMenu.vue +186 -0
  105. package/src/components/shared/controls/SContextToggleButton.vue +64 -0
  106. package/src/components/shared/controls/SDragHandle.vue +100 -0
  107. package/src/components/shared/controls/SDropdownMenu.css +5 -0
  108. package/src/components/shared/controls/SDropdownMenu.vue +188 -0
  109. package/src/components/shared/controls/SField.css +79 -0
  110. package/src/components/shared/controls/SField.vue +89 -0
  111. package/src/components/shared/controls/SFieldGroup.css +58 -0
  112. package/src/components/shared/controls/SFieldGroup.vue +117 -0
  113. package/src/components/shared/controls/SFieldLabel.css +12 -0
  114. package/src/components/shared/controls/SFieldLabel.vue +51 -0
  115. package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
  116. package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
  117. package/src/components/shared/controls/SInputText.css +66 -0
  118. package/src/components/shared/controls/SInputText.vue +321 -0
  119. package/src/components/shared/controls/SInteractiveSurface.css +72 -0
  120. package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
  121. package/src/components/shared/controls/SLink.css +114 -0
  122. package/src/components/shared/controls/SLink.ts +85 -0
  123. package/src/components/shared/controls/SLink.vue +114 -0
  124. package/src/components/shared/controls/SListbox.css +31 -0
  125. package/src/components/shared/controls/SListbox.vue +388 -0
  126. package/src/components/shared/controls/SListboxOption.css +63 -0
  127. package/src/components/shared/controls/SListboxOption.vue +85 -0
  128. package/src/components/shared/controls/SMarker.css +19 -0
  129. package/src/components/shared/controls/SMarker.vue +33 -0
  130. package/src/components/shared/controls/SMenuSurface.css +20 -0
  131. package/src/components/shared/controls/SMenuSurface.vue +333 -0
  132. package/src/components/shared/controls/SSegmentedControl.css +38 -0
  133. package/src/components/shared/controls/SSegmentedControl.vue +134 -0
  134. package/src/components/shared/controls/SSelect.css +12 -0
  135. package/src/components/shared/controls/SSelect.vue +338 -0
  136. package/src/components/shared/controls/STextarea.css +31 -0
  137. package/src/components/shared/controls/STextarea.vue +316 -0
  138. package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
  139. package/src/components/shared/controls/menu.ts +178 -0
  140. package/src/components/shared/data-display/SActionCard.css +6 -0
  141. package/src/components/shared/data-display/SActionCard.vue +49 -0
  142. package/src/components/shared/data-display/SActionList.css +15 -0
  143. package/src/components/shared/data-display/SActionList.vue +34 -0
  144. package/src/components/shared/data-display/SActionListItem.css +114 -0
  145. package/src/components/shared/data-display/SActionListItem.vue +230 -0
  146. package/src/components/shared/data-display/SBadge.css +73 -0
  147. package/src/components/shared/data-display/SBadge.vue +52 -0
  148. package/src/components/shared/data-display/SCodeBlock.css +56 -0
  149. package/src/components/shared/data-display/SCodeBlock.vue +188 -0
  150. package/src/components/shared/data-display/SCodeEditor.css +178 -0
  151. package/src/components/shared/data-display/SCodeEditor.vue +274 -0
  152. package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
  153. package/src/components/shared/data-display/SCopyField.vue +77 -0
  154. package/src/components/shared/data-display/SDocBlock.css +136 -0
  155. package/src/components/shared/data-display/SDocBlock.vue +358 -0
  156. package/src/components/shared/data-display/SEmptyState.css +42 -0
  157. package/src/components/shared/data-display/SEmptyState.vue +79 -0
  158. package/src/components/shared/data-display/SExpandableText.vue +314 -0
  159. package/src/components/shared/data-display/SJsonTree.css +6 -0
  160. package/src/components/shared/data-display/SJsonTree.vue +111 -0
  161. package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
  162. package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
  163. package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
  164. package/src/components/shared/data-display/SMessage.css +88 -0
  165. package/src/components/shared/data-display/SMessage.vue +152 -0
  166. package/src/components/shared/data-display/SMetricCard.css +32 -0
  167. package/src/components/shared/data-display/SMetricCard.vue +117 -0
  168. package/src/components/shared/data-display/SProgressBar.css +84 -0
  169. package/src/components/shared/data-display/SProgressBar.vue +114 -0
  170. package/src/components/shared/data-display/SProgressIndicator.css +59 -0
  171. package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
  172. package/src/components/shared/data-display/SSectionHeader.css +84 -0
  173. package/src/components/shared/data-display/SSectionHeader.vue +58 -0
  174. package/src/components/shared/data-display/SStatus.css +90 -0
  175. package/src/components/shared/data-display/SStatus.vue +79 -0
  176. package/src/components/shared/data-display/STable.css +83 -0
  177. package/src/components/shared/data-display/STable.vue +339 -0
  178. package/src/components/shared/data-display/SToastContainer.css +53 -0
  179. package/src/components/shared/data-display/SToastContainer.vue +31 -0
  180. package/src/components/shared/data-display/STooltip.css +114 -0
  181. package/src/components/shared/data-display/STooltip.vue +550 -0
  182. package/src/components/shared/data-display/STooltipAnchor.css +21 -0
  183. package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
  184. package/src/components/shared/data-display/SVirtualList.css +24 -0
  185. package/src/components/shared/data-display/SVirtualList.ts +19 -0
  186. package/src/components/shared/data-display/SVirtualList.vue +361 -0
  187. package/src/components/shared/data-display/json.ts +136 -0
  188. package/src/components/shared/data-display/table.ts +76 -0
  189. package/src/components/shared/database/SDataGrid.css +3 -0
  190. package/src/components/shared/database/SDataGrid.vue +464 -0
  191. package/src/components/shared/database/SSqlEditor.vue +363 -0
  192. package/src/components/shared/database/dataGrid.ts +61 -0
  193. package/src/components/shared/feedback/SAsyncState.css +26 -0
  194. package/src/components/shared/feedback/SAsyncState.vue +190 -0
  195. package/src/components/shared/graph/SGraphViewport.ts +1 -0
  196. package/src/components/shared/graph/SGraphViewport.vue +858 -0
  197. package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
  198. package/src/components/shared/markdown.ts +169 -0
  199. package/src/components/shared/navigation/SBottomNav.vue +692 -0
  200. package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
  201. package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
  202. package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
  203. package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
  204. package/src/components/shared/navigation/STab.vue +363 -0
  205. package/src/components/shared/navigation/STabList.vue +442 -0
  206. package/src/components/shared/navigation/STabPanel.vue +165 -0
  207. package/src/components/shared/navigation/STabPanels.vue +33 -0
  208. package/src/components/shared/navigation/STabs.vue +617 -0
  209. package/src/components/shared/navigation/SWizardSteps.css +69 -0
  210. package/src/components/shared/navigation/SWizardSteps.vue +171 -0
  211. package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
  212. package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
  213. package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
  214. package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
  215. package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
  216. package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
  217. package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
  218. package/src/components/shared/persona/_internal/inventory.ts +110 -0
  219. package/src/components/shared/persona/_internal/labels.ts +79 -0
  220. package/src/components/shared/persona/presentation.ts +371 -0
  221. package/src/components/shared/persona/types.ts +256 -0
  222. package/src/composables/uiPreferencesReset.ts +52 -0
  223. package/src/composables/useClipboard.ts +37 -0
  224. package/src/composables/useDialogOverlay.ts +85 -0
  225. package/src/composables/useDisclosure.ts +40 -0
  226. package/src/composables/useFloatingPosition.ts +245 -0
  227. package/src/composables/useNotifier.ts +26 -0
  228. package/src/composables/useSidebarPanelState.ts +610 -0
  229. package/src/internal/codeEditorContract.ts +110 -0
  230. package/src/internal/codeEditorExtensions.ts +27 -0
  231. package/src/internal/feedbackPresentation.ts +136 -0
  232. package/src/internal/fieldSurface.ts +59 -0
  233. package/src/internal/focusNavigation.ts +69 -0
  234. package/src/internal/inlineTokenEditorContract.ts +136 -0
  235. package/src/internal/interactiveElement.ts +33 -0
  236. package/src/internal/layerStack.ts +202 -0
  237. package/src/internal/linkTarget.ts +256 -0
  238. package/src/internal/ownedAttrs.ts +464 -0
  239. package/src/internal/passiveContentContract.ts +129 -0
  240. package/src/internal/pointerInteractionLease.ts +81 -0
  241. package/src/internal/runtimeContract.ts +194 -0
  242. package/src/internal/runtimeTheme.ts +159 -0
  243. package/src/internal/semanticSizing.ts +60 -0
  244. package/src/internal/useMenuLayer.ts +129 -0
  245. package/src/stores/useNotifierStore.ts +69 -0
  246. package/src/styles/reference.css +4 -0
  247. package/src/styles/style.css +151 -0
  248. package/src/styles/tokens.css +836 -0
  249. package/src/theme.ts +449 -0
  250. package/src/types/highlight-js-lib.d.ts +76 -0
  251. package/src/variants.ts +15 -0
@@ -0,0 +1,363 @@
1
+ <template>
2
+ <SContextMenu
3
+ :items="contextMenuItems ?? []"
4
+ :enabled="hasContextMenu"
5
+ :trigger="contextMenuTrigger"
6
+ boundary-layout="contents"
7
+ @select="$emit('context-menu-select', $event)"
8
+ >
9
+ <div
10
+ v-bind="ownedAttrs.bindings()"
11
+ class="s-tab-container"
12
+ :class="{ 's-tab-container--selected': state.active }"
13
+ :data-close-mode="ctx.closeButtonMode.value"
14
+ :data-disabled="state.disabled || undefined"
15
+ :data-editing="editing || undefined"
16
+ :style="containerStyle"
17
+ role="presentation"
18
+ >
19
+ <button
20
+ ref="tabButtonRef"
21
+ class="s-tab__trigger"
22
+ type="button"
23
+ :style="{ visibility: editing ? 'hidden' : undefined }"
24
+ :disabled="disabled"
25
+ :title="title"
26
+ :aria-hidden="editing || undefined"
27
+ :aria-selected="state.active"
28
+ :aria-controls="state.panelId"
29
+ :aria-label="ariaLabel"
30
+ :id="state.tabId"
31
+ :tabindex="editing ? -1 : state.tabIndex"
32
+ :data-editing="editing || undefined"
33
+ role="tab"
34
+ @click="ctx.activateTab(registrationKey)"
35
+ @focus="ctx.focusTab(registrationKey)"
36
+ @keydown="ctx.handleTabKeydown(registrationKey, $event)"
37
+ >
38
+ <PassiveContentBoundary :contract="passiveContentContract">
39
+ <span
40
+ v-if="$slots.leading"
41
+ class="s-tab__leading"
42
+ :aria-hidden="leadingDecorative ? 'true' : undefined"
43
+ >
44
+ <slot name="leading" />
45
+ </span>
46
+ <span v-if="$slots.default" class="s-tab__label">
47
+ <slot />
48
+ </span>
49
+ <span
50
+ v-if="$slots.trailing"
51
+ class="s-tab__trailing"
52
+ :aria-hidden="trailingDecorative ? 'true' : undefined"
53
+ >
54
+ <slot name="trailing" />
55
+ </span>
56
+ </PassiveContentBoundary>
57
+ </button>
58
+
59
+ <div v-if="editing && $slots.editor" class="s-tab__editor" @click.stop @dblclick.stop>
60
+ <slot name="editor" />
61
+ </div>
62
+
63
+ <div
64
+ v-if="hasSiblingActions"
65
+ ref="actionsRef"
66
+ class="s-tab__sibling-actions"
67
+ @click.stop
68
+ @dblclick.stop
69
+ @mousedown.stop
70
+ >
71
+ <slot name="actions" />
72
+ <span v-if="$slots.close" class="s-tab__close">
73
+ <slot name="close" />
74
+ </span>
75
+ </div>
76
+ </div>
77
+ </SContextMenu>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ import {
82
+ computed,
83
+ inject,
84
+ onBeforeUnmount,
85
+ onMounted,
86
+ onUpdated,
87
+ ref,
88
+ type VNodeChild,
89
+ } from 'vue'
90
+ import SContextMenu from '../controls/SContextMenu.vue'
91
+ import type { SMenuEntry, SMenuSelectableEntry } from '../controls/menu'
92
+ import { tabsContextKey, type STabsContext } from '../_internal/tabsContext'
93
+ import {
94
+ PassiveContentBoundary,
95
+ useInteractiveLeafRegistration,
96
+ type PassiveContentContract,
97
+ } from '../../../internal/passiveContentContract'
98
+ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
99
+
100
+ defineOptions({
101
+ inheritAttrs: false,
102
+ })
103
+
104
+ /**
105
+ * Публичный контракт участника STabs; selection, IDs и keyboard принадлежат owner STabs.
106
+ * Public STabs participant contract; selection, IDs, and keyboard behavior belong to STabs.
107
+ */
108
+ export interface Props {
109
+ /** Единственная stable identity selection и panel inventory. / Sole stable selection and panel-inventory identity. */
110
+ name: string
111
+ /** Исключает вкладку из активации и roving focus. / Excludes the tab from activation and roving focus. */
112
+ disabled?: boolean
113
+ /** Нативная подсказка tab trigger. / Native tab-trigger tooltip. */
114
+ title?: string
115
+ /** Явное accessible name для icon-only content. / Explicit accessible name for icon-only content. */
116
+ ariaLabel?: string
117
+ /** Показывает editor sibling и исключает trigger из tab order. / Shows the sibling editor and removes the trigger from tab order. */
118
+ editing?: boolean
119
+ /** Скрывает leading slot от accessibility tree, когда он декоративный. / Hides a decorative leading slot from the accessibility tree. */
120
+ leadingDecorative?: boolean
121
+ /** Скрывает trailing slot от accessibility tree, когда он декоративный. / Hides a decorative trailing slot from the accessibility tree. */
122
+ trailingDecorative?: boolean
123
+ /** Контекстные действия, размещаемые на внешнем host, а не внутри trigger. / Context actions hosted outside the tab trigger. */
124
+ contextMenuItems?: readonly SMenuEntry[]
125
+ /** Событие открытия контекстного меню. / Context-menu opening gesture. */
126
+ contextMenuTrigger?: 'contextmenu' | 'click' | 'both'
127
+ }
128
+
129
+ const props = withDefaults(defineProps<Props>(), {
130
+ disabled: false,
131
+ title: undefined,
132
+ ariaLabel: undefined,
133
+ editing: false,
134
+ leadingDecorative: true,
135
+ trailingDecorative: true,
136
+ contextMenuItems: undefined,
137
+ contextMenuTrigger: 'contextmenu',
138
+ })
139
+ defineEmits<{
140
+ 'context-menu-select': [entry: SMenuSelectableEntry]
141
+ }>()
142
+ const ownedAttrs = useOwnedAttrs({ component: 'STab', owner: 'tab participant container' })
143
+ const slots = defineSlots<{
144
+ /** Основная метка вкладки. / Primary tab label. */
145
+ default?: () => VNodeChild
146
+ /** Leading content; decorative by default. / Leading content; decorative by default. */
147
+ leading?: () => VNodeChild
148
+ /** Trailing content; decorative by default. / Trailing content; decorative by default. */
149
+ trailing?: () => VNodeChild
150
+ /** Focusable actions rendered as trigger siblings. / Focusable actions rendered as trigger siblings. */
151
+ actions?: () => VNodeChild
152
+ /** Close action rendered as a trigger sibling. / Close action rendered as a trigger sibling. */
153
+ close?: () => VNodeChild
154
+ /** Rename/editor control rendered as a trigger sibling. / Rename/editor control rendered as a trigger sibling. */
155
+ editor?: () => VNodeChild
156
+ }>()
157
+
158
+ const maybeCtx = inject<STabsContext>(tabsContextKey)
159
+ if (!maybeCtx) {
160
+ throw new Error(
161
+ 'STab должен использоваться внутри STabs. / STab must be used inside STabs.',
162
+ )
163
+ }
164
+ const ctx = maybeCtx
165
+
166
+ const tabButtonRef = ref<HTMLButtonElement | null>(null)
167
+ const actionsRef = ref<HTMLElement | null>(null)
168
+ const siblingActionsWidth = ref(0)
169
+ let actionsResizeObserver: ResizeObserver | null = null
170
+ let observedActionsElement: HTMLElement | null = null
171
+
172
+ useInteractiveLeafRegistration({ owner: 'STab' })
173
+ const passiveContentContract: PassiveContentContract = {
174
+ owner: 'STab',
175
+ region: 'passive tab trigger content',
176
+ guidance: 'Передайте focusable controls через actions, close или editor slot. / Use the actions, close, or editor slot for focusable controls.',
177
+ }
178
+
179
+ const tabElement = (): HTMLElement | null => {
180
+ return tabButtonRef.value
181
+ }
182
+
183
+ const registrationKey = ctx.registerTab({
184
+ tabKey: props.name,
185
+ disabled: () => props.disabled,
186
+ element: tabElement,
187
+ })
188
+
189
+ const state = computed(() => ctx.getTabState(registrationKey))
190
+ const hasSiblingActions = computed(() => Boolean(slots.actions || slots.close))
191
+ const hasContextMenu = computed(() => Boolean(props.contextMenuItems?.length))
192
+ const containerStyle = computed(() => {
193
+ const siblingSpace = hasSiblingActions.value ? siblingActionsWidth.value : 0
194
+ const basePaddingInline = 'var(--s-tab-base-padding-inline)'
195
+ const paddingInlineEnd = ctx.closeButtonMode.value === 'overlay' && siblingSpace > 0
196
+ ? `calc(${basePaddingInline} + ${siblingSpace}px + var(--s-tab-sibling-actions-gap))`
197
+ : basePaddingInline
198
+ return {
199
+ '--s-tab-sibling-actions-space': `${siblingSpace}px`,
200
+ '--s-tab-padding-inline-start': basePaddingInline,
201
+ '--s-tab-padding-inline-end': paddingInlineEnd,
202
+ }
203
+ })
204
+
205
+ function updateSiblingActionsWidth(): void {
206
+ siblingActionsWidth.value = actionsRef.value?.offsetWidth ?? 0
207
+ }
208
+
209
+ function syncActionsResizeObserver(): void {
210
+ const nextElement = actionsRef.value
211
+ if (observedActionsElement === nextElement) return
212
+ actionsResizeObserver?.disconnect()
213
+ actionsResizeObserver = null
214
+ observedActionsElement = nextElement
215
+ if (typeof ResizeObserver !== 'undefined' && nextElement) {
216
+ actionsResizeObserver = new ResizeObserver(updateSiblingActionsWidth)
217
+ actionsResizeObserver.observe(nextElement)
218
+ }
219
+ }
220
+
221
+ onMounted(() => {
222
+ updateSiblingActionsWidth()
223
+ syncActionsResizeObserver()
224
+ ctx.notifyTabOrderChanged()
225
+ })
226
+
227
+ onUpdated(() => {
228
+ if (props.name !== registrationKey) {
229
+ throw new Error('STab: name is immutable for the lifetime of a mounted participant')
230
+ }
231
+ updateSiblingActionsWidth()
232
+ syncActionsResizeObserver()
233
+ ctx.notifyTabOrderChanged()
234
+ })
235
+
236
+ onBeforeUnmount(() => {
237
+ actionsResizeObserver?.disconnect()
238
+ observedActionsElement = null
239
+ ctx.unregisterTab(registrationKey)
240
+ })
241
+ </script>
242
+
243
+ <style lang="postcss" scoped>
244
+ @reference "../../../styles/reference.css";
245
+
246
+ .s-tab-container {
247
+ @apply relative box-border inline-flex min-w-0 flex-none items-center whitespace-nowrap;
248
+ flex: var(--s-tab-flex);
249
+ width: var(--s-tab-width);
250
+ min-width: var(--s-tab-min-width);
251
+ max-width: var(--s-tab-max-width);
252
+ height: var(--s-tab-row-height);
253
+ border-bottom: var(--s-tab-indicator-width) solid transparent;
254
+ border-radius: var(--s-tab-radius);
255
+ overflow: hidden;
256
+ }
257
+
258
+ .s-tab-container--selected {
259
+ border-color: var(--s-tab-selected-border-color);
260
+ }
261
+
262
+ .s-tab__trigger {
263
+ @apply relative inline-flex min-w-0 flex-1 items-center justify-center border-0 bg-transparent text-sm font-medium leading-5;
264
+ align-self: stretch;
265
+ width: 100%;
266
+ min-height: var(--s-tab-trigger-min-height);
267
+ margin: 0;
268
+ padding-block-start: calc(var(--s-tab-padding-block) + var(--s-tab-content-offset-y));
269
+ padding-block-end: var(--s-tab-padding-block);
270
+ padding-inline-start: var(--s-tab-padding-inline-start);
271
+ padding-inline-end: var(--s-tab-padding-inline-end);
272
+ border-radius: inherit;
273
+ color: var(--s-tab-color);
274
+ cursor: pointer;
275
+ flex-direction: var(--s-tab-direction);
276
+ gap: var(--s-tab-gap);
277
+ font-family: inherit;
278
+ text-align: center;
279
+ touch-action: manipulation;
280
+ transition:
281
+ background-color var(--s-motion-fast) ease,
282
+ color var(--s-motion-fast) ease,
283
+ transform var(--s-motion-fast) ease;
284
+ appearance: none;
285
+ }
286
+
287
+ .s-tab__trigger:hover:not(:disabled) {
288
+ background-color: var(--s-tab-hover-background);
289
+ color: var(--s-tab-hover-color);
290
+ }
291
+
292
+ .s-tab__trigger[aria-selected='true'] {
293
+ color: var(--s-tab-selected-color);
294
+ }
295
+
296
+ .s-tab__trigger:focus-visible {
297
+ z-index: 1;
298
+ outline: var(--s-focus-ring-width) solid var(--s-focus-ring-color);
299
+ outline-offset: var(--s-focus-ring-inset-offset);
300
+ }
301
+
302
+ .s-tab__trigger:active:not(:disabled) {
303
+ transform: translateY(var(--s-tab-active-translate-y));
304
+ }
305
+
306
+ .s-tab__trigger:disabled {
307
+ color: var(--s-tab-disabled-color);
308
+ cursor: not-allowed;
309
+ opacity: var(--s-tab-disabled-opacity);
310
+ }
311
+
312
+ :global(.dark) .s-tab__trigger {
313
+ color: var(--s-tab-color);
314
+ }
315
+
316
+ :global(.dark) .s-tab__trigger:hover:not(:disabled) {
317
+ background-color: var(--s-tab-hover-background);
318
+ color: var(--s-tab-hover-color);
319
+ }
320
+
321
+ :global(.dark) .s-tab__trigger[aria-selected='true'] {
322
+ color: var(--s-tab-selected-color);
323
+ }
324
+
325
+ :global(.dark) .s-tab__trigger:disabled {
326
+ color: var(--s-tab-disabled-color);
327
+ }
328
+
329
+ .s-tab__leading,
330
+ .s-tab__trailing,
331
+ .s-tab__close {
332
+ @apply inline-flex shrink-0 items-center;
333
+ }
334
+
335
+ .s-tab__label {
336
+ display: var(--s-tab-label-display);
337
+ flex: var(--s-tab-label-flex);
338
+ min-width: 0;
339
+ overflow: hidden;
340
+ font-size: var(--s-tab-label-size);
341
+ line-height: var(--s-tab-label-line-height);
342
+ text-overflow: ellipsis;
343
+ white-space: nowrap;
344
+ }
345
+
346
+ .s-tab__sibling-actions {
347
+ @apply inline-flex shrink-0 items-center gap-0.5;
348
+ z-index: var(--s-layer-sticky);
349
+ }
350
+
351
+ .s-tab-container[data-close-mode='overlay'] .s-tab__sibling-actions {
352
+ @apply absolute inset-y-0 right-0;
353
+ }
354
+
355
+ .s-tab-container[data-editing='true'] .s-tab__editor {
356
+ padding-right: calc(var(--s-tab-sibling-actions-space) + var(--s-tab-sibling-actions-gap));
357
+ }
358
+
359
+ .s-tab__editor {
360
+ @apply absolute inset-0 z-0 flex min-w-0 items-center;
361
+ }
362
+
363
+ </style>