@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.
Files changed (298) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +471 -2
  3. package/dist/component/atom/AppMain/App.module.js +7 -0
  4. package/dist/component/atom/AppMain/App.module.js.map +1 -0
  5. package/dist/component/atom/AppMain/AppMain.d.ts +7 -0
  6. package/dist/component/atom/AppMain/AppMain.js +14 -0
  7. package/dist/component/atom/AppMain/AppMain.js.map +1 -0
  8. package/dist/component/atom/CancelEditButton/CancelEditButton.d.ts +6 -0
  9. package/dist/component/atom/CancelEditButton/CancelEditButton.js +24 -0
  10. package/dist/component/atom/CancelEditButton/CancelEditButton.js.map +1 -0
  11. package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.d.ts +13 -0
  12. package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.js +30 -0
  13. package/dist/component/atom/ConfirmEditButton/ConfirmEditButton.js.map +1 -0
  14. package/dist/component/atom/DateField/DateField.d.ts +7 -0
  15. package/dist/component/atom/DateField/DateField.js +21 -0
  16. package/dist/component/atom/DateField/DateField.js.map +1 -0
  17. package/dist/component/atom/EditValueButton/EditValueButton.d.ts +6 -0
  18. package/dist/component/atom/EditValueButton/EditValueButton.js +24 -0
  19. package/dist/component/atom/EditValueButton/EditValueButton.js.map +1 -0
  20. package/dist/component/atom/ErrorAlert/ErrorAlert.d.ts +8 -0
  21. package/dist/component/atom/ErrorAlert/ErrorAlert.js +21 -0
  22. package/dist/component/atom/ErrorAlert/ErrorAlert.js.map +1 -0
  23. package/dist/component/atom/Surface/Surface.d.ts +20 -0
  24. package/dist/component/atom/Surface/Surface.js +15 -0
  25. package/dist/component/atom/Surface/Surface.js.map +1 -0
  26. package/dist/component/molecule/Breadcrumb/Breadcrumb.d.ts +12 -0
  27. package/dist/component/molecule/Breadcrumb/Breadcrumb.js +38 -0
  28. package/dist/component/molecule/Breadcrumb/Breadcrumb.js.map +1 -0
  29. package/dist/component/molecule/ExportMenuButton/ExportMenuButton.d.ts +25 -0
  30. package/dist/component/molecule/ExportMenuButton/ExportMenuButton.js +49 -0
  31. package/dist/component/molecule/ExportMenuButton/ExportMenuButton.js.map +1 -0
  32. package/dist/component/molecule/PaperCard/PaperCard.d.ts +7 -0
  33. package/dist/component/molecule/PaperCard/PaperCard.js +17 -0
  34. package/dist/component/molecule/PaperCard/PaperCard.js.map +1 -0
  35. package/dist/component/molecule/PrintSection/PrintSection.d.ts +14 -0
  36. package/dist/component/molecule/PrintSection/PrintSection.js +29 -0
  37. package/dist/component/molecule/PrintSection/PrintSection.js.map +1 -0
  38. package/dist/component/molecule/SelectField/SelectField.d.ts +32 -0
  39. package/dist/component/molecule/SelectField/SelectField.js +38 -0
  40. package/dist/component/molecule/SelectField/SelectField.js.map +1 -0
  41. package/dist/component/molecule/StatsCard/Stats.module.js +15 -0
  42. package/dist/component/molecule/StatsCard/Stats.module.js.map +1 -0
  43. package/dist/component/molecule/StatsCard/StatsCard.d.ts +18 -0
  44. package/dist/component/molecule/StatsCard/StatsCard.js +60 -0
  45. package/dist/component/molecule/StatsCard/StatsCard.js.map +1 -0
  46. package/dist/component/organism/DataForm/DataForm.d.ts +26 -0
  47. package/dist/component/organism/DataForm/DataForm.js +83 -0
  48. package/dist/component/organism/DataForm/DataForm.js.map +1 -0
  49. package/dist/component/organism/DataTabList/DataTabList.d.ts +24 -0
  50. package/dist/component/organism/DataTabList/DataTabList.js +37 -0
  51. package/dist/component/organism/DataTabList/DataTabList.js.map +1 -0
  52. package/dist/component/organism/DataTabList/DataTabList.module.js +13 -0
  53. package/dist/component/organism/DataTabList/DataTabList.module.js.map +1 -0
  54. package/dist/component/organism/EChart/EChart.d.ts +47 -0
  55. package/dist/component/organism/EChart/EChart.js +37 -0
  56. package/dist/component/organism/EChart/EChart.js.map +1 -0
  57. package/dist/component/organism/EChart/buildEChartOption.d.ts +40 -0
  58. package/dist/component/organism/EChart/buildEChartOption.js +57 -0
  59. package/dist/component/organism/EChart/buildEChartOption.js.map +1 -0
  60. package/dist/component/organism/EChart/chart/BarChart.d.ts +22 -0
  61. package/dist/component/organism/EChart/chart/BarChart.js +34 -0
  62. package/dist/component/organism/EChart/chart/BarChart.js.map +1 -0
  63. package/dist/component/organism/EChart/chart/CartesianChart.d.ts +48 -0
  64. package/dist/component/organism/EChart/chart/CartesianChart.js +63 -0
  65. package/dist/component/organism/EChart/chart/CartesianChart.js.map +1 -0
  66. package/dist/component/organism/EChart/chart/ChartHeader.d.ts +36 -0
  67. package/dist/component/organism/EChart/chart/ChartHeader.js +105 -0
  68. package/dist/component/organism/EChart/chart/ChartHeader.js.map +1 -0
  69. package/dist/component/organism/EChart/chart/ChartSeries.d.ts +50 -0
  70. package/dist/component/organism/EChart/chart/ChartSeries.js +23 -0
  71. package/dist/component/organism/EChart/chart/ChartSeries.js.map +1 -0
  72. package/dist/component/organism/EChart/chart/ChartTexts.d.ts +12 -0
  73. package/dist/component/organism/EChart/chart/ChartTexts.js +21 -0
  74. package/dist/component/organism/EChart/chart/ChartTexts.js.map +1 -0
  75. package/dist/component/organism/EChart/chart/ChartType.d.ts +50 -0
  76. package/dist/component/organism/EChart/chart/ChartType.js +1 -0
  77. package/dist/component/organism/EChart/chart/ChartTypeId.d.ts +2 -0
  78. package/dist/component/organism/EChart/chart/ChartTypeId.js +1 -0
  79. package/dist/component/organism/EChart/chart/ChartTypes.d.ts +15 -0
  80. package/dist/component/organism/EChart/chart/ChartTypes.js +24 -0
  81. package/dist/component/organism/EChart/chart/ChartTypes.js.map +1 -0
  82. package/dist/component/organism/EChart/chart/DonutChart.d.ts +32 -0
  83. package/dist/component/organism/EChart/chart/DonutChart.js +111 -0
  84. package/dist/component/organism/EChart/chart/DonutChart.js.map +1 -0
  85. package/dist/component/organism/EChart/chart/LineChart.d.ts +11 -0
  86. package/dist/component/organism/EChart/chart/LineChart.js +20 -0
  87. package/dist/component/organism/EChart/chart/LineChart.js.map +1 -0
  88. package/dist/component/organism/EChart/chart/StackedBarChart.d.ts +15 -0
  89. package/dist/component/organism/EChart/chart/StackedBarChart.js +30 -0
  90. package/dist/component/organism/EChart/chart/StackedBarChart.js.map +1 -0
  91. package/dist/component/organism/EChart/chart/UnitPanels.d.ts +63 -0
  92. package/dist/component/organism/EChart/chart/UnitPanels.js +127 -0
  93. package/dist/component/organism/EChart/chart/UnitPanels.js.map +1 -0
  94. package/dist/component/organism/EChart/echartDefaults.d.ts +32 -0
  95. package/dist/component/organism/EChart/echartDefaults.js +47 -0
  96. package/dist/component/organism/EChart/echartDefaults.js.map +1 -0
  97. package/dist/component/organism/EChart/useEChart.d.ts +35 -0
  98. package/dist/component/organism/EChart/useEChart.js +112 -0
  99. package/dist/component/organism/EChart/useEChart.js.map +1 -0
  100. package/dist/component/organism/Navbar/NavItem.d.ts +16 -0
  101. package/dist/component/organism/Navbar/NavItem.js +1 -0
  102. package/dist/component/organism/Navbar/Navbar.d.ts +20 -0
  103. package/dist/component/organism/Navbar/Navbar.js +64 -0
  104. package/dist/component/organism/Navbar/Navbar.js.map +1 -0
  105. package/dist/component/organism/Navbar/Navbar.module.js +19 -0
  106. package/dist/component/organism/Navbar/Navbar.module.js.map +1 -0
  107. package/dist/component/organism/PageHeader/PageHeader.d.ts +14 -0
  108. package/dist/component/organism/PageHeader/PageHeader.js +55 -0
  109. package/dist/component/organism/PageHeader/PageHeader.js.map +1 -0
  110. package/dist/component/organism/StatsGrid/StatsGrid.d.ts +12 -0
  111. package/dist/component/organism/StatsGrid/StatsGrid.js +46 -0
  112. package/dist/component/organism/StatsGrid/StatsGrid.js.map +1 -0
  113. package/dist/component/organism/StatsGrid/StatsGrid.module.js +17 -0
  114. package/dist/component/organism/StatsGrid/StatsGrid.module.js.map +1 -0
  115. package/dist/component/organism/Table/RowActions.d.ts +23 -0
  116. package/dist/component/organism/Table/RowActions.js +121 -0
  117. package/dist/component/organism/Table/RowActions.js.map +1 -0
  118. package/dist/component/organism/Table/Table.d.ts +51 -0
  119. package/dist/component/organism/Table/Table.js +108 -0
  120. package/dist/component/organism/Table/Table.js.map +1 -0
  121. package/dist/component/organism/ViewTable/ColumnComparator.d.ts +11 -0
  122. package/dist/component/organism/ViewTable/ColumnComparator.js +25 -0
  123. package/dist/component/organism/ViewTable/ColumnComparator.js.map +1 -0
  124. package/dist/component/organism/ViewTable/ViewTable.d.ts +53 -0
  125. package/dist/component/organism/ViewTable/ViewTable.js +123 -0
  126. package/dist/component/organism/ViewTable/ViewTable.js.map +1 -0
  127. package/dist/component/template/CreateFormPage/CreateFormPage.d.ts +19 -0
  128. package/dist/component/template/CreateFormPage/CreateFormPage.js +41 -0
  129. package/dist/component/template/CreateFormPage/CreateFormPage.js.map +1 -0
  130. package/dist/component/template/EditFormPage/EditFormPage.d.ts +19 -0
  131. package/dist/component/template/EditFormPage/EditFormPage.js +42 -0
  132. package/dist/component/template/EditFormPage/EditFormPage.js.map +1 -0
  133. package/dist/component/template/FormPage/FormPage.d.ts +25 -0
  134. package/dist/component/template/FormPage/FormPage.js +130 -0
  135. package/dist/component/template/FormPage/FormPage.js.map +1 -0
  136. package/dist/component/template/FormPage/FormType.d.ts +6 -0
  137. package/dist/component/template/FormPage/FormType.js +12 -0
  138. package/dist/component/template/FormPage/FormType.js.map +1 -0
  139. package/dist/component/template/Layout/Layout.d.ts +21 -0
  140. package/dist/component/template/Layout/Layout.js +121 -0
  141. package/dist/component/template/Layout/Layout.js.map +1 -0
  142. package/dist/component/template/Layout/Layout.module.js +19 -0
  143. package/dist/component/template/Layout/Layout.module.js.map +1 -0
  144. package/dist/component/template/ListPage/ListPage.d.ts +32 -0
  145. package/dist/component/template/ListPage/ListPage.js +47 -0
  146. package/dist/component/template/ListPage/ListPage.js.map +1 -0
  147. package/dist/component/template/PrintPage/PrintPage.d.ts +25 -0
  148. package/dist/component/template/PrintPage/PrintPage.js +115 -0
  149. package/dist/component/template/PrintPage/PrintPage.js.map +1 -0
  150. package/dist/component/template/ViewPage/ViewPage.d.ts +11 -0
  151. package/dist/component/template/ViewPage/ViewPage.js +49 -0
  152. package/dist/component/template/ViewPage/ViewPage.js.map +1 -0
  153. package/dist/data/ListDataSource.d.ts +12 -0
  154. package/dist/data/ListDataSource.js +1 -0
  155. package/dist/data/SaveDataSource.d.ts +16 -0
  156. package/dist/data/SaveDataSource.js +1 -0
  157. package/dist/export/ExportCellText.d.ts +18 -0
  158. package/dist/export/ExportCellText.js +60 -0
  159. package/dist/export/ExportCellText.js.map +1 -0
  160. package/dist/export/ExportDocument.d.ts +127 -0
  161. package/dist/export/ExportDocument.js +59 -0
  162. package/dist/export/ExportDocument.js.map +1 -0
  163. package/dist/export/ExportDownload.d.ts +9 -0
  164. package/dist/export/ExportDownload.js +19 -0
  165. package/dist/export/ExportDownload.js.map +1 -0
  166. package/dist/export/ExportFileName.d.ts +11 -0
  167. package/dist/export/ExportFileName.js +24 -0
  168. package/dist/export/ExportFileName.js.map +1 -0
  169. package/dist/export/ExportFormat.d.ts +30 -0
  170. package/dist/export/ExportFormat.js +1 -0
  171. package/dist/export/ExportFormats.d.ts +12 -0
  172. package/dist/export/ExportFormats.js +16 -0
  173. package/dist/export/ExportFormats.js.map +1 -0
  174. package/dist/export/ExportTexts.d.ts +14 -0
  175. package/dist/export/ExportTexts.js +23 -0
  176. package/dist/export/ExportTexts.js.map +1 -0
  177. package/dist/export/csv/CsvFormat.d.ts +41 -0
  178. package/dist/export/csv/CsvFormat.js +91 -0
  179. package/dist/export/csv/CsvFormat.js.map +1 -0
  180. package/dist/export/xlsx/XlsxFormat.d.ts +35 -0
  181. package/dist/export/xlsx/XlsxFormat.js +66 -0
  182. package/dist/export/xlsx/XlsxFormat.js.map +1 -0
  183. package/dist/export/xlsx/XlsxNumberFormat.d.ts +8 -0
  184. package/dist/export/xlsx/XlsxNumberFormat.js +15 -0
  185. package/dist/export/xlsx/XlsxNumberFormat.js.map +1 -0
  186. package/dist/export/xlsx/XlsxSheetName.d.ts +12 -0
  187. package/dist/export/xlsx/XlsxSheetName.js +62 -0
  188. package/dist/export/xlsx/XlsxSheetName.js.map +1 -0
  189. package/dist/export/xlsx/XlsxSheetWriter.d.ts +67 -0
  190. package/dist/export/xlsx/XlsxSheetWriter.js +215 -0
  191. package/dist/export/xlsx/XlsxSheetWriter.js.map +1 -0
  192. package/dist/export/xlsx/XlsxStyle.d.ts +51 -0
  193. package/dist/export/xlsx/XlsxStyle.js +87 -0
  194. package/dist/export/xlsx/XlsxStyle.js.map +1 -0
  195. package/dist/hook/useIsMobile.d.ts +5 -0
  196. package/dist/hook/useIsMobile.js +16 -0
  197. package/dist/hook/useIsMobile.js.map +1 -0
  198. package/dist/hook/useKeyedService.d.ts +22 -0
  199. package/dist/hook/useKeyedService.js +38 -0
  200. package/dist/hook/useKeyedService.js.map +1 -0
  201. package/dist/hook/useLightColorSchemeOnPrint.d.ts +9 -0
  202. package/dist/hook/useLightColorSchemeOnPrint.js +36 -0
  203. package/dist/hook/useLightColorSchemeOnPrint.js.map +1 -0
  204. package/dist/hook/useService.d.ts +12 -0
  205. package/dist/hook/useService.js +29 -0
  206. package/dist/hook/useService.js.map +1 -0
  207. package/dist/http/ApiError.d.ts +13 -0
  208. package/dist/http/ApiError.js +22 -0
  209. package/dist/http/ApiError.js.map +1 -0
  210. package/dist/http/AxiosHttpClient.d.ts +21 -0
  211. package/dist/http/AxiosHttpClient.js +67 -0
  212. package/dist/http/AxiosHttpClient.js.map +1 -0
  213. package/dist/http/AxiosHttpClientConfig.d.ts +37 -0
  214. package/dist/http/AxiosHttpClientConfig.js +19 -0
  215. package/dist/http/AxiosHttpClientConfig.js.map +1 -0
  216. package/dist/i18n/KitLocale.d.ts +4 -0
  217. package/dist/i18n/KitLocale.js +7 -0
  218. package/dist/i18n/KitLocale.js.map +1 -0
  219. package/dist/i18n/KitMessages.d.ts +146 -0
  220. package/dist/i18n/KitMessages.js +1 -0
  221. package/dist/i18n/messages/enUS.d.ts +3 -0
  222. package/dist/i18n/messages/enUS.js +112 -0
  223. package/dist/i18n/messages/enUS.js.map +1 -0
  224. package/dist/i18n/messages/ptBR.d.ts +3 -0
  225. package/dist/i18n/messages/ptBR.js +97 -0
  226. package/dist/i18n/messages/ptBR.js.map +1 -0
  227. package/dist/i18n/resolveMessages.d.ts +13 -0
  228. package/dist/i18n/resolveMessages.js +29 -0
  229. package/dist/i18n/resolveMessages.js.map +1 -0
  230. package/dist/model/Identifiable.d.ts +7 -0
  231. package/dist/model/Identifiable.js +1 -0
  232. package/dist/provider/KitConfig.d.ts +26 -0
  233. package/dist/provider/KitConfig.js +1 -0
  234. package/dist/provider/KitContext.d.ts +9 -0
  235. package/dist/provider/KitContext.js +27 -0
  236. package/dist/provider/KitContext.js.map +1 -0
  237. package/dist/provider/KitProvider.d.ts +7 -0
  238. package/dist/provider/KitProvider.js +31 -0
  239. package/dist/provider/KitProvider.js.map +1 -0
  240. package/dist/provider/useKit.d.ts +15 -0
  241. package/dist/provider/useKit.js +28 -0
  242. package/dist/provider/useKit.js.map +1 -0
  243. package/dist/router/FormRouteSegments.d.ts +16 -0
  244. package/dist/router/FormRouteSegments.js +19 -0
  245. package/dist/router/FormRouteSegments.js.map +1 -0
  246. package/dist/router/RouterAdapter.d.ts +24 -0
  247. package/dist/router/RouterAdapter.js +1 -0
  248. package/dist/router/anchorRouter.d.ts +7 -0
  249. package/dist/router/anchorRouter.js +23 -0
  250. package/dist/router/anchorRouter.js.map +1 -0
  251. package/dist/styles/print.css +93 -0
  252. package/dist/styles.css +188 -0
  253. package/dist/util/ArrayUtil.d.ts +39 -0
  254. package/dist/util/ArrayUtil.js +48 -0
  255. package/dist/util/ArrayUtil.js.map +1 -0
  256. package/dist/util/ColorUtil.d.ts +7 -0
  257. package/dist/util/ColorUtil.js +16 -0
  258. package/dist/util/ColorUtil.js.map +1 -0
  259. package/dist/util/CompareUtil.d.ts +19 -0
  260. package/dist/util/CompareUtil.js +51 -0
  261. package/dist/util/CompareUtil.js.map +1 -0
  262. package/dist/util/DateUtil.d.ts +37 -0
  263. package/dist/util/DateUtil.js +53 -0
  264. package/dist/util/DateUtil.js.map +1 -0
  265. package/dist/util/DownloadUtil.d.ts +6 -0
  266. package/dist/util/DownloadUtil.js +25 -0
  267. package/dist/util/DownloadUtil.js.map +1 -0
  268. package/dist/util/EnumUtil.d.ts +19 -0
  269. package/dist/util/EnumUtil.js +32 -0
  270. package/dist/util/EnumUtil.js.map +1 -0
  271. package/dist/util/FormUtil.d.ts +8 -0
  272. package/dist/util/FormUtil.js +14 -0
  273. package/dist/util/FormUtil.js.map +1 -0
  274. package/dist/util/HtmlUtil.d.ts +8 -0
  275. package/dist/util/HtmlUtil.js +23 -0
  276. package/dist/util/HtmlUtil.js.map +1 -0
  277. package/dist/util/Notifications.d.ts +30 -0
  278. package/dist/util/Notifications.js +59 -0
  279. package/dist/util/Notifications.js.map +1 -0
  280. package/dist/util/NumberUtil.d.ts +19 -0
  281. package/dist/util/NumberUtil.js +33 -0
  282. package/dist/util/NumberUtil.js.map +1 -0
  283. package/dist/util/ObjectUtil.d.ts +22 -0
  284. package/dist/util/ObjectUtil.js +34 -0
  285. package/dist/util/ObjectUtil.js.map +1 -0
  286. package/dist/util/SearchParamsUtil.d.ts +23 -0
  287. package/dist/util/SearchParamsUtil.js +29 -0
  288. package/dist/util/SearchParamsUtil.js.map +1 -0
  289. package/dist/util/TransformData.d.ts +14 -0
  290. package/dist/util/TransformData.js +31 -0
  291. package/dist/util/TransformData.js.map +1 -0
  292. package/dist/util/ValidationUtil.d.ts +9 -0
  293. package/dist/util/ValidationUtil.js +16 -0
  294. package/dist/util/ValidationUtil.js.map +1 -0
  295. package/dist/util/parseActionsPageParams.d.ts +14 -0
  296. package/dist/util/parseActionsPageParams.js +29 -0
  297. package/dist/util/parseActionsPageParams.js.map +1 -0
  298. package/package.json +149 -5
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { useKitConfig } from "../../../provider/useKit.js";
3
+ import { columnComparator } from "./ColumnComparator.js";
4
+ import { useEffect, useMemo, useState } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { DataTable } from "mantine-datatable";
7
+ // src/component/organism/ViewTable/ViewTable.tsx
8
+ /** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */
9
+ var PAGE_SIZES = [
10
+ 10,
11
+ 20,
12
+ 50,
13
+ 200
14
+ ];
15
+ /** Default sort when neither `defaultSortStatus` nor `compare` is given. */
16
+ var DEFAULT_SORT_STATUS = {
17
+ columnAccessor: "id",
18
+ direction: "asc"
19
+ };
20
+ /**
21
+ * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no
22
+ * header shows the sort indicator, and the first click on any column sorts it in ascending order.
23
+ */
24
+ var NO_COLUMN_SORT_STATUS = {
25
+ columnAccessor: "",
26
+ direction: "asc"
27
+ };
28
+ /**
29
+ * Adds a unique key to each record shown in the table.
30
+ * The array is changed in place, instead of copying each object with destructuring: a copy is a plain object, which
31
+ * loses the accessors (get and set) of class instances, so reading an attribute through them would return nothing.
32
+ * @param data records shown in the table
33
+ * @return the same array, now with a `key` in each object
34
+ */
35
+ function addKeyToDataArray(data) {
36
+ data.forEach((element, index) => Object.assign(element, { key: index.toString() }));
37
+ return data;
38
+ }
39
+ /**
40
+ * The ViewTable component is a wrapper for the Mantine DataTable component.
41
+ * @template M - Model that extends from {@link Identifiable}
42
+ * @param data the data to be displayed in this ViewTable
43
+ * @param {DataTableColumn<M>[]} columns - The columns to be displayed in this ViewTable
44
+ * @param loading Indicates if the data to be displayed is being loaded yet or not
45
+ * @param pagination Indicates whether to show pagination or not.
46
+ * @param getPaginationControlProps extra attributes for the pagination control buttons
47
+ * @param compare comparison function of the initial order, replaced by the sort of the column chosen in the header
48
+ * @param rest remaining props, passed to the DataTable
49
+ * @return the table with the data, sortable and (optionally) paginated
50
+ */
51
+ var ViewTable = ({ data, columns, loading, pagination = true, showIdColumn = true, defaultSortStatus, compare, getPaginationControlProps, ...rest }) => {
52
+ const { locale, messages } = useKitConfig();
53
+ const [page, setPage] = useState(1);
54
+ const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);
55
+ const [sortStatus, setSortStatus] = useState(defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS));
56
+ const [compareActive, setCompareActive] = useState(compare !== void 0);
57
+ const sortedData = useMemo(() => {
58
+ const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);
59
+ return [...data].sort(comparator);
60
+ }, [
61
+ data,
62
+ sortStatus,
63
+ compareActive,
64
+ compare,
65
+ locale
66
+ ]);
67
+ const recordsWithKeys = addKeyToDataArray(useMemo(() => {
68
+ if (!pagination) return sortedData;
69
+ const from = (page - 1) * pageSize;
70
+ return sortedData.slice(from, from + pageSize);
71
+ }, [
72
+ sortedData,
73
+ pagination,
74
+ page,
75
+ pageSize
76
+ ]));
77
+ function changeSortStatus(status) {
78
+ setCompareActive(false);
79
+ setSortStatus(status);
80
+ }
81
+ const effectiveColumns = [{
82
+ title: "ID",
83
+ accessor: "id",
84
+ hidden: !showIdColumn,
85
+ sortable: true,
86
+ render: (item) => /* @__PURE__ */ jsx("span", { children: item.id })
87
+ }, ...columns];
88
+ useEffect(() => {
89
+ setPage(1);
90
+ }, [pageSize]);
91
+ useEffect(() => {
92
+ const totalPages = Math.ceil(data.length / pageSize);
93
+ setPage((currentPage) => totalPages && currentPage > totalPages ? totalPages : currentPage);
94
+ }, [data.length, pageSize]);
95
+ const paginationProps = pagination ? {
96
+ page,
97
+ onPageChange: (p) => setPage(p),
98
+ totalRecords: data.length,
99
+ recordsPerPage: pageSize,
100
+ recordsPerPageOptions: PAGE_SIZES,
101
+ onRecordsPerPageChange: setPageSize,
102
+ recordsPerPageLabel: messages.table.recordsPerPage,
103
+ paginationText: messages.table.pagination,
104
+ getPaginationControlProps
105
+ } : {};
106
+ return /* @__PURE__ */ jsx(DataTable, {
107
+ minHeight: 200,
108
+ verticalSpacing: "sm",
109
+ striped: true,
110
+ columns: effectiveColumns,
111
+ records: recordsWithKeys,
112
+ noRecordsText: messages.table.noRecords,
113
+ fetching: loading,
114
+ sortStatus,
115
+ onSortStatusChange: changeSortStatus,
116
+ customRowAttributes: (record) => ({ "data-testid": `row-${record.id}` }),
117
+ ...paginationProps,
118
+ ...rest
119
+ });
120
+ };
121
+ export { ViewTable as default };
122
+
123
+ //# sourceMappingURL=ViewTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewTable.js","names":[],"sources":["../../../../src/component/organism/ViewTable/ViewTable.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\nimport {\n DataTable,\n DataTableColumn,\n DataTablePaginationProps,\n DataTableProps,\n DataTableSortStatus\n} from 'mantine-datatable';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useKitConfig } from '../../../provider/useKit';\nimport { columnComparator, Comparator } from './ColumnComparator';\n\n/** Options of records per page. The largest one lets long tables (and end-to-end tests) show every row in one page. */\nconst PAGE_SIZES = [10, 20, 50, 200];\n\n/** Default sort when neither `defaultSortStatus` nor `compare` is given. */\nconst DEFAULT_SORT_STATUS = { columnAccessor: 'id', direction: 'asc' } as const;\n\n/**\n * Sort used while the `compare` function applies, when `defaultSortStatus` is not given: it matches no column, so no\n * header shows the sort indicator, and the first click on any column sorts it in ascending order.\n */\nconst NO_COLUMN_SORT_STATUS = { columnAccessor: '', direction: 'asc' } as const;\n\n/** Function that defines extra attributes (e.g. aria-label) for each pagination control button. */\ntype PaginationControlPropsGetter = Extract<DataTablePaginationProps, { page: number }>['getPaginationControlProps'];\n\n/**\n * Props of the {@link DataTable} passed straight through by the ViewTable.\n * Only the props actually used by the callers are accepted: using `Partial<DataTableProps<M>>` makes TypeScript combine\n * the variants of the `DataTableProps` union with the ones of `paginationProps`, which exhausts the memory of `tsc`.\n */\ntype DataTablePassThroughProps<M extends Identifiable> = Pick<\n DataTableProps<M>,\n | 'onRowClick'\n | 'rowStyle'\n | 'noRecordsText'\n | 'highlightOnHover'\n | 'highlightOnHoverColor'\n | 'className'\n | 'minHeight'\n | 'withColumnBorders'\n | 'verticalSpacing'\n | 'horizontalSpacing'\n>;\n\n/**\n * TableProps is a generic type for the ViewTable component props.\n */\ntype TableProps<M extends Identifiable> = {\n data: M[];\n columns: DataTableColumn<M>[];\n loading?: boolean;\n pagination?: boolean;\n /** Shows the ID column, automatically added to the given columns. */\n showIdColumn?: boolean;\n /**\n * Initial sort of the table. Default: by `id`, ascending.\n * Give another value when the natural order of the data should not be by `id` (e.g. when the backend already\n * returns the data in an order to keep). Along with `compare`, it only tells which header shows the sort indicator\n * in the initial order.\n */\n defaultSortStatus?: DataTableSortStatus<M>;\n /**\n * Comparison function that defines the initial order, instead of the column of `defaultSortStatus`. It allows\n * sorting by more than one criterion (e.g. city and, in the same city, year descending). After the user clicks the\n * header of a column, the sort of the column applies.\n */\n compare?: Comparator<M>;\n /** Extra attributes for the pagination control buttons. It only has effect when `pagination` is true. */\n getPaginationControlProps?: PaginationControlPropsGetter;\n} & DataTablePassThroughProps<M>;\n\n/**\n * Adds a unique key to each record shown in the table.\n * The array is changed in place, instead of copying each object with destructuring: a copy is a plain object, which\n * loses the accessors (get and set) of class instances, so reading an attribute through them would return nothing.\n * @param data records shown in the table\n * @return the same array, now with a `key` in each object\n */\nfunction addKeyToDataArray<M extends Identifiable>(data: M[]): M[] {\n data.forEach((element, index) => Object.assign(element, { key: index.toString() }));\n return data;\n}\n\n/**\n * The ViewTable component is a wrapper for the Mantine DataTable component.\n * @template M - Model that extends from {@link Identifiable}\n * @param data the data to be displayed in this ViewTable\n * @param {DataTableColumn<M>[]} columns - The columns to be displayed in this ViewTable\n * @param loading Indicates if the data to be displayed is being loaded yet or not\n * @param pagination Indicates whether to show pagination or not.\n * @param getPaginationControlProps extra attributes for the pagination control buttons\n * @param compare comparison function of the initial order, replaced by the sort of the column chosen in the header\n * @param rest remaining props, passed to the DataTable\n * @return the table with the data, sortable and (optionally) paginated\n */\nconst ViewTable = <M extends Identifiable>({\n data,\n columns,\n loading,\n pagination = true,\n showIdColumn = true,\n defaultSortStatus,\n compare,\n getPaginationControlProps,\n ...rest\n}: TableProps<M>) => {\n const { locale, messages } = useKitConfig();\n const [page, setPage] = useState(1);\n const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);\n const [sortStatus, setSortStatus] = useState<DataTableSortStatus<M>>(\n defaultSortStatus ?? (compare ? NO_COLUMN_SORT_STATUS : DEFAULT_SORT_STATUS)\n );\n // The `compare` function applies until the user chooses a column in the header.\n const [compareActive, setCompareActive] = useState(compare !== undefined);\n\n /*\n * Sorts all records before cutting the page, so each page (including in descending order) shows its part of the\n * whole set. The copy keeps the order of the given array. It is separate from the page cut, so not everything is\n * sorted again on every change of page or page size.\n */\n const sortedData = useMemo(() => {\n const comparator = compareActive && compare ? compare : columnComparator(sortStatus, locale);\n\n return [...data].sort(comparator);\n }, [data, sortStatus, compareActive, compare, locale]);\n\n // Without pagination, shows all records (e.g. a printed report), not only the ones of the first page.\n const records = useMemo(() => {\n if (!pagination) return sortedData;\n\n const from = (page - 1) * pageSize;\n return sortedData.slice(from, from + pageSize);\n }, [sortedData, pagination, page, pageSize]);\n const recordsWithKeys = addKeyToDataArray(records);\n\n function changeSortStatus(status: DataTableSortStatus<M>): void {\n setCompareActive(false);\n setSortStatus(status);\n }\n\n const effectiveColumns: DataTableColumn<M>[] = [\n {\n title: 'ID',\n accessor: 'id',\n hidden: !showIdColumn,\n sortable: true,\n render: item => <span>{item.id}</span>\n },\n ...columns\n ];\n\n useEffect(() => {\n setPage(1);\n }, [pageSize]);\n\n /* If the data shrinks, goes back to the last existing page. The functional update avoids using an outdated `page`\n when the effect above has just gone back to page 1. */\n useEffect(() => {\n const totalPages = Math.ceil(data.length / pageSize);\n setPage(currentPage => (totalPages && currentPage > totalPages ? totalPages : currentPage));\n }, [data.length, pageSize]);\n\n /* Typed only with the pagination props (and not as Partial<DataTableProps>) to not exhaust the memory of tsc.\n See the comment of DataTablePassThroughProps. */\n const paginationProps: DataTablePaginationProps = pagination\n ? {\n page,\n onPageChange: p => setPage(p),\n totalRecords: data.length,\n recordsPerPage: pageSize,\n recordsPerPageOptions: PAGE_SIZES,\n onRecordsPerPageChange: setPageSize,\n recordsPerPageLabel: messages.table.recordsPerPage,\n paginationText: messages.table.pagination,\n getPaginationControlProps\n }\n : {};\n\n // This DataTable is the one of https://www.npmjs.com/package/mantine-datatable (there are other libraries with this name).\n return (\n <DataTable\n minHeight={200}\n verticalSpacing=\"sm\"\n striped\n columns={effectiveColumns}\n records={recordsWithKeys}\n noRecordsText={messages.table.noRecords}\n fetching={loading}\n sortStatus={sortStatus}\n onSortStatusChange={changeSortStatus}\n customRowAttributes={record => ({ 'data-testid': `row-${record.id}` })}\n {...paginationProps}\n {...rest}\n />\n );\n};\n\nexport default ViewTable;\n"],"mappings":";;;;;;;;AAiBA,IAAM,aAAa;CAAC;CAAI;CAAI;CAAI;AAAG;;AAGnC,IAAM,sBAAsB;CAAE,gBAAgB;CAAM,WAAW;AAAM;;;;;AAMrE,IAAM,wBAAwB;CAAE,gBAAgB;CAAI,WAAW;AAAM;;;;;;;;AA0DrE,SAAS,kBAA0C,MAAgB;CAC/D,KAAK,SAAS,SAAS,UAAU,OAAO,OAAO,SAAS,EAAE,KAAK,MAAM,SAAS,EAAE,CAAC,CAAC;CAClF,OAAO;AACX;;;;;;;;;;;;;AAcA,IAAM,aAAqC,EACvC,MACA,SACA,SACA,aAAa,MACb,eAAe,MACf,mBACA,SACA,2BACA,GAAG,WACc;CACjB,MAAM,EAAE,QAAQ,aAAa,aAAa;CAC1C,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,CAAC,UAAU,eAAe,SAAS,WAAW,EAAE;CACtD,MAAM,CAAC,YAAY,iBAAiB,SAChC,sBAAsB,UAAU,wBAAwB,oBAC5D;CAEA,MAAM,CAAC,eAAe,oBAAoB,SAAS,YAAY,KAAA,CAAS;CAOxE,MAAM,aAAa,cAAc;EAC7B,MAAM,aAAa,iBAAiB,UAAU,UAAU,iBAAiB,YAAY,MAAM;EAE3F,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,UAAU;CACpC,GAAG;EAAC;EAAM;EAAY;EAAe;EAAS;CAAM,CAAC;CASrD,MAAM,kBAAkB,kBANR,cAAc;EAC1B,IAAI,CAAC,YAAY,OAAO;EAExB,MAAM,QAAQ,OAAO,KAAK;EAC1B,OAAO,WAAW,MAAM,MAAM,OAAO,QAAQ;CACjD,GAAG;EAAC;EAAY;EAAY;EAAM;CAAQ,CACA,CAAO;CAEjD,SAAS,iBAAiB,QAAsC;EAC5D,iBAAiB,KAAK;EACtB,cAAc,MAAM;CACxB;CAEA,MAAM,mBAAyC,CAC3C;EACI,OAAO;EACP,UAAU;EACV,QAAQ,CAAC;EACT,UAAU;EACV,SAAQ,SAAQ,oBAAC,QAAD,EAAA,UAAO,KAAK,GAAS,CAAA;CACzC,GACA,GAAG,OACP;CAEA,gBAAgB;EACZ,QAAQ,CAAC;CACb,GAAG,CAAC,QAAQ,CAAC;CAIb,gBAAgB;EACZ,MAAM,aAAa,KAAK,KAAK,KAAK,SAAS,QAAQ;EACnD,SAAQ,gBAAgB,cAAc,cAAc,aAAa,aAAa,WAAY;CAC9F,GAAG,CAAC,KAAK,QAAQ,QAAQ,CAAC;CAI1B,MAAM,kBAA4C,aAC5C;EACI;EACA,eAAc,MAAK,QAAQ,CAAC;EAC5B,cAAc,KAAK;EACnB,gBAAgB;EAChB,uBAAuB;EACvB,wBAAwB;EACxB,qBAAqB,SAAS,MAAM;EACpC,gBAAgB,SAAS,MAAM;EAC/B;CACJ,IACA,CAAC;CAGP,OACI,oBAAC,WAAD;EACI,WAAW;EACX,iBAAgB;EAChB,SAAA;EACA,SAAS;EACT,SAAS;EACT,eAAe,SAAS,MAAM;EAC9B,UAAU;EACE;EACZ,oBAAoB;EACpB,sBAAqB,YAAW,EAAE,eAAe,OAAO,OAAO,KAAK;EACpE,GAAI;EACJ,GAAI;CACP,CAAA;AAET"}
@@ -0,0 +1,19 @@
1
+ import React, { ReactElement } from 'react';
2
+ import type { UseFormReturnType } from '@mantine/form';
3
+ import { Identifiable } from '../../../model/Identifiable';
4
+ import { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';
5
+ interface CreateFormPageProps<T extends Identifiable, D extends object> {
6
+ /** Name of the record (e.g. "Region"). */
7
+ title: string;
8
+ breadcrumbItems: BreadcrumbItem[];
9
+ /** Sends the data of the new record (e.g. to the API). */
10
+ fetchMethod: (data: D) => Promise<T>;
11
+ form: UseFormReturnType<D>;
12
+ backRoute: string;
13
+ isSubmitDisabled?: boolean;
14
+ /** Question shown in a confirmation dialog before saving; absent to save right away. */
15
+ confirmationMessage?: string;
16
+ }
17
+ /** Form page to create a record, which sends the data with `fetchMethod`. */
18
+ declare function CreateFormPage<T extends Identifiable, D extends object>(props: React.PropsWithChildren<CreateFormPageProps<T, D>>): ReactElement;
19
+ export default CreateFormPage;
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { useDocumentTitle, useKitMessages } from "../../../provider/useKit.js";
3
+ import DataForm from "../../organism/DataForm/DataForm.js";
4
+ import PageHeader from "../../organism/PageHeader/PageHeader.js";
5
+ import "react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Stack } from "@mantine/core";
8
+ // src/component/template/CreateFormPage/CreateFormPage.tsx
9
+ /** Form page to create a record, which sends the data with `fetchMethod`. */
10
+ function CreateFormPage(props) {
11
+ const { title, breadcrumbItems, fetchMethod, form, backRoute, isSubmitDisabled, children, confirmationMessage } = props;
12
+ const messages = useKitMessages();
13
+ const pageTitle = `${messages.form.action.create} ${title}`;
14
+ const documentTitle = useDocumentTitle(pageTitle);
15
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("title", { children: documentTitle }), /* @__PURE__ */ jsxs(Stack, {
16
+ gap: "lg",
17
+ children: [/* @__PURE__ */ jsx(PageHeader, {
18
+ title: pageTitle,
19
+ breadcrumbItems: [...breadcrumbItems, {
20
+ title: pageTitle,
21
+ href: "#"
22
+ }]
23
+ }), /* @__PURE__ */ jsx(DataForm, {
24
+ form,
25
+ fetchMethod,
26
+ title,
27
+ backRoute,
28
+ submit: {
29
+ title: messages.common.save,
30
+ successMsg: messages.form.createSuccess,
31
+ errorMsg: messages.form.createError,
32
+ disabled: isSubmitDisabled,
33
+ confirmationMessage
34
+ },
35
+ children
36
+ })]
37
+ })] });
38
+ }
39
+ export { CreateFormPage as default };
40
+
41
+ //# sourceMappingURL=CreateFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateFormPage.js","names":["_Fragment"],"sources":["../../../../src/component/template/CreateFormPage/CreateFormPage.tsx"],"sourcesContent":["import React, { ReactElement } from 'react';\n\nimport { Stack } from '@mantine/core';\nimport type { UseFormReturnType } from '@mantine/form';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useDocumentTitle, useKitMessages } from '../../../provider/useKit';\nimport { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';\nimport DataForm from '../../organism/DataForm/DataForm';\nimport PageHeader from '../../organism/PageHeader/PageHeader';\n\ninterface CreateFormPageProps<T extends Identifiable, D extends object> {\n /** Name of the record (e.g. \"Region\"). */\n title: string;\n breadcrumbItems: BreadcrumbItem[];\n\n /** Sends the data of the new record (e.g. to the API). */\n fetchMethod: (data: D) => Promise<T>;\n form: UseFormReturnType<D>;\n backRoute: string;\n isSubmitDisabled?: boolean;\n\n /** Question shown in a confirmation dialog before saving; absent to save right away. */\n confirmationMessage?: string;\n}\n\n/** Form page to create a record, which sends the data with `fetchMethod`. */\nfunction CreateFormPage<T extends Identifiable, D extends object>(\n props: React.PropsWithChildren<CreateFormPageProps<T, D>>\n): ReactElement {\n const { title, breadcrumbItems, fetchMethod, form, backRoute, isSubmitDisabled, children, confirmationMessage } =\n props;\n const messages = useKitMessages();\n const pageTitle = `${messages.form.action.create} ${title}`;\n const documentTitle = useDocumentTitle(pageTitle);\n\n return (\n <>\n <title>{documentTitle}</title>\n <Stack gap=\"lg\">\n <PageHeader title={pageTitle} breadcrumbItems={[...breadcrumbItems, { title: pageTitle, href: '#' }]} />\n\n <DataForm\n form={form}\n fetchMethod={fetchMethod}\n title={title}\n backRoute={backRoute}\n submit={{\n title: messages.common.save,\n successMsg: messages.form.createSuccess,\n errorMsg: messages.form.createError,\n disabled: isSubmitDisabled,\n confirmationMessage\n }}\n >\n {children}\n </DataForm>\n </Stack>\n </>\n );\n}\n\nexport default CreateFormPage;\n"],"mappings":";;;;;;;;;AA2BA,SAAS,eACL,OACY;CACZ,MAAM,EAAE,OAAO,iBAAiB,aAAa,MAAM,WAAW,kBAAkB,UAAU,wBACtF;CACJ,MAAM,WAAW,eAAe;CAChC,MAAM,YAAY,GAAG,SAAS,KAAK,OAAO,OAAO,GAAG;CACpD,MAAM,gBAAgB,iBAAiB,SAAS;CAEhD,OACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,SAAD,EAAA,UAAQ,cAAqB,CAAA,GAC7B,qBAAC,OAAD;EAAO,KAAI;EAAX,UAAA,CACI,oBAAC,YAAD;GAAY,OAAO;GAAW,iBAAiB,CAAC,GAAG,iBAAiB;IAAE,OAAO;IAAW,MAAM;GAAI,CAAC;EAAI,CAAA,GAEvG,oBAAC,UAAD;GACU;GACO;GACN;GACI;GACX,QAAQ;IACJ,OAAO,SAAS,OAAO;IACvB,YAAY,SAAS,KAAK;IAC1B,UAAU,SAAS,KAAK;IACxB,UAAU;IACV;GACJ;GAEC;EACK,CAAA,CACP;CACT,CAAA,CAAA,EAAA,CAAA;AAEV"}
@@ -0,0 +1,19 @@
1
+ import React, { ReactElement } from 'react';
2
+ import type { UseFormReturnType } from '@mantine/form';
3
+ import { Identifiable } from '../../../model/Identifiable';
4
+ import { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';
5
+ interface EditFormPageProps<T extends Identifiable, D extends object> {
6
+ /** Name of the record (e.g. "Region"). */
7
+ title: string;
8
+ breadcrumbItems: BreadcrumbItem[];
9
+ /** Sends the changed data of the record (e.g. to the API). */
10
+ fetchMethod: (data: D) => Promise<T>;
11
+ form: UseFormReturnType<D>;
12
+ backRoute: string;
13
+ isSubmitDisabled?: boolean;
14
+ /** Question shown in a confirmation dialog before saving; absent to save right away. */
15
+ confirmationMessage?: string;
16
+ }
17
+ /** Form page to update a record, which sends the data with `fetchMethod`. */
18
+ declare function EditFormPage<T extends Identifiable, D extends object>(props: React.PropsWithChildren<EditFormPageProps<T, D>>): ReactElement;
19
+ export default EditFormPage;
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useDocumentTitle, useKitMessages } from "../../../provider/useKit.js";
3
+ import DataForm from "../../organism/DataForm/DataForm.js";
4
+ import PageHeader from "../../organism/PageHeader/PageHeader.js";
5
+ import "react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Stack } from "@mantine/core";
8
+ // src/component/template/EditFormPage/EditFormPage.tsx
9
+ /** Form page to update a record, which sends the data with `fetchMethod`. */
10
+ function EditFormPage(props) {
11
+ const { title, breadcrumbItems, fetchMethod, form, backRoute, isSubmitDisabled, children, confirmationMessage } = props;
12
+ const messages = useKitMessages();
13
+ const action = messages.form.action.edit;
14
+ const pageTitle = `${action} ${title}`;
15
+ const documentTitle = useDocumentTitle(pageTitle);
16
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("title", { children: documentTitle }), /* @__PURE__ */ jsxs(Stack, {
17
+ gap: "lg",
18
+ children: [/* @__PURE__ */ jsx(PageHeader, {
19
+ title: pageTitle,
20
+ breadcrumbItems: [...breadcrumbItems, {
21
+ title: pageTitle,
22
+ href: "#"
23
+ }]
24
+ }), /* @__PURE__ */ jsx(DataForm, {
25
+ form,
26
+ fetchMethod,
27
+ title,
28
+ backRoute,
29
+ submit: {
30
+ title: action,
31
+ successMsg: messages.form.updateSuccess,
32
+ errorMsg: messages.form.updateError,
33
+ disabled: isSubmitDisabled,
34
+ confirmationMessage
35
+ },
36
+ children
37
+ })]
38
+ })] });
39
+ }
40
+ export { EditFormPage as default };
41
+
42
+ //# sourceMappingURL=EditFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditFormPage.js","names":["_Fragment"],"sources":["../../../../src/component/template/EditFormPage/EditFormPage.tsx"],"sourcesContent":["import React, { ReactElement } from 'react';\n\nimport { Stack } from '@mantine/core';\nimport type { UseFormReturnType } from '@mantine/form';\n\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useDocumentTitle, useKitMessages } from '../../../provider/useKit';\nimport { BreadcrumbItem } from '../../molecule/Breadcrumb/Breadcrumb';\nimport DataForm from '../../organism/DataForm/DataForm';\nimport PageHeader from '../../organism/PageHeader/PageHeader';\n\ninterface EditFormPageProps<T extends Identifiable, D extends object> {\n /** Name of the record (e.g. \"Region\"). */\n title: string;\n breadcrumbItems: BreadcrumbItem[];\n\n /** Sends the changed data of the record (e.g. to the API). */\n fetchMethod: (data: D) => Promise<T>;\n form: UseFormReturnType<D>;\n backRoute: string;\n isSubmitDisabled?: boolean;\n\n /** Question shown in a confirmation dialog before saving; absent to save right away. */\n confirmationMessage?: string;\n}\n\n/** Form page to update a record, which sends the data with `fetchMethod`. */\nfunction EditFormPage<T extends Identifiable, D extends object>(\n props: React.PropsWithChildren<EditFormPageProps<T, D>>\n): ReactElement {\n const { title, breadcrumbItems, fetchMethod, form, backRoute, isSubmitDisabled, children, confirmationMessage } =\n props;\n const messages = useKitMessages();\n const action = messages.form.action.edit;\n const pageTitle = `${action} ${title}`;\n const documentTitle = useDocumentTitle(pageTitle);\n\n return (\n <>\n <title>{documentTitle}</title>\n <Stack gap=\"lg\">\n <PageHeader title={pageTitle} breadcrumbItems={[...breadcrumbItems, { title: pageTitle, href: '#' }]} />\n\n <DataForm\n form={form}\n fetchMethod={fetchMethod}\n title={title}\n backRoute={backRoute}\n submit={{\n title: action,\n successMsg: messages.form.updateSuccess,\n errorMsg: messages.form.updateError,\n disabled: isSubmitDisabled,\n confirmationMessage\n }}\n >\n {children}\n </DataForm>\n </Stack>\n </>\n );\n}\n\nexport default EditFormPage;\n"],"mappings":";;;;;;;;;AA2BA,SAAS,aACL,OACY;CACZ,MAAM,EAAE,OAAO,iBAAiB,aAAa,MAAM,WAAW,kBAAkB,UAAU,wBACtF;CACJ,MAAM,WAAW,eAAe;CAChC,MAAM,SAAS,SAAS,KAAK,OAAO;CACpC,MAAM,YAAY,GAAG,OAAO,GAAG;CAC/B,MAAM,gBAAgB,iBAAiB,SAAS;CAEhD,OACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,SAAD,EAAA,UAAQ,cAAqB,CAAA,GAC7B,qBAAC,OAAD;EAAO,KAAI;EAAX,UAAA,CACI,oBAAC,YAAD;GAAY,OAAO;GAAW,iBAAiB,CAAC,GAAG,iBAAiB;IAAE,OAAO;IAAW,MAAM;GAAI,CAAC;EAAI,CAAA,GAEvG,oBAAC,UAAD;GACU;GACO;GACN;GACI;GACX,QAAQ;IACJ,OAAO;IACP,YAAY,SAAS,KAAK;IAC1B,UAAU,SAAS,KAAK;IACxB,UAAU;IACV;GACJ;GAEC;EACK,CAAA,CACP;CACT,CAAA,CAAA,EAAA,CAAA;AAEV"}
@@ -0,0 +1,25 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { UseFormReturnType } from '@mantine/form';
3
+ import { SaveDataSource } from '../../../data/SaveDataSource';
4
+ import { Identifiable } from '../../../model/Identifiable';
5
+ import { FormType } from './FormType';
6
+ interface FormPageProps<T extends Identifiable, D extends object, S extends SaveDataSource<T, D>> {
7
+ /** Destination of the data (e.g. a service that accesses an API). */
8
+ service: S;
9
+ /** Name of the record (e.g. "Region"), shown after the action verb in the title. */
10
+ title: string;
11
+ formType: FormType;
12
+ form: UseFormReturnType<D & Identifiable>;
13
+ /** Page opened after saving and by the back button; absent for the first segment of the current path. */
14
+ backRoute?: string;
15
+ /** Shows a loading overlay over the form (e.g. while the record is fetched). */
16
+ isLoading?: boolean;
17
+ /** Whether the form takes the whole width, instead of half of it on large screens. */
18
+ isFullWidth?: boolean;
19
+ }
20
+ /**
21
+ * CRUD form page to create, edit or view a record (according to `formType`), which saves the data in `service` and
22
+ * goes back to the list page.
23
+ */
24
+ declare function FormPage<T extends Identifiable, D extends object, S extends SaveDataSource<T, D>>(props: React.PropsWithChildren<FormPageProps<T, D, S>>): ReactElement;
25
+ export default FormPage;
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import { useDocumentTitle, useKitConfig } from "../../../provider/useKit.js";
3
+ import { formatValues } from "../../../util/TransformData.js";
4
+ import { showErrorNotification, showSuccessNotification } from "../../../util/Notifications.js";
5
+ import PaperCard from "../../molecule/PaperCard/PaperCard.js";
6
+ import PageHeader from "../../organism/PageHeader/PageHeader.js";
7
+ import { FormType } from "./FormType.js";
8
+ import { useState } from "react";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Alert, Box, Button, Grid, Group, LoadingOverlay, Stack, Text } from "@mantine/core";
11
+ import { IconArrowBack, IconDeviceFloppy, IconInfoCircle } from "@tabler/icons-react";
12
+ // src/component/template/FormPage/FormPage.tsx
13
+ /** Action of each form type, the key of its verb in the texts. */
14
+ var FORM_ACTIONS = {
15
+ [FormType.Create]: "create",
16
+ [FormType.Edit]: "edit",
17
+ [FormType.View]: "view"
18
+ };
19
+ /**
20
+ * CRUD form page to create, edit or view a record (according to `formType`), which saves the data in `service` and
21
+ * goes back to the list page.
22
+ */
23
+ function FormPage(props) {
24
+ const { title, formType, service, form, backRoute, isLoading, isFullWidth, children } = props;
25
+ const { router, messages } = useKitConfig();
26
+ const { Link } = router;
27
+ const navigate = router.useNavigate();
28
+ const pathname = router.usePathname();
29
+ /**
30
+ * Checks whether a field of the validation schema is missing in the form.
31
+ * @param fieldName name of the field to find
32
+ */
33
+ function isFormFieldMissing(fieldName) {
34
+ return !form.getInputNode(fieldName);
35
+ }
36
+ /**
37
+ * Shows the errors of the fields that are in the validation schema given to `useForm`, but not in the form.
38
+ * Without it, these errors are not shown to the user (not even in the console), so neither the user nor the
39
+ * developer can understand why the form is not submitted.
40
+ */
41
+ function showErrorsForMissingFormFields() {
42
+ const errors = Object.entries(form.errors).map(([fieldName, errorMsg]) => ({
43
+ fieldName,
44
+ errorMsg
45
+ })).filter(({ fieldName }) => isFormFieldMissing(fieldName)).map(({ fieldName, errorMsg }) => /* @__PURE__ */ jsx("li", { children: errorMsg }, fieldName));
46
+ const icon = /* @__PURE__ */ jsx(IconInfoCircle, {});
47
+ return errors.length > 0 && /* @__PURE__ */ jsx(Alert, {
48
+ color: "red",
49
+ icon,
50
+ children: /* @__PURE__ */ jsx("ul", { children: errors })
51
+ });
52
+ }
53
+ const { id } = form.values;
54
+ const [loading, setLoading] = useState(false);
55
+ const effectiveBackRoute = backRoute || `/${pathname.split("/").at(1)}`;
56
+ const canShowSubmitButton = formType === FormType.Create || formType === FormType.Edit;
57
+ const formTypeTitle = messages.form.action[FORM_ACTIONS[formType] ?? "custom"];
58
+ const documentTitle = useDocumentTitle(`${formTypeTitle} ${title}`);
59
+ const handleSubmit = async (data) => {
60
+ setLoading(true);
61
+ try {
62
+ const effectiveData = formatValues(data);
63
+ await service.save(id, effectiveData);
64
+ showSuccessNotification(messages.form.saveSuccess(formTypeTitle), messages.common.success);
65
+ if (effectiveBackRoute) navigate(effectiveBackRoute);
66
+ } catch (e) {
67
+ showErrorNotification(e, messages.form.saveError(formTypeTitle), messages.common.error);
68
+ } finally {
69
+ setLoading(false);
70
+ }
71
+ };
72
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("title", { children: documentTitle }), /* @__PURE__ */ jsxs(Stack, {
73
+ gap: "lg",
74
+ children: [/* @__PURE__ */ jsx(PageHeader, {
75
+ title: `${formTypeTitle} ${title}`,
76
+ breadcrumbItems: [{
77
+ title,
78
+ href: effectiveBackRoute
79
+ }, {
80
+ title: `${formTypeTitle} ${title}`,
81
+ href: "#"
82
+ }]
83
+ }), /* @__PURE__ */ jsx(Grid, { children: /* @__PURE__ */ jsx(Grid.Col, {
84
+ span: {
85
+ base: 12,
86
+ lg: isFullWidth ? 12 : 6
87
+ },
88
+ children: /* @__PURE__ */ jsx(PaperCard, { children: /* @__PURE__ */ jsxs(Box, {
89
+ component: "form",
90
+ onSubmit: form.onSubmit(handleSubmit),
91
+ pos: "relative",
92
+ children: [/* @__PURE__ */ jsx(LoadingOverlay, {
93
+ visible: isLoading,
94
+ zIndex: 1e3,
95
+ overlayProps: {
96
+ radius: "sm",
97
+ blur: 2
98
+ }
99
+ }), /* @__PURE__ */ jsxs(Stack, { children: [
100
+ /* @__PURE__ */ jsxs(Text, {
101
+ size: "lg",
102
+ fw: 600,
103
+ children: [
104
+ formTypeTitle,
105
+ " ",
106
+ title
107
+ ]
108
+ }),
109
+ children,
110
+ showErrorsForMissingFormFields(),
111
+ /* @__PURE__ */ jsxs(Group, { children: [/* @__PURE__ */ jsx(Button, {
112
+ leftSection: /* @__PURE__ */ jsx(IconArrowBack, { size: 16 }),
113
+ component: Link,
114
+ href: effectiveBackRoute,
115
+ variant: "default",
116
+ children: messages.common.back
117
+ }), canShowSubmitButton && /* @__PURE__ */ jsx(Button, {
118
+ type: "submit",
119
+ leftSection: /* @__PURE__ */ jsx(IconDeviceFloppy, { size: 16 }),
120
+ loading,
121
+ children: formTypeTitle
122
+ })] })
123
+ ] })]
124
+ }) })
125
+ }) })]
126
+ })] });
127
+ }
128
+ export { FormPage as default };
129
+
130
+ //# sourceMappingURL=FormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormPage.js","names":["_Fragment"],"sources":["../../../../src/component/template/FormPage/FormPage.tsx"],"sourcesContent":["import React, { ReactElement, useState } from 'react';\n\nimport { Alert, Box, Button, Grid, Group, LoadingOverlay, Stack, Text } from '@mantine/core';\nimport { UseFormReturnType } from '@mantine/form';\nimport { IconArrowBack, IconDeviceFloppy, IconInfoCircle } from '@tabler/icons-react';\n\nimport { SaveDataSource } from '../../../data/SaveDataSource';\nimport { FormAction } from '../../../i18n/KitMessages';\nimport { Identifiable } from '../../../model/Identifiable';\nimport { useDocumentTitle, useKitConfig } from '../../../provider/useKit';\nimport { formatValues } from '../../../util/TransformData';\nimport { showErrorNotification, showSuccessNotification } from '../../../util/Notifications';\nimport PaperCard from '../../molecule/PaperCard/PaperCard';\nimport PageHeader from '../../organism/PageHeader/PageHeader';\nimport { FormType } from './FormType';\n\ninterface FormPageProps<T extends Identifiable, D extends object, S extends SaveDataSource<T, D>> {\n /** Destination of the data (e.g. a service that accesses an API). */\n service: S;\n\n /** Name of the record (e.g. \"Region\"), shown after the action verb in the title. */\n title: string;\n formType: FormType;\n form: UseFormReturnType<D & Identifiable>;\n\n /** Page opened after saving and by the back button; absent for the first segment of the current path. */\n backRoute?: string;\n\n /** Shows a loading overlay over the form (e.g. while the record is fetched). */\n isLoading?: boolean;\n\n /** Whether the form takes the whole width, instead of half of it on large screens. */\n isFullWidth?: boolean;\n}\n\n/** Action of each form type, the key of its verb in the texts. */\nconst FORM_ACTIONS: Record<FormType, FormAction> = {\n [FormType.Create]: 'create',\n [FormType.Edit]: 'edit',\n [FormType.View]: 'view'\n};\n\n/**\n * CRUD form page to create, edit or view a record (according to `formType`), which saves the data in `service` and\n * goes back to the list page.\n */\nfunction FormPage<T extends Identifiable, D extends object, S extends SaveDataSource<T, D>>(\n props: React.PropsWithChildren<FormPageProps<T, D, S>>\n): ReactElement {\n const { title, formType, service, form, backRoute, isLoading, isFullWidth, children } = props;\n const { router, messages } = useKitConfig();\n const { Link } = router;\n const navigate = router.useNavigate();\n const pathname = router.usePathname();\n\n /**\n * Checks whether a field of the validation schema is missing in the form.\n * @param fieldName name of the field to find\n */\n function isFormFieldMissing(fieldName: string): boolean {\n return !form.getInputNode(fieldName);\n }\n\n /**\n * Shows the errors of the fields that are in the validation schema given to `useForm`, but not in the form.\n * Without it, these errors are not shown to the user (not even in the console), so neither the user nor the\n * developer can understand why the form is not submitted.\n */\n function showErrorsForMissingFormFields(): ReactElement | false {\n const errors = Object.entries(form.errors)\n .map(([fieldName, errorMsg]) => ({ fieldName, errorMsg }))\n .filter(({ fieldName }) => isFormFieldMissing(fieldName))\n .map(({ fieldName, errorMsg }) => <li key={fieldName}>{errorMsg}</li>);\n\n const icon = <IconInfoCircle />;\n return (\n errors.length > 0 && (\n <Alert color=\"red\" icon={icon}>\n <ul>{errors}</ul>\n </Alert>\n )\n );\n }\n\n const { id } = form.values;\n\n const [loading, setLoading] = useState(false);\n const effectiveBackRoute = backRoute || `/${pathname.split('/').at(1)}`;\n\n const canShowSubmitButton = formType === FormType.Create || formType === FormType.Edit;\n const formTypeTitle = messages.form.action[FORM_ACTIONS[formType] ?? 'custom'];\n const documentTitle = useDocumentTitle(`${formTypeTitle} ${title}`);\n\n const handleSubmit = async (data: D) => {\n setLoading(true);\n try {\n const effectiveData = formatValues(data) as D;\n await service.save(id, effectiveData);\n showSuccessNotification(messages.form.saveSuccess(formTypeTitle), messages.common.success);\n\n if (effectiveBackRoute) navigate(effectiveBackRoute);\n } catch (e) {\n showErrorNotification(e, messages.form.saveError(formTypeTitle), messages.common.error);\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <>\n <title>{documentTitle}</title>\n <Stack gap=\"lg\">\n <PageHeader\n title={`${formTypeTitle} ${title}`}\n breadcrumbItems={[\n { title, href: effectiveBackRoute },\n { title: `${formTypeTitle} ${title}`, href: '#' }\n ]}\n />\n <Grid>\n <Grid.Col span={{ base: 12, lg: isFullWidth ? 12 : 6 }}>\n <PaperCard>\n <Box component=\"form\" onSubmit={form.onSubmit(handleSubmit)} pos=\"relative\">\n <LoadingOverlay\n visible={isLoading}\n zIndex={1000}\n overlayProps={{ radius: 'sm', blur: 2 }}\n />\n\n <Stack>\n <Text size=\"lg\" fw={600}>\n {formTypeTitle} {title}\n </Text>\n\n {children}\n {showErrorsForMissingFormFields()}\n\n <Group>\n <Button\n leftSection={<IconArrowBack size={16} />}\n component={Link}\n href={effectiveBackRoute}\n variant=\"default\"\n >\n {messages.common.back}\n </Button>\n {canShowSubmitButton && (\n <Button\n type=\"submit\"\n leftSection={<IconDeviceFloppy size={16} />}\n loading={loading}\n >\n {formTypeTitle}\n </Button>\n )}\n </Group>\n </Stack>\n </Box>\n </PaperCard>\n </Grid.Col>\n </Grid>\n </Stack>\n </>\n );\n}\n\nexport default FormPage;\n"],"mappings":";;;;;;;;;;;;;AAoCA,IAAM,eAA6C;EAC9C,SAAS,SAAS;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;AACrB;;;;;AAMA,SAAS,SACL,OACY;CACZ,MAAM,EAAE,OAAO,UAAU,SAAS,MAAM,WAAW,WAAW,aAAa,aAAa;CACxF,MAAM,EAAE,QAAQ,aAAa,aAAa;CAC1C,MAAM,EAAE,SAAS;CACjB,MAAM,WAAW,OAAO,YAAY;CACpC,MAAM,WAAW,OAAO,YAAY;;;;;CAMpC,SAAS,mBAAmB,WAA4B;EACpD,OAAO,CAAC,KAAK,aAAa,SAAS;CACvC;;;;;;CAOA,SAAS,iCAAuD;EAC5D,MAAM,SAAS,OAAO,QAAQ,KAAK,MAAM,CAAC,CACrC,KAAK,CAAC,WAAW,eAAe;GAAE;GAAW;EAAS,EAAE,CAAC,CACzD,QAAQ,EAAE,gBAAgB,mBAAmB,SAAS,CAAC,CAAC,CACxD,KAAK,EAAE,WAAW,eAAe,oBAAC,MAAD,EAAA,UAAqB,SAAa,GAAzB,SAAyB,CAAC;EAEzE,MAAM,OAAO,oBAAC,gBAAD,CAAiB,CAAA;EAC9B,OACI,OAAO,SAAS,KACZ,oBAAC,OAAD;GAAO,OAAM;GAAY;GACrB,UAAA,oBAAC,MAAD,EAAA,UAAK,OAAW,CAAA;EACb,CAAA;CAGnB;CAEA,MAAM,EAAE,OAAO,KAAK;CAEpB,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,qBAAqB,aAAa,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC;CAEpE,MAAM,sBAAsB,aAAa,SAAS,UAAU,aAAa,SAAS;CAClF,MAAM,gBAAgB,SAAS,KAAK,OAAO,aAAa,aAAa;CACrE,MAAM,gBAAgB,iBAAiB,GAAG,cAAc,GAAG,OAAO;CAElE,MAAM,eAAe,OAAO,SAAY;EACpC,WAAW,IAAI;EACf,IAAI;GACA,MAAM,gBAAgB,aAAa,IAAI;GACvC,MAAM,QAAQ,KAAK,IAAI,aAAa;GACpC,wBAAwB,SAAS,KAAK,YAAY,aAAa,GAAG,SAAS,OAAO,OAAO;GAEzF,IAAI,oBAAoB,SAAS,kBAAkB;EACvD,SAAS,GAAG;GACR,sBAAsB,GAAG,SAAS,KAAK,UAAU,aAAa,GAAG,SAAS,OAAO,KAAK;EAC1F,UAAU;GACN,WAAW,KAAK;EACpB;CACJ;CAEA,OACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,SAAD,EAAA,UAAQ,cAAqB,CAAA,GAC7B,qBAAC,OAAD;EAAO,KAAI;EAAX,UAAA,CACI,oBAAC,YAAD;GACI,OAAO,GAAG,cAAc,GAAG;GAC3B,iBAAiB,CACb;IAAE;IAAO,MAAM;GAAmB,GAClC;IAAE,OAAO,GAAG,cAAc,GAAG;IAAS,MAAM;GAAI,CACpD;EACH,CAAA,GACD,oBAAC,MAAD,EAAA,UACI,oBAAC,KAAK,KAAN;GAAU,MAAM;IAAE,MAAM;IAAI,IAAI,cAAc,KAAK;GAAE;GACjD,UAAA,oBAAC,WAAD,EAAA,UACI,qBAAC,KAAD;IAAK,WAAU;IAAO,UAAU,KAAK,SAAS,YAAY;IAAG,KAAI;IAAjE,UAAA,CACI,oBAAC,gBAAD;KACI,SAAS;KACT,QAAQ;KACR,cAAc;MAAE,QAAQ;MAAM,MAAM;KAAE;IACzC,CAAA,GAED,qBAAC,OAAD,EAAA,UAAA;KACI,qBAAC,MAAD;MAAM,MAAK;MAAK,IAAI;MAApB,UAAA;OACK;OAAc;OAAE;MACf;;KAEL;KACA,+BAA+B;KAEhC,qBAAC,OAAD,EAAA,UAAA,CACI,oBAAC,QAAD;MACI,aAAa,oBAAC,eAAD,EAAe,MAAM,GAAK,CAAA;MACvC,WAAW;MACX,MAAM;MACN,SAAQ;MAEP,UAAA,SAAS,OAAO;KACb,CAAA,GACP,uBACG,oBAAC,QAAD;MACI,MAAK;MACL,aAAa,oBAAC,kBAAD,EAAkB,MAAM,GAAK,CAAA;MACjC;MAER,UAAA;KACG,CAAA,CAET,EAAA,CAAA;IACJ,EAAA,CAAA,CACN;GACE,CAAA,EAAA,CAAA;EACL,CAAA,EACR,CAAA,CACH;CACT,CAAA,CAAA,EAAA,CAAA;AAEV"}
@@ -0,0 +1,6 @@
1
+ /** Mode in which a CRUD form is opened. */
2
+ export declare enum FormType {
3
+ Create = 0,
4
+ Edit = 1,
5
+ View = 2
6
+ }
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ // src/component/template/FormPage/FormType.ts
3
+ /** Mode in which a CRUD form is opened. */
4
+ var FormType = /* @__PURE__ */ function(FormType) {
5
+ FormType[FormType["Create"] = 0] = "Create";
6
+ FormType[FormType["Edit"] = 1] = "Edit";
7
+ FormType[FormType["View"] = 2] = "View";
8
+ return FormType;
9
+ }({});
10
+ export { FormType };
11
+
12
+ //# sourceMappingURL=FormType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormType.js","names":[],"sources":["../../../../src/component/template/FormPage/FormType.ts"],"sourcesContent":["/** Mode in which a CRUD form is opened. */\nexport enum FormType {\n Create,\n Edit,\n View\n}\n"],"mappings":";;;AACA,IAAY,WAAL,yBAAA,UAAA;CACH,SAAA,SAAA,YAAA,KAAA;CACA,SAAA,SAAA,UAAA,KAAA;CACA,SAAA,SAAA,UAAA,KAAA;;AACJ,EAAA,CAAA,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { NavItem } from '../../organism/Navbar/NavItem';
2
+ interface Props {
3
+ children: React.ReactNode;
4
+ /** Brand of the app in the header (e.g. the logo with a link to the home page). */
5
+ logo: React.ReactNode;
6
+ /** Items of the side menu. */
7
+ navItems: NavItem[];
8
+ /** Content fixed at the bottom of the side menu (e.g. a link to log out). */
9
+ navFooter?: React.ReactNode;
10
+ /**
11
+ * Items of the user menu (e.g. `Menu.Item`s for the profile and to log out), opened by the avatar in the header.
12
+ * Without them, the avatar is not shown.
13
+ */
14
+ userMenu?: React.ReactNode;
15
+ }
16
+ /**
17
+ * Structure of the pages of the app: header with the brand, the color scheme toggle and the user menu, collapsible side
18
+ * menu and the content area.
19
+ */
20
+ export declare const Layout: ({ children, logo, navItems, navFooter, userMenu }: Props) => import("react/jsx-runtime").JSX.Element;
21
+ export {};
@@ -0,0 +1,121 @@
1
+ "use client";
2
+ import AppMain from "../../atom/AppMain/AppMain.js";
3
+ import { Navbar } from "../../organism/Navbar/Navbar.js";
4
+ import { useKitMessages } from "../../../provider/useKit.js";
5
+ import Layout_module_default from "./Layout.module.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { useDisclosure, useMediaQuery, useMounted } from "@mantine/hooks";
8
+ import { ActionIcon, AppShell, Avatar, Burger, Container, Group, Menu, Tooltip, UnstyledButton, useComputedColorScheme, useMantineColorScheme, useMantineTheme } from "@mantine/core";
9
+ import { IconMoon, IconSun } from "@tabler/icons-react";
10
+ import cx from "clsx";
11
+ // src/component/template/Layout/Layout.tsx
12
+ /**
13
+ * Structure of the pages of the app: header with the brand, the color scheme toggle and the user menu, collapsible side
14
+ * menu and the content area.
15
+ */
16
+ var Layout = ({ children, logo, navItems, navFooter, userMenu }) => {
17
+ const messages = useKitMessages().layout;
18
+ const [mobileOpened, { toggle: toggleMobile }] = useDisclosure();
19
+ const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
20
+ const { setColorScheme } = useMantineColorScheme();
21
+ const savedColorScheme = useComputedColorScheme("light", { getInitialValueInEffect: true });
22
+ const nextColorScheme = (useMounted() ? savedColorScheme : "light") === "light" ? "dark" : "light";
23
+ const colorSchemeLabel = nextColorScheme === "dark" ? messages.enableDarkTheme : messages.enableLightTheme;
24
+ const theme = useMantineTheme();
25
+ const isMobile = !useMediaQuery(`(min-width: ${theme.breakpoints.sm})`);
26
+ const isOpened = isMobile ? mobileOpened : desktopOpened;
27
+ const toggleBurgerFn = isMobile ? toggleMobile : toggleDesktop;
28
+ return /* @__PURE__ */ jsxs(AppShell, {
29
+ header: { height: {
30
+ base: 60,
31
+ sm: 68
32
+ } },
33
+ navbar: {
34
+ width: { base: 300 },
35
+ breakpoint: "sm",
36
+ collapsed: {
37
+ mobile: !mobileOpened,
38
+ desktop: !desktopOpened
39
+ }
40
+ },
41
+ padding: 0,
42
+ children: [
43
+ /* @__PURE__ */ jsx(AppShell.Header, { children: /* @__PURE__ */ jsxs(Group, {
44
+ h: "100%",
45
+ justify: "space-between",
46
+ wrap: "nowrap",
47
+ gap: "xs",
48
+ px: {
49
+ base: "xs",
50
+ sm: "md"
51
+ },
52
+ children: [/* @__PURE__ */ jsxs(Group, {
53
+ wrap: "nowrap",
54
+ className: cx(Layout_module_default.shrinkable, Layout_module_default.headerGap),
55
+ children: [/* @__PURE__ */ jsx(Burger, {
56
+ opened: isOpened,
57
+ onClick: toggleBurgerFn,
58
+ size: "sm",
59
+ "aria-label": isOpened ? messages.collapseNavigation : messages.expandNavigation,
60
+ "aria-expanded": isOpened,
61
+ className: Layout_module_default.fixed
62
+ }), logo]
63
+ }), /* @__PURE__ */ jsxs(Group, {
64
+ wrap: "nowrap",
65
+ gap: "xs",
66
+ className: Layout_module_default.fixed,
67
+ children: [/* @__PURE__ */ jsx(Tooltip, {
68
+ label: colorSchemeLabel,
69
+ children: /* @__PURE__ */ jsxs(ActionIcon, {
70
+ onClick: () => setColorScheme(nextColorScheme),
71
+ variant: "subtle",
72
+ size: 40,
73
+ radius: "xl",
74
+ "aria-label": colorSchemeLabel,
75
+ children: [/* @__PURE__ */ jsx(IconSun, {
76
+ className: cx(Layout_module_default.icon, Layout_module_default.light),
77
+ stroke: 1.5
78
+ }), /* @__PURE__ */ jsx(IconMoon, {
79
+ className: cx(Layout_module_default.icon, Layout_module_default.dark),
80
+ stroke: 1.5
81
+ })]
82
+ })
83
+ }), userMenu && /* @__PURE__ */ jsxs(Menu, {
84
+ shadow: "lg",
85
+ width: 200,
86
+ children: [/* @__PURE__ */ jsx(Tooltip, {
87
+ label: messages.userMenu,
88
+ children: /* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(UnstyledButton, {
89
+ "aria-label": messages.userMenu,
90
+ style: { borderRadius: "50%" },
91
+ children: /* @__PURE__ */ jsx(Avatar, {
92
+ color: theme.primaryColor,
93
+ radius: "xl",
94
+ size: 40
95
+ })
96
+ }) })
97
+ }), /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [/* @__PURE__ */ jsx(Menu.Label, {
98
+ tt: "uppercase",
99
+ ta: "center",
100
+ fw: 600,
101
+ children: messages.userMenuTitle
102
+ }), userMenu] })]
103
+ })]
104
+ })]
105
+ }) }),
106
+ /* @__PURE__ */ jsx(Navbar, {
107
+ items: navItems,
108
+ footer: navFooter,
109
+ linkClickHandler: () => isMobile && toggleBurgerFn()
110
+ }),
111
+ /* @__PURE__ */ jsx(AppShell.Main, { children: /* @__PURE__ */ jsx(AppMain, { children: /* @__PURE__ */ jsx(Container, {
112
+ p: "md",
113
+ fluid: true,
114
+ children
115
+ }) }) })
116
+ ]
117
+ });
118
+ };
119
+ export { Layout };
120
+
121
+ //# sourceMappingURL=Layout.js.map