@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,274 @@
1
+ @reference "../../../styles/reference.css";
2
+
3
+ .s-button {
4
+ --s-button-accent: var(--s-button-primary-accent);
5
+ --s-button-solid-background: var(--s-button-primary-solid-background);
6
+ --s-button-solid-background-hover: var(--s-button-primary-solid-background-hover);
7
+ --s-button-solid-background-active: var(--s-button-primary-solid-background-active);
8
+ --s-button-on-solid: var(--s-button-primary-solid-foreground);
9
+ --s-button-foreground: var(--s-button-primary-foreground);
10
+ --s-button-outline: color-mix(in srgb, var(--s-button-accent) 45%, transparent);
11
+ --s-button-soft-background: color-mix(in srgb, var(--s-button-accent) 12%, transparent);
12
+ --s-button-hover-background: color-mix(in srgb, var(--s-button-accent) 10%, transparent);
13
+
14
+ @apply relative inline-flex min-w-0 items-center justify-center font-semibold;
15
+ border: 1px solid transparent;
16
+ border-radius: var(--radius-xs);
17
+ font-family: inherit;
18
+ line-height: 1.25;
19
+ text-decoration: none;
20
+ appearance: none;
21
+ touch-action: manipulation;
22
+ transition:
23
+ background-color var(--s-motion-standard) ease,
24
+ border-color var(--s-motion-standard) ease,
25
+ box-shadow var(--s-motion-standard) ease,
26
+ color var(--s-motion-standard) ease,
27
+ transform var(--s-motion-fast) ease;
28
+ }
29
+
30
+ .s-button[data-size='xs']:not([data-icon-only='true']) {
31
+ min-height: var(--s-control-hitbox-xs);
32
+ padding: 0.25rem 0.5rem;
33
+ font-size: 0.75rem;
34
+ }
35
+
36
+ .s-button[data-size='sm']:not([data-icon-only='true']) {
37
+ min-height: var(--s-control-hitbox-sm);
38
+ padding: 0.375rem 0.625rem;
39
+ font-size: 0.75rem;
40
+ }
41
+
42
+ .s-button[data-size='md']:not([data-icon-only='true']) {
43
+ min-height: var(--s-control-hitbox-md);
44
+ padding: 0.625rem 1.25rem;
45
+ font-size: 0.875rem;
46
+ }
47
+
48
+ .s-button[data-size='lg']:not([data-icon-only='true']) {
49
+ min-height: var(--s-control-hitbox-lg);
50
+ padding: 0.875rem 1.75rem;
51
+ font-size: 1.125rem;
52
+ }
53
+
54
+ .s-button[data-icon-only='true'] {
55
+ flex: 0 0 auto;
56
+ padding: 0;
57
+ }
58
+
59
+ .s-button[data-icon-only='true'][data-size='xs'] { width: var(--s-control-hitbox-xs); height: var(--s-control-hitbox-xs); }
60
+ .s-button[data-icon-only='true'][data-size='sm'] { width: var(--s-control-hitbox-sm); height: var(--s-control-hitbox-sm); }
61
+ .s-button[data-icon-only='true'][data-size='md'] { width: var(--s-control-hitbox-md); height: var(--s-control-hitbox-md); }
62
+ .s-button[data-icon-only='true'][data-size='lg'] { width: var(--s-control-hitbox-lg); height: var(--s-control-hitbox-lg); }
63
+
64
+ .s-button[data-severity='secondary'] {
65
+ --s-button-accent: var(--s-button-secondary-accent);
66
+ --s-button-solid-background: var(--s-button-secondary-solid-background);
67
+ --s-button-solid-background-hover: var(--s-button-secondary-solid-background-hover);
68
+ --s-button-solid-background-active: var(--s-button-secondary-solid-background-active);
69
+ --s-button-on-solid: var(--s-button-secondary-solid-foreground);
70
+ --s-button-foreground: var(--s-button-secondary-foreground);
71
+ --s-button-outline: var(--color-surface-300);
72
+ }
73
+
74
+ .s-button[data-severity='success'] {
75
+ --s-button-accent: var(--s-button-success-accent);
76
+ --s-button-solid-background: var(--s-button-success-solid-background);
77
+ --s-button-solid-background-hover: var(--s-button-success-solid-background-hover);
78
+ --s-button-solid-background-active: var(--s-button-success-solid-background-active);
79
+ --s-button-on-solid: var(--s-button-success-solid-foreground);
80
+ --s-button-foreground: var(--s-button-success-foreground);
81
+ }
82
+
83
+ .s-button[data-severity='info'] {
84
+ --s-button-accent: var(--s-button-info-accent);
85
+ --s-button-solid-background: var(--s-button-info-solid-background);
86
+ --s-button-solid-background-hover: var(--s-button-info-solid-background-hover);
87
+ --s-button-solid-background-active: var(--s-button-info-solid-background-active);
88
+ --s-button-on-solid: var(--s-button-info-solid-foreground);
89
+ --s-button-foreground: var(--s-button-info-foreground);
90
+ }
91
+
92
+ .s-button[data-severity='warn'] {
93
+ --s-button-accent: var(--s-button-warn-accent);
94
+ --s-button-solid-background: var(--s-button-warn-solid-background);
95
+ --s-button-solid-background-hover: var(--s-button-warn-solid-background-hover);
96
+ --s-button-solid-background-active: var(--s-button-warn-solid-background-active);
97
+ --s-button-on-solid: var(--s-button-warn-solid-foreground);
98
+ --s-button-foreground: var(--s-button-warn-foreground);
99
+ }
100
+
101
+ .s-button[data-severity='danger'] {
102
+ --s-button-accent: var(--s-button-danger-accent);
103
+ --s-button-solid-background: var(--s-button-danger-solid-background);
104
+ --s-button-solid-background-hover: var(--s-button-danger-solid-background-hover);
105
+ --s-button-solid-background-active: var(--s-button-danger-solid-background-active);
106
+ --s-button-on-solid: var(--s-button-danger-solid-foreground);
107
+ --s-button-foreground: var(--s-button-danger-foreground);
108
+ }
109
+
110
+ .s-button[data-severity='contrast'] {
111
+ --s-button-accent: var(--s-button-contrast-accent);
112
+ --s-button-solid-background: var(--s-button-contrast-solid-background);
113
+ --s-button-solid-background-hover: var(--s-button-contrast-solid-background-hover);
114
+ --s-button-solid-background-active: var(--s-button-contrast-solid-background-active);
115
+ --s-button-on-solid: var(--s-button-contrast-solid-foreground);
116
+ --s-button-foreground: var(--s-button-contrast-foreground);
117
+ --s-button-outline: var(--color-surface-500);
118
+ }
119
+
120
+ .s-button[data-appearance='solid'] {
121
+ border-color: var(--s-button-solid-background);
122
+ background: var(--s-button-solid-background);
123
+ box-shadow: var(--s-elevation-control);
124
+ color: var(--s-button-on-solid);
125
+ }
126
+
127
+ .s-button[data-appearance='solid']:hover:not(:disabled) {
128
+ border-color: var(--s-button-solid-background-hover);
129
+ background: var(--s-button-solid-background-hover);
130
+ }
131
+
132
+ .s-button[data-appearance='solid']:active:not(:disabled) {
133
+ border-color: var(--s-button-solid-background-active);
134
+ background: var(--s-button-solid-background-active);
135
+ }
136
+
137
+ .s-button[data-appearance='soft'] {
138
+ background: var(--s-button-soft-background);
139
+ color: var(--s-button-foreground);
140
+ }
141
+
142
+ .s-button[data-appearance='soft']:hover:not(:disabled) {
143
+ background: color-mix(in srgb, var(--s-button-accent) 20%, transparent);
144
+ }
145
+
146
+ .s-button[data-appearance='outline'] {
147
+ border-color: var(--s-button-outline);
148
+ background: transparent;
149
+ color: var(--s-button-foreground);
150
+ }
151
+
152
+ .s-button[data-appearance='outline']:hover:not(:disabled),
153
+ .s-button[data-appearance='ghost']:hover:not(:disabled) {
154
+ background: var(--s-button-hover-background);
155
+ }
156
+
157
+ .s-button[data-appearance='ghost'],
158
+ .s-button[data-appearance='text'] {
159
+ background: transparent;
160
+ box-shadow: none;
161
+ color: var(--s-button-foreground);
162
+ }
163
+
164
+ .s-button[data-appearance='text'] {
165
+ padding-inline: 0.25rem;
166
+ }
167
+
168
+ .s-button[data-appearance='text']:hover:not(:disabled) {
169
+ text-decoration: underline;
170
+ }
171
+
172
+ .s-button:focus-visible {
173
+ z-index: var(--s-layer-focused-control);
174
+ outline: var(--s-focus-ring-width) solid var(--s-button-accent);
175
+ outline-offset: var(--s-focus-ring-offset);
176
+ }
177
+
178
+ .s-button:active:not(:disabled) {
179
+ transform: translateY(1px);
180
+ }
181
+
182
+ .s-button[aria-pressed='true'],
183
+ .s-button[aria-pressed='mixed'] {
184
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--s-button-accent) 30%, transparent);
185
+ }
186
+
187
+ .s-button[data-appearance='ghost'][aria-current='page'],
188
+ .s-button[data-appearance='soft'][aria-current='page'] {
189
+ background: var(--s-button-soft-background);
190
+ color: var(--s-button-foreground);
191
+ }
192
+
193
+ .s-button:disabled {
194
+ border-color: transparent;
195
+ background: var(--s-button-disabled-background);
196
+ box-shadow: none;
197
+ color: var(--s-button-disabled-foreground);
198
+ cursor: not-allowed;
199
+ }
200
+
201
+ .s-button[data-appearance='ghost']:disabled,
202
+ .s-button[data-appearance='text']:disabled {
203
+ background: transparent;
204
+ }
205
+
206
+ .s-button[data-loading='true'] {
207
+ cursor: wait;
208
+ opacity: 0.75;
209
+ }
210
+
211
+ .s-button[data-loading='true'] .s-button__content {
212
+ opacity: 0;
213
+ }
214
+
215
+ .s-button__content {
216
+ @apply inline-flex min-w-0 shrink-0 items-center justify-center;
217
+ gap: var(--s-space-control-gap);
218
+ }
219
+
220
+ .s-button[data-width='full'] {
221
+ display: flex;
222
+ width: 100%;
223
+ }
224
+
225
+ .s-button[data-width='fit'] { width: fit-content; }
226
+ .s-button[data-width='full'] .s-button__content { width: 100%; }
227
+ .s-button[data-content-align='start'] .s-button__content { justify-content: flex-start; text-align: left; }
228
+ .s-button[data-content-align='between'] .s-button__content { justify-content: space-between; text-align: left; }
229
+
230
+ .s-button[data-shape='pill'],
231
+ .s-button[data-shape='circle'] { border-radius: var(--s-radius-pill); }
232
+ .s-button[data-shape='square'],
233
+ .s-button[data-shape='row'] { border-radius: var(--radius-xs); }
234
+ .s-button[data-shape='row'] { width: 100%; }
235
+
236
+ .s-button__label {
237
+ min-width: 0;
238
+ overflow: hidden;
239
+ text-overflow: ellipsis;
240
+ white-space: nowrap;
241
+ }
242
+
243
+ .s-button__badge-slot {
244
+ @apply ml-auto inline-flex;
245
+ }
246
+
247
+ .s-button__shortcut {
248
+ @apply ml-auto whitespace-nowrap text-right text-xs leading-4;
249
+ color: var(--color-surface-400);
250
+ }
251
+
252
+ :global(.dark) .s-button__shortcut { color: var(--color-surface-500); }
253
+
254
+ .s-button__icon {
255
+ width: var(--s-icon-size-xs);
256
+ height: var(--s-icon-size-xs);
257
+ flex: 0 0 auto;
258
+ }
259
+
260
+ .s-button[data-size='lg'] .s-button__icon {
261
+ width: var(--s-icon-size-sm);
262
+ height: var(--s-icon-size-sm);
263
+ }
264
+
265
+ .s-button__loader {
266
+ @apply absolute inline-flex;
267
+ }
268
+
269
+ .s-button__disclosure-icon {
270
+ transition: transform var(--s-motion-fast) ease;
271
+ }
272
+
273
+ .s-button__disclosure-icon[data-expanded='true'] { transform: rotate(90deg); }
274
+ .s-button__disclosure-icon > svg { width: 100%; height: 100%; }
@@ -0,0 +1,211 @@
1
+ <template>
2
+ <button
3
+ ref="buttonRef"
4
+ v-bind="buttonBindings"
5
+ class="s-button"
6
+ :data-severity="severity"
7
+ :data-size="size"
8
+ :data-width="width"
9
+ :data-content-align="contentAlign"
10
+ :data-icon-only="isIconOnly"
11
+ :data-appearance="appearance"
12
+ :data-shape="shape"
13
+ :data-loading="loading || undefined"
14
+ :aria-pressed="pressed"
15
+ :aria-busy="busy || loading || undefined"
16
+ :disabled="disabled || loading"
17
+ :type="type"
18
+ >
19
+ <span v-if="loading" class="s-button__loader" aria-hidden="true">
20
+ <SProgressIndicator
21
+ :accessibility="{ mode: 'decorative' }"
22
+ :size="progressIndicatorSize"
23
+ />
24
+ </span>
25
+ <span class="s-button__content">
26
+ <span
27
+ v-if="disclosure?.iconPlacement === 'leading'"
28
+ class="s-button__disclosure-icon s-button__icon s-button__icon--leading"
29
+ :data-expanded="disclosure.expanded"
30
+ data-disclosure-icon
31
+ aria-hidden="true"
32
+ ><ChevronRight /></span>
33
+ <component :is="leadingIcon" v-else-if="leadingIcon" class="s-button__icon s-button__icon--leading" aria-hidden="true" />
34
+ <span v-if="label" class="s-button__label">{{ label }}</span>
35
+ <PassiveContentBoundary v-if="renderDefaultSlot" :contract="passiveContentContract">
36
+ <slot />
37
+ </PassiveContentBoundary>
38
+ <span v-if="badge !== undefined" class="s-button__badge-slot">
39
+ <SBadge :severity="badgeSeverity" size="xs">{{ badge }}</SBadge>
40
+ </span>
41
+ <span v-if="shortcut" class="s-button__shortcut" aria-hidden="true">{{ shortcut }}</span>
42
+ <span
43
+ v-if="disclosure && disclosure.iconPlacement !== 'leading' && disclosure.iconPlacement !== 'none'"
44
+ class="s-button__disclosure-icon s-button__icon s-button__icon--trailing"
45
+ :data-expanded="disclosure.expanded"
46
+ data-disclosure-icon
47
+ aria-hidden="true"
48
+ ><ChevronRight /></span>
49
+ <component :is="trailingIcon" v-else-if="trailingIcon" class="s-button__icon s-button__icon--trailing" aria-hidden="true" />
50
+ </span>
51
+ </button>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import { computed, ref, watchEffect } from 'vue'
56
+ import type { Component, VNodeChild } from 'vue'
57
+ import { ChevronRight } from '../../icons/sputnigUiIcons'
58
+ import type { InteractiveElementApi } from '../../../internal/interactiveElement'
59
+ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
60
+ import {
61
+ PassiveContentBoundary,
62
+ useInteractiveLeafRegistration,
63
+ type PassiveContentContract,
64
+ } from '../../../internal/passiveContentContract'
65
+ import { useTreeActionRegistration } from '../_internal/treeRuntime'
66
+ import SBadge, { type BadgeSeverity } from '../data-display/SBadge.vue'
67
+ import SProgressIndicator, { type SProgressIndicatorSize } from '../data-display/SProgressIndicator.vue'
68
+
69
+ defineOptions({ inheritAttrs: false })
70
+
71
+ export type SButtonSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'contrast'
72
+ export type SButtonSize = 'xs' | 'sm' | 'md' | 'lg'
73
+ export type SButtonWidth = 'auto' | 'fit' | 'full'
74
+ export type SButtonContentAlign = 'center' | 'start' | 'between'
75
+ export type SButtonAppearance = 'solid' | 'soft' | 'outline' | 'ghost' | 'text'
76
+ export type SButtonShape = 'default' | 'pill' | 'square' | 'circle' | 'row'
77
+ export type SButtonNativeType = 'button' | 'reset' | 'submit'
78
+ export interface SButtonDisclosure {
79
+ expanded: boolean
80
+ controls: string
81
+ iconPlacement?: 'leading' | 'trailing' | 'none'
82
+ }
83
+
84
+ /** Typed native-button ownership surface for composite UI-kit components. / Типизированная ownership-поверхность native button для составных UI-kit-компонентов. */
85
+ export type SButtonApi = InteractiveElementApi<HTMLButtonElement>
86
+
87
+ export interface Props {
88
+ label?: string
89
+ severity?: SButtonSeverity
90
+ size?: SButtonSize
91
+ disabled?: boolean
92
+ loading?: boolean
93
+ loadingLabel?: string
94
+ leadingIcon?: Component
95
+ trailingIcon?: Component
96
+ badge?: string | number
97
+ badgeSeverity?: BadgeSeverity
98
+ shortcut?: string
99
+ iconOnly?: boolean
100
+ width?: SButtonWidth
101
+ contentAlign?: SButtonContentAlign
102
+ appearance?: SButtonAppearance
103
+ shape?: SButtonShape
104
+ pressed?: boolean | 'mixed'
105
+ busy?: boolean
106
+ disclosure?: SButtonDisclosure
107
+ type?: SButtonNativeType
108
+ }
109
+
110
+ const props = withDefaults(defineProps<Props>(), {
111
+ label: undefined,
112
+ severity: 'primary',
113
+ size: 'md',
114
+ disabled: false,
115
+ loading: false,
116
+ loadingLabel: undefined,
117
+ leadingIcon: undefined,
118
+ trailingIcon: undefined,
119
+ badge: undefined,
120
+ badgeSeverity: 'secondary',
121
+ shortcut: undefined,
122
+ iconOnly: false,
123
+ width: 'auto',
124
+ contentAlign: 'center',
125
+ appearance: 'solid',
126
+ shape: 'default',
127
+ pressed: undefined,
128
+ busy: false,
129
+ disclosure: undefined,
130
+ type: 'button',
131
+ })
132
+ const slots = defineSlots<{
133
+ /** Пассивная метка/графика действия; вложенные controls запрещены. / Passive action label/graphics; nested controls are forbidden. */
134
+ default?: () => VNodeChild
135
+ }>()
136
+ const buttonRef = ref<HTMLButtonElement | null>(null)
137
+ const ownedAttrs = useOwnedAttrs({ component: 'SButton', owner: 'native button' })
138
+ const isRegisteredTreeAction = useTreeActionRegistration({
139
+ owner: 'SButton',
140
+ element: buttonRef,
141
+ disabled: () => props.disabled || props.loading,
142
+ })
143
+
144
+ useInteractiveLeafRegistration({ owner: 'SButton' })
145
+
146
+ const buttonBindings = computed(() => {
147
+ const bindings = ownedAttrs.bindings()
148
+ const managedBindings = isRegisteredTreeAction ? { ...bindings, tabindex: -1 } : bindings
149
+ const semanticBindings = props.disclosure
150
+ ? {
151
+ ...managedBindings,
152
+ 'aria-expanded': props.disclosure.expanded,
153
+ 'aria-controls': props.disclosure.controls,
154
+ }
155
+ : managedBindings
156
+ if (!props.loading) return semanticBindings
157
+
158
+ const externalAccessibleName = semanticBindings['aria-label']
159
+ const accessibleName = [props.loadingLabel, props.label, externalAccessibleName]
160
+ .find((candidate): candidate is string => typeof candidate === 'string' && candidate.trim().length > 0)
161
+ if (!accessibleName && !slots.default) {
162
+ throw new Error('SButton: loading action requires label, loadingLabel, or aria-label')
163
+ }
164
+ return accessibleName
165
+ ? { ...semanticBindings, 'aria-label': accessibleName }
166
+ : semanticBindings
167
+ })
168
+
169
+ const passiveContentContract: PassiveContentContract = {
170
+ owner: 'SButton',
171
+ region: 'passive default content',
172
+ guidance: 'Вынесите независимое действие в соседний SButton. / Move the independent action to a sibling SButton.',
173
+ }
174
+
175
+ watchEffect(() => {
176
+ if (props.disclosure && props.disclosure.controls.trim().length === 0) {
177
+ throw new Error('SButton: disclosure.controls must be a non-empty id')
178
+ }
179
+ const placement = props.disclosure?.iconPlacement ?? 'trailing'
180
+ if (props.disclosure && (
181
+ (placement === 'leading' && props.leadingIcon)
182
+ || (placement === 'trailing' && props.trailingIcon)
183
+ )) {
184
+ throw new Error(`SButton: disclosure ${placement} icon conflicts with the matching icon prop`)
185
+ }
186
+ })
187
+
188
+ const isIconOnly = computed(() => props.iconOnly || (
189
+ props.label === '' && Boolean(props.leadingIcon || props.trailingIcon || slots.default)
190
+ ))
191
+ const renderDefaultSlot = computed(() => props.label === undefined || (
192
+ props.label === '' && !props.leadingIcon && !props.trailingIcon
193
+ ))
194
+ const progressIndicatorSize = computed<SProgressIndicatorSize>(() => props.size === 'lg' ? 'sm' : 'xs')
195
+
196
+ const getElement = (): HTMLButtonElement => {
197
+ const element = buttonRef.value
198
+ if (!element) {
199
+ throw new Error('SButton: native button element is unavailable')
200
+ }
201
+ return element
202
+ }
203
+
204
+ defineExpose<SButtonApi>({
205
+ getElement,
206
+ focus: (options?: FocusOptions) => getElement().focus(options),
207
+ })
208
+
209
+ </script>
210
+
211
+ <style lang="postcss" src="./SButton.css" scoped></style>
@@ -0,0 +1,65 @@
1
+ /*
2
+ Стили компонента SCheckbox.
3
+ */
4
+ @reference "../../../styles/reference.css";
5
+
6
+ .s-checkbox-control {
7
+ @apply relative inline-flex items-center;
8
+ width: var(--s-control-indicator-size);
9
+ height: var(--s-control-indicator-size);
10
+ }
11
+
12
+ /* Hide the browser's default checkbox */
13
+ .s-checkbox-input {
14
+ @apply absolute opacity-0 w-0 h-0;
15
+ }
16
+
17
+ /* The custom checkbox */
18
+ .s-checkbox-box {
19
+ @apply relative shrink-0 rounded-md border;
20
+ width: var(--s-control-indicator-size);
21
+ height: var(--s-control-indicator-size);
22
+ @apply border-surface-300 dark:border-surface-600;
23
+ @apply bg-surface-0 dark:bg-surface-900;
24
+ @apply transition-colors;
25
+ transition-duration: var(--s-motion-standard);
26
+ @apply flex items-center justify-center;
27
+ }
28
+
29
+ /* Hover state */
30
+ .s-checkbox-input:not(:disabled) + .s-checkbox-box {
31
+ @apply hover:border-primary-500 dark:hover:border-primary-400;
32
+ }
33
+
34
+ /* Checked state */
35
+ .s-checkbox-input:checked + .s-checkbox-box {
36
+ @apply border-primary-500 bg-primary-500;
37
+ @apply dark:border-primary-400 dark:bg-primary-400;
38
+ }
39
+
40
+ /* Disabled state */
41
+ .s-checkbox-input:disabled + .s-checkbox-box {
42
+ @apply border-surface-200 bg-surface-100;
43
+ @apply dark:border-surface-700 dark:bg-surface-800;
44
+ }
45
+
46
+ /* Checkmark icon */
47
+ .s-checkbox-check {
48
+ @apply text-white opacity-0 transition-opacity;
49
+ width: var(--s-icon-size-xs);
50
+ height: var(--s-icon-size-xs);
51
+ }
52
+
53
+ .s-checkbox-input:checked + .s-checkbox-box > .s-checkbox-check {
54
+ @apply opacity-100;
55
+ }
56
+
57
+ .s-checkbox-input:disabled + .s-checkbox-box > .s-checkbox-check {
58
+ @apply text-surface-400 dark:text-surface-600;
59
+ }
60
+
61
+ /* Focus state */
62
+ .s-checkbox-input:focus-visible + .s-checkbox-box {
63
+ outline: var(--s-focus-ring-width) solid var(--s-focus-ring-strong-color);
64
+ outline-offset: var(--s-focus-ring-offset);
65
+ }
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <SField
3
+ :control-id="inputId"
4
+ :label="label"
5
+ :required="required"
6
+ :help-text="helpText"
7
+ :error-message="errorMessage"
8
+ :invalid="invalid"
9
+ :disabled="disabled"
10
+ :readonly="readonly"
11
+ layout="inline"
12
+ >
13
+ <template #default="field">
14
+ <span class="s-checkbox-control">
15
+ <input
16
+ ref="inputEl"
17
+ v-bind="ownedAttrs.bindings()"
18
+ :id="field.controlId"
19
+ type="checkbox"
20
+ class="s-checkbox-input"
21
+ :checked="checkedValue"
22
+ :disabled="field.disabled"
23
+ :required="field.required"
24
+ :aria-invalid="field.invalid || undefined"
25
+ :aria-readonly="field.readonly || undefined"
26
+ :aria-describedby="mergeIdReferences(ownedAttrs.string('aria-describedby'), field.describedBy)"
27
+ @click.stop="onClick"
28
+ @change="onChange"
29
+ />
30
+ <span class="s-checkbox-box">
31
+ <component
32
+ :is="indeterminate ? Minus : Check"
33
+ class="s-checkbox-check"
34
+ aria-hidden="true"
35
+ :data-sputnig-ui-icon="indeterminate ? 'minus' : 'check'"
36
+ data-sputnig-ui-icon-color-mode="auto"
37
+ data-sputnig-ui-icon-set="classic"
38
+ />
39
+ </span>
40
+ </span>
41
+ </template>
42
+ </SField>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import { computed, onMounted, ref, useId, watch } from 'vue'
47
+ import { Check, Minus } from '../../icons/sputnigUiIcons'
48
+ import { mergeIdReferences, useOwnedAttrs } from '../../../internal/ownedAttrs'
49
+ import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
50
+ import { resolveOptionalDomId } from '../../../internal/runtimeContract'
51
+ import SField from './SField.vue'
52
+
53
+ defineOptions({ inheritAttrs: false })
54
+
55
+ export interface Props {
56
+ modelValue: boolean
57
+ label?: string
58
+ disabled?: boolean
59
+ readonly?: boolean
60
+ required?: boolean
61
+ invalid?: boolean
62
+ helpText?: string
63
+ errorMessage?: string
64
+ id?: string
65
+ indeterminate?: boolean
66
+ }
67
+
68
+ const props = withDefaults(defineProps<Props>(), {
69
+ label: undefined,
70
+ disabled: false,
71
+ readonly: false,
72
+ required: false,
73
+ invalid: false,
74
+ helpText: undefined,
75
+ errorMessage: undefined,
76
+ id: undefined,
77
+ indeterminate: false,
78
+ })
79
+
80
+ const emit = defineEmits<{
81
+ 'update:modelValue': [value: boolean]
82
+ click: [event: MouseEvent]
83
+ }>()
84
+ const ownedAttrs = useOwnedAttrs({ component: 'SCheckbox', owner: 'native checkbox input' })
85
+ useInteractiveLeafRegistration({ owner: 'SCheckbox' })
86
+
87
+ const generatedInputId = useId()
88
+ const inputId = computed(() => resolveOptionalDomId(
89
+ 'SCheckbox',
90
+ 'id',
91
+ props.id,
92
+ `s-checkbox-${generatedInputId}`,
93
+ ))
94
+ const inputEl = ref<HTMLInputElement | null>(null)
95
+
96
+ const checkedValue = computed(() => {
97
+ if (typeof props.modelValue !== 'boolean') {
98
+ throw new TypeError('SCheckbox: modelValue must be boolean')
99
+ }
100
+ return props.modelValue
101
+ })
102
+
103
+ function applyIndeterminate(): void {
104
+ if (inputEl.value) {
105
+ inputEl.value.indeterminate = props.indeterminate && !checkedValue.value
106
+ }
107
+ }
108
+
109
+ onMounted(applyIndeterminate)
110
+ watch([() => props.indeterminate, checkedValue], applyIndeterminate)
111
+
112
+ function onChange(event: Event): void {
113
+ if (props.readonly) return
114
+ const target = event.currentTarget
115
+ if (!(target instanceof HTMLInputElement)) {
116
+ throw new TypeError('SCheckbox: change event owner must be the native checkbox input')
117
+ }
118
+ emit('update:modelValue', target.checked)
119
+ }
120
+
121
+ function onClick(event: MouseEvent): void {
122
+ if (props.readonly) {
123
+ event.preventDefault()
124
+ return
125
+ }
126
+ emit('click', event)
127
+ }
128
+ </script>
129
+
130
+ <style lang="postcss" src="./SCheckbox.css" scoped></style>