@pgcorp/ui-kit 0.9.0 → 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 +188 -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/package.json +103 -50
- package/src/components/layout/SAppShell.vue +3 -1
- package/src/components/layout/SDockRegion.vue +5 -3
- package/src/components/layout/SWorkbenchPanelShell.vue +4 -2
- package/src/components/shared/_internal/SDialogSurface.css +5 -0
- package/src/components/shared/_internal/SDialogSurface.vue +3 -1
- 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/complex/SKanbanLane.vue +4 -2
- package/src/components/shared/containers/SCloudChromeRow.vue +3 -2
- package/src/components/shared/containers/SDrawer.vue +5 -2
- 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/_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 +42 -20
- 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/_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 +20 -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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
>
|
|
12
12
|
<div v-if="editing && draggable" class="s-cloud-chrome-row__drag-handle">
|
|
13
13
|
<SDragHandle
|
|
14
|
+
size="xs"
|
|
14
15
|
:label="label"
|
|
15
16
|
:can-move-previous="canMoveUp"
|
|
16
17
|
:can-move-next="canMoveDown"
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
<slot name="actions" />
|
|
28
29
|
<SButton
|
|
29
30
|
v-if="editing && canHide && !hidden"
|
|
30
|
-
size="
|
|
31
|
+
size="xs"
|
|
31
32
|
severity="secondary"
|
|
32
33
|
appearance="ghost"
|
|
33
34
|
icon-only
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
/>
|
|
39
40
|
<SButton
|
|
40
41
|
v-if="editing && canRestore"
|
|
41
|
-
size="
|
|
42
|
+
size="xs"
|
|
42
43
|
severity="secondary"
|
|
43
44
|
appearance="ghost"
|
|
44
45
|
icon-only
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
v-bind="ownedAttrs.bindings()"
|
|
12
12
|
:presentation="dialogPresentation"
|
|
13
13
|
:title="title"
|
|
14
|
-
:accessible-name="
|
|
14
|
+
:accessible-name="resolvedAriaLabel"
|
|
15
15
|
:closable="closable"
|
|
16
16
|
:set-surface-element="setPanelElement"
|
|
17
17
|
@request-close="closeDrawer"
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
<script setup lang="ts">
|
|
34
34
|
import { computed } from 'vue'
|
|
35
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
35
36
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
36
37
|
import { useDialogOverlay } from '../../../composables/useDialogOverlay'
|
|
37
38
|
import { validateBoolean, validateExactString } from '../../../internal/runtimeContract'
|
|
@@ -63,14 +64,16 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
63
64
|
size: 'md',
|
|
64
65
|
placement: 'right',
|
|
65
66
|
mobileFullScreen: true,
|
|
66
|
-
ariaLabel:
|
|
67
|
+
ariaLabel: undefined,
|
|
67
68
|
})
|
|
69
|
+
const { t } = useUiKitLocale()
|
|
68
70
|
|
|
69
71
|
const emit = defineEmits<{
|
|
70
72
|
'update:open': [value: boolean]
|
|
71
73
|
}>()
|
|
72
74
|
|
|
73
75
|
const ownedAttrs = useOwnedAttrs({ component: 'SDrawer', owner: 'drawer dialog surface' })
|
|
76
|
+
const resolvedAriaLabel = computed(() => props.ariaLabel ?? t('dialog.drawer'))
|
|
74
77
|
const validatedPlacement = computed<DrawerPlacement>(() => validateExactString(
|
|
75
78
|
'SDrawer',
|
|
76
79
|
'placement',
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
v-bind="ownedAttrs.bindings()"
|
|
12
12
|
:presentation="dialogPresentation"
|
|
13
13
|
:title="title"
|
|
14
|
-
:accessible-name="
|
|
14
|
+
:accessible-name="resolvedAriaLabel"
|
|
15
15
|
:closable="closable"
|
|
16
16
|
:set-surface-element="setPanelElement"
|
|
17
17
|
@request-close="closeModal"
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
<script setup lang="ts">
|
|
40
40
|
import { computed, toRef } from 'vue'
|
|
41
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
41
42
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
42
43
|
import { useDialogOverlay } from '../../../composables/useDialogOverlay'
|
|
43
44
|
import SDialogSurface from '../_internal/SDialogSurface.vue'
|
|
@@ -74,8 +75,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
74
75
|
layout: 'desktop',
|
|
75
76
|
surface: 'default',
|
|
76
77
|
scrollBehavior: 'page',
|
|
77
|
-
ariaLabel:
|
|
78
|
+
ariaLabel: undefined,
|
|
78
79
|
})
|
|
80
|
+
const { t } = useUiKitLocale()
|
|
79
81
|
|
|
80
82
|
const emit = defineEmits<{
|
|
81
83
|
'update:open': [value: boolean]
|
|
@@ -88,6 +90,7 @@ function closeModal() {
|
|
|
88
90
|
const isFullscreen = computed(() => props.fullScreen || props.layout === 'mobile-fullscreen')
|
|
89
91
|
|
|
90
92
|
const ownedAttrs = useOwnedAttrs({ component: 'SModal', owner: 'modal dialog surface' })
|
|
93
|
+
const resolvedAriaLabel = computed(() => props.ariaLabel ?? t('dialog.modal'))
|
|
91
94
|
const dialogPresentation = computed<ModalDialogSurfacePresentation>(() => ({
|
|
92
95
|
kind: 'modal',
|
|
93
96
|
size: props.size,
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
>
|
|
50
50
|
<SAsyncState :state="state">
|
|
51
51
|
<template v-if="state.status === 'error' && state.endpoint" #error>
|
|
52
|
-
<SMessage severity="danger" title="
|
|
52
|
+
<SMessage severity="danger" :title="t('feedback.serverResponseError')">
|
|
53
53
|
<div class="space-y-1">
|
|
54
54
|
<div>{{ state.message }}</div>
|
|
55
55
|
<div class="text-xs text-danger-700 dark:text-danger-300">
|
|
56
|
-
|
|
56
|
+
{{ t('feedback.request') }} {{ state.endpoint }}
|
|
57
57
|
</div>
|
|
58
58
|
</div>
|
|
59
59
|
</SMessage>
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
import { computed, useSlots, watchEffect, type FunctionalComponent, type HTMLAttributes, type VNodeProps } from 'vue'
|
|
83
83
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
84
84
|
import { validateDomId } from '../../../internal/runtimeContract'
|
|
85
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
85
86
|
import SAsyncState, { type SAsyncStateContract } from '../feedback/SAsyncState.vue'
|
|
86
87
|
import SMessage from '../data-display/SMessage.vue'
|
|
87
88
|
import SProgressBar from '../data-display/SProgressBar.vue'
|
|
@@ -148,6 +149,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
148
149
|
})
|
|
149
150
|
|
|
150
151
|
const slots = useSlots()
|
|
152
|
+
const { t } = useUiKitLocale()
|
|
151
153
|
const ownedAttrs = useOwnedAttrs({ component: 'SPanel', owner: 'panel surface root' })
|
|
152
154
|
const validatedTitleId = computed(() => {
|
|
153
155
|
if (props.titleId === undefined) return undefined
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
tabindex="-1"
|
|
57
57
|
role="dialog"
|
|
58
58
|
:id="panelId"
|
|
59
|
-
:aria-label="
|
|
59
|
+
:aria-label="resolvedAriaLabel"
|
|
60
60
|
@keydown.tab="handlePanelTab"
|
|
61
61
|
@focusout="handlePanelFocusout"
|
|
62
62
|
>
|
|
@@ -92,6 +92,7 @@ import SButton, {
|
|
|
92
92
|
type SButtonWidth,
|
|
93
93
|
} from '../controls/SButton.vue';
|
|
94
94
|
import { useFloatingPosition, type FloatingPlacement } from '../../../composables/useFloatingPosition';
|
|
95
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale';
|
|
95
96
|
import { findRelativeTabTarget, getFocusableElements } from '../../../internal/focusNavigation';
|
|
96
97
|
import { lastItem } from '../../../internal/es2020';
|
|
97
98
|
import { resolveInteractiveElement } from '../../../internal/interactiveElement';
|
|
@@ -141,7 +142,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
141
142
|
align: 'right',
|
|
142
143
|
surface: 'default',
|
|
143
144
|
width: 'trigger',
|
|
144
|
-
ariaLabel:
|
|
145
|
+
ariaLabel: undefined,
|
|
145
146
|
triggerAppearance: 'ghost',
|
|
146
147
|
triggerSize: 'md',
|
|
147
148
|
triggerWidth: 'full',
|
|
@@ -159,6 +160,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
159
160
|
disabled: false,
|
|
160
161
|
open: undefined,
|
|
161
162
|
});
|
|
163
|
+
const { t } = useUiKitLocale();
|
|
164
|
+
const resolvedAriaLabel = computed(() => props.ariaLabel ?? t('dialog.moreActions'));
|
|
162
165
|
const emit = defineEmits<{
|
|
163
166
|
(e: 'update:open', value: boolean): void;
|
|
164
167
|
}>();
|
|
@@ -192,7 +195,7 @@ const layerZIndex = ref('var(--s-layer-root-floating)');
|
|
|
192
195
|
const resolvedTriggerAriaLabel = computed(() =>
|
|
193
196
|
props.triggerAriaLabel ?? (
|
|
194
197
|
props.triggerIconOnly || (!props.triggerLabel && !slots.default)
|
|
195
|
-
?
|
|
198
|
+
? resolvedAriaLabel.value
|
|
196
199
|
: undefined
|
|
197
200
|
),
|
|
198
201
|
);
|
|
@@ -216,6 +219,7 @@ const {
|
|
|
216
219
|
boundaryPadding: () => 8,
|
|
217
220
|
flip: () => true,
|
|
218
221
|
shift: () => true,
|
|
222
|
+
constrainMainAxis: () => true,
|
|
219
223
|
widthMatch: () => props.width === 'trigger' ? 'min' : 'none',
|
|
220
224
|
zIndex: () => layerZIndex.value,
|
|
221
225
|
scrollStrategy: 'reposition',
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot v-if="acceptsNoOwnedAttributes" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { computed, provide } from 'vue'
|
|
7
|
+
|
|
8
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
9
|
+
import {
|
|
10
|
+
UI_KIT_LOCALE_CONTEXT,
|
|
11
|
+
resolveUiKitLocaleContext,
|
|
12
|
+
type UiKitLocaleContext,
|
|
13
|
+
} from '../../../internal/localeContext'
|
|
14
|
+
import { UI_KIT_LOCALES, type UiKitLocale, type UiKitMessagesOverride } from '../../../locales/locale'
|
|
15
|
+
import { createUiKitTranslate, mergeUiKitMessages, UI_KIT_MESSAGES } from '../../../locales/messages'
|
|
16
|
+
|
|
17
|
+
defineOptions({ inheritAttrs: false })
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
/** Locale for this subtree; omission inherits the nearest provider and ultimately defaults to ru. */
|
|
21
|
+
locale?: UiKitLocale
|
|
22
|
+
/** Exact deep partial override for this subtree. Unknown fields fail at runtime. */
|
|
23
|
+
messages?: UiKitMessagesOverride
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
defineSlots<{
|
|
27
|
+
default: () => unknown
|
|
28
|
+
}>()
|
|
29
|
+
|
|
30
|
+
const parent = resolveUiKitLocaleContext()
|
|
31
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SUiKitLocaleProvider', owner: 'DOM-less locale boundary' })
|
|
32
|
+
const locale = computed<UiKitLocale>(() => {
|
|
33
|
+
const value = props.locale ?? parent.locale.value
|
|
34
|
+
if (!UI_KIT_LOCALES.includes(value)) {
|
|
35
|
+
throw new TypeError(`SUiKitLocaleProvider: неизвестная locale ${JSON.stringify(value)}.`)
|
|
36
|
+
}
|
|
37
|
+
return value
|
|
38
|
+
})
|
|
39
|
+
const messages = computed(() => {
|
|
40
|
+
const base = props.locale === undefined || props.locale === parent.locale.value
|
|
41
|
+
? parent.messages.value
|
|
42
|
+
: UI_KIT_MESSAGES[locale.value]
|
|
43
|
+
return mergeUiKitMessages(base, props.messages)
|
|
44
|
+
})
|
|
45
|
+
const context: UiKitLocaleContext = Object.freeze({
|
|
46
|
+
locale,
|
|
47
|
+
messages,
|
|
48
|
+
t: createUiKitTranslate(() => messages.value),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
provide(UI_KIT_LOCALE_CONTEXT, context)
|
|
52
|
+
|
|
53
|
+
const acceptsNoOwnedAttributes = computed(() => {
|
|
54
|
+
ownedAttrs.reject('locale provider не создаёт DOM owner / locale provider does not render a DOM owner')
|
|
55
|
+
return true
|
|
56
|
+
})
|
|
57
|
+
</script>
|
|
@@ -28,20 +28,22 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.s-button[data-size='xs']:not([data-icon-only='true']) {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
block-size: var(--s-control-hitbox-xs);
|
|
32
|
+
min-block-size: var(--s-control-hitbox-xs);
|
|
33
|
+
padding: 0 0.5rem;
|
|
33
34
|
font-size: 0.75rem;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
.s-button[data-density='compact'][data-size='xs']:not([data-icon-only='true']) {
|
|
37
|
-
|
|
38
|
-
min-
|
|
38
|
+
block-size: var(--s-control-hitbox-2xs);
|
|
39
|
+
min-block-size: var(--s-control-hitbox-2xs);
|
|
39
40
|
padding-block: 0;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
.s-button[data-size='sm']:not([data-icon-only='true']) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
block-size: var(--s-control-hitbox-sm);
|
|
45
|
+
min-block-size: var(--s-control-hitbox-sm);
|
|
46
|
+
padding: 0 0.625rem;
|
|
45
47
|
font-size: 0.75rem;
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -57,14 +59,16 @@
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
.s-button[data-size='md']:not([data-icon-only='true']) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
block-size: var(--s-control-hitbox-md);
|
|
63
|
+
min-block-size: var(--s-control-hitbox-md);
|
|
64
|
+
padding: 0 1.25rem;
|
|
62
65
|
font-size: 0.875rem;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
.s-button[data-size='lg']:not([data-icon-only='true']) {
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
block-size: var(--s-control-hitbox-lg);
|
|
70
|
+
min-block-size: var(--s-control-hitbox-lg);
|
|
71
|
+
padding: 0 1.75rem;
|
|
68
72
|
font-size: 1.125rem;
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:id="inputId"
|
|
7
7
|
:model-value="validatedSearch"
|
|
8
8
|
:label="label"
|
|
9
|
-
:placeholder="placeholder"
|
|
9
|
+
:placeholder="contract.placeholder"
|
|
10
10
|
:disabled="disabled"
|
|
11
11
|
:readonly="readonly"
|
|
12
12
|
:required="required"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
:disabled="disabled"
|
|
40
40
|
:readonly="readonly"
|
|
41
41
|
:invalid="invalid"
|
|
42
|
-
:empty-label="emptyLabel"
|
|
42
|
+
:empty-label="contract.emptyLabel"
|
|
43
43
|
focus-mode="activedescendant"
|
|
44
44
|
:active-value="activeValue"
|
|
45
45
|
@update:model-value="onListboxUpdate"
|
|
@@ -95,6 +95,7 @@ type ComponentProps<TKey extends PublicSelectionKey> = SharedProps<TKey> & {
|
|
|
95
95
|
|
|
96
96
|
<script setup lang="ts" generic="TKey extends SelectionKey = string">
|
|
97
97
|
import { computed, nextTick, onBeforeUnmount, shallowRef, useId, watch } from 'vue'
|
|
98
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
98
99
|
import { useFloatingPosition } from '../../../composables/useFloatingPosition'
|
|
99
100
|
import { registerLayer, type LayerRegistration } from '../../../internal/layerStack'
|
|
100
101
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
@@ -121,18 +122,19 @@ defineOptions({ inheritAttrs: false })
|
|
|
121
122
|
const props = withDefaults(defineProps<ComponentProps<TKey>>(), {
|
|
122
123
|
filterMode: 'local',
|
|
123
124
|
label: undefined,
|
|
124
|
-
placeholder:
|
|
125
|
+
placeholder: undefined,
|
|
125
126
|
disabled: false,
|
|
126
127
|
readonly: false,
|
|
127
128
|
required: false,
|
|
128
129
|
invalid: false,
|
|
129
130
|
errorMessage: undefined,
|
|
130
131
|
helpText: undefined,
|
|
131
|
-
emptyLabel:
|
|
132
|
+
emptyLabel: undefined,
|
|
132
133
|
id: undefined,
|
|
133
134
|
size: 'md',
|
|
134
135
|
allowCustomValue: false,
|
|
135
136
|
})
|
|
137
|
+
const { t } = useUiKitLocale()
|
|
136
138
|
const emit = defineEmits<{
|
|
137
139
|
'update:modelValue': [value: TKey | null]
|
|
138
140
|
'update:search': [value: string]
|
|
@@ -162,12 +164,12 @@ const contract = computed(() => ({
|
|
|
162
164
|
filterMode: validateExactString(
|
|
163
165
|
'SCombobox', 'filterMode', props.filterMode, ['local', 'manual'] as const,
|
|
164
166
|
),
|
|
165
|
-
placeholder: validateNonEmptyString('SCombobox', 'placeholder', props.placeholder),
|
|
167
|
+
placeholder: validateNonEmptyString('SCombobox', 'placeholder', props.placeholder ?? t('forms.searchPlaceholder')),
|
|
166
168
|
disabled: validateBoolean('SCombobox', 'disabled', props.disabled),
|
|
167
169
|
readonly: validateBoolean('SCombobox', 'readonly', props.readonly),
|
|
168
170
|
required: validateBoolean('SCombobox', 'required', props.required),
|
|
169
171
|
invalid: validateBoolean('SCombobox', 'invalid', props.invalid),
|
|
170
|
-
emptyLabel: validateNonEmptyString('SCombobox', 'emptyLabel', props.emptyLabel),
|
|
172
|
+
emptyLabel: validateNonEmptyString('SCombobox', 'emptyLabel', props.emptyLabel ?? t('forms.noOptions')),
|
|
171
173
|
size: validateExactString('SCombobox', 'size', props.size, ['sm', 'md', 'lg'] as const),
|
|
172
174
|
allowCustomValue: validateBoolean('SCombobox', 'allowCustomValue', props.allowCustomValue),
|
|
173
175
|
label: validateOptionalSelectionString('SCombobox', 'label', props.label),
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
.s-combobox-trigger[data-width='full'] { @apply w-full; }
|
|
8
8
|
.s-combobox-trigger[data-width='auto'] { @apply w-auto; }
|
|
9
|
-
.s-combobox-trigger[data-size='xs'] {
|
|
10
|
-
.s-combobox-trigger[data-size='sm'] {
|
|
11
|
-
.s-combobox-trigger[data-size='md'] {
|
|
12
|
-
.s-combobox-trigger[data-appearance='swatch'] {
|
|
9
|
+
.s-combobox-trigger[data-size='xs'] { block-size: var(--s-control-hitbox-xs); @apply px-2 text-xs; }
|
|
10
|
+
.s-combobox-trigger[data-size='sm'] { block-size: var(--s-control-hitbox-sm); @apply px-2.5 text-xs; }
|
|
11
|
+
.s-combobox-trigger[data-size='md'] { block-size: var(--s-control-hitbox-md); @apply text-sm; }
|
|
12
|
+
.s-combobox-trigger[data-appearance='swatch'] { @apply justify-center rounded-full border border-transparent bg-transparent p-1; box-shadow: none; }
|
|
13
|
+
.s-combobox-trigger[data-appearance='swatch'][data-size='xs'] { inline-size: var(--s-control-hitbox-xs); }
|
|
14
|
+
.s-combobox-trigger[data-appearance='swatch'][data-size='sm'] { inline-size: var(--s-control-hitbox-sm); }
|
|
15
|
+
.s-combobox-trigger[data-appearance='swatch'][data-size='md'] { inline-size: var(--s-control-hitbox-md); }
|
|
13
16
|
.s-combobox-trigger[data-appearance='swatch']:disabled { @apply cursor-not-allowed opacity-50; }
|
|
14
17
|
|
|
15
18
|
.s-combobox-trigger__value {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
>
|
|
30
30
|
<SMarker v-if="marker" :marker="marker" :size="appearance === 'swatch' ? 'lg' : 'sm'" />
|
|
31
31
|
<span v-if="appearance === 'field'" class="s-combobox-trigger__value">
|
|
32
|
-
{{ displayValue ||
|
|
32
|
+
{{ displayValue || resolvedPlaceholder }}
|
|
33
33
|
</span>
|
|
34
34
|
<span class="s-combobox-trigger__caret" aria-hidden="true">
|
|
35
35
|
<ChevronsUpDown v-if="appearance === 'field'" />
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
<script setup lang="ts">
|
|
42
42
|
import { computed, ref } from 'vue'
|
|
43
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
43
44
|
import { ChevronDown, ChevronsUpDown } from '../../icons/sputnigUiIcons'
|
|
44
45
|
import type { InteractiveElementApi } from '../../../internal/interactiveElement'
|
|
45
46
|
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
@@ -76,7 +77,7 @@ defineOptions({ inheritAttrs: false })
|
|
|
76
77
|
|
|
77
78
|
const props = withDefaults(defineProps<Props>(), {
|
|
78
79
|
displayValue: undefined,
|
|
79
|
-
placeholder:
|
|
80
|
+
placeholder: undefined,
|
|
80
81
|
marker: undefined,
|
|
81
82
|
appearance: 'field',
|
|
82
83
|
size: 'md',
|
|
@@ -92,6 +93,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
92
93
|
taskMappingInternalValue: undefined,
|
|
93
94
|
taskMappingKind: undefined,
|
|
94
95
|
})
|
|
96
|
+
const { t } = useUiKitLocale()
|
|
97
|
+
const resolvedPlaceholder = computed(() => props.placeholder ?? t('forms.selectPlaceholder'))
|
|
95
98
|
|
|
96
99
|
const emit = defineEmits<{
|
|
97
100
|
toggle: [event: MouseEvent | KeyboardEvent]
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
19
|
import { computed, type Component } from 'vue'
|
|
20
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
20
21
|
import { Minus, MinusCircle, PlusCircle } from '../../icons/sputnigUiIcons'
|
|
21
22
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
22
23
|
import { validateNonEmptyString } from '../../../internal/runtimeContract'
|
|
@@ -36,11 +37,12 @@ export interface Props {
|
|
|
36
37
|
|
|
37
38
|
const props = withDefaults(defineProps<Props>(), {
|
|
38
39
|
partial: false,
|
|
39
|
-
addTitle:
|
|
40
|
-
removeTitle:
|
|
40
|
+
addTitle: undefined,
|
|
41
|
+
removeTitle: undefined,
|
|
41
42
|
size: 'sm',
|
|
42
43
|
loading: false,
|
|
43
44
|
})
|
|
45
|
+
const { t } = useUiKitLocale()
|
|
44
46
|
|
|
45
47
|
const emit = defineEmits<{
|
|
46
48
|
(e: 'click'): void
|
|
@@ -48,8 +50,8 @@ const emit = defineEmits<{
|
|
|
48
50
|
const ownedAttrs = useOwnedAttrs({ component: 'SContextToggleButton', owner: 'toggle button' })
|
|
49
51
|
|
|
50
52
|
const accessibleLabels = computed(() => ({
|
|
51
|
-
add: validateNonEmptyString('SContextToggleButton', 'addTitle', props.addTitle),
|
|
52
|
-
remove: validateNonEmptyString('SContextToggleButton', 'removeTitle', props.removeTitle),
|
|
53
|
+
add: validateNonEmptyString('SContextToggleButton', 'addTitle', props.addTitle ?? t('forms.addToContext')),
|
|
54
|
+
remove: validateNonEmptyString('SContextToggleButton', 'removeTitle', props.removeTitle ?? t('forms.removeFromContext')),
|
|
53
55
|
}))
|
|
54
56
|
const accessibleTitle = computed(() => (
|
|
55
57
|
props.inContext ? accessibleLabels.value.remove : accessibleLabels.value.add
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<SButton
|
|
3
3
|
v-bind="ownedAttrs.bindings()"
|
|
4
|
-
size="
|
|
4
|
+
:size="size"
|
|
5
5
|
severity="secondary"
|
|
6
6
|
appearance="ghost"
|
|
7
7
|
icon-only
|
|
@@ -22,20 +22,23 @@
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
23
|
import { computed, ref, watchEffect } from 'vue'
|
|
24
24
|
import { getSputnigUiIconComponent } from '../../icons/sputnigUiIcons'
|
|
25
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
25
26
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
26
27
|
import { validateNonEmptyString } from '../../../internal/runtimeContract'
|
|
27
|
-
import SButton from './SButton.vue'
|
|
28
|
+
import SButton, { type SButtonSize } from './SButton.vue'
|
|
28
29
|
|
|
29
30
|
defineOptions({ inheritAttrs: false })
|
|
30
31
|
|
|
31
32
|
const props = withDefaults(defineProps<{
|
|
32
33
|
label: string
|
|
33
34
|
orientation?: 'vertical' | 'horizontal'
|
|
35
|
+
size?: SButtonSize
|
|
34
36
|
canMovePrevious?: boolean
|
|
35
37
|
canMoveNext?: boolean
|
|
36
38
|
disabled?: boolean
|
|
37
39
|
}>(), {
|
|
38
40
|
orientation: 'vertical',
|
|
41
|
+
size: 'sm',
|
|
39
42
|
canMovePrevious: false,
|
|
40
43
|
canMoveNext: false,
|
|
41
44
|
disabled: false,
|
|
@@ -49,14 +52,15 @@ const emit = defineEmits<{
|
|
|
49
52
|
|
|
50
53
|
const DragIcon = getSputnigUiIconComponent('drag')
|
|
51
54
|
const ownedAttrs = useOwnedAttrs({ component: 'SDragHandle', owner: 'drag handle command' })
|
|
55
|
+
const { t } = useUiKitLocale()
|
|
52
56
|
const keyboardActive = ref(false)
|
|
53
57
|
const validatedLabel = computed(() => validateNonEmptyString('SDragHandle', 'label', props.label))
|
|
54
58
|
const keyboardShortcuts = computed(() => props.orientation === 'vertical'
|
|
55
59
|
? 'Enter Space ArrowUp ArrowDown Escape'
|
|
56
60
|
: 'Enter Space ArrowLeft ArrowRight Escape')
|
|
57
61
|
const accessibleLabel = computed(() => keyboardActive.value
|
|
58
|
-
?
|
|
59
|
-
:
|
|
62
|
+
? t('layout.dragActive', { label: validatedLabel.value })
|
|
63
|
+
: t('layout.dragIdle', { label: validatedLabel.value }))
|
|
60
64
|
|
|
61
65
|
function setKeyboardActive(active: boolean): void {
|
|
62
66
|
if (keyboardActive.value === active) return
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
v-if="isOpen"
|
|
14
14
|
ref="layerRef"
|
|
15
15
|
kind="dropdown"
|
|
16
|
-
:landmark-label="
|
|
16
|
+
:landmark-label="resolvedMenuLabel"
|
|
17
17
|
:position="position"
|
|
18
18
|
:z-index="zIndex"
|
|
19
19
|
>
|
|
20
20
|
<SMenuSurface
|
|
21
21
|
ref="menuSurfaceRef"
|
|
22
22
|
:id="menuId"
|
|
23
|
-
:label="
|
|
23
|
+
:label="resolvedMenuLabel"
|
|
24
24
|
density="compact"
|
|
25
25
|
autofocus
|
|
26
26
|
:initial-focus="initialFocus"
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type VNodeRef,
|
|
49
49
|
} from 'vue'
|
|
50
50
|
import { useOwnedAttrs, type OwnedAttributeBindings } from '../../../internal/ownedAttrs'
|
|
51
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
51
52
|
import { useMenuLayer } from '../../../internal/useMenuLayer'
|
|
52
53
|
import SMenuLayer from '../_internal/SMenuLayer.vue'
|
|
53
54
|
import type { SMenuLayerApi } from '../_internal/SMenuLayer.vue'
|
|
@@ -63,8 +64,10 @@ const props = withDefaults(defineProps<{
|
|
|
63
64
|
menuLabel?: string
|
|
64
65
|
}>(), {
|
|
65
66
|
align: 'right',
|
|
66
|
-
menuLabel:
|
|
67
|
+
menuLabel: undefined,
|
|
67
68
|
})
|
|
69
|
+
const { t } = useUiKitLocale()
|
|
70
|
+
const resolvedMenuLabel = computed(() => props.menuLabel ?? t('dialog.moreActions'))
|
|
68
71
|
const emit = defineEmits<{
|
|
69
72
|
select: [entry: SMenuSelectableEntry, event: SMenuActivationEvent]
|
|
70
73
|
}>()
|
|
@@ -180,7 +183,7 @@ function select(item: SMenuSelectableEntry, event: SMenuActivationEvent): void {
|
|
|
180
183
|
|
|
181
184
|
watchEffect(() => {
|
|
182
185
|
validateSMenuEntries(props.items)
|
|
183
|
-
if (!
|
|
186
|
+
if (!resolvedMenuLabel.value.trim()) throw new TypeError('SDropdownMenu: menuLabel must be a non-empty string')
|
|
184
187
|
if (props.align !== 'left' && props.align !== 'right') throw new TypeError("SDropdownMenu: align must be 'left' or 'right'")
|
|
185
188
|
})
|
|
186
189
|
</script>
|
|
@@ -11,16 +11,17 @@
|
|
|
11
11
|
@apply block w-full min-w-0 box-border;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.s-input-text[data-size='sm'] { @apply px-2.5 py-
|
|
15
|
-
.s-input-text[data-size='md'] { @apply px-3 py-
|
|
16
|
-
.s-input-text[data-size='lg'] { @apply px-4 py-
|
|
14
|
+
.s-input-text[data-size='sm'] { block-size: var(--s-control-hitbox-sm); @apply px-2.5 py-0 text-xs; }
|
|
15
|
+
.s-input-text[data-size='md'] { block-size: var(--s-control-hitbox-md); @apply px-3 py-0 text-sm; }
|
|
16
|
+
.s-input-text[data-size='lg'] { block-size: var(--s-control-hitbox-lg); @apply px-4 py-0 text-lg; }
|
|
17
17
|
|
|
18
18
|
.s-input-text--monospace {
|
|
19
19
|
@apply font-mono;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.s-input-text[data-appearance='grid-cell'] {
|
|
23
|
-
|
|
23
|
+
block-size: var(--s-density-row-compact);
|
|
24
|
+
@apply rounded-none border-transparent bg-transparent px-2 py-0 text-sm shadow-none;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
.s-input-text[data-appearance='workbench-title'] {
|
|
@@ -140,6 +140,7 @@ type ComponentProps<TKey extends PublicSelectionKey> = SharedProps<TKey> & (
|
|
|
140
140
|
|
|
141
141
|
<script setup lang="ts" generic="TKey extends SelectionKey = string">
|
|
142
142
|
import { computed, onBeforeUnmount, provide, useId } from 'vue'
|
|
143
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
143
144
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
144
145
|
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
145
146
|
import {
|
|
@@ -168,12 +169,13 @@ const props = withDefaults(defineProps<ComponentProps<TKey>>(), {
|
|
|
168
169
|
readonly: false,
|
|
169
170
|
invalid: false,
|
|
170
171
|
variant: 'select',
|
|
171
|
-
emptyLabel:
|
|
172
|
+
emptyLabel: undefined,
|
|
172
173
|
clearOptionLabel: undefined,
|
|
173
174
|
id: undefined,
|
|
174
175
|
focusMode: 'roving',
|
|
175
176
|
activeValue: undefined,
|
|
176
177
|
})
|
|
178
|
+
const { t } = useUiKitLocale()
|
|
177
179
|
const emit = defineEmits<{
|
|
178
180
|
'update:modelValue': [value: TKey | null] | [value: readonly TKey[]]
|
|
179
181
|
'update:activeValue': [value: TKey | null]
|
|
@@ -200,7 +202,7 @@ const contract = computed(() => {
|
|
|
200
202
|
? `s-listbox-${generatedId}`
|
|
201
203
|
: validateDomId('SListbox', 'id', props.id),
|
|
202
204
|
label: validateNonEmptyString('SListbox', 'label', props.label),
|
|
203
|
-
emptyLabel: validateNonEmptyString('SListbox', 'emptyLabel', props.emptyLabel),
|
|
205
|
+
emptyLabel: validateNonEmptyString('SListbox', 'emptyLabel', props.emptyLabel ?? t('forms.noOptions')),
|
|
204
206
|
variant: validateExactString(
|
|
205
207
|
'SListbox', 'variant', props.variant, ['select', 'color'] as const,
|
|
206
208
|
),
|
|
@@ -105,6 +105,7 @@ type ComponentProps<TKey extends PublicSelectionKey> = SharedProps<TKey> & (
|
|
|
105
105
|
|
|
106
106
|
<script setup lang="ts" generic="TKey extends SelectionKey = string">
|
|
107
107
|
import { computed, nextTick, onBeforeUnmount, ref, useId } from 'vue'
|
|
108
|
+
import { useUiKitLocale } from '../../../composables/useUiKitLocale'
|
|
108
109
|
import { useFloatingPosition } from '../../../composables/useFloatingPosition'
|
|
109
110
|
import type { FloatingPlacement } from '../../../composables/useFloatingPosition'
|
|
110
111
|
import { findRelativeTabTarget } from '../../../internal/focusNavigation'
|
|
@@ -135,7 +136,7 @@ defineOptions({ inheritAttrs: false })
|
|
|
135
136
|
|
|
136
137
|
const props = withDefaults(defineProps<ComponentProps<TKey>>(), {
|
|
137
138
|
label: undefined,
|
|
138
|
-
placeholder:
|
|
139
|
+
placeholder: undefined,
|
|
139
140
|
disabled: false,
|
|
140
141
|
readonly: false,
|
|
141
142
|
required: false,
|
|
@@ -151,6 +152,7 @@ const props = withDefaults(defineProps<ComponentProps<TKey>>(), {
|
|
|
151
152
|
title: undefined,
|
|
152
153
|
selectionMode: undefined,
|
|
153
154
|
})
|
|
155
|
+
const { t } = useUiKitLocale()
|
|
154
156
|
const emit = defineEmits<{
|
|
155
157
|
'update:modelValue': [value: TKey | null] | [value: readonly TKey[]]
|
|
156
158
|
}>()
|
|
@@ -176,7 +178,7 @@ const contract = computed(() => {
|
|
|
176
178
|
label: props.label === undefined
|
|
177
179
|
? undefined
|
|
178
180
|
: validateNonEmptyString('SSelect', 'label', props.label),
|
|
179
|
-
placeholder: validateNonEmptyString('SSelect', 'placeholder', props.placeholder),
|
|
181
|
+
placeholder: validateNonEmptyString('SSelect', 'placeholder', props.placeholder ?? t('forms.selectPlaceholder')),
|
|
180
182
|
disabled: validateBoolean('SSelect', 'disabled', props.disabled),
|
|
181
183
|
readonly: validateBoolean('SSelect', 'readonly', props.readonly),
|
|
182
184
|
required: validateBoolean('SSelect', 'required', props.required),
|