@pgcorp/ui-kit 0.8.1 → 0.10.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/README.md +332 -9
- package/docs/getting-started.md +135 -0
- package/docs/public-api.md +53 -4
- package/docs/rich-document.md +216 -0
- package/docs/scroll-viewport.md +167 -0
- package/docs/theming.md +17 -0
- package/package.json +108 -50
- package/src/components/layout/SAppShell.vue +3 -1
- package/src/components/layout/SDockRegion.vue +5 -3
- package/src/components/layout/SResizeHandle.vue +45 -5
- package/src/components/layout/SWorkbenchPanelShell.vue +4 -2
- package/src/components/shared/_internal/SDialogSurface.css +23 -0
- package/src/components/shared/_internal/SDialogSurface.vue +9 -2
- package/src/components/shared/_internal/SToastItem.css +4 -0
- package/src/components/shared/_internal/SToastItem.vue +44 -8
- package/src/components/shared/_internal/STreeNode.vue +24 -11
- package/src/components/shared/_internal/dialogSurface.ts +7 -1
- package/src/components/shared/complex/SKanbanLane.vue +4 -2
- package/src/components/shared/containers/SCloudChromeRow.vue +3 -2
- package/src/components/shared/containers/SDrawer.css +11 -2
- package/src/components/shared/containers/SDrawer.vue +48 -9
- package/src/components/shared/containers/SModal.vue +5 -2
- package/src/components/shared/containers/SPanel.vue +4 -2
- package/src/components/shared/containers/SPopover.vue +7 -3
- package/src/components/shared/containers/SUiKitLocaleProvider.vue +57 -0
- package/src/components/shared/controls/SButton.css +14 -10
- package/src/components/shared/controls/SCombobox.vue +8 -6
- package/src/components/shared/controls/SComboboxTrigger.css +7 -4
- package/src/components/shared/controls/SComboboxTrigger.vue +5 -2
- package/src/components/shared/controls/SContextToggleButton.vue +6 -4
- package/src/components/shared/controls/SDragHandle.vue +8 -4
- package/src/components/shared/controls/SDropdownMenu.vue +7 -4
- package/src/components/shared/controls/SInputText.css +5 -4
- package/src/components/shared/controls/SListbox.vue +4 -2
- package/src/components/shared/controls/SSelect.vue +4 -2
- package/src/components/shared/controls/STextarea.ts +13 -0
- package/src/components/shared/controls/STextarea.vue +391 -37
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +4 -2
- package/src/components/shared/data-display/SChart.vue +7 -5
- package/src/components/shared/data-display/SChip.vue +3 -1
- package/src/components/shared/data-display/SCodeBlock.css +4 -0
- package/src/components/shared/data-display/SCodeBlock.vue +46 -22
- package/src/components/shared/data-display/SCodeEditor.css +17 -0
- package/src/components/shared/data-display/SCodeEditor.vue +173 -8
- package/src/components/shared/data-display/SCodeSearchPanel.vue +48 -33
- package/src/components/shared/data-display/SCodeWorkspaceEditor.css +13 -0
- package/src/components/shared/data-display/SCodeWorkspaceEditor.vue +355 -0
- package/src/components/shared/data-display/SControlledToastViewport.css +89 -0
- package/src/components/shared/data-display/SControlledToastViewport.vue +205 -0
- package/src/components/shared/data-display/SCopyField.vue +12 -10
- package/src/components/shared/data-display/SDiffViewer.css +122 -0
- package/src/components/shared/data-display/SDiffViewer.vue +499 -0
- package/src/components/shared/data-display/SDocBlock.vue +99 -71
- package/src/components/shared/data-display/SExpandableText.vue +10 -8
- package/src/components/shared/data-display/SJsonTree.vue +4 -2
- package/src/components/shared/data-display/SLinkedSystemsList.vue +4 -2
- package/src/components/shared/data-display/SMessage.vue +6 -6
- package/src/components/shared/data-display/SRichDocument.css +8 -0
- package/src/components/shared/data-display/SRichDocument.vue +65 -0
- package/src/components/shared/data-display/SScrollViewport.css +53 -0
- package/src/components/shared/data-display/SScrollViewport.vue +470 -0
- package/src/components/shared/data-display/STable.vue +38 -19
- package/src/components/shared/data-display/SToastContainer.vue +22 -17
- package/src/components/shared/data-display/SVirtualList.ts +33 -0
- package/src/components/shared/data-display/SVirtualList.vue +321 -36
- package/src/components/shared/data-display/_internal/SCodeTokenLine.css +72 -0
- package/src/components/shared/data-display/_internal/SCodeTokenLine.vue +79 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.css +85 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.vue +282 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.css +6 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.vue +76 -0
- package/src/components/shared/data-display/_internal/SRichMath.css +42 -0
- package/src/components/shared/data-display/_internal/SRichMath.vue +102 -0
- package/src/components/shared/data-display/codeEditor.ts +200 -0
- package/src/components/shared/data-display/codeWorkspace.ts +233 -0
- package/src/components/shared/data-display/diff.ts +541 -0
- package/src/components/shared/data-display/richDocument.ts +876 -0
- package/src/components/shared/data-display/scrollViewport.ts +47 -0
- package/src/components/shared/data-display/toast.ts +194 -0
- package/src/components/shared/database/SDataGrid.vue +7 -4
- package/src/components/shared/database/SSqlEditor.vue +39 -39
- package/src/components/shared/feedback/SAsyncState.vue +3 -1
- package/src/components/shared/graph/SGraphViewport.vue +11 -9
- package/src/components/shared/navigation/SBreadcrumbs.vue +8 -5
- package/src/components/shared/navigation/SCatalogNavigator.vue +26 -20
- package/src/components/shared/navigation/STabList.vue +10 -6
- package/src/components/shared/navigation/STabs.vue +15 -11
- package/src/components/shared/navigation/SWizardSteps.vue +4 -2
- package/src/components/shared/persona/SPersonaProjectPicker.vue +31 -27
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +39 -33
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +8 -6
- package/src/components/shared/persona/_internal/labels.ts +66 -57
- package/src/components/shared/persona/presentation.ts +140 -110
- package/src/composables/useFloatingPosition.ts +39 -2
- package/src/composables/useUiKitLocale.ts +17 -0
- package/src/internal/codeHighlight.ts +110 -0
- package/src/internal/diffEngine.ts +464 -0
- package/src/internal/diffRuntime.ts +189 -0
- package/src/internal/feedbackPresentation.ts +33 -14
- package/src/internal/localeContext.ts +22 -0
- package/src/internal/ownedAttrs.ts +8 -0
- package/src/internal/runtimeContract.ts +11 -9
- package/src/internal/semanticSizing.ts +13 -0
- package/src/locales/catalog.ts +634 -0
- package/src/locales/locale.ts +472 -0
- package/src/locales/messages.generated.ts +1826 -0
- package/src/locales/messages.ts +150 -0
- package/src/stores/useNotifierStore.ts +61 -44
- package/src/styles/tokens.css +23 -2
- package/src/theme.ts +59 -32
- package/src/workers/diff.worker.ts +73 -0
- package/src/workers/diffProtocol.ts +155 -0
- package/src/components/shared/data-display/SToastContainer.css +0 -53
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<span v-if="renderMode" class="s-doc-block-language">{{ headerLabel }}</span>
|
|
14
14
|
</div>
|
|
15
15
|
<div class="s-doc-block-actions" v-if="contract.showActions">
|
|
16
|
-
<SButton appearance="text" severity="secondary" size="xs" icon-only :leading-icon="Copy" @click="copyContent" title="
|
|
16
|
+
<SButton appearance="text" severity="secondary" size="xs" icon-only :leading-icon="Copy" @click="copyContent" :title="t('clipboard.copyDocument')" :aria-label="t('clipboard.copyDocument')" />
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -38,7 +38,18 @@
|
|
|
38
38
|
</template>
|
|
39
39
|
|
|
40
40
|
<script setup lang="ts">
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
computed,
|
|
43
|
+
defineComponent,
|
|
44
|
+
h,
|
|
45
|
+
onBeforeUnmount,
|
|
46
|
+
ref,
|
|
47
|
+
shallowRef,
|
|
48
|
+
watch,
|
|
49
|
+
type Component,
|
|
50
|
+
type PropType,
|
|
51
|
+
type VNodeChild,
|
|
52
|
+
} from 'vue';
|
|
42
53
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs';
|
|
43
54
|
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract';
|
|
44
55
|
import { validateBoolean, validateExactString, validateNonEmptyString } from '../../../internal/runtimeContract';
|
|
@@ -48,21 +59,9 @@ import SAsyncState from '../feedback/SAsyncState.vue';
|
|
|
48
59
|
import { Copy } from '../../icons/sputnigUiIcons';
|
|
49
60
|
import { useNotifier } from '../../../composables/useNotifier';
|
|
50
61
|
import { useClipboard } from '../../../composables/useClipboard';
|
|
62
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale';
|
|
51
63
|
|
|
52
|
-
// Markdown rendering with highlight.js similar to MessageBubble
|
|
53
64
|
import { marked } from 'marked';
|
|
54
|
-
import hljs from 'highlight.js/lib/core';
|
|
55
|
-
import bash from 'highlight.js/lib/languages/bash';
|
|
56
|
-
import dockerfile from 'highlight.js/lib/languages/dockerfile';
|
|
57
|
-
import javascript from 'highlight.js/lib/languages/javascript';
|
|
58
|
-
import makefile from 'highlight.js/lib/languages/makefile';
|
|
59
|
-
import nginx from 'highlight.js/lib/languages/nginx';
|
|
60
|
-
import powershell from 'highlight.js/lib/languages/powershell';
|
|
61
|
-
import python from 'highlight.js/lib/languages/python';
|
|
62
|
-
import typescript from 'highlight.js/lib/languages/typescript';
|
|
63
|
-
import xml from 'highlight.js/lib/languages/xml';
|
|
64
|
-
import yaml from 'highlight.js/lib/languages/yaml';
|
|
65
|
-
import { resolveHighlightLanguageKey } from '../codeLanguages';
|
|
66
65
|
import {
|
|
67
66
|
SANITIZED_DOCUMENT_RENDER_ATTRIBUTES,
|
|
68
67
|
SANITIZED_DOCUMENT_TAGS,
|
|
@@ -71,30 +70,10 @@ import {
|
|
|
71
70
|
|
|
72
71
|
defineOptions({ inheritAttrs: false });
|
|
73
72
|
|
|
74
|
-
hljs.registerLanguage('bash', bash);
|
|
75
|
-
hljs.registerLanguage('dockerfile', dockerfile);
|
|
76
|
-
hljs.registerLanguage('javascript', javascript);
|
|
77
|
-
hljs.registerLanguage('makefile', makefile);
|
|
78
|
-
hljs.registerLanguage('nginx', nginx);
|
|
79
|
-
hljs.registerLanguage('powershell', powershell);
|
|
80
|
-
hljs.registerLanguage('python', python);
|
|
81
|
-
hljs.registerLanguage('typescript', typescript);
|
|
82
|
-
hljs.registerLanguage('xml', xml);
|
|
83
|
-
hljs.registerLanguage('yaml', yaml);
|
|
84
|
-
|
|
85
73
|
const ownedAttrs = useOwnedAttrs({ component: 'SDocBlock', owner: 'document block root' });
|
|
86
74
|
|
|
87
75
|
useInteractiveLeafRegistration({ owner: 'SDocBlock' });
|
|
88
76
|
|
|
89
|
-
function escapeHighlightedCode(code: string): string {
|
|
90
|
-
return code
|
|
91
|
-
.replace(/&/gu, '&')
|
|
92
|
-
.replace(/</gu, '<')
|
|
93
|
-
.replace(/>/gu, '>')
|
|
94
|
-
.replace(/"/gu, '"')
|
|
95
|
-
.replace(/'/gu, ''');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
77
|
function escapeHtmlAttribute(value: string): string {
|
|
99
78
|
return value
|
|
100
79
|
.replace(/&/gu, '&')
|
|
@@ -104,23 +83,38 @@ function escapeHtmlAttribute(value: string): string {
|
|
|
104
83
|
.replace(/'/gu, ''');
|
|
105
84
|
}
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
86
|
+
type CodeHighlightRuntime = typeof import('../../../internal/codeHighlight');
|
|
87
|
+
|
|
88
|
+
interface MarkdownRenderResult {
|
|
89
|
+
html: string;
|
|
90
|
+
requiresHighlightRuntime: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function renderMarkdown(content: string, highlighter: CodeHighlightRuntime | null): MarkdownRenderResult {
|
|
94
|
+
let requiresHighlightRuntime = false;
|
|
95
|
+
const renderer = new marked.Renderer();
|
|
96
|
+
renderer.code = ({ text, lang }) => {
|
|
97
|
+
const rawLanguage = (lang || '').match(/\S+/)?.[0] ?? '';
|
|
98
|
+
const language = rawLanguage ? { mode: 'explicit' as const, id: rawLanguage } : null;
|
|
99
|
+
if (language !== null && highlighter === null) requiresHighlightRuntime = true;
|
|
100
|
+
const languageClass = rawLanguage ? `language-${escapeHtmlAttribute(rawLanguage.toLowerCase())}` : '';
|
|
101
|
+
const isSupportedLanguage = language !== null
|
|
102
|
+
&& highlighter !== null
|
|
103
|
+
&& highlighter.isCodeHighlightLanguageSupported(language);
|
|
104
|
+
const highlightedCode = language !== null && highlighter !== null
|
|
105
|
+
? highlighter.highlightCodeToHtml(text, language)
|
|
106
|
+
: escapeHtmlAttribute(text);
|
|
107
|
+
const classNames = [languageClass, isSupportedLanguage ? 'hljs' : ''].filter(Boolean).join(' ');
|
|
108
|
+
const classAttribute = classNames ? ` class="${classNames}"` : '';
|
|
109
|
+
|
|
110
|
+
return `<pre><code${classAttribute}>${highlightedCode}</code></pre>\n`;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
html: marked.parse(content, { gfm: true, breaks: true, renderer }) as string,
|
|
115
|
+
requiresHighlightRuntime,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
124
118
|
|
|
125
119
|
interface Props {
|
|
126
120
|
content: string;
|
|
@@ -150,6 +144,7 @@ const emit = defineEmits<{
|
|
|
150
144
|
}>();
|
|
151
145
|
|
|
152
146
|
const notifier = useNotifier();
|
|
147
|
+
const { t } = useUiKitLocale();
|
|
153
148
|
const { copyText } = useClipboard();
|
|
154
149
|
const rootRef = ref<HTMLElement | null>(null);
|
|
155
150
|
|
|
@@ -186,28 +181,61 @@ const headerLabel = computed(() => {
|
|
|
186
181
|
switch (renderMode.value) {
|
|
187
182
|
case 'markdown': return 'Markdown';
|
|
188
183
|
case 'html': return 'HTML';
|
|
189
|
-
default: return '
|
|
184
|
+
default: return t('editor.plainText');
|
|
190
185
|
}
|
|
191
186
|
});
|
|
192
187
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
renderer: markdownRenderer,
|
|
197
|
-
}) as string);
|
|
188
|
+
const renderedDocumentHtml = shallowRef('');
|
|
189
|
+
const documentRenderError = shallowRef<Error | null>(null);
|
|
190
|
+
let documentRenderGeneration = 0;
|
|
198
191
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
202
|
-
}
|
|
192
|
+
function documentRenderingError(error: unknown): Error {
|
|
193
|
+
if (error instanceof Error) return error;
|
|
194
|
+
return new Error(`SDocBlock: document rendering failed with ${String(error)}.`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function enhanceMarkdownHighlighting(
|
|
198
|
+
content: string,
|
|
199
|
+
generation: number,
|
|
200
|
+
): Promise<void> {
|
|
201
|
+
try {
|
|
202
|
+
const highlighter = await import('../../../internal/codeHighlight');
|
|
203
|
+
const highlighted = renderMarkdown(content, highlighter);
|
|
204
|
+
if (generation !== documentRenderGeneration) return;
|
|
205
|
+
renderedDocumentHtml.value = sanitizeDocumentHtml(highlighted.html);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (generation !== documentRenderGeneration) return;
|
|
208
|
+
documentRenderError.value = documentRenderingError(error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
watch(
|
|
213
|
+
() => [contract.value.content, renderMode.value] as const,
|
|
214
|
+
([content, mode]) => {
|
|
215
|
+
const generation = documentRenderGeneration += 1;
|
|
216
|
+
documentRenderError.value = null;
|
|
217
|
+
if (mode === 'html') {
|
|
218
|
+
renderedDocumentHtml.value = sanitizeDocumentHtml(content);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (mode === 'plaintext') {
|
|
222
|
+
renderedDocumentHtml.value = '';
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const initial = renderMarkdown(content, null);
|
|
226
|
+
renderedDocumentHtml.value = sanitizeDocumentHtml(initial.html);
|
|
227
|
+
if (initial.requiresHighlightRuntime) void enhanceMarkdownHighlighting(content, generation);
|
|
228
|
+
},
|
|
229
|
+
{ immediate: true },
|
|
230
|
+
);
|
|
203
231
|
|
|
204
|
-
|
|
205
|
-
|
|
232
|
+
onBeforeUnmount(() => {
|
|
233
|
+
documentRenderGeneration += 1;
|
|
206
234
|
});
|
|
207
235
|
|
|
208
236
|
const sanitizedDocumentHtml = computed(() => {
|
|
209
|
-
if (
|
|
210
|
-
if (renderMode.value === 'html') return
|
|
237
|
+
if (documentRenderError.value !== null) throw documentRenderError.value;
|
|
238
|
+
if (renderMode.value === 'markdown' || renderMode.value === 'html') return renderedDocumentHtml.value;
|
|
211
239
|
throw new Error('SDocBlock: document renderer доступен только для markdown/html mode. / SDocBlock: the document renderer is available only in markdown/html mode.');
|
|
212
240
|
});
|
|
213
241
|
|
|
@@ -298,15 +326,15 @@ const copyContent = async () => {
|
|
|
298
326
|
if (!text) return;
|
|
299
327
|
const result = await copyText(text);
|
|
300
328
|
if (result.status === 'copied') {
|
|
301
|
-
notifier.success({ message: '
|
|
329
|
+
notifier.success({ message: t('clipboard.documentCopied') });
|
|
302
330
|
return;
|
|
303
331
|
}
|
|
304
332
|
notifier.danger({
|
|
305
333
|
message: result.status === 'unavailable'
|
|
306
|
-
? '
|
|
334
|
+
? t('clipboard.clipboardUnavailable')
|
|
307
335
|
: result.status === 'empty'
|
|
308
|
-
? '
|
|
309
|
-
:
|
|
336
|
+
? t('clipboard.emptyDocument')
|
|
337
|
+
: `${t('clipboard.documentCopyFailed')} ${result.error.message}`,
|
|
310
338
|
});
|
|
311
339
|
};
|
|
312
340
|
|
|
@@ -350,7 +378,7 @@ const handleDocumentLinkClick = (event: MouseEvent, payload: DocumentLinkPayload
|
|
|
350
378
|
try {
|
|
351
379
|
openExternalHref(new URL(payload.href, window.location.href).toString());
|
|
352
380
|
} catch {
|
|
353
|
-
notifier.danger({ message:
|
|
381
|
+
notifier.danger({ message: t('feedback.openLinkFailed', { href: payload.href }) });
|
|
354
382
|
}
|
|
355
383
|
};
|
|
356
384
|
</script>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
appearance="text"
|
|
27
27
|
severity="secondary"
|
|
28
28
|
size="sm"
|
|
29
|
-
:label="isExpanded ? '
|
|
29
|
+
:label="isExpanded ? t('actions.collapse') : t('actions.expand')"
|
|
30
30
|
:disclosure="{ expanded: isExpanded, controls: contentId, iconPlacement: 'leading' }"
|
|
31
31
|
@click="toggleExpanded"
|
|
32
32
|
/>
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
shape="circle"
|
|
39
39
|
icon-only
|
|
40
40
|
:leading-icon="Copy"
|
|
41
|
-
title="
|
|
42
|
-
aria-label="
|
|
41
|
+
:title="t('clipboard.copyText')"
|
|
42
|
+
:aria-label="t('clipboard.copyText')"
|
|
43
43
|
:disabled="!text"
|
|
44
44
|
@click="copyText"
|
|
45
45
|
/>
|
|
@@ -54,6 +54,7 @@ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
|
54
54
|
import SButton from '../controls/SButton.vue'
|
|
55
55
|
import { useNotifier } from '../../../composables/useNotifier'
|
|
56
56
|
import { useClipboard } from '../../../composables/useClipboard'
|
|
57
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
57
58
|
import {
|
|
58
59
|
validateExpandedContentMaxBlockSize,
|
|
59
60
|
type ExpandedContentMaxBlockSize,
|
|
@@ -106,6 +107,7 @@ const emit = defineEmits<{
|
|
|
106
107
|
}>()
|
|
107
108
|
|
|
108
109
|
const notifier = useNotifier()
|
|
110
|
+
const { t } = useUiKitLocale()
|
|
109
111
|
const { copyText: copyToClipboard } = useClipboard()
|
|
110
112
|
const contentEl = ref<HTMLElement | null>(null)
|
|
111
113
|
const ownedAttrs = useOwnedAttrs({ component: 'SExpandableText', owner: 'expandable text root' })
|
|
@@ -207,17 +209,17 @@ async function copyText(): Promise<void> {
|
|
|
207
209
|
}
|
|
208
210
|
const result = await copyToClipboard(props.text)
|
|
209
211
|
if (result.status === 'copied') {
|
|
210
|
-
notifier.success({ message: '
|
|
212
|
+
notifier.success({ message: t('clipboard.textCopied') })
|
|
211
213
|
return
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
notifier.danger({
|
|
215
|
-
title: '
|
|
217
|
+
title: t('clipboard.copyErrorTitle'),
|
|
216
218
|
message: result.status === 'unavailable'
|
|
217
|
-
? '
|
|
219
|
+
? t('clipboard.clipboardUnavailable')
|
|
218
220
|
: result.status === 'empty'
|
|
219
|
-
? '
|
|
220
|
-
:
|
|
221
|
+
? t('clipboard.emptyText')
|
|
222
|
+
: `${t('clipboard.textCopyFailed')} ${result.error.message}`,
|
|
221
223
|
})
|
|
222
224
|
}
|
|
223
225
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, watch } from 'vue'
|
|
3
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
3
4
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
4
5
|
import STree from '../complex/STree.vue'
|
|
5
6
|
import type { TreeModel, TreeSelection } from '../complex/types'
|
|
@@ -22,6 +23,7 @@ interface JsonTreeNode {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const props = withDefaults(defineProps<Props>(), { initiallyCollapsed: false })
|
|
26
|
+
const { t } = useUiKitLocale()
|
|
25
27
|
const expanded = ref<Set<string>>(new Set())
|
|
26
28
|
const ownedAttrs = useOwnedAttrs({ component: 'SJsonTree', owner: 'JSON tree root' })
|
|
27
29
|
const selection: TreeSelection<string> = { mode: 'none' }
|
|
@@ -29,7 +31,7 @@ const treeModel: TreeModel<JsonTreeNode, string> = {
|
|
|
29
31
|
key: (node) => node.id,
|
|
30
32
|
label: (node) => node.keyLabel === undefined
|
|
31
33
|
? node.summary
|
|
32
|
-
: node.keyLabel.length === 0 ? '
|
|
34
|
+
: node.keyLabel.length === 0 ? t('data.emptyKey') : node.keyLabel,
|
|
33
35
|
children: (node) => node.children,
|
|
34
36
|
disabled: () => false,
|
|
35
37
|
icon: () => undefined,
|
|
@@ -96,7 +98,7 @@ defineExpose({ expandAll, collapseAll })
|
|
|
96
98
|
|
|
97
99
|
<template>
|
|
98
100
|
<div v-bind="ownedAttrs.bindings()" class="s-json-tree">
|
|
99
|
-
<STree label="
|
|
101
|
+
<STree :label="t('data.jsonStructure')" :nodes="[root]" :model="treeModel" :selection="selection" :expanded="expanded" density="compact" actions-layout="overlay" @update:expanded="expanded = new Set($event)">
|
|
100
102
|
<template #col-left />
|
|
101
103
|
<template #col-center="{ node }">
|
|
102
104
|
<span class="s-json-tree__entry" :data-json-pointer="node.pointer">
|
|
@@ -113,6 +113,7 @@ export interface Slots<T extends LinkedSystemListItem = LinkedSystemListItem> {
|
|
|
113
113
|
|
|
114
114
|
<script setup lang="ts" generic="T extends LinkedSystemListItem = LinkedSystemListItem">
|
|
115
115
|
import { computed, watchEffect } from 'vue'
|
|
116
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
116
117
|
import { withErrorCause } from '../../../internal/es2020'
|
|
117
118
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
118
119
|
import {
|
|
@@ -128,17 +129,18 @@ import SStatus from './SStatus.vue'
|
|
|
128
129
|
defineOptions({ inheritAttrs: false })
|
|
129
130
|
|
|
130
131
|
const props = withDefaults(defineProps<Props<T>>(), {
|
|
131
|
-
accessibleLabel:
|
|
132
|
+
accessibleLabel: undefined,
|
|
132
133
|
presentation: 'list',
|
|
133
134
|
actionsDisabled: false,
|
|
134
135
|
})
|
|
136
|
+
const { t } = useUiKitLocale()
|
|
135
137
|
|
|
136
138
|
const slots = defineSlots<Slots<T>>()
|
|
137
139
|
const ownedAttrs = useOwnedAttrs({ component: 'SLinkedSystemsList', owner: 'active linked-systems list root' })
|
|
138
140
|
const validatedAccessibleLabel = computed(() => validateNonEmptyString(
|
|
139
141
|
'SLinkedSystemsList',
|
|
140
142
|
'accessibleLabel',
|
|
141
|
-
props.accessibleLabel,
|
|
143
|
+
props.accessibleLabel ?? t('data.linkedSystems'),
|
|
142
144
|
))
|
|
143
145
|
const validatedPresentation = computed(() => validateExactString(
|
|
144
146
|
'SLinkedSystemsList',
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
<script setup lang="ts">
|
|
43
43
|
import { computed } from 'vue'
|
|
44
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
44
45
|
import { X } from '../../icons/sputnigUiIcons'
|
|
45
46
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
46
47
|
import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
|
|
@@ -79,8 +80,6 @@ export interface SMessageDismissEvent {
|
|
|
79
80
|
const MESSAGE_SEVERITIES = ['info', 'success', 'warn', 'danger'] as const satisfies readonly Severity[]
|
|
80
81
|
const MESSAGE_LIVE_MODES = ['off', 'polite', 'assertive', 'auto'] as const satisfies readonly FeedbackLiveMode[]
|
|
81
82
|
const MESSAGE_SIZES = ['sm', 'md'] as const satisfies readonly SMessageSize[]
|
|
82
|
-
const DEFAULT_DISMISS_LABEL = 'Закрыть'
|
|
83
|
-
|
|
84
83
|
export interface Props {
|
|
85
84
|
severity?: Severity
|
|
86
85
|
title?: string
|
|
@@ -96,11 +95,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
96
95
|
severity: 'info',
|
|
97
96
|
title: undefined,
|
|
98
97
|
closable: false,
|
|
99
|
-
dismissLabel:
|
|
98
|
+
dismissLabel: undefined,
|
|
100
99
|
dismissDisabled: false,
|
|
101
100
|
live: 'off',
|
|
102
101
|
size: 'md',
|
|
103
102
|
})
|
|
103
|
+
const { t } = useUiKitLocale()
|
|
104
104
|
|
|
105
105
|
const emit = defineEmits<{
|
|
106
106
|
dismiss: [event: SMessageDismissEvent]
|
|
@@ -134,9 +134,9 @@ const validatedSize = computed<SMessageSize>(() => validateExactString(
|
|
|
134
134
|
const dismissAction = computed<SMessageDismissAction | undefined>(() => {
|
|
135
135
|
const closable = validateBoolean('SMessage', 'closable', props.closable)
|
|
136
136
|
const disabled = validateBoolean('SMessage', 'dismissDisabled', props.dismissDisabled)
|
|
137
|
-
const label = validateNonEmptyString('SMessage', 'dismissLabel', props.dismissLabel)
|
|
137
|
+
const label = validateNonEmptyString('SMessage', 'dismissLabel', props.dismissLabel ?? t('feedback.dismissMessage'))
|
|
138
138
|
if (!closable) {
|
|
139
|
-
if (disabled ||
|
|
139
|
+
if (disabled || props.dismissLabel !== undefined) {
|
|
140
140
|
throw new TypeError(
|
|
141
141
|
'SMessage: dismissLabel/dismissDisabled применяются только при closable=true. '
|
|
142
142
|
+ '/ SMessage: dismissLabel/dismissDisabled apply only when closable=true.',
|
|
@@ -147,7 +147,7 @@ const dismissAction = computed<SMessageDismissAction | undefined>(() => {
|
|
|
147
147
|
return { label, disabled }
|
|
148
148
|
})
|
|
149
149
|
|
|
150
|
-
const presentation = computed(() => resolveFeedbackPresentation(validatedSeverity.value))
|
|
150
|
+
const presentation = computed(() => resolveFeedbackPresentation(validatedSeverity.value, t))
|
|
151
151
|
const iconComponent = computed(() => presentation.value.icon)
|
|
152
152
|
const liveAttributes = computed(() => resolveFeedbackLive(validatedSeverity.value, validatedLive.value))
|
|
153
153
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<article
|
|
3
|
+
v-bind="ownedAttrs.bindings()"
|
|
4
|
+
class="s-rich-document"
|
|
5
|
+
:aria-label="validatedAccessibleLabel"
|
|
6
|
+
:data-document-id="model.document.id"
|
|
7
|
+
>
|
|
8
|
+
<SRichBlockNode
|
|
9
|
+
v-for="(node, index) in model.document.nodes"
|
|
10
|
+
:key="index"
|
|
11
|
+
:node="node"
|
|
12
|
+
:resources="model.resources"
|
|
13
|
+
:document-id="model.document.id"
|
|
14
|
+
:path="`document.nodes[${index}]`"
|
|
15
|
+
@link-activate="emit('link-activate', $event)"
|
|
16
|
+
@resource-activate="emit('resource-activate', $event)"
|
|
17
|
+
@action="emit('action', $event)"
|
|
18
|
+
/>
|
|
19
|
+
</article>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed } from 'vue'
|
|
24
|
+
|
|
25
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
26
|
+
import { validateNonEmptyString } from '../../../internal/runtimeContract'
|
|
27
|
+
import SRichBlockNode from './_internal/SRichBlockNode.vue'
|
|
28
|
+
import {
|
|
29
|
+
validateRichDocumentModel,
|
|
30
|
+
type SRichDocumentActionIntent,
|
|
31
|
+
type SRichDocumentLinkActivate,
|
|
32
|
+
type SRichDocumentResource,
|
|
33
|
+
type SRichDocumentResourceActivate,
|
|
34
|
+
type SRichDocumentV1,
|
|
35
|
+
} from './richDocument'
|
|
36
|
+
|
|
37
|
+
defineOptions({ inheritAttrs: false })
|
|
38
|
+
|
|
39
|
+
/** Safe renderer input. The document and resources are validated before any node reaches the DOM. */
|
|
40
|
+
export interface Props {
|
|
41
|
+
document: SRichDocumentV1
|
|
42
|
+
resources?: readonly SRichDocumentResource[]
|
|
43
|
+
accessibleLabel: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
47
|
+
resources: () => [],
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const emit = defineEmits<{
|
|
51
|
+
'link-activate': [payload: SRichDocumentLinkActivate]
|
|
52
|
+
'resource-activate': [payload: SRichDocumentResourceActivate]
|
|
53
|
+
action: [payload: SRichDocumentActionIntent]
|
|
54
|
+
}>()
|
|
55
|
+
|
|
56
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SRichDocument', owner: 'safe rich document root' })
|
|
57
|
+
const validatedAccessibleLabel = computed(() => validateNonEmptyString(
|
|
58
|
+
'SRichDocument',
|
|
59
|
+
'accessibleLabel',
|
|
60
|
+
props.accessibleLabel,
|
|
61
|
+
))
|
|
62
|
+
const model = computed(() => validateRichDocumentModel(props.document, props.resources))
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<style lang="postcss" src="./SRichDocument.css" scoped></style>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-scroll-viewport {
|
|
4
|
+
@apply relative min-h-0 min-w-0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.s-scroll-viewport[data-axis='x'] {
|
|
8
|
+
overflow-x: auto;
|
|
9
|
+
overflow-y: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.s-scroll-viewport[data-axis='y'] {
|
|
13
|
+
overflow-x: hidden;
|
|
14
|
+
overflow-y: auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.s-scroll-viewport[data-axis='both'] {
|
|
18
|
+
overflow: auto;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.s-scroll-viewport[data-block-size='sm'] {
|
|
22
|
+
block-size: var(--s-scroll-viewport-max-block-size-sm);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.s-scroll-viewport[data-block-size='md'] {
|
|
26
|
+
block-size: var(--s-scroll-viewport-max-block-size-md);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.s-scroll-viewport[data-block-size='lg'] {
|
|
30
|
+
block-size: var(--s-scroll-viewport-max-block-size-lg);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.s-scroll-viewport[data-block-size='fill'] {
|
|
34
|
+
block-size: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.s-scroll-viewport[data-overscroll-behavior='contain'] {
|
|
38
|
+
overscroll-behavior: contain;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.s-scroll-viewport[data-overscroll-behavior='none'] {
|
|
42
|
+
overscroll-behavior: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.s-scroll-viewport[data-focusable='true']:focus-visible {
|
|
46
|
+
outline: var(--s-focus-ring-width) solid var(--s-focus-ring-color);
|
|
47
|
+
outline-offset: var(--s-focus-ring-inset-offset);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.s-scroll-viewport__content {
|
|
51
|
+
min-block-size: 100%;
|
|
52
|
+
min-inline-size: 100%;
|
|
53
|
+
}
|