react-editable-photo-grid 1.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/src/utils.tsx ADDED
@@ -0,0 +1,366 @@
1
+ import { PhotoItem, PhotoRows, PhotoIdAndRowKey, PhotoGridProps } from './types';
2
+ import React from 'react';
3
+
4
+ /**
5
+ * Sorts the photos in a row by column number
6
+ * @param row
7
+ */
8
+ export const sortRow = (row: PhotoItem[]) => {
9
+ return row.sort((a, b) => Math.floor(a.column) - Math.floor(b.column))
10
+ }
11
+
12
+ /**
13
+ * Get a photo from a row via id
14
+ * @param row
15
+ * @param id
16
+ */
17
+ const getPhotoForId = (row: PhotoItem[], id: string): PhotoItem => {
18
+ const photoResult = row.find(photo => photo.id === id);
19
+
20
+ if (photoResult === undefined) {
21
+ throw new TypeError('No photo was found in this row for that id');
22
+ }
23
+
24
+ return photoResult;
25
+ }
26
+
27
+ /**
28
+ * Get a photo from a row by column number
29
+ * @param row
30
+ * @param column
31
+ */
32
+ const getPhotoForColumn = (row: PhotoItem[], column: number): PhotoItem => {
33
+ const photoResult = row.find(photo => photo.column === column);
34
+
35
+ if (photoResult === undefined) {
36
+ throw new TypeError('No photo was found in this row for that column');
37
+ }
38
+
39
+ return photoResult;
40
+ }
41
+
42
+ /**
43
+ * Deletes a photo from a row by column number
44
+ * @param row
45
+ * @param column
46
+ */
47
+ const deletePhotoFromRowByColumn = (row: PhotoItem[], column: number): PhotoItem[] => {
48
+ return row.filter((photo: PhotoItem) => photo.column !== column);
49
+ }
50
+
51
+ /**
52
+ * Adds a photo to a row
53
+ * @param row
54
+ * @param photo
55
+ * @param column
56
+ */
57
+ const addPhotoToRow = (row: PhotoItem[] = [], photo: PhotoItem, column: number): PhotoItem[] => {
58
+ photo.column = column;
59
+ row.push(photo);
60
+ return row;
61
+ }
62
+
63
+ /**
64
+ * Swaps two photos around in the row
65
+ * @param row
66
+ * @param firstPhoto
67
+ * @param secondPhoto
68
+ */
69
+ const swapPhotosAround = (
70
+ row: PhotoItem[],
71
+ firstPhoto: PhotoItem,
72
+ secondPhoto: PhotoItem): PhotoItem[] => {
73
+ const firstColumn = firstPhoto.column,
74
+ secondColumn = secondPhoto.column;
75
+
76
+ row = deletePhotoFromRowByColumn(row, firstColumn);
77
+ row = deletePhotoFromRowByColumn(row, secondColumn);
78
+ row = addPhotoToRow(row, firstPhoto, secondColumn);
79
+ row = addPhotoToRow(row, secondPhoto, firstColumn);
80
+
81
+ return row;
82
+ }
83
+
84
+ /**
85
+ * Casts a the rowKey to a number if necessary
86
+ * @param rowKey
87
+ */
88
+ export const castRowKey = (rowKey: string | number): number => {
89
+ if (typeof rowKey == 'string') {
90
+ rowKey = parseInt(rowKey);
91
+ }
92
+
93
+ return rowKey;
94
+ }
95
+
96
+ /**
97
+ * Extract the row key and photo id from the clicked element
98
+ * @param e
99
+ */
100
+ const getPhotoIdAndRowKey = (e: React.MouseEvent<HTMLButtonElement>): PhotoIdAndRowKey => {
101
+ const button = e.currentTarget as HTMLButtonElement,
102
+ id = button.dataset.id,
103
+ rowKey = button.dataset.row;
104
+
105
+ if (!id || !rowKey) {
106
+ throw new TypeError('id or row key missing from photo control');
107
+ }
108
+
109
+ return {
110
+ id: id,
111
+ rowKey: parseInt(rowKey)
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Moves a photo one column to the left
117
+ * @param e
118
+ * @param props
119
+ */
120
+ export const movePhotoLeft = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
121
+ e.preventDefault();
122
+
123
+ const { id, rowKey } = getPhotoIdAndRowKey(e);
124
+
125
+ let rowsCopy = { ...props.rows },
126
+ thisRow = sortRow(rowsCopy[rowKey]);
127
+
128
+ const thisPhoto = getPhotoForId(thisRow, id);
129
+
130
+ if (thisPhoto.column === 1) {
131
+ return;
132
+ }
133
+
134
+ const beforePhoto = getPhotoForColumn(thisRow, thisPhoto.column - 1);
135
+ thisRow = swapPhotosAround(thisRow, beforePhoto, thisPhoto);
136
+
137
+ delete rowsCopy[rowKey];
138
+ rowsCopy[rowKey] = thisRow;
139
+
140
+ props.updateRows(rowsCopy);
141
+ props.increaseChanges();
142
+ }
143
+
144
+ /**
145
+ * Move a photo one column to the right
146
+ * @param e
147
+ * @param props
148
+ */
149
+ export const movePhotoRight = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
150
+ e.preventDefault();
151
+
152
+ const { id, rowKey } = getPhotoIdAndRowKey(e);
153
+
154
+ let rowsCopy = { ...props.rows },
155
+ thisRow = sortRow(rowsCopy[rowKey]);
156
+
157
+ const thisPhoto = getPhotoForId(thisRow, id);
158
+
159
+ if (thisPhoto.column === thisRow.length) {
160
+ return;
161
+ }
162
+
163
+ const afterPhoto = getPhotoForColumn(thisRow, thisPhoto.column + 1);
164
+ thisRow = swapPhotosAround(thisRow, thisPhoto, afterPhoto);
165
+
166
+ delete rowsCopy[rowKey];
167
+ rowsCopy[rowKey] = thisRow;
168
+
169
+ props.updateRows(rowsCopy);
170
+ props.increaseChanges();
171
+ }
172
+
173
+ /**
174
+ * All photos in the row move back a column (to the left)
175
+ * @param row
176
+ * @param start
177
+ * @param end
178
+ */
179
+ const shufflePhotosBackOneColumn = (row: PhotoItem[], start: number, end: number): PhotoItem[] => {
180
+ for (let x = start; x <= end; x++) {
181
+ const selectedPhoto = getPhotoForColumn(row, x);
182
+ row = deletePhotoFromRowByColumn(row, x);
183
+ row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column - 1);
184
+ }
185
+
186
+ return row;
187
+ }
188
+
189
+ /**
190
+ * All the photos in the the row move forward a column (to the right)
191
+ * @param row
192
+ * @param start
193
+ */
194
+ const shufflePhotosForwardOneColumn = (row: PhotoItem[], start: number): PhotoItem[] => {
195
+ for (let x = start; x > 0; x--) {
196
+ const selectedPhoto = getPhotoForColumn(row, x);
197
+ row = deletePhotoFromRowByColumn(row, x);
198
+ row = addPhotoToRow(row, selectedPhoto, selectedPhoto.column + 1);
199
+ }
200
+
201
+ return row;
202
+ }
203
+
204
+ /**
205
+ * Moves a photo to the end of the previous row
206
+ * @param e
207
+ * @param props
208
+ */
209
+ export const movePhotoUp = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
210
+ e.preventDefault();
211
+ const { id, rowKey } = getPhotoIdAndRowKey(e);
212
+
213
+ let rowsCopy = { ...props.rows },
214
+ thisRow = sortRow(rowsCopy[rowKey]),
215
+ previousRowKey = rowKey - 1,
216
+ previousRow: PhotoItem[] = [];
217
+
218
+ if (rowsCopy[previousRowKey] != undefined) {
219
+ previousRow = sortRow(rowsCopy[previousRowKey]);
220
+ }
221
+
222
+ const thisPhoto = getPhotoForId(thisRow, id);
223
+
224
+ let start = thisPhoto.column + 1,
225
+ end = thisRow.length;
226
+
227
+ thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
228
+ delete rowsCopy[rowKey];
229
+
230
+ if (thisRow.length) {
231
+ shufflePhotosBackOneColumn(thisRow, start, end);
232
+ rowsCopy[rowKey] = thisRow;
233
+ }
234
+
235
+ previousRow = addPhotoToRow(previousRow, thisPhoto, previousRow.length + 1);
236
+ delete rowsCopy[previousRowKey];
237
+ rowsCopy[previousRowKey] = previousRow;
238
+
239
+ props.updateRows(rowsCopy);
240
+ props.increaseChanges();
241
+ }
242
+
243
+ /**
244
+ * Moves a photo to the beginning of the next row
245
+ * @param e
246
+ * @param props
247
+ */
248
+ export const movePhotoDown = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
249
+ e.preventDefault();
250
+ const { id, rowKey } = getPhotoIdAndRowKey(e);
251
+
252
+ let rowsCopy = { ...props.rows },
253
+ thisRow = sortRow(rowsCopy[rowKey]),
254
+ nextRowKey = rowKey + 1;
255
+
256
+ const thisPhoto = getPhotoForId(thisRow, id);
257
+
258
+ let start = thisPhoto.column + 1,
259
+ end = thisRow.length;
260
+
261
+ thisRow = deletePhotoFromRowByColumn(thisRow, thisPhoto.column);
262
+
263
+ delete rowsCopy[rowKey];
264
+
265
+ if (thisRow.length) {
266
+ thisRow = shufflePhotosBackOneColumn(thisRow, start, end);
267
+ }
268
+
269
+ let nextRow = null;
270
+
271
+ if (rowsCopy[nextRowKey] == undefined) {
272
+ thisPhoto.column = 1;
273
+ nextRow = [thisPhoto];
274
+ } else {
275
+ nextRow = sortRow(rowsCopy[nextRowKey]);
276
+ nextRow = shufflePhotosForwardOneColumn(nextRow, nextRow.length);
277
+ nextRow = addPhotoToRow(nextRow, thisPhoto, 1);
278
+ delete rowsCopy[nextRowKey];
279
+ }
280
+
281
+ rowsCopy[nextRowKey] = nextRow;
282
+
283
+ if (thisRow.length) {
284
+ rowsCopy[rowKey] = thisRow;
285
+ }
286
+
287
+ props.updateRows(rowsCopy);
288
+ props.increaseChanges();
289
+ }
290
+
291
+ /**
292
+ * Swaps the row order
293
+ * @param rows
294
+ * @param firstRow
295
+ * @param firstRowKey
296
+ * @param secondRow
297
+ * @param secondRowKey
298
+ */
299
+ const swapRows = (
300
+ rows: PhotoRows,
301
+ firstRow: PhotoItem[],
302
+ firstRowKey: number,
303
+ secondRow: PhotoItem[],
304
+ secondRowKey: number
305
+ ): PhotoRows => {
306
+ delete rows[secondRowKey];
307
+ delete rows[firstRowKey];
308
+ rows[secondRowKey] = firstRow;
309
+ rows[firstRowKey] = secondRow;
310
+
311
+ return rows;
312
+ }
313
+
314
+ /**
315
+ * Move a row up
316
+ * @param e
317
+ * @param props
318
+ */
319
+ export const moveRowUp = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
320
+ e.preventDefault();
321
+ const target = e.currentTarget as HTMLButtonElement;
322
+ let rowKey = target.dataset.row;
323
+
324
+ if (!rowKey) {
325
+ throw new TypeError('row missing from row control');
326
+ }
327
+
328
+ const rowIndex: number = parseInt(rowKey),
329
+ previousRowKey = rowIndex - 1;
330
+
331
+ let rowsCopy = { ...props.rows },
332
+ thisRow = sortRow(rowsCopy[rowIndex]),
333
+ previousRow = sortRow(rowsCopy[previousRowKey]);
334
+
335
+ rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, previousRow, previousRowKey);
336
+
337
+ props.updateRows(rowsCopy);
338
+ props.increaseChanges();
339
+ }
340
+
341
+ /**
342
+ * Moves a row down
343
+ * @param e
344
+ * @param props
345
+ */
346
+ export const moveRowDown = (e: React.MouseEvent<HTMLButtonElement>, props: PhotoGridProps) => {
347
+ e.preventDefault();
348
+ const target = e.currentTarget as HTMLButtonElement;
349
+ let rowKey = target.dataset.row;
350
+
351
+ if (!rowKey) {
352
+ throw new TypeError('row missing from row control');
353
+ }
354
+
355
+ const rowIndex: number = parseInt(rowKey),
356
+ nextRowKey = rowIndex + 1;
357
+
358
+ let rowsCopy = { ...props.rows },
359
+ thisRow = sortRow(rowsCopy[rowIndex]),
360
+ nextRow = sortRow(rowsCopy[nextRowKey]);
361
+
362
+ rowsCopy = swapRows(rowsCopy, thisRow, rowIndex, nextRow, nextRowKey);
363
+
364
+ props.updateRows(rowsCopy);
365
+ props.increaseChanges();
366
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES5",
4
+ "module": "ESNext",
5
+ "lib": ["dom", "es2015"],
6
+ "jsx": "react",
7
+ "moduleResolution": "node",
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "strict": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "outDir": "./dist",
14
+ "rootDir": "./src"
15
+ },
16
+ "include": ["src"]
17
+ }
18
+
@@ -0,0 +1,39 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ mode: 'production',
5
+ entry: './src/index.js',
6
+ output: {
7
+ path: path.resolve('dist'),
8
+ filename: 'index.js',
9
+ libraryTarget: 'commonjs2'
10
+ },
11
+ resolve: {
12
+ extensions: ['.tsx', '.ts', '.js'],
13
+ modules: [path.resolve(__dirname, 'src'), 'node_modules'],
14
+ },
15
+ module: {
16
+ rules: [
17
+ {
18
+ test: /\.tsx?$/,
19
+ exclude: /node_modules/,
20
+ use: 'ts-loader',
21
+ },
22
+ {
23
+ test: /\.js$/,
24
+ exclude: /node_modules/,
25
+ use: 'babel-loader',
26
+ },
27
+ {
28
+ test: /\.css$/,
29
+ exclude: /node_modules/,
30
+ use: [ 'style-loader', 'css-loader' ],
31
+ },
32
+ ]
33
+ },
34
+ externals: {
35
+ react: 'react',
36
+ 'react-dom': 'react-dom'
37
+ }
38
+ };
39
+