react-editable-photo-grid 2.3.6 → 3.0.0
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/README.md +42 -1
- package/dist/index.js +1 -1
- package/package.json +54 -54
- package/src/PhotoGrid.tsx +167 -187
- package/src/components/Checkbox.tsx +26 -26
- package/src/components/NonScrollGallery.tsx +187 -187
- package/src/components/PhotoControls.tsx +60 -60
- package/src/components/RowControls.tsx +32 -32
- package/src/components/ScrollGallery.tsx +212 -189
- package/src/{index.js → index.ts} +5 -5
- package/src/styles.css +416 -443
- package/src/types.ts +81 -77
- package/src/utils.tsx +461 -461
package/src/utils.tsx
CHANGED
|
@@ -1,462 +1,462 @@
|
|
|
1
|
-
import { PhotoItem, PhotoRows, PhotoIdAndRowKey, PhotoGridProps } from './types';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns true if a photo has details
|
|
6
|
-
* @param photo
|
|
7
|
-
*/
|
|
8
|
-
export const photoHasDetails = (photo: PhotoItem): boolean => {
|
|
9
|
-
if (
|
|
10
|
-
(photo.name != undefined && photo.name !== '')
|
|
11
|
-
|| (photo.description != undefined && photo.description !== '')
|
|
12
|
-
) {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Sorts photos into rows
|
|
21
|
-
* @param photos
|
|
22
|
-
*/
|
|
23
|
-
export const sortPhotosIntoRows = (photos: PhotoItem[]): PhotoRows => {
|
|
24
|
-
let rows = {} as PhotoRows;
|
|
25
|
-
|
|
26
|
-
for (const photo of photos) {
|
|
27
|
-
const rowKey = photo.row;
|
|
28
|
-
if (!rows[rowKey]) {
|
|
29
|
-
rows[rowKey] = [];
|
|
30
|
-
}
|
|
31
|
-
rows[rowKey].push(photo);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return rows;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Return the photo property to be used for the src
|
|
39
|
-
* @param photo
|
|
40
|
-
* @param property
|
|
41
|
-
*/
|
|
42
|
-
export const getImageSrcProperty = (photo: PhotoItem, property: string): string => {
|
|
43
|
-
switch (property) {
|
|
44
|
-
case 'id':
|
|
45
|
-
return photo.id;
|
|
46
|
-
case 'thumbnail_path':
|
|
47
|
-
return photo.thumbnail_path;
|
|
48
|
-
case 'image_path':
|
|
49
|
-
return photo.image_path;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return photo.image_path;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Sorts the photos in a row by column number
|
|
57
|
-
* @param row
|
|
58
|
-
*/
|
|
59
|
-
export const sortRow = (row: PhotoItem[]) => {
|
|
60
|
-
return row.sort((a, b) => Math.floor(a.column) - Math.floor(b.column))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Get a photo from a row via id
|
|
65
|
-
* @param row
|
|
66
|
-
* @param id
|
|
67
|
-
*/
|
|
68
|
-
const getPhotoForId = (row: PhotoItem[], id: string): PhotoItem => {
|
|
69
|
-
const photoResult = row.find(photo => photo.id === id);
|
|
70
|
-
|
|
71
|
-
if (photoResult === undefined) {
|
|
72
|
-
throw new TypeError('No photo was found in this row for that id');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return photoResult;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get a photo from a row by column number
|
|
80
|
-
* @param row
|
|
81
|
-
* @param column
|
|
82
|
-
*/
|
|
83
|
-
const getPhotoForColumn = (row: PhotoItem[], column: number): PhotoItem => {
|
|
84
|
-
const photoResult = row.find(photo => photo.column === column);
|
|
85
|
-
|
|
86
|
-
if (photoResult === undefined) {
|
|
87
|
-
throw new TypeError('No photo was found in this row for that column');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return photoResult;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Deletes a photo from a row by column number
|
|
95
|
-
* @param row
|
|
96
|
-
* @param column
|
|
97
|
-
*/
|
|
98
|
-
const deletePhotoFromRowByColumn = (row: PhotoItem[], column: number): PhotoItem[] => {
|
|
99
|
-
return row.filter((photo: PhotoItem) => photo.column !== column);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Adds a photo to a row
|
|
104
|
-
* @param row
|
|
105
|
-
* @param photo
|
|
106
|
-
* @param column
|
|
107
|
-
*/
|
|
108
|
-
const addPhotoToRow = (row: PhotoItem[] = [], photo: PhotoItem, column: number): PhotoItem[] => {
|
|
109
|
-
photo.column = column;
|
|
110
|
-
row.push(photo);
|
|
111
|
-
return row;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Swaps two photos around in the row
|
|
116
|
-
* @param row
|
|
117
|
-
* @param firstPhoto
|
|
118
|
-
* @param secondPhoto
|
|
119
|
-
*/
|
|
120
|
-
const swapPhotosAround = (
|
|
121
|
-
row: PhotoItem[],
|
|
122
|
-
firstPhoto: PhotoItem,
|
|
123
|
-
secondPhoto: PhotoItem): PhotoItem[] => {
|
|
124
|
-
const firstColumn = firstPhoto.column,
|
|
125
|
-
secondColumn = secondPhoto.column;
|
|
126
|
-
|
|
127
|
-
row = deletePhotoFromRowByColumn(row, firstColumn);
|
|
128
|
-
row = deletePhotoFromRowByColumn(row, secondColumn);
|
|
129
|
-
row = addPhotoToRow(row, firstPhoto, secondColumn);
|
|
130
|
-
row = addPhotoToRow(row, secondPhoto, firstColumn);
|
|
131
|
-
|
|
132
|
-
return row;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Casts a the rowKey to a number if necessary
|
|
137
|
-
* @param rowKey
|
|
138
|
-
*/
|
|
139
|
-
export const castRowKey = (rowKey: string | number): number => {
|
|
140
|
-
if (typeof rowKey == 'string') {
|
|
141
|
-
rowKey = parseInt(rowKey);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return rowKey;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Extract the row key and photo id from the clicked element
|
|
149
|
-
* @param e
|
|
150
|
-
*/
|
|
151
|
-
const getPhotoIdAndRowKey = (e: React.MouseEvent<HTMLButtonElement>): PhotoIdAndRowKey => {
|
|
152
|
-
const button = e.currentTarget as HTMLButtonElement,
|
|
153
|
-
id = button.dataset.id,
|
|
154
|
-
rowKey = button.dataset.row;
|
|
155
|
-
|
|
156
|
-
if (!id || !rowKey) {
|
|
157
|
-
throw new TypeError('id or row key missing from photo control');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
id: id,
|
|
162
|
-
rowKey: parseInt(rowKey)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Moves a photo one column to the left
|
|
168
|
-
* @param e
|
|
169
|
-
* @param props
|
|
170
|
-
*/
|
|
171
|
-
export const movePhotoLeft = (
|
|
172
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
173
|
-
props: PhotoGridProps
|
|
174
|
-
) => {
|
|
175
|
-
e.preventDefault();
|
|
176
|
-
|
|
177
|
-
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
178
|
-
|
|
179
|
-
let rowsCopy = { ...props.rows },
|
|
180
|
-
thisRow = sortRow(rowsCopy[rowKey]);
|
|
181
|
-
|
|
182
|
-
const thisPhoto = getPhotoForId(thisRow, id);
|
|
183
|
-
|
|
184
|
-
if (thisPhoto.column === 1) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const beforePhoto = getPhotoForColumn(thisRow, thisPhoto.column - 1);
|
|
189
|
-
thisRow = swapPhotosAround(thisRow, beforePhoto, thisPhoto);
|
|
190
|
-
|
|
191
|
-
delete rowsCopy[rowKey];
|
|
192
|
-
rowsCopy[rowKey] = thisRow;
|
|
193
|
-
|
|
194
|
-
props.updateRows(rowsCopy);
|
|
195
|
-
props.increaseChanges();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Move a photo one column to the right
|
|
200
|
-
* @param e
|
|
201
|
-
* @param props
|
|
202
|
-
*/
|
|
203
|
-
export const movePhotoRight = (
|
|
204
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
205
|
-
props: PhotoGridProps
|
|
206
|
-
) => {
|
|
207
|
-
e.preventDefault();
|
|
208
|
-
|
|
209
|
-
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
210
|
-
|
|
211
|
-
let rowsCopy = { ...props.rows },
|
|
212
|
-
thisRow = sortRow(rowsCopy[rowKey]);
|
|
213
|
-
|
|
214
|
-
const thisPhoto = getPhotoForId(thisRow, id);
|
|
215
|
-
|
|
216
|
-
if (thisPhoto.column === thisRow.length) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const afterPhoto = getPhotoForColumn(thisRow, thisPhoto.column + 1);
|
|
221
|
-
thisRow = swapPhotosAround(thisRow, thisPhoto, afterPhoto);
|
|
222
|
-
|
|
223
|
-
delete rowsCopy[rowKey];
|
|
224
|
-
rowsCopy[rowKey] = thisRow;
|
|
225
|
-
|
|
226
|
-
props.updateRows(rowsCopy);
|
|
227
|
-
props.increaseChanges();
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* All photos in the row move back a column (to the left)
|
|
232
|
-
* @param row
|
|
233
|
-
* @param start
|
|
234
|
-
* @param end
|
|
235
|
-
*/
|
|
236
|
-
const shufflePhotosBackOneColumn = (row: PhotoItem[], start: number, end: number): PhotoItem[] => {
|
|
237
|
-
for (let x = start; x <= end; x++) {
|
|
238
|
-
const selectedPhoto = getPhotoForColumn(row, x);
|
|
239
|
-
row = deletePhotoFromRowByColumn(row, x);
|
|
240
|
-
row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column - 1);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return row;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* All the photos in the the row move forward a column (to the right)
|
|
248
|
-
* @param row
|
|
249
|
-
* @param start
|
|
250
|
-
*/
|
|
251
|
-
const shufflePhotosForwardOneColumn = (row: PhotoItem[], start: number): PhotoItem[] => {
|
|
252
|
-
for (let x = start; x > 0; x--) {
|
|
253
|
-
const selectedPhoto = getPhotoForColumn(row, x);
|
|
254
|
-
row = deletePhotoFromRowByColumn(row, x);
|
|
255
|
-
row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column + 1);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return row;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Deletes a row index from the data if it's empty
|
|
263
|
-
*/
|
|
264
|
-
const deleteRowIfEmpty = (rows: PhotoRows, rowIndex: number) => {
|
|
265
|
-
if (rows[rowIndex] == undefined) {
|
|
266
|
-
Object.entries(rows).map(([index, photos]: [string, PhotoItem[]]) => {
|
|
267
|
-
const thisRowIndex = parseInt(index);
|
|
268
|
-
if (thisRowIndex > rowIndex) {
|
|
269
|
-
delete rows[thisRowIndex];
|
|
270
|
-
rows[thisRowIndex - 1] = photos;
|
|
271
|
-
}
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return rows;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Moves a photo to the end of the previous row
|
|
280
|
-
* @param e
|
|
281
|
-
* @param props
|
|
282
|
-
*/
|
|
283
|
-
export const movePhotoUp = (
|
|
284
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
285
|
-
props: PhotoGridProps
|
|
286
|
-
): void => {
|
|
287
|
-
e.preventDefault();
|
|
288
|
-
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
289
|
-
|
|
290
|
-
let rowsCopy = { ...props.rows },
|
|
291
|
-
thisRow = sortRow(rowsCopy[rowKey]),
|
|
292
|
-
previousRowKey = rowKey - 1,
|
|
293
|
-
previousRow: PhotoItem[] = [];
|
|
294
|
-
|
|
295
|
-
if (rowsCopy[previousRowKey] != undefined) {
|
|
296
|
-
previousRow = sortRow(rowsCopy[previousRowKey]);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const thisPhoto = getPhotoForId(thisRow, id);
|
|
300
|
-
|
|
301
|
-
let start = thisPhoto.column + 1,
|
|
302
|
-
end = thisRow.length;
|
|
303
|
-
|
|
304
|
-
thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
|
|
305
|
-
delete rowsCopy[rowKey];
|
|
306
|
-
|
|
307
|
-
if (thisRow.length) {
|
|
308
|
-
shufflePhotosBackOneColumn(thisRow, start, end);
|
|
309
|
-
rowsCopy[rowKey] = thisRow;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
previousRow = addPhotoToRow(previousRow, thisPhoto, previousRow.length + 1);
|
|
313
|
-
delete rowsCopy[previousRowKey];
|
|
314
|
-
rowsCopy[previousRowKey] = previousRow;
|
|
315
|
-
|
|
316
|
-
rowsCopy = deleteRowIfEmpty(rowsCopy, rowKey);
|
|
317
|
-
|
|
318
|
-
props.updateRows(rowsCopy);
|
|
319
|
-
props.increaseChanges();
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Moves a photo to the beginning of the next row
|
|
324
|
-
* @param e
|
|
325
|
-
* @param props
|
|
326
|
-
*/
|
|
327
|
-
export const movePhotoDown = (
|
|
328
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
329
|
-
props: PhotoGridProps
|
|
330
|
-
) => {
|
|
331
|
-
e.preventDefault();
|
|
332
|
-
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
333
|
-
|
|
334
|
-
let rowsCopy = { ...props.rows },
|
|
335
|
-
thisRow = sortRow(rowsCopy[rowKey]),
|
|
336
|
-
nextRowKey = rowKey + 1;
|
|
337
|
-
|
|
338
|
-
const thisPhoto = getPhotoForId(thisRow, id);
|
|
339
|
-
|
|
340
|
-
let start = thisPhoto.column + 1,
|
|
341
|
-
end = thisRow.length;
|
|
342
|
-
|
|
343
|
-
thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
|
|
344
|
-
|
|
345
|
-
delete rowsCopy[rowKey];
|
|
346
|
-
|
|
347
|
-
if (thisRow.length) {
|
|
348
|
-
thisRow = shufflePhotosBackOneColumn(thisRow, start, end);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
let nextRow = null;
|
|
352
|
-
|
|
353
|
-
if (rowsCopy[nextRowKey] == undefined) {
|
|
354
|
-
thisPhoto.column = 1;
|
|
355
|
-
nextRow = [thisPhoto];
|
|
356
|
-
} else {
|
|
357
|
-
nextRow = sortRow(rowsCopy[nextRowKey]);
|
|
358
|
-
nextRow = shufflePhotosForwardOneColumn(nextRow, nextRow.length);
|
|
359
|
-
nextRow = addPhotoToRow(nextRow, thisPhoto, 1);
|
|
360
|
-
delete rowsCopy[nextRowKey];
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
rowsCopy[nextRowKey] = nextRow;
|
|
364
|
-
|
|
365
|
-
if (thisRow.length) {
|
|
366
|
-
rowsCopy[rowKey] = thisRow;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
rowsCopy = deleteRowIfEmpty(rowsCopy, rowKey);
|
|
370
|
-
|
|
371
|
-
props.updateRows(rowsCopy);
|
|
372
|
-
props.increaseChanges();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Swaps the row order
|
|
377
|
-
* @param e
|
|
378
|
-
* @param props
|
|
379
|
-
*/
|
|
380
|
-
const swapRows = (
|
|
381
|
-
rows: PhotoRows,
|
|
382
|
-
firstRow: PhotoItem[],
|
|
383
|
-
firstRowKey: number,
|
|
384
|
-
secondRow: PhotoItem[],
|
|
385
|
-
secondRowKey: number
|
|
386
|
-
): PhotoRows => {
|
|
387
|
-
delete rows[secondRowKey];
|
|
388
|
-
delete rows[firstRowKey];
|
|
389
|
-
rows[secondRowKey] = firstRow;
|
|
390
|
-
rows[firstRowKey] = secondRow;
|
|
391
|
-
|
|
392
|
-
return rows;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Moves a row up
|
|
397
|
-
* @param e
|
|
398
|
-
* @param props
|
|
399
|
-
*/
|
|
400
|
-
export const moveRowUp = (
|
|
401
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
402
|
-
props: PhotoGridProps
|
|
403
|
-
) => {
|
|
404
|
-
e.preventDefault();
|
|
405
|
-
const target = e.currentTarget as HTMLButtonElement;
|
|
406
|
-
let rowKey = target.dataset.row;
|
|
407
|
-
|
|
408
|
-
if (!rowKey) {
|
|
409
|
-
throw new TypeError('row missing from row control');
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const rowIndex: number = parseInt(rowKey),
|
|
413
|
-
previousRowKey = rowIndex - 1;
|
|
414
|
-
|
|
415
|
-
let rowsCopy = { ...props.rows },
|
|
416
|
-
thisRow = sortRow(rowsCopy[rowIndex]);
|
|
417
|
-
|
|
418
|
-
if (rowsCopy[previousRowKey] == undefined) {
|
|
419
|
-
delete rowsCopy[rowIndex];
|
|
420
|
-
rowsCopy[previousRowKey] = thisRow;
|
|
421
|
-
} else {
|
|
422
|
-
let previousRow = sortRow(rowsCopy[previousRowKey]);
|
|
423
|
-
rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, previousRow, previousRowKey);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
props.updateRows(rowsCopy);
|
|
427
|
-
props.increaseChanges();
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Moves a row down
|
|
432
|
-
* @param e
|
|
433
|
-
* @param props
|
|
434
|
-
*/
|
|
435
|
-
export const moveRowDown = (
|
|
436
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
437
|
-
props: PhotoGridProps
|
|
438
|
-
) => {
|
|
439
|
-
e.preventDefault();
|
|
440
|
-
const target = e.currentTarget as HTMLButtonElement;
|
|
441
|
-
let rowKey = target.dataset.row;
|
|
442
|
-
|
|
443
|
-
if (!rowKey) {
|
|
444
|
-
throw new TypeError('row missing from row control');
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const rowIndex: number = parseInt(rowKey),
|
|
448
|
-
nextRowKey = rowIndex + 1;
|
|
449
|
-
|
|
450
|
-
let rowsCopy = { ...props.rows },
|
|
451
|
-
thisRow = sortRow(rowsCopy[rowIndex]);
|
|
452
|
-
|
|
453
|
-
if (!rowsCopy[nextRowKey]) {
|
|
454
|
-
throw new TypeError('There is no next row!');
|
|
455
|
-
} else {
|
|
456
|
-
const nextRow = sortRow(rowsCopy[nextRowKey]);
|
|
457
|
-
rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, nextRow, nextRowKey);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
props.updateRows(rowsCopy);
|
|
461
|
-
props.increaseChanges();
|
|
1
|
+
import { PhotoItem, PhotoRows, PhotoIdAndRowKey, PhotoGridProps } from './types';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns true if a photo has details
|
|
6
|
+
* @param photo
|
|
7
|
+
*/
|
|
8
|
+
export const photoHasDetails = (photo: PhotoItem): boolean => {
|
|
9
|
+
if (
|
|
10
|
+
(photo.name != undefined && photo.name !== '')
|
|
11
|
+
|| (photo.description != undefined && photo.description !== '')
|
|
12
|
+
) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sorts photos into rows
|
|
21
|
+
* @param photos
|
|
22
|
+
*/
|
|
23
|
+
export const sortPhotosIntoRows = (photos: PhotoItem[]): PhotoRows => {
|
|
24
|
+
let rows = {} as PhotoRows;
|
|
25
|
+
|
|
26
|
+
for (const photo of photos) {
|
|
27
|
+
const rowKey = photo.row;
|
|
28
|
+
if (!rows[rowKey]) {
|
|
29
|
+
rows[rowKey] = [];
|
|
30
|
+
}
|
|
31
|
+
rows[rowKey].push(photo);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return rows;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Return the photo property to be used for the src
|
|
39
|
+
* @param photo
|
|
40
|
+
* @param property
|
|
41
|
+
*/
|
|
42
|
+
export const getImageSrcProperty = (photo: PhotoItem, property: string): string => {
|
|
43
|
+
switch (property) {
|
|
44
|
+
case 'id':
|
|
45
|
+
return photo.id;
|
|
46
|
+
case 'thumbnail_path':
|
|
47
|
+
return photo.thumbnail_path;
|
|
48
|
+
case 'image_path':
|
|
49
|
+
return photo.image_path;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return photo.image_path;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sorts the photos in a row by column number
|
|
57
|
+
* @param row
|
|
58
|
+
*/
|
|
59
|
+
export const sortRow = (row: PhotoItem[]) => {
|
|
60
|
+
return row.sort((a, b) => Math.floor(a.column) - Math.floor(b.column))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get a photo from a row via id
|
|
65
|
+
* @param row
|
|
66
|
+
* @param id
|
|
67
|
+
*/
|
|
68
|
+
const getPhotoForId = (row: PhotoItem[], id: string): PhotoItem => {
|
|
69
|
+
const photoResult = row.find(photo => photo.id === id);
|
|
70
|
+
|
|
71
|
+
if (photoResult === undefined) {
|
|
72
|
+
throw new TypeError('No photo was found in this row for that id');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return photoResult;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a photo from a row by column number
|
|
80
|
+
* @param row
|
|
81
|
+
* @param column
|
|
82
|
+
*/
|
|
83
|
+
const getPhotoForColumn = (row: PhotoItem[], column: number): PhotoItem => {
|
|
84
|
+
const photoResult = row.find(photo => photo.column === column);
|
|
85
|
+
|
|
86
|
+
if (photoResult === undefined) {
|
|
87
|
+
throw new TypeError('No photo was found in this row for that column');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return photoResult;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Deletes a photo from a row by column number
|
|
95
|
+
* @param row
|
|
96
|
+
* @param column
|
|
97
|
+
*/
|
|
98
|
+
const deletePhotoFromRowByColumn = (row: PhotoItem[], column: number): PhotoItem[] => {
|
|
99
|
+
return row.filter((photo: PhotoItem) => photo.column !== column);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Adds a photo to a row
|
|
104
|
+
* @param row
|
|
105
|
+
* @param photo
|
|
106
|
+
* @param column
|
|
107
|
+
*/
|
|
108
|
+
const addPhotoToRow = (row: PhotoItem[] = [], photo: PhotoItem, column: number): PhotoItem[] => {
|
|
109
|
+
photo.column = column;
|
|
110
|
+
row.push(photo);
|
|
111
|
+
return row;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Swaps two photos around in the row
|
|
116
|
+
* @param row
|
|
117
|
+
* @param firstPhoto
|
|
118
|
+
* @param secondPhoto
|
|
119
|
+
*/
|
|
120
|
+
const swapPhotosAround = (
|
|
121
|
+
row: PhotoItem[],
|
|
122
|
+
firstPhoto: PhotoItem,
|
|
123
|
+
secondPhoto: PhotoItem): PhotoItem[] => {
|
|
124
|
+
const firstColumn = firstPhoto.column,
|
|
125
|
+
secondColumn = secondPhoto.column;
|
|
126
|
+
|
|
127
|
+
row = deletePhotoFromRowByColumn(row, firstColumn);
|
|
128
|
+
row = deletePhotoFromRowByColumn(row, secondColumn);
|
|
129
|
+
row = addPhotoToRow(row, firstPhoto, secondColumn);
|
|
130
|
+
row = addPhotoToRow(row, secondPhoto, firstColumn);
|
|
131
|
+
|
|
132
|
+
return row;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Casts a the rowKey to a number if necessary
|
|
137
|
+
* @param rowKey
|
|
138
|
+
*/
|
|
139
|
+
export const castRowKey = (rowKey: string | number): number => {
|
|
140
|
+
if (typeof rowKey == 'string') {
|
|
141
|
+
rowKey = parseInt(rowKey);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return rowKey;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Extract the row key and photo id from the clicked element
|
|
149
|
+
* @param e
|
|
150
|
+
*/
|
|
151
|
+
const getPhotoIdAndRowKey = (e: React.MouseEvent<HTMLButtonElement>): PhotoIdAndRowKey => {
|
|
152
|
+
const button = e.currentTarget as HTMLButtonElement,
|
|
153
|
+
id = button.dataset.id,
|
|
154
|
+
rowKey = button.dataset.row;
|
|
155
|
+
|
|
156
|
+
if (!id || !rowKey) {
|
|
157
|
+
throw new TypeError('id or row key missing from photo control');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
id: id,
|
|
162
|
+
rowKey: parseInt(rowKey)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Moves a photo one column to the left
|
|
168
|
+
* @param e
|
|
169
|
+
* @param props
|
|
170
|
+
*/
|
|
171
|
+
export const movePhotoLeft = (
|
|
172
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
173
|
+
props: PhotoGridProps
|
|
174
|
+
) => {
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
|
|
177
|
+
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
178
|
+
|
|
179
|
+
let rowsCopy = { ...props.rows },
|
|
180
|
+
thisRow = sortRow(rowsCopy[rowKey]);
|
|
181
|
+
|
|
182
|
+
const thisPhoto = getPhotoForId(thisRow, id);
|
|
183
|
+
|
|
184
|
+
if (thisPhoto.column === 1) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const beforePhoto = getPhotoForColumn(thisRow, thisPhoto.column - 1);
|
|
189
|
+
thisRow = swapPhotosAround(thisRow, beforePhoto, thisPhoto);
|
|
190
|
+
|
|
191
|
+
delete rowsCopy[rowKey];
|
|
192
|
+
rowsCopy[rowKey] = thisRow;
|
|
193
|
+
|
|
194
|
+
props.updateRows(rowsCopy);
|
|
195
|
+
props.increaseChanges();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Move a photo one column to the right
|
|
200
|
+
* @param e
|
|
201
|
+
* @param props
|
|
202
|
+
*/
|
|
203
|
+
export const movePhotoRight = (
|
|
204
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
205
|
+
props: PhotoGridProps
|
|
206
|
+
) => {
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
|
|
209
|
+
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
210
|
+
|
|
211
|
+
let rowsCopy = { ...props.rows },
|
|
212
|
+
thisRow = sortRow(rowsCopy[rowKey]);
|
|
213
|
+
|
|
214
|
+
const thisPhoto = getPhotoForId(thisRow, id);
|
|
215
|
+
|
|
216
|
+
if (thisPhoto.column === thisRow.length) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const afterPhoto = getPhotoForColumn(thisRow, thisPhoto.column + 1);
|
|
221
|
+
thisRow = swapPhotosAround(thisRow, thisPhoto, afterPhoto);
|
|
222
|
+
|
|
223
|
+
delete rowsCopy[rowKey];
|
|
224
|
+
rowsCopy[rowKey] = thisRow;
|
|
225
|
+
|
|
226
|
+
props.updateRows(rowsCopy);
|
|
227
|
+
props.increaseChanges();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* All photos in the row move back a column (to the left)
|
|
232
|
+
* @param row
|
|
233
|
+
* @param start
|
|
234
|
+
* @param end
|
|
235
|
+
*/
|
|
236
|
+
const shufflePhotosBackOneColumn = (row: PhotoItem[], start: number, end: number): PhotoItem[] => {
|
|
237
|
+
for (let x = start; x <= end; x++) {
|
|
238
|
+
const selectedPhoto = getPhotoForColumn(row, x);
|
|
239
|
+
row = deletePhotoFromRowByColumn(row, x);
|
|
240
|
+
row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column - 1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return row;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* All the photos in the the row move forward a column (to the right)
|
|
248
|
+
* @param row
|
|
249
|
+
* @param start
|
|
250
|
+
*/
|
|
251
|
+
const shufflePhotosForwardOneColumn = (row: PhotoItem[], start: number): PhotoItem[] => {
|
|
252
|
+
for (let x = start; x > 0; x--) {
|
|
253
|
+
const selectedPhoto = getPhotoForColumn(row, x);
|
|
254
|
+
row = deletePhotoFromRowByColumn(row, x);
|
|
255
|
+
row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column + 1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return row;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Deletes a row index from the data if it's empty
|
|
263
|
+
*/
|
|
264
|
+
const deleteRowIfEmpty = (rows: PhotoRows, rowIndex: number) => {
|
|
265
|
+
if (rows[rowIndex] == undefined) {
|
|
266
|
+
Object.entries(rows).map(([index, photos]: [string, PhotoItem[]]) => {
|
|
267
|
+
const thisRowIndex = parseInt(index);
|
|
268
|
+
if (thisRowIndex > rowIndex) {
|
|
269
|
+
delete rows[thisRowIndex];
|
|
270
|
+
rows[thisRowIndex - 1] = photos;
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return rows;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Moves a photo to the end of the previous row
|
|
280
|
+
* @param e
|
|
281
|
+
* @param props
|
|
282
|
+
*/
|
|
283
|
+
export const movePhotoUp = (
|
|
284
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
285
|
+
props: PhotoGridProps
|
|
286
|
+
): void => {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
289
|
+
|
|
290
|
+
let rowsCopy = { ...props.rows },
|
|
291
|
+
thisRow = sortRow(rowsCopy[rowKey]),
|
|
292
|
+
previousRowKey = rowKey - 1,
|
|
293
|
+
previousRow: PhotoItem[] = [];
|
|
294
|
+
|
|
295
|
+
if (rowsCopy[previousRowKey] != undefined) {
|
|
296
|
+
previousRow = sortRow(rowsCopy[previousRowKey]);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const thisPhoto = getPhotoForId(thisRow, id);
|
|
300
|
+
|
|
301
|
+
let start = thisPhoto.column + 1,
|
|
302
|
+
end = thisRow.length;
|
|
303
|
+
|
|
304
|
+
thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
|
|
305
|
+
delete rowsCopy[rowKey];
|
|
306
|
+
|
|
307
|
+
if (thisRow.length) {
|
|
308
|
+
shufflePhotosBackOneColumn(thisRow, start, end);
|
|
309
|
+
rowsCopy[rowKey] = thisRow;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
previousRow = addPhotoToRow(previousRow, thisPhoto, previousRow.length + 1);
|
|
313
|
+
delete rowsCopy[previousRowKey];
|
|
314
|
+
rowsCopy[previousRowKey] = previousRow;
|
|
315
|
+
|
|
316
|
+
rowsCopy = deleteRowIfEmpty(rowsCopy, rowKey);
|
|
317
|
+
|
|
318
|
+
props.updateRows(rowsCopy);
|
|
319
|
+
props.increaseChanges();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Moves a photo to the beginning of the next row
|
|
324
|
+
* @param e
|
|
325
|
+
* @param props
|
|
326
|
+
*/
|
|
327
|
+
export const movePhotoDown = (
|
|
328
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
329
|
+
props: PhotoGridProps
|
|
330
|
+
) => {
|
|
331
|
+
e.preventDefault();
|
|
332
|
+
const { id, rowKey } = getPhotoIdAndRowKey(e);
|
|
333
|
+
|
|
334
|
+
let rowsCopy = { ...props.rows },
|
|
335
|
+
thisRow = sortRow(rowsCopy[rowKey]),
|
|
336
|
+
nextRowKey = rowKey + 1;
|
|
337
|
+
|
|
338
|
+
const thisPhoto = getPhotoForId(thisRow, id);
|
|
339
|
+
|
|
340
|
+
let start = thisPhoto.column + 1,
|
|
341
|
+
end = thisRow.length;
|
|
342
|
+
|
|
343
|
+
thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
|
|
344
|
+
|
|
345
|
+
delete rowsCopy[rowKey];
|
|
346
|
+
|
|
347
|
+
if (thisRow.length) {
|
|
348
|
+
thisRow = shufflePhotosBackOneColumn(thisRow, start, end);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let nextRow = null;
|
|
352
|
+
|
|
353
|
+
if (rowsCopy[nextRowKey] == undefined) {
|
|
354
|
+
thisPhoto.column = 1;
|
|
355
|
+
nextRow = [thisPhoto];
|
|
356
|
+
} else {
|
|
357
|
+
nextRow = sortRow(rowsCopy[nextRowKey]);
|
|
358
|
+
nextRow = shufflePhotosForwardOneColumn(nextRow, nextRow.length);
|
|
359
|
+
nextRow = addPhotoToRow(nextRow, thisPhoto, 1);
|
|
360
|
+
delete rowsCopy[nextRowKey];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
rowsCopy[nextRowKey] = nextRow;
|
|
364
|
+
|
|
365
|
+
if (thisRow.length) {
|
|
366
|
+
rowsCopy[rowKey] = thisRow;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
rowsCopy = deleteRowIfEmpty(rowsCopy, rowKey);
|
|
370
|
+
|
|
371
|
+
props.updateRows(rowsCopy);
|
|
372
|
+
props.increaseChanges();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Swaps the row order
|
|
377
|
+
* @param e
|
|
378
|
+
* @param props
|
|
379
|
+
*/
|
|
380
|
+
const swapRows = (
|
|
381
|
+
rows: PhotoRows,
|
|
382
|
+
firstRow: PhotoItem[],
|
|
383
|
+
firstRowKey: number,
|
|
384
|
+
secondRow: PhotoItem[],
|
|
385
|
+
secondRowKey: number
|
|
386
|
+
): PhotoRows => {
|
|
387
|
+
delete rows[secondRowKey];
|
|
388
|
+
delete rows[firstRowKey];
|
|
389
|
+
rows[secondRowKey] = firstRow;
|
|
390
|
+
rows[firstRowKey] = secondRow;
|
|
391
|
+
|
|
392
|
+
return rows;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Moves a row up
|
|
397
|
+
* @param e
|
|
398
|
+
* @param props
|
|
399
|
+
*/
|
|
400
|
+
export const moveRowUp = (
|
|
401
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
402
|
+
props: PhotoGridProps
|
|
403
|
+
) => {
|
|
404
|
+
e.preventDefault();
|
|
405
|
+
const target = e.currentTarget as HTMLButtonElement;
|
|
406
|
+
let rowKey = target.dataset.row;
|
|
407
|
+
|
|
408
|
+
if (!rowKey) {
|
|
409
|
+
throw new TypeError('row missing from row control');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const rowIndex: number = parseInt(rowKey),
|
|
413
|
+
previousRowKey = rowIndex - 1;
|
|
414
|
+
|
|
415
|
+
let rowsCopy = { ...props.rows },
|
|
416
|
+
thisRow = sortRow(rowsCopy[rowIndex]);
|
|
417
|
+
|
|
418
|
+
if (rowsCopy[previousRowKey] == undefined) {
|
|
419
|
+
delete rowsCopy[rowIndex];
|
|
420
|
+
rowsCopy[previousRowKey] = thisRow;
|
|
421
|
+
} else {
|
|
422
|
+
let previousRow = sortRow(rowsCopy[previousRowKey]);
|
|
423
|
+
rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, previousRow, previousRowKey);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
props.updateRows(rowsCopy);
|
|
427
|
+
props.increaseChanges();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Moves a row down
|
|
432
|
+
* @param e
|
|
433
|
+
* @param props
|
|
434
|
+
*/
|
|
435
|
+
export const moveRowDown = (
|
|
436
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
437
|
+
props: PhotoGridProps
|
|
438
|
+
) => {
|
|
439
|
+
e.preventDefault();
|
|
440
|
+
const target = e.currentTarget as HTMLButtonElement;
|
|
441
|
+
let rowKey = target.dataset.row;
|
|
442
|
+
|
|
443
|
+
if (!rowKey) {
|
|
444
|
+
throw new TypeError('row missing from row control');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const rowIndex: number = parseInt(rowKey),
|
|
448
|
+
nextRowKey = rowIndex + 1;
|
|
449
|
+
|
|
450
|
+
let rowsCopy = { ...props.rows },
|
|
451
|
+
thisRow = sortRow(rowsCopy[rowIndex]);
|
|
452
|
+
|
|
453
|
+
if (!rowsCopy[nextRowKey]) {
|
|
454
|
+
throw new TypeError('There is no next row!');
|
|
455
|
+
} else {
|
|
456
|
+
const nextRow = sortRow(rowsCopy[nextRowKey]);
|
|
457
|
+
rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, nextRow, nextRowKey);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
props.updateRows(rowsCopy);
|
|
461
|
+
props.increaseChanges();
|
|
462
462
|
}
|