@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,178 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-code-editor {
|
|
4
|
+
@apply min-h-0 min-w-0;
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.s-code-editor[data-viewport='content'] {
|
|
11
|
+
max-height: var(--s-code-editor-viewport-content-max);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.s-code-editor[data-viewport='compact'] {
|
|
15
|
+
height: var(--s-code-editor-viewport-compact);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.s-code-editor[data-viewport='standard'] {
|
|
19
|
+
height: var(--s-code-editor-viewport-standard);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.s-code-editor[data-viewport='expanded'] {
|
|
23
|
+
height: var(--s-code-editor-viewport-expanded);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.s-code-editor[data-viewport='fill'] {
|
|
27
|
+
height: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.s-code-editor :deep(.cm-editor) {
|
|
31
|
+
@apply min-w-0 text-sm;
|
|
32
|
+
width: 100%;
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
height: inherit;
|
|
35
|
+
max-height: inherit;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.s-code-editor :deep(.cm-scroller) {
|
|
39
|
+
max-width: 100%;
|
|
40
|
+
overflow: auto;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.s-code-editor[data-line-numbers='hidden'] :deep(.cm-gutters) {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.s-code-editor[data-line-numbers='hidden'] :deep(.cm-content) {
|
|
48
|
+
padding-left: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.s-code-editor[data-variant='code'] :deep(.cm-scroller) {
|
|
52
|
+
@apply overflow-x-auto p-3;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.s-code-editor[data-variant='code'] :deep(.cm-lineNumbers) {
|
|
56
|
+
@apply px-1.5;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.s-code-editor[data-variant='sql'] :deep(.cm-line) {
|
|
60
|
+
padding-inline: 0.75rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.s-code-editor[data-variant='sql'] :deep(.cm-content) {
|
|
64
|
+
min-height: 12rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.s-code-editor[data-variant='sql']:not([data-viewport='content']) :deep(.cm-content) {
|
|
68
|
+
min-height: 100%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.s-code-editor :deep(.cm-gutters) {
|
|
72
|
+
@apply text-surface-400 dark:text-surface-500;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.s-code-editor :deep(.cm-tooltip) {
|
|
76
|
+
@apply rounded-md border border-surface-200 bg-surface-0 text-surface-900 shadow-lg;
|
|
77
|
+
@apply dark:border-surface-700 dark:bg-surface-800 dark:text-surface-100;
|
|
78
|
+
z-index: var(--s-layer-floating);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.s-code-editor :deep(.cm-panels) {
|
|
82
|
+
position: absolute;
|
|
83
|
+
inset-inline: 0;
|
|
84
|
+
bottom: 0;
|
|
85
|
+
z-index: var(--s-layer-affordance);
|
|
86
|
+
display: flex;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
padding: 0 1rem 1rem;
|
|
89
|
+
border: 0;
|
|
90
|
+
background: transparent;
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.s-code-editor :deep(.cm-panel[class~='s-codemirror-search-panel-host']) {
|
|
95
|
+
width: min(34rem, calc(100vw - 2rem));
|
|
96
|
+
max-width: calc(100vw - 2rem);
|
|
97
|
+
margin: 0;
|
|
98
|
+
padding: 0;
|
|
99
|
+
border: 0;
|
|
100
|
+
background: transparent;
|
|
101
|
+
pointer-events: auto;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.s-code-editor :deep(.cm-searchMatch) {
|
|
105
|
+
background-color: var(--s-code-editor-search-match-background);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.s-code-editor :deep(.cm-searchMatch.cm-searchMatch-selected) {
|
|
109
|
+
background-color: var(--s-code-editor-search-match-selected-background);
|
|
110
|
+
outline: 1px solid var(--s-code-editor-search-match-selected-outline);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.s-code-editor :deep(.cm-tooltip-hover),
|
|
114
|
+
.s-code-editor :deep(.cm-completionInfo),
|
|
115
|
+
.s-code-editor :deep(.cm-tooltip-markdown) {
|
|
116
|
+
@apply rounded-xl border border-surface-200 bg-surface-0 text-sm text-surface-900 shadow-xl;
|
|
117
|
+
@apply dark:border-surface-700 dark:bg-surface-900 dark:text-surface-50;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.s-code-editor :deep(.cm-tooltip-autocomplete) {
|
|
121
|
+
@apply min-w-72 rounded-md border border-surface-200 bg-surface-50 text-surface-900 shadow-xl;
|
|
122
|
+
@apply dark:border-surface-700 dark:bg-surface-800 dark:text-surface-50;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.s-code-editor :deep(.cm-tooltip-autocomplete > ul) {
|
|
126
|
+
@apply m-0 max-h-72 list-none overflow-y-auto p-1 font-sans text-sm;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.s-code-editor :deep(.cm-tooltip-autocomplete > ul > li) {
|
|
130
|
+
@apply flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-surface-700 dark:text-surface-300;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.s-code-editor :deep(.cm-tooltip-autocomplete > ul > li:hover) {
|
|
134
|
+
@apply bg-surface-200 dark:bg-surface-700;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.s-code-editor :deep(.cm-tooltip-autocomplete > ul > li[aria-selected]) {
|
|
138
|
+
@apply bg-primary-500 text-white;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.s-code-editor :deep(.cm-completionLabel) {
|
|
142
|
+
@apply truncate font-medium;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.s-code-editor :deep(.cm-completionDetail) {
|
|
146
|
+
@apply ml-auto truncate text-xs italic opacity-70;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.s-code-editor :deep(.cm-completionInfo),
|
|
150
|
+
.s-code-editor :deep(.cm-tooltip-markdown__content) {
|
|
151
|
+
max-height: min(26rem, calc(100vh - 3rem));
|
|
152
|
+
overflow: auto;
|
|
153
|
+
overscroll-behavior: contain;
|
|
154
|
+
padding: 0.75rem 0.875rem;
|
|
155
|
+
line-height: 1.45;
|
|
156
|
+
white-space: normal;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.s-code-editor :deep(.cm-tooltip-markdown) {
|
|
160
|
+
max-width: min(34rem, calc(100vw - 2rem));
|
|
161
|
+
max-height: min(30rem, calc(100vh - 3rem));
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
line-height: 1.45;
|
|
164
|
+
white-space: normal;
|
|
165
|
+
word-break: break-word;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@media (max-width: 768px) {
|
|
169
|
+
.s-code-editor :deep(.cm-panels) {
|
|
170
|
+
padding-inline: 0.75rem;
|
|
171
|
+
padding-bottom: 0.75rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.s-code-editor :deep(.cm-panel[class~='s-codemirror-search-panel-host']) {
|
|
175
|
+
width: calc(100vw - 1.5rem);
|
|
176
|
+
max-width: calc(100vw - 1.5rem);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Codemirror
|
|
3
|
+
v-bind="ownedAttrs.bindings()"
|
|
4
|
+
class="s-code-editor"
|
|
5
|
+
:data-variant="validatedVariant"
|
|
6
|
+
:data-viewport="validatedViewport"
|
|
7
|
+
:data-line-numbers="resolvedCapabilities.lineNumbers ? 'visible' : 'hidden'"
|
|
8
|
+
:model-value="validatedModelValue"
|
|
9
|
+
:autofocus="validatedAutofocus"
|
|
10
|
+
:indent-with-tab="resolvedCapabilities.indentation.keyBehavior === 'indent'"
|
|
11
|
+
:tab-size="resolvedCapabilities.indentation.tabSize"
|
|
12
|
+
:extensions="resolvedExtensions"
|
|
13
|
+
@ready="onReady"
|
|
14
|
+
@update:model-value="onUpdate"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { computed, ref, watch } from 'vue'
|
|
20
|
+
import { Codemirror } from 'vue-codemirror'
|
|
21
|
+
import { oneDark } from '@codemirror/theme-one-dark'
|
|
22
|
+
import { EditorSelection, EditorState, type Extension } from '@codemirror/state'
|
|
23
|
+
import { EditorView, lineNumbers, placeholder as codeMirrorPlaceholder } from '@codemirror/view'
|
|
24
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
25
|
+
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
26
|
+
import { useRuntimeTheme } from '../../../internal/runtimeTheme'
|
|
27
|
+
import {
|
|
28
|
+
validateBoolean,
|
|
29
|
+
validateBoundedInteger,
|
|
30
|
+
validateDomId,
|
|
31
|
+
validateNonEmptyString,
|
|
32
|
+
} from '../../../internal/runtimeContract'
|
|
33
|
+
import { useTrustedCodeEditorExtensions } from '../../../internal/codeEditorExtensions'
|
|
34
|
+
import {
|
|
35
|
+
CODE_EDITOR_VARIANTS,
|
|
36
|
+
CODE_EDITOR_VIEWPORTS,
|
|
37
|
+
type CodeEditorCapabilitiesContract,
|
|
38
|
+
type CodeEditorLanguageContract,
|
|
39
|
+
validateCodeEditorCapabilities,
|
|
40
|
+
validateCodeEditorLanguage,
|
|
41
|
+
validateCodeEditorVariant,
|
|
42
|
+
validateCodeEditorViewport,
|
|
43
|
+
} from '../../../internal/codeEditorContract'
|
|
44
|
+
import { getCodeMirrorLanguageExtension, resolveCodeLanguageKey } from '../codeLanguages'
|
|
45
|
+
|
|
46
|
+
defineOptions({ inheritAttrs: false })
|
|
47
|
+
|
|
48
|
+
export type SCodeEditorViewport = typeof CODE_EDITOR_VIEWPORTS[number]
|
|
49
|
+
export type SCodeEditorVariant = typeof CODE_EDITOR_VARIANTS[number]
|
|
50
|
+
/** Явный язык либо детектирование из обязательного filePath. / Explicit language or detection from a required filePath. */
|
|
51
|
+
export type SCodeEditorLanguage = CodeEditorLanguageContract
|
|
52
|
+
/** Явные возможности leaf-редактора. / Explicit capabilities of the leaf editor. */
|
|
53
|
+
export type SCodeEditorCapabilities = CodeEditorCapabilitiesContract
|
|
54
|
+
|
|
55
|
+
/** Exact-координаты документа для cursor activity. / Exact document coordinates emitted for cursor activity. */
|
|
56
|
+
export interface SCodeEditorCursorActivity {
|
|
57
|
+
pos: number
|
|
58
|
+
line: number
|
|
59
|
+
column: number
|
|
60
|
+
lineFrom: number
|
|
61
|
+
lineTo: number
|
|
62
|
+
lineText: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Public readiness snapshot без раскрытия third-party EditorView. / Public readiness snapshot without exposing the third-party EditorView instance. */
|
|
66
|
+
export interface SCodeEditorReadyPayload {
|
|
67
|
+
documentLength: number
|
|
68
|
+
viewport: SCodeEditorViewport
|
|
69
|
+
variant: SCodeEditorVariant
|
|
70
|
+
locked: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Подтверждённое применение selection и источник вызова. / Confirmed selection application and its initiating API. */
|
|
74
|
+
export interface SCodeEditorSelectionAppliedPayload {
|
|
75
|
+
position: number
|
|
76
|
+
source: 'controlled' | 'imperative'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type AccessibleName =
|
|
80
|
+
| { ariaLabel: string; ariaLabelledby?: never }
|
|
81
|
+
| { ariaLabel?: never; ariaLabelledby: string }
|
|
82
|
+
|
|
83
|
+
/** Public SCodeEditor contract; размер задаёт только semantic viewport. / Public SCodeEditor contract; sizing is expressed only by semantic viewport. */
|
|
84
|
+
export type Props = AccessibleName & {
|
|
85
|
+
modelValue: string
|
|
86
|
+
language: SCodeEditorLanguage
|
|
87
|
+
capabilities: SCodeEditorCapabilities
|
|
88
|
+
readOnly?: boolean
|
|
89
|
+
disabled?: boolean
|
|
90
|
+
autofocus?: boolean
|
|
91
|
+
placeholder?: string
|
|
92
|
+
viewport?: SCodeEditorViewport
|
|
93
|
+
variant?: SCodeEditorVariant
|
|
94
|
+
nextSelectionPos?: number | null
|
|
95
|
+
/** DOM id supplemental status/diagnostic owner. / DOM id of a supplemental status/diagnostic owner. */
|
|
96
|
+
ariaDescribedby?: string
|
|
97
|
+
/** Помечает editor content невалидным без владения presentation диагностики. / Marks the editor content as invalid without owning diagnostic presentation. */
|
|
98
|
+
invalid?: boolean
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
102
|
+
readOnly: false,
|
|
103
|
+
disabled: false,
|
|
104
|
+
autofocus: false,
|
|
105
|
+
placeholder: undefined,
|
|
106
|
+
viewport: 'content',
|
|
107
|
+
variant: 'code',
|
|
108
|
+
nextSelectionPos: null,
|
|
109
|
+
ariaDescribedby: undefined,
|
|
110
|
+
invalid: false,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const emit = defineEmits<{
|
|
114
|
+
'update:modelValue': [value: string]
|
|
115
|
+
'cursor-activity': [payload: SCodeEditorCursorActivity]
|
|
116
|
+
'selection-applied': [payload: SCodeEditorSelectionAppliedPayload]
|
|
117
|
+
ready: [payload: SCodeEditorReadyPayload]
|
|
118
|
+
}>()
|
|
119
|
+
|
|
120
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SCodeEditor', owner: 'CodeMirror root adapter' })
|
|
121
|
+
useInteractiveLeafRegistration({ owner: 'SCodeEditor' })
|
|
122
|
+
|
|
123
|
+
const runtimeTheme = useRuntimeTheme()
|
|
124
|
+
const trustedChannel = useTrustedCodeEditorExtensions()
|
|
125
|
+
const editorView = ref<EditorView | null>(null)
|
|
126
|
+
const pendingControlledSelection = ref<number | null>(null)
|
|
127
|
+
|
|
128
|
+
const validatedModelValue = computed(() => {
|
|
129
|
+
if (typeof props.modelValue === 'string') return props.modelValue
|
|
130
|
+
throw new TypeError('SCodeEditor: modelValue должен быть строкой. / SCodeEditor: modelValue must be a string.')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const validatedViewport = computed<SCodeEditorViewport>(() => validateCodeEditorViewport('SCodeEditor', props.viewport))
|
|
134
|
+
const validatedVariant = computed<SCodeEditorVariant>(() => validateCodeEditorVariant('SCodeEditor', props.variant))
|
|
135
|
+
const validatedAutofocus = computed(() => validateBoolean('SCodeEditor', 'autofocus', props.autofocus))
|
|
136
|
+
const validatedReadOnly = computed(() => validateBoolean('SCodeEditor', 'readOnly', props.readOnly))
|
|
137
|
+
const validatedDisabled = computed(() => validateBoolean('SCodeEditor', 'disabled', props.disabled))
|
|
138
|
+
const validatedInvalid = computed(() => validateBoolean('SCodeEditor', 'invalid', props.invalid))
|
|
139
|
+
const isLocked = computed(() => validatedReadOnly.value || validatedDisabled.value)
|
|
140
|
+
|
|
141
|
+
const resolvedLanguage = computed((): SCodeEditorLanguage => {
|
|
142
|
+
const language = validateCodeEditorLanguage('SCodeEditor', props.language)
|
|
143
|
+
const extension = getCodeMirrorLanguageExtension(language)
|
|
144
|
+
const languageKey = resolveCodeLanguageKey(language)
|
|
145
|
+
if (!extension && languageKey !== 'plaintext' && languageKey !== 'text') {
|
|
146
|
+
const coordinate = language.mode === 'explicit'
|
|
147
|
+
? `language.id ${JSON.stringify(language.id)}`
|
|
148
|
+
: `language.filePath ${JSON.stringify(language.filePath)}`
|
|
149
|
+
throw new RangeError(`SCodeEditor: ${coordinate} не поддерживается. / SCodeEditor: ${coordinate} is not supported.`)
|
|
150
|
+
}
|
|
151
|
+
return language
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const resolvedCapabilities = computed((): SCodeEditorCapabilities =>
|
|
155
|
+
validateCodeEditorCapabilities('SCodeEditor', props.capabilities))
|
|
156
|
+
|
|
157
|
+
const contentAttributes = computed((): Record<string, string> => {
|
|
158
|
+
if (props.ariaLabel !== undefined && props.ariaLabelledby !== undefined) {
|
|
159
|
+
throw new TypeError('SCodeEditor: задайте только ariaLabel или ariaLabelledby. / SCodeEditor: provide only ariaLabel or ariaLabelledby.')
|
|
160
|
+
}
|
|
161
|
+
const attributes: Record<string, string> = {}
|
|
162
|
+
if (props.ariaLabel !== undefined) {
|
|
163
|
+
attributes['aria-label'] = validateNonEmptyString('SCodeEditor', 'ariaLabel', props.ariaLabel)
|
|
164
|
+
} else if (props.ariaLabelledby !== undefined) {
|
|
165
|
+
attributes['aria-labelledby'] = validateDomId('SCodeEditor', 'ariaLabelledby', props.ariaLabelledby)
|
|
166
|
+
} else {
|
|
167
|
+
throw new TypeError('SCodeEditor: требуется ariaLabel или ariaLabelledby. / SCodeEditor: ariaLabel or ariaLabelledby is required.')
|
|
168
|
+
}
|
|
169
|
+
if (props.ariaDescribedby !== undefined) {
|
|
170
|
+
attributes['aria-describedby'] = validateDomId('SCodeEditor', 'ariaDescribedby', props.ariaDescribedby)
|
|
171
|
+
}
|
|
172
|
+
if (validatedInvalid.value) attributes['aria-invalid'] = 'true'
|
|
173
|
+
if (validatedReadOnly.value) attributes['aria-readonly'] = 'true'
|
|
174
|
+
if (validatedDisabled.value) attributes['aria-disabled'] = 'true'
|
|
175
|
+
return attributes
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const resolvedExtensions = computed<Extension[]>(() => {
|
|
179
|
+
const capabilities = resolvedCapabilities.value
|
|
180
|
+
const language = resolvedLanguage.value
|
|
181
|
+
const result: Extension[] = [...(trustedChannel?.extensions.value ?? [])]
|
|
182
|
+
const languageExtension = getCodeMirrorLanguageExtension(language)
|
|
183
|
+
if (languageExtension) result.push(languageExtension)
|
|
184
|
+
if (runtimeTheme.value.mode === 'dark') result.push(oneDark)
|
|
185
|
+
if (capabilities.lineNumbers) result.push(lineNumbers())
|
|
186
|
+
if (props.placeholder !== undefined) result.push(codeMirrorPlaceholder(validateNonEmptyString('SCodeEditor', 'placeholder', props.placeholder)))
|
|
187
|
+
result.push(EditorState.readOnly.of(isLocked.value))
|
|
188
|
+
result.push(EditorView.editable.of(!isLocked.value))
|
|
189
|
+
result.push(EditorView.contentAttributes.of(contentAttributes.value))
|
|
190
|
+
if (capabilities.keyInterceptor && !isLocked.value) {
|
|
191
|
+
result.push(EditorView.domEventHandlers({ keydown: (event) => {
|
|
192
|
+
const handled = capabilities.keyInterceptor?.(event)
|
|
193
|
+
if (!validateBoolean('SCodeEditor', 'capabilities.keyInterceptor return value', handled)) return false
|
|
194
|
+
event.preventDefault()
|
|
195
|
+
return true
|
|
196
|
+
} }))
|
|
197
|
+
}
|
|
198
|
+
result.push(EditorView.updateListener.of((update) => {
|
|
199
|
+
editorView.value = update.view
|
|
200
|
+
if ((isLocked.value && capabilities.cursorActivity !== 'always') || (!update.selectionSet && !update.docChanged)) return
|
|
201
|
+
const pos = update.state.selection.main.head
|
|
202
|
+
const line = update.state.doc.lineAt(pos)
|
|
203
|
+
emit('cursor-activity', {
|
|
204
|
+
pos,
|
|
205
|
+
line: line.number,
|
|
206
|
+
column: pos - line.from + 1,
|
|
207
|
+
lineFrom: line.from,
|
|
208
|
+
lineTo: line.to,
|
|
209
|
+
lineText: update.state.doc.sliceString(line.from, line.to),
|
|
210
|
+
})
|
|
211
|
+
}))
|
|
212
|
+
return result
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
const validateSelectionPosition = (position: unknown, documentLength: number, coordinate: string): number =>
|
|
216
|
+
validateBoundedInteger('SCodeEditor', coordinate, position, { minimum: 0, maximum: documentLength })
|
|
217
|
+
|
|
218
|
+
const dispatchSelection = (position: number, source: SCodeEditorSelectionAppliedPayload['source']): void => {
|
|
219
|
+
const view = editorView.value
|
|
220
|
+
if (!view) throw new Error('SCodeEditor: editor не готов; дождитесь ready. / SCodeEditor: editor is not ready; wait for ready.')
|
|
221
|
+
const validated = validateSelectionPosition(position, view.state.doc.length, `${source} selection position`)
|
|
222
|
+
view.dispatch({ selection: EditorSelection.single(validated), scrollIntoView: true })
|
|
223
|
+
view.focus()
|
|
224
|
+
emit('selection-applied', { position: validated, source })
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const onUpdate = (value: string): void => {
|
|
228
|
+
if (typeof value !== 'string') {
|
|
229
|
+
throw new TypeError('SCodeEditor: CodeMirror update payload должен быть строкой. / SCodeEditor: CodeMirror update payload must be a string.')
|
|
230
|
+
}
|
|
231
|
+
if (!isLocked.value) emit('update:modelValue', value)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const onReady = (payload: unknown): void => {
|
|
235
|
+
if (typeof payload !== 'object' || payload === null || !('view' in payload) || !((payload as { view?: unknown }).view instanceof EditorView)) {
|
|
236
|
+
throw new TypeError('SCodeEditor: CodeMirror ready payload должен содержать EditorView. / SCodeEditor: CodeMirror ready payload must contain EditorView.')
|
|
237
|
+
}
|
|
238
|
+
editorView.value = (payload as { view: EditorView }).view
|
|
239
|
+
const readyPayload: SCodeEditorReadyPayload = {
|
|
240
|
+
documentLength: editorView.value.state.doc.length,
|
|
241
|
+
viewport: validatedViewport.value,
|
|
242
|
+
variant: validatedVariant.value,
|
|
243
|
+
locked: isLocked.value,
|
|
244
|
+
}
|
|
245
|
+
emit('ready', readyPayload)
|
|
246
|
+
if (pendingControlledSelection.value !== null) {
|
|
247
|
+
const position = pendingControlledSelection.value
|
|
248
|
+
pendingControlledSelection.value = null
|
|
249
|
+
dispatchSelection(position, 'controlled')
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
watch(() => props.nextSelectionPos, (position) => {
|
|
254
|
+
if (position === null || position === undefined) return
|
|
255
|
+
if (!Number.isInteger(position)) {
|
|
256
|
+
throw new TypeError(`SCodeEditor: nextSelectionPos должен быть целым числом; получено ${String(position)}. / SCodeEditor: nextSelectionPos must be an integer; received ${String(position)}.`)
|
|
257
|
+
}
|
|
258
|
+
if (!editorView.value) {
|
|
259
|
+
pendingControlledSelection.value = position
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
dispatchSelection(position, 'controlled')
|
|
263
|
+
}, { immediate: true })
|
|
264
|
+
|
|
265
|
+
defineExpose({
|
|
266
|
+
applySelection: (position: number) => dispatchSelection(position, 'imperative'),
|
|
267
|
+
focus: () => {
|
|
268
|
+
if (!editorView.value) throw new Error('SCodeEditor: editor не готов; дождитесь ready. / SCodeEditor: editor is not ready; wait for ready.')
|
|
269
|
+
editorView.value.focus()
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
</script>
|
|
273
|
+
|
|
274
|
+
<style lang="postcss" src="./SCodeEditor.css" scoped></style>
|