@ryuzaki13/react-foundation-lib 2.20.0 → 2.21.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/README.md +2 -0
- package/dist/analytical-table/aggregation.d.ts +4 -0
- package/dist/analytical-table/aggregation.d.ts.map +1 -0
- package/dist/analytical-table/buildAnalyticalTableModel.d.ts +4 -0
- package/dist/analytical-table/buildAnalyticalTableModel.d.ts.map +1 -0
- package/dist/analytical-table/calculationTypes.d.ts +11 -0
- package/dist/analytical-table/calculationTypes.d.ts.map +1 -0
- package/dist/analytical-table/calculations.d.ts +6 -0
- package/dist/analytical-table/calculations.d.ts.map +1 -0
- package/dist/analytical-table/columnTypes.d.ts +30 -0
- package/dist/analytical-table/columnTypes.d.ts.map +1 -0
- package/dist/analytical-table/dataTypes.d.ts +14 -0
- package/dist/analytical-table/dataTypes.d.ts.map +1 -0
- package/dist/analytical-table/dateColumnTypes.d.ts +31 -0
- package/dist/analytical-table/dateColumnTypes.d.ts.map +1 -0
- package/dist/analytical-table/dateColumns.d.ts +11 -0
- package/dist/analytical-table/dateColumns.d.ts.map +1 -0
- package/dist/analytical-table/expansion.d.ts +4 -0
- package/dist/analytical-table/expansion.d.ts.map +1 -0
- package/dist/analytical-table/exportProjection.d.ts +4 -0
- package/dist/analytical-table/exportProjection.d.ts.map +1 -0
- package/dist/analytical-table/exportTypes.d.ts +31 -0
- package/dist/analytical-table/exportTypes.d.ts.map +1 -0
- package/dist/analytical-table/filtering.d.ts +4 -0
- package/dist/analytical-table/filtering.d.ts.map +1 -0
- package/dist/analytical-table/index.d.ts +8 -0
- package/dist/analytical-table/index.d.ts.map +1 -0
- package/dist/analytical-table/index.js +364 -0
- package/dist/analytical-table/index.js.map +1 -0
- package/dist/analytical-table/indexAnalyticalRows.d.ts +4 -0
- package/dist/analytical-table/indexAnalyticalRows.d.ts.map +1 -0
- package/dist/analytical-table/modelTypes.d.ts +43 -0
- package/dist/analytical-table/modelTypes.d.ts.map +1 -0
- package/dist/analytical-table/types.d.ts +8 -0
- package/dist/analytical-table/types.d.ts.map +1 -0
- package/dist/analytical-table/valueComparison.d.ts +5 -0
- package/dist/analytical-table/valueComparison.d.ts.map +1 -0
- package/dist/analytical-table/viewTypes.d.ts +24 -0
- package/dist/analytical-table/viewTypes.d.ts.map +1 -0
- package/dist/chunks/{formatters-hHUJpZqD.js → formatters-CEltML3y.js} +224 -224
- package/dist/chunks/{formatters-hHUJpZqD.js.map → formatters-CEltML3y.js.map} +1 -1
- package/dist/chunks/{validate-DnkaY38p.js → validate-M2zPiLXn.js} +24 -24
- package/dist/chunks/{validate-DnkaY38p.js.map → validate-M2zPiLXn.js.map} +1 -1
- package/dist/error-report/index.js +20 -20
- package/dist/excel/index.js +36 -36
- package/dist/excel/index.js.map +1 -1
- package/dist/formatters/index.js +2 -2
- package/dist/hooks/useFloatingListbox.d.ts +1 -1
- package/dist/odata-service/formatters.d.ts +2 -2
- package/package.json +5 -1
- package/src/analytical-table/README.mdx +169 -0
package/dist/excel/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/excel/excel.ts","../../src/excel/matrixWorkbook.ts","../../src/excel/tableExport.ts"],"sourcesContent":["import writeXlsxFile, { type Cell, type CellObject, type Feature, type Sheet, type SheetData } from \"write-excel-file/browser\";\n\nimport { parseDate } from \"../formatters/date\";\nimport { type State } from \"../types\";\n\nexport type ExcelColumn = {\n\tid: string;\n\theader: string;\n\twidth?: number;\n\ttype?: StringConstructor | NumberConstructor | DateConstructor | BooleanConstructor;\n\tformat?: string;\n};\n\nexport type ExcelRow = Record<string, unknown>;\n\nexport type ExcelCellStyle = Omit<CellObject, \"value\" | \"type\" | \"format\">;\n\nexport type DownloadExcelFileArgs = {\n\tfileName: string;\n\tcolumns: ExcelColumn[];\n\trows: ExcelRow[];\n\tautoFilter?: boolean;\n\tgetCellStyle?: (args: { row: ExcelRow; column: ExcelColumn; rowIndex: number; columnIndex: number }) => ExcelCellStyle | undefined;\n};\n\n/** Описание одного табличного листа внутри Excel-книги. */\nexport type ExcelWorkbookSheet = Omit<DownloadExcelFileArgs, \"fileName\"> & {\n\t/** Желаемое имя листа; writer нормализует ограничения формата и конфликты имён. */\n\tname: string;\n};\n\n/** Аргументы формирования Excel-книги с одним или несколькими табличными листами. */\nexport type DownloadExcelWorkbookArgs = {\n\t/** Имя скачиваемого `.xlsx` файла. */\n\tfileName: string;\n\t/** Листы книги в порядке отображения в Excel. */\n\tsheets: ExcelWorkbookSheet[];\n};\n\ntype BrowserExcelFileContent = File | Blob | ArrayBuffer;\n\nconst VALUE_STATE_TEXT_COLORS: Partial<Record<State, string>> = {\n\tinformation: \"#2563EB\",\n\tsuccess: \"#107C10\",\n\twarning: \"#B7791F\",\n\terror: \"#C50F1F\"\n};\n\nconst HEADER_CELL_STYLE = {\n\tfontWeight: \"bold\",\n\tbottomBorderStyle: \"thin\",\n\tbottomBorderColor: \"#D9D9D9\"\n} satisfies ExcelCellStyle;\n\nconst EXCEL_SHEET_NAME_MAX_LENGTH = 31;\nconst EXCEL_SHEET_NAME_INVALID_CHARACTERS = /[\\u0000-\\u001f[\\]/\\\\:*?]+/g;\nconst EXCEL_SHEET_NAME_EDGE_APOSTROPHES = /^'+|'+$/g;\nconst EXCEL_SHEET_NAME_WHITESPACE = /\\s+/g;\n\n/**\n * Приводит общий value-state проекта к базовому Excel-стилю.\n *\n * Helper живет в shared Excel-слое, чтобы analytical/detail экспорт не\n * дублировали палитру статусов и одинаково подсвечивали warning/error/success.\n */\nexport function resolveExcelCellStyleFromState(state: State | undefined): ExcelCellStyle | undefined {\n\tconst textColor = state ? VALUE_STATE_TEXT_COLORS[state] : undefined;\n\treturn textColor ? { textColor } : undefined;\n}\n\n/**\n * Переводит zero-based индекс колонки в Excel-адрес (`0 -> A`, `27 -> AB`).\n * Нужен для XML-диапазонов, потому что `write-excel-file` не предоставляет\n * отдельного API для построения ссылок вида `A1:C10`.\n */\nfunction numberToColumnName(index: number): string {\n\tlet value = index + 1;\n\tlet out = \"\";\n\n\twhile (value > 0) {\n\t\tconst remainder = (value - 1) % 26;\n\t\tout = String.fromCharCode(65 + remainder) + out;\n\t\tvalue = Math.floor((value - 1) / 26);\n\t}\n\n\treturn out;\n}\n\n/**\n * Строит диапазон автофильтра для всей выгруженной таблицы.\n *\n * Возвращает `undefined` для пустой сетки, чтобы не вставлять в xlsx\n * невалидный `<autoFilter>` без реального диапазона.\n */\nexport function buildExcelAutoFilterRef(columnCount: number, rowCount: number): string | undefined {\n\tif (columnCount <= 0 || rowCount <= 0) return undefined;\n\n\treturn `A1:${numberToColumnName(columnCount - 1)}${rowCount}`;\n}\n\n/**\n * Добавляет autoFilter через feature-transform `write-excel-file`.\n *\n * Библиотека умеет трансформировать XML листа, но не имеет высокоуровневого\n * параметра `autoFilter`, поэтому здесь изолирован низкоуровневый OpenXML-хук.\n */\nfunction createAutoFilterFeature<FileContent>(refs: readonly (string | undefined)[]): Feature<FileContent> | undefined {\n\tif (!refs.some(Boolean)) return undefined;\n\n\treturn {\n\t\tfiles: {\n\t\t\ttransform: {\n\t\t\t\t\"xl/worksheets/sheet{id}.xml\": {\n\t\t\t\t\ttransform: (content, _options, properties) => {\n\t\t\t\t\t\tconst ref = refs[properties.sheetIndex];\n\t\t\t\t\t\treturn ref ? content.replace(\"</sheetData>\", `</sheetData><autoFilter ref=\"${ref}\"/>`) : content;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Нормализует имена листов по ограничениям Excel и делает их уникальными.\n *\n * Runtime-заголовки могут содержать запрещённые символы, превышать 31 знак или\n * совпадать между несколькими представлениями одной конфигурации. Нормализация\n * выполняется централизованно, чтобы все consumers получали открываемую книгу.\n */\nexport function resolveExcelSheetNames(names: readonly string[]): string[] {\n\tconst usedNames = new Set<string>();\n\tconst resolvedNames: string[] = [];\n\n\tfor (let index = 0; index < names.length; index += 1) {\n\t\tconst fallbackName = `Лист ${index + 1}`;\n\t\tconst normalizedName = names[index]\n\t\t\t?.replace(EXCEL_SHEET_NAME_INVALID_CHARACTERS, \" \")\n\t\t\t.replace(EXCEL_SHEET_NAME_WHITESPACE, \" \")\n\t\t\t.trim()\n\t\t\t.replace(EXCEL_SHEET_NAME_EDGE_APOSTROPHES, \"\")\n\t\t\t.trim();\n\t\tconst baseName = normalizedName || fallbackName;\n\t\tlet resolvedName = baseName.slice(0, EXCEL_SHEET_NAME_MAX_LENGTH);\n\t\tlet duplicateIndex = 2;\n\n\t\twhile (usedNames.has(resolvedName.toLocaleLowerCase())) {\n\t\t\tconst suffix = ` (${duplicateIndex})`;\n\t\t\tresolvedName = `${baseName.slice(0, EXCEL_SHEET_NAME_MAX_LENGTH - suffix.length)}${suffix}`;\n\t\t\tduplicateIndex += 1;\n\t\t}\n\n\t\tusedNames.add(resolvedName.toLocaleLowerCase());\n\t\tresolvedNames.push(resolvedName);\n\t}\n\n\treturn resolvedNames;\n}\n\n/**\n * Приводит входное значение к типу Excel-ячейки.\n *\n * Важный смысл: если колонка числовая, в файл должен попасть настоящий Number,\n * а не уже отформатированная строка. Тогда Excel сможет применять `format`.\n */\nfunction normalizeCellValue(value: unknown, type: ExcelColumn[\"type\"]) {\n\tif (value == null || value === \"\") return undefined;\n\n\tif (type === Number) {\n\t\tconst numberValue = typeof value === \"number\" ? value : Number(value);\n\t\treturn Number.isFinite(numberValue) ? numberValue : undefined;\n\t}\n\n\tif (type === String) return String(value);\n\tif (type === Boolean) return Boolean(value);\n\tif (type === Date) return value instanceof Date ? value : (parseDate(value) ?? undefined);\n\n\treturn value as CellObject[\"value\"];\n}\n\n/**\n * Собирает SheetData для `write-excel-file`.\n *\n * Функция намеренно не знает о бизнес-сущностях: ей передают уже готовые\n * колонки/строки и необязательный callback стилей конкретных ячеек.\n */\nexport function buildExcelSheetData(args: Omit<DownloadExcelFileArgs, \"fileName\" | \"autoFilter\">): SheetData {\n\tconst headerRow: Cell[] = args.columns.map((column) => ({\n\t\ttype: String,\n\t\tvalue: column.header,\n\t\t...HEADER_CELL_STYLE\n\t}));\n\n\tconst sheetRows: SheetData = [headerRow];\n\n\tfor (let rowIndex = 0; rowIndex < args.rows.length; rowIndex++) {\n\t\tconst row = args.rows[rowIndex];\n\t\tconst sheetRow: Cell[] = [];\n\n\t\tfor (let columnIndex = 0; columnIndex < args.columns.length; columnIndex++) {\n\t\t\tconst column = args.columns[columnIndex];\n\t\t\tconst value = normalizeCellValue(row[column.id], column.type);\n\t\t\tconst style = args.getCellStyle?.({ row, column, rowIndex, columnIndex });\n\n\t\t\tsheetRow.push({\n\t\t\t\tvalue,\n\t\t\t\ttype: column.type,\n\t\t\t\tformat: column.format,\n\t\t\t\t...style\n\t\t\t});\n\t\t}\n\n\t\tsheetRows.push(sheetRow);\n\t}\n\n\treturn sheetRows;\n}\n\n/**\n * Формирует и скачивает Excel-книгу с независимыми табличными листами.\n *\n * Каждый лист получает собственные ширины колонок, стили ячеек и диапазон\n * автофильтра. Доменная подготовка колонок и строк остаётся у вызывающей стороны.\n */\nexport async function downloadExcelWorkbook(args: DownloadExcelWorkbookArgs): Promise<void> {\n\tif (args.sheets.length === 0) {\n\t\tthrow new Error(\"Для формирования Excel-книги нужен минимум один лист\");\n\t}\n\n\tconst sheetNames = resolveExcelSheetNames(args.sheets.map((sheet) => sheet.name));\n\tconst autoFilterRefs: Array<string | undefined> = [];\n\tconst sheets: Sheet<BrowserExcelFileContent>[] = args.sheets.map((sheet, index) => {\n\t\tconst data = buildExcelSheetData(sheet);\n\t\tautoFilterRefs.push(sheet.autoFilter ? buildExcelAutoFilterRef(sheet.columns.length, data.length) : undefined);\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tsheet: sheetNames[index],\n\t\t\tcolumns: sheet.columns.map((column) => ({\n\t\t\t\twidth: column.width\n\t\t\t}))\n\t\t};\n\t});\n\tconst autoFilterFeature = createAutoFilterFeature<BrowserExcelFileContent>(autoFilterRefs);\n\n\tawait writeXlsxFile(sheets, {\n\t\tfeatures: autoFilterFeature ? [autoFilterFeature] : undefined\n\t}).toFile(args.fileName);\n}\n\n/**\n * Формирует и скачивает `.xlsx` в браузере.\n *\n * Это публичная generic-точка shared-слоя: она отвечает только за Excel-файл,\n * а подготовка доменных данных остаётся на вызывающей стороне.\n */\nexport async function downloadExcelFile(args: DownloadExcelFileArgs): Promise<void> {\n\tawait downloadExcelWorkbook({\n\t\tfileName: args.fileName,\n\t\tsheets: [\n\t\t\t{\n\t\t\t\tname: \"Sheet1\",\n\t\t\t\tcolumns: args.columns,\n\t\t\t\trows: args.rows,\n\t\t\t\tautoFilter: args.autoFilter,\n\t\t\t\tgetCellStyle: args.getCellStyle\n\t\t\t}\n\t\t]\n\t});\n}\n","import writeXlsxFile, { type Cell, type Feature, type Sheet } from \"write-excel-file/browser\";\n\nimport { downloadFileFromBlob } from \"../dom\";\nimport { escapeXmlValue } from \"../xml\";\n\nimport { resolveExcelSheetNames } from \"./excel\";\n\ntype BrowserExcelFileContent = File | Blob | ArrayBuffer;\n\n/** Одна строка произвольной Excel-матрицы. Объединения задаются стилями первой ячейки диапазона. */\nexport type ExcelMatrixRow = readonly Cell[];\n\n/** Поля задаются в дюймах, как в OpenXML и диалоге параметров страницы Excel. */\nexport type ExcelPrintMargins = {\n\treadonly left: number;\n\treadonly right: number;\n\treadonly top: number;\n\treadonly bottom: number;\n\treadonly header: number;\n\treadonly footer: number;\n};\n\n/** Видимый колонтитул не принимает raw Excel-коды, чтобы текст не мог случайно изменить разметку секций. */\nexport type ExcelPrintHeaderFooter = {\n\treadonly left?: string;\n\treadonly center?: string;\n\treadonly right?: string;\n\treadonly includePageNumber?: boolean;\n};\n\n/** One-based прямоугольник печати соответствует координатам строк и столбцов Excel. */\nexport type ExcelPrintArea = {\n\treadonly firstRow: number;\n\treadonly lastRow: number;\n\treadonly firstColumn: number;\n\treadonly lastColumn: number;\n};\n\n/**\n * Печатный контракт одного листа.\n *\n * `columnBreaksAfter` и `rowBreaksAfter` используют one-based координату\n * последней ячейки страницы. Явная модель не позволяет consumer перепутать\n * индекс массива с адресом Excel и разрезать составной блок на печати.\n */\nexport type ExcelPrintSetup = {\n\treadonly orientation: \"portrait\" | \"landscape\";\n\treadonly paperSize?: \"a4\" | \"a3\";\n\treadonly fitToWidthPages?: number;\n\treadonly fitToHeightPages?: number;\n\treadonly pageOrder?: \"downThenOver\" | \"overThenDown\";\n\treadonly margins?: ExcelPrintMargins;\n\treadonly repeatRows?: readonly [firstRow: number, lastRow: number];\n\treadonly repeatColumns?: readonly [firstColumn: number, lastColumn: number];\n\treadonly printArea?: ExcelPrintArea;\n\treadonly columnBreaksAfter?: readonly number[];\n\treadonly rowBreaksAfter?: readonly number[];\n\treadonly header?: ExcelPrintHeaderFooter;\n\treadonly footer?: ExcelPrintHeaderFooter;\n};\n\n/** Произвольный лист сохраняет готовую матрицу и не навязывает табличную строку заголовков. */\nexport type ExcelMatrixWorkbookSheet = {\n\treadonly name: string;\n\treadonly rows: readonly ExcelMatrixRow[];\n\treadonly columns?: readonly { readonly width?: number }[];\n\treadonly showGridLines?: boolean;\n\treadonly zoomScale?: number;\n\treadonly print?: ExcelPrintSetup;\n};\n\n/**\n * Базовый шрифт стиля Normal задаёт метрику символьной ширины столбцов Excel.\n *\n * Шрифт отдельных ячеек может отличаться: эта настройка нужна прежде всего\n * для воспроизводимого преобразования `columns[].width` в экранные пиксели.\n */\nexport type ExcelMatrixWorkbookDefaultFont = {\n\treadonly fontFamily: string;\n\treadonly fontSize: number;\n};\n\n/** Аргументы generic-книги, пригодной как для скачивания, так и для file validation. */\nexport type ExcelMatrixWorkbookArgs = {\n\treadonly defaultFont?: ExcelMatrixWorkbookDefaultFont;\n\treadonly sheets: readonly ExcelMatrixWorkbookSheet[];\n};\n\nconst DEFAULT_PRINT_MARGINS = {\n\tleft: 0.2,\n\tright: 0.2,\n\ttop: 0.25,\n\tbottom: 0.25,\n\theader: 0.1,\n\tfooter: 0.1\n} as const satisfies ExcelPrintMargins;\n\nconst PAPER_SIZE = { a4: 9, a3: 8 } as const;\n\nfunction escapeHeaderFooterText(value: string): string {\n\treturn escapeXmlValue(value.replaceAll(\"&\", \"&&\"));\n}\n\nfunction numberToColumnName(index: number): string {\n\tlet value = index;\n\tlet result = \"\";\n\twhile (value > 0) {\n\t\tconst remainder = (value - 1) % 26;\n\t\tresult = String.fromCharCode(65 + remainder) + result;\n\t\tvalue = Math.floor((value - 1) / 26);\n\t}\n\treturn result;\n}\n\nfunction assertPositiveInteger(value: number, name: string): void {\n\tif (!Number.isInteger(value) || value <= 0) throw new Error(`${name} должен быть положительным целым числом`);\n}\n\nfunction assertNonnegativeInteger(value: number, name: string): void {\n\tif (!Number.isInteger(value) || value < 0) throw new Error(`${name} должен быть целым неотрицательным числом`);\n}\n\nfunction validateRange(range: readonly [number, number], name: string): void {\n\tassertPositiveInteger(range[0], `${name}: начало`);\n\tassertPositiveInteger(range[1], `${name}: конец`);\n\tif (range[0] > range[1]) throw new Error(`${name}: начало не может быть больше конца`);\n}\n\nfunction validatePrintSetup(print: ExcelPrintSetup): void {\n\t// В OpenXML ноль означает автоматическое число страниц по соответствующей оси.\n\tif (print.fitToWidthPages !== undefined) assertNonnegativeInteger(print.fitToWidthPages, \"fitToWidthPages\");\n\tif (print.fitToHeightPages !== undefined) assertNonnegativeInteger(print.fitToHeightPages, \"fitToHeightPages\");\n\tif (print.repeatRows) validateRange(print.repeatRows, \"repeatRows\");\n\tif (print.repeatColumns) validateRange(print.repeatColumns, \"repeatColumns\");\n\tfor (const value of print.columnBreaksAfter ?? []) assertPositiveInteger(value, \"columnBreaksAfter\");\n\tfor (const value of print.rowBreaksAfter ?? []) assertPositiveInteger(value, \"rowBreaksAfter\");\n\tif (print.printArea) {\n\t\tassertPositiveInteger(print.printArea.firstRow, \"printArea.firstRow\");\n\t\tassertPositiveInteger(print.printArea.lastRow, \"printArea.lastRow\");\n\t\tassertPositiveInteger(print.printArea.firstColumn, \"printArea.firstColumn\");\n\t\tassertPositiveInteger(print.printArea.lastColumn, \"printArea.lastColumn\");\n\t\tif (print.printArea.firstRow > print.printArea.lastRow || print.printArea.firstColumn > print.printArea.lastColumn) {\n\t\t\tthrow new Error(\"printArea должен задавать непустой прямоугольник\");\n\t\t}\n\t}\n}\n\nfunction buildHeaderFooterXml(\n\ttagName: \"headerFooter\",\n\theader: ExcelPrintHeaderFooter | undefined,\n\tfooter: ExcelPrintHeaderFooter | undefined\n): string {\n\tif (!header && !footer) return \"\";\n\tconst buildSection = (value: ExcelPrintHeaderFooter | undefined) => {\n\t\tif (!value) return \"\";\n\t\tconst content = [\n\t\t\tvalue.left ? `&L${escapeHeaderFooterText(value.left)}` : \"\",\n\t\t\tvalue.center ? `&C${escapeHeaderFooterText(value.center)}` : \"\",\n\t\t\tvalue.right ? `&R${escapeHeaderFooterText(value.right)}` : \"\",\n\t\t\tvalue.includePageNumber ? \"&RСтраница &P из &N\" : \"\"\n\t\t].join(\"\");\n\t\treturn content;\n\t};\n\tconst oddHeader = buildSection(header);\n\tconst oddFooter = buildSection(footer);\n\treturn `<${tagName}>${oddHeader ? `<oddHeader>${oddHeader}</oddHeader>` : \"\"}${oddFooter ? `<oddFooter>${oddFooter}</oddFooter>` : \"\"}</${tagName}>`;\n}\n\nfunction buildBreaksXml(tagName: \"colBreaks\" | \"rowBreaks\", values: readonly number[] | undefined): string {\n\tif (!values || values.length === 0) return \"\";\n\tconst uniqueValues = [...new Set(values)].sort((left, right) => left - right);\n\treturn `<${tagName} count=\"${uniqueValues.length}\" manualBreakCount=\"${uniqueValues.length}\">${uniqueValues\n\t\t.map((value) => `<brk id=\"${value}\" min=\"0\" max=\"16383\" man=\"1\"/>`)\n\t\t.join(\"\")}</${tagName}>`;\n}\n\nfunction applyWorksheetPrintSetup(content: string, print: ExcelPrintSetup): string {\n\tconst margins = print.margins ?? DEFAULT_PRINT_MARGINS;\n\tconst fitToPage = print.fitToWidthPages !== undefined || print.fitToHeightPages !== undefined;\n\tconst sheetProperties = fitToPage ? '<sheetPr><pageSetUpPr fitToPage=\"1\" autoPageBreaks=\"0\"/></sheetPr>' : \"\";\n\tconst result = sheetProperties ? content.replace(\"<sheetViews\", `${sheetProperties}<sheetViews`) : content;\n\tconst pageMargins = `<pageMargins left=\"${margins.left}\" right=\"${margins.right}\" top=\"${margins.top}\" bottom=\"${margins.bottom}\" header=\"${margins.header}\" footer=\"${margins.footer}\"/>`;\n\tconst pageSetupAttributes = [\n\t\t`paperSize=\"${PAPER_SIZE[print.paperSize ?? \"a4\"]}\"`,\n\t\t`orientation=\"${print.orientation}\"`,\n\t\tprint.fitToWidthPages !== undefined ? `fitToWidth=\"${print.fitToWidthPages}\"` : \"\",\n\t\tprint.fitToHeightPages !== undefined ? `fitToHeight=\"${print.fitToHeightPages}\"` : \"\",\n\t\tprint.pageOrder ? `pageOrder=\"${print.pageOrder}\"` : \"\",\n\t\t'usePrinterDefaults=\"0\"'\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tconst additions = [\n\t\tpageMargins,\n\t\t`<pageSetup ${pageSetupAttributes}/>`,\n\t\tbuildHeaderFooterXml(\"headerFooter\", print.header, print.footer),\n\t\tbuildBreaksXml(\"rowBreaks\", print.rowBreaksAfter),\n\t\tbuildBreaksXml(\"colBreaks\", print.columnBreaksAfter)\n\t].join(\"\");\n\tconst drawingIndex = result.indexOf(\"<drawing\");\n\treturn drawingIndex >= 0\n\t\t? `${result.slice(0, drawingIndex)}${additions}${result.slice(drawingIndex)}`\n\t\t: result.replace(\"</worksheet>\", `${additions}</worksheet>`);\n}\n\nfunction quoteFormulaSheetName(name: string): string {\n\treturn `'${name.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction buildDefinedNames(sheetNames: readonly string[], sheets: readonly ExcelMatrixWorkbookSheet[]): string {\n\tconst definitions: string[] = [];\n\tfor (let sheetIndex = 0; sheetIndex < sheets.length; sheetIndex += 1) {\n\t\tconst print = sheets[sheetIndex]?.print;\n\t\tif (!print) continue;\n\t\tconst sheetName = quoteFormulaSheetName(sheetNames[sheetIndex] ?? `Лист ${sheetIndex + 1}`);\n\t\tconst titles = [\n\t\t\tprint.repeatRows ? `${sheetName}!$${print.repeatRows[0]}:$${print.repeatRows[1]}` : \"\",\n\t\t\tprint.repeatColumns\n\t\t\t\t? `${sheetName}!$${numberToColumnName(print.repeatColumns[0])}:$${numberToColumnName(print.repeatColumns[1])}`\n\t\t\t\t: \"\"\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\",\");\n\t\tif (titles) {\n\t\t\tdefinitions.push(`<definedName name=\"_xlnm.Print_Titles\" localSheetId=\"${sheetIndex}\">${escapeXmlValue(titles)}</definedName>`);\n\t\t}\n\t\tif (print.printArea) {\n\t\t\tconst { firstRow, lastRow, firstColumn, lastColumn } = print.printArea;\n\t\t\tconst area = `${sheetName}!$${numberToColumnName(firstColumn)}$${firstRow}:$${numberToColumnName(lastColumn)}$${lastRow}`;\n\t\t\tdefinitions.push(`<definedName name=\"_xlnm.Print_Area\" localSheetId=\"${sheetIndex}\">${escapeXmlValue(area)}</definedName>`);\n\t\t}\n\t}\n\treturn definitions.join(\"\");\n}\n\n/**\n * Дополняет штатный контейнер definedNames, который write-excel-file уже\n * создаёт в workbook.xml. Два одноимённых контейнера формально нарушают схему\n * OOXML: Numbers читает такую книгу, а Microsoft Excel отклоняет её.\n */\nfunction applyWorkbookDefinedNames(content: string, definedNames: string): string {\n\tif (/<definedNames\\s*\\/>/.test(content)) {\n\t\treturn content.replace(/<definedNames\\s*\\/>/, `<definedNames>${definedNames}</definedNames>`);\n\t}\n\tif (content.includes(\"</definedNames>\")) {\n\t\treturn content.replace(\"</definedNames>\", `${definedNames}</definedNames>`);\n\t}\n\treturn content.replace(\"</sheets>\", `</sheets><definedNames>${definedNames}</definedNames>`);\n}\n\n/** Настройки печати требуют двух согласованных OpenXML-частей: worksheet и workbook defined names. */\nfunction createMatrixPrintFeature(\n\tsheetNames: readonly string[],\n\tsheets: readonly ExcelMatrixWorkbookSheet[]\n): Feature<BrowserExcelFileContent> | undefined {\n\tif (!sheets.some(({ print }) => print)) return undefined;\n\tconst definedNames = buildDefinedNames(sheetNames, sheets);\n\treturn {\n\t\tfiles: {\n\t\t\ttransform: {\n\t\t\t\t\"xl/worksheets/sheet{id}.xml\": {\n\t\t\t\t\ttransform: (content, _options, properties) => {\n\t\t\t\t\t\tconst setup = sheets[properties.sheetIndex]?.print;\n\t\t\t\t\t\treturn setup ? applyWorksheetPrintSetup(content, setup) : content;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"xl/workbook.xml\": {\n\t\t\t\t\ttransform: (content) => (definedNames ? applyWorkbookDefinedNames(content, definedNames) : content)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Формирует Blob без побочного эффекта скачивания.\n *\n * Этот вход нужен предпросмотру, file validation и сценариям, где download\n * выполняет отдельный browser-owner. KTK-specific layout сюда не передаётся.\n */\nexport async function createExcelMatrixWorkbookBlob(args: ExcelMatrixWorkbookArgs): Promise<Blob> {\n\tif (args.sheets.length === 0) throw new Error(\"Для формирования Excel-книги нужен минимум один лист\");\n\tif (args.defaultFont) {\n\t\tif (!args.defaultFont.fontFamily.trim()) throw new Error(\"Имя базового шрифта Excel-книги не может быть пустым\");\n\t\tif (!Number.isFinite(args.defaultFont.fontSize) || args.defaultFont.fontSize <= 0) {\n\t\t\tthrow new Error(\"Размер базового шрифта Excel-книги должен быть положительным числом\");\n\t\t}\n\t}\n\tfor (const sheet of args.sheets) {\n\t\tif (sheet.rows.length === 0) throw new Error(`Лист «${sheet.name}» не содержит строк`);\n\t\tif (sheet.print) validatePrintSetup(sheet.print);\n\t}\n\n\tconst sheetNames = resolveExcelSheetNames(args.sheets.map(({ name }) => name));\n\tconst sheets: Sheet<BrowserExcelFileContent>[] = args.sheets.map((sheet, index) => ({\n\t\tdata: sheet.rows.map((row) => [...row]),\n\t\tsheet: sheetNames[index],\n\t\tcolumns: sheet.columns?.map((column) => ({ width: column.width })),\n\t\tshowGridLines: sheet.showGridLines,\n\t\tzoomScale: sheet.zoomScale\n\t}));\n\tconst printFeature = createMatrixPrintFeature(sheetNames, args.sheets);\n\treturn writeXlsxFile(sheets, {\n\t\tfontFamily: args.defaultFont?.fontFamily,\n\t\tfontSize: args.defaultFont?.fontSize,\n\t\tfeatures: printFeature ? [printFeature] : undefined\n\t}).toBlob();\n}\n\n/** Скачивает готовую матричную книгу из явного пользовательского действия. */\nexport async function downloadExcelMatrixWorkbook(fileName: string, args: ExcelMatrixWorkbookArgs): Promise<void> {\n\tconst blob = await createExcelMatrixWorkbookBlob(args);\n\tdownloadFileFromBlob(fileName, blob);\n}\n","import { DEFAULT_DATE_PRESET_NAMES, getDatePreset, type DateFormatPreset } from \"../formatters/date\";\nimport { getNumberPreset } from \"../formatters/number\";\nimport {\n\tvalidateFormattersPipelineConfig,\n\ttype FormattersPipelineConfig,\n\ttype FormattersPipelineTypedValueFormatConfig\n} from \"../formatters/pipeline\";\nimport {\n\tisODataBooleanType,\n\tisODataDateType,\n\tisODataIntegerType,\n\tisODataNumericType,\n\ttype ColumnRole,\n\ttype ODataMetaType\n} from \"../odata-service\";\n\nimport { downloadExcelFile, downloadExcelWorkbook, type ExcelCellStyle, type ExcelColumn, type ExcelRow } from \"./excel\";\n\nexport type TableExcelColumnDescriptor = {\n\tid: string;\n\theader: string;\n\twidth?: number;\n\trole?: ColumnRole;\n\todataType?: ODataMetaType;\n\tclientOnly?: boolean;\n\tformattersPipeline?: FormattersPipelineConfig;\n};\n\nexport type ResolvedExcelTable = {\n\tcolumns: ExcelColumn[];\n\trows: ExcelRow[];\n\tcellStyles?: ReadonlyArray<Readonly<Record<string, ExcelCellStyle | undefined>>>;\n};\n\nexport type DownloadResolvedExcelTableArgs = {\n\tfileName: string;\n\ttable: ResolvedExcelTable;\n\tautoFilter?: boolean;\n};\n\n/** Один подготовленный табличный лист generic Excel-книги. */\nexport type ResolvedExcelWorkbookSheet = {\n\t/** Желаемое имя листа. */\n\tname: string;\n\t/** Подготовленные колонки, строки и optional стили ячеек. */\n\ttable: ResolvedExcelTable;\n\t/** Включить автофильтр для этого листа. По умолчанию включён. */\n\tautoFilter?: boolean;\n};\n\n/** Аргументы скачивания книги из нескольких подготовленных таблиц. */\nexport type DownloadResolvedExcelWorkbookArgs = {\n\t/** Имя скачиваемого `.xlsx` файла. */\n\tfileName: string;\n\t/** Табличные листы в порядке отображения в книге. */\n\tsheets: ResolvedExcelWorkbookSheet[];\n};\n\nconst INTEGER_EXCEL_FORMAT = \"#,##0\";\nconst DEFAULT_DECIMAL_EXCEL_FORMAT = \"#,##0.0\";\nconst EXCEL_DATE_FORMAT = \"dd.mm.yyyy\";\n/**\n * Excel-маски календарных пресетов сохраняют экранную детализацию:\n * month-* выводит день и месяц без года, а month-year-* — месяц и год без дня.\n */\nconst EXCEL_DAY_MONTH_SHORT_FORMAT = \"dd.mm\";\nconst EXCEL_DAY_MONTH_MEDIUM_FORMAT = \"d mmm\";\nconst EXCEL_DAY_MONTH_LONG_FORMAT = \"d mmmm\";\nconst EXCEL_MONTH_YEAR_MEDIUM_FORMAT = \"mmm yyyy\";\nconst EXCEL_MONTH_YEAR_LONG_FORMAT = \"mmmm yyyy\";\nconst EXCEL_TIME_FORMAT = \"hh:mm\";\nconst EXCEL_TIME_SECONDS_FORMAT = \"hh:mm:ss\";\nconst EXCEL_DATETIME_FORMAT = `${EXCEL_DATE_FORMAT} ${EXCEL_TIME_FORMAT}`;\nconst EXCEL_DATETIME_SECONDS_FORMAT = `${EXCEL_DATE_FORMAT} ${EXCEL_TIME_SECONDS_FORMAT}`;\n\nfunction resolveTypedValueFormatConfig(\n\tformattersPipeline: FormattersPipelineConfig | undefined\n): FormattersPipelineTypedValueFormatConfig | undefined {\n\tconst validation = validateFormattersPipelineConfig(formattersPipeline);\n\tconst typedValueFormatStep = validation.plan?.steps.find((step) => step.type === \"typedValueFormat\");\n\n\treturn typedValueFormatStep?.type === \"typedValueFormat\" ? typedValueFormatStep.config : undefined;\n}\n\nexport function resolveTableExcelColumnType(descriptor: TableExcelColumnDescriptor): ExcelColumn[\"type\"] {\n\tif (descriptor.clientOnly) return Number;\n\tif (isODataDateType(descriptor.odataType)) return Date;\n\tif (isODataBooleanType(descriptor.odataType)) return Boolean;\n\tif (isODataNumericType(descriptor.odataType)) return Number;\n\tif (descriptor.role === \"measure\") return Number;\n\treturn String;\n}\n\nfunction resolveNumberPresetName(descriptor: TableExcelColumnDescriptor): string {\n\tconst configuredPresetName = resolveTypedValueFormatConfig(descriptor.formattersPipeline)?.numberPresetName?.trim();\n\tif (configuredPresetName && getNumberPreset(configuredPresetName)) return configuredPresetName;\n\tif (isODataIntegerType(descriptor.odataType)) return \"integer\";\n\treturn \"decimal\";\n}\n\nfunction resolveExcelNumberFormat(descriptor: TableExcelColumnDescriptor): string {\n\tconst preset = getNumberPreset(resolveNumberPresetName(descriptor));\n\tif (!preset) return DEFAULT_DECIMAL_EXCEL_FORMAT;\n\treturn preset.decimals > 0 ? `#,##0.${\"0\".repeat(preset.decimals)}` : INTEGER_EXCEL_FORMAT;\n}\n\nfunction resolveDatePresetName(descriptor: TableExcelColumnDescriptor): string {\n\tconst configuredPresetName = resolveTypedValueFormatConfig(descriptor.formattersPipeline)?.datePresetName?.trim();\n\tif (configuredPresetName) return configuredPresetName;\n\tif (descriptor.odataType === \"time\") return DEFAULT_DATE_PRESET_NAMES.time;\n\tif (descriptor.odataType === \"datetime\" || descriptor.odataType === \"datetimeOffset\") return DEFAULT_DATE_PRESET_NAMES.datetime;\n\treturn DEFAULT_DATE_PRESET_NAMES.date;\n}\n\nfunction normalizeDatePatternForExcel(pattern: string): string {\n\treturn pattern.replace(/H/g, \"h\").replace(/M/g, \"m\");\n}\n\nfunction resolveBuiltinDatePresetExcelFormat(presetName: string): string | undefined {\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.odataDatetime || presetName === DEFAULT_DATE_PRESET_NAMES.abapDatetime) {\n\t\treturn EXCEL_DATETIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.odataDate || presetName === DEFAULT_DATE_PRESET_NAMES.abapDate) {\n\t\treturn EXCEL_DATE_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthShort) {\n\t\treturn EXCEL_DAY_MONTH_SHORT_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthMedium) {\n\t\treturn EXCEL_DAY_MONTH_MEDIUM_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthLong) {\n\t\treturn EXCEL_DAY_MONTH_LONG_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthYearMedium) {\n\t\treturn EXCEL_MONTH_YEAR_MEDIUM_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthYearLong) {\n\t\treturn EXCEL_MONTH_YEAR_LONG_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"datetime\")) {\n\t\treturn presetName === DEFAULT_DATE_PRESET_NAMES.datetime || presetName === DEFAULT_DATE_PRESET_NAMES.datetimeShort\n\t\t\t? EXCEL_DATETIME_FORMAT\n\t\t\t: EXCEL_DATETIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"time\")) {\n\t\treturn presetName === DEFAULT_DATE_PRESET_NAMES.time || presetName === DEFAULT_DATE_PRESET_NAMES.timeShort\n\t\t\t? EXCEL_TIME_FORMAT\n\t\t\t: EXCEL_TIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"date\")) return EXCEL_DATE_FORMAT;\n\n\treturn undefined;\n}\n\nfunction resolveExcelDateFormatFromPreset(presetName: string, preset: DateFormatPreset | undefined): string {\n\tconst builtinFormat = resolveBuiltinDatePresetExcelFormat(presetName);\n\tif (builtinFormat) return builtinFormat;\n\tif (preset?.pattern) return normalizeDatePatternForExcel(preset.pattern);\n\n\t// Настройка datePresetName исторически хранит и ручной шаблон. Если имени\n\t// нет в реестре, передаём шаблон в Excel как есть после нормализации токенов.\n\treturn normalizeDatePatternForExcel(presetName);\n}\n\nfunction resolveExcelDateFormat(descriptor: TableExcelColumnDescriptor): string {\n\tconst presetName = resolveDatePresetName(descriptor);\n\treturn resolveExcelDateFormatFromPreset(presetName, getDatePreset(presetName));\n}\n\nfunction resolveTableExcelColumnFormat(descriptor: TableExcelColumnDescriptor, type: ExcelColumn[\"type\"]): string | undefined {\n\tif (type === Number) return resolveExcelNumberFormat(descriptor);\n\tif (type === Date) return resolveExcelDateFormat(descriptor);\n\treturn undefined;\n}\n\nexport function createTableExcelColumn(descriptor: TableExcelColumnDescriptor): ExcelColumn {\n\tconst type = resolveTableExcelColumnType(descriptor);\n\treturn {\n\t\tid: descriptor.id,\n\t\theader: descriptor.header,\n\t\twidth: descriptor.width,\n\t\ttype,\n\t\tformat: resolveTableExcelColumnFormat(descriptor, type)\n\t};\n}\n\nexport async function downloadResolvedExcelTable({ fileName, table, autoFilter = true }: DownloadResolvedExcelTableArgs): Promise<void> {\n\tawait downloadExcelFile({\n\t\tfileName,\n\t\tcolumns: table.columns,\n\t\trows: table.rows,\n\t\tautoFilter,\n\t\tgetCellStyle: ({ rowIndex, column }) => table.cellStyles?.[rowIndex]?.[column.id]\n\t});\n}\n\n/** Скачивает многолистовую книгу из таблиц, уже подготовленных доменными consumers. */\nexport async function downloadResolvedExcelWorkbook({ fileName, sheets }: DownloadResolvedExcelWorkbookArgs): Promise<void> {\n\tawait downloadExcelWorkbook({\n\t\tfileName,\n\t\tsheets: sheets.map(({ name, table, autoFilter = true }) => ({\n\t\t\tname,\n\t\t\tcolumns: table.columns,\n\t\t\trows: table.rows,\n\t\t\tautoFilter,\n\t\t\tgetCellStyle: ({ rowIndex, column }) => table.cellStyles?.[rowIndex]?.[column.id]\n\t\t}))\n\t});\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/excel/excel.ts","../../src/excel/matrixWorkbook.ts","../../src/excel/tableExport.ts"],"sourcesContent":["import writeXlsxFile, { type Cell, type CellObject, type Feature, type Sheet, type SheetData } from \"write-excel-file/browser\";\n\nimport { parseDate } from \"../formatters/date\";\nimport { type State } from \"../types\";\n\nexport type ExcelColumn = {\n\tid: string;\n\theader: string;\n\twidth?: number;\n\ttype?: StringConstructor | NumberConstructor | DateConstructor | BooleanConstructor;\n\tformat?: string;\n};\n\nexport type ExcelRow = Record<string, unknown>;\n\nexport type ExcelCellStyle = Omit<CellObject, \"value\" | \"type\" | \"format\">;\n\nexport type DownloadExcelFileArgs = {\n\tfileName: string;\n\tcolumns: ExcelColumn[];\n\trows: ExcelRow[];\n\tautoFilter?: boolean;\n\tgetCellStyle?: (args: { row: ExcelRow; column: ExcelColumn; rowIndex: number; columnIndex: number }) => ExcelCellStyle | undefined;\n};\n\n/** Описание одного табличного листа внутри Excel-книги. */\nexport type ExcelWorkbookSheet = Omit<DownloadExcelFileArgs, \"fileName\"> & {\n\t/** Желаемое имя листа; writer нормализует ограничения формата и конфликты имён. */\n\tname: string;\n};\n\n/** Аргументы формирования Excel-книги с одним или несколькими табличными листами. */\nexport type DownloadExcelWorkbookArgs = {\n\t/** Имя скачиваемого `.xlsx` файла. */\n\tfileName: string;\n\t/** Листы книги в порядке отображения в Excel. */\n\tsheets: ExcelWorkbookSheet[];\n};\n\ntype BrowserExcelFileContent = File | Blob | ArrayBuffer;\n\nconst VALUE_STATE_TEXT_COLORS: Partial<Record<State, string>> = {\n\tinformation: \"#2563EB\",\n\tsuccess: \"#107C10\",\n\twarning: \"#B7791F\",\n\terror: \"#C50F1F\"\n};\n\nconst HEADER_CELL_STYLE = {\n\tfontWeight: \"bold\",\n\tbottomBorderStyle: \"thin\",\n\tbottomBorderColor: \"#D9D9D9\"\n} satisfies ExcelCellStyle;\n\nconst EXCEL_SHEET_NAME_MAX_LENGTH = 31;\nconst EXCEL_SHEET_NAME_INVALID_CHARACTERS = /[\\u0000-\\u001f[\\]/\\\\:*?]+/g;\nconst EXCEL_SHEET_NAME_EDGE_APOSTROPHES = /^'+|'+$/g;\nconst EXCEL_SHEET_NAME_WHITESPACE = /\\s+/g;\n\n/**\n * Приводит общий value-state проекта к базовому Excel-стилю.\n *\n * Helper живет в shared Excel-слое, чтобы analytical/detail экспорт не\n * дублировали палитру статусов и одинаково подсвечивали warning/error/success.\n */\nexport function resolveExcelCellStyleFromState(state: State | undefined): ExcelCellStyle | undefined {\n\tconst textColor = state ? VALUE_STATE_TEXT_COLORS[state] : undefined;\n\treturn textColor ? { textColor } : undefined;\n}\n\n/**\n * Переводит zero-based индекс колонки в Excel-адрес (`0 -> A`, `27 -> AB`).\n * Нужен для XML-диапазонов, потому что `write-excel-file` не предоставляет\n * отдельного API для построения ссылок вида `A1:C10`.\n */\nfunction numberToColumnName(index: number): string {\n\tlet value = index + 1;\n\tlet out = \"\";\n\n\twhile (value > 0) {\n\t\tconst remainder = (value - 1) % 26;\n\t\tout = String.fromCharCode(65 + remainder) + out;\n\t\tvalue = Math.floor((value - 1) / 26);\n\t}\n\n\treturn out;\n}\n\n/**\n * Строит диапазон автофильтра для всей выгруженной таблицы.\n *\n * Возвращает `undefined` для пустой сетки, чтобы не вставлять в xlsx\n * невалидный `<autoFilter>` без реального диапазона.\n */\nexport function buildExcelAutoFilterRef(columnCount: number, rowCount: number): string | undefined {\n\tif (columnCount <= 0 || rowCount <= 0) return undefined;\n\n\treturn `A1:${numberToColumnName(columnCount - 1)}${rowCount}`;\n}\n\n/**\n * Добавляет autoFilter через feature-transform `write-excel-file`.\n *\n * Библиотека умеет трансформировать XML листа, но не имеет высокоуровневого\n * параметра `autoFilter`, поэтому здесь изолирован низкоуровневый OpenXML-хук.\n */\nfunction createAutoFilterFeature<FileContent>(refs: readonly (string | undefined)[]): Feature<FileContent> | undefined {\n\tif (!refs.some(Boolean)) return undefined;\n\n\treturn {\n\t\tfiles: {\n\t\t\ttransform: {\n\t\t\t\t\"xl/worksheets/sheet{id}.xml\": {\n\t\t\t\t\ttransform: (content, _options, properties) => {\n\t\t\t\t\t\tconst ref = refs[properties.sheetIndex];\n\t\t\t\t\t\treturn ref ? content.replace(\"</sheetData>\", `</sheetData><autoFilter ref=\"${ref}\"/>`) : content;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Нормализует имена листов по ограничениям Excel и делает их уникальными.\n *\n * Runtime-заголовки могут содержать запрещённые символы, превышать 31 знак или\n * совпадать между несколькими представлениями одной конфигурации. Нормализация\n * выполняется централизованно, чтобы все consumers получали открываемую книгу.\n */\nexport function resolveExcelSheetNames(names: readonly string[]): string[] {\n\tconst usedNames = new Set<string>();\n\tconst resolvedNames: string[] = [];\n\n\tfor (let index = 0; index < names.length; index += 1) {\n\t\tconst fallbackName = `Лист ${index + 1}`;\n\t\tconst normalizedName = names[index]\n\t\t\t?.replace(EXCEL_SHEET_NAME_INVALID_CHARACTERS, \" \")\n\t\t\t.replace(EXCEL_SHEET_NAME_WHITESPACE, \" \")\n\t\t\t.trim()\n\t\t\t.replace(EXCEL_SHEET_NAME_EDGE_APOSTROPHES, \"\")\n\t\t\t.trim();\n\t\tconst baseName = normalizedName || fallbackName;\n\t\tlet resolvedName = baseName.slice(0, EXCEL_SHEET_NAME_MAX_LENGTH);\n\t\tlet duplicateIndex = 2;\n\n\t\twhile (usedNames.has(resolvedName.toLocaleLowerCase())) {\n\t\t\tconst suffix = ` (${duplicateIndex})`;\n\t\t\tresolvedName = `${baseName.slice(0, EXCEL_SHEET_NAME_MAX_LENGTH - suffix.length)}${suffix}`;\n\t\t\tduplicateIndex += 1;\n\t\t}\n\n\t\tusedNames.add(resolvedName.toLocaleLowerCase());\n\t\tresolvedNames.push(resolvedName);\n\t}\n\n\treturn resolvedNames;\n}\n\n/**\n * Приводит входное значение к типу Excel-ячейки.\n *\n * Важный смысл: если колонка числовая, в файл должен попасть настоящий Number,\n * а не уже отформатированная строка. Тогда Excel сможет применять `format`.\n */\nfunction normalizeCellValue(value: unknown, type: ExcelColumn[\"type\"]) {\n\tif (value == null || value === \"\") return undefined;\n\n\tif (type === Number) {\n\t\tconst numberValue = typeof value === \"number\" ? value : Number(value);\n\t\treturn Number.isFinite(numberValue) ? numberValue : undefined;\n\t}\n\n\tif (type === String) return String(value);\n\tif (type === Boolean) return Boolean(value);\n\tif (type === Date) return value instanceof Date ? value : (parseDate(value) ?? undefined);\n\n\treturn value as CellObject[\"value\"];\n}\n\n/**\n * Собирает SheetData для `write-excel-file`.\n *\n * Функция намеренно не знает о бизнес-сущностях: ей передают уже готовые\n * колонки/строки и необязательный callback стилей конкретных ячеек.\n */\nexport function buildExcelSheetData(args: Omit<DownloadExcelFileArgs, \"fileName\" | \"autoFilter\">): SheetData {\n\tconst headerRow: Cell[] = args.columns.map((column) => ({\n\t\ttype: String,\n\t\tvalue: column.header,\n\t\t...HEADER_CELL_STYLE\n\t}));\n\n\tconst sheetRows: SheetData = [headerRow];\n\n\tfor (let rowIndex = 0; rowIndex < args.rows.length; rowIndex++) {\n\t\tconst row = args.rows[rowIndex];\n\t\tconst sheetRow: Cell[] = [];\n\n\t\tfor (let columnIndex = 0; columnIndex < args.columns.length; columnIndex++) {\n\t\t\tconst column = args.columns[columnIndex];\n\t\t\tconst value = normalizeCellValue(row[column.id], column.type);\n\t\t\tconst style = args.getCellStyle?.({ row, column, rowIndex, columnIndex });\n\n\t\t\tsheetRow.push({\n\t\t\t\tvalue,\n\t\t\t\ttype: column.type,\n\t\t\t\tformat: column.format,\n\t\t\t\t...style\n\t\t\t});\n\t\t}\n\n\t\tsheetRows.push(sheetRow);\n\t}\n\n\treturn sheetRows;\n}\n\n/**\n * Формирует и скачивает Excel-книгу с независимыми табличными листами.\n *\n * Каждый лист получает собственные ширины колонок, стили ячеек и диапазон\n * автофильтра. Доменная подготовка колонок и строк остаётся у вызывающей стороны.\n */\nexport async function downloadExcelWorkbook(args: DownloadExcelWorkbookArgs): Promise<void> {\n\tif (args.sheets.length === 0) {\n\t\tthrow new Error(\"Для формирования Excel-книги нужен минимум один лист\");\n\t}\n\n\tconst sheetNames = resolveExcelSheetNames(args.sheets.map((sheet) => sheet.name));\n\tconst autoFilterRefs: Array<string | undefined> = [];\n\tconst sheets: Sheet<BrowserExcelFileContent>[] = args.sheets.map((sheet, index) => {\n\t\tconst data = buildExcelSheetData(sheet);\n\t\tautoFilterRefs.push(sheet.autoFilter ? buildExcelAutoFilterRef(sheet.columns.length, data.length) : undefined);\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tsheet: sheetNames[index],\n\t\t\tcolumns: sheet.columns.map((column) => ({\n\t\t\t\twidth: column.width\n\t\t\t}))\n\t\t};\n\t});\n\tconst autoFilterFeature = createAutoFilterFeature<BrowserExcelFileContent>(autoFilterRefs);\n\n\tawait writeXlsxFile(sheets, {\n\t\tfeatures: autoFilterFeature ? [autoFilterFeature] : undefined\n\t}).toFile(args.fileName);\n}\n\n/**\n * Формирует и скачивает `.xlsx` в браузере.\n *\n * Это публичная generic-точка shared-слоя: она отвечает только за Excel-файл,\n * а подготовка доменных данных остаётся на вызывающей стороне.\n */\nexport async function downloadExcelFile(args: DownloadExcelFileArgs): Promise<void> {\n\tawait downloadExcelWorkbook({\n\t\tfileName: args.fileName,\n\t\tsheets: [\n\t\t\t{\n\t\t\t\tname: \"Sheet1\",\n\t\t\t\tcolumns: args.columns,\n\t\t\t\trows: args.rows,\n\t\t\t\tautoFilter: args.autoFilter,\n\t\t\t\tgetCellStyle: args.getCellStyle\n\t\t\t}\n\t\t]\n\t});\n}\n","import writeXlsxFile, { type Cell, type Feature, type Sheet } from \"write-excel-file/browser\";\n\nimport { downloadFileFromBlob } from \"../dom\";\nimport { escapeXmlValue } from \"../xml\";\n\nimport { resolveExcelSheetNames } from \"./excel\";\n\ntype BrowserExcelFileContent = File | Blob | ArrayBuffer;\n\n/** Одна строка произвольной Excel-матрицы. Объединения задаются стилями первой ячейки диапазона. */\nexport type ExcelMatrixRow = readonly Cell[];\n\n/** Поля задаются в дюймах, как в OpenXML и диалоге параметров страницы Excel. */\nexport type ExcelPrintMargins = {\n\treadonly left: number;\n\treadonly right: number;\n\treadonly top: number;\n\treadonly bottom: number;\n\treadonly header: number;\n\treadonly footer: number;\n};\n\n/** Видимый колонтитул не принимает raw Excel-коды, чтобы текст не мог случайно изменить разметку секций. */\nexport type ExcelPrintHeaderFooter = {\n\treadonly left?: string;\n\treadonly center?: string;\n\treadonly right?: string;\n\treadonly includePageNumber?: boolean;\n};\n\n/** One-based прямоугольник печати соответствует координатам строк и столбцов Excel. */\nexport type ExcelPrintArea = {\n\treadonly firstRow: number;\n\treadonly lastRow: number;\n\treadonly firstColumn: number;\n\treadonly lastColumn: number;\n};\n\n/**\n * Печатный контракт одного листа.\n *\n * `columnBreaksAfter` и `rowBreaksAfter` используют one-based координату\n * последней ячейки страницы. Явная модель не позволяет consumer перепутать\n * индекс массива с адресом Excel и разрезать составной блок на печати.\n */\nexport type ExcelPrintSetup = {\n\treadonly orientation: \"portrait\" | \"landscape\";\n\treadonly paperSize?: \"a4\" | \"a3\";\n\treadonly fitToWidthPages?: number;\n\treadonly fitToHeightPages?: number;\n\treadonly pageOrder?: \"downThenOver\" | \"overThenDown\";\n\treadonly margins?: ExcelPrintMargins;\n\treadonly repeatRows?: readonly [firstRow: number, lastRow: number];\n\treadonly repeatColumns?: readonly [firstColumn: number, lastColumn: number];\n\treadonly printArea?: ExcelPrintArea;\n\treadonly columnBreaksAfter?: readonly number[];\n\treadonly rowBreaksAfter?: readonly number[];\n\treadonly header?: ExcelPrintHeaderFooter;\n\treadonly footer?: ExcelPrintHeaderFooter;\n};\n\n/** Произвольный лист сохраняет готовую матрицу и не навязывает табличную строку заголовков. */\nexport type ExcelMatrixWorkbookSheet = {\n\treadonly name: string;\n\treadonly rows: readonly ExcelMatrixRow[];\n\treadonly columns?: readonly { readonly width?: number }[];\n\treadonly showGridLines?: boolean;\n\treadonly zoomScale?: number;\n\treadonly print?: ExcelPrintSetup;\n};\n\n/**\n * Базовый шрифт стиля Normal задаёт метрику символьной ширины столбцов Excel.\n *\n * Шрифт отдельных ячеек может отличаться: эта настройка нужна прежде всего\n * для воспроизводимого преобразования `columns[].width` в экранные пиксели.\n */\nexport type ExcelMatrixWorkbookDefaultFont = {\n\treadonly fontFamily: string;\n\treadonly fontSize: number;\n};\n\n/** Аргументы generic-книги, пригодной как для скачивания, так и для file validation. */\nexport type ExcelMatrixWorkbookArgs = {\n\treadonly defaultFont?: ExcelMatrixWorkbookDefaultFont;\n\treadonly sheets: readonly ExcelMatrixWorkbookSheet[];\n};\n\nconst DEFAULT_PRINT_MARGINS = {\n\tleft: 0.2,\n\tright: 0.2,\n\ttop: 0.25,\n\tbottom: 0.25,\n\theader: 0.1,\n\tfooter: 0.1\n} as const satisfies ExcelPrintMargins;\n\nconst PAPER_SIZE = { a4: 9, a3: 8 } as const;\n\nfunction escapeHeaderFooterText(value: string): string {\n\treturn escapeXmlValue(value.replaceAll(\"&\", \"&&\"));\n}\n\nfunction numberToColumnName(index: number): string {\n\tlet value = index;\n\tlet result = \"\";\n\twhile (value > 0) {\n\t\tconst remainder = (value - 1) % 26;\n\t\tresult = String.fromCharCode(65 + remainder) + result;\n\t\tvalue = Math.floor((value - 1) / 26);\n\t}\n\treturn result;\n}\n\nfunction assertPositiveInteger(value: number, name: string): void {\n\tif (!Number.isInteger(value) || value <= 0) throw new Error(`${name} должен быть положительным целым числом`);\n}\n\nfunction assertNonnegativeInteger(value: number, name: string): void {\n\tif (!Number.isInteger(value) || value < 0) throw new Error(`${name} должен быть целым неотрицательным числом`);\n}\n\nfunction validateRange(range: readonly [number, number], name: string): void {\n\tassertPositiveInteger(range[0], `${name}: начало`);\n\tassertPositiveInteger(range[1], `${name}: конец`);\n\tif (range[0] > range[1]) throw new Error(`${name}: начало не может быть больше конца`);\n}\n\nfunction validatePrintSetup(print: ExcelPrintSetup): void {\n\t// В OpenXML ноль означает автоматическое число страниц по соответствующей оси.\n\tif (print.fitToWidthPages !== undefined) assertNonnegativeInteger(print.fitToWidthPages, \"fitToWidthPages\");\n\tif (print.fitToHeightPages !== undefined) assertNonnegativeInteger(print.fitToHeightPages, \"fitToHeightPages\");\n\tif (print.repeatRows) validateRange(print.repeatRows, \"repeatRows\");\n\tif (print.repeatColumns) validateRange(print.repeatColumns, \"repeatColumns\");\n\tfor (const value of print.columnBreaksAfter ?? []) assertPositiveInteger(value, \"columnBreaksAfter\");\n\tfor (const value of print.rowBreaksAfter ?? []) assertPositiveInteger(value, \"rowBreaksAfter\");\n\tif (print.printArea) {\n\t\tassertPositiveInteger(print.printArea.firstRow, \"printArea.firstRow\");\n\t\tassertPositiveInteger(print.printArea.lastRow, \"printArea.lastRow\");\n\t\tassertPositiveInteger(print.printArea.firstColumn, \"printArea.firstColumn\");\n\t\tassertPositiveInteger(print.printArea.lastColumn, \"printArea.lastColumn\");\n\t\tif (print.printArea.firstRow > print.printArea.lastRow || print.printArea.firstColumn > print.printArea.lastColumn) {\n\t\t\tthrow new Error(\"printArea должен задавать непустой прямоугольник\");\n\t\t}\n\t}\n}\n\nfunction buildHeaderFooterXml(\n\ttagName: \"headerFooter\",\n\theader: ExcelPrintHeaderFooter | undefined,\n\tfooter: ExcelPrintHeaderFooter | undefined\n): string {\n\tif (!header && !footer) return \"\";\n\tconst buildSection = (value: ExcelPrintHeaderFooter | undefined) => {\n\t\tif (!value) return \"\";\n\t\tconst content = [\n\t\t\tvalue.left ? `&L${escapeHeaderFooterText(value.left)}` : \"\",\n\t\t\tvalue.center ? `&C${escapeHeaderFooterText(value.center)}` : \"\",\n\t\t\tvalue.right ? `&R${escapeHeaderFooterText(value.right)}` : \"\",\n\t\t\tvalue.includePageNumber ? \"&RСтраница &P из &N\" : \"\"\n\t\t].join(\"\");\n\t\treturn content;\n\t};\n\tconst oddHeader = buildSection(header);\n\tconst oddFooter = buildSection(footer);\n\treturn `<${tagName}>${oddHeader ? `<oddHeader>${oddHeader}</oddHeader>` : \"\"}${oddFooter ? `<oddFooter>${oddFooter}</oddFooter>` : \"\"}</${tagName}>`;\n}\n\nfunction buildBreaksXml(tagName: \"colBreaks\" | \"rowBreaks\", values: readonly number[] | undefined): string {\n\tif (!values || values.length === 0) return \"\";\n\tconst uniqueValues = [...new Set(values)].sort((left, right) => left - right);\n\treturn `<${tagName} count=\"${uniqueValues.length}\" manualBreakCount=\"${uniqueValues.length}\">${uniqueValues\n\t\t.map((value) => `<brk id=\"${value}\" min=\"0\" max=\"16383\" man=\"1\"/>`)\n\t\t.join(\"\")}</${tagName}>`;\n}\n\nfunction applyWorksheetPrintSetup(content: string, print: ExcelPrintSetup): string {\n\tconst margins = print.margins ?? DEFAULT_PRINT_MARGINS;\n\tconst fitToPage = print.fitToWidthPages !== undefined || print.fitToHeightPages !== undefined;\n\tconst sheetProperties = fitToPage ? '<sheetPr><pageSetUpPr fitToPage=\"1\" autoPageBreaks=\"0\"/></sheetPr>' : \"\";\n\tconst result = sheetProperties ? content.replace(\"<sheetViews\", `${sheetProperties}<sheetViews`) : content;\n\tconst pageMargins = `<pageMargins left=\"${margins.left}\" right=\"${margins.right}\" top=\"${margins.top}\" bottom=\"${margins.bottom}\" header=\"${margins.header}\" footer=\"${margins.footer}\"/>`;\n\tconst pageSetupAttributes = [\n\t\t`paperSize=\"${PAPER_SIZE[print.paperSize ?? \"a4\"]}\"`,\n\t\t`orientation=\"${print.orientation}\"`,\n\t\tprint.fitToWidthPages !== undefined ? `fitToWidth=\"${print.fitToWidthPages}\"` : \"\",\n\t\tprint.fitToHeightPages !== undefined ? `fitToHeight=\"${print.fitToHeightPages}\"` : \"\",\n\t\tprint.pageOrder ? `pageOrder=\"${print.pageOrder}\"` : \"\",\n\t\t'usePrinterDefaults=\"0\"'\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tconst additions = [\n\t\tpageMargins,\n\t\t`<pageSetup ${pageSetupAttributes}/>`,\n\t\tbuildHeaderFooterXml(\"headerFooter\", print.header, print.footer),\n\t\tbuildBreaksXml(\"rowBreaks\", print.rowBreaksAfter),\n\t\tbuildBreaksXml(\"colBreaks\", print.columnBreaksAfter)\n\t].join(\"\");\n\tconst drawingIndex = result.indexOf(\"<drawing\");\n\treturn drawingIndex >= 0\n\t\t? `${result.slice(0, drawingIndex)}${additions}${result.slice(drawingIndex)}`\n\t\t: result.replace(\"</worksheet>\", `${additions}</worksheet>`);\n}\n\nfunction quoteFormulaSheetName(name: string): string {\n\treturn `'${name.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction buildDefinedNames(sheetNames: readonly string[], sheets: readonly ExcelMatrixWorkbookSheet[]): string {\n\tconst definitions: string[] = [];\n\tfor (let sheetIndex = 0; sheetIndex < sheets.length; sheetIndex += 1) {\n\t\tconst print = sheets[sheetIndex]?.print;\n\t\tif (!print) continue;\n\t\tconst sheetName = quoteFormulaSheetName(sheetNames[sheetIndex] ?? `Лист ${sheetIndex + 1}`);\n\t\tconst titles = [\n\t\t\tprint.repeatRows ? `${sheetName}!$${print.repeatRows[0]}:$${print.repeatRows[1]}` : \"\",\n\t\t\tprint.repeatColumns\n\t\t\t\t? `${sheetName}!$${numberToColumnName(print.repeatColumns[0])}:$${numberToColumnName(print.repeatColumns[1])}`\n\t\t\t\t: \"\"\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\",\");\n\t\tif (titles) {\n\t\t\tdefinitions.push(`<definedName name=\"_xlnm.Print_Titles\" localSheetId=\"${sheetIndex}\">${escapeXmlValue(titles)}</definedName>`);\n\t\t}\n\t\tif (print.printArea) {\n\t\t\tconst { firstRow, lastRow, firstColumn, lastColumn } = print.printArea;\n\t\t\tconst area = `${sheetName}!$${numberToColumnName(firstColumn)}$${firstRow}:$${numberToColumnName(lastColumn)}$${lastRow}`;\n\t\t\tdefinitions.push(`<definedName name=\"_xlnm.Print_Area\" localSheetId=\"${sheetIndex}\">${escapeXmlValue(area)}</definedName>`);\n\t\t}\n\t}\n\treturn definitions.join(\"\");\n}\n\n/**\n * Дополняет штатный контейнер definedNames, который write-excel-file уже\n * создаёт в workbook.xml. Два одноимённых контейнера формально нарушают схему\n * OOXML: Numbers читает такую книгу, а Microsoft Excel отклоняет её.\n */\nfunction applyWorkbookDefinedNames(content: string, definedNames: string): string {\n\tif (/<definedNames\\s*\\/>/.test(content)) {\n\t\treturn content.replace(/<definedNames\\s*\\/>/, `<definedNames>${definedNames}</definedNames>`);\n\t}\n\tif (content.includes(\"</definedNames>\")) {\n\t\treturn content.replace(\"</definedNames>\", `${definedNames}</definedNames>`);\n\t}\n\treturn content.replace(\"</sheets>\", `</sheets><definedNames>${definedNames}</definedNames>`);\n}\n\n/** Настройки печати требуют двух согласованных OpenXML-частей: worksheet и workbook defined names. */\nfunction createMatrixPrintFeature(\n\tsheetNames: readonly string[],\n\tsheets: readonly ExcelMatrixWorkbookSheet[]\n): Feature<BrowserExcelFileContent> | undefined {\n\tif (!sheets.some(({ print }) => print)) return undefined;\n\tconst definedNames = buildDefinedNames(sheetNames, sheets);\n\treturn {\n\t\tfiles: {\n\t\t\ttransform: {\n\t\t\t\t\"xl/worksheets/sheet{id}.xml\": {\n\t\t\t\t\ttransform: (content, _options, properties) => {\n\t\t\t\t\t\tconst setup = sheets[properties.sheetIndex]?.print;\n\t\t\t\t\t\treturn setup ? applyWorksheetPrintSetup(content, setup) : content;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"xl/workbook.xml\": {\n\t\t\t\t\ttransform: (content) => (definedNames ? applyWorkbookDefinedNames(content, definedNames) : content)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Формирует Blob без побочного эффекта скачивания.\n *\n * Этот вход нужен предпросмотру, file validation и сценариям, где download\n * выполняет отдельный browser-owner. KTK-specific layout сюда не передаётся.\n */\nexport async function createExcelMatrixWorkbookBlob(args: ExcelMatrixWorkbookArgs): Promise<Blob> {\n\tif (args.sheets.length === 0) throw new Error(\"Для формирования Excel-книги нужен минимум один лист\");\n\tif (args.defaultFont) {\n\t\tif (!args.defaultFont.fontFamily.trim()) throw new Error(\"Имя базового шрифта Excel-книги не может быть пустым\");\n\t\tif (!Number.isFinite(args.defaultFont.fontSize) || args.defaultFont.fontSize <= 0) {\n\t\t\tthrow new Error(\"Размер базового шрифта Excel-книги должен быть положительным числом\");\n\t\t}\n\t}\n\tfor (const sheet of args.sheets) {\n\t\tif (sheet.rows.length === 0) throw new Error(`Лист «${sheet.name}» не содержит строк`);\n\t\tif (sheet.print) validatePrintSetup(sheet.print);\n\t}\n\n\tconst sheetNames = resolveExcelSheetNames(args.sheets.map(({ name }) => name));\n\tconst sheets: Sheet<BrowserExcelFileContent>[] = args.sheets.map((sheet, index) => ({\n\t\tdata: sheet.rows.map((row) => [...row]),\n\t\tsheet: sheetNames[index],\n\t\tcolumns: sheet.columns?.map((column) => ({ width: column.width })),\n\t\tshowGridLines: sheet.showGridLines,\n\t\tzoomScale: sheet.zoomScale\n\t}));\n\tconst printFeature = createMatrixPrintFeature(sheetNames, args.sheets);\n\treturn writeXlsxFile(sheets, {\n\t\tfontFamily: args.defaultFont?.fontFamily,\n\t\tfontSize: args.defaultFont?.fontSize,\n\t\tfeatures: printFeature ? [printFeature] : undefined\n\t}).toBlob();\n}\n\n/** Скачивает готовую матричную книгу из явного пользовательского действия. */\nexport async function downloadExcelMatrixWorkbook(fileName: string, args: ExcelMatrixWorkbookArgs): Promise<void> {\n\tconst blob = await createExcelMatrixWorkbookBlob(args);\n\tdownloadFileFromBlob(fileName, blob);\n}\n","import { DEFAULT_DATE_PRESET_NAMES, getDatePreset, type DateFormatPreset } from \"../formatters/date\";\nimport { getNumberPreset } from \"../formatters/number\";\nimport {\n\tvalidateFormattersPipelineConfig,\n\ttype FormattersPipelineConfig,\n\ttype FormattersPipelineTypedValueFormatConfig\n} from \"../formatters/pipeline\";\nimport {\n\tisODataBooleanType,\n\tisODataDateType,\n\tisODataIntegerType,\n\tisODataNumericType,\n\ttype ColumnRole,\n\ttype ODataMetaType\n} from \"../odata-service\";\n\nimport { downloadExcelFile, downloadExcelWorkbook, type ExcelCellStyle, type ExcelColumn, type ExcelRow } from \"./excel\";\n\nexport type TableExcelColumnDescriptor = {\n\tid: string;\n\theader: string;\n\twidth?: number;\n\trole?: ColumnRole;\n\todataType?: ODataMetaType;\n\tclientOnly?: boolean;\n\tformattersPipeline?: FormattersPipelineConfig;\n};\n\nexport type ResolvedExcelTable = {\n\tcolumns: ExcelColumn[];\n\trows: ExcelRow[];\n\tcellStyles?: ReadonlyArray<Readonly<Record<string, ExcelCellStyle | undefined>>>;\n};\n\nexport type DownloadResolvedExcelTableArgs = {\n\tfileName: string;\n\ttable: ResolvedExcelTable;\n\tautoFilter?: boolean;\n};\n\n/** Один подготовленный табличный лист generic Excel-книги. */\nexport type ResolvedExcelWorkbookSheet = {\n\t/** Желаемое имя листа. */\n\tname: string;\n\t/** Подготовленные колонки, строки и optional стили ячеек. */\n\ttable: ResolvedExcelTable;\n\t/** Включить автофильтр для этого листа. По умолчанию включён. */\n\tautoFilter?: boolean;\n};\n\n/** Аргументы скачивания книги из нескольких подготовленных таблиц. */\nexport type DownloadResolvedExcelWorkbookArgs = {\n\t/** Имя скачиваемого `.xlsx` файла. */\n\tfileName: string;\n\t/** Табличные листы в порядке отображения в книге. */\n\tsheets: ResolvedExcelWorkbookSheet[];\n};\n\nconst INTEGER_EXCEL_FORMAT = \"#,##0\";\nconst DEFAULT_DECIMAL_EXCEL_FORMAT = \"#,##0.0\";\nconst EXCEL_DATE_FORMAT = \"dd.mm.yyyy\";\n/**\n * Excel-маски календарных пресетов сохраняют экранную детализацию:\n * month-* выводит день и месяц без года, а month-year-* — месяц и год без дня.\n */\nconst EXCEL_DAY_MONTH_SHORT_FORMAT = \"dd.mm\";\nconst EXCEL_DAY_MONTH_MEDIUM_FORMAT = \"d mmm\";\nconst EXCEL_DAY_MONTH_LONG_FORMAT = \"d mmmm\";\nconst EXCEL_MONTH_YEAR_MEDIUM_FORMAT = \"mmm yyyy\";\nconst EXCEL_MONTH_YEAR_LONG_FORMAT = \"mmmm yyyy\";\nconst EXCEL_TIME_FORMAT = \"hh:mm\";\nconst EXCEL_TIME_SECONDS_FORMAT = \"hh:mm:ss\";\nconst EXCEL_DATETIME_FORMAT = `${EXCEL_DATE_FORMAT} ${EXCEL_TIME_FORMAT}`;\nconst EXCEL_DATETIME_SECONDS_FORMAT = `${EXCEL_DATE_FORMAT} ${EXCEL_TIME_SECONDS_FORMAT}`;\n\nfunction resolveTypedValueFormatConfig(\n\tformattersPipeline: FormattersPipelineConfig | undefined\n): FormattersPipelineTypedValueFormatConfig | undefined {\n\tconst validation = validateFormattersPipelineConfig(formattersPipeline);\n\tconst typedValueFormatStep = validation.plan?.steps.find((step) => step.type === \"typedValueFormat\");\n\n\treturn typedValueFormatStep?.type === \"typedValueFormat\" ? typedValueFormatStep.config : undefined;\n}\n\nexport function resolveTableExcelColumnType(descriptor: TableExcelColumnDescriptor): ExcelColumn[\"type\"] {\n\tif (descriptor.clientOnly) return Number;\n\tif (isODataDateType(descriptor.odataType)) return Date;\n\tif (isODataBooleanType(descriptor.odataType)) return Boolean;\n\tif (isODataNumericType(descriptor.odataType)) return Number;\n\tif (descriptor.role === \"measure\") return Number;\n\treturn String;\n}\n\nfunction resolveNumberPresetName(descriptor: TableExcelColumnDescriptor): string {\n\tconst configuredPresetName = resolveTypedValueFormatConfig(descriptor.formattersPipeline)?.numberPresetName?.trim();\n\tif (configuredPresetName && getNumberPreset(configuredPresetName)) return configuredPresetName;\n\tif (isODataIntegerType(descriptor.odataType)) return \"integer\";\n\treturn \"decimal\";\n}\n\nfunction resolveExcelNumberFormat(descriptor: TableExcelColumnDescriptor): string {\n\tconst preset = getNumberPreset(resolveNumberPresetName(descriptor));\n\tif (!preset) return DEFAULT_DECIMAL_EXCEL_FORMAT;\n\treturn preset.decimals > 0 ? `#,##0.${\"0\".repeat(preset.decimals)}` : INTEGER_EXCEL_FORMAT;\n}\n\nfunction resolveDatePresetName(descriptor: TableExcelColumnDescriptor): string {\n\tconst configuredPresetName = resolveTypedValueFormatConfig(descriptor.formattersPipeline)?.datePresetName?.trim();\n\tif (configuredPresetName) return configuredPresetName;\n\tif (descriptor.odataType === \"time\") return DEFAULT_DATE_PRESET_NAMES.time;\n\tif (descriptor.odataType === \"datetime\" || descriptor.odataType === \"datetimeOffset\") return DEFAULT_DATE_PRESET_NAMES.datetime;\n\treturn DEFAULT_DATE_PRESET_NAMES.date;\n}\n\nfunction normalizeDatePatternForExcel(pattern: string): string {\n\treturn pattern.replace(/H/g, \"h\").replace(/M/g, \"m\");\n}\n\nfunction resolveBuiltinDatePresetExcelFormat(presetName: string): string | undefined {\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.odataDatetime || presetName === DEFAULT_DATE_PRESET_NAMES.abapDatetime) {\n\t\treturn EXCEL_DATETIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.odataDate || presetName === DEFAULT_DATE_PRESET_NAMES.abapDate) {\n\t\treturn EXCEL_DATE_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthShort) {\n\t\treturn EXCEL_DAY_MONTH_SHORT_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthMedium) {\n\t\treturn EXCEL_DAY_MONTH_MEDIUM_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthLong) {\n\t\treturn EXCEL_DAY_MONTH_LONG_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthYearMedium) {\n\t\treturn EXCEL_MONTH_YEAR_MEDIUM_FORMAT;\n\t}\n\n\tif (presetName === DEFAULT_DATE_PRESET_NAMES.monthYearLong) {\n\t\treturn EXCEL_MONTH_YEAR_LONG_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"datetime\")) {\n\t\treturn presetName === DEFAULT_DATE_PRESET_NAMES.datetime || presetName === DEFAULT_DATE_PRESET_NAMES.datetimeShort\n\t\t\t? EXCEL_DATETIME_FORMAT\n\t\t\t: EXCEL_DATETIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"time\")) {\n\t\treturn presetName === DEFAULT_DATE_PRESET_NAMES.time || presetName === DEFAULT_DATE_PRESET_NAMES.timeShort\n\t\t\t? EXCEL_TIME_FORMAT\n\t\t\t: EXCEL_TIME_SECONDS_FORMAT;\n\t}\n\n\tif (presetName.startsWith(\"date\")) return EXCEL_DATE_FORMAT;\n\n\treturn undefined;\n}\n\nfunction resolveExcelDateFormatFromPreset(presetName: string, preset: DateFormatPreset | undefined): string {\n\tconst builtinFormat = resolveBuiltinDatePresetExcelFormat(presetName);\n\tif (builtinFormat) return builtinFormat;\n\tif (preset?.pattern) return normalizeDatePatternForExcel(preset.pattern);\n\n\t// Настройка datePresetName исторически хранит и ручной шаблон. Если имени\n\t// нет в реестре, передаём шаблон в Excel как есть после нормализации токенов.\n\treturn normalizeDatePatternForExcel(presetName);\n}\n\nfunction resolveExcelDateFormat(descriptor: TableExcelColumnDescriptor): string {\n\tconst presetName = resolveDatePresetName(descriptor);\n\treturn resolveExcelDateFormatFromPreset(presetName, getDatePreset(presetName));\n}\n\nfunction resolveTableExcelColumnFormat(descriptor: TableExcelColumnDescriptor, type: ExcelColumn[\"type\"]): string | undefined {\n\tif (type === Number) return resolveExcelNumberFormat(descriptor);\n\tif (type === Date) return resolveExcelDateFormat(descriptor);\n\treturn undefined;\n}\n\nexport function createTableExcelColumn(descriptor: TableExcelColumnDescriptor): ExcelColumn {\n\tconst type = resolveTableExcelColumnType(descriptor);\n\treturn {\n\t\tid: descriptor.id,\n\t\theader: descriptor.header,\n\t\twidth: descriptor.width,\n\t\ttype,\n\t\tformat: resolveTableExcelColumnFormat(descriptor, type)\n\t};\n}\n\nexport async function downloadResolvedExcelTable({ fileName, table, autoFilter = true }: DownloadResolvedExcelTableArgs): Promise<void> {\n\tawait downloadExcelFile({\n\t\tfileName,\n\t\tcolumns: table.columns,\n\t\trows: table.rows,\n\t\tautoFilter,\n\t\tgetCellStyle: ({ rowIndex, column }) => table.cellStyles?.[rowIndex]?.[column.id]\n\t});\n}\n\n/** Скачивает многолистовую книгу из таблиц, уже подготовленных доменными consumers. */\nexport async function downloadResolvedExcelWorkbook({ fileName, sheets }: DownloadResolvedExcelWorkbookArgs): Promise<void> {\n\tawait downloadExcelWorkbook({\n\t\tfileName,\n\t\tsheets: sheets.map(({ name, table, autoFilter = true }) => ({\n\t\t\tname,\n\t\t\tcolumns: table.columns,\n\t\t\trows: table.rows,\n\t\t\tautoFilter,\n\t\t\tgetCellStyle: ({ rowIndex, column }) => table.cellStyles?.[rowIndex]?.[column.id]\n\t\t}))\n\t});\n}\n"],"mappings":";;;;;;AAyCA,IAAM,KAA0D;CAC/D,aAAa;CACb,SAAS;CACT,SAAS;CACT,OAAO;AACR,GAEM,KAAoB;CACzB,YAAY;CACZ,mBAAmB;CACnB,mBAAmB;AACpB,GAEM,IAA8B,IAC9B,KAAsC,8BACtC,IAAoC,YACpC,IAA8B;AAQpC,SAAgB,EAA+B,GAAsD;CACpG,IAAM,IAAY,IAAQ,GAAwB,KAAS,KAAA;CAC3D,OAAO,IAAY,EAAE,aAAU,IAAI,KAAA;AACpC;AAOA,SAAS,EAAmB,GAAuB;CAClD,IAAI,IAAQ,IAAQ,GAChB,IAAM;CAEV,OAAO,IAAQ,IAAG;EACjB,IAAM,KAAa,IAAQ,KAAK;EAEhC,AADA,IAAM,OAAO,aAAa,KAAK,CAAS,IAAI,GAC5C,IAAQ,KAAK,OAAO,IAAQ,KAAK,EAAE;CACpC;CAEA,OAAO;AACR;AAQA,SAAgB,EAAwB,GAAqB,GAAsC;CAC9F,WAAe,KAAK,KAAY,IAEpC,OAAO,MAAM,EAAmB,IAAc,CAAC,IAAI;AACpD;AAQA,SAAS,EAAqC,GAAyE;CACjH,MAAK,KAAK,OAAO,GAEtB,OAAO,EACN,OAAO,EACN,WAAW,EACV,+BAA+B,EAC9B,YAAY,GAAS,GAAU,MAAe;EAC7C,IAAM,IAAM,EAAK,EAAW;EAC5B,OAAO,IAAM,EAAQ,QAAQ,gBAAgB,gCAAgC,EAAI,IAAI,IAAI;CAC1F,EACD,EACD,EACD,EACD;AACD;AASA,SAAgB,EAAuB,GAAoC;CAC1E,IAAM,oBAAY,IAAI,IAAY,GAC5B,IAA0B,CAAC;CAEjC,KAAK,IAAI,IAAQ,GAAG,IAAQ,EAAM,QAAQ,KAAS,GAAG;EACrD,IAAM,IAAe,QAAQ,IAAQ,KAO/B,IANiB,EAAM,EAAM,EAChC,QAAQ,IAAqC,GAAG,CAAC,CAClD,QAAQ,GAA6B,GAAG,CAAC,CACzC,KAAK,CAAC,CACN,QAAQ,GAAmC,EAAE,CAAC,CAC9C,KAAK,KAC4B,GAC/B,IAAe,EAAS,MAAM,GAAG,CAA2B,GAC5D,IAAiB;EAErB,OAAO,EAAU,IAAI,EAAa,kBAAkB,CAAC,IAAG;GACvD,IAAM,IAAS,KAAK,EAAe;GAEnC,AADA,IAAe,GAAG,EAAS,MAAM,GAAG,IAA8B,EAAO,MAAM,IAAI,KACnF,KAAkB;EACnB;EAGA,AADA,EAAU,IAAI,EAAa,kBAAkB,CAAC,GAC9C,EAAc,KAAK,CAAY;CAChC;CAEA,OAAO;AACR;AAQA,SAAS,GAAmB,GAAgB,GAA2B;CAClE,SAAS,QAAQ,MAAU,IAE/B;MAAI,MAAS,QAAQ;GACpB,IAAM,IAAc,OAAO,KAAU,WAAW,IAAQ,OAAO,CAAK;GACpE,OAAO,OAAO,SAAS,CAAW,IAAI,IAAc,KAAA;EACrD;EAMA,OAJI,MAAS,SAAe,OAAO,CAAK,IACpC,MAAS,UAAgB,EAAQ,IACjC,MAAS,OAAa,aAAiB,OAAO,IAAS,EAAU,CAAK,KAAK,KAAA,IAExE;CANP;AAOD;AAQA,SAAgB,EAAoB,GAAyE;CAO5G,IAAM,IAAuB,CANH,EAAK,QAAQ,KAAK,OAAY;EACvD,MAAM;EACN,OAAO,EAAO;EACd,GAAG;CACJ,EAE8B,CAAS;CAEvC,KAAK,IAAI,IAAW,GAAG,IAAW,EAAK,KAAK,QAAQ,KAAY;EAC/D,IAAM,IAAM,EAAK,KAAK,IAChB,IAAmB,CAAC;EAE1B,KAAK,IAAI,IAAc,GAAG,IAAc,EAAK,QAAQ,QAAQ,KAAe;GAC3E,IAAM,IAAS,EAAK,QAAQ,IACtB,IAAQ,GAAmB,EAAI,EAAO,KAAK,EAAO,IAAI,GACtD,IAAQ,EAAK,eAAe;IAAE;IAAK;IAAQ;IAAU;GAAY,CAAC;GAExE,EAAS,KAAK;IACb;IACA,MAAM,EAAO;IACb,QAAQ,EAAO;IACf,GAAG;GACJ,CAAC;EACF;EAEA,EAAU,KAAK,CAAQ;CACxB;CAEA,OAAO;AACR;AAQA,eAAsB,EAAsB,GAAgD;CAC3F,IAAI,EAAK,OAAO,WAAW,GAC1B,MAAU,MAAM,sDAAsD;CAGvE,IAAM,IAAa,EAAuB,EAAK,OAAO,KAAK,MAAU,EAAM,IAAI,CAAC,GAC1E,IAA4C,CAAC,GAC7C,IAA2C,EAAK,OAAO,KAAK,GAAO,MAAU;EAClF,IAAM,IAAO,EAAoB,CAAK;EAGtC,OAFA,EAAe,KAAK,EAAM,aAAa,EAAwB,EAAM,QAAQ,QAAQ,EAAK,MAAM,IAAI,KAAA,CAAS,GAEtG;GACN;GACA,OAAO,EAAW;GAClB,SAAS,EAAM,QAAQ,KAAK,OAAY,EACvC,OAAO,EAAO,MACf,EAAE;EACH;CACD,CAAC,GACK,IAAoB,EAAiD,CAAc;CAEzF,MAAM,EAAc,GAAQ,EAC3B,UAAU,IAAoB,CAAC,CAAiB,IAAI,KAAA,EACrD,CAAC,CAAC,CAAC,OAAO,EAAK,QAAQ;AACxB;AAQA,eAAsB,EAAkB,GAA4C;CACnF,MAAM,EAAsB;EAC3B,UAAU,EAAK;EACf,QAAQ,CACP;GACC,MAAM;GACN,SAAS,EAAK;GACd,MAAM,EAAK;GACX,YAAY,EAAK;GACjB,cAAc,EAAK;EACpB,CACD;CACD,CAAC;AACF;;;ACrLA,IAAM,IAAwB;CAC7B,MAAM;CACN,OAAO;CACP,KAAK;CACL,QAAQ;CACR,QAAQ;CACR,QAAQ;AACT,GAEM,IAAa;CAAE,IAAI;CAAG,IAAI;AAAE;AAElC,SAAS,EAAuB,GAAuB;CACtD,OAAO,EAAe,EAAM,WAAW,KAAK,IAAI,CAAC;AAClD;AAEA,SAAS,EAAmB,GAAuB;CAClD,IAAI,IAAQ,GACR,IAAS;CACb,OAAO,IAAQ,IAAG;EACjB,IAAM,KAAa,IAAQ,KAAK;EAEhC,AADA,IAAS,OAAO,aAAa,KAAK,CAAS,IAAI,GAC/C,IAAQ,KAAK,OAAO,IAAQ,KAAK,EAAE;CACpC;CACA,OAAO;AACR;AAEA,SAAS,EAAsB,GAAe,GAAoB;CACjE,IAAI,CAAC,OAAO,UAAU,CAAK,KAAK,KAAS,GAAG,MAAU,MAAM,GAAG,EAAK,wCAAwC;AAC7G;AAEA,SAAS,EAAyB,GAAe,GAAoB;CACpE,IAAI,CAAC,OAAO,UAAU,CAAK,KAAK,IAAQ,GAAG,MAAU,MAAM,GAAG,EAAK,0CAA0C;AAC9G;AAEA,SAAS,EAAc,GAAkC,GAAoB;CAG5E,IAFA,EAAsB,EAAM,IAAI,GAAG,EAAK,SAAS,GACjD,EAAsB,EAAM,IAAI,GAAG,EAAK,QAAQ,GAC5C,EAAM,KAAK,EAAM,IAAI,MAAU,MAAM,GAAG,EAAK,oCAAoC;AACtF;AAEA,SAAS,EAAmB,GAA8B;CAKzD,AAHI,EAAM,oBAAoB,KAAA,KAAW,EAAyB,EAAM,iBAAiB,iBAAiB,GACtG,EAAM,qBAAqB,KAAA,KAAW,EAAyB,EAAM,kBAAkB,kBAAkB,GACzG,EAAM,cAAY,EAAc,EAAM,YAAY,YAAY,GAC9D,EAAM,iBAAe,EAAc,EAAM,eAAe,eAAe;CAC3E,KAAK,IAAM,KAAS,EAAM,qBAAqB,CAAC,GAAG,EAAsB,GAAO,mBAAmB;CACnG,KAAK,IAAM,KAAS,EAAM,kBAAkB,CAAC,GAAG,EAAsB,GAAO,gBAAgB;CAC7F,IAAI,EAAM,cACT,EAAsB,EAAM,UAAU,UAAU,oBAAoB,GACpE,EAAsB,EAAM,UAAU,SAAS,mBAAmB,GAClE,EAAsB,EAAM,UAAU,aAAa,uBAAuB,GAC1E,EAAsB,EAAM,UAAU,YAAY,sBAAsB,GACpE,EAAM,UAAU,WAAW,EAAM,UAAU,WAAW,EAAM,UAAU,cAAc,EAAM,UAAU,aACvG,MAAU,MAAM,kDAAkD;AAGrE;AAEA,SAAS,EACR,GACA,GACA,GACS;CACT,IAAI,CAAC,KAAU,CAAC,GAAQ,OAAO;CAC/B,IAAM,KAAgB,MAChB,IACW;EACf,EAAM,OAAO,SAAS,EAAuB,EAAM,IAAI,MAAM;EAC7D,EAAM,SAAS,SAAS,EAAuB,EAAM,MAAM,MAAM;EACjE,EAAM,QAAQ,SAAS,EAAuB,EAAM,KAAK,MAAM;EAC/D,EAAM,oBAAoB,oCAAoC;CAC/D,CAAC,CAAC,KAAK,EACA,IAPY,IASd,IAAY,EAAa,CAAM,GAC/B,IAAY,EAAa,CAAM;CACrC,OAAO,IAAI,EAAQ,GAAG,IAAY,cAAc,EAAU,gBAAgB,KAAK,IAAY,cAAc,EAAU,gBAAgB,GAAG,IAAI,EAAQ;AACnJ;AAEA,SAAS,EAAe,GAAoC,GAA+C;CAC1G,IAAI,CAAC,KAAU,EAAO,WAAW,GAAG,OAAO;CAC3C,IAAM,IAAe,CAAC,GAAG,IAAI,IAAI,CAAM,CAAC,CAAC,CAAC,MAAM,GAAM,MAAU,IAAO,CAAK;CAC5E,OAAO,IAAI,EAAQ,UAAU,EAAa,OAAO,sBAAsB,EAAa,OAAO,IAAI,EAC7F,KAAK,MAAU,YAAY,EAAM,gCAAgC,CAAC,CAClE,KAAK,EAAE,EAAE,IAAI,EAAQ;AACxB;AAEA,SAAS,EAAyB,GAAiB,GAAgC;CAClF,IAAM,IAAU,EAAM,WAAW,GAE3B,IADY,EAAM,oBAAoB,KAAA,KAAa,EAAM,qBAAqB,KAAA,IAChD,2EAAuE,IACrG,IAAS,IAAkB,EAAQ,QAAQ,eAAe,GAAG,EAAgB,YAAY,IAAI,GAY7F,IAAY;EACjB,sBAZyC,EAAQ,KAAK,WAAW,EAAQ,MAAM,SAAS,EAAQ,IAAI,YAAY,EAAQ,OAAO,YAAY,EAAQ,OAAO,YAAY,EAAQ,OAAO;EAarL,cAZ2B;GAC3B,cAAc,EAAW,EAAM,aAAa,MAAM;GAClD,gBAAgB,EAAM,YAAY;GAClC,EAAM,oBAAoB,KAAA,IAAsD,KAA1C,eAAe,EAAM,gBAAgB;GAC3E,EAAM,qBAAqB,KAAA,IAAwD,KAA5C,gBAAgB,EAAM,iBAAiB;GAC9E,EAAM,YAAY,cAAc,EAAM,UAAU,KAAK;GACrD;EACD,CAAC,CACC,OAAO,OAAO,CAAC,CACf,KAAK,GAGQ,EAAoB;EAClC,EAAqB,gBAAgB,EAAM,QAAQ,EAAM,MAAM;EAC/D,EAAe,aAAa,EAAM,cAAc;EAChD,EAAe,aAAa,EAAM,iBAAiB;CACpD,CAAC,CAAC,KAAK,EAAE,GACH,IAAe,EAAO,QAAQ,UAAU;CAC9C,OAAO,KAAgB,IACpB,GAAG,EAAO,MAAM,GAAG,CAAY,IAAI,IAAY,EAAO,MAAM,CAAY,MACxE,EAAO,QAAQ,gBAAgB,GAAG,EAAU,aAAa;AAC7D;AAEA,SAAS,EAAsB,GAAsB;CACpD,OAAO,IAAI,EAAK,WAAW,KAAK,IAAI,EAAE;AACvC;AAEA,SAAS,EAAkB,GAA+B,GAAqD;CAC9G,IAAM,IAAwB,CAAC;CAC/B,KAAK,IAAI,IAAa,GAAG,IAAa,EAAO,QAAQ,KAAc,GAAG;EACrE,IAAM,IAAQ,EAAO,EAAW,EAAE;EAClC,IAAI,CAAC,GAAO;EACZ,IAAM,IAAY,EAAsB,EAAW,MAAe,QAAQ,IAAa,GAAG,GACpF,IAAS,CACd,EAAM,aAAa,GAAG,EAAU,IAAI,EAAM,WAAW,GAAG,IAAI,EAAM,WAAW,OAAO,IACpF,EAAM,gBACH,GAAG,EAAU,IAAI,EAAmB,EAAM,cAAc,EAAE,EAAE,IAAI,EAAmB,EAAM,cAAc,EAAE,MACzG,EACJ,CAAC,CACC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAIV,IAHI,KACH,EAAY,KAAK,wDAAwD,EAAW,IAAI,EAAe,CAAM,EAAE,eAAe,GAE3H,EAAM,WAAW;GACpB,IAAM,EAAE,aAAU,YAAS,gBAAa,kBAAe,EAAM,WACvD,IAAO,GAAG,EAAU,IAAI,EAAmB,CAAW,EAAE,GAAG,EAAS,IAAI,EAAmB,CAAU,EAAE,GAAG;GAChH,EAAY,KAAK,sDAAsD,EAAW,IAAI,EAAe,CAAI,EAAE,eAAe;EAC3H;CACD;CACA,OAAO,EAAY,KAAK,EAAE;AAC3B;AAOA,SAAS,EAA0B,GAAiB,GAA8B;CAOjF,OANI,sBAAsB,KAAK,CAAO,IAC9B,EAAQ,QAAQ,uBAAuB,iBAAiB,EAAa,gBAAgB,IAEzF,EAAQ,SAAS,iBAAiB,IAC9B,EAAQ,QAAQ,mBAAmB,GAAG,EAAa,gBAAgB,IAEpE,EAAQ,QAAQ,aAAa,0BAA0B,EAAa,gBAAgB;AAC5F;AAGA,SAAS,EACR,GACA,GAC+C;CAC/C,IAAI,CAAC,EAAO,MAAM,EAAE,eAAY,CAAK,GAAG;CACxC,IAAM,IAAe,EAAkB,GAAY,CAAM;CACzD,OAAO,EACN,OAAO,EACN,WAAW;EACV,+BAA+B,EAC9B,YAAY,GAAS,GAAU,MAAe;GAC7C,IAAM,IAAQ,EAAO,EAAW,WAAW,EAAE;GAC7C,OAAO,IAAQ,EAAyB,GAAS,CAAK,IAAI;EAC3D,EACD;EACA,mBAAmB,EAClB,YAAY,MAAa,IAAe,EAA0B,GAAS,CAAY,IAAI,EAC5F;CACD,EACD,EACD;AACD;AAQA,eAAsB,EAA8B,GAA8C;CACjG,IAAI,EAAK,OAAO,WAAW,GAAG,MAAU,MAAM,sDAAsD;CACpG,IAAI,EAAK,aAAa;EACrB,IAAI,CAAC,EAAK,YAAY,WAAW,KAAK,GAAG,MAAU,MAAM,sDAAsD;EAC/G,IAAI,CAAC,OAAO,SAAS,EAAK,YAAY,QAAQ,KAAK,EAAK,YAAY,YAAY,GAC/E,MAAU,MAAM,qEAAqE;CAEvF;CACA,KAAK,IAAM,KAAS,EAAK,QAAQ;EAChC,IAAI,EAAM,KAAK,WAAW,GAAG,MAAU,MAAM,SAAS,EAAM,KAAK,oBAAoB;EACrF,AAAI,EAAM,SAAO,EAAmB,EAAM,KAAK;CAChD;CAEA,IAAM,IAAa,EAAuB,EAAK,OAAO,KAAK,EAAE,cAAW,CAAI,CAAC,GACvE,IAA2C,EAAK,OAAO,KAAK,GAAO,OAAW;EACnF,MAAM,EAAM,KAAK,KAAK,MAAQ,CAAC,GAAG,CAAG,CAAC;EACtC,OAAO,EAAW;EAClB,SAAS,EAAM,SAAS,KAAK,OAAY,EAAE,OAAO,EAAO,MAAM,EAAE;EACjE,eAAe,EAAM;EACrB,WAAW,EAAM;CAClB,EAAE,GACI,IAAe,EAAyB,GAAY,EAAK,MAAM;CACrE,OAAO,EAAc,GAAQ;EAC5B,YAAY,EAAK,aAAa;EAC9B,UAAU,EAAK,aAAa;EAC5B,UAAU,IAAe,CAAC,CAAY,IAAI,KAAA;CAC3C,CAAC,CAAC,CAAC,OAAO;AACX;AAGA,eAAsB,EAA4B,GAAkB,GAA8C;CACjH,IAAM,IAAO,MAAM,EAA8B,CAAI;CACrD,EAAqB,GAAU,CAAI;AACpC;;;AC/PA,IAAM,IAAuB,SACvB,IAA+B,WAC/B,IAAoB,cAKpB,IAA+B,SAC/B,KAAgC,SAChC,IAA8B,UAC9B,IAAiC,YACjC,IAA+B,aAC/B,IAAoB,SACpB,IAA4B,YAC5B,IAAwB,GAAG,EAAkB,GAAG,KAChD,IAAgC,GAAG,EAAkB,GAAG;AAE9D,SAAS,EACR,GACuD;CAEvD,IAAM,IADa,EAAiC,CACvB,CAAA,CAAW,MAAM,MAAM,MAAM,MAAS,EAAK,SAAS,kBAAkB;CAEnG,OAAO,GAAsB,SAAS,qBAAqB,EAAqB,SAAS,KAAA;AAC1F;AAEA,SAAgB,EAA4B,GAA6D;CAMxG,OALI,EAAW,aAAmB,SAC9B,EAAgB,EAAW,SAAS,IAAU,OAC9C,EAAmB,EAAW,SAAS,IAAU,UACjD,EAAmB,EAAW,SAAS,KACvC,EAAW,SAAS,YAAkB,SACnC;AACR;AAEA,SAAS,GAAwB,GAAgD;CAChF,IAAM,IAAuB,EAA8B,EAAW,kBAAkB,CAAC,EAAE,kBAAkB,KAAK;CAGlH,OAFI,KAAwB,EAAgB,CAAoB,IAAU,IACtE,EAAmB,EAAW,SAAS,IAAU,YAC9C;AACR;AAEA,SAAS,GAAyB,GAAgD;CACjF,IAAM,IAAS,EAAgB,GAAwB,CAAU,CAAC;CAElE,OADK,IACE,EAAO,WAAW,IAAI,SAAS,IAAI,OAAO,EAAO,QAAQ,MAAM,IADlD;AAErB;AAEA,SAAS,GAAsB,GAAgD;CAK9E,OAJ6B,EAA8B,EAAW,kBAAkB,CAAC,EAAE,gBAAgB,KAAK,MAE5G,EAAW,cAAc,SAAe,EAA0B,OAClE,EAAW,cAAc,cAAc,EAAW,cAAc,mBAAyB,EAA0B,WAChH,EAA0B;AAClC;AAEA,SAAS,EAA6B,GAAyB;CAC9D,OAAO,EAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,QAAQ,MAAM,GAAG;AACpD;AAEA,SAAS,GAAoC,GAAwC;CACpF,IAAI,MAAe,EAA0B,iBAAiB,MAAe,EAA0B,cACtG,OAAO;CAGR,IAAI,MAAe,EAA0B,aAAa,MAAe,EAA0B,UAClG,OAAO;CAGR,IAAI,MAAe,EAA0B,YAC5C,OAAO;CAGR,IAAI,MAAe,EAA0B,aAC5C,OAAO;CAGR,IAAI,MAAe,EAA0B,WAC5C,OAAO;CAGR,IAAI,MAAe,EAA0B,iBAC5C,OAAO;CAGR,IAAI,MAAe,EAA0B,eAC5C,OAAO;CAGR,IAAI,EAAW,WAAW,UAAU,GACnC,OAAO,MAAe,EAA0B,YAAY,MAAe,EAA0B,gBAClG,IACA;CAGJ,IAAI,EAAW,WAAW,MAAM,GAC/B,OAAO,MAAe,EAA0B,QAAQ,MAAe,EAA0B,YAC9F,IACA;CAGJ,IAAI,EAAW,WAAW,MAAM,GAAG,OAAO;AAG3C;AAEA,SAAS,GAAiC,GAAoB,GAA8C;CAO3G,OANsB,GAAoC,CACtD,MACA,GAAQ,UAAgB,EAA6B,EAAO,OAAO,IAIhE,EAA6B,CAAU;AAC/C;AAEA,SAAS,GAAuB,GAAgD;CAC/E,IAAM,IAAa,GAAsB,CAAU;CACnD,OAAO,GAAiC,GAAY,EAAc,CAAU,CAAC;AAC9E;AAEA,SAAS,GAA8B,GAAwC,GAA+C;CAC7H,IAAI,MAAS,QAAQ,OAAO,GAAyB,CAAU;CAC/D,IAAI,MAAS,MAAM,OAAO,GAAuB,CAAU;AAE5D;AAEA,SAAgB,GAAuB,GAAqD;CAC3F,IAAM,IAAO,EAA4B,CAAU;CACnD,OAAO;EACN,IAAI,EAAW;EACf,QAAQ,EAAW;EACnB,OAAO,EAAW;EAClB;EACA,QAAQ,GAA8B,GAAY,CAAI;CACvD;AACD;AAEA,eAAsB,GAA2B,EAAE,aAAU,UAAO,gBAAa,MAAuD;CACvI,MAAM,EAAkB;EACvB;EACA,SAAS,EAAM;EACf,MAAM,EAAM;EACZ;EACA,eAAe,EAAE,aAAU,gBAAa,EAAM,aAAa,EAAS,GAAG,EAAO;CAC/E,CAAC;AACF;AAGA,eAAsB,GAA8B,EAAE,aAAU,aAA4D;CAC3H,MAAM,EAAsB;EAC3B;EACA,QAAQ,EAAO,KAAK,EAAE,SAAM,UAAO,gBAAa,UAAY;GAC3D;GACA,SAAS,EAAM;GACf,MAAM,EAAM;GACZ;GACA,eAAe,EAAE,aAAU,gBAAa,EAAM,aAAa,EAAS,GAAG,EAAO;EAC/E,EAAE;CACH,CAAC;AACF"}
|
package/dist/formatters/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, G as p, Gt as m, H as h, Ht as g, I as _, It as v, J as y, K as b, Kt as x, L as S, Lt as C, M as w, Mt as T, N as E, Nt as D, O, Ot as k, P as A, Pt as j, Q as M, R as N, Rt as P, S as F, St as I, T as L, Tt as R, U as z, Ut as B, V, Vt as H, W as U, Wt as W, X as G, Y as K, Z as q, _ as J, _t as Y, a as X, at as Z, b as Q, bt as $, c as ee, ct as te, d as ne, dt as re, et as ie, f as ae, ft as oe, g as se, gt as ce, h as le, ht as ue, i as de, it as fe, j as pe, jt as me, k as he, kt as ge, l as _e, lt as ve, m as ye, mt as be, n as xe, nt as Se, o as Ce, ot as we, p as Te, pt as Ee, q as De, r as Oe, rt as ke, s as Ae, st as je, t as Me, tt as Ne, u as Pe, ut as Fe, v as Ie, vt as Le, w as Re, wt as ze, x as Be, xt as Ve, y as He, yt as Ue, z as We, zt as Ge } from "../chunks/formatters-
|
|
1
|
+
import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, G as p, Gt as m, H as h, Ht as g, I as _, It as v, J as y, K as b, Kt as x, L as S, Lt as C, M as w, Mt as T, N as E, Nt as D, O, Ot as k, P as A, Pt as j, Q as M, R as N, Rt as P, S as F, St as I, T as L, Tt as R, U as z, Ut as B, V, Vt as H, W as U, Wt as W, X as G, Y as K, Z as q, _ as J, _t as Y, a as X, at as Z, b as Q, bt as $, c as ee, ct as te, d as ne, dt as re, et as ie, f as ae, ft as oe, g as se, gt as ce, h as le, ht as ue, i as de, it as fe, j as pe, jt as me, k as he, kt as ge, l as _e, lt as ve, m as ye, mt as be, n as xe, nt as Se, o as Ce, ot as we, p as Te, pt as Ee, q as De, r as Oe, rt as ke, s as Ae, st as je, t as Me, tt as Ne, u as Pe, ut as Fe, v as Ie, vt as Le, w as Re, wt as ze, x as Be, xt as Ve, y as He, yt as Ue, z as We, zt as Ge } from "../chunks/formatters-CEltML3y.js";
|
|
2
2
|
import { _ as Ke, b as qe, g as Je, v as Ye, x as Xe, y as Ze } from "../chunks/array-D-_yMMUK.js";
|
|
3
3
|
import { $t as Qe, An as $e, Bt as et, Cn as tt, Dn as nt, En as rt, Fn as it, Gt as at, Ht as ot, It as st, Jt as ct, Kt as lt, Lt as ut, Mn as dt, Nn as ft, On as pt, Pn as mt, Qt as ht, Rt as gt, Sn as _t, Tn as vt, Ut as yt, Vt as bt, Wt as xt, Xt as St, Yt as Ct, Zt as wt, _n as Tt, an as Et, bn as Dt, cn as Ot, dn as kt, en as At, fn as jt, gn as Mt, hn as Nt, in as Pt, jn as Ft, kn as It, ln as Lt, mn as Rt, nn as zt, on as Bt, pn as Vt, qt as Ht, rn as Ut, sn as Wt, tn as Gt, un as Kt, vn as qt, wn as Jt, xn as Yt, yn as Xt, zt as Zt } from "../chunks/odata-service-CWkHASdw.js";
|
|
4
|
-
import { _ as Qt, a as $t, b as en, c as tn, d as nn, f as rn, g as an, h as on, i as sn, l as cn, m as ln, n as un, o as dn, p as fn, r as pn, s as mn, t as hn, u as gn, v as _n, x as vn, y as yn } from "../chunks/validate-
|
|
4
|
+
import { _ as Qt, a as $t, b as en, c as tn, d as nn, f as rn, g as an, h as on, i as sn, l as cn, m as ln, n as un, o as dn, p as fn, r as pn, s as mn, t as hn, u as gn, v as _n, x as vn, y as yn } from "../chunks/validate-M2zPiLXn.js";
|
|
5
5
|
export { Xt as DATE_FORMAT_DEFAULTS, Dt as DATE_FORMAT_STYLE_PRESET_NAMES, $e as DATE_PATTERN_DATE_TOKENS, Ft as DATE_PATTERN_TIME_TOKENS, dt as DATE_PATTERN_TOKENS, ft as DATE_PATTERN_TOKEN_RE, Yt as DEFAULT_DATE_PRESET_NAMES, an as DEFAULT_NUMBER_PRESET_NAMES, xe as DEFAULT_VALUE_STATES, Qt as NUMBER_FORMAT_DEFAULTS, Oe as VALUE_STATE_COLOR_TOKENS, u as addCalendarDays, c as addCalendarMonths, k as addCalendarYears, O as clearFormatCache, v as clearPhone, ue as cloneDate, cn as cloneFormatterPipelinePlanStep, gn as cloneFormatterPipelinePlanStepToNode, nn as cloneFormattersPipelineConfig, rn as cloneNormalizeLeadingZerosConfig, fn as cloneResolveValueStateConfig, ln as cloneRowBasedOverrideConfig, on as cloneTypedValueFormatConfig, Re as collectFormattersPipelineDependencyIds, L as collectRuntimeFieldDependencyIds, ne as compileFormattersPipelineExecutor, de as compileFormattersPipelineRuntime, X as compileFormattersPipelineRuntimeFields, J as computeShortHash, sn as configureRowBasedFormatterRegistry, ce as countCalendarDaysInDateRange, ge as createCalendarDate, Ne as createEducationDateTime, le as createFixedResolver, tn as createRowBasedFormatterContext, $t as createRowBasedFormatterRegistry, Te as createThresholdResolver, Ie as findResolverByCanonicalKey, et as formatDate, bt as formatDateAsAbapDate, ot as formatDateAsAbapDatetime, yt as formatDateAsAbapMonth, xt as formatDateAsAbapYear, at as formatDateAsDate, lt as formatDateAsDateLong, Ht as formatDateAsDateMedium, ct as formatDateAsDateShort, Ct as formatDateAsDateTime, St as formatDateAsDatetimeLong, wt as formatDateAsDatetimeMedium, ht as formatDateAsDatetimeShort, Qe as formatDateAsMonthLong, At as formatDateAsMonthMedium, Gt as formatDateAsMonthShort, zt as formatDateAsMonthYearLong, Ut as formatDateAsMonthYearMedium, Pt as formatDateAsODataDate, Et as formatDateAsODataDatetime, Bt as formatDateAsODataTime, Wt as formatDateAsTime, Ot as formatDateAsTimeLong, Lt as formatDateAsTimeMedium, Kt as formatDateAsTimeSeconds, kt as formatDateAsTimeShort, jt as formatDateRange, Se as formatEducationDateKey, ke as formatEducationTimeKey, P as formatFullName, ie as formatInstantDateTime, he as formatNumber, t as formatNumberAsCurrency, pe as formatNumberAsCurrencyOrEmpty, w as formatNumberAsDecimal, E as formatNumberAsDecimal2, A as formatNumberAsDecimal2OrEmpty, d as formatNumberAsDecimal3, _ as formatNumberAsDecimal3OrEmpty, S as formatNumberAsDecimalOrEmpty, N as formatNumberAsInteger, We as formatNumberAsIntegerOrEmpty, r as formatNumberAsPercent, V as formatNumberAsPercentOrEmpty, h as formatNumberAsPrice, z as formatNumberAsPriceOrEmpty, U as formatNumberAsTonnage, p as formatNumberAsTonnageOrEmpty, C as formatPhone, Ce as formatPipelineDisplayValue, m as formatRussianPlural, Ge as formatShortName, ae as formatTypedCellValue, o as getCalendarPeriod, fe as getCurrentEducationWeekKey, _t as getDatePreset, tt as getDatePresetNames, Z as getEducationWeekDays, we as getEducationWeekRange, je as getEducationWeekStartDate, te as getEducationYear, ve as getEducationYearRange, n as getEndOfDay, ee as getFormattersPipelineDefinitionById, _e as getFormattersPipelineDefinitions, _n as getNumberPreset, yn as getNumberPresetNames, dn as getRowBasedFormatterById, mn as getRowBasedFormatterList, me as getStartOfDay, T as getStartOfMonth, D as getStartOfWeek, j as getStartOfYear, He as getValueStateResolver, Q as getValueStateResolverIds, ze as isCalendarPeriodWithinDateBounds, Fe as isCurrentEducationWeek, Jt as isDateFormatPrecision, vt as isDateFormatStyle, R as isDateInsideCalendarPeriod, Y as isDateRangeTuple, re as isEducationDateInCurrentYear, st as isPositiveValue, f as isSameCalendarDay, Le as isValidDate, De as isZeroValue, Ue as normalizeDateRange, y as normalizeDateRangeReferenceDate, oe as normalizeEducationWeekKey, Pe as normalizeFormattersPipelineConfig, l as normalizeLeadingZeros, s as normalizeLeadingZerosStrict, Je as normalizeRequiredText, Ke as normalizeText, Ye as normalizeTextSpaces, Ze as normalizeTextToLower, qe as normalizeTextWithFallback, $ as orderDates, mt as parseAbapBoolean, it as parseBoolean, Vt as parseDate, Rt as parseDateByFormat, Nt as parseDateByPattern, Mt as parseDateTZ, Tt as parseDateValue, qt as parseDateValueTZ, Ee as parseEducationDateKey, ut as parseNumber, rt as registerDatePreset, se as registerFixedResolver, en as registerNumberPreset, Be as registerResolver, ye as registerThresholdResolver, Ae as rekeyFormattersPipelineConfig, Ve as requireDateRange, nt as resetDatePresets, vn as resetNumberPresets, F as resetValueStateResolvers, pt as resolveDateFormatName, It as resolveDateFormatPreset, I as resolveDateRangePair, be as resolveEducationWeekKey, K as resolveMonthAgoRange, G as resolveMonthStartToTodayRange, q as resolveMonthStartToYesterdayRange, M as resolveTodayRange, a as resolveValueState, Me as resolveValueStateClassName, e as resolveYesterdayRange, x as selectRussianPluralForm, W as startsWithIgnoringZeros, B as stripInnerSpaces, g as stripLeadingZeros, i as toAbapBoolean, gt as toFiniteNumber, Zt as toPositiveInteger, Xe as toSafeString, b as truncateNumber, H as truncateText, hn as validateFormattersPipelineConfig, un as validateFormattersPipelineGraph, pn as validateFormattersPipelinePlan };
|
|
@@ -52,8 +52,8 @@ export declare function useFloatingListbox<T>({ options, selectedIndex, open, on
|
|
|
52
52
|
} & import("@floating-ui/react").ExtendedRefs<import("@floating-ui/react").ReferenceType>;
|
|
53
53
|
context: {
|
|
54
54
|
x: number;
|
|
55
|
-
y: number;
|
|
56
55
|
update: () => void;
|
|
56
|
+
y: number;
|
|
57
57
|
placement: Placement;
|
|
58
58
|
strategy: import("@floating-ui/utils").Strategy;
|
|
59
59
|
middlewareData: import("@floating-ui/core").MiddlewareData;
|
|
@@ -14,8 +14,8 @@ export declare function odataParseValue(type: ODataDateType, value: unknown): Da
|
|
|
14
14
|
export declare function odataParseValue(type: ODataStringType, value: unknown): string | unknown;
|
|
15
15
|
export declare function odataParseValue(type: ODataMetaType, value: unknown): string | unknown;
|
|
16
16
|
export declare function odataParseValueByMetadata(descriptor: ODataMetadataValueDescriptor, value: unknown): unknown;
|
|
17
|
-
export declare function defaultODataTypeValue(type: ODataMetaType): false |
|
|
18
|
-
export declare function defaultControlTypeValue(type: BaseMetaType): false |
|
|
17
|
+
export declare function defaultODataTypeValue(type: ODataMetaType): false | "" | 0 | Date;
|
|
18
|
+
export declare function defaultControlTypeValue(type: BaseMetaType): false | "" | 0 | Date;
|
|
19
19
|
export declare function createFormatter<S extends z.ZodArray>(schema: S, fn: (...args: [...z.infer<S>]) => string): (...args: unknown[]) => string;
|
|
20
20
|
export declare function createBooleanFormatter(fn: (...args: boolean[]) => string): (...args: unknown[]) => string;
|
|
21
21
|
export declare function createStringFormatter(fn: (...args: string[]) => string): (...args: unknown[]) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuzaki13/react-foundation-lib",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryuzaki13",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"LICENSE"
|
|
34
34
|
],
|
|
35
35
|
"exports": {
|
|
36
|
+
"./analytical-table": {
|
|
37
|
+
"types": "./dist/analytical-table/index.d.ts",
|
|
38
|
+
"import": "./dist/analytical-table/index.js"
|
|
39
|
+
},
|
|
36
40
|
"./array": {
|
|
37
41
|
"types": "./dist/array/index.d.ts",
|
|
38
42
|
"import": "./dist/array/index.js"
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Analytical Table: чистая модель полного снимка
|
|
2
|
+
|
|
3
|
+
Модуль `@ryuzaki13/react-foundation-lib/analytical-table` предоставляет вычислительный
|
|
4
|
+
слой аналитической таблицы. Он подходит для SSR и браузера, не использует React hooks,
|
|
5
|
+
QueryClient, serverFn, OData-запросы, SAP metadata, DOM или browser storage. Загрузкой, ошибками,
|
|
6
|
+
отменой и авторизацией владеет транспорт приложения. Результат запроса передаётся
|
|
7
|
+
как неизменяемый `AnalyticalSnapshot`; дополнительных запросов при группировке,
|
|
8
|
+
сортировке или раскрытии нет.
|
|
9
|
+
|
|
10
|
+
## Контракт и владельцы состояния
|
|
11
|
+
|
|
12
|
+
`AnalyticalSnapshot<T>` содержит `rows`. Каждая `AnalyticalDataRow<T>` содержит
|
|
13
|
+
стабильный `id`, необязательный `parentId`, значения `values`, исходную запись
|
|
14
|
+
`original` и необязательный предметный `kind`. Нельзя использовать номер позиции
|
|
15
|
+
строки как идентификатор: сортировка и обновление снимка должны сохранять выбор и
|
|
16
|
+
раскрытие. Runtime не изменяет исходные строки и не копирует предметные DTO
|
|
17
|
+
рекурсивно: `original` остаётся ссылкой на неизменяемую запись потребителя.
|
|
18
|
+
|
|
19
|
+
`AnalyticalColumn<T>` задаёт идентификатор, подпись, тип значения, необязательные
|
|
20
|
+
сравнение, фильтр, агрегацию и вычисление. В столбцах должны присутствовать также
|
|
21
|
+
скрытые зависимости формул. Ключи и отображаемые поля группировки могут ссылаться
|
|
22
|
+
прямо на поля values, не создавая технических столбцов интерфейса. Видимость, ширина,
|
|
23
|
+
закрепление и порядок столбцов относятся к UI и не должны пересчитывать модель.
|
|
24
|
+
Форматирование значений выполняется существующим `/formatters` на границе UI или
|
|
25
|
+
экспорта; runtime сохраняет числа числами.
|
|
26
|
+
|
|
27
|
+
`AnalyticalViewState` содержит группировки, сортировку, фильтры и идентификаторы
|
|
28
|
+
раскрытых строк. Оно сериализуемо, но не является загружаемыми данными.
|
|
29
|
+
`AnalyticalTableModel` содержит дерево `rows`, все строки `flatRows`, видимые строки
|
|
30
|
+
`visibleRows`, индекс `rowById`, общие итоги `grandTotals` и диагностику иерархии.
|
|
31
|
+
`Map` результата вычисляется локально: передавать его через serverFn не требуется.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {
|
|
35
|
+
buildAnalyticalTableModel,
|
|
36
|
+
projectAnalyticalTableExpansion,
|
|
37
|
+
type AnalyticalSnapshot
|
|
38
|
+
} from "@ryuzaki13/react-foundation-lib/analytical-table";
|
|
39
|
+
|
|
40
|
+
const snapshot: AnalyticalSnapshot = {
|
|
41
|
+
rows: [
|
|
42
|
+
{ id: "order:1", values: { customer: "Компания А", amount: 100 } },
|
|
43
|
+
{ id: "line:1", parentId: "order:1", values: { amount: 100 } }
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
const model = buildAnalyticalTableModel({
|
|
47
|
+
snapshot,
|
|
48
|
+
columns: [
|
|
49
|
+
{ id: "customer", kind: "dimension" },
|
|
50
|
+
{ id: "amount", kind: "measure", aggregate: "sum" }
|
|
51
|
+
],
|
|
52
|
+
state: {
|
|
53
|
+
grouping: [{ id: "customer", keyColumnIds: ["customer"] }],
|
|
54
|
+
sorting: [{ id: "amount", desc: true }]
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const expanded = projectAnalyticalTableExpansion(model, "all");
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Иерархия и группировки
|
|
61
|
+
|
|
62
|
+
Построение исходной иерархии использует `/tree-table`. Повторяющиеся идентификаторы,
|
|
63
|
+
сироты и циклы диагностируются по правилам этого модуля. Идентификатор строки не
|
|
64
|
+
может быть пустым. Дополнительная группировка объединяет только корневые ветви:
|
|
65
|
+
исходные дети остаются под своими родителями. Это позволяет сохранять обязательный
|
|
66
|
+
предметный уровень и одновременно группировать его по нескольким признакам.
|
|
67
|
+
|
|
68
|
+
`AnalyticalGroupingLevel` задаёт `id`, составной `keyColumnIds` и отдельные
|
|
69
|
+
`displayColumnIds`. Идентификатор группы зависит от всего пути ключей и типов
|
|
70
|
+
значений, а не от подписи или порядка строк. Число, строка, null и undefined
|
|
71
|
+
различаются. Отображаемые поля берутся из первой строки группы: потребитель обязан
|
|
72
|
+
обеспечить, что они однозначно определяются ключом. Уровень не может иметь пустой
|
|
73
|
+
список ключей. Повторяющиеся уровни отклоняются; сортировка и фильтры требуют
|
|
74
|
+
известных дескрипторов столбцов.
|
|
75
|
+
|
|
76
|
+
Сортировка применяется между соседними узлами на каждом уровне; она не отделяет
|
|
77
|
+
детей от родителей. При равенстве сохраняется исходный порядок. Строковое сравнение
|
|
78
|
+
использует `/string-comparison`, календарные значения — `/formatters`. Для иной
|
|
79
|
+
семантики столбец задаёт `compare`.
|
|
80
|
+
|
|
81
|
+
Фильтры поддерживают equals, notEquals, contains, startsWith, in, between, gt, gte,
|
|
82
|
+
lt, lte, empty и notEmpty; текстовый поиск не зависит от регистра. Условия разных
|
|
83
|
+
столбцов объединяются через AND. Фильтрация работает над исходными вычисленными
|
|
84
|
+
строками **до** дополнительных группировок: совпавший родитель сохраняет ветвь
|
|
85
|
+
целиком, совпавший ребёнок сохраняет путь к нему. Для предметной семантики есть
|
|
86
|
+
`column.filter`.
|
|
87
|
+
|
|
88
|
+
## Итоги и зернистость
|
|
89
|
+
|
|
90
|
+
По умолчанию агрегируются только исходные корневые строки. Дочерняя детализация не
|
|
91
|
+
удваивает их меры. `isAggregateRow` позволяет явно выбрать другую зернистость.
|
|
92
|
+
`column.aggregate` поддерживает sum, min, max, avg, count и callback. Числовые
|
|
93
|
+
агрегации принимают только конечные числа; строковое преобразование внешних DTO
|
|
94
|
+
выполняется в адаптере источника. Пустое числовое множество возвращает null;
|
|
95
|
+
count считает непустые значения и возвращает 0 для пустого множества.
|
|
96
|
+
|
|
97
|
+
`aggregate(context)` возвращает значения групповых/общих итогов и может переопределить
|
|
98
|
+
результаты стандартных агрегаций. Контекст содержит `scope`, `groupingPath` и только
|
|
99
|
+
исходные строки выбранной зернистости. Например, доля должна вычисляться как
|
|
100
|
+
отношение сумм числителя и знаменателя, если предметный учёт требует взвешенного
|
|
101
|
+
результата. Библиотека не усредняет проценты вместо этого правила.
|
|
102
|
+
|
|
103
|
+
`grandTotalsScope: "filtered"` (по умолчанию) учитывает оставшиеся ветви, `"all"`
|
|
104
|
+
сохраняет общие итоги полного снимка при визуальной фильтрации. `grandTotals: false`
|
|
105
|
+
отключает их вычисление. Раскрытие и выбор строк общие итоги не меняют.
|
|
106
|
+
|
|
107
|
+
## Формулы и зависимые вычисления
|
|
108
|
+
|
|
109
|
+
`createAnalyticalCalculationPlan(columns, requiredColumnIds?)` проверяет уникальность
|
|
110
|
+
столбцов, зависимости и циклы, затем строит топологический порядок. `formula`
|
|
111
|
+
содержит `formulaId` и упорядоченные `dependencies`: исполнение использует уже
|
|
112
|
+
существующий реестр `/formulas`, который настраивает composition root приложения.
|
|
113
|
+
`calculate` содержит зависимости и чистый `compute(values)` для вычислений,
|
|
114
|
+
не представленных реестром. Одновременно задать formula и calculate нельзя.
|
|
115
|
+
|
|
116
|
+
`calculateAnalyticalValues` возвращает отдельные значения. План компилируется один
|
|
117
|
+
раз при построении модели и применяется к данным, затем к рассчитанным итогам.
|
|
118
|
+
Явная агрегация колонки, результат `aggregate(context)` и значения пути группировки
|
|
119
|
+
имеют приоритет над построчной формулой этой колонки. Зависимые формулы читают
|
|
120
|
+
готовые итоги: сумма произведений сохраняется, а отношение сумм вычисляется заново.
|
|
121
|
+
Вычисление должно учитывать отсутствующие значения в итоговой строке. Ошибки
|
|
122
|
+
конфигурации и выполнения формул не скрываются успешным нулём.
|
|
123
|
+
|
|
124
|
+
## Динамические календарные столбцы
|
|
125
|
+
|
|
126
|
+
`createAnalyticalDateColumnsPlan` строит day/week/month/quarter/year buckets с
|
|
127
|
+
включительными `from`/`to`; неделя начинается в понедельник. Идентификатор содержит
|
|
128
|
+
гранулярность и календарное начало bucket, а крайние интервалы обрезаются диапазоном.
|
|
129
|
+
По умолчанию допустимо не более 366 столбцов; превышение лимита приводит к ошибке.
|
|
130
|
+
|
|
131
|
+
Границы диапазона и поле даты каждого факта имеют контракт **YYYY-MM-DD**.
|
|
132
|
+
`Date`, числа и ISO timestamp с временем не принимаются как календарные факты.
|
|
133
|
+
Их приведение с явной timezone-политикой выполняет адаптер до передачи в runtime.
|
|
134
|
+
Неверные границы — ошибка; неверные факты и факты вне диапазона пропускаются.
|
|
135
|
+
|
|
136
|
+
`projectAnalyticalDateFacts({ rows, facts, plan })` сопоставляет факты по составному
|
|
137
|
+
`identityColumnIds`, добавляя ячейки к копиям значений. Не меняются количество,
|
|
138
|
+
порядок и идентичность исходных строк. Факты с отсутствующей частью ключа
|
|
139
|
+
пропускаются. Без фактов ячейка равна null; существующее одноимённое поле не
|
|
140
|
+
перезаписывается молча. Это отдельная проекция уже загруженных данных, не схема
|
|
141
|
+
дополнительной загрузки.
|
|
142
|
+
|
|
143
|
+
`aggregateAnalyticalDateFacts` считает общие значения по исходным фактам. Особенно
|
|
144
|
+
важно использовать его для avg: среднее от средних разных строк обычно неверно.
|
|
145
|
+
Групповые итоги для таких данных следует считать тем же способом по соответствующему
|
|
146
|
+
набору исходных фактов через aggregate callback. Факты с неполным ключом исключаются
|
|
147
|
+
и из строк, и из итогов. Область переданного набора фактов определяет потребитель.
|
|
148
|
+
|
|
149
|
+
## Экспорт и производительность
|
|
150
|
+
|
|
151
|
+
`projectAnalyticalTableExport` принимает модель, упорядоченные столбцы с необязательным
|
|
152
|
+
`format(value, row)`, область all/visible/selected, `includeGroups` и
|
|
153
|
+
`includeGrandTotals`. Результат содержит заголовки и массивы значений; дальнейшее
|
|
154
|
+
создание XLSX или документа принадлежит `/excel` или `/table-document`. По умолчанию
|
|
155
|
+
экспортируются все строки отфильтрованной модели, включая свёрнутые ветви.
|
|
156
|
+
Предметные строки-родители не считаются синтетическими группами. Общие итоги
|
|
157
|
+
форматируются отдельным `formatTotal(value)`: у них нет исходной строки `original`.
|
|
158
|
+
Без него итоги сохраняют исходные значения, даже если форматтер строк задан отдельно.
|
|
159
|
+
|
|
160
|
+
Для React следует мемоизировать базовую модель по `snapshot.rows`, колонкам,
|
|
161
|
+
группировкам, сортировке, фильтрам и правилам итогов. Время последнего запроса не
|
|
162
|
+
является зависимостью, когда данные структурно не изменились. Затем отдельно
|
|
163
|
+
вызывается `projectAnalyticalTableExpansion(model, expandedRowIds)`; раскрытие не
|
|
164
|
+
повторяет формулы, группировки и агрегации. Selection и layout остаются в UI.
|
|
165
|
+
|
|
166
|
+
Полный снимок не требует LRU страниц, sparse placeholders, remote paging или
|
|
167
|
+
второго кэша персональных данных. Если отдельный источник позднее потребует
|
|
168
|
+
постраничную серверную агрегацию, она оформляется отдельной транспортной
|
|
169
|
+
возможностью без изменения предметных правил существующего снимка.
|