@feugene/granularity 0.31.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/chunks/{config-Dv_r92GL.js → config-2vrOFn2f.js} +2 -2
  2. package/dist/chunks/{config-Dv_r92GL.js.map → config-2vrOFn2f.js.map} +1 -1
  3. package/dist/chunks/{config-g8-JGZ_Y.js → config-BscVxZR3.js} +1 -1
  4. package/dist/chunks/{config-g8-JGZ_Y.js.map → config-BscVxZR3.js.map} +1 -1
  5. package/dist/chunks/{grBadgeStyles-D8Mz73B5.js → grBadgeStyles-DV0DrAew.js} +17 -1
  6. package/dist/chunks/grBadgeStyles-DV0DrAew.js.map +1 -0
  7. package/dist/chunks/{grChipStyles-3JZApw03.js → grChipStyles-DcDyxAL0.js} +1 -1
  8. package/dist/chunks/{grChipStyles-3JZApw03.js.map → grChipStyles-DcDyxAL0.js.map} +1 -1
  9. package/dist/chunks/{granular-provider-Da2CJO6j.js → granular-provider-CrQua8PT.js} +2 -2
  10. package/dist/chunks/{granular-provider-Da2CJO6j.js.map → granular-provider-CrQua8PT.js.map} +1 -1
  11. package/dist/chunks/{overlayStack-C8cfsqXa.js → overlayStack-0CZS99Da.js} +40 -6
  12. package/dist/chunks/overlayStack-0CZS99Da.js.map +1 -0
  13. package/dist/chunks/{safelist-CtiHPgrj.js → safelist-x8USecK8.js} +3 -1
  14. package/dist/chunks/safelist-x8USecK8.js.map +1 -0
  15. package/dist/chunks/{useModalOverlay-Cv5hF63W.js → useModalOverlay-D__CqLGn.js} +3 -0
  16. package/dist/chunks/useModalOverlay-D__CqLGn.js.map +1 -0
  17. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-rYEY15UI.js → GrAutocomplete-CoQjEF8h.js} +2 -2
  18. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-rYEY15UI.js.map → GrAutocomplete-CoQjEF8h.js.map} +1 -1
  19. package/dist/components/GrAutocomplete/index.js +1 -1
  20. package/dist/components/GrBadge/chunks/{GrBadge-D2MfThPz.js → GrBadge-DpUDXKbq.js} +9 -4
  21. package/dist/components/GrBadge/chunks/GrBadge-DpUDXKbq.js.map +1 -0
  22. package/dist/components/GrBadge/index.js +3 -3
  23. package/dist/components/GrBadge/styles.css +1 -1
  24. package/dist/components/GrChip/chunks/{GrChip-C0vU69tf.js → GrChip-BTivtwff.js} +1 -1
  25. package/dist/components/GrChip/chunks/{GrChip-C0vU69tf.js.map → GrChip-BTivtwff.js.map} +1 -1
  26. package/dist/components/GrChip/index.js +2 -2
  27. package/dist/components/GrColorPicker/chunks/{GrColorPicker-B20gQ6AR.js → GrColorPicker-p2XH_8cE.js} +1 -1
  28. package/dist/components/GrColorPicker/chunks/{GrColorPicker-B20gQ6AR.js.map → GrColorPicker-p2XH_8cE.js.map} +1 -1
  29. package/dist/components/GrColorPicker/index.js +1 -1
  30. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Cd0Yo3Dw.js → GrCommandPalette-BXbh3q7O.js} +1 -1
  31. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Cd0Yo3Dw.js.map → GrCommandPalette-BXbh3q7O.js.map} +1 -1
  32. package/dist/components/GrCommandPalette/index.js +1 -1
  33. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DOsbRhoZ.js → GrConfirmDialog-CUP43q__.js} +1 -1
  34. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DOsbRhoZ.js.map → GrConfirmDialog-CUP43q__.js.map} +1 -1
  35. package/dist/components/GrConfirmDialog/index.js +1 -1
  36. package/dist/components/GrContextMenu/chunks/{GrContextMenu-_FKwQvuQ.js → GrContextMenu-ClkVjdcP.js} +1 -1
  37. package/dist/components/GrContextMenu/chunks/{GrContextMenu-_FKwQvuQ.js.map → GrContextMenu-ClkVjdcP.js.map} +1 -1
  38. package/dist/components/GrContextMenu/index.js +1 -1
  39. package/dist/components/GrDataTable/chunks/{GrDataTable-CAGgRk27.js → GrDataTable-4JAgOQ-7.js} +1 -1
  40. package/dist/components/GrDataTable/chunks/{GrDataTable-CAGgRk27.js.map → GrDataTable-4JAgOQ-7.js.map} +1 -1
  41. package/dist/components/GrDataTable/index.js +1 -1
  42. package/dist/components/GrDialog/chunks/{GrDialog-S1Vl-OhX.js → GrDialog-BiMJ9_T4.js} +1 -1
  43. package/dist/components/GrDialog/chunks/{GrDialog-S1Vl-OhX.js.map → GrDialog-BiMJ9_T4.js.map} +1 -1
  44. package/dist/components/GrDialog/index.js +1 -1
  45. package/dist/components/GrDialogService/chunks/{GrDialogService-Cuhc4YLp.js → GrDialogService-BxNK6crD.js} +2 -2
  46. package/dist/components/GrDialogService/chunks/{GrDialogService-Cuhc4YLp.js.map → GrDialogService-BxNK6crD.js.map} +1 -1
  47. package/dist/components/GrDialogService/index.js +1 -1
  48. package/dist/components/GrDrawer/chunks/{GrDrawer-C0YqVE9e.js → GrDrawer-Ce2l3zaG.js} +5 -4
  49. package/dist/components/GrDrawer/chunks/GrDrawer-Ce2l3zaG.js.map +1 -0
  50. package/dist/components/GrDrawer/index.js +1 -1
  51. package/dist/components/GrImageViewer/chunks/{GrImageViewer-CyVN6Zpc.js → GrImageViewer-CG4vUBg_.js} +4 -3
  52. package/dist/components/GrImageViewer/chunks/GrImageViewer-CG4vUBg_.js.map +1 -0
  53. package/dist/components/GrImageViewer/index.js +1 -1
  54. package/dist/components/GrInputTag/chunks/{GrInputTag-Cj4A9Xgs.js → GrInputTag-ByD9NugQ.js} +2 -2
  55. package/dist/components/GrInputTag/chunks/{GrInputTag-Cj4A9Xgs.js.map → GrInputTag-ByD9NugQ.js.map} +1 -1
  56. package/dist/components/GrInputTag/index.js +1 -1
  57. package/dist/components/GrModal/chunks/{GrModal-CCoWZuIl.js → GrModal-D0mf1SAx.js} +3 -2
  58. package/dist/components/GrModal/chunks/GrModal-D0mf1SAx.js.map +1 -0
  59. package/dist/components/GrModal/index.js +1 -1
  60. package/dist/components/GrPagination/chunks/{GrPagination-B_kAhoAV.js → GrPagination-FiiXzVyv.js} +1 -1
  61. package/dist/components/GrPagination/chunks/{GrPagination-B_kAhoAV.js.map → GrPagination-FiiXzVyv.js.map} +1 -1
  62. package/dist/components/GrPagination/index.js +1 -1
  63. package/dist/components/GrPopover/chunks/{GrPopover-CofG-Tfe.js → GrPopover-faJanmGs.js} +1 -1
  64. package/dist/components/GrPopover/chunks/{GrPopover-CofG-Tfe.js.map → GrPopover-faJanmGs.js.map} +1 -1
  65. package/dist/components/GrPopover/index.js +1 -1
  66. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BuJQMZLM.js → GrPromptDialog-C6QGC7QJ.js} +1 -1
  67. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BuJQMZLM.js.map → GrPromptDialog-C6QGC7QJ.js.map} +1 -1
  68. package/dist/components/GrPromptDialog/index.js +1 -1
  69. package/dist/components/GrSelect/chunks/{GrSelect-gCeRFgQo.js → GrSelect-Dt8iqruc.js} +2 -2
  70. package/dist/components/GrSelect/chunks/{GrSelect-gCeRFgQo.js.map → GrSelect-Dt8iqruc.js.map} +1 -1
  71. package/dist/components/GrSelect/index.js +1 -1
  72. package/dist/components/GrTable/chunks/{GrTable-Zcdz2St_.js → GrTable-jhUTczjv.js} +12 -1
  73. package/dist/components/GrTable/chunks/GrTable-jhUTczjv.js.map +1 -0
  74. package/dist/components/GrTable/index.js +1 -1
  75. package/dist/composables/useFloating.js +2 -22
  76. package/dist/composables/useFloating.js.map +1 -1
  77. package/dist/composables/useOverlayLayer.js +10 -3
  78. package/dist/composables/useOverlayLayer.js.map +1 -1
  79. package/dist/granular-provider-node.js +1 -1
  80. package/dist/granular-provider.js +1 -1
  81. package/dist/index.js +22 -22
  82. package/dist/types/components/GrBadge/GrBadge.vue.d.ts +11 -0
  83. package/dist/types/components/GrBadge/grBadgeStyles.d.ts +11 -0
  84. package/dist/types/composables/internal/overlayStack.d.ts +30 -0
  85. package/dist/types/composables/internal/useModalOverlay.d.ts +14 -0
  86. package/dist/types/composables/useOverlayLayer.d.ts +8 -0
  87. package/dist/web-types.json +5 -1
  88. package/package.json +3 -3
  89. package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +0 -1
  90. package/dist/chunks/overlayStack-C8cfsqXa.js.map +0 -1
  91. package/dist/chunks/safelist-CtiHPgrj.js.map +0 -1
  92. package/dist/chunks/useModalOverlay-Cv5hF63W.js.map +0 -1
  93. package/dist/components/GrBadge/chunks/GrBadge-D2MfThPz.js.map +0 -1
  94. package/dist/components/GrDrawer/chunks/GrDrawer-C0YqVE9e.js.map +0 -1
  95. package/dist/components/GrImageViewer/chunks/GrImageViewer-CyVN6Zpc.js.map +0 -1
  96. package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +0 -1
  97. package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +0 -1
@@ -1,3 +1,3 @@
1
- import { t as GrSelect_default } from "./chunks/GrSelect-gCeRFgQo.js";
1
+ import { t as GrSelect_default } from "./chunks/GrSelect-Dt8iqruc.js";
2
2
  import { n as grSelectSafelist, t as grSelectConfig } from "../../chunks/config-DnF472Ox.js";
3
3
  export { GrSelect_default as GrSelect, GrSelect_default as default, grSelectConfig, grSelectSafelist };
@@ -127,6 +127,17 @@ var GrTable_default = /* @__PURE__ */ defineComponent({
127
127
  /** Прокрутить к строке содержимого по её индексу в разметке. `false` — строки нет в DOM. */
128
128
  scrollToRow
129
129
  });
130
+ /**
131
+ * `relative` на скроллере — не для потомков с координатами, а чтобы он стал
132
+ * containing block. Без него абсолютные потомки (`sr-only`-подсказка у кнопки
133
+ * сортировки — единственный такой в самой таблице, но разметка ячеек чужая)
134
+ * считают позицию от документа и выпадают из прокрутки наружу: на узком экране
135
+ * широкая таблица начинает тащить вбок всю страницу, а не только себя.
136
+ *
137
+ * Живёт здесь, а не над `<div>`: комментарий в корне `<template>` делает
138
+ * компонент фрагментом, и `$el` у потребителя становится Comment-нодой —
139
+ * `GrDataTable` падает на `root.matches` ещё до первого рендера.
140
+ */
130
141
  return (_ctx, _cache) => {
131
142
  return openBlock(), createElementBlock("div", {
132
143
  ref_key: "scrollEl",
@@ -136,7 +147,7 @@ var GrTable_default = /* @__PURE__ */ defineComponent({
136
147
  "aria-label": __props.regionLabel,
137
148
  "aria-busy": __props.loading ? "true" : void 0,
138
149
  tabindex: "0",
139
- class: "overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]",
150
+ class: "relative overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]",
140
151
  style: normalizeStyle(scrollStyle.value)
141
152
  }, [createElementVNode("table", {
142
153
  "data-gr-table": "",
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrTable-jhUTczjv.js","names":[],"sources":["../../../../src/components/GrTable/GrTable.vue","../../../../src/components/GrTable/GrTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport {\n emptyCellClass,\n hoverableClass,\n loadingRowCellClass,\n stripedClass,\n tableSizes,\n type GrTableSize,\n} from './grTableStyles'\n\nexport interface GrTableProps {\n /**\n * Размер таблицы — базовый кегль текста. Паддинги ячеек оставлены за\n * консьюмером (GrTable — «тонкий» контейнер).\n */\n size?: GrTableSize\n /**\n * Текст caption для screen reader'ов. Рендерится как `<caption class=\"sr-only\">`,\n * если не передан слот `#caption`.\n */\n caption?: string\n /** Прямой ARIA-label для `<table>`. Игнорируется, если задан `ariaLabelledby`. */\n ariaLabel?: string\n /** ID элемента-заголовка, связанного с `<table>` через `aria-labelledby`. */\n ariaLabelledby?: string\n /**\n * ARIA-label для скролл-контейнера. Включает `role=\"region\"`; сам скролл\n * достижим с клавиатуры всегда, независимо от метки.\n */\n regionLabel?: string\n /** Идёт загрузка: вместо строк — скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /** Сколько колонок занимает служебная строка (пустое состояние и скелетоны). */\n columnCount?: number\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Таблица пуста. По умолчанию определяется по содержимому слота. */\n empty?: boolean\n /** Чередование строк. */\n striped?: boolean\n /** Подсветка строки под курсором. */\n hoverable?: boolean\n /**\n * Прилипающий заголовок: `<thead>` остаётся видимым при вертикальном скролле.\n * Осмысленно вместе с `maxHeight` (иначе таблица не скроллится вертикально).\n */\n stickyHeader?: boolean\n /**\n * Полное число строк набора, включая строки заголовка (`aria-rowcount`).\n *\n * Нужен, когда в DOM не весь набор — например, при виртуализации: диктор\n * считает строки по разметке и объявил бы «5 из 20» на таблице в десять\n * тысяч. Строки при этом обязаны нести `aria-rowindex`.\n */\n rowCount?: number\n /**\n * Фиксированная раскладка (`table-layout: fixed`): ширины колонок берутся из\n * первой строки, а не из содержимого всех.\n *\n * Обязателен, если в DOM не весь набор: иначе ширины считаются по\n * отрисованному окну и прыгают на каждой прокрутке.\n *\n * Класс — arbitrary-значение, а не `table-fixed`: такого правила в\n * `presetMini` нет, и класс молча не превратился бы в CSS.\n */\n fixedLayout?: boolean\n /**\n * Минимальная ширина самой таблицы (число — пиксели).\n *\n * Нужна при `fixedLayout`: с фиксированной раскладкой и шириной `auto`\n * браузер вписывает таблицу в контейнер и делит место между колонками\n * пропорционально — то есть заданные ширины молча ужимаются, а\n * горизонтальной прокрутки, на которой держатся закреплённые колонки, не\n * возникает вовсе.\n */\n tableMinWidth?: string | number\n /**\n * Максимальная высота скролл-контейнера (включает вертикальный скролл).\n * Число трактуется как пиксели. Нужен для работы `stickyHeader`.\n */\n maxHeight?: string | number\n}\n\n/**\n * `GrTable` — «тонкий» GR-контейнер для табличных данных.\n *\n * Рендерит `<table>` внутри скролл-обёртки и даёт слоты `#caption`, `#header`,\n * default (tbody) и `#footer`. Стили ячеек (`<th>`/`<td>` паддинги, выравнивание)\n * оставлены на консьюмере.\n *\n * ВАЖНО: `min-w-full` сам по себе не даст горизонтального скролла. Для него\n * консьюмеру нужны `white-space: nowrap` на ячейках или явные ширины колонок.\n */\nconst props = withDefaults(defineProps<GrTableProps>(), {\n size: undefined,\n caption: undefined,\n ariaLabel: undefined,\n ariaLabelledby: undefined,\n regionLabel: undefined,\n loading: false,\n loadingRows: 3,\n columnCount: 1,\n emptyText: undefined,\n empty: undefined,\n striped: false,\n hoverable: false,\n stickyHeader: false,\n maxHeight: undefined,\n rowCount: undefined,\n fixedLayout: false,\n tableMinWidth: undefined,\n})\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTable' })\nconst tableTextClass = computed(() => tableSizes[resolvedSize.value])\nconst hasCaption = computed(() => Boolean(slots.caption) || Boolean(props.caption))\n\nconst { t } = useGranularityTranslations()\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.table.empty', 'Nothing here yet'))\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое строкой не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showRows = computed(() => !props.loading && !isEmpty.value)\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\nconst serviceColSpan = computed(() => Math.max(1, Math.trunc(props.columnCount)))\n\nconst tbodyClass = computed(() => [\n 'text-[var(--gr-fg)]',\n props.striped && showRows.value ? stripedClass : '',\n props.hoverable && showRows.value ? hoverableClass : '',\n].filter(Boolean).join(' '))\n\nconst scrollStyle = computed(() => {\n // Неполный набор в DOM (`rowCount`) означает виртуализацию, а с ней обязан\n // быть выключен браузерный scroll anchoring: он подправляет `scrollTop`,\n // когда меняется высота содержимого выше видимого узла, — а окно меняет её\n // на каждом кадре прокрутки, и список уезжает.\n const anchor = props.rowCount === undefined ? undefined : { overflowAnchor: 'none' as const }\n\n if (props.maxHeight === undefined)\n return anchor\n\n const value = typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight\n return { ...anchor, maxHeight: value, overflowY: 'auto' as const }\n})\n\n// Прилипающий заголовок: `<thead>` на `position: sticky`; фон обязателен, иначе\n// сквозь него будут просвечивать строки при скролле.\nconst theadClass = computed(() => [\n 'bg-[var(--gr-muted)] text-[var(--gr-muted-fg)]',\n props.stickyHeader ? 'sticky top-0 z-[1]' : '',\n].filter(Boolean).join(' '))\n\nconst tableStyle = computed(() => {\n if (props.tableMinWidth === undefined)\n return undefined\n\n const value = typeof props.tableMinWidth === 'number' ? `${props.tableMinWidth}px` : props.tableMinWidth\n\n return { minWidth: value }\n})\n\nconst scrollEl = ref<HTMLElement | null>(null)\n\nfunction scrollTo(options: ScrollToOptions): void {\n scrollEl.value?.scrollTo(options)\n}\n\n// Только верхний уровень собственного tbody: вложенная таблица в ячейке не\n// должна сдвигать индексацию строк.\nfunction contentRows(): HTMLElement[] {\n if (!showRows.value)\n return []\n const el = scrollEl.value\n if (!el)\n return []\n return Array.from(el.querySelectorAll<HTMLElement>(':scope > table > tbody > tr'))\n}\n\nfunction scrollToRow(index: number, options?: ScrollIntoViewOptions): boolean {\n const row = contentRows()[index]\n if (!row)\n return false\n row.scrollIntoView(options ?? { block: 'nearest' })\n return true\n}\n\ndefineExpose({\n /** Прокрутить скролл-контейнер таблицы — тот же контракт, что у `GrDataTable`. */\n scrollTo,\n /** Прокрутить к строке содержимого по её индексу в разметке. `false` — строки нет в DOM. */\n scrollToRow,\n})\n\ndefineSlots<{\n /** Строки таблицы, когда разметка пишется руками вместо `data`. */\n default?: () => any\n /** Подпись таблицы — `<caption>`, читается диктором первой. */\n caption?: () => any\n /** Шапка вместо построенной по `columns`. */\n header?: () => any\n /** Содержимое, пока едут данные, — вместо строк-заглушек. */\n loading?: () => any\n /** Пустое состояние вместо текста по умолчанию. */\n empty?: () => any\n /** Итоговая строка под таблицей. */\n footer?: () => any\n}>()\n\n/**\n * `relative` на скроллере — не для потомков с координатами, а чтобы он стал\n * containing block. Без него абсолютные потомки (`sr-only`-подсказка у кнопки\n * сортировки — единственный такой в самой таблице, но разметка ячеек чужая)\n * считают позицию от документа и выпадают из прокрутки наружу: на узком экране\n * широкая таблица начинает тащить вбок всю страницу, а не только себя.\n *\n * Живёт здесь, а не над `<div>`: комментарий в корне `<template>` делает\n * компонент фрагментом, и `$el` у потребителя становится Comment-нодой —\n * `GrDataTable` падает на `root.matches` ещё до первого рендера.\n */\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n data-gr-table-scroll\n :role=\"regionLabel ? 'region' : undefined\"\n :aria-label=\"regionLabel\"\n :aria-busy=\"loading ? 'true' : undefined\"\n tabindex=\"0\"\n class=\"relative overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :style=\"scrollStyle\"\n >\n <table\n data-gr-table\n class=\"min-w-full\" :class=\"[tableTextClass, fixedLayout ? '[table-layout:fixed]' : '']\"\n :style=\"tableStyle\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :aria-rowcount=\"rowCount\"\n >\n <caption v-if=\"hasCaption\" class=\"sr-only\">\n <slot name=\"caption\">\n{{ caption }}\n</slot>\n </caption>\n <thead :class=\"theadClass\">\n <slot name=\"header\" />\n </thead>\n <tbody :class=\"tbodyClass\">\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <tr v-for=\"row in loadingRowCount\" :key=\"row\" data-gr-table-loading-row>\n <td :colspan=\"serviceColSpan\" :class=\"loadingRowCellClass\">\n <GrSkeleton />\n </td>\n </tr>\n </slot>\n </template>\n\n <tr v-else-if=\"isEmpty\" data-gr-table-empty>\n <td :colspan=\"serviceColSpan\" :class=\"emptyCellClass\">\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </td>\n </tr>\n\n <slot v-else />\n </tbody>\n <tfoot v-if=\"slots.footer\" class=\"text-[var(--gr-fg)]\">\n <slot name=\"footer\" />\n </tfoot>\n </table>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport {\n emptyCellClass,\n hoverableClass,\n loadingRowCellClass,\n stripedClass,\n tableSizes,\n type GrTableSize,\n} from './grTableStyles'\n\nexport interface GrTableProps {\n /**\n * Размер таблицы — базовый кегль текста. Паддинги ячеек оставлены за\n * консьюмером (GrTable — «тонкий» контейнер).\n */\n size?: GrTableSize\n /**\n * Текст caption для screen reader'ов. Рендерится как `<caption class=\"sr-only\">`,\n * если не передан слот `#caption`.\n */\n caption?: string\n /** Прямой ARIA-label для `<table>`. Игнорируется, если задан `ariaLabelledby`. */\n ariaLabel?: string\n /** ID элемента-заголовка, связанного с `<table>` через `aria-labelledby`. */\n ariaLabelledby?: string\n /**\n * ARIA-label для скролл-контейнера. Включает `role=\"region\"`; сам скролл\n * достижим с клавиатуры всегда, независимо от метки.\n */\n regionLabel?: string\n /** Идёт загрузка: вместо строк — скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /** Сколько колонок занимает служебная строка (пустое состояние и скелетоны). */\n columnCount?: number\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Таблица пуста. По умолчанию определяется по содержимому слота. */\n empty?: boolean\n /** Чередование строк. */\n striped?: boolean\n /** Подсветка строки под курсором. */\n hoverable?: boolean\n /**\n * Прилипающий заголовок: `<thead>` остаётся видимым при вертикальном скролле.\n * Осмысленно вместе с `maxHeight` (иначе таблица не скроллится вертикально).\n */\n stickyHeader?: boolean\n /**\n * Полное число строк набора, включая строки заголовка (`aria-rowcount`).\n *\n * Нужен, когда в DOM не весь набор — например, при виртуализации: диктор\n * считает строки по разметке и объявил бы «5 из 20» на таблице в десять\n * тысяч. Строки при этом обязаны нести `aria-rowindex`.\n */\n rowCount?: number\n /**\n * Фиксированная раскладка (`table-layout: fixed`): ширины колонок берутся из\n * первой строки, а не из содержимого всех.\n *\n * Обязателен, если в DOM не весь набор: иначе ширины считаются по\n * отрисованному окну и прыгают на каждой прокрутке.\n *\n * Класс — arbitrary-значение, а не `table-fixed`: такого правила в\n * `presetMini` нет, и класс молча не превратился бы в CSS.\n */\n fixedLayout?: boolean\n /**\n * Минимальная ширина самой таблицы (число — пиксели).\n *\n * Нужна при `fixedLayout`: с фиксированной раскладкой и шириной `auto`\n * браузер вписывает таблицу в контейнер и делит место между колонками\n * пропорционально — то есть заданные ширины молча ужимаются, а\n * горизонтальной прокрутки, на которой держатся закреплённые колонки, не\n * возникает вовсе.\n */\n tableMinWidth?: string | number\n /**\n * Максимальная высота скролл-контейнера (включает вертикальный скролл).\n * Число трактуется как пиксели. Нужен для работы `stickyHeader`.\n */\n maxHeight?: string | number\n}\n\n/**\n * `GrTable` — «тонкий» GR-контейнер для табличных данных.\n *\n * Рендерит `<table>` внутри скролл-обёртки и даёт слоты `#caption`, `#header`,\n * default (tbody) и `#footer`. Стили ячеек (`<th>`/`<td>` паддинги, выравнивание)\n * оставлены на консьюмере.\n *\n * ВАЖНО: `min-w-full` сам по себе не даст горизонтального скролла. Для него\n * консьюмеру нужны `white-space: nowrap` на ячейках или явные ширины колонок.\n */\nconst props = withDefaults(defineProps<GrTableProps>(), {\n size: undefined,\n caption: undefined,\n ariaLabel: undefined,\n ariaLabelledby: undefined,\n regionLabel: undefined,\n loading: false,\n loadingRows: 3,\n columnCount: 1,\n emptyText: undefined,\n empty: undefined,\n striped: false,\n hoverable: false,\n stickyHeader: false,\n maxHeight: undefined,\n rowCount: undefined,\n fixedLayout: false,\n tableMinWidth: undefined,\n})\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTable' })\nconst tableTextClass = computed(() => tableSizes[resolvedSize.value])\nconst hasCaption = computed(() => Boolean(slots.caption) || Boolean(props.caption))\n\nconst { t } = useGranularityTranslations()\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.table.empty', 'Nothing here yet'))\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое строкой не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showRows = computed(() => !props.loading && !isEmpty.value)\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\nconst serviceColSpan = computed(() => Math.max(1, Math.trunc(props.columnCount)))\n\nconst tbodyClass = computed(() => [\n 'text-[var(--gr-fg)]',\n props.striped && showRows.value ? stripedClass : '',\n props.hoverable && showRows.value ? hoverableClass : '',\n].filter(Boolean).join(' '))\n\nconst scrollStyle = computed(() => {\n // Неполный набор в DOM (`rowCount`) означает виртуализацию, а с ней обязан\n // быть выключен браузерный scroll anchoring: он подправляет `scrollTop`,\n // когда меняется высота содержимого выше видимого узла, — а окно меняет её\n // на каждом кадре прокрутки, и список уезжает.\n const anchor = props.rowCount === undefined ? undefined : { overflowAnchor: 'none' as const }\n\n if (props.maxHeight === undefined)\n return anchor\n\n const value = typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight\n return { ...anchor, maxHeight: value, overflowY: 'auto' as const }\n})\n\n// Прилипающий заголовок: `<thead>` на `position: sticky`; фон обязателен, иначе\n// сквозь него будут просвечивать строки при скролле.\nconst theadClass = computed(() => [\n 'bg-[var(--gr-muted)] text-[var(--gr-muted-fg)]',\n props.stickyHeader ? 'sticky top-0 z-[1]' : '',\n].filter(Boolean).join(' '))\n\nconst tableStyle = computed(() => {\n if (props.tableMinWidth === undefined)\n return undefined\n\n const value = typeof props.tableMinWidth === 'number' ? `${props.tableMinWidth}px` : props.tableMinWidth\n\n return { minWidth: value }\n})\n\nconst scrollEl = ref<HTMLElement | null>(null)\n\nfunction scrollTo(options: ScrollToOptions): void {\n scrollEl.value?.scrollTo(options)\n}\n\n// Только верхний уровень собственного tbody: вложенная таблица в ячейке не\n// должна сдвигать индексацию строк.\nfunction contentRows(): HTMLElement[] {\n if (!showRows.value)\n return []\n const el = scrollEl.value\n if (!el)\n return []\n return Array.from(el.querySelectorAll<HTMLElement>(':scope > table > tbody > tr'))\n}\n\nfunction scrollToRow(index: number, options?: ScrollIntoViewOptions): boolean {\n const row = contentRows()[index]\n if (!row)\n return false\n row.scrollIntoView(options ?? { block: 'nearest' })\n return true\n}\n\ndefineExpose({\n /** Прокрутить скролл-контейнер таблицы — тот же контракт, что у `GrDataTable`. */\n scrollTo,\n /** Прокрутить к строке содержимого по её индексу в разметке. `false` — строки нет в DOM. */\n scrollToRow,\n})\n\ndefineSlots<{\n /** Строки таблицы, когда разметка пишется руками вместо `data`. */\n default?: () => any\n /** Подпись таблицы — `<caption>`, читается диктором первой. */\n caption?: () => any\n /** Шапка вместо построенной по `columns`. */\n header?: () => any\n /** Содержимое, пока едут данные, — вместо строк-заглушек. */\n loading?: () => any\n /** Пустое состояние вместо текста по умолчанию. */\n empty?: () => any\n /** Итоговая строка под таблицей. */\n footer?: () => any\n}>()\n\n/**\n * `relative` на скроллере — не для потомков с координатами, а чтобы он стал\n * containing block. Без него абсолютные потомки (`sr-only`-подсказка у кнопки\n * сортировки — единственный такой в самой таблице, но разметка ячеек чужая)\n * считают позицию от документа и выпадают из прокрутки наружу: на узком экране\n * широкая таблица начинает тащить вбок всю страницу, а не только себя.\n *\n * Живёт здесь, а не над `<div>`: комментарий в корне `<template>` делает\n * компонент фрагментом, и `$el` у потребителя становится Comment-нодой —\n * `GrDataTable` падает на `root.matches` ещё до первого рендера.\n */\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n data-gr-table-scroll\n :role=\"regionLabel ? 'region' : undefined\"\n :aria-label=\"regionLabel\"\n :aria-busy=\"loading ? 'true' : undefined\"\n tabindex=\"0\"\n class=\"relative overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :style=\"scrollStyle\"\n >\n <table\n data-gr-table\n class=\"min-w-full\" :class=\"[tableTextClass, fixedLayout ? '[table-layout:fixed]' : '']\"\n :style=\"tableStyle\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :aria-rowcount=\"rowCount\"\n >\n <caption v-if=\"hasCaption\" class=\"sr-only\">\n <slot name=\"caption\">\n{{ caption }}\n</slot>\n </caption>\n <thead :class=\"theadClass\">\n <slot name=\"header\" />\n </thead>\n <tbody :class=\"tbodyClass\">\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <tr v-for=\"row in loadingRowCount\" :key=\"row\" data-gr-table-loading-row>\n <td :colspan=\"serviceColSpan\" :class=\"loadingRowCellClass\">\n <GrSkeleton />\n </td>\n </tr>\n </slot>\n </template>\n\n <tr v-else-if=\"isEmpty\" data-gr-table-empty>\n <td :colspan=\"serviceColSpan\" :class=\"emptyCellClass\">\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </td>\n </tr>\n\n <slot v-else />\n </tbody>\n <tfoot v-if=\"slots.footer\" class=\"text-[var(--gr-fg)]\">\n <slot name=\"footer\" />\n </tfoot>\n </table>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { t as GrTable_default } from "./chunks/GrTable-Zcdz2St_.js";
1
+ import { t as GrTable_default } from "./chunks/GrTable-jhUTczjv.js";
2
2
  import { n as grTableSafelist, t as grTableConfig } from "../../chunks/config-DCq4JySZ.js";
3
3
  export { GrTable_default as GrTable, GrTable_default as default, grTableConfig, grTableSafelist };
@@ -1,4 +1,4 @@
1
- import { n as openModalCount } from "../chunks/overlayStack-C8cfsqXa.js";
1
+ import { t as floatingLayerZIndex } from "../chunks/overlayStack-0CZS99Da.js";
2
2
  import { nextTick, onBeforeUnmount, ref, watch } from "vue";
3
3
  import { autoUpdate, computePosition, flip, offset, shift, size } from "@floating-ui/dom";
4
4
  //#region src/composables/useFloating.ts
@@ -9,26 +9,6 @@ import { autoUpdate, computePosition, flip, offset, shift, size } from "@floatin
9
9
  * официального `@floating-ui/vue` (`useFloating` → `roundByDPR`), чтобы не тянуть весь
10
10
  * пакет ради одной утилиты.
11
11
  */
12
- /**
13
- * Высота панели: своя шкала снаружи и подъём над окном внутри него.
14
- *
15
- * Панель телепортируется в общий портал, то есть становится **соседом** корня
16
- * модального окна, а не его потомком: собственный stacking-контекст окна её не
17
- * накрывает, и статический `--gr-z-dropdown` (1000) оставлял её под окном
18
- * (`--gr-z-modal`, 1100). Поэтому высоту берём из стека слоёв — единственного
19
- * места, которое знает, что сейчас открыто.
20
- *
21
- * Слагаемое — **число** модальных слоёв, а не шаг шкалы: вложенные окна дают
22
- * +2, а полноэкранная загрузка (1150) и тосты (1200) остаются сверху.
23
- *
24
- * Обратный случай — «поповер снаружи не должен перекрывать окно» — не страдает:
25
- * пока окно открыто, страница под ним в `inert`, и открыть там поповер нечем.
26
- * Ненулевой счётчик означает ровно «панель открыта изнутри окна».
27
- */
28
- function floatingZIndex(zIndexVar) {
29
- const modals = openModalCount();
30
- return modals > 0 ? `calc(var(--gr-z-modal) + ${modals})` : `var(${zIndexVar})`;
31
- }
32
12
  function getDpr(element) {
33
13
  if (typeof window === "undefined") return 1;
34
14
  return (element.ownerDocument?.defaultView ?? window).devicePixelRatio || 1;
@@ -133,7 +113,7 @@ function useFloating(reference, floatingEl, open, options = {}) {
133
113
  position: "fixed",
134
114
  left: `${roundByDpr(floating, x)}px`,
135
115
  top: `${roundByDpr(floating, y)}px`,
136
- zIndex: floatingZIndex(options.zIndexVar ?? "--gr-z-dropdown")
116
+ zIndex: floatingLayerZIndex(options.zIndexVar ?? "--gr-z-dropdown")
137
117
  };
138
118
  }
139
119
  function stop() {
@@ -1 +1 @@
1
- {"version":3,"file":"useFloating.js","names":[],"sources":["../../src/composables/useFloating.ts"],"sourcesContent":["import type { CSSProperties, Ref } from 'vue'\nimport type { Middleware, Placement, VirtualElement } from '@floating-ui/dom'\nimport { autoUpdate, computePosition, flip, offset as offsetMiddleware, shift, size as sizeMiddleware } from '@floating-ui/dom'\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\nimport { openModalCount } from './internal/overlayStack'\n\nexport type UseFloatingPlacement = Placement\n\n/**\n * Device pixel ratio плавающего элемента. Нужен, чтобы округлять координаты к сетке\n * физических пикселей (см. `roundByDPR`) — иначе субпиксельные `left/top` дают размытый\n * текст/границы панели на не-целочисленных позициях и HiDPI-экранах. Подход перенят у\n * официального `@floating-ui/vue` (`useFloating` → `roundByDPR`), чтобы не тянуть весь\n * пакет ради одной утилиты.\n */\n/**\n * Высота панели: своя шкала снаружи и подъём над окном внутри него.\n *\n * Панель телепортируется в общий портал, то есть становится **соседом** корня\n * модального окна, а не его потомком: собственный stacking-контекст окна её не\n * накрывает, и статический `--gr-z-dropdown` (1000) оставлял её под окном\n * (`--gr-z-modal`, 1100). Поэтому высоту берём из стека слоёв — единственного\n * места, которое знает, что сейчас открыто.\n *\n * Слагаемое — **число** модальных слоёв, а не шаг шкалы: вложенные окна дают\n * +2, а полноэкранная загрузка (1150) и тосты (1200) остаются сверху.\n *\n * Обратный случай — «поповер снаружи не должен перекрывать окно» — не страдает:\n * пока окно открыто, страница под ним в `inert`, и открыть там поповер нечем.\n * Ненулевой счётчик означает ровно «панель открыта изнутри окна».\n */\nfunction floatingZIndex(zIndexVar: string): string {\n const modals = openModalCount()\n\n return modals > 0 ? `calc(var(--gr-z-modal) + ${modals})` : `var(${zIndexVar})`\n}\n\nfunction getDpr(element: HTMLElement): number {\n if (typeof window === 'undefined')\n return 1\n\n const win = element.ownerDocument?.defaultView ?? window\n return win.devicePixelRatio || 1\n}\n\nfunction roundByDpr(element: HTMLElement, value: number): number {\n const dpr = getDpr(element)\n return Math.round(value * dpr) / dpr\n}\n\n/**\n * Прямоугольник в координатах вьюпорта — якорь, которого нет в DOM.\n *\n * Точка курсора описывается прямоугольником `0×0`. Размеры необязательны именно\n * поэтому: контекстное меню, открытое с клавиатуры, встаёт у **прямоугольника**\n * сфокусированной строки, а не у её угла, и переворачивается вместе с ней.\n */\nexport interface GrFloatingAnchorRect {\n x: number\n y: number\n /** По умолчанию 0. */\n width?: number\n /** По умолчанию 0. */\n height?: number\n}\n\n/** Ссылка позиционирования: элемент, виртуальный якорь или ничего. */\nexport type UseFloatingReference = HTMLElement | VirtualElement | null\n\n/**\n * Виртуальный якорь: объект создаётся один раз, координаты читает геттер на\n * каждом измерении.\n *\n * Стабильность объекта здесь не стилистика, а условие работы: `useFloating`\n * пересоздаёт подписку `autoUpdate` при смене самой ссылки, поэтому новый объект\n * на каждое движение курсора рвал бы и заводил её заново. С одним объектом смена\n * точки — это просто `update()`.\n */\nexport function createFloatingAnchor(\n get: () => GrFloatingAnchorRect | null | undefined,\n options: { contextElement?: () => Element | null | undefined } = {},\n): VirtualElement {\n return {\n getBoundingClientRect() {\n const rect = get() ?? { x: 0, y: 0 }\n const width = rect.width ?? 0\n const height = rect.height ?? 0\n\n return {\n x: rect.x,\n y: rect.y,\n width,\n height,\n top: rect.y,\n left: rect.x,\n right: rect.x + width,\n bottom: rect.y + height,\n }\n },\n get contextElement() {\n return options.contextElement?.() ?? undefined\n },\n }\n}\n\nexport interface UseFloatingOptions {\n /**\n * Предпочитаемое место панели относительно триггера. По умолчанию `bottom-start`.\n * Можно передать функцию-геттер, если место зависит от реактивного пропа\n * (например, `align`) — она вызывается заново при каждом пересчёте позиции.\n */\n placement?: UseFloatingPlacement | (() => UseFloatingPlacement)\n /** Зазор между триггером и панелью, px. По умолчанию `8`. */\n offsetPx?: number\n /** Минимальный отступ от края viewport при flip/shift, px. По умолчанию `8`. */\n boundaryPadding?: number\n /**\n * Согласовать ширину панели с шириной триггера (актуально для Select/TreeSelect).\n * Не годится для Dropdown/Tooltip, где ширина панели определяется контентом.\n * - `true` — точная ширина (`width = ширина триггера`);\n * - `'min'` — панель растёт по контенту (`width: max-content`), но не уже триггера\n * (`min-width = ширина триггера`) — режим `GrSelect view=\"link\"`;\n * - функция-геттер — как и `placement`, для реактивной зависимости от пропа.\n *\n * С виртуальным якорем не работает: ширина точки равна нулю.\n */\n matchWidth?: boolean | 'min' | (() => boolean | 'min')\n /**\n * CSS-переменная шкалы слоёв (см. `styles/tokens.css`). По умолчанию\n * `--gr-z-dropdown`. Действует, пока панель открыта вне модального окна: над\n * окном высоту задаёт стек слоёв (см. {@link floatingZIndex}).\n */\n zIndexVar?: string\n}\n\nexport interface UseFloatingReturn {\n /** Готовый объект для `:style` на плавающем элементе. */\n floatingStyle: Ref<CSSProperties>\n /**\n * Итоговое место панели ПОСЛЕ применения `flip` — может отличаться от запрошенного\n * `placement`, если места не хватило и панель перевернуло на противоположную сторону.\n * Полезно для выбора `transform-origin`/направления transition в потребителе.\n */\n resolvedPlacement: Ref<Placement>\n /** Принудительный пересчёт позиции (например, после изменения контента панели). */\n update: () => void\n}\n\n/**\n * Единый движок позиционирования floating-панелей (Dropdown/Select/TreeSelect/Tooltip)\n * поверх `@floating-ui/dom`: flip (переворот при нехватке места), shift (клэмп к границам\n * viewport), опциональный `size`-мидлвар для растягивания по ширине триггера.\n *\n * Пересчёт положения при скролле/ресайзе делает `autoUpdate` из `@floating-ui/dom` —\n * он сам использует `ResizeObserver`/`IntersectionObserver` и пассивные слушатели вместо\n * наивного враппера на каждый `scroll`/`resize`, поэтому отдельный rAF-throttling здесь\n * не нужен (в отличие от прежней ручной реализации в `GrSelect`/`GrDropdown`).\n *\n * Требует, чтобы `floatingEl` был примонтирован в DOM на момент открытия (обычно —\n * `<teleport to=\"body\">` + `v-show`, а не `v-if`): позиционирование не может измерить\n * элемент, которого нет в дереве.\n */\nexport function useFloating(\n // Объединение, а не `Ref<HTMLElement | VirtualElement | null>`: аксессорный\n // `Ref` проверяется контравариантно по записи, и расширение элементного типа\n // отняло бы у шести нынешних потребителей право передавать свой `Ref` как есть.\n reference: Ref<HTMLElement | null> | (() => UseFloatingReference),\n floatingEl: Ref<HTMLElement | null>,\n open: Ref<boolean>,\n options: UseFloatingOptions = {},\n): UseFloatingReturn {\n const readReference: () => UseFloatingReference\n = typeof reference === 'function' ? reference : () => reference.value\n\n const floatingStyle = ref<CSSProperties>({\n position: 'fixed',\n top: '0px',\n left: '0px',\n })\n function resolveRequestedPlacement(): Placement {\n return typeof options.placement === 'function' ? options.placement() : options.placement ?? 'bottom-start'\n }\n\n function resolveMatchWidth(): boolean | 'min' {\n return typeof options.matchWidth === 'function' ? options.matchWidth() : options.matchWidth ?? false\n }\n\n const resolvedPlacement = ref<Placement>(resolveRequestedPlacement())\n\n let stopAutoUpdate: (() => void) | null = null\n\n async function update(): Promise<void> {\n const reference = readReference()\n const floating = floatingEl.value\n if (!reference || !floating)\n return\n\n const middleware: Middleware[] = [\n offsetMiddleware(options.offsetPx ?? 8),\n flip({ padding: options.boundaryPadding ?? 8 }),\n shift({ padding: options.boundaryPadding ?? 8 }),\n ]\n\n const matchWidth = resolveMatchWidth()\n if (matchWidth) {\n middleware.push(\n sizeMiddleware({\n apply({ rects, elements }) {\n if (matchWidth === 'min') {\n Object.assign(elements.floating.style, {\n width: 'max-content',\n minWidth: `${rects.reference.width}px`,\n })\n return\n }\n\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n minWidth: '',\n })\n },\n }),\n )\n }\n\n const { x, y, placement } = await computePosition(reference, floating, {\n placement: resolveRequestedPlacement(),\n strategy: 'fixed',\n middleware,\n })\n\n resolvedPlacement.value = placement\n floatingStyle.value = {\n position: 'fixed',\n left: `${roundByDpr(floating, x)}px`,\n top: `${roundByDpr(floating, y)}px`,\n zIndex: floatingZIndex(options.zIndexVar ?? '--gr-z-dropdown'),\n }\n }\n\n function stop(): void {\n stopAutoUpdate?.()\n stopAutoUpdate = null\n }\n\n function start(): void {\n stop()\n const reference = readReference()\n const floating = floatingEl.value\n if (!reference || !floating)\n return\n\n // `autoUpdate` подписывается на `ResizeObserver`/`IntersectionObserver` для\n // последующих пересчётов, но не гарантирует немедленный вызов `update` —\n // особенно в средах без этих API (например, jsdom в тестах). Считаем позицию\n // сразу, чтобы панель не «мигала» в 0,0 до первого scroll/resize.\n void update()\n stopAutoUpdate = autoUpdate(reference, floating, () => void update())\n }\n\n // На какую ссылку подписан текущий `autoUpdate`. Отличает смену самого якоря\n // (подписку пересоздать) от смены его координат (достаточно `update()`): у\n // виртуального якоря объект один на всё время жизни.\n let subscribedTo: UseFloatingReference = null\n\n watch(\n [open, () => readReference()],\n ([isOpen, nextReference]) => {\n if (!isOpen || !nextReference) {\n stop()\n subscribedTo = null\n return\n }\n\n if (nextReference === subscribedTo && stopAutoUpdate)\n return\n\n // `floatingEl` монтируется в том же тике, что и `open` переключается\n // (v-show уже в DOM, но teleport/переход могут ещё не отработать) —\n // ждём тик, чтобы `getBoundingClientRect` не читал нулевые размеры.\n // Перепроверка `open`: панель могли закрыть, пока `start()` ждал в\n // очереди, — `stop()` уже отработал, и снимать подписку было бы некому.\n void nextTick(() => {\n if (!open.value)\n return\n subscribedTo = readReference()\n start()\n })\n },\n { immediate: true },\n )\n\n onBeforeUnmount(stop)\n\n return {\n floatingStyle,\n resolvedPlacement,\n update: () => void update(),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useFloating.js","names":[],"sources":["../../src/composables/useFloating.ts"],"sourcesContent":["import type { CSSProperties, Ref } from 'vue'\nimport type { Middleware, Placement, VirtualElement } from '@floating-ui/dom'\nimport { autoUpdate, computePosition, flip, offset as offsetMiddleware, shift, size as sizeMiddleware } from '@floating-ui/dom'\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\nimport { floatingLayerZIndex } from './internal/overlayStack'\n\nexport type UseFloatingPlacement = Placement\n\n/**\n * Device pixel ratio плавающего элемента. Нужен, чтобы округлять координаты к сетке\n * физических пикселей (см. `roundByDPR`) — иначе субпиксельные `left/top` дают размытый\n * текст/границы панели на не-целочисленных позициях и HiDPI-экранах. Подход перенят у\n * официального `@floating-ui/vue` (`useFloating` → `roundByDPR`), чтобы не тянуть весь\n * пакет ради одной утилиты.\n */\nfunction getDpr(element: HTMLElement): number {\n if (typeof window === 'undefined')\n return 1\n\n const win = element.ownerDocument?.defaultView ?? window\n return win.devicePixelRatio || 1\n}\n\nfunction roundByDpr(element: HTMLElement, value: number): number {\n const dpr = getDpr(element)\n return Math.round(value * dpr) / dpr\n}\n\n/**\n * Прямоугольник в координатах вьюпорта — якорь, которого нет в DOM.\n *\n * Точка курсора описывается прямоугольником `0×0`. Размеры необязательны именно\n * поэтому: контекстное меню, открытое с клавиатуры, встаёт у **прямоугольника**\n * сфокусированной строки, а не у её угла, и переворачивается вместе с ней.\n */\nexport interface GrFloatingAnchorRect {\n x: number\n y: number\n /** По умолчанию 0. */\n width?: number\n /** По умолчанию 0. */\n height?: number\n}\n\n/** Ссылка позиционирования: элемент, виртуальный якорь или ничего. */\nexport type UseFloatingReference = HTMLElement | VirtualElement | null\n\n/**\n * Виртуальный якорь: объект создаётся один раз, координаты читает геттер на\n * каждом измерении.\n *\n * Стабильность объекта здесь не стилистика, а условие работы: `useFloating`\n * пересоздаёт подписку `autoUpdate` при смене самой ссылки, поэтому новый объект\n * на каждое движение курсора рвал бы и заводил её заново. С одним объектом смена\n * точки — это просто `update()`.\n */\nexport function createFloatingAnchor(\n get: () => GrFloatingAnchorRect | null | undefined,\n options: { contextElement?: () => Element | null | undefined } = {},\n): VirtualElement {\n return {\n getBoundingClientRect() {\n const rect = get() ?? { x: 0, y: 0 }\n const width = rect.width ?? 0\n const height = rect.height ?? 0\n\n return {\n x: rect.x,\n y: rect.y,\n width,\n height,\n top: rect.y,\n left: rect.x,\n right: rect.x + width,\n bottom: rect.y + height,\n }\n },\n get contextElement() {\n return options.contextElement?.() ?? undefined\n },\n }\n}\n\nexport interface UseFloatingOptions {\n /**\n * Предпочитаемое место панели относительно триггера. По умолчанию `bottom-start`.\n * Можно передать функцию-геттер, если место зависит от реактивного пропа\n * (например, `align`) — она вызывается заново при каждом пересчёте позиции.\n */\n placement?: UseFloatingPlacement | (() => UseFloatingPlacement)\n /** Зазор между триггером и панелью, px. По умолчанию `8`. */\n offsetPx?: number\n /** Минимальный отступ от края viewport при flip/shift, px. По умолчанию `8`. */\n boundaryPadding?: number\n /**\n * Согласовать ширину панели с шириной триггера (актуально для Select/TreeSelect).\n * Не годится для Dropdown/Tooltip, где ширина панели определяется контентом.\n * - `true` — точная ширина (`width = ширина триггера`);\n * - `'min'` — панель растёт по контенту (`width: max-content`), но не уже триггера\n * (`min-width = ширина триггера`) — режим `GrSelect view=\"link\"`;\n * - функция-геттер — как и `placement`, для реактивной зависимости от пропа.\n *\n * С виртуальным якорем не работает: ширина точки равна нулю.\n */\n matchWidth?: boolean | 'min' | (() => boolean | 'min')\n /**\n * CSS-переменная шкалы слоёв (см. `styles/tokens.css`). По умолчанию\n * `--gr-z-dropdown`. Действует, пока панель открыта вне модального окна: над\n * окном высоту задаёт стек слоёв (см. {@link floatingZIndex}).\n */\n zIndexVar?: string\n}\n\nexport interface UseFloatingReturn {\n /** Готовый объект для `:style` на плавающем элементе. */\n floatingStyle: Ref<CSSProperties>\n /**\n * Итоговое место панели ПОСЛЕ применения `flip` — может отличаться от запрошенного\n * `placement`, если места не хватило и панель перевернуло на противоположную сторону.\n * Полезно для выбора `transform-origin`/направления transition в потребителе.\n */\n resolvedPlacement: Ref<Placement>\n /** Принудительный пересчёт позиции (например, после изменения контента панели). */\n update: () => void\n}\n\n/**\n * Единый движок позиционирования floating-панелей (Dropdown/Select/TreeSelect/Tooltip)\n * поверх `@floating-ui/dom`: flip (переворот при нехватке места), shift (клэмп к границам\n * viewport), опциональный `size`-мидлвар для растягивания по ширине триггера.\n *\n * Пересчёт положения при скролле/ресайзе делает `autoUpdate` из `@floating-ui/dom` —\n * он сам использует `ResizeObserver`/`IntersectionObserver` и пассивные слушатели вместо\n * наивного враппера на каждый `scroll`/`resize`, поэтому отдельный rAF-throttling здесь\n * не нужен (в отличие от прежней ручной реализации в `GrSelect`/`GrDropdown`).\n *\n * Требует, чтобы `floatingEl` был примонтирован в DOM на момент открытия (обычно —\n * `<teleport to=\"body\">` + `v-show`, а не `v-if`): позиционирование не может измерить\n * элемент, которого нет в дереве.\n */\nexport function useFloating(\n // Объединение, а не `Ref<HTMLElement | VirtualElement | null>`: аксессорный\n // `Ref` проверяется контравариантно по записи, и расширение элементного типа\n // отняло бы у шести нынешних потребителей право передавать свой `Ref` как есть.\n reference: Ref<HTMLElement | null> | (() => UseFloatingReference),\n floatingEl: Ref<HTMLElement | null>,\n open: Ref<boolean>,\n options: UseFloatingOptions = {},\n): UseFloatingReturn {\n const readReference: () => UseFloatingReference\n = typeof reference === 'function' ? reference : () => reference.value\n\n const floatingStyle = ref<CSSProperties>({\n position: 'fixed',\n top: '0px',\n left: '0px',\n })\n function resolveRequestedPlacement(): Placement {\n return typeof options.placement === 'function' ? options.placement() : options.placement ?? 'bottom-start'\n }\n\n function resolveMatchWidth(): boolean | 'min' {\n return typeof options.matchWidth === 'function' ? options.matchWidth() : options.matchWidth ?? false\n }\n\n const resolvedPlacement = ref<Placement>(resolveRequestedPlacement())\n\n let stopAutoUpdate: (() => void) | null = null\n\n async function update(): Promise<void> {\n const reference = readReference()\n const floating = floatingEl.value\n if (!reference || !floating)\n return\n\n const middleware: Middleware[] = [\n offsetMiddleware(options.offsetPx ?? 8),\n flip({ padding: options.boundaryPadding ?? 8 }),\n shift({ padding: options.boundaryPadding ?? 8 }),\n ]\n\n const matchWidth = resolveMatchWidth()\n if (matchWidth) {\n middleware.push(\n sizeMiddleware({\n apply({ rects, elements }) {\n if (matchWidth === 'min') {\n Object.assign(elements.floating.style, {\n width: 'max-content',\n minWidth: `${rects.reference.width}px`,\n })\n return\n }\n\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n minWidth: '',\n })\n },\n }),\n )\n }\n\n const { x, y, placement } = await computePosition(reference, floating, {\n placement: resolveRequestedPlacement(),\n strategy: 'fixed',\n middleware,\n })\n\n resolvedPlacement.value = placement\n floatingStyle.value = {\n position: 'fixed',\n left: `${roundByDpr(floating, x)}px`,\n top: `${roundByDpr(floating, y)}px`,\n zIndex: floatingLayerZIndex(options.zIndexVar ?? '--gr-z-dropdown'),\n }\n }\n\n function stop(): void {\n stopAutoUpdate?.()\n stopAutoUpdate = null\n }\n\n function start(): void {\n stop()\n const reference = readReference()\n const floating = floatingEl.value\n if (!reference || !floating)\n return\n\n // `autoUpdate` подписывается на `ResizeObserver`/`IntersectionObserver` для\n // последующих пересчётов, но не гарантирует немедленный вызов `update` —\n // особенно в средах без этих API (например, jsdom в тестах). Считаем позицию\n // сразу, чтобы панель не «мигала» в 0,0 до первого scroll/resize.\n void update()\n stopAutoUpdate = autoUpdate(reference, floating, () => void update())\n }\n\n // На какую ссылку подписан текущий `autoUpdate`. Отличает смену самого якоря\n // (подписку пересоздать) от смены его координат (достаточно `update()`): у\n // виртуального якоря объект один на всё время жизни.\n let subscribedTo: UseFloatingReference = null\n\n watch(\n [open, () => readReference()],\n ([isOpen, nextReference]) => {\n if (!isOpen || !nextReference) {\n stop()\n subscribedTo = null\n return\n }\n\n if (nextReference === subscribedTo && stopAutoUpdate)\n return\n\n // `floatingEl` монтируется в том же тике, что и `open` переключается\n // (v-show уже в DOM, но teleport/переход могут ещё не отработать) —\n // ждём тик, чтобы `getBoundingClientRect` не читал нулевые размеры.\n // Перепроверка `open`: панель могли закрыть, пока `start()` ждал в\n // очереди, — `stop()` уже отработал, и снимать подписку было бы некому.\n void nextTick(() => {\n if (!open.value)\n return\n subscribedTo = readReference()\n start()\n })\n },\n { immediate: true },\n )\n\n onBeforeUnmount(stop)\n\n return {\n floatingStyle,\n resolvedPlacement,\n update: () => void update(),\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { i as removeOverlayLayer, r as pushOverlayLayer, t as layerRootsAbove } from "../chunks/overlayStack-C8cfsqXa.js";
2
- import { nextTick, onUnmounted, watch } from "vue";
1
+ import { a as removeOverlayLayer, i as pushOverlayLayer, n as layerRootsAbove } from "../chunks/overlayStack-0CZS99Da.js";
2
+ import { nextTick, onUnmounted, ref, watch } from "vue";
3
3
  //#region src/composables/useOverlayLayer.ts
4
4
  /**
5
5
  * Регистрирует оверлей в общем стеке слоёв, пока `open` истинно.
@@ -24,6 +24,7 @@ import { nextTick, onUnmounted, watch } from "vue";
24
24
  function useOverlayLayer(open, onDismiss, options = {}) {
25
25
  let layerId = null;
26
26
  let previouslyFocused = null;
27
+ const depth = ref(0);
27
28
  function register() {
28
29
  if (layerId !== null) return;
29
30
  if (typeof window === "undefined") return;
@@ -33,6 +34,9 @@ function useOverlayLayer(open, onDismiss, options = {}) {
33
34
  shouldClose: () => options.closeOnEscape?.() ?? true,
34
35
  close: onDismiss,
35
36
  setTopmost: options.onTopmostChange,
37
+ setDepth: (value) => {
38
+ depth.value = value;
39
+ },
36
40
  root: () => options.root?.value ?? null
37
41
  });
38
42
  }
@@ -59,7 +63,10 @@ function useOverlayLayer(open, onDismiss, options = {}) {
59
63
  else unregister();
60
64
  }, { immediate: true });
61
65
  onUnmounted(unregister);
62
- return { rootsAbove: () => layerId === null ? [] : layerRootsAbove(layerId) };
66
+ return {
67
+ rootsAbove: () => layerId === null ? [] : layerRootsAbove(layerId),
68
+ depth
69
+ };
63
70
  }
64
71
  //#endregion
65
72
  export { useOverlayLayer };
@@ -1 +1 @@
1
- {"version":3,"file":"useOverlayLayer.js","names":[],"sources":["../../src/composables/useOverlayLayer.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { nextTick, onUnmounted, watch } from 'vue'\n\nimport { layerRootsAbove, pushOverlayLayer, removeOverlayLayer } from './internal/overlayStack'\n\nexport interface UseOverlayLayerOptions {\n /**\n * Модальный слой: блокирует страницу, и модалки под ним получают `inert`.\n * Немодальные (поповеры, меню, подсказки) участвуют только в очереди Esc.\n * По умолчанию `false`.\n *\n * Геттером — там, где модальность включается пропом (`GrDrawer`): значение\n * читается в момент регистрации, то есть при каждом открытии.\n */\n modal?: boolean | (() => boolean)\n /**\n * Закрывать ли слой по Esc. Геттер, а не значение, — чтобы читать реактивный\n * проп (`closeOnEsc`). По умолчанию закрывается всегда.\n *\n * Если геттер вернёт `false`, нажатие всё равно не пройдёт на слой ниже:\n * пользователь видит верхним этот слой, и Esc адресован ему.\n */\n closeOnEscape?: () => boolean\n /**\n * Уведомление «этот модальный слой сейчас верхний». Компонент вешает по нему\n * `inert` на свой корень, когда перестал быть верхним.\n */\n onTopmostChange?: (isTopmost: boolean) => void\n /**\n * Возвращать фокус элементу, который был активен до открытия. По умолчанию —\n * да. Отключать имеет смысл там, где фокус с триггера и не уходил: `GrSelect`\n * ведёт список через `aria-activedescendant`.\n */\n restoreFocus?: boolean\n /**\n * Корень слоя. Нужен дважды: для правила возврата фокуса (если на момент\n * закрытия фокус уже вне слоя, пользователь ушёл сам — забирать нельзя) и\n * для ловушки фокуса слоя под ним, которая обязана считать этот корень\n * своим (панель селекта внутри модалки телепортирована в `body`).\n */\n root?: Ref<HTMLElement | null>\n}\n\nexport interface OverlayLayerHandle {\n /**\n * Корни слоёв, открытых поверх этого. Передаётся в `useFocusTrap` как\n * `containers`: пока панель, открытая изнутри окна, жива, фокус в ней\n * законен.\n */\n rootsAbove: () => HTMLElement[]\n}\n\n/**\n * Регистрирует оверлей в общем стеке слоёв, пока `open` истинно.\n *\n * Один контракт на порядок Esc, `inert` и возврат фокуса — три механизма,\n * которые обязаны видеть один и тот же порядок слоёв. Что берёт на себя:\n *\n * - **Esc** — верхнему слою, вниз не проваливается;\n * - **`inert`** — через `onTopmostChange` у модальных слоёв;\n * - **возврат фокуса** — по единому правилу (см. ниже).\n *\n * Чего **не** делает: фокус-ловушку — это `useFocusTrap`, отдельный примитив.\n * Поповеру ловушка и не нужна: Tab обязан уводить фокус наружу и закрывать его,\n * поэтому связка «слой + ловушка» собирается в компоненте, а не тут.\n *\n * Правило возврата фокуса: восстанавливаем, **только если на момент закрытия\n * фокус всё ещё внутри слоя**. Если пользователь кликнул в другое поле, фокус\n * уже там — забирать его обратно нельзя. Эвристика «возвращать, если открыли с\n * клавиатуры» промахивается в обе стороны: мышью открытый слой фокус не вернёт,\n * а клавиатурный отберёт его у того, куда пользователь ушёл сам.\n */\nexport function useOverlayLayer(\n open: Ref<boolean>,\n onDismiss: () => void,\n options: UseOverlayLayerOptions = {},\n): OverlayLayerHandle {\n let layerId: number | null = null\n let previouslyFocused: HTMLElement | null = null\n\n function register(): void {\n if (layerId !== null)\n return\n\n // Стек — браузерное понятие: Esc, `inert` и возврат фокуса на сервере\n // бессмысленны. Регистрируйся слой и там — он остался бы в модульном массиве\n // навсегда, потому что `onUnmounted` при `renderToString` не вызывается, и\n // каждый серверный рендер открытого оверлея копил бы замыкание со ссылками\n // на свои компоненты.\n if (typeof window === 'undefined')\n return\n\n if (options.restoreFocus !== false && typeof document !== 'undefined')\n previouslyFocused = (document.activeElement as HTMLElement) ?? null\n\n layerId = pushOverlayLayer({\n modal: typeof options.modal === 'function' ? options.modal() : options.modal ?? false,\n shouldClose: () => options.closeOnEscape?.() ?? true,\n close: onDismiss,\n setTopmost: options.onTopmostChange,\n root: () => options.root?.value ?? null,\n })\n }\n\n function unregister(): void {\n if (layerId === null)\n return\n removeOverlayLayer(layerId)\n layerId = null\n\n restoreFocus()\n }\n\n function restoreFocus(): void {\n const target = previouslyFocused\n previouslyFocused = null\n\n if (options.restoreFocus === false || !target)\n return\n if (typeof document === 'undefined')\n return\n\n // Фокус уже вне слоя — пользователь ушёл сам, отбирать нельзя. Решение\n // принимаем сейчас, пока слой ещё в DOM, а применяем следующим тиком.\n const active = document.activeElement\n const root = options.root?.value\n if (root && active && active !== document.body && !root.contains(active))\n return\n\n // Возврат откладывается намеренно: ловушка фокуса того же слоя снимает свои\n // слушатели в этом же флаше, и сфокусируй мы триггер сразу — она затащила бы\n // фокус обратно в закрывающийся слой.\n void nextTick(() => {\n if (target.isConnected)\n target.focus?.()\n })\n }\n\n watch(\n open,\n (isOpen) => {\n if (isOpen)\n register()\n else unregister()\n },\n { immediate: true },\n )\n\n onUnmounted(unregister)\n\n return {\n rootsAbove: () => (layerId === null ? [] : layerRootsAbove(layerId)),\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAAgB,gBACd,MACA,WACA,UAAkC,CAAC,GACf;CACpB,IAAI,UAAyB;CAC7B,IAAI,oBAAwC;CAE5C,SAAS,WAAiB;EACxB,IAAI,YAAY,MACd;EAOF,IAAI,OAAO,WAAW,aACpB;EAEF,IAAI,QAAQ,iBAAiB,SAAS,OAAO,aAAa,aACxD,oBAAqB,SAAS,iBAAiC;EAEjE,UAAU,iBAAiB;GACzB,OAAO,OAAO,QAAQ,UAAU,aAAa,QAAQ,MAAM,IAAI,QAAQ,SAAS;GAChF,mBAAmB,QAAQ,gBAAgB,KAAK;GAChD,OAAO;GACP,YAAY,QAAQ;GACpB,YAAY,QAAQ,MAAM,SAAS;EACrC,CAAC;CACH;CAEA,SAAS,aAAmB;EAC1B,IAAI,YAAY,MACd;EACF,mBAAmB,OAAO;EAC1B,UAAU;EAEV,aAAa;CACf;CAEA,SAAS,eAAqB;EAC5B,MAAM,SAAS;EACf,oBAAoB;EAEpB,IAAI,QAAQ,iBAAiB,SAAS,CAAC,QACrC;EACF,IAAI,OAAO,aAAa,aACtB;EAIF,MAAM,SAAS,SAAS;EACxB,MAAM,OAAO,QAAQ,MAAM;EAC3B,IAAI,QAAQ,UAAU,WAAW,SAAS,QAAQ,CAAC,KAAK,SAAS,MAAM,GACrE;EAKF,eAAoB;GAClB,IAAI,OAAO,aACT,OAAO,QAAQ;EACnB,CAAC;CACH;CAEA,MACE,OACC,WAAW;EACV,IAAI,QACF,SAAS;OACN,WAAW;CAClB,GACA,EAAE,WAAW,KAAK,CACpB;CAEA,YAAY,UAAU;CAEtB,OAAO,EACL,kBAAmB,YAAY,OAAO,CAAC,IAAI,gBAAgB,OAAO,EACpE;AACF"}
1
+ {"version":3,"file":"useOverlayLayer.js","names":[],"sources":["../../src/composables/useOverlayLayer.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport { layerRootsAbove, pushOverlayLayer, removeOverlayLayer } from './internal/overlayStack'\n\nexport interface UseOverlayLayerOptions {\n /**\n * Модальный слой: блокирует страницу, и модалки под ним получают `inert`.\n * Немодальные (поповеры, меню, подсказки) участвуют только в очереди Esc.\n * По умолчанию `false`.\n *\n * Геттером — там, где модальность включается пропом (`GrDrawer`): значение\n * читается в момент регистрации, то есть при каждом открытии.\n */\n modal?: boolean | (() => boolean)\n /**\n * Закрывать ли слой по Esc. Геттер, а не значение, — чтобы читать реактивный\n * проп (`closeOnEsc`). По умолчанию закрывается всегда.\n *\n * Если геттер вернёт `false`, нажатие всё равно не пройдёт на слой ниже:\n * пользователь видит верхним этот слой, и Esc адресован ему.\n */\n closeOnEscape?: () => boolean\n /**\n * Уведомление «этот модальный слой сейчас верхний». Компонент вешает по нему\n * `inert` на свой корень, когда перестал быть верхним.\n */\n onTopmostChange?: (isTopmost: boolean) => void\n /**\n * Возвращать фокус элементу, который был активен до открытия. По умолчанию —\n * да. Отключать имеет смысл там, где фокус с триггера и не уходил: `GrSelect`\n * ведёт список через `aria-activedescendant`.\n */\n restoreFocus?: boolean\n /**\n * Корень слоя. Нужен дважды: для правила возврата фокуса (если на момент\n * закрытия фокус уже вне слоя, пользователь ушёл сам — забирать нельзя) и\n * для ловушки фокуса слоя под ним, которая обязана считать этот корень\n * своим (панель селекта внутри модалки телепортирована в `body`).\n */\n root?: Ref<HTMLElement | null>\n}\n\nexport interface OverlayLayerHandle {\n /**\n * Корни слоёв, открытых поверх этого. Передаётся в `useFocusTrap` как\n * `containers`: пока панель, открытая изнутри окна, жива, фокус в ней\n * законен.\n */\n rootsAbove: () => HTMLElement[]\n /**\n * Позиция среди модальных слоёв, от нуля. По ней компонент считает высоту.\n *\n * Значение **не сбрасывается** при закрытии: слой уходит из стека сразу, а\n * его поддерево живёт до конца leave-анимации. Обнули глубину там — уходящее\n * окно нырнуло бы под соседнее прямо во время ухода.\n */\n depth: Ref<number>\n}\n\n/**\n * Регистрирует оверлей в общем стеке слоёв, пока `open` истинно.\n *\n * Один контракт на порядок Esc, `inert` и возврат фокуса — три механизма,\n * которые обязаны видеть один и тот же порядок слоёв. Что берёт на себя:\n *\n * - **Esc** — верхнему слою, вниз не проваливается;\n * - **`inert`** — через `onTopmostChange` у модальных слоёв;\n * - **возврат фокуса** — по единому правилу (см. ниже).\n *\n * Чего **не** делает: фокус-ловушку — это `useFocusTrap`, отдельный примитив.\n * Поповеру ловушка и не нужна: Tab обязан уводить фокус наружу и закрывать его,\n * поэтому связка «слой + ловушка» собирается в компоненте, а не тут.\n *\n * Правило возврата фокуса: восстанавливаем, **только если на момент закрытия\n * фокус всё ещё внутри слоя**. Если пользователь кликнул в другое поле, фокус\n * уже там — забирать его обратно нельзя. Эвристика «возвращать, если открыли с\n * клавиатуры» промахивается в обе стороны: мышью открытый слой фокус не вернёт,\n * а клавиатурный отберёт его у того, куда пользователь ушёл сам.\n */\nexport function useOverlayLayer(\n open: Ref<boolean>,\n onDismiss: () => void,\n options: UseOverlayLayerOptions = {},\n): OverlayLayerHandle {\n let layerId: number | null = null\n let previouslyFocused: HTMLElement | null = null\n const depth = ref(0)\n\n function register(): void {\n if (layerId !== null)\n return\n\n // Стек — браузерное понятие: Esc, `inert` и возврат фокуса на сервере\n // бессмысленны. Регистрируйся слой и там — он остался бы в модульном массиве\n // навсегда, потому что `onUnmounted` при `renderToString` не вызывается, и\n // каждый серверный рендер открытого оверлея копил бы замыкание со ссылками\n // на свои компоненты.\n if (typeof window === 'undefined')\n return\n\n if (options.restoreFocus !== false && typeof document !== 'undefined')\n previouslyFocused = (document.activeElement as HTMLElement) ?? null\n\n layerId = pushOverlayLayer({\n modal: typeof options.modal === 'function' ? options.modal() : options.modal ?? false,\n shouldClose: () => options.closeOnEscape?.() ?? true,\n close: onDismiss,\n setTopmost: options.onTopmostChange,\n setDepth: (value) => { depth.value = value },\n root: () => options.root?.value ?? null,\n })\n }\n\n function unregister(): void {\n if (layerId === null)\n return\n removeOverlayLayer(layerId)\n layerId = null\n\n restoreFocus()\n }\n\n function restoreFocus(): void {\n const target = previouslyFocused\n previouslyFocused = null\n\n if (options.restoreFocus === false || !target)\n return\n if (typeof document === 'undefined')\n return\n\n // Фокус уже вне слоя — пользователь ушёл сам, отбирать нельзя. Решение\n // принимаем сейчас, пока слой ещё в DOM, а применяем следующим тиком.\n const active = document.activeElement\n const root = options.root?.value\n if (root && active && active !== document.body && !root.contains(active))\n return\n\n // Возврат откладывается намеренно: ловушка фокуса того же слоя снимает свои\n // слушатели в этом же флаше, и сфокусируй мы триггер сразу — она затащила бы\n // фокус обратно в закрывающийся слой.\n void nextTick(() => {\n if (target.isConnected)\n target.focus?.()\n })\n }\n\n watch(\n open,\n (isOpen) => {\n if (isOpen)\n register()\n else unregister()\n },\n { immediate: true },\n )\n\n onUnmounted(unregister)\n\n return {\n rootsAbove: () => (layerId === null ? [] : layerRootsAbove(layerId)),\n depth,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAgB,gBACd,MACA,WACA,UAAkC,CAAC,GACf;CACpB,IAAI,UAAyB;CAC7B,IAAI,oBAAwC;CAC5C,MAAM,QAAQ,IAAI,CAAC;CAEnB,SAAS,WAAiB;EACxB,IAAI,YAAY,MACd;EAOF,IAAI,OAAO,WAAW,aACpB;EAEF,IAAI,QAAQ,iBAAiB,SAAS,OAAO,aAAa,aACxD,oBAAqB,SAAS,iBAAiC;EAEjE,UAAU,iBAAiB;GACzB,OAAO,OAAO,QAAQ,UAAU,aAAa,QAAQ,MAAM,IAAI,QAAQ,SAAS;GAChF,mBAAmB,QAAQ,gBAAgB,KAAK;GAChD,OAAO;GACP,YAAY,QAAQ;GACpB,WAAW,UAAU;IAAE,MAAM,QAAQ;GAAM;GAC3C,YAAY,QAAQ,MAAM,SAAS;EACrC,CAAC;CACH;CAEA,SAAS,aAAmB;EAC1B,IAAI,YAAY,MACd;EACF,mBAAmB,OAAO;EAC1B,UAAU;EAEV,aAAa;CACf;CAEA,SAAS,eAAqB;EAC5B,MAAM,SAAS;EACf,oBAAoB;EAEpB,IAAI,QAAQ,iBAAiB,SAAS,CAAC,QACrC;EACF,IAAI,OAAO,aAAa,aACtB;EAIF,MAAM,SAAS,SAAS;EACxB,MAAM,OAAO,QAAQ,MAAM;EAC3B,IAAI,QAAQ,UAAU,WAAW,SAAS,QAAQ,CAAC,KAAK,SAAS,MAAM,GACrE;EAKF,eAAoB;GAClB,IAAI,OAAO,aACT,OAAO,QAAQ;EACnB,CAAC;CACH;CAEA,MACE,OACC,WAAW;EACV,IAAI,QACF,SAAS;OACN,WAAW;CAClB,GACA,EAAE,WAAW,KAAK,CACpB;CAEA,YAAY,UAAU;CAEtB,OAAO;EACL,kBAAmB,YAAY,OAAO,CAAC,IAAI,gBAAgB,OAAO;EAClE;CACF;AACF"}
@@ -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-Da2CJO6j.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-CrQua8PT.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-Da2CJO6j.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-CrQua8PT.js";
2
2
  export { GRANULARITY_PROVIDER_ID, createGranularityProvider, granularityProvider as default, granularityProvider, granularityComponentConfigs, granularityDefaultThemes, granularityThemeNames };
package/dist/index.js CHANGED
@@ -21,15 +21,15 @@ import { n as useGrFormFieldContext } from "./chunks/context-DdNIxa1w.js";
21
21
  import { useGrFormControl } from "./composables/useGrFormControl.js";
22
22
  import { useComboboxNavigation } from "./composables/useComboboxNavigation.js";
23
23
  import { useRovingFocus } from "./composables/useRovingFocus.js";
24
- import { t as GrChip_default } from "./components/GrChip/chunks/GrChip-C0vU69tf.js";
24
+ import { t as GrChip_default } from "./components/GrChip/chunks/GrChip-BTivtwff.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-rYEY15UI.js";
26
+ import { t as GrAutocomplete_default } from "./components/GrAutocomplete/chunks/GrAutocomplete-CoQjEF8h.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";
30
- import { t as GrBadge_default } from "./components/GrBadge/chunks/GrBadge-D2MfThPz.js";
31
- import { t as grBadgeSafelist } from "./chunks/safelist-CtiHPgrj.js";
32
- import { t as grBadgeConfig } from "./chunks/config-g8-JGZ_Y.js";
30
+ import { t as GrBadge_default } from "./components/GrBadge/chunks/GrBadge-DpUDXKbq.js";
31
+ import { t as grBadgeSafelist } from "./chunks/safelist-x8USecK8.js";
32
+ import { t as grBadgeConfig } from "./chunks/config-BscVxZR3.js";
33
33
  import { n as grBadgeWrapSafelist, o as formatBadgeValue, t as grBadgeWrapConfig } from "./chunks/config-DwABJHEo.js";
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";
@@ -51,7 +51,7 @@ import { n as grCheckboxSafelist, t as grCheckboxConfig } from "./chunks/config-
51
51
  import "./components/GrCheckbox/index.js";
52
52
  import { t as GrCheckboxGroup_default } from "./components/GrCheckboxGroup/chunks/GrCheckboxGroup-BOwrVcFr.js";
53
53
  import { t as grCheckboxGroupConfig } from "./chunks/config-BuGlNPE9.js";
54
- import { n as grChipSafelist, t as grChipConfig } from "./chunks/config-Dv_r92GL.js";
54
+ import { n as grChipSafelist, t as grChipConfig } from "./chunks/config-2vrOFn2f.js";
55
55
  import "./components/GrChip/index.js";
56
56
  import { n as grChipGroupSafelist, t as grChipGroupConfig } from "./chunks/config-CZPDPJlg.js";
57
57
  import { t as GrChipGroup_default } from "./components/GrChipGroup/chunks/GrChipGroup-C78Vcwos.js";
@@ -63,28 +63,28 @@ import { n as GrCollapse_default, t as GrCollapseItem_default } from "./componen
63
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-BLywRZUN.js";
65
65
  import { useFocusTrap } from "./composables/useFocusTrap.js";
66
- import { t as GrPopover_default } from "./components/GrPopover/chunks/GrPopover-CofG-Tfe.js";
66
+ import { t as GrPopover_default } from "./components/GrPopover/chunks/GrPopover-faJanmGs.js";
67
67
  import { useDragGesture } from "./composables/useDragGesture.js";
68
68
  import { t as GrSlider_default } from "./components/GrSlider/chunks/GrSlider-DTTrZTHK.js";
69
- import { t as GrColorPicker_default } from "./components/GrColorPicker/chunks/GrColorPicker-B20gQ6AR.js";
69
+ import { t as GrColorPicker_default } from "./components/GrColorPicker/chunks/GrColorPicker-p2XH_8cE.js";
70
70
  import { n as grColorPickerSafelist, t as grColorPickerConfig } from "./chunks/config-DIDz_E5m.js";
71
71
  import { a as GR_KBD_TOKENS, o as findKbdToken, t as GrKbd_default } from "./components/GrKbd/chunks/GrKbd-D2ga1H9i.js";
72
- import { t as GrModal_default } from "./components/GrModal/chunks/GrModal-CCoWZuIl.js";
73
- import { t as GrCommandPalette_default } from "./components/GrCommandPalette/chunks/GrCommandPalette-Cd0Yo3Dw.js";
72
+ import { t as GrModal_default } from "./components/GrModal/chunks/GrModal-D0mf1SAx.js";
73
+ import { t as GrCommandPalette_default } from "./components/GrCommandPalette/chunks/GrCommandPalette-BXbh3q7O.js";
74
74
  import { n as grCommandPaletteSafelist, t as grCommandPaletteConfig } from "./chunks/config-D0NzGYaE.js";
75
75
  import { t as GrConfigProvider_default } from "./components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js";
76
76
  import { t as grConfigProviderConfig } from "./chunks/config-B7W9VlZ7.js";
77
- import { i as GrDialogFooter_default, n as GrDialogHeader_default, r as GrDialogCloseButton_default, t as GrDialog_default } from "./components/GrDialog/chunks/GrDialog-S1Vl-OhX.js";
77
+ import { i as GrDialogFooter_default, n as GrDialogHeader_default, r as GrDialogCloseButton_default, t as GrDialog_default } from "./components/GrDialog/chunks/GrDialog-BiMJ9_T4.js";
78
78
  import { a as extendDefaultParsers, n as coreResponseErrorParsers, r as createResponseErrorClassifier, s as normalizeError, t as GrResponseErrorBanner_default } from "./components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DMeOWlzj.js";
79
- import { t as GrConfirmDialog_default } from "./components/GrConfirmDialog/chunks/GrConfirmDialog-DOsbRhoZ.js";
79
+ import { t as GrConfirmDialog_default } from "./components/GrConfirmDialog/chunks/GrConfirmDialog-CUP43q__.js";
80
80
  import { t as grConfirmDialogConfig } from "./chunks/config-A-bBiotF.js";
81
81
  import "./components/GrConfirmDialog/index.js";
82
82
  import { a as GrDropdownMenuItem_default, c as GrDropdownMenuDivider_default, o as GrDropdownMenuGroup_default, s as GrDropdownMenuHeader_default, t as GrDropdownMenuList_default } from "./components/GrDropdownMenu/chunks/GrDropdownMenuList-B33ZzwfB.js";
83
- import { t as GrContextMenu_default } from "./components/GrContextMenu/chunks/GrContextMenu-_FKwQvuQ.js";
83
+ import { t as GrContextMenu_default } from "./components/GrContextMenu/chunks/GrContextMenu-ClkVjdcP.js";
84
84
  import { t as grContextMenuConfig } from "./chunks/config-CPSxM7XE.js";
85
- import { t as GrTable_default } from "./components/GrTable/chunks/GrTable-Zcdz2St_.js";
85
+ import { t as GrTable_default } from "./components/GrTable/chunks/GrTable-jhUTczjv.js";
86
86
  import { t as useDragSort } from "./chunks/useDragSort-0l47h4yb.js";
87
- import { t as GrDataTable_default } from "./components/GrDataTable/chunks/GrDataTable-CAGgRk27.js";
87
+ import { t as GrDataTable_default } from "./components/GrDataTable/chunks/GrDataTable-4JAgOQ-7.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";
@@ -99,13 +99,13 @@ import { n as useGrFormContext } from "./chunks/context-Bfrgsluh.js";
99
99
  import { t as GrFormField_default } from "./components/GrFormField/chunks/GrFormField-DSylU7IU.js";
100
100
  import { t as GrTextarea_default } from "./components/GrTextarea/chunks/GrTextarea-DinXG12-.js";
101
101
  import { n as getByPath, o as runFieldRules, r as isEmpty, s as setByPath, t as createGrFormMessageResolver } from "./chunks/validation-5HhpanPP.js";
102
- import { t as GrPromptDialog_default } from "./components/GrPromptDialog/chunks/GrPromptDialog-BuJQMZLM.js";
103
- import { a as useDialogService, i as teardownDialogService, n as dialogService, r as granularityDialogServicePlugin, t as GrDialogServiceHost_default } from "./components/GrDialogService/chunks/GrDialogService-Cuhc4YLp.js";
102
+ import { t as GrPromptDialog_default } from "./components/GrPromptDialog/chunks/GrPromptDialog-C6QGC7QJ.js";
103
+ import { a as useDialogService, i as teardownDialogService, n as dialogService, r as granularityDialogServicePlugin, t as GrDialogServiceHost_default } from "./components/GrDialogService/chunks/GrDialogService-BxNK6crD.js";
104
104
  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-C0YqVE9e.js";
108
+ import { t as GrDrawer_default } from "./components/GrDrawer/chunks/GrDrawer-Ce2l3zaG.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";
@@ -126,12 +126,12 @@ import { t as GrFormFile_default } from "./components/GrFormFile/chunks/GrFormFi
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-CyVN6Zpc.js";
129
+ import { t as GrImageViewer_default } from "./components/GrImageViewer/chunks/GrImageViewer-CG4vUBg_.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-CIkdsWUk.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-Cj4A9Xgs.js";
134
+ import { t as GrInputTag_default } from "./components/GrInputTag/chunks/GrInputTag-ByD9NugQ.js";
135
135
  import { t as GrTree_default } from "./components/GrTree/chunks/GrTree-Ci6CZfKR.js";
136
136
  import { t as GrJsonViewer_default } from "./components/GrJsonViewer/chunks/GrJsonViewer-BCZ7lE2k.js";
137
137
  import { n as grJsonViewerSafelist, t as grJsonViewerConfig } from "./chunks/config-DpAYidyY2.js";
@@ -149,9 +149,9 @@ import { t as GrNavbar_default } from "./components/GrNavbar/chunks/GrNavbar-_rt
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
151
  import { t as GrNumberInput_default } from "./components/GrNumberInput/chunks/GrNumberInput-DQ59YICu.js";
152
- import { t as GrSelect_default } from "./components/GrSelect/chunks/GrSelect-gCeRFgQo.js";
152
+ import { t as GrSelect_default } from "./components/GrSelect/chunks/GrSelect-Dt8iqruc.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-B_kAhoAV.js";
154
+ import { t as GrPagination_default } from "./components/GrPagination/chunks/GrPagination-FiiXzVyv.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";
@@ -9,6 +9,17 @@ export interface GrBadgeProps {
9
9
  type __VLS_Slots = {
10
10
  /** Содержимое метки. */
11
11
  default?: () => any;
12
+ /**
13
+ * Иконка перед подписью: статус со спиннером, флаг, значок типа.
14
+ *
15
+ * Размер задаёт обёртка по ступени `size`, поэтому содержимое слота тянется
16
+ * до неё — `class="h-full w-full"`, как у `GrChip`. Вращение спиннера —
17
+ * `animate-spin` на самой иконке.
18
+ *
19
+ * `GrProgressCircle` сюда не кладут: у кругового индикатора шкала виджетная,
20
+ * нижняя ступень — `2rem`, и в строку бейджа он не помещается.
21
+ */
22
+ icon?: () => any;
12
23
  };
13
24
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<GrBadgeProps>, {
14
25
  tone: undefined;
@@ -4,6 +4,17 @@ export type GrBadgeTone = GrTone;
4
4
  export type GrBadgeSize = GrComponentSize;
5
5
  export type GrBadgeRadius = 'square' | 'semi' | 'round';
6
6
  export declare const sizeClassBySize: Record<GrBadgeSize, string>;
7
+ /**
8
+ * Иконка перед подписью растёт медленнее кегля: на нижних ступенях плашка
9
+ * узкая, и иконка «один в один с текстом» разгоняла бы её по высоте.
10
+ *
11
+ * Своя карта, а не заимствованная у `GrChip`: у чипа фиксированная высота
12
+ * плитки и своя шкала кегля, и совпадение значений на средних ступенях —
13
+ * совпадение, а не общий контракт.
14
+ */
15
+ export declare const badgeIconSizeClassBySize: Record<GrBadgeSize, string>;
16
+ /** Иконка не жмётся: подпись бейджа не переносится, сжиматься должно нечему. */
17
+ export declare const badgeIconClass = "shrink-0";
7
18
  export declare const semiRadiusClassBySize: Record<GrBadgeSize, string>;
8
19
  export declare const lightToneClassByTone: Record<GrBadgeTone, string>;
9
20
  /**
@@ -36,6 +36,16 @@ export interface OverlayLayer {
36
36
  close: () => void;
37
37
  /** Уведомление «этот модальный слой сейчас верхний». Только для `modal`. */
38
38
  setTopmost?: (isTopmost: boolean) => void;
39
+ /**
40
+ * Позиция слоя среди модальных, от нуля. Только для `modal`.
41
+ *
42
+ * От неё считается высота: у всех модальных слоёв один токен
43
+ * (`--gr-z-modal`), и без этой прибавки порядок отрисовки решал бы порядок
44
+ * узлов в портале, а он задаётся **созданием** телепорта, а не открытием
45
+ * слоя. Диалог, объявленный в шаблоне статически, вставал бы под окном,
46
+ * открытым позже, — и получал бы `inert` от стека, оставаясь видимым.
47
+ */
48
+ setDepth?: (depth: number) => void;
39
49
  /**
40
50
  * Корень слоя. Ловушка фокуса нижнего слоя обязана считать его своим:
41
51
  * панель селекта, открытого внутри модалки, телепортирована в `body` и лежит
@@ -67,5 +77,25 @@ export declare function overlayStackSize(): number;
67
77
  * стек-контекст её не спасает — высоту приходится брать из порядка слоёв.
68
78
  */
69
79
  export declare function openModalCount(): number;
80
+ /**
81
+ * Высота слоя, лежащего внутри открытого окна.
82
+ *
83
+ * Панель телепортирована в общий портал и становится **соседом** корня окна, а
84
+ * не его потомком: собственный stacking-контекст окна её не накрывает, и
85
+ * статический `--gr-z-dropdown` (1000) оставлял её под окном (1100). Слагаемое —
86
+ * число модальных слоёв, поэтому панель встаёт над самым верхним из них, а
87
+ * полноэкранная загрузка (1150) и тосты (1200) остаются сверху.
88
+ *
89
+ * Обратный случай — «поповер снаружи не должен перекрывать окно» — не страдает:
90
+ * пока окно открыто, страница под ним в `inert`, и открыть там поповер нечем.
91
+ */
92
+ export declare function floatingLayerZIndex(zIndexVar: string): string;
93
+ /**
94
+ * Высота самого модального слоя: базовый токен плюс его позиция в стеке.
95
+ *
96
+ * Нулевая глубина оставляет чистый `var(...)` — у единственного окна на экране
97
+ * в стилях не появляется лишней арифметики.
98
+ */
99
+ export declare function modalLayerZIndex(depth: number, zIndexVar?: string): string;
70
100
  /** Тестовая/служебная очистка стека. */
71
101
  export declare function resetOverlayStack(): void;
@@ -47,6 +47,11 @@ export interface UseModalOverlayOptions {
47
47
  * композабл заводит корень сам, как у центрированных окон.
48
48
  */
49
49
  root?: Ref<HTMLElement | null>;
50
+ /**
51
+ * Имя CSS-переменной слоя. Escape-hatch мимо `--gr-z-modal` — им пользуется
52
+ * просмотрщик. Глубина слоя прибавляется и к подменённой переменной.
53
+ */
54
+ zIndexVar?: () => string | undefined;
50
55
  /**
51
56
  * Точечное переопределение точки монтирования (проп `teleportTo`). По
52
57
  * умолчанию — общий портал оверлеев.
@@ -75,6 +80,15 @@ export interface ModalOverlay {
75
80
  isTopmost: Ref<boolean>;
76
81
  /** Значение атрибута `inert` для корня: слой перестал быть верхним. */
77
82
  inertAttr: ComputedRef<true | undefined>;
83
+ /**
84
+ * Высота слоя: базовый токен плюс позиция в стеке.
85
+ *
86
+ * Один токен на все модальные слои означал бы, что порядок отрисовки решает
87
+ * порядок узлов в портале, а он задан **созданием** телепорта, а не
88
+ * открытием: статически объявленный диалог вставал бы под окном, открытым
89
+ * позже, и оказывался невидимым — при том что стек считает верхним его.
90
+ */
91
+ layerZIndex: ComputedRef<string>;
78
92
  portalTarget: ComputedRef<string | HTMLElement>;
79
93
  teleportEnabled: ComputedRef<boolean>;
80
94
  /** `data-theme` на телепортированный корень: в DOM он вне обёртки провайдера. */
@@ -43,6 +43,14 @@ export interface OverlayLayerHandle {
43
43
  * законен.
44
44
  */
45
45
  rootsAbove: () => HTMLElement[];
46
+ /**
47
+ * Позиция среди модальных слоёв, от нуля. По ней компонент считает высоту.
48
+ *
49
+ * Значение **не сбрасывается** при закрытии: слой уходит из стека сразу, а
50
+ * его поддерево живёт до конца leave-анимации. Обнули глубину там — уходящее
51
+ * окно нырнуло бы под соседнее прямо во время ухода.
52
+ */
53
+ depth: Ref<number>;
46
54
  }
47
55
  /**
48
56
  * Регистрирует оверлей в общем стеке слоёв, пока `open` истинно.
@@ -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.31.1",
4
+ "version": "0.33.0",
5
5
  "framework": "vue",
6
6
  "js-types-syntax": "typescript",
7
7
  "description-markup": "markdown",
@@ -615,6 +615,10 @@
615
615
  {
616
616
  "name": "default",
617
617
  "description": "Содержимое метки."
618
+ },
619
+ {
620
+ "name": "icon",
621
+ "description": "Иконка перед подписью: статус со спиннером, флаг, значок типа. Размер задаёт обёртка по ступени `size`, поэтому содержимое слота тянется до неё — `class=\"h-full w-full\"`, как у `GrChip`. Вращение спиннера — `animate-spin` на самой иконке. `GrProgressCircle` сюда не кладут: у кругового индикатора шкала виджетная, нижняя ступень — `2rem`, и в строку бейджа он не помещается."
618
622
  }
619
623
  ]
620
624
  },