@feugene/granularity 0.47.0 → 0.49.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 +3 -3
- package/dist/chunks/{config-2vrOFn2f.js → config-AKpLyIGD.js} +2 -2
- package/dist/chunks/{config-2vrOFn2f.js.map → config-AKpLyIGD.js.map} +1 -1
- package/dist/chunks/config-BBtBQE3s.js +108 -0
- package/dist/chunks/config-BBtBQE3s.js.map +1 -0
- package/dist/chunks/{config-DeyT9dJl.js → config-Bs1HwUcx.js} +47 -4
- package/dist/chunks/{config-DeyT9dJl.js.map → config-Bs1HwUcx.js.map} +1 -1
- package/dist/chunks/{config-B3J0alyY.js → config-CzeZg2LO.js} +49 -4
- package/dist/chunks/config-CzeZg2LO.js.map +1 -0
- package/dist/chunks/{config-DCq4JySZ.js → config-D3HOuHDe.js} +1 -1
- package/dist/chunks/{config-DCq4JySZ.js.map → config-D3HOuHDe.js.map} +1 -1
- package/dist/chunks/{config-BscVxZR3.js → config-DF1ezU6y.js} +1 -1
- package/dist/chunks/{config-BscVxZR3.js.map → config-DF1ezU6y.js.map} +1 -1
- package/dist/chunks/config-MUkhktTP.js +267 -0
- package/dist/chunks/config-MUkhktTP.js.map +1 -0
- package/dist/chunks/{en-DjTnfTqM.js → en-CsQUoBS2.js} +14 -2
- package/dist/chunks/en-CsQUoBS2.js.map +1 -0
- package/dist/chunks/{es-Bc_t3FI8.js → es-9nbkPvdA.js} +14 -2
- package/dist/chunks/es-9nbkPvdA.js.map +1 -0
- package/dist/chunks/{es-DIFzSXSZ.js → es-DrUz5J6I.js} +3 -3
- package/dist/chunks/{es-DIFzSXSZ.js.map → es-DrUz5J6I.js.map} +1 -1
- package/dist/chunks/{generated-CQ4h6Ieq.js → generated-CxvC5O0V.js} +140 -0
- package/dist/chunks/{generated-CQ4h6Ieq.js.map → generated-CxvC5O0V.js.map} +1 -1
- package/dist/chunks/{grBadgeStyles-DV0DrAew.js → grBadgeStyles-D51AXbj5.js} +57 -1
- package/dist/chunks/grBadgeStyles-D51AXbj5.js.map +1 -0
- package/dist/chunks/{grChipStyles-DcDyxAL0.js → grChipStyles-CA9Wj1_E.js} +1 -1
- package/dist/chunks/{grChipStyles-DcDyxAL0.js.map → grChipStyles-CA9Wj1_E.js.map} +1 -1
- package/dist/chunks/{grTableStyles-uuMPSOBR.js → grTableStyles-DkQPMfx9.js} +16 -3
- package/dist/chunks/grTableStyles-DkQPMfx9.js.map +1 -0
- package/dist/chunks/{granular-provider-AKukmYva.js → granular-provider-B6xomapW.js} +8 -6
- package/dist/chunks/granular-provider-B6xomapW.js.map +1 -0
- package/dist/chunks/{ru-Bb-co59W.js → ru-88i1WG0b.js} +14 -2
- package/dist/chunks/ru-88i1WG0b.js.map +1 -0
- package/dist/chunks/{safelist-x8USecK8.js → safelist-CSfynUC1.js} +5 -1
- package/dist/chunks/safelist-CSfynUC1.js.map +1 -0
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-C-uovdv8.js → GrAutocomplete-Chhw65pI.js} +2 -2
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-C-uovdv8.js.map → GrAutocomplete-Chhw65pI.js.map} +1 -1
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrAvatar/chunks/{GrAvatar-dWEXiCJG.js → GrAvatar-Do5-C409.js} +12 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-Do5-C409.js.map +1 -0
- package/dist/components/GrAvatar/index.js +2 -2
- package/dist/components/GrAvatar/themes/light.css +35 -0
- package/dist/components/GrBadge/chunks/GrBadge-C42KLyUD.js +71 -0
- package/dist/components/GrBadge/chunks/GrBadge-C42KLyUD.js.map +1 -0
- package/dist/components/GrBadge/index.js +3 -3
- package/dist/components/GrBadge/styles.css +1 -1
- package/dist/components/GrChip/chunks/{GrChip-CKqyzYcv.js → GrChip-DoyS1Dr4.js} +1 -1
- package/dist/components/GrChip/chunks/{GrChip-CKqyzYcv.js.map → GrChip-DoyS1Dr4.js.map} +1 -1
- package/dist/components/GrChip/index.js +2 -2
- package/dist/components/GrDataTable/chunks/{GrDataTable-Dp9Fp_P5.js → GrDataTable-DQxYYgQk.js} +438 -160
- package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js.map +1 -0
- package/dist/components/GrDataTable/index.js +2 -2
- package/dist/components/GrDataTable/themes/light.css +10 -0
- package/dist/components/GrInputTag/chunks/{GrInputTag-CO0YU-ok.js → GrInputTag-qnRdAZnn.js} +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-CO0YU-ok.js.map → GrInputTag-qnRdAZnn.js.map} +1 -1
- package/dist/components/GrInputTag/index.js +1 -1
- package/dist/components/GrOtpInput/chunks/GrOtpInput-B_Q-RDLw.js +419 -0
- package/dist/components/GrOtpInput/chunks/GrOtpInput-B_Q-RDLw.js.map +1 -0
- package/dist/components/GrOtpInput/index.js +3 -0
- package/dist/components/GrOtpInput/styles.css +1 -0
- package/dist/components/GrPagination/chunks/{GrPagination-BbaF5eXk.js → GrPagination-CMrLPzvL.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-BbaF5eXk.js.map → GrPagination-CMrLPzvL.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-0iI4wdR_.js → GrSelect-DvSq-nV-.js} +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-0iI4wdR_.js.map → GrSelect-DvSq-nV-.js.map} +1 -1
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrSwitch/chunks/{GrSwitch-CriHWT2A.js → GrSwitch-D7X42uIA.js} +61 -7
- package/dist/components/GrSwitch/chunks/GrSwitch-D7X42uIA.js.map +1 -0
- package/dist/components/GrSwitch/index.js +2 -2
- package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js → GrTable-CKJ-HrsN.js} +1 -1
- package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js.map → GrTable-CKJ-HrsN.js.map} +1 -1
- package/dist/components/GrTable/index.js +2 -2
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.js +20 -19
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrAvatar/GrAvatar.vue.d.ts +11 -0
- package/dist/types/components/GrAvatar/defaults.d.ts +1 -0
- package/dist/types/components/GrAvatar/grAvatarStyles.d.ts +26 -1
- package/dist/types/components/GrBadge/GrBadge.vue.d.ts +15 -1
- package/dist/types/components/GrBadge/grBadgeStyles.d.ts +57 -0
- package/dist/types/components/GrBadge/index.d.ts +1 -1
- package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +59 -0
- package/dist/types/components/GrDataTable/composables/useDataTableExpansion.d.ts +46 -0
- package/dist/types/components/GrDataTable/grDataTableStyles.d.ts +23 -0
- package/dist/types/components/GrOtpInput/GrOtpInput.vue.d.ts +164 -0
- package/dist/types/components/GrOtpInput/config.d.ts +1 -0
- package/dist/types/components/GrOtpInput/defaults.d.ts +15 -0
- package/dist/types/components/GrOtpInput/grOtpInputStyles.d.ts +62 -0
- package/dist/types/components/GrOtpInput/index.d.ts +9 -0
- package/dist/types/components/GrOtpInput/otpValue.d.ts +71 -0
- package/dist/types/components/GrOtpInput/safelist.d.ts +7 -0
- package/dist/types/components/GrSwitch/GrSwitch.vue.d.ts +42 -4
- package/dist/types/components/GrSwitch/defaults.d.ts +2 -0
- package/dist/types/components/GrSwitch/grSwitchStyles.d.ts +129 -25
- package/dist/types/components/GrSwitch/index.d.ts +1 -1
- package/dist/types/components/GrSwitch/safelist.d.ts +4 -3
- package/dist/types/components/GrTable/grTableStyles.d.ts +16 -3
- package/dist/types/granular-provider/shared.d.ts +1 -0
- package/dist/types/i18n/messages/en.d.ts +20 -0
- package/dist/types/i18n/messages/es.d.ts +20 -0
- package/dist/types/i18n/messages/ru.d.ts +20 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/web-types.json +329 -8
- package/package.json +5 -1
- package/dist/chunks/config-B3J0alyY.js.map +0 -1
- package/dist/chunks/config-Yy67uWHY.js +0 -140
- package/dist/chunks/config-Yy67uWHY.js.map +0 -1
- package/dist/chunks/en-DjTnfTqM.js.map +0 -1
- package/dist/chunks/es-Bc_t3FI8.js.map +0 -1
- package/dist/chunks/grBadgeStyles-DV0DrAew.js.map +0 -1
- package/dist/chunks/grTableStyles-uuMPSOBR.js.map +0 -1
- package/dist/chunks/granular-provider-AKukmYva.js.map +0 -1
- package/dist/chunks/ru-Bb-co59W.js.map +0 -1
- package/dist/chunks/safelist-x8USecK8.js.map +0 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-dWEXiCJG.js.map +0 -1
- package/dist/components/GrBadge/chunks/GrBadge-DBeQXEir.js +0 -47
- package/dist/components/GrBadge/chunks/GrBadge-DBeQXEir.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-Dp9Fp_P5.js.map +0 -1
- package/dist/components/GrSwitch/chunks/GrSwitch-CriHWT2A.js.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrAutocomplete_default } from "./chunks/GrAutocomplete-
|
|
1
|
+
import { t as GrAutocomplete_default } from "./chunks/GrAutocomplete-Chhw65pI.js";
|
|
2
2
|
import { n as grAutocompleteSafelist, t as grAutocompleteConfig } from "../../chunks/config-BHLMTJaR.js";
|
|
3
3
|
export { GrAutocomplete_default as GrAutocomplete, GrAutocomplete_default as default, grAutocompleteConfig, grAutocompleteSafelist };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as useGrComponentSize, i as useGrComponentProp } from "../../../chunks/context-CPMRfwVr.js";
|
|
2
2
|
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
3
3
|
import { t as GrSkeleton_default } from "../../GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js";
|
|
4
|
-
import { a as
|
|
4
|
+
import { _ as statusToneClass, a as avatarToneIndex, c as groupItemClass, d as mediaClass, f as mediaClipClass, g as statusDotClass, h as rootStatusClass, i as avatarToneClasses, l as groupOverflowClass, m as rootNeutralClass, o as grAvatarClass, p as rootBaseClass, r as avatarFontSizePx, s as groupBaseClass, u as initialsFrom } from "../../../chunks/config-CzeZg2LO.js";
|
|
5
5
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, inject, normalizeClass, normalizeStyle, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useSlots, watch } from "vue";
|
|
6
6
|
//#region src/components/GrAvatar/avatarContext.ts
|
|
7
7
|
var GR_AVATAR_GROUP_KEY = Symbol.for("@feugene/granularity/avatar-group");
|
|
@@ -28,6 +28,10 @@ var GrAvatar_default = /* @__PURE__ */ defineComponent({
|
|
|
28
28
|
alt: { default: void 0 },
|
|
29
29
|
name: { default: void 0 },
|
|
30
30
|
shape: { default: void 0 },
|
|
31
|
+
autoColor: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: void 0
|
|
34
|
+
},
|
|
31
35
|
status: { default: void 0 }
|
|
32
36
|
},
|
|
33
37
|
setup(__props) {
|
|
@@ -44,6 +48,7 @@ var GrAvatar_default = /* @__PURE__ */ defineComponent({
|
|
|
44
48
|
const localSize = computed(() => props.size ?? group?.size);
|
|
45
49
|
const resolvedScaleSize = useGrComponentSize(() => typeof localSize.value === "number" ? void 0 : localSize.value, { component: "GrAvatar" });
|
|
46
50
|
const resolvedShape = useGrComponentProp("GrAvatar", "shape", () => props.shape ?? group?.shape, "circle");
|
|
51
|
+
const resolvedAutoColor = useGrComponentProp("GrAvatar", "autoColor", () => props.autoColor, false);
|
|
47
52
|
const sizePx = computed(() => typeof localSize.value === "number" ? localSize.value : GR_AVATAR_SIZE_PX[resolvedScaleSize.value]);
|
|
48
53
|
const style = computed(() => {
|
|
49
54
|
const px = `${sizePx.value}px`;
|
|
@@ -87,8 +92,14 @@ var GrAvatar_default = /* @__PURE__ */ defineComponent({
|
|
|
87
92
|
*/
|
|
88
93
|
const accessibleName = computed(() => props.alt ?? props.name ?? "");
|
|
89
94
|
const rootIsImage = computed(() => !activeSrc.value && accessibleName.value !== "");
|
|
95
|
+
/**
|
|
96
|
+
* Автоцвет виден только под инициалами: картинка закрывает фон целиком, а у
|
|
97
|
+
* пустого аватара красить нечего.
|
|
98
|
+
*/
|
|
99
|
+
const toneClass = computed(() => resolvedAutoColor.value && showInitials.value && props.name ? avatarToneClasses[avatarToneIndex(props.name)] : rootNeutralClass);
|
|
90
100
|
const rootClass = computed(() => [
|
|
91
101
|
rootBaseClass,
|
|
102
|
+
toneClass.value,
|
|
92
103
|
grAvatarClass(resolvedShape.value),
|
|
93
104
|
props.status ? rootStatusClass : ""
|
|
94
105
|
]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrAvatar-Do5-C409.js","names":[],"sources":["../../../../src/components/GrAvatar/avatarContext.ts","../../../../src/components/GrAvatar/GrAvatar.vue","../../../../src/components/GrAvatar/GrAvatar.vue","../../../../src/components/GrAvatar/GrAvatarGroup.vue","../../../../src/components/GrAvatar/GrAvatarGroup.vue"],"sourcesContent":["import type { InjectionKey } from 'vue'\n\nimport type { GrAvatarShape } from './grAvatarStyles'\nimport type { GrSizeWithPx } from '../shared/sizes'\n\n/**\n * Контекст `GrAvatarGroup`: размер и форма задаются на группе, чтобы ряд\n * аватаров не расползался, когда потребитель забыл повторить пропы у каждого.\n */\nexport interface GrAvatarGroupContext {\n size?: GrSizeWithPx\n shape?: GrAvatarShape\n}\n\nexport const GR_AVATAR_GROUP_KEY: InjectionKey<GrAvatarGroupContext> = Symbol.for('@feugene/granularity/avatar-group')\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch } from 'vue'\n\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrSizeWithPx } from '../shared/sizes'\n\nimport { GR_AVATAR_GROUP_KEY } from './avatarContext'\nimport {\n avatarFontSizePx,\n grAvatarClass,\n initialsFrom,\n mediaClass,\n mediaClipClass,\n avatarToneClasses,\n avatarToneIndex,\n rootBaseClass,\n rootNeutralClass,\n rootStatusClass,\n statusDotClass,\n statusToneClass,\n type GrAvatarShape,\n type GrAvatarStatus,\n} from './grAvatarStyles'\n\nexport type { GrAvatarShape, GrAvatarStatus } from './grAvatarStyles'\n\n/** Диаметр аватара для каждой ступени канонической шкалы. */\nconst GR_AVATAR_SIZE_PX = {\n xs: 24,\n sm: 32,\n md: 40,\n lg: 56,\n} as const\n\nexport interface GrAvatarProps {\n /**\n * Размер по канонической шкале (`xs|sm|md|lg`) — тогда работает\n * `GrConfigProvider`. Число — escape-hatch: аватар нужен произвольного\n * диаметра (24px в строке списка, 96px в профиле), и четыре ступени шкалы\n * этого не закрывают.\n */\n size?: GrSizeWithPx\n src?: string\n /** Резервная картинка: показывается, когда `src` не загрузился. */\n fallbackSrc?: string\n alt?: string\n /** Имя участника: даёт инициалы и доступное имя аватара. */\n name?: string\n shape?: GrAvatarShape\n /**\n * Красить фон по имени: одинаковое имя всегда даёт одинаковый цвет.\n *\n * Выключен по умолчанию — серый аватар это осознанный нейтральный дефолт, а\n * цветной может спорить с оформлением продукта. Палитру задаёт тема\n * (`--gr-avatar-N-bg`/`-fg`), компонент только выбирает слот.\n */\n autoColor?: boolean\n /** Статус участника. Точка декоративна — рядом идёт скрытая подпись. */\n status?: GrAvatarStatus\n}\n\nconst props = withDefaults(defineProps<GrAvatarProps>(), {\n size: undefined,\n src: undefined,\n fallbackSrc: undefined,\n alt: undefined,\n name: undefined,\n // Дефолт живёт в резолвере: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n shape: undefined,\n // По той же причине, что `shape`: булев проп без этой строки Vue приводит к\n // `false` сам, и резолвер уже не отличит «не задано» от «выключено явно».\n autoColor: undefined,\n status: undefined,\n})\n\nconst slots = defineSlots<{\n /** Содержимое вместо картинки и инициалов. */\n default?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst group = inject(GR_AVATAR_GROUP_KEY, null)\n\n// Размер и форма группы сильнее провайдера, но слабее собственного пропа.\nconst localSize = computed(() => props.size ?? group?.size)\n\nconst resolvedScaleSize = useGrComponentSize(\n () => (typeof localSize.value === 'number' ? undefined : localSize.value),\n { component: 'GrAvatar' },\n)\n\nconst resolvedShape = useGrComponentProp(\n 'GrAvatar',\n 'shape',\n () => props.shape ?? group?.shape,\n 'circle',\n)\n\nconst resolvedAutoColor = useGrComponentProp('GrAvatar', 'autoColor', () => props.autoColor, false)\n\nconst sizePx = computed(() => (\n typeof localSize.value === 'number' ? localSize.value : GR_AVATAR_SIZE_PX[resolvedScaleSize.value]\n))\n\nconst style = computed(() => {\n const px = `${sizePx.value}px`\n // Кегль на корне, а не на span инициалов: так он достаётся и слотовому тексту.\n return { width: px, height: px, fontSize: `${avatarFontSizePx(sizePx.value)}px` }\n})\n\n/**\n * Состояние картинки. Без него битая ссылка оставляла `v-if=\"src\"` истинным:\n * браузер рисовал сломанное изображение, а запасной вариант не показывался\n * никогда.\n */\ntype ImageState = 'loading' | 'loaded' | 'error'\n\nconst primaryState = ref<ImageState>('loading')\nconst fallbackState = ref<ImageState>('loading')\n\n// Новая ссылка не должна наследовать ошибку прошлой.\nwatch(() => props.src, () => {\n primaryState.value = 'loading'\n})\nwatch(() => props.fallbackSrc, () => {\n fallbackState.value = 'loading'\n})\n\nconst activeSrc = computed(() => {\n if (props.src && primaryState.value !== 'error')\n return props.src\n if (props.fallbackSrc && fallbackState.value !== 'error')\n return props.fallbackSrc\n return undefined\n})\n\nconst isFallbackActive = computed(() => Boolean(activeSrc.value) && activeSrc.value !== props.src)\n\n// Скелет держит место, пока картинка едет: иначе ряд аватаров мигает пустотой.\nconst showSkeleton = computed(() => {\n if (!activeSrc.value)\n return false\n return (isFallbackActive.value ? fallbackState.value : primaryState.value) === 'loading'\n})\n\nconst initials = computed(() => (props.name ? initialsFrom(props.name) : ''))\n\n// Пусто — когда нет ни картинки, ни слота, ни имени.\nconst showInitials = computed(() => !activeSrc.value && !slots.default && Boolean(initials.value))\n\nconst statusLabel = computed(() => (props.status\n ? t(`gr.avatar.status.${props.status}`, props.status)\n : undefined))\n\n/**\n * Доступное имя: `alt` сильнее всего, иначе имя участника.\n *\n * Когда есть картинка, имя несёт её `alt` — так привычнее потребителю и не\n * возникает двойного объявления. Без картинки роль и имя берёт на себя корень:\n * иначе аватар с инициалами остался бы для диктора пустым.\n */\nconst accessibleName = computed(() => props.alt ?? props.name ?? '')\n\nconst rootIsImage = computed(() => !activeSrc.value && accessibleName.value !== '')\n\n/**\n * Автоцвет виден только под инициалами: картинка закрывает фон целиком, а у\n * пустого аватара красить нечего.\n */\nconst toneClass = computed(() => (\n resolvedAutoColor.value && showInitials.value && props.name\n ? avatarToneClasses[avatarToneIndex(props.name)]\n : rootNeutralClass\n))\n\nconst rootClass = computed(() => [\n rootBaseClass,\n toneClass.value,\n grAvatarClass(resolvedShape.value),\n props.status ? rootStatusClass : '',\n])\n\nfunction onError(kind: 'primary' | 'fallback'): void {\n if (kind === 'primary')\n primaryState.value = 'error'\n else fallbackState.value = 'error'\n}\n\nfunction onLoad(kind: 'primary' | 'fallback'): void {\n if (kind === 'primary')\n primaryState.value = 'loaded'\n else fallbackState.value = 'loaded'\n}\n</script>\n\n<template>\n <span\n data-gr-avatar\n :style=\"style\"\n :class=\"rootClass\"\n :role=\"rootIsImage ? 'img' : undefined\"\n :aria-label=\"rootIsImage ? accessibleName : undefined\"\n >\n <span :class=\"[mediaClass, status ? [mediaClipClass, grAvatarClass(resolvedShape)] : '']\">\n <GrSkeleton\n v-if=\"showSkeleton\"\n data-gr-avatar-skeleton\n width=\"100%\"\n height=\"100%\"\n :rounded=\"resolvedShape === 'circle' ? 'var(--gr-radius-full)' : 'var(--gr-avatar-square-radius, 10px)'\"\n />\n\n <img\n v-if=\"activeSrc\"\n :key=\"activeSrc\"\n data-gr-avatar-image\n :src=\"activeSrc\"\n :alt=\"accessibleName\"\n class=\"h-full w-full object-cover\"\n :class=\"showSkeleton ? 'invisible absolute' : ''\"\n @error=\"onError(isFallbackActive ? 'fallback' : 'primary')\"\n @load=\"onLoad(isFallbackActive ? 'fallback' : 'primary')\"\n >\n\n <span v-else-if=\"showInitials\" data-gr-avatar-initials>{{ initials }}</span>\n\n <slot v-else />\n </span>\n\n <template v-if=\"status\">\n <span\n data-gr-avatar-status\n :class=\"[statusDotClass, statusToneClass[status]]\"\n aria-hidden=\"true\"\n />\n <!-- Цвет точки смысл не передаёт: статус объявляется словом (WCAG 1.4.1). -->\n <span data-gr-avatar-status-label class=\"sr-only\">{{ statusLabel }}</span>\n </template>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch } from 'vue'\n\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrSizeWithPx } from '../shared/sizes'\n\nimport { GR_AVATAR_GROUP_KEY } from './avatarContext'\nimport {\n avatarFontSizePx,\n grAvatarClass,\n initialsFrom,\n mediaClass,\n mediaClipClass,\n avatarToneClasses,\n avatarToneIndex,\n rootBaseClass,\n rootNeutralClass,\n rootStatusClass,\n statusDotClass,\n statusToneClass,\n type GrAvatarShape,\n type GrAvatarStatus,\n} from './grAvatarStyles'\n\nexport type { GrAvatarShape, GrAvatarStatus } from './grAvatarStyles'\n\n/** Диаметр аватара для каждой ступени канонической шкалы. */\nconst GR_AVATAR_SIZE_PX = {\n xs: 24,\n sm: 32,\n md: 40,\n lg: 56,\n} as const\n\nexport interface GrAvatarProps {\n /**\n * Размер по канонической шкале (`xs|sm|md|lg`) — тогда работает\n * `GrConfigProvider`. Число — escape-hatch: аватар нужен произвольного\n * диаметра (24px в строке списка, 96px в профиле), и четыре ступени шкалы\n * этого не закрывают.\n */\n size?: GrSizeWithPx\n src?: string\n /** Резервная картинка: показывается, когда `src` не загрузился. */\n fallbackSrc?: string\n alt?: string\n /** Имя участника: даёт инициалы и доступное имя аватара. */\n name?: string\n shape?: GrAvatarShape\n /**\n * Красить фон по имени: одинаковое имя всегда даёт одинаковый цвет.\n *\n * Выключен по умолчанию — серый аватар это осознанный нейтральный дефолт, а\n * цветной может спорить с оформлением продукта. Палитру задаёт тема\n * (`--gr-avatar-N-bg`/`-fg`), компонент только выбирает слот.\n */\n autoColor?: boolean\n /** Статус участника. Точка декоративна — рядом идёт скрытая подпись. */\n status?: GrAvatarStatus\n}\n\nconst props = withDefaults(defineProps<GrAvatarProps>(), {\n size: undefined,\n src: undefined,\n fallbackSrc: undefined,\n alt: undefined,\n name: undefined,\n // Дефолт живёт в резолвере: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n shape: undefined,\n // По той же причине, что `shape`: булев проп без этой строки Vue приводит к\n // `false` сам, и резолвер уже не отличит «не задано» от «выключено явно».\n autoColor: undefined,\n status: undefined,\n})\n\nconst slots = defineSlots<{\n /** Содержимое вместо картинки и инициалов. */\n default?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst group = inject(GR_AVATAR_GROUP_KEY, null)\n\n// Размер и форма группы сильнее провайдера, но слабее собственного пропа.\nconst localSize = computed(() => props.size ?? group?.size)\n\nconst resolvedScaleSize = useGrComponentSize(\n () => (typeof localSize.value === 'number' ? undefined : localSize.value),\n { component: 'GrAvatar' },\n)\n\nconst resolvedShape = useGrComponentProp(\n 'GrAvatar',\n 'shape',\n () => props.shape ?? group?.shape,\n 'circle',\n)\n\nconst resolvedAutoColor = useGrComponentProp('GrAvatar', 'autoColor', () => props.autoColor, false)\n\nconst sizePx = computed(() => (\n typeof localSize.value === 'number' ? localSize.value : GR_AVATAR_SIZE_PX[resolvedScaleSize.value]\n))\n\nconst style = computed(() => {\n const px = `${sizePx.value}px`\n // Кегль на корне, а не на span инициалов: так он достаётся и слотовому тексту.\n return { width: px, height: px, fontSize: `${avatarFontSizePx(sizePx.value)}px` }\n})\n\n/**\n * Состояние картинки. Без него битая ссылка оставляла `v-if=\"src\"` истинным:\n * браузер рисовал сломанное изображение, а запасной вариант не показывался\n * никогда.\n */\ntype ImageState = 'loading' | 'loaded' | 'error'\n\nconst primaryState = ref<ImageState>('loading')\nconst fallbackState = ref<ImageState>('loading')\n\n// Новая ссылка не должна наследовать ошибку прошлой.\nwatch(() => props.src, () => {\n primaryState.value = 'loading'\n})\nwatch(() => props.fallbackSrc, () => {\n fallbackState.value = 'loading'\n})\n\nconst activeSrc = computed(() => {\n if (props.src && primaryState.value !== 'error')\n return props.src\n if (props.fallbackSrc && fallbackState.value !== 'error')\n return props.fallbackSrc\n return undefined\n})\n\nconst isFallbackActive = computed(() => Boolean(activeSrc.value) && activeSrc.value !== props.src)\n\n// Скелет держит место, пока картинка едет: иначе ряд аватаров мигает пустотой.\nconst showSkeleton = computed(() => {\n if (!activeSrc.value)\n return false\n return (isFallbackActive.value ? fallbackState.value : primaryState.value) === 'loading'\n})\n\nconst initials = computed(() => (props.name ? initialsFrom(props.name) : ''))\n\n// Пусто — когда нет ни картинки, ни слота, ни имени.\nconst showInitials = computed(() => !activeSrc.value && !slots.default && Boolean(initials.value))\n\nconst statusLabel = computed(() => (props.status\n ? t(`gr.avatar.status.${props.status}`, props.status)\n : undefined))\n\n/**\n * Доступное имя: `alt` сильнее всего, иначе имя участника.\n *\n * Когда есть картинка, имя несёт её `alt` — так привычнее потребителю и не\n * возникает двойного объявления. Без картинки роль и имя берёт на себя корень:\n * иначе аватар с инициалами остался бы для диктора пустым.\n */\nconst accessibleName = computed(() => props.alt ?? props.name ?? '')\n\nconst rootIsImage = computed(() => !activeSrc.value && accessibleName.value !== '')\n\n/**\n * Автоцвет виден только под инициалами: картинка закрывает фон целиком, а у\n * пустого аватара красить нечего.\n */\nconst toneClass = computed(() => (\n resolvedAutoColor.value && showInitials.value && props.name\n ? avatarToneClasses[avatarToneIndex(props.name)]\n : rootNeutralClass\n))\n\nconst rootClass = computed(() => [\n rootBaseClass,\n toneClass.value,\n grAvatarClass(resolvedShape.value),\n props.status ? rootStatusClass : '',\n])\n\nfunction onError(kind: 'primary' | 'fallback'): void {\n if (kind === 'primary')\n primaryState.value = 'error'\n else fallbackState.value = 'error'\n}\n\nfunction onLoad(kind: 'primary' | 'fallback'): void {\n if (kind === 'primary')\n primaryState.value = 'loaded'\n else fallbackState.value = 'loaded'\n}\n</script>\n\n<template>\n <span\n data-gr-avatar\n :style=\"style\"\n :class=\"rootClass\"\n :role=\"rootIsImage ? 'img' : undefined\"\n :aria-label=\"rootIsImage ? accessibleName : undefined\"\n >\n <span :class=\"[mediaClass, status ? [mediaClipClass, grAvatarClass(resolvedShape)] : '']\">\n <GrSkeleton\n v-if=\"showSkeleton\"\n data-gr-avatar-skeleton\n width=\"100%\"\n height=\"100%\"\n :rounded=\"resolvedShape === 'circle' ? 'var(--gr-radius-full)' : 'var(--gr-avatar-square-radius, 10px)'\"\n />\n\n <img\n v-if=\"activeSrc\"\n :key=\"activeSrc\"\n data-gr-avatar-image\n :src=\"activeSrc\"\n :alt=\"accessibleName\"\n class=\"h-full w-full object-cover\"\n :class=\"showSkeleton ? 'invisible absolute' : ''\"\n @error=\"onError(isFallbackActive ? 'fallback' : 'primary')\"\n @load=\"onLoad(isFallbackActive ? 'fallback' : 'primary')\"\n >\n\n <span v-else-if=\"showInitials\" data-gr-avatar-initials>{{ initials }}</span>\n\n <slot v-else />\n </span>\n\n <template v-if=\"status\">\n <span\n data-gr-avatar-status\n :class=\"[statusDotClass, statusToneClass[status]]\"\n aria-hidden=\"true\"\n />\n <!-- Цвет точки смысл не передаёт: статус объявляется словом (WCAG 1.4.1). -->\n <span data-gr-avatar-status-label class=\"sr-only\">{{ statusLabel }}</span>\n </template>\n </span>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrAvatarGroup — ряд аватаров со стекингом и счётчиком скрытых.\n *\n * Группа объявлена `role=\"group\"` с именем и числом участников: без этого\n * скринридер читает набор безымянных картинок и не понимает, что их больше,\n * чем видно.\n */\nimport { computed, provide, type VNode } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrSizeWithPx } from '../shared/sizes'\n\nimport { GR_AVATAR_GROUP_KEY } from './avatarContext'\nimport {\n avatarFontSizePx,\n grAvatarClass,\n groupBaseClass,\n groupItemClass,\n groupOverflowClass,\n rootBaseClass,\n type GrAvatarShape,\n} from './grAvatarStyles'\n\nconst GR_AVATAR_GROUP_SIZE_PX = {\n xs: 24,\n sm: 32,\n md: 40,\n lg: 56,\n} as const\n\nexport interface GrAvatarGroupProps {\n /** Размер и форма ряда: дети берут их из контекста. */\n size?: GrSizeWithPx\n shape?: GrAvatarShape\n /** Сколько аватаров показывать; остальные сворачиваются в «+N». */\n max?: number\n /** Сколько участников всего, если в слоте лежит лишь часть. */\n total?: number\n /** Имя группы. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrAvatarGroupProps>(), {\n size: undefined,\n shape: undefined,\n max: undefined,\n total: undefined,\n ariaLabel: undefined,\n})\n\nconst slots = defineSlots<{\n /** Аватары ряда. */\n default?: () => VNode[]\n}>()\n\nconst { t } = useGranularityTranslations()\n\nprovide(GR_AVATAR_GROUP_KEY, {\n get size() {\n return props.size\n },\n get shape() {\n return props.shape\n },\n})\n\nconst children = computed(() => slots.default?.() ?? [])\n\n// `v-for` внутри слота приходит фрагментом — считаем его детей, иначе весь\n// список выглядел бы одним аватаром.\nconst items = computed<VNode[]>(() => children.value.flatMap((node) => {\n const nested = node.children\n return Array.isArray(nested) && typeof node.type === 'symbol' ? (nested as VNode[]) : [node]\n}))\n\nconst visible = computed(() => (props.max === undefined ? items.value : items.value.slice(0, Math.max(0, props.max))))\n\nconst hiddenCount = computed(() => {\n const total = props.total ?? items.value.length\n return Math.max(0, total - visible.value.length)\n})\n\nconst overflowSizePx = computed(() => (\n typeof props.size === 'number' ? props.size : GR_AVATAR_GROUP_SIZE_PX[props.size ?? 'md']\n))\n\nconst overflowStyle = computed(() => {\n const px = `${overflowSizePx.value}px`\n return { width: px, height: px, fontSize: `${avatarFontSizePx(overflowSizePx.value)}px` }\n})\n\nconst groupLabel = computed(() => props.ariaLabel ?? t('gr.avatar.group', 'People'))\n\nconst accessibleLabel = computed(() => (hiddenCount.value > 0\n ? `${groupLabel.value}, ${t('gr.avatar.groupOverflow', 'and {count} more', { count: hiddenCount.value, n: hiddenCount.value })}`\n : groupLabel.value))\n</script>\n\n<template>\n <div\n data-gr-avatar-group\n role=\"group\"\n :aria-label=\"accessibleLabel\"\n :class=\"groupBaseClass\"\n >\n <component\n :is=\"node\"\n v-for=\"(node, index) in visible\"\n :key=\"index\"\n :class=\"groupItemClass\"\n />\n\n <span\n v-if=\"hiddenCount > 0\"\n data-gr-avatar-group-overflow\n aria-hidden=\"true\"\n :class=\"[rootBaseClass, groupOverflowClass, groupItemClass, grAvatarClass(shape ?? 'circle')]\"\n :style=\"overflowStyle\"\n >+{{ hiddenCount }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrAvatarGroup — ряд аватаров со стекингом и счётчиком скрытых.\n *\n * Группа объявлена `role=\"group\"` с именем и числом участников: без этого\n * скринридер читает набор безымянных картинок и не понимает, что их больше,\n * чем видно.\n */\nimport { computed, provide, type VNode } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrSizeWithPx } from '../shared/sizes'\n\nimport { GR_AVATAR_GROUP_KEY } from './avatarContext'\nimport {\n avatarFontSizePx,\n grAvatarClass,\n groupBaseClass,\n groupItemClass,\n groupOverflowClass,\n rootBaseClass,\n type GrAvatarShape,\n} from './grAvatarStyles'\n\nconst GR_AVATAR_GROUP_SIZE_PX = {\n xs: 24,\n sm: 32,\n md: 40,\n lg: 56,\n} as const\n\nexport interface GrAvatarGroupProps {\n /** Размер и форма ряда: дети берут их из контекста. */\n size?: GrSizeWithPx\n shape?: GrAvatarShape\n /** Сколько аватаров показывать; остальные сворачиваются в «+N». */\n max?: number\n /** Сколько участников всего, если в слоте лежит лишь часть. */\n total?: number\n /** Имя группы. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrAvatarGroupProps>(), {\n size: undefined,\n shape: undefined,\n max: undefined,\n total: undefined,\n ariaLabel: undefined,\n})\n\nconst slots = defineSlots<{\n /** Аватары ряда. */\n default?: () => VNode[]\n}>()\n\nconst { t } = useGranularityTranslations()\n\nprovide(GR_AVATAR_GROUP_KEY, {\n get size() {\n return props.size\n },\n get shape() {\n return props.shape\n },\n})\n\nconst children = computed(() => slots.default?.() ?? [])\n\n// `v-for` внутри слота приходит фрагментом — считаем его детей, иначе весь\n// список выглядел бы одним аватаром.\nconst items = computed<VNode[]>(() => children.value.flatMap((node) => {\n const nested = node.children\n return Array.isArray(nested) && typeof node.type === 'symbol' ? (nested as VNode[]) : [node]\n}))\n\nconst visible = computed(() => (props.max === undefined ? items.value : items.value.slice(0, Math.max(0, props.max))))\n\nconst hiddenCount = computed(() => {\n const total = props.total ?? items.value.length\n return Math.max(0, total - visible.value.length)\n})\n\nconst overflowSizePx = computed(() => (\n typeof props.size === 'number' ? props.size : GR_AVATAR_GROUP_SIZE_PX[props.size ?? 'md']\n))\n\nconst overflowStyle = computed(() => {\n const px = `${overflowSizePx.value}px`\n return { width: px, height: px, fontSize: `${avatarFontSizePx(overflowSizePx.value)}px` }\n})\n\nconst groupLabel = computed(() => props.ariaLabel ?? t('gr.avatar.group', 'People'))\n\nconst accessibleLabel = computed(() => (hiddenCount.value > 0\n ? `${groupLabel.value}, ${t('gr.avatar.groupOverflow', 'and {count} more', { count: hiddenCount.value, n: hiddenCount.value })}`\n : groupLabel.value))\n</script>\n\n<template>\n <div\n data-gr-avatar-group\n role=\"group\"\n :aria-label=\"accessibleLabel\"\n :class=\"groupBaseClass\"\n >\n <component\n :is=\"node\"\n v-for=\"(node, index) in visible\"\n :key=\"index\"\n :class=\"groupItemClass\"\n />\n\n <span\n v-if=\"hiddenCount > 0\"\n data-gr-avatar-group-overflow\n aria-hidden=\"true\"\n :class=\"[rootBaseClass, groupOverflowClass, groupItemClass, grAvatarClass(shape ?? 'circle')]\"\n :style=\"overflowStyle\"\n >+{{ hiddenCount }}</span>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as GrAvatar_default, t as GrAvatarGroup_default } from "./chunks/GrAvatar-
|
|
2
|
-
import {
|
|
1
|
+
import { n as GrAvatar_default, t as GrAvatarGroup_default } from "./chunks/GrAvatar-Do5-C409.js";
|
|
2
|
+
import { n as grAvatarSafelist, t as grAvatarConfig, u as initialsFrom } from "../../chunks/config-CzeZg2LO.js";
|
|
3
3
|
export { GrAvatar_default as GrAvatar, GrAvatar_default as default, GrAvatarGroup_default as GrAvatarGroup, grAvatarConfig, grAvatarSafelist, initialsFrom };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Палитра автоцвета аватара — только ссылки на роли тонов.
|
|
3
|
+
*
|
|
4
|
+
* Слоты живут здесь, а не в глобальной теме, потому что нужны ровно одному
|
|
5
|
+
* компоненту: в глобальной за них платил бы и тот, кто `GrAvatar` не берёт.
|
|
6
|
+
*
|
|
7
|
+
* **Тёмного файла у палитры нет, и это не пропуск.** У `GrBadge` и `GrButton`
|
|
8
|
+
* он есть потому, что там переопределяются `--gr-{tone}` и `-solid`, которые в
|
|
9
|
+
* тёмной теме означают другое. Здесь слот ссылается на пару `-light`/`-text`,
|
|
10
|
+
* а она сама переключается вместе с темой — переопределять нечего, и второй
|
|
11
|
+
* файл только дублировал бы первый.
|
|
12
|
+
*
|
|
13
|
+
* Состав палитры — шесть тонов с полным набором ролей. `primary` не входит:
|
|
14
|
+
* у него нет `-light` (мягкая подложка бренда называется `--gr-accent`).
|
|
15
|
+
* Приложение вправе переназначить любой слот, не трогая компонент.
|
|
16
|
+
*/
|
|
17
|
+
:root {
|
|
18
|
+
--gr-avatar-1-bg: var(--gr-info-light);
|
|
19
|
+
--gr-avatar-1-fg: var(--gr-info-text);
|
|
20
|
+
|
|
21
|
+
--gr-avatar-2-bg: var(--gr-warning-light);
|
|
22
|
+
--gr-avatar-2-fg: var(--gr-warning-text);
|
|
23
|
+
|
|
24
|
+
--gr-avatar-3-bg: var(--gr-success-light);
|
|
25
|
+
--gr-avatar-3-fg: var(--gr-success-text);
|
|
26
|
+
|
|
27
|
+
--gr-avatar-4-bg: var(--gr-danger-light);
|
|
28
|
+
--gr-avatar-4-fg: var(--gr-danger-text);
|
|
29
|
+
|
|
30
|
+
--gr-avatar-5-bg: var(--gr-azure-light);
|
|
31
|
+
--gr-avatar-5-fg: var(--gr-azure-text);
|
|
32
|
+
|
|
33
|
+
--gr-avatar-6-bg: var(--gr-slate-light);
|
|
34
|
+
--gr-avatar-6-fg: var(--gr-slate-text);
|
|
35
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { i as useGrComponentProp } from "../../../chunks/context-CPMRfwVr.js";
|
|
2
|
+
import { t as warnRenamedProp } from "../../../chunks/renamedProp-Dy96tT-F.js";
|
|
3
|
+
import { l as grBadgeClass, n as badgeDotClassFor, o as badgeIconClass, s as badgeIconSizeClassBySize } from "../../../chunks/grBadgeStyles-D51AXbj5.js";
|
|
4
|
+
import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
5
|
+
import { computed, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, renderSlot, unref, useAttrs, useSlots, watchEffect } from "vue";
|
|
6
|
+
import '../styles.css';//#region src/components/GrBadge/GrBadge.vue?vue&type=script&setup=true&lang.ts
|
|
7
|
+
var _hoisted_1 = { class: "gr-badge__label" };
|
|
8
|
+
var _hoisted_2 = { class: "gr-badge__text" };
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/components/GrBadge/GrBadge.vue
|
|
11
|
+
var GrBadge_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
12
|
+
__name: "GrBadge",
|
|
13
|
+
props: {
|
|
14
|
+
tone: { default: void 0 },
|
|
15
|
+
dark: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false
|
|
18
|
+
},
|
|
19
|
+
size: { default: void 0 },
|
|
20
|
+
radius: { default: void 0 },
|
|
21
|
+
dot: {
|
|
22
|
+
type: [String, Boolean],
|
|
23
|
+
default: false
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
setup(__props) {
|
|
27
|
+
const props = __props;
|
|
28
|
+
const slots = useSlots();
|
|
29
|
+
const resolvedTone = useGrComponentProp("GrBadge", "tone", () => props.tone, "neutral");
|
|
30
|
+
const resolvedSize = useGrComponentProp("GrBadge", "size", () => props.size, "sm");
|
|
31
|
+
const resolvedRadius = useGrComponentProp("GrBadge", "radius", () => props.radius, "round");
|
|
32
|
+
warnRenamedProp("GrBadge", useAttrs(), { variant: "tone" });
|
|
33
|
+
const className = computed(() => {
|
|
34
|
+
return grBadgeClass({
|
|
35
|
+
tone: resolvedTone.value,
|
|
36
|
+
dark: props.dark,
|
|
37
|
+
size: resolvedSize.value,
|
|
38
|
+
radius: resolvedRadius.value
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
const iconSizeClass = computed(() => badgeIconSizeClassBySize[resolvedSize.value]);
|
|
42
|
+
const showDot = computed(() => Boolean(props.dot) && !slots.icon);
|
|
43
|
+
const dotClass = computed(() => badgeDotClassFor({
|
|
44
|
+
tone: typeof props.dot === "string" ? props.dot : resolvedTone.value,
|
|
45
|
+
dark: props.dark,
|
|
46
|
+
size: resolvedSize.value
|
|
47
|
+
}));
|
|
48
|
+
if (process.env.NODE_ENV !== "production") watchEffect(() => {
|
|
49
|
+
if (props.dot && slots.icon) console.warn("[granularity] GrBadge: `dot` и слот `icon` заняли бы одно место перед подписью — нарисована иконка, маркер пропущен.");
|
|
50
|
+
if (props.dark && typeof props.dot === "string") console.warn(`[granularity] GrBadge: маркер тона \`${props.dot}\` на заливке не рисуется — подложка filled-бейджа берётся из того же слоя цветов, и маркер слился бы с ней. На заливке он всегда цвета текста.`);
|
|
51
|
+
});
|
|
52
|
+
return (_ctx, _cache) => {
|
|
53
|
+
return openBlock(), createElementBlock("span", { class: normalizeClass(["gr-badge inline-block border whitespace-nowrap leading-none", className.value]) }, [createElementVNode("span", _hoisted_1, [
|
|
54
|
+
showDot.value ? (openBlock(), createElementBlock("span", {
|
|
55
|
+
key: 0,
|
|
56
|
+
"data-gr-badge-dot": "",
|
|
57
|
+
class: normalizeClass(dotClass.value),
|
|
58
|
+
"aria-hidden": "true"
|
|
59
|
+
}, null, 2)) : createCommentVNode("", true),
|
|
60
|
+
_ctx.$slots.icon ? (openBlock(), createElementBlock("span", {
|
|
61
|
+
key: 1,
|
|
62
|
+
"data-gr-badge-icon": "",
|
|
63
|
+
class: normalizeClass([unref(badgeIconClass), iconSizeClass.value])
|
|
64
|
+
}, [renderSlot(_ctx.$slots, "icon", {}, void 0, true)], 2)) : createCommentVNode("", true),
|
|
65
|
+
createElementVNode("span", _hoisted_2, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])
|
|
66
|
+
])], 2);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}), [["__scopeId", "data-v-cbcfb89e"]]);
|
|
70
|
+
//#endregion
|
|
71
|
+
export { GrBadge_default as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrBadge-C42KLyUD.js","names":["$slots"],"sources":["../../../../src/components/GrBadge/GrBadge.vue","../../../../src/components/GrBadge/GrBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs, watchEffect } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\n\nimport {\n badgeDotClassFor,\n badgeIconClass,\n badgeIconSizeClassBySize,\n grBadgeClass,\n type GrBadgeDotTone,\n type GrBadgeRadius,\n type GrBadgeSize,\n type GrBadgeTone,\n} from './grBadgeStyles'\n\nexport interface GrBadgeProps {\n tone?: GrBadgeTone\n dark?: boolean\n size?: GrBadgeSize\n radius?: GrBadgeRadius\n /**\n * Точка-маркер перед подписью: «● Активен».\n *\n * Имя тона красит маркер отдельно от плашки — ради пары «тихая плашка +\n * цветной маркер», из-за которой паттерн и существует. `true` берёт тон\n * самого бейджа.\n *\n * На заливке (`dark`) маркер всегда цвета текста: тон там слился бы с\n * подложкой, взятой из того же слоя.\n */\n dot?: GrBadgeDotTone | boolean\n}\n\nconst props = withDefaults(\n defineProps<GrBadgeProps>(),\n {\n // `tone`/`size`/`radius` настраиваются через `GrConfigProvider`, поэтому\n // их дефолты живут в резолверах ниже, а не в `withDefaults`.\n tone: undefined,\n dark: false,\n size: undefined,\n radius: undefined,\n dot: false,\n },\n)\n\nconst slots = defineSlots<{\n /** Содержимое метки. */\n default?: () => any\n /**\n * Иконка перед подписью: статус со спиннером, флаг, значок типа.\n *\n * Размер задаёт обёртка по ступени `size`, поэтому содержимое слота тянется\n * до неё — `class=\"h-full w-full\"`, как у `GrChip`. Вращение спиннера —\n * `animate-spin` на самой иконке.\n *\n * `GrProgressCircle` сюда не кладут: у кругового индикатора шкала виджетная,\n * нижняя ступень — `2rem`, и в строку бейджа он не помещается.\n */\n icon?: () => any\n}>()\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedTone = useGrComponentProp('GrBadge', 'tone', () => props.tone, 'neutral')\nconst resolvedSize = useGrComponentProp('GrBadge', 'size', () => props.size, 'sm')\nconst resolvedRadius = useGrComponentProp('GrBadge', 'radius', () => props.radius, 'round')\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrBadge', useAttrs(), { variant: 'tone' })\n\nconst className = computed(() => {\n return grBadgeClass({\n tone: resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n })\n})\n\nconst iconSizeClass = computed(() => badgeIconSizeClassBySize[resolvedSize.value])\n\n// Иконка и маркер стоят на одном месте и говорят об одном; два подряд — шум.\n// Выигрывает иконка: она несёт больше, чем цвет.\nconst showDot = computed(() => Boolean(props.dot) && !slots.icon)\n\nconst dotClass = computed(() => badgeDotClassFor({\n tone: typeof props.dot === 'string' ? props.dot : resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n}))\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (props.dot && slots.icon) {\n console.warn(\n '[granularity] GrBadge: `dot` и слот `icon` заняли бы одно место перед подписью — '\n + 'нарисована иконка, маркер пропущен.',\n )\n }\n\n if (props.dark && typeof props.dot === 'string') {\n console.warn(\n `[granularity] GrBadge: маркер тона \\`${props.dot}\\` на заливке не рисуется — `\n + 'подложка filled-бейджа берётся из того же слоя цветов, и маркер слился бы с ней. '\n + 'На заливке он всегда цвета текста.',\n )\n }\n })\n}\n</script>\n\n<template>\n <span\n class=\"gr-badge inline-block border whitespace-nowrap leading-none\"\n :class=\"className\"\n >\n <span class=\"gr-badge__label\">\n <span\n v-if=\"showDot\"\n data-gr-badge-dot\n :class=\"dotClass\"\n aria-hidden=\"true\"\n />\n <span\n v-if=\"$slots.icon\"\n data-gr-badge-icon\n :class=\"[badgeIconClass, iconSizeClass]\"\n >\n <slot name=\"icon\" />\n </span>\n <span class=\"gr-badge__text\">\n <slot />\n </span>\n </span>\n </span>\n</template>\n\n<style scoped>\n/*\n * Оптически центрируем текст по вертикали независимо от регистра,\n * не уменьшая высоту самого бейджа.\n *\n * Высоту держит обёртка `.gr-badge__label` (`min-height: 1lh` — высота\n * line-box при `leading-none`), а текст (`.gr-badge__text`) обрезается до\n * cap-height и центрируется внутри этой высоты через flex.\n *\n * Обрезка обязана жить на вложенном узле, а не на самом тексте: `text-box-trim`\n * уменьшает line-box, и повесь его прямо на текст — вместе с ним просядет\n * высота контента, а за ней и весь бейдж.\n *\n * `gap` задан в `em`, чтобы просвет перед маркером или иконкой шёл за кеглем\n * ступени, а не за корневым размером. Бейджа без них он не касается: у\n * flex-контейнера с единственным ребёнком просветов нет.\n */\n.gr-badge__label {\n display: flex;\n align-items: center;\n gap: 0.25em;\n min-height: 1lh;\n}\n\n.gr-badge__text {\n display: block;\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n /* Шорткат для браузеров с поддержкой нового синтаксиса. */\n text-box: trim-both cap alphabetic;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useAttrs, watchEffect } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\n\nimport {\n badgeDotClassFor,\n badgeIconClass,\n badgeIconSizeClassBySize,\n grBadgeClass,\n type GrBadgeDotTone,\n type GrBadgeRadius,\n type GrBadgeSize,\n type GrBadgeTone,\n} from './grBadgeStyles'\n\nexport interface GrBadgeProps {\n tone?: GrBadgeTone\n dark?: boolean\n size?: GrBadgeSize\n radius?: GrBadgeRadius\n /**\n * Точка-маркер перед подписью: «● Активен».\n *\n * Имя тона красит маркер отдельно от плашки — ради пары «тихая плашка +\n * цветной маркер», из-за которой паттерн и существует. `true` берёт тон\n * самого бейджа.\n *\n * На заливке (`dark`) маркер всегда цвета текста: тон там слился бы с\n * подложкой, взятой из того же слоя.\n */\n dot?: GrBadgeDotTone | boolean\n}\n\nconst props = withDefaults(\n defineProps<GrBadgeProps>(),\n {\n // `tone`/`size`/`radius` настраиваются через `GrConfigProvider`, поэтому\n // их дефолты живут в резолверах ниже, а не в `withDefaults`.\n tone: undefined,\n dark: false,\n size: undefined,\n radius: undefined,\n dot: false,\n },\n)\n\nconst slots = defineSlots<{\n /** Содержимое метки. */\n default?: () => any\n /**\n * Иконка перед подписью: статус со спиннером, флаг, значок типа.\n *\n * Размер задаёт обёртка по ступени `size`, поэтому содержимое слота тянется\n * до неё — `class=\"h-full w-full\"`, как у `GrChip`. Вращение спиннера —\n * `animate-spin` на самой иконке.\n *\n * `GrProgressCircle` сюда не кладут: у кругового индикатора шкала виджетная,\n * нижняя ступень — `2rem`, и в строку бейджа он не помещается.\n */\n icon?: () => any\n}>()\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedTone = useGrComponentProp('GrBadge', 'tone', () => props.tone, 'neutral')\nconst resolvedSize = useGrComponentProp('GrBadge', 'size', () => props.size, 'sm')\nconst resolvedRadius = useGrComponentProp('GrBadge', 'radius', () => props.radius, 'round')\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrBadge', useAttrs(), { variant: 'tone' })\n\nconst className = computed(() => {\n return grBadgeClass({\n tone: resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n })\n})\n\nconst iconSizeClass = computed(() => badgeIconSizeClassBySize[resolvedSize.value])\n\n// Иконка и маркер стоят на одном месте и говорят об одном; два подряд — шум.\n// Выигрывает иконка: она несёт больше, чем цвет.\nconst showDot = computed(() => Boolean(props.dot) && !slots.icon)\n\nconst dotClass = computed(() => badgeDotClassFor({\n tone: typeof props.dot === 'string' ? props.dot : resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n}))\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (props.dot && slots.icon) {\n console.warn(\n '[granularity] GrBadge: `dot` и слот `icon` заняли бы одно место перед подписью — '\n + 'нарисована иконка, маркер пропущен.',\n )\n }\n\n if (props.dark && typeof props.dot === 'string') {\n console.warn(\n `[granularity] GrBadge: маркер тона \\`${props.dot}\\` на заливке не рисуется — `\n + 'подложка filled-бейджа берётся из того же слоя цветов, и маркер слился бы с ней. '\n + 'На заливке он всегда цвета текста.',\n )\n }\n })\n}\n</script>\n\n<template>\n <span\n class=\"gr-badge inline-block border whitespace-nowrap leading-none\"\n :class=\"className\"\n >\n <span class=\"gr-badge__label\">\n <span\n v-if=\"showDot\"\n data-gr-badge-dot\n :class=\"dotClass\"\n aria-hidden=\"true\"\n />\n <span\n v-if=\"$slots.icon\"\n data-gr-badge-icon\n :class=\"[badgeIconClass, iconSizeClass]\"\n >\n <slot name=\"icon\" />\n </span>\n <span class=\"gr-badge__text\">\n <slot />\n </span>\n </span>\n </span>\n</template>\n\n<style scoped>\n/*\n * Оптически центрируем текст по вертикали независимо от регистра,\n * не уменьшая высоту самого бейджа.\n *\n * Высоту держит обёртка `.gr-badge__label` (`min-height: 1lh` — высота\n * line-box при `leading-none`), а текст (`.gr-badge__text`) обрезается до\n * cap-height и центрируется внутри этой высоты через flex.\n *\n * Обрезка обязана жить на вложенном узле, а не на самом тексте: `text-box-trim`\n * уменьшает line-box, и повесь его прямо на текст — вместе с ним просядет\n * высота контента, а за ней и весь бейдж.\n *\n * `gap` задан в `em`, чтобы просвет перед маркером или иконкой шёл за кеглем\n * ступени, а не за корневым размером. Бейджа без них он не касается: у\n * flex-контейнера с единственным ребёнком просветов нет.\n */\n.gr-badge__label {\n display: flex;\n align-items: center;\n gap: 0.25em;\n min-height: 1lh;\n}\n\n.gr-badge__text {\n display: block;\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n /* Шорткат для браузеров с поддержкой нового синтаксиса. */\n text-box: trim-both cap alphabetic;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;EAsCA,MAAM,QAAQ;EAad,MAAM,QAAQ,SAAA;EAiBd,MAAM,eAAe,mBAAmB,WAAW,cAAc,MAAM,MAAM,SAAS;EACtF,MAAM,eAAe,mBAAmB,WAAW,cAAc,MAAM,MAAM,IAAI;EACjF,MAAM,iBAAiB,mBAAmB,WAAW,gBAAgB,MAAM,QAAQ,OAAO;EAK1F,gBAAgB,WAAW,SAAS,GAAG,EAAE,SAAS,OAAO,CAAC;EAE1D,MAAM,YAAY,eAAe;GAC/B,OAAO,aAAa;IAClB,MAAM,aAAa;IACnB,MAAM,MAAM;IACZ,MAAM,aAAa;IACnB,QAAQ,eAAe;GACzB,CAAC;EACH,CAAC;EAED,MAAM,gBAAgB,eAAe,yBAAyB,aAAa,MAAM;EAIjF,MAAM,UAAU,eAAe,QAAQ,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI;EAEhE,MAAM,WAAW,eAAe,iBAAiB;GAC/C,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,aAAa;GAC/D,MAAM,MAAM;GACZ,MAAM,aAAa;EACrB,CAAC,CAAC;EAEF,IAAA,QAAA,IAAA,aAAA,cACE,kBAAkB;GAChB,IAAI,MAAM,OAAO,MAAM,MACrB,QAAQ,KACN,sHAEF;GAGF,IAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,UACrC,QAAQ,KACN,wCAAwC,MAAM,IAAI,gJAGpD;EAEJ,CAAC;;GAKD,OAAA,UAAA,GAAA,mBAsBO,QAAA,EArBH,OAAK,eAAA,CAAC,+DACE,UAAA,KAAS,CAAA,EAAA,GAAA,CAEnB,mBAiBO,QAjBP,YAiBO;IAfG,QAAA,SADR,UAAA,GAAA,mBAKE,QAAA;;KAHA,qBAAA;KACC,OAAK,eAAE,SAAA,KAAQ;KAChB,eAAY;;IAGNA,KAAAA,OAAO,QADf,UAAA,GAAA,mBAMO,QAAA;;KAJL,sBAAA;KACC,OAAK,eAAA,CAAG,MAAA,cAAA,GAAgB,cAAA,KAAa,CAAA;IAEtC,GAAA,CAAA,WAAoB,KAAA,QAAA,QAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAEtB,mBAEO,QAFP,YAEO,CADL,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as GrBadge_default } from "./chunks/GrBadge-
|
|
2
|
-
import { t as grBadgeSafelist } from "../../chunks/safelist-
|
|
3
|
-
import { t as grBadgeConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrBadge_default } from "./chunks/GrBadge-C42KLyUD.js";
|
|
2
|
+
import { t as grBadgeSafelist } from "../../chunks/safelist-CSfynUC1.js";
|
|
3
|
+
import { t as grBadgeConfig } from "../../chunks/config-DF1ezU6y.js";
|
|
4
4
|
export { GrBadge_default as GrBadge, GrBadge_default as default, grBadgeConfig, grBadgeSafelist };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.gr-badge__label[data-v-
|
|
1
|
+
.gr-badge__label[data-v-cbcfb89e]{align-items:center;gap:.25em;min-height:1lh;display:flex}.gr-badge__text[data-v-cbcfb89e]{text-box-trim:trim-both;text-box-edge:cap alphabetic;text-box:trim-both cap alphabetic;display:block}
|
|
@@ -3,7 +3,7 @@ import { a as useGrComponentSize, i as useGrComponentProp } from "../../../chunk
|
|
|
3
3
|
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
4
4
|
import { t as warnRenamedProp } from "../../../chunks/renamedProp-Dy96tT-F.js";
|
|
5
5
|
import { t as GR_CHIP_GROUP_CONTEXT } from "../../../chunks/grChipGroupContext-DZXkACap.js";
|
|
6
|
-
import { a as chipIconSizeClassBySize, f as grChipClass, i as chipIconClass, n as chipCloseClass, s as chipLabelClass, t as chipCloseButtonClass } from "../../../chunks/grChipStyles-
|
|
6
|
+
import { a as chipIconSizeClassBySize, f as grChipClass, i as chipIconClass, n as chipCloseClass, s as chipLabelClass, t as chipCloseButtonClass } from "../../../chunks/grChipStyles-CA9Wj1_E.js";
|
|
7
7
|
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, normalizeClass, onBeforeUnmount, openBlock, ref, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, withCtx, withModifiers } from "vue";
|
|
8
8
|
//#region src/components/GrChip/GrChip.vue?vue&type=script&setup=true&lang.ts
|
|
9
9
|
var _hoisted_1 = ["title"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrChip-CKqyzYcv.js","names":["$slots"],"sources":["../../../../src/components/GrChip/GrChip.vue","../../../../src/components/GrChip/GrChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, inject, onBeforeUnmount, ref, useAttrs } from 'vue'\n\nimport IconClose from '~icons/lucide/x'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrChipValue } from './grChipGroupContext'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\nimport { GR_CHIP_GROUP_CONTEXT } from './grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from './grChipStyles'\nimport {\n chipCloseButtonClass,\n chipCloseClass,\n chipIconClass,\n chipIconSizeClassBySize,\n chipLabelClass,\n grChipClass,\n} from './grChipStyles'\n\n/**\n * Интерактивный тег: снимаемый, выбираемый, с иконкой.\n *\n * От `GrBadge` отличается не видом, а природой: бейдж — метка, чип — виджет.\n * Поэтому цвета и радиусы взяты у бейджа как есть (в одном ряду они обязаны\n * совпадать), а роль, клавиатура и размеры — свои.\n */\nexport interface GrChipProps {\n /** Значение чипа в группе. Вне `GrChipGroup` не используется. */\n value?: GrChipValue\n /**\n * Подпись. Нужна отдельно от слота: из неё собирается имя кнопки снятия —\n * «Убрать» на двадцати кнопках подряд не даёт выбрать нужную.\n */\n label?: string\n tone?: GrChipTone\n dark?: boolean\n size?: GrChipSize\n radius?: GrChipRadius\n /** Чип становится переключателем: `aria-pressed`, `Enter`/`Space`. */\n selectable?: boolean\n /** Состояние переключателя вне группы. Внутри группы значение ведёт группа. */\n selected?: boolean\n /** Крестик. Внутри виджета он не кнопка — см. блок про роли ниже. */\n closable?: boolean\n disabled?: boolean\n /** Доступное имя, когда подпись не объясняет назначения. */\n ariaLabel?: string\n /** Имя кнопки снятия целиком. Перебивает собранное из `label`. */\n removeLabel?: string\n /**\n * `tabindex` кнопки снятия — для чипа внутри поля ввода.\n *\n * Там таб-стоп принадлежит `<input>`, а ряд чипов ходит стрелками: у\n * `GrInputTag` кольцо оставляет ровно один `0`, у `GrAutocomplete` их нет\n * вовсе. Проп нужен потому, что кольцом управляет родитель — он знает и\n * порядок чипов, и что стоит за краем ряда.\n */\n removeTabindex?: number\n}\n\nexport interface GrChipEmits {\n (e: 'update:selected', value: boolean): void\n (e: 'remove'): void\n}\n\nconst props = withDefaults(defineProps<GrChipProps>(), {\n value: undefined,\n label: undefined,\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n tone: undefined,\n dark: undefined,\n size: undefined,\n radius: undefined,\n selectable: false,\n selected: false,\n closable: false,\n disabled: false,\n ariaLabel: undefined,\n removeLabel: undefined,\n removeTabindex: undefined,\n})\n\nconst emit = defineEmits<GrChipEmits>()\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrChip', useAttrs(), { variant: 'tone' })\n\ndefineSlots<{\n /** Подпись чипа. Внутри выбираемого чипа — только фразовое содержимое. */\n default?: () => unknown\n /** Иконка перед подписью. */\n icon?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst group = inject(GR_CHIP_GROUP_CONTEXT, null)\nconst rootEl = ref<HTMLElement | null>(null)\n\nconst inGroup = computed(() => group !== null && props.value !== undefined)\n\nconst isDisabled = computed(() => props.disabled || (group?.disabled.value ?? false))\nconst isReadonly = computed(() => group?.readonly.value ?? false)\n\n/**\n * Чип — виджет ровно тогда, когда по нему кликают.\n *\n * От этого зависит вся разметка: у виджета роль, `tabindex` и клавиатура, а\n * крестик внутри него перестаёт быть кнопкой.\n */\nconst isInteractive = computed(() => inGroup.value || props.selectable)\n\nconst isSelected = computed(() => {\n if (inGroup.value && props.value !== undefined)\n return group!.isSelected(props.value)\n return props.selected\n})\n\nconst isClosable = computed(() => {\n if (isReadonly.value || isDisabled.value)\n return false\n return props.closable || (inGroup.value ? group!.closable.value : false)\n})\n\n// Приоритет: локальный проп → значение группы → `GrConfigProvider` → дефолт.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize<GrChipSize>(\n () => props.size ?? (inGroup.value ? group!.size.value : undefined),\n { component: 'GrChip' },\n)\nconst resolvedTone = useGrComponentProp(\n 'GrChip',\n 'tone',\n () => props.tone ?? (inGroup.value ? group!.tone.value : undefined),\n 'neutral',\n)\nconst resolvedRadius = useGrComponentProp(\n 'GrChip',\n 'radius',\n () => props.radius ?? (inGroup.value ? group!.radius.value : undefined),\n 'round',\n)\nconst resolvedDark = useGrComponentProp(\n 'GrChip',\n 'dark',\n () => props.dark ?? (inGroup.value ? group!.dark.value : undefined),\n false,\n)\n\nconst rootClass = computed(() => grChipClass({\n tone: resolvedTone.value,\n dark: resolvedDark.value,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n interactive: isInteractive.value && !isReadonly.value,\n selected: isSelected.value,\n disabled: isDisabled.value,\n}))\n\nconst iconSizeClass = computed(() => chipIconSizeClassBySize[resolvedSize.value])\n\nconst removeTitle = computed(() => {\n if (props.removeLabel)\n return props.removeLabel\n if (props.label)\n return t('gr.chip.removeNamed', 'Remove {label}', { label: props.label })\n return t('gr.chip.remove', 'Remove')\n})\n\n/**\n * Роль чипа задаёт группа, а не потребитель.\n *\n * Одиночный переключатель — кнопка с `aria-pressed`; в группе роль зависит от\n * множественности выбора: `radio` в одиночной (образец — `GrSegmented`),\n * `option` во множественной, где контейнер объявлен `listbox` c\n * `aria-multiselectable`.\n */\nconst chipRole = computed(() => {\n if (!inGroup.value)\n return undefined\n return group!.selection.value === 'single' ? 'radio' : 'option'\n})\n\nconst tabindex = computed(() => {\n if (!isInteractive.value)\n return undefined\n if (isDisabled.value)\n return -1\n if (!inGroup.value || props.value === undefined)\n return 0\n return group!.rovingValue.value === props.value ? 0 : -1\n})\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n if (inGroup.value && props.value !== undefined) {\n group!.toggle(props.value)\n return\n }\n\n if (props.selectable)\n emit('update:selected', !props.selected)\n}\n\nfunction requestRemove(): void {\n if (!isClosable.value)\n return\n\n emit('remove')\n if (inGroup.value && props.value !== undefined)\n group!.requestRemove(props.value)\n}\n\n/**\n * Крестик внутри виджета — не кнопка, поэтому его клик приходит сюда же и\n * разбирается по цели. Тот же приём у `GrTabs`: внутрь роли-виджета вложенная\n * кнопка запрещена (axe: `nested-interactive`), а `<button>` внутри `<button>`\n * невалиден и по контент-модели HTML.\n */\nfunction onRootClick(event: MouseEvent): void {\n if (isDisabled.value)\n return\n\n if (event.target instanceof Element && event.target.closest('[data-gr-chip-close]')) {\n requestRemove()\n return\n }\n\n toggle()\n}\n\nfunction onRootKeydown(event: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n // Навигация принадлежит группе: состав знает только она.\n if (inGroup.value && group!.handleNavigationKeys(event))\n return\n\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (!isClosable.value)\n return\n event.preventDefault()\n requestRemove()\n }\n}\n\nif (group) {\n const unregister = group.register({\n value: () => props.value as GrChipValue,\n disabled: () => isDisabled.value,\n el: () => rootEl.value,\n })\n onBeforeUnmount(unregister)\n}\n\nconst removeEl = ref<HTMLButtonElement | null>(null)\n\ndefineExpose({\n focus: () => rootEl.value?.focus(),\n blur: () => rootEl.value?.blur(),\n /**\n * Кнопка снятия — цель roving-фокуса у родителя. Отдаётся элементом, а не\n * методом `focusRemove`: кольцу нужен сам узел, оно само решает, когда и\n * куда переносить фокус.\n */\n removeEl,\n})\n</script>\n\n<template>\n <component\n :is=\"isInteractive ? 'button' : 'span'\"\n ref=\"rootEl\"\n data-gr-chip\n :type=\"isInteractive ? 'button' : undefined\"\n :class=\"rootClass\"\n :role=\"chipRole\"\n :data-value=\"value\"\n :aria-label=\"ariaLabel\"\n :aria-checked=\"chipRole === 'radio' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-selected=\"chipRole === 'option' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-pressed=\"isInteractive && !chipRole ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :disabled=\"isInteractive && isDisabled ? true : undefined\"\n :aria-keyshortcuts=\"isInteractive && isClosable ? 'Delete' : undefined\"\n :tabindex=\"tabindex\"\n @click=\"isInteractive ? onRootClick($event) : undefined\"\n @keydown=\"isInteractive ? onRootKeydown($event) : undefined\"\n >\n <span v-if=\"$slots.icon\" data-gr-chip-icon :class=\"[chipIconClass, iconSizeClass]\">\n <slot name=\"icon\" />\n </span>\n\n <span data-gr-chip-label :class=\"chipLabelClass\">\n <slot>{{ label }}</slot>\n </span>\n\n <!--\n Крестик существует в двух видах, и различитель — роль самого чипа.\n Виджет: `<span aria-hidden>` вне таб-порядка, снятие идёт `Delete`/`Backspace`.\n Не виджет: настоящая кнопка со своей остановкой `Tab` и своим именем.\n -->\n <span\n v-if=\"isClosable && isInteractive\"\n data-gr-chip-close\n :class=\"[chipCloseClass, iconSizeClass]\"\n :title=\"removeTitle\"\n aria-hidden=\"true\"\n >\n <IconClose class=\"h-full w-full\" />\n </span>\n\n <button\n v-else-if=\"isClosable\"\n ref=\"removeEl\"\n data-gr-chip-close\n type=\"button\"\n :class=\"[chipCloseButtonClass, iconSizeClass]\"\n :aria-label=\"removeTitle\"\n :tabindex=\"removeTabindex\"\n @mousedown.prevent.stop\n @click.stop=\"requestRemove()\"\n >\n <IconClose class=\"h-full w-full\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, onBeforeUnmount, ref, useAttrs } from 'vue'\n\nimport IconClose from '~icons/lucide/x'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrChipValue } from './grChipGroupContext'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\nimport { GR_CHIP_GROUP_CONTEXT } from './grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from './grChipStyles'\nimport {\n chipCloseButtonClass,\n chipCloseClass,\n chipIconClass,\n chipIconSizeClassBySize,\n chipLabelClass,\n grChipClass,\n} from './grChipStyles'\n\n/**\n * Интерактивный тег: снимаемый, выбираемый, с иконкой.\n *\n * От `GrBadge` отличается не видом, а природой: бейдж — метка, чип — виджет.\n * Поэтому цвета и радиусы взяты у бейджа как есть (в одном ряду они обязаны\n * совпадать), а роль, клавиатура и размеры — свои.\n */\nexport interface GrChipProps {\n /** Значение чипа в группе. Вне `GrChipGroup` не используется. */\n value?: GrChipValue\n /**\n * Подпись. Нужна отдельно от слота: из неё собирается имя кнопки снятия —\n * «Убрать» на двадцати кнопках подряд не даёт выбрать нужную.\n */\n label?: string\n tone?: GrChipTone\n dark?: boolean\n size?: GrChipSize\n radius?: GrChipRadius\n /** Чип становится переключателем: `aria-pressed`, `Enter`/`Space`. */\n selectable?: boolean\n /** Состояние переключателя вне группы. Внутри группы значение ведёт группа. */\n selected?: boolean\n /** Крестик. Внутри виджета он не кнопка — см. блок про роли ниже. */\n closable?: boolean\n disabled?: boolean\n /** Доступное имя, когда подпись не объясняет назначения. */\n ariaLabel?: string\n /** Имя кнопки снятия целиком. Перебивает собранное из `label`. */\n removeLabel?: string\n /**\n * `tabindex` кнопки снятия — для чипа внутри поля ввода.\n *\n * Там таб-стоп принадлежит `<input>`, а ряд чипов ходит стрелками: у\n * `GrInputTag` кольцо оставляет ровно один `0`, у `GrAutocomplete` их нет\n * вовсе. Проп нужен потому, что кольцом управляет родитель — он знает и\n * порядок чипов, и что стоит за краем ряда.\n */\n removeTabindex?: number\n}\n\nexport interface GrChipEmits {\n (e: 'update:selected', value: boolean): void\n (e: 'remove'): void\n}\n\nconst props = withDefaults(defineProps<GrChipProps>(), {\n value: undefined,\n label: undefined,\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n tone: undefined,\n dark: undefined,\n size: undefined,\n radius: undefined,\n selectable: false,\n selected: false,\n closable: false,\n disabled: false,\n ariaLabel: undefined,\n removeLabel: undefined,\n removeTabindex: undefined,\n})\n\nconst emit = defineEmits<GrChipEmits>()\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrChip', useAttrs(), { variant: 'tone' })\n\ndefineSlots<{\n /** Подпись чипа. Внутри выбираемого чипа — только фразовое содержимое. */\n default?: () => unknown\n /** Иконка перед подписью. */\n icon?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst group = inject(GR_CHIP_GROUP_CONTEXT, null)\nconst rootEl = ref<HTMLElement | null>(null)\n\nconst inGroup = computed(() => group !== null && props.value !== undefined)\n\nconst isDisabled = computed(() => props.disabled || (group?.disabled.value ?? false))\nconst isReadonly = computed(() => group?.readonly.value ?? false)\n\n/**\n * Чип — виджет ровно тогда, когда по нему кликают.\n *\n * От этого зависит вся разметка: у виджета роль, `tabindex` и клавиатура, а\n * крестик внутри него перестаёт быть кнопкой.\n */\nconst isInteractive = computed(() => inGroup.value || props.selectable)\n\nconst isSelected = computed(() => {\n if (inGroup.value && props.value !== undefined)\n return group!.isSelected(props.value)\n return props.selected\n})\n\nconst isClosable = computed(() => {\n if (isReadonly.value || isDisabled.value)\n return false\n return props.closable || (inGroup.value ? group!.closable.value : false)\n})\n\n// Приоритет: локальный проп → значение группы → `GrConfigProvider` → дефолт.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize<GrChipSize>(\n () => props.size ?? (inGroup.value ? group!.size.value : undefined),\n { component: 'GrChip' },\n)\nconst resolvedTone = useGrComponentProp(\n 'GrChip',\n 'tone',\n () => props.tone ?? (inGroup.value ? group!.tone.value : undefined),\n 'neutral',\n)\nconst resolvedRadius = useGrComponentProp(\n 'GrChip',\n 'radius',\n () => props.radius ?? (inGroup.value ? group!.radius.value : undefined),\n 'round',\n)\nconst resolvedDark = useGrComponentProp(\n 'GrChip',\n 'dark',\n () => props.dark ?? (inGroup.value ? group!.dark.value : undefined),\n false,\n)\n\nconst rootClass = computed(() => grChipClass({\n tone: resolvedTone.value,\n dark: resolvedDark.value,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n interactive: isInteractive.value && !isReadonly.value,\n selected: isSelected.value,\n disabled: isDisabled.value,\n}))\n\nconst iconSizeClass = computed(() => chipIconSizeClassBySize[resolvedSize.value])\n\nconst removeTitle = computed(() => {\n if (props.removeLabel)\n return props.removeLabel\n if (props.label)\n return t('gr.chip.removeNamed', 'Remove {label}', { label: props.label })\n return t('gr.chip.remove', 'Remove')\n})\n\n/**\n * Роль чипа задаёт группа, а не потребитель.\n *\n * Одиночный переключатель — кнопка с `aria-pressed`; в группе роль зависит от\n * множественности выбора: `radio` в одиночной (образец — `GrSegmented`),\n * `option` во множественной, где контейнер объявлен `listbox` c\n * `aria-multiselectable`.\n */\nconst chipRole = computed(() => {\n if (!inGroup.value)\n return undefined\n return group!.selection.value === 'single' ? 'radio' : 'option'\n})\n\nconst tabindex = computed(() => {\n if (!isInteractive.value)\n return undefined\n if (isDisabled.value)\n return -1\n if (!inGroup.value || props.value === undefined)\n return 0\n return group!.rovingValue.value === props.value ? 0 : -1\n})\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n if (inGroup.value && props.value !== undefined) {\n group!.toggle(props.value)\n return\n }\n\n if (props.selectable)\n emit('update:selected', !props.selected)\n}\n\nfunction requestRemove(): void {\n if (!isClosable.value)\n return\n\n emit('remove')\n if (inGroup.value && props.value !== undefined)\n group!.requestRemove(props.value)\n}\n\n/**\n * Крестик внутри виджета — не кнопка, поэтому его клик приходит сюда же и\n * разбирается по цели. Тот же приём у `GrTabs`: внутрь роли-виджета вложенная\n * кнопка запрещена (axe: `nested-interactive`), а `<button>` внутри `<button>`\n * невалиден и по контент-модели HTML.\n */\nfunction onRootClick(event: MouseEvent): void {\n if (isDisabled.value)\n return\n\n if (event.target instanceof Element && event.target.closest('[data-gr-chip-close]')) {\n requestRemove()\n return\n }\n\n toggle()\n}\n\nfunction onRootKeydown(event: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n // Навигация принадлежит группе: состав знает только она.\n if (inGroup.value && group!.handleNavigationKeys(event))\n return\n\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (!isClosable.value)\n return\n event.preventDefault()\n requestRemove()\n }\n}\n\nif (group) {\n const unregister = group.register({\n value: () => props.value as GrChipValue,\n disabled: () => isDisabled.value,\n el: () => rootEl.value,\n })\n onBeforeUnmount(unregister)\n}\n\nconst removeEl = ref<HTMLButtonElement | null>(null)\n\ndefineExpose({\n focus: () => rootEl.value?.focus(),\n blur: () => rootEl.value?.blur(),\n /**\n * Кнопка снятия — цель roving-фокуса у родителя. Отдаётся элементом, а не\n * методом `focusRemove`: кольцу нужен сам узел, оно само решает, когда и\n * куда переносить фокус.\n */\n removeEl,\n})\n</script>\n\n<template>\n <component\n :is=\"isInteractive ? 'button' : 'span'\"\n ref=\"rootEl\"\n data-gr-chip\n :type=\"isInteractive ? 'button' : undefined\"\n :class=\"rootClass\"\n :role=\"chipRole\"\n :data-value=\"value\"\n :aria-label=\"ariaLabel\"\n :aria-checked=\"chipRole === 'radio' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-selected=\"chipRole === 'option' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-pressed=\"isInteractive && !chipRole ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :disabled=\"isInteractive && isDisabled ? true : undefined\"\n :aria-keyshortcuts=\"isInteractive && isClosable ? 'Delete' : undefined\"\n :tabindex=\"tabindex\"\n @click=\"isInteractive ? onRootClick($event) : undefined\"\n @keydown=\"isInteractive ? onRootKeydown($event) : undefined\"\n >\n <span v-if=\"$slots.icon\" data-gr-chip-icon :class=\"[chipIconClass, iconSizeClass]\">\n <slot name=\"icon\" />\n </span>\n\n <span data-gr-chip-label :class=\"chipLabelClass\">\n <slot>{{ label }}</slot>\n </span>\n\n <!--\n Крестик существует в двух видах, и различитель — роль самого чипа.\n Виджет: `<span aria-hidden>` вне таб-порядка, снятие идёт `Delete`/`Backspace`.\n Не виджет: настоящая кнопка со своей остановкой `Tab` и своим именем.\n -->\n <span\n v-if=\"isClosable && isInteractive\"\n data-gr-chip-close\n :class=\"[chipCloseClass, iconSizeClass]\"\n :title=\"removeTitle\"\n aria-hidden=\"true\"\n >\n <IconClose class=\"h-full w-full\" />\n </span>\n\n <button\n v-else-if=\"isClosable\"\n ref=\"removeEl\"\n data-gr-chip-close\n type=\"button\"\n :class=\"[chipCloseButtonClass, iconSizeClass]\"\n :aria-label=\"removeTitle\"\n :tabindex=\"removeTabindex\"\n @mousedown.prevent.stop\n @click.stop=\"requestRemove()\"\n >\n <IconClose class=\"h-full w-full\" />\n </button>\n </component>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GrChip-DoyS1Dr4.js","names":["$slots"],"sources":["../../../../src/components/GrChip/GrChip.vue","../../../../src/components/GrChip/GrChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, inject, onBeforeUnmount, ref, useAttrs } from 'vue'\n\nimport IconClose from '~icons/lucide/x'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrChipValue } from './grChipGroupContext'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\nimport { GR_CHIP_GROUP_CONTEXT } from './grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from './grChipStyles'\nimport {\n chipCloseButtonClass,\n chipCloseClass,\n chipIconClass,\n chipIconSizeClassBySize,\n chipLabelClass,\n grChipClass,\n} from './grChipStyles'\n\n/**\n * Интерактивный тег: снимаемый, выбираемый, с иконкой.\n *\n * От `GrBadge` отличается не видом, а природой: бейдж — метка, чип — виджет.\n * Поэтому цвета и радиусы взяты у бейджа как есть (в одном ряду они обязаны\n * совпадать), а роль, клавиатура и размеры — свои.\n */\nexport interface GrChipProps {\n /** Значение чипа в группе. Вне `GrChipGroup` не используется. */\n value?: GrChipValue\n /**\n * Подпись. Нужна отдельно от слота: из неё собирается имя кнопки снятия —\n * «Убрать» на двадцати кнопках подряд не даёт выбрать нужную.\n */\n label?: string\n tone?: GrChipTone\n dark?: boolean\n size?: GrChipSize\n radius?: GrChipRadius\n /** Чип становится переключателем: `aria-pressed`, `Enter`/`Space`. */\n selectable?: boolean\n /** Состояние переключателя вне группы. Внутри группы значение ведёт группа. */\n selected?: boolean\n /** Крестик. Внутри виджета он не кнопка — см. блок про роли ниже. */\n closable?: boolean\n disabled?: boolean\n /** Доступное имя, когда подпись не объясняет назначения. */\n ariaLabel?: string\n /** Имя кнопки снятия целиком. Перебивает собранное из `label`. */\n removeLabel?: string\n /**\n * `tabindex` кнопки снятия — для чипа внутри поля ввода.\n *\n * Там таб-стоп принадлежит `<input>`, а ряд чипов ходит стрелками: у\n * `GrInputTag` кольцо оставляет ровно один `0`, у `GrAutocomplete` их нет\n * вовсе. Проп нужен потому, что кольцом управляет родитель — он знает и\n * порядок чипов, и что стоит за краем ряда.\n */\n removeTabindex?: number\n}\n\nexport interface GrChipEmits {\n (e: 'update:selected', value: boolean): void\n (e: 'remove'): void\n}\n\nconst props = withDefaults(defineProps<GrChipProps>(), {\n value: undefined,\n label: undefined,\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n tone: undefined,\n dark: undefined,\n size: undefined,\n radius: undefined,\n selectable: false,\n selected: false,\n closable: false,\n disabled: false,\n ariaLabel: undefined,\n removeLabel: undefined,\n removeTabindex: undefined,\n})\n\nconst emit = defineEmits<GrChipEmits>()\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrChip', useAttrs(), { variant: 'tone' })\n\ndefineSlots<{\n /** Подпись чипа. Внутри выбираемого чипа — только фразовое содержимое. */\n default?: () => unknown\n /** Иконка перед подписью. */\n icon?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst group = inject(GR_CHIP_GROUP_CONTEXT, null)\nconst rootEl = ref<HTMLElement | null>(null)\n\nconst inGroup = computed(() => group !== null && props.value !== undefined)\n\nconst isDisabled = computed(() => props.disabled || (group?.disabled.value ?? false))\nconst isReadonly = computed(() => group?.readonly.value ?? false)\n\n/**\n * Чип — виджет ровно тогда, когда по нему кликают.\n *\n * От этого зависит вся разметка: у виджета роль, `tabindex` и клавиатура, а\n * крестик внутри него перестаёт быть кнопкой.\n */\nconst isInteractive = computed(() => inGroup.value || props.selectable)\n\nconst isSelected = computed(() => {\n if (inGroup.value && props.value !== undefined)\n return group!.isSelected(props.value)\n return props.selected\n})\n\nconst isClosable = computed(() => {\n if (isReadonly.value || isDisabled.value)\n return false\n return props.closable || (inGroup.value ? group!.closable.value : false)\n})\n\n// Приоритет: локальный проп → значение группы → `GrConfigProvider` → дефолт.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize<GrChipSize>(\n () => props.size ?? (inGroup.value ? group!.size.value : undefined),\n { component: 'GrChip' },\n)\nconst resolvedTone = useGrComponentProp(\n 'GrChip',\n 'tone',\n () => props.tone ?? (inGroup.value ? group!.tone.value : undefined),\n 'neutral',\n)\nconst resolvedRadius = useGrComponentProp(\n 'GrChip',\n 'radius',\n () => props.radius ?? (inGroup.value ? group!.radius.value : undefined),\n 'round',\n)\nconst resolvedDark = useGrComponentProp(\n 'GrChip',\n 'dark',\n () => props.dark ?? (inGroup.value ? group!.dark.value : undefined),\n false,\n)\n\nconst rootClass = computed(() => grChipClass({\n tone: resolvedTone.value,\n dark: resolvedDark.value,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n interactive: isInteractive.value && !isReadonly.value,\n selected: isSelected.value,\n disabled: isDisabled.value,\n}))\n\nconst iconSizeClass = computed(() => chipIconSizeClassBySize[resolvedSize.value])\n\nconst removeTitle = computed(() => {\n if (props.removeLabel)\n return props.removeLabel\n if (props.label)\n return t('gr.chip.removeNamed', 'Remove {label}', { label: props.label })\n return t('gr.chip.remove', 'Remove')\n})\n\n/**\n * Роль чипа задаёт группа, а не потребитель.\n *\n * Одиночный переключатель — кнопка с `aria-pressed`; в группе роль зависит от\n * множественности выбора: `radio` в одиночной (образец — `GrSegmented`),\n * `option` во множественной, где контейнер объявлен `listbox` c\n * `aria-multiselectable`.\n */\nconst chipRole = computed(() => {\n if (!inGroup.value)\n return undefined\n return group!.selection.value === 'single' ? 'radio' : 'option'\n})\n\nconst tabindex = computed(() => {\n if (!isInteractive.value)\n return undefined\n if (isDisabled.value)\n return -1\n if (!inGroup.value || props.value === undefined)\n return 0\n return group!.rovingValue.value === props.value ? 0 : -1\n})\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n if (inGroup.value && props.value !== undefined) {\n group!.toggle(props.value)\n return\n }\n\n if (props.selectable)\n emit('update:selected', !props.selected)\n}\n\nfunction requestRemove(): void {\n if (!isClosable.value)\n return\n\n emit('remove')\n if (inGroup.value && props.value !== undefined)\n group!.requestRemove(props.value)\n}\n\n/**\n * Крестик внутри виджета — не кнопка, поэтому его клик приходит сюда же и\n * разбирается по цели. Тот же приём у `GrTabs`: внутрь роли-виджета вложенная\n * кнопка запрещена (axe: `nested-interactive`), а `<button>` внутри `<button>`\n * невалиден и по контент-модели HTML.\n */\nfunction onRootClick(event: MouseEvent): void {\n if (isDisabled.value)\n return\n\n if (event.target instanceof Element && event.target.closest('[data-gr-chip-close]')) {\n requestRemove()\n return\n }\n\n toggle()\n}\n\nfunction onRootKeydown(event: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n // Навигация принадлежит группе: состав знает только она.\n if (inGroup.value && group!.handleNavigationKeys(event))\n return\n\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (!isClosable.value)\n return\n event.preventDefault()\n requestRemove()\n }\n}\n\nif (group) {\n const unregister = group.register({\n value: () => props.value as GrChipValue,\n disabled: () => isDisabled.value,\n el: () => rootEl.value,\n })\n onBeforeUnmount(unregister)\n}\n\nconst removeEl = ref<HTMLButtonElement | null>(null)\n\ndefineExpose({\n focus: () => rootEl.value?.focus(),\n blur: () => rootEl.value?.blur(),\n /**\n * Кнопка снятия — цель roving-фокуса у родителя. Отдаётся элементом, а не\n * методом `focusRemove`: кольцу нужен сам узел, оно само решает, когда и\n * куда переносить фокус.\n */\n removeEl,\n})\n</script>\n\n<template>\n <component\n :is=\"isInteractive ? 'button' : 'span'\"\n ref=\"rootEl\"\n data-gr-chip\n :type=\"isInteractive ? 'button' : undefined\"\n :class=\"rootClass\"\n :role=\"chipRole\"\n :data-value=\"value\"\n :aria-label=\"ariaLabel\"\n :aria-checked=\"chipRole === 'radio' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-selected=\"chipRole === 'option' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-pressed=\"isInteractive && !chipRole ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :disabled=\"isInteractive && isDisabled ? true : undefined\"\n :aria-keyshortcuts=\"isInteractive && isClosable ? 'Delete' : undefined\"\n :tabindex=\"tabindex\"\n @click=\"isInteractive ? onRootClick($event) : undefined\"\n @keydown=\"isInteractive ? onRootKeydown($event) : undefined\"\n >\n <span v-if=\"$slots.icon\" data-gr-chip-icon :class=\"[chipIconClass, iconSizeClass]\">\n <slot name=\"icon\" />\n </span>\n\n <span data-gr-chip-label :class=\"chipLabelClass\">\n <slot>{{ label }}</slot>\n </span>\n\n <!--\n Крестик существует в двух видах, и различитель — роль самого чипа.\n Виджет: `<span aria-hidden>` вне таб-порядка, снятие идёт `Delete`/`Backspace`.\n Не виджет: настоящая кнопка со своей остановкой `Tab` и своим именем.\n -->\n <span\n v-if=\"isClosable && isInteractive\"\n data-gr-chip-close\n :class=\"[chipCloseClass, iconSizeClass]\"\n :title=\"removeTitle\"\n aria-hidden=\"true\"\n >\n <IconClose class=\"h-full w-full\" />\n </span>\n\n <button\n v-else-if=\"isClosable\"\n ref=\"removeEl\"\n data-gr-chip-close\n type=\"button\"\n :class=\"[chipCloseButtonClass, iconSizeClass]\"\n :aria-label=\"removeTitle\"\n :tabindex=\"removeTabindex\"\n @mousedown.prevent.stop\n @click.stop=\"requestRemove()\"\n >\n <IconClose class=\"h-full w-full\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, onBeforeUnmount, ref, useAttrs } from 'vue'\n\nimport IconClose from '~icons/lucide/x'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrChipValue } from './grChipGroupContext'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\nimport { GR_CHIP_GROUP_CONTEXT } from './grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from './grChipStyles'\nimport {\n chipCloseButtonClass,\n chipCloseClass,\n chipIconClass,\n chipIconSizeClassBySize,\n chipLabelClass,\n grChipClass,\n} from './grChipStyles'\n\n/**\n * Интерактивный тег: снимаемый, выбираемый, с иконкой.\n *\n * От `GrBadge` отличается не видом, а природой: бейдж — метка, чип — виджет.\n * Поэтому цвета и радиусы взяты у бейджа как есть (в одном ряду они обязаны\n * совпадать), а роль, клавиатура и размеры — свои.\n */\nexport interface GrChipProps {\n /** Значение чипа в группе. Вне `GrChipGroup` не используется. */\n value?: GrChipValue\n /**\n * Подпись. Нужна отдельно от слота: из неё собирается имя кнопки снятия —\n * «Убрать» на двадцати кнопках подряд не даёт выбрать нужную.\n */\n label?: string\n tone?: GrChipTone\n dark?: boolean\n size?: GrChipSize\n radius?: GrChipRadius\n /** Чип становится переключателем: `aria-pressed`, `Enter`/`Space`. */\n selectable?: boolean\n /** Состояние переключателя вне группы. Внутри группы значение ведёт группа. */\n selected?: boolean\n /** Крестик. Внутри виджета он не кнопка — см. блок про роли ниже. */\n closable?: boolean\n disabled?: boolean\n /** Доступное имя, когда подпись не объясняет назначения. */\n ariaLabel?: string\n /** Имя кнопки снятия целиком. Перебивает собранное из `label`. */\n removeLabel?: string\n /**\n * `tabindex` кнопки снятия — для чипа внутри поля ввода.\n *\n * Там таб-стоп принадлежит `<input>`, а ряд чипов ходит стрелками: у\n * `GrInputTag` кольцо оставляет ровно один `0`, у `GrAutocomplete` их нет\n * вовсе. Проп нужен потому, что кольцом управляет родитель — он знает и\n * порядок чипов, и что стоит за краем ряда.\n */\n removeTabindex?: number\n}\n\nexport interface GrChipEmits {\n (e: 'update:selected', value: boolean): void\n (e: 'remove'): void\n}\n\nconst props = withDefaults(defineProps<GrChipProps>(), {\n value: undefined,\n label: undefined,\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n tone: undefined,\n dark: undefined,\n size: undefined,\n radius: undefined,\n selectable: false,\n selected: false,\n closable: false,\n disabled: false,\n ariaLabel: undefined,\n removeLabel: undefined,\n removeTabindex: undefined,\n})\n\nconst emit = defineEmits<GrChipEmits>()\n\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrChip', useAttrs(), { variant: 'tone' })\n\ndefineSlots<{\n /** Подпись чипа. Внутри выбираемого чипа — только фразовое содержимое. */\n default?: () => unknown\n /** Иконка перед подписью. */\n icon?: () => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst group = inject(GR_CHIP_GROUP_CONTEXT, null)\nconst rootEl = ref<HTMLElement | null>(null)\n\nconst inGroup = computed(() => group !== null && props.value !== undefined)\n\nconst isDisabled = computed(() => props.disabled || (group?.disabled.value ?? false))\nconst isReadonly = computed(() => group?.readonly.value ?? false)\n\n/**\n * Чип — виджет ровно тогда, когда по нему кликают.\n *\n * От этого зависит вся разметка: у виджета роль, `tabindex` и клавиатура, а\n * крестик внутри него перестаёт быть кнопкой.\n */\nconst isInteractive = computed(() => inGroup.value || props.selectable)\n\nconst isSelected = computed(() => {\n if (inGroup.value && props.value !== undefined)\n return group!.isSelected(props.value)\n return props.selected\n})\n\nconst isClosable = computed(() => {\n if (isReadonly.value || isDisabled.value)\n return false\n return props.closable || (inGroup.value ? group!.closable.value : false)\n})\n\n// Приоритет: локальный проп → значение группы → `GrConfigProvider` → дефолт.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize<GrChipSize>(\n () => props.size ?? (inGroup.value ? group!.size.value : undefined),\n { component: 'GrChip' },\n)\nconst resolvedTone = useGrComponentProp(\n 'GrChip',\n 'tone',\n () => props.tone ?? (inGroup.value ? group!.tone.value : undefined),\n 'neutral',\n)\nconst resolvedRadius = useGrComponentProp(\n 'GrChip',\n 'radius',\n () => props.radius ?? (inGroup.value ? group!.radius.value : undefined),\n 'round',\n)\nconst resolvedDark = useGrComponentProp(\n 'GrChip',\n 'dark',\n () => props.dark ?? (inGroup.value ? group!.dark.value : undefined),\n false,\n)\n\nconst rootClass = computed(() => grChipClass({\n tone: resolvedTone.value,\n dark: resolvedDark.value,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n interactive: isInteractive.value && !isReadonly.value,\n selected: isSelected.value,\n disabled: isDisabled.value,\n}))\n\nconst iconSizeClass = computed(() => chipIconSizeClassBySize[resolvedSize.value])\n\nconst removeTitle = computed(() => {\n if (props.removeLabel)\n return props.removeLabel\n if (props.label)\n return t('gr.chip.removeNamed', 'Remove {label}', { label: props.label })\n return t('gr.chip.remove', 'Remove')\n})\n\n/**\n * Роль чипа задаёт группа, а не потребитель.\n *\n * Одиночный переключатель — кнопка с `aria-pressed`; в группе роль зависит от\n * множественности выбора: `radio` в одиночной (образец — `GrSegmented`),\n * `option` во множественной, где контейнер объявлен `listbox` c\n * `aria-multiselectable`.\n */\nconst chipRole = computed(() => {\n if (!inGroup.value)\n return undefined\n return group!.selection.value === 'single' ? 'radio' : 'option'\n})\n\nconst tabindex = computed(() => {\n if (!isInteractive.value)\n return undefined\n if (isDisabled.value)\n return -1\n if (!inGroup.value || props.value === undefined)\n return 0\n return group!.rovingValue.value === props.value ? 0 : -1\n})\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n if (inGroup.value && props.value !== undefined) {\n group!.toggle(props.value)\n return\n }\n\n if (props.selectable)\n emit('update:selected', !props.selected)\n}\n\nfunction requestRemove(): void {\n if (!isClosable.value)\n return\n\n emit('remove')\n if (inGroup.value && props.value !== undefined)\n group!.requestRemove(props.value)\n}\n\n/**\n * Крестик внутри виджета — не кнопка, поэтому его клик приходит сюда же и\n * разбирается по цели. Тот же приём у `GrTabs`: внутрь роли-виджета вложенная\n * кнопка запрещена (axe: `nested-interactive`), а `<button>` внутри `<button>`\n * невалиден и по контент-модели HTML.\n */\nfunction onRootClick(event: MouseEvent): void {\n if (isDisabled.value)\n return\n\n if (event.target instanceof Element && event.target.closest('[data-gr-chip-close]')) {\n requestRemove()\n return\n }\n\n toggle()\n}\n\nfunction onRootKeydown(event: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n // Навигация принадлежит группе: состав знает только она.\n if (inGroup.value && group!.handleNavigationKeys(event))\n return\n\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (!isClosable.value)\n return\n event.preventDefault()\n requestRemove()\n }\n}\n\nif (group) {\n const unregister = group.register({\n value: () => props.value as GrChipValue,\n disabled: () => isDisabled.value,\n el: () => rootEl.value,\n })\n onBeforeUnmount(unregister)\n}\n\nconst removeEl = ref<HTMLButtonElement | null>(null)\n\ndefineExpose({\n focus: () => rootEl.value?.focus(),\n blur: () => rootEl.value?.blur(),\n /**\n * Кнопка снятия — цель roving-фокуса у родителя. Отдаётся элементом, а не\n * методом `focusRemove`: кольцу нужен сам узел, оно само решает, когда и\n * куда переносить фокус.\n */\n removeEl,\n})\n</script>\n\n<template>\n <component\n :is=\"isInteractive ? 'button' : 'span'\"\n ref=\"rootEl\"\n data-gr-chip\n :type=\"isInteractive ? 'button' : undefined\"\n :class=\"rootClass\"\n :role=\"chipRole\"\n :data-value=\"value\"\n :aria-label=\"ariaLabel\"\n :aria-checked=\"chipRole === 'radio' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-selected=\"chipRole === 'option' ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-pressed=\"isInteractive && !chipRole ? (isSelected ? 'true' : 'false') : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :disabled=\"isInteractive && isDisabled ? true : undefined\"\n :aria-keyshortcuts=\"isInteractive && isClosable ? 'Delete' : undefined\"\n :tabindex=\"tabindex\"\n @click=\"isInteractive ? onRootClick($event) : undefined\"\n @keydown=\"isInteractive ? onRootKeydown($event) : undefined\"\n >\n <span v-if=\"$slots.icon\" data-gr-chip-icon :class=\"[chipIconClass, iconSizeClass]\">\n <slot name=\"icon\" />\n </span>\n\n <span data-gr-chip-label :class=\"chipLabelClass\">\n <slot>{{ label }}</slot>\n </span>\n\n <!--\n Крестик существует в двух видах, и различитель — роль самого чипа.\n Виджет: `<span aria-hidden>` вне таб-порядка, снятие идёт `Delete`/`Backspace`.\n Не виджет: настоящая кнопка со своей остановкой `Tab` и своим именем.\n -->\n <span\n v-if=\"isClosable && isInteractive\"\n data-gr-chip-close\n :class=\"[chipCloseClass, iconSizeClass]\"\n :title=\"removeTitle\"\n aria-hidden=\"true\"\n >\n <IconClose class=\"h-full w-full\" />\n </span>\n\n <button\n v-else-if=\"isClosable\"\n ref=\"removeEl\"\n data-gr-chip-close\n type=\"button\"\n :class=\"[chipCloseButtonClass, iconSizeClass]\"\n :aria-label=\"removeTitle\"\n :tabindex=\"removeTabindex\"\n @mousedown.prevent.stop\n @click.stop=\"requestRemove()\"\n >\n <IconClose class=\"h-full w-full\" />\n </button>\n </component>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrChip_default } from "./chunks/GrChip-
|
|
2
|
-
import { n as grChipSafelist, t as grChipConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrChip_default } from "./chunks/GrChip-DoyS1Dr4.js";
|
|
2
|
+
import { n as grChipSafelist, t as grChipConfig } from "../../chunks/config-AKpLyIGD.js";
|
|
3
3
|
export { GrChip_default as GrChip, GrChip_default as default, grChipConfig, grChipSafelist };
|