@ifto/mantine-kit 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +471 -2
- package/dist/component/atom/AppMain/App.module.js +7 -0
- package/dist/component/atom/AppMain/App.module.js.map +1 -0
- package/dist/component/atom/AppMain/AppMain.d.ts +7 -0
- package/dist/component/atom/AppMain/AppMain.js +14 -0
- package/dist/component/atom/AppMain/AppMain.js.map +1 -0
- package/dist/component/atom/CancelEditButton/CancelEditButton.d.ts +6 -0
- package/dist/component/atom/CancelEditButton/CancelEditButton.js +24 -0
- package/dist/component/atom/CancelEditButton/CancelEditButton.js.map +1 -0
- package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.d.ts +13 -0
- package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.js +30 -0
- package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.js.map +1 -0
- package/dist/component/atom/DateField/DateField.d.ts +7 -0
- package/dist/component/atom/DateField/DateField.js +21 -0
- package/dist/component/atom/DateField/DateField.js.map +1 -0
- package/dist/component/atom/EditValueButton/EditValueButton.d.ts +6 -0
- package/dist/component/atom/EditValueButton/EditValueButton.js +24 -0
- package/dist/component/atom/EditValueButton/EditValueButton.js.map +1 -0
- package/dist/component/atom/ErrorAlert/ErrorAlert.d.ts +8 -0
- package/dist/component/atom/ErrorAlert/ErrorAlert.js +21 -0
- package/dist/component/atom/ErrorAlert/ErrorAlert.js.map +1 -0
- package/dist/component/atom/Surface/Surface.d.ts +20 -0
- package/dist/component/atom/Surface/Surface.js +15 -0
- package/dist/component/atom/Surface/Surface.js.map +1 -0
- package/dist/component/molecule/Breadcrumb/Breadcrumb.d.ts +12 -0
- package/dist/component/molecule/Breadcrumb/Breadcrumb.js +38 -0
- package/dist/component/molecule/Breadcrumb/Breadcrumb.js.map +1 -0
- package/dist/component/molecule/ExportMenuButton/ExportMenuButton.d.ts +25 -0
- package/dist/component/molecule/ExportMenuButton/ExportMenuButton.js +49 -0
- package/dist/component/molecule/ExportMenuButton/ExportMenuButton.js.map +1 -0
- package/dist/component/molecule/PaperCard/PaperCard.d.ts +7 -0
- package/dist/component/molecule/PaperCard/PaperCard.js +17 -0
- package/dist/component/molecule/PaperCard/PaperCard.js.map +1 -0
- package/dist/component/molecule/PrintSection/PrintSection.d.ts +14 -0
- package/dist/component/molecule/PrintSection/PrintSection.js +29 -0
- package/dist/component/molecule/PrintSection/PrintSection.js.map +1 -0
- package/dist/component/molecule/SelectField/SelectField.d.ts +32 -0
- package/dist/component/molecule/SelectField/SelectField.js +38 -0
- package/dist/component/molecule/SelectField/SelectField.js.map +1 -0
- package/dist/component/molecule/StatsCard/Stats.module.js +15 -0
- package/dist/component/molecule/StatsCard/Stats.module.js.map +1 -0
- package/dist/component/molecule/StatsCard/StatsCard.d.ts +18 -0
- package/dist/component/molecule/StatsCard/StatsCard.js +60 -0
- package/dist/component/molecule/StatsCard/StatsCard.js.map +1 -0
- package/dist/component/organism/DataForm/DataForm.d.ts +26 -0
- package/dist/component/organism/DataForm/DataForm.js +83 -0
- package/dist/component/organism/DataForm/DataForm.js.map +1 -0
- package/dist/component/organism/DataTabList/DataTabList.d.ts +24 -0
- package/dist/component/organism/DataTabList/DataTabList.js +37 -0
- package/dist/component/organism/DataTabList/DataTabList.js.map +1 -0
- package/dist/component/organism/DataTabList/DataTabList.module.js +13 -0
- package/dist/component/organism/DataTabList/DataTabList.module.js.map +1 -0
- package/dist/component/organism/EChart/EChart.d.ts +47 -0
- package/dist/component/organism/EChart/EChart.js +37 -0
- package/dist/component/organism/EChart/EChart.js.map +1 -0
- package/dist/component/organism/EChart/buildEChartOption.d.ts +40 -0
- package/dist/component/organism/EChart/buildEChartOption.js +57 -0
- package/dist/component/organism/EChart/buildEChartOption.js.map +1 -0
- package/dist/component/organism/EChart/chart/BarChart.d.ts +22 -0
- package/dist/component/organism/EChart/chart/BarChart.js +34 -0
- package/dist/component/organism/EChart/chart/BarChart.js.map +1 -0
- package/dist/component/organism/EChart/chart/CartesianChart.d.ts +48 -0
- package/dist/component/organism/EChart/chart/CartesianChart.js +63 -0
- package/dist/component/organism/EChart/chart/CartesianChart.js.map +1 -0
- package/dist/component/organism/EChart/chart/ChartHeader.d.ts +36 -0
- package/dist/component/organism/EChart/chart/ChartHeader.js +105 -0
- package/dist/component/organism/EChart/chart/ChartHeader.js.map +1 -0
- package/dist/component/organism/EChart/chart/ChartSeries.d.ts +50 -0
- package/dist/component/organism/EChart/chart/ChartSeries.js +23 -0
- package/dist/component/organism/EChart/chart/ChartSeries.js.map +1 -0
- package/dist/component/organism/EChart/chart/ChartTexts.d.ts +12 -0
- package/dist/component/organism/EChart/chart/ChartTexts.js +21 -0
- package/dist/component/organism/EChart/chart/ChartTexts.js.map +1 -0
- package/dist/component/organism/EChart/chart/ChartType.d.ts +50 -0
- package/dist/component/organism/EChart/chart/ChartType.js +1 -0
- package/dist/component/organism/EChart/chart/ChartTypeId.d.ts +2 -0
- package/dist/component/organism/EChart/chart/ChartTypeId.js +1 -0
- package/dist/component/organism/EChart/chart/ChartTypes.d.ts +15 -0
- package/dist/component/organism/EChart/chart/ChartTypes.js +24 -0
- package/dist/component/organism/EChart/chart/ChartTypes.js.map +1 -0
- package/dist/component/organism/EChart/chart/DonutChart.d.ts +32 -0
- package/dist/component/organism/EChart/chart/DonutChart.js +111 -0
- package/dist/component/organism/EChart/chart/DonutChart.js.map +1 -0
- package/dist/component/organism/EChart/chart/LineChart.d.ts +11 -0
- package/dist/component/organism/EChart/chart/LineChart.js +20 -0
- package/dist/component/organism/EChart/chart/LineChart.js.map +1 -0
- package/dist/component/organism/EChart/chart/StackedBarChart.d.ts +15 -0
- package/dist/component/organism/EChart/chart/StackedBarChart.js +30 -0
- package/dist/component/organism/EChart/chart/StackedBarChart.js.map +1 -0
- package/dist/component/organism/EChart/chart/UnitPanels.d.ts +63 -0
- package/dist/component/organism/EChart/chart/UnitPanels.js +127 -0
- package/dist/component/organism/EChart/chart/UnitPanels.js.map +1 -0
- package/dist/component/organism/EChart/echartDefaults.d.ts +32 -0
- package/dist/component/organism/EChart/echartDefaults.js +47 -0
- package/dist/component/organism/EChart/echartDefaults.js.map +1 -0
- package/dist/component/organism/EChart/useEChart.d.ts +35 -0
- package/dist/component/organism/EChart/useEChart.js +112 -0
- package/dist/component/organism/EChart/useEChart.js.map +1 -0
- package/dist/component/organism/Navbar/NavItem.d.ts +16 -0
- package/dist/component/organism/Navbar/NavItem.js +1 -0
- package/dist/component/organism/Navbar/Navbar.d.ts +20 -0
- package/dist/component/organism/Navbar/Navbar.js +64 -0
- package/dist/component/organism/Navbar/Navbar.js.map +1 -0
- package/dist/component/organism/Navbar/Navbar.module.js +19 -0
- package/dist/component/organism/Navbar/Navbar.module.js.map +1 -0
- package/dist/component/organism/PageHeader/PageHeader.d.ts +14 -0
- package/dist/component/organism/PageHeader/PageHeader.js +55 -0
- package/dist/component/organism/PageHeader/PageHeader.js.map +1 -0
- package/dist/component/organism/StatsGrid/StatsGrid.d.ts +12 -0
- package/dist/component/organism/StatsGrid/StatsGrid.js +46 -0
- package/dist/component/organism/StatsGrid/StatsGrid.js.map +1 -0
- package/dist/component/organism/StatsGrid/StatsGrid.module.js +17 -0
- package/dist/component/organism/StatsGrid/StatsGrid.module.js.map +1 -0
- package/dist/component/organism/Table/RowActions.d.ts +23 -0
- package/dist/component/organism/Table/RowActions.js +121 -0
- package/dist/component/organism/Table/RowActions.js.map +1 -0
- package/dist/component/organism/Table/Table.d.ts +51 -0
- package/dist/component/organism/Table/Table.js +108 -0
- package/dist/component/organism/Table/Table.js.map +1 -0
- package/dist/component/organism/ViewTable/ColumnComparator.d.ts +11 -0
- package/dist/component/organism/ViewTable/ColumnComparator.js +25 -0
- package/dist/component/organism/ViewTable/ColumnComparator.js.map +1 -0
- package/dist/component/organism/ViewTable/ViewTable.d.ts +53 -0
- package/dist/component/organism/ViewTable/ViewTable.js +123 -0
- package/dist/component/organism/ViewTable/ViewTable.js.map +1 -0
- package/dist/component/template/CreateFormPage/CreateFormPage.d.ts +19 -0
- package/dist/component/template/CreateFormPage/CreateFormPage.js +41 -0
- package/dist/component/template/CreateFormPage/CreateFormPage.js.map +1 -0
- package/dist/component/template/EditFormPage/EditFormPage.d.ts +19 -0
- package/dist/component/template/EditFormPage/EditFormPage.js +42 -0
- package/dist/component/template/EditFormPage/EditFormPage.js.map +1 -0
- package/dist/component/template/FormPage/FormPage.d.ts +25 -0
- package/dist/component/template/FormPage/FormPage.js +130 -0
- package/dist/component/template/FormPage/FormPage.js.map +1 -0
- package/dist/component/template/FormPage/FormType.d.ts +6 -0
- package/dist/component/template/FormPage/FormType.js +12 -0
- package/dist/component/template/FormPage/FormType.js.map +1 -0
- package/dist/component/template/Layout/Layout.d.ts +21 -0
- package/dist/component/template/Layout/Layout.js +121 -0
- package/dist/component/template/Layout/Layout.js.map +1 -0
- package/dist/component/template/Layout/Layout.module.js +19 -0
- package/dist/component/template/Layout/Layout.module.js.map +1 -0
- package/dist/component/template/ListPage/ListPage.d.ts +32 -0
- package/dist/component/template/ListPage/ListPage.js +47 -0
- package/dist/component/template/ListPage/ListPage.js.map +1 -0
- package/dist/component/template/PrintPage/PrintPage.d.ts +25 -0
- package/dist/component/template/PrintPage/PrintPage.js +115 -0
- package/dist/component/template/PrintPage/PrintPage.js.map +1 -0
- package/dist/component/template/ViewPage/ViewPage.d.ts +11 -0
- package/dist/component/template/ViewPage/ViewPage.js +49 -0
- package/dist/component/template/ViewPage/ViewPage.js.map +1 -0
- package/dist/data/ListDataSource.d.ts +12 -0
- package/dist/data/ListDataSource.js +1 -0
- package/dist/data/SaveDataSource.d.ts +16 -0
- package/dist/data/SaveDataSource.js +1 -0
- package/dist/export/ExportCellText.d.ts +18 -0
- package/dist/export/ExportCellText.js +60 -0
- package/dist/export/ExportCellText.js.map +1 -0
- package/dist/export/ExportDocument.d.ts +127 -0
- package/dist/export/ExportDocument.js +59 -0
- package/dist/export/ExportDocument.js.map +1 -0
- package/dist/export/ExportDownload.d.ts +9 -0
- package/dist/export/ExportDownload.js +19 -0
- package/dist/export/ExportDownload.js.map +1 -0
- package/dist/export/ExportFileName.d.ts +11 -0
- package/dist/export/ExportFileName.js +24 -0
- package/dist/export/ExportFileName.js.map +1 -0
- package/dist/export/ExportFormat.d.ts +30 -0
- package/dist/export/ExportFormat.js +1 -0
- package/dist/export/ExportFormats.d.ts +12 -0
- package/dist/export/ExportFormats.js +16 -0
- package/dist/export/ExportFormats.js.map +1 -0
- package/dist/export/ExportTexts.d.ts +14 -0
- package/dist/export/ExportTexts.js +23 -0
- package/dist/export/ExportTexts.js.map +1 -0
- package/dist/export/csv/CsvFormat.d.ts +41 -0
- package/dist/export/csv/CsvFormat.js +91 -0
- package/dist/export/csv/CsvFormat.js.map +1 -0
- package/dist/export/xlsx/XlsxFormat.d.ts +35 -0
- package/dist/export/xlsx/XlsxFormat.js +66 -0
- package/dist/export/xlsx/XlsxFormat.js.map +1 -0
- package/dist/export/xlsx/XlsxNumberFormat.d.ts +8 -0
- package/dist/export/xlsx/XlsxNumberFormat.js +15 -0
- package/dist/export/xlsx/XlsxNumberFormat.js.map +1 -0
- package/dist/export/xlsx/XlsxSheetName.d.ts +12 -0
- package/dist/export/xlsx/XlsxSheetName.js +62 -0
- package/dist/export/xlsx/XlsxSheetName.js.map +1 -0
- package/dist/export/xlsx/XlsxSheetWriter.d.ts +67 -0
- package/dist/export/xlsx/XlsxSheetWriter.js +215 -0
- package/dist/export/xlsx/XlsxSheetWriter.js.map +1 -0
- package/dist/export/xlsx/XlsxStyle.d.ts +51 -0
- package/dist/export/xlsx/XlsxStyle.js +87 -0
- package/dist/export/xlsx/XlsxStyle.js.map +1 -0
- package/dist/hook/useIsMobile.d.ts +5 -0
- package/dist/hook/useIsMobile.js +16 -0
- package/dist/hook/useIsMobile.js.map +1 -0
- package/dist/hook/useKeyedService.d.ts +22 -0
- package/dist/hook/useKeyedService.js +38 -0
- package/dist/hook/useKeyedService.js.map +1 -0
- package/dist/hook/useLightColorSchemeOnPrint.d.ts +9 -0
- package/dist/hook/useLightColorSchemeOnPrint.js +36 -0
- package/dist/hook/useLightColorSchemeOnPrint.js.map +1 -0
- package/dist/hook/useService.d.ts +12 -0
- package/dist/hook/useService.js +29 -0
- package/dist/hook/useService.js.map +1 -0
- package/dist/http/ApiError.d.ts +13 -0
- package/dist/http/ApiError.js +22 -0
- package/dist/http/ApiError.js.map +1 -0
- package/dist/http/AxiosHttpClient.d.ts +21 -0
- package/dist/http/AxiosHttpClient.js +67 -0
- package/dist/http/AxiosHttpClient.js.map +1 -0
- package/dist/http/AxiosHttpClientConfig.d.ts +37 -0
- package/dist/http/AxiosHttpClientConfig.js +19 -0
- package/dist/http/AxiosHttpClientConfig.js.map +1 -0
- package/dist/i18n/KitLocale.d.ts +4 -0
- package/dist/i18n/KitLocale.js +7 -0
- package/dist/i18n/KitLocale.js.map +1 -0
- package/dist/i18n/KitMessages.d.ts +146 -0
- package/dist/i18n/KitMessages.js +1 -0
- package/dist/i18n/messages/enUS.d.ts +3 -0
- package/dist/i18n/messages/enUS.js +112 -0
- package/dist/i18n/messages/enUS.js.map +1 -0
- package/dist/i18n/messages/ptBR.d.ts +3 -0
- package/dist/i18n/messages/ptBR.js +97 -0
- package/dist/i18n/messages/ptBR.js.map +1 -0
- package/dist/i18n/resolveMessages.d.ts +13 -0
- package/dist/i18n/resolveMessages.js +29 -0
- package/dist/i18n/resolveMessages.js.map +1 -0
- package/dist/model/Identifiable.d.ts +7 -0
- package/dist/model/Identifiable.js +1 -0
- package/dist/provider/KitConfig.d.ts +26 -0
- package/dist/provider/KitConfig.js +1 -0
- package/dist/provider/KitContext.d.ts +9 -0
- package/dist/provider/KitContext.js +27 -0
- package/dist/provider/KitContext.js.map +1 -0
- package/dist/provider/KitProvider.d.ts +7 -0
- package/dist/provider/KitProvider.js +31 -0
- package/dist/provider/KitProvider.js.map +1 -0
- package/dist/provider/useKit.d.ts +15 -0
- package/dist/provider/useKit.js +28 -0
- package/dist/provider/useKit.js.map +1 -0
- package/dist/router/FormRouteSegments.d.ts +16 -0
- package/dist/router/FormRouteSegments.js +19 -0
- package/dist/router/FormRouteSegments.js.map +1 -0
- package/dist/router/RouterAdapter.d.ts +24 -0
- package/dist/router/RouterAdapter.js +1 -0
- package/dist/router/anchorRouter.d.ts +7 -0
- package/dist/router/anchorRouter.js +23 -0
- package/dist/router/anchorRouter.js.map +1 -0
- package/dist/styles/print.css +93 -0
- package/dist/styles.css +188 -0
- package/dist/util/ArrayUtil.d.ts +39 -0
- package/dist/util/ArrayUtil.js +48 -0
- package/dist/util/ArrayUtil.js.map +1 -0
- package/dist/util/ColorUtil.d.ts +7 -0
- package/dist/util/ColorUtil.js +16 -0
- package/dist/util/ColorUtil.js.map +1 -0
- package/dist/util/CompareUtil.d.ts +19 -0
- package/dist/util/CompareUtil.js +51 -0
- package/dist/util/CompareUtil.js.map +1 -0
- package/dist/util/DateUtil.d.ts +37 -0
- package/dist/util/DateUtil.js +53 -0
- package/dist/util/DateUtil.js.map +1 -0
- package/dist/util/DownloadUtil.d.ts +6 -0
- package/dist/util/DownloadUtil.js +25 -0
- package/dist/util/DownloadUtil.js.map +1 -0
- package/dist/util/EnumUtil.d.ts +19 -0
- package/dist/util/EnumUtil.js +32 -0
- package/dist/util/EnumUtil.js.map +1 -0
- package/dist/util/FormUtil.d.ts +8 -0
- package/dist/util/FormUtil.js +14 -0
- package/dist/util/FormUtil.js.map +1 -0
- package/dist/util/HtmlUtil.d.ts +8 -0
- package/dist/util/HtmlUtil.js +23 -0
- package/dist/util/HtmlUtil.js.map +1 -0
- package/dist/util/Notifications.d.ts +30 -0
- package/dist/util/Notifications.js +59 -0
- package/dist/util/Notifications.js.map +1 -0
- package/dist/util/NumberUtil.d.ts +19 -0
- package/dist/util/NumberUtil.js +33 -0
- package/dist/util/NumberUtil.js.map +1 -0
- package/dist/util/ObjectUtil.d.ts +22 -0
- package/dist/util/ObjectUtil.js +34 -0
- package/dist/util/ObjectUtil.js.map +1 -0
- package/dist/util/SearchParamsUtil.d.ts +23 -0
- package/dist/util/SearchParamsUtil.js +29 -0
- package/dist/util/SearchParamsUtil.js.map +1 -0
- package/dist/util/TransformData.d.ts +14 -0
- package/dist/util/TransformData.js +31 -0
- package/dist/util/TransformData.js.map +1 -0
- package/dist/util/ValidationUtil.d.ts +9 -0
- package/dist/util/ValidationUtil.js +16 -0
- package/dist/util/ValidationUtil.js.map +1 -0
- package/dist/util/parseActionsPageParams.d.ts +14 -0
- package/dist/util/parseActionsPageParams.js +29 -0
- package/dist/util/parseActionsPageParams.js.map +1 -0
- package/package.json +149 -5
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { DEFAULT_MESSAGES } from "../../i18n/resolveMessages.js";
|
|
3
|
+
// src/export/xlsx/XlsxSheetName.ts
|
|
4
|
+
/** Maximum length of a sheet name in Excel. */
|
|
5
|
+
var MAX_SHEET_NAME_LENGTH = 31;
|
|
6
|
+
/** Characters Excel does not accept in a sheet name. */
|
|
7
|
+
var INVALID_CHARS = /[[\]:*?/\\]/g;
|
|
8
|
+
/** Spaces and apostrophes at the ends of the name: Excel accepts no apostrophe at the beginning or at the end. */
|
|
9
|
+
var EDGES = /^[\s']+|[\s']+$/g;
|
|
10
|
+
/** Name reserved by Excel, which cannot be used by a sheet (case insensitive). */
|
|
11
|
+
var RESERVED_NAME = "history";
|
|
12
|
+
/**
|
|
13
|
+
* Adapts the sheet names to the Excel rules: at most {@link MAX_SHEET_NAME_LENGTH} characters, without `[ ] : * ? /
|
|
14
|
+
* \`, without an apostrophe at the beginning or at the end, without the reserved name "History" and without repeating
|
|
15
|
+
* a name (Excel is case insensitive). A repeated or reserved name gets a number at the end (e.g. "Sales (2)").
|
|
16
|
+
* @param names desired names, in the order of the sheets
|
|
17
|
+
* @param defaultName builds the name of a sheet without any valid character, from its position (starting at 1);
|
|
18
|
+
* defaults to the one of the default locale
|
|
19
|
+
* @return the names accepted by Excel, in the same order
|
|
20
|
+
*/
|
|
21
|
+
function xlsxSheetNames(names, defaultName = DEFAULT_MESSAGES.export.defaultSheetName) {
|
|
22
|
+
const usedNames = /* @__PURE__ */ new Set([RESERVED_NAME]);
|
|
23
|
+
return names.map((name, i) => {
|
|
24
|
+
const sheetName = uniqueName(sanitize(name) || defaultName(i + 1), usedNames);
|
|
25
|
+
usedNames.add(sheetName.toLowerCase());
|
|
26
|
+
return sheetName;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Removes from the name the characters Excel does not accept.
|
|
31
|
+
* @param name desired name of the sheet
|
|
32
|
+
* @return the name without the invalid characters and without spaces or apostrophes at the ends
|
|
33
|
+
*/
|
|
34
|
+
function sanitize(name) {
|
|
35
|
+
return name.replace(INVALID_CHARS, "-").replace(EDGES, "");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Cuts the name to the maximum length and adds a number if it is already used.
|
|
39
|
+
* @param name name of the sheet, already without invalid characters
|
|
40
|
+
* @param usedNames names already used, in lower case
|
|
41
|
+
* @return a name not used yet
|
|
42
|
+
*/
|
|
43
|
+
function uniqueName(name, usedNames) {
|
|
44
|
+
let candidate = cut(name, 31);
|
|
45
|
+
for (let number = 2; usedNames.has(candidate.toLowerCase()); number++) {
|
|
46
|
+
const suffix = ` (${number})`;
|
|
47
|
+
candidate = cut(name, 31 - suffix.length) + suffix;
|
|
48
|
+
}
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Cuts the name to a length and removes again the spaces and apostrophes at the ends, which the cut may have left.
|
|
53
|
+
* @param name name of the sheet
|
|
54
|
+
* @param maxLength maximum length
|
|
55
|
+
* @return the cut name
|
|
56
|
+
*/
|
|
57
|
+
function cut(name, maxLength) {
|
|
58
|
+
return name.slice(0, maxLength).replace(EDGES, "");
|
|
59
|
+
}
|
|
60
|
+
export { MAX_SHEET_NAME_LENGTH, xlsxSheetNames };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=XlsxSheetName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XlsxSheetName.js","names":[],"sources":["../../../src/export/xlsx/XlsxSheetName.ts"],"sourcesContent":["import { DEFAULT_MESSAGES } from '../../i18n/resolveMessages';\n\n/** Maximum length of a sheet name in Excel. */\nexport const MAX_SHEET_NAME_LENGTH = 31;\n\n/** Characters Excel does not accept in a sheet name. */\nconst INVALID_CHARS = /[[\\]:*?/\\\\]/g;\n\n/** Spaces and apostrophes at the ends of the name: Excel accepts no apostrophe at the beginning or at the end. */\nconst EDGES = /^[\\s']+|[\\s']+$/g;\n\n/** Name reserved by Excel, which cannot be used by a sheet (case insensitive). */\nconst RESERVED_NAME = 'history';\n\n/**\n * Adapts the sheet names to the Excel rules: at most {@link MAX_SHEET_NAME_LENGTH} characters, without `[ ] : * ? /\n * \\`, without an apostrophe at the beginning or at the end, without the reserved name \"History\" and without repeating\n * a name (Excel is case insensitive). A repeated or reserved name gets a number at the end (e.g. \"Sales (2)\").\n * @param names desired names, in the order of the sheets\n * @param defaultName builds the name of a sheet without any valid character, from its position (starting at 1);\n * defaults to the one of the default locale\n * @return the names accepted by Excel, in the same order\n */\nexport function xlsxSheetNames(\n names: string[],\n defaultName: (position: number) => string = DEFAULT_MESSAGES.export.defaultSheetName\n): string[] {\n const usedNames = new Set<string>([RESERVED_NAME]);\n\n return names.map((name, i) => {\n const sheetName = uniqueName(sanitize(name) || defaultName(i + 1), usedNames);\n usedNames.add(sheetName.toLowerCase());\n return sheetName;\n });\n}\n\n/**\n * Removes from the name the characters Excel does not accept.\n * @param name desired name of the sheet\n * @return the name without the invalid characters and without spaces or apostrophes at the ends\n */\nfunction sanitize(name: string): string {\n return name.replace(INVALID_CHARS, '-').replace(EDGES, '');\n}\n\n/**\n * Cuts the name to the maximum length and adds a number if it is already used.\n * @param name name of the sheet, already without invalid characters\n * @param usedNames names already used, in lower case\n * @return a name not used yet\n */\nfunction uniqueName(name: string, usedNames: Set<string>): string {\n let candidate = cut(name, MAX_SHEET_NAME_LENGTH);\n for (let number = 2; usedNames.has(candidate.toLowerCase()); number++) {\n const suffix = ` (${number})`;\n candidate = cut(name, MAX_SHEET_NAME_LENGTH - suffix.length) + suffix;\n }\n\n return candidate;\n}\n\n/**\n * Cuts the name to a length and removes again the spaces and apostrophes at the ends, which the cut may have left.\n * @param name name of the sheet\n * @param maxLength maximum length\n * @return the cut name\n */\nfunction cut(name: string, maxLength: number): string {\n return name.slice(0, maxLength).replace(EDGES, '');\n}\n"],"mappings":";;;;AAGA,IAAa,wBAAwB;;AAGrC,IAAM,gBAAgB;;AAGtB,IAAM,QAAQ;;AAGd,IAAM,gBAAgB;;;;;;;;;;AAWtB,SAAgB,eACZ,OACA,cAA4C,iBAAiB,OAAO,kBAC5D;CACR,MAAM,4BAAY,IAAI,IAAY,CAAC,aAAa,CAAC;CAEjD,OAAO,MAAM,KAAK,MAAM,MAAM;EAC1B,MAAM,YAAY,WAAW,SAAS,IAAI,KAAK,YAAY,IAAI,CAAC,GAAG,SAAS;EAC5E,UAAU,IAAI,UAAU,YAAY,CAAC;EACrC,OAAO;CACX,CAAC;AACL;;;;;;AAOA,SAAS,SAAS,MAAsB;CACpC,OAAO,KAAK,QAAQ,eAAe,GAAG,CAAC,CAAC,QAAQ,OAAO,EAAE;AAC7D;;;;;;;AAQA,SAAS,WAAW,MAAc,WAAgC;CAC9D,IAAI,YAAY,IAAI,MAAA,EAA2B;CAC/C,KAAK,IAAI,SAAS,GAAG,UAAU,IAAI,UAAU,YAAY,CAAC,GAAG,UAAU;EACnE,MAAM,SAAS,KAAK,OAAO;EAC3B,YAAY,IAAI,MAAA,KAA8B,OAAO,MAAM,IAAI;CACnE;CAEA,OAAO;AACX;;;;;;;AAQA,SAAS,IAAI,MAAc,WAA2B;CAClD,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,QAAQ,OAAO,EAAE;AACrD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Worksheet } from 'exceljs';
|
|
2
|
+
import { ExportDocument, ExportSheet } from '../ExportDocument';
|
|
3
|
+
import { XlsxStyles } from './XlsxStyle';
|
|
4
|
+
/**
|
|
5
|
+
* Writes a sheet of the `.xlsx`, from top to bottom: the logo, the title, the generation date, the context info, the
|
|
6
|
+
* table and the footer with the link of the system. Numbers are numeric (not text), with the format of each cell.
|
|
7
|
+
* @author Manoel Campos
|
|
8
|
+
*/
|
|
9
|
+
export declare class XlsxSheetWriter {
|
|
10
|
+
private readonly worksheet;
|
|
11
|
+
private readonly document;
|
|
12
|
+
private readonly messages;
|
|
13
|
+
private readonly styles;
|
|
14
|
+
/** Next free row of the sheet (starts at 1, as in Excel). */
|
|
15
|
+
private nextRow;
|
|
16
|
+
/**
|
|
17
|
+
* @param worksheet sheet to write to
|
|
18
|
+
* @param document exported document, where the title, the context, the texts and the visual identity come from
|
|
19
|
+
* @param styles fonts, fills and borders of the sheet
|
|
20
|
+
*/
|
|
21
|
+
constructor(worksheet: Worksheet, document: ExportDocument, styles?: XlsxStyles);
|
|
22
|
+
/**
|
|
23
|
+
* Writes the whole sheet.
|
|
24
|
+
* @param sheet columns and rows of the table
|
|
25
|
+
* @param logoImageId id of the logo registered in the workbook; absent to show no logo
|
|
26
|
+
*/
|
|
27
|
+
write(sheet: ExportSheet, logoImageId?: number): void;
|
|
28
|
+
/**
|
|
29
|
+
* Writes the logo, the title, the generation date and the context info.
|
|
30
|
+
* @param logoImageId id of the logo registered in the workbook
|
|
31
|
+
*/
|
|
32
|
+
private writeHeader;
|
|
33
|
+
/**
|
|
34
|
+
* Writes the table: the header and the rows, with borders, zebra stripes and the Excel filter in the header.
|
|
35
|
+
* @param sheet columns of the table
|
|
36
|
+
* @param rows cells of each row, already classified
|
|
37
|
+
* @return the number of the header row
|
|
38
|
+
*/
|
|
39
|
+
private writeTable;
|
|
40
|
+
/**
|
|
41
|
+
* Writes a row of the table.
|
|
42
|
+
* @param row cells of the row, already classified
|
|
43
|
+
* @param index position of the row in the table (0 for the first one), used in the zebra stripes
|
|
44
|
+
*/
|
|
45
|
+
private writeTableRow;
|
|
46
|
+
/** Writes the footer, with the name of the system and the link to it. */
|
|
47
|
+
private writeFooter;
|
|
48
|
+
/**
|
|
49
|
+
* Fits the width of each column to the longest text of the table. The rows outside the table (title, info and
|
|
50
|
+
* footer) do not count, since their text can flow to the empty cells beside them.
|
|
51
|
+
* @param sheet columns of the table
|
|
52
|
+
* @param rows cells of each row, already classified
|
|
53
|
+
*/
|
|
54
|
+
private adjustColumnWidths;
|
|
55
|
+
/**
|
|
56
|
+
* Sets up printing: landscape, the table fitting the page width, the header repeated on every page and a page
|
|
57
|
+
* footer with the system and the page number.
|
|
58
|
+
* @param headerRow number of the header row
|
|
59
|
+
*/
|
|
60
|
+
private setupPrinting;
|
|
61
|
+
/**
|
|
62
|
+
* Adds a row at the next free position.
|
|
63
|
+
* @param values values of the cells, from the first column on
|
|
64
|
+
* @return the created row
|
|
65
|
+
*/
|
|
66
|
+
private addRow;
|
|
67
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cellText } from "../ExportCellText.js";
|
|
3
|
+
import { classifyCell } from "../ExportDocument.js";
|
|
4
|
+
import { exportLocale, exportMessages } from "../ExportTexts.js";
|
|
5
|
+
import { xlsxNumberFormat } from "./XlsxNumberFormat.js";
|
|
6
|
+
import { xlsxStyles } from "./XlsxStyle.js";
|
|
7
|
+
// src/export/xlsx/XlsxSheetWriter.ts
|
|
8
|
+
/**
|
|
9
|
+
* Writes a sheet of the `.xlsx`, from top to bottom: the logo, the title, the generation date, the context info, the
|
|
10
|
+
* table and the footer with the link of the system. Numbers are numeric (not text), with the format of each cell.
|
|
11
|
+
* @author Manoel Campos
|
|
12
|
+
*/
|
|
13
|
+
var XlsxSheetWriter = class {
|
|
14
|
+
worksheet;
|
|
15
|
+
document;
|
|
16
|
+
messages;
|
|
17
|
+
styles;
|
|
18
|
+
/** Next free row of the sheet (starts at 1, as in Excel). */
|
|
19
|
+
nextRow = 1;
|
|
20
|
+
/**
|
|
21
|
+
* @param worksheet sheet to write to
|
|
22
|
+
* @param document exported document, where the title, the context, the texts and the visual identity come from
|
|
23
|
+
* @param styles fonts, fills and borders of the sheet
|
|
24
|
+
*/
|
|
25
|
+
constructor(worksheet, document, styles = xlsxStyles()) {
|
|
26
|
+
this.worksheet = worksheet;
|
|
27
|
+
this.document = document;
|
|
28
|
+
this.messages = exportMessages(document);
|
|
29
|
+
this.styles = styles;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Writes the whole sheet.
|
|
33
|
+
* @param sheet columns and rows of the table
|
|
34
|
+
* @param logoImageId id of the logo registered in the workbook; absent to show no logo
|
|
35
|
+
*/
|
|
36
|
+
write(sheet, logoImageId) {
|
|
37
|
+
this.writeHeader(logoImageId);
|
|
38
|
+
const rows = classifyRows(sheet, this.messages.invalidCell);
|
|
39
|
+
const headerRow = this.writeTable(sheet, rows);
|
|
40
|
+
this.writeFooter();
|
|
41
|
+
this.adjustColumnWidths(sheet, rows);
|
|
42
|
+
this.setupPrinting(headerRow);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Writes the logo, the title, the generation date and the context info.
|
|
46
|
+
* @param logoImageId id of the logo registered in the workbook
|
|
47
|
+
*/
|
|
48
|
+
writeHeader(logoImageId) {
|
|
49
|
+
const { logo } = this.document.branding;
|
|
50
|
+
if (logoImageId !== void 0 && logo) {
|
|
51
|
+
const width = Math.round(48 * logo.width / logo.height);
|
|
52
|
+
this.worksheet.addImage(logoImageId, {
|
|
53
|
+
tl: {
|
|
54
|
+
col: 0,
|
|
55
|
+
row: 0
|
|
56
|
+
},
|
|
57
|
+
ext: {
|
|
58
|
+
width,
|
|
59
|
+
height: 48
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
this.nextRow += 3;
|
|
63
|
+
}
|
|
64
|
+
this.addRow([this.document.title]).getCell(1).font = this.styles.titleFont;
|
|
65
|
+
this.addRow([this.messages.generatedAt(this.document.generatedAt)]).getCell(1).font = this.styles.subtitleFont;
|
|
66
|
+
this.document.info.forEach((info) => {
|
|
67
|
+
this.addRow([]).getCell(1).value = { richText: [{
|
|
68
|
+
font: this.styles.infoLabelFont,
|
|
69
|
+
text: `${info.label}: `
|
|
70
|
+
}, { text: info.value }] };
|
|
71
|
+
});
|
|
72
|
+
this.nextRow++;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Writes the table: the header and the rows, with borders, zebra stripes and the Excel filter in the header.
|
|
76
|
+
* @param sheet columns of the table
|
|
77
|
+
* @param rows cells of each row, already classified
|
|
78
|
+
* @return the number of the header row
|
|
79
|
+
*/
|
|
80
|
+
writeTable(sheet, rows) {
|
|
81
|
+
const headerRow = this.nextRow;
|
|
82
|
+
this.addRow(sheet.columns.map((column) => column.header)).eachCell((cell) => {
|
|
83
|
+
cell.font = this.styles.headerFont;
|
|
84
|
+
cell.fill = this.styles.headerFill;
|
|
85
|
+
cell.border = this.styles.cellBorder;
|
|
86
|
+
cell.alignment = {
|
|
87
|
+
vertical: "middle",
|
|
88
|
+
wrapText: true
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
rows.forEach((row, i) => this.writeTableRow(row, i));
|
|
92
|
+
if (rows.length > 0 && sheet.columns.length > 0) this.worksheet.autoFilter = {
|
|
93
|
+
from: {
|
|
94
|
+
row: headerRow,
|
|
95
|
+
column: 1
|
|
96
|
+
},
|
|
97
|
+
to: {
|
|
98
|
+
row: headerRow + rows.length,
|
|
99
|
+
column: sheet.columns.length
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return headerRow;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Writes a row of the table.
|
|
106
|
+
* @param row cells of the row, already classified
|
|
107
|
+
* @param index position of the row in the table (0 for the first one), used in the zebra stripes
|
|
108
|
+
*/
|
|
109
|
+
writeTableRow(row, index) {
|
|
110
|
+
const excelRow = this.worksheet.getRow(this.nextRow++);
|
|
111
|
+
row.forEach((classified, i) => {
|
|
112
|
+
const cell = excelRow.getCell(i + 1);
|
|
113
|
+
writeCell(cell, classified, this.styles);
|
|
114
|
+
cell.border = this.styles.cellBorder;
|
|
115
|
+
if (index % 2 === 1) cell.fill = this.styles.zebraFill;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** Writes the footer, with the name of the system and the link to it. */
|
|
119
|
+
writeFooter() {
|
|
120
|
+
const { branding } = this.document;
|
|
121
|
+
this.nextRow++;
|
|
122
|
+
this.addRow([this.messages.footer(branding.systemName)]).getCell(1).font = this.styles.footerFont;
|
|
123
|
+
const link = this.addRow([]).getCell(1);
|
|
124
|
+
link.value = {
|
|
125
|
+
text: branding.siteUrl,
|
|
126
|
+
hyperlink: branding.siteUrl
|
|
127
|
+
};
|
|
128
|
+
link.font = this.styles.linkFont;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Fits the width of each column to the longest text of the table. The rows outside the table (title, info and
|
|
132
|
+
* footer) do not count, since their text can flow to the empty cells beside them.
|
|
133
|
+
* @param sheet columns of the table
|
|
134
|
+
* @param rows cells of each row, already classified
|
|
135
|
+
*/
|
|
136
|
+
adjustColumnWidths(sheet, rows) {
|
|
137
|
+
const locale = exportLocale(this.document);
|
|
138
|
+
sheet.columns.forEach((column, i) => {
|
|
139
|
+
const maxLength = rows.reduce((max, row) => Math.max(max, cellText(row[i], true, locale).length), column.header.length);
|
|
140
|
+
this.worksheet.getColumn(i + 1).width = Math.min(60, Math.max(8, maxLength + 2));
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Sets up printing: landscape, the table fitting the page width, the header repeated on every page and a page
|
|
145
|
+
* footer with the system and the page number.
|
|
146
|
+
* @param headerRow number of the header row
|
|
147
|
+
*/
|
|
148
|
+
setupPrinting(headerRow) {
|
|
149
|
+
this.worksheet.pageSetup = {
|
|
150
|
+
...this.worksheet.pageSetup,
|
|
151
|
+
orientation: "landscape",
|
|
152
|
+
fitToPage: true,
|
|
153
|
+
fitToWidth: 1,
|
|
154
|
+
fitToHeight: 0,
|
|
155
|
+
printTitlesRow: `${headerRow}:${headerRow}`
|
|
156
|
+
};
|
|
157
|
+
const { branding } = this.document;
|
|
158
|
+
const footer = `${this.messages.footer(branding.systemName)} - ${branding.siteUrl}`.replaceAll("&", "&&");
|
|
159
|
+
this.worksheet.headerFooter.oddFooter = `&L${footer}&R${this.messages.excelPageNumber}`;
|
|
160
|
+
this.worksheet.views = [{ showGridLines: false }];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Adds a row at the next free position.
|
|
164
|
+
* @param values values of the cells, from the first column on
|
|
165
|
+
* @return the created row
|
|
166
|
+
*/
|
|
167
|
+
addRow(values) {
|
|
168
|
+
const row = this.worksheet.getRow(this.nextRow++);
|
|
169
|
+
values.forEach((value, i) => {
|
|
170
|
+
row.getCell(i + 1).value = value;
|
|
171
|
+
});
|
|
172
|
+
return row;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Classifies the cells of all rows of the table, completing with empty cells the rows shorter than the number of
|
|
177
|
+
* columns.
|
|
178
|
+
* @param sheet columns and rows of the table
|
|
179
|
+
* @param invalidText text of an infinite number
|
|
180
|
+
* @return a classified cell per column, in each row
|
|
181
|
+
* @throws Error if a row has more cells than columns, since the extra data would be lost without warning
|
|
182
|
+
*/
|
|
183
|
+
function classifyRows(sheet, invalidText) {
|
|
184
|
+
return sheet.rows.map((row, rowIndex) => {
|
|
185
|
+
if (row.length > sheet.columns.length) throw new Error(`Row ${rowIndex + 1} of the sheet "${sheet.name}" has ${row.length} cells, but the sheet has ${sheet.columns.length} columns.`);
|
|
186
|
+
return sheet.columns.map((column, i) => classifyCell(row[i], column.format, invalidText));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Writes the value of a table cell, with the number format and the highlight of the invalid results.
|
|
191
|
+
* @param cell Excel cell
|
|
192
|
+
* @param classified value already classified (see {@link classifyCell})
|
|
193
|
+
* @param styles styles of the sheet, for the highlight of the invalid results
|
|
194
|
+
*/
|
|
195
|
+
function writeCell(cell, classified, styles) {
|
|
196
|
+
switch (classified.kind) {
|
|
197
|
+
case "empty":
|
|
198
|
+
cell.value = null;
|
|
199
|
+
break;
|
|
200
|
+
case "text":
|
|
201
|
+
cell.value = classified.text;
|
|
202
|
+
break;
|
|
203
|
+
case "invalid":
|
|
204
|
+
cell.value = classified.text;
|
|
205
|
+
cell.font = styles.invalidFont;
|
|
206
|
+
cell.alignment = { horizontal: "right" };
|
|
207
|
+
break;
|
|
208
|
+
case "number":
|
|
209
|
+
cell.value = classified.value;
|
|
210
|
+
cell.numFmt = xlsxNumberFormat(classified.format);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export { XlsxSheetWriter };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=XlsxSheetWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XlsxSheetWriter.js","names":[],"sources":["../../../src/export/xlsx/XlsxSheetWriter.ts"],"sourcesContent":["import type { Cell, Row, Worksheet } from 'exceljs';\n\nimport { ExportMessages } from '../../i18n/KitMessages';\nimport { cellText } from '../ExportCellText';\nimport { ClassifiedCell, classifyCell, ExportDocument, ExportSheet } from '../ExportDocument';\nimport { exportLocale, exportMessages } from '../ExportTexts';\nimport { xlsxNumberFormat } from './XlsxNumberFormat';\nimport {\n COLUMN_PADDING,\n LOGO_HEIGHT,\n LOGO_ROWS,\n MAX_COLUMN_WIDTH,\n MIN_COLUMN_WIDTH,\n xlsxStyles,\n XlsxStyles\n} from './XlsxStyle';\n\n/**\n * Writes a sheet of the `.xlsx`, from top to bottom: the logo, the title, the generation date, the context info, the\n * table and the footer with the link of the system. Numbers are numeric (not text), with the format of each cell.\n * @author Manoel Campos\n */\nexport class XlsxSheetWriter {\n private readonly worksheet: Worksheet;\n private readonly document: ExportDocument;\n private readonly messages: ExportMessages;\n private readonly styles: XlsxStyles;\n\n /** Next free row of the sheet (starts at 1, as in Excel). */\n private nextRow = 1;\n\n /**\n * @param worksheet sheet to write to\n * @param document exported document, where the title, the context, the texts and the visual identity come from\n * @param styles fonts, fills and borders of the sheet\n */\n constructor(worksheet: Worksheet, document: ExportDocument, styles: XlsxStyles = xlsxStyles()) {\n this.worksheet = worksheet;\n this.document = document;\n this.messages = exportMessages(document);\n this.styles = styles;\n }\n\n /**\n * Writes the whole sheet.\n * @param sheet columns and rows of the table\n * @param logoImageId id of the logo registered in the workbook; absent to show no logo\n */\n write(sheet: ExportSheet, logoImageId?: number): void {\n this.writeHeader(logoImageId);\n const rows = classifyRows(sheet, this.messages.invalidCell);\n const headerRow = this.writeTable(sheet, rows);\n this.writeFooter();\n this.adjustColumnWidths(sheet, rows);\n this.setupPrinting(headerRow);\n }\n\n /**\n * Writes the logo, the title, the generation date and the context info.\n * @param logoImageId id of the logo registered in the workbook\n */\n private writeHeader(logoImageId?: number): void {\n const { logo } = this.document.branding;\n if (logoImageId !== undefined && logo) {\n const width = Math.round((LOGO_HEIGHT * logo.width) / logo.height);\n this.worksheet.addImage(logoImageId, { tl: { col: 0, row: 0 }, ext: { width, height: LOGO_HEIGHT } });\n this.nextRow += LOGO_ROWS;\n }\n\n this.addRow([this.document.title]).getCell(1).font = this.styles.titleFont;\n this.addRow([this.messages.generatedAt(this.document.generatedAt)]).getCell(1).font = this.styles.subtitleFont;\n /* The label and the value stay in a single cell (as rich text, with the label in bold): the text flows to the\n empty cells beside it, instead of being cut by the width of the first column, which fits the table. */\n this.document.info.forEach(info => {\n this.addRow([]).getCell(1).value = {\n richText: [{ font: this.styles.infoLabelFont, text: `${info.label}: ` }, { text: info.value }]\n };\n });\n this.nextRow++;\n }\n\n /**\n * Writes the table: the header and the rows, with borders, zebra stripes and the Excel filter in the header.\n * @param sheet columns of the table\n * @param rows cells of each row, already classified\n * @return the number of the header row\n */\n private writeTable(sheet: ExportSheet, rows: ClassifiedCell[][]): number {\n const headerRow = this.nextRow;\n this.addRow(sheet.columns.map(column => column.header)).eachCell(cell => {\n cell.font = this.styles.headerFont;\n cell.fill = this.styles.headerFill;\n cell.border = this.styles.cellBorder;\n cell.alignment = { vertical: 'middle', wrapText: true };\n });\n\n rows.forEach((row, i) => this.writeTableRow(row, i));\n if (rows.length > 0 && sheet.columns.length > 0) {\n this.worksheet.autoFilter = {\n from: { row: headerRow, column: 1 },\n to: { row: headerRow + rows.length, column: sheet.columns.length }\n };\n }\n\n return headerRow;\n }\n\n /**\n * Writes a row of the table.\n * @param row cells of the row, already classified\n * @param index position of the row in the table (0 for the first one), used in the zebra stripes\n */\n private writeTableRow(row: ClassifiedCell[], index: number): void {\n const excelRow = this.worksheet.getRow(this.nextRow++);\n row.forEach((classified, i) => {\n const cell = excelRow.getCell(i + 1);\n writeCell(cell, classified, this.styles);\n cell.border = this.styles.cellBorder;\n if (index % 2 === 1) cell.fill = this.styles.zebraFill;\n });\n }\n\n /** Writes the footer, with the name of the system and the link to it. */\n private writeFooter(): void {\n const { branding } = this.document;\n this.nextRow++;\n this.addRow([this.messages.footer(branding.systemName)]).getCell(1).font = this.styles.footerFont;\n\n const link = this.addRow([]).getCell(1);\n link.value = { text: branding.siteUrl, hyperlink: branding.siteUrl };\n link.font = this.styles.linkFont;\n }\n\n /**\n * Fits the width of each column to the longest text of the table. The rows outside the table (title, info and\n * footer) do not count, since their text can flow to the empty cells beside them.\n * @param sheet columns of the table\n * @param rows cells of each row, already classified\n */\n private adjustColumnWidths(sheet: ExportSheet, rows: ClassifiedCell[][]): void {\n const locale = exportLocale(this.document);\n sheet.columns.forEach((column, i) => {\n // reduce, not Math.max(...): the spread passes a row per argument and overflows the stack in a big sheet.\n const maxLength = rows.reduce(\n (max, row) => Math.max(max, cellText(row[i], true, locale).length),\n column.header.length\n );\n this.worksheet.getColumn(i + 1).width = Math.min(\n MAX_COLUMN_WIDTH,\n Math.max(MIN_COLUMN_WIDTH, maxLength + COLUMN_PADDING)\n );\n });\n }\n\n /**\n * Sets up printing: landscape, the table fitting the page width, the header repeated on every page and a page\n * footer with the system and the page number.\n * @param headerRow number of the header row\n */\n private setupPrinting(headerRow: number): void {\n this.worksheet.pageSetup = {\n ...this.worksheet.pageSetup,\n orientation: 'landscape',\n fitToPage: true,\n fitToWidth: 1,\n fitToHeight: 0,\n printTitlesRow: `${headerRow}:${headerRow}`\n };\n\n // In the Excel page footer, `&` starts a code (e.g. &P is the page number), so it is escaped.\n const { branding } = this.document;\n const footer = `${this.messages.footer(branding.systemName)} - ${branding.siteUrl}`.replaceAll('&', '&&');\n this.worksheet.headerFooter.oddFooter = `&L${footer}&R${this.messages.excelPageNumber}`;\n this.worksheet.views = [{ showGridLines: false }];\n }\n\n /**\n * Adds a row at the next free position.\n * @param values values of the cells, from the first column on\n * @return the created row\n */\n private addRow(values: string[]): Row {\n const row = this.worksheet.getRow(this.nextRow++);\n values.forEach((value, i) => {\n row.getCell(i + 1).value = value;\n });\n\n return row;\n }\n}\n\n/**\n * Classifies the cells of all rows of the table, completing with empty cells the rows shorter than the number of\n * columns.\n * @param sheet columns and rows of the table\n * @param invalidText text of an infinite number\n * @return a classified cell per column, in each row\n * @throws Error if a row has more cells than columns, since the extra data would be lost without warning\n */\nfunction classifyRows(sheet: ExportSheet, invalidText: string): ClassifiedCell[][] {\n return sheet.rows.map((row, rowIndex) => {\n if (row.length > sheet.columns.length) {\n throw new Error(\n `Row ${rowIndex + 1} of the sheet \"${sheet.name}\" has ${row.length} cells, but the sheet has ` +\n `${sheet.columns.length} columns.`\n );\n }\n\n return sheet.columns.map((column, i) => classifyCell(row[i], column.format, invalidText));\n });\n}\n\n/**\n * Writes the value of a table cell, with the number format and the highlight of the invalid results.\n * @param cell Excel cell\n * @param classified value already classified (see {@link classifyCell})\n * @param styles styles of the sheet, for the highlight of the invalid results\n */\nfunction writeCell(cell: Cell, classified: ClassifiedCell, styles: XlsxStyles): void {\n switch (classified.kind) {\n case 'empty':\n cell.value = null;\n break;\n case 'text':\n cell.value = classified.text;\n break;\n case 'invalid':\n cell.value = classified.text;\n cell.font = styles.invalidFont;\n cell.alignment = { horizontal: 'right' };\n break;\n case 'number':\n cell.value = classified.value;\n cell.numFmt = xlsxNumberFormat(classified.format);\n break;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Border, Fill, Font } from 'exceljs';
|
|
2
|
+
/**
|
|
3
|
+
* Styles of the `.xlsx` file, built from a set of colors so that all sheets look the same and an app can use its own
|
|
4
|
+
* brand colors.
|
|
5
|
+
* @author Manoel Campos
|
|
6
|
+
*/
|
|
7
|
+
/** Colors of the `.xlsx` file, in the Excel ARGB format (e.g. `FF055087`). */
|
|
8
|
+
export type XlsxColors = {
|
|
9
|
+
/** Title, table header background and links. */
|
|
10
|
+
primary: string;
|
|
11
|
+
/** Background of the even rows (zebra stripes). */
|
|
12
|
+
primaryLight: string;
|
|
13
|
+
/** Text of the table header. */
|
|
14
|
+
headerText: string;
|
|
15
|
+
/** Table borders. */
|
|
16
|
+
border: string;
|
|
17
|
+
/** Secondary texts: generation date, info labels and footer. */
|
|
18
|
+
secondaryText: string;
|
|
19
|
+
/** Cells with an invalid result. */
|
|
20
|
+
invalid: string;
|
|
21
|
+
};
|
|
22
|
+
/** Default colors: a dark blue primary color with a light blue zebra. */
|
|
23
|
+
export declare const DEFAULT_XLSX_COLORS: XlsxColors;
|
|
24
|
+
/** Fonts, fills and borders of the `.xlsx` file. */
|
|
25
|
+
export type XlsxStyles = {
|
|
26
|
+
titleFont: Partial<Font>;
|
|
27
|
+
subtitleFont: Partial<Font>;
|
|
28
|
+
infoLabelFont: Partial<Font>;
|
|
29
|
+
headerFont: Partial<Font>;
|
|
30
|
+
invalidFont: Partial<Font>;
|
|
31
|
+
footerFont: Partial<Font>;
|
|
32
|
+
linkFont: Partial<Font>;
|
|
33
|
+
headerFill: Fill;
|
|
34
|
+
zebraFill: Fill;
|
|
35
|
+
cellBorder: Partial<Record<'top' | 'left' | 'bottom' | 'right', Partial<Border>>>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Builds the styles of the `.xlsx` file from a set of colors.
|
|
39
|
+
* @param colors colors of the file; the ones not given use {@link DEFAULT_XLSX_COLORS}
|
|
40
|
+
* @return the fonts, fills and borders of the file
|
|
41
|
+
*/
|
|
42
|
+
export declare function xlsxStyles(colors?: Partial<XlsxColors>): XlsxStyles;
|
|
43
|
+
/** Height of the logo in the header (px). The width follows the aspect ratio of the image. */
|
|
44
|
+
export declare const LOGO_HEIGHT = 48;
|
|
45
|
+
/** Header rows taken by the logo, so the text is not placed under it. */
|
|
46
|
+
export declare const LOGO_ROWS = 3;
|
|
47
|
+
/** Minimum and maximum width of a column, in characters. */
|
|
48
|
+
export declare const MIN_COLUMN_WIDTH = 8;
|
|
49
|
+
export declare const MAX_COLUMN_WIDTH = 60;
|
|
50
|
+
/** Room added to the longest text of the column, in characters, so the text does not touch the border. */
|
|
51
|
+
export declare const COLUMN_PADDING = 2;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/export/xlsx/XlsxStyle.ts
|
|
3
|
+
/** Default colors: a dark blue primary color with a light blue zebra. */
|
|
4
|
+
var DEFAULT_XLSX_COLORS = {
|
|
5
|
+
primary: "FF055087",
|
|
6
|
+
primaryLight: "FFF0F8FF",
|
|
7
|
+
headerText: "FFFFFFFF",
|
|
8
|
+
border: "FFD0D7DE",
|
|
9
|
+
secondaryText: "FF5F6B7A",
|
|
10
|
+
invalid: "FFC92A2A"
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Builds the styles of the `.xlsx` file from a set of colors.
|
|
14
|
+
* @param colors colors of the file; the ones not given use {@link DEFAULT_XLSX_COLORS}
|
|
15
|
+
* @return the fonts, fills and borders of the file
|
|
16
|
+
*/
|
|
17
|
+
function xlsxStyles(colors = {}) {
|
|
18
|
+
const color = {
|
|
19
|
+
...DEFAULT_XLSX_COLORS,
|
|
20
|
+
...colors
|
|
21
|
+
};
|
|
22
|
+
const thinBorder = {
|
|
23
|
+
style: "thin",
|
|
24
|
+
color: { argb: color.border }
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
titleFont: {
|
|
28
|
+
size: 16,
|
|
29
|
+
bold: true,
|
|
30
|
+
color: { argb: color.primary }
|
|
31
|
+
},
|
|
32
|
+
subtitleFont: {
|
|
33
|
+
size: 10,
|
|
34
|
+
italic: true,
|
|
35
|
+
color: { argb: color.secondaryText }
|
|
36
|
+
},
|
|
37
|
+
infoLabelFont: {
|
|
38
|
+
bold: true,
|
|
39
|
+
color: { argb: color.secondaryText }
|
|
40
|
+
},
|
|
41
|
+
headerFont: {
|
|
42
|
+
bold: true,
|
|
43
|
+
color: { argb: color.headerText }
|
|
44
|
+
},
|
|
45
|
+
invalidFont: {
|
|
46
|
+
bold: true,
|
|
47
|
+
color: { argb: color.invalid }
|
|
48
|
+
},
|
|
49
|
+
footerFont: {
|
|
50
|
+
size: 9,
|
|
51
|
+
color: { argb: color.secondaryText }
|
|
52
|
+
},
|
|
53
|
+
linkFont: {
|
|
54
|
+
size: 9,
|
|
55
|
+
underline: true,
|
|
56
|
+
color: { argb: color.primary }
|
|
57
|
+
},
|
|
58
|
+
headerFill: {
|
|
59
|
+
type: "pattern",
|
|
60
|
+
pattern: "solid",
|
|
61
|
+
fgColor: { argb: color.primary }
|
|
62
|
+
},
|
|
63
|
+
zebraFill: {
|
|
64
|
+
type: "pattern",
|
|
65
|
+
pattern: "solid",
|
|
66
|
+
fgColor: { argb: color.primaryLight }
|
|
67
|
+
},
|
|
68
|
+
cellBorder: {
|
|
69
|
+
top: thinBorder,
|
|
70
|
+
left: thinBorder,
|
|
71
|
+
bottom: thinBorder,
|
|
72
|
+
right: thinBorder
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Height of the logo in the header (px). The width follows the aspect ratio of the image. */
|
|
77
|
+
var LOGO_HEIGHT = 48;
|
|
78
|
+
/** Header rows taken by the logo, so the text is not placed under it. */
|
|
79
|
+
var LOGO_ROWS = 3;
|
|
80
|
+
/** Minimum and maximum width of a column, in characters. */
|
|
81
|
+
var MIN_COLUMN_WIDTH = 8;
|
|
82
|
+
var MAX_COLUMN_WIDTH = 60;
|
|
83
|
+
/** Room added to the longest text of the column, in characters, so the text does not touch the border. */
|
|
84
|
+
var COLUMN_PADDING = 2;
|
|
85
|
+
export { COLUMN_PADDING, DEFAULT_XLSX_COLORS, LOGO_HEIGHT, LOGO_ROWS, MAX_COLUMN_WIDTH, MIN_COLUMN_WIDTH, xlsxStyles };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=XlsxStyle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XlsxStyle.js","names":[],"sources":["../../../src/export/xlsx/XlsxStyle.ts"],"sourcesContent":["import type { Border, Fill, Font } from 'exceljs';\n\n/**\n * Styles of the `.xlsx` file, built from a set of colors so that all sheets look the same and an app can use its own\n * brand colors.\n * @author Manoel Campos\n */\n\n/** Colors of the `.xlsx` file, in the Excel ARGB format (e.g. `FF055087`). */\nexport type XlsxColors = {\n /** Title, table header background and links. */\n primary: string;\n\n /** Background of the even rows (zebra stripes). */\n primaryLight: string;\n\n /** Text of the table header. */\n headerText: string;\n\n /** Table borders. */\n border: string;\n\n /** Secondary texts: generation date, info labels and footer. */\n secondaryText: string;\n\n /** Cells with an invalid result. */\n invalid: string;\n};\n\n/** Default colors: a dark blue primary color with a light blue zebra. */\nexport const DEFAULT_XLSX_COLORS: XlsxColors = {\n primary: 'FF055087',\n primaryLight: 'FFF0F8FF',\n headerText: 'FFFFFFFF',\n border: 'FFD0D7DE',\n secondaryText: 'FF5F6B7A',\n invalid: 'FFC92A2A'\n};\n\n/** Fonts, fills and borders of the `.xlsx` file. */\nexport type XlsxStyles = {\n titleFont: Partial<Font>;\n subtitleFont: Partial<Font>;\n infoLabelFont: Partial<Font>;\n headerFont: Partial<Font>;\n invalidFont: Partial<Font>;\n footerFont: Partial<Font>;\n linkFont: Partial<Font>;\n headerFill: Fill;\n zebraFill: Fill;\n cellBorder: Partial<Record<'top' | 'left' | 'bottom' | 'right', Partial<Border>>>;\n};\n\n/**\n * Builds the styles of the `.xlsx` file from a set of colors.\n * @param colors colors of the file; the ones not given use {@link DEFAULT_XLSX_COLORS}\n * @return the fonts, fills and borders of the file\n */\nexport function xlsxStyles(colors: Partial<XlsxColors> = {}): XlsxStyles {\n const color = { ...DEFAULT_XLSX_COLORS, ...colors };\n const thinBorder: Partial<Border> = { style: 'thin', color: { argb: color.border } };\n\n return {\n titleFont: { size: 16, bold: true, color: { argb: color.primary } },\n subtitleFont: { size: 10, italic: true, color: { argb: color.secondaryText } },\n infoLabelFont: { bold: true, color: { argb: color.secondaryText } },\n headerFont: { bold: true, color: { argb: color.headerText } },\n invalidFont: { bold: true, color: { argb: color.invalid } },\n footerFont: { size: 9, color: { argb: color.secondaryText } },\n linkFont: { size: 9, underline: true, color: { argb: color.primary } },\n headerFill: { type: 'pattern', pattern: 'solid', fgColor: { argb: color.primary } },\n zebraFill: { type: 'pattern', pattern: 'solid', fgColor: { argb: color.primaryLight } },\n cellBorder: { top: thinBorder, left: thinBorder, bottom: thinBorder, right: thinBorder }\n };\n}\n\n/** Height of the logo in the header (px). The width follows the aspect ratio of the image. */\nexport const LOGO_HEIGHT = 48;\n\n/** Header rows taken by the logo, so the text is not placed under it. */\nexport const LOGO_ROWS = 3;\n\n/** Minimum and maximum width of a column, in characters. */\nexport const MIN_COLUMN_WIDTH = 8;\nexport const MAX_COLUMN_WIDTH = 60;\n\n/** Room added to the longest text of the column, in characters, so the text does not touch the border. */\nexport const COLUMN_PADDING = 2;\n"],"mappings":";;;AA8BA,IAAa,sBAAkC;CAC3C,SAAS;CACT,cAAc;CACd,YAAY;CACZ,QAAQ;CACR,eAAe;CACf,SAAS;AACb;;;;;;AAqBA,SAAgB,WAAW,SAA8B,CAAC,GAAe;CACrE,MAAM,QAAQ;EAAE,GAAG;EAAqB,GAAG;CAAO;CAClD,MAAM,aAA8B;EAAE,OAAO;EAAQ,OAAO,EAAE,MAAM,MAAM,OAAO;CAAE;CAEnF,OAAO;EACH,WAAW;GAAE,MAAM;GAAI,MAAM;GAAM,OAAO,EAAE,MAAM,MAAM,QAAQ;EAAE;EAClE,cAAc;GAAE,MAAM;GAAI,QAAQ;GAAM,OAAO,EAAE,MAAM,MAAM,cAAc;EAAE;EAC7E,eAAe;GAAE,MAAM;GAAM,OAAO,EAAE,MAAM,MAAM,cAAc;EAAE;EAClE,YAAY;GAAE,MAAM;GAAM,OAAO,EAAE,MAAM,MAAM,WAAW;EAAE;EAC5D,aAAa;GAAE,MAAM;GAAM,OAAO,EAAE,MAAM,MAAM,QAAQ;EAAE;EAC1D,YAAY;GAAE,MAAM;GAAG,OAAO,EAAE,MAAM,MAAM,cAAc;EAAE;EAC5D,UAAU;GAAE,MAAM;GAAG,WAAW;GAAM,OAAO,EAAE,MAAM,MAAM,QAAQ;EAAE;EACrE,YAAY;GAAE,MAAM;GAAW,SAAS;GAAS,SAAS,EAAE,MAAM,MAAM,QAAQ;EAAE;EAClF,WAAW;GAAE,MAAM;GAAW,SAAS;GAAS,SAAS,EAAE,MAAM,MAAM,aAAa;EAAE;EACtF,YAAY;GAAE,KAAK;GAAY,MAAM;GAAY,QAAQ;GAAY,OAAO;EAAW;CAC3F;AACJ;;AAGA,IAAa,cAAc;;AAG3B,IAAa,YAAY;;AAGzB,IAAa,mBAAmB;AAChC,IAAa,mBAAmB;;AAGhC,IAAa,iBAAiB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMediaQuery } from "@mantine/hooks";
|
|
3
|
+
// src/hook/useIsMobile.ts
|
|
4
|
+
/** Maximum screen width considered a mobile device. */
|
|
5
|
+
var MOBILE_MEDIA_QUERY = "(max-width: 768px)";
|
|
6
|
+
/**
|
|
7
|
+
* Tells whether the user is on a mobile device.
|
|
8
|
+
* @return true if the screen has the width of a mobile device, false otherwise
|
|
9
|
+
*/
|
|
10
|
+
var useIsMobile = () => {
|
|
11
|
+
const isMobile = useMediaQuery(MOBILE_MEDIA_QUERY);
|
|
12
|
+
return isMobile == void 0 ? false : isMobile;
|
|
13
|
+
};
|
|
14
|
+
export { useIsMobile };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=useIsMobile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsMobile.js","names":[],"sources":["../../src/hook/useIsMobile.ts"],"sourcesContent":["import { useMediaQuery } from '@mantine/hooks';\n\n/** Maximum screen width considered a mobile device. */\nconst MOBILE_MEDIA_QUERY = '(max-width: 768px)';\n\n/**\n * Tells whether the user is on a mobile device.\n * @return true if the screen has the width of a mobile device, false otherwise\n */\nexport const useIsMobile = () => {\n const isMobile = useMediaQuery(MOBILE_MEDIA_QUERY);\n\n /* `useMediaQuery` returns `undefined` before the first measurement. The caller expects just true or false, so it is\n treated as false. */\n return isMobile == undefined ? false : isMobile;\n};\n"],"mappings":";;;;AAGA,IAAM,qBAAqB;;;;;AAM3B,IAAa,oBAAoB;CAC7B,MAAM,WAAW,cAAc,kBAAkB;CAIjD,OAAO,YAAY,KAAA,IAAY,QAAQ;AAC3C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Key of a fetch (e.g. the year being queried), which tells which selection a result belongs to. */
|
|
2
|
+
export type ServiceKey = string | number;
|
|
3
|
+
/** Values returned by {@link useKeyedService}. */
|
|
4
|
+
export type UseKeyedService<T> = {
|
|
5
|
+
/** Data of the fetch of the current key; `undefined` while it has not finished or if it failed. */
|
|
6
|
+
data?: T;
|
|
7
|
+
/** Error of the fetch of the current key; `undefined` if it has not finished yet or succeeded. */
|
|
8
|
+
error?: unknown;
|
|
9
|
+
/** Whether the fetch of the current key has not finished yet. */
|
|
10
|
+
loading: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Fetches data that depends on a key (e.g. a filter of the screen) and exposes only the result of the current key.
|
|
14
|
+
* When the key changes, the data and the error of the previous fetch are no longer exposed and `loading` becomes
|
|
15
|
+
* `true` until the new fetch finishes, so the screen never shows data of another selection. A response that arrives
|
|
16
|
+
* after the key changed is discarded.
|
|
17
|
+
* @param key key of the fetch; a new key triggers a new fetch
|
|
18
|
+
* @param fetchMethod function that fetches the data of a key. It must be stable (e.g. defined outside the component),
|
|
19
|
+
* since a new function also triggers a new fetch
|
|
20
|
+
* @return the data, the error and the loading state of the current key
|
|
21
|
+
*/
|
|
22
|
+
export declare function useKeyedService<T>(key: ServiceKey, fetchMethod: (key: ServiceKey) => Promise<T>): UseKeyedService<T>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
// src/hook/useKeyedService.ts
|
|
4
|
+
/**
|
|
5
|
+
* Fetches data that depends on a key (e.g. a filter of the screen) and exposes only the result of the current key.
|
|
6
|
+
* When the key changes, the data and the error of the previous fetch are no longer exposed and `loading` becomes
|
|
7
|
+
* `true` until the new fetch finishes, so the screen never shows data of another selection. A response that arrives
|
|
8
|
+
* after the key changed is discarded.
|
|
9
|
+
* @param key key of the fetch; a new key triggers a new fetch
|
|
10
|
+
* @param fetchMethod function that fetches the data of a key. It must be stable (e.g. defined outside the component),
|
|
11
|
+
* since a new function also triggers a new fetch
|
|
12
|
+
* @return the data, the error and the loading state of the current key
|
|
13
|
+
*/
|
|
14
|
+
function useKeyedService(key, fetchMethod) {
|
|
15
|
+
const [result, setResult] = useState();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
let obsolete = false;
|
|
18
|
+
fetchMethod(key).then((data) => !obsolete && setResult({
|
|
19
|
+
key,
|
|
20
|
+
data
|
|
21
|
+
})).catch((error) => !obsolete && setResult({
|
|
22
|
+
key,
|
|
23
|
+
error
|
|
24
|
+
}));
|
|
25
|
+
return () => {
|
|
26
|
+
obsolete = true;
|
|
27
|
+
};
|
|
28
|
+
}, [key, fetchMethod]);
|
|
29
|
+
const current = result?.key === key ? result : void 0;
|
|
30
|
+
return {
|
|
31
|
+
data: current?.data,
|
|
32
|
+
error: current?.error,
|
|
33
|
+
loading: !current
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export { useKeyedService };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=useKeyedService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKeyedService.js","names":[],"sources":["../../src/hook/useKeyedService.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/** Key of a fetch (e.g. the year being queried), which tells which selection a result belongs to. */\nexport type ServiceKey = string | number;\n\n/** Values returned by {@link useKeyedService}. */\nexport type UseKeyedService<T> = {\n /** Data of the fetch of the current key; `undefined` while it has not finished or if it failed. */\n data?: T;\n\n /** Error of the fetch of the current key; `undefined` if it has not finished yet or succeeded. */\n error?: unknown;\n\n /** Whether the fetch of the current key has not finished yet. */\n loading: boolean;\n};\n\n/** Result of a fetch, kept with the key that was queried. */\ntype KeyedResult<T> = { key: ServiceKey; data?: T; error?: unknown };\n\n/**\n * Fetches data that depends on a key (e.g. a filter of the screen) and exposes only the result of the current key.\n * When the key changes, the data and the error of the previous fetch are no longer exposed and `loading` becomes\n * `true` until the new fetch finishes, so the screen never shows data of another selection. A response that arrives\n * after the key changed is discarded.\n * @param key key of the fetch; a new key triggers a new fetch\n * @param fetchMethod function that fetches the data of a key. It must be stable (e.g. defined outside the component),\n * since a new function also triggers a new fetch\n * @return the data, the error and the loading state of the current key\n */\nexport function useKeyedService<T>(key: ServiceKey, fetchMethod: (key: ServiceKey) => Promise<T>): UseKeyedService<T> {\n const [result, setResult] = useState<KeyedResult<T>>();\n\n useEffect(() => {\n // If the key changes before the fetch finishes, its response cannot replace the one of the new key.\n let obsolete = false;\n fetchMethod(key)\n .then(data => !obsolete && setResult({ key, data }))\n .catch(error => !obsolete && setResult({ key, error }));\n\n return () => {\n obsolete = true;\n };\n }, [key, fetchMethod]);\n\n // A result of another key stays in the state until the new fetch finishes: it is not exposed.\n const current = result?.key === key ? result : undefined;\n return { data: current?.data, error: current?.error, loading: !current };\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,SAAgB,gBAAmB,KAAiB,aAAkE;CAClH,MAAM,CAAC,QAAQ,aAAa,SAAyB;CAErD,gBAAgB;EAEZ,IAAI,WAAW;EACf,YAAY,GAAG,CAAC,CACX,MAAK,SAAQ,CAAC,YAAY,UAAU;GAAE;GAAK;EAAK,CAAC,CAAC,CAAC,CACnD,OAAM,UAAS,CAAC,YAAY,UAAU;GAAE;GAAK;EAAM,CAAC,CAAC;EAE1D,aAAa;GACT,WAAW;EACf;CACJ,GAAG,CAAC,KAAK,WAAW,CAAC;CAGrB,MAAM,UAAU,QAAQ,QAAQ,MAAM,SAAS,KAAA;CAC/C,OAAO;EAAE,MAAM,SAAS;EAAM,OAAO,SAAS;EAAO,SAAS,CAAC;CAAQ;AAC3E"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Always prints the page with the light color scheme, even if the user uses the dark one: on paper (or in a PDF),
|
|
3
|
+
* light text on a dark background wastes ink and is hard to read. The scheme changes only while the print dialog is
|
|
4
|
+
* open (`beforeprint`/`afterprint`), without changing the preference saved by the user.
|
|
5
|
+
*
|
|
6
|
+
* It changes only what comes from the Mantine CSS variables. A component that chooses the color in JavaScript from the
|
|
7
|
+
* color scheme (e.g. with `useMantineColorScheme`) is not redrawn and keeps the dark colors.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useLightColorSchemeOnPrint(): void;
|