@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/lib/cjs/ButtonPopupCheckbox.d.ts +3 -2
- package/lib/cjs/ButtonPopupCheckbox.js +6 -4
- package/lib/cjs/DnDSortableList.d.ts +105 -0
- package/lib/cjs/DnDSortableList.js +130 -0
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.js +1 -1
- package/lib/mjs/ButtonPopupCheckbox.d.ts +3 -2
- package/lib/mjs/ButtonPopupCheckbox.js +6 -4
- package/lib/mjs/DnDSortableList.d.ts +105 -0
- package/lib/mjs/DnDSortableList.js +122 -0
- package/lib/mjs/index.d.ts +1 -1
- package/lib/mjs/index.js +1 -1
- package/package.json +13 -15
- package/src/ButtonPopupCheckbox.tsx +20 -10
- package/src/DnDSortableList.tsx +318 -0
- package/src/index.ts +1 -1
- package/lib/cjs/DnDList.d.ts +0 -111
- package/lib/cjs/DnDList.js +0 -238
- package/lib/mjs/DnDList.d.ts +0 -111
- package/lib/mjs/DnDList.js +0 -230
- package/src/DnDList.tsx +0 -528
package/src/DnDList.tsx
DELETED
|
@@ -1,528 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
DndContext,
|
|
3
|
-
DragEndEvent,
|
|
4
|
-
DragStartEvent,
|
|
5
|
-
UniqueIdentifier
|
|
6
|
-
} from "@dnd-kit/core";
|
|
7
|
-
import { SortableContext } from "@dnd-kit/sortable/dist/components/SortableContext";
|
|
8
|
-
import { useSortable } from "@dnd-kit/sortable/dist/hooks/useSortable";
|
|
9
|
-
import { horizontalListSortingStrategy } from "@dnd-kit/sortable/dist/strategies/horizontalListSorting";
|
|
10
|
-
import { rectSortingStrategy } from "@dnd-kit/sortable/dist/strategies/rectSorting";
|
|
11
|
-
import { rectSwappingStrategy } from "@dnd-kit/sortable/dist/strategies/rectSwapping";
|
|
12
|
-
import { verticalListSortingStrategy } from "@dnd-kit/sortable/dist/strategies/verticalListSorting";
|
|
13
|
-
import { SortingStrategy } from "@dnd-kit/sortable/dist/types/strategies";
|
|
14
|
-
import type { CSS } from "@dnd-kit/utilities";
|
|
15
|
-
import { DataTypes } from "@etsoo/shared";
|
|
16
|
-
import Skeleton from "@mui/material/Skeleton";
|
|
17
|
-
import { Theme, useTheme } from "@mui/material/styles";
|
|
18
|
-
import React, { CSSProperties } from "react";
|
|
19
|
-
|
|
20
|
-
function SortableItem(props: {
|
|
21
|
-
id: UniqueIdentifier;
|
|
22
|
-
useSortableType: typeof useSortable;
|
|
23
|
-
CSSType: typeof CSS;
|
|
24
|
-
itemRenderer: (
|
|
25
|
-
nodeRef: React.ComponentProps<any>,
|
|
26
|
-
actionNodeRef: React.ComponentProps<any>
|
|
27
|
-
) => React.ReactElement;
|
|
28
|
-
style?: React.CSSProperties;
|
|
29
|
-
}) {
|
|
30
|
-
// Destruct
|
|
31
|
-
const { id, useSortableType, CSSType, itemRenderer, style = {} } = props;
|
|
32
|
-
|
|
33
|
-
// Use sortable
|
|
34
|
-
const {
|
|
35
|
-
attributes,
|
|
36
|
-
listeners,
|
|
37
|
-
setNodeRef,
|
|
38
|
-
transform,
|
|
39
|
-
transition,
|
|
40
|
-
setActivatorNodeRef
|
|
41
|
-
} = useSortableType({ id });
|
|
42
|
-
|
|
43
|
-
const allStyle = {
|
|
44
|
-
...style,
|
|
45
|
-
transform: CSSType.Transform.toString(transform),
|
|
46
|
-
transition
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const nodeRef = {
|
|
50
|
-
style: allStyle,
|
|
51
|
-
ref: setNodeRef,
|
|
52
|
-
...attributes
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const actionNodeRef = {
|
|
56
|
-
...listeners,
|
|
57
|
-
ref: setActivatorNodeRef
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return itemRenderer(nodeRef, actionNodeRef);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* DnD item default style
|
|
65
|
-
* @param index Item index
|
|
66
|
-
* @param isDragging Is dragging
|
|
67
|
-
* @param theme Theme
|
|
68
|
-
* @returns Style
|
|
69
|
-
*/
|
|
70
|
-
export const DnDItemStyle = (
|
|
71
|
-
index: number,
|
|
72
|
-
isDragging: boolean,
|
|
73
|
-
theme: Theme
|
|
74
|
-
) => ({
|
|
75
|
-
padding: theme.spacing(1),
|
|
76
|
-
zIndex: isDragging ? 1 : "auto",
|
|
77
|
-
background: isDragging
|
|
78
|
-
? theme.palette.primary.light
|
|
79
|
-
: index % 2 === 0
|
|
80
|
-
? theme.palette.grey[100]
|
|
81
|
-
: theme.palette.grey[50]
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* DnD list forward ref
|
|
86
|
-
*/
|
|
87
|
-
export interface DnDListRef<D extends object> {
|
|
88
|
-
/**
|
|
89
|
-
* Add item
|
|
90
|
-
* @param item New item
|
|
91
|
-
*/
|
|
92
|
-
addItem(item: D): void;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Add items
|
|
96
|
-
* @param items items
|
|
97
|
-
*/
|
|
98
|
-
addItems(items: D[]): void;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Delete item
|
|
102
|
-
* @param index Item index
|
|
103
|
-
*/
|
|
104
|
-
deleteItem(index: number): void;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Edit item
|
|
108
|
-
* @param newItem New item
|
|
109
|
-
* @param index Index
|
|
110
|
-
*/
|
|
111
|
-
editItem(newItem: D, index: number): boolean;
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get all items
|
|
115
|
-
*/
|
|
116
|
-
getItems(): D[];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* DnD sortable list properties
|
|
121
|
-
*/
|
|
122
|
-
export interface DnDListPros<
|
|
123
|
-
D extends { id: UniqueIdentifier },
|
|
124
|
-
E extends React.ElementType = React.ElementType
|
|
125
|
-
> {
|
|
126
|
-
/**
|
|
127
|
-
* Component type to render the list into
|
|
128
|
-
* Default is React.Fragment
|
|
129
|
-
*/
|
|
130
|
-
component?: E;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Component props
|
|
134
|
-
*/
|
|
135
|
-
componentProps?: React.ComponentProps<E>;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get list item style callback
|
|
139
|
-
*/
|
|
140
|
-
getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Item renderer
|
|
144
|
-
*/
|
|
145
|
-
itemRenderer: (
|
|
146
|
-
item: D,
|
|
147
|
-
index: number,
|
|
148
|
-
nodeRef: React.ComponentProps<any>,
|
|
149
|
-
actionNodeRef: React.ComponentProps<any>
|
|
150
|
-
) => React.ReactElement;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Height
|
|
154
|
-
*/
|
|
155
|
-
height?: string | number;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* List items
|
|
159
|
-
*/
|
|
160
|
-
items: D[];
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Label field
|
|
164
|
-
*/
|
|
165
|
-
labelField: DataTypes.Keys<D>;
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Methods ref
|
|
169
|
-
*/
|
|
170
|
-
mRef?: React.Ref<DnDListRef<D>>;
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Sorting strategy
|
|
174
|
-
*/
|
|
175
|
-
sortingStrategy?:
|
|
176
|
-
| "rect"
|
|
177
|
-
| "vertical"
|
|
178
|
-
| "horizontal"
|
|
179
|
-
| "rectSwapping"
|
|
180
|
-
| (() => SortingStrategy);
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Data change handler
|
|
184
|
-
*/
|
|
185
|
-
onChange?: (items: D[]) => void;
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Form data change handler
|
|
189
|
-
*/
|
|
190
|
-
onFormChange?: (items: D[]) => void;
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Drag end handler
|
|
194
|
-
*/
|
|
195
|
-
onDragEnd?: (items: D[]) => void;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* DnD (Drag and Drop) sortable list
|
|
200
|
-
* @param props Props
|
|
201
|
-
* @returns Component
|
|
202
|
-
*/
|
|
203
|
-
export function DnDList<
|
|
204
|
-
D extends { id: UniqueIdentifier },
|
|
205
|
-
E extends React.ElementType = React.ElementType
|
|
206
|
-
>(props: DnDListPros<D, E>) {
|
|
207
|
-
// Destruct
|
|
208
|
-
const {
|
|
209
|
-
componentProps,
|
|
210
|
-
height = 360,
|
|
211
|
-
itemRenderer,
|
|
212
|
-
labelField,
|
|
213
|
-
mRef,
|
|
214
|
-
sortingStrategy,
|
|
215
|
-
onChange,
|
|
216
|
-
onFormChange,
|
|
217
|
-
onDragEnd
|
|
218
|
-
} = props;
|
|
219
|
-
|
|
220
|
-
const Component = props.component || React.Fragment;
|
|
221
|
-
|
|
222
|
-
// Theme
|
|
223
|
-
const theme = useTheme();
|
|
224
|
-
|
|
225
|
-
// States
|
|
226
|
-
const [items, setItems] = React.useState<D[]>([]);
|
|
227
|
-
const [activeId, setActiveId] = React.useState<UniqueIdentifier>();
|
|
228
|
-
|
|
229
|
-
React.useEffect(() => {
|
|
230
|
-
setItems(props.items);
|
|
231
|
-
}, [props.items]);
|
|
232
|
-
|
|
233
|
-
const doFormChange = React.useCallback(
|
|
234
|
-
(newItems?: D[] | Event) => {
|
|
235
|
-
if (onFormChange) {
|
|
236
|
-
const locals = Array.isArray(newItems) ? newItems : items;
|
|
237
|
-
onFormChange(locals);
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
[items, onFormChange]
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
const changeItems = React.useCallback(
|
|
244
|
-
(newItems: D[]) => {
|
|
245
|
-
// Possible to alter items with the handler
|
|
246
|
-
if (onChange) onChange(newItems);
|
|
247
|
-
|
|
248
|
-
doFormChange(newItems);
|
|
249
|
-
|
|
250
|
-
// Update state
|
|
251
|
-
setItems(newItems);
|
|
252
|
-
},
|
|
253
|
-
[onChange, doFormChange]
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
// Methods
|
|
257
|
-
React.useImperativeHandle(
|
|
258
|
-
mRef,
|
|
259
|
-
() => {
|
|
260
|
-
return {
|
|
261
|
-
addItem(newItem: D) {
|
|
262
|
-
// Existence check
|
|
263
|
-
if (items.some((item) => item[labelField] === newItem[labelField])) {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Clone
|
|
268
|
-
const newItems = [newItem, ...items];
|
|
269
|
-
|
|
270
|
-
// Update the state
|
|
271
|
-
changeItems(newItems);
|
|
272
|
-
|
|
273
|
-
return true;
|
|
274
|
-
},
|
|
275
|
-
|
|
276
|
-
addItems(inputItems: D[]) {
|
|
277
|
-
// Clone
|
|
278
|
-
const newItems = [...items];
|
|
279
|
-
|
|
280
|
-
// Insert items
|
|
281
|
-
inputItems.forEach((newItem) => {
|
|
282
|
-
// Existence check
|
|
283
|
-
if (
|
|
284
|
-
newItems.some((item) => item[labelField] === newItem[labelField])
|
|
285
|
-
) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
newItems.push(newItem);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Update the state
|
|
293
|
-
changeItems(newItems);
|
|
294
|
-
|
|
295
|
-
return newItems.length - items.length;
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
editItem(newItem: D, index: number) {
|
|
299
|
-
// Existence check
|
|
300
|
-
const newIndex = items.findIndex(
|
|
301
|
-
(item) => item[labelField] === newItem[labelField]
|
|
302
|
-
);
|
|
303
|
-
if (newIndex >= 0 && newIndex !== index) {
|
|
304
|
-
// Label field is the same with a different item
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Clone
|
|
309
|
-
const newItems = [...items];
|
|
310
|
-
|
|
311
|
-
// Remove the item
|
|
312
|
-
newItems.splice(index, 1, newItem);
|
|
313
|
-
|
|
314
|
-
// Update the state
|
|
315
|
-
changeItems(newItems);
|
|
316
|
-
|
|
317
|
-
return true;
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
deleteItem(index: number) {
|
|
321
|
-
// Clone
|
|
322
|
-
const newItems = [...items];
|
|
323
|
-
|
|
324
|
-
// Remove the item
|
|
325
|
-
newItems.splice(index, 1);
|
|
326
|
-
|
|
327
|
-
// Update the state
|
|
328
|
-
changeItems(newItems);
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
getItems() {
|
|
332
|
-
return items;
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
},
|
|
336
|
-
[items, labelField, changeItems]
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
// Dynamic import library
|
|
340
|
-
const [dnd, setDnd] =
|
|
341
|
-
React.useState<
|
|
342
|
-
[
|
|
343
|
-
typeof DndContext,
|
|
344
|
-
typeof SortableContext,
|
|
345
|
-
typeof useSortable,
|
|
346
|
-
typeof rectSortingStrategy,
|
|
347
|
-
typeof rectSwappingStrategy,
|
|
348
|
-
typeof horizontalListSortingStrategy,
|
|
349
|
-
typeof verticalListSortingStrategy,
|
|
350
|
-
typeof CSS
|
|
351
|
-
]
|
|
352
|
-
>();
|
|
353
|
-
|
|
354
|
-
React.useEffect(() => {
|
|
355
|
-
Promise.all([
|
|
356
|
-
import("@dnd-kit/core"),
|
|
357
|
-
import("@dnd-kit/sortable"),
|
|
358
|
-
import("@dnd-kit/utilities")
|
|
359
|
-
]).then(
|
|
360
|
-
([
|
|
361
|
-
{ DndContext },
|
|
362
|
-
{
|
|
363
|
-
SortableContext,
|
|
364
|
-
useSortable,
|
|
365
|
-
rectSortingStrategy,
|
|
366
|
-
rectSwappingStrategy,
|
|
367
|
-
horizontalListSortingStrategy,
|
|
368
|
-
verticalListSortingStrategy
|
|
369
|
-
},
|
|
370
|
-
{ CSS }
|
|
371
|
-
]) => {
|
|
372
|
-
setDnd([
|
|
373
|
-
DndContext,
|
|
374
|
-
SortableContext,
|
|
375
|
-
useSortable,
|
|
376
|
-
rectSortingStrategy,
|
|
377
|
-
rectSwappingStrategy,
|
|
378
|
-
horizontalListSortingStrategy,
|
|
379
|
-
verticalListSortingStrategy,
|
|
380
|
-
CSS
|
|
381
|
-
]);
|
|
382
|
-
}
|
|
383
|
-
);
|
|
384
|
-
}, []);
|
|
385
|
-
|
|
386
|
-
const setupDiv = (div: HTMLDivElement, clearup: boolean = false) => {
|
|
387
|
-
// Inputs
|
|
388
|
-
div
|
|
389
|
-
.querySelectorAll("input")
|
|
390
|
-
.forEach((input) =>
|
|
391
|
-
clearup
|
|
392
|
-
? input.removeEventListener("change", doFormChange)
|
|
393
|
-
: input.addEventListener("change", doFormChange)
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
// Textareas
|
|
397
|
-
div
|
|
398
|
-
.querySelectorAll("textarea")
|
|
399
|
-
.forEach((input) =>
|
|
400
|
-
clearup
|
|
401
|
-
? input.removeEventListener("change", doFormChange)
|
|
402
|
-
: input.addEventListener("change", doFormChange)
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
// Select
|
|
406
|
-
div
|
|
407
|
-
.querySelectorAll("select")
|
|
408
|
-
.forEach((input) =>
|
|
409
|
-
clearup
|
|
410
|
-
? input.removeEventListener("change", doFormChange)
|
|
411
|
-
: input.addEventListener("change", doFormChange)
|
|
412
|
-
);
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
const divRef = React.useRef<HTMLDivElement>(null);
|
|
416
|
-
|
|
417
|
-
if (dnd == null) {
|
|
418
|
-
return <Skeleton variant="rectangular" width="100%" height={height} />;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const [
|
|
422
|
-
DndContextType,
|
|
423
|
-
SortableContextType,
|
|
424
|
-
useSortableType,
|
|
425
|
-
rectSortingStrategyType,
|
|
426
|
-
rectSwappingStrategyType,
|
|
427
|
-
horizontalListSortingStrategyType,
|
|
428
|
-
verticalListSortingStrategyType,
|
|
429
|
-
CSSType
|
|
430
|
-
] = dnd;
|
|
431
|
-
|
|
432
|
-
const strategy: SortingStrategy | undefined =
|
|
433
|
-
typeof sortingStrategy === "function"
|
|
434
|
-
? sortingStrategy()
|
|
435
|
-
: sortingStrategy === "rect"
|
|
436
|
-
? rectSortingStrategyType
|
|
437
|
-
: sortingStrategy === "rectSwapping"
|
|
438
|
-
? rectSwappingStrategyType
|
|
439
|
-
: sortingStrategy === "horizontal"
|
|
440
|
-
? horizontalListSortingStrategyType
|
|
441
|
-
: sortingStrategy === "vertical"
|
|
442
|
-
? verticalListSortingStrategyType
|
|
443
|
-
: undefined;
|
|
444
|
-
|
|
445
|
-
let getItemStyle = props.getItemStyle;
|
|
446
|
-
if (getItemStyle == null) {
|
|
447
|
-
getItemStyle = (index, isDragging) =>
|
|
448
|
-
DnDItemStyle(index, isDragging, theme);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Drag event handlers
|
|
452
|
-
function handleDragStart(event: DragStartEvent) {
|
|
453
|
-
const { active } = event;
|
|
454
|
-
setActiveId(active.id);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function handleDragEnd(event: DragEndEvent) {
|
|
458
|
-
const { active, over } = event;
|
|
459
|
-
|
|
460
|
-
if (over && active.id !== over.id) {
|
|
461
|
-
// Indices
|
|
462
|
-
const oldIndex = items.findIndex((item) => item.id === active.id);
|
|
463
|
-
const newIndex = items.findIndex((item) => item.id === over.id);
|
|
464
|
-
|
|
465
|
-
// Clone
|
|
466
|
-
const newItems = [...items];
|
|
467
|
-
|
|
468
|
-
// Removed item
|
|
469
|
-
const [removed] = newItems.splice(oldIndex, 1);
|
|
470
|
-
|
|
471
|
-
// Insert to the destination index
|
|
472
|
-
newItems.splice(newIndex, 0, removed);
|
|
473
|
-
|
|
474
|
-
changeItems(newItems);
|
|
475
|
-
|
|
476
|
-
// Drag end handler
|
|
477
|
-
if (onDragEnd) onDragEnd(newItems);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
setActiveId(undefined);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const children = (
|
|
484
|
-
<DndContextType onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
|
485
|
-
<SortableContextType items={items} strategy={strategy}>
|
|
486
|
-
<Component {...componentProps}>
|
|
487
|
-
{items.map((item, index) => {
|
|
488
|
-
const id = item.id;
|
|
489
|
-
return (
|
|
490
|
-
<SortableItem
|
|
491
|
-
id={id}
|
|
492
|
-
useSortableType={useSortableType}
|
|
493
|
-
CSSType={CSSType}
|
|
494
|
-
key={id}
|
|
495
|
-
style={getItemStyle!(index, id === activeId)}
|
|
496
|
-
itemRenderer={(nodeRef, actionNodeRef) =>
|
|
497
|
-
itemRenderer(item, index, nodeRef, actionNodeRef)
|
|
498
|
-
}
|
|
499
|
-
/>
|
|
500
|
-
);
|
|
501
|
-
})}
|
|
502
|
-
</Component>
|
|
503
|
-
</SortableContextType>
|
|
504
|
-
</DndContextType>
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
if (onFormChange) {
|
|
508
|
-
return (
|
|
509
|
-
<div
|
|
510
|
-
style={{ width: "100%" }}
|
|
511
|
-
ref={(div) => {
|
|
512
|
-
if (div && divRef.current != div) {
|
|
513
|
-
if (divRef.current) {
|
|
514
|
-
setupDiv(divRef.current, true);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
divRef.current = div;
|
|
518
|
-
setupDiv(div);
|
|
519
|
-
}
|
|
520
|
-
}}
|
|
521
|
-
>
|
|
522
|
-
{children}
|
|
523
|
-
</div>
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
return children;
|
|
528
|
-
}
|