@etsoo/materialui 1.1.6 → 1.1.8

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/src/DnDList.tsx CHANGED
@@ -1,58 +1,58 @@
1
1
  import {
2
- DndContext,
3
- DragEndEvent,
4
- DragStartEvent,
5
- UniqueIdentifier
6
- } from '@dnd-kit/core';
2
+ DndContext,
3
+ DragEndEvent,
4
+ DragStartEvent,
5
+ UniqueIdentifier
6
+ } from "@dnd-kit/core";
7
7
  import {
8
- SortableContext,
9
- useSortable,
10
- verticalListSortingStrategy
11
- } from '@dnd-kit/sortable';
12
- import { CSS } from '@dnd-kit/utilities';
13
- import { DataTypes } from '@etsoo/shared';
14
- import { Theme, useTheme } from '@mui/material';
15
- import React, { CSSProperties } from 'react';
8
+ SortableContext,
9
+ useSortable,
10
+ verticalListSortingStrategy
11
+ } from "@dnd-kit/sortable";
12
+ import { CSS } from "@dnd-kit/utilities";
13
+ import { DataTypes } from "@etsoo/shared";
14
+ import { Theme, useTheme } from "@mui/material";
15
+ import React, { CSSProperties } from "react";
16
16
 
17
17
  function SortableItem(props: {
18
- id: UniqueIdentifier;
19
- itemRenderer: (
20
- nodeRef: React.ComponentProps<any>,
21
- actionNodeRef: React.ComponentProps<any>
22
- ) => React.ReactElement;
23
- style?: React.CSSProperties;
18
+ id: UniqueIdentifier;
19
+ itemRenderer: (
20
+ nodeRef: React.ComponentProps<any>,
21
+ actionNodeRef: React.ComponentProps<any>
22
+ ) => React.ReactElement;
23
+ style?: React.CSSProperties;
24
24
  }) {
25
- // Destruct
26
- const { id, itemRenderer, style = {} } = props;
27
-
28
- // Use sortable
29
- const {
30
- attributes,
31
- listeners,
32
- setNodeRef,
33
- transform,
34
- transition,
35
- setActivatorNodeRef
36
- } = useSortable({ id });
37
-
38
- const allStyle = {
39
- ...style,
40
- transform: CSS.Transform.toString(transform),
41
- transition
42
- };
43
-
44
- const nodeRef = {
45
- style: allStyle,
46
- ref: setNodeRef,
47
- ...attributes
48
- };
49
-
50
- const actionNodeRef = {
51
- ...listeners,
52
- ref: setActivatorNodeRef
53
- };
54
-
55
- return itemRenderer(nodeRef, actionNodeRef);
25
+ // Destruct
26
+ const { id, itemRenderer, style = {} } = props;
27
+
28
+ // Use sortable
29
+ const {
30
+ attributes,
31
+ listeners,
32
+ setNodeRef,
33
+ transform,
34
+ transition,
35
+ setActivatorNodeRef
36
+ } = useSortable({ id });
37
+
38
+ const allStyle = {
39
+ ...style,
40
+ transform: CSS.Transform.toString(transform),
41
+ transition
42
+ };
43
+
44
+ const nodeRef = {
45
+ style: allStyle,
46
+ ref: setNodeRef,
47
+ ...attributes
48
+ };
49
+
50
+ const actionNodeRef = {
51
+ ...listeners,
52
+ ref: setActivatorNodeRef
53
+ };
54
+
55
+ return itemRenderer(nodeRef, actionNodeRef);
56
56
  }
57
57
 
58
58
  /**
@@ -63,102 +63,107 @@ function SortableItem(props: {
63
63
  * @returns Style
64
64
  */
65
65
  export const DnDItemStyle = (
66
- index: number,
67
- isDragging: boolean,
68
- theme: Theme
66
+ index: number,
67
+ isDragging: boolean,
68
+ theme: Theme
69
69
  ) => ({
70
- padding: theme.spacing(1),
71
- zIndex: isDragging ? 1 : 'auto',
72
- background: isDragging
73
- ? theme.palette.primary.light
74
- : index % 2 === 0
75
- ? theme.palette.grey[100]
76
- : theme.palette.grey[50]
70
+ padding: theme.spacing(1),
71
+ zIndex: isDragging ? 1 : "auto",
72
+ background: isDragging
73
+ ? theme.palette.primary.light
74
+ : index % 2 === 0
75
+ ? theme.palette.grey[100]
76
+ : theme.palette.grey[50]
77
77
  });
78
78
 
79
79
  /**
80
80
  * DnD list forward ref
81
81
  */
82
82
  export interface DnDListRef<D extends object> {
83
- /**
84
- * Add item
85
- * @param item New item
86
- */
87
- addItem(item: D): void;
88
-
89
- /**
90
- * Add items
91
- * @param items items
92
- */
93
- addItems(items: D[]): void;
94
-
95
- /**
96
- * Delete item
97
- * @param index Item index
98
- */
99
- deleteItem(index: number): void;
100
-
101
- /**
102
- * Edit item
103
- * @param newItem New item
104
- * @param index Index
105
- */
106
- editItem(newItem: D, index: number): boolean;
107
-
108
- /**
109
- * Get all items
110
- */
111
- getItems(): D[];
83
+ /**
84
+ * Add item
85
+ * @param item New item
86
+ */
87
+ addItem(item: D): void;
88
+
89
+ /**
90
+ * Add items
91
+ * @param items items
92
+ */
93
+ addItems(items: D[]): void;
94
+
95
+ /**
96
+ * Delete item
97
+ * @param index Item index
98
+ */
99
+ deleteItem(index: number): void;
100
+
101
+ /**
102
+ * Edit item
103
+ * @param newItem New item
104
+ * @param index Index
105
+ */
106
+ editItem(newItem: D, index: number): boolean;
107
+
108
+ /**
109
+ * Get all items
110
+ */
111
+ getItems(): D[];
112
112
  }
113
113
 
114
114
  /**
115
115
  * DnD sortable list properties
116
116
  */
117
117
  export interface DnDListPros<D extends object, K extends DataTypes.Keys<D>> {
118
- /**
119
- * Get list item style callback
120
- */
121
- getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
122
-
123
- /**
124
- * Item renderer
125
- */
126
- itemRenderer: (
127
- item: D,
128
- index: number,
129
- nodeRef: React.ComponentProps<any>,
130
- actionNodeRef: React.ComponentProps<any>
131
- ) => React.ReactElement;
132
-
133
- /**
134
- * List items
135
- */
136
- items: D[];
137
-
138
- /**
139
- * Unique key field
140
- */
141
- keyField: K;
142
-
143
- /**
144
- * Label field
145
- */
146
- labelField: K;
147
-
148
- /**
149
- * Methods ref
150
- */
151
- mRef?: React.Ref<DnDListRef<D>>;
152
-
153
- /**
154
- * Data change handler
155
- */
156
- onChange?: (items: D[]) => void;
157
-
158
- /**
159
- * Drag end handler
160
- */
161
- onDragEnd?: (items: D[]) => void;
118
+ /**
119
+ * Get list item style callback
120
+ */
121
+ getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
122
+
123
+ /**
124
+ * Item renderer
125
+ */
126
+ itemRenderer: (
127
+ item: D,
128
+ index: number,
129
+ nodeRef: React.ComponentProps<any>,
130
+ actionNodeRef: React.ComponentProps<any>
131
+ ) => React.ReactElement;
132
+
133
+ /**
134
+ * List items
135
+ */
136
+ items: D[];
137
+
138
+ /**
139
+ * Unique key field
140
+ */
141
+ keyField: K;
142
+
143
+ /**
144
+ * Label field
145
+ */
146
+ labelField: K;
147
+
148
+ /**
149
+ * Methods ref
150
+ */
151
+ mRef?: React.Ref<DnDListRef<D>>;
152
+
153
+ /**
154
+ * Data change handler
155
+ */
156
+ onChange?: (items: D[]) => void;
157
+
158
+ /**
159
+ * Form data change handler
160
+ */
161
+ onFormChange?: (items: D[]) => void;
162
+
163
+ /**
164
+ * Drag end handler
165
+ */
166
+ onDragEnd?: (items: D[]) => void;
162
167
  }
163
168
 
164
169
  /**
@@ -167,187 +172,214 @@ export interface DnDListPros<D extends object, K extends DataTypes.Keys<D>> {
167
172
  * @returns Component
168
173
  */
169
174
  export function DnDList<
170
- D extends { id: UniqueIdentifier },
171
- K extends DataTypes.Keys<D, UniqueIdentifier> = DataTypes.Keys<
172
- D,
173
- UniqueIdentifier
174
- >
175
+ D extends { id: UniqueIdentifier },
176
+ K extends DataTypes.Keys<D, UniqueIdentifier> = DataTypes.Keys<
177
+ D,
178
+ UniqueIdentifier
179
+ >
175
180
  >(props: DnDListPros<D, K>) {
176
- // Destruct
177
- const { keyField, itemRenderer, labelField, mRef, onChange, onDragEnd } =
178
- props;
179
-
180
- let getItemStyle = props.getItemStyle;
181
- if (getItemStyle == null) {
182
- // Theme
183
- const theme = useTheme();
184
- getItemStyle = (index, isDragging) =>
185
- DnDItemStyle(index, isDragging, theme);
181
+ // Destruct
182
+ const {
183
+ keyField,
184
+ itemRenderer,
185
+ labelField,
186
+ mRef,
187
+ onChange,
188
+ onFormChange,
189
+ onDragEnd
190
+ } = props;
191
+
192
+ let getItemStyle = props.getItemStyle;
193
+ if (getItemStyle == null) {
194
+ // Theme
195
+ const theme = useTheme();
196
+ getItemStyle = (index, isDragging) =>
197
+ DnDItemStyle(index, isDragging, theme);
198
+ }
199
+
200
+ // States
201
+ const [items, setItems] = React.useState<D[]>([]);
202
+ const [activeId, setActiveId] = React.useState<UniqueIdentifier>();
203
+
204
+ const doFormChange = (newItems?: D[]) => {
205
+ if (onFormChange) onFormChange(newItems ?? items);
206
+ };
207
+
208
+ const changeItems = (newItems: D[]) => {
209
+ // Possible to alter items with the handler
210
+ if (onChange) onChange(newItems);
211
+
212
+ doFormChange(newItems);
213
+
214
+ // Update state
215
+ setItems(newItems);
216
+ };
217
+
218
+ // Drag event handlers
219
+ function handleDragStart(event: DragStartEvent) {
220
+ const { active } = event;
221
+ setActiveId(active.id);
222
+ }
223
+
224
+ function handleDragEnd(event: DragEndEvent) {
225
+ const { active, over } = event;
226
+
227
+ if (over && active.id !== over.id) {
228
+ // Indices
229
+ const oldIndex = items.findIndex((item) => item.id === active.id);
230
+ const newIndex = items.findIndex((item) => item.id === over.id);
231
+
232
+ // Clone
233
+ const newItems = [...items];
234
+
235
+ // Removed item
236
+ const [removed] = newItems.splice(oldIndex, 1);
237
+
238
+ // Insert to the destination index
239
+ newItems.splice(newIndex, 0, removed);
240
+
241
+ changeItems(newItems);
242
+
243
+ // Drag end handler
244
+ if (onDragEnd) onDragEnd(newItems);
186
245
  }
187
246
 
188
- // States
189
- const [items, setItems] = React.useState<D[]>([]);
190
- const [activeId, setActiveId] = React.useState<UniqueIdentifier>();
247
+ setActiveId(undefined);
248
+ }
191
249
 
192
- const changeItems = (newItems: D[]) => {
193
- // Possible to alter items with the handler
194
- if (onChange) onChange(newItems);
250
+ // Methods
251
+ React.useImperativeHandle(
252
+ mRef,
253
+ () => {
254
+ return {
255
+ addItem(newItem: D) {
256
+ // Existence check
257
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
258
+ return false;
259
+ }
195
260
 
196
- // Update state
197
- setItems(newItems);
198
- };
261
+ // Clone
262
+ const newItems = [newItem, ...items];
199
263
 
200
- // Drag event handlers
201
- function handleDragStart(event: DragStartEvent) {
202
- const { active } = event;
203
- setActiveId(active.id);
204
- }
264
+ // Update the state
265
+ changeItems(newItems);
205
266
 
206
- function handleDragEnd(event: DragEndEvent) {
207
- const { active, over } = event;
267
+ return true;
268
+ },
208
269
 
209
- if (over && active.id !== over.id) {
210
- // Indices
211
- const oldIndex = items.findIndex((item) => item.id === active.id);
212
- const newIndex = items.findIndex((item) => item.id === over.id);
270
+ addItems(inputItems: D[]) {
271
+ // Clone
272
+ const newItems = [...items];
213
273
 
214
- // Clone
215
- const newItems = [...items];
274
+ // Insert items
275
+ inputItems.forEach((newItem) => {
276
+ // Existence check
277
+ if (
278
+ newItems.some((item) => item[labelField] === newItem[labelField])
279
+ ) {
280
+ return;
281
+ }
216
282
 
217
- // Removed item
218
- const [removed] = newItems.splice(oldIndex, 1);
283
+ newItems.push(newItem);
284
+ });
219
285
 
220
- // Insert to the destination index
221
- newItems.splice(newIndex, 0, removed);
286
+ // Update the state
287
+ changeItems(newItems);
222
288
 
223
- changeItems(newItems);
289
+ return newItems.length - items.length;
290
+ },
224
291
 
225
- // Drag end handler
226
- if (onDragEnd) onDragEnd(newItems);
227
- }
292
+ editItem(newItem: D, index: number) {
293
+ // Existence check
294
+ const newIndex = items.findIndex(
295
+ (item) => item[labelField] === newItem[labelField]
296
+ );
297
+ if (newIndex >= 0 && newIndex !== index) {
298
+ // Label field is the same with a different item
299
+ return false;
300
+ }
228
301
 
229
- setActiveId(undefined);
230
- }
302
+ // Clone
303
+ const newItems = [...items];
231
304
 
232
- // Methods
233
- React.useImperativeHandle(
234
- mRef,
235
- () => {
236
- return {
237
- addItem(newItem: D) {
238
- // Existence check
239
- if (
240
- items.some(
241
- (item) => item[labelField] === newItem[labelField]
242
- )
243
- ) {
244
- return false;
245
- }
246
-
247
- // Clone
248
- const newItems = [newItem, ...items];
249
-
250
- // Update the state
251
- changeItems(newItems);
252
-
253
- return true;
254
- },
255
-
256
- addItems(inputItems: D[]) {
257
- // Clone
258
- const newItems = [...items];
259
-
260
- // Insert items
261
- inputItems.forEach((newItem) => {
262
- // Existence check
263
- if (
264
- newItems.some(
265
- (item) =>
266
- item[labelField] === newItem[labelField]
267
- )
268
- ) {
269
- return;
270
- }
271
-
272
- newItems.push(newItem);
273
- });
274
-
275
- // Update the state
276
- changeItems(newItems);
277
-
278
- return newItems.length - items.length;
279
- },
280
-
281
- editItem(newItem: D, index: number) {
282
- // Existence check
283
- const newIndex = items.findIndex(
284
- (item) => item[labelField] === newItem[labelField]
285
- );
286
- if (newIndex >= 0 && newIndex !== index) {
287
- // Label field is the same with a different item
288
- return false;
289
- }
290
-
291
- // Clone
292
- const newItems = [...items];
293
-
294
- // Remove the item
295
- newItems.splice(index, 1, newItem);
296
-
297
- // Update the state
298
- changeItems(newItems);
299
-
300
- return true;
301
- },
302
-
303
- deleteItem(index: number) {
304
- // Clone
305
- const newItems = [...items];
306
-
307
- // Remove the item
308
- newItems.splice(index, 1);
309
-
310
- // Update the state
311
- changeItems(newItems);
312
- },
313
-
314
- getItems() {
315
- return items;
316
- }
317
- };
305
+ // Remove the item
306
+ newItems.splice(index, 1, newItem);
307
+
308
+ // Update the state
309
+ changeItems(newItems);
310
+
311
+ return true;
312
+ },
313
+
314
+ deleteItem(index: number) {
315
+ // Clone
316
+ const newItems = [...items];
317
+
318
+ // Remove the item
319
+ newItems.splice(index, 1);
320
+
321
+ // Update the state
322
+ changeItems(newItems);
318
323
  },
319
- [items]
320
- );
321
-
322
- React.useEffect(() => {
323
- setItems(props.items);
324
- }, [props.items]);
325
-
326
- return (
327
- <DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
328
- <SortableContext
329
- items={items}
330
- strategy={verticalListSortingStrategy}
331
- >
332
- {items.map((item, index) => {
333
- const id = item[keyField] as unknown as UniqueIdentifier;
334
- return (
335
- <SortableItem
336
- id={id}
337
- key={id}
338
- style={getItemStyle!(index, id === activeId)}
339
- itemRenderer={(nodeRef, actionNodeRef) =>
340
- itemRenderer(
341
- item,
342
- index,
343
- nodeRef,
344
- actionNodeRef
345
- )
346
- }
347
- />
348
- );
349
- })}
350
- </SortableContext>
351
- </DndContext>
352
- );
324
+
325
+ getItems() {
326
+ return items;
327
+ }
328
+ };
329
+ },
330
+ [items]
331
+ );
332
+
333
+ const setupDiv = (div: HTMLDivElement) => {
334
+ // Inputs
335
+ div
336
+ .querySelectorAll("input")
337
+ .forEach((input) =>
338
+ input.addEventListener("change", () => doFormChange())
339
+ );
340
+
341
+ // Textareas
342
+ div
343
+ .querySelectorAll("textarea")
344
+ .forEach((input) =>
345
+ input.addEventListener("change", () => doFormChange())
346
+ );
347
+
348
+ // Select
349
+ div
350
+ .querySelectorAll("select")
351
+ .forEach((input) =>
352
+ input.addEventListener("change", () => doFormChange())
353
+ );
354
+ };
355
+
356
+ React.useEffect(() => {
357
+ setItems(props.items);
358
+ }, [props.items]);
359
+
360
+ return (
361
+ <div
362
+ ref={(div) => {
363
+ if (div) setupDiv(div);
364
+ }}
365
+ >
366
+ <DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
367
+ <SortableContext items={items} strategy={verticalListSortingStrategy}>
368
+ {items.map((item, index) => {
369
+ const id = item[keyField] as unknown as UniqueIdentifier;
370
+ return (
371
+ <SortableItem
372
+ id={id}
373
+ key={id}
374
+ style={getItemStyle!(index, id === activeId)}
375
+ itemRenderer={(nodeRef, actionNodeRef) =>
376
+ itemRenderer(item, index, nodeRef, actionNodeRef)
377
+ }
378
+ />
379
+ );
380
+ })}
381
+ </SortableContext>
382
+ </DndContext>
383
+ </div>
384
+ );
353
385
  }