@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,224 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
createTextVNode,
|
|
5
|
+
defineComponent,
|
|
6
|
+
h,
|
|
7
|
+
type ComponentPublicInstance,
|
|
8
|
+
type PropType,
|
|
9
|
+
} from 'vue'
|
|
10
|
+
|
|
11
|
+
import { X } from '../../../icons/sputnigUiIcons'
|
|
12
|
+
import type { FieldSurfaceBindings } from '../../../../internal/fieldSurface'
|
|
13
|
+
import type { InlineTokenEditorSegmentContract } from '../../../../internal/inlineTokenEditorContract'
|
|
14
|
+
import type { OwnedAttributeBindings } from '../../../../internal/ownedAttrs'
|
|
15
|
+
import { useInteractiveLeafRegistration } from '../../../../internal/passiveContentContract'
|
|
16
|
+
import { validateBoundedInteger } from '../../../../internal/runtimeContract'
|
|
17
|
+
import SButton, { type Props as SButtonProps } from '../SButton.vue'
|
|
18
|
+
|
|
19
|
+
/** Текст визуального caret helper. / Visual caret-helper text. */
|
|
20
|
+
export const INLINE_TOKEN_EDITOR_VISUAL_CARET_TEXT = '\u200B'
|
|
21
|
+
|
|
22
|
+
/** Действие над одним token occurrence. / Action for one token occurrence. */
|
|
23
|
+
export type InlineTokenAction = 'open' | 'remove'
|
|
24
|
+
|
|
25
|
+
/** Точное событие token action. / Exact token-action event. */
|
|
26
|
+
export interface InlineTokenActionEvent {
|
|
27
|
+
readonly action: InlineTokenAction
|
|
28
|
+
readonly occurrenceId: string
|
|
29
|
+
readonly id: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Ref-контракт native editing surface. / Native editing-surface ref contract. */
|
|
33
|
+
export type InlineTokenElementRef = (
|
|
34
|
+
target: Element | ComponentPublicInstance | null,
|
|
35
|
+
refs: Record<string, unknown>,
|
|
36
|
+
) => void
|
|
37
|
+
|
|
38
|
+
interface InlineTokenActionButtonBindings extends SButtonProps {
|
|
39
|
+
readonly 'aria-label': string
|
|
40
|
+
readonly title?: string
|
|
41
|
+
readonly onClick: (event: MouseEvent) => void
|
|
42
|
+
readonly onKeydown: (event: KeyboardEvent) => void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveSurfaceStyle(
|
|
46
|
+
rowsInput: number,
|
|
47
|
+
maxRowsInput: number,
|
|
48
|
+
appearance: 'default' | 'ghost',
|
|
49
|
+
): Record<string, string> {
|
|
50
|
+
const rows = validateBoundedInteger('SInlineTokenEditor', 'rows', rowsInput, { minimum: 1, maximum: 100 })
|
|
51
|
+
const maxRows = validateBoundedInteger('SInlineTokenEditor', 'maxRows', maxRowsInput, { minimum: 0, maximum: 100 })
|
|
52
|
+
if (maxRows !== 0 && maxRows < rows) {
|
|
53
|
+
throw new RangeError(`SInlineTokenEditor: maxRows должен быть 0 или >= rows (${rows}); получено ${maxRows}. / SInlineTokenEditor: maxRows must be 0 or >= rows (${rows}); received ${maxRows}.`)
|
|
54
|
+
}
|
|
55
|
+
const verticalPaddingRem = appearance === 'ghost' ? 0 : 1
|
|
56
|
+
const lineHeightRem = appearance === 'ghost' ? 0.875 * 1.45 : 0.875 * 1.5
|
|
57
|
+
const style: Record<string, string> = {
|
|
58
|
+
minHeight: `${lineHeightRem * rows + verticalPaddingRem}rem`,
|
|
59
|
+
}
|
|
60
|
+
if (maxRows > 0) {
|
|
61
|
+
style.maxHeight = `${lineHeightRem * maxRows + verticalPaddingRem}rem`
|
|
62
|
+
style.overflowY = 'auto'
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
style.maxHeight = 'none'
|
|
66
|
+
style.overflowY = 'visible'
|
|
67
|
+
}
|
|
68
|
+
return style
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Private leaf-владелец exact contenteditable DOM, ARIA, field surface и token actions.
|
|
73
|
+
* Private leaf owner of the exact contenteditable DOM, ARIA, field surface, and token actions.
|
|
74
|
+
*/
|
|
75
|
+
export default defineComponent({
|
|
76
|
+
name: 'SInlineTokenSurface',
|
|
77
|
+
inheritAttrs: false,
|
|
78
|
+
props: {
|
|
79
|
+
id: { type: String, required: true },
|
|
80
|
+
elementRef: { type: Function as PropType<InlineTokenElementRef>, required: true },
|
|
81
|
+
ownedAttributes: {
|
|
82
|
+
type: Object as PropType<OwnedAttributeBindings<'SInlineTokenEditor'>>,
|
|
83
|
+
required: true,
|
|
84
|
+
},
|
|
85
|
+
fieldBindings: { type: Object as PropType<FieldSurfaceBindings>, required: true },
|
|
86
|
+
segments: {
|
|
87
|
+
type: Array as PropType<readonly InlineTokenEditorSegmentContract[]>,
|
|
88
|
+
required: true,
|
|
89
|
+
},
|
|
90
|
+
trailingCaretHelper: { type: Boolean, required: true },
|
|
91
|
+
openDisabled: { type: Boolean, required: true },
|
|
92
|
+
removeDisabled: { type: Boolean, required: true },
|
|
93
|
+
appearance: { type: String as PropType<'default' | 'ghost'>, required: true },
|
|
94
|
+
invalid: { type: Boolean, required: true },
|
|
95
|
+
disabled: { type: Boolean, required: true },
|
|
96
|
+
readonly: { type: Boolean, required: true },
|
|
97
|
+
required: { type: Boolean, required: true },
|
|
98
|
+
rows: { type: Number, required: true },
|
|
99
|
+
maxRows: { type: Number, required: true },
|
|
100
|
+
placeholder: { type: String, default: undefined },
|
|
101
|
+
placeholderVisible: { type: Boolean, required: true },
|
|
102
|
+
labelledBy: { type: String, default: undefined },
|
|
103
|
+
describedBy: { type: String, default: undefined },
|
|
104
|
+
},
|
|
105
|
+
emits: {
|
|
106
|
+
beforeinput: (_event: InputEvent) => true,
|
|
107
|
+
input: (_event: Event) => true,
|
|
108
|
+
keydown: (_event: KeyboardEvent) => true,
|
|
109
|
+
keyup: (_event: KeyboardEvent) => true,
|
|
110
|
+
click: (_event: MouseEvent) => true,
|
|
111
|
+
paste: (_event: ClipboardEvent) => true,
|
|
112
|
+
tokenAction: (event: InlineTokenActionEvent) => (
|
|
113
|
+
(event.action === 'open' || event.action === 'remove')
|
|
114
|
+
&& typeof event.occurrenceId === 'string'
|
|
115
|
+
&& event.occurrenceId.length > 0
|
|
116
|
+
&& typeof event.id === 'string'
|
|
117
|
+
&& event.id.length > 0
|
|
118
|
+
),
|
|
119
|
+
},
|
|
120
|
+
setup(props, { emit }) {
|
|
121
|
+
useInteractiveLeafRegistration({
|
|
122
|
+
owner: 'SInlineTokenEditor',
|
|
123
|
+
active: () => !props.disabled,
|
|
124
|
+
})
|
|
125
|
+
const surfaceStyle = computed(() => resolveSurfaceStyle(props.rows, props.maxRows, props.appearance))
|
|
126
|
+
const stopKeyPropagation = (event: KeyboardEvent): void => event.stopPropagation()
|
|
127
|
+
const emitTokenAction = (
|
|
128
|
+
event: MouseEvent,
|
|
129
|
+
action: InlineTokenAction,
|
|
130
|
+
segment: Extract<InlineTokenEditorSegmentContract, { kind: 'token' }>,
|
|
131
|
+
): void => {
|
|
132
|
+
event.stopPropagation()
|
|
133
|
+
emit('tokenAction', { action, occurrenceId: segment.occurrenceId, id: segment.id })
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const renderTokenAction = (
|
|
137
|
+
action: InlineTokenAction,
|
|
138
|
+
segment: Extract<InlineTokenEditorSegmentContract, { kind: 'token' }>,
|
|
139
|
+
) => {
|
|
140
|
+
const bindings: InlineTokenActionButtonBindings = action === 'open'
|
|
141
|
+
? {
|
|
142
|
+
label: segment.label,
|
|
143
|
+
'aria-label': segment.title ? `Открыть ${segment.title}` : `Открыть ${segment.label}`,
|
|
144
|
+
...(segment.title === undefined ? {} : { title: segment.title }),
|
|
145
|
+
size: 'xs',
|
|
146
|
+
width: 'fit',
|
|
147
|
+
appearance: 'text',
|
|
148
|
+
shape: 'pill',
|
|
149
|
+
disabled: props.openDisabled,
|
|
150
|
+
onClick: (event) => emitTokenAction(event, action, segment),
|
|
151
|
+
onKeydown: stopKeyPropagation,
|
|
152
|
+
}
|
|
153
|
+
: {
|
|
154
|
+
leadingIcon: X,
|
|
155
|
+
'aria-label': `Убрать ${segment.label}`,
|
|
156
|
+
size: 'xs',
|
|
157
|
+
appearance: 'ghost',
|
|
158
|
+
shape: 'circle',
|
|
159
|
+
iconOnly: true,
|
|
160
|
+
disabled: props.removeDisabled,
|
|
161
|
+
onClick: (event) => emitTokenAction(event, action, segment),
|
|
162
|
+
onKeydown: stopKeyPropagation,
|
|
163
|
+
}
|
|
164
|
+
return h(SButton, { ...bindings, key: action })
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return () => h('div', {
|
|
168
|
+
...props.ownedAttributes,
|
|
169
|
+
...props.fieldBindings,
|
|
170
|
+
id: props.id,
|
|
171
|
+
ref: props.elementRef,
|
|
172
|
+
class: 's-inline-token-editor',
|
|
173
|
+
'data-appearance': props.appearance,
|
|
174
|
+
'data-invalid': props.invalid || undefined,
|
|
175
|
+
'data-disabled': props.disabled || undefined,
|
|
176
|
+
'data-readonly': props.readonly || undefined,
|
|
177
|
+
'data-placeholder': props.placeholder,
|
|
178
|
+
'data-placeholder-visible': props.placeholderVisible ? 'true' : 'false',
|
|
179
|
+
'data-component': 's-inline-token-editor',
|
|
180
|
+
style: surfaceStyle.value,
|
|
181
|
+
role: 'textbox',
|
|
182
|
+
'aria-multiline': 'true',
|
|
183
|
+
contenteditable: props.disabled || props.readonly ? 'false' : 'true',
|
|
184
|
+
tabindex: props.disabled ? -1 : 0,
|
|
185
|
+
'aria-disabled': props.disabled || undefined,
|
|
186
|
+
'aria-invalid': props.invalid || undefined,
|
|
187
|
+
'aria-required': props.required || undefined,
|
|
188
|
+
'aria-readonly': props.readonly || undefined,
|
|
189
|
+
'aria-labelledby': props.labelledBy,
|
|
190
|
+
'aria-describedby': props.describedBy,
|
|
191
|
+
onBeforeinput: (event: InputEvent) => emit('beforeinput', event),
|
|
192
|
+
onInput: (event: Event) => emit('input', event),
|
|
193
|
+
onKeydown: (event: KeyboardEvent) => emit('keydown', event),
|
|
194
|
+
onKeyup: (event: KeyboardEvent) => emit('keyup', event),
|
|
195
|
+
onClick: (event: MouseEvent) => emit('click', event),
|
|
196
|
+
onPaste: (event: ClipboardEvent) => emit('paste', event),
|
|
197
|
+
}, [
|
|
198
|
+
...props.segments.map((segment) => (
|
|
199
|
+
segment.kind === 'text'
|
|
200
|
+
? createTextVNode(segment.text)
|
|
201
|
+
: h('span', {
|
|
202
|
+
key: `token:${segment.occurrenceId}`,
|
|
203
|
+
class: 's-inline-token-editor__token',
|
|
204
|
+
'data-testid': 's-inline-token-editor-token',
|
|
205
|
+
'data-segment-kind': 'token',
|
|
206
|
+
'data-segment-occurrence-id': segment.occurrenceId,
|
|
207
|
+
'data-segment-id': segment.id,
|
|
208
|
+
'data-segment-label': segment.label,
|
|
209
|
+
'data-segment-title': segment.title,
|
|
210
|
+
contenteditable: 'false',
|
|
211
|
+
}, [
|
|
212
|
+
renderTokenAction('open', segment),
|
|
213
|
+
renderTokenAction('remove', segment),
|
|
214
|
+
])
|
|
215
|
+
)),
|
|
216
|
+
...(props.trailingCaretHelper
|
|
217
|
+
? [createTextVNode(INLINE_TOKEN_EDITOR_VISUAL_CARET_TEXT)]
|
|
218
|
+
: []),
|
|
219
|
+
])
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
</script>
|
|
223
|
+
|
|
224
|
+
<style lang="postcss" src="../SInlineTokenEditor.css" scoped></style>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { Component } from 'vue'
|
|
2
|
+
import { resolveLinkTarget } from '../../../internal/linkTarget'
|
|
3
|
+
import {
|
|
4
|
+
validateBoolean,
|
|
5
|
+
validateExactString,
|
|
6
|
+
validateNonEmptyString,
|
|
7
|
+
} from '../../../internal/runtimeContract'
|
|
8
|
+
import type { BadgeSeverity } from '../data-display/SBadge.vue'
|
|
9
|
+
import type { LinkDestination } from './SLink'
|
|
10
|
+
|
|
11
|
+
export type SMenuItemLayout = 'row' | 'tile'
|
|
12
|
+
export type SMenuCheckedState = boolean | 'mixed'
|
|
13
|
+
export type SMenuActivationEvent = MouseEvent | KeyboardEvent
|
|
14
|
+
|
|
15
|
+
interface SMenuLabelledEntry {
|
|
16
|
+
readonly id: string
|
|
17
|
+
readonly label: string
|
|
18
|
+
readonly icon?: Component
|
|
19
|
+
readonly disabled?: boolean
|
|
20
|
+
readonly shortcut?: string
|
|
21
|
+
readonly badge?: string | number
|
|
22
|
+
readonly badgeSeverity?: BadgeSeverity
|
|
23
|
+
readonly layout?: SMenuItemLayout
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Команда без собственной navigation-state semantics. / A command without navigation-state semantics. */
|
|
27
|
+
export interface SMenuAction extends SMenuLabelledEntry {
|
|
28
|
+
readonly kind: 'action'
|
|
29
|
+
readonly current?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Навигационное назначение с ровно одним href/to target. / A navigation destination with exactly one href/to target. */
|
|
33
|
+
export type SMenuDestination = SMenuLabelledEntry & LinkDestination & {
|
|
34
|
+
readonly kind: 'destination'
|
|
35
|
+
readonly current?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Управляемый checkbox intent; parent обновляет checked-state. / A controlled checkbox intent; the parent updates checked state. */
|
|
39
|
+
export interface SMenuCheckbox extends SMenuLabelledEntry {
|
|
40
|
+
readonly kind: 'checkbox'
|
|
41
|
+
readonly checked: SMenuCheckedState
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Управляемый radio intent внутри стабильной groupId. / A controlled radio intent within a stable groupId. */
|
|
45
|
+
export interface SMenuRadio extends SMenuLabelledEntry {
|
|
46
|
+
readonly kind: 'radio'
|
|
47
|
+
readonly groupId: string
|
|
48
|
+
readonly checked: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Ветка вложенного меню; сама не является доменной командой. / A nested-menu branch; it is not itself a domain command. */
|
|
52
|
+
export interface SMenuSubmenu extends SMenuLabelledEntry {
|
|
53
|
+
readonly kind: 'submenu'
|
|
54
|
+
readonly children: readonly SMenuEntry[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SMenuSeparatorEntry {
|
|
58
|
+
readonly kind: 'separator'
|
|
59
|
+
readonly id: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type SMenuSelectableEntry = SMenuAction | SMenuDestination | SMenuCheckbox | SMenuRadio
|
|
63
|
+
export type SMenuInteractiveEntry = SMenuSelectableEntry | SMenuSubmenu
|
|
64
|
+
export type SMenuEntry = SMenuInteractiveEntry | SMenuSeparatorEntry
|
|
65
|
+
|
|
66
|
+
const ENTRY_KINDS = ['action', 'destination', 'checkbox', 'radio', 'separator', 'submenu'] as const
|
|
67
|
+
const LAYOUTS = ['row', 'tile'] as const
|
|
68
|
+
const BADGE_SEVERITIES = ['secondary', 'success', 'info', 'warn', 'danger', 'contrast'] as const
|
|
69
|
+
|
|
70
|
+
const COMMON_KEYS = ['kind', 'id', 'label', 'icon', 'disabled', 'shortcut', 'badge', 'badgeSeverity', 'layout'] as const
|
|
71
|
+
const KIND_KEYS = {
|
|
72
|
+
action: ['current'],
|
|
73
|
+
destination: ['href', 'to', 'target', 'rel', 'current'],
|
|
74
|
+
checkbox: ['checked'],
|
|
75
|
+
radio: ['groupId', 'checked'],
|
|
76
|
+
separator: [],
|
|
77
|
+
submenu: ['children'],
|
|
78
|
+
} as const
|
|
79
|
+
|
|
80
|
+
function assertRecord(value: unknown, coordinate: string): asserts value is Record<string, unknown> {
|
|
81
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) return
|
|
82
|
+
throw new TypeError(`SMenuSurface: ${coordinate} must be an object`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function validateOptionalBoolean(entry: Record<string, unknown>, key: string, coordinate: string): void {
|
|
86
|
+
if (entry[key] !== undefined) validateBoolean('SMenuSurface', `${coordinate}.${key}`, entry[key])
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function validateOptionalString(entry: Record<string, unknown>, key: string, coordinate: string): void {
|
|
90
|
+
if (entry[key] !== undefined) validateNonEmptyString('SMenuSurface', `${coordinate}.${key}`, entry[key])
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function validateCommon(entry: Record<string, unknown>, coordinate: string): void {
|
|
94
|
+
validateNonEmptyString('SMenuSurface', `${coordinate}.id`, entry.id)
|
|
95
|
+
validateNonEmptyString('SMenuSurface', `${coordinate}.label`, entry.label)
|
|
96
|
+
validateOptionalBoolean(entry, 'disabled', coordinate)
|
|
97
|
+
validateOptionalString(entry, 'shortcut', coordinate)
|
|
98
|
+
if (entry.icon !== undefined && typeof entry.icon !== 'object' && typeof entry.icon !== 'function') {
|
|
99
|
+
throw new TypeError(`SMenuSurface: ${coordinate}.icon must be a Vue component`)
|
|
100
|
+
}
|
|
101
|
+
if (entry.badge !== undefined && (
|
|
102
|
+
(typeof entry.badge !== 'string' || entry.badge.length === 0)
|
|
103
|
+
&& (typeof entry.badge !== 'number' || !Number.isFinite(entry.badge))
|
|
104
|
+
)) throw new TypeError(`SMenuSurface: ${coordinate}.badge must be a non-empty string or finite number`)
|
|
105
|
+
if (entry.badgeSeverity !== undefined) {
|
|
106
|
+
validateExactString('SMenuSurface', `${coordinate}.badgeSeverity`, entry.badgeSeverity, BADGE_SEVERITIES)
|
|
107
|
+
}
|
|
108
|
+
if (entry.layout !== undefined) validateExactString('SMenuSurface', `${coordinate}.layout`, entry.layout, LAYOUTS)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function assertExactKeys(entry: Record<string, unknown>, kind: typeof ENTRY_KINDS[number], coordinate: string): void {
|
|
112
|
+
const allowed = new Set<string>(kind === 'separator'
|
|
113
|
+
? ['kind', 'id']
|
|
114
|
+
: [...COMMON_KEYS, ...KIND_KEYS[kind]])
|
|
115
|
+
for (const key of Object.keys(entry)) {
|
|
116
|
+
if (!allowed.has(key)) throw new TypeError(`SMenuSurface: ${coordinate} has unsupported field '${key}'`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function validateList(
|
|
121
|
+
value: unknown,
|
|
122
|
+
coordinate: string,
|
|
123
|
+
ids: Set<string>,
|
|
124
|
+
): asserts value is readonly SMenuEntry[] {
|
|
125
|
+
if (!Array.isArray(value)) throw new TypeError(`SMenuSurface: ${coordinate} must be an array`)
|
|
126
|
+
if (coordinate !== 'items' && value.length === 0) {
|
|
127
|
+
throw new TypeError(`SMenuSurface: ${coordinate} children must be a non-empty array`)
|
|
128
|
+
}
|
|
129
|
+
const checkedRadioGroups = new Set<string>()
|
|
130
|
+
let previousSeparator = false
|
|
131
|
+
for (const [index, candidate] of value.entries()) {
|
|
132
|
+
const entryCoordinate = `${coordinate}[${index}]`
|
|
133
|
+
assertRecord(candidate, entryCoordinate)
|
|
134
|
+
const kind = validateExactString('SMenuSurface', `${entryCoordinate}.kind`, candidate.kind, ENTRY_KINDS)
|
|
135
|
+
assertExactKeys(candidate, kind, entryCoordinate)
|
|
136
|
+
const id = validateNonEmptyString('SMenuSurface', `${entryCoordinate}.id`, candidate.id)
|
|
137
|
+
if (ids.has(id)) throw new TypeError(`SMenuSurface: duplicate entry id '${id}'`)
|
|
138
|
+
ids.add(id)
|
|
139
|
+
if (kind === 'separator') {
|
|
140
|
+
if (index === 0 || index === value.length - 1 || previousSeparator) {
|
|
141
|
+
throw new TypeError('SMenuSurface: separator cannot be first, last, or consecutive')
|
|
142
|
+
}
|
|
143
|
+
previousSeparator = true
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
previousSeparator = false
|
|
147
|
+
validateCommon(candidate, entryCoordinate)
|
|
148
|
+
if (kind === 'action' || kind === 'destination') validateOptionalBoolean(candidate, 'current', entryCoordinate)
|
|
149
|
+
if (kind === 'destination') {
|
|
150
|
+
resolveLinkTarget({
|
|
151
|
+
owner: `SMenuSurface: ${entryCoordinate}`,
|
|
152
|
+
href: candidate.href,
|
|
153
|
+
to: candidate.to,
|
|
154
|
+
target: candidate.target,
|
|
155
|
+
rel: candidate.rel,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
if (kind === 'checkbox') {
|
|
159
|
+
if (candidate.checked !== 'mixed' && typeof candidate.checked !== 'boolean') {
|
|
160
|
+
throw new TypeError(`SMenuSurface: ${entryCoordinate}.checked must be a boolean or "mixed"`)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (kind === 'radio') {
|
|
164
|
+
const groupId = validateNonEmptyString('SMenuSurface', `${entryCoordinate}.groupId`, candidate.groupId)
|
|
165
|
+
const checked = validateBoolean('SMenuSurface', `${entryCoordinate}.checked`, candidate.checked)
|
|
166
|
+
if (checked && checkedRadioGroups.has(groupId)) {
|
|
167
|
+
throw new TypeError(`SMenuSurface: radio group '${groupId}' has more than one checked entry`)
|
|
168
|
+
}
|
|
169
|
+
if (checked) checkedRadioGroups.add(groupId)
|
|
170
|
+
}
|
|
171
|
+
if (kind === 'submenu') validateList(candidate.children, `${entryCoordinate}.children`, ids)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Проверяет полный recursive discriminated menu contract без coercion/fallback. / Validates the complete recursive discriminated menu contract without coercion/fallback. */
|
|
176
|
+
export function validateSMenuEntries(value: unknown): asserts value is readonly SMenuEntry[] {
|
|
177
|
+
validateList(value, 'items', new Set<string>())
|
|
178
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-action-card { @apply grid min-h-0 min-w-0 items-start gap-2; grid-template-columns: minmax(0, 1fr) auto; }
|
|
4
|
+
.s-action-card[data-fill='container'] { @apply h-full w-full; }
|
|
5
|
+
.s-action-card__content { @apply flex min-h-0 min-w-0 w-full flex-1 flex-col; }
|
|
6
|
+
.s-action-card__actions { @apply flex shrink-0 items-center gap-1; }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { InteractiveSurfaceContract, InteractiveSurfaceDensity, InteractiveSurfaceFill, InteractiveSurfacePadding, InteractiveSurfaceRadius, InteractiveSurfaceTone } from '../controls/SInteractiveSurface.vue'
|
|
3
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
4
|
+
import SInteractiveSurface from '../controls/SInteractiveSurface.vue'
|
|
5
|
+
|
|
6
|
+
defineOptions({ inheritAttrs: false })
|
|
7
|
+
|
|
8
|
+
export interface Props {
|
|
9
|
+
surface: InteractiveSurfaceContract
|
|
10
|
+
selected?: boolean
|
|
11
|
+
tone?: InteractiveSurfaceTone
|
|
12
|
+
density?: InteractiveSurfaceDensity
|
|
13
|
+
padding?: InteractiveSurfacePadding
|
|
14
|
+
radius?: InteractiveSurfaceRadius
|
|
15
|
+
fill?: InteractiveSurfaceFill
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
withDefaults(defineProps<Props>(), {
|
|
19
|
+
selected: false,
|
|
20
|
+
tone: 'default',
|
|
21
|
+
density: 'comfortable',
|
|
22
|
+
padding: 'md',
|
|
23
|
+
radius: 'lg',
|
|
24
|
+
fill: 'content',
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{ activate: [event: MouseEvent] }>()
|
|
28
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SActionCard', owner: 'action card root' })
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div v-bind="ownedAttrs.bindings()" class="s-action-card" :data-has-actions="Boolean($slots.actions) || undefined" :data-fill="fill" :data-mode="surface.mode">
|
|
33
|
+
<SInteractiveSurface
|
|
34
|
+
:surface="surface"
|
|
35
|
+
:selected="selected"
|
|
36
|
+
:tone="tone"
|
|
37
|
+
:density="density"
|
|
38
|
+
:padding="padding"
|
|
39
|
+
:radius="radius"
|
|
40
|
+
:fill="fill"
|
|
41
|
+
@activate="emit('activate', $event)"
|
|
42
|
+
>
|
|
43
|
+
<div class="s-action-card__content"><slot /></div>
|
|
44
|
+
</SInteractiveSurface>
|
|
45
|
+
<div v-if="$slots.actions" class="s-action-card__actions"><slot name="actions" /></div>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<style lang="postcss" src="./SActionCard.css" scoped></style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-action-list {
|
|
4
|
+
@apply grid min-w-0 gap-1 overflow-y-auto rounded-md border border-surface-200 p-1 dark:border-surface-700;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
overscroll-behavior: contain;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.s-action-list[data-surface='plain'] {
|
|
10
|
+
@apply rounded-none border-0 p-0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.s-action-list[data-max-height='sm'] { max-block-size: var(--s-scroll-viewport-max-block-size-sm); }
|
|
14
|
+
.s-action-list[data-max-height='md'] { max-block-size: var(--s-scroll-viewport-max-block-size-md); }
|
|
15
|
+
.s-action-list[data-max-height='lg'] { max-block-size: var(--s-scroll-viewport-max-block-size-lg); }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
4
|
+
import {
|
|
5
|
+
validateScrollableViewportMaxBlockSize,
|
|
6
|
+
type ScrollableViewportMaxBlockSize,
|
|
7
|
+
} from '../../../internal/semanticSizing'
|
|
8
|
+
|
|
9
|
+
defineOptions({ inheritAttrs: false })
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
accessibleLabel: string
|
|
13
|
+
maxHeight?: ScrollableViewportMaxBlockSize
|
|
14
|
+
surface?: 'bordered' | 'plain'
|
|
15
|
+
}>(), {
|
|
16
|
+
maxHeight: 'none',
|
|
17
|
+
surface: 'bordered',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SActionList', owner: 'action list root' })
|
|
21
|
+
const validatedMaxHeight = computed(() => validateScrollableViewportMaxBlockSize(
|
|
22
|
+
'SActionList',
|
|
23
|
+
'maxHeight',
|
|
24
|
+
props.maxHeight,
|
|
25
|
+
))
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div v-bind="ownedAttrs.bindings()" class="s-action-list" :data-max-height="validatedMaxHeight" :data-surface="surface" role="list" :aria-label="accessibleLabel">
|
|
30
|
+
<slot />
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style lang="postcss" src="./SActionList.css" scoped></style>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-action-list-item {
|
|
4
|
+
@apply grid min-h-12 min-w-0 items-center gap-2;
|
|
5
|
+
height: max-content;
|
|
6
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.s-action-list-item[data-height-mode='fill'] {
|
|
10
|
+
@apply h-full overflow-hidden;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.s-action-list-item[data-height-mode='fill'] .s-action-list-item__main--static {
|
|
14
|
+
@apply h-full min-h-0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.s-action-list-item__main {
|
|
18
|
+
@apply flex min-h-11 min-w-0 flex-1 items-center gap-3 rounded-md px-3 py-2 text-left;
|
|
19
|
+
padding-left: calc(0.75rem + var(--s-action-list-item-indent));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.s-action-list-item__main--static {
|
|
23
|
+
@apply cursor-default;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.s-action-list-item__icon {
|
|
27
|
+
@apply grid h-9 w-9 shrink-0 place-items-center rounded-md bg-primary-100 text-primary-700;
|
|
28
|
+
@apply dark:bg-primary-900 dark:text-primary-200;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.s-action-list-item[data-tone='success'] .s-action-list-item__icon {
|
|
32
|
+
@apply bg-success-100 text-success-700 dark:bg-success-900 dark:text-success-200;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.s-action-list-item[data-tone='danger'] .s-action-list-item__icon {
|
|
36
|
+
@apply bg-danger-100 text-danger-700 dark:bg-danger-900 dark:text-danger-200;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.s-action-list-item__icon svg {
|
|
40
|
+
@apply h-5 w-5;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.s-action-list-item__copy {
|
|
44
|
+
@apply grid min-w-0 flex-1 gap-0.5;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.s-action-list-item__title,
|
|
48
|
+
.s-action-list-item__description,
|
|
49
|
+
.s-action-list-item__detail {
|
|
50
|
+
@apply truncate;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.s-action-list-item__title {
|
|
54
|
+
@apply text-sm font-semibold text-surface-900 dark:text-surface-50;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.s-action-list-item[data-title-weight='regular'] .s-action-list-item__title {
|
|
58
|
+
@apply font-normal;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.s-action-list-item[data-title-presentation='code'] .s-action-list-item__title {
|
|
62
|
+
@apply font-mono;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.s-action-list-item__description {
|
|
66
|
+
@apply text-xs text-surface-600 dark:text-surface-400;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.s-action-list-item__detail {
|
|
70
|
+
@apply line-clamp-2 whitespace-normal text-surface-600 dark:text-surface-300;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.s-action-list-item[data-detail-overflow='wrap'] .s-action-list-item__detail {
|
|
74
|
+
@apply line-clamp-none;
|
|
75
|
+
overflow-wrap: anywhere;
|
|
76
|
+
word-break: break-word;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.s-action-list-item[data-detail-presentation='code'] .s-action-list-item__detail {
|
|
80
|
+
@apply rounded-md bg-surface-100 p-2 font-mono text-xs dark:bg-surface-800;
|
|
81
|
+
white-space: pre-wrap;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.s-action-list-item__trailing {
|
|
85
|
+
@apply flex shrink-0 items-center gap-2 pr-2;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.s-action-list-item__trailing-icon {
|
|
89
|
+
@apply h-4 w-4 shrink-0 text-surface-400;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.s-action-list-item[data-density='compact'] { @apply min-h-10 gap-1; }
|
|
93
|
+
.s-action-list-item[data-density='compact'] .s-action-list-item__main { @apply min-h-9 gap-2 px-2 py-1.5; }
|
|
94
|
+
.s-action-list-item[data-density='compact'] .s-action-list-item__icon { @apply h-7 w-7; }
|
|
95
|
+
|
|
96
|
+
.s-action-list-item[data-trailing-layout='stacked'] {
|
|
97
|
+
grid-template-columns: minmax(0, 1fr);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.s-action-list-item[data-trailing-layout='stacked'] .s-action-list-item__trailing {
|
|
101
|
+
@apply w-full justify-end pb-2 pr-2;
|
|
102
|
+
padding-left: calc(3.5rem + var(--s-action-list-item-indent));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@container (max-width: 32rem) {
|
|
106
|
+
.s-action-list-item[data-trailing-layout='responsive'] {
|
|
107
|
+
grid-template-columns: minmax(0, 1fr);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.s-action-list-item[data-trailing-layout='responsive'] .s-action-list-item__trailing {
|
|
111
|
+
@apply w-full justify-end pb-2 pr-2;
|
|
112
|
+
padding-left: calc(3.5rem + var(--s-action-list-item-indent));
|
|
113
|
+
}
|
|
114
|
+
}
|