@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.esm.js CHANGED
@@ -1,307 +1,616 @@
1
- import { Button, styled, Box, Typography, Stack as Stack$1, Card, CardActionArea, Grid, FormControl, TextField, InputAdornment, IconButton, useTheme, useMediaQuery, Chip, Tooltip, InputLabel, Select, MenuItem, Menu } from '@mui/material';
2
- import React, { createContext, useContext, Children, useState, useEffect } from 'react';
3
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
- import { Stack } from '@mui/system';
5
- import SearchIcon from '@mui/icons-material/Search';
6
- import ClearIcon from '@mui/icons-material/Clear';
7
- import SwapVertIcon from '@mui/icons-material/SwapVert';
8
- import SortIcon from '@mui/icons-material/Sort';
9
-
10
- function HelloWorld() {
11
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h1", null, "Hello World"), /*#__PURE__*/React.createElement(Button, {
12
- variant: "contained"
13
- }, "Contained"));
14
- }
15
-
16
- /* eslint-disable @typescript-eslint/no-explicit-any */
17
- const ThumbnailListItemContext = createContext(undefined);
18
- // Create a custom hook to consume the context
19
- const useThumbnailListItemContext = () => {
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
- const EllipsisContainer = styled('div')((p) => ({
58
- [p.theme.breakpoints.up('xs')]: {
59
- overflow: 'hidden',
60
- display: '-webkit-box',
61
- WebkitLineClamp: props.lineClamp.xs.toString(),
62
- WebkitBoxOrient: 'vertical',
63
- },
64
- [p.theme.breakpoints.up('sm')]: {
65
- overflow: 'hidden',
66
- display: '-webkit-box',
67
- WebkitLineClamp: props.lineClamp.sm.toString(),
68
- WebkitBoxOrient: 'vertical', /* number of lines to show */
69
- },
70
- }));
71
- return (jsx(EllipsisContainer, { children: props.children }));
72
- }
73
-
74
- function ThumbnailListItemTitle(props) {
75
- const StyledCardContent = styled('div')((props) => ({
76
- [props.theme.breakpoints.up('xs')]: {
77
- 'padding': props.theme.spacing(1),
78
- 'flex': '1 0 auto',
79
- '&:last-child': { paddingBottom: 0 },
80
- 'overflow': 'hidden',
81
- },
82
- }));
83
- const children = Children.toArray(props.children);
84
- return jsx(Fragment, { children: jsx(Box, { children: jsxs(StyledCardContent, { children: [jsx(EllipsisContainer, { lineClamp: { xs: 1, sm: 2 }, children: jsx(Typography, { variant: 'h4', children: props.title }) }), jsx(Stack, { direction: "row", gap: 1, children: children.map((child, index) => jsx(Typography, { variant: "subtitle2", color: "text.secondary", children: child }, index)) })] }) }) });
85
- }
86
-
87
- function ThumbnailListItemInfoLabel(props) {
88
- return jsx(Fragment, { children: jsxs(Stack$1, { textAlign: "right", justifyContent: "space-between", children: [jsx(Box, { textAlign: "right", padding: 1, children: props.topContent }), jsx(Box, { textAlign: "right", padding: 1, children: props.bottomContent })] }) });
89
- }
90
-
91
- // import {Link} from 'react-router-dom';
92
- const ThumbnailListItem = (props) => {
93
- return jsx(Fragment, { children: jsx(Card, { sx: { display: 'flex' }, children: jsx(CardActionArea, { children: jsxs(Stack$1, { direction: "row", width: "100%", children: [jsx(ImageCropper, { width: { xs: '98px', sm: '160px' }, height: { xs: '54px', sm: '90px' }, children: jsx(Box, { children: "Placeholder for image" }) }), jsx(Stack$1, { direction: "row", justifyContent: "space-between", width: "100%", gap: 1, children: props.children })] }) }) }) });
94
- };
95
- ThumbnailListItem.Title = ThumbnailListItemTitle;
96
- ThumbnailListItem.InfoLabel = ThumbnailListItemInfoLabel;
97
-
98
- function ThumbnailListMainContent( /* props: {children: ReactNode}*/) {
99
- // const children = Children.toArray(props.children);
100
- const { items } = useThumbnailListItemContext();
101
- return (jsx(Fragment, { children: items.map((item) => {
102
- return (jsx(Grid, { item: true, xs: 12, lg: 6, xl: 3, children: jsxs(ThumbnailListItem, { id: item.id,
103
- // link={item.link}
104
- thumbnailUrl: item.thumbnailUrl, children: [jsx(ThumbnailListItem.Title, { title: item.title, children: item.subTitle }), item.label] }) }, item.id));
105
- }) }));
106
- }
107
-
108
- const ThumbnailListSearchField = () => {
109
- const [input, setInput] = useState('');
110
- const [showClearIcon, setShowClearIcon] = useState('hidden');
111
- const { setSearchTerm } = useThumbnailListItemContext();
112
- const handleChange = (value) => {
113
- setInput(value);
114
- setShowClearIcon(value === '' ? 'hidden' : '');
115
- setSearchTerm(value);
116
- };
117
- return (jsx(Fragment, { children: jsx(FormControl, { children: jsx(TextField, { sx: { input: { color: 'white' } }, fullWidth: true, value: input, size: "small", variant: "outlined", onChange: (event) => handleChange(event.target.value), InputProps: {
118
- startAdornment: (jsx(InputAdornment, { position: "start", children: jsx(SearchIcon, {}) })),
119
- endAdornment: (jsx(InputAdornment, { position: "end", children: jsx(IconButton, { onClick: () => handleChange(''), sx: { visibility: showClearIcon, padding: 0 }, children: jsx(ClearIcon, {}) }) })),
120
- } }) }) }));
121
- };
122
-
123
- function ThumbnailListFilterTag(props) {
124
- const theme = useTheme();
125
- const handleOnClick = (value) => {
126
- if (props.onClickCallback) {
127
- props.onClickCallback(value);
128
- }
129
- };
130
- return (jsx(Fragment, { children: useMediaQuery(theme.breakpoints.up(props.collapseBreakpoint ?? 0)) || !props.icon ?
131
- jsx(Fragment, { children: jsx(Chip, { label: props.label, variant: props.variant, onClick: props.onClickCallback ? () => handleOnClick(props.value) : undefined }) }) :
132
- jsx(Fragment, { children: jsx(Tooltip, { title: props.label, children: jsx(IconButton, { onClick: props.onClickCallback ? () => handleOnClick(props.value) : undefined, children: props.icon }) }) }) }));
133
- }
134
-
135
- function ThumbnailListFilterTags(props) {
136
- const { tagFilterCallback, tagAndCondition } = useThumbnailListItemContext();
137
- return (jsx(Fragment, { children: props.tags.map((tag) => {
138
- return (jsx(ThumbnailListFilterTag, { label: tag.label, value: tag.value, variant: tagAndCondition.tag === tag.value ? 'filled' : 'outlined', collapseBreakpoint: props.collapseBreakpoint, onClickCallback: (value) => tagFilterCallback({ tag: value, condition: tag.condition }), icon: tag.icon }));
139
- }) }));
140
- }
141
-
142
- /**
143
- * Displays a generic MUI select dropdown.
144
- * Optinal collapses to a sort icon at a certain breakpoint
145
- * @param props.label Select Label
146
- * @param props.width * Width of the input field
147
- * @param props.collapseBreakPoint * MUI breakpoint after that the select will collapse to the sort icon
148
- * @param props.onChangeCallback * Callback function that gets triggered once a item is selected
149
- * @param props.items * Array of items (name-value-pairs) that will be the select options
150
- * @returns Drowpdown Input Component
151
- */
152
- function DropdownInput(props) {
153
- const [value, setValue] = useState(props.defaultValue ?? '');
154
- const theme = useTheme();
155
- const [anchorEl, setAnchorEl] = useState(null);
156
- const handleChange = (value, name) => {
157
- setValue(value);
158
- setAnchorEl(null);
159
- props.onChangeCallback(value, name);
160
- };
161
- return (jsxs(Fragment, { children: [useMediaQuery(theme.breakpoints.up(props.collapseBreakpoint ?? 0)) ?
162
- jsxs(FormControl, { sx: { width: props.width, textAlign: 'start' }, children: [jsx(InputLabel, { size: 'small', id: "demo-simple-select-label", children: props.label }), jsx(Select, { value: value, size: 'small', label: props.label, onChange: (event) => handleChange(event.target.value, event.target.name), children: props.items.map((item) => {
163
- return jsx(MenuItem, { value: item.value, children: item.name }, item.value);
164
- }) })] }) : (jsx(IconButton
165
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
- , {
167
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
- onClick: (event) => setAnchorEl(event.currentTarget), children: props.icon })), jsx(Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => setAnchorEl(null), children: props.items.map((item) => (jsx(MenuItem, { onClick: () => handleChange(item.value), children: item.name }, item.value))) })] }));
169
- }
170
-
171
- function ThumbnailListHeaderSort(props) {
172
- const { setSortAscending, sortAscending, setSortBy } = useThumbnailListItemContext();
173
- return (jsx(Fragment, { children: jsxs(Box, { sx: { width: '-webkit-fill-available', textAlign: 'end' }, children: [jsx(Tooltip, { title: "asc/desc", children: jsx(IconButton, { onClick: () => setSortAscending(!sortAscending), children: jsx(SwapVertIcon, {}) }) }), jsx(DropdownInput, { width: "130px", collapseBreakpoint: 'md', label: 'sort', defaultValue: "creationTimeStamp", icon: jsx(Tooltip, { title: 'sort', children: jsx(SortIcon, {}) }), items: props.items, onChangeCallback: (value) => setSortBy(value) })] }) }));
174
- }
175
-
176
- const ThumbnailListHeader = function (props) {
177
- return (jsx(Fragment, { children: jsx(Grid, { item: true, xs: 12, children: jsx(Stack$1, { direction: "row", alignItems: "center", justifyContent: props.justifyContent ?? 'space-between', gap: 2, children: props.children }) }) }));
178
- };
179
- ThumbnailListHeader.SearchField = ThumbnailListSearchField;
180
- ThumbnailListHeader.FilterTags = ThumbnailListFilterTags;
181
- ThumbnailListHeader.Sort = ThumbnailListHeaderSort;
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
- return [...values].sort((a, b) => {
191
- const valueA = getComparableValue(a[orderType]);
192
- const valueB = getComparableValue(b[orderType]);
193
- return compareValues(valueA, valueB);
194
- });
195
- }
196
- function getComparableValue(value) {
197
- if (typeof value === 'number') {
198
- return value;
199
- }
200
- else {
201
- return String(value);
202
- }
203
- }
204
- function compareValues(a, b) {
205
- if (typeof a === 'string' && typeof b === 'string') {
206
- return a.localeCompare(b, undefined, { sensitivity: 'base' });
207
- }
208
- else {
209
- return a < b ? -1 : a > b ? 1 : 0;
210
- }
211
- }
212
- function filterByTag(array, tagType, condition) {
213
- const filteredArray = array.filter((item) => {
214
- const tagValue = item[tagType];
215
- return condition ? condition(tagValue) : !!tagValue;
216
- });
217
- console.log('filter array');
218
- console.log(filteredArray);
219
- return [...filteredArray];
220
- }
221
-
222
- // /* eslint-disable @typescript-eslint/no-explicit-any */
223
- const useTagFilteredThumbnailListItems = ({ allItems, initialTag, initialCondition }) => {
224
- const [tagAndCondition, setTagAndCondition] = useState({ tag: initialTag, condition: initialCondition });
225
- const [tagFilteredItems, setTagFilteredItems] = useState(allItems);
226
- const setTagWithCondition = (t, c) => {
227
- setTagAndCondition({ tag: t, condition: c });
228
- };
229
- useEffect(() => {
230
- const tagFiltered = tagAndCondition.tag === 'id' ?
231
- allItems :
232
- filterByTag(allItems, tagAndCondition.tag, tagAndCondition.condition);
233
- setTagFilteredItems(tagFiltered);
234
- }, [allItems, tagAndCondition]);
235
- return {
236
- tagAndCondition,
237
- setTagAndCondition,
238
- tagFilteredItems,
239
- setTagWithCondition,
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, initialSearchTerm = '') => {
250
- const [searchTerm, setSearchTerm] = useState(initialSearchTerm);
251
- const [filteredItems, setFilteredItems] = useState(allItems);
252
- useEffect(() => {
253
- const filterEvents = () => {
254
- const filtered = [...allItems].filter((item) => item.title.toLowerCase().includes(searchTerm.toLowerCase()));
255
- setFilteredItems(filtered);
256
- };
257
- filterEvents();
258
- }, [allItems, searchTerm]);
259
- return { searchTerm, setSearchTerm, filteredItems };
260
- };
261
-
262
- const useSortedThumbnailListItems = (allItems, initialSortBy, initialSortAscending) => {
263
- const [sortBy, setSortBy] = useState(initialSortBy);
264
- const [sortAscending, setSortAscending] = useState(initialSortAscending);
265
- const [sortedItems, setSortedItems] = useState(allItems);
266
- useEffect(() => {
267
- let sorted = orderByArray(allItems, sortBy);
268
- if (!sortAscending) {
269
- sorted = sorted.reverse();
270
- }
271
- setSortedItems(sorted);
272
- }, [allItems, sortBy, sortAscending, initialSortBy, initialSortAscending]);
273
- return { sortBy, sortAscending, setSortBy, setSortAscending, sortedItems };
274
- };
275
-
276
- const ThumbnailList = function (props) {
277
- // const [originalItems, setOriginalItems] = useState(props.items);
278
- const [listItems, setListItems] = useState(props.items);
279
- const { sortedItems, setSortBy, setSortAscending, sortAscending } = useSortedThumbnailListItems(listItems, 'creationTimeStamp', false);
280
- const { setSearchTerm, filteredItems } = useFilteredThumbnailListItems(sortedItems);
281
- const { tagFilteredItems, setTagAndCondition, tagAndCondition } = useTagFilteredThumbnailListItems({ allItems: filteredItems, initialTag: 'id' });
282
- console.log('tag filterd');
283
- console.log(tagFilteredItems);
284
- console.log(listItems);
285
- useEffect(() => {
286
- if (props.items) {
287
- // setOriginalItems(props.items);
288
- setListItems(props.items);
289
- }
290
- }, [props.items]);
291
- return (jsx(Fragment, { children: jsx(ThumbnailListItemContext.Provider, { value: {
292
- items: tagFilteredItems,
293
- setItems: setListItems,
294
- originalItems: listItems,
295
- setOriginalItems: setListItems,
296
- tagFilterCallback: setTagAndCondition,
297
- tagAndCondition: tagAndCondition,
298
- setSearchTerm: setSearchTerm,
299
- setSortAscending: setSortAscending,
300
- sortAscending: sortAscending,
301
- setSortBy: setSortBy,
302
- }, children: jsx(Grid, { container: true, spacing: 2, children: props.children }) }) }));
303
- };
304
- ThumbnailList.MainContent = ThumbnailListMainContent;
305
- ThumbnailList.Header = ThumbnailListHeader;
306
-
307
- export { HelloWorld, ThumbnailList };
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 };