@ryuzaki13/react-foundation-lib 2.2.0 → 2.3.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.
@@ -0,0 +1,7 @@
1
+ export * from "./tableDocument";
2
+ export * from "./tableDocumentGeometry";
3
+ export * from "./mergeTableDocumentCells";
4
+ export * from "./insertTableDocumentAxis";
5
+ export * from "./updateTableDocumentCell";
6
+ export * from "./tableDocumentAxisGroups";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/table-document/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,227 @@
1
+ import { n as e } from "../chunks/array-D-_yMMUK.js";
2
+ //#region src/table-document/tableDocument.ts
3
+ var t = 2e4;
4
+ //#endregion
5
+ //#region src/table-document/tableDocumentGeometry.ts
6
+ function n(e, t) {
7
+ return e.row < t.row + t.rowSpan && t.row < e.row + e.rowSpan && e.column < t.column + t.columnSpan && t.column < e.column + e.columnSpan;
8
+ }
9
+ function r(e) {
10
+ let t = e.rows.length, n = e.columns.length;
11
+ if (e.version !== 1 || !t || !n || t * n > 2e4) throw Error("Недопустимый размер таблицы.");
12
+ if (!Number.isInteger(e.headerRowCount) || e.headerRowCount < 0 || e.headerRowCount > t) throw Error("Некорректное число строк заголовка.");
13
+ let r = /* @__PURE__ */ new Set(), a = (e) => {
14
+ if (!e || r.has(e)) throw Error("Идентификаторы строк, столбцов и ячеек должны быть уникальными.");
15
+ r.add(e);
16
+ };
17
+ for (let t of e.columns) if (a(t.id), t.width !== null && (!Number.isFinite(t.width) || t.width <= 0)) throw Error("Некорректная ширина столбца.");
18
+ for (let t of e.rows) {
19
+ if (a(t.id), t.cells.length !== n) throw Error("Все строки должны иметь одинаковое число ячеек.");
20
+ t.cells.forEach((e) => a(e.id));
21
+ }
22
+ let o = /* @__PURE__ */ new Set();
23
+ for (let t of e.merges) {
24
+ if (i(e, t), t.rowSpan * t.columnSpan === 1) throw Error("Объединение должно содержать несколько ячеек.");
25
+ if (t.row < e.headerRowCount && t.row + t.rowSpan > e.headerRowCount) throw Error("Нельзя объединять заголовок с основной частью таблицы.");
26
+ for (let e = t.row; e < t.row + t.rowSpan; e++) for (let r = t.column; r < t.column + t.columnSpan; r++) {
27
+ let t = e * n + r;
28
+ if (o.has(t)) throw Error("Объединённые области пересекаются.");
29
+ o.add(t);
30
+ }
31
+ }
32
+ }
33
+ function i(e, t) {
34
+ if (![
35
+ t.row,
36
+ t.column,
37
+ t.rowSpan,
38
+ t.columnSpan
39
+ ].every(Number.isInteger) || t.row < 0 || t.column < 0 || t.rowSpan < 1 || t.columnSpan < 1 || t.row + t.rowSpan > e.rows.length || t.column + t.columnSpan > e.columns.length) throw Error("Выделение выходит за границы таблицы.");
40
+ }
41
+ function a(e, t) {
42
+ let r = {
43
+ ...t,
44
+ rowSpan: 1,
45
+ columnSpan: 1
46
+ };
47
+ return i(e, r), e.merges.find((e) => n(e, r)) ?? r;
48
+ }
49
+ function o(e, t, r) {
50
+ let a = {
51
+ row: Math.min(t.row, r.row),
52
+ column: Math.min(t.column, r.column),
53
+ rowSpan: Math.abs(t.row - r.row) + 1,
54
+ columnSpan: Math.abs(t.column - r.column) + 1
55
+ };
56
+ i(e, a);
57
+ let o = !0;
58
+ for (; o;) {
59
+ o = !1;
60
+ for (let t of e.merges) {
61
+ if (!n(a, t)) continue;
62
+ let e = Math.min(a.row, t.row), r = Math.min(a.column, t.column), i = Math.max(a.row + a.rowSpan, t.row + t.rowSpan) - e, s = Math.max(a.column + a.columnSpan, t.column + t.columnSpan) - r;
63
+ (e !== a.row || r !== a.column || i !== a.rowSpan || s !== a.columnSpan) && (a = {
64
+ row: e,
65
+ column: r,
66
+ rowSpan: i,
67
+ columnSpan: s
68
+ }, o = !0);
69
+ }
70
+ }
71
+ return a;
72
+ }
73
+ //#endregion
74
+ //#region src/table-document/mergeTableDocumentCells.ts
75
+ function s(e, t, i) {
76
+ r(e);
77
+ let a = o(e, t, i);
78
+ if (a.rowSpan * a.columnSpan === 1) return e;
79
+ let s = e.merges.filter((e) => n(e, a));
80
+ if (s.length === 1 && a.row === s[0].row && a.column === s[0].column && a.rowSpan === s[0].rowSpan && a.columnSpan === s[0].columnSpan) return e;
81
+ let c = {
82
+ ...e,
83
+ merges: [...e.merges.filter((e) => !n(e, a)), a]
84
+ };
85
+ return r(c), c;
86
+ }
87
+ function c(e, t) {
88
+ r(e);
89
+ let n = a(e, t);
90
+ return n.rowSpan * n.columnSpan === 1 ? e : {
91
+ ...e,
92
+ merges: e.merges.filter((e) => e !== n)
93
+ };
94
+ }
95
+ //#endregion
96
+ //#region src/table-document/insertTableDocumentAxis.ts
97
+ function l(e, t, n, i, a, o = "body") {
98
+ r(e);
99
+ let s = t === "row" ? e.rows.length : e.columns.length;
100
+ if (!Number.isInteger(n) || n < 0 || n > s) throw Error("Некорректная позиция вставки.");
101
+ if (a.length !== (t === "row" ? e.columns.length : e.rows.length)) throw Error("Неверное число новых ячеек.");
102
+ if (t === "row" && (o === "header" ? n > e.headerRowCount : n < e.headerRowCount)) throw Error("Строка вставляется вне выбранной части таблицы.");
103
+ let c = t === "row" ? "rowSpan" : "columnSpan", l = e.merges.map((e) => n <= e[t] ? {
104
+ ...e,
105
+ [t]: e[t] + 1
106
+ } : n < e[t] + e[c] ? {
107
+ ...e,
108
+ [c]: e[c] + 1
109
+ } : e), u = t === "row" ? {
110
+ ...e,
111
+ merges: l,
112
+ headerRowCount: e.headerRowCount + +(o === "header"),
113
+ rows: [
114
+ ...e.rows.slice(0, n),
115
+ {
116
+ id: i,
117
+ cells: a
118
+ },
119
+ ...e.rows.slice(n)
120
+ ]
121
+ } : {
122
+ ...e,
123
+ merges: l,
124
+ columns: [
125
+ ...e.columns.slice(0, n),
126
+ {
127
+ id: i,
128
+ width: null
129
+ },
130
+ ...e.columns.slice(n)
131
+ ],
132
+ rows: e.rows.map((e, t) => ({
133
+ ...e,
134
+ cells: [
135
+ ...e.cells.slice(0, n),
136
+ a[t],
137
+ ...e.cells.slice(n)
138
+ ]
139
+ }))
140
+ };
141
+ return r(u), u;
142
+ }
143
+ //#endregion
144
+ //#region src/table-document/updateTableDocumentCell.ts
145
+ function u(e, t, n) {
146
+ return i(e, {
147
+ ...t,
148
+ rowSpan: 1,
149
+ columnSpan: 1
150
+ }), Object.is(e.rows[t.row].cells[t.column].value, n) ? e : {
151
+ ...e,
152
+ rows: e.rows.map((e, r) => r === t.row ? {
153
+ ...e,
154
+ cells: e.cells.map((e, r) => r === t.column ? {
155
+ ...e,
156
+ value: n
157
+ } : e)
158
+ } : e)
159
+ };
160
+ }
161
+ //#endregion
162
+ //#region src/table-document/tableDocumentAxisGroups.ts
163
+ function d(e, t) {
164
+ r(e);
165
+ let n = t === "row" ? e.rows.length : e.columns.length, i = /* @__PURE__ */ new Set();
166
+ for (let n of e.merges) {
167
+ let e = t === "row" ? n.rowSpan : n.columnSpan;
168
+ for (let r = 1; r < e; r++) i.add(n[t] + r);
169
+ }
170
+ let a = [], o = 0;
171
+ for (let e = 1; e <= n; e++) (e === n || !i.has(e)) && (a.push({
172
+ start: o,
173
+ count: e - o
174
+ }), o = e);
175
+ return a;
176
+ }
177
+ function f(e, t, n) {
178
+ let i = d(e, t).find((e) => n >= e.start && n < e.start + e.count);
179
+ if (!i) throw Error("Группа не найдена.");
180
+ let a = t === "row" ? e.rows.length : e.columns.length;
181
+ if (i.count === a) throw Error("В таблице должна остаться хотя бы одна строка и один столбец.");
182
+ let o = i.start + i.count, s = e.merges.filter((e) => e[t] < i.start || e[t] >= o).map((e) => e[t] < o ? e : {
183
+ ...e,
184
+ [t]: e[t] - i.count
185
+ }), c = t === "row" ? {
186
+ ...e,
187
+ merges: s,
188
+ rows: e.rows.filter((e, t) => t < i.start || t >= o),
189
+ headerRowCount: e.headerRowCount - (i.start < e.headerRowCount ? i.count : 0)
190
+ } : {
191
+ ...e,
192
+ merges: s,
193
+ columns: e.columns.filter((e, t) => t < i.start || t >= o),
194
+ rows: e.rows.map((e) => ({
195
+ ...e,
196
+ cells: e.cells.filter((e, t) => t < i.start || t >= o)
197
+ }))
198
+ };
199
+ return r(c), c;
200
+ }
201
+ function p(t, n, i, a) {
202
+ let o = d(t, n), s = o.findIndex((e) => i >= e.start && i < e.start + e.count);
203
+ if (s < 0) throw Error("Группа не найдена.");
204
+ let c = s + a;
205
+ if (c < 0 || c >= o.length || n === "row" && o[s].start < t.headerRowCount != o[c].start < t.headerRowCount) return t;
206
+ let l = e(o, s, a === -1 ? "up" : "down").flatMap((e) => Array.from({ length: e.count }, (t, n) => e.start + n)), u = new Map(l.map((e, t) => [e, t])), f = t.merges.map((e) => ({
207
+ ...e,
208
+ [n]: u.get(e[n]) ?? e[n]
209
+ })), p = n === "row" ? {
210
+ ...t,
211
+ merges: f,
212
+ rows: l.map((e) => t.rows[e])
213
+ } : {
214
+ ...t,
215
+ merges: f,
216
+ columns: l.map((e) => t.columns[e]),
217
+ rows: t.rows.map((e) => ({
218
+ ...e,
219
+ cells: l.map((t) => e.cells[t])
220
+ }))
221
+ };
222
+ return r(p), p;
223
+ }
224
+ //#endregion
225
+ export { t as TABLE_DOCUMENT_MAX_CELLS, r as assertTableDocument, i as assertTableDocumentRange, f as deleteTableDocumentAxisGroup, o as expandTableDocumentRange, d as getTableDocumentAxisGroups, a as getTableDocumentCellRange, l as insertTableDocumentAxis, s as mergeTableDocumentCells, p as moveTableDocumentAxisGroup, c as splitTableDocumentCell, n as tableDocumentRangesIntersect, u as updateTableDocumentCell };
226
+
227
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/table-document/tableDocument.ts","../../src/table-document/tableDocumentGeometry.ts","../../src/table-document/mergeTableDocumentCells.ts","../../src/table-document/insertTableDocumentAxis.ts","../../src/table-document/updateTableDocumentCell.ts","../../src/table-document/tableDocumentAxisGroups.ts"],"sourcesContent":["/** Координаты относятся к полной сетке, включая скрытые объединением ячейки. */\nexport type TableDocumentPosition = { readonly row: number; readonly column: number };\n\n/** Прямоугольная область; spans всегда положительные целые числа. */\nexport type TableDocumentRange = TableDocumentPosition & { readonly rowSpan: number; readonly columnSpan: number };\n\n/** Значение принадлежит приложению; ядро никогда не преобразует и не объединяет содержимое. */\nexport type TableDocumentCell<T> = { readonly id: string; readonly value: T };\nexport type TableDocumentRow<T> = { readonly id: string; readonly cells: readonly TableDocumentCell<T>[] };\nexport type TableDocumentColumn = { readonly id: string; readonly width: number | null };\n\n/**\n * Документ редактора, независимый от TanStack и DOM. Полная матрица хранит все\n * значения; merges содержит только непересекающиеся видимые объединения.\n * Это исключает устаревшие spans у скрытых ячеек после повторного merge/split.\n */\nexport type TableDocument<T> = {\n\treadonly version: 1;\n\treadonly caption: string;\n\treadonly columns: readonly TableDocumentColumn[];\n\treadonly rows: readonly TableDocumentRow<T>[];\n\treadonly merges: readonly TableDocumentRange[];\n\treadonly headerRowCount: number;\n\treadonly hideHeaders: boolean;\n\treadonly showRowNumbers: boolean;\n};\n\n/** Лимит защищает проверки геометрии и UI от случайно огромной сетки. */\nexport const TABLE_DOCUMENT_MAX_CELLS = 20_000;\n","import { TABLE_DOCUMENT_MAX_CELLS, type TableDocument, type TableDocumentPosition, type TableDocumentRange } from \"./tableDocument\";\n\n/** Проверяет пересечение двух включительных прямоугольников полной сетки. */\nexport function tableDocumentRangesIntersect(a: TableDocumentRange, b: TableDocumentRange): boolean {\n\treturn (\n\t\ta.row < b.row + b.rowSpan && b.row < a.row + a.rowSpan && a.column < b.column + b.columnSpan && b.column < a.column + a.columnSpan\n\t);\n}\n\n/** Fail-closed инварианты для команд и внешней runtime-schema приложения. */\nexport function assertTableDocument<T>(document: TableDocument<T>): void {\n\tconst height = document.rows.length;\n\tconst width = document.columns.length;\n\tif (document.version !== 1 || !height || !width || height * width > TABLE_DOCUMENT_MAX_CELLS)\n\t\tthrow new Error(\"Недопустимый размер таблицы.\");\n\tif (!Number.isInteger(document.headerRowCount) || document.headerRowCount < 0 || document.headerRowCount > height)\n\t\tthrow new Error(\"Некорректное число строк заголовка.\");\n\tconst identities = new Set<string>();\n\tconst checkId = (id: string) => {\n\t\tif (!id || identities.has(id)) throw new Error(\"Идентификаторы строк, столбцов и ячеек должны быть уникальными.\");\n\t\tidentities.add(id);\n\t};\n\tfor (const column of document.columns) {\n\t\tcheckId(column.id);\n\t\tif (column.width !== null && (!Number.isFinite(column.width) || column.width <= 0)) throw new Error(\"Некорректная ширина столбца.\");\n\t}\n\tfor (const row of document.rows) {\n\t\tcheckId(row.id);\n\t\tif (row.cells.length !== width) throw new Error(\"Все строки должны иметь одинаковое число ячеек.\");\n\t\trow.cells.forEach((cell) => checkId(cell.id));\n\t}\n\tconst occupied = new Set<number>();\n\tfor (const merge of document.merges) {\n\t\tassertTableDocumentRange(document, merge);\n\t\tif (merge.rowSpan * merge.columnSpan === 1) throw new Error(\"Объединение должно содержать несколько ячеек.\");\n\t\tif (merge.row < document.headerRowCount && merge.row + merge.rowSpan > document.headerRowCount)\n\t\t\tthrow new Error(\"Нельзя объединять заголовок с основной частью таблицы.\");\n\t\tfor (let r = merge.row; r < merge.row + merge.rowSpan; r++) {\n\t\t\tfor (let c = merge.column; c < merge.column + merge.columnSpan; c++) {\n\t\t\t\tconst key = r * width + c;\n\t\t\t\tif (occupied.has(key)) throw new Error(\"Объединённые области пересекаются.\");\n\t\t\t\toccupied.add(key);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Проверяет границы до выполнения команды, не исправляя ошибочный ввод молча. */\nexport function assertTableDocumentRange<T>(document: TableDocument<T>, range: TableDocumentRange): void {\n\tif (\n\t\t![range.row, range.column, range.rowSpan, range.columnSpan].every(Number.isInteger) ||\n\t\trange.row < 0 ||\n\t\trange.column < 0 ||\n\t\trange.rowSpan < 1 ||\n\t\trange.columnSpan < 1 ||\n\t\trange.row + range.rowSpan > document.rows.length ||\n\t\trange.column + range.columnSpan > document.columns.length\n\t)\n\t\tthrow new Error(\"Выделение выходит за границы таблицы.\");\n}\n\n/** Возвращает видимый anchor для любой позиции, включая покрытую объединением. */\nexport function getTableDocumentCellRange<T>(document: TableDocument<T>, position: TableDocumentPosition): TableDocumentRange {\n\tconst point = { ...position, rowSpan: 1, columnSpan: 1 };\n\tassertTableDocumentRange(document, point);\n\treturn document.merges.find((merge) => tableDocumentRangesIntersect(merge, point)) ?? point;\n}\n\n/**\n * Замыкает выделение до неподвижной точки. Повторный обход обязателен: новая\n * строка прямоугольника может пересечь ещё одно объединение вне исходной области.\n */\nexport function expandTableDocumentRange<T>(\n\tdocument: TableDocument<T>,\n\tstart: TableDocumentPosition,\n\tend: TableDocumentPosition\n): TableDocumentRange {\n\tlet range = {\n\t\trow: Math.min(start.row, end.row),\n\t\tcolumn: Math.min(start.column, end.column),\n\t\trowSpan: Math.abs(start.row - end.row) + 1,\n\t\tcolumnSpan: Math.abs(start.column - end.column) + 1\n\t};\n\tassertTableDocumentRange(document, range);\n\tlet changed = true;\n\twhile (changed) {\n\t\tchanged = false;\n\t\tfor (const merge of document.merges) {\n\t\t\tif (!tableDocumentRangesIntersect(range, merge)) continue;\n\t\t\tconst row = Math.min(range.row, merge.row);\n\t\t\tconst column = Math.min(range.column, merge.column);\n\t\t\tconst rowSpan = Math.max(range.row + range.rowSpan, merge.row + merge.rowSpan) - row;\n\t\t\tconst columnSpan = Math.max(range.column + range.columnSpan, merge.column + merge.columnSpan) - column;\n\t\t\tif (row !== range.row || column !== range.column || rowSpan !== range.rowSpan || columnSpan !== range.columnSpan) {\n\t\t\t\trange = { row, column, rowSpan, columnSpan };\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t}\n\treturn range;\n}\n","import { type TableDocument, type TableDocumentPosition } from \"./tableDocument\";\nimport {\n\tassertTableDocument,\n\texpandTableDocumentRange,\n\tgetTableDocumentCellRange,\n\ttableDocumentRangesIntersect\n} from \"./tableDocumentGeometry\";\n\n/** Объединяет геометрию; исходные значения и identities всех ячеек остаются неизменными. */\nexport function mergeTableDocumentCells<T>(\n\tdocument: TableDocument<T>,\n\tstart: TableDocumentPosition,\n\tend: TableDocumentPosition\n): TableDocument<T> {\n\tassertTableDocument(document);\n\tconst range = expandTableDocumentRange(document, start, end);\n\tif (range.rowSpan * range.columnSpan === 1) return document;\n\tconst intersected = document.merges.filter((merge) => tableDocumentRangesIntersect(merge, range));\n\tif (\n\t\tintersected.length === 1 &&\n\t\trange.row === intersected[0].row &&\n\t\trange.column === intersected[0].column &&\n\t\trange.rowSpan === intersected[0].rowSpan &&\n\t\trange.columnSpan === intersected[0].columnSpan\n\t)\n\t\treturn document;\n\tconst result = { ...document, merges: [...document.merges.filter((merge) => !tableDocumentRangesIntersect(merge, range)), range] };\n\tassertTableDocument(result);\n\treturn result;\n}\n\n/** Разъединяет область целиком; скрытые значения становятся видны без восстановления из копий. */\nexport function splitTableDocumentCell<T>(document: TableDocument<T>, position: TableDocumentPosition): TableDocument<T> {\n\tassertTableDocument(document);\n\tconst range = getTableDocumentCellRange(document, position);\n\tif (range.rowSpan * range.columnSpan === 1) return document;\n\treturn { ...document, merges: document.merges.filter((merge) => merge !== range) };\n}\n","import { type TableDocument, type TableDocumentCell } from \"./tableDocument\";\nimport { assertTableDocument } from \"./tableDocumentGeometry\";\n\n/**\n * Вставка внутрь объединения увеличивает его span; вставка перед anchor сдвигает\n * область. ID и значения создаются вызывающим кодом до изменения React state.\n */\nexport function insertTableDocumentAxis<T>(\n\tdocument: TableDocument<T>,\n\taxis: \"row\" | \"column\",\n\tindex: number,\n\tid: string,\n\tcells: readonly TableDocumentCell<T>[],\n\tsection: \"header\" | \"body\" = \"body\"\n): TableDocument<T> {\n\tassertTableDocument(document);\n\tconst count = axis === \"row\" ? document.rows.length : document.columns.length;\n\tif (!Number.isInteger(index) || index < 0 || index > count) throw new Error(\"Некорректная позиция вставки.\");\n\tif (cells.length !== (axis === \"row\" ? document.columns.length : document.rows.length)) throw new Error(\"Неверное число новых ячеек.\");\n\tif (axis === \"row\" && (section === \"header\" ? index > document.headerRowCount : index < document.headerRowCount))\n\t\tthrow new Error(\"Строка вставляется вне выбранной части таблицы.\");\n\tconst spanKey = axis === \"row\" ? \"rowSpan\" : \"columnSpan\";\n\tconst merges = document.merges.map((merge) =>\n\t\tindex <= merge[axis]\n\t\t\t? { ...merge, [axis]: merge[axis] + 1 }\n\t\t\t: index < merge[axis] + merge[spanKey]\n\t\t\t\t? { ...merge, [spanKey]: merge[spanKey] + 1 }\n\t\t\t\t: merge\n\t);\n\tconst result: TableDocument<T> =\n\t\taxis === \"row\"\n\t\t\t? {\n\t\t\t\t\t...document,\n\t\t\t\t\tmerges,\n\t\t\t\t\theaderRowCount: document.headerRowCount + (section === \"header\" ? 1 : 0),\n\t\t\t\t\trows: [...document.rows.slice(0, index), { id, cells }, ...document.rows.slice(index)]\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\t...document,\n\t\t\t\t\tmerges,\n\t\t\t\t\tcolumns: [...document.columns.slice(0, index), { id, width: null }, ...document.columns.slice(index)],\n\t\t\t\t\trows: document.rows.map((row, r) => ({\n\t\t\t\t\t\t...row,\n\t\t\t\t\t\tcells: [...row.cells.slice(0, index), cells[r], ...row.cells.slice(index)]\n\t\t\t\t\t}))\n\t\t\t\t};\n\tassertTableDocument(result);\n\treturn result;\n}\n","import { type TableDocument, type TableDocumentPosition } from \"./tableDocument\";\nimport { assertTableDocumentRange } from \"./tableDocumentGeometry\";\n\n/** Меняет ровно одну логическую ячейку; преобразование rich text/документов выполняет приложение. */\nexport function updateTableDocumentCell<T>(document: TableDocument<T>, position: TableDocumentPosition, value: T): TableDocument<T> {\n\tassertTableDocumentRange(document, { ...position, rowSpan: 1, columnSpan: 1 });\n\tif (Object.is(document.rows[position.row].cells[position.column].value, value)) return document;\n\treturn {\n\t\t...document,\n\t\trows: document.rows.map((row, r) =>\n\t\t\tr !== position.row ? row : { ...row, cells: row.cells.map((cell, c) => (c !== position.column ? cell : { ...cell, value })) }\n\t\t)\n\t};\n}\n","import { moveArrayItemByIndex } from \"../array\";\n\nimport { type TableDocument } from \"./tableDocument\";\nimport { assertTableDocument } from \"./tableDocumentGeometry\";\n\n/** Неразрывная группа строк/столбцов; применима к reorder и подтверждению удаления. */\nexport type TableDocumentAxisGroup = { readonly start: number; readonly count: number };\n\n/** Находит безопасные границы без случайных ID и без повторного учёта скрытых spans. */\nexport function getTableDocumentAxisGroups<T>(document: TableDocument<T>, axis: \"row\" | \"column\"): readonly TableDocumentAxisGroup[] {\n\tassertTableDocument(document);\n\tconst length = axis === \"row\" ? document.rows.length : document.columns.length;\n\tconst blocked = new Set<number>();\n\tfor (const merge of document.merges) {\n\t\tconst span = axis === \"row\" ? merge.rowSpan : merge.columnSpan;\n\t\tfor (let offset = 1; offset < span; offset++) blocked.add(merge[axis] + offset);\n\t}\n\tconst groups: TableDocumentAxisGroup[] = [];\n\tlet start = 0;\n\tfor (let i = 1; i <= length; i++) {\n\t\tif (i === length || !blocked.has(i)) {\n\t\t\tgroups.push({ start, count: i - start });\n\t\t\tstart = i;\n\t\t}\n\t}\n\treturn groups;\n}\n\n/** Удаляет только явно выбранную неразрывную группу; UI обязан показать её размер до подтверждения. */\nexport function deleteTableDocumentAxisGroup<T>(document: TableDocument<T>, axis: \"row\" | \"column\", index: number): TableDocument<T> {\n\tconst group = getTableDocumentAxisGroups(document, axis).find((item) => index >= item.start && index < item.start + item.count);\n\tif (!group) throw new Error(\"Группа не найдена.\");\n\tconst length = axis === \"row\" ? document.rows.length : document.columns.length;\n\tif (group.count === length) throw new Error(\"В таблице должна остаться хотя бы одна строка и один столбец.\");\n\tconst end = group.start + group.count;\n\tconst merges = document.merges\n\t\t.filter((merge) => merge[axis] < group.start || merge[axis] >= end)\n\t\t.map((merge) => (merge[axis] < end ? merge : { ...merge, [axis]: merge[axis] - group.count }));\n\tconst result: TableDocument<T> =\n\t\taxis === \"row\"\n\t\t\t? {\n\t\t\t\t\t...document,\n\t\t\t\t\tmerges,\n\t\t\t\t\trows: document.rows.filter((_, r) => r < group.start || r >= end),\n\t\t\t\t\theaderRowCount: document.headerRowCount - (group.start < document.headerRowCount ? group.count : 0)\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\t...document,\n\t\t\t\t\tmerges,\n\t\t\t\t\tcolumns: document.columns.filter((_, c) => c < group.start || c >= end),\n\t\t\t\t\trows: document.rows.map((row) => ({ ...row, cells: row.cells.filter((_, c) => c < group.start || c >= end) }))\n\t\t\t\t};\n\tassertTableDocument(result);\n\treturn result;\n}\n\n/** Меняет местами соседние неразрывные группы, сохраняя порядок внутри объединений. */\nexport function moveTableDocumentAxisGroup<T>(\n\tdocument: TableDocument<T>,\n\taxis: \"row\" | \"column\",\n\tindex: number,\n\tdirection: -1 | 1\n): TableDocument<T> {\n\tconst groups = getTableDocumentAxisGroups(document, axis);\n\tconst current = groups.findIndex((item) => index >= item.start && index < item.start + item.count);\n\tif (current < 0) throw new Error(\"Группа не найдена.\");\n\tconst neighbor = current + direction;\n\tif (neighbor < 0 || neighbor >= groups.length) return document;\n\tif (axis === \"row\" && groups[current].start < document.headerRowCount !== groups[neighbor].start < document.headerRowCount)\n\t\treturn document;\n\tconst ordered = moveArrayItemByIndex(groups, current, direction === -1 ? \"up\" : \"down\");\n\tconst indices = ordered.flatMap((group) => Array.from({ length: group.count }, (_, offset) => group.start + offset));\n\tconst inverse = new Map(indices.map((oldIndex, newIndex) => [oldIndex, newIndex]));\n\tconst merges = document.merges.map((merge) => ({ ...merge, [axis]: inverse.get(merge[axis]) ?? merge[axis] }));\n\tconst result =\n\t\taxis === \"row\"\n\t\t\t? { ...document, merges, rows: indices.map((i) => document.rows[i]) }\n\t\t\t: {\n\t\t\t\t\t...document,\n\t\t\t\t\tmerges,\n\t\t\t\t\tcolumns: indices.map((i) => document.columns[i]),\n\t\t\t\t\trows: document.rows.map((row) => ({ ...row, cells: indices.map((i) => row.cells[i]) }))\n\t\t\t\t};\n\tassertTableDocument(result);\n\treturn result;\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type TableDocument, type TableDocumentCell } from "./tableDocument";
2
+ /**
3
+ * Вставка внутрь объединения увеличивает его span; вставка перед anchor сдвигает
4
+ * область. ID и значения создаются вызывающим кодом до изменения React state.
5
+ */
6
+ export declare function insertTableDocumentAxis<T>(document: TableDocument<T>, axis: "row" | "column", index: number, id: string, cells: readonly TableDocumentCell<T>[], section?: "header" | "body"): TableDocument<T>;
7
+ //# sourceMappingURL=insertTableDocumentAxis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insertTableDocumentAxis.d.ts","sourceRoot":"","sources":["../../src/table-document/insertTableDocumentAxis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAG7E;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACxC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,IAAI,EAAE,KAAK,GAAG,QAAQ,EACtB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,EACtC,OAAO,GAAE,QAAQ,GAAG,MAAe,GACjC,aAAa,CAAC,CAAC,CAAC,CAkClB"}
@@ -0,0 +1,6 @@
1
+ import { type TableDocument, type TableDocumentPosition } from "./tableDocument";
2
+ /** Объединяет геометрию; исходные значения и identities всех ячеек остаются неизменными. */
3
+ export declare function mergeTableDocumentCells<T>(document: TableDocument<T>, start: TableDocumentPosition, end: TableDocumentPosition): TableDocument<T>;
4
+ /** Разъединяет область целиком; скрытые значения становятся видны без восстановления из копий. */
5
+ export declare function splitTableDocumentCell<T>(document: TableDocument<T>, position: TableDocumentPosition): TableDocument<T>;
6
+ //# sourceMappingURL=mergeTableDocumentCells.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeTableDocumentCells.d.ts","sourceRoot":"","sources":["../../src/table-document/mergeTableDocumentCells.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAQjF,4FAA4F;AAC5F,wBAAgB,uBAAuB,CAAC,CAAC,EACxC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,KAAK,EAAE,qBAAqB,EAC5B,GAAG,EAAE,qBAAqB,GACxB,aAAa,CAAC,CAAC,CAAC,CAgBlB;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,qBAAqB,GAAG,aAAa,CAAC,CAAC,CAAC,CAKvH"}
@@ -0,0 +1,41 @@
1
+ /** Координаты относятся к полной сетке, включая скрытые объединением ячейки. */
2
+ export type TableDocumentPosition = {
3
+ readonly row: number;
4
+ readonly column: number;
5
+ };
6
+ /** Прямоугольная область; spans всегда положительные целые числа. */
7
+ export type TableDocumentRange = TableDocumentPosition & {
8
+ readonly rowSpan: number;
9
+ readonly columnSpan: number;
10
+ };
11
+ /** Значение принадлежит приложению; ядро никогда не преобразует и не объединяет содержимое. */
12
+ export type TableDocumentCell<T> = {
13
+ readonly id: string;
14
+ readonly value: T;
15
+ };
16
+ export type TableDocumentRow<T> = {
17
+ readonly id: string;
18
+ readonly cells: readonly TableDocumentCell<T>[];
19
+ };
20
+ export type TableDocumentColumn = {
21
+ readonly id: string;
22
+ readonly width: number | null;
23
+ };
24
+ /**
25
+ * Документ редактора, независимый от TanStack и DOM. Полная матрица хранит все
26
+ * значения; merges содержит только непересекающиеся видимые объединения.
27
+ * Это исключает устаревшие spans у скрытых ячеек после повторного merge/split.
28
+ */
29
+ export type TableDocument<T> = {
30
+ readonly version: 1;
31
+ readonly caption: string;
32
+ readonly columns: readonly TableDocumentColumn[];
33
+ readonly rows: readonly TableDocumentRow<T>[];
34
+ readonly merges: readonly TableDocumentRange[];
35
+ readonly headerRowCount: number;
36
+ readonly hideHeaders: boolean;
37
+ readonly showRowNumbers: boolean;
38
+ };
39
+ /** Лимит защищает проверки геометрии и UI от случайно огромной сетки. */
40
+ export declare const TABLE_DOCUMENT_MAX_CELLS = 20000;
41
+ //# sourceMappingURL=tableDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableDocument.d.ts","sourceRoot":"","sources":["../../src/table-document/tableDocument.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,qBAAqB,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtF,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnH,+FAA+F;AAC/F,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;CAAE,CAAC;AAC3G,MAAM,MAAM,mBAAmB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEzF;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC9B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,SAAS,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,wBAAwB,QAAS,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type TableDocument } from "./tableDocument";
2
+ /** Неразрывная группа строк/столбцов; применима к reorder и подтверждению удаления. */
3
+ export type TableDocumentAxisGroup = {
4
+ readonly start: number;
5
+ readonly count: number;
6
+ };
7
+ /** Находит безопасные границы без случайных ID и без повторного учёта скрытых spans. */
8
+ export declare function getTableDocumentAxisGroups<T>(document: TableDocument<T>, axis: "row" | "column"): readonly TableDocumentAxisGroup[];
9
+ /** Удаляет только явно выбранную неразрывную группу; UI обязан показать её размер до подтверждения. */
10
+ export declare function deleteTableDocumentAxisGroup<T>(document: TableDocument<T>, axis: "row" | "column", index: number): TableDocument<T>;
11
+ /** Меняет местами соседние неразрывные группы, сохраняя порядок внутри объединений. */
12
+ export declare function moveTableDocumentAxisGroup<T>(document: TableDocument<T>, axis: "row" | "column", index: number, direction: -1 | 1): TableDocument<T>;
13
+ //# sourceMappingURL=tableDocumentAxisGroups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableDocumentAxisGroups.d.ts","sourceRoot":"","sources":["../../src/table-document/tableDocumentAxisGroups.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF,wFAAwF;AACxF,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,sBAAsB,EAAE,CAiBnI;AAED,uGAAuG;AACvG,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAyBnI;AAED,uFAAuF;AACvF,wBAAgB,0BAA0B,CAAC,CAAC,EAC3C,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,IAAI,EAAE,KAAK,GAAG,QAAQ,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,GACf,aAAa,CAAC,CAAC,CAAC,CAuBlB"}
@@ -0,0 +1,15 @@
1
+ import { type TableDocument, type TableDocumentPosition, type TableDocumentRange } from "./tableDocument";
2
+ /** Проверяет пересечение двух включительных прямоугольников полной сетки. */
3
+ export declare function tableDocumentRangesIntersect(a: TableDocumentRange, b: TableDocumentRange): boolean;
4
+ /** Fail-closed инварианты для команд и внешней runtime-schema приложения. */
5
+ export declare function assertTableDocument<T>(document: TableDocument<T>): void;
6
+ /** Проверяет границы до выполнения команды, не исправляя ошибочный ввод молча. */
7
+ export declare function assertTableDocumentRange<T>(document: TableDocument<T>, range: TableDocumentRange): void;
8
+ /** Возвращает видимый anchor для любой позиции, включая покрытую объединением. */
9
+ export declare function getTableDocumentCellRange<T>(document: TableDocument<T>, position: TableDocumentPosition): TableDocumentRange;
10
+ /**
11
+ * Замыкает выделение до неподвижной точки. Повторный обход обязателен: новая
12
+ * строка прямоугольника может пересечь ещё одно объединение вне исходной области.
13
+ */
14
+ export declare function expandTableDocumentRange<T>(document: TableDocument<T>, start: TableDocumentPosition, end: TableDocumentPosition): TableDocumentRange;
15
+ //# sourceMappingURL=tableDocumentGeometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableDocumentGeometry.d.ts","sourceRoot":"","sources":["../../src/table-document/tableDocumentGeometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEpI,6EAA6E;AAC7E,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAIlG;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAmCvE;AAED,kFAAkF;AAClF,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAWvG;AAED,kFAAkF;AAClF,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,qBAAqB,GAAG,kBAAkB,CAI5H;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACzC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,KAAK,EAAE,qBAAqB,EAC5B,GAAG,EAAE,qBAAqB,GACxB,kBAAkB,CAwBpB"}
@@ -0,0 +1,4 @@
1
+ import { type TableDocument, type TableDocumentPosition } from "./tableDocument";
2
+ /** Меняет ровно одну логическую ячейку; преобразование rich text/документов выполняет приложение. */
3
+ export declare function updateTableDocumentCell<T>(document: TableDocument<T>, position: TableDocumentPosition, value: T): TableDocument<T>;
4
+ //# sourceMappingURL=updateTableDocumentCell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateTableDocumentCell.d.ts","sourceRoot":"","sources":["../../src/table-document/updateTableDocumentCell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGjF,qGAAqG;AACrG,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CASlI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuzaki13/react-foundation-lib",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
5
5
  "license": "MIT",
6
6
  "author": "Ryuzaki13",
@@ -165,6 +165,10 @@
165
165
  "types": "./dist/table/index.d.ts",
166
166
  "import": "./dist/table/index.js"
167
167
  },
168
+ "./table-document": {
169
+ "types": "./dist/table-document/index.d.ts",
170
+ "import": "./dist/table-document/index.js"
171
+ },
168
172
  "./three-scene": {
169
173
  "types": "./dist/three-scene/index.d.ts",
170
174
  "import": "./dist/three-scene/index.js"
@@ -0,0 +1,23 @@
1
+ # Документ редактируемой таблицы
2
+
3
+ Public API: `@ryuzaki13/react-foundation-lib/table-document`.
4
+
5
+ Чистое ядро для конструктора таблиц. Полная прямоугольная матрица хранит значения
6
+ всех ячеек, а отдельный массив `merges` — непересекающиеся объединения. Merge
7
+ никогда не удаляет содержимое покрытых ячеек; split возвращает их видимость.
8
+ Значения generic: приложение владеет rich text, ссылками, DTO и сериализацией.
9
+
10
+ Команды возвращают новый документ без мутации исходного. Неизменённые строки
11
+ и значения сохраняются по ссылке, что позволяет строить историю undo/redo.
12
+ Выделение замыкается по всем пересечённым объединениям до неподвижной точки.
13
+ Заголовок не объединяется и не переставляется с body. Вставка внутри области
14
+ увеличивает span; удаление и перемещение работают с неразрывной группой.
15
+ Перед удалением UI должен показать фактический размер группы и подтвердить его.
16
+
17
+ `assertTableDocument` проверяет геометрию типизированного документа, но не
18
+ заменяет runtime-парсер неизвестного JSON на внешней границе приложения.
19
+ Лимит — 20 000 логических ячеек. ID должны быть непустыми и уникальными во всём
20
+ документе. Генерацию ID выполнять в обработчике события, не во время render.
21
+
22
+ Это новый additive API, существующий `/table` и его selection contracts не
23
+ изменяются. Миграцию базы потребителя пакет не выполняет.