@feugene/granularity 0.29.0 → 0.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/chunks/{config-_jO3q1Le.js → config-3bQeQvqd.js} +1 -1
  2. package/dist/chunks/{config-_jO3q1Le.js.map → config-3bQeQvqd.js.map} +1 -1
  3. package/dist/chunks/{grIconStyles-B6BRqUld.js → grIconStyles-BwBrJY1E.js} +8 -5
  4. package/dist/chunks/grIconStyles-BwBrJY1E.js.map +1 -0
  5. package/dist/chunks/{granular-provider-Dds_519x.js → granular-provider-NtErwzYB.js} +1 -1
  6. package/dist/chunks/{granular-provider-Dds_519x.js.map → granular-provider-NtErwzYB.js.map} +1 -1
  7. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-BwhUTqmG.js → GrAutocomplete-CaHys_A_.js} +2 -2
  8. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-CaHys_A_.js.map +1 -0
  9. package/dist/components/GrAutocomplete/index.js +1 -1
  10. package/dist/components/GrAutocomplete/styles.css +1 -1
  11. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-BKOvl8W1.js → GrBreadcrumbs-DRDNan_n.js} +1 -1
  12. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-BKOvl8W1.js.map → GrBreadcrumbs-DRDNan_n.js.map} +1 -1
  13. package/dist/components/GrBreadcrumbs/index.js +1 -1
  14. package/dist/components/GrCollapse/chunks/{GrCollapse-CytiA2Sx.js → GrCollapse-Bw9_JatF.js} +1 -1
  15. package/dist/components/GrCollapse/chunks/{GrCollapse-CytiA2Sx.js.map → GrCollapse-Bw9_JatF.js.map} +1 -1
  16. package/dist/components/GrCollapse/index.js +1 -1
  17. package/dist/components/GrDataTable/chunks/{GrDataTable-DU00V44y.js → GrDataTable-C8a9_nCg.js} +1 -1
  18. package/dist/components/GrDataTable/chunks/{GrDataTable-DU00V44y.js.map → GrDataTable-C8a9_nCg.js.map} +1 -1
  19. package/dist/components/GrDataTable/index.js +1 -1
  20. package/dist/components/GrDrawer/chunks/{GrDrawer-BCreL-eq.js → GrDrawer-B7PiAcbU.js} +1 -1
  21. package/dist/components/GrDrawer/chunks/{GrDrawer-BCreL-eq.js.map → GrDrawer-B7PiAcbU.js.map} +1 -1
  22. package/dist/components/GrDrawer/index.js +1 -1
  23. package/dist/components/GrEmptyState/chunks/{GrEmptyState-D4T5pzXA.js → GrEmptyState-RqsWvgI9.js} +1 -1
  24. package/dist/components/GrEmptyState/chunks/{GrEmptyState-D4T5pzXA.js.map → GrEmptyState-RqsWvgI9.js.map} +1 -1
  25. package/dist/components/GrEmptyState/index.js +1 -1
  26. package/dist/components/GrFileUpload/chunks/{GrFileUpload-FjbaYGjL.js → GrFileUpload-NsHkRRIa.js} +1 -1
  27. package/dist/components/GrFileUpload/chunks/{GrFileUpload-FjbaYGjL.js.map → GrFileUpload-NsHkRRIa.js.map} +1 -1
  28. package/dist/components/GrFileUpload/index.js +1 -1
  29. package/dist/components/GrFormFile/chunks/{GrFormFile-ChdKlN83.js → GrFormFile-CYtXU9HV.js} +1 -1
  30. package/dist/components/GrFormFile/chunks/{GrFormFile-ChdKlN83.js.map → GrFormFile-CYtXU9HV.js.map} +1 -1
  31. package/dist/components/GrFormFile/index.js +1 -1
  32. package/dist/components/GrIcon/chunks/{GrIcon-Bn1XEriL.js → GrIcon-Dmf0c9ba.js} +1 -1
  33. package/dist/components/GrIcon/chunks/{GrIcon-Bn1XEriL.js.map → GrIcon-Dmf0c9ba.js.map} +1 -1
  34. package/dist/components/GrIcon/index.js +2 -2
  35. package/dist/components/GrImageViewer/chunks/{GrImageViewer-COUcpOgR.js → GrImageViewer-CyVN6Zpc.js} +1 -1
  36. package/dist/components/GrImageViewer/chunks/{GrImageViewer-COUcpOgR.js.map → GrImageViewer-CyVN6Zpc.js.map} +1 -1
  37. package/dist/components/GrImageViewer/index.js +1 -1
  38. package/dist/components/GrInputTag/chunks/{GrInputTag-LP3smqOv.js → GrInputTag-5wVcB0zG.js} +1 -1
  39. package/dist/components/GrInputTag/chunks/{GrInputTag-LP3smqOv.js.map → GrInputTag-5wVcB0zG.js.map} +1 -1
  40. package/dist/components/GrInputTag/index.js +1 -1
  41. package/dist/components/GrLink/chunks/{GrLink-DAGCFYO7.js → GrLink-DYXw3W9w.js} +1 -1
  42. package/dist/components/GrLink/chunks/{GrLink-DAGCFYO7.js.map → GrLink-DYXw3W9w.js.map} +1 -1
  43. package/dist/components/GrLink/index.js +1 -1
  44. package/dist/components/GrLoading/chunks/{GrLoading-DDD8WAIH.js → GrLoading-CMD3Ri9P.js} +1 -1
  45. package/dist/components/GrLoading/chunks/{GrLoading-DDD8WAIH.js.map → GrLoading-CMD3Ri9P.js.map} +1 -1
  46. package/dist/components/GrLoading/index.js +1 -1
  47. package/dist/components/GrNavbar/chunks/{GrNavbar-DhswlYyh.js → GrNavbar-C5EjMKaC.js} +1 -1
  48. package/dist/components/GrNavbar/chunks/{GrNavbar-DhswlYyh.js.map → GrNavbar-C5EjMKaC.js.map} +1 -1
  49. package/dist/components/GrNavbar/index.js +1 -1
  50. package/dist/components/GrNumberInput/chunks/{GrNumberInput-Daud9Jdm.js → GrNumberInput-DQ59YICu.js} +1 -1
  51. package/dist/components/GrNumberInput/chunks/{GrNumberInput-Daud9Jdm.js.map → GrNumberInput-DQ59YICu.js.map} +1 -1
  52. package/dist/components/GrNumberInput/index.js +1 -1
  53. package/dist/components/GrPagination/chunks/{GrPagination-kQhWlfs-.js → GrPagination-CX9ZV8mR.js} +1 -1
  54. package/dist/components/GrPagination/chunks/{GrPagination-kQhWlfs-.js.map → GrPagination-CX9ZV8mR.js.map} +1 -1
  55. package/dist/components/GrPagination/index.js +1 -1
  56. package/dist/components/GrSelect/chunks/{GrSelect-D-KU9Lx_.js → GrSelect-DKoQu3w9.js} +3 -3
  57. package/dist/components/GrSelect/chunks/{GrSelect-D-KU9Lx_.js.map → GrSelect-DKoQu3w9.js.map} +1 -1
  58. package/dist/components/GrSelect/index.js +1 -1
  59. package/dist/components/GrSelect/styles.css +1 -1
  60. package/dist/components/GrSidebar/chunks/{GrSidebar-DxPhIiWs.js → GrSidebar-B8EQIlfW.js} +1 -1
  61. package/dist/components/GrSidebar/chunks/{GrSidebar-DxPhIiWs.js.map → GrSidebar-B8EQIlfW.js.map} +1 -1
  62. package/dist/components/GrSidebar/index.js +1 -1
  63. package/dist/components/GrToaster/chunks/{GrToaster-CUi3ldJ0.js → GrToaster-Dp7-4B8k.js} +1 -1
  64. package/dist/components/GrToaster/chunks/{GrToaster-CUi3ldJ0.js.map → GrToaster-Dp7-4B8k.js.map} +1 -1
  65. package/dist/components/GrToaster/index.js +1 -1
  66. package/dist/components/GrTooltip/chunks/{GrTooltip-BKFjqu0E.js → GrTooltip-uYETdbCD.js} +1 -1
  67. package/dist/components/GrTooltip/chunks/{GrTooltip-BKFjqu0E.js.map → GrTooltip-uYETdbCD.js.map} +1 -1
  68. package/dist/components/GrTooltip/index.js +1 -1
  69. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-B1GdEn5K.js → GrTreeSelect-CymO68Cv.js} +1 -1
  70. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-CymO68Cv.js.map +1 -0
  71. package/dist/components/GrTreeSelect/index.js +1 -1
  72. package/dist/directives/loading.js +1 -1
  73. package/dist/granular-provider-node.js +1 -1
  74. package/dist/granular-provider.js +1 -1
  75. package/dist/index.js +22 -22
  76. package/dist/types/components/GrIcon/grIconStyles.d.ts +7 -4
  77. package/dist/web-types.json +1 -1
  78. package/package.json +2 -2
  79. package/dist/chunks/grIconStyles-B6BRqUld.js.map +0 -1
  80. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-BwhUTqmG.js.map +0 -1
  81. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-B1GdEn5K.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"GrTooltip-BKFjqu0E.js","names":[],"sources":["../../../../src/components/GrTooltip/GrTooltip.vue","../../../../src/components/GrTooltip/GrTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * `placement` задаёт предпочтительную сторону, `flip` переворачивает подсказку,\n * если места не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onMounted, onUnmounted, onUpdated, ref, useId, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport GrIcon from '../GrIcon'\nimport { useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { type GrTooltipSize, panelSizes, panelWidths, triggerIconSizes } from './grTooltipStyles'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport type GrTooltipPlacement = UseFloatingPlacement\n\nexport interface GrTooltipProps {\n /** Текст подсказки. Разметка вместо текста — слот `#content`. */\n text?: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. */\n iconColor?: string\n /** Размер панели и дефолтной триггер-иконки. */\n size?: GrTooltipSize\n /** Предпочтительная сторона панели; `flip` может её изменить. */\n placement?: GrTooltipPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n /** Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. */\n openDelay?: number\n /** Задержка перед скрытием, мс. */\n closeDelay?: number\n /** Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. */\n disabled?: boolean\n /** Управляемая видимость. Без неё компонент ведёт видимость сам. */\n open?: boolean\n}\n\nexport interface GrTooltipEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n text: undefined,\n iconColor: 'var(--gr-muted-fg)',\n size: undefined,\n placement: 'top',\n offsetPx: undefined,\n openDelay: 0,\n closeDelay: 0,\n disabled: false,\n open: undefined,\n})\n\nconst emit = defineEmits<GrTooltipEmits>()\n\ndefineSlots<{\n /** Триггер. По умолчанию — иконка info. */\n default?: () => unknown\n /** Содержимое подсказки; заменяет проп `text`. */\n content?: () => unknown\n}>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTooltip' })\n\nconst panelClass = computed(() => [\n panelSizes[resolvedSize.value],\n panelWidths[resolvedSize.value],\n].join(' '))\nconst triggerIconSize = computed(() => triggerIconSizes[resolvedSize.value])\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\nconst uncontrolledOpen = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n/** Показывать нечего — пустая панель хуже отсутствующей. */\nconst hasContent = computed(() => Boolean(slots.content) || Boolean(props.text))\n\nconst open = computed<boolean>({\n get: () => {\n if (props.disabled || !hasContent.value) return false\n return props.open ?? uncontrolledOpen.value\n },\n set: (next) => {\n uncontrolledOpen.value = next\n emit('update:open', next)\n },\n})\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: () => props.placement,\n // Геттер, а не значение: `useFloating` читает опцию на каждом пересчёте, и\n // так проп остаётся живым, а не замирает на моменте setup.\n get offsetPx() { return props.offsetPx },\n zIndexVar: '--gr-z-tooltip',\n})\n\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction schedule(next: boolean, delayMs: number): void {\n clearTimeout(delayTimer)\n\n if (delayMs <= 0) {\n open.value = next\n return\n }\n\n delayTimer = setTimeout(() => { open.value = next }, delayMs)\n}\n\nfunction show(): void {\n if (props.disabled) return\n schedule(true, props.openDelay)\n}\n\nfunction hide(): void {\n schedule(false, props.closeDelay)\n}\n\n/** Escape и клик вне закрывают мгновенно: задержка тут читалась бы как залипание. */\nfunction dismiss(): void {\n clearTimeout(delayTimer)\n if (open.value) open.value = false\n}\n\n// Нужен только тач-режиму: курсор и фокус закрывают подсказку сами.\nconst dismissOnOutside = computed(() => ({ handler: dismiss, enabled: open.value }))\n\n/**\n * На тач-устройствах hover не существует, а фокус по тапу не гарантирован —\n * без этого подсказка там недоступна вовсе. Тап по триггеру переключает\n * панель, тап вне — закрывает (`vClickOutside`).\n */\nfunction onTouch(): void {\n if (open.value) dismiss()\n else show()\n}\n\nonUnmounted(() => clearTimeout(delayTimer))\n\nuseDismissible(open, dismiss)\n\nconst FOCUSABLE_SELECTOR = 'a[href],button,input,select,textarea,[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Типовое употребление — подсказка у кнопки. Фокусируемая обёртка вокруг\n * фокусируемого контрола дала бы два таб-стопа на один визуальный элемент,\n * поэтому обёртка забирает `tabindex` только когда в слоте фокусироваться\n * нечему (текст, иконка). Разметку слота пишет потребитель, привязать к ней\n * `aria-describedby` из шаблона нельзя — отсюда единственная точка, где атрибут\n * ставится вручную.\n */\nconst slotFocusableEl = ref<HTMLElement | null>(null)\n\nfunction syncSlotFocusable(): void {\n slotFocusableEl.value = slots.default\n ? triggerEl.value?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? null\n : null\n}\n\nonMounted(syncSlotFocusable)\nonUpdated(syncSlotFocusable)\n\nwatchEffect((onCleanup) => {\n const el = slotFocusableEl.value\n if (!el) return\n\n el.setAttribute('aria-describedby', tooltipId)\n onCleanup(() => el.removeAttribute('aria-describedby'))\n})\n\nconst wrapperTabindex = computed(() => (slotFocusableEl.value ? undefined : 0))\nconst wrapperDescribedBy = computed(() => (slotFocusableEl.value ? undefined : tooltipId))\n</script>\n\n<template>\n <span\n v-click-outside=\"dismissOnOutside\"\n data-gr-tooltip\n class=\"relative inline-flex\"\n >\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n :tabindex=\"wrapperTabindex\"\n :aria-describedby=\"wrapperDescribedBy\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n @touchstart.passive=\"onTouch\"\n >\n <slot>\n <GrIcon :size=\"triggerIconSize\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <!--\n Свой набор, а не общий `panelPopTransition`: подсказка появляется фейдом\n без масштабирования — она догоняет курсор, и рост от 95% читался бы как\n второй, лишний жест. В общей карте живут наборы с двумя и более\n потребителями, у этого он один.\n -->\n <transition\n enter-active-class=\"transition-opacity ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n v-bind=\"themeAttrs\"\n data-gr-tooltip-panel\n data-gr-overlay-root\n class=\"pointer-events-none rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :class=\"panelClass\"\n :style=\"floatingStyle\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * `placement` задаёт предпочтительную сторону, `flip` переворачивает подсказку,\n * если места не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onMounted, onUnmounted, onUpdated, ref, useId, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport GrIcon from '../GrIcon'\nimport { useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { type GrTooltipSize, panelSizes, panelWidths, triggerIconSizes } from './grTooltipStyles'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport type GrTooltipPlacement = UseFloatingPlacement\n\nexport interface GrTooltipProps {\n /** Текст подсказки. Разметка вместо текста — слот `#content`. */\n text?: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. */\n iconColor?: string\n /** Размер панели и дефолтной триггер-иконки. */\n size?: GrTooltipSize\n /** Предпочтительная сторона панели; `flip` может её изменить. */\n placement?: GrTooltipPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n /** Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. */\n openDelay?: number\n /** Задержка перед скрытием, мс. */\n closeDelay?: number\n /** Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. */\n disabled?: boolean\n /** Управляемая видимость. Без неё компонент ведёт видимость сам. */\n open?: boolean\n}\n\nexport interface GrTooltipEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n text: undefined,\n iconColor: 'var(--gr-muted-fg)',\n size: undefined,\n placement: 'top',\n offsetPx: undefined,\n openDelay: 0,\n closeDelay: 0,\n disabled: false,\n open: undefined,\n})\n\nconst emit = defineEmits<GrTooltipEmits>()\n\ndefineSlots<{\n /** Триггер. По умолчанию — иконка info. */\n default?: () => unknown\n /** Содержимое подсказки; заменяет проп `text`. */\n content?: () => unknown\n}>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTooltip' })\n\nconst panelClass = computed(() => [\n panelSizes[resolvedSize.value],\n panelWidths[resolvedSize.value],\n].join(' '))\nconst triggerIconSize = computed(() => triggerIconSizes[resolvedSize.value])\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\nconst uncontrolledOpen = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n/** Показывать нечего — пустая панель хуже отсутствующей. */\nconst hasContent = computed(() => Boolean(slots.content) || Boolean(props.text))\n\nconst open = computed<boolean>({\n get: () => {\n if (props.disabled || !hasContent.value) return false\n return props.open ?? uncontrolledOpen.value\n },\n set: (next) => {\n uncontrolledOpen.value = next\n emit('update:open', next)\n },\n})\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: () => props.placement,\n // Геттер, а не значение: `useFloating` читает опцию на каждом пересчёте, и\n // так проп остаётся живым, а не замирает на моменте setup.\n get offsetPx() { return props.offsetPx },\n zIndexVar: '--gr-z-tooltip',\n})\n\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction schedule(next: boolean, delayMs: number): void {\n clearTimeout(delayTimer)\n\n if (delayMs <= 0) {\n open.value = next\n return\n }\n\n delayTimer = setTimeout(() => { open.value = next }, delayMs)\n}\n\nfunction show(): void {\n if (props.disabled) return\n schedule(true, props.openDelay)\n}\n\nfunction hide(): void {\n schedule(false, props.closeDelay)\n}\n\n/** Escape и клик вне закрывают мгновенно: задержка тут читалась бы как залипание. */\nfunction dismiss(): void {\n clearTimeout(delayTimer)\n if (open.value) open.value = false\n}\n\n// Нужен только тач-режиму: курсор и фокус закрывают подсказку сами.\nconst dismissOnOutside = computed(() => ({ handler: dismiss, enabled: open.value }))\n\n/**\n * На тач-устройствах hover не существует, а фокус по тапу не гарантирован —\n * без этого подсказка там недоступна вовсе. Тап по триггеру переключает\n * панель, тап вне — закрывает (`vClickOutside`).\n */\nfunction onTouch(): void {\n if (open.value) dismiss()\n else show()\n}\n\nonUnmounted(() => clearTimeout(delayTimer))\n\nuseDismissible(open, dismiss)\n\nconst FOCUSABLE_SELECTOR = 'a[href],button,input,select,textarea,[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Типовое употребление — подсказка у кнопки. Фокусируемая обёртка вокруг\n * фокусируемого контрола дала бы два таб-стопа на один визуальный элемент,\n * поэтому обёртка забирает `tabindex` только когда в слоте фокусироваться\n * нечему (текст, иконка). Разметку слота пишет потребитель, привязать к ней\n * `aria-describedby` из шаблона нельзя — отсюда единственная точка, где атрибут\n * ставится вручную.\n */\nconst slotFocusableEl = ref<HTMLElement | null>(null)\n\nfunction syncSlotFocusable(): void {\n slotFocusableEl.value = slots.default\n ? triggerEl.value?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? null\n : null\n}\n\nonMounted(syncSlotFocusable)\nonUpdated(syncSlotFocusable)\n\nwatchEffect((onCleanup) => {\n const el = slotFocusableEl.value\n if (!el) return\n\n el.setAttribute('aria-describedby', tooltipId)\n onCleanup(() => el.removeAttribute('aria-describedby'))\n})\n\nconst wrapperTabindex = computed(() => (slotFocusableEl.value ? undefined : 0))\nconst wrapperDescribedBy = computed(() => (slotFocusableEl.value ? undefined : tooltipId))\n</script>\n\n<template>\n <span\n v-click-outside=\"dismissOnOutside\"\n data-gr-tooltip\n class=\"relative inline-flex\"\n >\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n :tabindex=\"wrapperTabindex\"\n :aria-describedby=\"wrapperDescribedBy\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n @touchstart.passive=\"onTouch\"\n >\n <slot>\n <GrIcon :size=\"triggerIconSize\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <!--\n Свой набор, а не общий `panelPopTransition`: подсказка появляется фейдом\n без масштабирования — она догоняет курсор, и рост от 95% читался бы как\n второй, лишний жест. В общей карте живут наборы с двумя и более\n потребителями, у этого он один.\n -->\n <transition\n enter-active-class=\"transition-opacity ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n v-bind=\"themeAttrs\"\n data-gr-tooltip-panel\n data-gr-overlay-root\n class=\"pointer-events-none rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :class=\"panelClass\"\n :style=\"floatingStyle\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrTooltip-uYETdbCD.js","names":[],"sources":["../../../../src/components/GrTooltip/GrTooltip.vue","../../../../src/components/GrTooltip/GrTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * `placement` задаёт предпочтительную сторону, `flip` переворачивает подсказку,\n * если места не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onMounted, onUnmounted, onUpdated, ref, useId, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport GrIcon from '../GrIcon'\nimport { useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { type GrTooltipSize, panelSizes, panelWidths, triggerIconSizes } from './grTooltipStyles'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport type GrTooltipPlacement = UseFloatingPlacement\n\nexport interface GrTooltipProps {\n /** Текст подсказки. Разметка вместо текста — слот `#content`. */\n text?: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. */\n iconColor?: string\n /** Размер панели и дефолтной триггер-иконки. */\n size?: GrTooltipSize\n /** Предпочтительная сторона панели; `flip` может её изменить. */\n placement?: GrTooltipPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n /** Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. */\n openDelay?: number\n /** Задержка перед скрытием, мс. */\n closeDelay?: number\n /** Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. */\n disabled?: boolean\n /** Управляемая видимость. Без неё компонент ведёт видимость сам. */\n open?: boolean\n}\n\nexport interface GrTooltipEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n text: undefined,\n iconColor: 'var(--gr-muted-fg)',\n size: undefined,\n placement: 'top',\n offsetPx: undefined,\n openDelay: 0,\n closeDelay: 0,\n disabled: false,\n open: undefined,\n})\n\nconst emit = defineEmits<GrTooltipEmits>()\n\ndefineSlots<{\n /** Триггер. По умолчанию — иконка info. */\n default?: () => unknown\n /** Содержимое подсказки; заменяет проп `text`. */\n content?: () => unknown\n}>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTooltip' })\n\nconst panelClass = computed(() => [\n panelSizes[resolvedSize.value],\n panelWidths[resolvedSize.value],\n].join(' '))\nconst triggerIconSize = computed(() => triggerIconSizes[resolvedSize.value])\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\nconst uncontrolledOpen = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n/** Показывать нечего — пустая панель хуже отсутствующей. */\nconst hasContent = computed(() => Boolean(slots.content) || Boolean(props.text))\n\nconst open = computed<boolean>({\n get: () => {\n if (props.disabled || !hasContent.value) return false\n return props.open ?? uncontrolledOpen.value\n },\n set: (next) => {\n uncontrolledOpen.value = next\n emit('update:open', next)\n },\n})\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: () => props.placement,\n // Геттер, а не значение: `useFloating` читает опцию на каждом пересчёте, и\n // так проп остаётся живым, а не замирает на моменте setup.\n get offsetPx() { return props.offsetPx },\n zIndexVar: '--gr-z-tooltip',\n})\n\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction schedule(next: boolean, delayMs: number): void {\n clearTimeout(delayTimer)\n\n if (delayMs <= 0) {\n open.value = next\n return\n }\n\n delayTimer = setTimeout(() => { open.value = next }, delayMs)\n}\n\nfunction show(): void {\n if (props.disabled) return\n schedule(true, props.openDelay)\n}\n\nfunction hide(): void {\n schedule(false, props.closeDelay)\n}\n\n/** Escape и клик вне закрывают мгновенно: задержка тут читалась бы как залипание. */\nfunction dismiss(): void {\n clearTimeout(delayTimer)\n if (open.value) open.value = false\n}\n\n// Нужен только тач-режиму: курсор и фокус закрывают подсказку сами.\nconst dismissOnOutside = computed(() => ({ handler: dismiss, enabled: open.value }))\n\n/**\n * На тач-устройствах hover не существует, а фокус по тапу не гарантирован —\n * без этого подсказка там недоступна вовсе. Тап по триггеру переключает\n * панель, тап вне — закрывает (`vClickOutside`).\n */\nfunction onTouch(): void {\n if (open.value) dismiss()\n else show()\n}\n\nonUnmounted(() => clearTimeout(delayTimer))\n\nuseDismissible(open, dismiss)\n\nconst FOCUSABLE_SELECTOR = 'a[href],button,input,select,textarea,[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Типовое употребление — подсказка у кнопки. Фокусируемая обёртка вокруг\n * фокусируемого контрола дала бы два таб-стопа на один визуальный элемент,\n * поэтому обёртка забирает `tabindex` только когда в слоте фокусироваться\n * нечему (текст, иконка). Разметку слота пишет потребитель, привязать к ней\n * `aria-describedby` из шаблона нельзя — отсюда единственная точка, где атрибут\n * ставится вручную.\n */\nconst slotFocusableEl = ref<HTMLElement | null>(null)\n\nfunction syncSlotFocusable(): void {\n slotFocusableEl.value = slots.default\n ? triggerEl.value?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? null\n : null\n}\n\nonMounted(syncSlotFocusable)\nonUpdated(syncSlotFocusable)\n\nwatchEffect((onCleanup) => {\n const el = slotFocusableEl.value\n if (!el) return\n\n el.setAttribute('aria-describedby', tooltipId)\n onCleanup(() => el.removeAttribute('aria-describedby'))\n})\n\nconst wrapperTabindex = computed(() => (slotFocusableEl.value ? undefined : 0))\nconst wrapperDescribedBy = computed(() => (slotFocusableEl.value ? undefined : tooltipId))\n</script>\n\n<template>\n <span\n v-click-outside=\"dismissOnOutside\"\n data-gr-tooltip\n class=\"relative inline-flex\"\n >\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n :tabindex=\"wrapperTabindex\"\n :aria-describedby=\"wrapperDescribedBy\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n @touchstart.passive=\"onTouch\"\n >\n <slot>\n <GrIcon :size=\"triggerIconSize\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <!--\n Свой набор, а не общий `panelPopTransition`: подсказка появляется фейдом\n без масштабирования — она догоняет курсор, и рост от 95% читался бы как\n второй, лишний жест. В общей карте живут наборы с двумя и более\n потребителями, у этого он один.\n -->\n <transition\n enter-active-class=\"transition-opacity ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n v-bind=\"themeAttrs\"\n data-gr-tooltip-panel\n data-gr-overlay-root\n class=\"pointer-events-none rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :class=\"panelClass\"\n :style=\"floatingStyle\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * `placement` задаёт предпочтительную сторону, `flip` переворачивает подсказку,\n * если места не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onMounted, onUnmounted, onUpdated, ref, useId, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport GrIcon from '../GrIcon'\nimport { useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { type GrTooltipSize, panelSizes, panelWidths, triggerIconSizes } from './grTooltipStyles'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport type GrTooltipPlacement = UseFloatingPlacement\n\nexport interface GrTooltipProps {\n /** Текст подсказки. Разметка вместо текста — слот `#content`. */\n text?: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. */\n iconColor?: string\n /** Размер панели и дефолтной триггер-иконки. */\n size?: GrTooltipSize\n /** Предпочтительная сторона панели; `flip` может её изменить. */\n placement?: GrTooltipPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n /** Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. */\n openDelay?: number\n /** Задержка перед скрытием, мс. */\n closeDelay?: number\n /** Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. */\n disabled?: boolean\n /** Управляемая видимость. Без неё компонент ведёт видимость сам. */\n open?: boolean\n}\n\nexport interface GrTooltipEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n text: undefined,\n iconColor: 'var(--gr-muted-fg)',\n size: undefined,\n placement: 'top',\n offsetPx: undefined,\n openDelay: 0,\n closeDelay: 0,\n disabled: false,\n open: undefined,\n})\n\nconst emit = defineEmits<GrTooltipEmits>()\n\ndefineSlots<{\n /** Триггер. По умолчанию — иконка info. */\n default?: () => unknown\n /** Содержимое подсказки; заменяет проп `text`. */\n content?: () => unknown\n}>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTooltip' })\n\nconst panelClass = computed(() => [\n panelSizes[resolvedSize.value],\n panelWidths[resolvedSize.value],\n].join(' '))\nconst triggerIconSize = computed(() => triggerIconSizes[resolvedSize.value])\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\nconst uncontrolledOpen = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n/** Показывать нечего — пустая панель хуже отсутствующей. */\nconst hasContent = computed(() => Boolean(slots.content) || Boolean(props.text))\n\nconst open = computed<boolean>({\n get: () => {\n if (props.disabled || !hasContent.value) return false\n return props.open ?? uncontrolledOpen.value\n },\n set: (next) => {\n uncontrolledOpen.value = next\n emit('update:open', next)\n },\n})\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: () => props.placement,\n // Геттер, а не значение: `useFloating` читает опцию на каждом пересчёте, и\n // так проп остаётся живым, а не замирает на моменте setup.\n get offsetPx() { return props.offsetPx },\n zIndexVar: '--gr-z-tooltip',\n})\n\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction schedule(next: boolean, delayMs: number): void {\n clearTimeout(delayTimer)\n\n if (delayMs <= 0) {\n open.value = next\n return\n }\n\n delayTimer = setTimeout(() => { open.value = next }, delayMs)\n}\n\nfunction show(): void {\n if (props.disabled) return\n schedule(true, props.openDelay)\n}\n\nfunction hide(): void {\n schedule(false, props.closeDelay)\n}\n\n/** Escape и клик вне закрывают мгновенно: задержка тут читалась бы как залипание. */\nfunction dismiss(): void {\n clearTimeout(delayTimer)\n if (open.value) open.value = false\n}\n\n// Нужен только тач-режиму: курсор и фокус закрывают подсказку сами.\nconst dismissOnOutside = computed(() => ({ handler: dismiss, enabled: open.value }))\n\n/**\n * На тач-устройствах hover не существует, а фокус по тапу не гарантирован —\n * без этого подсказка там недоступна вовсе. Тап по триггеру переключает\n * панель, тап вне — закрывает (`vClickOutside`).\n */\nfunction onTouch(): void {\n if (open.value) dismiss()\n else show()\n}\n\nonUnmounted(() => clearTimeout(delayTimer))\n\nuseDismissible(open, dismiss)\n\nconst FOCUSABLE_SELECTOR = 'a[href],button,input,select,textarea,[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Типовое употребление — подсказка у кнопки. Фокусируемая обёртка вокруг\n * фокусируемого контрола дала бы два таб-стопа на один визуальный элемент,\n * поэтому обёртка забирает `tabindex` только когда в слоте фокусироваться\n * нечему (текст, иконка). Разметку слота пишет потребитель, привязать к ней\n * `aria-describedby` из шаблона нельзя — отсюда единственная точка, где атрибут\n * ставится вручную.\n */\nconst slotFocusableEl = ref<HTMLElement | null>(null)\n\nfunction syncSlotFocusable(): void {\n slotFocusableEl.value = slots.default\n ? triggerEl.value?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? null\n : null\n}\n\nonMounted(syncSlotFocusable)\nonUpdated(syncSlotFocusable)\n\nwatchEffect((onCleanup) => {\n const el = slotFocusableEl.value\n if (!el) return\n\n el.setAttribute('aria-describedby', tooltipId)\n onCleanup(() => el.removeAttribute('aria-describedby'))\n})\n\nconst wrapperTabindex = computed(() => (slotFocusableEl.value ? undefined : 0))\nconst wrapperDescribedBy = computed(() => (slotFocusableEl.value ? undefined : tooltipId))\n</script>\n\n<template>\n <span\n v-click-outside=\"dismissOnOutside\"\n data-gr-tooltip\n class=\"relative inline-flex\"\n >\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n :tabindex=\"wrapperTabindex\"\n :aria-describedby=\"wrapperDescribedBy\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n @touchstart.passive=\"onTouch\"\n >\n <slot>\n <GrIcon :size=\"triggerIconSize\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <!--\n Свой набор, а не общий `panelPopTransition`: подсказка появляется фейдом\n без масштабирования — она догоняет курсор, и рост от 95% читался бы как\n второй, лишний жест. В общей карте живут наборы с двумя и более\n потребителями, у этого он один.\n -->\n <transition\n enter-active-class=\"transition-opacity ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n v-bind=\"themeAttrs\"\n data-gr-tooltip-panel\n data-gr-overlay-root\n class=\"pointer-events-none rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :class=\"panelClass\"\n :style=\"floatingStyle\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { n as grTooltipSafelist, t as grTooltipConfig } from "../../chunks/config-BTmfQFwi.js";
2
- import { t as GrTooltip_default } from "./chunks/GrTooltip-BKFjqu0E.js";
2
+ import { t as GrTooltip_default } from "./chunks/GrTooltip-uYETdbCD.js";
3
3
  export { GrTooltip_default as GrTooltip, GrTooltip_default as default, grTooltipConfig, grTooltipSafelist };
@@ -603,7 +603,7 @@ var GrTreeSelect_default = /* @__PURE__ */ defineComponent({
603
603
  selected: selectedKeySet.value.has(node.key)
604
604
  }, () => [createElementVNode("div", _hoisted_7, [!checkboxMode.value ? (openBlock(), createElementBlock("span", _hoisted_8, [selectedKeySet.value.has(node.key) ? (openBlock(), createBlock(unref(check_default), {
605
605
  key: 0,
606
- class: "block h-4 w-4 text-[var(--gr-primary)]"
606
+ class: "block h-4 w-4 text-[var(--gr-primary-text)]"
607
607
  })) : createCommentVNode("", true)])) : createCommentVNode("", true), createElementVNode("span", _hoisted_9, toDisplayString(node.label), 1)])])]),
608
608
  _: 3
609
609
  }, 8, [
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrTreeSelect-CymO68Cv.js","names":["$slots"],"sources":["../../../../src/components/GrTreeSelect/GrTreeSelect.vue","../../../../src/components/GrTreeSelect/GrTreeSelect.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, ref, useId, useSlots, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useOverlayLayer } from '../../composables/useOverlayLayer'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrInputSize } from '../GrInput/GrInput.vue'\nimport { panelPopTransition } from '../shared/overlayTransition'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass, grTreeSelectStateClass, paddingX, trailingZoneWidth } from './grTreeSelectStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport interface GrTreeSelectEmits<T extends Record<string, any> = any> {\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: undefined,\n loading: false,\n invalid: false,\n readonly: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n multiple: false,\n showCheckbox: false,\n checkStrictly: false,\n clearable: false,\n open: undefined,\n name: undefined,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n virtual: false,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<GrTreeSelectEmits<T>>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n /** Содержимое панели, пока данные едут. */\n loading?: () => any\n /** Аддон слева от значения: иконка, код валюты, метка. */\n prefix?: () => any\n /** Аддон справа от значения, перед крестиком и шевроном. */\n suffix?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst treeId = `gr-tree-select-tree-${useId()}`\n\n// Контекст `GrFormField` + общий контракт форм-контрола.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\n\nfunction focus(): void {\n triggerEl.value?.focus()\n}\n\nfunction blur(): void {\n triggerEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, setOpen: setOpenState } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\nconst filterValue = ref('')\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\n/** Дерево рендерится только когда есть что показать — ссылаться иначе не на что. */\nconst hasTree = computed(() => !props.loading && (props.data?.length ?? 0) > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTreeSelect' })\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: resolvedSize.value,\n state: props.state,\n invalid: isInvalid.value,\n disabled: isDisabled.value,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrInputSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\nconst slots = useSlots()\n\nconst {\n hasPrefix,\n hasSuffix,\n prefixEl,\n suffixEl,\n prefixStyle,\n suffixStyle,\n fieldPadding: triggerStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => paddingX[resolvedSize.value],\n // Место под крестик и шеврон уже отдано классом `pr-9`, но инлайн-паддинг\n // перекрывает класс целиком — с суффиксом эту зону приходится вернуть числом.\n trailingReserve: () => (slots.suffix ? trailingZoneWidth : '0px'),\n})\n\n// Слот `value` рисуется поверх поля и обязан жить в тех же границах, что и\n// текст под ним. Без аддонов границы задаёт класс — стиль остаётся пустым.\nconst valueOverlayStyle = computed(() => ({\n left: hasPrefix.value ? triggerStyle.value.paddingLeft : undefined,\n right: hasSuffix.value ? triggerStyle.value.paddingRight : undefined,\n}))\n\nfunction setOpen(next: boolean) {\n if (isDisabled.value || isReadonly.value)\n return\n\n setOpenState(next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\n/**\n * Стек слоёв возвращает фокус на триггер сразу после закрытия, а у триггера\n * открытие висит на `focus`. Без флага `Escape` из дерева закрывал бы панель и\n * тем же движением открывал её заново.\n */\nlet suppressFocusOpen = false\n\nfunction closeDropdown(): void {\n suppressFocusOpen = true\n setOpen(false)\n setTimeout(() => {\n suppressFocusOpen = false\n }, 0)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\n// Клик по триггеру сам переключает панель через `@click`; флаг гасит открытие\n// по приходящему следом `focus`, иначе один клик открыл бы и тут же закрыл.\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger || suppressFocusOpen)\n return\n openDropdown()\n}\n\n// Возврат фокуса на триггер — из контракта слоя, а не руками через `nextTick`.\nuseOverlayLayer(open, closeDropdown, { root: panelEl })\n\nwatch(\n open,\n async (isOpen) => {\n if (!isOpen) {\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n return\n }\n\n // Подсветка дерева идёт за значением: панель могли открыть после того, как\n // значение сменили снаружи.\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\n/**\n * Отдаёт клавиатуру дереву. Панель под `v-show`, поэтому дерево уже в DOM, но\n * скрытый элемент сфокусировать нельзя — сначала открытие, потом кадр, потом\n * фокус. Дальше работают клавиши `GrTree`, а `Escape` возвращает фокус на\n * триггер силами общего стека слоёв.\n */\nasync function focusTree(): Promise<boolean> {\n if (!open.value)\n return false\n\n await nextTick()\n return treeRef.value?.focus() ?? false\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n return\n }\n\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n\n // При `filterable` фокус уходит в поле поиска (см. watch на `open`), и\n // дальше в дерево ведёт стрелка уже оттуда.\n if (!props.filterable)\n void focusTree()\n }\n}\n\n/** Стрелка из поля поиска — единственный путь из фильтра в дерево. */\nfunction onFilterKeydown(e: KeyboardEvent): void {\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp')\n return\n\n e.preventDefault()\n void focusTree()\n}\n\n/** `Tab` уводит из панели — оставлять её открытой за спиной незачем. */\nfunction onPanelKeydown(e: KeyboardEvent): void {\n if (e.key === 'Tab')\n closeDropdown()\n}\n\n// Чекбоксы осмысленны только в множественном выборе: одиночный уже подсвечен\n// текущим узлом.\nconst checkboxMode = computed(() => props.multiple && props.showCheckbox)\n\n// В режиме чекбоксов каскад по родителям и детям считает `GrTree`, а не копия\n// его логики здесь: значение приходит обратно одним `update:checkedKeys`.\nfunction onCheckedKeys(keys: GrTreeKey[]): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n emitModel([...keys])\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (isDisabled.value)\n return\n\n if (checkboxMode.value) {\n // Клик по строке — тот же переключатель отметки, что и сам чекбокс;\n // значение придёт через `update:checkedKeys`, поэтому здесь не эмитим.\n treeRef.value?.setChecked(node.key, !selectedKeySet.value.has(node.key))\n return\n }\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-tree-select\n class=\"relative\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: hidden на каждый выбранный ключ. -->\n <template v-if=\"name\">\n <input\n v-for=\"key in selectedKeys\"\n :key=\"`hidden-${String(key)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(key)\"\n >\n </template>\n <div class=\"relative\">\n <div\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-tree-select-prefix\n class=\"absolute inset-y-px left-px flex items-center justify-center rounded-l-[var(--gr-radius-control)] border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"fieldId\"\n ref=\"triggerEl\"\n data-testid=\"gr-tree-select-trigger\"\n data-gr-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n aria-haspopup=\"tree\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-controls=\"open && hasTree ? treeId : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-label=\"ariaLabel\"\n class=\"w-full rounded-[var(--gr-radius-control)] border placeholder:text-[var(--gr-muted-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n :style=\"triggerStyle\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection && !disabled && !isReadonly\"\n data-testid=\"gr-tree-select-clear\"\n data-gr-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)] hover:bg-[color-mix(in_srgb,var(--gr-muted)_25%,transparent)]\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <IconX class=\"inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-tree-select-chevron\"\n data-gr-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown\n class=\"inline-block h-4 w-4 transition-transform duration-[var(--gr-duration-fast)]\"\n :class=\"open ? 'rotate-180' : ''\"\n aria-hidden=\"true\"\n />\n </span>\n\n <div\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-tree-select-suffix\n class=\"absolute inset-y-px right-9 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n :style=\"valueOverlayStyle\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n :enter-active-class=\"panelPopTransition.enter\"\n :enter-from-class=\"panelPopTransition.enterFrom\"\n :enter-to-class=\"panelPopTransition.enterTo\"\n :leave-active-class=\"panelPopTransition.leave\"\n :leave-from-class=\"panelPopTransition.leaveFrom\"\n :leave-to-class=\"panelPopTransition.leaveTo\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-tree-select-panel\"\n v-bind=\"themeAttrs\"\n data-gr-tree-select-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n @keydown=\"onPanelKeydown\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--gr-brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"gr-tree-select-filter\"\n data-gr-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n @keydown=\"onFilterKeydown\"\n />\n </div>\n\n <!--\n Скроллер один. При виртуализации его берёт на себя дерево: своя\n высота нужна ему, чтобы посчитать окно, а вложенный скроллер поверх\n дал бы вторую полосу прокрутки на том же списке.\n -->\n <div\n class=\"p-1\"\n :class=\"virtual ? '' : 'overflow-auto'\"\n :style=\"virtual ? undefined : { maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div\n v-if=\"loading\"\n data-gr-tree-select-loading\n role=\"status\"\n :class=\"grTreeSelectStateClass\"\n >\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ t('gr.treeSelect.loading', 'Loading…') }}</span>\n </slot>\n </div>\n\n <div v-else-if=\"(data?.length ?? 0) === 0\" :class=\"grTreeSelectStateClass\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n :id=\"treeId\"\n ref=\"treeRef\"\n :size=\"resolvedSize\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n :virtual=\"virtual\"\n :max-height=\"dropdownMaxHeight\"\n :show-checkbox=\"checkboxMode\"\n :check-strictly=\"checkStrictly\"\n :checked-keys=\"selectedKeys\"\n @node-click=\"onNodeClick\"\n @update:checked-keys=\"onCheckedKeys\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <!-- Чекбокс дерева уже показывает отметку: вторая галочка\n рядом читалась бы как ошибка. -->\n <span\n v-if=\"!checkboxMode\"\n data-gr-tree-select-check\n class=\"inline-block h-4 w-4 shrink-0\"\n aria-hidden=\"true\"\n >\n <IconCheck v-if=\"selectedKeySet.has(node.key)\" class=\"block h-4 w-4 text-[var(--gr-primary-text)]\" />\n </span>\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, ref, useId, useSlots, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useOverlayLayer } from '../../composables/useOverlayLayer'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrInputSize } from '../GrInput/GrInput.vue'\nimport { panelPopTransition } from '../shared/overlayTransition'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass, grTreeSelectStateClass, paddingX, trailingZoneWidth } from './grTreeSelectStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport interface GrTreeSelectEmits<T extends Record<string, any> = any> {\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: undefined,\n loading: false,\n invalid: false,\n readonly: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n multiple: false,\n showCheckbox: false,\n checkStrictly: false,\n clearable: false,\n open: undefined,\n name: undefined,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n virtual: false,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<GrTreeSelectEmits<T>>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n /** Содержимое панели, пока данные едут. */\n loading?: () => any\n /** Аддон слева от значения: иконка, код валюты, метка. */\n prefix?: () => any\n /** Аддон справа от значения, перед крестиком и шевроном. */\n suffix?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst treeId = `gr-tree-select-tree-${useId()}`\n\n// Контекст `GrFormField` + общий контракт форм-контрола.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\n\nfunction focus(): void {\n triggerEl.value?.focus()\n}\n\nfunction blur(): void {\n triggerEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, setOpen: setOpenState } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\nconst filterValue = ref('')\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\n/** Дерево рендерится только когда есть что показать — ссылаться иначе не на что. */\nconst hasTree = computed(() => !props.loading && (props.data?.length ?? 0) > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTreeSelect' })\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: resolvedSize.value,\n state: props.state,\n invalid: isInvalid.value,\n disabled: isDisabled.value,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrInputSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\nconst slots = useSlots()\n\nconst {\n hasPrefix,\n hasSuffix,\n prefixEl,\n suffixEl,\n prefixStyle,\n suffixStyle,\n fieldPadding: triggerStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => paddingX[resolvedSize.value],\n // Место под крестик и шеврон уже отдано классом `pr-9`, но инлайн-паддинг\n // перекрывает класс целиком — с суффиксом эту зону приходится вернуть числом.\n trailingReserve: () => (slots.suffix ? trailingZoneWidth : '0px'),\n})\n\n// Слот `value` рисуется поверх поля и обязан жить в тех же границах, что и\n// текст под ним. Без аддонов границы задаёт класс — стиль остаётся пустым.\nconst valueOverlayStyle = computed(() => ({\n left: hasPrefix.value ? triggerStyle.value.paddingLeft : undefined,\n right: hasSuffix.value ? triggerStyle.value.paddingRight : undefined,\n}))\n\nfunction setOpen(next: boolean) {\n if (isDisabled.value || isReadonly.value)\n return\n\n setOpenState(next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\n/**\n * Стек слоёв возвращает фокус на триггер сразу после закрытия, а у триггера\n * открытие висит на `focus`. Без флага `Escape` из дерева закрывал бы панель и\n * тем же движением открывал её заново.\n */\nlet suppressFocusOpen = false\n\nfunction closeDropdown(): void {\n suppressFocusOpen = true\n setOpen(false)\n setTimeout(() => {\n suppressFocusOpen = false\n }, 0)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\n// Клик по триггеру сам переключает панель через `@click`; флаг гасит открытие\n// по приходящему следом `focus`, иначе один клик открыл бы и тут же закрыл.\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger || suppressFocusOpen)\n return\n openDropdown()\n}\n\n// Возврат фокуса на триггер — из контракта слоя, а не руками через `nextTick`.\nuseOverlayLayer(open, closeDropdown, { root: panelEl })\n\nwatch(\n open,\n async (isOpen) => {\n if (!isOpen) {\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n return\n }\n\n // Подсветка дерева идёт за значением: панель могли открыть после того, как\n // значение сменили снаружи.\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\n/**\n * Отдаёт клавиатуру дереву. Панель под `v-show`, поэтому дерево уже в DOM, но\n * скрытый элемент сфокусировать нельзя — сначала открытие, потом кадр, потом\n * фокус. Дальше работают клавиши `GrTree`, а `Escape` возвращает фокус на\n * триггер силами общего стека слоёв.\n */\nasync function focusTree(): Promise<boolean> {\n if (!open.value)\n return false\n\n await nextTick()\n return treeRef.value?.focus() ?? false\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value)\n return\n\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n return\n }\n\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n\n // При `filterable` фокус уходит в поле поиска (см. watch на `open`), и\n // дальше в дерево ведёт стрелка уже оттуда.\n if (!props.filterable)\n void focusTree()\n }\n}\n\n/** Стрелка из поля поиска — единственный путь из фильтра в дерево. */\nfunction onFilterKeydown(e: KeyboardEvent): void {\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp')\n return\n\n e.preventDefault()\n void focusTree()\n}\n\n/** `Tab` уводит из панели — оставлять её открытой за спиной незачем. */\nfunction onPanelKeydown(e: KeyboardEvent): void {\n if (e.key === 'Tab')\n closeDropdown()\n}\n\n// Чекбоксы осмысленны только в множественном выборе: одиночный уже подсвечен\n// текущим узлом.\nconst checkboxMode = computed(() => props.multiple && props.showCheckbox)\n\n// В режиме чекбоксов каскад по родителям и детям считает `GrTree`, а не копия\n// его логики здесь: значение приходит обратно одним `update:checkedKeys`.\nfunction onCheckedKeys(keys: GrTreeKey[]): void {\n if (isDisabled.value || isReadonly.value)\n return\n\n emitModel([...keys])\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (isDisabled.value)\n return\n\n if (checkboxMode.value) {\n // Клик по строке — тот же переключатель отметки, что и сам чекбокс;\n // значение придёт через `update:checkedKeys`, поэтому здесь не эмитим.\n treeRef.value?.setChecked(node.key, !selectedKeySet.value.has(node.key))\n return\n }\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-tree-select\n class=\"relative\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: hidden на каждый выбранный ключ. -->\n <template v-if=\"name\">\n <input\n v-for=\"key in selectedKeys\"\n :key=\"`hidden-${String(key)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(key)\"\n >\n </template>\n <div class=\"relative\">\n <div\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-tree-select-prefix\n class=\"absolute inset-y-px left-px flex items-center justify-center rounded-l-[var(--gr-radius-control)] border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"fieldId\"\n ref=\"triggerEl\"\n data-testid=\"gr-tree-select-trigger\"\n data-gr-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n aria-haspopup=\"tree\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-controls=\"open && hasTree ? treeId : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-label=\"ariaLabel\"\n class=\"w-full rounded-[var(--gr-radius-control)] border placeholder:text-[var(--gr-muted-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n :style=\"triggerStyle\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection && !disabled && !isReadonly\"\n data-testid=\"gr-tree-select-clear\"\n data-gr-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)] hover:bg-[color-mix(in_srgb,var(--gr-muted)_25%,transparent)]\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <IconX class=\"inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-tree-select-chevron\"\n data-gr-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown\n class=\"inline-block h-4 w-4 transition-transform duration-[var(--gr-duration-fast)]\"\n :class=\"open ? 'rotate-180' : ''\"\n aria-hidden=\"true\"\n />\n </span>\n\n <div\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-tree-select-suffix\n class=\"absolute inset-y-px right-9 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n :style=\"valueOverlayStyle\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n :enter-active-class=\"panelPopTransition.enter\"\n :enter-from-class=\"panelPopTransition.enterFrom\"\n :enter-to-class=\"panelPopTransition.enterTo\"\n :leave-active-class=\"panelPopTransition.leave\"\n :leave-from-class=\"panelPopTransition.leaveFrom\"\n :leave-to-class=\"panelPopTransition.leaveTo\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-tree-select-panel\"\n v-bind=\"themeAttrs\"\n data-gr-tree-select-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n @keydown=\"onPanelKeydown\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--gr-brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"gr-tree-select-filter\"\n data-gr-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n @keydown=\"onFilterKeydown\"\n />\n </div>\n\n <!--\n Скроллер один. При виртуализации его берёт на себя дерево: своя\n высота нужна ему, чтобы посчитать окно, а вложенный скроллер поверх\n дал бы вторую полосу прокрутки на том же списке.\n -->\n <div\n class=\"p-1\"\n :class=\"virtual ? '' : 'overflow-auto'\"\n :style=\"virtual ? undefined : { maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div\n v-if=\"loading\"\n data-gr-tree-select-loading\n role=\"status\"\n :class=\"grTreeSelectStateClass\"\n >\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ t('gr.treeSelect.loading', 'Loading…') }}</span>\n </slot>\n </div>\n\n <div v-else-if=\"(data?.length ?? 0) === 0\" :class=\"grTreeSelectStateClass\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n :id=\"treeId\"\n ref=\"treeRef\"\n :size=\"resolvedSize\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n :virtual=\"virtual\"\n :max-height=\"dropdownMaxHeight\"\n :show-checkbox=\"checkboxMode\"\n :check-strictly=\"checkStrictly\"\n :checked-keys=\"selectedKeys\"\n @node-click=\"onNodeClick\"\n @update:checked-keys=\"onCheckedKeys\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <!-- Чекбокс дерева уже показывает отметку: вторая галочка\n рядом читалась бы как ошибка. -->\n <span\n v-if=\"!checkboxMode\"\n data-gr-tree-select-check\n class=\"inline-block h-4 w-4 shrink-0\"\n aria-hidden=\"true\"\n >\n <IconCheck v-if=\"selectedKeySet.has(node.key)\" class=\"block h-4 w-4 text-[var(--gr-primary-text)]\" />\n </span>\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { n as grTreeSelectSafelist, t as grTreeSelectConfig } from "../../chunks/config-T3-DMXYm.js";
2
- import { t as GrTreeSelect_default } from "./chunks/GrTreeSelect-B1GdEn5K.js";
2
+ import { t as GrTreeSelect_default } from "./chunks/GrTreeSelect-CymO68Cv.js";
3
3
  export { GrTreeSelect_default as GrTreeSelect, GrTreeSelect_default as default, grTreeSelectConfig, grTreeSelectSafelist };
@@ -1,6 +1,6 @@
1
1
  import { t as ensurePortalRoot } from "../chunks/portalRoot-CVhCgdG6.js";
2
2
  import { n as markInert, t as inertableOutside } from "../chunks/inert-DIHCCn_1.js";
3
- import { t as GrLoading_default } from "../components/GrLoading/chunks/GrLoading-DDD8WAIH.js";
3
+ import { t as GrLoading_default } from "../components/GrLoading/chunks/GrLoading-CMD3Ri9P.js";
4
4
  import { createVNode, render } from "vue";
5
5
  //#region src/directives/loading.ts
6
6
  function isHTMLElement(value) {
@@ -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-Dds_519x.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-NtErwzYB.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-Dds_519x.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-NtErwzYB.js";
2
2
  export { GRANULARITY_PROVIDER_ID, createGranularityProvider, granularityProvider as default, granularityProvider, granularityComponentConfigs, granularityDefaultThemes, granularityThemeNames };
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ import { vClickOutside } from "./directives/clickOutside.js";
10
10
  import { p as FileValidationError } from "./chunks/fileValidation-D5TFBhbv.js";
11
11
  import { vDropzone } from "./directives/dropzone.js";
12
12
  import { vHotkey } from "./directives/hotkey.js";
13
- import { t as GrIcon_default } from "./components/GrIcon/chunks/GrIcon-Bn1XEriL.js";
14
- import { t as GrLoading_default } from "./components/GrLoading/chunks/GrLoading-DDD8WAIH.js";
13
+ import { t as GrIcon_default } from "./components/GrIcon/chunks/GrIcon-Dmf0c9ba.js";
14
+ import { t as GrLoading_default } from "./components/GrLoading/chunks/GrLoading-CMD3Ri9P.js";
15
15
  import { createLoading, vLoading } from "./directives/loading.js";
16
16
  import { useFloating } from "./composables/useFloating.js";
17
17
  import { useOverlayLayer } from "./composables/useOverlayLayer.js";
@@ -23,7 +23,7 @@ import { useComboboxNavigation } from "./composables/useComboboxNavigation.js";
23
23
  import { useRovingFocus } from "./composables/useRovingFocus.js";
24
24
  import { t as GrChip_default } from "./components/GrChip/chunks/GrChip-Df4orxRH.js";
25
25
  import { n as grAutocompleteSafelist, t as grAutocompleteConfig } from "./chunks/config-vYMJ3Cr1.js";
26
- import { t as GrAutocomplete_default } from "./components/GrAutocomplete/chunks/GrAutocomplete-BwhUTqmG.js";
26
+ import { t as GrAutocomplete_default } from "./components/GrAutocomplete/chunks/GrAutocomplete-CaHys_A_.js";
27
27
  import { t as GrSkeleton_default } from "./components/GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js";
28
28
  import { n as GrAvatar_default, t as GrAvatarGroup_default } from "./components/GrAvatar/chunks/GrAvatar-CNmLmgcr.js";
29
29
  import { c as initialsFrom, n as grAvatarSafelist, t as grAvatarConfig } from "./chunks/config-B3J0alyY.js";
@@ -34,9 +34,9 @@ import { n as grBadgeWrapSafelist, o as formatBadgeValue, t as grBadgeWrapConfig
34
34
  import { t as GrBadgeWrap_default } from "./components/GrBadgeWrap/chunks/GrBadgeWrap-B-A3PRkE.js";
35
35
  import { n as grBottomNavSafelist, t as grBottomNavConfig } from "./chunks/config-DM0lsduD.js";
36
36
  import { t as GrBottomNav_default } from "./components/GrBottomNav/chunks/GrBottomNav-wtFatLjB.js";
37
- import { t as GrLink_default } from "./components/GrLink/chunks/GrLink-DAGCFYO7.js";
37
+ import { t as GrLink_default } from "./components/GrLink/chunks/GrLink-DYXw3W9w.js";
38
38
  import { g as resolveBreadcrumbsLayout, h as resolveBreadcrumbsFit, n as grBreadcrumbsSafelist, t as grBreadcrumbsConfig } from "./chunks/config-C0Eu8jpG.js";
39
- import { t as GrBreadcrumbs_default } from "./components/GrBreadcrumbs/chunks/GrBreadcrumbs-BKOvl8W1.js";
39
+ import { t as GrBreadcrumbs_default } from "./components/GrBreadcrumbs/chunks/GrBreadcrumbs-DRDNan_n.js";
40
40
  import { n as useGrButtonGroup } from "./chunks/context-BfIQv7m1.js";
41
41
  import { t as GrButton_default } from "./components/GrButton/chunks/GrButton-Bw2AH4uX.js";
42
42
  import { t as grButtonSafelist } from "./chunks/safelist-BOq5Li7p.js";
@@ -59,8 +59,8 @@ import { useAnnouncer } from "./composables/useAnnouncer.js";
59
59
  import { t as GrCodeBlock_default } from "./components/GrCodeBlock/chunks/GrCodeBlock-Dkvkr91t.js";
60
60
  import { n as grCodeBlockSafelist, t as grCodeBlockConfig } from "./chunks/config-DolULhjJ.js";
61
61
  import { n as grCollapseSafelist, t as grCollapseConfig } from "./chunks/config-BoUI0t-t.js";
62
- import { n as GrCollapse_default, t as GrCollapseItem_default } from "./components/GrCollapse/chunks/GrCollapse-CytiA2Sx.js";
63
- import { n as grIconSafelist, t as grIconConfig } from "./chunks/config-_jO3q1Le.js";
62
+ import { n as GrCollapse_default, t as GrCollapseItem_default } from "./components/GrCollapse/chunks/GrCollapse-Bw9_JatF.js";
63
+ import { n as grIconSafelist, t as grIconConfig } from "./chunks/config-3bQeQvqd.js";
64
64
  import { t as GrInput_default } from "./components/GrInput/chunks/GrInput-BqqoEl1b.js";
65
65
  import { useFocusTrap } from "./composables/useFocusTrap.js";
66
66
  import { t as GrPopover_default } from "./components/GrPopover/chunks/GrPopover-CofG-Tfe.js";
@@ -84,7 +84,7 @@ import { t as GrContextMenu_default } from "./components/GrContextMenu/chunks/Gr
84
84
  import { t as grContextMenuConfig } from "./chunks/config-CPSxM7XE.js";
85
85
  import { t as GrTable_default } from "./components/GrTable/chunks/GrTable-Zcdz2St_.js";
86
86
  import { t as useDragSort } from "./chunks/useDragSort-0l47h4yb.js";
87
- import { t as GrDataTable_default } from "./components/GrDataTable/chunks/GrDataTable-DU00V44y.js";
87
+ import { t as GrDataTable_default } from "./components/GrDataTable/chunks/GrDataTable-C8a9_nCg.js";
88
88
  import { n as grDataTableSafelist, t as grDataTableConfig } from "./chunks/config-DeyT9dJl.js";
89
89
  import { t as GrDelta_default } from "./components/GrDelta/chunks/GrDelta-BZwkgQW-.js";
90
90
  import { n as grValueSafelist, t as grValueConfig } from "./chunks/config-Z5LeUXys.js";
@@ -105,33 +105,33 @@ import { t as grDialogServiceConfig } from "./chunks/config-CjXjhphs.js";
105
105
  import { n as grDividerSafelist, t as grDividerConfig } from "./chunks/config-D82r782v.js";
106
106
  import { t as GrDivider_default } from "./components/GrDivider/chunks/GrDivider-BXavGcMX.js";
107
107
  import { n as grDrawerSafelist, t as grDrawerConfig } from "./chunks/config-ChPg-BQv.js";
108
- import { t as GrDrawer_default } from "./components/GrDrawer/chunks/GrDrawer-BCreL-eq.js";
108
+ import { t as GrDrawer_default } from "./components/GrDrawer/chunks/GrDrawer-B7PiAcbU.js";
109
109
  import { t as GrDropdown_default } from "./components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js";
110
110
  import { n as grDropdownSafelist, t as grDropdownConfig } from "./chunks/config-uzUuosv7.js";
111
111
  import "./components/GrDropdown/index.js";
112
112
  import { n as GrDropdownMenuColumn_default, r as GrDropdownMenu_default, t as GrDropdownMenuColumns_default } from "./components/GrDropdownMenu/chunks/GrDropdownMenu-Cg7qHV9J.js";
113
113
  import { n as grDropdownMenuSafelist, t as grDropdownMenuConfig } from "./chunks/config-_jbgsFpw.js";
114
- import { t as GrEmptyState_default } from "./components/GrEmptyState/chunks/GrEmptyState-D4T5pzXA.js";
114
+ import { t as GrEmptyState_default } from "./components/GrEmptyState/chunks/GrEmptyState-RqsWvgI9.js";
115
115
  import { n as grEmptyStateSafelist, t as grEmptyStateConfig } from "./chunks/config-CWtfxkM9.js";
116
116
  import { n as fileKindOf, r as isPreviewableKind, t as GrFilePreview_default } from "./components/GrFilePreview/chunks/GrFilePreview-C9Z7hMK8.js";
117
117
  import { n as grFilePreviewSafelist, t as grFilePreviewConfig } from "./chunks/config-BaWANzzB.js";
118
118
  import { t as GrProgressBar_default } from "./components/GrProgressBar/chunks/GrProgressBar-hszUOC8f.js";
119
- import { n as GrUploadAbortError, r as GrUploadHttpError, t as GrFileUpload_default } from "./components/GrFileUpload/chunks/GrFileUpload-FjbaYGjL.js";
119
+ import { n as GrUploadAbortError, r as GrUploadHttpError, t as GrFileUpload_default } from "./components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js";
120
120
  import { n as grFileUploadSafelist, t as grFileUploadConfig } from "./chunks/config-BlJa1Mj9.js";
121
121
  import { t as GrForm_default } from "./components/GrForm/chunks/GrForm-CrnfCsgj.js";
122
122
  import { t as grFormConfig } from "./chunks/config-Droqn55j.js";
123
123
  import { n as grFormFieldSafelist, t as grFormFieldConfig } from "./chunks/config-B0uvcTPH.js";
124
124
  import "./components/GrFormField/index.js";
125
- import { t as GrFormFile_default } from "./components/GrFormFile/chunks/GrFormFile-ChdKlN83.js";
125
+ import { t as GrFormFile_default } from "./components/GrFormFile/chunks/GrFormFile-CYtXU9HV.js";
126
126
  import { n as grFormFileSafelist, t as grFormFileConfig } from "./chunks/config-BcXX7S7B.js";
127
127
  import { n as grFormSectionSafelist, t as grFormSectionConfig } from "./chunks/config-BEO7brpO.js";
128
128
  import { t as GrFormSection_default } from "./components/GrFormSection/chunks/GrFormSection-ejieOg9H.js";
129
- import { t as GrImageViewer_default } from "./components/GrImageViewer/chunks/GrImageViewer-COUcpOgR.js";
129
+ import { t as GrImageViewer_default } from "./components/GrImageViewer/chunks/GrImageViewer-CyVN6Zpc.js";
130
130
  import { n as grImageViewerSafelist, t as grImageViewerConfig } from "./chunks/config-BDSaD-5p.js";
131
131
  import { n as grInputSafelist, t as grInputConfig } from "./chunks/config-DO8mjeIo.js";
132
132
  import "./components/GrInput/index.js";
133
133
  import { n as grInputTagSafelist, t as grInputTagConfig } from "./chunks/config-BqT6PMzO.js";
134
- import { t as GrInputTag_default } from "./components/GrInputTag/chunks/GrInputTag-LP3smqOv.js";
134
+ import { t as GrInputTag_default } from "./components/GrInputTag/chunks/GrInputTag-5wVcB0zG.js";
135
135
  import { t as GrTree_default } from "./components/GrTree/chunks/GrTree-DC89I4AP.js";
136
136
  import { t as GrJsonViewer_default } from "./components/GrJsonViewer/chunks/GrJsonViewer-CgwjoUJt.js";
137
137
  import { n as grJsonViewerSafelist, t as grJsonViewerConfig } from "./chunks/config-DpAYidyY2.js";
@@ -145,13 +145,13 @@ import { n as grLoadingSafelist, t as grLoadingConfig } from "./chunks/config-Bj
145
145
  import "./components/GrLoading/index.js";
146
146
  import { n as grModalSafelist, t as grModalConfig } from "./chunks/config-mlheIMtV.js";
147
147
  import "./components/GrModal/index.js";
148
- import { t as GrNavbar_default } from "./components/GrNavbar/chunks/GrNavbar-DhswlYyh.js";
148
+ import { t as GrNavbar_default } from "./components/GrNavbar/chunks/GrNavbar-C5EjMKaC.js";
149
149
  import { n as grNavbarSafelist, t as grNavbarConfig } from "./chunks/config-2ZHRsdOc.js";
150
150
  import { n as grNumberInputSafelist, t as grNumberInputConfig } from "./chunks/config-MmPym-Z72.js";
151
- import { t as GrNumberInput_default } from "./components/GrNumberInput/chunks/GrNumberInput-Daud9Jdm.js";
152
- import { t as GrSelect_default } from "./components/GrSelect/chunks/GrSelect-D-KU9Lx_.js";
151
+ import { t as GrNumberInput_default } from "./components/GrNumberInput/chunks/GrNumberInput-DQ59YICu.js";
152
+ import { t as GrSelect_default } from "./components/GrSelect/chunks/GrSelect-DKoQu3w9.js";
153
153
  import { n as grPaginationSafelist, t as grPaginationConfig } from "./chunks/config-CjKpUcmy.js";
154
- import { t as GrPagination_default } from "./components/GrPagination/chunks/GrPagination-kQhWlfs-.js";
154
+ import { t as GrPagination_default } from "./components/GrPagination/chunks/GrPagination-CX9ZV8mR.js";
155
155
  import { n as grPopoverSafelist, t as grPopoverConfig } from "./chunks/config-BTwYMQaz2.js";
156
156
  import "./components/GrPopover/index.js";
157
157
  import { n as grProgressBarSafelist, r as GR_TONES, t as grProgressBarConfig } from "./chunks/config-DWL81-aM.js";
@@ -173,7 +173,7 @@ import { n as grSegmentedSafelist, t as grSegmentedConfig } from "./chunks/confi
173
173
  import { n as grSelectSafelist, t as grSelectConfig } from "./chunks/config-DnF472Ox.js";
174
174
  import "./components/GrSelect/index.js";
175
175
  import { n as grSidebarSafelist, t as grSidebarConfig } from "./chunks/config-DNDgp9bB.js";
176
- import { n as GrSidebarItem_default, r as GrSidebar_default, t as GrSidebarGroup_default } from "./components/GrSidebar/chunks/GrSidebar-DxPhIiWs.js";
176
+ import { n as GrSidebarItem_default, r as GrSidebar_default, t as GrSidebarGroup_default } from "./components/GrSidebar/chunks/GrSidebar-B8EQIlfW.js";
177
177
  import { t as grSkeletonConfig } from "./chunks/config-8Pcutu1j.js";
178
178
  import "./components/GrSkeleton/index.js";
179
179
  import { n as grSliderSafelist, t as grSliderConfig } from "./chunks/config-tiUtEn4Q.js";
@@ -200,12 +200,12 @@ import { n as GrTimeline_default, t as GrTimelineItem_default } from "./componen
200
200
  import { n as grTimelineSafelist, t as grTimelineConfig } from "./chunks/config-Bd7QfiGj.js";
201
201
  import { GRANULARITY_TOAST_STATE, granularityToastPlugin, useToast } from "./composables/useToast.js";
202
202
  import { n as grToasterSafelist, t as grToasterConfig } from "./chunks/config-B5SBUa6u.js";
203
- import { t as GrToaster_default } from "./components/GrToaster/chunks/GrToaster-CUi3ldJ0.js";
203
+ import { t as GrToaster_default } from "./components/GrToaster/chunks/GrToaster-Dp7-4B8k.js";
204
204
  import { n as grTooltipSafelist, t as grTooltipConfig } from "./chunks/config-BTmfQFwi.js";
205
- import { t as GrTooltip_default } from "./components/GrTooltip/chunks/GrTooltip-BKFjqu0E.js";
205
+ import { t as GrTooltip_default } from "./components/GrTooltip/chunks/GrTooltip-uYETdbCD.js";
206
206
  import { n as grTreeSafelist, t as grTreeConfig } from "./chunks/config-CDat1wXf.js";
207
207
  import { n as grTreeSelectSafelist, t as grTreeSelectConfig } from "./chunks/config-T3-DMXYm.js";
208
- import { t as GrTreeSelect_default } from "./components/GrTreeSelect/chunks/GrTreeSelect-B1GdEn5K.js";
208
+ import { t as GrTreeSelect_default } from "./components/GrTreeSelect/chunks/GrTreeSelect-CymO68Cv.js";
209
209
  import "./composables/useGrComponentConfig.js";
210
210
  import "./composables/useGranularityTranslations.js";
211
211
  import { GRANULARITY_THEME_STATE, granularityThemePlugin, initThemeEarly, useTheme } from "./composables/useTheme.js";
@@ -10,10 +10,13 @@ export type GrIconTone = GrTone | 'current';
10
10
  */
11
11
  export declare const iconSizeVarBySize: Record<GrIconSize, string>;
12
12
  /**
13
- * Тон → токен **текста**, а не насыщенный тон: правило пакета запрещает
14
- * насыщенный цвет как цвет текста (контраст на `-light`-подложках падает до
15
- * 2:1). Отображение повторяет `GrAlert/grAlertStyles.ts`: у `primary` и
16
- * `neutral` пары `-text` нет, поэтому берутся ближайшие семантические роли.
13
+ * Тон → токен **текста**, а не насыщенный тон: насыщенный не держит контраст в
14
+ * роли переднего плана. Причём каждому тону найдётся тема, где он провален:
15
+ * `--gr-primary` даёт 5.74 на светлой и 3.70 на тёмной, `--gr-success` — 2.32
16
+ * на светлой. Парные `-text` проходят AA везде, минимум 5.46.
17
+ *
18
+ * У `neutral` пары `-text` нет — для него роль приглушённого текста и есть
19
+ * ближайшая семантика.
17
20
  */
18
21
  export declare const iconToneClass: Record<GrTone, string>;
19
22
  /** `gr-icon` — не утилита, а зацепка для глобального стиля компонента. */
@@ -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.29.0",
4
+ "version": "0.29.1",
5
5
  "framework": "vue",
6
6
  "js-types-syntax": "typescript",
7
7
  "description-markup": "markdown",
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.29.0",
4
+ "version": "0.29.1",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "Evgeniy Fureev",
@@ -607,7 +607,7 @@
607
607
  "devDependencies": {
608
608
  "@antfu/eslint-config": "^9.1.0",
609
609
  "@feugene/fint-i18n": "^0.6.0",
610
- "@feugene/granularity-test-kit": "^0.5.0",
610
+ "@feugene/granularity-test-kit": "^0.6.0",
611
611
  "@feugene/unocss-preset-granular": "^0.10.1",
612
612
  "@floating-ui/dom": "^1.8.0",
613
613
  "@iconify-json/lucide": "^1.2.117",
@@ -1 +0,0 @@
1
- {"version":3,"file":"grIconStyles-B6BRqUld.js","names":[],"sources":["../../src/components/GrIcon/grIconStyles.ts"],"sourcesContent":["import { splitClassTokens } from '../shared/classTokens'\nimport type { GrComponentSize } from '../shared/sizes'\nimport type { GrTone } from '../shared/tones'\n\nexport type GrIconSize = GrComponentSize\n\n/** Цвет иконки: тон палитры либо наследование от родителя. */\nexport type GrIconTone = GrTone | 'current'\n\n/**\n * Шкала размеров — токенами, а не числами: иконка обязана масштабироваться\n * вместе с темой, а не вместе с пересборкой пакета. Произвольный размер\n * задаётся числом прямо на компоненте, это escape-hatch мимо шкалы.\n */\nexport const iconSizeVarBySize: Record<GrIconSize, string> = {\n xs: 'var(--gr-icon-size-xs)',\n sm: 'var(--gr-icon-size-sm)',\n md: 'var(--gr-icon-size-md)',\n lg: 'var(--gr-icon-size-lg)',\n}\n\n/**\n * Тон → токен **текста**, а не насыщенный тон: правило пакета запрещает\n * насыщенный цвет как цвет текста (контраст на `-light`-подложках падает до\n * 2:1). Отображение повторяет `GrAlert/grAlertStyles.ts`: у `primary` и\n * `neutral` пары `-text` нет, поэтому берутся ближайшие семантические роли.\n */\nexport const iconToneClass: Record<GrTone, string> = {\n primary: 'text-[var(--gr-primary)]',\n neutral: 'text-[var(--gr-muted-fg)]',\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\n/** `gr-icon` — не утилита, а зацепка для глобального стиля компонента. */\nexport const iconHookClass = 'gr-icon'\nexport const iconUtilityClass = 'inline-flex items-center justify-center align-middle'\nexport const iconBaseClass = `${iconHookClass} ${iconUtilityClass}`\nexport const iconSpinClass = 'animate-spin'\n\nexport function grIconToneClass(tone: GrIconTone): string {\n return tone === 'current' ? '' : iconToneClass[tone]\n}\n\n// В safelist идут только утилиты: `gr-icon` CSS не порождает — он лишь селектор\n// собственного стиля компонента, и гейт `documentedConfig` справедливо считает\n// такую запись мёртвой.\nexport const grIconClassTokens = [\n ...splitClassTokens(iconUtilityClass),\n ...splitClassTokens(iconSpinClass),\n ...Object.values(iconToneClass).flatMap(splitClassTokens),\n]\n"],"mappings":";;;;;;;AAcA,IAAa,oBAAgD;CAC3D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;;;;;AAQA,IAAa,gBAAwC;CACnD,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;CACR,MAAM;CACN,OAAO;CACP,OAAO;AACT;;AAGA,IAAa,gBAAgB;AAC7B,IAAa,mBAAmB;AAChC,IAAa,gBAAgB,GAAG,cAAc,GAAG;AACjD,IAAa,gBAAgB;AAE7B,SAAgB,gBAAgB,MAA0B;CACxD,OAAO,SAAS,YAAY,KAAK,cAAc;AACjD;AAKA,IAAa,oBAAoB;CAC/B,GAAG,iBAAiB,gBAAgB;CACpC,GAAG,iBAAiB,aAAa;CACjC,GAAG,OAAO,OAAO,aAAa,CAAC,CAAC,QAAQ,gBAAgB;AAC1D"}