@ryuzaki13/react-foundation-lib 2.10.0 → 2.11.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.
- package/dist/decimal/index.d.ts +1 -0
- package/dist/decimal/index.d.ts.map +1 -1
- package/dist/decimal/index.js +20 -3
- package/dist/decimal/index.js.map +1 -1
- package/dist/decimal/normalizeDecimalLiteral.d.ts +8 -0
- package/dist/decimal/normalizeDecimalLiteral.d.ts.map +1 -0
- package/dist/table/index.js +4 -2
- package/dist/table/index.js.map +1 -1
- package/dist/table/utils.d.ts +2 -0
- package/dist/table/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/decimal/README.md +16 -8
package/dist/decimal/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { compareDecimals } from "./compareDecimals";
|
|
|
4
4
|
export { isCanonicalDecimal } from "./isCanonicalDecimal";
|
|
5
5
|
export { multiplyDecimals } from "./multiplyDecimals";
|
|
6
6
|
export { normalizeDecimal } from "./normalizeDecimal";
|
|
7
|
+
export { normalizeDecimalLiteral } from "./normalizeDecimalLiteral";
|
|
7
8
|
export { subtractDecimals } from "./subtractDecimals";
|
|
8
9
|
export { sumDecimals } from "./sumDecimals";
|
|
9
10
|
export type { DecimalComparison, DecimalOptions, DecimalRatio } from "./types";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decimal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decimal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/decimal/index.js
CHANGED
|
@@ -86,8 +86,25 @@ function l(e, n) {
|
|
|
86
86
|
return i && l !== "0" ? `-${l}` : l;
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
|
+
//#region src/decimal/normalizeDecimalLiteral.ts
|
|
90
|
+
function u(e, n) {
|
|
91
|
+
let r = t(n);
|
|
92
|
+
if (typeof e != "string" || e.length > r) throw TypeError("Ожидался ограниченный десятичный числовой литерал.");
|
|
93
|
+
let i = /^(-?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?(?![\s\S])/u.exec(e);
|
|
94
|
+
if (!i) throw TypeError("Ожидался конечный десятичный числовой литерал.");
|
|
95
|
+
let [, a, o, s = "", c] = i;
|
|
96
|
+
if (c === void 0) return l(e, n);
|
|
97
|
+
let u = o + s, d = u.replace(/^0+/u, "");
|
|
98
|
+
if (!d) return "0";
|
|
99
|
+
let f = d.replace(/0+$/u, ""), p = Number(c);
|
|
100
|
+
if (!Number.isSafeInteger(p)) throw RangeError("Экспонента превышает допустимую длину результата.");
|
|
101
|
+
let m = o.length + p - (u.length - d.length);
|
|
102
|
+
if (a.length + (m <= 0 ? 2 - m + f.length : Math.max(m, f.length + +(m < f.length))) > r) throw RangeError("Десятичный результат превышает допустимую длину.");
|
|
103
|
+
return m <= 0 ? `${a}0.${"0".repeat(-m)}${f}` : m >= f.length ? a + f + "0".repeat(m - f.length) : `${a}${f.slice(0, m)}.${f.slice(m)}`;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
89
106
|
//#region src/decimal/subtractDecimals.ts
|
|
90
|
-
function
|
|
107
|
+
function d(t, n, a) {
|
|
91
108
|
let o = e(r(t, a), r(n, a));
|
|
92
109
|
return i({
|
|
93
110
|
coefficient: o.left - o.right,
|
|
@@ -96,13 +113,13 @@ function u(t, n, a) {
|
|
|
96
113
|
}
|
|
97
114
|
//#endregion
|
|
98
115
|
//#region src/decimal/sumDecimals.ts
|
|
99
|
-
function
|
|
116
|
+
function f(e, n) {
|
|
100
117
|
t(n);
|
|
101
118
|
let r = "0";
|
|
102
119
|
for (let t of e) r = a(r, t, n);
|
|
103
120
|
return r;
|
|
104
121
|
}
|
|
105
122
|
//#endregion
|
|
106
|
-
export { a as addDecimals, o as compareDecimalRatios, s as compareDecimals, n as isCanonicalDecimal, c as multiplyDecimals, l as normalizeDecimal, u as
|
|
123
|
+
export { a as addDecimals, o as compareDecimalRatios, s as compareDecimals, n as isCanonicalDecimal, c as multiplyDecimals, l as normalizeDecimal, u as normalizeDecimalLiteral, d as subtractDecimals, f as sumDecimals };
|
|
107
124
|
|
|
108
125
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/decimal/alignDecimalParts.ts","../../src/decimal/resolveDecimalMaxLength.ts","../../src/decimal/isCanonicalDecimal.ts","../../src/decimal/readDecimalParts.ts","../../src/decimal/writeDecimalParts.ts","../../src/decimal/addDecimals.ts","../../src/decimal/compareDecimalRatios.ts","../../src/decimal/compareDecimals.ts","../../src/decimal/multiplyDecimals.ts","../../src/decimal/normalizeDecimal.ts","../../src/decimal/subtractDecimals.ts","../../src/decimal/sumDecimals.ts"],"sourcesContent":["import type { DecimalParts } from \"./types\";\n\n/** Выравнивает только разрядность ограниченных операндов; глобального кеша степеней нет. */\nexport function alignDecimalParts(left: DecimalParts, right: DecimalParts): Readonly<{ left: bigint; right: bigint; scale: number }> {\n\tconst scale = Math.max(left.scale, right.scale);\n\n\treturn {\n\t\tleft: left.coefficient * 10n ** BigInt(scale - left.scale),\n\t\tright: right.coefficient * 10n ** BigInt(scale - right.scale),\n\t\tscale\n\t};\n}\n","import type { DecimalOptions } from \"./types\";\n\n/** Проверяет предел до разбора числа и выделения вычислительных буферов. */\nexport function resolveDecimalMaxLength(options?: DecimalOptions): number {\n\tconst maxLength = options?.maxLength ?? 512;\n\n\tif (!Number.isSafeInteger(maxLength) || maxLength <= 0) {\n\t\tthrow new RangeError(\"Предел длины десятичного значения должен быть положительным безопасным целым числом.\");\n\t}\n\n\treturn maxLength;\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Runtime-проверка для внешних данных без преобразования значащих цифр в number. */\nexport function isCanonicalDecimal(value: unknown, options?: DecimalOptions): value is string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\treturn (\n\t\ttypeof value === \"string\" && value.length <= maxLength && /^(?:0|-?(?:[1-9]\\d*(?:\\.\\d*[1-9])?|0\\.\\d*[1-9]))(?![\\s\\S])/u.test(value)\n\t);\n}\n","import { isCanonicalDecimal } from \"./isCanonicalDecimal\";\n\nimport type { DecimalOptions, DecimalParts } from \"./types\";\n\n/** Арифметика принимает только канонические строки; нормализация принадлежит входной границе. */\nexport function readDecimalParts(value: string, options?: DecimalOptions): DecimalParts {\n\tif (!isCanonicalDecimal(value, options)) {\n\t\tthrow new TypeError(\"Ожидалась каноническая десятичная строка в пределах допустимой длины.\");\n\t}\n\n\tconst point = value.indexOf(\".\");\n\n\treturn {\n\t\tcoefficient: BigInt(value.replace(\".\", \"\")),\n\t\tscale: point === -1 ? 0 : value.length - point - 1\n\t};\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions, DecimalParts } from \"./types\";\n\n/** Канонизирует результат после точного вычисления и отклоняет overflow вместо округления. */\nexport function writeDecimalParts(parts: DecimalParts, options?: DecimalOptions): string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\tif (parts.coefficient === 0n) {\n\t\treturn \"0\";\n\t}\n\n\tconst negative = parts.coefficient < 0n;\n\tconst magnitude = negative ? -parts.coefficient : parts.coefficient;\n\tconst digits = magnitude.toString().padStart(parts.scale + 1, \"0\");\n\tconst point = digits.length - parts.scale;\n\tconst unsigned = parts.scale === 0 ? digits : `${digits.slice(0, point)}.${digits.slice(point)}`.replace(/\\.?0+$/u, \"\");\n\tconst result = negative ? `-${unsigned}` : unsigned;\n\n\tif (result.length > maxLength) {\n\t\tthrow new RangeError(\"Результат десятичного вычисления превысил допустимую длину.\");\n\t}\n\n\treturn result;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Складывает значения без IEEE 754 и возвращает каноническую строку. */\nexport function addDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn writeDecimalParts({ coefficient: aligned.left + aligned.right, scale: aligned.scale }, options);\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\n\nimport type { DecimalComparison, DecimalOptions, DecimalRatio } from \"./types\";\n\n/** Сравнивает отношения перекрёстным умножением. Периодическая дробь не делится и не округляется. */\nexport function compareDecimalRatios(left: DecimalRatio, right: DecimalRatio, options?: DecimalOptions): DecimalComparison {\n\tconst leftNumerator = readDecimalParts(left.numerator, options);\n\tconst leftDenominator = readDecimalParts(left.denominator, options);\n\tconst rightNumerator = readDecimalParts(right.numerator, options);\n\tconst rightDenominator = readDecimalParts(right.denominator, options);\n\n\tif (leftDenominator.coefficient <= 0n || rightDenominator.coefficient <= 0n) {\n\t\tthrow new RangeError(\"Знаменатель отношения должен быть строго положительным.\");\n\t}\n\n\t// Произведения приватные: их разрядность ограничена четырьмя проверенными операндами,\n\t// но может превышать длину сериализуемого числа. Проверять их как внешний результат нельзя.\n\tconst aligned = alignDecimalParts(\n\t\t{ coefficient: leftNumerator.coefficient * rightDenominator.coefficient, scale: leftNumerator.scale + rightDenominator.scale },\n\t\t{ coefficient: rightNumerator.coefficient * leftDenominator.coefficient, scale: rightNumerator.scale + leftDenominator.scale }\n\t);\n\n\treturn aligned.left < aligned.right ? -1 : aligned.left > aligned.right ? 1 : 0;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\n\nimport type { DecimalComparison, DecimalOptions } from \"./types\";\n\n/** Сравнивает исходные значения, включая разрядность за пределами безопасного number. */\nexport function compareDecimals(left: string, right: string, options?: DecimalOptions): DecimalComparison {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn aligned.left < aligned.right ? -1 : aligned.left > aligned.right ? 1 : 0;\n}\n","import { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Точное умножение не устанавливает фиксированную сетку дробности. */\nexport function multiplyDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst first = readDecimalParts(left, options);\n\tconst second = readDecimalParts(right, options);\n\n\treturn writeDecimalParts({ coefficient: first.coefficient * second.coefficient, scale: first.scale + second.scale }, options);\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Убирает лишние нули. Пробелы, запятую, плюс и экспоненту обрабатывает владелец внешнего формата. */\nexport function normalizeDecimal(value: string, options?: DecimalOptions): string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\tif (typeof value !== \"string\" || value.length > maxLength || !/^-?\\d+(?:\\.\\d+)?(?![\\s\\S])/u.test(value)) {\n\t\tthrow new TypeError(\"Ожидалась ограниченная конечная десятичная строка с точкой.\");\n\t}\n\n\tconst negative = value.startsWith(\"-\");\n\tconst unsigned = negative ? value.slice(1) : value;\n\tconst [integer, fraction = \"\"] = unsigned.split(\".\");\n\tconst whole = integer.replace(/^0+(?=\\d)/u, \"\");\n\tconst fractional = fraction.replace(/0+$/u, \"\");\n\tconst magnitude = fractional ? `${whole}.${fractional}` : whole;\n\n\treturn negative && magnitude !== \"0\" ? `-${magnitude}` : magnitude;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Сохраняет знак остатка; предметные ограничения отрицательных значений находятся у consumer. */\nexport function subtractDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn writeDecimalParts({ coefficient: aligned.left - aligned.right, scale: aligned.scale }, options);\n}\n","import { addDecimals } from \"./addDecimals\";\nimport { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Читает iterable один раз и проверяет каждый промежуточный итог; пустой набор даёт ноль. */\nexport function sumDecimals(values: Iterable<string>, options?: DecimalOptions): string {\n\tresolveDecimalMaxLength(options);\n\tlet total = \"0\";\n\n\tfor (const value of values) {\n\t\ttotal = addDecimals(total, value, options);\n\t}\n\n\treturn total;\n}\n"],"mappings":";AAGA,SAAgB,EAAkB,GAAoB,GAA+E;CACpI,IAAM,IAAQ,KAAK,IAAI,EAAK,OAAO,EAAM,KAAK;CAE9C,OAAO;EACN,MAAM,EAAK,cAAc,OAAO,OAAO,IAAQ,EAAK,KAAK;EACzD,OAAO,EAAM,cAAc,OAAO,OAAO,IAAQ,EAAM,KAAK;EAC5D;CACD;AACD;;;ACRA,SAAgB,EAAwB,GAAkC;CACzE,IAAM,IAAY,GAAS,aAAa;CAExC,IAAI,CAAC,OAAO,cAAc,CAAS,KAAK,KAAa,GACpD,MAAU,WAAW,sFAAsF;CAG5G,OAAO;AACR;;;ACNA,SAAgB,EAAmB,GAAgB,GAA2C;CAC7F,IAAM,IAAY,EAAwB,CAAO;CAEjD,OACC,OAAO,KAAU,YAAY,EAAM,UAAU,KAAa,8DAA8D,KAAK,CAAK;AAEpI;;;ACNA,SAAgB,EAAiB,GAAe,GAAwC;CACvF,IAAI,CAAC,EAAmB,GAAO,CAAO,GACrC,MAAU,UAAU,uEAAuE;CAG5F,IAAM,IAAQ,EAAM,QAAQ,GAAG;CAE/B,OAAO;EACN,aAAa,OAAO,EAAM,QAAQ,KAAK,EAAE,CAAC;EAC1C,OAAO,MAAU,KAAK,IAAI,EAAM,SAAS,IAAQ;CAClD;AACD;;;ACXA,SAAgB,EAAkB,GAAqB,GAAkC;CACxF,IAAM,IAAY,EAAwB,CAAO;CAEjD,IAAI,EAAM,gBAAgB,IACzB,OAAO;CAGR,IAAM,IAAW,EAAM,cAAc,IAE/B,KADY,IAAW,CAAC,EAAM,cAAc,EAAM,YAAA,CAC/B,SAAS,CAAC,CAAC,SAAS,EAAM,QAAQ,GAAG,GAAG,GAC3D,IAAQ,EAAO,SAAS,EAAM,OAC9B,IAAW,EAAM,UAAU,IAAI,IAAS,GAAG,EAAO,MAAM,GAAG,CAAK,EAAE,GAAG,EAAO,MAAM,CAAK,IAAI,QAAQ,WAAW,EAAE,GAChH,IAAS,IAAW,IAAI,MAAa;CAE3C,IAAI,EAAO,SAAS,GACnB,MAAU,WAAW,6DAA6D;CAGnF,OAAO;AACR;;;ACjBA,SAAgB,EAAY,GAAc,GAAe,GAAkC;CAC1F,IAAM,IAAU,EAAkB,EAAiB,GAAM,CAAO,GAAG,EAAiB,GAAO,CAAO,CAAC;CAEnG,OAAO,EAAkB;EAAE,aAAa,EAAQ,OAAO,EAAQ;EAAO,OAAO,EAAQ;CAAM,GAAG,CAAO;AACtG;;;ACLA,SAAgB,EAAqB,GAAoB,GAAqB,GAA6C;CAC1H,IAAM,IAAgB,EAAiB,EAAK,WAAW,CAAO,GACxD,IAAkB,EAAiB,EAAK,aAAa,CAAO,GAC5D,IAAiB,EAAiB,EAAM,WAAW,CAAO,GAC1D,IAAmB,EAAiB,EAAM,aAAa,CAAO;CAEpE,IAAI,EAAgB,eAAe,MAAM,EAAiB,eAAe,IACxE,MAAU,WAAW,yDAAyD;CAK/E,IAAM,IAAU,EACf;EAAE,aAAa,EAAc,cAAc,EAAiB;EAAa,OAAO,EAAc,QAAQ,EAAiB;CAAM,GAC7H;EAAE,aAAa,EAAe,cAAc,EAAgB;EAAa,OAAO,EAAe,QAAQ,EAAgB;CAAM,CAC9H;CAEA,OAAO,EAAQ,OAAO,EAAQ,QAAQ,KAAK,IAAQ,OAAO,EAAQ;AACnE;;;AClBA,SAAgB,EAAgB,GAAc,GAAe,GAA6C;CACzG,IAAM,IAAU,EAAkB,EAAiB,GAAM,CAAO,GAAG,EAAiB,GAAO,CAAO,CAAC;CAEnG,OAAO,EAAQ,OAAO,EAAQ,QAAQ,KAAK,IAAQ,OAAO,EAAQ;AACnE;;;ACJA,SAAgB,EAAiB,GAAc,GAAe,GAAkC;CAC/F,IAAM,IAAQ,EAAiB,GAAM,CAAO,GACtC,IAAS,EAAiB,GAAO,CAAO;CAE9C,OAAO,EAAkB;EAAE,aAAa,EAAM,cAAc,EAAO;EAAa,OAAO,EAAM,QAAQ,EAAO;CAAM,GAAG,CAAO;AAC7H;;;ACNA,SAAgB,EAAiB,GAAe,GAAkC;CACjF,IAAM,IAAY,EAAwB,CAAO;CAEjD,IAAI,OAAO,KAAU,YAAY,EAAM,SAAS,KAAa,CAAC,8BAA8B,KAAK,CAAK,GACrG,MAAU,UAAU,6DAA6D;CAGlF,IAAM,IAAW,EAAM,WAAW,GAAG,GAE/B,CAAC,GAAS,IAAW,OADV,IAAW,EAAM,MAAM,CAAC,IAAI,EAAA,CACH,MAAM,GAAG,GAC7C,IAAQ,EAAQ,QAAQ,cAAc,EAAE,GACxC,IAAa,EAAS,QAAQ,QAAQ,EAAE,GACxC,IAAY,IAAa,GAAG,EAAM,GAAG,MAAe;CAE1D,OAAO,KAAY,MAAc,MAAM,IAAI,MAAc;AAC1D;;;ACbA,SAAgB,EAAiB,GAAc,GAAe,GAAkC;CAC/F,IAAM,IAAU,EAAkB,EAAiB,GAAM,CAAO,GAAG,EAAiB,GAAO,CAAO,CAAC;CAEnG,OAAO,EAAkB;EAAE,aAAa,EAAQ,OAAO,EAAQ;EAAO,OAAO,EAAQ;CAAM,GAAG,CAAO;AACtG;;;ACLA,SAAgB,EAAY,GAA0B,GAAkC;CACvF,EAAwB,CAAO;CAC/B,IAAI,IAAQ;CAEZ,KAAK,IAAM,KAAS,GACnB,IAAQ,EAAY,GAAO,GAAO,CAAO;CAG1C,OAAO;AACR"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/decimal/alignDecimalParts.ts","../../src/decimal/resolveDecimalMaxLength.ts","../../src/decimal/isCanonicalDecimal.ts","../../src/decimal/readDecimalParts.ts","../../src/decimal/writeDecimalParts.ts","../../src/decimal/addDecimals.ts","../../src/decimal/compareDecimalRatios.ts","../../src/decimal/compareDecimals.ts","../../src/decimal/multiplyDecimals.ts","../../src/decimal/normalizeDecimal.ts","../../src/decimal/normalizeDecimalLiteral.ts","../../src/decimal/subtractDecimals.ts","../../src/decimal/sumDecimals.ts"],"sourcesContent":["import type { DecimalParts } from \"./types\";\n\n/** Выравнивает только разрядность ограниченных операндов; глобального кеша степеней нет. */\nexport function alignDecimalParts(left: DecimalParts, right: DecimalParts): Readonly<{ left: bigint; right: bigint; scale: number }> {\n\tconst scale = Math.max(left.scale, right.scale);\n\n\treturn {\n\t\tleft: left.coefficient * 10n ** BigInt(scale - left.scale),\n\t\tright: right.coefficient * 10n ** BigInt(scale - right.scale),\n\t\tscale\n\t};\n}\n","import type { DecimalOptions } from \"./types\";\n\n/** Проверяет предел до разбора числа и выделения вычислительных буферов. */\nexport function resolveDecimalMaxLength(options?: DecimalOptions): number {\n\tconst maxLength = options?.maxLength ?? 512;\n\n\tif (!Number.isSafeInteger(maxLength) || maxLength <= 0) {\n\t\tthrow new RangeError(\"Предел длины десятичного значения должен быть положительным безопасным целым числом.\");\n\t}\n\n\treturn maxLength;\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Runtime-проверка для внешних данных без преобразования значащих цифр в number. */\nexport function isCanonicalDecimal(value: unknown, options?: DecimalOptions): value is string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\treturn (\n\t\ttypeof value === \"string\" && value.length <= maxLength && /^(?:0|-?(?:[1-9]\\d*(?:\\.\\d*[1-9])?|0\\.\\d*[1-9]))(?![\\s\\S])/u.test(value)\n\t);\n}\n","import { isCanonicalDecimal } from \"./isCanonicalDecimal\";\n\nimport type { DecimalOptions, DecimalParts } from \"./types\";\n\n/** Арифметика принимает только канонические строки; нормализация принадлежит входной границе. */\nexport function readDecimalParts(value: string, options?: DecimalOptions): DecimalParts {\n\tif (!isCanonicalDecimal(value, options)) {\n\t\tthrow new TypeError(\"Ожидалась каноническая десятичная строка в пределах допустимой длины.\");\n\t}\n\n\tconst point = value.indexOf(\".\");\n\n\treturn {\n\t\tcoefficient: BigInt(value.replace(\".\", \"\")),\n\t\tscale: point === -1 ? 0 : value.length - point - 1\n\t};\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions, DecimalParts } from \"./types\";\n\n/** Канонизирует результат после точного вычисления и отклоняет overflow вместо округления. */\nexport function writeDecimalParts(parts: DecimalParts, options?: DecimalOptions): string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\tif (parts.coefficient === 0n) {\n\t\treturn \"0\";\n\t}\n\n\tconst negative = parts.coefficient < 0n;\n\tconst magnitude = negative ? -parts.coefficient : parts.coefficient;\n\tconst digits = magnitude.toString().padStart(parts.scale + 1, \"0\");\n\tconst point = digits.length - parts.scale;\n\tconst unsigned = parts.scale === 0 ? digits : `${digits.slice(0, point)}.${digits.slice(point)}`.replace(/\\.?0+$/u, \"\");\n\tconst result = negative ? `-${unsigned}` : unsigned;\n\n\tif (result.length > maxLength) {\n\t\tthrow new RangeError(\"Результат десятичного вычисления превысил допустимую длину.\");\n\t}\n\n\treturn result;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Складывает значения без IEEE 754 и возвращает каноническую строку. */\nexport function addDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn writeDecimalParts({ coefficient: aligned.left + aligned.right, scale: aligned.scale }, options);\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\n\nimport type { DecimalComparison, DecimalOptions, DecimalRatio } from \"./types\";\n\n/** Сравнивает отношения перекрёстным умножением. Периодическая дробь не делится и не округляется. */\nexport function compareDecimalRatios(left: DecimalRatio, right: DecimalRatio, options?: DecimalOptions): DecimalComparison {\n\tconst leftNumerator = readDecimalParts(left.numerator, options);\n\tconst leftDenominator = readDecimalParts(left.denominator, options);\n\tconst rightNumerator = readDecimalParts(right.numerator, options);\n\tconst rightDenominator = readDecimalParts(right.denominator, options);\n\n\tif (leftDenominator.coefficient <= 0n || rightDenominator.coefficient <= 0n) {\n\t\tthrow new RangeError(\"Знаменатель отношения должен быть строго положительным.\");\n\t}\n\n\t// Произведения приватные: их разрядность ограничена четырьмя проверенными операндами,\n\t// но может превышать длину сериализуемого числа. Проверять их как внешний результат нельзя.\n\tconst aligned = alignDecimalParts(\n\t\t{ coefficient: leftNumerator.coefficient * rightDenominator.coefficient, scale: leftNumerator.scale + rightDenominator.scale },\n\t\t{ coefficient: rightNumerator.coefficient * leftDenominator.coefficient, scale: rightNumerator.scale + leftDenominator.scale }\n\t);\n\n\treturn aligned.left < aligned.right ? -1 : aligned.left > aligned.right ? 1 : 0;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\n\nimport type { DecimalComparison, DecimalOptions } from \"./types\";\n\n/** Сравнивает исходные значения, включая разрядность за пределами безопасного number. */\nexport function compareDecimals(left: string, right: string, options?: DecimalOptions): DecimalComparison {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn aligned.left < aligned.right ? -1 : aligned.left > aligned.right ? 1 : 0;\n}\n","import { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Точное умножение не устанавливает фиксированную сетку дробности. */\nexport function multiplyDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst first = readDecimalParts(left, options);\n\tconst second = readDecimalParts(right, options);\n\n\treturn writeDecimalParts({ coefficient: first.coefficient * second.coefficient, scale: first.scale + second.scale }, options);\n}\n","import { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Убирает лишние нули. Пробелы, запятую, плюс и экспоненту обрабатывает владелец внешнего формата. */\nexport function normalizeDecimal(value: string, options?: DecimalOptions): string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\n\tif (typeof value !== \"string\" || value.length > maxLength || !/^-?\\d+(?:\\.\\d+)?(?![\\s\\S])/u.test(value)) {\n\t\tthrow new TypeError(\"Ожидалась ограниченная конечная десятичная строка с точкой.\");\n\t}\n\n\tconst negative = value.startsWith(\"-\");\n\tconst unsigned = negative ? value.slice(1) : value;\n\tconst [integer, fraction = \"\"] = unsigned.split(\".\");\n\tconst whole = integer.replace(/^0+(?=\\d)/u, \"\");\n\tconst fractional = fraction.replace(/0+$/u, \"\");\n\tconst magnitude = fractional ? `${whole}.${fractional}` : whole;\n\n\treturn negative && magnitude !== \"0\" ? `-${magnitude}` : magnitude;\n}\n","import { normalizeDecimal } from \"./normalizeDecimal\";\nimport { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/**\n * Сохраняет все цифры внешнего числового литерала, включая научную запись JSON.\n * На вход передаётся исходная лексема parser, а не уже округлённый number.\n * Локализованные разделители и пробелы остаются ответственностью формы ввода.\n */\nexport function normalizeDecimalLiteral(value: string, options?: DecimalOptions): string {\n\tconst maxLength = resolveDecimalMaxLength(options);\n\tif (typeof value !== \"string\" || value.length > maxLength) {\n\t\tthrow new TypeError(\"Ожидался ограниченный десятичный числовой литерал.\");\n\t}\n\tconst match = /^(-?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?(?![\\s\\S])/u.exec(value);\n\tif (!match) throw new TypeError(\"Ожидался конечный десятичный числовой литерал.\");\n\tconst [, sign, integer, fraction = \"\", exponent] = match;\n\tif (exponent === undefined) return normalizeDecimal(value, options);\n\n\tconst digits = integer + fraction;\n\tconst significant = digits.replace(/^0+/u, \"\");\n\tif (!significant) return \"0\";\n\tconst coefficient = significant.replace(/0+$/u, \"\");\n\t// Number описывает только ограниченный сдвиг точки, никогда значение числа.\n\tconst shift = Number(exponent);\n\tif (!Number.isSafeInteger(shift)) throw new RangeError(\"Экспонента превышает допустимую длину результата.\");\n\tconst point = integer.length + shift - (digits.length - significant.length);\n\tconst length =\n\t\tsign.length +\n\t\t(point <= 0 ? 2 - point + coefficient.length : Math.max(point, coefficient.length + (point < coefficient.length ? 1 : 0)));\n\tif (length > maxLength) throw new RangeError(\"Десятичный результат превышает допустимую длину.\");\n\n\tif (point <= 0) return `${sign}0.${\"0\".repeat(-point)}${coefficient}`;\n\tif (point >= coefficient.length) return sign + coefficient + \"0\".repeat(point - coefficient.length);\n\treturn `${sign}${coefficient.slice(0, point)}.${coefficient.slice(point)}`;\n}\n","import { alignDecimalParts } from \"./alignDecimalParts\";\nimport { readDecimalParts } from \"./readDecimalParts\";\nimport { writeDecimalParts } from \"./writeDecimalParts\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Сохраняет знак остатка; предметные ограничения отрицательных значений находятся у consumer. */\nexport function subtractDecimals(left: string, right: string, options?: DecimalOptions): string {\n\tconst aligned = alignDecimalParts(readDecimalParts(left, options), readDecimalParts(right, options));\n\n\treturn writeDecimalParts({ coefficient: aligned.left - aligned.right, scale: aligned.scale }, options);\n}\n","import { addDecimals } from \"./addDecimals\";\nimport { resolveDecimalMaxLength } from \"./resolveDecimalMaxLength\";\n\nimport type { DecimalOptions } from \"./types\";\n\n/** Читает iterable один раз и проверяет каждый промежуточный итог; пустой набор даёт ноль. */\nexport function sumDecimals(values: Iterable<string>, options?: DecimalOptions): string {\n\tresolveDecimalMaxLength(options);\n\tlet total = \"0\";\n\n\tfor (const value of values) {\n\t\ttotal = addDecimals(total, value, options);\n\t}\n\n\treturn total;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DecimalOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Сохраняет все цифры внешнего числового литерала, включая научную запись JSON.
|
|
4
|
+
* На вход передаётся исходная лексема parser, а не уже округлённый number.
|
|
5
|
+
* Локализованные разделители и пробелы остаются ответственностью формы ввода.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeDecimalLiteral(value: string, options?: DecimalOptions): string;
|
|
8
|
+
//# sourceMappingURL=normalizeDecimalLiteral.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeDecimalLiteral.d.ts","sourceRoot":"","sources":["../../src/decimal/normalizeDecimalLiteral.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM,CA0BvF"}
|
package/dist/table/index.js
CHANGED
|
@@ -286,8 +286,10 @@ function ee(e) {
|
|
|
286
286
|
}
|
|
287
287
|
function te(e, t) {
|
|
288
288
|
if (typeof Element > "u" || !(e instanceof Element)) return !1;
|
|
289
|
-
let n = t ? `${Z}, ${t}` : Z;
|
|
290
|
-
|
|
289
|
+
let n = t ? `${Z}, ${t}` : Z, r = e.closest(n);
|
|
290
|
+
if (!r) return !1;
|
|
291
|
+
let i = e.closest("tr");
|
|
292
|
+
return !i || i.contains(r);
|
|
291
293
|
}
|
|
292
294
|
//#endregion
|
|
293
295
|
export { C as DEFAULT_TABLE_COLUMN_MIN_WIDTH, A as areTableCellSelectionsEqual, G as areTableRowSelectionsEqual, v as buildTableColumnLayout, B as createTableColumnVisibilityFromODataMetadata, V as createTableColumnsFromODataMetadata, H as enrichTableColumnsWithODataFormatting, Y as foundationTableFeatures, $ as getTableColumnFormattingMeta, Q as getTableColumnMeta, te as isTableInteractiveElement, N as normalizeTableCellSelectionActivationMode, y as normalizeTableColumnOrder, T as normalizeTableColumnSizing, w as normalizeTableColumnWidth, K as normalizeTableSelectionMode, E as patchTableColumnWidth, j as pruneTableCellSelection, q as pruneTableRowSelection, D as removeTableColumnWidth, x as reorderTableHeaderColumns, S as resolveReorderedTableHeaderColumns, R as resolveStableColumnId, F as resolveTableColumnFormattingContextFromODataColumn, _ as resolveTableColumnOrder, ee as resolveTableLength, P as shouldActivateTableCellSelection, M as toggleTableCellSelection, J as toggleTableRowSelection, X as useTableRowSelection };
|
package/dist/table/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/table/columnLayout.ts","../../src/table/columnOrder.ts","../../src/table/columnSizing.ts","../../src/table/cellSelection.ts","../../src/table/odataAdapter.ts","../../src/table/selection.ts","../../src/table/tanStackFeatures.ts","../../src/table/useTableRowSelection.ts","../../src/table/utils.ts"],"sourcesContent":["import { appendMissingIds, filterAndDeduplicateIds } from \"../array\";\n\nexport type TableColumnLayoutItem<TColumnId extends string = string, TWidth = number> = {\n\tid: TColumnId;\n\twidth: TWidth;\n};\n\nexport type ResolveTableColumnOrderArgs<TColumnId extends string = string> = {\n\tids: readonly TColumnId[];\n\torder?: readonly string[];\n};\n\nexport type BuildTableColumnLayoutArgs<TColumnId extends string = string, TWidth = number> = {\n\tids: readonly TColumnId[];\n\tgetWidth: (id: TColumnId) => TWidth;\n};\n\n/**\n * Возвращает порядок колонок как в AnalyticalTable: сначала сохранённый order,\n * затем новые или отсутствующие в order id. Ширины при этом остаются привязаны\n * к columnId, а не к визуальной позиции.\n */\nexport function resolveTableColumnOrder<TColumnId extends string>({ ids, order }: ResolveTableColumnOrderArgs<TColumnId>): TColumnId[] {\n\treturn appendMissingIds(filterAndDeduplicateIds(order, ids), ids);\n}\n\n/**\n * Строит layout колонок из уже вычисленного порядка и функции ширины по id.\n */\nexport function buildTableColumnLayout<TColumnId extends string, TWidth>({\n\tids,\n\tgetWidth\n}: BuildTableColumnLayoutArgs<TColumnId, TWidth>): TableColumnLayoutItem<TColumnId, TWidth>[] {\n\treturn ids.map((id) => ({\n\t\tid,\n\t\twidth: getWidth(id)\n\t}));\n}\n","import { arraysEqual } from \"../array\";\n\nexport type ReorderTableHeaderColumnsArgs = {\n\torder: readonly string[];\n\theaderIds: readonly string[];\n\tlockedIds?: readonly string[];\n\tpinnedIds?: readonly string[];\n\tactiveId: string;\n\toverId: string;\n};\n\nexport type TableColumnOrderState = string[];\n\nexport function normalizeTableColumnOrder(order: readonly string[] | undefined): TableColumnOrderState {\n\treturn Array.from(new Set((order ?? []).filter((columnId) => columnId.trim().length > 0)));\n}\n\nfunction moveItem<T>(items: readonly T[], fromIndex: number, toIndex: number): T[] {\n\tconst nextItems = [...items];\n\tconst [item] = nextItems.splice(fromIndex, 1);\n\n\tif (item === undefined) {\n\t\treturn nextItems;\n\t}\n\n\tnextItems.splice(toIndex, 0, item);\n\treturn nextItems;\n}\n\n/**\n * Переставляет только верхнеуровневые колонки табличной шапки.\n *\n * Ограничения:\n * - дочерние колонки групп не являются самостоятельными draggable-элементами;\n * - locked-колонки остаются в фиксированном порядке;\n * - pinned-колонки не переносятся между закреплённой и обычной зонами.\n */\nexport function reorderTableHeaderColumns(args: ReorderTableHeaderColumnsArgs): string[] {\n\tconst { activeId, overId, order } = args;\n\tif (!activeId || !overId || activeId === overId) return [...order];\n\n\tconst headerSet = new Set(args.headerIds);\n\tif (!headerSet.has(activeId) || !headerSet.has(overId)) return [...order];\n\n\tconst lockedSet = new Set(args.lockedIds ?? []);\n\tif (lockedSet.has(activeId) || lockedSet.has(overId)) return [...order];\n\n\tconst pinnedSet = new Set(args.pinnedIds ?? []);\n\tif (pinnedSet.has(activeId) || pinnedSet.has(overId)) return [...order];\n\n\tconst nextOrder = [...order];\n\tif (!nextOrder.includes(activeId)) {\n\t\tnextOrder.push(activeId);\n\t}\n\tif (!nextOrder.includes(overId)) {\n\t\tnextOrder.push(overId);\n\t}\n\n\tconst fromIndex = nextOrder.indexOf(activeId);\n\tconst toIndex = nextOrder.indexOf(overId);\n\tif (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return nextOrder;\n\n\treturn moveItem(nextOrder, fromIndex, toIndex);\n}\n\nexport function resolveReorderedTableHeaderColumns(args: ReorderTableHeaderColumnsArgs): string[] | null {\n\tconst nextOrder = reorderTableHeaderColumns(args);\n\n\treturn arraysEqual(nextOrder, args.order) ? null : nextOrder;\n}\n","export type TableColumnSizingState = Record<string, number>;\n\nexport const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 60;\n\nexport function normalizeTableColumnWidth(px: number, minWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH): number {\n\tconst normalizedMinWidth = Number.isFinite(minWidth) && minWidth > 0 ? Math.floor(minWidth) : DEFAULT_TABLE_COLUMN_MIN_WIDTH;\n\tconst normalizedWidth = Number.isFinite(px) ? Math.floor(px) : normalizedMinWidth;\n\n\treturn Math.max(normalizedMinWidth, normalizedWidth);\n}\n\nexport function normalizeTableColumnSizing(\n\tstate: Readonly<Record<string, number>> | undefined,\n\tminWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH\n): TableColumnSizingState {\n\tconst nextState: TableColumnSizingState = {};\n\n\tfor (const [columnId, width] of Object.entries(state ?? {})) {\n\t\tif (typeof columnId !== \"string\" || columnId.trim().length === 0 || !Number.isFinite(width)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tnextState[columnId] = normalizeTableColumnWidth(width, minWidth);\n\t}\n\n\treturn nextState;\n}\n\nexport function patchTableColumnWidth(\n\tstate: Readonly<Record<string, number>>,\n\tcolumnId: string,\n\twidth: number,\n\tminWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH\n): TableColumnSizingState {\n\tif (columnId.trim().length === 0) {\n\t\treturn normalizeTableColumnSizing(state, minWidth);\n\t}\n\n\treturn {\n\t\t...normalizeTableColumnSizing(state, minWidth),\n\t\t[columnId]: normalizeTableColumnWidth(width, minWidth)\n\t};\n}\n\nexport function removeTableColumnWidth(state: Readonly<Record<string, number>>, columnId: string): TableColumnSizingState {\n\tconst nextState: TableColumnSizingState = {};\n\n\tfor (const [key, width] of Object.entries(state)) {\n\t\tif (key !== columnId) {\n\t\t\tnextState[key] = width;\n\t\t}\n\t}\n\n\treturn nextState;\n}\n","import {\n\ttype TableCellCoordinates,\n\ttype TableCellSelectionActivationMode,\n\ttype TableCellSelectionMode,\n\ttype TableCellSelectionState,\n\ttype TableSelectionModifierEvent\n} from \"./types\";\n\nconst TABLE_CELL_SELECTION_ACTIVATION_MODES: ReadonlySet<TableCellSelectionActivationMode> = new Set([\"direct\", \"primary-modifier\"]);\n\n/** Строит collision-safe identity координат для дедупликации и сравнения. */\nfunction buildTableCellKey(cell: TableCellCoordinates): string {\n\treturn `${encodeURIComponent(cell.rowId)}::${encodeURIComponent(cell.columnId)}`;\n}\n\n/** Проверяет равенство двух наборов координат без зависимости от порядка. */\nexport function areTableCellSelectionsEqual(left: TableCellSelectionState, right: TableCellSelectionState): boolean {\n\tconst leftKeys = new Set(left.map(buildTableCellKey));\n\tconst rightKeys = new Set(right.map(buildTableCellKey));\n\n\tif (leftKeys.size !== rightKeys.size) {\n\t\treturn false;\n\t}\n\n\treturn [...leftKeys].every((key) => rightKeys.has(key));\n}\n\n/**\n * Оставляет только доступные координаты и приводит их к порядку строк/колонок.\n *\n * Функция не мутирует входные массивы. В `single` последняя координата в\n * детерминированном порядке становится единственным выбранным значением.\n */\nexport function pruneTableCellSelection(\n\tselection: TableCellSelectionState | undefined,\n\tavailableRowIds: readonly string[],\n\tavailableColumnIds: readonly string[],\n\tselectionMode: TableCellSelectionMode\n): TableCellCoordinates[] {\n\tif (selectionMode === \"none\" || !selection?.length) {\n\t\treturn [];\n\t}\n\n\tconst rowOrder = new Map(availableRowIds.map((rowId, index) => [rowId, index]));\n\tconst columnOrder = new Map(availableColumnIds.map((columnId, index) => [columnId, index]));\n\tconst seen = new Set<string>();\n\tconst nextSelection = selection\n\t\t.filter((cell) => {\n\t\t\tif (!(rowOrder.has(cell.rowId) && columnOrder.has(cell.columnId))) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst key = buildTableCellKey(cell);\n\t\t\tif (seen.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tseen.add(key);\n\t\t\treturn true;\n\t\t})\n\t\t.sort((left, right) => {\n\t\t\tconst rowDelta = (rowOrder.get(left.rowId) ?? 0) - (rowOrder.get(right.rowId) ?? 0);\n\t\t\tif (rowDelta !== 0) {\n\t\t\t\treturn rowDelta;\n\t\t\t}\n\n\t\t\treturn (columnOrder.get(left.columnId) ?? 0) - (columnOrder.get(right.columnId) ?? 0);\n\t\t});\n\n\tif (selectionMode === \"single\") {\n\t\tconst lastCell = nextSelection.at(-1);\n\t\treturn lastCell ? [lastCell] : [];\n\t}\n\n\treturn nextSelection;\n}\n\n/**\n * Вычисляет следующее состояние после активации ячейки.\n *\n * Одиночный выбор допускает явное снятие повторной активацией, а `multi`\n * переключает только переданную координату.\n */\nexport function toggleTableCellSelection(\n\tselection: TableCellSelectionState,\n\tcell: TableCellCoordinates,\n\tselectionMode: TableCellSelectionMode\n): TableCellCoordinates[] {\n\tif (selectionMode === \"none\") {\n\t\treturn [];\n\t}\n\n\tconst cellKey = buildTableCellKey(cell);\n\tconst hasCell = selection.some((item) => buildTableCellKey(item) === cellKey);\n\n\tif (selectionMode === \"single\") {\n\t\treturn hasCell ? [] : [cell];\n\t}\n\n\treturn hasCell ? selection.filter((item) => buildTableCellKey(item) !== cellKey) : [...selection, cell];\n}\n\n/** Нормализует способ активации ячейки на внешней config-boundary. */\nexport function normalizeTableCellSelectionActivationMode(\n\tvalue: unknown,\n\tfallback: TableCellSelectionActivationMode = \"direct\"\n): TableCellSelectionActivationMode {\n\treturn typeof value === \"string\" && TABLE_CELL_SELECTION_ACTIVATION_MODES.has(value as TableCellSelectionActivationMode)\n\t\t? (value as TableCellSelectionActivationMode)\n\t\t: fallback;\n}\n\n/**\n * Проверяет, должно ли событие активировать выбор ячейки.\n *\n * Для `primary-modifier` принимается Ctrl или Command, но не комбинации с\n * Shift/Alt: они зарезервированы браузером, ОС и стандартными selection-flow.\n */\nexport function shouldActivateTableCellSelection(\n\tevent: TableSelectionModifierEvent,\n\tactivationMode: TableCellSelectionActivationMode\n): boolean {\n\tif (activationMode === \"direct\") {\n\t\treturn true;\n\t}\n\n\treturn (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey;\n}\n","import type { FormattersPipelineTypedValueContext } from \"../formatters/pipeline\";\nimport type { EntityColumnProperty } from \"../odata-service\";\nimport type { TableColumnAlign, TableColumnDef, TableColumnFormattingMetaInput } from \"./types\";\n\n/**\n * Опции генерации колонок по OData metadata.\n */\nexport interface CreateTableColumnsFromODataMetadataOptions {\n\t/**\n\t * Позволяет переопределить заголовок колонки.\n\t */\n\tresolveHeader?: (column: EntityColumnProperty) => string;\n\t/**\n\t * Позволяет сразу навесить formatting-конфиг на generated column.\n\t *\n\t * `id`, `role` и `type` будут автоматически получены из metadata.\n\t */\n\tresolveFormatting?: (column: EntityColumnProperty) => TableColumnFormattingMetaInput | undefined;\n}\n\n/**\n * Опции обогащения уже существующих колонок данными OData metadata.\n */\nexport interface EnrichTableColumnsWithODataFormattingOptions {\n\t/**\n\t * Позволяет подменить стратегию поиска metadata по колонке.\n\t */\n\tresolveMetadataColumn?: (args: {\n\t\tcolumnId: string;\n\t\tmetadataColumnsById: ReadonlyMap<string, EntityColumnProperty>;\n\t}) => EntityColumnProperty | undefined;\n}\n\n/**\n * Опции построения стартовой карты видимости колонок по OData metadata.\n */\nexport interface CreateTableColumnVisibilityFromODataMetadataOptions {\n\t/**\n\t * Позволяет явно переопределить стартовую видимость колонки.\n\t */\n\tresolveVisible?: (column: EntityColumnProperty) => boolean;\n}\n\n/**\n * Возвращает typed-formatting контекст по metadata OData-колонки.\n */\nexport function resolveTableColumnFormattingContextFromODataColumn(column: EntityColumnProperty): FormattersPipelineTypedValueContext {\n\tconst { role, type } = column;\n\tconst forceMeasure = type === \"decimal\" || type === \"float\" || type === \"double\" || type === \"int\" || type === \"byte\";\n\n\treturn {\n\t\trole: forceMeasure ? \"measure\" : role,\n\t\ttype: type\n\t};\n}\n\n/**\n * Создаёт базовое выравнивание по семантике OData-колонки.\n *\n * NOTE: \"не аналитические\" все поля возвращают как \"dimension\"...\n */\nfunction resolveTableColumnAlignFromODataColumn(columnContext: FormattersPipelineTypedValueContext): TableColumnAlign {\n\treturn columnContext.role === \"measure\" ? \"right\" : \"left\";\n}\n\n/**\n * Определяет видимость metadata-колонки по умолчанию.\n *\n * Кодовые поля скрываются только если у них есть явная text-пара,\n * чтобы экран по умолчанию показывал человекочитаемое значение.\n */\nfunction resolveMetadataColumnVisibleByDefault(\n\tcolumn: EntityColumnProperty,\n\tmetadataColumnsById: ReadonlyMap<string, EntityColumnProperty>\n): boolean {\n\tif (column.semanticType !== \"code\" || !column.linkedColumnId) {\n\t\treturn true;\n\t}\n\n\tconst linkedColumn = metadataColumnsById.get(column.linkedColumnId);\n\n\treturn linkedColumn?.semanticType !== \"text\";\n}\n\n/**\n * Определяет стабильный идентификатор колонки для сопоставления с metadata.\n */\nexport function resolveStableColumnId<TData extends object>(column: TableColumnDef<TData>): string | undefined {\n\tif (typeof column.id === \"string\" && column.id.trim()) {\n\t\treturn column.id;\n\t}\n\n\tif (\"accessorKey\" in column && typeof column.accessorKey === \"string\" && column.accessorKey.trim()) {\n\t\treturn column.accessorKey;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Рекурсивно обходит только leaf-колонки.\n */\nfunction mapLeafTableColumns<TData extends object>(\n\tcolumns: readonly TableColumnDef<TData>[],\n\tmapper: (column: TableColumnDef<TData>) => TableColumnDef<TData>\n): TableColumnDef<TData>[] {\n\treturn columns.map((column) => {\n\t\tif (\"columns\" in column && Array.isArray(column.columns) && column.columns.length > 0) {\n\t\t\treturn {\n\t\t\t\t...column,\n\t\t\t\tcolumns: mapLeafTableColumns(column.columns as TableColumnDef<TData>[], mapper)\n\t\t\t};\n\t\t}\n\n\t\treturn mapper(column);\n\t});\n}\n\n/**\n * Строит стартовую карту видимости колонок из metadata OData-сущности.\n *\n * Возвращает visibility-state для TanStack Table, не удаляя сами колонки\n * из результата build-режима.\n */\nexport function createTableColumnVisibilityFromODataMetadata(\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: CreateTableColumnVisibilityFromODataMetadataOptions\n): Record<string, boolean> {\n\tconst metadataColumnsById = new Map(metadataColumns.map((column) => [column.id, column]));\n\n\treturn Object.fromEntries(\n\t\tmetadataColumns.map((column) => {\n\t\t\tconst visible = options?.resolveVisible?.(column) ?? resolveMetadataColumnVisibleByDefault(column, metadataColumnsById);\n\n\t\t\treturn [column.id, visible];\n\t\t})\n\t);\n}\n\n/**\n * Создаёт базовые generic-колонки из metadata OData-сущности.\n */\nexport function createTableColumnsFromODataMetadata<TData extends object = Record<string, unknown>>(\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: CreateTableColumnsFromODataMetadataOptions\n): TableColumnDef<TData>[] {\n\treturn metadataColumns.map((metadataColumn) => {\n\t\t// NOTE: Ячейка сама определит выравнивание, `align` только для переопределения\n\t\tconst columnContext = resolveTableColumnFormattingContextFromODataColumn(metadataColumn);\n\t\tconst align = resolveTableColumnAlignFromODataColumn(columnContext);\n\t\tconst resolvedFormatting = options?.resolveFormatting?.(metadataColumn);\n\n\t\treturn {\n\t\t\tid: metadataColumn.id,\n\t\t\taccessorKey: metadataColumn.id,\n\t\t\theader: options?.resolveHeader?.(metadataColumn) ?? metadataColumn.label ?? metadataColumn.id,\n\t\t\tmeta: {\n\t\t\t\talign,\n\t\t\t\tformatting: {\n\t\t\t\t\t...resolvedFormatting,\n\t\t\t\t\tid: metadataColumn.id,\n\t\t\t\t\trole: columnContext.role,\n\t\t\t\t\ttype: columnContext.type\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}) as TableColumnDef<TData>[];\n}\n\n/**\n * Дозаполняет уже существующие колонки typed-formatting контекстом из OData metadata.\n */\nexport function enrichTableColumnsWithODataFormatting<TData extends object>(\n\tcolumns: readonly TableColumnDef<TData>[],\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: EnrichTableColumnsWithODataFormattingOptions\n): TableColumnDef<TData>[] {\n\tconst metadataColumnsById = new Map(metadataColumns.map((column) => [column.id, column]));\n\n\treturn mapLeafTableColumns(columns, (column) => {\n\t\tconst columnId = resolveStableColumnId(column);\n\t\tif (!columnId) {\n\t\t\treturn column;\n\t\t}\n\n\t\tconst metadataColumn =\n\t\t\toptions?.resolveMetadataColumn?.({\n\t\t\t\tcolumnId,\n\t\t\t\tmetadataColumnsById\n\t\t\t}) ?? metadataColumnsById.get(columnId);\n\n\t\tif (!metadataColumn) {\n\t\t\treturn column;\n\t\t}\n\n\t\tif (column.meta?.formatting?.role && column.meta.formatting.type) {\n\t\t\treturn column;\n\t\t}\n\t\tconst columnContext = resolveTableColumnFormattingContextFromODataColumn(metadataColumn);\n\n\t\treturn {\n\t\t\t...column,\n\t\t\tmeta: {\n\t\t\t\t...column.meta,\n\t\t\t\tformatting: {\n\t\t\t\t\t...column.meta?.formatting,\n\t\t\t\t\tid: columnId,\n\t\t\t\t\trole: columnContext.role,\n\t\t\t\t\ttype: columnContext.type\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t});\n}\n","import { RowSelectionState } from \"@tanstack/react-table\";\n\nimport { TableSelectionMode } from \"./types\";\n\nconst TABLE_SELECTION_MODES: ReadonlySet<TableSelectionMode> = new Set([\"none\", \"single\", \"multi\"]);\n\n/**\n * Возвращает только активные идентификаторы выбранных строк.\n */\nfunction getSelectedRowIds(selection: RowSelectionState): string[] {\n\treturn Object.keys(selection).filter((rowId) => selection[rowId]);\n}\n\n/** Проверяет равенство активных row-selection идентификаторов без учёта порядка ключей. */\nexport function areTableRowSelectionsEqual(left: RowSelectionState, right: RowSelectionState): boolean {\n\tconst leftRowIds = getSelectedRowIds(left);\n\tconst rightRowIds = getSelectedRowIds(right);\n\n\tif (leftRowIds.length !== rightRowIds.length) {\n\t\treturn false;\n\t}\n\n\treturn leftRowIds.every((rowId) => right[rowId]);\n}\n\n/** Нормализует неизвестное значение режима выбора строк на внешней config-boundary. */\nexport function normalizeTableSelectionMode(value: unknown, fallback: TableSelectionMode = \"none\"): TableSelectionMode {\n\treturn typeof value === \"string\" && TABLE_SELECTION_MODES.has(value as TableSelectionMode) ? (value as TableSelectionMode) : fallback;\n}\n\n/**\n * Нормализует состояние выбора относительно списка доступных строк и текущего режима.\n */\nexport function pruneTableRowSelection(\n\tselection: RowSelectionState,\n\tavailableRowIds: readonly string[],\n\tselectionMode: TableSelectionMode\n): RowSelectionState {\n\tif (selectionMode === \"none\") {\n\t\treturn {};\n\t}\n\n\tconst availableRowIdSet = new Set(availableRowIds);\n\tconst selectedRowIds = getSelectedRowIds(selection).filter((rowId) => availableRowIdSet.has(rowId));\n\n\tif (selectionMode === \"single\") {\n\t\tconst lastSelectedRowId = selectedRowIds.at(-1);\n\n\t\treturn lastSelectedRowId ? { [lastSelectedRowId]: true } : {};\n\t}\n\n\treturn Object.fromEntries(selectedRowIds.map((rowId) => [rowId, true]));\n}\n\n/**\n * Вычисляет следующее состояние выбора после активации строки пользователем.\n *\n * Семантика:\n * - `none` не хранит выбор;\n * - `single` всегда оставляет только одну строку и не снимает выбор повторным кликом;\n * - `multi` переключает строку как toggle.\n */\nexport function toggleTableRowSelection(selection: RowSelectionState, rowId: string, selectionMode: TableSelectionMode): RowSelectionState {\n\tif (selectionMode === \"none\") {\n\t\treturn {};\n\t}\n\n\tif (selectionMode === \"single\") {\n\t\tconst selectedRowIds = getSelectedRowIds(selection);\n\t\treturn selectedRowIds.length === 1 && selectedRowIds[0] === rowId ? selection : { [rowId]: true };\n\t}\n\n\tif (selection[rowId]) {\n\t\tconst nextSelection = { ...selection };\n\n\t\tdelete nextSelection[rowId];\n\n\t\treturn nextSelection;\n\t}\n\n\treturn {\n\t\t...selection,\n\t\t[rowId]: true\n\t};\n}\n","import {\n\tcolumnOrderingFeature,\n\tcolumnPinningFeature,\n\tcolumnResizingFeature,\n\tcolumnSizingFeature,\n\tcolumnVisibilityFeature,\n\tcreateExpandedRowModel,\n\trowExpandingFeature,\n\trowSelectionFeature,\n\ttableFeatures\n} from \"@tanstack/react-table\";\n\n/**\n * Единый набор TanStack Table v9 features для foundation-таблиц.\n *\n * Контракт хранится в `lib`, чтобы определения колонок, строк и экземпляров\n * таблицы в `lib` и `ui` всегда использовали один и тот же набор generic-типов.\n */\nexport const foundationTableFeatures = tableFeatures({\n\tcolumnOrderingFeature,\n\tcolumnPinningFeature,\n\tcolumnSizingFeature,\n\tcolumnResizingFeature,\n\tcolumnVisibilityFeature,\n\trowSelectionFeature,\n\trowExpandingFeature,\n\texpandedRowModel: createExpandedRowModel()\n});\n\n/** Generic-контракт features для публичных типов foundation-таблиц. */\nexport type FoundationTableFeatures = typeof foundationTableFeatures;\n","import { useCallback, useEffect, useEffectEvent, useRef, useState } from \"react\";\n\nimport { RowSelectionState } from \"@tanstack/react-table\";\n\nimport { pruneTableRowSelection, toggleTableRowSelection } from \"./selection\";\nimport { TableSelectionMode } from \"./types\";\n\ninterface UseTableRowSelectionParams<TData extends object> {\n\tavailableRowIds: readonly string[];\n\trowById: Map<string, TData>;\n\tselectionMode: TableSelectionMode;\n\tselectedRowIds?: readonly string[];\n\tonRowSelectionChange?: (rows: TData[]) => void;\n}\n\n/**\n * Общая модель выбора строк для плоской и древовидной таблиц.\n */\nexport function useTableRowSelection<TData extends object>({\n\tavailableRowIds,\n\trowById,\n\tselectionMode,\n\tselectedRowIds,\n\tonRowSelectionChange\n}: UseTableRowSelectionParams<TData>) {\n\tconst [rowSelection, setRowSelection] = useState<RowSelectionState>({});\n\tconst shouldSkipNextSelectionEmitRef = useRef(false);\n\n\t/**\n\t * Сравнивает состояния выбора без лишних перерисовок.\n\t */\n\tconst areSelectionsEqual = useCallback((left: RowSelectionState, right: RowSelectionState) => {\n\t\tconst leftRowIds = Object.keys(left).filter((rowId) => left[rowId]);\n\t\tconst rightRowIds = Object.keys(right).filter((rowId) => right[rowId]);\n\n\t\tif (leftRowIds.length !== rightRowIds.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn leftRowIds.every((rowId) => right[rowId]);\n\n\t\t/*\n\n Более оптимизированная версия, но для текущего случая может быть практически незначительной\n\n let leftCount = 0;\n\t\tlet rightCount = 0;\n\n\t\tfor (const rowId of Object.keys(left)) {\n\t\t\tif (!left[rowId]) continue;\n\t\t\tleftCount += 1;\n\t\t\tif (!right[rowId]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfor (const rowId of Object.keys(right)) {\n\t\t\tif (right[rowId]) {\n\t\t\t\trightCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tif (leftCount !== rightCount) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\n */\n\t}, []);\n\n\tconst syncRowSelectionState = useEffectEvent((nextAvailableRowIds: readonly string[]) => {\n\t\tsetRowSelection((currentSelection) => pruneTableRowSelection(currentSelection, nextAvailableRowIds, selectionMode));\n\t});\n\n\tconst syncControlledRowSelectionState = useEffectEvent(\n\t\t(nextSelectedRowIds: readonly string[], nextAvailableRowIds: readonly string[]) => {\n\t\t\tconst nextSelection = pruneTableRowSelection(\n\t\t\t\tObject.fromEntries(nextSelectedRowIds.map((rowId) => [rowId, true])),\n\t\t\t\tnextAvailableRowIds,\n\t\t\t\tselectionMode\n\t\t\t);\n\n\t\t\tsetRowSelection((currentSelection) => {\n\t\t\t\tif (areSelectionsEqual(currentSelection, nextSelection)) {\n\t\t\t\t\treturn currentSelection;\n\t\t\t\t}\n\n\t\t\t\t// Пропускаем следующий emit, потому что это синхронизация от внешнего controlled-состояния,\n\t\t\t\t// а не пользовательское изменение выбора внутри таблицы.\n\t\t\t\tshouldSkipNextSelectionEmitRef.current = true;\n\n\t\t\t\treturn nextSelection;\n\t\t\t});\n\t\t}\n\t);\n\n\tconst emitRowSelectionChange = useEffectEvent((currentSelection: RowSelectionState) => {\n\t\tif (!onRowSelectionChange) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst selectedRows: TData[] = [];\n\n\t\tfor (const rowId of Object.keys(currentSelection)) {\n\t\t\tif (!currentSelection[rowId]) continue;\n\n\t\t\tconst row = rowById.get(rowId);\n\t\t\tif (row) {\n\t\t\t\tselectedRows.push(row);\n\t\t\t}\n\t\t}\n\n\t\tonRowSelectionChange(selectedRows);\n\t});\n\n\tuseEffect(() => {\n\t\tif (selectedRowIds !== undefined) {\n\t\t\tsyncControlledRowSelectionState(selectedRowIds, availableRowIds);\n\t\t\treturn;\n\t\t}\n\n\t\tsyncRowSelectionState(availableRowIds);\n\t}, [availableRowIds, selectedRowIds, selectionMode]);\n\n\tuseEffect(() => {\n\t\tif (shouldSkipNextSelectionEmitRef.current) {\n\t\t\tshouldSkipNextSelectionEmitRef.current = false;\n\t\t\treturn;\n\t\t}\n\n\t\temitRowSelectionChange(rowSelection);\n\t}, [rowSelection]);\n\n\t/**\n\t * Применяет изменение выбора при активации конкретной строки.\n\t */\n\tconst activateRowSelection = useCallback(\n\t\t(rowId: string, canSelect: boolean) => {\n\t\t\tif (selectionMode === \"none\" || !canSelect) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetRowSelection((currentSelection) => toggleTableRowSelection(currentSelection, rowId, selectionMode));\n\t\t},\n\t\t[selectionMode]\n\t);\n\n\treturn {\n\t\trowSelection,\n\t\tsetRowSelection,\n\t\tactivateRowSelection\n\t};\n}\n","import { TableColumnDef, TableColumnFormattingMeta, TableColumnMeta } from \"./types\";\n\n/**\n * Интерактивные descendants, действие которых таблица не должна подменять\n * выбором строки или ячейки.\n *\n * `td`, `th` и `tr` намеренно исключены из tabindex-ветки: сама таблица может\n * использовать roving tabindex для selection, не превращая ячейку в nested control.\n */\nconst TABLE_INTERACTIVE_ELEMENT_SELECTOR = [\n\t\"button\",\n\t\"a\",\n\t\"input\",\n\t\"select\",\n\t\"textarea\",\n\t\"label\",\n\t\"summary\",\n\t\"audio[controls]\",\n\t\"video[controls]\",\n\t'[contenteditable]:not([contenteditable=\"false\"])',\n\t\"[tabindex]:not(td):not(th):not(tr)\",\n\t'[draggable=\"true\"]',\n\t'[role=\"button\"]',\n\t'[role=\"link\"]',\n\t'[role=\"checkbox\"]',\n\t'[role=\"radio\"]',\n\t'[role=\"switch\"]',\n\t'[role=\"menu\"]',\n\t'[role=\"menuitem\"]',\n\t'[role=\"menuitemcheckbox\"]',\n\t'[role=\"menuitemradio\"]',\n\t'[role=\"listbox\"]',\n\t'[role=\"option\"]',\n\t'[role=\"tab\"]',\n\t'[role=\"tablist\"]',\n\t'[role=\"tree\"]',\n\t'[role=\"treeitem\"]',\n\t'[role=\"slider\"]',\n\t'[role=\"spinbutton\"]',\n\t'[role=\"textbox\"]',\n\t'[role=\"searchbox\"]',\n\t'[role=\"combobox\"]'\n].join(\", \");\n\n/**\n * Возвращает метаданные колонки таблицы.\n */\nexport function getTableColumnMeta<TData extends object>(column: TableColumnDef<TData>): TableColumnMeta | undefined {\n\treturn column.meta;\n}\n\n/**\n * Возвращает форматирующий блок метаданных колонки.\n */\nexport function getTableColumnFormattingMeta<TData extends object>(column: TableColumnDef<TData>): TableColumnFormattingMeta | undefined {\n\treturn column.meta?.formatting;\n}\n\n/**\n * Нормализует размер таблицы.\n *\n * Числовое значение трактуется как `em`, чтобы разметка масштабировалась вместе со шрифтом.\n */\nexport function resolveTableLength(length: string | number): string {\n\tif (typeof length === \"number\") {\n\t\treturn `${length}em`;\n\t}\n\n\treturn length;\n}\n\n/**\n * Определяет, пришёл ли клик из интерактивного элемента внутри таблицы.\n */\nexport function isTableInteractiveElement(target: EventTarget | null, extraSelector?: string): boolean {\n\tif (typeof Element === \"undefined\" || !(target instanceof Element)) {\n\t\treturn false;\n\t}\n\n\tconst selector = extraSelector ? `${TABLE_INTERACTIVE_ELEMENT_SELECTOR}, ${extraSelector}` : TABLE_INTERACTIVE_ELEMENT_SELECTOR;\n\n\treturn Boolean(target.closest(selector));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/table/columnLayout.ts","../../src/table/columnOrder.ts","../../src/table/columnSizing.ts","../../src/table/cellSelection.ts","../../src/table/odataAdapter.ts","../../src/table/selection.ts","../../src/table/tanStackFeatures.ts","../../src/table/useTableRowSelection.ts","../../src/table/utils.ts"],"sourcesContent":["import { appendMissingIds, filterAndDeduplicateIds } from \"../array\";\n\nexport type TableColumnLayoutItem<TColumnId extends string = string, TWidth = number> = {\n\tid: TColumnId;\n\twidth: TWidth;\n};\n\nexport type ResolveTableColumnOrderArgs<TColumnId extends string = string> = {\n\tids: readonly TColumnId[];\n\torder?: readonly string[];\n};\n\nexport type BuildTableColumnLayoutArgs<TColumnId extends string = string, TWidth = number> = {\n\tids: readonly TColumnId[];\n\tgetWidth: (id: TColumnId) => TWidth;\n};\n\n/**\n * Возвращает порядок колонок как в AnalyticalTable: сначала сохранённый order,\n * затем новые или отсутствующие в order id. Ширины при этом остаются привязаны\n * к columnId, а не к визуальной позиции.\n */\nexport function resolveTableColumnOrder<TColumnId extends string>({ ids, order }: ResolveTableColumnOrderArgs<TColumnId>): TColumnId[] {\n\treturn appendMissingIds(filterAndDeduplicateIds(order, ids), ids);\n}\n\n/**\n * Строит layout колонок из уже вычисленного порядка и функции ширины по id.\n */\nexport function buildTableColumnLayout<TColumnId extends string, TWidth>({\n\tids,\n\tgetWidth\n}: BuildTableColumnLayoutArgs<TColumnId, TWidth>): TableColumnLayoutItem<TColumnId, TWidth>[] {\n\treturn ids.map((id) => ({\n\t\tid,\n\t\twidth: getWidth(id)\n\t}));\n}\n","import { arraysEqual } from \"../array\";\n\nexport type ReorderTableHeaderColumnsArgs = {\n\torder: readonly string[];\n\theaderIds: readonly string[];\n\tlockedIds?: readonly string[];\n\tpinnedIds?: readonly string[];\n\tactiveId: string;\n\toverId: string;\n};\n\nexport type TableColumnOrderState = string[];\n\nexport function normalizeTableColumnOrder(order: readonly string[] | undefined): TableColumnOrderState {\n\treturn Array.from(new Set((order ?? []).filter((columnId) => columnId.trim().length > 0)));\n}\n\nfunction moveItem<T>(items: readonly T[], fromIndex: number, toIndex: number): T[] {\n\tconst nextItems = [...items];\n\tconst [item] = nextItems.splice(fromIndex, 1);\n\n\tif (item === undefined) {\n\t\treturn nextItems;\n\t}\n\n\tnextItems.splice(toIndex, 0, item);\n\treturn nextItems;\n}\n\n/**\n * Переставляет только верхнеуровневые колонки табличной шапки.\n *\n * Ограничения:\n * - дочерние колонки групп не являются самостоятельными draggable-элементами;\n * - locked-колонки остаются в фиксированном порядке;\n * - pinned-колонки не переносятся между закреплённой и обычной зонами.\n */\nexport function reorderTableHeaderColumns(args: ReorderTableHeaderColumnsArgs): string[] {\n\tconst { activeId, overId, order } = args;\n\tif (!activeId || !overId || activeId === overId) return [...order];\n\n\tconst headerSet = new Set(args.headerIds);\n\tif (!headerSet.has(activeId) || !headerSet.has(overId)) return [...order];\n\n\tconst lockedSet = new Set(args.lockedIds ?? []);\n\tif (lockedSet.has(activeId) || lockedSet.has(overId)) return [...order];\n\n\tconst pinnedSet = new Set(args.pinnedIds ?? []);\n\tif (pinnedSet.has(activeId) || pinnedSet.has(overId)) return [...order];\n\n\tconst nextOrder = [...order];\n\tif (!nextOrder.includes(activeId)) {\n\t\tnextOrder.push(activeId);\n\t}\n\tif (!nextOrder.includes(overId)) {\n\t\tnextOrder.push(overId);\n\t}\n\n\tconst fromIndex = nextOrder.indexOf(activeId);\n\tconst toIndex = nextOrder.indexOf(overId);\n\tif (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return nextOrder;\n\n\treturn moveItem(nextOrder, fromIndex, toIndex);\n}\n\nexport function resolveReorderedTableHeaderColumns(args: ReorderTableHeaderColumnsArgs): string[] | null {\n\tconst nextOrder = reorderTableHeaderColumns(args);\n\n\treturn arraysEqual(nextOrder, args.order) ? null : nextOrder;\n}\n","export type TableColumnSizingState = Record<string, number>;\n\nexport const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 60;\n\nexport function normalizeTableColumnWidth(px: number, minWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH): number {\n\tconst normalizedMinWidth = Number.isFinite(minWidth) && minWidth > 0 ? Math.floor(minWidth) : DEFAULT_TABLE_COLUMN_MIN_WIDTH;\n\tconst normalizedWidth = Number.isFinite(px) ? Math.floor(px) : normalizedMinWidth;\n\n\treturn Math.max(normalizedMinWidth, normalizedWidth);\n}\n\nexport function normalizeTableColumnSizing(\n\tstate: Readonly<Record<string, number>> | undefined,\n\tminWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH\n): TableColumnSizingState {\n\tconst nextState: TableColumnSizingState = {};\n\n\tfor (const [columnId, width] of Object.entries(state ?? {})) {\n\t\tif (typeof columnId !== \"string\" || columnId.trim().length === 0 || !Number.isFinite(width)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tnextState[columnId] = normalizeTableColumnWidth(width, minWidth);\n\t}\n\n\treturn nextState;\n}\n\nexport function patchTableColumnWidth(\n\tstate: Readonly<Record<string, number>>,\n\tcolumnId: string,\n\twidth: number,\n\tminWidth: number = DEFAULT_TABLE_COLUMN_MIN_WIDTH\n): TableColumnSizingState {\n\tif (columnId.trim().length === 0) {\n\t\treturn normalizeTableColumnSizing(state, minWidth);\n\t}\n\n\treturn {\n\t\t...normalizeTableColumnSizing(state, minWidth),\n\t\t[columnId]: normalizeTableColumnWidth(width, minWidth)\n\t};\n}\n\nexport function removeTableColumnWidth(state: Readonly<Record<string, number>>, columnId: string): TableColumnSizingState {\n\tconst nextState: TableColumnSizingState = {};\n\n\tfor (const [key, width] of Object.entries(state)) {\n\t\tif (key !== columnId) {\n\t\t\tnextState[key] = width;\n\t\t}\n\t}\n\n\treturn nextState;\n}\n","import {\n\ttype TableCellCoordinates,\n\ttype TableCellSelectionActivationMode,\n\ttype TableCellSelectionMode,\n\ttype TableCellSelectionState,\n\ttype TableSelectionModifierEvent\n} from \"./types\";\n\nconst TABLE_CELL_SELECTION_ACTIVATION_MODES: ReadonlySet<TableCellSelectionActivationMode> = new Set([\"direct\", \"primary-modifier\"]);\n\n/** Строит collision-safe identity координат для дедупликации и сравнения. */\nfunction buildTableCellKey(cell: TableCellCoordinates): string {\n\treturn `${encodeURIComponent(cell.rowId)}::${encodeURIComponent(cell.columnId)}`;\n}\n\n/** Проверяет равенство двух наборов координат без зависимости от порядка. */\nexport function areTableCellSelectionsEqual(left: TableCellSelectionState, right: TableCellSelectionState): boolean {\n\tconst leftKeys = new Set(left.map(buildTableCellKey));\n\tconst rightKeys = new Set(right.map(buildTableCellKey));\n\n\tif (leftKeys.size !== rightKeys.size) {\n\t\treturn false;\n\t}\n\n\treturn [...leftKeys].every((key) => rightKeys.has(key));\n}\n\n/**\n * Оставляет только доступные координаты и приводит их к порядку строк/колонок.\n *\n * Функция не мутирует входные массивы. В `single` последняя координата в\n * детерминированном порядке становится единственным выбранным значением.\n */\nexport function pruneTableCellSelection(\n\tselection: TableCellSelectionState | undefined,\n\tavailableRowIds: readonly string[],\n\tavailableColumnIds: readonly string[],\n\tselectionMode: TableCellSelectionMode\n): TableCellCoordinates[] {\n\tif (selectionMode === \"none\" || !selection?.length) {\n\t\treturn [];\n\t}\n\n\tconst rowOrder = new Map(availableRowIds.map((rowId, index) => [rowId, index]));\n\tconst columnOrder = new Map(availableColumnIds.map((columnId, index) => [columnId, index]));\n\tconst seen = new Set<string>();\n\tconst nextSelection = selection\n\t\t.filter((cell) => {\n\t\t\tif (!(rowOrder.has(cell.rowId) && columnOrder.has(cell.columnId))) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst key = buildTableCellKey(cell);\n\t\t\tif (seen.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tseen.add(key);\n\t\t\treturn true;\n\t\t})\n\t\t.sort((left, right) => {\n\t\t\tconst rowDelta = (rowOrder.get(left.rowId) ?? 0) - (rowOrder.get(right.rowId) ?? 0);\n\t\t\tif (rowDelta !== 0) {\n\t\t\t\treturn rowDelta;\n\t\t\t}\n\n\t\t\treturn (columnOrder.get(left.columnId) ?? 0) - (columnOrder.get(right.columnId) ?? 0);\n\t\t});\n\n\tif (selectionMode === \"single\") {\n\t\tconst lastCell = nextSelection.at(-1);\n\t\treturn lastCell ? [lastCell] : [];\n\t}\n\n\treturn nextSelection;\n}\n\n/**\n * Вычисляет следующее состояние после активации ячейки.\n *\n * Одиночный выбор допускает явное снятие повторной активацией, а `multi`\n * переключает только переданную координату.\n */\nexport function toggleTableCellSelection(\n\tselection: TableCellSelectionState,\n\tcell: TableCellCoordinates,\n\tselectionMode: TableCellSelectionMode\n): TableCellCoordinates[] {\n\tif (selectionMode === \"none\") {\n\t\treturn [];\n\t}\n\n\tconst cellKey = buildTableCellKey(cell);\n\tconst hasCell = selection.some((item) => buildTableCellKey(item) === cellKey);\n\n\tif (selectionMode === \"single\") {\n\t\treturn hasCell ? [] : [cell];\n\t}\n\n\treturn hasCell ? selection.filter((item) => buildTableCellKey(item) !== cellKey) : [...selection, cell];\n}\n\n/** Нормализует способ активации ячейки на внешней config-boundary. */\nexport function normalizeTableCellSelectionActivationMode(\n\tvalue: unknown,\n\tfallback: TableCellSelectionActivationMode = \"direct\"\n): TableCellSelectionActivationMode {\n\treturn typeof value === \"string\" && TABLE_CELL_SELECTION_ACTIVATION_MODES.has(value as TableCellSelectionActivationMode)\n\t\t? (value as TableCellSelectionActivationMode)\n\t\t: fallback;\n}\n\n/**\n * Проверяет, должно ли событие активировать выбор ячейки.\n *\n * Для `primary-modifier` принимается Ctrl или Command, но не комбинации с\n * Shift/Alt: они зарезервированы браузером, ОС и стандартными selection-flow.\n */\nexport function shouldActivateTableCellSelection(\n\tevent: TableSelectionModifierEvent,\n\tactivationMode: TableCellSelectionActivationMode\n): boolean {\n\tif (activationMode === \"direct\") {\n\t\treturn true;\n\t}\n\n\treturn (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey;\n}\n","import type { FormattersPipelineTypedValueContext } from \"../formatters/pipeline\";\nimport type { EntityColumnProperty } from \"../odata-service\";\nimport type { TableColumnAlign, TableColumnDef, TableColumnFormattingMetaInput } from \"./types\";\n\n/**\n * Опции генерации колонок по OData metadata.\n */\nexport interface CreateTableColumnsFromODataMetadataOptions {\n\t/**\n\t * Позволяет переопределить заголовок колонки.\n\t */\n\tresolveHeader?: (column: EntityColumnProperty) => string;\n\t/**\n\t * Позволяет сразу навесить formatting-конфиг на generated column.\n\t *\n\t * `id`, `role` и `type` будут автоматически получены из metadata.\n\t */\n\tresolveFormatting?: (column: EntityColumnProperty) => TableColumnFormattingMetaInput | undefined;\n}\n\n/**\n * Опции обогащения уже существующих колонок данными OData metadata.\n */\nexport interface EnrichTableColumnsWithODataFormattingOptions {\n\t/**\n\t * Позволяет подменить стратегию поиска metadata по колонке.\n\t */\n\tresolveMetadataColumn?: (args: {\n\t\tcolumnId: string;\n\t\tmetadataColumnsById: ReadonlyMap<string, EntityColumnProperty>;\n\t}) => EntityColumnProperty | undefined;\n}\n\n/**\n * Опции построения стартовой карты видимости колонок по OData metadata.\n */\nexport interface CreateTableColumnVisibilityFromODataMetadataOptions {\n\t/**\n\t * Позволяет явно переопределить стартовую видимость колонки.\n\t */\n\tresolveVisible?: (column: EntityColumnProperty) => boolean;\n}\n\n/**\n * Возвращает typed-formatting контекст по metadata OData-колонки.\n */\nexport function resolveTableColumnFormattingContextFromODataColumn(column: EntityColumnProperty): FormattersPipelineTypedValueContext {\n\tconst { role, type } = column;\n\tconst forceMeasure = type === \"decimal\" || type === \"float\" || type === \"double\" || type === \"int\" || type === \"byte\";\n\n\treturn {\n\t\trole: forceMeasure ? \"measure\" : role,\n\t\ttype: type\n\t};\n}\n\n/**\n * Создаёт базовое выравнивание по семантике OData-колонки.\n *\n * NOTE: \"не аналитические\" все поля возвращают как \"dimension\"...\n */\nfunction resolveTableColumnAlignFromODataColumn(columnContext: FormattersPipelineTypedValueContext): TableColumnAlign {\n\treturn columnContext.role === \"measure\" ? \"right\" : \"left\";\n}\n\n/**\n * Определяет видимость metadata-колонки по умолчанию.\n *\n * Кодовые поля скрываются только если у них есть явная text-пара,\n * чтобы экран по умолчанию показывал человекочитаемое значение.\n */\nfunction resolveMetadataColumnVisibleByDefault(\n\tcolumn: EntityColumnProperty,\n\tmetadataColumnsById: ReadonlyMap<string, EntityColumnProperty>\n): boolean {\n\tif (column.semanticType !== \"code\" || !column.linkedColumnId) {\n\t\treturn true;\n\t}\n\n\tconst linkedColumn = metadataColumnsById.get(column.linkedColumnId);\n\n\treturn linkedColumn?.semanticType !== \"text\";\n}\n\n/**\n * Определяет стабильный идентификатор колонки для сопоставления с metadata.\n */\nexport function resolveStableColumnId<TData extends object>(column: TableColumnDef<TData>): string | undefined {\n\tif (typeof column.id === \"string\" && column.id.trim()) {\n\t\treturn column.id;\n\t}\n\n\tif (\"accessorKey\" in column && typeof column.accessorKey === \"string\" && column.accessorKey.trim()) {\n\t\treturn column.accessorKey;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Рекурсивно обходит только leaf-колонки.\n */\nfunction mapLeafTableColumns<TData extends object>(\n\tcolumns: readonly TableColumnDef<TData>[],\n\tmapper: (column: TableColumnDef<TData>) => TableColumnDef<TData>\n): TableColumnDef<TData>[] {\n\treturn columns.map((column) => {\n\t\tif (\"columns\" in column && Array.isArray(column.columns) && column.columns.length > 0) {\n\t\t\treturn {\n\t\t\t\t...column,\n\t\t\t\tcolumns: mapLeafTableColumns(column.columns as TableColumnDef<TData>[], mapper)\n\t\t\t};\n\t\t}\n\n\t\treturn mapper(column);\n\t});\n}\n\n/**\n * Строит стартовую карту видимости колонок из metadata OData-сущности.\n *\n * Возвращает visibility-state для TanStack Table, не удаляя сами колонки\n * из результата build-режима.\n */\nexport function createTableColumnVisibilityFromODataMetadata(\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: CreateTableColumnVisibilityFromODataMetadataOptions\n): Record<string, boolean> {\n\tconst metadataColumnsById = new Map(metadataColumns.map((column) => [column.id, column]));\n\n\treturn Object.fromEntries(\n\t\tmetadataColumns.map((column) => {\n\t\t\tconst visible = options?.resolveVisible?.(column) ?? resolveMetadataColumnVisibleByDefault(column, metadataColumnsById);\n\n\t\t\treturn [column.id, visible];\n\t\t})\n\t);\n}\n\n/**\n * Создаёт базовые generic-колонки из metadata OData-сущности.\n */\nexport function createTableColumnsFromODataMetadata<TData extends object = Record<string, unknown>>(\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: CreateTableColumnsFromODataMetadataOptions\n): TableColumnDef<TData>[] {\n\treturn metadataColumns.map((metadataColumn) => {\n\t\t// NOTE: Ячейка сама определит выравнивание, `align` только для переопределения\n\t\tconst columnContext = resolveTableColumnFormattingContextFromODataColumn(metadataColumn);\n\t\tconst align = resolveTableColumnAlignFromODataColumn(columnContext);\n\t\tconst resolvedFormatting = options?.resolveFormatting?.(metadataColumn);\n\n\t\treturn {\n\t\t\tid: metadataColumn.id,\n\t\t\taccessorKey: metadataColumn.id,\n\t\t\theader: options?.resolveHeader?.(metadataColumn) ?? metadataColumn.label ?? metadataColumn.id,\n\t\t\tmeta: {\n\t\t\t\talign,\n\t\t\t\tformatting: {\n\t\t\t\t\t...resolvedFormatting,\n\t\t\t\t\tid: metadataColumn.id,\n\t\t\t\t\trole: columnContext.role,\n\t\t\t\t\ttype: columnContext.type\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}) as TableColumnDef<TData>[];\n}\n\n/**\n * Дозаполняет уже существующие колонки typed-formatting контекстом из OData metadata.\n */\nexport function enrichTableColumnsWithODataFormatting<TData extends object>(\n\tcolumns: readonly TableColumnDef<TData>[],\n\tmetadataColumns: readonly EntityColumnProperty[],\n\toptions?: EnrichTableColumnsWithODataFormattingOptions\n): TableColumnDef<TData>[] {\n\tconst metadataColumnsById = new Map(metadataColumns.map((column) => [column.id, column]));\n\n\treturn mapLeafTableColumns(columns, (column) => {\n\t\tconst columnId = resolveStableColumnId(column);\n\t\tif (!columnId) {\n\t\t\treturn column;\n\t\t}\n\n\t\tconst metadataColumn =\n\t\t\toptions?.resolveMetadataColumn?.({\n\t\t\t\tcolumnId,\n\t\t\t\tmetadataColumnsById\n\t\t\t}) ?? metadataColumnsById.get(columnId);\n\n\t\tif (!metadataColumn) {\n\t\t\treturn column;\n\t\t}\n\n\t\tif (column.meta?.formatting?.role && column.meta.formatting.type) {\n\t\t\treturn column;\n\t\t}\n\t\tconst columnContext = resolveTableColumnFormattingContextFromODataColumn(metadataColumn);\n\n\t\treturn {\n\t\t\t...column,\n\t\t\tmeta: {\n\t\t\t\t...column.meta,\n\t\t\t\tformatting: {\n\t\t\t\t\t...column.meta?.formatting,\n\t\t\t\t\tid: columnId,\n\t\t\t\t\trole: columnContext.role,\n\t\t\t\t\ttype: columnContext.type\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t});\n}\n","import { RowSelectionState } from \"@tanstack/react-table\";\n\nimport { TableSelectionMode } from \"./types\";\n\nconst TABLE_SELECTION_MODES: ReadonlySet<TableSelectionMode> = new Set([\"none\", \"single\", \"multi\"]);\n\n/**\n * Возвращает только активные идентификаторы выбранных строк.\n */\nfunction getSelectedRowIds(selection: RowSelectionState): string[] {\n\treturn Object.keys(selection).filter((rowId) => selection[rowId]);\n}\n\n/** Проверяет равенство активных row-selection идентификаторов без учёта порядка ключей. */\nexport function areTableRowSelectionsEqual(left: RowSelectionState, right: RowSelectionState): boolean {\n\tconst leftRowIds = getSelectedRowIds(left);\n\tconst rightRowIds = getSelectedRowIds(right);\n\n\tif (leftRowIds.length !== rightRowIds.length) {\n\t\treturn false;\n\t}\n\n\treturn leftRowIds.every((rowId) => right[rowId]);\n}\n\n/** Нормализует неизвестное значение режима выбора строк на внешней config-boundary. */\nexport function normalizeTableSelectionMode(value: unknown, fallback: TableSelectionMode = \"none\"): TableSelectionMode {\n\treturn typeof value === \"string\" && TABLE_SELECTION_MODES.has(value as TableSelectionMode) ? (value as TableSelectionMode) : fallback;\n}\n\n/**\n * Нормализует состояние выбора относительно списка доступных строк и текущего режима.\n */\nexport function pruneTableRowSelection(\n\tselection: RowSelectionState,\n\tavailableRowIds: readonly string[],\n\tselectionMode: TableSelectionMode\n): RowSelectionState {\n\tif (selectionMode === \"none\") {\n\t\treturn {};\n\t}\n\n\tconst availableRowIdSet = new Set(availableRowIds);\n\tconst selectedRowIds = getSelectedRowIds(selection).filter((rowId) => availableRowIdSet.has(rowId));\n\n\tif (selectionMode === \"single\") {\n\t\tconst lastSelectedRowId = selectedRowIds.at(-1);\n\n\t\treturn lastSelectedRowId ? { [lastSelectedRowId]: true } : {};\n\t}\n\n\treturn Object.fromEntries(selectedRowIds.map((rowId) => [rowId, true]));\n}\n\n/**\n * Вычисляет следующее состояние выбора после активации строки пользователем.\n *\n * Семантика:\n * - `none` не хранит выбор;\n * - `single` всегда оставляет только одну строку и не снимает выбор повторным кликом;\n * - `multi` переключает строку как toggle.\n */\nexport function toggleTableRowSelection(selection: RowSelectionState, rowId: string, selectionMode: TableSelectionMode): RowSelectionState {\n\tif (selectionMode === \"none\") {\n\t\treturn {};\n\t}\n\n\tif (selectionMode === \"single\") {\n\t\tconst selectedRowIds = getSelectedRowIds(selection);\n\t\treturn selectedRowIds.length === 1 && selectedRowIds[0] === rowId ? selection : { [rowId]: true };\n\t}\n\n\tif (selection[rowId]) {\n\t\tconst nextSelection = { ...selection };\n\n\t\tdelete nextSelection[rowId];\n\n\t\treturn nextSelection;\n\t}\n\n\treturn {\n\t\t...selection,\n\t\t[rowId]: true\n\t};\n}\n","import {\n\tcolumnOrderingFeature,\n\tcolumnPinningFeature,\n\tcolumnResizingFeature,\n\tcolumnSizingFeature,\n\tcolumnVisibilityFeature,\n\tcreateExpandedRowModel,\n\trowExpandingFeature,\n\trowSelectionFeature,\n\ttableFeatures\n} from \"@tanstack/react-table\";\n\n/**\n * Единый набор TanStack Table v9 features для foundation-таблиц.\n *\n * Контракт хранится в `lib`, чтобы определения колонок, строк и экземпляров\n * таблицы в `lib` и `ui` всегда использовали один и тот же набор generic-типов.\n */\nexport const foundationTableFeatures = tableFeatures({\n\tcolumnOrderingFeature,\n\tcolumnPinningFeature,\n\tcolumnSizingFeature,\n\tcolumnResizingFeature,\n\tcolumnVisibilityFeature,\n\trowSelectionFeature,\n\trowExpandingFeature,\n\texpandedRowModel: createExpandedRowModel()\n});\n\n/** Generic-контракт features для публичных типов foundation-таблиц. */\nexport type FoundationTableFeatures = typeof foundationTableFeatures;\n","import { useCallback, useEffect, useEffectEvent, useRef, useState } from \"react\";\n\nimport { RowSelectionState } from \"@tanstack/react-table\";\n\nimport { pruneTableRowSelection, toggleTableRowSelection } from \"./selection\";\nimport { TableSelectionMode } from \"./types\";\n\ninterface UseTableRowSelectionParams<TData extends object> {\n\tavailableRowIds: readonly string[];\n\trowById: Map<string, TData>;\n\tselectionMode: TableSelectionMode;\n\tselectedRowIds?: readonly string[];\n\tonRowSelectionChange?: (rows: TData[]) => void;\n}\n\n/**\n * Общая модель выбора строк для плоской и древовидной таблиц.\n */\nexport function useTableRowSelection<TData extends object>({\n\tavailableRowIds,\n\trowById,\n\tselectionMode,\n\tselectedRowIds,\n\tonRowSelectionChange\n}: UseTableRowSelectionParams<TData>) {\n\tconst [rowSelection, setRowSelection] = useState<RowSelectionState>({});\n\tconst shouldSkipNextSelectionEmitRef = useRef(false);\n\n\t/**\n\t * Сравнивает состояния выбора без лишних перерисовок.\n\t */\n\tconst areSelectionsEqual = useCallback((left: RowSelectionState, right: RowSelectionState) => {\n\t\tconst leftRowIds = Object.keys(left).filter((rowId) => left[rowId]);\n\t\tconst rightRowIds = Object.keys(right).filter((rowId) => right[rowId]);\n\n\t\tif (leftRowIds.length !== rightRowIds.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn leftRowIds.every((rowId) => right[rowId]);\n\n\t\t/*\n\n Более оптимизированная версия, но для текущего случая может быть практически незначительной\n\n let leftCount = 0;\n\t\tlet rightCount = 0;\n\n\t\tfor (const rowId of Object.keys(left)) {\n\t\t\tif (!left[rowId]) continue;\n\t\t\tleftCount += 1;\n\t\t\tif (!right[rowId]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfor (const rowId of Object.keys(right)) {\n\t\t\tif (right[rowId]) {\n\t\t\t\trightCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tif (leftCount !== rightCount) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\n */\n\t}, []);\n\n\tconst syncRowSelectionState = useEffectEvent((nextAvailableRowIds: readonly string[]) => {\n\t\tsetRowSelection((currentSelection) => pruneTableRowSelection(currentSelection, nextAvailableRowIds, selectionMode));\n\t});\n\n\tconst syncControlledRowSelectionState = useEffectEvent(\n\t\t(nextSelectedRowIds: readonly string[], nextAvailableRowIds: readonly string[]) => {\n\t\t\tconst nextSelection = pruneTableRowSelection(\n\t\t\t\tObject.fromEntries(nextSelectedRowIds.map((rowId) => [rowId, true])),\n\t\t\t\tnextAvailableRowIds,\n\t\t\t\tselectionMode\n\t\t\t);\n\n\t\t\tsetRowSelection((currentSelection) => {\n\t\t\t\tif (areSelectionsEqual(currentSelection, nextSelection)) {\n\t\t\t\t\treturn currentSelection;\n\t\t\t\t}\n\n\t\t\t\t// Пропускаем следующий emit, потому что это синхронизация от внешнего controlled-состояния,\n\t\t\t\t// а не пользовательское изменение выбора внутри таблицы.\n\t\t\t\tshouldSkipNextSelectionEmitRef.current = true;\n\n\t\t\t\treturn nextSelection;\n\t\t\t});\n\t\t}\n\t);\n\n\tconst emitRowSelectionChange = useEffectEvent((currentSelection: RowSelectionState) => {\n\t\tif (!onRowSelectionChange) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst selectedRows: TData[] = [];\n\n\t\tfor (const rowId of Object.keys(currentSelection)) {\n\t\t\tif (!currentSelection[rowId]) continue;\n\n\t\t\tconst row = rowById.get(rowId);\n\t\t\tif (row) {\n\t\t\t\tselectedRows.push(row);\n\t\t\t}\n\t\t}\n\n\t\tonRowSelectionChange(selectedRows);\n\t});\n\n\tuseEffect(() => {\n\t\tif (selectedRowIds !== undefined) {\n\t\t\tsyncControlledRowSelectionState(selectedRowIds, availableRowIds);\n\t\t\treturn;\n\t\t}\n\n\t\tsyncRowSelectionState(availableRowIds);\n\t}, [availableRowIds, selectedRowIds, selectionMode]);\n\n\tuseEffect(() => {\n\t\tif (shouldSkipNextSelectionEmitRef.current) {\n\t\t\tshouldSkipNextSelectionEmitRef.current = false;\n\t\t\treturn;\n\t\t}\n\n\t\temitRowSelectionChange(rowSelection);\n\t}, [rowSelection]);\n\n\t/**\n\t * Применяет изменение выбора при активации конкретной строки.\n\t */\n\tconst activateRowSelection = useCallback(\n\t\t(rowId: string, canSelect: boolean) => {\n\t\t\tif (selectionMode === \"none\" || !canSelect) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetRowSelection((currentSelection) => toggleTableRowSelection(currentSelection, rowId, selectionMode));\n\t\t},\n\t\t[selectionMode]\n\t);\n\n\treturn {\n\t\trowSelection,\n\t\tsetRowSelection,\n\t\tactivateRowSelection\n\t};\n}\n","import { TableColumnDef, TableColumnFormattingMeta, TableColumnMeta } from \"./types\";\n\n/**\n * Интерактивные descendants, действие которых таблица не должна подменять\n * выбором строки или ячейки.\n *\n * `td`, `th` и `tr` намеренно исключены из tabindex-ветки: сама таблица может\n * использовать roving tabindex для selection, не превращая ячейку в nested control.\n */\nconst TABLE_INTERACTIVE_ELEMENT_SELECTOR = [\n\t\"button\",\n\t\"a\",\n\t\"input\",\n\t\"select\",\n\t\"textarea\",\n\t\"label\",\n\t\"summary\",\n\t\"audio[controls]\",\n\t\"video[controls]\",\n\t'[contenteditable]:not([contenteditable=\"false\"])',\n\t\"[tabindex]:not(td):not(th):not(tr)\",\n\t'[draggable=\"true\"]',\n\t'[role=\"button\"]',\n\t'[role=\"link\"]',\n\t'[role=\"checkbox\"]',\n\t'[role=\"radio\"]',\n\t'[role=\"switch\"]',\n\t'[role=\"menu\"]',\n\t'[role=\"menuitem\"]',\n\t'[role=\"menuitemcheckbox\"]',\n\t'[role=\"menuitemradio\"]',\n\t'[role=\"listbox\"]',\n\t'[role=\"option\"]',\n\t'[role=\"tab\"]',\n\t'[role=\"tablist\"]',\n\t'[role=\"tree\"]',\n\t'[role=\"treeitem\"]',\n\t'[role=\"slider\"]',\n\t'[role=\"spinbutton\"]',\n\t'[role=\"textbox\"]',\n\t'[role=\"searchbox\"]',\n\t'[role=\"combobox\"]'\n].join(\", \");\n\n/**\n * Возвращает метаданные колонки таблицы.\n */\nexport function getTableColumnMeta<TData extends object>(column: TableColumnDef<TData>): TableColumnMeta | undefined {\n\treturn column.meta;\n}\n\n/**\n * Возвращает форматирующий блок метаданных колонки.\n */\nexport function getTableColumnFormattingMeta<TData extends object>(column: TableColumnDef<TData>): TableColumnFormattingMeta | undefined {\n\treturn column.meta?.formatting;\n}\n\n/**\n * Нормализует размер таблицы.\n *\n * Числовое значение трактуется как `em`, чтобы разметка масштабировалась вместе со шрифтом.\n */\nexport function resolveTableLength(length: string | number): string {\n\tif (typeof length === \"number\") {\n\t\treturn `${length}em`;\n\t}\n\n\treturn length;\n}\n\n/**\n * Определяет, пришёл ли клик из интерактивного элемента внутри таблицы.\n * Учитывает только элементы внутри ближайшей строки: фокусируемая вкладка или\n * диалог вокруг таблицы не должны перехватывать выбор её строк.\n */\nexport function isTableInteractiveElement(target: EventTarget | null, extraSelector?: string): boolean {\n\tif (typeof Element === \"undefined\" || !(target instanceof Element)) {\n\t\treturn false;\n\t}\n\n\tconst selector = extraSelector ? `${TABLE_INTERACTIVE_ELEMENT_SELECTOR}, ${extraSelector}` : TABLE_INTERACTIVE_ELEMENT_SELECTOR;\n\tconst interactiveElement = target.closest(selector);\n\tif (!interactiveElement) return false;\n\n\tconst row = target.closest(\"tr\");\n\treturn !row || row.contains(interactiveElement);\n}\n"],"mappings":"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"}
|
package/dist/table/utils.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare function getTableColumnFormattingMeta<TData extends object>(colum
|
|
|
15
15
|
export declare function resolveTableLength(length: string | number): string;
|
|
16
16
|
/**
|
|
17
17
|
* Определяет, пришёл ли клик из интерактивного элемента внутри таблицы.
|
|
18
|
+
* Учитывает только элементы внутри ближайшей строки: фокусируемая вкладка или
|
|
19
|
+
* диалог вокруг таблицы не должны перехватывать выбор её строк.
|
|
18
20
|
*/
|
|
19
21
|
export declare function isTableInteractiveElement(target: EventTarget | null, extraSelector?: string): boolean;
|
|
20
22
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/table/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA4CrF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,eAAe,GAAG,SAAS,CAEnH;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,yBAAyB,GAAG,SAAS,CAEvI;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAMlE;AAED
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/table/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA4CrF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,eAAe,GAAG,SAAS,CAEnH;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,yBAAyB,GAAG,SAAS,CAEvI;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAMlE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAWrG"}
|
package/package.json
CHANGED
package/src/decimal/README.md
CHANGED
|
@@ -12,14 +12,15 @@ addDecimals("0.1", "0.2"); // "0.3"
|
|
|
12
12
|
compareDecimalRatios({ numerator: "10", denominator: "3" }, { numerator: "3.33", denominator: "1" }); // 1
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
| API | Назначение
|
|
16
|
-
| ----------------------------------------------------- |
|
|
17
|
-
| `isCanonicalDecimal(unknown, options?)` | Проверить внешнее значение без преобразования
|
|
18
|
-
| `normalizeDecimal(string, options?)` | Убрать начальные и конечные нули; привести отрицательный ноль к `"0"`
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
15
|
+
| API | Назначение |
|
|
16
|
+
| ----------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
17
|
+
| `isCanonicalDecimal(unknown, options?)` | Проверить внешнее значение без преобразования |
|
|
18
|
+
| `normalizeDecimal(string, options?)` | Убрать начальные и конечные нули; привести отрицательный ноль к `"0"` |
|
|
19
|
+
| `normalizeDecimalLiteral(string, options?)` | Точно раскрыть научную запись внешнего числа без преобразования значения в number |
|
|
20
|
+
| `addDecimals`, `subtractDecimals`, `multiplyDecimals` | Получить точный канонический результат двух операндов |
|
|
21
|
+
| `sumDecimals(iterable, options?)` | Суммировать за один проход; пустой набор даёт `"0"` |
|
|
22
|
+
| `compareDecimals` | Вернуть `-1`, `0` или `1` |
|
|
23
|
+
| `compareDecimalRatios` | Сравнить отношения без деления; знаменатели строго положительные |
|
|
23
24
|
|
|
24
25
|
Каноническая строка содержит знак минус при необходимости, цифры и точку:
|
|
25
26
|
`"0"`, `"0.3"`, `"-12.05"`. Пробелы, плюс, экспонента, запятая, начальные
|
|
@@ -27,6 +28,13 @@ compareDecimalRatios({ numerator: "10", denominator: "3" }, { numerator: "3.33",
|
|
|
27
28
|
допускает только лишние нули; обработка локализованного ввода и внешних JSON
|
|
28
29
|
чисел принадлежит соответствующей входной границе.
|
|
29
30
|
|
|
31
|
+
Для исходной лексемы JSON-числа входная граница использует
|
|
32
|
+
`normalizeDecimalLiteral("1e-1")`, получая `"0.1"`. Parser должен сохранить
|
|
33
|
+
исходные цифры: передавать `String` уже прочитанного `number` слишком поздно.
|
|
34
|
+
Научная запись разворачивается только в пределах `maxLength`; значение
|
|
35
|
+
`"1e1000000000"` отклоняется до выделения большого буфера. Прежние API
|
|
36
|
+
арифметики и `normalizeDecimal` по-прежнему не принимают экспоненту.
|
|
37
|
+
|
|
30
38
|
`DecimalOptions.maxLength` задаёт положительный безопасный целый предел длины
|
|
31
39
|
входа и результата, включая знак и точку; по умолчанию — 512 символов.
|
|
32
40
|
Вход отклоняется до создания `bigint` и выравнивания разрядов. Неверная форма
|