ics-ui-kit 0.1.13 → 0.1.14

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 (105) hide show
  1. package/dist/{AlertIcon-5B6AgZpe.js → AlertIcon-BphnEYvV.js} +3 -3
  2. package/dist/AlertIcon-BphnEYvV.js.map +1 -0
  3. package/dist/Field-CP_-li9L.js +90 -0
  4. package/dist/Field-CP_-li9L.js.map +1 -0
  5. package/dist/{FormFooterTemplate-ClTxqrIB.js → FormFooterTemplate-Cfn5Nmge.js} +2 -2
  6. package/dist/{FormFooterTemplate-ClTxqrIB.js.map → FormFooterTemplate-Cfn5Nmge.js.map} +1 -1
  7. package/dist/{FormStack-B3UBh9oF.js → FormStack-Cc_ELdWL.js} +2 -2
  8. package/dist/{FormStack-B3UBh9oF.js.map → FormStack-Cc_ELdWL.js.map} +1 -1
  9. package/dist/{SearchSelectTag-DFCX9zT1.js → SearchSelectTag-CA7Ie3Nx.js} +2 -2
  10. package/dist/{SearchSelectTag-DFCX9zT1.js.map → SearchSelectTag-CA7Ie3Nx.js.map} +1 -1
  11. package/dist/TableFillHandle-C_yvCTSl.js +65 -0
  12. package/dist/TableFillHandle-C_yvCTSl.js.map +1 -0
  13. package/dist/TextOverflowTooltip-B1I5qwAT.js.map +1 -1
  14. package/dist/blocks/auth/otp-form.js +1 -1
  15. package/dist/components/alert/Alert.d.ts +4 -0
  16. package/dist/components/alert/Alert.stories.d.ts +1 -0
  17. package/dist/components/alert-dialog.js +1 -1
  18. package/dist/components/alert.js +18 -18
  19. package/dist/components/alert.js.map +1 -1
  20. package/dist/components/card.js +1 -1
  21. package/dist/components/dialog.js +1 -1
  22. package/dist/components/field/Field.d.ts +4 -0
  23. package/dist/components/field/Field.stories.d.ts +1 -0
  24. package/dist/components/field.js +1 -1
  25. package/dist/components/file-upload.js +1 -1
  26. package/dist/components/form.js +3 -3
  27. package/dist/components/search-select.js +1 -1
  28. package/dist/components/sidebar.js +204 -210
  29. package/dist/components/sidebar.js.map +1 -1
  30. package/dist/components/system-state.js +1 -1
  31. package/dist/components/table/Table.d.ts +109 -5
  32. package/dist/components/table/Table.stories.d.ts +285 -9
  33. package/dist/components/table/column-reorder/index.d.ts +1 -0
  34. package/dist/components/table/column-reorder/useColumnReorder.d.ts +82 -0
  35. package/dist/components/table/column-reorder.d.ts +2 -0
  36. package/dist/components/table/column-reorder.js +6 -0
  37. package/dist/components/table/column-reorder.js.map +1 -0
  38. package/dist/components/table/column-resize/columnAutoFit.d.ts +21 -0
  39. package/dist/components/table/column-resize/columnResizeSession.d.ts +9 -0
  40. package/dist/components/table/column-resize/index.d.ts +3 -0
  41. package/dist/components/table/column-resize/useColumnResize.d.ts +52 -0
  42. package/dist/components/table/column-resize.d.ts +2 -0
  43. package/dist/components/table/column-resize.js +7 -0
  44. package/dist/components/table/column-resize.js.map +1 -0
  45. package/dist/components/table/editable/TableFillHandle.d.ts +23 -0
  46. package/dist/components/table/editable/cellRange.d.ts +83 -0
  47. package/dist/components/table/editable/editableCell.d.ts +90 -0
  48. package/dist/components/table/editable/index.d.ts +3 -0
  49. package/dist/components/table/editable.d.ts +2 -0
  50. package/dist/components/table/editable.js +12 -0
  51. package/dist/components/table/editable.js.map +1 -0
  52. package/dist/components/table/index.d.ts +8 -1
  53. package/dist/components/table/opaqueCell.d.ts +47 -0
  54. package/dist/components/table/rowTint.d.ts +60 -0
  55. package/dist/components/table/sticky/index.d.ts +1 -0
  56. package/dist/components/table/sticky/stickyCell.d.ts +105 -0
  57. package/dist/components/table/sticky.d.ts +2 -0
  58. package/dist/components/table/sticky.js +22 -0
  59. package/dist/components/table/sticky.js.map +1 -0
  60. package/dist/components/table/tableStoryData.d.ts +84 -0
  61. package/dist/components/table/tableStoryParts.d.ts +72 -0
  62. package/dist/components/table/tree/TableTree.d.ts +106 -0
  63. package/dist/components/table/tree/index.d.ts +3 -0
  64. package/dist/components/table/tree/treeGeometry.d.ts +86 -0
  65. package/dist/components/table/tree/treeModel.d.ts +87 -0
  66. package/dist/components/table/tree.d.ts +2 -0
  67. package/dist/components/table/tree.js +19 -0
  68. package/dist/components/table/tree.js.map +1 -0
  69. package/dist/components/table.js +52 -79
  70. package/dist/components/table.js.map +1 -1
  71. package/dist/components/tag-input.js +1 -1
  72. package/dist/components/toolbar.js +1 -2
  73. package/dist/components/toolbar.js.map +1 -1
  74. package/dist/hooks.js +1 -1
  75. package/dist/lib/utils/dom.d.ts +72 -0
  76. package/dist/opaqueCell-SGaiyL54.js +31 -0
  77. package/dist/opaqueCell-SGaiyL54.js.map +1 -0
  78. package/dist/plus-BpxYPnAG.js +15 -0
  79. package/dist/plus-BpxYPnAG.js.map +1 -0
  80. package/dist/styles-scoped.css +315 -72
  81. package/dist/styles.css +315 -72
  82. package/dist/theme.css +2 -0
  83. package/dist/treeModel-BlPWVj4W.js +319 -0
  84. package/dist/treeModel-BlPWVj4W.js.map +1 -0
  85. package/dist/useBreakpoints-DHvp74UL.js +31 -0
  86. package/dist/useBreakpoints-DHvp74UL.js.map +1 -0
  87. package/dist/useColumnReorder-BxVZQ_Y5.js +119 -0
  88. package/dist/useColumnReorder-BxVZQ_Y5.js.map +1 -0
  89. package/dist/useColumnResize-wjfEyBBg.js +130 -0
  90. package/dist/useColumnResize-wjfEyBBg.js.map +1 -0
  91. package/dist/vendors/date-fns/index.d.ts +7 -14
  92. package/dist/vendors/date-fns/locale/en-US/index.d.ts +4 -0
  93. package/dist/vendors/date-fns/locale/ru/index.d.ts +4 -0
  94. package/dist/vendors/react-day-picker/index.d.ts +8 -17
  95. package/dist/vendors/react-hook-form/index.d.ts +8 -15
  96. package/dist/vendors/tanstack/react-table/index.d.ts +12 -0
  97. package/dist/vendors/zod/index.d.ts +8 -9
  98. package/package.json +3 -2
  99. package/tailwind.preset.js +6 -2
  100. package/dist/AlertIcon-5B6AgZpe.js.map +0 -1
  101. package/dist/Field-COD9VbUX.js +0 -86
  102. package/dist/Field-COD9VbUX.js.map +0 -1
  103. package/dist/components/data-table/DataTable.stories.d.ts +0 -8
  104. package/dist/useBreakpoints-D8wKOvil.js +0 -103
  105. package/dist/useBreakpoints-D8wKOvil.js.map +0 -1
@@ -3,13 +3,289 @@ import { Table } from './Table';
3
3
  declare const meta: Meta<typeof Table>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof Table>;
6
- export declare const Default: Story;
7
- export declare const WithCheckboxes: Story;
8
- export declare const WithActions: Story;
9
- export declare const WithDropdownMenu: Story;
10
- export declare const WithSortableHeaders: Story;
11
- export declare const WithFooter: Story;
12
- export declare const LargeDataExample: Story;
6
+ export declare const TableBase: Story;
7
+ /**
8
+ * Подсветка строк слоями. Механизм целиком описан в разделе «Раскраска строк».
9
+ *
10
+ * В примере три прикладных состояния: выделение чекбоксом, деактивация и просрочка.
11
+ * Для первых двух взяты готовые цвета кита (`TABLE_ROW_TINT`), для просрочки рядом положен
12
+ * свой — `hsl(var(--status-warning) / 12%)`. Колонка «Слои» перечисляет то, что применено
13
+ * к строке; на последней строке сходятся все три и видны разом.
14
+ *
15
+ * Две нижние строки вдобавок перекрашены целиком — своя заливка через
16
+ * `className="bg-… hover:bg-…"`. Слои ложатся на них так же, как на строки со штатной
17
+ * заливкой, и наведение продолжает работать.
18
+ */
19
+ export declare const RowTints: StoryObj;
13
20
  export declare const ScrollableTable: Story;
14
- export declare const WithScrollShadow: Story;
15
- export declare const EmptyState: Story;
21
+ /**
22
+ * Итоговая строка и подпись таблицы.
23
+ *
24
+ * **Почему итоги — `TableFooter`, а не последняя строка тела.** Итог не запись, а сводка
25
+ * по записям, и из `tbody` он должен выпадать сам собой: сортировка, фильтр, выделение
26
+ * и «отметить все» работают со строками тела, и особая последняя строка каждый раз была бы
27
+ * исключением, про которое надо помнить. Скринридер читает `tfoot` так же — как итог,
28
+ * а не как ещё одного дистрибьютора.
29
+ *
30
+ * Считает итоги вызывающая сторона: кит не знает, что складывать, что усреднять, а что
31
+ * не сводится вовсе. Здесь суммируются штуки и рубли, а рост оставлен прочерком — среднее
32
+ * по процентам без весов врёт, и честнее показать это прямо в примере.
33
+ *
34
+ * Верхнюю линию итогов рисуют ячейки, а не сам `tfoot`: в раздельной модели границ
35
+ * (`border-separate` у `Table`) собственная граница групп строк игнорируется.
36
+ *
37
+ * **Подпись — `caption`, а не абзац рядом с таблицей.** `caption` связан с таблицей
38
+ * разметкой, а не соседством: скринридер называет им таблицу при входе, а перенос таблицы
39
+ * в другое место не оставляет подпись позади. Стоит она под таблицей (`caption-bottom`
40
+ * у `Table`) — сверху обычно уже есть заголовок раздела, и вторая надпись спорила бы с ним.
41
+ */
42
+ export declare const TotalsAndCaption: Story;
43
+ /**
44
+ * Итоги, которые не уезжают: `sticky` у шапки держит её у верха контейнера, `sticky` у итогов —
45
+ * у низа. Между ними прокручивается тело.
46
+ *
47
+ * Прокручивается при этом контейнер таблицы, как в «Scrollable Table», поэтому таблица
48
+ * остаётся таблицей: ширины колонок считает браузер, `colSpan` в итогах работает, разметка
49
+ * ничем не отличается от обычной. Обоим пропам нужна ограниченная высота у контейнера
50
+ * (`containerClassName="h-80"`): без неё прокручивать нечего, а `position: sticky` без
51
+ * прокрутки ничего не делает.
52
+ *
53
+ * Слой у шапки и итогов один (`z-20`): друг с другом они не спорят — стоят по разные стороны
54
+ * тела, — но оба обязаны перекрывать проезжающие под ними строки.
55
+ */
56
+ export declare const StickyTotals: Story;
57
+ /**
58
+ * Прокрутка по обеим осям: шапка прилипает сверху, первые две колонки — слева.
59
+ *
60
+ * Закреплённых колонок две, поэтому `left` второй равен ширине первой (`ACCOUNT_WIDTH`).
61
+ * Случай, когда ширины меняются на ходу, разобран в «Sticky + Resizable Columns».
62
+ *
63
+ * Ширину таблице диктует `w-max`: она растягивается по содержимому, а незакреплённые
64
+ * колонки сами берут нужный размер.
65
+ */
66
+ export declare const StickyColumns: Story;
67
+ type ResizableColumnsArgs = {
68
+ handleScope: "header" | "table";
69
+ };
70
+ /**
71
+ * Ресайз колонок перетаскиванием и автофит по двойному клику.
72
+ *
73
+ * Колонка «Условия работы» специально многострочная: автофит и `table-layout: fixed`
74
+ * проверяются в том числе на переносе текста.
75
+ *
76
+ * Заголовок обрезается вложенным `<span>`: `overflow: hidden` на самой ячейке срезал бы
77
+ * выступающую половину зоны захвата, и граница колонки перестала бы ловить курсор.
78
+ *
79
+ * `handleScope` переключает зону захвата. `header` — ручка только в шапке. `table` — ещё
80
+ * и в каждой ячейке тела, так что вдоль всей границы колонки получается непрерывная полоса,
81
+ * как в Excel.
82
+ *
83
+ * Полоса собрана из посегментных ручек намеренно. Растянуть одну ручку из шапки на всю
84
+ * высоту нельзя: она позиционирована абсолютно, и большая высота раздувает `scrollHeight`
85
+ * контейнера, добавляя пустую вертикальную прокрутку. Вариант с замером высоты таблицы
86
+ * через `ResizeObserver` добавляет к сторибучному примеру наблюдатель и лишний ререндер
87
+ * на каждое изменение размера — тут это не окупается. Цена посегментного варианта —
88
+ * по ручке на ячейку; для больших таблиц её стоит заменить на оверлей поверх таблицы.
89
+ *
90
+ * Чтобы подсвечивалась граница целиком, наведённая колонка хранится в состоянии
91
+ * и раздаётся всем её ручкам через `active`.
92
+ *
93
+ * Обёртке заданы `w-fit max-w-full`: по умолчанию она `w-full` и, когда таблица уже
94
+ * контейнера, справа остаётся пустая рамка. `w-fit` прижимает её к ширине таблицы,
95
+ * `max-w-full` не даёт вылезти за экран и включает горизонтальную прокрутку,
96
+ * когда колонки растянули шире доступного места.
97
+ */
98
+ export declare const ResizableColumns: StoryObj<ResizableColumnsArgs>;
99
+ type StickyResizableColumnsArgs = {
100
+ pinnedCount: number;
101
+ };
102
+ /**
103
+ * Закреплённые колонки вместе с изменяемой шириной.
104
+ *
105
+ * Переключатель числа закреплённых колонок здесь ради того, что начинается со второй
106
+ * колонки: её `left` равен **сумме ширин всех предыдущих закреплённых**, и у каждой
107
+ * следующей значение своё. Считает его `stickyOffset`. Под заголовком закреплённой колонки
108
+ * показано текущее значение — переключите количество и посмотрите, как растёт ряд.
109
+ * Минимальный случай с одной колонкой и константой `left-0` — в «Sticky Columns».
110
+ *
111
+ * Отсюда два следствия, которые видны начиная со второй колонки:
112
+ *
113
+ * 1. Ширины обязаны быть известны заранее, поэтому таблице нужен `table-layout: fixed`.
114
+ * При `auto` браузер считает ширины по содержимому, заданные значения игнорирует,
115
+ * и вычисленный `left` промахивается.
116
+ * 2. Закреплённые колонки должны помещаться в контейнер — они занимают его левый край
117
+ * насовсем. Закрепите четыре и сузьте окно: прокручивать станет нечего.
118
+ *
119
+ * **Как это уживается с ресайзом.** Ширины берутся из `useColumnResize` — того же источника,
120
+ * которым таблица задаёт `width` колонкам, так что после каждой правки `left` пересчитывается
121
+ * сам. Есть ровно одна шероховатость: **во время** перетаскивания ручки хук пишет ширину прямо
122
+ * в DOM и React не трогает, поэтому вычисленный из состояния `left` отстаёт до отпускания
123
+ * кнопки — следующая закреплённая колонка на это время наезжает на предыдущую.
124
+ *
125
+ * Лечится тем же приёмом с другой стороны: пока ручку тянут, `left` дописывается прямо в DOM
126
+ * на тех же событиях мыши — тоже без единого ререндера. Слушатель вешается поверх
127
+ * `getResizeHandleProps` и снимается на `mouseup`; менять ширину закреплённой колонки больше
128
+ * нечему, поэтому наблюдать за раскладкой целиком (`ResizeObserver`) незачем.
129
+ */
130
+ export declare const StickyResizableColumns: StoryObj<StickyResizableColumnsArgs>;
131
+ /**
132
+ * Ресайз плюс перетаскивание колонок.
133
+ *
134
+ * Перетаскивание — `useColumnReorder`: колонка едет за курсором, соседи разъезжаются,
135
+ * отпускание укладывает её в новый слот. Разбор, почему здесь это дешевле DnD-библиотеки,
136
+ * лежит в документации самого хука.
137
+ *
138
+ * Драг начинается с грипа: нажатие по всей ячейке конфликтовало бы с ресайзером, который
139
+ * тоже стартует в шапке.
140
+ *
141
+ * Размер шрифта оставлен `text-xs` от таблицы. В макете этот вариант шапки нарисован
142
+ * в размере `sm` (14/20), но вся остальная таблица собрана в `xs` (12/16), и мешать
143
+ * их в одной таблице было бы хуже, чем разойтись с документационным фреймом.
144
+ */
145
+ export declare const ReorderableColumns: StoryObj;
146
+ /**
147
+ * Инлайн-редактирование: выделение диапазона, копирование, автозаполнение, readonly.
148
+ *
149
+ * Внешний вид сетки лежит в модуле `editable`: заливку ячейки даёт `cellStateStyles`, рамку
150
+ * диапазона — `cellRangeEdges`, ручку — `TableFillHandle`, поле правки — `cellEditorStyles`.
151
+ * Здесь остаётся механика: выделение, клавиатура, буфер обмена, автозаполнение.
152
+ *
153
+ * Состояния взяты из макета `Table / Cell` и разложены по токенам кита:
154
+ *
155
+ * | состояние | токен |
156
+ * |------------|-------|
157
+ * | read-only | `bg-secondary-bg-hover` (#f1f5f9) |
158
+ * | selected | рамка 2px `--primary-accent` по краю диапазона |
159
+ * | editing | `bg-status-warning-bg-hover` (#ffedd5) + `shadow-focus` |
160
+ * | modified | `bg-status-warning-bg-hover` (#ffedd5) |
161
+ * | fill-source| `bg-primary-bg-hover` (#e2e8f0) |
162
+ * | drag-range | рамка 1px `--primary-border` (#cbd5e1) с альфой 0.75 |
163
+ *
164
+ * Копирование отдаёт TSV: так вставка попадает в Excel и Google Sheets по ячейкам.
165
+ * `navigator.clipboard` в iframe Storybook может быть недоступен, поэтому есть запасной
166
+ * путь через скрытую `textarea` и `execCommand`.
167
+ *
168
+ * Над таблицей — `Toolbar` с откатом правок. Кнопка недоступна, пока правок нет, и держит
169
+ * в подписи их число: отмена оптом требует понимания, сколько именно будет потеряно.
170
+ * Откат возвращает исходные данные вместе с признаком изменённых ячеек и состоянием
171
+ * редактирования, поэтому таблица приходит ровно в тот вид, с которого стори открывается.
172
+ *
173
+ * **Каретка в точку двойного клика.** Поле ввода появляется уже после клика, поэтому
174
+ * по умолчанию каретка встаёт в начало. Координата запоминается на `dblclick` и применяется
175
+ * при фокусе через `placeCaretAtPoint` (`lib/utils/dom`). Содержимое `input` лежит вне
176
+ * DOM-дерева, и `caretPositionFromPoint` вернул бы сам элемент, поэтому индекс считается
177
+ * измерением текста на canvas. Приём взят из MDT (`_core/scripts/helpers/utils/placeCaret.ts`,
178
+ * применение — `inline/scripts/transformers/CellTransformer.ts`).
179
+ *
180
+ * У числовых колонок каретка может промахнуться на пару пикселей: на экране стоит
181
+ * отформатированное значение с разделителем разрядов, а в поле — сырое число, и ширины
182
+ * строк слегка разъезжаются.
183
+ *
184
+ * **Многострочная колонка.** «Комментарий» хранит текст с переносами и правится редактируемым
185
+ * `div` (`MultilineCellEditor`): у `textarea` собственная высота и свой скролл, из-за чего
186
+ * ячейка прыгала бы при входе в правку и держала бы фиксированный размер по мере набора.
187
+ * Каретка ставится штатным `caretPositionFromPoint` (`placeCaretAtClientPoint`) — содержимое
188
+ * `div` есть в DOM, поэтому точка попадает в нужную строку.
189
+ * Enter сохраняет, перенос — Shift+Enter или Alt+Enter, как в Excel. Вставка из буфера
190
+ * принудительно приводится к тексту: иначе `contenteditable` примет чужую разметку.
191
+ *
192
+ * В режиме просмотра текст обрезан тремя строками, чтобы одна ячейка не задирала высоту всей
193
+ * строки; из-за этого двойной клик по обрезанной третьей строке может поставить каретку
194
+ * на пару символов мимо. Копирование отдаёт такие значения в кавычках, по правилу CSV —
195
+ * иначе перенос развалил бы строку TSV при вставке.
196
+ *
197
+ * **Клавиатура.** Раскладка снята с MDT и Excel:
198
+ *
199
+ * | клавиши | действие |
200
+ * |---------|----------|
201
+ * | стрелки | активная ячейка; Shift — расширить выделение, Ctrl — прыжок к краю |
202
+ * | Home / End | первая / последняя колонка, с Ctrl — ещё и строка |
203
+ * | Enter, Tab | сохранить и перейти (Shift — в обратную сторону) |
204
+ * | F2 | редактирование, каретка в конце |
205
+ * | любой символ | быстрое редактирование: набранное заменяет значение |
206
+ * | Escape | отменить правку, иначе — снять выделение |
207
+ * | Delete | очистить выделенные ячейки |
208
+ * | Ctrl+C | скопировать выделение как TSV |
209
+ *
210
+ * Ключевое различие — «полное» и «быстрое» редактирование (`EditState.full`): в первом
211
+ * стрелки двигают каретку внутри поля, во втором сохраняют и переводят ячейку. Enter и Tab
212
+ * переводят активную ячейку, как в Excel.
213
+ *
214
+ * Tab перехватывается только при наличии активной ячейки — иначе фокус запирался бы
215
+ * в таблице. Выход с клавиатуры: Escape снимает выделение, следующий Tab уводит дальше.
216
+ */
217
+ export declare const InlineEditing: StoryObj;
218
+ /**
219
+ * Список пользователей с шапкой раздела и тулбаром.
220
+ *
221
+ * Выделение чекбоксами и меню «Активность», которое реально включает и выключает
222
+ * отмеченные строки. Обе подсветки — слои `tableRowTints()` с готовыми цветами кита
223
+ * (`TABLE_ROW_TINT`); строка о состояниях ничего не знает, их собирает прикладной код.
224
+ *
225
+ * Слои складываются альфами, поэтому выделенная деактивированная строка видна и как
226
+ * выделенная, и как деактивированная. Так и должно быть — выделяют её обычно затем,
227
+ * чтобы включить обратно.
228
+ *
229
+ * Часть строк деактивирована и выделена изначально — так оба состояния видно сразу.
230
+ * Индексы деактивированных те же, что на макете. После применения действия выделение
231
+ * сбрасывается — так же, как на макете, где чекбоксы уже сняты.
232
+ *
233
+ * Контент — простой текст, как в остальных стори; бейджи и селекты из макета остаются
234
+ * отдельной задачей.
235
+ */
236
+ export declare const UserListWithToolbar: StoryObj;
237
+ /**
238
+ * Колоночное меню: быстрый поиск, сортировка и фильтр по значениям.
239
+ *
240
+ * **Триггер — вся ячейка шапки**: заголовок сам служит кнопкой. Разметка триггера вместе
241
+ * с разбором, почему кликабельна кнопка внутри `th`, вынесена в `ColumnHeaderMenu`
242
+ * (`tableStoryParts.tsx`).
243
+ *
244
+ * **Фон ячейки** — два токена из макета шапки: наведение `secondary-bg-hover`, раскрытое
245
+ * меню `primary-bg-hover` (насыщеннее). Оба живут в `TableHead` под флагом `interactive`.
246
+ *
247
+ * **Содержимое меню** повторяет макет `Results`: группа сортировки, разделитель, группа
248
+ * фильтра с поиском по значениям и списком галочек, разделитель, «Очистить все». Сверху
249
+ * добавлена группа быстрого поиска по подстроке. Всё собрано из готовых примитивов кита —
250
+ * `DropdownMenuGroup` / `DropdownMenuLabel` / `DropdownMenuSearchItem` /
251
+ * `DropdownMenuRadioGroup` / `DropdownMenuCheckboxItem` / `DropdownMenuSeparator`;
252
+ * длинный список значений прокручивается в `ScrollShadowContainer`.
253
+ *
254
+ * Список галочек строится по всем строкам таблицы: при построении по видимым снятая галочка
255
+ * убирала бы из списка саму себя, и вернуть её было бы нечем.
256
+ */
257
+ export declare const ColumnMenu: StoryObj;
258
+ /**
259
+ * Иерархия строк с линиями связи.
260
+ *
261
+ * **Отрисовка.** Ячейка — `position: relative` с растущим по глубине `padding-left`, внутри
262
+ * абсолютно позиционированные спаны толщиной 1px. Вертикали выходят за padding-box на 1px
263
+ * сверху и снизу: нижняя граница ячейки лежит вне его, и вынос делает линию сплошной.
264
+ * У `end` вертикаль обрывается на коннекторе — это и даёт уголок. Спуск к первому потомку
265
+ * рисует строка родителя: вертикаль потомка начинается от его собственной строки, поэтому
266
+ * участок под раскрывателем остаётся за родителем.
267
+ *
268
+ * Коннектор привязан к центру первой строки текста: при переносе длинной подписи он
269
+ * остаётся напротив узла.
270
+ *
271
+ * Раскрыватель центрируется точно на вертикали своего уровня
272
+ * (`padding-left = treeLineX(level) - TREE_TOGGLE / 2`), поэтому линии детей выходят ровно
273
+ * из его центра. У листьев на его месте распорка той же ширины, чтобы подписи соседних
274
+ * строк стояли на одной вертикали. Сторона квадрата равна стороне чекбокса, а зона нажатия
275
+ * расширена до 24px невидимым псевдоэлементом: сам квадрат остаётся на сетке линий.
276
+ *
277
+ * Вся разметка линий размечена data-атрибутами: на обёртке лежит модель строки целиком
278
+ * (`data-tree-lines="pass none end"`), на каждом спане — его роль и номер колонки. В DevTools
279
+ * рядом с нарисованным видно и то, что было велено нарисовать.
280
+ *
281
+ */
282
+ export declare const RowHierarchy: StoryObj;
283
+ /**
284
+ * Группировка строк по двум полям.
285
+ *
286
+ * Группировка — это то же дерево, только узлы разного сорта.
287
+ * Данные сворачиваются в дерево `GroupNode` (группа → группа → строка), а дальше работают
288
+ * ровно те же `flattenTree` и `TableTreeCell`, что и в `RowHierarchy`, — линии считает
289
+ * модель.
290
+ */
291
+ export declare const RowGrouping: StoryObj;
@@ -0,0 +1 @@
1
+ export { useColumnReorder, COLUMN_KEY_ATTRIBUTE, type UseColumnReorderOptions, type UseColumnReorderResult } from './useColumnReorder';
@@ -0,0 +1,82 @@
1
+ import { PointerEvent as ReactPointerEvent } from 'react';
2
+ /** Атрибут, по которому хук находит в DOM все ячейки одной колонки. */
3
+ export declare const COLUMN_KEY_ATTRIBUTE = "data-column-key";
4
+ export interface UseColumnReorderOptions<K extends string> {
5
+ /** Полный видимый порядок колонок — включая те, что двигать нельзя. */
6
+ columns: K[];
7
+ /** Ширины колонок: из них считаются границы слотов. Обычно это `widths` из `useColumnResize`. */
8
+ widths: Record<K, number>;
9
+ /** Новый порядок. Хук ничего не хранит — состояние остаётся у вызывающей стороны. */
10
+ onReorder: (next: K[]) => void;
11
+ /**
12
+ * Колонки, которые нельзя ни таскать, ни занимать: например колонка иерархии,
13
+ * которая обязана оставаться первой. Свой индекс они сохраняют при любой перестановке.
14
+ */
15
+ locked?: K[];
16
+ }
17
+ export interface UseColumnReorderResult<K extends string> {
18
+ /** Колонка, которую тащат прямо сейчас. Годится для подсветки грипа. */
19
+ dragKey: K | null;
20
+ /** Пропы для ручки-грипа. Драг начинается только с неё. */
21
+ getGripProps: (key: K) => {
22
+ onPointerDown: (event: ReactPointerEvent) => void;
23
+ style: {
24
+ touchAction: "none";
25
+ };
26
+ };
27
+ /** Пропы для каждой ячейки колонки — и в шапке, и в теле. Без них колонка не поедет. */
28
+ getColumnProps: (key: K) => Record<string, string>;
29
+ }
30
+ /**
31
+ * Перетаскивание колонок таблицы: колонка едет за курсором, соседи разъезжаются, отпускание
32
+ * укладывает её в новый слот.
33
+ *
34
+ * **Почему не библиотека.** Универсальные DnD-библиотеки (dnd-kit и подобные) существуют ради
35
+ * произвольной геометрии: они измеряют узлы, ищут коллизии, держат подписку на каждый
36
+ * перетаскиваемый элемент. Здесь ничего этого не нужно — колонки лежат в один ряд, а их ширины
37
+ * уже известны из `useColumnResize`, так что смещение слота это префиксная сумма, а выбор цели —
38
+ * ближайший центр. Ровно то же самое делает `closestCenter`, только без обхода DOM.
39
+ *
40
+ * Вторая причина — цена на большой таблице. Библиотечный подход требует хука на **каждую**
41
+ * ячейку, включая ячейки тела, иначе колонка не поедет целиком: на таблице 7×34 это больше
42
+ * двухсот подписок и столько же ререндеров на каждое движение мыши. Здесь во время драга
43
+ * React не участвует вовсе: трансформации пишутся прямо в DOM, а состояние меняется один раз,
44
+ * когда кнопку отпустили. Тот же приём и по той же причине используется в `useColumnResize`.
45
+ *
46
+ * **Оговорка про инлайновые стили.** На время драга хук пишет ячейкам `transform`,
47
+ * `transition`, `position`, `z-index` и `box-shadow` — последний рисует левый край, которого
48
+ * у плывущей колонки нет (см. `setColumnSeparator`). Свои значения этих свойств ячейка
49
+ * получает обратно после отпускания: хук снимает их при первом касании и возвращает
50
+ * в конце. Классы при этом не трогаются вовсе, поэтому оформление классами живёт своей
51
+ * жизнью, а тень от хука просто лежит поверх на время перетаскивания.
52
+ *
53
+ * **Оговорка про фон.** Плывущая колонка на время перетаскивания получает классы
54
+ * `opaqueCellStyles`: строка прозрачна, и без непрозрачного фона колонка едет поверх соседей
55
+ * вместе с их текстом, наложенным на свой. Тот же рецепт, что у закреплённой колонки, — и по
56
+ * той же причине. Подсветку строки он сохраняет, поэтому наведение и `selected` под колонкой
57
+ * не пропадают.
58
+ *
59
+ * **Чего нет.** Перетаскивания с клавиатуры: правильный клавиатурный аналог — пункты меню
60
+ * «сдвинуть влево / вправо», а не имитация драга. Автопрокрутки контейнера у края тоже нет —
61
+ * при перетаскивании за пределы видимой области колонка просто упрётся в край.
62
+ *
63
+ * **Как подключается.** `getGripProps` — на ручку, `getColumnProps` — на `TableHead`
64
+ * и на каждую `TableCell` этой колонки. Драг начинается только с ручки: `pointerdown`
65
+ * по всей ячейке конфликтовал бы с ресайзером, который начинается так же.
66
+ *
67
+ * @example
68
+ * const [order, setOrder] = useState(["tree", "client", "sum"]);
69
+ *
70
+ * const { getGripProps, getColumnProps } = useColumnReorder({
71
+ * columns: order,
72
+ * widths, // из useColumnResize
73
+ * onReorder: setOrder,
74
+ * locked: ["tree"]
75
+ * });
76
+ *
77
+ * <TableHead {...getColumnProps(key)}>
78
+ * <span {...getGripProps(key)}>⠿</span>
79
+ * </TableHead>
80
+ * <TableCell {...getColumnProps(key)}>…</TableCell>
81
+ */
82
+ export declare function useColumnReorder<K extends string>({ columns, widths, onReorder, locked }: UseColumnReorderOptions<K>): UseColumnReorderResult<K>;
@@ -0,0 +1,2 @@
1
+ export * from './column-reorder/index'
2
+ export {}
@@ -0,0 +1,6 @@
1
+ import { C as r, u as s } from "../../useColumnReorder-BxVZQ_Y5.js";
2
+ export {
3
+ r as COLUMN_KEY_ATTRIBUTE,
4
+ s as useColumnReorder
5
+ };
6
+ //# sourceMappingURL=column-reorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-reorder.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Автофит колонки по содержимому (двойной клик по ручке ресайза).
3
+ *
4
+ * Меряет «естественную» ширину контента колонки: клонирует заголовок и видимые ячейки
5
+ * тела в скрытую sandbox-таблицу с `table-layout: auto` / `width: max-content` и берёт
6
+ * итоговую ширину. `cloneCell` переносит на клон вычисленные шрифтовые стили, иначе
7
+ * замер вне исходного каскада получился бы неверным.
8
+ */
9
+ export interface ColumnAutoFitOptions {
10
+ minWidth: number;
11
+ maxWidth: number;
12
+ }
13
+ /**
14
+ * Ширина колонки по её содержимому, в пикселях. `undefined` — если замерить не удалось
15
+ * (заголовок вне таблицы). Результат зажат между `minWidth` и `maxWidth`.
16
+ *
17
+ * @example
18
+ * const width = getColumnAutoFitWidth(headerElement, { minWidth: 60, maxWidth: 500 });
19
+ * if (width) setWidths((current) => ({ ...current, name: width }));
20
+ */
21
+ export declare function getColumnAutoFitWidth(header: HTMLTableCellElement, options: ColumnAutoFitOptions): number | undefined;
@@ -0,0 +1,9 @@
1
+ export declare class ColumnResizeSession {
2
+ private minWidth;
3
+ private state?;
4
+ constructor(minWidth: number);
5
+ start(columnKey: string, startWidth: number, startX: number): void;
6
+ getWidth(currentX: number): number | undefined;
7
+ getColumnKey(): string | undefined;
8
+ end(): void;
9
+ }
@@ -0,0 +1,3 @@
1
+ export { ColumnResizeSession } from './columnResizeSession';
2
+ export { getColumnAutoFitWidth, type ColumnAutoFitOptions } from './columnAutoFit';
3
+ export { useColumnResize, type ResizableColumn, type UseColumnResizeOptions, type UseColumnResizeResult } from './useColumnResize';
@@ -0,0 +1,52 @@
1
+ import { MouseEvent as ReactMouseEvent, RefObject } from 'react';
2
+ export interface ResizableColumn<K extends string = string> {
3
+ key: K;
4
+ defaultWidth: number;
5
+ minWidth: number;
6
+ maxWidth?: number;
7
+ }
8
+ export interface UseColumnResizeOptions {
9
+ /** Глобальный минимум-подстраховка для драга. */
10
+ minWidth?: number;
11
+ /** Дефолтный максимум для колонок без своего `maxWidth` (используется в драге и автофите). */
12
+ maxWidth?: number;
13
+ }
14
+ export interface UseColumnResizeResult<K extends string> {
15
+ /**
16
+ * Ref на саму таблицу — вешается на `<Table ref>`.
17
+ *
18
+ * Именно `RefObject<HTMLTableElement>`, а не `RefObject<HTMLTableElement | null>`: это то,
19
+ * что возвращает `useRef` в типах React 18, и то, что принимает проп `ref` у `forwardRef`.
20
+ * Аннотация с `| null` пришла из React 19 и делала результат хука непередаваемым в `ref`.
21
+ */
22
+ tableRef: RefObject<HTMLTableElement>;
23
+ widths: Record<K, number>;
24
+ totalWidth: number;
25
+ getHeaderRef: (key: K) => (el: HTMLTableCellElement | null) => void;
26
+ getResizeHandleProps: (key: K) => {
27
+ onMouseDown: (event: ReactMouseEvent) => void;
28
+ onDoubleClick: (event: ReactMouseEvent) => void;
29
+ };
30
+ reset: () => void;
31
+ }
32
+ /**
33
+ * Ресайз и автофит колонок обычной HTML-таблицы, без table-библиотек.
34
+ *
35
+ * Во время `mousemove` ширина мутируется напрямую в DOM (`th.style.width`, `table.style.width`)
36
+ * через рефы — без React state на каждый пиксель. На `mouseup` — один коммит в state.
37
+ *
38
+ * `columns` должен быть стабильной ссылкой (модульная константа или `useMemo` вызывающей
39
+ * стороны) — из неё один раз считаются дефолтные ширины.
40
+ *
41
+ * @example
42
+ * const COLUMNS = [{ key: "name", defaultWidth: 240, minWidth: 120 }];
43
+ *
44
+ * const { tableRef, widths, totalWidth, getHeaderRef, getResizeHandleProps } = useColumnResize(COLUMNS);
45
+ *
46
+ * <Table ref={tableRef} style={{ width: totalWidth, tableLayout: "fixed" }}>
47
+ * <TableHead ref={getHeaderRef("name")} className="relative" style={{ width: widths.name }}>
48
+ * Контрагент
49
+ * <TableResizeHandle {...getResizeHandleProps("name")} />
50
+ * </TableHead>
51
+ */
52
+ export declare function useColumnResize<K extends string>(columns: ResizableColumn<K>[], options?: UseColumnResizeOptions): UseColumnResizeResult<K>;
@@ -0,0 +1,2 @@
1
+ export * from './column-resize/index'
2
+ export {}
@@ -0,0 +1,7 @@
1
+ import { C as o, g as u, u as i } from "../../useColumnResize-wjfEyBBg.js";
2
+ export {
3
+ o as ColumnResizeSession,
4
+ u as getColumnAutoFitWidth,
5
+ i as useColumnResize
6
+ };
7
+ //# sourceMappingURL=column-resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-resize.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ export type TableFillHandleProps = React.HTMLAttributes<HTMLSpanElement>;
3
+ /**
4
+ * Ручка автозаполнения — точка на нижнем правом углу выделения, за которую тянут значения.
5
+ *
6
+ * Рисуется внутри угловой ячейки диапазона и позиционируется от неё, поэтому ячейке нужен
7
+ * `position: relative` — он уже есть в `cellStateStyles`.
8
+ *
9
+ * По макету ручка круглая (`border radius/full`, 10px), хотя в Excel это квадрат — оставлено
10
+ * как в макете.
11
+ *
12
+ * Кит даёт только вид и попадание курсором: что протянется, куда и как далеко — решает
13
+ * прикладной код по `onMouseDown` и движению мыши.
14
+ *
15
+ * **Почему `span`, а не `button`.** Ручка работает только протягиванием мыши, клавиатурного
16
+ * аналога у неё нет. Настоящая кнопка встала бы в порядок обхода табом и по Enter не делала
17
+ * бы ничего — остановка фокуса, которая ничем не отвечает, хуже её отсутствия. Роль и подпись
18
+ * при этом остаются: скринридер ручку видит и называет.
19
+ *
20
+ * @example
21
+ * {isCorner && <TableFillHandle onMouseDown={() => setFillFrom(rowIndex)} />}
22
+ */
23
+ export declare const TableFillHandle: React.ForwardRefExoticComponent<TableFillHandleProps & React.RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,83 @@
1
+ import { CSSProperties } from 'react';
2
+ /** Адрес ячейки в сетке: индекс видимой строки и индекс видимой колонки. */
3
+ export type CellRef = {
4
+ row: number;
5
+ col: number;
6
+ };
7
+ /** Прямоугольный диапазон ячеек. Границы включительно, поэтому одна ячейка — тоже диапазон. */
8
+ export type CellRange = {
9
+ top: number;
10
+ bottom: number;
11
+ left: number;
12
+ right: number;
13
+ };
14
+ /**
15
+ * Диапазон по двум углам — якорю выделения и текущей ячейке.
16
+ *
17
+ * Порядок углов не важен: тянут выделение в любую сторону, поэтому границы нормализуются.
18
+ * Это геометрия, а не поведение: где стоит якорь, когда он переставляется и что вообще
19
+ * считается выделением — знание прикладного кода.
20
+ *
21
+ * @example
22
+ * cellRange({ row: 4, col: 1 }, { row: 2, col: 3 }); // { top: 2, bottom: 4, left: 1, right: 3 }
23
+ */
24
+ export declare const cellRange: (anchor: CellRef, focus: CellRef) => CellRange;
25
+ /** Попадает ли ячейка в диапазон. Отсутствие диапазона — не попадает, проверять отдельно не нужно. */
26
+ export declare const inCellRange: (range: CellRange | null | undefined, cell: CellRef) => boolean;
27
+ /** Как выглядит грань диапазона: цвет и толщина линии. */
28
+ export type CellRangeEdge = {
29
+ color: string;
30
+ width: number;
31
+ };
32
+ /**
33
+ * Готовые рамки диапазонов: `selection` — то, что выделено, `preview` — то, что получится,
34
+ * если отпустить сейчас (протягивание значений, перенос).
35
+ *
36
+ * Предпросмотр намеренно тоньше и бледнее выделения: две одинаковые рамки на экране
37
+ * невозможно различить, а они означают разное — состоявшееся и предполагаемое.
38
+ *
39
+ * Список открытый: своя рамка — это `{ color, width }` с любым CSS-цветом.
40
+ */
41
+ export declare const CELL_RANGE_EDGE: {
42
+ readonly selection: {
43
+ readonly color: "hsl(var(--primary-accent))";
44
+ readonly width: 2;
45
+ };
46
+ readonly preview: {
47
+ readonly color: "hsl(var(--primary-border) / 0.75)";
48
+ readonly width: 1;
49
+ };
50
+ };
51
+ /**
52
+ * Грани диапазона, проходящие через эту ячейку, — готовая строка для `box-shadow`.
53
+ *
54
+ * Рамка обводит диапазон целиком, а не каждую его ячейку: грань достаётся только тем ячейкам,
55
+ * что стоят по краю, поэтому внутри диапазона линий нет. Ячейка вне диапазона (и отсутствие
56
+ * диапазона) даёт пустую строку — проверять попадание перед вызовом не нужно.
57
+ *
58
+ * **Почему тень, а не рамка.** `border` входит в бокс ячейки и раздвинул бы колонки на
59
+ * ширину линии, `outline` обвёл бы каждую ячейку целиком. `inset`-тень рисуется внутри
60
+ * ячейки и ничего не двигает.
61
+ *
62
+ * Значения считаются на ходу и потому уезжают в инлайновый стиль, но цвет всё равно берётся
63
+ * из токена, а не хардкодом.
64
+ *
65
+ * @example
66
+ * cellRangeEdges(selection, { row: 2, col: 0 }); // "inset 0 2px 0 0 …, inset 2px 0 0 0 …"
67
+ */
68
+ export declare const cellRangeEdges: (range: CellRange | null | undefined, cell: CellRef, edge?: CellRangeEdge) => string;
69
+ /**
70
+ * Рамки нескольких диапазонов в одном `style` ячейки. Первый аргумент — верхняя тень.
71
+ *
72
+ * Ложные и пустые значения отбрасываются, поэтому условия пишутся прямо в аргументах,
73
+ * а ячейка без единой грани не получает инлайнового стиля вовсе.
74
+ *
75
+ * @example
76
+ * <TableCell
77
+ * style={cellRangeStyle(
78
+ * cellRangeEdges(selection, cell),
79
+ * cellRangeEdges(fillPreview, cell, CELL_RANGE_EDGE.preview)
80
+ * )}
81
+ * >
82
+ */
83
+ export declare const cellRangeStyle: (...shadows: (string | false | null | undefined)[]) => CSSProperties;