@etsoo/materialui 1.6.10 → 1.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -36,17 +36,16 @@
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",
44
43
  "@etsoo/notificationbase": "^1.1.66",
45
- "@etsoo/react": "^1.8.72",
44
+ "@etsoo/react": "^1.8.74",
46
45
  "@etsoo/shared": "^1.2.80",
47
- "@mui/icons-material": "^7.3.7",
48
- "@mui/material": "^7.3.7",
49
- "@mui/x-data-grid": "^8.26.0",
46
+ "@mui/icons-material": "^7.3.8",
47
+ "@mui/material": "^7.3.8",
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",
@@ -65,22 +64,21 @@
65
64
  "react-dom": "$react-dom"
66
65
  },
67
66
  "devDependencies": {
68
- "@babel/cli": "^7.28.6",
69
- "@babel/core": "^7.28.6",
70
- "@babel/plugin-transform-runtime": "^7.28.5",
71
- "@babel/preset-env": "^7.28.6",
67
+ "@babel/core": "^7.29.0",
68
+ "@babel/plugin-transform-runtime": "^7.29.0",
69
+ "@babel/preset-env": "^7.29.0",
72
70
  "@babel/preset-react": "^7.28.5",
73
71
  "@babel/preset-typescript": "^7.28.5",
74
- "@babel/runtime-corejs3": "^7.28.6",
72
+ "@babel/runtime-corejs3": "^7.29.0",
75
73
  "@testing-library/react": "^16.3.2",
76
74
  "@types/pica": "^9.0.5",
77
75
  "@types/pulltorefreshjs": "^0.1.7",
78
- "@types/react": "^19.2.10",
76
+ "@types/react": "^19.2.14",
79
77
  "@types/react-avatar-editor": "^13.0.4",
80
78
  "@types/react-dom": "^19.2.3",
81
79
  "@types/react-input-mask": "^3.0.6",
82
- "@vitejs/plugin-react": "^5.1.2",
83
- "jsdom": "^27.4.0",
80
+ "@vitejs/plugin-react": "^5.1.4",
81
+ "jsdom": "^28.1.0",
84
82
  "typescript": "^5.9.3",
85
83
  "vitest": "^4.0.18"
86
84
  }
@@ -13,7 +13,11 @@ 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";
17
21
 
18
22
  type DnDItemType = {
19
23
  id: IdType;
@@ -46,7 +50,7 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
46
50
  /**
47
51
  * Label field in items
48
52
  */
49
- labelField: DataTypes.Keys<D>;
53
+ labelField: DnDSortableListProps<D>["labelField"];
50
54
 
51
55
  /**
52
56
  * Label formatter
@@ -134,7 +138,7 @@ function ButtonPopupList<D extends DnDItemType>(
134
138
  } = props;
135
139
 
136
140
  // Methods
137
- const dndRef = React.createRef<DnDListRef<D>>();
141
+ const dndRef = React.createRef<DnDSortableListRef<D>>();
138
142
 
139
143
  // Refs
140
144
  const inputRef = React.useRef<HTMLInputElement>(null);
@@ -158,32 +162,37 @@ function ButtonPopupList<D extends DnDItemType>(
158
162
 
159
163
  return (
160
164
  <VBox gap={2}>
161
- <DnDList<D>
165
+ <DnDSortableList<D>
162
166
  component={Grid}
163
167
  componentProps={{ container: true, spacing: 0 }}
164
168
  items={items}
165
169
  labelField={labelField}
166
- onFormChange={(items) => {
170
+ onChange={(items) => {
167
171
  const ids = items
168
172
  .filter((item) => tempSelectedIds.current.includes(item.id))
169
173
  .map((item) => item.id);
170
174
 
171
175
  onValueChange(ids);
172
176
  }}
173
- itemRenderer={(item, index, nodeRef, actionNodeRef) => (
177
+ itemRenderer={(
178
+ item,
179
+ style,
180
+ { sortable: { index }, ref, handleRef }
181
+ ) => (
174
182
  <Grid
175
183
  size={{ xs: 12, md: 6, lg: 4 }}
176
184
  display="flex"
177
185
  justifyContent="flex-start"
178
186
  alignItems="center"
179
187
  gap={1}
180
- {...nodeRef}
188
+ ref={ref}
189
+ style={style}
181
190
  >
182
191
  <IconButton
183
192
  style={{ cursor: "move" }}
184
193
  size="small"
185
194
  title={labels?.dragIndicator}
186
- {...actionNodeRef}
195
+ ref={handleRef}
187
196
  >
188
197
  <DragIndicatorIcon />
189
198
  </IconButton>
@@ -206,9 +215,8 @@ function ButtonPopupList<D extends DnDItemType>(
206
215
  />
207
216
  </Grid>
208
217
  )}
209
- height={200}
210
218
  mRef={dndRef}
211
- ></DnDList>
219
+ ></DnDSortableList>
212
220
  {onAdd && (
213
221
  <HBox gap={1}>
214
222
  <TextField
@@ -276,6 +284,8 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
276
284
  label,
277
285
  labelEnd,
278
286
  labelFormatter = (data) => {
287
+ if (typeof labelField === "function") return labelField(data);
288
+
279
289
  if (labelField in data) {
280
290
  return data[labelField] as string;
281
291
  }
@@ -0,0 +1,318 @@
1
+ import { DataTypes, IdType } from "@etsoo/shared";
2
+ import React from "react";
3
+ import { CSSProperties, Theme, useTheme } from "@mui/material/styles";
4
+ import { useSortable } from "@dnd-kit/react/sortable";
5
+ import { DragDropEvents, DragDropProvider } from "@dnd-kit/react";
6
+
7
+ /**
8
+ * DnD sortable item default style
9
+ * @param index Item index
10
+ * @param isDragging Is dragging
11
+ * @param theme Theme
12
+ * @returns Style
13
+ */
14
+ export const DnDSortableItemStyle = (
15
+ index: number,
16
+ isDragging: boolean,
17
+ theme: Theme
18
+ ) => ({
19
+ padding: theme.spacing(1),
20
+ transform: isDragging ? "scale(1.03)" : "none",
21
+ zIndex: isDragging ? 1 : "auto",
22
+ boxShadow: isDragging
23
+ ? `-1px 0 8px 0 ${theme.palette.grey[400]}, 0px 8px 8px 0 ${theme.palette.grey[200]}`
24
+ : "none",
25
+ background: isDragging
26
+ ? theme.palette.primary.light
27
+ : index % 2 === 0
28
+ ? theme.palette.grey[100]
29
+ : theme.palette.grey[50]
30
+ });
31
+
32
+ /**
33
+ * DnD sortable list forward ref
34
+ */
35
+ export interface DnDSortableListRef<D extends object> {
36
+ /**
37
+ * Add item
38
+ * @param item New item
39
+ */
40
+ addItem(item: D): void;
41
+
42
+ /**
43
+ * Add items
44
+ * @param items items
45
+ */
46
+ addItems(items: D[]): void;
47
+
48
+ /**
49
+ * Delete item
50
+ * @param index Item index
51
+ */
52
+ deleteItem(index: number): void;
53
+
54
+ /**
55
+ * Edit item
56
+ * @param newItem New item
57
+ * @param index Index
58
+ */
59
+ editItem(newItem: D, index: number): boolean;
60
+
61
+ /**
62
+ * Get all items
63
+ */
64
+ getItems(): D[];
65
+ }
66
+
67
+ /**
68
+ * DnD sortable list props
69
+ */
70
+ export type DnDSortableListProps<
71
+ D extends object,
72
+ E extends React.ElementType = React.ElementType
73
+ > = {
74
+ /**
75
+ * Component type to render the list into
76
+ * Default is React.Fragment
77
+ */
78
+ component?: E;
79
+
80
+ /**
81
+ * Component props
82
+ */
83
+ componentProps?: React.ComponentProps<E>;
84
+
85
+ /**
86
+ * List items
87
+ */
88
+ items: D[];
89
+
90
+ /**
91
+ * Id field
92
+ */
93
+ idField?: DataTypes.Keys<D> | ((item: D) => IdType);
94
+
95
+ /**
96
+ * Label field
97
+ */
98
+ labelField: DataTypes.Keys<D, string> | ((item: D) => string);
99
+
100
+ /**
101
+ * Methods ref
102
+ */
103
+ mRef?: React.Ref<DnDSortableListRef<D>>;
104
+
105
+ /**
106
+ * Item renderer
107
+ */
108
+ itemRenderer: (
109
+ data: D,
110
+ style: CSSProperties,
111
+ state: ReturnType<typeof useSortable<D>>
112
+ ) => React.ReactElement;
113
+
114
+ /**
115
+ * Get list item style callback
116
+ */
117
+ itemStyle?: (index: number, isDragging: boolean) => CSSProperties;
118
+
119
+ /**
120
+ * Data change handler
121
+ */
122
+ onChange?: (items: D[]) => void;
123
+
124
+ /**
125
+ * Drag start handler
126
+ */
127
+ onDragStart?: (
128
+ items: D[],
129
+ source: Parameters<DragDropEvents["dragstart"]>[0]
130
+ ) => void;
131
+
132
+ /**
133
+ * Drag end handler
134
+ */
135
+ onDragEnd?: (
136
+ items: D[],
137
+ ...args: Parameters<DragDropEvents["dragend"]>
138
+ ) => void;
139
+ };
140
+
141
+ type SortableItemProps<D extends object> = {
142
+ id: IdType;
143
+ index: number;
144
+ data: D;
145
+ itemStyle?: (index: number, isDragging: boolean) => CSSProperties;
146
+ } & Pick<DnDSortableListProps<D>, "itemRenderer">;
147
+
148
+ function SortableItem<D extends object>(props: SortableItemProps<D>) {
149
+ const theme = useTheme();
150
+ const {
151
+ id,
152
+ data,
153
+ index,
154
+ itemRenderer,
155
+ itemStyle = (index, isDragging) =>
156
+ DnDSortableItemStyle(index, isDragging, theme)
157
+ } = props;
158
+ const state = useSortable<D>({ id, data, index });
159
+ const style = itemStyle(index, state.isDragging);
160
+ return itemRenderer(data, style, state);
161
+ }
162
+
163
+ /**
164
+ * DnD sortable list component
165
+ * @param props Props
166
+ * @returns Component
167
+ */
168
+ export function DnDSortableList<
169
+ D extends object,
170
+ E extends React.ElementType = React.ElementType
171
+ >(props: DnDSortableListProps<D, E>) {
172
+ // Destruct
173
+ const Component = props.component || React.Fragment;
174
+ const {
175
+ componentProps,
176
+ idField,
177
+ itemRenderer,
178
+ itemStyle,
179
+ labelField,
180
+ mRef,
181
+ onChange,
182
+ onDragStart,
183
+ onDragEnd
184
+ } = props;
185
+
186
+ const idFn =
187
+ typeof idField === "function"
188
+ ? idField
189
+ : (item: D) =>
190
+ !idField
191
+ ? (Reflect.get(item, "id") as IdType)
192
+ : (item[idField] as IdType);
193
+
194
+ const labelFn = React.useCallback(
195
+ typeof labelField === "function"
196
+ ? labelField
197
+ : (item: D) => item[labelField] as string,
198
+ [labelField]
199
+ );
200
+
201
+ // States
202
+ const [items, setItems] = React.useState<D[]>([]);
203
+
204
+ React.useEffect(() => {
205
+ setItems(props.items);
206
+ }, [props.items]);
207
+
208
+ const changeItems = React.useCallback(
209
+ (newItems: D[]) => {
210
+ // Possible to alter items with the handler
211
+ onChange?.(newItems);
212
+
213
+ // Update state
214
+ setItems(newItems);
215
+ },
216
+ [onChange]
217
+ );
218
+
219
+ // Methods
220
+ React.useImperativeHandle(mRef, () => {
221
+ return {
222
+ addItem(newItem: D) {
223
+ // Existence check
224
+ if (items.some((item) => labelFn(item) === labelFn(newItem))) {
225
+ return false;
226
+ }
227
+
228
+ // Clone
229
+ const newItems = [newItem, ...items];
230
+
231
+ // Update the state
232
+ changeItems(newItems);
233
+
234
+ return true;
235
+ },
236
+
237
+ addItems(inputItems: D[]) {
238
+ // Clone
239
+ const newItems = [...items];
240
+
241
+ // Insert items
242
+ inputItems.forEach((newItem) => {
243
+ // Existence check
244
+ if (newItems.some((item) => labelFn(item) === labelFn(newItem))) {
245
+ return;
246
+ }
247
+
248
+ newItems.push(newItem);
249
+ });
250
+
251
+ // Update the state
252
+ changeItems(newItems);
253
+
254
+ return newItems.length - items.length;
255
+ },
256
+
257
+ editItem(newItem: D, index: number) {
258
+ // Existence check
259
+ const newIndex = items.findIndex(
260
+ (item) => labelFn(item) === labelFn(newItem)
261
+ );
262
+ if (newIndex >= 0 && newIndex !== index) {
263
+ // Label field is the same with a different item
264
+ return false;
265
+ }
266
+
267
+ // Clone
268
+ const newItems = [...items];
269
+
270
+ // Remove the item
271
+ newItems.splice(index, 1, newItem);
272
+
273
+ // Update the state
274
+ changeItems(newItems);
275
+
276
+ return true;
277
+ },
278
+
279
+ deleteItem(index: number) {
280
+ // Clone
281
+ const newItems = [...items];
282
+
283
+ // Remove the item
284
+ newItems.splice(index, 1);
285
+
286
+ // Update the state
287
+ changeItems(newItems);
288
+ },
289
+
290
+ getItems() {
291
+ return items;
292
+ }
293
+ };
294
+ }, [items, labelFn, changeItems]);
295
+
296
+ return (
297
+ <DragDropProvider
298
+ onDragStart={(source) => onDragStart?.(items, source)}
299
+ onDragEnd={(source, target) => onDragEnd?.(items, source, target)}
300
+ >
301
+ <Component {...componentProps}>
302
+ {items.map((item, index) => {
303
+ const id = idFn(item);
304
+ return (
305
+ <SortableItem
306
+ key={id}
307
+ id={id}
308
+ index={index}
309
+ data={item}
310
+ itemRenderer={itemRenderer}
311
+ itemStyle={itemStyle}
312
+ />
313
+ );
314
+ })}
315
+ </Component>
316
+ </DragDropProvider>
317
+ );
318
+ }
package/src/index.ts CHANGED
@@ -60,7 +60,7 @@ export * from "./DataLoadField";
60
60
  export * from "./DataSteps";
61
61
  export * from "./DataTable";
62
62
  export * from "./DialogButton";
63
- export * from "./DnDList";
63
+ export * from "./DnDSortableList";
64
64
  export * from "./DownUpButton";
65
65
  export * from "./DraggablePaperComponent";
66
66
  export * from "./EmailInput";
@@ -1,111 +0,0 @@
1
- import type { UniqueIdentifier } from "@dnd-kit/core";
2
- import { SortingStrategy } from "@dnd-kit/sortable/dist/types/strategies";
3
- import { DataTypes } from "@etsoo/shared";
4
- import { Theme } from "@mui/material/styles";
5
- import React, { CSSProperties } from "react";
6
- /**
7
- * DnD 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 DnDItemStyle: (index: number, isDragging: boolean, theme: Theme) => {
14
- padding: string;
15
- zIndex: string | number;
16
- background: string;
17
- };
18
- /**
19
- * DnD list forward ref
20
- */
21
- export interface DnDListRef<D extends object> {
22
- /**
23
- * Add item
24
- * @param item New item
25
- */
26
- addItem(item: D): void;
27
- /**
28
- * Add items
29
- * @param items items
30
- */
31
- addItems(items: D[]): void;
32
- /**
33
- * Delete item
34
- * @param index Item index
35
- */
36
- deleteItem(index: number): void;
37
- /**
38
- * Edit item
39
- * @param newItem New item
40
- * @param index Index
41
- */
42
- editItem(newItem: D, index: number): boolean;
43
- /**
44
- * Get all items
45
- */
46
- getItems(): D[];
47
- }
48
- /**
49
- * DnD sortable list properties
50
- */
51
- export interface DnDListPros<D extends {
52
- id: UniqueIdentifier;
53
- }, 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
- * Get list item style callback
65
- */
66
- getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
67
- /**
68
- * Item renderer
69
- */
70
- itemRenderer: (item: D, index: number, nodeRef: React.ComponentProps<any>, actionNodeRef: React.ComponentProps<any>) => React.ReactElement;
71
- /**
72
- * Height
73
- */
74
- height?: string | number;
75
- /**
76
- * List items
77
- */
78
- items: D[];
79
- /**
80
- * Label field
81
- */
82
- labelField: DataTypes.Keys<D>;
83
- /**
84
- * Methods ref
85
- */
86
- mRef?: React.Ref<DnDListRef<D>>;
87
- /**
88
- * Sorting strategy
89
- */
90
- sortingStrategy?: "rect" | "vertical" | "horizontal" | "rectSwapping" | (() => SortingStrategy);
91
- /**
92
- * Data change handler
93
- */
94
- onChange?: (items: D[]) => void;
95
- /**
96
- * Form data change handler
97
- */
98
- onFormChange?: (items: D[]) => void;
99
- /**
100
- * Drag end handler
101
- */
102
- onDragEnd?: (items: D[]) => void;
103
- }
104
- /**
105
- * DnD (Drag and Drop) sortable list
106
- * @param props Props
107
- * @returns Component
108
- */
109
- export declare function DnDList<D extends {
110
- id: UniqueIdentifier;
111
- }, E extends React.ElementType = React.ElementType>(props: DnDListPros<D, E>): import("react/jsx-runtime").JSX.Element;