@feugene/granularity 0.22.0 → 0.23.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.
Files changed (38) hide show
  1. package/dist/chunks/{config-jyZq8XKL.js → config-DCwQBMmJ.js} +12 -3
  2. package/dist/chunks/config-DCwQBMmJ.js.map +1 -0
  3. package/dist/chunks/{trending-up-Bf9AQmoq.js → deltaTone-B5OoS1vo.js} +19 -1
  4. package/dist/chunks/deltaTone-B5OoS1vo.js.map +1 -0
  5. package/dist/chunks/{granular-provider-CYroWMjO.js → granular-provider-CQKhl5C_.js} +1 -1
  6. package/dist/chunks/{granular-provider-CYroWMjO.js.map → granular-provider-CQKhl5C_.js.map} +1 -1
  7. package/dist/components/GrDelta/chunks/{GrDelta-BMmpYz8K.js → GrDelta-DFvxNpmN.js} +0 -0
  8. package/dist/components/GrDelta/chunks/GrDelta-DFvxNpmN.js.map +1 -0
  9. package/dist/components/GrDelta/index.js +3 -2
  10. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-pXh1LZEe.js → GrDescriptionList-2zFXzX83.js} +15 -4
  11. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-2zFXzX83.js.map +1 -0
  12. package/dist/components/GrDescriptionList/index.js +2 -2
  13. package/dist/components/GrFilePreview/chunks/{GrFilePreview-rbE3zL9w.js → GrFilePreview-D0Bt-0nn.js} +1 -1
  14. package/dist/components/GrFilePreview/chunks/{GrFilePreview-rbE3zL9w.js.map → GrFilePreview-D0Bt-0nn.js.map} +1 -1
  15. package/dist/components/GrFilePreview/index.js +2 -2
  16. package/dist/components/GrStatistic/chunks/{GrStatistic-DkzWUAw1.js → GrStatistic-C7wQECeS.js} +20 -4
  17. package/dist/components/GrStatistic/chunks/GrStatistic-C7wQECeS.js.map +1 -0
  18. package/dist/components/GrStatistic/index.js +1 -1
  19. package/dist/granular-provider-node.js +1 -1
  20. package/dist/granular-provider.js +1 -1
  21. package/dist/index.js +7 -6
  22. package/dist/types/components/GrDelta/index.d.ts +1 -0
  23. package/dist/types/components/GrDescriptionList/GrDescriptionList.vue.d.ts +6 -2
  24. package/dist/types/components/GrDescriptionList/grDescriptionListStyles.d.ts +12 -5
  25. package/dist/types/components/GrFilePreview/index.d.ts +1 -0
  26. package/dist/types/components/GrInput/GrInput.vue.d.ts +1 -1
  27. package/dist/types/components/GrNumberInput/GrNumberInput.vue.d.ts +1 -1
  28. package/dist/types/components/GrPromptDialog/GrPromptDialog.vue.d.ts +1 -1
  29. package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +12 -2
  30. package/dist/types/components/GrStatistic/defaults.d.ts +6 -0
  31. package/dist/types/internal/granularityFormat.d.ts +26 -0
  32. package/dist/web-types.json +14 -4
  33. package/package.json +1 -1
  34. package/dist/chunks/config-jyZq8XKL.js.map +0 -1
  35. package/dist/chunks/trending-up-Bf9AQmoq.js.map +0 -1
  36. package/dist/components/GrDelta/chunks/GrDelta-BMmpYz8K.js.map +0 -1
  37. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-pXh1LZEe.js.map +0 -1
  38. package/dist/components/GrStatistic/chunks/GrStatistic-DkzWUAw1.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrStatistic-C7wQECeS.js","names":["$slots"],"sources":["../../../../src/components/GrStatistic/countUp.ts","../../../../src/components/GrStatistic/formatStatisticValue.ts","../../../../src/components/GrStatistic/GrStatistic.vue","../../../../src/components/GrStatistic/GrStatistic.vue"],"sourcesContent":["/**\n * Перебор чисел для `GrStatistic`: чистая арифметика кадра без Vue и таймеров.\n *\n * Вынесено отдельным модулем по той же причине, что и `formatStatisticValue`:\n * кривая и округление проверяются напрямую, без монтирования и подмены `rAF`.\n */\n\n/** Быстрый старт, мягкая остановка — та же форма, что у `--gr-ease-out`. */\nexport function easeOutCubic(t: number): number {\n const clamped = Math.min(Math.max(t, 0), 1)\n return 1 - (1 - clamped) ** 3\n}\n\n/**\n * Значение кадра на `elapsed` миллисекунде перехода `from → to`.\n *\n * Округление до `precision` обязательно: без него кадры показывали бы\n * `1283.6666666666667` там, где показатель целый, и ширина строки прыгала бы\n * даже при `tabular-nums`. Последний кадр возвращает ровно `to` — приближение\n * не должно оставлять показатель в 1283.9999.\n */\nexport function countUpFrame(\n from: number,\n to: number,\n elapsed: number,\n duration: number,\n precision?: number,\n): number {\n if (!(duration > 0) || elapsed >= duration) return to\n\n const digits = Math.min(Math.max(precision ?? 0, 0), 20)\n const value = from + (to - from) * easeOutCubic(elapsed / duration)\n\n return Number(value.toFixed(digits))\n}\n","export type GrStatisticFormatOptions = {\n /** Число знаков после запятой. `undefined` — оставить как есть. */\n precision?: number\n /** Разделитель групп разрядов. Сильнее локали. */\n groupSeparator?: string\n /** Десятичный разделитель. Сильнее локали. */\n decimalSeparator?: string\n /** BCP-47 локаль: разделители берутся из `Intl.NumberFormat`. */\n locale?: string\n}\n\nconst DEFAULT_GROUP_SEPARATOR = ' '\nconst DEFAULT_DECIMAL_SEPARATOR = '.'\n\n/**\n * Форматирует значение показателя: фиксирует точность и расставляет разделители.\n *\n * Нечисловые значения возвращаются без изменений — это позволяет показывать в\n * `GrStatistic` уже готовые строки («2 ч 15 мин», «—») без обходных пропов.\n * Вынесено из SFC отдельным модулем, чтобы тестировалось без монтирования.\n *\n * Порядок разрешения разделителей: явный проп → локаль → встроенный дефолт.\n */\nexport function formatStatisticValue(\n value: number | string,\n options: GrStatisticFormatOptions = {},\n): string {\n const { precision, groupSeparator, decimalSeparator, locale } = options\n\n const numeric = typeof value === 'number' ? value : Number(value)\n if (typeof value === 'string' && (value.trim() === '' || !Number.isFinite(numeric))) {\n return value\n }\n if (!Number.isFinite(numeric)) return String(value)\n\n if (locale) {\n return formatByLocale(numeric, locale, { precision, groupSeparator, decimalSeparator })\n }\n\n const fixed = precision === undefined ? String(numeric) : numeric.toFixed(precision)\n // `toFixed` не даёт экспоненциальной записи, а `String(число)` — может (1e21+).\n // В таком случае группировать нечего: возвращаем как есть.\n if (fixed.includes('e') || fixed.includes('E')) return fixed\n\n const negative = fixed.startsWith('-')\n const [intPart, fracPart] = (negative ? fixed.slice(1) : fixed).split('.')\n\n const separator = groupSeparator ?? DEFAULT_GROUP_SEPARATOR\n const grouped = separator\n ? intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, separator)\n : intPart\n\n return [\n negative ? '-' : '',\n grouped,\n fracPart ? `${decimalSeparator ?? DEFAULT_DECIMAL_SEPARATOR}${fracPart}` : '',\n ].join('')\n}\n\n/**\n * `formatToParts` вместо `format`: даёт заменить отдельные разделители, оставив\n * остальные правила локали (индийская группировка, знак минуса, порядок частей).\n */\nfunction formatByLocale(\n value: number,\n locale: string,\n options: Pick<GrStatisticFormatOptions, 'precision' | 'groupSeparator' | 'decimalSeparator'>,\n): string {\n const { precision, groupSeparator, decimalSeparator } = options\n // `Intl` принимает не больше 20 знаков и падает `RangeError` на большем,\n // тогда как ручная ветка через `toFixed` пережила бы до 100.\n const digits = precision === undefined ? undefined : Math.min(Math.max(precision, 0), 20)\n\n return new Intl.NumberFormat(locale, {\n useGrouping: true,\n minimumFractionDigits: digits,\n maximumFractionDigits: digits ?? 20,\n })\n .formatToParts(value)\n .map((part) => {\n if (part.type === 'group' && groupSeparator !== undefined) return groupSeparator\n if (part.type === 'decimal' && decimalSeparator !== undefined) return decimalSeparator\n return part.value\n })\n .join('')\n}\n","<script setup lang=\"ts\">\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { computed, markRaw, onBeforeUnmount, onMounted, ref, useSlots, watch, type Component } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { deltaTone, type GrDeltaPolarity } from '../GrDelta/deltaTone'\nimport { iconClass, iconTag } from '../shared/icon'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\n\nimport IconMinus from '~icons/lucide/minus'\nimport IconTrendingDown from '~icons/lucide/trending-down'\nimport IconTrendingUp from '~icons/lucide/trending-up'\n\nimport { countUpFrame } from './countUp'\nimport { formatStatisticValue } from './formatStatisticValue'\n\nimport {\n statisticAffixSizeBySize,\n statisticPlaceholderHeightBySize,\n statisticRootClass,\n statisticTitleClass,\n statisticTitleSizeBySize,\n statisticTrendClassByTrend,\n statisticTrendSizeBySize,\n statisticValueClass,\n type GrStatisticSize,\n type GrStatisticTone,\n type GrStatisticTrend,\n} from './grStatisticStyles'\n\nexport type {\n GrStatisticSize,\n GrStatisticTone,\n GrStatisticTrend,\n} from './grStatisticStyles'\nexport type { GrStatisticFormatOptions } from './formatStatisticValue'\n\n/**\n * Публичный GR-примитив «Statistic» — крупный числовой показатель с подписью,\n * префиксом/суффиксом и строкой динамики.\n *\n * Компонент презентационный: не грузит данные и не считает дельту, только\n * форматирует и подаёт. Форматирование — чистая функция `formatStatisticValue`.\n */\nexport interface GrStatisticProps {\n /** Значение показателя. Нечисловая строка выводится как есть. */\n value: number | string\n /** Подпись над значением. */\n title?: string\n /** Число знаков после запятой. */\n precision?: number\n /** Разделитель разрядов. Сильнее локали; без него и без локали — узкий пробел. */\n groupSeparator?: string\n /** Десятичный разделитель. Сильнее локали; без него и без локали — точка. */\n decimalSeparator?: string\n /**\n * BCP-47 локаль форматирования. Не задана — берётся из i18n-адаптера; нет и\n * адаптера — работают ручные разделители.\n */\n locale?: string\n /** Приписка перед значением (валюта, знак). */\n prefix?: string\n /** Приписка после значения (единица измерения, `%`). */\n suffix?: string\n /**\n * Иконка слева от блока: Vue-компонент либо класс иконки вашей UnoCSS-сборки\n * (`'i-lucide-users'` — тогда нужен ваш `presetIcons`, см. `docs/installation.md`).\n */\n icon?: string | Component\n size?: GrStatisticSize\n /** Тон значения; точечно перекрывается `--gr-statistic-value-color`. */\n tone?: GrStatisticTone\n /**\n * Что считать хорошим: тон выводится из знака самого значения. Для выручки\n * рост — успех, для себестоимости и оттока — наоборот. Явный `tone` сильнее:\n * выведенный тон — умолчание, а не диктат.\n */\n polarity?: GrDeltaPolarity\n /** Направление динамики — задаёт цвет и иконку строки под значением. */\n trend?: GrStatisticTrend\n /** Текст динамики (например `+12,5% к прошлой неделе`). */\n trendText?: string\n /** Состояние загрузки: вместо значения — плейсхолдер. */\n loading?: boolean\n /**\n * Перебирать числа при появлении и при смене значения. Нечисловое значение\n * ставится сразу, под `prefers-reduced-motion: reduce` — тоже.\n */\n animate?: boolean\n /**\n * Длительность перебора в миллисекундах. Не токеном: шкала `--gr-duration-*`\n * заканчивается на 300 мс и описывает смену состояния, а перебор чисел —\n * другой жанр, и его число живёт там же, где твин.\n */\n animateDuration?: number\n /** Свой корневой тег (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Показатель-ссылка: переход к деталям. */\n href?: string\n /** Показатель-кнопка: интерактивна вся плитка. */\n clickable?: boolean\n}\n\nexport interface GrStatisticEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrStatisticProps>(),\n {\n title: undefined,\n precision: undefined,\n // Дефолты живут в `formatStatisticValue`: иначе «пользователь задал пробел»\n // было бы неотличимо от «сработал дефолт», и локаль не смогла бы победить.\n groupSeparator: undefined,\n decimalSeparator: undefined,\n locale: undefined,\n prefix: undefined,\n suffix: undefined,\n icon: undefined,\n size: undefined,\n // Дефолт тона живёт в `resolvedTone`: с ним здесь «пользователь задал\n // neutral» было бы неотличимо от «сработал дефолт», и явный\n // `tone=\"neutral\"` не смог бы отменить `polarity`.\n tone: undefined,\n polarity: undefined,\n trend: undefined,\n trendText: undefined,\n loading: false,\n animate: false,\n animateDuration: 600,\n as: undefined,\n href: undefined,\n clickable: false,\n },\n)\n\nconst emit = defineEmits<GrStatisticEmits>()\n\nconst slots = useSlots()\n\nconst { t, locale } = useGranularityTranslations()\n\nconst resolvedLocale = computed(() => props.locale ?? locale.value)\n\n/**\n * Число текущего кадра перебора. `null` — перебора нет, показываем сам проп:\n * так значение остаётся конечным и на сервере, и до первого твина.\n */\nconst frameValue = ref<number | null>(null)\n\nconst formatted = computed(() => formatStatisticValue(frameValue.value ?? props.value, {\n precision: props.precision,\n groupSeparator: props.groupSeparator,\n decimalSeparator: props.decimalSeparator,\n locale: resolvedLocale.value,\n}))\n\n/** Конечное значение — то, что читает диктор, пока на экране идёт перебор. */\nconst finalFormatted = computed(() => formatStatisticValue(props.value, {\n precision: props.precision,\n groupSeparator: props.groupSeparator,\n decimalSeparator: props.decimalSeparator,\n locale: resolvedLocale.value,\n}))\n\nconst isCounting = computed(() => frameValue.value !== null)\n\nconst hasTitle = computed(() => Boolean(props.title || slots.title))\n\nconst isInteractive = computed(() => Boolean(props.as || props.href || props.clickable))\n\nconst rootTag = computed<string | Component>(() => {\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n if (props.href)\n return 'a'\n\n return props.clickable ? 'button' : 'div'\n})\n\n/**\n * Компонент-ссылка (`Link` от Inertia, `RouterLink`) рендерит `<a>` сам, и без\n * `href` он ведёт в никуда. Строковый тег, кроме `a`, атрибут не понимает —\n * там он и гасится.\n */\nconst rootHref = computed(() => (\n typeof rootTag.value === 'string' && rootTag.value !== 'a' ? undefined : props.href\n))\n\nconst rootClass = computed(() => statisticRootClass({ interactive: isInteractive.value }))\n\nfunction toNumber(value: number | string): number | null {\n const numeric = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nlet frameHandle: number | null = null\n\nfunction stopCounting(): void {\n if (frameHandle !== null) cancelAnimationFrame(frameHandle)\n frameHandle = null\n}\n\n/**\n * Перебор чисел ведёт JS, а глобальный кламп в `base.css` гасит только CSS —\n * значит уважать «уменьшить движение» обязан сам компонент (`docs/motion.md`).\n * Читается в момент старта, а не в теле `setup`: `matchMedia` на сервере нет, и\n * первый клиентский рендер разошёлся бы с серверным.\n */\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n}\n\nfunction runCountUp(from: number, to: number): void {\n stopCounting()\n\n const duration = props.animateDuration\n if (!(duration > 0) || from === to || prefersReducedMotion()) {\n frameValue.value = null\n return\n }\n\n const started = performance.now()\n frameValue.value = from\n\n const step = (now: number): void => {\n const elapsed = now - started\n const value = countUpFrame(from, to, elapsed, duration, props.precision)\n\n if (elapsed >= duration) {\n // Конец перебора снимает и подмену значения, и подмену для диктора.\n frameValue.value = null\n frameHandle = null\n return\n }\n\n frameValue.value = value\n frameHandle = requestAnimationFrame(step)\n }\n\n frameHandle = requestAnimationFrame(step)\n}\n\n/** Перебирать можно только числа: «2 ч 15 мин» и «—» ставятся сразу. */\nfunction startIfNumeric(from: number | string, to: number | string): void {\n if (!props.animate || props.loading) {\n stopCounting()\n frameValue.value = null\n return\n }\n\n const target = toNumber(to)\n const source = toNumber(from)\n\n if (target === null || source === null) {\n stopCounting()\n frameValue.value = null\n return\n }\n\n runCountUp(source, target)\n}\n\n// Появление плитки считается переходом от нуля: ради этого эффекта проп и нужен.\nonMounted(() => startIfNumeric(0, props.value))\n\nwatch(() => props.value, (next, prev) => startIfNumeric(prev, next))\n\nonBeforeUnmount(stopCounting)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrStatistic' })\nconst resolvedPolarity = useGrComponentProp('GrStatistic', 'polarity', () => props.polarity, 'none')\n\n/**\n * Тон значения: явный проп → выведенный из знака → нейтральный.\n *\n * Отдельной ветки «полярность не задана» нет намеренно: `deltaTone(x, 'none')`\n * нейтрален при любом значении, то есть `'none'` и есть прежнее поведение.\n *\n * Знак берётся у самого пропа, а не у кадра перебора: `frameValue` стартует с\n * нуля, и цвет плитки мигал бы нейтральным на каждой анимации.\n */\nconst resolvedTone = computed<GrStatisticTone>(() => {\n if (props.tone !== undefined) return props.tone\n\n const numeric = toNumber(props.value)\n\n // «2 ч 15 мин» и «—» знака не имеют — красить нечем.\n return numeric === null ? 'neutral' : deltaTone(numeric, resolvedPolarity.value)\n})\n\nconst trendIconByTrend: Record<GrStatisticTrend, Component> = {\n up: IconTrendingUp,\n down: IconTrendingDown,\n flat: IconMinus,\n}\n\n// Иконка направления `aria-hidden`, а «+12,5 %» само по себе рост от падения не\n// отличает: направление доносит скрытая подпись.\nconst trendLabel = computed(() => {\n if (!props.trend) return undefined\n\n const labels = {\n up: () => t('gr.statistic.trendUp', 'Increase'),\n down: () => t('gr.statistic.trendDown', 'Decrease'),\n flat: () => t('gr.statistic.trendFlat', 'No change'),\n }\n\n return labels[props.trend]()\n})\n</script>\n\n<template>\n <component\n :is=\"rootTag\"\n data-gr-statistic\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :class=\"rootClass\"\n @click=\"emit('click', $event)\"\n >\n <span\n v-if=\"icon || $slots.icon\"\n data-gr-statistic-icon\n class=\"mt-0.5 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--gr-radius-lg)] bg-[var(--gr-muted)] text-[var(--gr-muted-fg)]\"\n >\n <slot name=\"icon\">\n <component :is=\"iconTag(icon)\" class=\"block h-4 w-4\" :class=\"iconClass(icon)\" aria-hidden=\"true\" />\n </slot>\n </span>\n\n <div class=\"min-w-0\">\n <!--\n Подпись и значение — пара «термин — значение», а не два соседних блока.\n `<dl>` появляется только вместе с подписью: список определений без `<dt>`\n — та же бессвязность, только с претензией на семантику. Поля обнуляются\n руками: preflight пакета сбрасывает их лишь у `body`.\n -->\n <component :is=\"hasTitle ? 'dl' : 'div'\" class=\"m-0\">\n <dt\n v-if=\"hasTitle\"\n data-gr-statistic-title\n :class=\"[statisticTitleClass, statisticTitleSizeBySize[resolvedSize]]\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </dt>\n\n <!-- Плейсхолдер загрузки повторяет высоту строки значения, чтобы блок не прыгал. -->\n <component\n :is=\"hasTitle ? 'dd' : 'div'\"\n v-if=\"loading\"\n data-gr-statistic-placeholder\n data-testid=\"gr-statistic-placeholder\"\n class=\"m-0 mt-1\"\n role=\"status\"\n aria-busy=\"true\"\n >\n <GrSkeleton\n width=\"6rem\"\n :height=\"statisticPlaceholderHeightBySize[resolvedSize]\"\n rounded=\"var(--gr-radius-md)\"\n />\n <span class=\"sr-only\">{{ t('gr.loading.defaultText', 'Loading...') }}</span>\n </component>\n\n <component\n :is=\"hasTitle ? 'dd' : 'div'\"\n v-else\n data-gr-statistic-value\n data-testid=\"gr-statistic-value\"\n class=\"m-0 mt-0.5 flex items-baseline gap-1\"\n >\n <span\n v-if=\"prefix || $slots.prefix\"\n data-gr-statistic-prefix\n class=\"text-[var(--gr-muted-fg)]\"\n :class=\"statisticAffixSizeBySize[resolvedSize]\"\n >\n <slot name=\"prefix\">{{ prefix }}</slot>\n </span>\n\n <span\n data-gr-statistic-number\n :class=\"statisticValueClass({ size: resolvedSize, tone: resolvedTone })\"\n :aria-hidden=\"isCounting ? 'true' : undefined\"\n >\n <slot>{{ formatted }}</slot>\n </span>\n\n <!--\n Пока идёт перебор, на экране промежуточное число. Диктору отдаётся\n конечное: «1 284» глазами и «743» в ушах — не шум, а неверные данные.\n -->\n <span v-if=\"isCounting\" data-gr-statistic-final class=\"sr-only\">{{ finalFormatted }}</span>\n\n <span\n v-if=\"suffix || $slots.suffix\"\n data-gr-statistic-suffix\n class=\"text-[var(--gr-muted-fg)]\"\n :class=\"statisticAffixSizeBySize[resolvedSize]\"\n >\n <slot name=\"suffix\">{{ suffix }}</slot>\n </span>\n </component>\n </component>\n\n <div\n v-if=\"trend || trendText || $slots.trend\"\n data-gr-statistic-trend\n data-testid=\"gr-statistic-trend\"\n class=\"mt-1 inline-flex items-center gap-1\"\n :class=\"[statisticTrendSizeBySize[resolvedSize], statisticTrendClassByTrend[trend ?? 'flat']]\"\n >\n <span v-if=\"trendLabel\" data-gr-statistic-trend-label class=\"sr-only\">{{ trendLabel }}</span>\n\n <slot name=\"trend\">\n <component\n :is=\"trendIconByTrend[trend]\"\n v-if=\"trend\"\n class=\"block h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n <span>{{ trendText }}</span>\n </slot>\n </div>\n </div>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { computed, markRaw, onBeforeUnmount, onMounted, ref, useSlots, watch, type Component } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { deltaTone, type GrDeltaPolarity } from '../GrDelta/deltaTone'\nimport { iconClass, iconTag } from '../shared/icon'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\n\nimport IconMinus from '~icons/lucide/minus'\nimport IconTrendingDown from '~icons/lucide/trending-down'\nimport IconTrendingUp from '~icons/lucide/trending-up'\n\nimport { countUpFrame } from './countUp'\nimport { formatStatisticValue } from './formatStatisticValue'\n\nimport {\n statisticAffixSizeBySize,\n statisticPlaceholderHeightBySize,\n statisticRootClass,\n statisticTitleClass,\n statisticTitleSizeBySize,\n statisticTrendClassByTrend,\n statisticTrendSizeBySize,\n statisticValueClass,\n type GrStatisticSize,\n type GrStatisticTone,\n type GrStatisticTrend,\n} from './grStatisticStyles'\n\nexport type {\n GrStatisticSize,\n GrStatisticTone,\n GrStatisticTrend,\n} from './grStatisticStyles'\nexport type { GrStatisticFormatOptions } from './formatStatisticValue'\n\n/**\n * Публичный GR-примитив «Statistic» — крупный числовой показатель с подписью,\n * префиксом/суффиксом и строкой динамики.\n *\n * Компонент презентационный: не грузит данные и не считает дельту, только\n * форматирует и подаёт. Форматирование — чистая функция `formatStatisticValue`.\n */\nexport interface GrStatisticProps {\n /** Значение показателя. Нечисловая строка выводится как есть. */\n value: number | string\n /** Подпись над значением. */\n title?: string\n /** Число знаков после запятой. */\n precision?: number\n /** Разделитель разрядов. Сильнее локали; без него и без локали — узкий пробел. */\n groupSeparator?: string\n /** Десятичный разделитель. Сильнее локали; без него и без локали — точка. */\n decimalSeparator?: string\n /**\n * BCP-47 локаль форматирования. Не задана — берётся из i18n-адаптера; нет и\n * адаптера — работают ручные разделители.\n */\n locale?: string\n /** Приписка перед значением (валюта, знак). */\n prefix?: string\n /** Приписка после значения (единица измерения, `%`). */\n suffix?: string\n /**\n * Иконка слева от блока: Vue-компонент либо класс иконки вашей UnoCSS-сборки\n * (`'i-lucide-users'` — тогда нужен ваш `presetIcons`, см. `docs/installation.md`).\n */\n icon?: string | Component\n size?: GrStatisticSize\n /** Тон значения; точечно перекрывается `--gr-statistic-value-color`. */\n tone?: GrStatisticTone\n /**\n * Что считать хорошим: тон выводится из знака самого значения. Для выручки\n * рост — успех, для себестоимости и оттока — наоборот. Явный `tone` сильнее:\n * выведенный тон — умолчание, а не диктат.\n */\n polarity?: GrDeltaPolarity\n /** Направление динамики — задаёт цвет и иконку строки под значением. */\n trend?: GrStatisticTrend\n /** Текст динамики (например `+12,5% к прошлой неделе`). */\n trendText?: string\n /** Состояние загрузки: вместо значения — плейсхолдер. */\n loading?: boolean\n /**\n * Перебирать числа при появлении и при смене значения. Нечисловое значение\n * ставится сразу, под `prefers-reduced-motion: reduce` — тоже.\n */\n animate?: boolean\n /**\n * Длительность перебора в миллисекундах. Не токеном: шкала `--gr-duration-*`\n * заканчивается на 300 мс и описывает смену состояния, а перебор чисел —\n * другой жанр, и его число живёт там же, где твин.\n */\n animateDuration?: number\n /** Свой корневой тег (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Показатель-ссылка: переход к деталям. */\n href?: string\n /** Показатель-кнопка: интерактивна вся плитка. */\n clickable?: boolean\n}\n\nexport interface GrStatisticEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrStatisticProps>(),\n {\n title: undefined,\n precision: undefined,\n // Дефолты живут в `formatStatisticValue`: иначе «пользователь задал пробел»\n // было бы неотличимо от «сработал дефолт», и локаль не смогла бы победить.\n groupSeparator: undefined,\n decimalSeparator: undefined,\n locale: undefined,\n prefix: undefined,\n suffix: undefined,\n icon: undefined,\n size: undefined,\n // Дефолт тона живёт в `resolvedTone`: с ним здесь «пользователь задал\n // neutral» было бы неотличимо от «сработал дефолт», и явный\n // `tone=\"neutral\"` не смог бы отменить `polarity`.\n tone: undefined,\n polarity: undefined,\n trend: undefined,\n trendText: undefined,\n loading: false,\n animate: false,\n animateDuration: 600,\n as: undefined,\n href: undefined,\n clickable: false,\n },\n)\n\nconst emit = defineEmits<GrStatisticEmits>()\n\nconst slots = useSlots()\n\nconst { t, locale } = useGranularityTranslations()\n\nconst resolvedLocale = computed(() => props.locale ?? locale.value)\n\n/**\n * Число текущего кадра перебора. `null` — перебора нет, показываем сам проп:\n * так значение остаётся конечным и на сервере, и до первого твина.\n */\nconst frameValue = ref<number | null>(null)\n\nconst formatted = computed(() => formatStatisticValue(frameValue.value ?? props.value, {\n precision: props.precision,\n groupSeparator: props.groupSeparator,\n decimalSeparator: props.decimalSeparator,\n locale: resolvedLocale.value,\n}))\n\n/** Конечное значение — то, что читает диктор, пока на экране идёт перебор. */\nconst finalFormatted = computed(() => formatStatisticValue(props.value, {\n precision: props.precision,\n groupSeparator: props.groupSeparator,\n decimalSeparator: props.decimalSeparator,\n locale: resolvedLocale.value,\n}))\n\nconst isCounting = computed(() => frameValue.value !== null)\n\nconst hasTitle = computed(() => Boolean(props.title || slots.title))\n\nconst isInteractive = computed(() => Boolean(props.as || props.href || props.clickable))\n\nconst rootTag = computed<string | Component>(() => {\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n if (props.href)\n return 'a'\n\n return props.clickable ? 'button' : 'div'\n})\n\n/**\n * Компонент-ссылка (`Link` от Inertia, `RouterLink`) рендерит `<a>` сам, и без\n * `href` он ведёт в никуда. Строковый тег, кроме `a`, атрибут не понимает —\n * там он и гасится.\n */\nconst rootHref = computed(() => (\n typeof rootTag.value === 'string' && rootTag.value !== 'a' ? undefined : props.href\n))\n\nconst rootClass = computed(() => statisticRootClass({ interactive: isInteractive.value }))\n\nfunction toNumber(value: number | string): number | null {\n const numeric = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nlet frameHandle: number | null = null\n\nfunction stopCounting(): void {\n if (frameHandle !== null) cancelAnimationFrame(frameHandle)\n frameHandle = null\n}\n\n/**\n * Перебор чисел ведёт JS, а глобальный кламп в `base.css` гасит только CSS —\n * значит уважать «уменьшить движение» обязан сам компонент (`docs/motion.md`).\n * Читается в момент старта, а не в теле `setup`: `matchMedia` на сервере нет, и\n * первый клиентский рендер разошёлся бы с серверным.\n */\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n}\n\nfunction runCountUp(from: number, to: number): void {\n stopCounting()\n\n const duration = props.animateDuration\n if (!(duration > 0) || from === to || prefersReducedMotion()) {\n frameValue.value = null\n return\n }\n\n const started = performance.now()\n frameValue.value = from\n\n const step = (now: number): void => {\n const elapsed = now - started\n const value = countUpFrame(from, to, elapsed, duration, props.precision)\n\n if (elapsed >= duration) {\n // Конец перебора снимает и подмену значения, и подмену для диктора.\n frameValue.value = null\n frameHandle = null\n return\n }\n\n frameValue.value = value\n frameHandle = requestAnimationFrame(step)\n }\n\n frameHandle = requestAnimationFrame(step)\n}\n\n/** Перебирать можно только числа: «2 ч 15 мин» и «—» ставятся сразу. */\nfunction startIfNumeric(from: number | string, to: number | string): void {\n if (!props.animate || props.loading) {\n stopCounting()\n frameValue.value = null\n return\n }\n\n const target = toNumber(to)\n const source = toNumber(from)\n\n if (target === null || source === null) {\n stopCounting()\n frameValue.value = null\n return\n }\n\n runCountUp(source, target)\n}\n\n// Появление плитки считается переходом от нуля: ради этого эффекта проп и нужен.\nonMounted(() => startIfNumeric(0, props.value))\n\nwatch(() => props.value, (next, prev) => startIfNumeric(prev, next))\n\nonBeforeUnmount(stopCounting)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrStatistic' })\nconst resolvedPolarity = useGrComponentProp('GrStatistic', 'polarity', () => props.polarity, 'none')\n\n/**\n * Тон значения: явный проп → выведенный из знака → нейтральный.\n *\n * Отдельной ветки «полярность не задана» нет намеренно: `deltaTone(x, 'none')`\n * нейтрален при любом значении, то есть `'none'` и есть прежнее поведение.\n *\n * Знак берётся у самого пропа, а не у кадра перебора: `frameValue` стартует с\n * нуля, и цвет плитки мигал бы нейтральным на каждой анимации.\n */\nconst resolvedTone = computed<GrStatisticTone>(() => {\n if (props.tone !== undefined) return props.tone\n\n const numeric = toNumber(props.value)\n\n // «2 ч 15 мин» и «—» знака не имеют — красить нечем.\n return numeric === null ? 'neutral' : deltaTone(numeric, resolvedPolarity.value)\n})\n\nconst trendIconByTrend: Record<GrStatisticTrend, Component> = {\n up: IconTrendingUp,\n down: IconTrendingDown,\n flat: IconMinus,\n}\n\n// Иконка направления `aria-hidden`, а «+12,5 %» само по себе рост от падения не\n// отличает: направление доносит скрытая подпись.\nconst trendLabel = computed(() => {\n if (!props.trend) return undefined\n\n const labels = {\n up: () => t('gr.statistic.trendUp', 'Increase'),\n down: () => t('gr.statistic.trendDown', 'Decrease'),\n flat: () => t('gr.statistic.trendFlat', 'No change'),\n }\n\n return labels[props.trend]()\n})\n</script>\n\n<template>\n <component\n :is=\"rootTag\"\n data-gr-statistic\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :class=\"rootClass\"\n @click=\"emit('click', $event)\"\n >\n <span\n v-if=\"icon || $slots.icon\"\n data-gr-statistic-icon\n class=\"mt-0.5 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--gr-radius-lg)] bg-[var(--gr-muted)] text-[var(--gr-muted-fg)]\"\n >\n <slot name=\"icon\">\n <component :is=\"iconTag(icon)\" class=\"block h-4 w-4\" :class=\"iconClass(icon)\" aria-hidden=\"true\" />\n </slot>\n </span>\n\n <div class=\"min-w-0\">\n <!--\n Подпись и значение — пара «термин — значение», а не два соседних блока.\n `<dl>` появляется только вместе с подписью: список определений без `<dt>`\n — та же бессвязность, только с претензией на семантику. Поля обнуляются\n руками: preflight пакета сбрасывает их лишь у `body`.\n -->\n <component :is=\"hasTitle ? 'dl' : 'div'\" class=\"m-0\">\n <dt\n v-if=\"hasTitle\"\n data-gr-statistic-title\n :class=\"[statisticTitleClass, statisticTitleSizeBySize[resolvedSize]]\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </dt>\n\n <!-- Плейсхолдер загрузки повторяет высоту строки значения, чтобы блок не прыгал. -->\n <component\n :is=\"hasTitle ? 'dd' : 'div'\"\n v-if=\"loading\"\n data-gr-statistic-placeholder\n data-testid=\"gr-statistic-placeholder\"\n class=\"m-0 mt-1\"\n role=\"status\"\n aria-busy=\"true\"\n >\n <GrSkeleton\n width=\"6rem\"\n :height=\"statisticPlaceholderHeightBySize[resolvedSize]\"\n rounded=\"var(--gr-radius-md)\"\n />\n <span class=\"sr-only\">{{ t('gr.loading.defaultText', 'Loading...') }}</span>\n </component>\n\n <component\n :is=\"hasTitle ? 'dd' : 'div'\"\n v-else\n data-gr-statistic-value\n data-testid=\"gr-statistic-value\"\n class=\"m-0 mt-0.5 flex items-baseline gap-1\"\n >\n <span\n v-if=\"prefix || $slots.prefix\"\n data-gr-statistic-prefix\n class=\"text-[var(--gr-muted-fg)]\"\n :class=\"statisticAffixSizeBySize[resolvedSize]\"\n >\n <slot name=\"prefix\">{{ prefix }}</slot>\n </span>\n\n <span\n data-gr-statistic-number\n :class=\"statisticValueClass({ size: resolvedSize, tone: resolvedTone })\"\n :aria-hidden=\"isCounting ? 'true' : undefined\"\n >\n <slot>{{ formatted }}</slot>\n </span>\n\n <!--\n Пока идёт перебор, на экране промежуточное число. Диктору отдаётся\n конечное: «1 284» глазами и «743» в ушах — не шум, а неверные данные.\n -->\n <span v-if=\"isCounting\" data-gr-statistic-final class=\"sr-only\">{{ finalFormatted }}</span>\n\n <span\n v-if=\"suffix || $slots.suffix\"\n data-gr-statistic-suffix\n class=\"text-[var(--gr-muted-fg)]\"\n :class=\"statisticAffixSizeBySize[resolvedSize]\"\n >\n <slot name=\"suffix\">{{ suffix }}</slot>\n </span>\n </component>\n </component>\n\n <div\n v-if=\"trend || trendText || $slots.trend\"\n data-gr-statistic-trend\n data-testid=\"gr-statistic-trend\"\n class=\"mt-1 inline-flex items-center gap-1\"\n :class=\"[statisticTrendSizeBySize[resolvedSize], statisticTrendClassByTrend[trend ?? 'flat']]\"\n >\n <span v-if=\"trendLabel\" data-gr-statistic-trend-label class=\"sr-only\">{{ trendLabel }}</span>\n\n <slot name=\"trend\">\n <component\n :is=\"trendIconByTrend[trend]\"\n v-if=\"trend\"\n class=\"block h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n <span>{{ trendText }}</span>\n </slot>\n </div>\n </div>\n </component>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { n as formatStatisticValue, t as GrStatistic_default } from "./chunks/GrStatistic-DkzWUAw1.js";
1
+ import { n as formatStatisticValue, t as GrStatistic_default } from "./chunks/GrStatistic-C7wQECeS.js";
2
2
  import { n as grStatisticSafelist, t as grStatisticConfig } from "../../chunks/config-Be9k9bOf.js";
3
3
  export { GrStatistic_default as GrStatistic, GrStatistic_default as default, formatStatisticValue, grStatisticConfig, grStatisticSafelist };
@@ -1,2 +1,2 @@
1
- import { a as granularityDefaultThemes, i as granularityComponentConfigs, n as GRANULARITY_PROVIDER_ID, o as granularityThemeNames, r as createGranularityProvider, t as granularityProvider } from "./chunks/granular-provider-CYroWMjO.js";
1
+ import { a as granularityDefaultThemes, i as granularityComponentConfigs, n as GRANULARITY_PROVIDER_ID, o as granularityThemeNames, r as createGranularityProvider, t as granularityProvider } from "./chunks/granular-provider-CQKhl5C_.js";
2
2
  export { GRANULARITY_PROVIDER_ID, createGranularityProvider, granularityProvider as default, granularityProvider, granularityComponentConfigs, granularityDefaultThemes, granularityThemeNames };
@@ -1,2 +1,2 @@
1
- import { a as granularityDefaultThemes, i as granularityComponentConfigs, n as GRANULARITY_PROVIDER_ID, o as granularityThemeNames, r as createGranularityProvider, t as granularityProvider } from "./chunks/granular-provider-CYroWMjO.js";
1
+ import { a as granularityDefaultThemes, i as granularityComponentConfigs, n as GRANULARITY_PROVIDER_ID, o as granularityThemeNames, r as createGranularityProvider, t as granularityProvider } from "./chunks/granular-provider-CQKhl5C_.js";
2
2
  export { GRANULARITY_PROVIDER_ID, createGranularityProvider, granularityProvider as default, granularityProvider, granularityComponentConfigs, granularityDefaultThemes, granularityThemeNames };
package/dist/index.js CHANGED
@@ -78,10 +78,11 @@ import { t as useDragSort } from "./chunks/useDragSort-0l47h4yb.js";
78
78
  import { useRovingFocus } from "./composables/useRovingFocus.js";
79
79
  import { t as GrDataTable_default } from "./components/GrDataTable/chunks/GrDataTable-B5MmZFUg.js";
80
80
  import { n as grDataTableSafelist, t as grDataTableConfig } from "./chunks/config-BC8d_smz.js";
81
- import { t as GrDelta_default } from "./components/GrDelta/chunks/GrDelta-BMmpYz8K.js";
81
+ import { t as GrDelta_default } from "./components/GrDelta/chunks/GrDelta-DFvxNpmN.js";
82
+ import { n as deltaTone, t as deltaDirection } from "./chunks/deltaTone-B5OoS1vo.js";
82
83
  import { n as grDeltaSafelist, t as grDeltaConfig } from "./chunks/config-DBQQT-WP.js";
83
- import { n as grDescriptionListSafelist, t as grDescriptionListConfig } from "./chunks/config-jyZq8XKL.js";
84
- import { t as GrDescriptionList_default } from "./components/GrDescriptionList/chunks/GrDescriptionList-pXh1LZEe.js";
84
+ import { n as grDescriptionListSafelist, t as grDescriptionListConfig } from "./chunks/config-DCwQBMmJ.js";
85
+ import { t as GrDescriptionList_default } from "./components/GrDescriptionList/chunks/GrDescriptionList-2zFXzX83.js";
85
86
  import { t as grDialogSafelist } from "./chunks/safelist-CkF4v7DC.js";
86
87
  import "./components/GrDialog/index.js";
87
88
  import { n as useGrFormContext } from "./chunks/context-Bfrgsluh.js";
@@ -102,7 +103,7 @@ import { n as grDropdownMenuSafelist, t as grDropdownMenuConfig } from "./chunks
102
103
  import { a as GrDropdownMenuItem_default, c as GrDropdownMenuDivider_default, i as GrDropdownMenuList_default, n as GrDropdownMenuColumn_default, o as GrDropdownMenuGroup_default, r as GrDropdownMenu_default, s as GrDropdownMenuHeader_default, t as GrDropdownMenuColumns_default } from "./components/GrDropdownMenu/chunks/GrDropdownMenu-DyS6dWDp.js";
103
104
  import { t as GrEmptyState_default } from "./components/GrEmptyState/chunks/GrEmptyState-CS9do8eM.js";
104
105
  import { n as grEmptyStateSafelist, t as grEmptyStateConfig } from "./chunks/config-vSji8w90.js";
105
- import { t as GrFilePreview_default } from "./components/GrFilePreview/chunks/GrFilePreview-rbE3zL9w.js";
106
+ import { n as fileKindOf, r as isPreviewableKind, t as GrFilePreview_default } from "./components/GrFilePreview/chunks/GrFilePreview-D0Bt-0nn.js";
106
107
  import { n as grFilePreviewSafelist, t as grFilePreviewConfig } from "./chunks/config-QJ54_fcM.js";
107
108
  import { t as GrProgressBar_default } from "./components/GrProgressBar/chunks/GrProgressBar-hszUOC8f.js";
108
109
  import { n as GrUploadAbortError, r as GrUploadHttpError, t as GrFileUpload_default } from "./components/GrFileUpload/chunks/GrFileUpload-D3-vC-ap.js";
@@ -171,7 +172,7 @@ import { n as grSortableListSafelist, t as grSortableListConfig } from "./chunks
171
172
  import { t as GrSortableList_default } from "./components/GrSortableList/chunks/GrSortableList-DPjxilmN.js";
172
173
  import { n as grSplitterSafelist, t as grSplitterConfig } from "./chunks/config-BE6n20np.js";
173
174
  import { n as sizeFromPointer, t as GrSplitter_default } from "./components/GrSplitter/chunks/GrSplitter-B9_JrT7q.js";
174
- import { n as formatStatisticValue, t as GrStatistic_default } from "./components/GrStatistic/chunks/GrStatistic-DkzWUAw1.js";
175
+ import { n as formatStatisticValue, t as GrStatistic_default } from "./components/GrStatistic/chunks/GrStatistic-C7wQECeS.js";
175
176
  import { n as grStatisticSafelist, t as grStatisticConfig } from "./chunks/config-Be9k9bOf.js";
176
177
  import { n as grSwitchSafelist, t as grSwitchConfig } from "./chunks/config-BoNR7_PD2.js";
177
178
  import { t as GrSwitch_default } from "./components/GrSwitch/chunks/GrSwitch-x_vQQ6ao.js";
@@ -196,4 +197,4 @@ import { t as GrTreeSelect_default } from "./components/GrTreeSelect/chunks/GrTr
196
197
  import "./composables/useGrComponentConfig.js";
197
198
  import "./composables/useGranularityTranslations.js";
198
199
  import { GRANULARITY_THEME_STATE, granularityThemePlugin, initThemeEarly, useTheme } from "./composables/useTheme.js";
199
- export { FileValidationError, GRANULARITY_THEME_STATE, GRANULARITY_TOAST_STATE, GR_KBD_TOKENS, GR_TONES, GrAlert_default as GrAlert, GrAutocomplete_default as GrAutocomplete, GrAvatar_default as GrAvatar, GrAvatarGroup_default as GrAvatarGroup, GrBadge_default as GrBadge, GrBadgeWrap_default as GrBadgeWrap, GrBottomNav_default as GrBottomNav, GrBreadcrumbs_default as GrBreadcrumbs, GrButton_default as GrButton, GrButtonGroup_default as GrButtonGroup, GrCard_default as GrCard, GrCheckbox_default as GrCheckbox, GrCheckboxGroup_default as GrCheckboxGroup, GrCodeBlock_default as GrCodeBlock, GrCollapse_default as GrCollapse, GrCollapseItem_default as GrCollapseItem, GrColorPicker_default as GrColorPicker, GrCommandPalette_default as GrCommandPalette, GrConfigProvider_default as GrConfigProvider, GrConfirmDialog_default as GrConfirmDialog, GrDataTable_default as GrDataTable, GrDelta_default as GrDelta, GrDescriptionList_default as GrDescriptionList, GrDialog_default as GrDialog, GrDialogCloseButton_default as GrDialogCloseButton, GrDialogFooter_default as GrDialogFooter, GrDialogHeader_default as GrDialogHeader, GrDialogServiceHost_default as GrDialogServiceHost, GrDivider_default as GrDivider, GrDrawer_default as GrDrawer, GrDropdown_default as GrDropdown, GrDropdownMenu_default as GrDropdownMenu, GrDropdownMenuColumn_default as GrDropdownMenuColumn, GrDropdownMenuColumns_default as GrDropdownMenuColumns, GrDropdownMenuDivider_default as GrDropdownMenuDivider, GrDropdownMenuGroup_default as GrDropdownMenuGroup, GrDropdownMenuHeader_default as GrDropdownMenuHeader, GrDropdownMenuItem_default as GrDropdownMenuItem, GrDropdownMenuList_default as GrDropdownMenuList, GrEmptyState_default as GrEmptyState, GrFilePreview_default as GrFilePreview, GrFileUpload_default as GrFileUpload, GrForm_default as GrForm, GrFormErrorBanner_default as GrFormErrorBanner, GrFormField_default as GrFormField, GrFormFile_default as GrFormFile, GrFormSection_default as GrFormSection, GrIcon_default as GrIcon, GrImageViewer_default as GrImageViewer, GrInput_default as GrInput, GrInputTag_default as GrInputTag, GrJsonViewer_default as GrJsonViewer, GrKbd_default as GrKbd, GrLink_default as GrLink, GrList_default as GrList, GrListItem_default as GrListItem, GrLoading_default as GrLoading, GrModal_default as GrModal, GrNavbar_default as GrNavbar, GrNumberInput_default as GrNumberInput, GrPagination_default as GrPagination, GrPopover_default as GrPopover, GrProgressBar_default as GrProgressBar, GrProgressCircle_default as GrProgressCircle, GrPromptDialog_default as GrPromptDialog, GrRadio_default as GrRadio, GrRadioGroup_default as GrRadioGroup, GrRating_default as GrRating, GrResponseErrorBanner_default as GrResponseErrorBanner, GrSegmented_default as GrSegmented, GrSelect_default as GrSelect, GrSidebar_default as GrSidebar, GrSidebarGroup_default as GrSidebarGroup, GrSidebarItem_default as GrSidebarItem, GrSkeleton_default as GrSkeleton, GrSlider_default as GrSlider, GrSortableList_default as GrSortableList, GrSplitter_default as GrSplitter, GrStatistic_default as GrStatistic, GrSwitch_default as GrSwitch, GrTabPanel_default as GrTabPanel, GrTabPanels_default as GrTabPanels, GrTable_default as GrTable, GrTabs_default as GrTabs, GrTextarea_default as GrTextarea, GrTimeline_default as GrTimeline, GrTimelineItem_default as GrTimelineItem, GrToaster_default as GrToaster, GrTooltip_default as GrTooltip, GrTree_default as GrTree, GrTreeSelect_default as GrTreeSelect, GrUploadAbortError, GrUploadErrorBanner_default as GrUploadErrorBanner, GrUploadHttpError, coreResponseErrorParsers, createGrFormMessageResolver, createLoading, createResponseErrorClassifier, dialogService, extendDefaultParsers, findKbdToken, formatBadgeValue, formatStatisticValue, grAlertConfig, grAutocompleteConfig, grAutocompleteSafelist, grAvatarConfig, grAvatarSafelist, grBadgeConfig, grBadgeSafelist, grBadgeWrapConfig, grBadgeWrapSafelist, grBottomNavConfig, grBottomNavSafelist, grBreadcrumbsConfig, grBreadcrumbsSafelist, grButtonConfig, grButtonGroupConfig, grButtonSafelist, grCardConfig, grCardSafelist, grCheckboxConfig, grCheckboxGroupConfig, grCheckboxSafelist, grCodeBlockConfig, grCodeBlockSafelist, grCollapseConfig, grCollapseSafelist, grColorPickerConfig, grColorPickerSafelist, grCommandPaletteConfig, grCommandPaletteSafelist, grConfigProviderConfig, grConfirmDialogConfig, grDataTableConfig, grDataTableSafelist, grDeltaConfig, grDeltaSafelist, grDescriptionListConfig, grDescriptionListSafelist, grDialogSafelist, grDialogServiceConfig, grDividerConfig, grDividerSafelist, grDrawerConfig, grDrawerSafelist, grDropdownConfig, grDropdownMenuConfig, grDropdownMenuSafelist, grDropdownSafelist, grEmptyStateConfig, grEmptyStateSafelist, grFilePreviewConfig, grFilePreviewSafelist, grFileUploadConfig, grFileUploadSafelist, grFormConfig, grFormFieldConfig, grFormFieldSafelist, grFormFileConfig, grFormFileSafelist, grFormSectionConfig, grFormSectionSafelist, grIconConfig, grIconSafelist, grImageViewerConfig, grImageViewerSafelist, grInputConfig, grInputSafelist, grInputTagConfig, grInputTagSafelist, grJsonViewerConfig, grJsonViewerSafelist, grKbdConfig, grKbdSafelist, grLinkConfig, grLinkSafelist, grListConfig, grListSafelist, grLoadingConfig, grLoadingSafelist, grModalConfig, grModalSafelist, grNavbarConfig, grNavbarSafelist, grNumberInputConfig, grNumberInputSafelist, grPaginationConfig, grPaginationSafelist, grPopoverConfig, grPopoverSafelist, grProgressBarConfig, grProgressBarSafelist, grProgressCircleConfig, grProgressCircleSafelist, grPromptDialogConfig, grRadioConfig, grRadioGroupConfig, grRadioSafelist, grRatingConfig, grRatingSafelist, grSegmentedConfig, grSegmentedSafelist, grSelectConfig, grSelectSafelist, grSidebarConfig, grSidebarSafelist, grSkeletonConfig, grSliderConfig, grSliderSafelist, grSortableListConfig, grSortableListSafelist, grSplitterConfig, grSplitterSafelist, grStatisticConfig, grStatisticSafelist, grSwitchConfig, grSwitchSafelist, grTabPanelsConfig, grTableConfig, grTableSafelist, grTabsConfig, grTabsSafelist, grTextareaConfig, grTextareaSafelist, grTimelineConfig, grTimelineSafelist, grToasterConfig, grToasterSafelist, grTooltipConfig, grTooltipSafelist, grTreeConfig, grTreeSafelist, grTreeSelectConfig, grTreeSelectSafelist, granularityDialogServicePlugin, granularityThemePlugin, granularityToastPlugin, initThemeEarly, initialsFrom, normalizeError, resolveBreadcrumbsFit, resolveBreadcrumbsLayout, resolveGrConfig, runFieldRules, sizeFromPointer, teardownDialogService, useAnnouncer, useComboboxNavigation, useDialogService, useDismissible, useDragGesture, useDragSort, useFloating, useFocusTrap, useGrButtonGroup, useGrComponentDefaults, useGrComponentProp, useGrComponentSize, useGrConfig, useGrFormContext, useGrFormControl, useGrFormFieldContext, useGrThemeAttrs, useGranularityTranslations, useOverlayLayer, usePortalTarget, useResponseError, useRovingFocus, useTheme, useToast, useVirtualList, vAutofocus, vAutosize, vClickOutside, vDropzone, vHotkey, vLoading };
200
+ export { FileValidationError, GRANULARITY_THEME_STATE, GRANULARITY_TOAST_STATE, GR_KBD_TOKENS, GR_TONES, GrAlert_default as GrAlert, GrAutocomplete_default as GrAutocomplete, GrAvatar_default as GrAvatar, GrAvatarGroup_default as GrAvatarGroup, GrBadge_default as GrBadge, GrBadgeWrap_default as GrBadgeWrap, GrBottomNav_default as GrBottomNav, GrBreadcrumbs_default as GrBreadcrumbs, GrButton_default as GrButton, GrButtonGroup_default as GrButtonGroup, GrCard_default as GrCard, GrCheckbox_default as GrCheckbox, GrCheckboxGroup_default as GrCheckboxGroup, GrCodeBlock_default as GrCodeBlock, GrCollapse_default as GrCollapse, GrCollapseItem_default as GrCollapseItem, GrColorPicker_default as GrColorPicker, GrCommandPalette_default as GrCommandPalette, GrConfigProvider_default as GrConfigProvider, GrConfirmDialog_default as GrConfirmDialog, GrDataTable_default as GrDataTable, GrDelta_default as GrDelta, GrDescriptionList_default as GrDescriptionList, GrDialog_default as GrDialog, GrDialogCloseButton_default as GrDialogCloseButton, GrDialogFooter_default as GrDialogFooter, GrDialogHeader_default as GrDialogHeader, GrDialogServiceHost_default as GrDialogServiceHost, GrDivider_default as GrDivider, GrDrawer_default as GrDrawer, GrDropdown_default as GrDropdown, GrDropdownMenu_default as GrDropdownMenu, GrDropdownMenuColumn_default as GrDropdownMenuColumn, GrDropdownMenuColumns_default as GrDropdownMenuColumns, GrDropdownMenuDivider_default as GrDropdownMenuDivider, GrDropdownMenuGroup_default as GrDropdownMenuGroup, GrDropdownMenuHeader_default as GrDropdownMenuHeader, GrDropdownMenuItem_default as GrDropdownMenuItem, GrDropdownMenuList_default as GrDropdownMenuList, GrEmptyState_default as GrEmptyState, GrFilePreview_default as GrFilePreview, GrFileUpload_default as GrFileUpload, GrForm_default as GrForm, GrFormErrorBanner_default as GrFormErrorBanner, GrFormField_default as GrFormField, GrFormFile_default as GrFormFile, GrFormSection_default as GrFormSection, GrIcon_default as GrIcon, GrImageViewer_default as GrImageViewer, GrInput_default as GrInput, GrInputTag_default as GrInputTag, GrJsonViewer_default as GrJsonViewer, GrKbd_default as GrKbd, GrLink_default as GrLink, GrList_default as GrList, GrListItem_default as GrListItem, GrLoading_default as GrLoading, GrModal_default as GrModal, GrNavbar_default as GrNavbar, GrNumberInput_default as GrNumberInput, GrPagination_default as GrPagination, GrPopover_default as GrPopover, GrProgressBar_default as GrProgressBar, GrProgressCircle_default as GrProgressCircle, GrPromptDialog_default as GrPromptDialog, GrRadio_default as GrRadio, GrRadioGroup_default as GrRadioGroup, GrRating_default as GrRating, GrResponseErrorBanner_default as GrResponseErrorBanner, GrSegmented_default as GrSegmented, GrSelect_default as GrSelect, GrSidebar_default as GrSidebar, GrSidebarGroup_default as GrSidebarGroup, GrSidebarItem_default as GrSidebarItem, GrSkeleton_default as GrSkeleton, GrSlider_default as GrSlider, GrSortableList_default as GrSortableList, GrSplitter_default as GrSplitter, GrStatistic_default as GrStatistic, GrSwitch_default as GrSwitch, GrTabPanel_default as GrTabPanel, GrTabPanels_default as GrTabPanels, GrTable_default as GrTable, GrTabs_default as GrTabs, GrTextarea_default as GrTextarea, GrTimeline_default as GrTimeline, GrTimelineItem_default as GrTimelineItem, GrToaster_default as GrToaster, GrTooltip_default as GrTooltip, GrTree_default as GrTree, GrTreeSelect_default as GrTreeSelect, GrUploadAbortError, GrUploadErrorBanner_default as GrUploadErrorBanner, GrUploadHttpError, coreResponseErrorParsers, createGrFormMessageResolver, createLoading, createResponseErrorClassifier, deltaDirection, deltaTone, dialogService, extendDefaultParsers, fileKindOf, findKbdToken, formatBadgeValue, formatStatisticValue, grAlertConfig, grAutocompleteConfig, grAutocompleteSafelist, grAvatarConfig, grAvatarSafelist, grBadgeConfig, grBadgeSafelist, grBadgeWrapConfig, grBadgeWrapSafelist, grBottomNavConfig, grBottomNavSafelist, grBreadcrumbsConfig, grBreadcrumbsSafelist, grButtonConfig, grButtonGroupConfig, grButtonSafelist, grCardConfig, grCardSafelist, grCheckboxConfig, grCheckboxGroupConfig, grCheckboxSafelist, grCodeBlockConfig, grCodeBlockSafelist, grCollapseConfig, grCollapseSafelist, grColorPickerConfig, grColorPickerSafelist, grCommandPaletteConfig, grCommandPaletteSafelist, grConfigProviderConfig, grConfirmDialogConfig, grDataTableConfig, grDataTableSafelist, grDeltaConfig, grDeltaSafelist, grDescriptionListConfig, grDescriptionListSafelist, grDialogSafelist, grDialogServiceConfig, grDividerConfig, grDividerSafelist, grDrawerConfig, grDrawerSafelist, grDropdownConfig, grDropdownMenuConfig, grDropdownMenuSafelist, grDropdownSafelist, grEmptyStateConfig, grEmptyStateSafelist, grFilePreviewConfig, grFilePreviewSafelist, grFileUploadConfig, grFileUploadSafelist, grFormConfig, grFormFieldConfig, grFormFieldSafelist, grFormFileConfig, grFormFileSafelist, grFormSectionConfig, grFormSectionSafelist, grIconConfig, grIconSafelist, grImageViewerConfig, grImageViewerSafelist, grInputConfig, grInputSafelist, grInputTagConfig, grInputTagSafelist, grJsonViewerConfig, grJsonViewerSafelist, grKbdConfig, grKbdSafelist, grLinkConfig, grLinkSafelist, grListConfig, grListSafelist, grLoadingConfig, grLoadingSafelist, grModalConfig, grModalSafelist, grNavbarConfig, grNavbarSafelist, grNumberInputConfig, grNumberInputSafelist, grPaginationConfig, grPaginationSafelist, grPopoverConfig, grPopoverSafelist, grProgressBarConfig, grProgressBarSafelist, grProgressCircleConfig, grProgressCircleSafelist, grPromptDialogConfig, grRadioConfig, grRadioGroupConfig, grRadioSafelist, grRatingConfig, grRatingSafelist, grSegmentedConfig, grSegmentedSafelist, grSelectConfig, grSelectSafelist, grSidebarConfig, grSidebarSafelist, grSkeletonConfig, grSliderConfig, grSliderSafelist, grSortableListConfig, grSortableListSafelist, grSplitterConfig, grSplitterSafelist, grStatisticConfig, grStatisticSafelist, grSwitchConfig, grSwitchSafelist, grTabPanelsConfig, grTableConfig, grTableSafelist, grTabsConfig, grTabsSafelist, grTextareaConfig, grTextareaSafelist, grTimelineConfig, grTimelineSafelist, grToasterConfig, grToasterSafelist, grTooltipConfig, grTooltipSafelist, grTreeConfig, grTreeSafelist, grTreeSelectConfig, grTreeSelectSafelist, granularityDialogServicePlugin, granularityThemePlugin, granularityToastPlugin, initThemeEarly, initialsFrom, isPreviewableKind, normalizeError, resolveBreadcrumbsFit, resolveBreadcrumbsLayout, resolveGrConfig, runFieldRules, sizeFromPointer, teardownDialogService, useAnnouncer, useComboboxNavigation, useDialogService, useDismissible, useDragGesture, useDragSort, useFloating, useFocusTrap, useGrButtonGroup, useGrComponentDefaults, useGrComponentProp, useGrComponentSize, useGrConfig, useGrFormContext, useGrFormControl, useGrFormFieldContext, useGrThemeAttrs, useGranularityTranslations, useOverlayLayer, usePortalTarget, useResponseError, useRovingFocus, useTheme, useToast, useVirtualList, vAutofocus, vAutosize, vClickOutside, vDropzone, vHotkey, vLoading };
@@ -1,6 +1,7 @@
1
1
  export { default } from './GrDelta.vue';
2
2
  export { default as GrDelta } from './GrDelta.vue';
3
3
  export { grDeltaConfig } from './config';
4
+ export { deltaDirection, deltaTone } from './deltaTone';
4
5
  export type { GrDeltaPolarity, GrDeltaProps, GrDeltaTone } from './GrDelta.vue';
5
6
  export type { GrDeltaConfigurableProps } from './defaults';
6
7
  export { grDeltaSafelist } from './safelist';
@@ -20,7 +20,10 @@ export interface GrDescriptionItem {
20
20
  */
21
21
  export interface GrDescriptionListProps {
22
22
  items: readonly GrDescriptionItem[];
23
- /** `inline` — подпись слева колонкой; `stacked` — над значением. */
23
+ /**
24
+ * `inline` — подпись слева колонкой; `stacked` — над значением; `flow` —
25
+ * пары текут по строке с переносом (метаданные внутри пункта списка).
26
+ */
24
27
  layout?: GrDescriptionLayout;
25
28
  /** Ширина колонки подписей при `inline`. */
26
29
  labelWidth?: string;
@@ -30,8 +33,9 @@ export interface GrDescriptionListProps {
30
33
  */
31
34
  stackBelow?: number;
32
35
  density?: GrDescriptionDensity;
33
- /** Отбивать пары линиями. */
36
+ /** Отбивать пары линиями. В `flow` не применяется. */
34
37
  divided?: boolean;
38
+ /** Колонки сетки. В `flow` не применяются: строка колонок не имеет. */
35
39
  columns?: GrDescriptionColumns;
36
40
  /** Чем печатать пустое значение. */
37
41
  emptyText?: string;
@@ -6,15 +6,22 @@ import type { GrTone } from '../shared/tones';
6
6
  * Тон перечислен целиком по одному литералу на ветку: UnoCSS сканирует исходный
7
7
  * текст файла, и собранное шаблонной строкой имя уехало бы в CSS литералом.
8
8
  */
9
- /** `inline` — подпись колонкой слева; `stacked` — над значением. */
10
- export type GrDescriptionLayout = 'inline' | 'stacked';
9
+ /**
10
+ * `inline` подпись колонкой слева; `stacked` — над значением; `flow` — пары
11
+ * текут по строке с переносом.
12
+ */
13
+ export type GrDescriptionLayout = 'inline' | 'stacked' | 'flow';
11
14
  export type GrDescriptionDensity = 'regular' | 'compact';
12
- /** Сколько колонок пар. Длинный список читается в две. */
13
- export type GrDescriptionColumns = 1 | 2;
14
- export declare const descriptionRootClass = "m-0 grid gap-x-4 [row-gap:var(--gr-description-list-row-gap,0.375rem)]";
15
+ /** Сколько колонок пар. Длинный список читается в несколько. */
16
+ export type GrDescriptionColumns = 1 | 2 | 3 | 4;
17
+ export declare const descriptionRootClass = "m-0 gap-x-4 [row-gap:var(--gr-description-list-row-gap,0.375rem)]";
18
+ /** Сетка колонок — раскладки `inline` и `stacked`. */
19
+ export declare const descriptionRootGridClass = "grid";
20
+ export declare const descriptionRootFlowClass = "flex flex-wrap items-baseline";
15
21
  export declare const descriptionColumnsClass: Record<GrDescriptionColumns, string>;
16
22
  export declare const descriptionPairInlineClass = "flex min-w-0 items-baseline gap-2";
17
23
  export declare const descriptionPairStackedClass = "min-w-0";
24
+ export declare const descriptionPairFlowClass = "flex min-w-0 items-baseline gap-1";
18
25
  export declare const descriptionDividedClass = "border-b border-[var(--gr-description-list-divider,var(--gr-brd))] pb-1 last:border-b-0 last:pb-0";
19
26
  export declare const descriptionLabelInlineClass = "shrink-0 text-[var(--gr-description-list-label-color,var(--gr-muted-fg))]";
20
27
  export declare const descriptionLabelStackedClass = "text-[var(--gr-description-list-label-color,var(--gr-muted-fg))]";
@@ -1,6 +1,7 @@
1
1
  export { default } from './GrFilePreview.vue';
2
2
  export { default as GrFilePreview } from './GrFilePreview.vue';
3
3
  export { grFilePreviewConfig } from './config';
4
+ export { fileKindOf, isPreviewableKind } from './fileKindOf';
4
5
  export type { GrFileKind, GrFilePreviewEmits, GrFilePreviewProps, GrFilePreviewRatio, } from './GrFilePreview.vue';
5
6
  export type { GrFilePreviewConfigurableProps } from './defaults';
6
7
  export { grFilePreviewSafelist } from './safelist';
@@ -172,7 +172,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
172
172
  suffixFixed: boolean;
173
173
  autocomplete: string;
174
174
  showCount: boolean;
175
- inputmode: "search" | "text" | "none" | "email" | "tel" | "url" | "numeric" | "decimal";
175
+ inputmode: "none" | "search" | "text" | "email" | "tel" | "url" | "numeric" | "decimal";
176
176
  state: "default" | "success" | "warning" | "danger";
177
177
  maxlength: number;
178
178
  passwordToggle: boolean;
@@ -184,7 +184,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
184
184
  suffixFixed: boolean;
185
185
  autocomplete: string;
186
186
  step: number;
187
- inputmode: "search" | "text" | "none" | "email" | "tel" | "url" | "numeric" | "decimal";
187
+ inputmode: "none" | "search" | "text" | "email" | "tel" | "url" | "numeric" | "decimal";
188
188
  state: GrNumberInputState;
189
189
  locale: string;
190
190
  precision: number;
@@ -187,7 +187,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
187
187
  required: boolean;
188
188
  placeholder: string;
189
189
  showCount: boolean;
190
- inputmode: "search" | "text" | "none" | "email" | "tel" | "url" | "numeric" | "decimal";
190
+ inputmode: "none" | "search" | "text" | "email" | "tel" | "url" | "numeric" | "decimal";
191
191
  maxlength: number;
192
192
  closeOnEsc: boolean;
193
193
  closeOnBackdrop: boolean;
@@ -1,4 +1,5 @@
1
1
  import { type Component } from 'vue';
2
+ import { type GrDeltaPolarity } from '../GrDelta/deltaTone';
2
3
  import { type GrStatisticSize, type GrStatisticTone, type GrStatisticTrend } from './grStatisticStyles';
3
4
  export type { GrStatisticSize, GrStatisticTone, GrStatisticTrend, } from './grStatisticStyles';
4
5
  export type { GrStatisticFormatOptions } from './formatStatisticValue';
@@ -37,6 +38,12 @@ export interface GrStatisticProps {
37
38
  size?: GrStatisticSize;
38
39
  /** Тон значения; точечно перекрывается `--gr-statistic-value-color`. */
39
40
  tone?: GrStatisticTone;
41
+ /**
42
+ * Что считать хорошим: тон выводится из знака самого значения. Для выручки
43
+ * рост — успех, для себестоимости и оттока — наоборот. Явный `tone` сильнее:
44
+ * выведенный тон — умолчание, а не диктат.
45
+ */
46
+ polarity?: GrDeltaPolarity;
40
47
  /** Направление динамики — задаёт цвет и иконку строки под значением. */
41
48
  trend?: GrStatisticTrend;
42
49
  /** Текст динамики (например `+12,5% к прошлой неделе`). */
@@ -88,7 +95,8 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
88
95
  suffix: undefined;
89
96
  icon: undefined;
90
97
  size: undefined;
91
- tone: string;
98
+ tone: undefined;
99
+ polarity: undefined;
92
100
  trend: undefined;
93
101
  trendText: undefined;
94
102
  loading: boolean;
@@ -109,7 +117,8 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
109
117
  suffix: undefined;
110
118
  icon: undefined;
111
119
  size: undefined;
112
- tone: string;
120
+ tone: undefined;
121
+ polarity: undefined;
113
122
  trend: undefined;
114
123
  trendText: undefined;
115
124
  loading: boolean;
@@ -123,6 +132,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
123
132
  }>, {
124
133
  tone: GrStatisticTone;
125
134
  size: GrStatisticSize;
135
+ polarity: GrDeltaPolarity;
126
136
  loading: boolean;
127
137
  title: string;
128
138
  icon: string | Component;
@@ -1,11 +1,17 @@
1
+ import type { GrDeltaPolarity } from '../GrDelta/deltaTone';
1
2
  import type { GrStatisticSize } from './grStatisticStyles';
2
3
  /**
3
4
  * Пропы `GrStatistic`, настраиваемые глобально через
4
5
  * `<GrConfigProvider :component-defaults="{ GrStatistic: { … } }">`.
5
6
  * Только оформление — см. `GrButton/defaults.ts`.
7
+ *
8
+ * `polarity` здесь по той же причине, что и у `GrDelta`: на дашборде расходов
9
+ * «рост — плохо» верно сразу для всех плиток ряда, и повторять это на каждой
10
+ * значит однажды пропустить одну.
6
11
  */
7
12
  export interface GrStatisticConfigurableProps {
8
13
  size: GrStatisticSize;
14
+ polarity: GrDeltaPolarity;
9
15
  }
10
16
  declare module '../../composables/useGrComponentConfig' {
11
17
  interface GrComponentDefaultsRegistry {
@@ -16,4 +16,30 @@
16
16
  * сводим к `en`.
17
17
  */
18
18
  export declare function formatNumber(locale: string, value: number | bigint, options?: Intl.NumberFormatOptions): string;
19
+ /**
20
+ * Те же правила, что у `formatNumber`, но по частям: вызывающему нужно знать,
21
+ * где в записи стоит знак, чтобы вынести его отдельным узлом.
22
+ *
23
+ * Инстанс тот же, что у `formatNumber`: `Intl.NumberFormat` умеет оба метода, а
24
+ * второй кэш под теми же ключами разъехался бы с первым на первом вытеснении.
25
+ */
26
+ export declare function formatNumberToParts(locale: string, value: number | bigint, options?: Intl.NumberFormatOptions): Intl.NumberFormatPart[];
27
+ /**
28
+ * Ведущий знак и остаток записи.
29
+ *
30
+ * Нужно там, где знаку полагается свой узел разметки: валюта обязана стоять
31
+ * после знака (`+$0,03`, а не `$+0,03`). Резать готовую строку по первому
32
+ * символу нельзя — в RTL-локалях знаку предшествует невидимая метка
33
+ * направления (`he-IL` — U+200E, `ar-EG` — U+061C), которой и разворачивается
34
+ * минус относительно цифр. Метка уходит вместе со знаком: она управляет им.
35
+ *
36
+ * `sign` пуст, когда знака нет вовсе (ноль, положительное при `signDisplay:
37
+ * 'auto'`, `NaN`) либо когда перед ним стоит содержательная часть. Во втором
38
+ * случае запись не делится совсем: вернуть половину по догадке хуже, чем
39
+ * вернуть целое.
40
+ */
41
+ export declare function splitLeadingSign(parts: readonly Intl.NumberFormatPart[]): {
42
+ sign: string;
43
+ rest: string;
44
+ };
19
45
  export declare function formatDate(locale: string, value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "@feugene/granularity",
4
- "version": "0.22.0",
4
+ "version": "0.23.0",
5
5
  "framework": "vue",
6
6
  "js-types-syntax": "typescript",
7
7
  "description-markup": "markdown",
@@ -2734,7 +2734,7 @@
2734
2734
  },
2735
2735
  {
2736
2736
  "name": "divided",
2737
- "description": "Отбивать пары линиями.",
2737
+ "description": "Отбивать пары линиями. В `flow` не применяется.",
2738
2738
  "default": "undefined",
2739
2739
  "value": {
2740
2740
  "kind": "expression",
@@ -2752,6 +2752,7 @@
2752
2752
  },
2753
2753
  {
2754
2754
  "name": "columns",
2755
+ "description": "Колонки сетки. В `flow` не применяются: строка колонок не имеет.",
2755
2756
  "default": "undefined",
2756
2757
  "value": {
2757
2758
  "kind": "expression",
@@ -2760,7 +2761,7 @@
2760
2761
  },
2761
2762
  {
2762
2763
  "name": "layout",
2763
- "description": "`inline` — подпись слева колонкой; `stacked` — над значением.",
2764
+ "description": "`inline` — подпись слева колонкой; `stacked` — над значением; `flow` — пары текут по строке с переносом (метаданные внутри пункта списка).",
2764
2765
  "default": "undefined",
2765
2766
  "value": {
2766
2767
  "kind": "expression",
@@ -9070,7 +9071,7 @@
9070
9071
  {
9071
9072
  "name": "tone",
9072
9073
  "description": "Тон значения; точечно перекрывается `--gr-statistic-value-color`.",
9073
- "default": "\"neutral\"",
9074
+ "default": "undefined",
9074
9075
  "value": {
9075
9076
  "kind": "expression",
9076
9077
  "type": "\"primary\" | \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\" | \"slate\" | \"azure\" | undefined"
@@ -9183,6 +9184,15 @@
9183
9184
  "type": "string | undefined"
9184
9185
  }
9185
9186
  },
9187
+ {
9188
+ "name": "polarity",
9189
+ "description": "Что считать хорошим: тон выводится из знака самого значения. Для выручки рост — успех, для себестоимости и оттока — наоборот. Явный `tone` сильнее: выведенный тон — умолчание, а не диктат.",
9190
+ "default": "undefined",
9191
+ "value": {
9192
+ "kind": "expression",
9193
+ "type": "GrDeltaPolarity | undefined"
9194
+ }
9195
+ },
9186
9196
  {
9187
9197
  "name": "decimalSeparator",
9188
9198
  "description": "Десятичный разделитель. Сильнее локали; без него и без локали — точка.",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@feugene/granularity",
3
3
  "description": "Granularity design system package with Vue 3 components, consumed via the `@feugene/unocss-preset-granular` preset.",
4
- "version": "0.22.0",
4
+ "version": "0.23.0",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "Evgeniy Fureev",
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-jyZq8XKL.js","names":[],"sources":["../../src/components/GrDescriptionList/grDescriptionListStyles.ts","../../src/components/GrDescriptionList/safelist.ts","../../src/components/GrDescriptionList/config.ts"],"sourcesContent":["import type { GrComponentSize } from '../shared/sizes'\nimport type { GrTone } from '../shared/tones'\n\n/**\n * Классы `GrDescriptionList`.\n *\n * Тон перечислен целиком по одному литералу на ветку: UnoCSS сканирует исходный\n * текст файла, и собранное шаблонной строкой имя уехало бы в CSS литералом.\n */\n\n/** `inline` — подпись колонкой слева; `stacked` — над значением. */\nexport type GrDescriptionLayout = 'inline' | 'stacked'\n\nexport type GrDescriptionDensity = 'regular' | 'compact'\n\n/** Сколько колонок пар. Длинный список читается в две. */\nexport type GrDescriptionColumns = 1 | 2\n\n// `m-0` — у нативного `<dl>` есть браузерные отступы, и без сброса список\n// разъезжается по вертикали. Ряды разводит `--gr-description-list-row-gap`,\n// потому что зазор здесь — точка кастомизации, а не ступень шкалы.\nexport const descriptionRootClass = 'm-0 grid gap-x-4 [row-gap:var(--gr-description-list-row-gap,0.375rem)]'\n\nexport const descriptionColumnsClass: Record<GrDescriptionColumns, string> = {\n 1: 'grid-cols-1',\n // Пара — элемент сетки, поэтому между колонками она не рвётся. `column-count`\n // рвал бы: он раскладывает поток, а не блоки.\n 2: 'grid-cols-1 sm:grid-cols-2',\n}\n\nexport const descriptionPairInlineClass = 'flex min-w-0 items-baseline gap-2'\nexport const descriptionPairStackedClass = 'min-w-0'\n\nexport const descriptionDividedClass = 'border-b border-[var(--gr-description-list-divider,var(--gr-brd))] pb-1 last:border-b-0 last:pb-0'\n\n// `shrink-0` держит колонку подписей ровной: иначе длинное значение сжимало бы\n// подпись, и значения переставали бы стоять в колонку — ровно то, ради чего\n// раскладка `inline` и существует.\nexport const descriptionLabelInlineClass = 'shrink-0 text-[var(--gr-description-list-label-color,var(--gr-muted-fg))]'\nexport const descriptionLabelStackedClass = 'text-[var(--gr-description-list-label-color,var(--gr-muted-fg))]'\n\n// `break-words` обязателен: в парах живут идентификаторы, хеши и `X-Request-Id`,\n// и без переноса они выталкивают вёрстку.\nexport const descriptionValueClass = 'm-0 min-w-0 break-words'\n\n/** Тон — только у значения: красная подпись читается как «поле сломано». */\nexport const descriptionValueToneClass: Record<GrTone, string> = {\n primary: 'text-[var(--gr-primary-text)]',\n neutral: '',\n success: 'text-[var(--gr-success-text)]',\n warning: 'text-[var(--gr-warning-text)]',\n danger: 'text-[var(--gr-danger-text)]',\n info: 'text-[var(--gr-info-text)]',\n slate: 'text-[var(--gr-slate-text)]',\n azure: 'text-[var(--gr-azure-text)]',\n}\n\nexport const descriptionSizeClass: Record<GrComponentSize, string> = {\n xs: 'text-[length:var(--gr-control-text-2xs)]',\n sm: 'text-[length:var(--gr-control-text-xs)]',\n md: 'text-[length:var(--gr-control-text-sm)]',\n lg: 'text-[length:var(--gr-control-text-md)]',\n}\n\nexport const descriptionDensityClass: Record<GrDescriptionDensity, string> = {\n regular: '',\n compact: '[row-gap:var(--gr-description-list-row-gap,0.125rem)]',\n}\n","import { splitClassTokens } from '../shared/classTokens'\n\nimport {\n descriptionColumnsClass,\n descriptionDensityClass,\n descriptionDividedClass,\n descriptionLabelInlineClass,\n descriptionLabelStackedClass,\n descriptionPairInlineClass,\n descriptionPairStackedClass,\n descriptionRootClass,\n descriptionSizeClass,\n descriptionValueClass,\n descriptionValueToneClass,\n} from './grDescriptionListStyles'\n\n// Классы из вычисляемых мап (тон, размер, колонки) UnoCSS сканом не находит —\n// только safelist. Литералы хелпера туда же: на сборке он уезжает в общий чанк.\nexport const grDescriptionListSafelist = [...new Set([\n ...splitClassTokens(descriptionRootClass),\n ...splitClassTokens(descriptionPairInlineClass),\n ...splitClassTokens(descriptionPairStackedClass),\n ...splitClassTokens(descriptionDividedClass),\n ...splitClassTokens(descriptionLabelInlineClass),\n ...splitClassTokens(descriptionLabelStackedClass),\n ...splitClassTokens(descriptionValueClass),\n ...Object.values(descriptionColumnsClass).flatMap(splitClassTokens),\n ...Object.values(descriptionValueToneClass).flatMap(splitClassTokens),\n ...Object.values(descriptionSizeClass).flatMap(splitClassTokens),\n ...Object.values(descriptionDensityClass).flatMap(splitClassTokens),\n])]\n","import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nimport { grDescriptionListSafelist } from './safelist'\n\nexport const grDescriptionListConfig = defineGranularComponent(import.meta.url, {\n name: 'GrDescriptionList',\n safelist: grDescriptionListSafelist,\n})\n"],"mappings":";;;AAqBA,IAAa,uBAAuB;AAEpC,IAAa,0BAAgE;CAC3E,GAAG;CAGH,GAAG;AACL;AAEA,IAAa,6BAA6B;AAC1C,IAAa,8BAA8B;AAE3C,IAAa,0BAA0B;AAKvC,IAAa,8BAA8B;AAC3C,IAAa,+BAA+B;AAI5C,IAAa,wBAAwB;;AAGrC,IAAa,4BAAoD;CAC/D,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;CACR,MAAM;CACN,OAAO;CACP,OAAO;AACT;AAEA,IAAa,uBAAwD;CACnE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,0BAAgE;CAC3E,SAAS;CACT,SAAS;AACX;;;ACjDA,IAAa,4BAA4B,CAAC,mBAAG,IAAI,IAAI;CACnD,GAAG,iBAAiB,oBAAoB;CACxC,GAAG,iBAAiB,0BAA0B;CAC9C,GAAG,iBAAiB,2BAA2B;CAC/C,GAAG,iBAAiB,uBAAuB;CAC3C,GAAG,iBAAiB,2BAA2B;CAC/C,GAAG,iBAAiB,4BAA4B;CAChD,GAAG,iBAAiB,qBAAqB;CACzC,GAAG,OAAO,OAAO,uBAAuB,CAAC,CAAC,QAAQ,gBAAgB;CAClE,GAAG,OAAO,OAAO,yBAAyB,CAAC,CAAC,QAAQ,gBAAgB;CACpE,GAAG,OAAO,OAAO,oBAAoB,CAAC,CAAC,QAAQ,gBAAgB;CAC/D,GAAG,OAAO,OAAO,uBAAuB,CAAC,CAAC,QAAQ,gBAAgB;AACpE,CAAC,CAAC;;;AC1BF,IAAa,0BAA0B,wBAAwB,OAAO,KAAK,KAAK;CAC9E,MAAM;CACN,UAAU;AACZ,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"trending-up-Bf9AQmoq.js","names":[],"sources":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrDelta-BMmpYz8K.js","names":[],"sources":["../../../../src/internal/granularityFormat.ts","../../../../src/components/GrDelta/deltaTone.ts","../../../../src/components/GrDelta/GrDelta.vue","../../../../src/components/GrDelta/GrDelta.vue"],"sourcesContent":["/**\n * Форматирование чисел и дат по локали.\n *\n * Своё, а не из `@feugene/fint-i18n`: тот объявлен **опциональной** peer-зависимостью,\n * и пакет обязан работать без него. Под капотом всё равно `Intl`, то есть\n * результат совпадает с `useI18nFormat()` библиотеки — расхождения между\n * приложением с i18n и без него не возникает.\n *\n * Инстансы `Intl` кэшируются: их построение примерно на порядок дороже самого\n * форматирования. Кэш ограничен и сбрасывается целиком — иначе сгенерированные\n * на лету опции растили бы его без предела.\n */\n\nconst CACHE_LIMIT = 64\n\nconst numberFormats = new Map<string, Intl.NumberFormat>()\nconst dateFormats = new Map<string, Intl.DateTimeFormat>()\n\nfunction cacheKey(locale: string, options?: object): string {\n return options ? `${locale}\u0000${JSON.stringify(options)}` : locale\n}\n\nfunction take<T>(cache: Map<string, T>, key: string, create: () => T): T {\n const cached = cache.get(key)\n if (cached) return cached\n\n const created = create()\n if (cache.size >= CACHE_LIMIT) cache.clear()\n cache.set(key, created)\n\n return created\n}\n\n/**\n * Локаль приходит из настроек приложения, а `Intl` бросает на некорректном\n * теге. Форматирование не имеет права уронить рендер, поэтому неизвестный тег\n * сводим к `en`.\n */\nexport function formatNumber(\n locale: string,\n value: number | bigint,\n options?: Intl.NumberFormatOptions,\n): string {\n try {\n return take(numberFormats, cacheKey(locale, options), () => new Intl.NumberFormat(locale, options)).format(value)\n }\n catch {\n return take(numberFormats, cacheKey('en', options), () => new Intl.NumberFormat('en', options)).format(value)\n }\n}\n\nexport function formatDate(\n locale: string,\n value: Date | number | string,\n options?: Intl.DateTimeFormatOptions,\n): string {\n const date = value instanceof Date ? value : new Date(value)\n\n // Невалидную дату возвращаем как есть: пустая или «Invalid Date» в интерфейсе\n // хуже, чем исходное значение, но лучше, чем исключение на рендере.\n if (Number.isNaN(date.getTime())) return String(value)\n\n try {\n return take(dateFormats, cacheKey(locale, options), () => new Intl.DateTimeFormat(locale, options)).format(date)\n }\n catch {\n return take(dateFormats, cacheKey('en', options), () => new Intl.DateTimeFormat('en', options)).format(date)\n }\n}\n","/**\n * Выбор тона для величины со знаком.\n *\n * Чистая функция, а не `switch` внутри компонента: ровно этот `switch`\n * расходился по приложениям копиями, и в одной из них нулевое движение\n * красилось зелёным — как поступление.\n */\n\n/** Что считать хорошим ростом. */\nexport type GrDeltaPolarity\n /** Рост — успех: выручка, конверсия, число подписок. */\n = | 'positive-good'\n /** Рост — проблема: себестоимость, время отклика, отток. */\n | 'negative-good'\n /** Знак ничего не говорит о качестве: сальдо, смещение, разница координат. */\n | 'none'\n\nexport type GrDeltaTone = 'success' | 'danger' | 'neutral'\n\n/**\n * Тон величины по её знаку и полярности.\n *\n * Ноль нейтрален при любой полярности: «не изменилось» — третье состояние, и\n * двумя цветами его не выразить. `null` величины нет вовсе — тона у неё тоже\n * нет, это решает вызывающий.\n */\nexport function deltaTone(value: number, polarity: GrDeltaPolarity = 'positive-good'): GrDeltaTone {\n if (polarity === 'none' || value === 0 || Number.isNaN(value)) return 'neutral'\n\n const good = polarity === 'positive-good' ? value > 0 : value < 0\n\n return good ? 'success' : 'danger'\n}\n\n/** Направление стрелки. Ноль стрелки не получает: двигаться было некуда. */\nexport function deltaDirection(value: number): 'up' | 'down' | 'flat' {\n if (Number.isNaN(value) || value === 0) return 'flat'\n return value > 0 ? 'up' : 'down'\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { formatNumber } from '../../internal/granularityFormat'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconMinus from '~icons/lucide/minus'\nimport IconTrendingDown from '~icons/lucide/trending-down'\nimport IconTrendingUp from '~icons/lucide/trending-up'\n\nimport { deltaDirection, deltaTone, type GrDeltaPolarity, type GrDeltaTone } from './deltaTone'\nimport {\n deltaArrowClass,\n deltaEmptyClass,\n deltaRootClass,\n deltaSizeClass,\n deltaSuffixClass,\n deltaToneClass,\n} from './grDeltaStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrDeltaPolarity, GrDeltaTone } from './deltaTone'\n\n/**\n * Величина со знаком и тоном **внутри строки текста**: «Дельта: +$0.004».\n *\n * Компонент презентационный: дельту не считает и периоды не сравнивает —\n * «к чему сравнивать» знает приложение. Блочная плитка показателя — `GrStatistic`.\n */\nexport interface GrDeltaProps {\n /** Величина. `null` — её нет; это не ноль. */\n value: number | null\n /** Знаков после запятой. Не задано — как отдаст локаль. */\n precision?: number\n /** Приписка перед числом: валюта, единица. */\n prefix?: string\n /** Приписка после числа: `%`, `мс`. */\n suffix?: string\n /**\n * Что считать хорошим. Для выручки рост — успех, для себестоимости и времени\n * отклика — наоборот; без этого компонент врал бы в половине случаев.\n */\n polarity?: GrDeltaPolarity\n /** Ставить `+` у положительных. Минус подставляет `Intl` сам. */\n showSign?: boolean\n /** Стрелка направления. Декоративна: направление уже в знаке. */\n showArrow?: boolean\n /** Чем печатать отсутствующую величину. */\n emptyText?: string\n /** Локаль форматирования. Не задана — локаль i18n-адаптера. */\n locale?: string\n size?: GrComponentSize\n}\n\nconst props = withDefaults(defineProps<GrDeltaProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n precision: undefined,\n prefix: undefined,\n suffix: undefined,\n polarity: undefined,\n showSign: undefined,\n showArrow: undefined,\n emptyText: undefined,\n locale: undefined,\n size: undefined,\n})\n\nconst { locale } = useGranularityTranslations()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrDelta' })\nconst resolvedLocale = computed(() => props.locale ?? locale.value ?? 'en')\nconst resolvedPolarity = useGrComponentProp('GrDelta', 'polarity', () => props.polarity, 'positive-good')\nconst resolvedShowSign = useGrComponentProp('GrDelta', 'showSign', () => props.showSign, true)\nconst resolvedShowArrow = useGrComponentProp('GrDelta', 'showArrow', () => props.showArrow, false)\nconst resolvedEmptyText = useGrComponentProp('GrDelta', 'emptyText', () => props.emptyText, '—')\n\nconst isEmpty = computed(() => props.value === null || props.value === undefined)\n\nconst tone = computed<GrDeltaTone>(() => (\n isEmpty.value ? 'neutral' : deltaTone(props.value as number, resolvedPolarity.value)\n))\n\nconst direction = computed(() => (isEmpty.value ? 'flat' : deltaDirection(props.value as number)))\n\n/**\n * Знак — префикс форматирования, а не склейка строки. `'+' + value` уже ломало\n * `GrStatistic`: строка `'+10'` разбирается как число, и разряды теряются.\n */\nconst formatted = computed(() => {\n if (isEmpty.value) return resolvedEmptyText.value\n\n return formatNumber(resolvedLocale.value, props.value as number, {\n signDisplay: resolvedShowSign.value ? 'exceptZero' : 'auto',\n ...(props.precision === undefined\n ? {}\n : { minimumFractionDigits: props.precision, maximumFractionDigits: props.precision }),\n })\n})\n\nconst arrowIcon = computed(() => {\n if (direction.value === 'up') return IconTrendingUp\n if (direction.value === 'down') return IconTrendingDown\n return IconMinus\n})\n\nconst rootClass = computed(() => [\n deltaRootClass,\n deltaSizeClass[resolvedSize.value],\n isEmpty.value ? deltaEmptyClass : deltaToneClass[tone.value],\n].filter(Boolean).join(' '))\n</script>\n\n<template>\n <span data-gr-delta :data-tone=\"isEmpty ? undefined : tone\" :class=\"rootClass\">\n <component\n :is=\"arrowIcon\"\n v-if=\"resolvedShowArrow && !isEmpty\"\n data-gr-delta-arrow\n :class=\"deltaArrowClass\"\n aria-hidden=\"true\"\n />\n\n <span v-if=\"prefix && !isEmpty\" data-gr-delta-prefix>{{ prefix }}</span>\n <span data-gr-delta-value>{{ formatted }}</span>\n <span v-if=\"suffix && !isEmpty\" data-gr-delta-suffix :class=\"deltaSuffixClass\">{{ suffix }}</span>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { formatNumber } from '../../internal/granularityFormat'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconMinus from '~icons/lucide/minus'\nimport IconTrendingDown from '~icons/lucide/trending-down'\nimport IconTrendingUp from '~icons/lucide/trending-up'\n\nimport { deltaDirection, deltaTone, type GrDeltaPolarity, type GrDeltaTone } from './deltaTone'\nimport {\n deltaArrowClass,\n deltaEmptyClass,\n deltaRootClass,\n deltaSizeClass,\n deltaSuffixClass,\n deltaToneClass,\n} from './grDeltaStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrDeltaPolarity, GrDeltaTone } from './deltaTone'\n\n/**\n * Величина со знаком и тоном **внутри строки текста**: «Дельта: +$0.004».\n *\n * Компонент презентационный: дельту не считает и периоды не сравнивает —\n * «к чему сравнивать» знает приложение. Блочная плитка показателя — `GrStatistic`.\n */\nexport interface GrDeltaProps {\n /** Величина. `null` — её нет; это не ноль. */\n value: number | null\n /** Знаков после запятой. Не задано — как отдаст локаль. */\n precision?: number\n /** Приписка перед числом: валюта, единица. */\n prefix?: string\n /** Приписка после числа: `%`, `мс`. */\n suffix?: string\n /**\n * Что считать хорошим. Для выручки рост — успех, для себестоимости и времени\n * отклика — наоборот; без этого компонент врал бы в половине случаев.\n */\n polarity?: GrDeltaPolarity\n /** Ставить `+` у положительных. Минус подставляет `Intl` сам. */\n showSign?: boolean\n /** Стрелка направления. Декоративна: направление уже в знаке. */\n showArrow?: boolean\n /** Чем печатать отсутствующую величину. */\n emptyText?: string\n /** Локаль форматирования. Не задана — локаль i18n-адаптера. */\n locale?: string\n size?: GrComponentSize\n}\n\nconst props = withDefaults(defineProps<GrDeltaProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n precision: undefined,\n prefix: undefined,\n suffix: undefined,\n polarity: undefined,\n showSign: undefined,\n showArrow: undefined,\n emptyText: undefined,\n locale: undefined,\n size: undefined,\n})\n\nconst { locale } = useGranularityTranslations()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrDelta' })\nconst resolvedLocale = computed(() => props.locale ?? locale.value ?? 'en')\nconst resolvedPolarity = useGrComponentProp('GrDelta', 'polarity', () => props.polarity, 'positive-good')\nconst resolvedShowSign = useGrComponentProp('GrDelta', 'showSign', () => props.showSign, true)\nconst resolvedShowArrow = useGrComponentProp('GrDelta', 'showArrow', () => props.showArrow, false)\nconst resolvedEmptyText = useGrComponentProp('GrDelta', 'emptyText', () => props.emptyText, '—')\n\nconst isEmpty = computed(() => props.value === null || props.value === undefined)\n\nconst tone = computed<GrDeltaTone>(() => (\n isEmpty.value ? 'neutral' : deltaTone(props.value as number, resolvedPolarity.value)\n))\n\nconst direction = computed(() => (isEmpty.value ? 'flat' : deltaDirection(props.value as number)))\n\n/**\n * Знак — префикс форматирования, а не склейка строки. `'+' + value` уже ломало\n * `GrStatistic`: строка `'+10'` разбирается как число, и разряды теряются.\n */\nconst formatted = computed(() => {\n if (isEmpty.value) return resolvedEmptyText.value\n\n return formatNumber(resolvedLocale.value, props.value as number, {\n signDisplay: resolvedShowSign.value ? 'exceptZero' : 'auto',\n ...(props.precision === undefined\n ? {}\n : { minimumFractionDigits: props.precision, maximumFractionDigits: props.precision }),\n })\n})\n\nconst arrowIcon = computed(() => {\n if (direction.value === 'up') return IconTrendingUp\n if (direction.value === 'down') return IconTrendingDown\n return IconMinus\n})\n\nconst rootClass = computed(() => [\n deltaRootClass,\n deltaSizeClass[resolvedSize.value],\n isEmpty.value ? deltaEmptyClass : deltaToneClass[tone.value],\n].filter(Boolean).join(' '))\n</script>\n\n<template>\n <span data-gr-delta :data-tone=\"isEmpty ? undefined : tone\" :class=\"rootClass\">\n <component\n :is=\"arrowIcon\"\n v-if=\"resolvedShowArrow && !isEmpty\"\n data-gr-delta-arrow\n :class=\"deltaArrowClass\"\n aria-hidden=\"true\"\n />\n\n <span v-if=\"prefix && !isEmpty\" data-gr-delta-prefix>{{ prefix }}</span>\n <span data-gr-delta-value>{{ formatted }}</span>\n <span v-if=\"suffix && !isEmpty\" data-gr-delta-suffix :class=\"deltaSuffixClass\">{{ suffix }}</span>\n </span>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAaA,IAAM,cAAc;AAEpB,IAAM,gCAAgB,IAAI,IAA+B;AAGzD,SAAS,SAAS,QAAgB,SAA0B;CAC1D,OAAO,UAAU,GAAG,OAAO,GAAG,KAAK,UAAU,OAAO,MAAM;AAC5D;AAEA,SAAS,KAAQ,OAAuB,KAAa,QAAoB;CACvE,MAAM,SAAS,MAAM,IAAI,GAAG;CAC5B,IAAI,QAAQ,OAAO;CAEnB,MAAM,UAAU,OAAO;CACvB,IAAI,MAAM,QAAQ,aAAa,MAAM,MAAM;CAC3C,MAAM,IAAI,KAAK,OAAO;CAEtB,OAAO;AACT;;;;;;AAOA,SAAgB,aACd,QACA,OACA,SACQ;CACR,IAAI;EACF,OAAO,KAAK,eAAe,SAAS,QAAQ,OAAO,SAAS,IAAI,KAAK,aAAa,QAAQ,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK;CAClH,QACM;EACJ,OAAO,KAAK,eAAe,SAAS,MAAM,OAAO,SAAS,IAAI,KAAK,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK;CAC9G;AACF;;;;;;;;;;ACvBA,SAAgB,UAAU,OAAe,WAA4B,iBAA8B;CACjG,IAAI,aAAa,UAAU,UAAU,KAAK,OAAO,MAAM,KAAK,GAAG,OAAO;CAItE,QAFa,aAAa,kBAAkB,QAAQ,IAAI,QAAQ,KAElD,YAAY;AAC5B;;AAGA,SAAgB,eAAe,OAAuC;CACpE,IAAI,OAAO,MAAM,KAAK,KAAK,UAAU,GAAG,OAAO;CAC/C,OAAO,QAAQ,IAAI,OAAO;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECkBA,MAAM,QAAQ;EAcd,MAAM,EAAE,WAAW,2BAA2B;EAE9C,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,UAAU,CAAC;EAClF,MAAM,iBAAiB,eAAe,MAAM,UAAU,OAAO,SAAS,IAAI;EAC1E,MAAM,mBAAmB,mBAAmB,WAAW,kBAAkB,MAAM,UAAU,eAAe;EACxG,MAAM,mBAAmB,mBAAmB,WAAW,kBAAkB,MAAM,UAAU,IAAI;EAC7F,MAAM,oBAAoB,mBAAmB,WAAW,mBAAmB,MAAM,WAAW,KAAK;EACjG,MAAM,oBAAoB,mBAAmB,WAAW,mBAAmB,MAAM,WAAW,GAAG;EAE/F,MAAM,UAAU,eAAe,MAAM,UAAU,QAAQ,MAAM,UAAU,KAAA,CAAS;EAEhF,MAAM,OAAO,eACX,QAAQ,QAAQ,YAAY,UAAU,MAAM,OAAiB,iBAAiB,KAAK,CACpF;EAED,MAAM,YAAY,eAAgB,QAAQ,QAAQ,SAAS,eAAe,MAAM,KAAe,CAAE;;;;;EAMjG,MAAM,YAAY,eAAe;GAC/B,IAAI,QAAQ,OAAO,OAAO,kBAAkB;GAE5C,OAAO,aAAa,eAAe,OAAO,MAAM,OAAiB;IAC/D,aAAa,iBAAiB,QAAQ,eAAe;IACrD,GAAI,MAAM,cAAc,KAAA,IACpB,CAAC,IACD;KAAE,uBAAuB,MAAM;KAAW,uBAAuB,MAAM;IAAU;GACvF,CAAC;EACH,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B,IAAI,UAAU,UAAU,MAAM,OAAO;GACrC,IAAI,UAAU,UAAU,QAAQ,OAAO;GACvC,OAAO;EACT,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B;GACA,eAAe,aAAa;GAC5B,QAAQ,QAAQ,kBAAkB,eAAe,KAAK;EACxD,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC;;uBAIzB,mBAYO,QAAA;IAZD,iBAAA;IAAe,aAAW,QAAA,QAAU,KAAA,IAAY,KAAA;IAAO,OAAK,eAAE,UAAA,KAAS;;IAGnE,MAAA,iBAAA,KAAiB,CAAK,QAAA,SAAA,UAAA,GAF9B,YAME,wBALK,UAAA,KAAS,GAAA;;KAEd,uBAAA;KACC,OAAK,eAAE,MAAA,eAAA,CAAe;KACvB,eAAY;;IAGF,QAAA,UAAM,CAAK,QAAA,SAAA,UAAA,GAAvB,mBAAwE,QAAxE,YAAwE,gBAAhB,QAAA,MAAM,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAC9D,mBAAgD,QAAhD,YAAgD,gBAAnB,UAAA,KAAS,GAAA,CAAA;IAC1B,QAAA,UAAM,CAAK,QAAA,SAAA,UAAA,GAAvB,mBAAkG,QAAA;;KAAlE,wBAAA;KAAsB,OAAK,eAAE,MAAA,gBAAA,CAAgB;uBAAK,QAAA,MAAM,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}