@etsoo/materialui 1.5.30 → 1.5.32

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.
@@ -72,10 +72,10 @@ const root = document.body;
72
72
  const container: HTMLElement = document.createElement("div");
73
73
  root.append(container);
74
74
 
75
- // The state provider
76
- const Provider = ReactApp.notifierProvider;
77
-
78
75
  act(() => {
76
+ // The state provider
77
+ const Provider = ReactApp.notifierProvider;
78
+
79
79
  // Concorrent renderer needs act block
80
80
  const reactRoot = createRoot(container);
81
81
  reactRoot.render(<Provider />);
@@ -1,5 +1,5 @@
1
- import { GridRowId, GridValidRowModel } from "@mui/x-data-grid/models/gridRows";
2
- import { DataGridProps } from "@mui/x-data-grid/models/props/DataGridProps";
1
+ import { GridRowId, GridValidRowModel } from "@mui/x-data-grid/models";
2
+ import { DataGridProps } from "@mui/x-data-grid/internals";
3
3
  /**
4
4
  * Data table selected cell params
5
5
  */
@@ -7,7 +7,7 @@ exports.DataTable = DataTable;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const react_1 = __importDefault(require("react"));
9
9
  const ReactApp_1 = require("./app/ReactApp");
10
- const DataGrid_1 = require("@mui/x-data-grid/DataGrid/DataGrid");
10
+ const DataGrid_1 = require("@mui/x-data-grid/DataGrid");
11
11
  /**
12
12
  * Data table
13
13
  * @param props Props
@@ -0,0 +1,51 @@
1
+ import { DataTypes, IdType } from "@etsoo/shared";
2
+ import { ListProps } from "@mui/material/List";
3
+ import { ListItemProps } from "@mui/material/ListItem";
4
+ import { ListItemTextProps } from "@mui/material/ListItemText";
5
+ type ListItemLabel<T extends object> = DataTypes.Keys<T, string | undefined> | ((item: T) => string | undefined);
6
+ /**
7
+ * List multipler component props
8
+ */
9
+ export type ListMultiplerProps<T extends object> = ListProps & {
10
+ /**
11
+ * List data
12
+ */
13
+ data: T[];
14
+ /**
15
+ * Id field name
16
+ */
17
+ idField: DataTypes.Keys<T, IdType>;
18
+ /**
19
+ * Primary label field name or function
20
+ */
21
+ primaryLabel: ListItemLabel<T>;
22
+ /**
23
+ * Secondary label field name or function
24
+ */
25
+ secondaryLabel?: ListItemLabel<T>;
26
+ /**
27
+ * List item props
28
+ */
29
+ listItemProps?: ListItemProps;
30
+ /**
31
+ * List item text props
32
+ */
33
+ listItemTextProps?: Omit<ListItemTextProps, "primary" | "secondary">;
34
+ /**
35
+ * Input name
36
+ */
37
+ name?: string;
38
+ /**
39
+ * On change event
40
+ * @param items The selected items
41
+ * @param ids The selected IDs
42
+ */
43
+ onCheckItems?: (items: T[], ids: unknown[]) => void;
44
+ };
45
+ /**
46
+ * List multipler component
47
+ * @param props Props
48
+ * @returns Component
49
+ */
50
+ export declare function ListMultipler<T extends object>(props: ListMultiplerProps<T>): import("react/jsx-runtime").JSX.Element;
51
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ListMultipler = ListMultipler;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const Checkbox_1 = __importDefault(require("@mui/material/Checkbox"));
9
+ const List_1 = __importDefault(require("@mui/material/List"));
10
+ const ListItem_1 = __importDefault(require("@mui/material/ListItem"));
11
+ const ListItemButton_1 = __importDefault(require("@mui/material/ListItemButton"));
12
+ const ListItemIcon_1 = __importDefault(require("@mui/material/ListItemIcon"));
13
+ const ListItemText_1 = __importDefault(require("@mui/material/ListItemText"));
14
+ const react_1 = __importDefault(require("react"));
15
+ function GetListItemLabel(data, label) {
16
+ if (label == null)
17
+ return undefined;
18
+ if (typeof label === "function") {
19
+ return label(data);
20
+ }
21
+ else {
22
+ return data[label];
23
+ }
24
+ }
25
+ /**
26
+ * List multipler component
27
+ * @param props Props
28
+ * @returns Component
29
+ */
30
+ function ListMultipler(props) {
31
+ // Destruct
32
+ const { data, idField, primaryLabel, secondaryLabel, listItemProps, listItemTextProps, name, onCheckItems, ...rest } = props;
33
+ // Refs
34
+ const initialized = react_1.default.useRef(false);
35
+ react_1.default.useEffect(() => {
36
+ initialized.current = true;
37
+ }, []);
38
+ // State
39
+ const [checked, setChecked] = react_1.default.useState([]);
40
+ const ids = react_1.default.useMemo(() => {
41
+ const ids = checked.map((u) => u[idField]);
42
+ if (onCheckItems && initialized.current) {
43
+ onCheckItems(checked, ids);
44
+ }
45
+ return ids;
46
+ }, [checked]);
47
+ function handleToggle(id) {
48
+ if (ids.includes(id)) {
49
+ setChecked((prev) => prev.filter((u) => u[idField] !== id));
50
+ }
51
+ else {
52
+ const item = data.find((u) => u[idField] === id);
53
+ if (item) {
54
+ setChecked((prev) => [...prev, item]);
55
+ }
56
+ }
57
+ }
58
+ const inputType = typeof ids[0] === "string" ? "text" : "number";
59
+ // Layout
60
+ return ((0, jsx_runtime_1.jsxs)(List_1.default, { ...rest, children: [name && ((0, jsx_runtime_1.jsx)("input", { type: inputType, style: { display: "none" }, name: name, value: ids.join(","), readOnly: true })), data.map((u) => ((0, jsx_runtime_1.jsx)(ListItem_1.default, { ...listItemProps, children: (0, jsx_runtime_1.jsxs)(ListItemButton_1.default, { dense: true, onClick: () => handleToggle(u[idField]), children: [(0, jsx_runtime_1.jsx)(ListItemIcon_1.default, { children: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { edge: "start", disableRipple: true, checked: ids.includes(u[idField]) }) }), (0, jsx_runtime_1.jsx)(ListItemText_1.default, { primary: GetListItemLabel(u, primaryLabel), secondary: GetListItemLabel(u, secondaryLabel), ...listItemTextProps })] }) }, `${u[idField]}`)))] }));
61
+ }
@@ -1,6 +1,6 @@
1
1
  import { QueryRQ } from "@etsoo/appscript";
2
2
  import { IdType } from "@etsoo/shared";
3
- import { GridApiCommunity } from "@mui/x-data-grid/models/api/gridApiCommunity";
3
+ import { GridApiCommunity } from "@mui/x-data-grid/internals";
4
4
  /**
5
5
  * MU utilities
6
6
  */
@@ -1,4 +1,4 @@
1
- import { ApiRefreshTokenDto, IApi, IApiPayload } from "@etsoo/appscript";
1
+ import { ApiRefreshTokenDto, IApi, IApiPayload, TokenAuthRQ } from "@etsoo/appscript";
2
2
  import { ReactAppType } from "./ReactApp";
3
3
  import { IServiceUser, ServiceUserToken } from "./IServiceUser";
4
4
  import { IActionResult } from "@etsoo/shared";
@@ -14,6 +14,12 @@ export interface IServiceApp extends ReactAppType {
14
14
  * Core system origin
15
15
  */
16
16
  readonly coreOrigin: string;
17
+ /**
18
+ * Get token authorization request data
19
+ * @param api API, if not provided, use the core API
20
+ * @returns Result
21
+ */
22
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ;
17
23
  /**
18
24
  * Load core system UI
19
25
  * @param tryLogin Try login or not
@@ -1,4 +1,4 @@
1
- import { ApiRefreshTokenDto, AppLoginParams, AppTryLoginParams, ExternalEndpoint, IApi, IApiPayload } from "@etsoo/appscript";
1
+ import { ApiRefreshTokenDto, AppLoginParams, AppTryLoginParams, ExternalEndpoint, IApi, IApiPayload, TokenAuthRQ } from "@etsoo/appscript";
2
2
  import { IServiceApp } from "./IServiceApp";
3
3
  import { IServiceAppSettings } from "./IServiceAppSettings";
4
4
  import { IServiceUser, ServiceUserToken } from "./IServiceUser";
@@ -31,6 +31,12 @@ export declare class ServiceApp<U extends IServiceUser = IServiceUser, S extends
31
31
  * @param debug Debug mode
32
32
  */
33
33
  constructor(settings: S, name: string, debug?: boolean);
34
+ /**
35
+ * Get token authorization request data
36
+ * @param api API, if not provided, use the core API
37
+ * @returns Result
38
+ */
39
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ;
34
40
  /**
35
41
  * Load core system UI
36
42
  * @param tryLogin Try login or not
@@ -43,6 +43,19 @@ class ServiceApp extends ReactApp_1.ReactApp {
43
43
  this.coreApi = this.createApi(this.coreName, coreEndpoint);
44
44
  this.keepLogin = true;
45
45
  }
46
+ /**
47
+ * Get token authorization request data
48
+ * @param api API, if not provided, use the core API
49
+ * @returns Result
50
+ */
51
+ getTokenAuthRQ(api) {
52
+ api ??= this.coreApi;
53
+ const auth = api.getAuthorization();
54
+ if (auth == null) {
55
+ throw new Error("Authorization is required.");
56
+ }
57
+ return { accessToken: auth.token, tokenScheme: auth.scheme };
58
+ }
46
59
  /**
47
60
  * Load core system UI
48
61
  * @param tryLogin Try login or not
@@ -73,6 +73,7 @@ export * from "./LinkEx";
73
73
  export * from "./ListChooser";
74
74
  export * from "./ListItemRightIcon";
75
75
  export * from "./ListMoreDisplay";
76
+ export * from "./ListMultipler";
76
77
  export * from "./LoadingButton";
77
78
  export * from "./MaskInput";
78
79
  export * from "./MenuButton";
package/lib/cjs/index.js CHANGED
@@ -89,6 +89,7 @@ __exportStar(require("./LinkEx"), exports);
89
89
  __exportStar(require("./ListChooser"), exports);
90
90
  __exportStar(require("./ListItemRightIcon"), exports);
91
91
  __exportStar(require("./ListMoreDisplay"), exports);
92
+ __exportStar(require("./ListMultipler"), exports);
92
93
  __exportStar(require("./LoadingButton"), exports);
93
94
  __exportStar(require("./MaskInput"), exports);
94
95
  __exportStar(require("./MenuButton"), exports);
@@ -1,5 +1,5 @@
1
- import { GridRowId, GridValidRowModel } from "@mui/x-data-grid/models/gridRows";
2
- import { DataGridProps } from "@mui/x-data-grid/models/props/DataGridProps";
1
+ import { GridRowId, GridValidRowModel } from "@mui/x-data-grid/models";
2
+ import { DataGridProps } from "@mui/x-data-grid/internals";
3
3
  /**
4
4
  * Data table selected cell params
5
5
  */
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { useAppContext } from "./app/ReactApp";
4
- import { DataGrid } from "@mui/x-data-grid/DataGrid/DataGrid";
4
+ import { DataGrid } from "@mui/x-data-grid/DataGrid";
5
5
  /**
6
6
  * Data table
7
7
  * @param props Props
@@ -0,0 +1,51 @@
1
+ import { DataTypes, IdType } from "@etsoo/shared";
2
+ import { ListProps } from "@mui/material/List";
3
+ import { ListItemProps } from "@mui/material/ListItem";
4
+ import { ListItemTextProps } from "@mui/material/ListItemText";
5
+ type ListItemLabel<T extends object> = DataTypes.Keys<T, string | undefined> | ((item: T) => string | undefined);
6
+ /**
7
+ * List multipler component props
8
+ */
9
+ export type ListMultiplerProps<T extends object> = ListProps & {
10
+ /**
11
+ * List data
12
+ */
13
+ data: T[];
14
+ /**
15
+ * Id field name
16
+ */
17
+ idField: DataTypes.Keys<T, IdType>;
18
+ /**
19
+ * Primary label field name or function
20
+ */
21
+ primaryLabel: ListItemLabel<T>;
22
+ /**
23
+ * Secondary label field name or function
24
+ */
25
+ secondaryLabel?: ListItemLabel<T>;
26
+ /**
27
+ * List item props
28
+ */
29
+ listItemProps?: ListItemProps;
30
+ /**
31
+ * List item text props
32
+ */
33
+ listItemTextProps?: Omit<ListItemTextProps, "primary" | "secondary">;
34
+ /**
35
+ * Input name
36
+ */
37
+ name?: string;
38
+ /**
39
+ * On change event
40
+ * @param items The selected items
41
+ * @param ids The selected IDs
42
+ */
43
+ onCheckItems?: (items: T[], ids: unknown[]) => void;
44
+ };
45
+ /**
46
+ * List multipler component
47
+ * @param props Props
48
+ * @returns Component
49
+ */
50
+ export declare function ListMultipler<T extends object>(props: ListMultiplerProps<T>): import("react/jsx-runtime").JSX.Element;
51
+ export {};
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Checkbox from "@mui/material/Checkbox";
3
+ import List from "@mui/material/List";
4
+ import ListItem from "@mui/material/ListItem";
5
+ import ListItemButton from "@mui/material/ListItemButton";
6
+ import ListItemIcon from "@mui/material/ListItemIcon";
7
+ import ListItemText from "@mui/material/ListItemText";
8
+ import React from "react";
9
+ function GetListItemLabel(data, label) {
10
+ if (label == null)
11
+ return undefined;
12
+ if (typeof label === "function") {
13
+ return label(data);
14
+ }
15
+ else {
16
+ return data[label];
17
+ }
18
+ }
19
+ /**
20
+ * List multipler component
21
+ * @param props Props
22
+ * @returns Component
23
+ */
24
+ export function ListMultipler(props) {
25
+ // Destruct
26
+ const { data, idField, primaryLabel, secondaryLabel, listItemProps, listItemTextProps, name, onCheckItems, ...rest } = props;
27
+ // Refs
28
+ const initialized = React.useRef(false);
29
+ React.useEffect(() => {
30
+ initialized.current = true;
31
+ }, []);
32
+ // State
33
+ const [checked, setChecked] = React.useState([]);
34
+ const ids = React.useMemo(() => {
35
+ const ids = checked.map((u) => u[idField]);
36
+ if (onCheckItems && initialized.current) {
37
+ onCheckItems(checked, ids);
38
+ }
39
+ return ids;
40
+ }, [checked]);
41
+ function handleToggle(id) {
42
+ if (ids.includes(id)) {
43
+ setChecked((prev) => prev.filter((u) => u[idField] !== id));
44
+ }
45
+ else {
46
+ const item = data.find((u) => u[idField] === id);
47
+ if (item) {
48
+ setChecked((prev) => [...prev, item]);
49
+ }
50
+ }
51
+ }
52
+ const inputType = typeof ids[0] === "string" ? "text" : "number";
53
+ // Layout
54
+ return (_jsxs(List, { ...rest, children: [name && (_jsx("input", { type: inputType, style: { display: "none" }, name: name, value: ids.join(","), readOnly: true })), data.map((u) => (_jsx(ListItem, { ...listItemProps, children: _jsxs(ListItemButton, { dense: true, onClick: () => handleToggle(u[idField]), children: [_jsx(ListItemIcon, { children: _jsx(Checkbox, { edge: "start", disableRipple: true, checked: ids.includes(u[idField]) }) }), _jsx(ListItemText, { primary: GetListItemLabel(u, primaryLabel), secondary: GetListItemLabel(u, secondaryLabel), ...listItemTextProps })] }) }, `${u[idField]}`)))] }));
55
+ }
@@ -1,6 +1,6 @@
1
1
  import { QueryRQ } from "@etsoo/appscript";
2
2
  import { IdType } from "@etsoo/shared";
3
- import { GridApiCommunity } from "@mui/x-data-grid/models/api/gridApiCommunity";
3
+ import { GridApiCommunity } from "@mui/x-data-grid/internals";
4
4
  /**
5
5
  * MU utilities
6
6
  */
@@ -1,4 +1,4 @@
1
- import { ApiRefreshTokenDto, IApi, IApiPayload } from "@etsoo/appscript";
1
+ import { ApiRefreshTokenDto, IApi, IApiPayload, TokenAuthRQ } from "@etsoo/appscript";
2
2
  import { ReactAppType } from "./ReactApp";
3
3
  import { IServiceUser, ServiceUserToken } from "./IServiceUser";
4
4
  import { IActionResult } from "@etsoo/shared";
@@ -14,6 +14,12 @@ export interface IServiceApp extends ReactAppType {
14
14
  * Core system origin
15
15
  */
16
16
  readonly coreOrigin: string;
17
+ /**
18
+ * Get token authorization request data
19
+ * @param api API, if not provided, use the core API
20
+ * @returns Result
21
+ */
22
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ;
17
23
  /**
18
24
  * Load core system UI
19
25
  * @param tryLogin Try login or not
@@ -1,4 +1,4 @@
1
- import { ApiRefreshTokenDto, AppLoginParams, AppTryLoginParams, ExternalEndpoint, IApi, IApiPayload } from "@etsoo/appscript";
1
+ import { ApiRefreshTokenDto, AppLoginParams, AppTryLoginParams, ExternalEndpoint, IApi, IApiPayload, TokenAuthRQ } from "@etsoo/appscript";
2
2
  import { IServiceApp } from "./IServiceApp";
3
3
  import { IServiceAppSettings } from "./IServiceAppSettings";
4
4
  import { IServiceUser, ServiceUserToken } from "./IServiceUser";
@@ -31,6 +31,12 @@ export declare class ServiceApp<U extends IServiceUser = IServiceUser, S extends
31
31
  * @param debug Debug mode
32
32
  */
33
33
  constructor(settings: S, name: string, debug?: boolean);
34
+ /**
35
+ * Get token authorization request data
36
+ * @param api API, if not provided, use the core API
37
+ * @returns Result
38
+ */
39
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ;
34
40
  /**
35
41
  * Load core system UI
36
42
  * @param tryLogin Try login or not
@@ -40,6 +40,19 @@ export class ServiceApp extends ReactApp {
40
40
  this.coreApi = this.createApi(this.coreName, coreEndpoint);
41
41
  this.keepLogin = true;
42
42
  }
43
+ /**
44
+ * Get token authorization request data
45
+ * @param api API, if not provided, use the core API
46
+ * @returns Result
47
+ */
48
+ getTokenAuthRQ(api) {
49
+ api ??= this.coreApi;
50
+ const auth = api.getAuthorization();
51
+ if (auth == null) {
52
+ throw new Error("Authorization is required.");
53
+ }
54
+ return { accessToken: auth.token, tokenScheme: auth.scheme };
55
+ }
43
56
  /**
44
57
  * Load core system UI
45
58
  * @param tryLogin Try login or not
@@ -73,6 +73,7 @@ export * from "./LinkEx";
73
73
  export * from "./ListChooser";
74
74
  export * from "./ListItemRightIcon";
75
75
  export * from "./ListMoreDisplay";
76
+ export * from "./ListMultipler";
76
77
  export * from "./LoadingButton";
77
78
  export * from "./MaskInput";
78
79
  export * from "./MenuButton";
package/lib/mjs/index.js CHANGED
@@ -73,6 +73,7 @@ export * from "./LinkEx";
73
73
  export * from "./ListChooser";
74
74
  export * from "./ListItemRightIcon";
75
75
  export * from "./ListMoreDisplay";
76
+ export * from "./ListMultipler";
76
77
  export * from "./LoadingButton";
77
78
  export * from "./MaskInput";
78
79
  export * from "./MenuButton";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.5.30",
3
+ "version": "1.5.32",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -40,14 +40,14 @@
40
40
  "@dnd-kit/sortable": "^10.0.0",
41
41
  "@emotion/react": "^11.14.0",
42
42
  "@emotion/styled": "^11.14.0",
43
- "@etsoo/appscript": "^1.6.22",
43
+ "@etsoo/appscript": "^1.6.26",
44
44
  "@etsoo/notificationbase": "^1.1.60",
45
- "@etsoo/react": "^1.8.39",
46
- "@etsoo/shared": "^1.2.68",
45
+ "@etsoo/react": "^1.8.40",
46
+ "@etsoo/shared": "^1.2.69",
47
47
  "@mui/icons-material": "^7.0.2",
48
48
  "@mui/material": "^7.0.2",
49
- "@mui/x-data-grid": "^7.28.3",
50
- "chart.js": "^4.4.8",
49
+ "@mui/x-data-grid": "^8.0.0",
50
+ "chart.js": "^4.4.9",
51
51
  "chartjs-plugin-datalabels": "^2.2.0",
52
52
  "dompurify": "^3.2.5",
53
53
  "eventemitter3": "^5.0.1",
@@ -81,7 +81,7 @@
81
81
  "@types/react-dom": "^18.3.6",
82
82
  "@types/react-input-mask": "^3.0.6",
83
83
  "@types/react-window": "^1.8.8",
84
- "@vitejs/plugin-react": "^4.3.4",
84
+ "@vitejs/plugin-react": "^4.4.1",
85
85
  "jsdom": "^26.1.0",
86
86
  "typescript": "^5.8.3",
87
87
  "vitest": "^3.1.1"
@@ -2,7 +2,7 @@ import { DataTable, DataTableProps } from "./DataTable";
2
2
  import React from "react";
3
3
  import { ListType1 } from "@etsoo/shared";
4
4
  import { useAppContext } from "./app/ReactApp";
5
- import { GridRenderCellParams } from "@mui/x-data-grid/models/params/gridCellParams";
5
+ import { GridRenderCellParams } from "@mui/x-data-grid/models/params";
6
6
 
7
7
  /**
8
8
  * Culture table props
package/src/DataTable.tsx CHANGED
@@ -1,9 +1,12 @@
1
1
  import React from "react";
2
2
  import { useAppContext } from "./app/ReactApp";
3
- import { GridRowId, GridValidRowModel } from "@mui/x-data-grid/models/gridRows";
4
- import { DataGridProps } from "@mui/x-data-grid/models/props/DataGridProps";
5
- import { GridCellModesModel } from "@mui/x-data-grid/models/api/gridEditingApi";
6
- import { DataGrid } from "@mui/x-data-grid/DataGrid/DataGrid";
3
+ import {
4
+ GridCellModesModel,
5
+ GridRowId,
6
+ GridValidRowModel
7
+ } from "@mui/x-data-grid/models";
8
+ import { DataGridProps } from "@mui/x-data-grid/internals";
9
+ import { DataGrid } from "@mui/x-data-grid/DataGrid";
7
10
 
8
11
  /**
9
12
  * Data table selected cell params
@@ -0,0 +1,153 @@
1
+ import { DataTypes, IdType } from "@etsoo/shared";
2
+ import Checkbox from "@mui/material/Checkbox";
3
+ import List, { ListProps } from "@mui/material/List";
4
+ import ListItem, { ListItemProps } from "@mui/material/ListItem";
5
+ import ListItemButton from "@mui/material/ListItemButton";
6
+ import ListItemIcon from "@mui/material/ListItemIcon";
7
+ import ListItemText, { ListItemTextProps } from "@mui/material/ListItemText";
8
+ import React from "react";
9
+
10
+ type ListItemLabel<T extends object> =
11
+ | DataTypes.Keys<T, string | undefined>
12
+ | ((item: T) => string | undefined);
13
+
14
+ /**
15
+ * List multipler component props
16
+ */
17
+ export type ListMultiplerProps<T extends object> = ListProps & {
18
+ /**
19
+ * List data
20
+ */
21
+ data: T[];
22
+
23
+ /**
24
+ * Id field name
25
+ */
26
+ idField: DataTypes.Keys<T, IdType>;
27
+
28
+ /**
29
+ * Primary label field name or function
30
+ */
31
+ primaryLabel: ListItemLabel<T>;
32
+
33
+ /**
34
+ * Secondary label field name or function
35
+ */
36
+ secondaryLabel?: ListItemLabel<T>;
37
+
38
+ /**
39
+ * List item props
40
+ */
41
+ listItemProps?: ListItemProps;
42
+
43
+ /**
44
+ * List item text props
45
+ */
46
+ listItemTextProps?: Omit<ListItemTextProps, "primary" | "secondary">;
47
+
48
+ /**
49
+ * Input name
50
+ */
51
+ name?: string;
52
+
53
+ /**
54
+ * On change event
55
+ * @param items The selected items
56
+ * @param ids The selected IDs
57
+ */
58
+ onCheckItems?: (items: T[], ids: unknown[]) => void;
59
+ };
60
+
61
+ function GetListItemLabel<T extends object>(data: T, label?: ListItemLabel<T>) {
62
+ if (label == null) return undefined;
63
+ if (typeof label === "function") {
64
+ return label(data);
65
+ } else {
66
+ return data[label] as string | undefined;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * List multipler component
72
+ * @param props Props
73
+ * @returns Component
74
+ */
75
+ export function ListMultipler<T extends object>(props: ListMultiplerProps<T>) {
76
+ // Destruct
77
+ const {
78
+ data,
79
+ idField,
80
+ primaryLabel,
81
+ secondaryLabel,
82
+ listItemProps,
83
+ listItemTextProps,
84
+ name,
85
+ onCheckItems,
86
+ ...rest
87
+ } = props;
88
+
89
+ // Refs
90
+ const initialized = React.useRef(false);
91
+ React.useEffect(() => {
92
+ initialized.current = true;
93
+ }, []);
94
+
95
+ // State
96
+ const [checked, setChecked] = React.useState<T[]>([]);
97
+
98
+ const ids = React.useMemo(() => {
99
+ const ids = checked.map((u) => u[idField]);
100
+
101
+ if (onCheckItems && initialized.current) {
102
+ onCheckItems(checked, ids);
103
+ }
104
+
105
+ return ids;
106
+ }, [checked]);
107
+
108
+ function handleToggle(id: T[DataTypes.Keys<T, IdType>]) {
109
+ if (ids.includes(id)) {
110
+ setChecked((prev) => prev.filter((u) => u[idField] !== id));
111
+ } else {
112
+ const item = data.find((u) => u[idField] === id);
113
+ if (item) {
114
+ setChecked((prev) => [...prev, item]);
115
+ }
116
+ }
117
+ }
118
+
119
+ const inputType = typeof ids[0] === "string" ? "text" : "number";
120
+
121
+ // Layout
122
+ return (
123
+ <List {...rest}>
124
+ {name && (
125
+ <input
126
+ type={inputType}
127
+ style={{ display: "none" }}
128
+ name={name}
129
+ value={ids.join(",")}
130
+ readOnly
131
+ />
132
+ )}
133
+ {data.map((u) => (
134
+ <ListItem key={`${u[idField]}`} {...listItemProps}>
135
+ <ListItemButton dense onClick={() => handleToggle(u[idField])}>
136
+ <ListItemIcon>
137
+ <Checkbox
138
+ edge="start"
139
+ disableRipple
140
+ checked={ids.includes(u[idField])}
141
+ />
142
+ </ListItemIcon>
143
+ <ListItemText
144
+ primary={GetListItemLabel(u, primaryLabel)}
145
+ secondary={GetListItemLabel(u, secondaryLabel)}
146
+ {...listItemTextProps}
147
+ />
148
+ </ListItemButton>
149
+ </ListItem>
150
+ ))}
151
+ </List>
152
+ );
153
+ }
package/src/MUUtils.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { QueryRQ } from "@etsoo/appscript";
2
2
  import { IdType } from "@etsoo/shared";
3
- import { GridApiCommunity } from "@mui/x-data-grid/models/api/gridApiCommunity";
3
+ import { GridApiCommunity } from "@mui/x-data-grid/internals";
4
4
 
5
5
  /**
6
6
  * MU utilities
@@ -1,4 +1,9 @@
1
- import { ApiRefreshTokenDto, IApi, IApiPayload } from "@etsoo/appscript";
1
+ import {
2
+ ApiRefreshTokenDto,
3
+ IApi,
4
+ IApiPayload,
5
+ TokenAuthRQ
6
+ } from "@etsoo/appscript";
2
7
  import { ReactAppType } from "./ReactApp";
3
8
  import { IServiceUser, ServiceUserToken } from "./IServiceUser";
4
9
  import { IActionResult } from "@etsoo/shared";
@@ -17,6 +22,13 @@ export interface IServiceApp extends ReactAppType {
17
22
  */
18
23
  readonly coreOrigin: string;
19
24
 
25
+ /**
26
+ * Get token authorization request data
27
+ * @param api API, if not provided, use the core API
28
+ * @returns Result
29
+ */
30
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ;
31
+
20
32
  /**
21
33
  * Load core system UI
22
34
  * @param tryLogin Try login or not
@@ -6,7 +6,8 @@ import {
6
6
  BridgeUtils,
7
7
  ExternalEndpoint,
8
8
  IApi,
9
- IApiPayload
9
+ IApiPayload,
10
+ TokenAuthRQ
10
11
  } from "@etsoo/appscript";
11
12
  import { IServiceApp } from "./IServiceApp";
12
13
  import { IServiceAppSettings } from "./IServiceAppSettings";
@@ -68,6 +69,23 @@ export class ServiceApp<
68
69
  this.keepLogin = true;
69
70
  }
70
71
 
72
+ /**
73
+ * Get token authorization request data
74
+ * @param api API, if not provided, use the core API
75
+ * @returns Result
76
+ */
77
+ getTokenAuthRQ(api?: IApi): TokenAuthRQ {
78
+ api ??= this.coreApi;
79
+
80
+ const auth = api.getAuthorization();
81
+
82
+ if (auth == null) {
83
+ throw new Error("Authorization is required.");
84
+ }
85
+
86
+ return { accessToken: auth.token, tokenScheme: auth.scheme };
87
+ }
88
+
71
89
  /**
72
90
  * Load core system UI
73
91
  * @param tryLogin Try login or not
package/src/index.ts CHANGED
@@ -79,6 +79,7 @@ export * from "./LinkEx";
79
79
  export * from "./ListChooser";
80
80
  export * from "./ListItemRightIcon";
81
81
  export * from "./ListMoreDisplay";
82
+ export * from "./ListMultipler";
82
83
  export * from "./LoadingButton";
83
84
  export * from "./MaskInput";
84
85
  export * from "./MenuButton";
package/vite.config.mts CHANGED
@@ -4,6 +4,11 @@ import react from "@vitejs/plugin-react";
4
4
  export default defineConfig({
5
5
  plugins: [react()],
6
6
  test: {
7
+ server: {
8
+ deps: {
9
+ inline: [/@mui\/x-data-grid.*/]
10
+ }
11
+ },
7
12
  globals: true,
8
13
  environment: "jsdom",
9
14
  include: ["__tests__/**/*.ts(x)?"]