@feugene/granularity 0.46.0 → 0.48.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 (162) hide show
  1. package/README.md +3 -3
  2. package/dist/chunks/{x-circle-BCBURwyX.js → check-circle-Bnrb-CGJ.js} +8 -32
  3. package/dist/chunks/check-circle-Bnrb-CGJ.js.map +1 -0
  4. package/dist/chunks/{config-BqT6PMzO.js → config-B42iJ48O.js} +5 -1
  5. package/dist/chunks/{config-BqT6PMzO.js.map → config-B42iJ48O.js.map} +1 -1
  6. package/dist/chunks/{config-BGCyyXiD.js → config-BAse0laT.js} +5 -1
  7. package/dist/chunks/{config-BGCyyXiD.js.map → config-BAse0laT.js.map} +1 -1
  8. package/dist/chunks/{config-MmPym-Z7.js → config-BLP94dPY.js} +5 -1
  9. package/dist/chunks/{config-MmPym-Z7.js.map → config-BLP94dPY.js.map} +1 -1
  10. package/dist/chunks/{config-CjKpUcmy.js → config-Bdf1bt-m.js} +25 -16
  11. package/dist/chunks/config-Bdf1bt-m.js.map +1 -0
  12. package/dist/chunks/{config-DeyT9dJl.js → config-Bs1HwUcx.js} +47 -4
  13. package/dist/chunks/{config-DeyT9dJl.js.map → config-Bs1HwUcx.js.map} +1 -1
  14. package/dist/chunks/{config-CIkdsWUk.js → config-CIUU9ZpY.js} +4 -1
  15. package/dist/chunks/config-CIUU9ZpY.js.map +1 -0
  16. package/dist/chunks/{config-B3J0alyY.js → config-CzeZg2LO.js} +49 -4
  17. package/dist/chunks/config-CzeZg2LO.js.map +1 -0
  18. package/dist/chunks/{config-DCq4JySZ.js → config-D3HOuHDe.js} +1 -1
  19. package/dist/chunks/{config-DCq4JySZ.js.map → config-D3HOuHDe.js.map} +1 -1
  20. package/dist/chunks/{config-BhfNgyWL.js → config-DENqOoh0.js} +5 -1
  21. package/dist/chunks/config-DENqOoh0.js.map +1 -0
  22. package/dist/chunks/controlState-DoImKciE.js +35 -0
  23. package/dist/chunks/controlState-DoImKciE.js.map +1 -0
  24. package/dist/chunks/{en-DiHzFact.js → en-Bxyn5lW7.js} +12 -2
  25. package/dist/chunks/en-Bxyn5lW7.js.map +1 -0
  26. package/dist/chunks/{es-CeKkMxZ_.js → es-Bj_vc18Z.js} +3 -3
  27. package/dist/chunks/{es-CeKkMxZ_.js.map → es-Bj_vc18Z.js.map} +1 -1
  28. package/dist/chunks/{es-1ZL6NtkB.js → es-Dtl-a7ui.js} +12 -2
  29. package/dist/chunks/es-Dtl-a7ui.js.map +1 -0
  30. package/dist/chunks/{generated-CQ4h6Ieq.js → generated-hELWB0gZ.js} +98 -0
  31. package/dist/chunks/{generated-CQ4h6Ieq.js.map → generated-hELWB0gZ.js.map} +1 -1
  32. package/dist/chunks/{grTableStyles-uuMPSOBR.js → grTableStyles-DkQPMfx9.js} +16 -3
  33. package/dist/chunks/grTableStyles-DkQPMfx9.js.map +1 -0
  34. package/dist/chunks/{granular-provider-CsN_R9dB.js → granular-provider-BOQp7N5o.js} +9 -9
  35. package/dist/chunks/{granular-provider-CsN_R9dB.js.map → granular-provider-BOQp7N5o.js.map} +1 -1
  36. package/dist/chunks/{ru-Dr2RYn0P.js → ru-hrB4I1u_.js} +12 -2
  37. package/dist/chunks/ru-hrB4I1u_.js.map +1 -0
  38. package/dist/chunks/x-circle-BigSZy8m.js +26 -0
  39. package/dist/chunks/x-circle-BigSZy8m.js.map +1 -0
  40. package/dist/components/GrAlert/chunks/{GrAlert-D72y1IEh.js → GrAlert-CfEZghSY.js} +2 -1
  41. package/dist/components/GrAlert/chunks/{GrAlert-D72y1IEh.js.map → GrAlert-CfEZghSY.js.map} +1 -1
  42. package/dist/components/GrAlert/index.js +1 -1
  43. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-D396_JrW.js → GrAutocomplete-C-uovdv8.js} +320 -224
  44. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-C-uovdv8.js.map +1 -0
  45. package/dist/components/GrAutocomplete/index.js +1 -1
  46. package/dist/components/GrAutocomplete/styles.css +1 -1
  47. package/dist/components/GrAvatar/chunks/{GrAvatar-dWEXiCJG.js → GrAvatar-Do5-C409.js} +12 -1
  48. package/dist/components/GrAvatar/chunks/GrAvatar-Do5-C409.js.map +1 -0
  49. package/dist/components/GrAvatar/index.js +2 -2
  50. package/dist/components/GrAvatar/themes/light.css +35 -0
  51. package/dist/components/GrColorPicker/chunks/{GrColorPicker-9mTR-YZ_.js → GrColorPicker-BRM_4lyI.js} +1 -1
  52. package/dist/components/GrColorPicker/chunks/{GrColorPicker-9mTR-YZ_.js.map → GrColorPicker-BRM_4lyI.js.map} +1 -1
  53. package/dist/components/GrColorPicker/index.js +1 -1
  54. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Ha4s3DMT.js.map → GrCommandPalette-BJHmkDQv.js.map} +1 -1
  55. package/dist/components/GrCommandPalette/index.js +1 -1
  56. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-e15nFI8M.js → GrConfirmDialog-BzUQwbec.js} +1 -1
  57. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-e15nFI8M.js.map → GrConfirmDialog-BzUQwbec.js.map} +1 -1
  58. package/dist/components/GrConfirmDialog/index.js +1 -1
  59. package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js +1519 -0
  60. package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js.map +1 -0
  61. package/dist/components/GrDataTable/index.js +2 -2
  62. package/dist/components/GrDataTable/themes/light.css +10 -0
  63. package/dist/components/GrDialogService/chunks/{GrDialogService-BmXiYmUp.js → GrDialogService-Dtq0ZCN5.js} +3 -3
  64. package/dist/components/GrDialogService/chunks/{GrDialogService-BmXiYmUp.js.map → GrDialogService-Dtq0ZCN5.js.map} +1 -1
  65. package/dist/components/GrDialogService/index.js +1 -1
  66. package/dist/components/GrFormField/chunks/GrFormField-DO61vxpS.js.map +1 -1
  67. package/dist/components/GrInput/chunks/{GrInput-TFeBm3gQ.js → GrInput-DV4gMWPR.js} +40 -11
  68. package/dist/components/GrInput/chunks/GrInput-DV4gMWPR.js.map +1 -0
  69. package/dist/components/GrInput/index.js +2 -2
  70. package/dist/components/GrInputTag/chunks/{GrInputTag-Ci8AO1ze.js → GrInputTag-CO0YU-ok.js} +30 -6
  71. package/dist/components/GrInputTag/chunks/GrInputTag-CO0YU-ok.js.map +1 -0
  72. package/dist/components/GrInputTag/index.js +2 -2
  73. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-C6q8dEEZ.js → GrJsonViewer-D7CyWzhT.js} +1 -1
  74. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-C6q8dEEZ.js.map → GrJsonViewer-D7CyWzhT.js.map} +1 -1
  75. package/dist/components/GrJsonViewer/index.js +1 -1
  76. package/dist/components/GrNumberInput/chunks/{GrNumberInput-CL5QhOmx.js → GrNumberInput-BI6-vkcs.js} +48 -16
  77. package/dist/components/GrNumberInput/chunks/GrNumberInput-BI6-vkcs.js.map +1 -0
  78. package/dist/components/GrNumberInput/index.js +2 -2
  79. package/dist/components/GrPagination/chunks/{GrPagination-DhbNZHEF.js → GrPagination-BbaF5eXk.js} +10 -7
  80. package/dist/components/GrPagination/chunks/GrPagination-BbaF5eXk.js.map +1 -0
  81. package/dist/components/GrPagination/index.js +2 -2
  82. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-8RxQsOQ_.js → GrPromptDialog-BEL-QgwE.js} +3 -3
  83. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-8RxQsOQ_.js.map → GrPromptDialog-BEL-QgwE.js.map} +1 -1
  84. package/dist/components/GrPromptDialog/index.js +1 -1
  85. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-NEEJZ8zi.js → GrResponseErrorBanner-Bdd5c07x.js} +1 -1
  86. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-NEEJZ8zi.js.map → GrResponseErrorBanner-Bdd5c07x.js.map} +1 -1
  87. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-ZvBvEQDZ.js → GrResponseErrorBanner-DySe1ym4.js} +1 -1
  88. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-ZvBvEQDZ.js.map → GrResponseErrorBanner-DySe1ym4.js.map} +1 -1
  89. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  90. package/dist/components/GrSelect/chunks/{GrSelect-Do1iAyp1.js → GrSelect-0iI4wdR_.js} +3 -3
  91. package/dist/components/GrSelect/chunks/{GrSelect-Do1iAyp1.js.map → GrSelect-0iI4wdR_.js.map} +1 -1
  92. package/dist/components/GrSelect/index.js +1 -1
  93. package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js → GrTable-CKJ-HrsN.js} +1 -1
  94. package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js.map → GrTable-CKJ-HrsN.js.map} +1 -1
  95. package/dist/components/GrTable/index.js +2 -2
  96. package/dist/components/GrTextarea/chunks/{GrTextarea-BII3w61K.js → GrTextarea-Bfgc0DD2.js} +73 -32
  97. package/dist/components/GrTextarea/chunks/GrTextarea-Bfgc0DD2.js.map +1 -0
  98. package/dist/components/GrTextarea/index.js +2 -2
  99. package/dist/components/GrToaster/chunks/{GrToaster-D-sV-9of.js → GrToaster-CZixNk0l.js} +2 -1
  100. package/dist/components/GrToaster/chunks/{GrToaster-D-sV-9of.js.map → GrToaster-CZixNk0l.js.map} +1 -1
  101. package/dist/components/GrToaster/index.js +1 -1
  102. package/dist/components/GrTransfer/chunks/{GrTransfer-8FA78ny6.js → GrTransfer-DdfBUf35.js} +2 -2
  103. package/dist/components/GrTransfer/chunks/{GrTransfer-8FA78ny6.js.map → GrTransfer-DdfBUf35.js.map} +1 -1
  104. package/dist/components/GrTransfer/index.js +1 -1
  105. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DBu5nMGA.js → GrTreeSelect-B6KoDYiZ.js} +43 -18
  106. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-B6KoDYiZ.js.map +1 -0
  107. package/dist/components/GrTreeSelect/index.js +2 -2
  108. package/dist/granular-provider-node.js +1 -1
  109. package/dist/granular-provider.js +1 -1
  110. package/dist/i18n/all.js +1 -1
  111. package/dist/i18n/index.js +1 -1
  112. package/dist/index.js +32 -32
  113. package/dist/theme.js +1 -1
  114. package/dist/tokens.js +1 -1
  115. package/dist/types/components/GrAutocomplete/composables/useAutocompleteNavigation.d.ts +39 -0
  116. package/dist/types/components/GrAutocomplete/composables/useAutocompletePanel.d.ts +36 -0
  117. package/dist/types/components/GrAutocomplete/composables/useAutocompleteValues.d.ts +28 -0
  118. package/dist/types/components/GrAutocomplete/composables/useAutocompleteVirtual.d.ts +33 -0
  119. package/dist/types/components/GrAutocomplete/composables/useRemoteOptions.d.ts +33 -0
  120. package/dist/types/components/GrAvatar/GrAvatar.vue.d.ts +11 -0
  121. package/dist/types/components/GrAvatar/defaults.d.ts +1 -0
  122. package/dist/types/components/GrAvatar/grAvatarStyles.d.ts +26 -1
  123. package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +60 -1
  124. package/dist/types/components/GrDataTable/composables/useDataTableColumnOrder.d.ts +45 -0
  125. package/dist/types/components/GrDataTable/composables/useDataTableColumnWidths.d.ts +38 -0
  126. package/dist/types/components/GrDataTable/composables/useDataTableExpansion.d.ts +46 -0
  127. package/dist/types/components/GrDataTable/composables/useDataTableLayout.d.ts +29 -0
  128. package/dist/types/components/GrDataTable/composables/useDataTableRowKeys.d.ts +16 -0
  129. package/dist/types/components/GrDataTable/composables/useDataTableSelection.d.ts +32 -0
  130. package/dist/types/components/GrDataTable/composables/useDataTableSort.d.ts +37 -0
  131. package/dist/types/components/GrDataTable/grDataTableStyles.d.ts +23 -0
  132. package/dist/types/components/GrFormField/GrFormField.vue.d.ts +7 -1
  133. package/dist/types/components/GrPagination/GrPagination.vue.d.ts +17 -2
  134. package/dist/types/components/GrPagination/grPaginationStyles.d.ts +15 -6
  135. package/dist/types/components/GrPagination/index.d.ts +1 -1
  136. package/dist/types/components/GrTable/grTableStyles.d.ts +16 -3
  137. package/dist/types/components/shared/controlState.d.ts +36 -0
  138. package/dist/types/i18n/messages/en.d.ts +20 -0
  139. package/dist/types/i18n/messages/es.d.ts +20 -0
  140. package/dist/types/i18n/messages/ru.d.ts +20 -0
  141. package/dist/web-types.json +79 -3
  142. package/package.json +1 -1
  143. package/dist/chunks/config-B3J0alyY.js.map +0 -1
  144. package/dist/chunks/config-BhfNgyWL.js.map +0 -1
  145. package/dist/chunks/config-CIkdsWUk.js.map +0 -1
  146. package/dist/chunks/config-CjKpUcmy.js.map +0 -1
  147. package/dist/chunks/en-DiHzFact.js.map +0 -1
  148. package/dist/chunks/es-1ZL6NtkB.js.map +0 -1
  149. package/dist/chunks/grTableStyles-uuMPSOBR.js.map +0 -1
  150. package/dist/chunks/ru-Dr2RYn0P.js.map +0 -1
  151. package/dist/chunks/x-circle-BCBURwyX.js.map +0 -1
  152. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-D396_JrW.js.map +0 -1
  153. package/dist/components/GrAvatar/chunks/GrAvatar-dWEXiCJG.js.map +0 -1
  154. package/dist/components/GrDataTable/chunks/GrDataTable-xNSmO_I-.js +0 -1134
  155. package/dist/components/GrDataTable/chunks/GrDataTable-xNSmO_I-.js.map +0 -1
  156. package/dist/components/GrInput/chunks/GrInput-TFeBm3gQ.js.map +0 -1
  157. package/dist/components/GrInputTag/chunks/GrInputTag-Ci8AO1ze.js.map +0 -1
  158. package/dist/components/GrNumberInput/chunks/GrNumberInput-CL5QhOmx.js.map +0 -1
  159. package/dist/components/GrPagination/chunks/GrPagination-DhbNZHEF.js.map +0 -1
  160. package/dist/components/GrTextarea/chunks/GrTextarea-BII3w61K.js.map +0 -1
  161. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-DBu5nMGA.js.map +0 -1
  162. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Ha4s3DMT.js → GrCommandPalette-BJHmkDQv.js} +1 -1
@@ -0,0 +1,1519 @@
1
+ import { a as useGrComponentSize } from "../../../chunks/context-CPMRfwVr.js";
2
+ import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
+ import { t as loader_circle_default } from "../../../chunks/loader-circle-CfVLz7lb.js";
4
+ import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-DTZIgmje.js";
5
+ import { useVirtualList } from "../../../composables/useVirtualList.js";
6
+ import { useRovingFocus } from "../../../composables/useRovingFocus.js";
7
+ import { t as GrSkeleton_default } from "../../GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js";
8
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CS4TqMtG.js";
9
+ import { useAnnouncer } from "../../../composables/useAnnouncer.js";
10
+ import { useDragGesture } from "../../../composables/useDragGesture.js";
11
+ import { t as chevron_right_default } from "../../../chunks/chevron-right-C6Z01h0X.js";
12
+ import { t as GrCheckbox_default } from "../../GrCheckbox/chunks/GrCheckbox-Dp201frb.js";
13
+ import { t as GrTable_default } from "../../GrTable/chunks/GrTable-CKJ-HrsN.js";
14
+ import { i as moveItem, r as insertionIndex } from "../../../chunks/dragSortGeometry-CY-llfrQ.js";
15
+ import { useDragSort } from "../../../composables/useDragSort.js";
16
+ import { A as selectCheckboxSizes, C as expandIconClass, D as placeholderPaddings, E as headerTextSizes, M as sortIconSizes, N as spinnerSizes, O as rowHeightEstimates, P as summaryRowClass, S as expandColumnWidths, T as headerGaps, _ as detailContentClass, a as columnDropAfterClass, b as detailRowClass, c as columnHandleClass, d as columnPinnedRightEdgeClass, f as columnResizerClass, g as detailCellClass, h as columnResizerLineClass, i as columnDraggingClass, j as selectColumnWidths, k as rowSelectedClass, l as columnPinnedClass, m as columnResizerLineActiveClass, o as columnDropBeforeClass, p as columnResizerHoverClass, r as cellPaddings, s as columnHandleActiveClass, u as columnPinnedLeftEdgeClass, v as detailErrorClass, w as expandIconOpenClass, x as expandButtonClass, y as detailPaddings } from "../../../chunks/config-Bs1HwUcx.js";
17
+ import { t as arrow_up_default } from "../../../chunks/arrow-up-C_G4jg8M.js";
18
+ import { t as grip_vertical_default } from "../../../chunks/grip-vertical-lRha2S7q.js";
19
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, markRaw, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onUnmounted, openBlock, ref, renderList, renderSlot, toDisplayString, unref, useId, watch, watchEffect, withCtx, withModifiers } from "vue";
20
+ //#region src/components/GrDataTable/composables/useDataTableRowKeys.ts
21
+ function useDataTableRowKeys(options) {
22
+ const syntheticKeys = /* @__PURE__ */ new WeakMap();
23
+ let syntheticKeyCounter = 0;
24
+ let missingKeyWarned = false;
25
+ function syntheticRowKey(row) {
26
+ const existing = syntheticKeys.get(row);
27
+ if (existing !== void 0) return existing;
28
+ syntheticKeyCounter += 1;
29
+ const generated = `gr-row-${syntheticKeyCounter}`;
30
+ syntheticKeys.set(row, generated);
31
+ if (!missingKeyWarned && process.env.NODE_ENV !== "production") {
32
+ missingKeyWarned = true;
33
+ console.warn(`[GrDataTable] У строки нет значения по ключу "${String(options.rowKey())}". Задайте \`rowKey\` (поле или функцию) — иначе выбор строк и переиспользование DOM работают по синтетическому ключу, который не переживёт перезагрузку данных.`);
34
+ }
35
+ return generated;
36
+ }
37
+ function rowKeyValue(row) {
38
+ const rk = options.rowKey();
39
+ if (typeof rk === "function") return rk(row);
40
+ const value = row[rk];
41
+ if (typeof value === "string" && value !== "") return value;
42
+ if (typeof value === "number") return value;
43
+ return syntheticRowKey(row);
44
+ }
45
+ return { rowKeyValue };
46
+ }
47
+ //#endregion
48
+ //#region src/components/GrDataTable/grDataTableSort.ts
49
+ /** Пустое значение ячейки: нечего сравнивать, место таким — в конце списка. */
50
+ function isEmptyCell(value) {
51
+ if (value === null || value === void 0) return true;
52
+ return typeof value === "string" && value.trim() === "";
53
+ }
54
+ /**
55
+ * Число, с которым имеет смысл сравнивать: сам `number`, числовая строка, `Date`
56
+ * (по таймстампу), `boolean` (`false` < `true`). Иначе — `null`, и сравнение
57
+ * уходит в строковую ветку.
58
+ */
59
+ function toComparableNumber(value) {
60
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
61
+ if (typeof value === "boolean") return value ? 1 : 0;
62
+ if (typeof value === "bigint") return Number(value);
63
+ if (value instanceof Date) {
64
+ const time = value.getTime();
65
+ return Number.isNaN(time) ? null : time;
66
+ }
67
+ if (typeof value === "string") {
68
+ const trimmed = value.trim();
69
+ if (trimmed === "") return null;
70
+ const parsed = Number(trimmed);
71
+ return Number.isFinite(parsed) ? parsed : null;
72
+ }
73
+ return null;
74
+ }
75
+ /**
76
+ * Сравнивает два значения ячейки. Возвращает готовую разницу с учётом `dir` —
77
+ * кроме пустых значений: они всегда в конце, иначе «пустые» в `desc` уезжали бы
78
+ * наверх и таблица выглядела бы сломанной.
79
+ */
80
+ function compareCells(a, b, dir, locale) {
81
+ const aEmpty = isEmptyCell(a);
82
+ const bEmpty = isEmptyCell(b);
83
+ if (aEmpty || bEmpty) return aEmpty && bEmpty ? 0 : aEmpty ? 1 : -1;
84
+ const aNum = toComparableNumber(a);
85
+ const bNum = toComparableNumber(b);
86
+ const result = aNum !== null && bNum !== null ? aNum - bNum : String(a).localeCompare(String(b), locale, {
87
+ sensitivity: "base",
88
+ numeric: true
89
+ });
90
+ return dir === "asc" ? result : -result;
91
+ }
92
+ /**
93
+ * Новый отсортированный массив. Исходный не мутируется: `rows` приходят пропом,
94
+ * сортировка на месте меняла бы данные потребителя.
95
+ */
96
+ function sortRows(rows, key, dir, locale) {
97
+ return [...rows].sort((a, b) => compareCells(a[key], b[key], dir, locale));
98
+ }
99
+ //#endregion
100
+ //#region src/components/GrDataTable/composables/useDataTableSort.ts
101
+ function useDataTableSort(options) {
102
+ const internalSortKey = ref(options.initialSortKey() ?? "");
103
+ const internalSortDir = ref(options.initialSortDir());
104
+ const isSortKeyControlled = computed(() => options.sortKey() !== void 0);
105
+ const isSortDirControlled = computed(() => options.sortDir() !== void 0);
106
+ const currentSortKey = computed(() => options.sortKey() ?? internalSortKey.value);
107
+ const currentSortDir = computed(() => options.sortDir() ?? internalSortDir.value);
108
+ function applySort(key, dir) {
109
+ if (!isSortKeyControlled.value) internalSortKey.value = key;
110
+ if (!isSortDirControlled.value) internalSortDir.value = dir;
111
+ options.onSortChange(key, dir);
112
+ }
113
+ const sortedRows = computed(() => {
114
+ if (options.externalSort()) return [...options.rows()];
115
+ const key = currentSortKey.value;
116
+ if (!key) return [...options.rows()];
117
+ return sortRows(options.rows(), key, currentSortDir.value, options.locale());
118
+ });
119
+ function toggleSort(col) {
120
+ if (!col.sortable) return;
121
+ if (currentSortKey.value !== col.key) {
122
+ applySort(col.key, "asc");
123
+ return;
124
+ }
125
+ if (currentSortDir.value === "asc") {
126
+ applySort(col.key, "desc");
127
+ return;
128
+ }
129
+ if (options.sortCycle() === "asc-desc-none") {
130
+ applySort("", "asc");
131
+ return;
132
+ }
133
+ applySort(col.key, "asc");
134
+ }
135
+ function clearSort() {
136
+ applySort("", "asc");
137
+ }
138
+ function ariaSortFor(col) {
139
+ if (!col.sortable) return void 0;
140
+ if (currentSortKey.value !== col.key) return "none";
141
+ return currentSortDir.value === "asc" ? "ascending" : "descending";
142
+ }
143
+ return {
144
+ currentSortKey,
145
+ currentSortDir,
146
+ sortedRows,
147
+ toggleSort,
148
+ clearSort,
149
+ ariaSortFor
150
+ };
151
+ }
152
+ //#endregion
153
+ //#region src/components/GrDataTable/composables/useDataTableSelection.ts
154
+ function useDataTableSelection(options) {
155
+ const internalSelected = ref([]);
156
+ const isSelectedControlled = computed(() => options.selected() !== void 0);
157
+ const selectedKeys = computed(() => new Set(options.selected() ?? internalSelected.value));
158
+ function isRowSelectable(row) {
159
+ const predicate = options.selectableRow();
160
+ return predicate ? predicate(row) : true;
161
+ }
162
+ function isRowSelected(row) {
163
+ return selectedKeys.value.has(options.rowKeyValue(row));
164
+ }
165
+ /** «Выбрать все» работает по видимым и выбираемым строкам — они же считают состояние шапки. */
166
+ const selectableRows = computed(() => options.rows().filter(isRowSelectable));
167
+ const allSelected = computed(() => selectableRows.value.length > 0 && selectableRows.value.every(isRowSelected));
168
+ const someSelected = computed(() => selectableRows.value.some(isRowSelected) && !allSelected.value);
169
+ function emitSelected(next) {
170
+ if (!isSelectedControlled.value) internalSelected.value = [...next];
171
+ options.onSelectedChange([...next]);
172
+ }
173
+ function toggleRow(row) {
174
+ if (!isRowSelectable(row)) return;
175
+ const key = options.rowKeyValue(row);
176
+ const next = new Set(selectedKeys.value);
177
+ if (next.has(key)) next.delete(key);
178
+ else next.add(key);
179
+ emitSelected(next);
180
+ }
181
+ function toggleAll() {
182
+ const next = new Set(selectedKeys.value);
183
+ if (allSelected.value) {
184
+ for (const row of selectableRows.value) next.delete(options.rowKeyValue(row));
185
+ emitSelected(next);
186
+ return;
187
+ }
188
+ for (const row of selectableRows.value) next.add(options.rowKeyValue(row));
189
+ emitSelected(next);
190
+ }
191
+ return {
192
+ selectedKeys,
193
+ isRowSelectable,
194
+ isRowSelected,
195
+ selectableRows,
196
+ allSelected,
197
+ someSelected,
198
+ toggleRow,
199
+ toggleAll
200
+ };
201
+ }
202
+ //#endregion
203
+ //#region src/components/GrDataTable/composables/useDataTableColumnOrder.ts
204
+ function useDataTableColumnOrder(options) {
205
+ /** Uncontrolled-состояние; в controlled-режиме перекрывается пропом `columnOrder`. */
206
+ const internalColumnOrder = ref(options.columns().map((col) => String(col.key)));
207
+ /**
208
+ * Колонки в пользовательском порядке.
209
+ *
210
+ * Состав задаёт `columns`, порядок — только порядок: ключ, которого в наборе
211
+ * нет, игнорируется, а колонка, которой нет в порядке (её только что
212
+ * добавили), встаёт в конец. Иначе правка `columns` теряла бы колонки молча.
213
+ */
214
+ const orderedColumns = computed(() => {
215
+ const order = options.columnOrder() ?? internalColumnOrder.value;
216
+ const remaining = new Map(options.columns().map((col) => [String(col.key), col]));
217
+ const ordered = [];
218
+ for (const key of order) {
219
+ const col = remaining.get(key);
220
+ if (!col) continue;
221
+ remaining.delete(key);
222
+ ordered.push(col);
223
+ }
224
+ for (const col of options.columns()) if (remaining.has(String(col.key))) ordered.push(col);
225
+ return ordered.sort((a, b) => pinGroupOf(a) - pinGroupOf(b));
226
+ });
227
+ /** Группа закрепления: 0 — слева, 1 — обычная колонка, 2 — справа. */
228
+ function pinGroupOf(col) {
229
+ if (col.pinned === "left") return 0;
230
+ if (col.pinned === "right") return 2;
231
+ return 1;
232
+ }
233
+ const columnKeys = computed(() => orderedColumns.value.map((col) => String(col.key)));
234
+ function indexOfColumn(key) {
235
+ return columnKeys.value.indexOf(key);
236
+ }
237
+ function sameGroup(from, to) {
238
+ const cols = orderedColumns.value;
239
+ if (!cols[from] || !cols[to]) return false;
240
+ return pinGroupOf(cols[from]) === pinGroupOf(cols[to]);
241
+ }
242
+ function applyColumnMove(from, to) {
243
+ const keys = columnKeys.value;
244
+ if (from < 0 || to < 0 || to >= keys.length || from === to) return;
245
+ if (!sameGroup(from, to)) return;
246
+ const key = keys[from];
247
+ const label = orderedColumns.value[from]?.label ?? key;
248
+ const next = moveItem(keys, from, to);
249
+ internalColumnOrder.value = next;
250
+ options.onOrderChange(next, {
251
+ key,
252
+ from,
253
+ to
254
+ });
255
+ options.announceMove(label, to + 1, keys.length);
256
+ }
257
+ const columnSort = useDragSort({
258
+ items: () => columnKeys.value,
259
+ elementFor: (key) => options.headerCellEls.get(key) ?? null,
260
+ orientation: () => "horizontal",
261
+ disabled: () => !options.reorderableColumns() || options.loading(),
262
+ resolveTarget: (hit, source) => {
263
+ const to = insertionIndex(hit, indexOfColumn(source), columnKeys.value.length);
264
+ return sameGroup(indexOfColumn(source), to) ? to : null;
265
+ },
266
+ onDrop: (source, to) => applyColumnMove(indexOfColumn(source), to)
267
+ });
268
+ /**
269
+ * Кольцо роверного фокуса по ручкам: одна остановка `Tab` на всю шапку.
270
+ * Иначе таблица из десяти колонок добавляла бы десять таб-стопов подряд.
271
+ */
272
+ const columnRoving = useRovingFocus({
273
+ items: () => options.reorderableColumns() ? columnKeys.value : [],
274
+ elementFor: (key) => options.columnHandleEls.get(key) ?? null,
275
+ orientation: () => "horizontal",
276
+ wrap: () => false
277
+ });
278
+ async function onColumnHandleKeydown(event, key) {
279
+ if (!options.reorderableColumns()) return;
280
+ if (event.shiftKey && (event.key === "ArrowLeft" || event.key === "ArrowRight")) {
281
+ event.preventDefault();
282
+ const from = indexOfColumn(key);
283
+ applyColumnMove(from, event.key === "ArrowLeft" ? from - 1 : from + 1);
284
+ await nextTick();
285
+ await columnRoving.focusKey(key);
286
+ return;
287
+ }
288
+ columnRoving.handleNavigationKeys(event);
289
+ }
290
+ const draggingColumnKey = computed(() => columnSort.mode.value === null ? null : columnSort.source.value);
291
+ /** Полоса места вставки — на колонке-соседе, со стороны движения. */
292
+ function columnDropClass(index) {
293
+ const target = columnSort.target.value;
294
+ if (!columnSort.isActive.value || target === null || target !== index) return "";
295
+ const active = columnSort.source.value === null ? -1 : indexOfColumn(columnSort.source.value);
296
+ if (index === active) return "";
297
+ return target < active ? columnDropBeforeClass : columnDropAfterClass;
298
+ }
299
+ return {
300
+ orderedColumns,
301
+ columnKeys,
302
+ pinGroupOf,
303
+ indexOfColumn,
304
+ draggingColumnKey,
305
+ columnDropClass,
306
+ onColumnHandleKeydown,
307
+ columnSort,
308
+ columnRoving
309
+ };
310
+ }
311
+ /** Шаг клавиатуры и крупный шаг — как у `GrSplitter`. */
312
+ var RESIZE_STEP = 16;
313
+ var RESIZE_BIG_STEP = 48;
314
+ function useDataTableColumnWidths(options) {
315
+ const internalColumnWidths = ref({});
316
+ const columnWidths = computed(() => options.columnWidths() ?? internalColumnWidths.value);
317
+ /** Фактические ширины шапки: заполняются замером раскладки. */
318
+ const measuredColumnWidths = ref({});
319
+ function setMeasuredWidths(widths) {
320
+ measuredColumnWidths.value = widths;
321
+ }
322
+ /** Ширина колонки: заданная пользователем сильнее объявленной в `columns`. */
323
+ function widthOf(col) {
324
+ return columnWidths.value[String(col.key)] ?? col.width;
325
+ }
326
+ /**
327
+ * Сумма заданных ширин — минимальная ширина таблицы.
328
+ *
329
+ * Без неё фиксированная раскладка вписывает таблицу в контейнер и делит место
330
+ * пропорционально: колонка, которую пользователь растянул, ужимается обратно, а
331
+ * горизонтальной прокрутки — той самой, ради которой закрепляют колонки, — не
332
+ * возникает. Считается, только когда ширина известна у **всех** колонок: с
333
+ * одной неизвестной сумма врала бы.
334
+ */
335
+ const tableMinWidth = computed(() => {
336
+ const cols = options.orderedColumns();
337
+ if (cols.length === 0) return void 0;
338
+ let total = 0;
339
+ for (const col of cols) {
340
+ const width = widthOf(col);
341
+ if (typeof width !== "number") return void 0;
342
+ total += width;
343
+ }
344
+ return total;
345
+ });
346
+ const resizingKey = ref(null);
347
+ let pendingResizeKey = null;
348
+ let resizeStartX = 0;
349
+ let resizeStartWidth = 0;
350
+ let widthBeforeResize;
351
+ function commitWidths(next) {
352
+ internalColumnWidths.value = next;
353
+ options.onWidthsChange(next);
354
+ }
355
+ function setColumnWidth(key, width) {
356
+ commitWidths({
357
+ ...columnWidths.value,
358
+ [key]: Math.max(48, Math.round(width))
359
+ });
360
+ }
361
+ function announceWidth(key) {
362
+ const col = options.orderedColumns().find((item) => String(item.key) === key);
363
+ if (!col) return;
364
+ options.announceWidth(col.label, columnWidths.value[key] ?? 0);
365
+ }
366
+ const resizeGesture = useDragGesture({
367
+ disabled: () => !options.resizableColumns() || options.loading(),
368
+ onStart: (event) => {
369
+ const key = pendingResizeKey;
370
+ const el = key === null ? null : options.headerCellEls.get(key);
371
+ if (key === null || !el) return false;
372
+ resizeStartX = event.clientX;
373
+ resizeStartWidth = el.getBoundingClientRect().width;
374
+ widthBeforeResize = columnWidths.value[key];
375
+ resizingKey.value = key;
376
+ },
377
+ onMove: (event) => {
378
+ if (resizingKey.value === null) return;
379
+ event.preventDefault();
380
+ setColumnWidth(resizingKey.value, resizeStartWidth + (event.clientX - resizeStartX));
381
+ },
382
+ onEnd: () => {
383
+ const key = resizingKey.value;
384
+ resizingKey.value = null;
385
+ if (key === null) return;
386
+ options.onColumnResize(key, columnWidths.value[key] ?? Math.round(resizeStartWidth));
387
+ announceWidth(key);
388
+ },
389
+ onCancel: () => {
390
+ const key = resizingKey.value;
391
+ resizingKey.value = null;
392
+ if (key === null) return;
393
+ const next = { ...columnWidths.value };
394
+ if (widthBeforeResize === void 0) delete next[key];
395
+ else next[key] = widthBeforeResize;
396
+ commitWidths(next);
397
+ }
398
+ });
399
+ function onResizerPointerDown(event, key) {
400
+ pendingResizeKey = key;
401
+ resizeGesture.start(event);
402
+ }
403
+ /** Сброс к авторазметке: колонка снова считается по содержимому. */
404
+ function resetColumnWidth(key) {
405
+ if (columnWidths.value[key] === void 0) return;
406
+ const next = { ...columnWidths.value };
407
+ delete next[key];
408
+ commitWidths(next);
409
+ options.onColumnResize(key, 0);
410
+ }
411
+ function resizeByKeyboard(key, delta) {
412
+ const measured = options.headerCellEls.get(key)?.getBoundingClientRect().width ?? 48;
413
+ const current = columnWidths.value[key] ?? measured;
414
+ setColumnWidth(key, current + delta);
415
+ options.onColumnResize(key, columnWidths.value[key] ?? current);
416
+ announceWidth(key);
417
+ }
418
+ function onResizerKeydown(event, key) {
419
+ if (!options.resizableColumns() || options.loading()) return;
420
+ const step = event.shiftKey ? RESIZE_BIG_STEP : RESIZE_STEP;
421
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
422
+ event.preventDefault();
423
+ resizeByKeyboard(key, event.key === "ArrowLeft" ? -step : step);
424
+ return;
425
+ }
426
+ if (event.key === "Enter") {
427
+ event.preventDefault();
428
+ resetColumnWidth(key);
429
+ }
430
+ }
431
+ /**
432
+ * Ручка ресайза — фокусируемый `separator`, а значит обязана называть текущее
433
+ * значение с первого рендера, до всякого перетаскивания. Заданной ширины у
434
+ * колонки может не быть вовсе (авторазметка), поэтому за значением идём к
435
+ * измеренной; до замера остаётся объявленная в `columns`, а её тоже может не
436
+ * быть — тогда минимум.
437
+ */
438
+ function resizerValue(col) {
439
+ const key = String(col.key);
440
+ const declared = typeof col.width === "number" ? col.width : void 0;
441
+ return columnWidths.value[key] ?? measuredColumnWidths.value[key] ?? declared ?? 48;
442
+ }
443
+ return {
444
+ columnWidths,
445
+ measuredColumnWidths,
446
+ setMeasuredWidths,
447
+ widthOf,
448
+ tableMinWidth,
449
+ resizingKey,
450
+ onResizerPointerDown,
451
+ onResizerKeydown,
452
+ resetColumnWidth,
453
+ resizerValue
454
+ };
455
+ }
456
+ //#endregion
457
+ //#region src/components/GrDataTable/composables/useDataTableLayout.ts
458
+ /** Служебная колонка выбора в реестре ячеек: своего ключа у неё нет. */
459
+ var SELECT_COLUMN_KEY = "__select__";
460
+ function useDataTableLayout(options) {
461
+ const hasPinnedLeft = computed(() => options.orderedColumns().some((col) => col.pinned === "left"));
462
+ const pinnedOffsets = ref({});
463
+ /**
464
+ * Смещения липких колонок считаются по **измеренным** ширинам соседей: колонка
465
+ * может жить на авторазметке, и тогда взять смещение больше неоткуда. Отсюда же
466
+ * порядок: замер после отрисовки, а не в вычислении.
467
+ */
468
+ function measurePinnedOffsets() {
469
+ const next = {};
470
+ const cols = options.orderedColumns();
471
+ let left = options.selectable() && hasPinnedLeft.value ? options.headerCellEls.get("__select__")?.getBoundingClientRect().width ?? 0 : 0;
472
+ for (const col of cols) {
473
+ if (col.pinned !== "left") continue;
474
+ next[String(col.key)] = left;
475
+ left += options.headerCellEls.get(String(col.key))?.getBoundingClientRect().width ?? 0;
476
+ }
477
+ let right = 0;
478
+ for (let index = cols.length - 1; index >= 0; index -= 1) {
479
+ const col = cols[index];
480
+ if (col.pinned !== "right") continue;
481
+ next[String(col.key)] = right;
482
+ right += options.headerCellEls.get(String(col.key))?.getBoundingClientRect().width ?? 0;
483
+ }
484
+ pinnedOffsets.value = next;
485
+ }
486
+ function measureColumnWidths() {
487
+ if (!options.resizableColumns()) return;
488
+ const next = {};
489
+ for (const col of options.orderedColumns()) {
490
+ const key = String(col.key);
491
+ const width = options.headerCellEls.get(key)?.getBoundingClientRect().width;
492
+ if (width) next[key] = Math.round(width);
493
+ }
494
+ options.onMeasuredWidths(next);
495
+ }
496
+ function measureLayout() {
497
+ measurePinnedOffsets();
498
+ measureColumnWidths();
499
+ }
500
+ function pinnedStyleOf(col) {
501
+ if (!col.pinned) return void 0;
502
+ const offset = pinnedOffsets.value[String(col.key)] ?? 0;
503
+ return col.pinned === "left" ? { left: `${offset}px` } : { right: `${offset}px` };
504
+ }
505
+ /** Тень рисует только крайняя колонка группы — иначе полос было бы столько же, сколько колонок. */
506
+ function pinnedEdgeClass(col, index) {
507
+ const cols = options.orderedColumns();
508
+ if (col.pinned === "left") return cols[index + 1]?.pinned === "left" ? "" : columnPinnedLeftEdgeClass;
509
+ if (col.pinned === "right") return cols[index - 1]?.pinned === "right" ? "" : columnPinnedRightEdgeClass;
510
+ return "";
511
+ }
512
+ function pinnedCellClass(col, index) {
513
+ if (!col.pinned) return [];
514
+ return [columnPinnedClass, pinnedEdgeClass(col, index)];
515
+ }
516
+ /**
517
+ * Ширину колонки меняет не только правка пропов: окно, шрифт и содержимое
518
+ * ячейки двигают её сами. `ResizeObserver` — единственный способ узнать об этом;
519
+ * в jsdom и на сервере его нет, там хватает пересчёта по изменению данных.
520
+ */
521
+ let layoutObserver = null;
522
+ onMounted(() => {
523
+ measureLayout();
524
+ if (typeof ResizeObserver === "undefined") return;
525
+ layoutObserver = new ResizeObserver(() => measureLayout());
526
+ const el = options.headerCellEls.get("__select__") ?? options.headerCellEls.get(options.columnKeys()[0]);
527
+ if (el?.parentElement) layoutObserver.observe(el.parentElement);
528
+ });
529
+ onUnmounted(() => {
530
+ layoutObserver?.disconnect();
531
+ layoutObserver = null;
532
+ });
533
+ watch(options.watchSources, () => void nextTick(measureLayout));
534
+ return {
535
+ hasPinnedLeft,
536
+ pinnedStyleOf,
537
+ pinnedCellClass,
538
+ measureLayout
539
+ };
540
+ }
541
+ //#endregion
542
+ //#region src/components/GrDataTable/composables/useDataTableExpansion.ts
543
+ function useDataTableExpansion(options) {
544
+ const internalExpanded = ref([]);
545
+ const isExpandedControlled = computed(() => options.expandedKeys() !== void 0);
546
+ const expandedKeys = computed(() => new Set(options.expandedKeys() ?? internalExpanded.value));
547
+ const states = ref(/* @__PURE__ */ new Map());
548
+ const data = ref(/* @__PURE__ */ new Map());
549
+ /**
550
+ * Летящие запросы по ключу. Счётчик поколений отдельно от контроллера: ответ,
551
+ * стартовавший раньше и пришедший позже, обязан проиграть последнему, а
552
+ * `abort()` сам по себе этого не гарантирует — промис уже мог разрешиться.
553
+ */
554
+ const inflight = /* @__PURE__ */ new Map();
555
+ const generation = /* @__PURE__ */ new Map();
556
+ function setState(key, state) {
557
+ const next = new Map(states.value);
558
+ next.set(key, state);
559
+ states.value = next;
560
+ }
561
+ function cancel(key) {
562
+ inflight.get(key)?.abort();
563
+ inflight.delete(key);
564
+ generation.set(key, (generation.get(key) ?? 0) + 1);
565
+ }
566
+ function isRowExpandable(row) {
567
+ const predicate = options.expandableRow();
568
+ return predicate ? predicate(row) : true;
569
+ }
570
+ function isRowExpanded(row) {
571
+ return expandedKeys.value.has(options.rowKeyValue(row));
572
+ }
573
+ function detailStateOf(row) {
574
+ return states.value.get(options.rowKeyValue(row)) ?? "idle";
575
+ }
576
+ function detailDataOf(row) {
577
+ return data.value.get(options.rowKeyValue(row));
578
+ }
579
+ async function load(row, key) {
580
+ const loadDetail = options.loadDetail();
581
+ if (!loadDetail) return;
582
+ cancel(key);
583
+ const controller = new AbortController();
584
+ inflight.set(key, controller);
585
+ const seq = generation.get(key) ?? 0;
586
+ setState(key, "loading");
587
+ try {
588
+ const result = await loadDetail(row, controller.signal);
589
+ if (seq !== generation.get(key)) return;
590
+ const next = new Map(data.value);
591
+ next.set(key, result);
592
+ data.value = next;
593
+ setState(key, "ready");
594
+ } catch (error) {
595
+ if (seq !== generation.get(key) || controller.signal.aborted) return;
596
+ setState(key, "error");
597
+ options.onDetailLoadError(row, key, error);
598
+ } finally {
599
+ if (inflight.get(key) === controller) inflight.delete(key);
600
+ }
601
+ }
602
+ function commit(keys) {
603
+ if (!isExpandedControlled.value) internalExpanded.value = keys;
604
+ options.onExpandedChange(keys);
605
+ }
606
+ function toggleRow(row) {
607
+ if (!isRowExpandable(row)) return;
608
+ const key = options.rowKeyValue(row);
609
+ if (expandedKeys.value.has(key)) {
610
+ cancel(key);
611
+ if (states.value.get(key) === "loading") setState(key, "idle");
612
+ commit([...expandedKeys.value].filter((item) => item !== key));
613
+ options.onCollapse(row, key);
614
+ return;
615
+ }
616
+ commit(options.accordion() ? [key] : [...expandedKeys.value, key]);
617
+ options.onExpand(row, key);
618
+ if (options.loadDetail() && states.value.get(key) !== "ready") load(row, key);
619
+ }
620
+ function collapseAll() {
621
+ for (const key of expandedKeys.value) cancel(key);
622
+ commit([]);
623
+ }
624
+ function retryDetail(row) {
625
+ load(row, options.rowKeyValue(row));
626
+ }
627
+ function invalidateDetail(key) {
628
+ if (key === void 0) {
629
+ for (const item of inflight.keys()) cancel(item);
630
+ states.value = /* @__PURE__ */ new Map();
631
+ data.value = /* @__PURE__ */ new Map();
632
+ return;
633
+ }
634
+ cancel(key);
635
+ const nextStates = new Map(states.value);
636
+ nextStates.delete(key);
637
+ states.value = nextStates;
638
+ const nextData = new Map(data.value);
639
+ nextData.delete(key);
640
+ data.value = nextData;
641
+ }
642
+ /**
643
+ * Смена идентичности набора сбрасывает загруженное — то же правило, что у
644
+ * виртуализатора: мутация массива на месте сохраняет, замена сбрасывает.
645
+ * Иначе строка показывала бы подробности прошлого набора данных.
646
+ */
647
+ watch(() => options.rows(), () => invalidateDetail());
648
+ onBeforeUnmount(() => {
649
+ for (const key of inflight.keys()) cancel(key);
650
+ });
651
+ return {
652
+ expandedKeys,
653
+ isRowExpandable,
654
+ isRowExpanded,
655
+ toggleRow,
656
+ collapseAll,
657
+ detailStateOf,
658
+ detailDataOf,
659
+ retryDetail,
660
+ invalidateDetail
661
+ };
662
+ }
663
+ //#endregion
664
+ //#region ~icons/lucide/arrow-down
665
+ var _hoisted_1$1 = {
666
+ viewBox: "0 0 24 24",
667
+ width: "1.2em",
668
+ height: "1.2em"
669
+ };
670
+ function render$1(_ctx, _cache) {
671
+ return openBlock(), createElementBlock("svg", _hoisted_1$1, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
672
+ fill: "none",
673
+ stroke: "currentColor",
674
+ "stroke-linecap": "round",
675
+ "stroke-linejoin": "round",
676
+ "stroke-width": "2",
677
+ d: "M12 5v14m7-7l-7 7l-7-7"
678
+ }, null, -1)])]);
679
+ }
680
+ var arrow_down_default = markRaw({
681
+ name: "lucide-arrow-down",
682
+ render: render$1
683
+ });
684
+ //#endregion
685
+ //#region src/components/GrDataTable/GrDataTable.vue?vue&type=script&setup=true&lang.ts
686
+ var _hoisted_1 = ["id"];
687
+ var _hoisted_2 = ["aria-rowindex"];
688
+ var _hoisted_3 = ["data-column-key", "aria-sort"];
689
+ var _hoisted_4 = [
690
+ "tabindex",
691
+ "aria-label",
692
+ "disabled",
693
+ "onPointerdown",
694
+ "onKeydown",
695
+ "onFocus"
696
+ ];
697
+ var _hoisted_5 = ["onClick"];
698
+ var _hoisted_6 = { class: "sr-only" };
699
+ var _hoisted_7 = {
700
+ class: "inline-flex",
701
+ "aria-hidden": "true"
702
+ };
703
+ var _hoisted_8 = {
704
+ key: 2,
705
+ class: "text-[var(--gr-muted-fg)]"
706
+ };
707
+ var _hoisted_9 = [
708
+ "aria-label",
709
+ "aria-valuenow",
710
+ "aria-valuemin",
711
+ "aria-valuetext",
712
+ "onPointerdown",
713
+ "onKeydown",
714
+ "onDblclick"
715
+ ];
716
+ var _hoisted_10 = {
717
+ key: 0,
718
+ "data-gr-datatable-loading": ""
719
+ };
720
+ var _hoisted_11 = ["colspan"];
721
+ var _hoisted_12 = { class: "inline-flex items-center gap-2" };
722
+ var _hoisted_13 = {
723
+ key: 1,
724
+ "data-gr-datatable-empty": ""
725
+ };
726
+ var _hoisted_14 = ["colspan"];
727
+ var _hoisted_15 = {
728
+ key: 0,
729
+ "aria-hidden": "true",
730
+ "data-gr-datatable-spacer": "before",
731
+ "data-gr-table-off-grid": "",
732
+ style: { pointerEvents: "none" }
733
+ };
734
+ var _hoisted_16 = ["colspan"];
735
+ var _hoisted_17 = [
736
+ "data-row-key",
737
+ "data-selected",
738
+ "aria-rowindex",
739
+ "onClick"
740
+ ];
741
+ var _hoisted_18 = [
742
+ "aria-expanded",
743
+ "aria-controls",
744
+ "aria-label",
745
+ "onClick"
746
+ ];
747
+ var _hoisted_19 = ["id"];
748
+ var _hoisted_20 = ["colspan"];
749
+ var _hoisted_21 = {
750
+ key: 0,
751
+ "data-gr-datatable-detail-loading": ""
752
+ };
753
+ var _hoisted_22 = {
754
+ key: 1,
755
+ "aria-hidden": "true",
756
+ "data-gr-datatable-spacer": "after",
757
+ "data-gr-table-off-grid": "",
758
+ style: { pointerEvents: "none" }
759
+ };
760
+ var _hoisted_23 = ["colspan"];
761
+ var _hoisted_24 = ["aria-rowindex"];
762
+ var _hoisted_25 = ["data-column-key"];
763
+ //#endregion
764
+ //#region src/components/GrDataTable/GrDataTable.vue
765
+ var GrDataTable_default = /* @__PURE__ */ defineComponent({
766
+ __name: "GrDataTable",
767
+ props: {
768
+ rows: {},
769
+ columns: {},
770
+ rowKey: {
771
+ type: [String, Function],
772
+ default: "id"
773
+ },
774
+ initialSortKey: { default: void 0 },
775
+ initialSortDir: { default: "asc" },
776
+ sortKey: { default: void 0 },
777
+ sortDir: { default: void 0 },
778
+ sortCycle: { default: "asc-desc" },
779
+ externalSort: {
780
+ type: Boolean,
781
+ default: false
782
+ },
783
+ selectable: {
784
+ type: Boolean,
785
+ default: false
786
+ },
787
+ selected: { default: void 0 },
788
+ selectableRow: {
789
+ type: Function,
790
+ default: void 0
791
+ },
792
+ loading: {
793
+ type: Boolean,
794
+ default: false
795
+ },
796
+ loadingText: { default: void 0 },
797
+ emptyText: { default: void 0 },
798
+ rowClass: {
799
+ type: [String, Function],
800
+ default: void 0
801
+ },
802
+ rowProps: {
803
+ type: Function,
804
+ default: void 0
805
+ },
806
+ summaryRow: { default: void 0 },
807
+ size: { default: void 0 },
808
+ caption: { default: void 0 },
809
+ ariaLabel: { default: void 0 },
810
+ ariaLabelledby: { default: void 0 },
811
+ regionLabel: { default: void 0 },
812
+ stickyHeader: {
813
+ type: Boolean,
814
+ default: false
815
+ },
816
+ virtual: {
817
+ type: Boolean,
818
+ default: false
819
+ },
820
+ maxHeight: { default: void 0 },
821
+ reorderableColumns: {
822
+ type: Boolean,
823
+ default: false
824
+ },
825
+ columnOrder: { default: void 0 },
826
+ resizableColumns: {
827
+ type: Boolean,
828
+ default: false
829
+ },
830
+ columnWidths: { default: void 0 },
831
+ expandable: {
832
+ type: Boolean,
833
+ default: false
834
+ },
835
+ expandedKeys: { default: void 0 },
836
+ expandableRow: {
837
+ type: Function,
838
+ default: void 0
839
+ },
840
+ loadDetail: {
841
+ type: Function,
842
+ default: void 0
843
+ },
844
+ accordion: {
845
+ type: Boolean,
846
+ default: false
847
+ },
848
+ expandColumn: {
849
+ type: Boolean,
850
+ default: true
851
+ }
852
+ },
853
+ emits: [
854
+ "update:sortKey",
855
+ "update:sortDir",
856
+ "sortChange",
857
+ "update:selected",
858
+ "rowClick",
859
+ "update:columnOrder",
860
+ "columnReorder",
861
+ "update:columnWidths",
862
+ "columnResize",
863
+ "update:expandedKeys",
864
+ "expand",
865
+ "collapse",
866
+ "detailLoadError"
867
+ ],
868
+ setup(__props, { expose: __expose, emit: __emit }) {
869
+ const props = __props;
870
+ if (process.env.NODE_ENV !== "production") watchEffect(() => {
871
+ if (!Array.isArray(props.rows)) console.warn(`[granularity] GrDataTable: обязательный проп \`rows\` должен быть массивом — получено ${String(props.rows)}.`);
872
+ if (!Array.isArray(props.columns)) console.warn(`[granularity] GrDataTable: обязательный проп \`columns\` должен быть массивом — получено ${String(props.columns)}.`);
873
+ });
874
+ const emit = __emit;
875
+ const { t, locale } = useGranularityTranslations();
876
+ const { announce } = useAnnouncer();
877
+ const resolvedLoadingText = computed(() => props.loadingText ?? t("gr.dataTable.loading", "Loading…"));
878
+ const resolvedEmptyText = computed(() => props.emptyText ?? t("gr.dataTable.empty", "No data"));
879
+ const { currentSortKey, currentSortDir, sortedRows, toggleSort, clearSort, ariaSortFor } = useDataTableSort({
880
+ rows: () => props.rows,
881
+ sortKey: () => props.sortKey,
882
+ sortDir: () => props.sortDir,
883
+ initialSortKey: () => props.initialSortKey,
884
+ initialSortDir: () => props.initialSortDir,
885
+ externalSort: () => props.externalSort,
886
+ sortCycle: () => props.sortCycle,
887
+ locale: () => locale.value,
888
+ onSortChange: (key, dir) => {
889
+ emit("update:sortKey", key);
890
+ emit("update:sortDir", dir);
891
+ emit("sortChange", {
892
+ key,
893
+ dir
894
+ });
895
+ }
896
+ });
897
+ function cellAlign(col) {
898
+ if (col.align === "right") return "text-right";
899
+ if (col.align === "center") return "text-center";
900
+ return "text-left";
901
+ }
902
+ const { rowKeyValue } = useDataTableRowKeys({ rowKey: () => props.rowKey });
903
+ /**
904
+ * Подсказка «что сделает нажатие» — visually hidden текстом **после** подписи
905
+ * колонки, а не `aria-label`'ом на кнопке. `aria-label` подменял бы собой имя
906
+ * `<th>`, и AT при переходе по ячейкам вместо «Название» читала бы
907
+ * «Отсортировано по Название по возрастанию, нажмите…». Текущее состояние
908
+ * сортировки при этом объявляет `aria-sort` на самом `<th>`.
909
+ */
910
+ function sortHint(col) {
911
+ if (currentSortKey.value !== col.key) return t("gr.dataTable.sortAsc", "Activate to sort ascending");
912
+ if (currentSortDir.value === "asc") return t("gr.dataTable.sortDesc", "Activate to sort descending");
913
+ return props.sortCycle === "asc-desc-none" ? t("gr.dataTable.sortNone", "Activate to remove sorting") : t("gr.dataTable.sortAsc", "Activate to sort ascending");
914
+ }
915
+ const resolvedSize = useGrComponentSize(() => props.size, { component: "GrDataTable" });
916
+ const cellClass = computed(() => cellPaddings[resolvedSize.value]);
917
+ const placeholderClass = computed(() => placeholderPaddings[resolvedSize.value]);
918
+ const headerTextClass = computed(() => headerTextSizes[resolvedSize.value]);
919
+ const headerGapClass = computed(() => headerGaps[resolvedSize.value]);
920
+ const selectColumnClass = computed(() => selectColumnWidths[resolvedSize.value]);
921
+ const spinnerClass = computed(() => spinnerSizes[resolvedSize.value]);
922
+ const sortIconSize = computed(() => sortIconSizes[resolvedSize.value]);
923
+ const checkboxSize = computed(() => selectCheckboxSizes[resolvedSize.value]);
924
+ /**
925
+ * Номер строки и полное их число объявляем только при виртуализации: иначе
926
+ * диктор считает строки по разметке, а там всего окно. Заголовок — строка
927
+ * первая, поэтому строки набора начинаются со второй.
928
+ */
929
+ /**
930
+ * Итог показывается, только когда набор на экране: «Итого 1 234» над спиннером —
931
+ * утверждение о том, чего ещё не показали. Пустой набор при этом итог не гасит:
932
+ * «Итого 0» под «нет данных» осмысленно, и считает его потребитель.
933
+ *
934
+ * Предикат один на всех: шаблон, `aria-rowcount` и номер строки обязаны сходиться,
935
+ * иначе диктор объявит строку, которой в разметке нет.
936
+ */
937
+ const hasSummary = computed(() => props.summaryRow != null && !props.loading);
938
+ const ariaRowCount = computed(() => props.virtual ? sortedRows.value.length + 1 + (hasSummary.value ? 1 : 0) : void 0);
939
+ function ariaRowIndex(index) {
940
+ return props.virtual ? index + 2 : void 0;
941
+ }
942
+ /** Итог идёт следующим номером за последней строкой набора. */
943
+ const summaryAriaRowIndex = computed(() => props.virtual ? sortedRows.value.length + 2 : void 0);
944
+ const isEmpty = computed(() => sortedRows.value.length === 0);
945
+ /**
946
+ * Значение итога по ключу колонки. Как и `cellValue`, отдаёт сырое: отсутствие
947
+ * ключа даёт пустую ячейку, а ноль — это значение, а не пустота.
948
+ */
949
+ function summaryValue(key) {
950
+ return props.summaryRow?.[key];
951
+ }
952
+ /**
953
+ * Раскрытие выключено при виртуализации целиком — см. предупреждение ниже.
954
+ * Объявлено здесь, а не рядом с ним: от него зависит `colspan` служебных строк.
955
+ */
956
+ const expandEnabled = computed(() => props.expandable && !props.virtual);
957
+ /** Своя колонка с кнопкой: её может не быть, если кнопку ставит потребитель. */
958
+ const expandColumnVisible = computed(() => expandEnabled.value && props.expandColumn);
959
+ const totalColumns = computed(() => props.columns.length + (props.selectable ? 1 : 0) + (expandColumnVisible.value ? 1 : 0));
960
+ /**
961
+ * Карты живут в компоненте, а не в модуле: наполняет их шаблон, а читают все
962
+ * три колоночных модуля — порядок тянет за ячейку, ширина её замеряет,
963
+ * раскладка считает по ней смещения закрепления.
964
+ */
965
+ const headerCellEls = /* @__PURE__ */ new Map();
966
+ const columnHandleEls = /* @__PURE__ */ new Map();
967
+ function registerEl(store, key, el) {
968
+ if (el instanceof HTMLElement) {
969
+ store.set(key, el);
970
+ return;
971
+ }
972
+ const previous = store.get(key);
973
+ if (previous && !previous.isConnected) store.delete(key);
974
+ }
975
+ const { orderedColumns, columnKeys, draggingColumnKey, columnDropClass, onColumnHandleKeydown, columnSort, columnRoving } = useDataTableColumnOrder({
976
+ columns: () => props.columns,
977
+ columnOrder: () => props.columnOrder,
978
+ reorderableColumns: () => props.reorderableColumns,
979
+ loading: () => props.loading,
980
+ headerCellEls,
981
+ columnHandleEls,
982
+ onOrderChange: (order, moved) => {
983
+ emit("update:columnOrder", order);
984
+ emit("columnReorder", moved);
985
+ },
986
+ announceMove: (label, position, count) => {
987
+ announce(t("gr.dataTable.columnMoved", "Column {label} moved to position {position} of {count}", {
988
+ label,
989
+ position,
990
+ count
991
+ }));
992
+ }
993
+ });
994
+ const { columnWidths, setMeasuredWidths, widthOf, tableMinWidth, resizingKey, onResizerPointerDown, onResizerKeydown, resetColumnWidth, resizerValue } = useDataTableColumnWidths({
995
+ orderedColumns: () => orderedColumns.value,
996
+ columnWidths: () => props.columnWidths,
997
+ resizableColumns: () => props.resizableColumns,
998
+ loading: () => props.loading,
999
+ headerCellEls,
1000
+ onWidthsChange: (widths) => emit("update:columnWidths", widths),
1001
+ onColumnResize: (key, width) => emit("columnResize", {
1002
+ key,
1003
+ width
1004
+ }),
1005
+ announceWidth: (label, width) => {
1006
+ announce(t("gr.dataTable.columnResized", "Column {label} is {width} pixels wide", {
1007
+ label,
1008
+ width
1009
+ }));
1010
+ }
1011
+ });
1012
+ const { hasPinnedLeft, pinnedStyleOf, pinnedCellClass } = useDataTableLayout({
1013
+ orderedColumns: () => orderedColumns.value,
1014
+ columnKeys: () => columnKeys.value,
1015
+ selectable: () => props.selectable,
1016
+ resizableColumns: () => props.resizableColumns,
1017
+ headerCellEls,
1018
+ watchSources: () => [
1019
+ orderedColumns.value,
1020
+ columnWidths.value,
1021
+ props.loading,
1022
+ props.rows.length
1023
+ ],
1024
+ onMeasuredWidths: setMeasuredWidths
1025
+ });
1026
+ const tableProps = computed(() => ({
1027
+ size: resolvedSize.value,
1028
+ ariaLabel: props.ariaLabel,
1029
+ ariaLabelledby: props.ariaLabelledby,
1030
+ regionLabel: props.regionLabel,
1031
+ stickyHeader: props.stickyHeader,
1032
+ maxHeight: props.maxHeight,
1033
+ rowCount: ariaRowCount.value,
1034
+ fixedLayout: props.virtual || props.resizableColumns,
1035
+ tableMinWidth: tableMinWidth.value
1036
+ }));
1037
+ function columnHandleLabel(col) {
1038
+ return t("gr.dataTable.moveColumn", "Move column {label}", { label: col.label });
1039
+ }
1040
+ function resizerLabel(col) {
1041
+ return t("gr.dataTable.resizeColumn", "Width of column {label}", { label: col.label });
1042
+ }
1043
+ /** Уже колонки не бывает: в неё перестаёт помещаться даже стрелка сортировки. */
1044
+ /**
1045
+ * Живой регион существует с первого рендера и пуст, пока объявлять нечего.
1046
+ * Регион, который появляется уже с текстом, часть AT не объявляет вовсе —
1047
+ * а именно так вело себя `role="status"` внутри строки загрузки.
1048
+ */
1049
+ const liveMessage = computed(() => {
1050
+ if (props.loading) return resolvedLoadingText.value;
1051
+ if (isEmpty.value) return resolvedEmptyText.value;
1052
+ return "";
1053
+ });
1054
+ function cellValue(row, key) {
1055
+ return row[key];
1056
+ }
1057
+ function rowClassName(row, index) {
1058
+ const source = props.rowClass;
1059
+ return typeof source === "function" ? source(row, index) : source;
1060
+ }
1061
+ function onRowClick(row, index, event) {
1062
+ emit("rowClick", {
1063
+ row,
1064
+ index,
1065
+ event
1066
+ });
1067
+ }
1068
+ const { isRowSelectable, isRowSelected, selectableRows, allSelected, someSelected, toggleRow, toggleAll } = useDataTableSelection({
1069
+ rows: () => sortedRows.value,
1070
+ selected: () => props.selected,
1071
+ selectableRow: () => props.selectableRow,
1072
+ rowKeyValue,
1073
+ onSelectedChange: (keys) => emit("update:selected", keys)
1074
+ });
1075
+ const expandDetailId = useId();
1076
+ const { isRowExpandable, isRowExpanded, toggleRow: toggleRowExpanded, detailStateOf, detailDataOf, retryDetail, invalidateDetail } = useDataTableExpansion({
1077
+ expandedKeys: () => props.expandedKeys,
1078
+ rows: () => props.rows,
1079
+ expandableRow: () => props.expandableRow,
1080
+ loadDetail: () => props.loadDetail,
1081
+ accordion: () => props.accordion,
1082
+ rowKeyValue,
1083
+ onExpandedChange: (keys) => emit("update:expandedKeys", keys),
1084
+ onExpand: (row, key) => {
1085
+ emit("expand", {
1086
+ row,
1087
+ key
1088
+ });
1089
+ announce(t("gr.dataTable.detailExpanded", "Row details shown"));
1090
+ },
1091
+ onCollapse: (row, key) => {
1092
+ emit("collapse", {
1093
+ row,
1094
+ key
1095
+ });
1096
+ announce(t("gr.dataTable.detailCollapsed", "Row details hidden"));
1097
+ },
1098
+ onDetailLoadError: (row, key, error) => emit("detailLoadError", {
1099
+ row,
1100
+ key,
1101
+ error
1102
+ })
1103
+ });
1104
+ /** Id строки подробностей: адресат `aria-controls` у кнопки. */
1105
+ function detailRowId(row) {
1106
+ return `${expandDetailId}-${rowKeyValue(row)}`;
1107
+ }
1108
+ /**
1109
+ * Раскрытие и виртуализация вместе пока не работают: замер виртуализатора висит
1110
+ * на самой строке, и высота второго яруса в него не попадает — распорка
1111
+ * разъедется молча. Запрет снимется, когда пара строк переедет в свою группу
1112
+ * `<tbody>`; до тех пор лучше предупредить, чем показать прыгающую таблицу.
1113
+ */
1114
+ if (process.env.NODE_ENV !== "production") watchEffect(() => {
1115
+ if (props.expandable && props.virtual) console.warn("[granularity] GrDataTable: `expandable` вместе с `virtual` пока не поддержан — раскрытие выключено. Замер виртуализатора висит на строке и не видит высоту подробностей, поэтому распорки разъезжаются.");
1116
+ });
1117
+ const tableRef = ref(null);
1118
+ const rootId = useId();
1119
+ function rootEl() {
1120
+ return tableRef.value?.$el ?? null;
1121
+ }
1122
+ /** Скролл-контейнер таблицы — он же элемент с `maxHeight`. */
1123
+ function scrollEl() {
1124
+ const root = rootEl();
1125
+ return root?.matches("[data-gr-table-scroll]") ? root : root?.querySelector("[data-gr-table-scroll]") ?? null;
1126
+ }
1127
+ /**
1128
+ * Виртуализация строк.
1129
+ *
1130
+ * Распорки здесь не псевдоэлементы, как у списочных компонентов: `<tbody>`
1131
+ * игнорирует `padding`, а псевдоэлемент внутри группы строк не образует строку
1132
+ * с управляемой высотой. Поэтому срезанное держат две служебные `<tr>` — той же
1133
+ * формы, что строки загрузки и пустоты у `GrTable`.
1134
+ */
1135
+ const scrollContainer = ref(null);
1136
+ onMounted(() => {
1137
+ scrollContainer.value = scrollEl();
1138
+ });
1139
+ const virtualizer = useVirtualList({
1140
+ container: scrollContainer,
1141
+ count: () => props.virtual ? sortedRows.value.length : 0,
1142
+ source: () => sortedRows.value,
1143
+ itemSize: () => rowHeightEstimates[resolvedSize.value],
1144
+ viewportSize: () => typeof props.maxHeight === "number" ? props.maxHeight : void 0
1145
+ });
1146
+ /** Строки к отрисовке вместе с их абсолютной позицией в наборе. */
1147
+ const renderedRows = computed(() => {
1148
+ const rows = sortedRows.value;
1149
+ if (!props.virtual) return rows.map((row, index) => ({
1150
+ row,
1151
+ index
1152
+ }));
1153
+ const { start, end } = virtualizer.range.value;
1154
+ return rows.slice(start, end).map((row, offset) => ({
1155
+ row,
1156
+ index: start + offset
1157
+ }));
1158
+ });
1159
+ const spacerBefore = computed(() => props.virtual ? virtualizer.offset.value : 0);
1160
+ const spacerAfter = computed(() => props.virtual ? virtualizer.offsetEnd.value : 0);
1161
+ /** Ширина колонки: число трактуем как пиксели. */
1162
+ function columnWidthStyle(width) {
1163
+ if (width === void 0) return void 0;
1164
+ return { width: typeof width === "number" ? `${width}px` : width };
1165
+ }
1166
+ function scrollToRow(key, options) {
1167
+ const target = String(key);
1168
+ if (props.virtual) {
1169
+ const index = sortedRows.value.findIndex((row) => String(rowKeyValue(row)) === target);
1170
+ if (index < 0) return false;
1171
+ virtualizer.scrollToIndex(index);
1172
+ nextTick(() => {
1173
+ findRowEl(target)?.scrollIntoView?.(options ?? { block: "nearest" });
1174
+ });
1175
+ return true;
1176
+ }
1177
+ const row = findRowEl(target);
1178
+ if (!row) return false;
1179
+ row.scrollIntoView(options ?? { block: "nearest" });
1180
+ return true;
1181
+ }
1182
+ function findRowEl(rowKey) {
1183
+ const rows = rootEl()?.querySelectorAll("[data-gr-datatable-row]") ?? [];
1184
+ return Array.prototype.find.call(rows, (el) => el.dataset.rowKey === rowKey);
1185
+ }
1186
+ function scrollTo(options) {
1187
+ scrollEl()?.scrollTo(options);
1188
+ }
1189
+ __expose({
1190
+ /** Прокрутить к строке по её ключу. `false` — строки нет в DOM. */
1191
+ scrollToRow,
1192
+ /** Прокрутить скролл-контейнер таблицы. */
1193
+ scrollTo,
1194
+ /** Снять сортировку (эквивалент третьего состояния). */
1195
+ clearSort,
1196
+ /** Отметить/снять все выбираемые строки. */
1197
+ toggleAll,
1198
+ /**
1199
+ * Сбросить загруженные подробности: у одной строки либо у всех.
1200
+ * Нужен, когда запись изменилась на сервере, — само по себе раскрытие
1201
+ * держит загруженное, пока строка жива.
1202
+ */
1203
+ invalidateDetail
1204
+ });
1205
+ /**
1206
+ * Три семейства слотов по ключу колонки — `#cell-<key>` (`{ row, index }`),
1207
+ * `#header-<key>` (`{ column }`) и `#summary-<key>` (`{ value, column }`) —
1208
+ * попадают под общую индексную сигнатуру, а не под шаблонные.
1209
+ *
1210
+ * Шаблонные (`` `cell-${string}` ``) сужали бы тип слотов строже
1211
+ * `InternalSlots`, и дженерик-компонент переставал приниматься `h()`: ломался
1212
+ * бы рендер-функцией, оставаясь рабочим в шаблоне. Цена — пропсы этих трёх
1213
+ * семейств не типизированы; они описаны здесь и на странице компонента.
1214
+ */
1215
+ return (_ctx, _cache) => {
1216
+ return openBlock(), createBlock(GrTable_default, mergeProps(tableProps.value, {
1217
+ ref_key: "tableRef",
1218
+ ref: tableRef,
1219
+ "data-gr-datatable": ""
1220
+ }), createSlots({
1221
+ caption: withCtx(() => [renderSlot(_ctx.$slots, "caption", {}, () => [createTextVNode(toDisplayString(__props.caption), 1)]), createElementVNode("span", {
1222
+ id: `${unref(rootId)}-live`,
1223
+ "data-gr-datatable-live": "",
1224
+ role: "status",
1225
+ "aria-live": "polite"
1226
+ }, toDisplayString(liveMessage.value), 9, _hoisted_1)]),
1227
+ header: withCtx(() => [createElementVNode("tr", {
1228
+ "data-gr-datatable-header": "",
1229
+ "aria-rowindex": __props.virtual ? 1 : void 0
1230
+ }, [
1231
+ expandColumnVisible.value ? (openBlock(), createElementBlock("th", {
1232
+ key: 0,
1233
+ "data-gr-datatable-expand-head": "",
1234
+ scope: "col",
1235
+ class: normalizeClass([unref(expandColumnWidths)[unref(resolvedSize)], cellClass.value])
1236
+ }, null, 2)) : createCommentVNode("", true),
1237
+ __props.selectable ? (openBlock(), createElementBlock("th", {
1238
+ key: 1,
1239
+ ref: (el) => registerEl(unref(headerCellEls), unref(SELECT_COLUMN_KEY), el),
1240
+ class: normalizeClass(["text-left", [
1241
+ selectColumnClass.value,
1242
+ cellClass.value,
1243
+ unref(hasPinnedLeft) ? unref(columnPinnedClass) : ""
1244
+ ]]),
1245
+ style: normalizeStyle(unref(hasPinnedLeft) ? { left: "0px" } : void 0),
1246
+ scope: "col"
1247
+ }, [createVNode(GrCheckbox_default, {
1248
+ "data-gr-datatable-select-all": "",
1249
+ "model-value": unref(allSelected),
1250
+ indeterminate: unref(someSelected),
1251
+ size: checkboxSize.value,
1252
+ disabled: __props.loading || unref(selectableRows).length === 0,
1253
+ "aria-label": unref(t)("gr.dataTable.selectAll", "Select all rows"),
1254
+ "onUpdate:modelValue": unref(toggleAll)
1255
+ }, null, 8, [
1256
+ "model-value",
1257
+ "indeterminate",
1258
+ "size",
1259
+ "disabled",
1260
+ "aria-label",
1261
+ "onUpdate:modelValue"
1262
+ ])], 6)) : createCommentVNode("", true),
1263
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(orderedColumns), (col, colIndex) => {
1264
+ return openBlock(), createElementBlock("th", {
1265
+ key: col.key,
1266
+ ref_for: true,
1267
+ ref: (el) => registerEl(unref(headerCellEls), String(col.key), el),
1268
+ class: normalizeClass(["font-700", [
1269
+ headerTextClass.value,
1270
+ cellClass.value,
1271
+ cellAlign(col),
1272
+ __props.reorderableColumns || __props.resizableColumns ? "group relative" : "",
1273
+ unref(draggingColumnKey) === String(col.key) ? unref(columnDraggingClass) : "",
1274
+ unref(columnDropClass)(colIndex),
1275
+ ...unref(pinnedCellClass)(col, colIndex)
1276
+ ]]),
1277
+ "data-column-key": col.key,
1278
+ style: normalizeStyle({
1279
+ ...columnWidthStyle(unref(widthOf)(col)),
1280
+ ...unref(pinnedStyleOf)(col)
1281
+ }),
1282
+ "aria-sort": unref(ariaSortFor)(col),
1283
+ scope: "col"
1284
+ }, [createElementVNode("div", { class: normalizeClass(["inline-flex items-center", headerGapClass.value]) }, [__props.reorderableColumns ? (openBlock(), createElementBlock("button", {
1285
+ key: 0,
1286
+ ref_for: true,
1287
+ ref: (el) => registerEl(unref(columnHandleEls), String(col.key), el),
1288
+ type: "button",
1289
+ "data-gr-datatable-column-handle": "",
1290
+ class: normalizeClass([unref(columnHandleClass), unref(columnSort).isActive.value ? unref(columnHandleActiveClass) : ""]),
1291
+ tabindex: unref(columnRoving).tabindexFor(String(col.key)),
1292
+ "aria-label": columnHandleLabel(col),
1293
+ disabled: __props.loading,
1294
+ onPointerdown: ($event) => unref(columnSort).startFrom(String(col.key))($event),
1295
+ onKeydown: ($event) => unref(onColumnHandleKeydown)($event, String(col.key)),
1296
+ onFocus: ($event) => unref(columnRoving).setActive(String(col.key)),
1297
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
1298
+ }, [createVNode(unref(grip_vertical_default), {
1299
+ class: "block",
1300
+ "aria-hidden": "true"
1301
+ })], 42, _hoisted_4)) : createCommentVNode("", true), col.sortable ? (openBlock(), createElementBlock("button", {
1302
+ key: 1,
1303
+ type: "button",
1304
+ "data-gr-datatable-sort": "",
1305
+ class: normalizeClass(["inline-flex items-center text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] rounded-[var(--gr-radius-sm)]", headerGapClass.value]),
1306
+ onClick: ($event) => unref(toggleSort)(col)
1307
+ }, [
1308
+ createElementVNode("span", null, [renderSlot(_ctx.$slots, `header-${col.key}`, { column: col }, () => [createTextVNode(toDisplayString(col.label), 1)])]),
1309
+ createElementVNode("span", _hoisted_6, toDisplayString(sortHint(col)), 1),
1310
+ createElementVNode("span", _hoisted_7, [unref(currentSortKey) === col.key && unref(currentSortDir) === "asc" ? (openBlock(), createBlock(GrIcon_default, {
1311
+ key: 0,
1312
+ size: sortIconSize.value
1313
+ }, {
1314
+ default: withCtx(() => [createVNode(unref(arrow_up_default))]),
1315
+ _: 1
1316
+ }, 8, ["size"])) : unref(currentSortKey) === col.key && unref(currentSortDir) === "desc" ? (openBlock(), createBlock(GrIcon_default, {
1317
+ key: 1,
1318
+ size: sortIconSize.value
1319
+ }, {
1320
+ default: withCtx(() => [createVNode(unref(arrow_down_default))]),
1321
+ _: 1
1322
+ }, 8, ["size"])) : createCommentVNode("", true)])
1323
+ ], 10, _hoisted_5)) : (openBlock(), createElementBlock("span", _hoisted_8, [renderSlot(_ctx.$slots, `header-${col.key}`, { column: col }, () => [createTextVNode(toDisplayString(col.label), 1)])]))], 2), __props.resizableColumns ? (openBlock(), createElementBlock("span", {
1324
+ key: 0,
1325
+ "data-gr-datatable-column-resizer": "",
1326
+ role: "separator",
1327
+ tabindex: "0",
1328
+ "aria-orientation": "vertical",
1329
+ class: normalizeClass(unref(columnResizerClass)),
1330
+ "aria-label": resizerLabel(col),
1331
+ "aria-valuenow": unref(resizerValue)(col),
1332
+ "aria-valuemin": unref(48),
1333
+ "aria-valuetext": `${unref(resizerValue)(col)}px`,
1334
+ onPointerdown: ($event) => unref(onResizerPointerDown)($event, String(col.key)),
1335
+ onKeydown: ($event) => unref(onResizerKeydown)($event, String(col.key)),
1336
+ onDblclick: ($event) => unref(resetColumnWidth)(String(col.key)),
1337
+ onClick: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
1338
+ }, [createElementVNode("span", {
1339
+ class: normalizeClass([
1340
+ unref(columnResizerLineClass),
1341
+ unref(columnResizerHoverClass),
1342
+ unref(resizingKey) === String(col.key) ? unref(columnResizerLineActiveClass) : ""
1343
+ ]),
1344
+ "aria-hidden": "true"
1345
+ }, null, 2)], 42, _hoisted_9)) : createCommentVNode("", true)], 14, _hoisted_3);
1346
+ }), 128))
1347
+ ], 8, _hoisted_2)]),
1348
+ default: withCtx(() => [__props.loading ? (openBlock(), createElementBlock("tr", _hoisted_10, [createElementVNode("td", {
1349
+ colspan: totalColumns.value,
1350
+ class: normalizeClass(["text-center text-[var(--gr-muted-fg)]", placeholderClass.value])
1351
+ }, [renderSlot(_ctx.$slots, "loading", {}, () => [createElementVNode("span", _hoisted_12, [createVNode(unref(loader_circle_default), {
1352
+ class: normalizeClass(["block animate-spin", spinnerClass.value]),
1353
+ "aria-hidden": "true"
1354
+ }, null, 8, ["class"]), createElementVNode("span", null, toDisplayString(resolvedLoadingText.value), 1)])])], 10, _hoisted_11)])) : isEmpty.value ? (openBlock(), createElementBlock("tr", _hoisted_13, [createElementVNode("td", {
1355
+ colspan: totalColumns.value,
1356
+ class: normalizeClass(["text-center text-[var(--gr-muted-fg)]", placeholderClass.value])
1357
+ }, [renderSlot(_ctx.$slots, "empty", {}, () => [createTextVNode(toDisplayString(resolvedEmptyText.value), 1)])], 10, _hoisted_14)])) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
1358
+ __props.virtual && spacerBefore.value > 0 ? (openBlock(), createElementBlock("tr", _hoisted_15, [createElementVNode("td", {
1359
+ colspan: totalColumns.value,
1360
+ style: normalizeStyle({
1361
+ height: `${spacerBefore.value}px`,
1362
+ padding: "0",
1363
+ border: "0"
1364
+ })
1365
+ }, null, 12, _hoisted_16)])) : createCommentVNode("", true),
1366
+ (openBlock(true), createElementBlock(Fragment, null, renderList(renderedRows.value, ({ row, index }) => {
1367
+ return openBlock(), createElementBlock(Fragment, { key: unref(rowKeyValue)(row) }, [createElementVNode("tr", mergeProps({
1368
+ ref_for: true,
1369
+ ref: (el) => __props.virtual && unref(virtualizer).measure(index, el),
1370
+ class: ["border-t border-[var(--gr-brd)]", [unref(isRowSelected)(row) ? unref(rowSelectedClass) : "", rowClassName(row, index)]],
1371
+ "data-gr-datatable-row": "",
1372
+ "data-row-key": unref(rowKeyValue)(row),
1373
+ "data-selected": __props.selectable && unref(isRowSelected)(row) ? "true" : void 0,
1374
+ "aria-rowindex": ariaRowIndex(index)
1375
+ }, { ref_for: true }, __props.rowProps?.(row, index), { onClick: ($event) => onRowClick(row, index, $event) }), [
1376
+ expandColumnVisible.value ? (openBlock(), createElementBlock("td", {
1377
+ key: 0,
1378
+ "data-gr-datatable-expand-cell": "",
1379
+ class: normalizeClass(["text-left", [unref(expandColumnWidths)[unref(resolvedSize)], cellClass.value]])
1380
+ }, [unref(isRowExpandable)(row) ? (openBlock(), createElementBlock("button", {
1381
+ key: 0,
1382
+ type: "button",
1383
+ "data-gr-datatable-expand": "",
1384
+ class: normalizeClass([unref(expandButtonClass), checkboxSize.value === "xs" ? "h-5 w-5" : "h-6 w-6"]),
1385
+ "aria-expanded": unref(isRowExpanded)(row),
1386
+ "aria-controls": detailRowId(row),
1387
+ "aria-label": unref(isRowExpanded)(row) ? unref(t)("gr.dataTable.collapseRow", "Hide details") : unref(t)("gr.dataTable.expandRow", "Show details"),
1388
+ onClick: withModifiers(($event) => unref(toggleRowExpanded)(row), ["stop"])
1389
+ }, [renderSlot(_ctx.$slots, "expand-icon", {
1390
+ expanded: unref(isRowExpanded)(row),
1391
+ row
1392
+ }, () => [createVNode(unref(chevron_right_default), { class: normalizeClass([unref(expandIconClass), unref(isRowExpanded)(row) ? unref(expandIconOpenClass) : ""]) }, null, 8, ["class"])])], 10, _hoisted_18)) : createCommentVNode("", true)], 2)) : createCommentVNode("", true),
1393
+ __props.selectable ? (openBlock(), createElementBlock("td", {
1394
+ key: 1,
1395
+ class: normalizeClass(["text-left", [
1396
+ selectColumnClass.value,
1397
+ cellClass.value,
1398
+ unref(hasPinnedLeft) ? unref(columnPinnedClass) : "",
1399
+ unref(hasPinnedLeft) && unref(isRowSelected)(row) ? unref(rowSelectedClass) : ""
1400
+ ]]),
1401
+ style: normalizeStyle(unref(hasPinnedLeft) ? { left: "0px" } : void 0)
1402
+ }, [unref(isRowSelectable)(row) ? (openBlock(), createBlock(GrCheckbox_default, {
1403
+ key: 0,
1404
+ "data-gr-datatable-select-row": "",
1405
+ "model-value": unref(isRowSelected)(row),
1406
+ size: checkboxSize.value,
1407
+ "aria-label": unref(t)("gr.dataTable.selectRow", "Select row"),
1408
+ onClick: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"])),
1409
+ "onUpdate:modelValue": ($event) => unref(toggleRow)(row)
1410
+ }, null, 8, [
1411
+ "model-value",
1412
+ "size",
1413
+ "aria-label",
1414
+ "onUpdate:modelValue"
1415
+ ])) : createCommentVNode("", true)], 6)) : createCommentVNode("", true),
1416
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(orderedColumns), (col, colIndex) => {
1417
+ return openBlock(), createElementBlock("td", {
1418
+ key: col.key,
1419
+ class: normalizeClass([
1420
+ cellClass.value,
1421
+ cellAlign(col),
1422
+ ...unref(pinnedCellClass)(col, colIndex),
1423
+ col.pinned && unref(isRowSelected)(row) ? unref(rowSelectedClass) : ""
1424
+ ]),
1425
+ style: normalizeStyle({
1426
+ ...columnWidthStyle(unref(widthOf)(col)),
1427
+ ...unref(pinnedStyleOf)(col)
1428
+ })
1429
+ }, [renderSlot(_ctx.$slots, `cell-${col.key}`, {
1430
+ row,
1431
+ index,
1432
+ expanded: unref(isRowExpanded)(row),
1433
+ toggleExpand: () => unref(toggleRowExpanded)(row)
1434
+ }, () => [createElementVNode("span", null, toDisplayString(cellValue(row, col.key)), 1)])], 6);
1435
+ }), 128))
1436
+ ], 16, _hoisted_17), unref(isRowExpanded)(row) ? (openBlock(), createElementBlock("tr", {
1437
+ key: 0,
1438
+ id: detailRowId(row),
1439
+ "data-gr-datatable-detail": "",
1440
+ "data-gr-table-off-grid": "",
1441
+ class: normalizeClass(unref(detailRowClass))
1442
+ }, [createElementVNode("td", {
1443
+ colspan: totalColumns.value,
1444
+ class: normalizeClass([unref(detailCellClass), unref(detailPaddings)[unref(resolvedSize)]])
1445
+ }, [createElementVNode("div", { class: normalizeClass(unref(detailContentClass)) }, [unref(detailStateOf)(row) === "loading" ? (openBlock(), createElementBlock("div", _hoisted_21, [renderSlot(_ctx.$slots, "detail-loading", { row }, () => [createVNode(GrSkeleton_default, { count: 2 })])])) : unref(detailStateOf)(row) === "error" ? (openBlock(), createElementBlock("div", {
1446
+ key: 1,
1447
+ class: normalizeClass(unref(detailErrorClass)),
1448
+ "data-gr-datatable-detail-error": ""
1449
+ }, [renderSlot(_ctx.$slots, "detail-error", {
1450
+ row,
1451
+ retry: () => unref(retryDetail)(row)
1452
+ }, () => [createElementVNode("span", null, toDisplayString(unref(t)("gr.dataTable.detailError", "Could not load details")), 1), createVNode(GrButton_default, {
1453
+ size: "xs",
1454
+ tone: "danger",
1455
+ variant: "outline",
1456
+ onClick: ($event) => unref(retryDetail)(row)
1457
+ }, {
1458
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("gr.dataTable.detailRetry", "Retry")), 1)]),
1459
+ _: 1
1460
+ }, 8, ["onClick"])])], 2)) : renderSlot(_ctx.$slots, "detail", {
1461
+ row,
1462
+ index,
1463
+ data: unref(detailDataOf)(row),
1464
+ state: unref(detailStateOf)(row)
1465
+ }, void 0, void 0, 2)], 2)], 10, _hoisted_20)], 10, _hoisted_19)) : createCommentVNode("", true)], 64);
1466
+ }), 128)),
1467
+ __props.virtual && spacerAfter.value > 0 ? (openBlock(), createElementBlock("tr", _hoisted_22, [createElementVNode("td", {
1468
+ colspan: totalColumns.value,
1469
+ style: normalizeStyle({
1470
+ height: `${spacerAfter.value}px`,
1471
+ padding: "0",
1472
+ border: "0"
1473
+ })
1474
+ }, null, 12, _hoisted_23)])) : createCommentVNode("", true)
1475
+ ], 64))]),
1476
+ _: 2
1477
+ }, [_ctx.$slots.footer || hasSummary.value ? {
1478
+ name: "footer",
1479
+ fn: withCtx(() => [hasSummary.value ? (openBlock(), createElementBlock("tr", {
1480
+ key: 0,
1481
+ "data-gr-datatable-summary": "",
1482
+ class: normalizeClass(unref(summaryRowClass)),
1483
+ "aria-rowindex": summaryAriaRowIndex.value
1484
+ }, [__props.selectable ? (openBlock(), createElementBlock("td", {
1485
+ key: 0,
1486
+ class: normalizeClass([
1487
+ selectColumnClass.value,
1488
+ cellClass.value,
1489
+ unref(hasPinnedLeft) ? unref(columnPinnedClass) : ""
1490
+ ]),
1491
+ style: normalizeStyle(unref(hasPinnedLeft) ? { left: "0px" } : void 0)
1492
+ }, null, 6)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(orderedColumns), (col, colIndex) => {
1493
+ return openBlock(), createElementBlock("td", {
1494
+ key: col.key,
1495
+ "data-column-key": col.key,
1496
+ class: normalizeClass([
1497
+ cellClass.value,
1498
+ cellAlign(col),
1499
+ ...unref(pinnedCellClass)(col, colIndex)
1500
+ ]),
1501
+ style: normalizeStyle({
1502
+ ...columnWidthStyle(unref(widthOf)(col)),
1503
+ ...unref(pinnedStyleOf)(col)
1504
+ })
1505
+ }, [renderSlot(_ctx.$slots, `summary-${col.key}`, {
1506
+ value: summaryValue(col.key),
1507
+ column: col
1508
+ }, () => [createElementVNode("span", null, toDisplayString(summaryValue(col.key)), 1)])], 14, _hoisted_25);
1509
+ }), 128))], 10, _hoisted_24)) : createCommentVNode("", true), renderSlot(_ctx.$slots, "footer", {
1510
+ columns: unref(orderedColumns),
1511
+ totalColumns: totalColumns.value
1512
+ })]),
1513
+ key: "0"
1514
+ } : void 0]), 1040);
1515
+ };
1516
+ }
1517
+ });
1518
+ //#endregion
1519
+ export { GrDataTable_default as t };