@pgcorp/ui-kit 0.6.0 → 0.7.1
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 +57 -0
- package/docs/public-api.md +6 -0
- package/docs/theming.md +58 -0
- package/package.json +5 -1
- package/src/components/shared/_internal/SToastItem.css +8 -8
- package/src/components/shared/_internal/SToastItem.vue +8 -1
- package/src/components/shared/controls/SButton.css +6 -6
- package/src/components/shared/controls/SButton.vue +9 -1
- package/src/components/shared/controls/SInteractiveSurface.css +3 -3
- package/src/components/shared/controls/SInteractiveSurface.vue +9 -2
- package/src/components/shared/controls/SMarker.css +16 -16
- package/src/components/shared/controls/SMarker.vue +27 -1
- package/src/components/shared/data-display/SActionListItem.css +2 -2
- package/src/components/shared/data-display/SActionListItem.vue +8 -1
- package/src/components/shared/data-display/SMessage.css +8 -8
- package/src/components/shared/data-display/SMessage.vue +8 -1
- package/src/components/shared/data-display/SProgressBar.css +12 -12
- package/src/components/shared/data-display/SProgressBar.vue +12 -6
- package/src/components/shared/data-display/SProgressIndicator.css +5 -5
- package/src/components/shared/data-display/SProgressIndicator.vue +9 -1
- package/src/components/shared/data-display/SStatus.css +10 -10
- package/src/components/shared/data-display/SStatus.vue +9 -1
- package/src/components/shared/data-display/STable.vue +27 -4
- package/src/components/shared/data-display/STooltip.css +4 -4
- package/src/components/shared/data-display/STooltip.vue +159 -29
- package/src/components/shared/data-display/STooltipTarget.css +26 -0
- package/src/components/shared/data-display/STooltipTarget.vue +94 -0
- package/src/components/shared/navigation/STabs.vue +11 -3
- package/src/internal/ownedAttrs.ts +3 -0
- package/src/internal/pillSurface.ts +4 -3
- package/src/internal/semanticTone.ts +23 -0
- package/src/styles/style.css +7 -7
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-tooltip-target {
|
|
4
|
+
@apply inline-flex min-w-0 shrink-0 items-center justify-center align-middle;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.s-tooltip-target[data-appearance='keycap'] {
|
|
8
|
+
@apply border border-surface-300 bg-surface-100 font-mono font-semibold text-surface-700 shadow-sm;
|
|
9
|
+
border-radius: var(--radius-xs);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.s-tooltip-target[data-appearance='keycap'][data-size='xs'] {
|
|
13
|
+
@apply min-h-4 min-w-4 px-1 text-[0.625rem] leading-none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.s-tooltip-target[data-appearance='keycap'][data-size='sm'] {
|
|
17
|
+
@apply min-h-5 min-w-5 px-1.5 text-xs leading-none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.s-tooltip-target[data-appearance='keycap'][data-size='md'] {
|
|
21
|
+
@apply min-h-6 min-w-6 px-2 text-sm leading-none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:global(.dark .s-tooltip-target[data-appearance='keycap']) {
|
|
25
|
+
@apply border-surface-600 bg-surface-800 text-surface-200;
|
|
26
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="rootTag"
|
|
4
|
+
ref="targetRef"
|
|
5
|
+
v-bind="ownedAttrs.bindings()"
|
|
6
|
+
class="s-tooltip-target"
|
|
7
|
+
:data-appearance="resolvedAppearance"
|
|
8
|
+
:data-size="resolvedSize"
|
|
9
|
+
@pointerenter="emit('pointerenter', $event)"
|
|
10
|
+
@pointerleave="emit('pointerleave', $event)"
|
|
11
|
+
>
|
|
12
|
+
<slot />
|
|
13
|
+
</component>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { computed, ref, type VNodeChild } from 'vue'
|
|
18
|
+
import type { ElementApi } from '../../../internal/interactiveElement'
|
|
19
|
+
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
20
|
+
import { validateExactString } from '../../../internal/runtimeContract'
|
|
21
|
+
|
|
22
|
+
defineOptions({
|
|
23
|
+
name: 'STooltipTarget',
|
|
24
|
+
inheritAttrs: false,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export type STooltipTargetTag = 'span' | 'kbd'
|
|
28
|
+
export type STooltipTargetAppearance = 'plain' | 'keycap'
|
|
29
|
+
export type STooltipTargetSize = 'xs' | 'sm' | 'md'
|
|
30
|
+
export type STooltipTargetApi = ElementApi<HTMLElement>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Пассивная pointer/geometry target для split-контракта STooltip.
|
|
34
|
+
* Passive pointer and geometry target for the STooltip split contract.
|
|
35
|
+
*/
|
|
36
|
+
export interface Props {
|
|
37
|
+
/** Семантический HTML root без собственной интерактивности. / Semantic HTML root without its own interactivity. */
|
|
38
|
+
as?: STooltipTargetTag
|
|
39
|
+
/** Нейтральная или keycap presentation. / Plain or keycap presentation. */
|
|
40
|
+
appearance?: STooltipTargetAppearance
|
|
41
|
+
/** Централизованная visual density target. / Centralized visual density of the target. */
|
|
42
|
+
size?: STooltipTargetSize
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
46
|
+
as: 'span',
|
|
47
|
+
appearance: 'plain',
|
|
48
|
+
size: 'sm',
|
|
49
|
+
})
|
|
50
|
+
const emit = defineEmits<{
|
|
51
|
+
pointerenter: [event: PointerEvent]
|
|
52
|
+
pointerleave: [event: PointerEvent]
|
|
53
|
+
}>()
|
|
54
|
+
const slots = defineSlots<{
|
|
55
|
+
/** Пассивное содержимое target. / Passive target content. */
|
|
56
|
+
default: () => VNodeChild
|
|
57
|
+
}>()
|
|
58
|
+
|
|
59
|
+
if (!slots.default) {
|
|
60
|
+
throw new Error('STooltipTarget: default slot обязателен. / STooltipTarget: the default slot is required.')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const targetRef = ref<HTMLElement | null>(null)
|
|
64
|
+
const ownedAttrs = useOwnedAttrs({ component: 'STooltipTarget', owner: 'passive tooltip target' })
|
|
65
|
+
const resolvedAs = computed(() => validateExactString(
|
|
66
|
+
'STooltipTarget',
|
|
67
|
+
'as',
|
|
68
|
+
props.as,
|
|
69
|
+
['span', 'kbd'] as const,
|
|
70
|
+
))
|
|
71
|
+
const rootTag = computed(() => resolvedAs.value === 'kbd' ? 'kbd' : 'span')
|
|
72
|
+
const resolvedAppearance = computed(() => validateExactString(
|
|
73
|
+
'STooltipTarget',
|
|
74
|
+
'appearance',
|
|
75
|
+
props.appearance,
|
|
76
|
+
['plain', 'keycap'] as const,
|
|
77
|
+
))
|
|
78
|
+
const resolvedSize = computed(() => validateExactString(
|
|
79
|
+
'STooltipTarget',
|
|
80
|
+
'size',
|
|
81
|
+
props.size,
|
|
82
|
+
['xs', 'sm', 'md'] as const,
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
const getElement = (): HTMLElement => {
|
|
86
|
+
const element = targetRef.value
|
|
87
|
+
if (!element) throw new Error('STooltipTarget: native target element is unavailable')
|
|
88
|
+
return element
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
defineExpose<STooltipTargetApi>({ getElement })
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<style lang="postcss" src="./STooltipTarget.css" scoped></style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-bind="ownedAttrs.bindings()"
|
|
4
|
+
:data-s-tone="toneBinding['data-s-tone']"
|
|
4
5
|
class="s-tabs"
|
|
5
6
|
:data-testid="testId"
|
|
6
7
|
:data-composition="composition"
|
|
@@ -9,7 +10,6 @@
|
|
|
9
10
|
:data-density="tabDensity"
|
|
10
11
|
:data-sizing="tabSizing"
|
|
11
12
|
:data-list-width="tabListWidth"
|
|
12
|
-
:data-tone="tone"
|
|
13
13
|
:data-close-mode="closeButtonMode"
|
|
14
14
|
:data-close-visibility="closeButtonVisibility"
|
|
15
15
|
:data-radius="surfaceRadius"
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
type VNodeChild,
|
|
59
59
|
} from 'vue'
|
|
60
60
|
import { useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
61
|
+
import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
|
|
61
62
|
import { lastItem } from '../../../internal/es2020'
|
|
62
63
|
import {
|
|
63
64
|
tabsContextKey,
|
|
@@ -80,6 +81,7 @@ export type STabsComposition = 'managed' | 'custom'
|
|
|
80
81
|
export type STabsVariant = 'default' | 'flat' | 'bottom' | 'icon-only' | 'icon-with-label'
|
|
81
82
|
export type STabsActivationMode = 'automatic' | 'manual'
|
|
82
83
|
export type STabsTone = 'primary' | 'success'
|
|
84
|
+
const TABS_TONES = ['primary', 'success'] as const satisfies readonly STabsTone[]
|
|
83
85
|
export type STabsSizing =
|
|
84
86
|
| 'content'
|
|
85
87
|
| 'compact'
|
|
@@ -176,6 +178,12 @@ const slots = defineSlots<{
|
|
|
176
178
|
controls?: () => VNodeChild
|
|
177
179
|
}>()
|
|
178
180
|
const ownedAttrs = useOwnedAttrs({ component: 'STabs', owner: 'tabs state and layout root' })
|
|
181
|
+
const toneBinding = computed(() => resolveSemanticToneBinding(
|
|
182
|
+
'STabs',
|
|
183
|
+
'tone',
|
|
184
|
+
props.tone,
|
|
185
|
+
TABS_TONES,
|
|
186
|
+
))
|
|
179
187
|
|
|
180
188
|
function assertStableKey(value: unknown, coordinate: string): asserts value is string {
|
|
181
189
|
if (typeof value !== 'string' || !value.trim() || value !== value.trim()) {
|
|
@@ -569,12 +577,12 @@ defineExpose({
|
|
|
569
577
|
--s-tab-selected-border-color: var(--color-primary-400);
|
|
570
578
|
--s-tab-disabled-color: var(--color-surface-600);
|
|
571
579
|
}
|
|
572
|
-
.s-tabs[data-tone='success'] {
|
|
580
|
+
.s-tabs[data-s-tone='success'] {
|
|
573
581
|
--s-tab-hover-color: var(--color-success-700);
|
|
574
582
|
--s-tab-selected-color: var(--color-success-700);
|
|
575
583
|
--s-tab-selected-border-color: var(--color-success-600);
|
|
576
584
|
}
|
|
577
|
-
:global(.dark) .s-tabs[data-tone='success'] {
|
|
585
|
+
:global(.dark) .s-tabs[data-s-tone='success'] {
|
|
578
586
|
--s-tab-hover-color: var(--color-success-300);
|
|
579
587
|
--s-tab-selected-color: var(--color-success-300);
|
|
580
588
|
--s-tab-selected-border-color: var(--color-success-400);
|
|
@@ -294,6 +294,9 @@ export const ownedAttributeSchemas = Object.freeze({
|
|
|
294
294
|
'data-testid': 'data',
|
|
295
295
|
id: 'string',
|
|
296
296
|
}),
|
|
297
|
+
STooltipTarget: defineOwnedAttributeSchema({
|
|
298
|
+
'data-testid': 'data',
|
|
299
|
+
}),
|
|
297
300
|
SVirtualList: defineOwnedAttributeSchema({}),
|
|
298
301
|
SAsyncState: defineOwnedAttributeSchema({}),
|
|
299
302
|
SDataGrid: defineOwnedAttributeSchema({}),
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
validateBoolean,
|
|
3
3
|
validateExactString,
|
|
4
4
|
} from './runtimeContract'
|
|
5
|
+
import { resolveSemanticToneBinding } from './semanticTone'
|
|
5
6
|
|
|
6
7
|
const PILL_KINDS = ['badge', 'chip'] as const
|
|
7
8
|
const PILL_SEVERITIES = [
|
|
@@ -43,7 +44,7 @@ export interface PillSurfaceContract {
|
|
|
43
44
|
|
|
44
45
|
export interface PillSurfaceBindings {
|
|
45
46
|
'data-s-pill-surface': PillSurfaceKind
|
|
46
|
-
'data-s-
|
|
47
|
+
'data-s-tone': PillSurfaceSeverity
|
|
47
48
|
'data-s-pill-size': PillSurfaceSize
|
|
48
49
|
'data-s-pill-dot'?: 'true'
|
|
49
50
|
'data-s-pill-closable'?: 'true'
|
|
@@ -62,7 +63,7 @@ export function resolvePillSurfaceBindings(
|
|
|
62
63
|
contract: PillSurfaceContract,
|
|
63
64
|
): PillSurfaceBindings {
|
|
64
65
|
const kind = validateExactString(owner, 'pill kind', contract.kind, PILL_KINDS)
|
|
65
|
-
const
|
|
66
|
+
const toneBinding = resolveSemanticToneBinding(owner, 'severity', contract.severity, PILL_SEVERITIES)
|
|
66
67
|
const size = validateExactString(owner, 'size', contract.size, PILL_SIZES)
|
|
67
68
|
const maxWidth = validateExactString(owner, 'maxWidth', contract.maxWidth, PILL_MAX_WIDTHS)
|
|
68
69
|
const iconMotion = validateExactString(owner, 'iconMotion', contract.iconMotion, PILL_ICON_MOTIONS)
|
|
@@ -80,7 +81,7 @@ export function resolvePillSurfaceBindings(
|
|
|
80
81
|
|
|
81
82
|
return {
|
|
82
83
|
'data-s-pill-surface': kind,
|
|
83
|
-
|
|
84
|
+
...toneBinding,
|
|
84
85
|
'data-s-pill-size': size,
|
|
85
86
|
...(dot ? { 'data-s-pill-dot': 'true' as const } : {}),
|
|
86
87
|
...(closable ? { 'data-s-pill-closable': 'true' as const } : {}),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { validateExactString } from './runtimeContract'
|
|
2
|
+
|
|
3
|
+
/** Единственный internal DOM marker для component-owned visual tone. */
|
|
4
|
+
export const SEMANTIC_TONE_ATTRIBUTE = 'data-s-tone' as const
|
|
5
|
+
|
|
6
|
+
export type SemanticToneBinding<Tone extends string> = Readonly<{
|
|
7
|
+
'data-s-tone': Tone
|
|
8
|
+
}>
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Проверяет component-specific tone vocabulary и создаёт единый namespaced DOM binding.
|
|
12
|
+
* Validates a component-specific tone vocabulary and creates the canonical namespaced DOM binding.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveSemanticToneBinding<const Tones extends readonly string[]>(
|
|
15
|
+
owner: string,
|
|
16
|
+
coordinate: string,
|
|
17
|
+
value: unknown,
|
|
18
|
+
tones: Tones,
|
|
19
|
+
): SemanticToneBinding<Tones[number]> {
|
|
20
|
+
return {
|
|
21
|
+
[SEMANTIC_TONE_ATTRIBUTE]: validateExactString(owner, coordinate, value, tones),
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/styles/style.css
CHANGED
|
@@ -26,37 +26,37 @@ body {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
@layer components {
|
|
29
|
-
:where([data-s-
|
|
29
|
+
:where([data-s-tone="primary"]) {
|
|
30
30
|
--s-feedback-tone-background: var(--s-feedback-primary-background);
|
|
31
31
|
--s-feedback-tone-foreground: var(--s-feedback-primary-foreground);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
:where([data-s-
|
|
34
|
+
:where([data-s-tone="secondary"]) {
|
|
35
35
|
--s-feedback-tone-background: var(--s-feedback-secondary-background);
|
|
36
36
|
--s-feedback-tone-foreground: var(--s-feedback-secondary-foreground);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
:where([data-s-
|
|
39
|
+
:where([data-s-tone="success"]) {
|
|
40
40
|
--s-feedback-tone-background: var(--s-feedback-success-background);
|
|
41
41
|
--s-feedback-tone-foreground: var(--s-feedback-success-foreground);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
:where([data-s-
|
|
44
|
+
:where([data-s-tone="info"]) {
|
|
45
45
|
--s-feedback-tone-background: var(--s-feedback-info-background);
|
|
46
46
|
--s-feedback-tone-foreground: var(--s-feedback-info-foreground);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
:where([data-s-
|
|
49
|
+
:where([data-s-tone="warn"]) {
|
|
50
50
|
--s-feedback-tone-background: var(--s-feedback-warn-background);
|
|
51
51
|
--s-feedback-tone-foreground: var(--s-feedback-warn-foreground);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
:where([data-s-
|
|
54
|
+
:where([data-s-tone="danger"]) {
|
|
55
55
|
--s-feedback-tone-background: var(--s-feedback-danger-background);
|
|
56
56
|
--s-feedback-tone-foreground: var(--s-feedback-danger-foreground);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
:where([data-s-
|
|
59
|
+
:where([data-s-tone="contrast"]) {
|
|
60
60
|
--s-feedback-tone-background: var(--s-feedback-contrast-background);
|
|
61
61
|
--s-feedback-tone-foreground: var(--s-feedback-contrast-foreground);
|
|
62
62
|
}
|