@etsoo/materialui 1.2.53 → 1.2.54

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.
@@ -1,6 +1,6 @@
1
- import { DataTypes, DelayedExecutorType, IdDefaultType } from '@etsoo/shared';
2
- import { ListItemButtonProps, ListProps } from '@mui/material';
3
- import React from 'react';
1
+ import { DataTypes, DelayedExecutorType, IdDefaultType } from "@etsoo/shared";
2
+ import { ListItemButtonProps, ListProps } from "@mui/material";
3
+ import React from "react";
4
4
  type QueryData = {
5
5
  title?: string;
6
6
  };
@@ -46,6 +46,10 @@ export type ListChooserProps<T extends object, D extends DataTypes.Keys<T>, Q ex
46
46
  * Title
47
47
  */
48
48
  title: string;
49
+ /**
50
+ * Double click enabled
51
+ */
52
+ doubleClickEnabled?: boolean;
49
53
  };
50
54
  /**
51
55
  * List chooser
@@ -1,7 +1,8 @@
1
- import { useDelayedExecutor } from '@etsoo/react';
2
- import { List, ListItem, ListItemButton, ListItemText, TextField } from '@mui/material';
3
- import React from 'react';
4
- import { VBox } from './FlexBox';
1
+ import { useDelayedExecutor } from "@etsoo/react";
2
+ import { List, ListItem, ListItemButton, ListItemText, TextField } from "@mui/material";
3
+ import CheckBoxIcon from "@mui/icons-material/CheckBox";
4
+ import React from "react";
5
+ import { VBox } from "./FlexBox";
5
6
  /**
6
7
  * List chooser
7
8
  * @param props Props
@@ -29,19 +30,20 @@ export function ListChooser(props) {
29
30
  });
30
31
  // Destruct
31
32
  const { conditionRenderer = (rq, delayed) => (React.createElement(TextField, { autoFocus: true, margin: "dense", name: "title", label: title, fullWidth: true, variant: "standard", inputProps: { maxLength: 128 }, onChange: (event) => {
32
- Reflect.set(rq, 'title', event.target.value);
33
+ Reflect.set(rq, "title", event.target.value);
33
34
  delayed.call();
34
35
  } })), itemRenderer = (item, selectProps) => {
35
36
  const id = item[idField];
36
- const label = typeof labelField === 'function'
37
+ const sp = selectProps(id);
38
+ const label = typeof labelField === "function"
37
39
  ? labelField(item)
38
40
  : Reflect.get(item, labelField);
39
- return (React.createElement(ListItem, { disableGutters: true, key: `${id}` },
40
- React.createElement(ListItemButton, { ...selectProps(id) },
41
+ return (React.createElement(ListItem, { disableGutters: true, key: `${id}`, secondaryAction: sp.selected ? React.createElement(CheckBoxIcon, { fontSize: "small" }) : undefined },
42
+ React.createElement(ListItemButton, { ...sp },
41
43
  React.createElement(ListItemText, { primary: label }))));
42
- }, idField = 'id', labelField = 'label', loadData, multiple = false, onItemChange, title, ...rest } = props;
44
+ }, idField = "id", labelField = "label", loadData, multiple = false, onItemChange, title, doubleClickEnabled = false, onDoubleClick, ...rest } = props;
43
45
  // Default minimum height
44
- (_a = rest.sx) !== null && _a !== void 0 ? _a : (rest.sx = { minHeight: '220px' });
46
+ (_a = rest.sx) !== null && _a !== void 0 ? _a : (rest.sx = { minHeight: "220px" });
45
47
  // State
46
48
  const [items, setItems] = React.useState([]);
47
49
  // Query request data
@@ -72,8 +74,20 @@ export function ListChooser(props) {
72
74
  delayed.clear();
73
75
  };
74
76
  }, [delayed]);
77
+ const onDoubleClickLocal = (event) => {
78
+ var _a;
79
+ if (onDoubleClick)
80
+ onDoubleClick(event);
81
+ if (doubleClickEnabled) {
82
+ const button = (_a = event.currentTarget
83
+ .closest("form")) === null || _a === void 0 ? void 0 : _a.elements.namedItem("okButton");
84
+ if (button) {
85
+ button.click();
86
+ }
87
+ }
88
+ };
75
89
  // Layout
76
90
  return (React.createElement(VBox, null,
77
91
  conditionRenderer(rq.current, delayed),
78
- React.createElement(List, { disablePadding: true, dense: true, ...rest }, items.map((item) => itemRenderer(item, selectProps)))));
92
+ React.createElement(List, { onDoubleClick: onDoubleClickLocal, disablePadding: true, dense: true, ...rest }, items.map((item) => itemRenderer(item, selectProps)))));
79
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.2.53",
3
+ "version": "1.2.54",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,84 +1,90 @@
1
- import { useDelayedExecutor } from '@etsoo/react';
2
- import { DataTypes, DelayedExecutorType, IdDefaultType } from '@etsoo/shared';
1
+ import { useDelayedExecutor } from "@etsoo/react";
2
+ import { DataTypes, DelayedExecutorType, IdDefaultType } from "@etsoo/shared";
3
3
  import {
4
- List,
5
- ListItem,
6
- ListItemButton,
7
- ListItemButtonProps,
8
- ListItemText,
9
- ListProps,
10
- TextField
11
- } from '@mui/material';
12
- import React from 'react';
13
- import { VBox } from './FlexBox';
4
+ List,
5
+ ListItem,
6
+ ListItemButton,
7
+ ListItemButtonProps,
8
+ ListItemText,
9
+ ListProps,
10
+ TextField
11
+ } from "@mui/material";
12
+ import CheckBoxIcon from "@mui/icons-material/CheckBox";
13
+ import React from "react";
14
+ import { VBox } from "./FlexBox";
14
15
 
15
16
  type QueryData = {
16
- title?: string;
17
+ title?: string;
17
18
  };
18
19
 
19
20
  /**
20
21
  * List chooser button props
21
22
  */
22
23
  export interface ListChooserButtonProps<
23
- T extends object,
24
- D extends DataTypes.Keys<T>
24
+ T extends object,
25
+ D extends DataTypes.Keys<T>
25
26
  > {
26
- (id: T[D]): ListItemButtonProps;
27
+ (id: T[D]): ListItemButtonProps;
27
28
  }
28
29
 
29
30
  /**
30
31
  * List chooser props
31
32
  */
32
33
  export type ListChooserProps<
33
- T extends object,
34
- D extends DataTypes.Keys<T>,
35
- Q extends object
34
+ T extends object,
35
+ D extends DataTypes.Keys<T>,
36
+ Q extends object
36
37
  > = ListProps & {
37
- /**
38
- * Condition renderer
39
- */
40
- conditionRenderer?: (
41
- rq: Partial<Q>,
42
- delayed: DelayedExecutorType
43
- ) => React.ReactNode;
44
-
45
- /**
46
- * List item renderer
47
- */
48
- itemRenderer?: (
49
- data: T,
50
- props: ListChooserButtonProps<T, D>
51
- ) => React.ReactNode;
52
-
53
- /**
54
- * Label field
55
- */
56
- labelField?: DataTypes.Keys<T, string> | ((data: T) => string);
57
-
58
- /**
59
- * Id field
60
- */
61
- idField?: D;
62
-
63
- /**
64
- * Load data callback
65
- */
66
- loadData: (rq: Partial<Q>) => PromiseLike<T[] | null | undefined>;
67
-
68
- /**
69
- * Multiple selected
70
- */
71
- multiple?: boolean;
72
-
73
- /**
74
- * Item onchange callback
75
- */
76
- onItemChange: (items: T[], ids: T[D][]) => void;
77
-
78
- /**
79
- * Title
80
- */
81
- title: string;
38
+ /**
39
+ * Condition renderer
40
+ */
41
+ conditionRenderer?: (
42
+ rq: Partial<Q>,
43
+ delayed: DelayedExecutorType
44
+ ) => React.ReactNode;
45
+
46
+ /**
47
+ * List item renderer
48
+ */
49
+ itemRenderer?: (
50
+ data: T,
51
+ props: ListChooserButtonProps<T, D>
52
+ ) => React.ReactNode;
53
+
54
+ /**
55
+ * Label field
56
+ */
57
+ labelField?: DataTypes.Keys<T, string> | ((data: T) => string);
58
+
59
+ /**
60
+ * Id field
61
+ */
62
+ idField?: D;
63
+
64
+ /**
65
+ * Load data callback
66
+ */
67
+ loadData: (rq: Partial<Q>) => PromiseLike<T[] | null | undefined>;
68
+
69
+ /**
70
+ * Multiple selected
71
+ */
72
+ multiple?: boolean;
73
+
74
+ /**
75
+ * Item onchange callback
76
+ */
77
+ onItemChange: (items: T[], ids: T[D][]) => void;
78
+
79
+ /**
80
+ * Title
81
+ */
82
+ title: string;
83
+
84
+ /**
85
+ * Double click enabled
86
+ */
87
+ doubleClickEnabled?: boolean;
82
88
  };
83
89
 
84
90
  /**
@@ -87,118 +93,139 @@ export type ListChooserProps<
87
93
  * @returns Component
88
94
  */
89
95
  export function ListChooser<
90
- T extends object,
91
- D extends DataTypes.Keys<T> = IdDefaultType<T>,
92
- Q extends object = QueryData
96
+ T extends object,
97
+ D extends DataTypes.Keys<T> = IdDefaultType<T>,
98
+ Q extends object = QueryData
93
99
  >(props: ListChooserProps<T, D, Q>) {
94
- // Selected ids state
95
- const [selectedIds, setSelectedIds] = React.useState<T[D][]>([]);
96
-
97
- const selectProps: ListChooserButtonProps<T, D> = (id: T[D]) => ({
98
- selected: selectedIds.includes(id),
99
- onClick: () => {
100
- if (multiple) {
101
- const index = selectedIds.indexOf(id);
102
- if (index === -1) selectedIds.push(id);
103
- else selectedIds.splice(index, 1);
104
- setSelectedIds([...selectedIds]);
105
- } else {
106
- setSelectedIds([id]);
107
- }
108
- }
109
- });
110
-
111
- // Destruct
112
- const {
113
- conditionRenderer = (rq: Partial<Q>, delayed: DelayedExecutorType) => (
114
- <TextField
115
- autoFocus
116
- margin="dense"
117
- name="title"
118
- label={title}
119
- fullWidth
120
- variant="standard"
121
- inputProps={{ maxLength: 128 }}
122
- onChange={(event) => {
123
- Reflect.set(rq, 'title', event.target.value);
124
- delayed.call();
125
- }}
126
- />
127
- ),
128
- itemRenderer = (item, selectProps) => {
129
- const id = item[idField];
130
- const label =
131
- typeof labelField === 'function'
132
- ? labelField(item)
133
- : (Reflect.get(item, labelField) as React.ReactNode);
134
-
135
- return (
136
- <ListItem disableGutters key={`${id}`}>
137
- <ListItemButton {...selectProps(id)}>
138
- <ListItemText primary={label} />
139
- </ListItemButton>
140
- </ListItem>
141
- );
142
- },
143
- idField = 'id' as D,
144
- labelField = 'label',
145
- loadData,
146
- multiple = false,
147
- onItemChange,
148
- title,
149
- ...rest
150
- } = props;
151
-
152
- // Default minimum height
153
- rest.sx ??= { minHeight: '220px' };
154
-
155
- // State
156
- const [items, setItems] = React.useState<T[]>([]);
157
-
158
- // Query request data
159
- const mounted = React.useRef<boolean>(false);
160
- const rq = React.useRef<Partial<Q>>({});
161
-
162
- // Delayed execution
163
- const delayed = useDelayedExecutor(async () => {
164
- const result = await loadData(rq.current);
165
- if (result == null || !mounted.current) return;
166
-
167
- if (
168
- !multiple &&
169
- selectedIds.length > 0 &&
170
- !result.some((item) => selectedIds.includes(item[idField]))
171
- ) {
172
- setSelectedIds([]);
173
- }
174
-
175
- setItems(result);
176
- }, 480);
177
-
178
- React.useEffect(() => {
179
- if (!mounted.current) return;
180
- onItemChange(
181
- items.filter((item) => selectedIds.includes(item[idField])),
182
- selectedIds
183
- );
184
- }, [selectedIds]);
185
-
186
- React.useEffect(() => {
187
- mounted.current = true;
188
- delayed.call(0);
189
- return () => {
190
- mounted.current = false;
191
- delayed.clear();
192
- };
193
- }, [delayed]);
194
-
195
- // Layout
196
- return (
197
- <VBox>
198
- {conditionRenderer(rq.current, delayed)}
199
- <List disablePadding dense {...rest}>
200
- {items.map((item) => itemRenderer(item, selectProps))}
201
- </List>
202
- </VBox>
100
+ // Selected ids state
101
+ const [selectedIds, setSelectedIds] = React.useState<T[D][]>([]);
102
+
103
+ const selectProps: ListChooserButtonProps<T, D> = (id: T[D]) => ({
104
+ selected: selectedIds.includes(id),
105
+ onClick: () => {
106
+ if (multiple) {
107
+ const index = selectedIds.indexOf(id);
108
+ if (index === -1) selectedIds.push(id);
109
+ else selectedIds.splice(index, 1);
110
+ setSelectedIds([...selectedIds]);
111
+ } else {
112
+ setSelectedIds([id]);
113
+ }
114
+ }
115
+ });
116
+
117
+ // Destruct
118
+ const {
119
+ conditionRenderer = (rq: Partial<Q>, delayed: DelayedExecutorType) => (
120
+ <TextField
121
+ autoFocus
122
+ margin="dense"
123
+ name="title"
124
+ label={title}
125
+ fullWidth
126
+ variant="standard"
127
+ inputProps={{ maxLength: 128 }}
128
+ onChange={(event) => {
129
+ Reflect.set(rq, "title", event.target.value);
130
+ delayed.call();
131
+ }}
132
+ />
133
+ ),
134
+ itemRenderer = (item, selectProps) => {
135
+ const id = item[idField];
136
+ const sp = selectProps(id);
137
+ const label =
138
+ typeof labelField === "function"
139
+ ? labelField(item)
140
+ : (Reflect.get(item, labelField) as React.ReactNode);
141
+
142
+ return (
143
+ <ListItem
144
+ disableGutters
145
+ key={`${id}`}
146
+ secondaryAction={
147
+ sp.selected ? <CheckBoxIcon fontSize="small" /> : undefined
148
+ }
149
+ >
150
+ <ListItemButton {...sp}>
151
+ <ListItemText primary={label} />
152
+ </ListItemButton>
153
+ </ListItem>
154
+ );
155
+ },
156
+ idField = "id" as D,
157
+ labelField = "label",
158
+ loadData,
159
+ multiple = false,
160
+ onItemChange,
161
+ title,
162
+ doubleClickEnabled = false,
163
+ onDoubleClick,
164
+ ...rest
165
+ } = props;
166
+
167
+ // Default minimum height
168
+ rest.sx ??= { minHeight: "220px" };
169
+
170
+ // State
171
+ const [items, setItems] = React.useState<T[]>([]);
172
+
173
+ // Query request data
174
+ const mounted = React.useRef<boolean>(false);
175
+ const rq = React.useRef<Partial<Q>>({});
176
+
177
+ // Delayed execution
178
+ const delayed = useDelayedExecutor(async () => {
179
+ const result = await loadData(rq.current);
180
+ if (result == null || !mounted.current) return;
181
+
182
+ if (
183
+ !multiple &&
184
+ selectedIds.length > 0 &&
185
+ !result.some((item) => selectedIds.includes(item[idField]))
186
+ ) {
187
+ setSelectedIds([]);
188
+ }
189
+
190
+ setItems(result);
191
+ }, 480);
192
+
193
+ React.useEffect(() => {
194
+ if (!mounted.current) return;
195
+ onItemChange(
196
+ items.filter((item) => selectedIds.includes(item[idField])),
197
+ selectedIds
203
198
  );
199
+ }, [selectedIds]);
200
+
201
+ React.useEffect(() => {
202
+ mounted.current = true;
203
+ delayed.call(0);
204
+ return () => {
205
+ mounted.current = false;
206
+ delayed.clear();
207
+ };
208
+ }, [delayed]);
209
+
210
+ const onDoubleClickLocal = (event: React.MouseEvent<HTMLUListElement>) => {
211
+ if (onDoubleClick) onDoubleClick(event);
212
+ if (doubleClickEnabled) {
213
+ const button = event.currentTarget
214
+ .closest("form")
215
+ ?.elements.namedItem("okButton") as HTMLButtonElement | undefined;
216
+ if (button) {
217
+ button.click();
218
+ }
219
+ }
220
+ };
221
+
222
+ // Layout
223
+ return (
224
+ <VBox>
225
+ {conditionRenderer(rq.current, delayed)}
226
+ <List onDoubleClick={onDoubleClickLocal} disablePadding dense {...rest}>
227
+ {items.map((item) => itemRenderer(item, selectProps))}
228
+ </List>
229
+ </VBox>
230
+ );
204
231
  }