@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,277 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Устойчивый доменный ключ строки definition list. / Stable domain key of a definition-list row. */
|
|
3
|
+
export type SKeyValueGridKey = string | number
|
|
4
|
+
|
|
5
|
+
/** Значение, которое компонент умеет представить без formatter. / Value the component can present without a formatter. */
|
|
6
|
+
export type SKeyValueGridPrimitiveValue = string | number | boolean | null | undefined
|
|
7
|
+
|
|
8
|
+
/** Typed presentation-модель одной definition-list строки. / Typed presentation model of one definition-list row. */
|
|
9
|
+
export interface SKeyValueGridItem<Value = SKeyValueGridPrimitiveValue> {
|
|
10
|
+
label: string
|
|
11
|
+
value: Value
|
|
12
|
+
valueMonospace?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generic API definition-list: identity задаёт обязательный resolver, форматирование не меняет raw value.
|
|
17
|
+
* Generic definition-list API: identity comes from the required resolver; formatting preserves the raw value.
|
|
18
|
+
*/
|
|
19
|
+
export interface SKeyValueGridProps<
|
|
20
|
+
Item extends SKeyValueGridItem = SKeyValueGridItem,
|
|
21
|
+
ItemKey extends SKeyValueGridKey = SKeyValueGridKey,
|
|
22
|
+
> {
|
|
23
|
+
items: readonly Item[]
|
|
24
|
+
getItemKey: (item: Item) => ItemKey
|
|
25
|
+
formatValue?: (value: Item['value'], item: Item) => string
|
|
26
|
+
emptyValueLabel?: string
|
|
27
|
+
variant?: 'cards' | 'inline' | 'plain'
|
|
28
|
+
columns?: 1 | 2 | 3
|
|
29
|
+
density?: 'comfortable' | 'compact'
|
|
30
|
+
valueAlign?: 'start' | 'end'
|
|
31
|
+
labelWidth?: 'narrow' | 'balanced' | 'wide'
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts" generic="Item extends SKeyValueGridItem, ItemKey extends SKeyValueGridKey">
|
|
36
|
+
import { computed } from 'vue'
|
|
37
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
38
|
+
import {
|
|
39
|
+
validateBoolean,
|
|
40
|
+
validateBoundedInteger,
|
|
41
|
+
validateExactString,
|
|
42
|
+
validateNonEmptyString,
|
|
43
|
+
} from '../../../internal/runtimeContract'
|
|
44
|
+
|
|
45
|
+
defineOptions({ inheritAttrs: false })
|
|
46
|
+
|
|
47
|
+
const props = withDefaults(defineProps<SKeyValueGridProps<Item, ItemKey>>(), {
|
|
48
|
+
formatValue: undefined,
|
|
49
|
+
emptyValueLabel: '—',
|
|
50
|
+
variant: 'cards',
|
|
51
|
+
columns: 3,
|
|
52
|
+
density: 'comfortable',
|
|
53
|
+
valueAlign: 'start',
|
|
54
|
+
labelWidth: 'narrow',
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
defineSlots<{
|
|
58
|
+
label?(props: { item: Item; itemKey: ItemKey; index: number }): unknown
|
|
59
|
+
value?(props: {
|
|
60
|
+
item: Item
|
|
61
|
+
itemKey: ItemKey
|
|
62
|
+
index: number
|
|
63
|
+
value: Item['value']
|
|
64
|
+
formattedValue: SKeyValueGridPrimitiveValue
|
|
65
|
+
}): unknown
|
|
66
|
+
}>()
|
|
67
|
+
|
|
68
|
+
const VARIANTS = ['cards', 'inline', 'plain'] as const
|
|
69
|
+
const DENSITIES = ['comfortable', 'compact'] as const
|
|
70
|
+
const VALUE_ALIGNMENTS = ['start', 'end'] as const
|
|
71
|
+
const LABEL_WIDTHS = ['narrow', 'balanced', 'wide'] as const
|
|
72
|
+
|
|
73
|
+
interface ResolvedItem {
|
|
74
|
+
item: Item
|
|
75
|
+
itemKey: ItemKey
|
|
76
|
+
index: number
|
|
77
|
+
label: string
|
|
78
|
+
formattedValue: SKeyValueGridPrimitiveValue
|
|
79
|
+
valueMonospace: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SKeyValueGrid', owner: 'key-value definition list' })
|
|
83
|
+
const validatedVariant = computed(() => validateExactString(
|
|
84
|
+
'SKeyValueGrid',
|
|
85
|
+
'variant',
|
|
86
|
+
props.variant,
|
|
87
|
+
VARIANTS,
|
|
88
|
+
))
|
|
89
|
+
const validatedColumns = computed(() => validateBoundedInteger(
|
|
90
|
+
'SKeyValueGrid',
|
|
91
|
+
'columns',
|
|
92
|
+
props.columns,
|
|
93
|
+
{ minimum: 1, maximum: 3 },
|
|
94
|
+
))
|
|
95
|
+
const validatedDensity = computed(() => validateExactString(
|
|
96
|
+
'SKeyValueGrid',
|
|
97
|
+
'density',
|
|
98
|
+
props.density,
|
|
99
|
+
DENSITIES,
|
|
100
|
+
))
|
|
101
|
+
const validatedValueAlign = computed(() => validateExactString(
|
|
102
|
+
'SKeyValueGrid',
|
|
103
|
+
'valueAlign',
|
|
104
|
+
props.valueAlign,
|
|
105
|
+
VALUE_ALIGNMENTS,
|
|
106
|
+
))
|
|
107
|
+
const validatedLabelWidth = computed(() => validateExactString(
|
|
108
|
+
'SKeyValueGrid',
|
|
109
|
+
'labelWidth',
|
|
110
|
+
props.labelWidth,
|
|
111
|
+
LABEL_WIDTHS,
|
|
112
|
+
))
|
|
113
|
+
|
|
114
|
+
function describeKey(key: SKeyValueGridKey): string {
|
|
115
|
+
return typeof key === 'string' ? JSON.stringify(key) : String(key)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validateItemKey(candidate: unknown, coordinate: string): asserts candidate is ItemKey {
|
|
119
|
+
if (typeof candidate === 'string') {
|
|
120
|
+
if (candidate.trim().length > 0) return
|
|
121
|
+
throw new TypeError(
|
|
122
|
+
`SKeyValueGrid: getItemKey вернул пустую строку для ${coordinate}. `
|
|
123
|
+
+ `/ SKeyValueGrid: getItemKey returned an empty string for ${coordinate}.`,
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
if (typeof candidate === 'number') {
|
|
127
|
+
if (Number.isFinite(candidate)) return
|
|
128
|
+
throw new TypeError(
|
|
129
|
+
`SKeyValueGrid: getItemKey вернул неконечное число для ${coordinate}. `
|
|
130
|
+
+ `/ SKeyValueGrid: getItemKey returned a non-finite number for ${coordinate}.`,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
throw new TypeError(
|
|
134
|
+
`SKeyValueGrid: getItemKey вернул неподдерживаемый key ${typeof candidate} для ${coordinate}. `
|
|
135
|
+
+ `/ SKeyValueGrid: getItemKey returned unsupported ${typeof candidate} key for ${coordinate}.`,
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function validateUnformattedValue(
|
|
140
|
+
value: unknown,
|
|
141
|
+
coordinate: string,
|
|
142
|
+
): asserts value is SKeyValueGridPrimitiveValue {
|
|
143
|
+
if (
|
|
144
|
+
value === null
|
|
145
|
+
|| value === undefined
|
|
146
|
+
|| typeof value === 'string'
|
|
147
|
+
|| typeof value === 'boolean'
|
|
148
|
+
|| (typeof value === 'number' && Number.isFinite(value))
|
|
149
|
+
) return
|
|
150
|
+
|
|
151
|
+
throw new TypeError(
|
|
152
|
+
`SKeyValueGrid: ${coordinate} должен быть string | finite number | boolean | null | undefined `
|
|
153
|
+
+ `или иметь formatValue. / SKeyValueGrid: ${coordinate} must be string | finite number | boolean `
|
|
154
|
+
+ '| null | undefined or provide formatValue.',
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolveFormattedValue(item: Item, index: number): SKeyValueGridPrimitiveValue {
|
|
159
|
+
const coordinate = `items[${index}]`
|
|
160
|
+
if (props.formatValue !== undefined) {
|
|
161
|
+
if (typeof props.formatValue !== 'function') {
|
|
162
|
+
throw new TypeError(
|
|
163
|
+
'SKeyValueGrid: formatValue должен быть function. '
|
|
164
|
+
+ '/ SKeyValueGrid: formatValue must be a function.',
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
const result: unknown = props.formatValue(item.value, item)
|
|
168
|
+
if (typeof result !== 'string') {
|
|
169
|
+
throw new TypeError(
|
|
170
|
+
`SKeyValueGrid: formatValue должен вернуть string для ${coordinate}. `
|
|
171
|
+
+ `/ SKeyValueGrid: formatValue must return a string for ${coordinate}.`,
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
return result
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
validateUnformattedValue(item.value, `${coordinate}.value`)
|
|
178
|
+
return item.value ?? validateNonEmptyString('SKeyValueGrid', 'emptyValueLabel', props.emptyValueLabel)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const resolvedItems = computed<readonly ResolvedItem[]>(() => {
|
|
182
|
+
if (!Array.isArray(props.items)) {
|
|
183
|
+
throw new TypeError(
|
|
184
|
+
'SKeyValueGrid: items должен быть readonly array. '
|
|
185
|
+
+ '/ SKeyValueGrid: items must be a readonly array.',
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
if (typeof props.getItemKey !== 'function') {
|
|
189
|
+
throw new TypeError(
|
|
190
|
+
'SKeyValueGrid: getItemKey должен быть обязательным function resolver. '
|
|
191
|
+
+ '/ SKeyValueGrid: getItemKey must be a required function resolver.',
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const coordinatesByKey = new Map<SKeyValueGridKey, string>()
|
|
196
|
+
return props.items.map((candidate, index) => {
|
|
197
|
+
const coordinate = `items[${index}]`
|
|
198
|
+
if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {
|
|
199
|
+
throw new TypeError(
|
|
200
|
+
`SKeyValueGrid: ${coordinate} должен быть object. `
|
|
201
|
+
+ `/ SKeyValueGrid: ${coordinate} must be an object.`,
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
const item = candidate as Item
|
|
205
|
+
const label = validateNonEmptyString('SKeyValueGrid', `${coordinate}.label`, item.label)
|
|
206
|
+
const rawKey: unknown = props.getItemKey(item)
|
|
207
|
+
validateItemKey(rawKey, coordinate)
|
|
208
|
+
const previousCoordinate = coordinatesByKey.get(rawKey)
|
|
209
|
+
if (previousCoordinate !== undefined) {
|
|
210
|
+
throw new TypeError(
|
|
211
|
+
`SKeyValueGrid: getItemKey вернул duplicate key ${describeKey(rawKey)} для ${coordinate}; `
|
|
212
|
+
+ `впервые объявлен в ${previousCoordinate}. / SKeyValueGrid: getItemKey returned duplicate key `
|
|
213
|
+
+ `${describeKey(rawKey)} for ${coordinate}; first declared at ${previousCoordinate}.`,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
coordinatesByKey.set(rawKey, coordinate)
|
|
217
|
+
|
|
218
|
+
const valueMonospace = item.valueMonospace === undefined
|
|
219
|
+
? false
|
|
220
|
+
: validateBoolean('SKeyValueGrid', `${coordinate}.valueMonospace`, item.valueMonospace)
|
|
221
|
+
return {
|
|
222
|
+
item,
|
|
223
|
+
itemKey: rawKey,
|
|
224
|
+
index,
|
|
225
|
+
label,
|
|
226
|
+
formattedValue: resolveFormattedValue(item, index),
|
|
227
|
+
valueMonospace,
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
</script>
|
|
232
|
+
|
|
233
|
+
<template>
|
|
234
|
+
<dl
|
|
235
|
+
v-bind="ownedAttrs.bindings()"
|
|
236
|
+
class="s-key-value-grid"
|
|
237
|
+
:data-variant="validatedVariant"
|
|
238
|
+
:data-columns="validatedColumns"
|
|
239
|
+
:data-density="validatedDensity"
|
|
240
|
+
:data-value-align="validatedValueAlign"
|
|
241
|
+
:data-label-width="validatedLabelWidth"
|
|
242
|
+
>
|
|
243
|
+
<div
|
|
244
|
+
v-for="entry in resolvedItems"
|
|
245
|
+
:key="entry.itemKey"
|
|
246
|
+
class="s-key-value-grid__item"
|
|
247
|
+
>
|
|
248
|
+
<dt class="s-key-value-grid__label">
|
|
249
|
+
<slot
|
|
250
|
+
name="label"
|
|
251
|
+
:item="entry.item"
|
|
252
|
+
:item-key="entry.itemKey"
|
|
253
|
+
:index="entry.index"
|
|
254
|
+
>
|
|
255
|
+
{{ entry.label }}
|
|
256
|
+
</slot>
|
|
257
|
+
</dt>
|
|
258
|
+
<dd
|
|
259
|
+
class="s-key-value-grid__value"
|
|
260
|
+
:data-monospace="entry.valueMonospace ? 'true' : undefined"
|
|
261
|
+
>
|
|
262
|
+
<slot
|
|
263
|
+
name="value"
|
|
264
|
+
:item="entry.item"
|
|
265
|
+
:item-key="entry.itemKey"
|
|
266
|
+
:index="entry.index"
|
|
267
|
+
:value="entry.item.value"
|
|
268
|
+
:formatted-value="entry.formattedValue"
|
|
269
|
+
>
|
|
270
|
+
{{ entry.formattedValue }}
|
|
271
|
+
</slot>
|
|
272
|
+
</dd>
|
|
273
|
+
</div>
|
|
274
|
+
</dl>
|
|
275
|
+
</template>
|
|
276
|
+
|
|
277
|
+
<style lang="postcss" src="./SKeyValueGrid.css" scoped></style>
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="validatedPresentation === 'chips'"
|
|
4
|
+
v-bind="ownedAttrs.bindings()"
|
|
5
|
+
class="s-linked-systems-list"
|
|
6
|
+
data-presentation="chips"
|
|
7
|
+
role="list"
|
|
8
|
+
:aria-label="validatedAccessibleLabel"
|
|
9
|
+
>
|
|
10
|
+
<span
|
|
11
|
+
v-for="item in validatedItems"
|
|
12
|
+
:key="item.id"
|
|
13
|
+
class="s-linked-systems-list__chip"
|
|
14
|
+
role="listitem"
|
|
15
|
+
:title="`${item.providerName}: ${item.externalId}`"
|
|
16
|
+
>
|
|
17
|
+
<SStatus
|
|
18
|
+
:label="item.syncStatus.label"
|
|
19
|
+
:tone="item.syncStatus.tone"
|
|
20
|
+
:title="item.syncStatus.title"
|
|
21
|
+
:icon="item.syncStatus.icon"
|
|
22
|
+
label-visibility="sr-only"
|
|
23
|
+
size="xs"
|
|
24
|
+
/>
|
|
25
|
+
<SLink
|
|
26
|
+
v-if="item.externalUrl"
|
|
27
|
+
:href="item.externalUrl"
|
|
28
|
+
target="_blank"
|
|
29
|
+
external
|
|
30
|
+
variant="subtle"
|
|
31
|
+
underline="hover"
|
|
32
|
+
wrap="anywhere"
|
|
33
|
+
content-kind="identifier"
|
|
34
|
+
>
|
|
35
|
+
{{ item.externalId }}
|
|
36
|
+
</SLink>
|
|
37
|
+
<span v-else class="s-linked-systems-list__external-id">{{ item.externalId }}</span>
|
|
38
|
+
</span>
|
|
39
|
+
</div>
|
|
40
|
+
<SActionList v-else v-bind="ownedAttrs.bindings()" :accessible-label="validatedAccessibleLabel">
|
|
41
|
+
<SActionListItem
|
|
42
|
+
v-for="item in validatedItems"
|
|
43
|
+
:key="item.id"
|
|
44
|
+
:title="item.providerName"
|
|
45
|
+
:surface="{ mode: 'static' }"
|
|
46
|
+
:action-disabled="validatedActionsDisabled"
|
|
47
|
+
trailing-layout="responsive"
|
|
48
|
+
>
|
|
49
|
+
<template #description>
|
|
50
|
+
<SLink
|
|
51
|
+
v-if="item.externalUrl"
|
|
52
|
+
:href="item.externalUrl"
|
|
53
|
+
target="_blank"
|
|
54
|
+
external
|
|
55
|
+
variant="subtle"
|
|
56
|
+
>
|
|
57
|
+
{{ item.externalId }}
|
|
58
|
+
</SLink>
|
|
59
|
+
<span v-else>
|
|
60
|
+
{{ item.externalId }}
|
|
61
|
+
</span>
|
|
62
|
+
</template>
|
|
63
|
+
<template #status>
|
|
64
|
+
<SStatus
|
|
65
|
+
:label="item.syncStatus.label"
|
|
66
|
+
:tone="item.syncStatus.tone"
|
|
67
|
+
:title="item.syncStatus.title"
|
|
68
|
+
:icon="item.syncStatus.icon"
|
|
69
|
+
size="sm"
|
|
70
|
+
/>
|
|
71
|
+
</template>
|
|
72
|
+
<template v-if="$slots.actions" #actions="{ disabled }">
|
|
73
|
+
<slot name="actions" :item="item" :disabled="disabled" />
|
|
74
|
+
</template>
|
|
75
|
+
</SActionListItem>
|
|
76
|
+
</SActionList>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script lang="ts">
|
|
80
|
+
import type { Component, VNodeChild } from 'vue'
|
|
81
|
+
import type { StatusTone } from './SStatus.vue'
|
|
82
|
+
|
|
83
|
+
export type LinkedSystemPresentation = 'list' | 'chips'
|
|
84
|
+
|
|
85
|
+
export interface LinkedSystemSyncStatus {
|
|
86
|
+
readonly label: string
|
|
87
|
+
readonly tone: StatusTone
|
|
88
|
+
readonly title?: string
|
|
89
|
+
readonly icon?: Component | null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface LinkedSystemListItem {
|
|
93
|
+
readonly id: string
|
|
94
|
+
readonly providerName: string
|
|
95
|
+
readonly externalId: string
|
|
96
|
+
readonly externalUrl: string | null
|
|
97
|
+
readonly syncStatus: LinkedSystemSyncStatus
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface Props<T extends LinkedSystemListItem = LinkedSystemListItem> {
|
|
101
|
+
readonly items: readonly T[]
|
|
102
|
+
readonly accessibleLabel?: string
|
|
103
|
+
/** List подходит для inspector, chips — для компактных строк сущностей. / List suits inspectors; chips suit compact entity rows. */
|
|
104
|
+
readonly presentation?: LinkedSystemPresentation
|
|
105
|
+
/** Единое disabled-состояние для слота действий. / Shared disabled state for the actions slot. */
|
|
106
|
+
readonly actionsDisabled?: boolean
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface Slots<T extends LinkedSystemListItem = LinkedSystemListItem> {
|
|
110
|
+
actions?: (props: { readonly item: T, readonly disabled: boolean }) => VNodeChild
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<script setup lang="ts" generic="T extends LinkedSystemListItem = LinkedSystemListItem">
|
|
115
|
+
import { computed, watchEffect } from 'vue'
|
|
116
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
117
|
+
import {
|
|
118
|
+
validateBoolean,
|
|
119
|
+
validateExactString,
|
|
120
|
+
validateNonEmptyString,
|
|
121
|
+
} from '../../../internal/runtimeContract'
|
|
122
|
+
import SLink from '../controls/SLink.vue'
|
|
123
|
+
import SActionList from './SActionList.vue'
|
|
124
|
+
import SActionListItem from './SActionListItem.vue'
|
|
125
|
+
import SStatus from './SStatus.vue'
|
|
126
|
+
|
|
127
|
+
defineOptions({ inheritAttrs: false })
|
|
128
|
+
|
|
129
|
+
const props = withDefaults(defineProps<Props<T>>(), {
|
|
130
|
+
accessibleLabel: 'Связанные системы',
|
|
131
|
+
presentation: 'list',
|
|
132
|
+
actionsDisabled: false,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const slots = defineSlots<Slots<T>>()
|
|
136
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SLinkedSystemsList', owner: 'active linked-systems list root' })
|
|
137
|
+
const validatedAccessibleLabel = computed(() => validateNonEmptyString(
|
|
138
|
+
'SLinkedSystemsList',
|
|
139
|
+
'accessibleLabel',
|
|
140
|
+
props.accessibleLabel,
|
|
141
|
+
))
|
|
142
|
+
const validatedPresentation = computed(() => validateExactString(
|
|
143
|
+
'SLinkedSystemsList',
|
|
144
|
+
'presentation',
|
|
145
|
+
props.presentation,
|
|
146
|
+
['list', 'chips'] as const,
|
|
147
|
+
))
|
|
148
|
+
const validatedActionsDisabled = computed(() => validateBoolean(
|
|
149
|
+
'SLinkedSystemsList',
|
|
150
|
+
'actionsDisabled',
|
|
151
|
+
props.actionsDisabled,
|
|
152
|
+
))
|
|
153
|
+
|
|
154
|
+
function requireRecord(value: unknown, coordinate: string): Record<string, unknown> {
|
|
155
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
156
|
+
return value as Record<string, unknown>
|
|
157
|
+
}
|
|
158
|
+
throw new TypeError(
|
|
159
|
+
`SLinkedSystemsList: ${coordinate} должен быть объектом. `
|
|
160
|
+
+ `/ SLinkedSystemsList: ${coordinate} must be an object.`,
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function validateExternalUrl(value: unknown, index: number): void {
|
|
165
|
+
const coordinate = `items[${index}].externalUrl`
|
|
166
|
+
if (value === null) return
|
|
167
|
+
const externalUrl = validateNonEmptyString('SLinkedSystemsList', coordinate, value)
|
|
168
|
+
let parsedUrl: URL
|
|
169
|
+
try {
|
|
170
|
+
parsedUrl = new URL(externalUrl)
|
|
171
|
+
} catch (error: unknown) {
|
|
172
|
+
throw new TypeError(
|
|
173
|
+
`SLinkedSystemsList: ${coordinate} должен быть абсолютным HTTP(S) URL или null; получено ${JSON.stringify(externalUrl)}. `
|
|
174
|
+
+ `/ SLinkedSystemsList: ${coordinate} must be an absolute HTTP(S) URL or null; received ${JSON.stringify(externalUrl)}.`,
|
|
175
|
+
{ cause: error },
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
if (
|
|
179
|
+
(parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:')
|
|
180
|
+
&& parsedUrl.hostname.length > 0
|
|
181
|
+
&& externalUrl.trim() === externalUrl
|
|
182
|
+
) return
|
|
183
|
+
throw new TypeError(
|
|
184
|
+
`SLinkedSystemsList: ${coordinate} должен быть абсолютным HTTP(S) URL без окружающих пробелов или null; получено ${JSON.stringify(externalUrl)}. `
|
|
185
|
+
+ `/ SLinkedSystemsList: ${coordinate} must be an absolute HTTP(S) URL without surrounding whitespace or null; received ${JSON.stringify(externalUrl)}.`,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function validateSyncStatus(value: unknown, index: number): void {
|
|
190
|
+
const coordinate = `items[${index}].syncStatus`
|
|
191
|
+
const status = requireRecord(value, coordinate)
|
|
192
|
+
validateNonEmptyString('SLinkedSystemsList', `${coordinate}.label`, status.label)
|
|
193
|
+
validateExactString(
|
|
194
|
+
'SLinkedSystemsList',
|
|
195
|
+
`${coordinate}.tone`,
|
|
196
|
+
status.tone,
|
|
197
|
+
['neutral', 'info', 'success', 'warn', 'danger'] as const,
|
|
198
|
+
)
|
|
199
|
+
if (status.title !== undefined) {
|
|
200
|
+
validateNonEmptyString('SLinkedSystemsList', `${coordinate}.title`, status.title)
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
status.icon !== undefined
|
|
204
|
+
&& status.icon !== null
|
|
205
|
+
&& (
|
|
206
|
+
Array.isArray(status.icon)
|
|
207
|
+
|| (typeof status.icon !== 'object' && typeof status.icon !== 'function')
|
|
208
|
+
)
|
|
209
|
+
) {
|
|
210
|
+
throw new TypeError(
|
|
211
|
+
`SLinkedSystemsList: ${coordinate}.icon должен быть Vue Component, null или undefined. `
|
|
212
|
+
+ `/ SLinkedSystemsList: ${coordinate}.icon must be a Vue Component, null, or undefined.`,
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const validatedItems = computed<readonly T[]>(() => {
|
|
218
|
+
const candidate: unknown = props.items
|
|
219
|
+
if (!Array.isArray(candidate)) {
|
|
220
|
+
throw new TypeError(
|
|
221
|
+
'SLinkedSystemsList: items должен быть readonly-массивом linked-system объектов. '
|
|
222
|
+
+ '/ SLinkedSystemsList: items must be a readonly array of linked-system objects.',
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
const identities = new Set<string>()
|
|
226
|
+
for (const [index, item] of candidate.entries()) {
|
|
227
|
+
const record = requireRecord(item, `items[${index}]`)
|
|
228
|
+
const identity = validateNonEmptyString('SLinkedSystemsList', `items[${index}].id`, record.id)
|
|
229
|
+
if (identities.has(identity)) {
|
|
230
|
+
throw new TypeError(
|
|
231
|
+
`SLinkedSystemsList: items должен содержать уникальные stable id; дублируется ${JSON.stringify(identity)}. `
|
|
232
|
+
+ `/ SLinkedSystemsList: items must contain unique stable ids; duplicate ${JSON.stringify(identity)}.`,
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
identities.add(identity)
|
|
236
|
+
validateNonEmptyString('SLinkedSystemsList', `items[${index}].providerName`, record.providerName)
|
|
237
|
+
validateNonEmptyString('SLinkedSystemsList', `items[${index}].externalId`, record.externalId)
|
|
238
|
+
validateExternalUrl(record.externalUrl, index)
|
|
239
|
+
validateSyncStatus(record.syncStatus, index)
|
|
240
|
+
}
|
|
241
|
+
return props.items
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
watchEffect(() => {
|
|
245
|
+
void validatedItems.value
|
|
246
|
+
void validatedAccessibleLabel.value
|
|
247
|
+
void validatedActionsDisabled.value
|
|
248
|
+
if (validatedPresentation.value === 'chips' && slots.actions) {
|
|
249
|
+
throw new Error('SLinkedSystemsList: presentation="chips" не принимает actions slot. / Chips presentation does not accept the actions slot.')
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<style lang="postcss" scoped>
|
|
255
|
+
@reference "../../../styles/reference.css";
|
|
256
|
+
|
|
257
|
+
.s-linked-systems-list[data-presentation='chips'] {
|
|
258
|
+
@apply flex min-w-0 flex-wrap items-center gap-1.5;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.s-linked-systems-list__chip {
|
|
262
|
+
@apply inline-flex min-w-0 items-center gap-1.5 rounded-full border border-surface-200 bg-surface-100 px-1.5 py-0.5 text-xs dark:border-surface-700 dark:bg-surface-700/50;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.s-linked-systems-list__external-id {
|
|
266
|
+
@apply min-w-0 font-mono;
|
|
267
|
+
overflow-wrap: anywhere;
|
|
268
|
+
word-break: break-word;
|
|
269
|
+
}
|
|
270
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-message {
|
|
4
|
+
@apply flex min-w-0 items-start p-4 rounded-lg border;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.s-message[data-size='sm'] {
|
|
8
|
+
@apply p-2;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.s-message-icon {
|
|
12
|
+
@apply shrink-0 mr-3;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.s-message[data-size='sm'] .s-message-icon {
|
|
16
|
+
@apply mr-2;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.s-message-icon-graphic {
|
|
20
|
+
width: var(--s-icon-size-sm);
|
|
21
|
+
height: var(--s-icon-size-sm);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.s-message[data-size='sm'] .s-message-icon-graphic {
|
|
25
|
+
width: var(--s-icon-size-xs);
|
|
26
|
+
height: var(--s-icon-size-xs);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.s-message-content {
|
|
30
|
+
@apply min-w-0 flex-grow text-sm;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.s-message[data-size='sm'] .s-message-content {
|
|
34
|
+
@apply text-xs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.s-message-title {
|
|
38
|
+
@apply block font-bold mb-1;
|
|
39
|
+
overflow-wrap: anywhere;
|
|
40
|
+
word-break: break-word;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.s-message-body {
|
|
44
|
+
@apply min-w-0 opacity-90;
|
|
45
|
+
white-space: pre-wrap;
|
|
46
|
+
overflow-wrap: anywhere;
|
|
47
|
+
word-break: break-word;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.s-message-close {
|
|
51
|
+
@apply shrink-0 ml-auto -my-1 -mr-1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Severity: info */
|
|
55
|
+
.s-message[data-severity='info'] {
|
|
56
|
+
@apply bg-info-50 border-info-200 text-info-800;
|
|
57
|
+
@apply dark:bg-info-950 dark:border-info-800 dark:text-info-200;
|
|
58
|
+
}
|
|
59
|
+
.s-message[data-severity='info'] .s-message-icon {
|
|
60
|
+
@apply text-info-500 dark:text-info-400;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Severity: success */
|
|
64
|
+
.s-message[data-severity='success'] {
|
|
65
|
+
@apply bg-success-50 border-success-200 text-success-800;
|
|
66
|
+
@apply dark:bg-success-950 dark:border-success-800 dark:text-success-200;
|
|
67
|
+
}
|
|
68
|
+
.s-message[data-severity='success'] .s-message-icon {
|
|
69
|
+
@apply text-success-500 dark:text-success-400;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Severity: warn */
|
|
73
|
+
.s-message[data-severity='warn'] {
|
|
74
|
+
@apply bg-warn-50 border-warn-200 text-warn-800;
|
|
75
|
+
@apply dark:bg-warn-950 dark:border-warn-800 dark:text-warn-200;
|
|
76
|
+
}
|
|
77
|
+
.s-message[data-severity='warn'] .s-message-icon {
|
|
78
|
+
@apply text-warn-500 dark:text-warn-400;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Severity: danger */
|
|
82
|
+
.s-message[data-severity='danger'] {
|
|
83
|
+
@apply bg-danger-50 border-danger-200 text-danger-800;
|
|
84
|
+
@apply dark:bg-danger-950 dark:border-danger-800 dark:text-danger-200;
|
|
85
|
+
}
|
|
86
|
+
.s-message[data-severity='danger'] .s-message-icon {
|
|
87
|
+
@apply text-danger-500 dark:text-danger-400;
|
|
88
|
+
}
|