@pgcorp/ui-kit 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/docs/public-api.md +8 -0
- package/package.json +15 -3
- package/src/components/layout/SAppShell.vue +3 -2
- package/src/components/layout/SStack.css +9 -3
- package/src/components/layout/SStack.vue +10 -2
- package/src/components/shared/complex/STree.vue +29 -4
- package/src/components/shared/complex/treeAdapter.ts +2 -1
- package/src/components/shared/complex/types.ts +1 -12
- package/src/components/shared/containers/SLeftSidebar.vue +7 -2
- package/src/components/shared/containers/SPageHeader.css +11 -0
- package/src/components/shared/containers/SPageHeader.vue +7 -1
- package/src/components/shared/containers/SPopover.vue +2 -1
- package/src/components/shared/containers/SSidebarGroup.vue +10 -5
- package/src/components/shared/containers/sidebar.ts +2 -1
- package/src/components/shared/controls/SButton.css +13 -0
- package/src/components/shared/controls/SButton.vue +135 -29
- package/src/components/shared/controls/SCombobox.css +9 -0
- package/src/components/shared/controls/SCombobox.vue +322 -0
- package/src/components/shared/controls/SInputText.vue +15 -0
- package/src/components/shared/controls/SInteractiveSurface.vue +2 -1
- package/src/components/shared/controls/SLink.ts +1 -17
- package/src/components/shared/controls/SLink.vue +26 -2
- package/src/components/shared/controls/SListbox.vue +99 -1
- package/src/components/shared/controls/SListboxOption.vue +15 -2
- package/src/components/shared/controls/SSwitch.css +48 -0
- package/src/components/shared/controls/SSwitch.vue +109 -0
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +6 -3
- package/src/components/shared/data-display/SChip.css +59 -0
- package/src/components/shared/data-display/SChip.vue +92 -0
- package/src/components/shared/data-display/SDocBlock.vue +10 -10
- package/src/components/shared/data-display/SLinkedSystemsList.vue +3 -3
- package/src/components/shared/data-display/STable.vue +150 -14
- package/src/components/shared/data-display/STooltip.vue +2 -1
- package/src/components/shared/data-display/SVirtualList.ts +1 -16
- package/src/components/shared/data-display/SVirtualList.vue +18 -2
- package/src/components/shared/data-display/json.ts +1 -1
- package/src/components/shared/data-display/table.ts +9 -28
- package/src/components/shared/database/SDataGrid.vue +29 -8
- package/src/components/shared/database/SSqlEditor.vue +3 -2
- package/src/components/shared/database/dataGrid.ts +1 -17
- package/src/components/shared/navigation/SBottomNav.vue +3 -0
- package/src/components/shared/navigation/STabs.vue +2 -1
- package/src/composables/uiPreferencesReset.ts +2 -1
- package/src/composables/useClipboard.ts +2 -1
- package/src/composables/useSidebarPanelState.ts +3 -2
- package/src/internal/es2020.ts +50 -0
- package/src/internal/inlineTokenEditorContract.ts +4 -3
- package/src/internal/ownedAttrs.ts +24 -2
- package/src/internal/pointerInteractionLease.ts +2 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
**A typed PGCorp design system for Vue 3, spanning foundational controls and
|
|
7
7
|
complete workbench interfaces.**
|
|
8
8
|
|
|
9
|
-
`Vue 3` · `TypeScript` · `
|
|
9
|
+
`Vue 3` · `TypeScript` · `122 exact exports` · `6 themes` · `MIT`
|
|
10
10
|
|
|
11
11
|
## Что входит / What is included
|
|
12
12
|
|
|
@@ -32,7 +32,7 @@ npm install @pgcorp/ui-kit vue pinia vue-router
|
|
|
32
32
|
| Требование / Requirement | Версия / Version |
|
|
33
33
|
| --- | --- |
|
|
34
34
|
| Node.js | `>=20.19.0` |
|
|
35
|
-
| npm | `>=
|
|
35
|
+
| npm | `>=10.9.2 <12` |
|
|
36
36
|
| Vue | `^3.5.0` |
|
|
37
37
|
| Pinia | `^3.0.4` |
|
|
38
38
|
| Vue Router | `^5.0.0` |
|
|
@@ -82,7 +82,7 @@ import SButton from '@pgcorp/ui-kit/shared/controls/SButton.vue'
|
|
|
82
82
|
content-layout="stack"
|
|
83
83
|
content-gap="content"
|
|
84
84
|
>
|
|
85
|
-
<SButton
|
|
85
|
+
<SButton appearance="solid" severity="primary">
|
|
86
86
|
Сохранить
|
|
87
87
|
</SButton>
|
|
88
88
|
</SPanel>
|
|
@@ -100,9 +100,9 @@ monorepo alias or copied UI-kit files.
|
|
|
100
100
|
| Группа / Group | Назначение / Purpose | Примеры exports / Example exports |
|
|
101
101
|
| --- | --- | --- |
|
|
102
102
|
| Layout | App shell, dock regions, stacks, workbench layout | `layout/SAppShell.vue`, `layout/SStack.vue`, `layout/SWorkbenchLayout.vue` |
|
|
103
|
-
| Controls | Buttons, fields, inputs, selects,
|
|
103
|
+
| Controls | Buttons, fields, inputs, switches, selects, comboboxes, menus | `shared/controls/SButton.vue`, `shared/controls/SSwitch.vue`, `shared/controls/SCombobox.vue` |
|
|
104
104
|
| Containers | Panels, modal, drawer, popover, sidebars | `shared/containers/SPanel.vue`, `shared/containers/SModal.vue`, `shared/containers/SPopover.vue` |
|
|
105
|
-
| Data display | Tables, status, progress, code, JSON, tooltips | `shared/data-display/STable.vue`, `shared/data-display/
|
|
105
|
+
| Data display | Tables, chips, status, progress, code, JSON, tooltips | `shared/data-display/STable.vue`, `shared/data-display/SChip.vue`, `shared/data-display/SStatus.vue` |
|
|
106
106
|
| Navigation | Tabs, breadcrumbs, catalog navigation, wizard | `shared/navigation/STabs.vue`, `shared/navigation/SBreadcrumbs.vue`, `shared/navigation/SWizardSteps.vue` |
|
|
107
107
|
| Complex surfaces | Tree, Kanban, data grid, SQL editor, graph | `shared/complex/STree.vue`, `shared/database/SDataGrid.vue`, `shared/graph/SGraphViewport.vue` |
|
|
108
108
|
| Runtime | Themes, variants, icons, composables, stores | `theme`, `variants`, `icons`, `composables/useNotifier`, `stores/useNotifierStore` |
|
package/docs/public-api.md
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
|
|
19
19
|
Компонентный contract выражается через typed props, slots, events и exposed
|
|
20
20
|
methods. Внутренний DOM, CSS-классы и private modules не являются API.
|
|
21
|
+
Публичные source-SFC объявляют runtime props и events локально: consumer Vue
|
|
22
|
+
compiler регистрирует их без межфайлового filesystem type resolver.
|
|
23
|
+
Публикуемый production-source компилируется с `target/lib: ES2020`, совпадающим
|
|
24
|
+
с baseline `@vue/tsconfig/tsconfig.dom.json`, и не требует скрытых полифиллов.
|
|
21
25
|
|
|
22
26
|
## English
|
|
23
27
|
|
|
@@ -37,3 +41,7 @@ Primary groups:
|
|
|
37
41
|
|
|
38
42
|
Component contracts are expressed through typed props, slots, events, and exposed
|
|
39
43
|
methods. Internal DOM, CSS classes, and private modules are not API.
|
|
44
|
+
Public source SFCs declare runtime props and events locally, so the consumer Vue
|
|
45
|
+
compiler registers them without a cross-file filesystem type resolver.
|
|
46
|
+
Published production source compiles with the `target/lib: ES2020` baseline from
|
|
47
|
+
`@vue/tsconfig/tsconfig.dom.json` and does not require implicit polyfills.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgcorp/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Typed Vue 3 design system with accessible components, semantic themes, and workbench patterns.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=20.19.0",
|
|
41
|
-
"npm": ">=
|
|
41
|
+
"npm": ">=10.9.2 <12"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public",
|
|
@@ -207,6 +207,9 @@
|
|
|
207
207
|
"shared/controls/SColorSelect.vue": [
|
|
208
208
|
"src/components/shared/controls/SColorSelect.vue"
|
|
209
209
|
],
|
|
210
|
+
"shared/controls/SCombobox.vue": [
|
|
211
|
+
"src/components/shared/controls/SCombobox.vue"
|
|
212
|
+
],
|
|
210
213
|
"shared/controls/SContextMenu.vue": [
|
|
211
214
|
"src/components/shared/controls/SContextMenu.vue"
|
|
212
215
|
],
|
|
@@ -261,6 +264,9 @@
|
|
|
261
264
|
"shared/controls/SSelect.vue": [
|
|
262
265
|
"src/components/shared/controls/SSelect.vue"
|
|
263
266
|
],
|
|
267
|
+
"shared/controls/SSwitch.vue": [
|
|
268
|
+
"src/components/shared/controls/SSwitch.vue"
|
|
269
|
+
],
|
|
264
270
|
"shared/controls/STextarea.vue": [
|
|
265
271
|
"src/components/shared/controls/STextarea.vue"
|
|
266
272
|
],
|
|
@@ -279,6 +285,9 @@
|
|
|
279
285
|
"shared/data-display/SBadge.vue": [
|
|
280
286
|
"src/components/shared/data-display/SBadge.vue"
|
|
281
287
|
],
|
|
288
|
+
"shared/data-display/SChip.vue": [
|
|
289
|
+
"src/components/shared/data-display/SChip.vue"
|
|
290
|
+
],
|
|
282
291
|
"shared/data-display/SCodeBlock.vue": [
|
|
283
292
|
"src/components/shared/data-display/SCodeBlock.vue"
|
|
284
293
|
],
|
|
@@ -468,6 +477,7 @@
|
|
|
468
477
|
"./shared/controls/SCheckboxGroup.vue": "./src/components/shared/controls/SCheckboxGroup.vue",
|
|
469
478
|
"./shared/controls/SChoiceCards.vue": "./src/components/shared/controls/SChoiceCards.vue",
|
|
470
479
|
"./shared/controls/SColorSelect.vue": "./src/components/shared/controls/SColorSelect.vue",
|
|
480
|
+
"./shared/controls/SCombobox.vue": "./src/components/shared/controls/SCombobox.vue",
|
|
471
481
|
"./shared/controls/SContextMenu.vue": "./src/components/shared/controls/SContextMenu.vue",
|
|
472
482
|
"./shared/controls/SContextToggleButton.vue": "./src/components/shared/controls/SContextToggleButton.vue",
|
|
473
483
|
"./shared/controls/SDragHandle.vue": "./src/components/shared/controls/SDragHandle.vue",
|
|
@@ -486,12 +496,14 @@
|
|
|
486
496
|
"./shared/controls/SRange.vue": "./src/components/shared/controls/SRange.vue",
|
|
487
497
|
"./shared/controls/SSegmentedControl.vue": "./src/components/shared/controls/SSegmentedControl.vue",
|
|
488
498
|
"./shared/controls/SSelect.vue": "./src/components/shared/controls/SSelect.vue",
|
|
499
|
+
"./shared/controls/SSwitch.vue": "./src/components/shared/controls/SSwitch.vue",
|
|
489
500
|
"./shared/controls/STextarea.vue": "./src/components/shared/controls/STextarea.vue",
|
|
490
501
|
"./shared/data-display/json": "./src/components/shared/data-display/json.ts",
|
|
491
502
|
"./shared/data-display/SActionCard.vue": "./src/components/shared/data-display/SActionCard.vue",
|
|
492
503
|
"./shared/data-display/SActionList.vue": "./src/components/shared/data-display/SActionList.vue",
|
|
493
504
|
"./shared/data-display/SActionListItem.vue": "./src/components/shared/data-display/SActionListItem.vue",
|
|
494
505
|
"./shared/data-display/SBadge.vue": "./src/components/shared/data-display/SBadge.vue",
|
|
506
|
+
"./shared/data-display/SChip.vue": "./src/components/shared/data-display/SChip.vue",
|
|
495
507
|
"./shared/data-display/SCodeBlock.vue": "./src/components/shared/data-display/SCodeBlock.vue",
|
|
496
508
|
"./shared/data-display/SCodeEditor.vue": "./src/components/shared/data-display/SCodeEditor.vue",
|
|
497
509
|
"./shared/data-display/SCodeSearchPanel.vue": "./src/components/shared/data-display/SCodeSearchPanel.vue",
|
|
@@ -582,7 +594,7 @@
|
|
|
582
594
|
"eslint-plugin-vue": "^10.8.0",
|
|
583
595
|
"jsdom": "^28.1.0",
|
|
584
596
|
"pinia": "^3.0.4",
|
|
585
|
-
"postcss": "8.5.
|
|
597
|
+
"postcss": "8.5.19",
|
|
586
598
|
"postcss-selector-parser": "7.1.4",
|
|
587
599
|
"storybook": "10.5.3",
|
|
588
600
|
"typescript": "^5.9.3",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, getCurrentInstance, useSlots } from 'vue'
|
|
3
3
|
import type { RouteLocationRaw } from 'vue-router'
|
|
4
|
+
import { hasOwn } from '../../internal/es2020'
|
|
4
5
|
import { useOwnedAttrs } from '../../internal/ownedAttrs'
|
|
5
6
|
import { validateNonEmptyString } from '../../internal/runtimeContract'
|
|
6
7
|
import SBadge from '../shared/data-display/SBadge.vue'
|
|
@@ -98,8 +99,8 @@ function toKebabCase(value: string): string {
|
|
|
98
99
|
function hasExplicitProp(propName: string): boolean {
|
|
99
100
|
const vnodeProps = componentInstance.vnode.props
|
|
100
101
|
return vnodeProps !== null && (
|
|
101
|
-
|
|
102
|
-
||
|
|
102
|
+
hasOwn(vnodeProps, propName)
|
|
103
|
+
|| hasOwn(vnodeProps, toKebabCase(propName))
|
|
103
104
|
)
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
@reference "../../styles/reference.css";
|
|
2
2
|
|
|
3
|
-
.s-stack {
|
|
4
|
-
|
|
5
|
-
}
|
|
3
|
+
.s-stack { @apply flex min-h-0 min-w-0; }
|
|
4
|
+
|
|
5
|
+
.s-stack[data-direction='column'] { @apply flex-col; }
|
|
6
|
+
.s-stack[data-direction='row'] { @apply flex-row; }
|
|
6
7
|
|
|
7
8
|
.s-stack[data-fill='container'] {
|
|
8
9
|
@apply h-full flex-1;
|
|
@@ -13,6 +14,11 @@
|
|
|
13
14
|
.s-stack[data-align='end'] { @apply items-end; }
|
|
14
15
|
.s-stack[data-align='stretch'] { @apply items-stretch; }
|
|
15
16
|
|
|
17
|
+
.s-stack[data-justify='start'] { @apply justify-start; }
|
|
18
|
+
.s-stack[data-justify='center'] { @apply justify-center; }
|
|
19
|
+
.s-stack[data-justify='end'] { @apply justify-end; }
|
|
20
|
+
.s-stack[data-justify='between'] { @apply justify-between; }
|
|
21
|
+
|
|
16
22
|
.s-stack[data-gap='tight'] { gap: 0.25rem; }
|
|
17
23
|
.s-stack[data-gap='control'] { gap: var(--s-space-control-gap); }
|
|
18
24
|
.s-stack[data-gap='content'] { gap: var(--s-space-content-gap); }
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
v-bind="ownedAttrs.bindings()"
|
|
5
5
|
class="s-stack"
|
|
6
6
|
:data-gap="gap"
|
|
7
|
+
:data-direction="direction"
|
|
7
8
|
:data-padding-end="paddingEnd"
|
|
8
9
|
:data-fill="fill"
|
|
9
10
|
:data-align="align"
|
|
11
|
+
:data-justify="justify"
|
|
10
12
|
>
|
|
11
13
|
<slot />
|
|
12
14
|
</component>
|
|
@@ -22,12 +24,14 @@ export type SStackGap = 'none' | 'tight' | 'control' | 'content' | 'section' | '
|
|
|
22
24
|
export type SStackPadding = 'none' | 'control' | 'content' | 'section' | 'panel'
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
+
* Flex layout-owner с ограниченной семантической шкалой интервалов.
|
|
28
|
+
* Flex layout owner with a constrained semantic spacing scale.
|
|
27
29
|
*/
|
|
28
30
|
export interface Props {
|
|
29
31
|
/** Семантический HTML-container без интерактивной роли. / Semantic non-interactive HTML container. */
|
|
30
32
|
as?: 'div' | 'section' | 'article' | 'ul' | 'ol'
|
|
33
|
+
/** Направление основной оси. / Main-axis direction. */
|
|
34
|
+
direction?: 'column' | 'row'
|
|
31
35
|
/** Интервал между непосредственными участниками. / Gap between direct participants. */
|
|
32
36
|
gap?: SStackGap
|
|
33
37
|
/** Нижний внутренний отступ для scroll-safe composition. / End padding for scroll-safe composition. */
|
|
@@ -36,14 +40,18 @@ export interface Props {
|
|
|
36
40
|
fill?: 'content' | 'container'
|
|
37
41
|
/** Выравнивание участников по поперечной оси. / Cross-axis participant alignment. */
|
|
38
42
|
align?: 'stretch' | 'start' | 'center' | 'end'
|
|
43
|
+
/** Распределение участников по основной оси. / Main-axis participant distribution. */
|
|
44
|
+
justify?: 'start' | 'center' | 'end' | 'between'
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
withDefaults(defineProps<Props>(), {
|
|
42
48
|
as: 'div',
|
|
49
|
+
direction: 'column',
|
|
43
50
|
gap: 'content',
|
|
44
51
|
paddingEnd: 'none',
|
|
45
52
|
fill: 'content',
|
|
46
53
|
align: 'stretch',
|
|
54
|
+
justify: 'start',
|
|
47
55
|
})
|
|
48
56
|
|
|
49
57
|
const ownedAttrs = useOwnedAttrs({ component: 'SStack', owner: 'semantic stack root' })
|
|
@@ -32,8 +32,36 @@
|
|
|
32
32
|
</ul>
|
|
33
33
|
</template>
|
|
34
34
|
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import type {
|
|
37
|
+
SelectionKey,
|
|
38
|
+
TreeActionsLayout,
|
|
39
|
+
TreeDensity,
|
|
40
|
+
TreeExpandControl,
|
|
41
|
+
TreeModel,
|
|
42
|
+
TreeRowVariant,
|
|
43
|
+
TreeSelection,
|
|
44
|
+
TreeSelectionRail,
|
|
45
|
+
} from './types'
|
|
46
|
+
|
|
47
|
+
/** Публичный контракт дерева принадлежит source-SFC runtime. / The public tree contract is owned by the source-SFC runtime. */
|
|
48
|
+
export interface STreeProps<T extends object, K extends SelectionKey = SelectionKey> {
|
|
49
|
+
nodes: readonly T[]
|
|
50
|
+
model: TreeModel<T, K>
|
|
51
|
+
label: string
|
|
52
|
+
selection: TreeSelection<K>
|
|
53
|
+
expanded?: ReadonlySet<K>
|
|
54
|
+
density?: TreeDensity
|
|
55
|
+
actionsLayout?: TreeActionsLayout
|
|
56
|
+
expandControl?: TreeExpandControl
|
|
57
|
+
selectionRail?: TreeSelectionRail
|
|
58
|
+
rowVariant?: TreeRowVariant
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
35
62
|
<script setup lang="ts" generic="T extends object, K extends SelectionKey">
|
|
36
63
|
import { computed, nextTick, provide, ref, watch } from 'vue'
|
|
64
|
+
import { lastItem } from '../../../internal/es2020'
|
|
37
65
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
38
66
|
import { assertSelectionKey, selectionKeyToken } from '../_internal/useSelectionRoving'
|
|
39
67
|
import STreeNode from '../_internal/STreeNode.vue'
|
|
@@ -46,12 +74,9 @@ import {
|
|
|
46
74
|
import { validateTreeModel } from './treeAdapter'
|
|
47
75
|
import type {
|
|
48
76
|
STreeNodeContentScope,
|
|
49
|
-
STreeProps,
|
|
50
|
-
SelectionKey,
|
|
51
77
|
TreeActivationOrigin,
|
|
52
78
|
TreeNodeActivationEvent,
|
|
53
79
|
TreeNodeEvent,
|
|
54
|
-
TreeSelection,
|
|
55
80
|
} from './types'
|
|
56
81
|
|
|
57
82
|
defineOptions({ inheritAttrs: false })
|
|
@@ -172,7 +197,7 @@ function handleTreeItemKeydown(item: RegisteredTreeItem<T, K>, event: KeyboardEv
|
|
|
172
197
|
}
|
|
173
198
|
if (event.key === 'End') {
|
|
174
199
|
event.preventDefault()
|
|
175
|
-
void focusItem(items
|
|
200
|
+
void focusItem(lastItem(items))
|
|
176
201
|
return
|
|
177
202
|
}
|
|
178
203
|
if (event.key === 'ArrowRight' && item.hasChildren()) {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type SelectionIdentity,
|
|
6
6
|
type SelectionKey,
|
|
7
7
|
} from '../_internal/useSelectionRoving'
|
|
8
|
+
import { withErrorCause } from '../../../internal/es2020'
|
|
8
9
|
import type { TreeModel } from './types'
|
|
9
10
|
|
|
10
11
|
export interface FlatTreeAdapter<T extends object, K extends SelectionKey> {
|
|
@@ -130,7 +131,7 @@ function readModelValue<R>(
|
|
|
130
131
|
try {
|
|
131
132
|
return reader()
|
|
132
133
|
} catch (error) {
|
|
133
|
-
throw new TypeError(`STree: model.${field} failed at ${coordinate}
|
|
134
|
+
throw withErrorCause(new TypeError(`STree: model.${field} failed at ${coordinate}`), error)
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -51,18 +51,7 @@ export type TreeExpandControl = 'button' | 'row'
|
|
|
51
51
|
export type TreeSelectionRail = 'hidden' | 'visible'
|
|
52
52
|
export type TreeRowVariant = 'default' | 'table'
|
|
53
53
|
|
|
54
|
-
export
|
|
55
|
-
nodes: readonly T[]
|
|
56
|
-
model: TreeModel<T, K>
|
|
57
|
-
label: string
|
|
58
|
-
selection: TreeSelection<K>
|
|
59
|
-
expanded?: ReadonlySet<K>
|
|
60
|
-
density?: TreeDensity
|
|
61
|
-
actionsLayout?: TreeActionsLayout
|
|
62
|
-
expandControl?: TreeExpandControl
|
|
63
|
-
selectionRail?: TreeSelectionRail
|
|
64
|
-
rowVariant?: TreeRowVariant
|
|
65
|
-
}
|
|
54
|
+
export type { STreeProps } from './STree.vue'
|
|
66
55
|
|
|
67
56
|
export interface TreeNodeEvent<T extends object, K extends SelectionKey = SelectionKey> {
|
|
68
57
|
node: T
|
|
@@ -33,7 +33,7 @@ import { type FunctionalComponent, type HTMLAttributes, type VNodeProps } from '
|
|
|
33
33
|
import SPanel from './SPanel.vue';
|
|
34
34
|
import SSidebarGroup from './SSidebarGroup.vue';
|
|
35
35
|
import type { SAsyncStateContract } from '../feedback/SAsyncState.vue';
|
|
36
|
-
import type {
|
|
36
|
+
import type { SidebarSectionsState } from './sidebar';
|
|
37
37
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs';
|
|
38
38
|
|
|
39
39
|
defineOptions({ inheritAttrs: false });
|
|
@@ -48,6 +48,11 @@ export interface Props {
|
|
|
48
48
|
scrollBehavior?: 'auto' | 'body' | 'self' | 'child';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
interface Emits {
|
|
52
|
+
'update-sections': [state: SidebarSectionsState];
|
|
53
|
+
'toggle-section': [key: string];
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
withDefaults(defineProps<Props>(), {
|
|
52
57
|
title: undefined,
|
|
53
58
|
icon: undefined,
|
|
@@ -56,7 +61,7 @@ withDefaults(defineProps<Props>(), {
|
|
|
56
61
|
scrollBehavior: 'child',
|
|
57
62
|
});
|
|
58
63
|
|
|
59
|
-
const emit = defineEmits<
|
|
64
|
+
const emit = defineEmits<Emits>();
|
|
60
65
|
const ownedAttrs = useOwnedAttrs({ component: 'SLeftSidebar', owner: 'left-sidebar root' });
|
|
61
66
|
</script>
|
|
62
67
|
|
|
@@ -31,6 +31,17 @@
|
|
|
31
31
|
@apply m-0 text-2xl font-semibold leading-tight text-surface-900 dark:text-surface-50;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
.s-page-header__title[data-presentation='assistive'] {
|
|
35
|
+
position: absolute;
|
|
36
|
+
width: 1px;
|
|
37
|
+
height: 1px;
|
|
38
|
+
padding: 0;
|
|
39
|
+
margin: -1px;
|
|
40
|
+
clip-path: inset(50%);
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
border: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
34
45
|
.s-page-header[data-size='compact'] .s-page-header__title {
|
|
35
46
|
@apply text-xl;
|
|
36
47
|
}
|
|
@@ -11,6 +11,7 @@ withDefaults(defineProps<{
|
|
|
11
11
|
appearance?: 'plain' | 'panel'
|
|
12
12
|
size?: 'compact' | 'default'
|
|
13
13
|
actionsLayout?: 'responsive' | 'inline' | 'stacked'
|
|
14
|
+
titlePresentation?: 'visible' | 'assistive'
|
|
14
15
|
}>(), {
|
|
15
16
|
description: undefined,
|
|
16
17
|
eyebrow: undefined,
|
|
@@ -18,6 +19,7 @@ withDefaults(defineProps<{
|
|
|
18
19
|
appearance: 'panel',
|
|
19
20
|
size: 'default',
|
|
20
21
|
actionsLayout: 'responsive',
|
|
22
|
+
titlePresentation: 'visible',
|
|
21
23
|
})
|
|
22
24
|
|
|
23
25
|
const ownedAttrs = useOwnedAttrs({ component: 'SPageHeader', owner: 'page header root' })
|
|
@@ -33,7 +35,11 @@ const ownedAttrs = useOwnedAttrs({ component: 'SPageHeader', owner: 'page header
|
|
|
33
35
|
>
|
|
34
36
|
<div class="s-page-header__copy">
|
|
35
37
|
<div v-if="eyebrow" class="s-page-header__eyebrow">{{ eyebrow }}</div>
|
|
36
|
-
<component
|
|
38
|
+
<component
|
|
39
|
+
:is="`h${headingLevel}`"
|
|
40
|
+
class="s-page-header__title"
|
|
41
|
+
:data-presentation="titlePresentation"
|
|
42
|
+
>{{ title }}</component>
|
|
37
43
|
<p v-if="description" class="s-page-header__description">{{ description }}</p>
|
|
38
44
|
</div>
|
|
39
45
|
<div v-if="$slots.actions" class="s-page-header__actions">
|
|
@@ -93,6 +93,7 @@ import SButton, {
|
|
|
93
93
|
} from '../controls/SButton.vue';
|
|
94
94
|
import { useFloatingPosition, type FloatingPlacement } from '../../../composables/useFloatingPosition';
|
|
95
95
|
import { findRelativeTabTarget, getFocusableElements } from '../../../internal/focusNavigation';
|
|
96
|
+
import { lastItem } from '../../../internal/es2020';
|
|
96
97
|
import { resolveInteractiveElement } from '../../../internal/interactiveElement';
|
|
97
98
|
import { registerLayer, type LayerRegistration } from '../../../internal/layerStack';
|
|
98
99
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs';
|
|
@@ -495,7 +496,7 @@ function handlePanelTab(event: KeyboardEvent): void {
|
|
|
495
496
|
const focusable = getFocusableElements(panel);
|
|
496
497
|
const active = document.activeElement;
|
|
497
498
|
const first = focusable[0];
|
|
498
|
-
const last = focusable
|
|
499
|
+
const last = lastItem(focusable);
|
|
499
500
|
const leavesPanel = focusable.length === 0
|
|
500
501
|
|| (event.shiftKey ? active === panel || active === first : active === last);
|
|
501
502
|
if (!leavesPanel) return;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from 'vue';
|
|
17
17
|
|
|
18
18
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs';
|
|
19
|
+
import { hasOwn, lastItem } from '../../../internal/es2020';
|
|
19
20
|
import {
|
|
20
21
|
sidebarGroupContextKey,
|
|
21
22
|
type RegisteredSidebarSection,
|
|
@@ -27,7 +28,6 @@ import {
|
|
|
27
28
|
createSidebarSectionsTransition,
|
|
28
29
|
sidebarSectionsStateEqual,
|
|
29
30
|
type SectionState,
|
|
30
|
-
type SidebarGroupEmits,
|
|
31
31
|
type SidebarSectionReplacement,
|
|
32
32
|
type SidebarSectionsState,
|
|
33
33
|
} from './sidebar';
|
|
@@ -38,7 +38,12 @@ const props = defineProps<{
|
|
|
38
38
|
sectionsState: SidebarSectionsState;
|
|
39
39
|
}>();
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
interface Emits {
|
|
42
|
+
'update-sections': [state: SidebarSectionsState];
|
|
43
|
+
'toggle-section': [key: string];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const emit = defineEmits<Emits>();
|
|
42
47
|
|
|
43
48
|
const ownedAttrs = useOwnedAttrs({ component: 'SSidebarGroup', owner: 'sidebar group root' });
|
|
44
49
|
const groupRef = ref<HTMLDivElement | null>(null);
|
|
@@ -147,7 +152,7 @@ function fitOpenSectionSizes(
|
|
|
147
152
|
|
|
148
153
|
let delta = targetSize - [...sizes.values()].reduce((sum, size) => sum + size, 0);
|
|
149
154
|
if (delta > 0) {
|
|
150
|
-
const growTarget = flexible ?? openSections
|
|
155
|
+
const growTarget = flexible ?? lastItem(openSections);
|
|
151
156
|
if (growTarget) sizes.set(growTarget.key, sizes.get(growTarget.key)! + delta);
|
|
152
157
|
delta = 0;
|
|
153
158
|
}
|
|
@@ -200,7 +205,7 @@ function registerSection(registration: SidebarSectionRegistration): () => void {
|
|
|
200
205
|
if (!(registration.element instanceof HTMLElement)) {
|
|
201
206
|
throw new TypeError('SSidebarGroup: registered section element must be an HTMLElement');
|
|
202
207
|
}
|
|
203
|
-
if (!
|
|
208
|
+
if (!hasOwn(currentState(), registration.key)) {
|
|
204
209
|
throw new Error(`SSidebarGroup: registered section '${registration.key}' has no controlled state`);
|
|
205
210
|
}
|
|
206
211
|
if (registrations.value.some((current) => current.key === registration.key)) {
|
|
@@ -371,7 +376,7 @@ function resizeSections(topKey: string, bottomKey: string, requestedTopSize: num
|
|
|
371
376
|
);
|
|
372
377
|
const delta = requestedTopSize - currentUpperSize;
|
|
373
378
|
const shrinking = delta > 0 ? [...lower] : [...upper].reverse();
|
|
374
|
-
const growing = delta > 0 ? upper
|
|
379
|
+
const growing = delta > 0 ? lastItem(upper) : lower[0];
|
|
375
380
|
let transferable = Math.abs(delta);
|
|
376
381
|
transferable = Math.min(
|
|
377
382
|
transferable,
|
|
@@ -163,7 +163,7 @@ export function createSidebarSectionsTransition(
|
|
|
163
163
|
if (typeof replacement.key !== 'string' || replacement.key.trim().length === 0) {
|
|
164
164
|
throw new TypeError(`SSidebarGroup: replacements[${index}].key must be a non-empty string`);
|
|
165
165
|
}
|
|
166
|
-
if (!
|
|
166
|
+
if (!hasOwn(current, replacement.key)) {
|
|
167
167
|
throw new Error(`SSidebarGroup: cannot replace unknown section '${replacement.key}'`);
|
|
168
168
|
}
|
|
169
169
|
if (replacementsByKey.has(replacement.key)) {
|
|
@@ -204,3 +204,4 @@ export function sidebarSectionsStateEqual(
|
|
|
204
204
|
&& leftSection.minSize === rightSection.minSize;
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
|
+
import { hasOwn } from '../../../internal/es2020';
|
|
@@ -146,6 +146,10 @@
|
|
|
146
146
|
background: var(--s-button-solid-background-hover);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
.s-button[aria-disabled='true'] {
|
|
150
|
+
pointer-events: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
149
153
|
.s-button[data-appearance='solid']:active:not(:disabled) {
|
|
150
154
|
border-color: var(--s-button-solid-background-active);
|
|
151
155
|
background: var(--s-button-solid-background-active);
|
|
@@ -219,6 +223,14 @@
|
|
|
219
223
|
cursor: not-allowed;
|
|
220
224
|
}
|
|
221
225
|
|
|
226
|
+
.s-button[aria-disabled='true'] {
|
|
227
|
+
border-color: transparent;
|
|
228
|
+
background: var(--s-button-disabled-background);
|
|
229
|
+
box-shadow: none;
|
|
230
|
+
color: var(--s-button-disabled-foreground);
|
|
231
|
+
cursor: not-allowed;
|
|
232
|
+
}
|
|
233
|
+
|
|
222
234
|
.s-button[data-appearance='ghost']:disabled,
|
|
223
235
|
.s-button[data-appearance='text']:disabled {
|
|
224
236
|
background: transparent;
|
|
@@ -246,6 +258,7 @@
|
|
|
246
258
|
.s-button[data-width='fit'] { width: fit-content; }
|
|
247
259
|
.s-button[data-width='full'] .s-button__content { width: 100%; }
|
|
248
260
|
.s-button[data-content-align='start'] .s-button__content { justify-content: flex-start; text-align: left; }
|
|
261
|
+
.s-button[data-content-align='end'] .s-button__content { justify-content: flex-end; text-align: right; }
|
|
249
262
|
.s-button[data-content-align='between'] .s-button__content { justify-content: space-between; text-align: left; }
|
|
250
263
|
|
|
251
264
|
.s-button[data-shape='pill'],
|