@pgcorp/ui-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/docs/accessibility.md +27 -0
- package/docs/getting-started.md +80 -0
- package/docs/licensing.md +30 -0
- package/docs/public-api.md +39 -0
- package/docs/security.md +21 -0
- package/docs/theming.md +35 -0
- package/docs/versioning.md +23 -0
- package/index.ts +3 -0
- package/package.json +592 -0
- package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
- package/src/components/icons/sputnigUiIcons.ts +530 -0
- package/src/components/layout/SAppShell.vue +232 -0
- package/src/components/layout/SDockRegion.vue +200 -0
- package/src/components/layout/SResizeHandle.vue +251 -0
- package/src/components/layout/SSideMenuButton.vue +104 -0
- package/src/components/layout/SSideMenuRail.vue +47 -0
- package/src/components/layout/SStack.css +25 -0
- package/src/components/layout/SStack.vue +57 -0
- package/src/components/layout/SStatusBarShell.css +15 -0
- package/src/components/layout/SStatusBarShell.vue +26 -0
- package/src/components/layout/SWorkbenchLayout.vue +53 -0
- package/src/components/layout/SWorkbenchPanelShell.css +161 -0
- package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
- package/src/components/layout/_internal/SAppBrand.css +45 -0
- package/src/components/layout/_internal/SAppBrand.vue +37 -0
- package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
- package/src/components/layout/types.ts +28 -0
- package/src/components/shared/_internal/SDialogSurface.css +127 -0
- package/src/components/shared/_internal/SDialogSurface.vue +117 -0
- package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
- package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
- package/src/components/shared/_internal/SListboxGroup.css +18 -0
- package/src/components/shared/_internal/SListboxGroup.vue +24 -0
- package/src/components/shared/_internal/SMenuItem.css +112 -0
- package/src/components/shared/_internal/SMenuItem.vue +213 -0
- package/src/components/shared/_internal/SMenuLayer.css +21 -0
- package/src/components/shared/_internal/SMenuLayer.vue +48 -0
- package/src/components/shared/_internal/SMenuSeparator.css +5 -0
- package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
- package/src/components/shared/_internal/SToastItem.css +65 -0
- package/src/components/shared/_internal/SToastItem.vue +58 -0
- package/src/components/shared/_internal/STreeNode.vue +761 -0
- package/src/components/shared/_internal/dialogSurface.ts +105 -0
- package/src/components/shared/_internal/focusTrap.ts +40 -0
- package/src/components/shared/_internal/listboxContext.ts +16 -0
- package/src/components/shared/_internal/markerContract.ts +10 -0
- package/src/components/shared/_internal/menuContext.ts +23 -0
- package/src/components/shared/_internal/selectionContract.ts +239 -0
- package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
- package/src/components/shared/_internal/tabsContext.ts +57 -0
- package/src/components/shared/_internal/treeRuntime.ts +203 -0
- package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
- package/src/components/shared/codeLanguages.ts +168 -0
- package/src/components/shared/complex/SKanbanCard.css +8 -0
- package/src/components/shared/complex/SKanbanCard.vue +76 -0
- package/src/components/shared/complex/SKanbanLane.vue +82 -0
- package/src/components/shared/complex/STree.css +24 -0
- package/src/components/shared/complex/STree.vue +288 -0
- package/src/components/shared/complex/treeAdapter.ts +210 -0
- package/src/components/shared/complex/types.ts +102 -0
- package/src/components/shared/containers/SCloudChrome.css +103 -0
- package/src/components/shared/containers/SCloudChrome.vue +87 -0
- package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
- package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
- package/src/components/shared/containers/SCloudChromeRow.css +78 -0
- package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
- package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
- package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
- package/src/components/shared/containers/SDrawer.css +60 -0
- package/src/components/shared/containers/SDrawer.vue +86 -0
- package/src/components/shared/containers/SFormGrid.css +26 -0
- package/src/components/shared/containers/SFormGrid.vue +46 -0
- package/src/components/shared/containers/SFormGridItem.css +18 -0
- package/src/components/shared/containers/SFormGridItem.vue +29 -0
- package/src/components/shared/containers/SLeftSidebar.vue +74 -0
- package/src/components/shared/containers/SModal.css +27 -0
- package/src/components/shared/containers/SModal.vue +105 -0
- package/src/components/shared/containers/SPageHeader.css +66 -0
- package/src/components/shared/containers/SPageHeader.vue +45 -0
- package/src/components/shared/containers/SPanel.css +134 -0
- package/src/components/shared/containers/SPanel.vue +169 -0
- package/src/components/shared/containers/SPopover.css +35 -0
- package/src/components/shared/containers/SPopover.vue +551 -0
- package/src/components/shared/containers/SSidebarGroup.css +12 -0
- package/src/components/shared/containers/SSidebarGroup.vue +443 -0
- package/src/components/shared/containers/SSidebarSection.css +41 -0
- package/src/components/shared/containers/SSidebarSection.vue +268 -0
- package/src/components/shared/containers/disclosureSummary.ts +48 -0
- package/src/components/shared/containers/popover.ts +72 -0
- package/src/components/shared/containers/sidebar.ts +206 -0
- package/src/components/shared/controls/SButton.css +274 -0
- package/src/components/shared/controls/SButton.vue +211 -0
- package/src/components/shared/controls/SCheckbox.css +65 -0
- package/src/components/shared/controls/SCheckbox.vue +130 -0
- package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
- package/src/components/shared/controls/SChoiceCards.css +29 -0
- package/src/components/shared/controls/SChoiceCards.vue +145 -0
- package/src/components/shared/controls/SColorSelect.vue +146 -0
- package/src/components/shared/controls/SComboboxTrigger.css +35 -0
- package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
- package/src/components/shared/controls/SContextMenu.css +11 -0
- package/src/components/shared/controls/SContextMenu.vue +186 -0
- package/src/components/shared/controls/SContextToggleButton.vue +64 -0
- package/src/components/shared/controls/SDragHandle.vue +100 -0
- package/src/components/shared/controls/SDropdownMenu.css +5 -0
- package/src/components/shared/controls/SDropdownMenu.vue +188 -0
- package/src/components/shared/controls/SField.css +79 -0
- package/src/components/shared/controls/SField.vue +89 -0
- package/src/components/shared/controls/SFieldGroup.css +58 -0
- package/src/components/shared/controls/SFieldGroup.vue +117 -0
- package/src/components/shared/controls/SFieldLabel.css +12 -0
- package/src/components/shared/controls/SFieldLabel.vue +51 -0
- package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
- package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
- package/src/components/shared/controls/SInputText.css +66 -0
- package/src/components/shared/controls/SInputText.vue +321 -0
- package/src/components/shared/controls/SInteractiveSurface.css +72 -0
- package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
- package/src/components/shared/controls/SLink.css +114 -0
- package/src/components/shared/controls/SLink.ts +85 -0
- package/src/components/shared/controls/SLink.vue +114 -0
- package/src/components/shared/controls/SListbox.css +31 -0
- package/src/components/shared/controls/SListbox.vue +388 -0
- package/src/components/shared/controls/SListboxOption.css +63 -0
- package/src/components/shared/controls/SListboxOption.vue +85 -0
- package/src/components/shared/controls/SMarker.css +19 -0
- package/src/components/shared/controls/SMarker.vue +33 -0
- package/src/components/shared/controls/SMenuSurface.css +20 -0
- package/src/components/shared/controls/SMenuSurface.vue +333 -0
- package/src/components/shared/controls/SSegmentedControl.css +38 -0
- package/src/components/shared/controls/SSegmentedControl.vue +134 -0
- package/src/components/shared/controls/SSelect.css +12 -0
- package/src/components/shared/controls/SSelect.vue +338 -0
- package/src/components/shared/controls/STextarea.css +31 -0
- package/src/components/shared/controls/STextarea.vue +316 -0
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
- package/src/components/shared/controls/menu.ts +178 -0
- package/src/components/shared/data-display/SActionCard.css +6 -0
- package/src/components/shared/data-display/SActionCard.vue +49 -0
- package/src/components/shared/data-display/SActionList.css +15 -0
- package/src/components/shared/data-display/SActionList.vue +34 -0
- package/src/components/shared/data-display/SActionListItem.css +114 -0
- package/src/components/shared/data-display/SActionListItem.vue +230 -0
- package/src/components/shared/data-display/SBadge.css +73 -0
- package/src/components/shared/data-display/SBadge.vue +52 -0
- package/src/components/shared/data-display/SCodeBlock.css +56 -0
- package/src/components/shared/data-display/SCodeBlock.vue +188 -0
- package/src/components/shared/data-display/SCodeEditor.css +178 -0
- package/src/components/shared/data-display/SCodeEditor.vue +274 -0
- package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
- package/src/components/shared/data-display/SCopyField.vue +77 -0
- package/src/components/shared/data-display/SDocBlock.css +136 -0
- package/src/components/shared/data-display/SDocBlock.vue +358 -0
- package/src/components/shared/data-display/SEmptyState.css +42 -0
- package/src/components/shared/data-display/SEmptyState.vue +79 -0
- package/src/components/shared/data-display/SExpandableText.vue +314 -0
- package/src/components/shared/data-display/SJsonTree.css +6 -0
- package/src/components/shared/data-display/SJsonTree.vue +111 -0
- package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
- package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
- package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
- package/src/components/shared/data-display/SMessage.css +88 -0
- package/src/components/shared/data-display/SMessage.vue +152 -0
- package/src/components/shared/data-display/SMetricCard.css +32 -0
- package/src/components/shared/data-display/SMetricCard.vue +117 -0
- package/src/components/shared/data-display/SProgressBar.css +84 -0
- package/src/components/shared/data-display/SProgressBar.vue +114 -0
- package/src/components/shared/data-display/SProgressIndicator.css +59 -0
- package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
- package/src/components/shared/data-display/SSectionHeader.css +84 -0
- package/src/components/shared/data-display/SSectionHeader.vue +58 -0
- package/src/components/shared/data-display/SStatus.css +90 -0
- package/src/components/shared/data-display/SStatus.vue +79 -0
- package/src/components/shared/data-display/STable.css +83 -0
- package/src/components/shared/data-display/STable.vue +339 -0
- package/src/components/shared/data-display/SToastContainer.css +53 -0
- package/src/components/shared/data-display/SToastContainer.vue +31 -0
- package/src/components/shared/data-display/STooltip.css +114 -0
- package/src/components/shared/data-display/STooltip.vue +550 -0
- package/src/components/shared/data-display/STooltipAnchor.css +21 -0
- package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
- package/src/components/shared/data-display/SVirtualList.css +24 -0
- package/src/components/shared/data-display/SVirtualList.ts +19 -0
- package/src/components/shared/data-display/SVirtualList.vue +361 -0
- package/src/components/shared/data-display/json.ts +136 -0
- package/src/components/shared/data-display/table.ts +76 -0
- package/src/components/shared/database/SDataGrid.css +3 -0
- package/src/components/shared/database/SDataGrid.vue +464 -0
- package/src/components/shared/database/SSqlEditor.vue +363 -0
- package/src/components/shared/database/dataGrid.ts +61 -0
- package/src/components/shared/feedback/SAsyncState.css +26 -0
- package/src/components/shared/feedback/SAsyncState.vue +190 -0
- package/src/components/shared/graph/SGraphViewport.ts +1 -0
- package/src/components/shared/graph/SGraphViewport.vue +858 -0
- package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
- package/src/components/shared/markdown.ts +169 -0
- package/src/components/shared/navigation/SBottomNav.vue +692 -0
- package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
- package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
- package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
- package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
- package/src/components/shared/navigation/STab.vue +363 -0
- package/src/components/shared/navigation/STabList.vue +442 -0
- package/src/components/shared/navigation/STabPanel.vue +165 -0
- package/src/components/shared/navigation/STabPanels.vue +33 -0
- package/src/components/shared/navigation/STabs.vue +617 -0
- package/src/components/shared/navigation/SWizardSteps.css +69 -0
- package/src/components/shared/navigation/SWizardSteps.vue +171 -0
- package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
- package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
- package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
- package/src/components/shared/persona/_internal/inventory.ts +110 -0
- package/src/components/shared/persona/_internal/labels.ts +79 -0
- package/src/components/shared/persona/presentation.ts +371 -0
- package/src/components/shared/persona/types.ts +256 -0
- package/src/composables/uiPreferencesReset.ts +52 -0
- package/src/composables/useClipboard.ts +37 -0
- package/src/composables/useDialogOverlay.ts +85 -0
- package/src/composables/useDisclosure.ts +40 -0
- package/src/composables/useFloatingPosition.ts +245 -0
- package/src/composables/useNotifier.ts +26 -0
- package/src/composables/useSidebarPanelState.ts +610 -0
- package/src/internal/codeEditorContract.ts +110 -0
- package/src/internal/codeEditorExtensions.ts +27 -0
- package/src/internal/feedbackPresentation.ts +136 -0
- package/src/internal/fieldSurface.ts +59 -0
- package/src/internal/focusNavigation.ts +69 -0
- package/src/internal/inlineTokenEditorContract.ts +136 -0
- package/src/internal/interactiveElement.ts +33 -0
- package/src/internal/layerStack.ts +202 -0
- package/src/internal/linkTarget.ts +256 -0
- package/src/internal/ownedAttrs.ts +464 -0
- package/src/internal/passiveContentContract.ts +129 -0
- package/src/internal/pointerInteractionLease.ts +81 -0
- package/src/internal/runtimeContract.ts +194 -0
- package/src/internal/runtimeTheme.ts +159 -0
- package/src/internal/semanticSizing.ts +60 -0
- package/src/internal/useMenuLayer.ts +129 -0
- package/src/stores/useNotifierStore.ts +69 -0
- package/src/styles/reference.css +4 -0
- package/src/styles/style.css +151 -0
- package/src/styles/tokens.css +836 -0
- package/src/theme.ts +449 -0
- package/src/types/highlight-js-lib.d.ts +76 -0
- package/src/variants.ts +15 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
import SButton from '../../controls/SButton.vue'
|
|
5
|
+
import SActionListItem from '../../data-display/SActionListItem.vue'
|
|
6
|
+
import SCodeBlock from '../../data-display/SCodeBlock.vue'
|
|
7
|
+
import SEmptyState from '../../data-display/SEmptyState.vue'
|
|
8
|
+
import SMessage from '../../data-display/SMessage.vue'
|
|
9
|
+
import SSectionHeader from '../../data-display/SSectionHeader.vue'
|
|
10
|
+
import SVirtualList from '../../data-display/SVirtualList.vue'
|
|
11
|
+
import SKeyValueGrid from '../../data-display/SKeyValueGrid.vue'
|
|
12
|
+
import SAsyncState from '../../feedback/SAsyncState.vue'
|
|
13
|
+
import {
|
|
14
|
+
assertPersonaInventory,
|
|
15
|
+
findExactPersonaInventoryItem,
|
|
16
|
+
resolvePersonaInventoryAsyncState,
|
|
17
|
+
} from './inventory'
|
|
18
|
+
import { personaDistinctMessage } from '../presentation'
|
|
19
|
+
import { getPersonaRunConsoleFactKey } from '../types'
|
|
20
|
+
import type {
|
|
21
|
+
PersonaPaginatedInventoryState,
|
|
22
|
+
PersonaRunConsoleCapabilities,
|
|
23
|
+
PersonaRunConsoleDensity,
|
|
24
|
+
PersonaRunConsoleEvent,
|
|
25
|
+
PersonaRunConsoleItem,
|
|
26
|
+
PersonaRunConsoleLabels,
|
|
27
|
+
PersonaRunConsoleOperation,
|
|
28
|
+
PersonaRunConsoleRun,
|
|
29
|
+
} from '../types'
|
|
30
|
+
|
|
31
|
+
defineOptions({ inheritAttrs: false })
|
|
32
|
+
|
|
33
|
+
const props = withDefaults(
|
|
34
|
+
defineProps<{
|
|
35
|
+
runsState: PersonaPaginatedInventoryState<PersonaRunConsoleRun>
|
|
36
|
+
itemsState: PersonaPaginatedInventoryState<PersonaRunConsoleItem>
|
|
37
|
+
eventsState: PersonaPaginatedInventoryState<PersonaRunConsoleEvent>
|
|
38
|
+
capabilities: PersonaRunConsoleCapabilities
|
|
39
|
+
operation: PersonaRunConsoleOperation
|
|
40
|
+
resolvedLabels: PersonaRunConsoleLabels
|
|
41
|
+
selectedRunId?: string | null
|
|
42
|
+
reportSummary?: string | null
|
|
43
|
+
density?: PersonaRunConsoleDensity
|
|
44
|
+
listHeight?: number
|
|
45
|
+
itemsHeight?: number
|
|
46
|
+
eventsHeight?: number
|
|
47
|
+
}>(),
|
|
48
|
+
{
|
|
49
|
+
selectedRunId: null,
|
|
50
|
+
reportSummary: null,
|
|
51
|
+
density: 'comfortable',
|
|
52
|
+
listHeight: 560,
|
|
53
|
+
itemsHeight: 250,
|
|
54
|
+
eventsHeight: 430,
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const emit = defineEmits<{
|
|
59
|
+
'select-run': [runId: string]
|
|
60
|
+
'load-more-runs': []
|
|
61
|
+
'refresh-events': []
|
|
62
|
+
'cancel-run': []
|
|
63
|
+
'load-more-items': []
|
|
64
|
+
'append-events': []
|
|
65
|
+
'load-more-events': []
|
|
66
|
+
}>()
|
|
67
|
+
const runs = computed(() => props.runsState.status === 'ready'
|
|
68
|
+
? assertPersonaInventory('SPersonaRunConsole.runsState', props.runsState.items)
|
|
69
|
+
: [])
|
|
70
|
+
const items = computed(() => props.itemsState.status === 'ready'
|
|
71
|
+
? assertPersonaInventory('SPersonaRunConsole.itemsState', props.itemsState.items)
|
|
72
|
+
: [])
|
|
73
|
+
const events = computed(() => props.eventsState.status === 'ready'
|
|
74
|
+
? assertPersonaInventory('SPersonaRunConsole.eventsState', props.eventsState.items)
|
|
75
|
+
: [])
|
|
76
|
+
const runsAsyncState = computed(() => resolvePersonaInventoryAsyncState(
|
|
77
|
+
'SPersonaRunConsole.runsState',
|
|
78
|
+
props.runsState,
|
|
79
|
+
{
|
|
80
|
+
loadingMessage: 'Загрузка запусков',
|
|
81
|
+
emptyMessage: props.resolvedLabels.noRuns,
|
|
82
|
+
},
|
|
83
|
+
))
|
|
84
|
+
const itemsAsyncState = computed(() => resolvePersonaInventoryAsyncState(
|
|
85
|
+
'SPersonaRunConsole.itemsState',
|
|
86
|
+
props.itemsState,
|
|
87
|
+
{
|
|
88
|
+
loadingMessage: 'Загрузка рабочих элементов',
|
|
89
|
+
emptyMessage: props.resolvedLabels.noItems,
|
|
90
|
+
},
|
|
91
|
+
))
|
|
92
|
+
const eventsAsyncState = computed(() => resolvePersonaInventoryAsyncState(
|
|
93
|
+
'SPersonaRunConsole.eventsState',
|
|
94
|
+
props.eventsState,
|
|
95
|
+
{
|
|
96
|
+
loadingMessage: 'Загрузка событий',
|
|
97
|
+
emptyMessage: props.resolvedLabels.noEvents,
|
|
98
|
+
},
|
|
99
|
+
))
|
|
100
|
+
const selectedRun = computed(() => findExactPersonaInventoryItem(
|
|
101
|
+
'SPersonaRunConsole.runsState',
|
|
102
|
+
runs.value,
|
|
103
|
+
props.selectedRunId,
|
|
104
|
+
))
|
|
105
|
+
const selectedFacts = computed(() => selectedRun.value?.facts ?? [])
|
|
106
|
+
const virtualRowSize = 96
|
|
107
|
+
const runsViewportHeight = computed(() => Math.min(props.listHeight, runs.value.length * virtualRowSize))
|
|
108
|
+
const itemsViewportHeight = computed(() => Math.min(props.itemsHeight, items.value.length * virtualRowSize))
|
|
109
|
+
const eventsViewportHeight = computed(() => Math.min(props.eventsHeight, events.value.length * virtualRowSize))
|
|
110
|
+
|
|
111
|
+
function eventDetail(event: PersonaRunConsoleEvent): string | null {
|
|
112
|
+
return personaDistinctMessage(event.title, event.message)
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<template>
|
|
117
|
+
<div class="s-persona-run-console-shell">
|
|
118
|
+
<div class="s-persona-run-console" :data-density="density">
|
|
119
|
+
<section class="s-persona-run-console__section">
|
|
120
|
+
<SSectionHeader :title="resolvedLabels.runListTitle" size="sm" />
|
|
121
|
+
<SAsyncState :state="runsAsyncState">
|
|
122
|
+
<SVirtualList
|
|
123
|
+
:items="runs"
|
|
124
|
+
:item-size="virtualRowSize"
|
|
125
|
+
:height="runsViewportHeight"
|
|
126
|
+
:item-key="(run) => run.id"
|
|
127
|
+
:accessible-label="resolvedLabels.runListTitle"
|
|
128
|
+
:item-label="(run) => run.title"
|
|
129
|
+
>
|
|
130
|
+
<template #default="{ item }">
|
|
131
|
+
<SActionListItem
|
|
132
|
+
role="presentation"
|
|
133
|
+
:title="item.title"
|
|
134
|
+
:description="item.meta"
|
|
135
|
+
:detail="item.detail"
|
|
136
|
+
:selected="selectedRunId === item.id"
|
|
137
|
+
:status-label="item.statusLabel"
|
|
138
|
+
:status-severity="item.statusSeverity"
|
|
139
|
+
trailing-layout="inline"
|
|
140
|
+
height-mode="fill"
|
|
141
|
+
:surface="{ mode: 'action', accessibleLabel: `Открыть запуск ${item.title}` }"
|
|
142
|
+
@activate="emit('select-run', item.id)"
|
|
143
|
+
/>
|
|
144
|
+
</template>
|
|
145
|
+
</SVirtualList>
|
|
146
|
+
</SAsyncState>
|
|
147
|
+
<div
|
|
148
|
+
v-if="runsState.status === 'ready' && runsState.pagination !== 'complete'"
|
|
149
|
+
class="s-persona-run-console__actions-line"
|
|
150
|
+
>
|
|
151
|
+
<SButton
|
|
152
|
+
size="sm"
|
|
153
|
+
severity="secondary"
|
|
154
|
+
:loading="runsState.pagination === 'loading-more'"
|
|
155
|
+
@click="emit('load-more-runs')"
|
|
156
|
+
>
|
|
157
|
+
{{ resolvedLabels.loadMoreRuns }}
|
|
158
|
+
</SButton>
|
|
159
|
+
</div>
|
|
160
|
+
</section>
|
|
161
|
+
|
|
162
|
+
<section class="s-persona-run-console__section">
|
|
163
|
+
<SSectionHeader :title="resolvedLabels.selectedRunTitle" size="sm" />
|
|
164
|
+
<div v-if="selectedRun" class="s-persona-run-console__detail">
|
|
165
|
+
<SSectionHeader :title="selectedRun.title" :description="selectedRun.meta">
|
|
166
|
+
<template #actions>
|
|
167
|
+
<SButton
|
|
168
|
+
size="sm"
|
|
169
|
+
severity="secondary"
|
|
170
|
+
:loading="operation === 'refreshing-events'"
|
|
171
|
+
@click="emit('refresh-events')"
|
|
172
|
+
>
|
|
173
|
+
{{ resolvedLabels.refreshEvents }}
|
|
174
|
+
</SButton>
|
|
175
|
+
<SButton
|
|
176
|
+
v-if="capabilities.cancelRun === 'enabled'"
|
|
177
|
+
size="sm"
|
|
178
|
+
severity="danger"
|
|
179
|
+
:loading="operation === 'cancelling-run'"
|
|
180
|
+
@click="emit('cancel-run')"
|
|
181
|
+
>
|
|
182
|
+
{{ resolvedLabels.cancelRun }}
|
|
183
|
+
</SButton>
|
|
184
|
+
</template>
|
|
185
|
+
</SSectionHeader>
|
|
186
|
+
|
|
187
|
+
<SMessage v-if="selectedRun.artifactLabel" severity="info" title="Артефакты">
|
|
188
|
+
{{ selectedRun.artifactLabel }}
|
|
189
|
+
</SMessage>
|
|
190
|
+
|
|
191
|
+
<SKeyValueGrid
|
|
192
|
+
v-if="selectedFacts.length > 0"
|
|
193
|
+
:items="selectedFacts"
|
|
194
|
+
:get-item-key="getPersonaRunConsoleFactKey"
|
|
195
|
+
variant="inline"
|
|
196
|
+
:columns="1"
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
<SSectionHeader :title="resolvedLabels.itemsTitle" size="sm" />
|
|
200
|
+
<SAsyncState :state="itemsAsyncState">
|
|
201
|
+
<SVirtualList
|
|
202
|
+
:items="items"
|
|
203
|
+
:item-size="virtualRowSize"
|
|
204
|
+
:height="itemsViewportHeight"
|
|
205
|
+
:item-key="(workItem) => workItem.id"
|
|
206
|
+
:accessible-label="resolvedLabels.itemsTitle"
|
|
207
|
+
:item-label="(workItem) => workItem.title"
|
|
208
|
+
>
|
|
209
|
+
<template #default="{ item }">
|
|
210
|
+
<SActionListItem
|
|
211
|
+
role="presentation"
|
|
212
|
+
:title="item.title"
|
|
213
|
+
:description="item.meta"
|
|
214
|
+
:detail="item.detail"
|
|
215
|
+
:status-label="item.statusLabel"
|
|
216
|
+
:status-severity="item.statusSeverity"
|
|
217
|
+
:badges="item.badges"
|
|
218
|
+
:surface="{ mode: 'static' }"
|
|
219
|
+
trailing-layout="inline"
|
|
220
|
+
height-mode="fill"
|
|
221
|
+
/>
|
|
222
|
+
</template>
|
|
223
|
+
</SVirtualList>
|
|
224
|
+
</SAsyncState>
|
|
225
|
+
<div
|
|
226
|
+
v-if="itemsState.status === 'ready' && itemsState.pagination !== 'complete'"
|
|
227
|
+
class="s-persona-run-console__actions-line"
|
|
228
|
+
>
|
|
229
|
+
<SButton
|
|
230
|
+
size="sm"
|
|
231
|
+
severity="secondary"
|
|
232
|
+
:loading="itemsState.pagination === 'loading-more'"
|
|
233
|
+
@click="emit('load-more-items')"
|
|
234
|
+
>
|
|
235
|
+
{{ resolvedLabels.loadMoreItems }}
|
|
236
|
+
</SButton>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<SCodeBlock
|
|
240
|
+
v-if="reportSummary"
|
|
241
|
+
:code="reportSummary"
|
|
242
|
+
:language="{ id: 'plaintext' }"
|
|
243
|
+
:show-header="false"
|
|
244
|
+
:show-line-numbers="false"
|
|
245
|
+
viewport="compact"
|
|
246
|
+
:aria-label="resolvedLabels.reportSummary"
|
|
247
|
+
/>
|
|
248
|
+
|
|
249
|
+
<SSectionHeader :title="resolvedLabels.eventsTitle" size="sm" />
|
|
250
|
+
<SAsyncState :state="eventsAsyncState">
|
|
251
|
+
<SVirtualList
|
|
252
|
+
:items="events"
|
|
253
|
+
:item-size="virtualRowSize"
|
|
254
|
+
:height="eventsViewportHeight"
|
|
255
|
+
:item-key="(event) => event.id"
|
|
256
|
+
:accessible-label="resolvedLabels.eventsTitle"
|
|
257
|
+
:item-label="(event) => event.title"
|
|
258
|
+
>
|
|
259
|
+
<template #default="{ item }">
|
|
260
|
+
<SActionListItem
|
|
261
|
+
role="presentation"
|
|
262
|
+
:title="item.title"
|
|
263
|
+
:description="item.meta"
|
|
264
|
+
:detail="eventDetail(item)"
|
|
265
|
+
:badges="item.badges"
|
|
266
|
+
:surface="{ mode: 'static' }"
|
|
267
|
+
height-mode="fill"
|
|
268
|
+
/>
|
|
269
|
+
</template>
|
|
270
|
+
</SVirtualList>
|
|
271
|
+
</SAsyncState>
|
|
272
|
+
<div v-if="eventsState.status === 'ready'" class="s-persona-run-console__actions-line">
|
|
273
|
+
<SButton
|
|
274
|
+
size="sm"
|
|
275
|
+
severity="secondary"
|
|
276
|
+
:loading="operation === 'appending-events'"
|
|
277
|
+
@click="emit('append-events')"
|
|
278
|
+
>
|
|
279
|
+
{{ resolvedLabels.appendEvents }}
|
|
280
|
+
</SButton>
|
|
281
|
+
<SButton
|
|
282
|
+
v-if="eventsState.status === 'ready' && eventsState.pagination !== 'complete'"
|
|
283
|
+
size="sm"
|
|
284
|
+
severity="secondary"
|
|
285
|
+
:loading="eventsState.pagination === 'loading-more'"
|
|
286
|
+
@click="emit('load-more-events')"
|
|
287
|
+
>
|
|
288
|
+
{{ resolvedLabels.loadMoreEvents }}
|
|
289
|
+
</SButton>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
<SEmptyState v-else :title="resolvedLabels.emptyRun" size="sm" />
|
|
293
|
+
</section>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
</template>
|
|
297
|
+
|
|
298
|
+
<style lang="postcss" src="./SPersonaRunConsole.css" scoped></style>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { SAsyncStateContract } from '../../feedback/SAsyncState.vue'
|
|
2
|
+
import type {
|
|
3
|
+
PersonaInventoryState,
|
|
4
|
+
PersonaPaginatedInventoryState,
|
|
5
|
+
PersonaProjectPickerState,
|
|
6
|
+
} from '../types'
|
|
7
|
+
|
|
8
|
+
export interface PersonaStableInventoryItem {
|
|
9
|
+
readonly id: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PersonaAsyncPresentation {
|
|
13
|
+
readonly loadingMessage: string
|
|
14
|
+
readonly emptyMessage: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function assertPersonaInventory<Item extends PersonaStableInventoryItem>(
|
|
18
|
+
owner: string,
|
|
19
|
+
items: Item[],
|
|
20
|
+
): Item[] {
|
|
21
|
+
if (!Array.isArray(items)) {
|
|
22
|
+
throw new TypeError(
|
|
23
|
+
`${owner}: inventory должен быть массивом. / ${owner}: inventory must be an array.`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const identities = new Set<string>()
|
|
28
|
+
for (const [index, item] of items.entries()) {
|
|
29
|
+
const candidate: unknown = item
|
|
30
|
+
if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {
|
|
31
|
+
throw new TypeError(
|
|
32
|
+
`${owner}: inventory[${index}] должен быть объектом со стабильным id. `
|
|
33
|
+
+ `/ ${owner}: inventory[${index}] must be an object with a stable id.`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
const id = (candidate as { id?: unknown }).id
|
|
37
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
38
|
+
throw new TypeError(
|
|
39
|
+
`${owner}: inventory[${index}].id должен быть непустой строкой. `
|
|
40
|
+
+ `/ ${owner}: inventory[${index}].id must be a non-empty string.`,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
if (identities.has(id)) {
|
|
44
|
+
throw new TypeError(
|
|
45
|
+
`${owner}: inventory содержит duplicate id ${JSON.stringify(id)}. `
|
|
46
|
+
+ `/ ${owner}: inventory contains duplicate id ${JSON.stringify(id)}.`,
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
identities.add(id)
|
|
50
|
+
}
|
|
51
|
+
return items
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function findExactPersonaInventoryItem<Item extends PersonaStableInventoryItem>(
|
|
55
|
+
owner: string,
|
|
56
|
+
items: Item[],
|
|
57
|
+
selectedId: string | null,
|
|
58
|
+
): Item | null {
|
|
59
|
+
const inventory = assertPersonaInventory(owner, items)
|
|
60
|
+
if (selectedId === null) return null
|
|
61
|
+
if (typeof selectedId !== 'string' || selectedId.length === 0) {
|
|
62
|
+
throw new TypeError(
|
|
63
|
+
`${owner}: selectedId должен быть непустой строкой или null. `
|
|
64
|
+
+ `/ ${owner}: selectedId must be a non-empty string or null.`,
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
const selectedItem = inventory.find((item) => item.id === selectedId)
|
|
68
|
+
if (selectedItem !== undefined) return selectedItem
|
|
69
|
+
throw new Error(
|
|
70
|
+
`${owner}: selectedId ${JSON.stringify(selectedId)} отсутствует в inventory. `
|
|
71
|
+
+ `/ ${owner}: selectedId ${JSON.stringify(selectedId)} is absent from the inventory.`,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Проецирует доменный inventory contract в единственный canonical async-state contract.
|
|
77
|
+
* Projects a domain inventory contract onto the sole canonical async-state contract.
|
|
78
|
+
*/
|
|
79
|
+
export function resolvePersonaInventoryAsyncState<Item extends PersonaStableInventoryItem>(
|
|
80
|
+
owner: string,
|
|
81
|
+
state: PersonaInventoryState<Item> | PersonaPaginatedInventoryState<Item>,
|
|
82
|
+
presentation: PersonaAsyncPresentation,
|
|
83
|
+
): SAsyncStateContract {
|
|
84
|
+
if (state.status === 'loading') {
|
|
85
|
+
return { status: 'loading', message: state.message ?? presentation.loadingMessage }
|
|
86
|
+
}
|
|
87
|
+
if (state.status === 'error') return state
|
|
88
|
+
|
|
89
|
+
const inventory = assertPersonaInventory(owner, state.items)
|
|
90
|
+
return inventory.length === 0
|
|
91
|
+
? { status: 'empty', message: presentation.emptyMessage }
|
|
92
|
+
: { status: 'ready' }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Проецирует picker state в canonical async-state, сохраняя ready listing как доменный payload.
|
|
97
|
+
* Projects picker state onto canonical async-state while preserving the ready listing payload.
|
|
98
|
+
*/
|
|
99
|
+
export function resolvePersonaProjectPickerAsyncState(
|
|
100
|
+
state: PersonaProjectPickerState,
|
|
101
|
+
presentation: PersonaAsyncPresentation,
|
|
102
|
+
): SAsyncStateContract {
|
|
103
|
+
if (state.status === 'loading') {
|
|
104
|
+
return { status: 'loading', message: state.message ?? presentation.loadingMessage }
|
|
105
|
+
}
|
|
106
|
+
if (state.status === 'error') return state
|
|
107
|
+
return state.listing === null
|
|
108
|
+
? { status: 'empty', message: presentation.emptyMessage }
|
|
109
|
+
: { status: 'ready' }
|
|
110
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { validateNonEmptyString } from '../../../../internal/runtimeContract'
|
|
2
|
+
|
|
3
|
+
import type { PersonaRunWorkbenchLabels } from '../types'
|
|
4
|
+
|
|
5
|
+
const PERSONA_RUN_WORKBENCH_DEFAULT_LABELS = Object.freeze({
|
|
6
|
+
projectTitle: 'Проект',
|
|
7
|
+
discoveryTitle: 'Discovery',
|
|
8
|
+
catalogTitle: 'Каталог тестов',
|
|
9
|
+
structureTitle: 'Структура сценария',
|
|
10
|
+
selectedTestTitle: 'Выбранный тест',
|
|
11
|
+
runSelected: 'Запустить тест',
|
|
12
|
+
runAll: 'Запустить каталог',
|
|
13
|
+
discover: 'Обнаружить',
|
|
14
|
+
watch: 'Наблюдать',
|
|
15
|
+
searchPlaceholder: 'Поиск по тестам',
|
|
16
|
+
noCatalogTests: 'Тесты не найдены.',
|
|
17
|
+
emptyStructure: 'Структура сценария не сохранена.',
|
|
18
|
+
runListTitle: 'Запуски',
|
|
19
|
+
selectedRunTitle: 'События запуска',
|
|
20
|
+
itemsTitle: 'Рабочие элементы',
|
|
21
|
+
eventsTitle: 'События',
|
|
22
|
+
loadMoreRuns: 'Загрузить ещё',
|
|
23
|
+
loadMoreItems: 'Догрузить рабочие элементы',
|
|
24
|
+
appendEvents: 'Догрузить новые',
|
|
25
|
+
loadMoreEvents: 'Догрузить историю',
|
|
26
|
+
refreshEvents: 'Обновить события',
|
|
27
|
+
cancelRun: 'Отменить',
|
|
28
|
+
reportSummary: 'Сводка отчёта',
|
|
29
|
+
emptyRun: 'Выберите запуск для просмотра событий и артефактов.',
|
|
30
|
+
noRuns: 'Запуски не найдены.',
|
|
31
|
+
noItems: 'Рабочие элементы ещё не появились.',
|
|
32
|
+
noEvents: 'События запуска ещё не поступили.',
|
|
33
|
+
} satisfies PersonaRunWorkbenchLabels)
|
|
34
|
+
|
|
35
|
+
type PersonaRunWorkbenchLabelKey = keyof PersonaRunWorkbenchLabels
|
|
36
|
+
|
|
37
|
+
function resolveLabel(
|
|
38
|
+
overrides: Readonly<Partial<PersonaRunWorkbenchLabels>>,
|
|
39
|
+
key: PersonaRunWorkbenchLabelKey,
|
|
40
|
+
): string {
|
|
41
|
+
const override = overrides[key]
|
|
42
|
+
return override === undefined
|
|
43
|
+
? PERSONA_RUN_WORKBENCH_DEFAULT_LABELS[key]
|
|
44
|
+
: validateNonEmptyString('SPersonaRunWorkbench', `labels.${key}`, override)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolvePersonaRunWorkbenchLabels(
|
|
48
|
+
overrides: Readonly<Partial<PersonaRunWorkbenchLabels>> = {},
|
|
49
|
+
): PersonaRunWorkbenchLabels {
|
|
50
|
+
return {
|
|
51
|
+
projectTitle: resolveLabel(overrides, 'projectTitle'),
|
|
52
|
+
discoveryTitle: resolveLabel(overrides, 'discoveryTitle'),
|
|
53
|
+
catalogTitle: resolveLabel(overrides, 'catalogTitle'),
|
|
54
|
+
structureTitle: resolveLabel(overrides, 'structureTitle'),
|
|
55
|
+
selectedTestTitle: resolveLabel(overrides, 'selectedTestTitle'),
|
|
56
|
+
runSelected: resolveLabel(overrides, 'runSelected'),
|
|
57
|
+
runAll: resolveLabel(overrides, 'runAll'),
|
|
58
|
+
discover: resolveLabel(overrides, 'discover'),
|
|
59
|
+
watch: resolveLabel(overrides, 'watch'),
|
|
60
|
+
searchPlaceholder: resolveLabel(overrides, 'searchPlaceholder'),
|
|
61
|
+
noCatalogTests: resolveLabel(overrides, 'noCatalogTests'),
|
|
62
|
+
emptyStructure: resolveLabel(overrides, 'emptyStructure'),
|
|
63
|
+
runListTitle: resolveLabel(overrides, 'runListTitle'),
|
|
64
|
+
selectedRunTitle: resolveLabel(overrides, 'selectedRunTitle'),
|
|
65
|
+
itemsTitle: resolveLabel(overrides, 'itemsTitle'),
|
|
66
|
+
eventsTitle: resolveLabel(overrides, 'eventsTitle'),
|
|
67
|
+
loadMoreRuns: resolveLabel(overrides, 'loadMoreRuns'),
|
|
68
|
+
loadMoreItems: resolveLabel(overrides, 'loadMoreItems'),
|
|
69
|
+
appendEvents: resolveLabel(overrides, 'appendEvents'),
|
|
70
|
+
loadMoreEvents: resolveLabel(overrides, 'loadMoreEvents'),
|
|
71
|
+
refreshEvents: resolveLabel(overrides, 'refreshEvents'),
|
|
72
|
+
cancelRun: resolveLabel(overrides, 'cancelRun'),
|
|
73
|
+
reportSummary: resolveLabel(overrides, 'reportSummary'),
|
|
74
|
+
emptyRun: resolveLabel(overrides, 'emptyRun'),
|
|
75
|
+
noRuns: resolveLabel(overrides, 'noRuns'),
|
|
76
|
+
noItems: resolveLabel(overrides, 'noItems'),
|
|
77
|
+
noEvents: resolveLabel(overrides, 'noEvents'),
|
|
78
|
+
}
|
|
79
|
+
}
|