@etsoo/materialui 1.6.11 → 1.6.13

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.
@@ -0,0 +1,105 @@
1
+ import { DataTypes, IdType } from "@etsoo/shared";
2
+ import React from "react";
3
+ import { CSSProperties, Theme } from "@mui/material/styles";
4
+ import { useSortable } from "@dnd-kit/react/sortable";
5
+ import { DragDropEvents } from "@dnd-kit/react";
6
+ /**
7
+ * DnD sortable item default style
8
+ * @param index Item index
9
+ * @param isDragging Is dragging
10
+ * @param theme Theme
11
+ * @returns Style
12
+ */
13
+ export declare const DnDSortableItemStyle: (index: number, isDragging: boolean, theme: Theme) => {
14
+ padding: string;
15
+ transform: string;
16
+ zIndex: string | number;
17
+ boxShadow: string;
18
+ background: string;
19
+ };
20
+ /**
21
+ * DnD sortable list forward ref
22
+ */
23
+ export interface DnDSortableListRef<D extends object> {
24
+ /**
25
+ * Add item
26
+ * @param item New item
27
+ */
28
+ addItem(item: D): void;
29
+ /**
30
+ * Add items
31
+ * @param items items
32
+ */
33
+ addItems(items: D[]): void;
34
+ /**
35
+ * Delete item
36
+ * @param index Item index
37
+ */
38
+ deleteItem(index: number): void;
39
+ /**
40
+ * Edit item
41
+ * @param newItem New item
42
+ * @param index Index
43
+ */
44
+ editItem(newItem: D, index: number): boolean;
45
+ /**
46
+ * Get all items
47
+ */
48
+ getItems(): D[];
49
+ }
50
+ /**
51
+ * DnD sortable list props
52
+ */
53
+ export type DnDSortableListProps<D extends object, E extends React.ElementType = React.ElementType> = {
54
+ /**
55
+ * Component type to render the list into
56
+ * Default is React.Fragment
57
+ */
58
+ component?: E;
59
+ /**
60
+ * Component props
61
+ */
62
+ componentProps?: React.ComponentProps<E>;
63
+ /**
64
+ * List items
65
+ */
66
+ items: D[];
67
+ /**
68
+ * Id field
69
+ */
70
+ idField?: DataTypes.Keys<D> | ((item: D) => IdType);
71
+ /**
72
+ * Label field
73
+ */
74
+ labelField: DataTypes.Keys<D, string> | ((item: D) => string);
75
+ /**
76
+ * Methods ref
77
+ */
78
+ mRef?: React.Ref<DnDSortableListRef<D>>;
79
+ /**
80
+ * Item renderer
81
+ */
82
+ itemRenderer: (data: D, style: CSSProperties, state: ReturnType<typeof useSortable<D>>) => React.ReactElement;
83
+ /**
84
+ * Get list item style callback
85
+ */
86
+ itemStyle?: (index: number, isDragging: boolean) => CSSProperties;
87
+ /**
88
+ * Data change handler
89
+ */
90
+ onChange?: (items: D[]) => void;
91
+ /**
92
+ * Drag start handler
93
+ */
94
+ onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
95
+ /**
96
+ * Drag end handler
97
+ */
98
+ onDragEnd?: (items: D[], ...args: Parameters<DragDropEvents["dragend"]>) => void;
99
+ };
100
+ /**
101
+ * DnD sortable list component
102
+ * @param props Props
103
+ * @returns Component
104
+ */
105
+ export declare function DnDSortableList<D extends object, E extends React.ElementType = React.ElementType>(props: DnDSortableListProps<D, E>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,143 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { useTheme } from "@mui/material/styles";
4
+ import { isSortableOperation, useSortable } from "@dnd-kit/react/sortable";
5
+ import { DragDropProvider } from "@dnd-kit/react";
6
+ /**
7
+ * DnD sortable item default style
8
+ * @param index Item index
9
+ * @param isDragging Is dragging
10
+ * @param theme Theme
11
+ * @returns Style
12
+ */
13
+ export const DnDSortableItemStyle = (index, isDragging, theme) => ({
14
+ padding: theme.spacing(1),
15
+ transform: isDragging ? "scale(1.03)" : "none",
16
+ zIndex: isDragging ? 1 : "auto",
17
+ boxShadow: isDragging
18
+ ? `-1px 0 8px 0 ${theme.palette.grey[400]}, 0px 8px 8px 0 ${theme.palette.grey[200]}`
19
+ : "none",
20
+ background: isDragging
21
+ ? theme.palette.primary.light
22
+ : index % 2 === 0
23
+ ? theme.palette.grey[100]
24
+ : theme.palette.grey[50]
25
+ });
26
+ function SortableItem(props) {
27
+ const theme = useTheme();
28
+ const { id, data, index, itemRenderer, itemStyle = (index, isDragging) => DnDSortableItemStyle(index, isDragging, theme) } = props;
29
+ const state = useSortable({ id, data, index });
30
+ const style = itemStyle(index, state.isDragging);
31
+ return itemRenderer(data, style, state);
32
+ }
33
+ /**
34
+ * DnD sortable list component
35
+ * @param props Props
36
+ * @returns Component
37
+ */
38
+ export function DnDSortableList(props) {
39
+ // Destruct
40
+ const Component = props.component || React.Fragment;
41
+ const { componentProps, idField, itemRenderer, itemStyle, labelField, mRef, onChange, onDragStart, onDragEnd } = props;
42
+ const idFn = typeof idField === "function"
43
+ ? idField
44
+ : (item) => !idField
45
+ ? Reflect.get(item, "id")
46
+ : item[idField];
47
+ const labelFn = React.useCallback(typeof labelField === "function"
48
+ ? labelField
49
+ : (item) => item[labelField], [labelField]);
50
+ // States
51
+ const [items, setItems] = React.useState([]);
52
+ React.useEffect(() => {
53
+ setItems(props.items);
54
+ }, [props.items]);
55
+ const changeItems = React.useCallback((newItems) => {
56
+ // Possible to alter items with the handler
57
+ onChange?.(newItems);
58
+ // Update state
59
+ setItems(newItems);
60
+ }, [onChange]);
61
+ // Methods
62
+ React.useImperativeHandle(mRef, () => {
63
+ return {
64
+ addItem(newItem) {
65
+ // Existence check
66
+ if (items.some((item) => labelFn(item) === labelFn(newItem))) {
67
+ return false;
68
+ }
69
+ // Clone
70
+ const newItems = [newItem, ...items];
71
+ // Update the state
72
+ changeItems(newItems);
73
+ return true;
74
+ },
75
+ addItems(inputItems) {
76
+ // Clone
77
+ const newItems = [...items];
78
+ // Insert items
79
+ inputItems.forEach((newItem) => {
80
+ // Existence check
81
+ if (newItems.some((item) => labelFn(item) === labelFn(newItem))) {
82
+ return;
83
+ }
84
+ newItems.push(newItem);
85
+ });
86
+ // Update the state
87
+ changeItems(newItems);
88
+ return newItems.length - items.length;
89
+ },
90
+ editItem(newItem, index) {
91
+ // Existence check
92
+ const newIndex = items.findIndex((item) => labelFn(item) === labelFn(newItem));
93
+ if (newIndex >= 0 && newIndex !== index) {
94
+ // Label field is the same with a different item
95
+ return false;
96
+ }
97
+ // Clone
98
+ const newItems = [...items];
99
+ // Remove the item
100
+ newItems.splice(index, 1, newItem);
101
+ // Update the state
102
+ changeItems(newItems);
103
+ return true;
104
+ },
105
+ deleteItem(index) {
106
+ // Clone
107
+ const newItems = [...items];
108
+ // Remove the item
109
+ newItems.splice(index, 1);
110
+ // Update the state
111
+ changeItems(newItems);
112
+ },
113
+ getItems() {
114
+ return items;
115
+ }
116
+ };
117
+ }, [items, labelFn, changeItems]);
118
+ function handleDragEnd(...args) {
119
+ // Event
120
+ const event = args[0];
121
+ // Cancelled
122
+ if (event.canceled)
123
+ return;
124
+ if (isSortableOperation(event.operation) && event.operation.source) {
125
+ const { initialIndex, index } = event.operation.source;
126
+ if (initialIndex === index)
127
+ return;
128
+ // Clone
129
+ const newItems = [...items];
130
+ // Removed item
131
+ const [removed] = newItems.splice(initialIndex, 1);
132
+ // Insert to the destination index
133
+ newItems.splice(index, 0, removed);
134
+ changeItems(newItems);
135
+ // Drag end handler
136
+ onDragEnd?.(newItems, ...args);
137
+ }
138
+ }
139
+ return (_jsx(DragDropProvider, { onDragStart: (event) => onDragStart?.(items, event), onDragEnd: (event, manager) => handleDragEnd(event, manager), children: _jsx(Component, { ...componentProps, children: items.map((item, index) => {
140
+ const id = idFn(item);
141
+ return (_jsx(SortableItem, { id: id, index: index, data: item, itemRenderer: itemRenderer, itemStyle: itemStyle }, id));
142
+ }) }) }));
143
+ }
@@ -54,7 +54,7 @@ export * from "./DataLoadField";
54
54
  export * from "./DataSteps";
55
55
  export * from "./DataTable";
56
56
  export * from "./DialogButton";
57
- export * from "./DnDList";
57
+ export * from "./DnDSortableList";
58
58
  export * from "./DownUpButton";
59
59
  export * from "./DraggablePaperComponent";
60
60
  export * from "./EmailInput";
package/lib/mjs/index.js CHANGED
@@ -54,7 +54,7 @@ export * from "./DataLoadField";
54
54
  export * from "./DataSteps";
55
55
  export * from "./DataTable";
56
56
  export * from "./DialogButton";
57
- export * from "./DnDList";
57
+ export * from "./DnDSortableList";
58
58
  export * from "./DownUpButton";
59
59
  export * from "./DraggablePaperComponent";
60
60
  export * from "./EmailInput";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -36,8 +36,7 @@
36
36
  },
37
37
  "homepage": "https://github.com/ETSOO/ReactMU#readme",
38
38
  "dependencies": {
39
- "@dnd-kit/core": "^6.3.1",
40
- "@dnd-kit/sortable": "^10.0.0",
39
+ "@dnd-kit/react": "^0.3.2",
41
40
  "@emotion/react": "^11.14.0",
42
41
  "@emotion/styled": "^11.14.1",
43
42
  "@etsoo/appscript": "^1.6.53",
@@ -46,7 +45,7 @@
46
45
  "@etsoo/shared": "^1.2.80",
47
46
  "@mui/icons-material": "^7.3.8",
48
47
  "@mui/material": "^7.3.8",
49
- "@mui/x-data-grid": "^8.27.1",
48
+ "@mui/x-data-grid": "^8.27.3",
50
49
  "chart.js": "^4.5.1",
51
50
  "chartjs-plugin-datalabels": "^2.2.0",
52
51
  "dompurify": "^3.3.1",
@@ -2,7 +2,7 @@ import Button, { ButtonProps } from "@mui/material/Button";
2
2
  import Chip from "@mui/material/Chip";
3
3
  import React from "react";
4
4
  import { DataTypes, IdType } from "@etsoo/shared";
5
- import Grid from "@mui/material/Grid";
5
+ import Grid, { GridSize } from "@mui/material/Grid";
6
6
  import Typography from "@mui/material/Typography";
7
7
  import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
8
8
  import AddIcon from "@mui/icons-material/Add";
@@ -13,7 +13,12 @@ import TextField from "@mui/material/TextField";
13
13
  import { HBox, VBox } from "./FlexBox";
14
14
  import { useRequiredAppContext } from "./app/ReactApp";
15
15
  import FormLabel from "@mui/material/FormLabel";
16
- import { DnDList, DnDListRef } from "./DnDList";
16
+ import {
17
+ DnDSortableList,
18
+ DnDSortableListProps,
19
+ DnDSortableListRef
20
+ } from "./DnDSortableList";
21
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
17
22
 
18
23
  type DnDItemType = {
19
24
  id: IdType;
@@ -46,7 +51,7 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
46
51
  /**
47
52
  * Label field in items
48
53
  */
49
- labelField: DataTypes.Keys<D>;
54
+ labelField: DnDSortableListProps<D>["labelField"];
50
55
 
51
56
  /**
52
57
  * Label formatter
@@ -95,6 +100,11 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
95
100
  */
96
101
  required?: boolean;
97
102
 
103
+ /**
104
+ * Item size
105
+ */
106
+ itemSize?: ResponsiveStyleValue<GridSize>;
107
+
98
108
  /**
99
109
  * Value
100
110
  */
@@ -103,7 +113,7 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
103
113
 
104
114
  type ButtonPopupListProps<D extends DnDItemType> = Pick<
105
115
  ButtonPopupCheckboxProps<D>,
106
- "addSplitter" | "labelField" | "labels" | "onAdd" | "value"
116
+ "addSplitter" | "labelField" | "labels" | "onAdd" | "value" | "itemSize"
107
117
  > &
108
118
  Required<Pick<ButtonPopupCheckboxProps<D>, "labelFormatter">> & {
109
119
  /**
@@ -126,6 +136,7 @@ function ButtonPopupList<D extends DnDItemType>(
126
136
  addSplitter = /\s*[,;]\s*/,
127
137
  value = [],
128
138
  items,
139
+ itemSize = { xs: 12, md: 6, lx: 4 },
129
140
  labelField,
130
141
  labelFormatter,
131
142
  labels,
@@ -134,7 +145,7 @@ function ButtonPopupList<D extends DnDItemType>(
134
145
  } = props;
135
146
 
136
147
  // Methods
137
- const dndRef = React.createRef<DnDListRef<D>>();
148
+ const dndRef = React.createRef<DnDSortableListRef<D>>();
138
149
 
139
150
  // Refs
140
151
  const inputRef = React.useRef<HTMLInputElement>(null);
@@ -158,32 +169,37 @@ function ButtonPopupList<D extends DnDItemType>(
158
169
 
159
170
  return (
160
171
  <VBox gap={2}>
161
- <DnDList<D>
172
+ <DnDSortableList<D>
162
173
  component={Grid}
163
174
  componentProps={{ container: true, spacing: 0 }}
164
175
  items={items}
165
176
  labelField={labelField}
166
- onFormChange={(items) => {
177
+ onChange={(items) => {
167
178
  const ids = items
168
179
  .filter((item) => tempSelectedIds.current.includes(item.id))
169
180
  .map((item) => item.id);
170
181
 
171
182
  onValueChange(ids);
172
183
  }}
173
- itemRenderer={(item, index, nodeRef, actionNodeRef) => (
184
+ itemRenderer={(
185
+ item,
186
+ style,
187
+ { sortable: { index }, ref, handleRef }
188
+ ) => (
174
189
  <Grid
175
- size={{ xs: 12, md: 6, lg: 4 }}
190
+ size={itemSize}
176
191
  display="flex"
177
192
  justifyContent="flex-start"
178
193
  alignItems="center"
179
194
  gap={1}
180
- {...nodeRef}
195
+ ref={ref}
196
+ style={style}
181
197
  >
182
198
  <IconButton
183
199
  style={{ cursor: "move" }}
184
200
  size="small"
185
201
  title={labels?.dragIndicator}
186
- {...actionNodeRef}
202
+ ref={handleRef}
187
203
  >
188
204
  <DragIndicatorIcon />
189
205
  </IconButton>
@@ -206,9 +222,8 @@ function ButtonPopupList<D extends DnDItemType>(
206
222
  />
207
223
  </Grid>
208
224
  )}
209
- height={200}
210
225
  mRef={dndRef}
211
- ></DnDList>
226
+ ></DnDSortableList>
212
227
  {onAdd && (
213
228
  <HBox gap={1}>
214
229
  <TextField
@@ -273,9 +288,12 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
273
288
  addSplitter,
274
289
  value = [],
275
290
  inputName,
291
+ itemSize,
276
292
  label,
277
293
  labelEnd,
278
294
  labelFormatter = (data) => {
295
+ if (typeof labelField === "function") return labelField(data);
296
+
279
297
  if (labelField in data) {
280
298
  return data[labelField] as string;
281
299
  }
@@ -343,6 +361,7 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
343
361
  items={items}
344
362
  labelFormatter={labelFormatter}
345
363
  labelField={labelField}
364
+ itemSize={itemSize}
346
365
  labels={labels}
347
366
  onAdd={onAdd}
348
367
  onValueChange={(ids) => {
@@ -2,7 +2,7 @@ import Button, { ButtonProps } from "@mui/material/Button";
2
2
  import Chip from "@mui/material/Chip";
3
3
  import React from "react";
4
4
  import { DataTypes, IdType, NumberUtils } from "@etsoo/shared";
5
- import Grid from "@mui/material/Grid";
5
+ import Grid, { GridSize } from "@mui/material/Grid";
6
6
  import Typography from "@mui/material/Typography";
7
7
  import AddIcon from "@mui/icons-material/Add";
8
8
  import FormControlLabel from "@mui/material/FormControlLabel";
@@ -12,6 +12,7 @@ import FormLabel from "@mui/material/FormLabel";
12
12
  import RadioGroup from "@mui/material/RadioGroup";
13
13
  import { HBox, VBox } from "./FlexBox";
14
14
  import { useRequiredAppContext } from "./app/ReactApp";
15
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
15
16
 
16
17
  type DnDItemType = {
17
18
  id: IdType;
@@ -93,6 +94,11 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<
93
94
  */
94
95
  required?: boolean;
95
96
 
97
+ /**
98
+ * Item size
99
+ */
100
+ itemSize?: ResponsiveStyleValue<GridSize>;
101
+
96
102
  /**
97
103
  * Value
98
104
  */
@@ -101,7 +107,7 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<
101
107
 
102
108
  type ButtonPopupListProps<D extends DnDItemType> = Pick<
103
109
  ButtonPopupRadioProps<D>,
104
- "addSplitter" | "labels" | "onAdd" | "value"
110
+ "addSplitter" | "labels" | "onAdd" | "value" | "itemSize"
105
111
  > &
106
112
  Required<Pick<ButtonPopupRadioProps<D>, "labelFormatter">> & {
107
113
  /**
@@ -126,6 +132,7 @@ function ButtonPopupList<D extends DnDItemType>(
126
132
  items,
127
133
  labelFormatter,
128
134
  labels,
135
+ itemSize = { xs: 12, md: 6, lx: 4 },
129
136
  onAdd,
130
137
  onValueChange
131
138
  } = props;
@@ -159,7 +166,7 @@ function ButtonPopupList<D extends DnDItemType>(
159
166
  <Grid container spacing={0}>
160
167
  {items.map((item) => (
161
168
  <Grid
162
- size={{ xs: 12, md: 6, lg: 4 }}
169
+ size={itemSize}
163
170
  display="flex"
164
171
  justifyContent="flex-start"
165
172
  alignItems="center"
@@ -233,6 +240,7 @@ export function ButtonPopupRadio<D extends DnDItemType>(
233
240
  const {
234
241
  addSplitter,
235
242
  inputName,
243
+ itemSize,
236
244
  label,
237
245
  labelEnd,
238
246
  labelFormatter = (data) => {
@@ -305,6 +313,7 @@ export function ButtonPopupRadio<D extends DnDItemType>(
305
313
  addSplitter={addSplitter}
306
314
  value={currentValue}
307
315
  items={items}
316
+ itemSize={itemSize}
308
317
  labelFormatter={labelFormatter}
309
318
  labels={labels}
310
319
  onAdd={onAdd}