@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,339 @@
1
+ <template>
2
+ <div
3
+ v-bind="ownedAttrs.bindings()"
4
+ class="s-table-wrapper"
5
+ :data-surface="validatedSurface"
6
+ :data-min-width="validatedMinWidth"
7
+ :data-max-height="validatedMaxHeight"
8
+ >
9
+ <table
10
+ class="s-table"
11
+ :data-density="validatedDensity"
12
+ :aria-label="resolvedAccessibleName.ariaLabel"
13
+ :aria-labelledby="resolvedAccessibleName.ariaLabelledby"
14
+ :aria-describedby="validatedAriaDescribedby"
15
+ :aria-busy="validatedLoading ? 'true' : undefined"
16
+ >
17
+ <thead class="s-table-header">
18
+ <tr>
19
+ <th
20
+ v-for="column in validatedColumns"
21
+ :key="column.field"
22
+ scope="col"
23
+ :data-width="column.width"
24
+ :data-min-width="column.minWidth"
25
+ :data-align="column.align ?? 'left'"
26
+ >
27
+ <slot name="column" :column="column">
28
+ <slot :name="`header-${column.field}`" :column="column">{{ column.header }}</slot>
29
+ </slot>
30
+ </th>
31
+ </tr>
32
+ </thead>
33
+ <tbody class="s-table-body">
34
+ <SAsyncState
35
+ v-if="validatedLoading || errorMessage || validatedRows.length === 0"
36
+ :state="validatedLoading
37
+ ? { status: 'loading', message: loadingMessage }
38
+ : errorMessage
39
+ ? { status: 'error', message: errorMessage }
40
+ : { status: 'empty', message: emptyMessage }"
41
+ presentation="table-row"
42
+ :colspan="validatedColumns.length || 1"
43
+ />
44
+ <template v-else>
45
+ <tr
46
+ v-for="(row, rowIndex) in validatedRows"
47
+ :key="resolvedRowKeys.get(row)"
48
+ :data-state="resolveRowState(row)"
49
+ :aria-selected="resolvedRowKeys.get(row) === validatedSelectedRowKey ? 'true' : undefined"
50
+ >
51
+ <td
52
+ v-for="column in validatedColumns"
53
+ :key="column.field"
54
+ :data-align="column.align ?? 'left'"
55
+ :data-state="resolveCellState(row, column)"
56
+ >
57
+ <slot
58
+ :name="`cell-${column.field}`"
59
+ :value="resolveCellValue(row, column)"
60
+ :row="row"
61
+ :column="column"
62
+ :row-index="rowIndex"
63
+ >
64
+ <slot
65
+ name="cell"
66
+ :value="resolveCellValue(row, column)"
67
+ :row="row"
68
+ :column="column"
69
+ :row-index="rowIndex"
70
+ >
71
+ {{ resolveCellValue(row, column) }}
72
+ </slot>
73
+ </slot>
74
+ </td>
75
+ </tr>
76
+ </template>
77
+ </tbody>
78
+ </table>
79
+ </div>
80
+ </template>
81
+
82
+ <script lang="ts">
83
+ export type {
84
+ STableAlignment,
85
+ STableCellState,
86
+ STableColumn,
87
+ STableColumnContent,
88
+ STableDensity,
89
+ STableMinWidth,
90
+ STableProps,
91
+ STableRowKey,
92
+ STableRowState,
93
+ STableSurface,
94
+ } from './table'
95
+ </script>
96
+
97
+ <script setup lang="ts" generic="Row extends object = Record<string, unknown>, RowKey extends STableRowKey = STableRowKey, Field extends string = Extract<keyof Row, string>">
98
+ import { computed, useSlots } from 'vue'
99
+ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
100
+ import {
101
+ validateScrollableViewportMaxBlockSize,
102
+ validateTableColumnInlineSize,
103
+ validateTableColumnMinInlineSize,
104
+ } from '../../../internal/semanticSizing'
105
+ import {
106
+ validateBoolean,
107
+ validateDomId,
108
+ validateExactString,
109
+ validateNonEmptyString,
110
+ } from '../../../internal/runtimeContract'
111
+ import SAsyncState from '../feedback/SAsyncState.vue'
112
+ import type {
113
+ STableCellState,
114
+ STableColumn,
115
+ STableColumnContent,
116
+ STableProps,
117
+ STableRowKey,
118
+ STableRowState,
119
+ } from './table'
120
+
121
+ defineOptions({ inheritAttrs: false })
122
+
123
+ const props = withDefaults(defineProps<STableProps<Row, RowKey, Field>>(), {
124
+ loading: false,
125
+ loadingMessage: 'Загрузка...',
126
+ errorMessage: '',
127
+ emptyMessage: 'Нет данных для отображения',
128
+ selectedRowKey: undefined,
129
+ maxHeight: 'none',
130
+ density: 'default',
131
+ surface: 'bordered',
132
+ minWidth: 'content',
133
+ rowState: undefined,
134
+ cellState: undefined,
135
+ ariaLabel: undefined,
136
+ ariaLabelledby: undefined,
137
+ ariaDescribedby: undefined,
138
+ })
139
+ const slots = useSlots()
140
+
141
+ const TABLE_DENSITIES = ['default', 'compact'] as const
142
+ const TABLE_SURFACES = ['bordered', 'plain'] as const
143
+ const TABLE_MIN_WIDTHS = ['content', 'md', 'lg'] as const
144
+ const TABLE_ALIGNMENTS = ['left', 'center', 'right'] as const
145
+ const TABLE_ROW_STATES = ['default', 'inserted', 'updated', 'deleted'] as const
146
+ const TABLE_CELL_STATES = ['default', 'pending'] as const
147
+ const TABLE_COLUMN_CONTENT = ['field', 'value', 'slot'] as const
148
+
149
+ const ownedAttrs = useOwnedAttrs({ component: 'STable', owner: 'table scroll surface' })
150
+ const validatedLoading = computed(() => validateBoolean('STable', 'loading', props.loading))
151
+ const resolvedAccessibleName = computed(() => {
152
+ if (props.ariaLabel !== undefined && props.ariaLabelledby !== undefined) {
153
+ throw new TypeError('STable: задайте только ariaLabel или ariaLabelledby. / STable: provide only ariaLabel or ariaLabelledby.')
154
+ }
155
+ if (props.ariaLabelledby !== undefined) {
156
+ return {
157
+ ariaLabel: undefined,
158
+ ariaLabelledby: validateDomId('STable', 'ariaLabelledby', props.ariaLabelledby),
159
+ }
160
+ }
161
+ return {
162
+ ariaLabel: props.ariaLabel === undefined
163
+ ? 'Таблица данных'
164
+ : validateNonEmptyString('STable', 'ariaLabel', props.ariaLabel),
165
+ ariaLabelledby: undefined,
166
+ }
167
+ })
168
+ const validatedAriaDescribedby = computed(() => props.ariaDescribedby === undefined
169
+ ? undefined
170
+ : validateDomId('STable', 'ariaDescribedby', props.ariaDescribedby))
171
+ const validatedDensity = computed(() => validateExactString(
172
+ 'STable',
173
+ 'density',
174
+ props.density,
175
+ TABLE_DENSITIES,
176
+ ))
177
+ const validatedSurface = computed(() => validateExactString(
178
+ 'STable',
179
+ 'surface',
180
+ props.surface,
181
+ TABLE_SURFACES,
182
+ ))
183
+ const validatedMinWidth = computed(() => validateExactString(
184
+ 'STable',
185
+ 'minWidth',
186
+ props.minWidth,
187
+ TABLE_MIN_WIDTHS,
188
+ ))
189
+ const validatedMaxHeight = computed(() => validateScrollableViewportMaxBlockSize(
190
+ 'STable',
191
+ 'maxHeight',
192
+ props.maxHeight,
193
+ ))
194
+
195
+ const validatedColumns = computed<readonly STableColumn<Row, Field>[]>(() => {
196
+ const fields = new Set<string>()
197
+ for (const column of props.columns) {
198
+ const field = validateNonEmptyString('STable', 'columns[].field', column.field)
199
+ validateNonEmptyString('STable', `column ${JSON.stringify(field)}.header`, column.header)
200
+ if (fields.has(field)) {
201
+ throw new TypeError(
202
+ `STable: columns содержит duplicate field ${JSON.stringify(field)}. `
203
+ + `/ STable: columns contains duplicate field ${JSON.stringify(field)}.`,
204
+ )
205
+ }
206
+ fields.add(field)
207
+ if (column.align !== undefined) {
208
+ validateExactString('STable', `column ${JSON.stringify(field)}.align`, column.align, TABLE_ALIGNMENTS)
209
+ }
210
+ if (column.width !== undefined) {
211
+ validateTableColumnInlineSize('STable', `column ${JSON.stringify(field)}.width`, column.width)
212
+ }
213
+ if (column.minWidth !== undefined) {
214
+ validateTableColumnMinInlineSize('STable', `column ${JSON.stringify(field)}.minWidth`, column.minWidth)
215
+ }
216
+ const content: STableColumnContent = validateExactString(
217
+ 'STable',
218
+ `column ${JSON.stringify(field)}.content`,
219
+ column.content ?? (column.value === undefined ? 'field' : 'value'),
220
+ TABLE_COLUMN_CONTENT,
221
+ )
222
+ if (content === 'value' && typeof column.value !== 'function') {
223
+ throw new TypeError(
224
+ `STable: value column ${JSON.stringify(field)} должен предоставить function value resolver. `
225
+ + `/ STable: value column ${JSON.stringify(field)} must provide a function value resolver.`,
226
+ )
227
+ }
228
+ if (content !== 'value' && column.value !== undefined) {
229
+ throw new TypeError(
230
+ `STable: ${content} column ${JSON.stringify(field)} не принимает value resolver. `
231
+ + `/ STable: ${content} column ${JSON.stringify(field)} does not accept a value resolver.`,
232
+ )
233
+ }
234
+ if (content === 'slot' && typeof slots[`cell-${field}`] !== 'function') {
235
+ throw new TypeError(
236
+ `STable: slot column ${JSON.stringify(field)} требует named slot ${JSON.stringify(`cell-${field}`)}. `
237
+ + `/ STable: slot column ${JSON.stringify(field)} requires named slot ${JSON.stringify(`cell-${field}`)}.`,
238
+ )
239
+ }
240
+ }
241
+ return props.columns
242
+ })
243
+
244
+ function describeRowKey(key: STableRowKey): string {
245
+ return typeof key === 'string' ? JSON.stringify(key) : String(key)
246
+ }
247
+
248
+ function validateRowKey(value: unknown): RowKey {
249
+ if (
250
+ (typeof value === 'string' && value.trim().length > 0)
251
+ || (typeof value === 'number' && Number.isFinite(value))
252
+ ) return value as RowKey
253
+
254
+ throw new TypeError(
255
+ `STable: getRowKey должен вернуть non-empty string | finite number. `
256
+ + '/ STable: getRowKey must return a non-empty string | finite number.',
257
+ )
258
+ }
259
+
260
+ const resolvedRowKeys = computed<ReadonlyMap<Row, RowKey>>(() => {
261
+ if (typeof props.getRowKey !== 'function') {
262
+ throw new TypeError(
263
+ 'STable: getRowKey должен быть обязательным function resolver. '
264
+ + '/ STable: getRowKey must be a required function resolver.',
265
+ )
266
+ }
267
+
268
+ const keys = new Set<STableRowKey>()
269
+ const rowKeys = new Map<Row, RowKey>()
270
+ for (const row of props.data) {
271
+ const key = validateRowKey(props.getRowKey(row))
272
+ if (keys.has(key)) {
273
+ throw new TypeError(
274
+ `STable: getRowKey вернул duplicate key ${describeRowKey(key)}. `
275
+ + `/ STable: getRowKey returned duplicate key ${describeRowKey(key)}.`,
276
+ )
277
+ }
278
+ keys.add(key)
279
+ rowKeys.set(row, key)
280
+ }
281
+ return rowKeys
282
+ })
283
+
284
+ const validatedRows = computed<readonly Row[]>(() => {
285
+ const columns = validatedColumns.value
286
+ void resolvedRowKeys.value
287
+ void validatedSelectedRowKey.value
288
+ for (const row of props.data) {
289
+ for (const column of columns) {
290
+ const content = column.content ?? (column.value === undefined ? 'field' : 'value')
291
+ if (content === 'field' && !Object.hasOwn(row, column.field)) {
292
+ throw new TypeError(
293
+ `STable: column ${JSON.stringify(column.field)} не известен row; добавьте typed field или value resolver. `
294
+ + `/ STable: column ${JSON.stringify(column.field)} is unknown to row; add a typed field or value resolver.`,
295
+ )
296
+ }
297
+ }
298
+ }
299
+ return props.data
300
+ })
301
+
302
+ const validatedSelectedRowKey = computed<RowKey | undefined>(() => {
303
+ if (props.selectedRowKey === undefined) return undefined
304
+ const selectedKey = validateRowKey(props.selectedRowKey)
305
+ const rowKeys = new Set(resolvedRowKeys.value.values())
306
+ if (!rowKeys.has(selectedKey)) {
307
+ throw new TypeError(
308
+ `STable: selectedRowKey ${describeRowKey(selectedKey)} отсутствует в resolved row keys. `
309
+ + `/ STable: selectedRowKey ${describeRowKey(selectedKey)} is absent from resolved row keys.`,
310
+ )
311
+ }
312
+ return selectedKey
313
+ })
314
+
315
+ const resolveCellValue = (row: Row, column: STableColumn<Row, Field>): unknown => column.value
316
+ ? column.value(row)
317
+ : column.content === 'slot'
318
+ ? undefined
319
+ : Reflect.get(row, column.field)
320
+
321
+ const resolveRowState = (row: Row): STableRowState => validateExactString(
322
+ 'STable',
323
+ 'rowState',
324
+ props.rowState?.(row) ?? 'default',
325
+ TABLE_ROW_STATES,
326
+ )
327
+
328
+ const resolveCellState = (
329
+ row: Row,
330
+ column: STableColumn<Row, Field>,
331
+ ): STableCellState => validateExactString(
332
+ 'STable',
333
+ 'cellState',
334
+ props.cellState?.(row, column) ?? 'default',
335
+ TABLE_CELL_STATES,
336
+ )
337
+ </script>
338
+
339
+ <style lang="postcss" src="./STable.css" scoped></style>
@@ -0,0 +1,53 @@
1
+ @reference "../../../styles/reference.css";
2
+
3
+ .s-toast-container {
4
+ @apply fixed top-0 right-0 flex max-h-full min-w-0 w-full max-w-md flex-col overflow-y-auto overscroll-contain p-4;
5
+ z-index: var(--s-layer-toast);
6
+ @apply pointer-events-none;
7
+ }
8
+
9
+ .s-toast-transition-wrapper {
10
+ @apply flex flex-col items-end gap-2;
11
+ }
12
+
13
+ /*
14
+ Transition classes for TransitionGroup.
15
+ This animates toasts sliding in and out from the right.
16
+ */
17
+
18
+ /* 1. Enter transitions */
19
+ .notification-transition-enter-active {
20
+ transition: all var(--s-motion-standard) ease-out;
21
+ }
22
+
23
+ .notification-transition-enter-from {
24
+ opacity: 0;
25
+ transform: translate3d(16px, 12px, 0) scale(0.98);
26
+ }
27
+
28
+ .notification-transition-enter-to {
29
+ opacity: 1;
30
+ transform: translate3d(0, 0, 0) scale(1);
31
+ }
32
+
33
+ /* 2. Leave transitions */
34
+ .notification-transition-leave-active {
35
+ transition: all var(--s-motion-fast) ease-in;
36
+ /* To keep layout from jumping, we make the leaving element absolute */
37
+ position: absolute;
38
+ }
39
+
40
+ .notification-transition-leave-from {
41
+ opacity: 1;
42
+ transform: translate3d(0, 0, 0) scale(1);
43
+ }
44
+
45
+ .notification-transition-leave-to {
46
+ opacity: 0;
47
+ transform: translate3d(12px, 8px, 0) scale(0.98);
48
+ }
49
+
50
+ /* 3. Move transition */
51
+ .notification-transition-move {
52
+ transition: transform var(--s-motion-standard) ease;
53
+ }
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <div v-bind="ownedAttrs.bindings()" class="s-toast-container">
3
+ <TransitionGroup name="notification-transition" tag="div" class="s-toast-transition-wrapper">
4
+ <SToastItem
5
+ v-for="notification in notifications"
6
+ :key="notification.id"
7
+ :notification="notification"
8
+ @dismiss="dismiss"
9
+ />
10
+ </TransitionGroup>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { useNotifierStore } from '../../../stores/useNotifierStore'
16
+ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
17
+ import { storeToRefs } from 'pinia'
18
+ import SToastItem from '../_internal/SToastItem.vue'
19
+
20
+ defineOptions({ inheritAttrs: false })
21
+
22
+ const store = useNotifierStore()
23
+ const { notifications } = storeToRefs(store)
24
+ const ownedAttrs = useOwnedAttrs({ component: 'SToastContainer', owner: 'toast container root' })
25
+
26
+ function dismiss(id: string): void {
27
+ store.remove(id)
28
+ }
29
+ </script>
30
+
31
+ <style lang="postcss" src="./SToastContainer.css" scoped></style>
@@ -0,0 +1,114 @@
1
+ @reference "../../../styles/reference.css";
2
+
3
+ .s-tooltip {
4
+ @apply fixed rounded-md border px-2 py-1;
5
+ @apply text-xs font-normal;
6
+ background-color: var(--s-floating-surface);
7
+ color: var(--s-floating-foreground);
8
+ box-shadow: 0 0 0 1px var(--s-floating-outline), var(--s-elevation-floating);
9
+ max-width: 32rem;
10
+ overflow-wrap: anywhere;
11
+ word-break: break-word;
12
+ white-space: normal;
13
+ pointer-events: none;
14
+ user-select: none;
15
+ }
16
+
17
+ .s-tooltip__content {
18
+ @apply grid gap-1;
19
+ }
20
+
21
+ .s-tooltip__title,
22
+ .s-tooltip__section-title {
23
+ @apply font-semibold text-surface-900 dark:text-surface-50;
24
+ }
25
+
26
+ .s-tooltip__description,
27
+ .s-tooltip__text,
28
+ .s-tooltip__section-description {
29
+ @apply m-0 text-surface-700 dark:text-surface-200;
30
+ }
31
+
32
+ .s-tooltip__text {
33
+ white-space: pre-line;
34
+ }
35
+
36
+ .s-tooltip__details {
37
+ @apply m-0 grid gap-0.5;
38
+ }
39
+
40
+ .s-tooltip__detail {
41
+ @apply grid min-w-0 items-baseline gap-2;
42
+ grid-template-columns: auto minmax(0, 1fr);
43
+ }
44
+
45
+ .s-tooltip__detail-label {
46
+ @apply text-surface-500 dark:text-surface-400;
47
+ }
48
+
49
+ .s-tooltip__detail-value {
50
+ @apply m-0 min-w-0 text-surface-900 dark:text-surface-100;
51
+ }
52
+
53
+ .s-tooltip__detail-value[data-presentation='code'] {
54
+ @apply font-mono;
55
+ }
56
+
57
+ .s-tooltip__detail-value[data-tone='info'] {
58
+ @apply text-info-700 dark:text-info-300;
59
+ }
60
+
61
+ .s-tooltip__detail-value[data-tone='success'] {
62
+ @apply text-success-700 dark:text-success-300;
63
+ }
64
+
65
+ .s-tooltip__detail-value[data-tone='warn'] {
66
+ @apply text-warn-700 dark:text-warn-300;
67
+ }
68
+
69
+ .s-tooltip__detail-value[data-tone='danger'] {
70
+ @apply text-danger-700 dark:text-danger-300;
71
+ }
72
+
73
+ .s-tooltip__sections {
74
+ @apply mt-1 grid gap-2 border-t border-surface-200 pt-2 dark:border-surface-600;
75
+ }
76
+
77
+ .s-tooltip__section {
78
+ @apply grid gap-0.5;
79
+ }
80
+
81
+ .s-tooltip__section-items {
82
+ @apply m-0 list-disc space-y-0.5 pl-4 text-surface-700 dark:text-surface-200;
83
+ }
84
+
85
+ .s-tooltip-arrow {
86
+ @apply absolute h-0 w-0;
87
+ border-style: solid;
88
+ border-width: 5px;
89
+ border-color: transparent;
90
+ }
91
+
92
+ .s-tooltip[data-placement^='top-'] .s-tooltip-arrow {
93
+ bottom: -10px;
94
+ transform: translateX(-50%);
95
+ border-top-color: var(--s-floating-surface);
96
+ }
97
+
98
+ .s-tooltip[data-placement^='bottom-'] .s-tooltip-arrow {
99
+ top: -10px;
100
+ transform: translateX(-50%);
101
+ border-bottom-color: var(--s-floating-surface);
102
+ }
103
+
104
+ .s-tooltip[data-placement^='left-'] .s-tooltip-arrow {
105
+ right: -10px;
106
+ transform: translateY(-50%);
107
+ border-left-color: var(--s-floating-surface);
108
+ }
109
+
110
+ .s-tooltip[data-placement^='right-'] .s-tooltip-arrow {
111
+ left: -10px;
112
+ transform: translateY(-50%);
113
+ border-right-color: var(--s-floating-surface);
114
+ }