@janrankenhohn/react-thumbnail-list 0.5.0 → 0.5.2
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/dist/index.cjs.js +614 -306
- package/dist/index.esm.js +602 -293
- package/package.json +15 -4
package/dist/index.esm.js
CHANGED
|
@@ -1,307 +1,616 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { Stack } from '@mui/system';
|
|
5
|
-
import SearchIcon from '@mui/icons-material/Search';
|
|
6
|
-
import ClearIcon from '@mui/icons-material/Clear';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const context = useContext(ThumbnailListItemContext);
|
|
21
|
-
if (!context) {
|
|
22
|
-
throw new Error('useMyContext must be used within a MyContextProvider');
|
|
23
|
-
}
|
|
24
|
-
return context;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Can be used as parent component to crop a wrapped image
|
|
29
|
-
* @param props width: width for cropping
|
|
30
|
-
* height: height for cropping
|
|
31
|
-
* seperate xs and sm values for mui breakpoints
|
|
32
|
-
* @returns component
|
|
33
|
-
*/
|
|
34
|
-
function ImageCropper(props) {
|
|
35
|
-
const ThumbnailImageCrop = styled('div')((p) => ({
|
|
36
|
-
[p.theme.breakpoints.up('xs')]: {
|
|
37
|
-
minWidth: props.width.xs,
|
|
38
|
-
maxWidth: props.width.xs,
|
|
39
|
-
height: props.height.xs,
|
|
40
|
-
overflow: 'hidden'
|
|
41
|
-
},
|
|
42
|
-
[p.theme.breakpoints.up('sm')]: {
|
|
43
|
-
minWidth: props.width.sm,
|
|
44
|
-
maxwWidth: props.width.sm,
|
|
45
|
-
height: props.height.sm,
|
|
46
|
-
},
|
|
47
|
-
}));
|
|
48
|
-
return (jsx(Fragment, { children: jsx(ThumbnailImageCrop, { children: props.children }) }));
|
|
49
|
-
}
|
|
50
|
-
|
|
1
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { styled, Box, Typography, Card, CardActionArea, Stack as Stack$1, Grid, LinearProgress, FormControl, TextField, InputAdornment, IconButton, useTheme, useMediaQuery, Chip, Tooltip, InputLabel, Select, MenuItem, Menu } from '@mui/material';
|
|
3
|
+
import React, { createContext, useContext, useMemo, useState, useCallback, useEffect, Children } from 'react';
|
|
4
|
+
import { Stack } from '@mui/system';
|
|
5
|
+
import SearchIcon from '@mui/icons-material/Search';
|
|
6
|
+
import ClearIcon from '@mui/icons-material/Clear';
|
|
7
|
+
import { debounce } from 'lodash';
|
|
8
|
+
import SwapVertIcon from '@mui/icons-material/SwapVert';
|
|
9
|
+
import SortIcon from '@mui/icons-material/Sort';
|
|
10
|
+
|
|
11
|
+
const ThumbnailListItemContext = /*#__PURE__*/createContext(undefined);
|
|
12
|
+
const useThumbnailListItemContext = () => {
|
|
13
|
+
const context = useContext(ThumbnailListItemContext);
|
|
14
|
+
if (!context) {
|
|
15
|
+
throw new Error('no context provider available');
|
|
16
|
+
}
|
|
17
|
+
return context;
|
|
18
|
+
};
|
|
19
|
+
|
|
51
20
|
/**
|
|
52
21
|
* Creates a ellipies text with webkit css styles
|
|
53
22
|
* @param props lineClamp: lines till ellipses
|
|
54
23
|
* @returns component
|
|
55
|
-
*/
|
|
56
|
-
function EllipsisContainer(props) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
24
|
+
*/
|
|
25
|
+
function EllipsisContainer(props) {
|
|
26
|
+
const EllipsisContainer = styled('div')(p => ({
|
|
27
|
+
[p.theme.breakpoints.up('xs')]: {
|
|
28
|
+
overflow: 'hidden',
|
|
29
|
+
display: '-webkit-box',
|
|
30
|
+
WebkitLineClamp: props.lineClamp.xs.toString() /* number of lines to show */,
|
|
31
|
+
WebkitBoxOrient: 'vertical'
|
|
32
|
+
},
|
|
33
|
+
[p.theme.breakpoints.up('sm')]: {
|
|
34
|
+
overflow: 'hidden',
|
|
35
|
+
display: '-webkit-box',
|
|
36
|
+
WebkitLineClamp: props.lineClamp.sm.toString() /* number of lines to show */,
|
|
37
|
+
WebkitBoxOrient: 'vertical' /* number of lines to show */
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
return jsx(EllipsisContainer, {
|
|
41
|
+
children: props.children
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ThumbnailListItemTitle(props) {
|
|
46
|
+
const StyledCardContent = styled('div')(p => ({
|
|
47
|
+
[p.theme.breakpoints.up('xs')]: {
|
|
48
|
+
padding: p.theme.spacing(1),
|
|
49
|
+
flex: '1 0 auto',
|
|
50
|
+
'&:last-child': {
|
|
51
|
+
paddingBottom: 0
|
|
52
|
+
},
|
|
53
|
+
overflow: 'hidden'
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
console.log('item title rerenders');
|
|
57
|
+
return jsx(Fragment, {
|
|
58
|
+
children: jsx(Box, {
|
|
59
|
+
children: jsxs(StyledCardContent, {
|
|
60
|
+
children: [jsx(EllipsisContainer, {
|
|
61
|
+
lineClamp: {
|
|
62
|
+
xs: 1,
|
|
63
|
+
sm: 2
|
|
64
|
+
},
|
|
65
|
+
children: jsx(Typography, {
|
|
66
|
+
variant: "subtitle2",
|
|
67
|
+
sx: {
|
|
68
|
+
fontWeight: 'bold'
|
|
69
|
+
},
|
|
70
|
+
children: props.title
|
|
71
|
+
})
|
|
72
|
+
}), jsx(Stack, {
|
|
73
|
+
direction: "row",
|
|
74
|
+
gap: 1,
|
|
75
|
+
children: jsx(EllipsisContainer, {
|
|
76
|
+
lineClamp: {
|
|
77
|
+
xs: 1,
|
|
78
|
+
sm: 2
|
|
79
|
+
},
|
|
80
|
+
children: jsx(Typography, {
|
|
81
|
+
variant: "subtitle2",
|
|
82
|
+
sx: {
|
|
83
|
+
fontSize: '0.84rem'
|
|
84
|
+
},
|
|
85
|
+
color: "text.secondary",
|
|
86
|
+
children: props.subTitle
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
})]
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const ThumbnailListItem = props => {
|
|
96
|
+
console.log('ThumbnailListItems renders');
|
|
97
|
+
return jsx(Fragment, {
|
|
98
|
+
children: jsx(Card, {
|
|
99
|
+
sx: {
|
|
100
|
+
display: 'flex'
|
|
101
|
+
},
|
|
102
|
+
children: jsx(CardActionArea, {
|
|
103
|
+
disabled: !props.onClick,
|
|
104
|
+
onClick: () => props.onClick(props.id),
|
|
105
|
+
children: jsxs(Stack$1, {
|
|
106
|
+
direction: "row",
|
|
107
|
+
width: "100%",
|
|
108
|
+
children: [jsx("img", {
|
|
109
|
+
src: props.thumbnailUrl,
|
|
110
|
+
width: '45%'
|
|
111
|
+
}), jsxs(Stack$1, {
|
|
112
|
+
direction: "row",
|
|
113
|
+
justifyContent: "space-between",
|
|
114
|
+
width: "100%",
|
|
115
|
+
gap: 1,
|
|
116
|
+
children: [jsx(ThumbnailListItemTitle, {
|
|
117
|
+
title: props.title,
|
|
118
|
+
subTitle: props.subTitle
|
|
119
|
+
}), props.infoLabel]
|
|
120
|
+
})]
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
var ThumbnailListItem$1 = /*#__PURE__*/React.memo(ThumbnailListItem);
|
|
127
|
+
|
|
128
|
+
const RatioWrapper = styled('div')(() => ({
|
|
129
|
+
// Assuming a 16:9 aspect ratio
|
|
130
|
+
paddingTop: '27.75%',
|
|
131
|
+
position: 'relative',
|
|
132
|
+
width: '100%',
|
|
133
|
+
'& > *': {
|
|
134
|
+
position: 'absolute',
|
|
135
|
+
top: 0,
|
|
136
|
+
left: 0,
|
|
137
|
+
right: 0,
|
|
138
|
+
bottom: 0
|
|
139
|
+
}
|
|
140
|
+
}));
|
|
141
|
+
function ThumbnailListMainContent(props) {
|
|
142
|
+
const {
|
|
143
|
+
items,
|
|
144
|
+
isLoading
|
|
145
|
+
} = useThumbnailListItemContext();
|
|
146
|
+
console.log('main content rerenders');
|
|
147
|
+
const memoizedItems = useMemo(() => {
|
|
148
|
+
return items.map(item => jsx(Grid, {
|
|
149
|
+
item: true,
|
|
150
|
+
xs: props.muiBreakpoints.xs,
|
|
151
|
+
sm: props.muiBreakpoints.sm,
|
|
152
|
+
md: props.muiBreakpoints.md,
|
|
153
|
+
lg: props.muiBreakpoints.lg,
|
|
154
|
+
xl: props.muiBreakpoints.xl,
|
|
155
|
+
children: jsx(RatioWrapper, {
|
|
156
|
+
children: jsx(ThumbnailListItem$1, {
|
|
157
|
+
id: item.id,
|
|
158
|
+
thumbnailUrl: item.thumbnailUrl,
|
|
159
|
+
title: item.title,
|
|
160
|
+
subTitle: item.subTitle,
|
|
161
|
+
infoLabel: item.label,
|
|
162
|
+
onClick: item.onClick
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
}, item.id));
|
|
166
|
+
}, [items, props.muiBreakpoints]);
|
|
167
|
+
return jsxs(Fragment, {
|
|
168
|
+
children: [jsx(Box, {
|
|
169
|
+
sx: {
|
|
170
|
+
mt: 0.75,
|
|
171
|
+
mb: 0.75
|
|
172
|
+
},
|
|
173
|
+
children: jsx(LinearProgress, {
|
|
174
|
+
sx: {
|
|
175
|
+
opacity: isLoading ? 1 : 0
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
}), jsx(Grid, {
|
|
179
|
+
container: true,
|
|
180
|
+
spacing: props.spacing,
|
|
181
|
+
children: memoizedItems
|
|
182
|
+
})]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
ThumbnailListMainContent.defaultProps = {
|
|
186
|
+
spacing: 2,
|
|
187
|
+
muiBreakpoints: {
|
|
188
|
+
xs: 12,
|
|
189
|
+
sm: 6,
|
|
190
|
+
md: 6,
|
|
191
|
+
lg: 4,
|
|
192
|
+
xl: 3
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
183
196
|
/**
|
|
184
197
|
* Generic method that sorts an array of items based on an item key
|
|
185
198
|
* @param values The array that should be sorted
|
|
186
199
|
* @param orderType The key of the entity that the array should be sorted by
|
|
187
200
|
* @returns A new reference of the ordered array
|
|
188
|
-
*/
|
|
189
|
-
function orderByArray(values, orderType) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
function getComparableValue(value) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
function filterByTag(array, tagType, condition) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
};
|
|
242
|
-
|
|
201
|
+
*/
|
|
202
|
+
function orderByArray(values, orderType) {
|
|
203
|
+
return [...values].sort((a, b) => {
|
|
204
|
+
const valueA = getComparableValue(a[orderType]);
|
|
205
|
+
const valueB = getComparableValue(b[orderType]);
|
|
206
|
+
return compareValues(valueA, valueB);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function getComparableValue(value) {
|
|
210
|
+
if (typeof value === 'number') {
|
|
211
|
+
return value;
|
|
212
|
+
} else {
|
|
213
|
+
return String(value);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function compareValues(a, b) {
|
|
217
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
218
|
+
return a.localeCompare(b, undefined, {
|
|
219
|
+
sensitivity: 'base'
|
|
220
|
+
});
|
|
221
|
+
} else {
|
|
222
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function filterByTag(array, tagType, condition) {
|
|
226
|
+
const filteredArray = array.filter(item => {
|
|
227
|
+
const tagValue = item[tagType];
|
|
228
|
+
return condition ? condition(tagValue) : !!tagValue;
|
|
229
|
+
});
|
|
230
|
+
console.log('filter array');
|
|
231
|
+
console.log(filteredArray);
|
|
232
|
+
return [...filteredArray];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const useTagFilteredThumbnailListItems = _ref => {
|
|
236
|
+
let {
|
|
237
|
+
allItems,
|
|
238
|
+
initialTag,
|
|
239
|
+
initialCondition
|
|
240
|
+
} = _ref;
|
|
241
|
+
const [tagAndCondition, setTagAndCondition] = useState({
|
|
242
|
+
tag: initialTag,
|
|
243
|
+
condition: initialCondition
|
|
244
|
+
});
|
|
245
|
+
const setTagWithCondition = (t, c) => {
|
|
246
|
+
setTagAndCondition({
|
|
247
|
+
tag: t,
|
|
248
|
+
condition: c
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const tagFilteredItems = useMemo(() => {
|
|
252
|
+
const tagFiltered = tagAndCondition.tag === 'id' ? allItems : filterByTag(allItems, tagAndCondition.tag, tagAndCondition.condition);
|
|
253
|
+
return tagFiltered;
|
|
254
|
+
}, [allItems, tagAndCondition]);
|
|
255
|
+
return {
|
|
256
|
+
tagAndCondition,
|
|
257
|
+
setTagAndCondition,
|
|
258
|
+
tagFilteredItems,
|
|
259
|
+
setTagWithCondition
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
|
|
243
263
|
/**
|
|
244
264
|
* Filters a list of event by a search term
|
|
245
265
|
* @param allEvents event list that will be formatted
|
|
246
266
|
* @param initialSearchTerm
|
|
247
267
|
* @returns
|
|
248
|
-
*/
|
|
249
|
-
const useFilteredThumbnailListItems = (allItems
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
268
|
+
*/
|
|
269
|
+
const useFilteredThumbnailListItems = function (allItems) {
|
|
270
|
+
let initialSearchTerm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
271
|
+
const [searchTerm, setSearchTerm] = useState(initialSearchTerm);
|
|
272
|
+
const filteredItems = useMemo(() => {
|
|
273
|
+
const filtered = [...allItems].filter(item => item.title.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
274
|
+
return filtered;
|
|
275
|
+
}, [allItems, searchTerm]);
|
|
276
|
+
return {
|
|
277
|
+
searchTerm,
|
|
278
|
+
setSearchTerm,
|
|
279
|
+
filteredItems
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const useSortedThumbnailListItems = (allItems, initialSortBy, initialSortAscending) => {
|
|
284
|
+
const [sortBy, setSortBy] = useState(initialSortBy);
|
|
285
|
+
const [sortAscending, setSortAscending] = useState(initialSortAscending);
|
|
286
|
+
const sortedItems = useMemo(() => {
|
|
287
|
+
let sorted = orderByArray(allItems, sortBy);
|
|
288
|
+
if (!sortAscending) {
|
|
289
|
+
sorted = sorted.reverse();
|
|
290
|
+
}
|
|
291
|
+
return sorted;
|
|
292
|
+
}, [allItems, sortBy, sortAscending]);
|
|
293
|
+
return {
|
|
294
|
+
sortBy,
|
|
295
|
+
sortAscending,
|
|
296
|
+
setSortBy,
|
|
297
|
+
setSortAscending,
|
|
298
|
+
sortedItems
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const defaultConfiguration = {
|
|
303
|
+
sortBy: 'id',
|
|
304
|
+
sortAscending: true,
|
|
305
|
+
tag: 'id'
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const ThumbnailListSearchField = () => {
|
|
309
|
+
const [input, setInput] = useState('');
|
|
310
|
+
const [showClearIcon, setShowClearIcon] = useState('hidden');
|
|
311
|
+
const {
|
|
312
|
+
setSearchTerm
|
|
313
|
+
} = useThumbnailListItemContext();
|
|
314
|
+
console.log('Searchfield rerenders');
|
|
315
|
+
const handleChange = value => {
|
|
316
|
+
setInput(value);
|
|
317
|
+
setShowClearIcon(value === '' ? 'hidden' : '');
|
|
318
|
+
};
|
|
319
|
+
const debouncedSetSearchTerm = useCallback(debounce(setSearchTerm, 50), []);
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
debouncedSetSearchTerm(input);
|
|
322
|
+
return () => {
|
|
323
|
+
debouncedSetSearchTerm.cancel();
|
|
324
|
+
};
|
|
325
|
+
}, [input, debouncedSetSearchTerm]);
|
|
326
|
+
return jsx(Box, {
|
|
327
|
+
sx: {
|
|
328
|
+
marginLeft: 'auto'
|
|
329
|
+
},
|
|
330
|
+
children: jsx(FormControl, {
|
|
331
|
+
children: jsx(TextField, {
|
|
332
|
+
fullWidth: true,
|
|
333
|
+
value: input,
|
|
334
|
+
size: "small",
|
|
335
|
+
variant: "outlined",
|
|
336
|
+
onChange: event => handleChange(event.target.value),
|
|
337
|
+
InputProps: {
|
|
338
|
+
startAdornment: jsx(InputAdornment, {
|
|
339
|
+
position: "start",
|
|
340
|
+
children: jsx(SearchIcon, {})
|
|
341
|
+
}),
|
|
342
|
+
endAdornment: jsx(InputAdornment, {
|
|
343
|
+
position: "end",
|
|
344
|
+
children: jsx(IconButton, {
|
|
345
|
+
onClick: () => handleChange(''),
|
|
346
|
+
sx: {
|
|
347
|
+
visibility: showClearIcon,
|
|
348
|
+
padding: 0
|
|
349
|
+
},
|
|
350
|
+
children: jsx(ClearIcon, {})
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
ThumbnailListSearchField.defaultProps = {
|
|
359
|
+
align: 'start'
|
|
360
|
+
};
|
|
361
|
+
var ThumbnailListSearchField$1 = /*#__PURE__*/React.memo(ThumbnailListSearchField);
|
|
362
|
+
|
|
363
|
+
function ThumbnailListFilterTag(props) {
|
|
364
|
+
var _props$collapseBreakp;
|
|
365
|
+
const theme = useTheme();
|
|
366
|
+
const handleOnClick = value => {
|
|
367
|
+
if (props.onClickCallback) {
|
|
368
|
+
props.onClickCallback(value);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
return jsx(Fragment, {
|
|
372
|
+
children: useMediaQuery(theme.breakpoints.up((_props$collapseBreakp = props.collapseBreakpoint) !== null && _props$collapseBreakp !== void 0 ? _props$collapseBreakp : 0)) || !props.icon ? jsx(Fragment, {
|
|
373
|
+
children: jsx(Chip, {
|
|
374
|
+
label: props.label,
|
|
375
|
+
variant: props.variant,
|
|
376
|
+
onClick: props.onClickCallback ? () => handleOnClick(props.value) : undefined
|
|
377
|
+
})
|
|
378
|
+
}) : jsx(Fragment, {
|
|
379
|
+
children: jsx(Tooltip, {
|
|
380
|
+
title: props.label,
|
|
381
|
+
children: jsx(IconButton, {
|
|
382
|
+
onClick: props.onClickCallback ? () => handleOnClick(props.value) : undefined,
|
|
383
|
+
children: props.icon
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function ThumbnailListFilterTags(props) {
|
|
391
|
+
const {
|
|
392
|
+
tagFilterCallback,
|
|
393
|
+
tagAndCondition
|
|
394
|
+
} = useThumbnailListItemContext();
|
|
395
|
+
console.log('filter tags rerenders');
|
|
396
|
+
return jsx(Fragment, {
|
|
397
|
+
children: props.tags.map((tag, index) => {
|
|
398
|
+
return jsx(ThumbnailListFilterTag, {
|
|
399
|
+
label: tag.label,
|
|
400
|
+
value: tag.key.toString(),
|
|
401
|
+
variant: tagAndCondition.tag === tag.key ? 'filled' : 'outlined',
|
|
402
|
+
collapseBreakpoint: props.muiCollapseBreakpoint,
|
|
403
|
+
onClickCallback: value => tagFilterCallback({
|
|
404
|
+
tag: value,
|
|
405
|
+
condition: tag.condition
|
|
406
|
+
}),
|
|
407
|
+
icon: tag.icon
|
|
408
|
+
}, "".concat(index, "_").concat(tag.key.toString()));
|
|
409
|
+
})
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
ThumbnailListFilterTags.defaultProps = {
|
|
413
|
+
align: 'start',
|
|
414
|
+
muiCollapseBreakpoint: 'md'
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Displays a generic MUI select dropdown.
|
|
419
|
+
* Optinal collapses to a sort icon at a certain breakpoint
|
|
420
|
+
* @param props.label Select Label
|
|
421
|
+
* @param props.width * Width of the input field
|
|
422
|
+
* @param props.collapseBreakPoint * MUI breakpoint after that the select will collapse to the sort icon
|
|
423
|
+
* @param props.onChangeCallback * Callback function that gets triggered once a item is selected
|
|
424
|
+
* @param props.items * Array of items (name-value-pairs) that will be the select options
|
|
425
|
+
* @returns Drowpdown Input Component
|
|
426
|
+
*/
|
|
427
|
+
function DropdownInput(props) {
|
|
428
|
+
var _props$defaultValue, _props$collapseBreakp;
|
|
429
|
+
const [value, setValue] = useState((_props$defaultValue = props.defaultValue) !== null && _props$defaultValue !== void 0 ? _props$defaultValue : '');
|
|
430
|
+
const theme = useTheme();
|
|
431
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
432
|
+
const handleChange = (value, name) => {
|
|
433
|
+
setValue(value);
|
|
434
|
+
setAnchorEl(null);
|
|
435
|
+
props.onChangeCallback(value, name);
|
|
436
|
+
};
|
|
437
|
+
return jsxs(Fragment, {
|
|
438
|
+
children: [useMediaQuery(theme.breakpoints.up((_props$collapseBreakp = props.collapseBreakpoint) !== null && _props$collapseBreakp !== void 0 ? _props$collapseBreakp : 0)) ? jsxs(FormControl, {
|
|
439
|
+
sx: {
|
|
440
|
+
width: props.width,
|
|
441
|
+
textAlign: 'start'
|
|
442
|
+
},
|
|
443
|
+
children: [jsx(InputLabel, {
|
|
444
|
+
size: "small",
|
|
445
|
+
children: props.label
|
|
446
|
+
}), jsx(Select, {
|
|
447
|
+
value: value,
|
|
448
|
+
size: "small",
|
|
449
|
+
label: props.label,
|
|
450
|
+
onChange: event => handleChange(event.target.value, event.target.name),
|
|
451
|
+
children: props.items.map(item => {
|
|
452
|
+
return jsx(MenuItem, {
|
|
453
|
+
value: item.value,
|
|
454
|
+
children: item.name
|
|
455
|
+
}, item.value);
|
|
456
|
+
})
|
|
457
|
+
})]
|
|
458
|
+
}) : jsx(IconButton
|
|
459
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
460
|
+
, {
|
|
461
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
462
|
+
onClick: event => setAnchorEl(event.currentTarget),
|
|
463
|
+
children: props.icon
|
|
464
|
+
}), jsx(Menu, {
|
|
465
|
+
anchorEl: anchorEl,
|
|
466
|
+
open: Boolean(anchorEl),
|
|
467
|
+
onClose: () => setAnchorEl(null),
|
|
468
|
+
children: props.items.map(item => jsx(MenuItem, {
|
|
469
|
+
onClick: () => handleChange(item.value),
|
|
470
|
+
children: item.name
|
|
471
|
+
}, item.value))
|
|
472
|
+
})]
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function ThumbnailListHeaderSort(props) {
|
|
477
|
+
const {
|
|
478
|
+
setSortAscending,
|
|
479
|
+
sortAscending,
|
|
480
|
+
setSortBy,
|
|
481
|
+
sortBy
|
|
482
|
+
} = useThumbnailListItemContext();
|
|
483
|
+
console.log('Header sort rerenders');
|
|
484
|
+
return jsx(Fragment, {
|
|
485
|
+
children: jsxs(Box, {
|
|
486
|
+
sx: {
|
|
487
|
+
minWidth: '80px'
|
|
488
|
+
},
|
|
489
|
+
children: [jsx(Tooltip, {
|
|
490
|
+
title: "asc/desc",
|
|
491
|
+
children: jsx(IconButton, {
|
|
492
|
+
onClick: () => setSortAscending(!sortAscending),
|
|
493
|
+
children: jsx(SwapVertIcon, {})
|
|
494
|
+
})
|
|
495
|
+
}), jsx(DropdownInput, {
|
|
496
|
+
width: "130px",
|
|
497
|
+
collapseBreakpoint: props.muiBreakpoint,
|
|
498
|
+
label: 'sort',
|
|
499
|
+
defaultValue: sortBy,
|
|
500
|
+
icon: jsx(Tooltip, {
|
|
501
|
+
title: 'sort',
|
|
502
|
+
children: jsx(SortIcon, {})
|
|
503
|
+
}),
|
|
504
|
+
items: props.items.map(i => {
|
|
505
|
+
return {
|
|
506
|
+
name: i.label,
|
|
507
|
+
value: i.key.toString()
|
|
508
|
+
};
|
|
509
|
+
}),
|
|
510
|
+
onChangeCallback: value => setSortBy(value)
|
|
511
|
+
})]
|
|
512
|
+
})
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
ThumbnailListHeaderSort.defaultProps = {
|
|
516
|
+
align: 'start',
|
|
517
|
+
muiBreakpoint: 'md'
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const ThumbnailListHeader = function (props) {
|
|
521
|
+
var _props$justifyContent;
|
|
522
|
+
const startAlignedItems = [];
|
|
523
|
+
const endAlignedItems = [];
|
|
524
|
+
// Iterate through each child to categorize them based on their 'align' prop
|
|
525
|
+
Children.forEach(props.children, child => {
|
|
526
|
+
if ( /*#__PURE__*/React.isValidElement(child)) {
|
|
527
|
+
const alignment = child.props.align || 'start';
|
|
528
|
+
if (alignment === 'end') {
|
|
529
|
+
endAlignedItems.push(child);
|
|
530
|
+
} else {
|
|
531
|
+
startAlignedItems.push(child);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
return jsx(Fragment, {
|
|
536
|
+
children: jsxs(Stack$1, {
|
|
537
|
+
direction: "row",
|
|
538
|
+
alignItems: "center",
|
|
539
|
+
justifyContent: (_props$justifyContent = props.justifyContent) !== null && _props$justifyContent !== void 0 ? _props$justifyContent : 'space-between',
|
|
540
|
+
gap: 2,
|
|
541
|
+
children: [jsx(Stack$1, {
|
|
542
|
+
direction: "row",
|
|
543
|
+
alignItems: "center",
|
|
544
|
+
gap: 2,
|
|
545
|
+
justifyContent: "start",
|
|
546
|
+
children: startAlignedItems
|
|
547
|
+
}), endAlignedItems]
|
|
548
|
+
})
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
ThumbnailListHeader.SearchField = ThumbnailListSearchField$1;
|
|
552
|
+
ThumbnailListHeader.FilterTags = ThumbnailListFilterTags;
|
|
553
|
+
ThumbnailListHeader.Sort = ThumbnailListHeaderSort;
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Main Component: Renders all sub components
|
|
557
|
+
* Includes ThumbnailList Provider for context data
|
|
558
|
+
* @param props react children, items
|
|
559
|
+
* @returns component
|
|
560
|
+
*/
|
|
561
|
+
function ThumbnailList(props) {
|
|
562
|
+
const combinedConfig = {
|
|
563
|
+
...defaultConfiguration,
|
|
564
|
+
...props.config // This will override the defaults with any props that are not undefined
|
|
565
|
+
};
|
|
566
|
+
const [listItems, setListItems] = useState(props.items);
|
|
567
|
+
const {
|
|
568
|
+
sortedItems,
|
|
569
|
+
setSortBy,
|
|
570
|
+
setSortAscending,
|
|
571
|
+
sortAscending
|
|
572
|
+
} = useSortedThumbnailListItems(listItems, combinedConfig.sortBy.toString(), combinedConfig.sortAscending);
|
|
573
|
+
const {
|
|
574
|
+
tagFilteredItems,
|
|
575
|
+
setTagAndCondition,
|
|
576
|
+
tagAndCondition
|
|
577
|
+
} = useTagFilteredThumbnailListItems({
|
|
578
|
+
allItems: sortedItems,
|
|
579
|
+
initialTag: combinedConfig.tag.toString()
|
|
580
|
+
});
|
|
581
|
+
const {
|
|
582
|
+
setSearchTerm,
|
|
583
|
+
filteredItems
|
|
584
|
+
} = useFilteredThumbnailListItems(tagFilteredItems);
|
|
585
|
+
console.log('Thumbnaillist renders');
|
|
586
|
+
return jsx(Fragment, {
|
|
587
|
+
children: jsx(ThumbnailListItemContext.Provider, {
|
|
588
|
+
value: {
|
|
589
|
+
items: filteredItems,
|
|
590
|
+
setItems: setListItems,
|
|
591
|
+
originalItems: listItems,
|
|
592
|
+
setOriginalItems: setListItems,
|
|
593
|
+
tagFilterCallback: setTagAndCondition,
|
|
594
|
+
tagAndCondition: tagAndCondition,
|
|
595
|
+
setSearchTerm: setSearchTerm,
|
|
596
|
+
setSortAscending: setSortAscending,
|
|
597
|
+
sortAscending: sortAscending,
|
|
598
|
+
setSortBy: setSortBy,
|
|
599
|
+
sortBy: combinedConfig.sortBy.toString(),
|
|
600
|
+
isLoading: false
|
|
601
|
+
},
|
|
602
|
+
children: jsx(Stack$1, {
|
|
603
|
+
direction: "column",
|
|
604
|
+
sx: {
|
|
605
|
+
width: '100%',
|
|
606
|
+
minWidth: '425px'
|
|
607
|
+
},
|
|
608
|
+
children: props.children
|
|
609
|
+
})
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
ThumbnailList.MainContent = ThumbnailListMainContent;
|
|
614
|
+
ThumbnailList.Header = ThumbnailListHeader;
|
|
615
|
+
|
|
616
|
+
export { ThumbnailList };
|