@orangelogic/orange-dam-content-browser-sdk 2.1.21 → 2.1.23

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.
@@ -1,63 +1,63 @@
1
1
  import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
 
3
3
  import { SortOrder } from '@/types/assets';
4
- import { Filter, GridView, SortDirection } from '@/types/search';
4
+ import { Facet as FacetType, GridView, SortDirection } from '@/types/search';
5
5
  import type {
6
- CxChangeEvent, CxDropdown, CxInput, CxRemoveEvent, CxSelectEvent, CxSelectionChangeEvent,
6
+ CxChangeEvent,
7
+ CxDropdown,
8
+ CxInput,
9
+ CxRemoveEvent,
10
+ CxSelectEvent,
11
+ CxSelectionChangeEvent,
7
12
  } from '@orangelogic-private/design-system';
8
13
 
9
14
  import { sortDirections, views } from './ControlBar.constants';
10
15
  import { Container } from './ControlBar.styled';
11
16
  import Facet from './Facet';
12
17
 
13
- const TYPE = {
14
- type: 'type',
15
- visibilityClass: 'visibilityClass',
16
- status: 'status',
17
- extension: 'extension',
18
- };
19
-
20
18
  type Props = {
21
19
  allowSorting: boolean;
20
+ availableFacets: FacetType['facetDetails'][];
22
21
  currentCount: number;
23
22
  loading: boolean;
24
- extensions: string[];
25
- facets?: Record<string, Record<string, number>>;
23
+ facets?: FacetType[];
26
24
  isMobile: boolean;
27
25
  isSeeThrough: boolean;
28
- mediaTypes: string[];
29
26
  searchValue: string;
27
+ selectedFacets: Record<string, string[]>;
30
28
  sortDirection?: 'ascending' | 'descending';
31
29
  sortOrder: string;
32
30
  sortOrders?: Record<string, SortOrder[]>;
33
- statuses: string[];
34
31
  totalCount: number;
35
32
  view: GridView;
36
- visibilityClasses: string[];
37
33
  onSearchChange: (value: string) => void;
38
34
  onSettingChange: (
39
35
  setting: string,
40
- value: GridView | SortDirection | Filter | string | boolean | string[]
36
+ value:
37
+ | GridView
38
+ | SortDirection
39
+ | Record<string, string[]>
40
+ | string
41
+ | boolean
42
+ | string[]
41
43
  ) => void;
42
44
  };
43
45
 
44
46
  const ControlBar: FC<Props> = ({
45
47
  allowSorting,
48
+ availableFacets,
46
49
  currentCount,
47
50
  loading,
48
- extensions,
49
51
  facets,
50
52
  isMobile,
51
53
  isSeeThrough,
52
- mediaTypes,
53
54
  searchValue: searchText,
55
+ selectedFacets,
54
56
  sortDirection,
55
57
  sortOrder,
56
58
  sortOrders,
57
- statuses,
58
59
  totalCount,
59
60
  view,
60
- visibilityClasses,
61
61
  onSearchChange,
62
62
  onSettingChange,
63
63
  }) => {
@@ -119,41 +119,25 @@ const ControlBar: FC<Props> = ({
119
119
  const type = target.dataset.type;
120
120
  const value = target.dataset.value;
121
121
 
122
- if (!value) {
122
+ if (!value || !type) {
123
123
  return;
124
124
  }
125
125
 
126
- let newMediaTypes = mediaTypes;
127
- let newVisibilityClasses = visibilityClasses;
128
- let newStatuses = statuses;
129
- let newExtensions = extensions;
130
-
131
- switch (type) {
132
- case TYPE.type:
133
- newMediaTypes = mediaTypes.filter((item) => item !== value);
134
- break;
135
- case TYPE.visibilityClass:
136
- newVisibilityClasses = visibilityClasses.filter((item) => item !== value);
137
- break;
138
- case TYPE.status:
139
- newStatuses = statuses.filter((item) => item !== value);
140
- break;
141
- case TYPE.extension:
142
- newExtensions = extensions.filter((item) => item !== value);
143
- break;
144
- default:
145
- break;
126
+ const newFilter = { ...selectedFacets };
127
+
128
+ if (newFilter[type]) {
129
+ newFilter[type] = newFilter[type].filter((item) => item !== value);
146
130
  }
147
131
 
148
- onSettingChange('filter', {
149
- mediaTypes: newMediaTypes,
150
- visibilityClasses: newVisibilityClasses,
151
- statuses: newStatuses,
152
- extensions: newExtensions,
153
- });
132
+ onSettingChange('filter', newFilter);
154
133
  };
155
134
  const onFilterSelectionChange = (e: CxSelectionChangeEvent) => {
156
135
  const facet = (e.target as HTMLElement).dataset.facet;
136
+
137
+ if (!facet) {
138
+ return;
139
+ }
140
+
157
141
  setNewlyChangedOption({
158
142
  type: 'filter',
159
143
  value: facet,
@@ -163,34 +147,24 @@ const ControlBar: FC<Props> = ({
163
147
  (acc, item) => {
164
148
  const type = item.dataset.type;
165
149
  const value = item.dataset.value;
166
-
167
- if (!value) {
150
+
151
+ if (!value || !type) {
168
152
  return acc;
169
153
  }
170
154
 
171
- switch (type) {
172
- case TYPE.type:
173
- acc.mediaTypes.push(value);
174
- break;
175
- case TYPE.visibilityClass:
176
- acc.visibilityClasses.push(value);
177
- break;
178
- case TYPE.status:
179
- acc.statuses.push(value);
180
- break;
181
- case TYPE.extension:
182
- acc.extensions.push(value);
183
- break;
184
- default:
185
- break;
155
+ const copiedAcc = { ...acc };
156
+
157
+ if (!selectedFacets[type]) {
158
+ copiedAcc[type] = [];
186
159
  }
187
160
 
188
- return acc;
189
- }, {
190
- extensions: facet === TYPE.extension ? [] as string[] : extensions,
191
- mediaTypes: facet === TYPE.type ? [] as string[] : mediaTypes,
192
- visibilityClasses: facet === TYPE.visibilityClass ? [] as string[] : visibilityClasses,
193
- statuses: facet === TYPE.status ? [] as string[] : statuses,
161
+ copiedAcc[type].push(value);
162
+
163
+ return copiedAcc;
164
+ },
165
+ {
166
+ ...selectedFacets,
167
+ [facet]: [] as string[],
194
168
  },
195
169
  );
196
170
 
@@ -220,33 +194,56 @@ const ControlBar: FC<Props> = ({
220
194
  };
221
195
  const filterDropdown = filterDropdownRef.current;
222
196
  const sortDropdown = sortDropdownRef.current;
223
- filterDropdown?.addEventListener('cx-selection-change', onFilterSelectionChange);
197
+ filterDropdown?.addEventListener(
198
+ 'cx-selection-change',
199
+ onFilterSelectionChange,
200
+ );
224
201
  filterDropdown?.addEventListener('cx-remove', onFilterRemove);
225
202
  sortDropdown?.addEventListener('cx-select', onSortSelect);
226
203
  return () => {
227
- filterDropdown?.removeEventListener('cx-selection-change', onFilterSelectionChange);
204
+ filterDropdown?.removeEventListener(
205
+ 'cx-selection-change',
206
+ onFilterSelectionChange,
207
+ );
228
208
  filterDropdown?.removeEventListener('cx-remove', onFilterRemove);
229
209
  sortDropdown?.removeEventListener('cx-select', onSortSelect);
230
210
  };
231
- }, [isDefined, extensions, mediaTypes, statuses, visibilityClasses, onSettingChange]);
211
+ }, [isDefined, selectedFacets, onSettingChange]);
232
212
 
233
- const selectedView = useMemo(() => views.find((item) => item.value === view), [view]);
213
+ const selectedView = useMemo(
214
+ () => views.find((item) => item.value === view),
215
+ [view],
216
+ );
234
217
 
235
218
  const appliedFilersCount = useMemo(() => {
236
- return mediaTypes.length + visibilityClasses.length + statuses.length + extensions.length;
237
- }, [
238
- mediaTypes.length,
239
- visibilityClasses.length,
240
- statuses.length,
241
- extensions.length,
242
- ]);
219
+ return Object.values(selectedFacets).reduce((acc, values) => {
220
+ return acc + values.length;
221
+ }, 0);
222
+ }, [selectedFacets]);
223
+
224
+ const mappedDisplayNames = useMemo(() => {
225
+ return facets?.reduce((acc, facet) => {
226
+ const displayNames = facet.values.reduce((displayNamesAcc, { value, displayValue }) => {
227
+ displayNamesAcc[value] = displayValue;
228
+
229
+ return displayNamesAcc;
230
+ }, {} as Record<string, string>);
231
+
232
+ return {
233
+ ...acc,
234
+ [facet.facetDetails.facetFieldName]: displayNames,
235
+ };
236
+ }, {} as Record<string, Record<string, string>>) ?? {};
237
+ }, [facets]);
243
238
 
244
239
  const renderAppliedFilters = useCallback(() => {
245
240
  return (
246
241
  <cx-details
247
242
  data-cy="applied-filters"
248
243
  open
249
- className={`filter-details ${appliedFilersCount === 0 ? 'filter-details--empty' : ''}`.trim()}
244
+ className={`filter-details ${
245
+ appliedFilersCount === 0 ? 'filter-details--empty' : ''
246
+ }`.trim()}
250
247
  >
251
248
  <cx-space
252
249
  slot="summary"
@@ -254,59 +251,40 @@ const ControlBar: FC<Props> = ({
254
251
  spacing="x-small"
255
252
  wrap="nowrap"
256
253
  >
257
- <span>Applied filters {appliedFilersCount > 0 ? ` (${appliedFilersCount})` : ''}</span>
258
- {loading && newlyChangedOption.type === 'filter' && <cx-spinner></cx-spinner>}
254
+ <span>
255
+ Applied filters{' '}
256
+ {appliedFilersCount > 0 ? ` (${appliedFilersCount})` : ''}
257
+ </span>
258
+ {loading && newlyChangedOption.type === 'filter' && (
259
+ <cx-spinner></cx-spinner>
260
+ )}
259
261
  </cx-space>
260
262
  <cx-space direction="horizontal" spacing="small">
261
- {mediaTypes.map((item) => (
262
- <cx-tag
263
- key={item}
264
- removable
265
- data-value={item}
266
- data-type={TYPE.type}
267
- size='small'
268
- >
269
- {item.toLowerCase()}
270
- </cx-tag>
271
- ))}
272
- {visibilityClasses.map((item) => (
273
- <cx-tag
274
- key={item}
275
- removable
276
- data-value={item}
277
- data-type={TYPE.visibilityClass}
278
- size='small'
279
- >
280
- {item.toLowerCase()}
281
- </cx-tag>
282
- ))}
283
- {statuses.map((item) => (
284
- <cx-tag
285
- key={item}
286
- removable
287
- data-value={item}
288
- data-type={TYPE.status}
289
- size='small'
290
- >
291
- {item.toLowerCase()}
292
- </cx-tag>
293
- ))}
294
- {extensions.map((item) => (
295
- <cx-tag
296
- key={item}
297
- removable
298
- data-value={item}
299
- data-type={TYPE.extension}
300
- size='small'
301
- >
302
- {item.toLowerCase()}
303
- </cx-tag>
304
- ))}
263
+ {Object.entries(selectedFacets).map(([key, values]) => {
264
+ if (!values || values.length === 0) {
265
+ return null;
266
+ }
267
+
268
+ return values.map((value) => {
269
+ return (
270
+ <cx-tag
271
+ key={key}
272
+ removable
273
+ data-value={value}
274
+ data-type={key}
275
+ size="small"
276
+ >
277
+ {mappedDisplayNames[key][value]}
278
+ <cx-icon slot="suffix" name="close"></cx-icon>
279
+ </cx-tag>
280
+ );
281
+ });
282
+ })}
305
283
  </cx-space>
306
284
  {appliedFilersCount > 0 && (
307
- <cx-button
285
+ <cx-button
308
286
  variant="text"
309
- className='clear-all-button'
287
+ className="clear-all-button"
310
288
  onClick={() => {
311
289
  onSettingChange('filter', {
312
290
  mediaTypes: [],
@@ -322,16 +300,7 @@ const ControlBar: FC<Props> = ({
322
300
  )}
323
301
  </cx-details>
324
302
  );
325
- }, [
326
- appliedFilersCount,
327
- extensions,
328
- loading,
329
- mediaTypes,
330
- newlyChangedOption.type,
331
- statuses,
332
- visibilityClasses,
333
- onSettingChange,
334
- ]);
303
+ }, [appliedFilersCount, loading, mappedDisplayNames, newlyChangedOption.type, onSettingChange, selectedFacets]);
335
304
 
336
305
  return (
337
306
  <Container>
@@ -365,46 +334,42 @@ const ControlBar: FC<Props> = ({
365
334
  outline
366
335
  data-cy="filter-button"
367
336
  >
368
- {
369
- appliedFilersCount > 0 && (
370
- <cx-badge slot="badge" pill size="small">
371
- {appliedFilersCount}
372
- </cx-badge>
373
- )
374
- }
337
+ {appliedFilersCount > 0 && (
338
+ <cx-badge slot="badge" pill size="small">
339
+ {appliedFilersCount}
340
+ </cx-badge>
341
+ )}
375
342
  </cx-icon-button>
376
343
  </cx-tooltip>
377
344
  </div>
378
345
  {renderAppliedFilters()}
379
- <Facet
380
- key={TYPE.type}
381
- facet={facets?.type ?? {}}
382
- displayName="Type"
383
- type={TYPE.type}
384
- collections={mediaTypes}
385
- />
386
- <Facet
387
- key={TYPE.visibilityClass}
388
- facet={facets?.visibilityClass ?? {}}
389
- displayName="Visibility class"
390
- type={TYPE.visibilityClass}
391
- collections={visibilityClasses}
392
- />
393
- <Facet
394
- key={TYPE.status}
395
- facet={facets?.status ?? {}}
396
- displayName="Status"
397
- type={TYPE.status}
398
- collections={statuses}
399
- />
400
- <Facet
401
- key={TYPE.extension}
402
- facet={facets?.extension ?? {}}
403
- type={TYPE.extension}
404
- displayName="Extension"
405
- collections={extensions}
406
- capitalize={false}
407
- />
346
+ {availableFacets.length > 0 && (
347
+ <cx-space direction="vertical" className="filter-details">
348
+ {availableFacets.map((availableFacet) => {
349
+ const facet = facets?.find(
350
+ (item) =>
351
+ item.facetDetails.facetFieldName ===
352
+ availableFacet.facetFieldName,
353
+ );
354
+
355
+ if (!facet) {
356
+ return null;
357
+ }
358
+
359
+ return (
360
+ <Facet
361
+ key={facet.facetDetails.facetFieldName}
362
+ values={facet.values}
363
+ displayName={facet.facetDetails.displayName}
364
+ type={facet.facetDetails.facetFieldName}
365
+ collections={
366
+ selectedFacets[facet.facetDetails.facetFieldName] || []
367
+ }
368
+ />
369
+ );
370
+ })}
371
+ </cx-space>
372
+ )}
408
373
  </cx-dropdown>
409
374
  </cx-space>
410
375
  <cx-space
@@ -475,41 +440,49 @@ const ControlBar: FC<Props> = ({
475
440
  >
476
441
  <div slot="trigger">
477
442
  <cx-tooltip content="Sort">
478
- <cx-icon-button name="sort" label="Sort" outline data-cy="sort-button"></cx-icon-button>
443
+ <cx-icon-button
444
+ name="sort"
445
+ label="Sort"
446
+ outline
447
+ data-cy="sort-button"
448
+ ></cx-icon-button>
479
449
  </cx-tooltip>
480
450
  </div>
481
451
  <cx-menu>
482
- {(sortOrders ? sortDirections
483
- .map(
484
- (item) => {
485
- const label = sortOrders[sortOrder]?.find(
486
- (sort) => sort.sortDirection.toLowerCase() === item.value.toLowerCase(),
487
- )?.sortDirectionDisplayName;
488
-
489
- return {
490
- ...item,
491
- label: label ?? item.value,
492
- };
493
- },
494
- ) : sortDirections)
495
- .map((item) => (
496
- <cx-menu-item
497
- key={item.value}
498
- data-type="sort-direction"
499
- disabled={!allowSorting}
500
- value={item.value}
501
- class={allowSorting && sortDirection === item.value ? 'selected' : ''}
502
- >
503
- {item.label.replace(/\b\w/g, (char) => char.toUpperCase())}
504
- {loading &&
505
- newlyChangedOption.type === 'sortDirection' &&
506
- newlyChangedOption.value === item.value ? (
507
- <cx-spinner slot="prefix"></cx-spinner>
508
- ) : (
509
- <item.icon></item.icon>
510
- )}
511
- </cx-menu-item>
512
- ))}
452
+ {(sortOrders
453
+ ? sortDirections.map((item) => {
454
+ const label = sortOrders[sortOrder]?.find(
455
+ (sort) =>
456
+ sort.sortDirection.toLowerCase() ===
457
+ item.value.toLowerCase(),
458
+ )?.sortDirectionDisplayName;
459
+
460
+ return {
461
+ ...item,
462
+ label: label ?? item.value,
463
+ };
464
+ })
465
+ : sortDirections
466
+ ).map((item) => (
467
+ <cx-menu-item
468
+ key={item.value}
469
+ data-type="sort-direction"
470
+ disabled={!allowSorting}
471
+ value={item.value}
472
+ class={
473
+ allowSorting && sortDirection === item.value ? 'selected' : ''
474
+ }
475
+ >
476
+ {item.label.replace(/\b\w/g, (char) => char.toUpperCase())}
477
+ {loading &&
478
+ newlyChangedOption.type === 'sortDirection' &&
479
+ newlyChangedOption.value === item.value ? (
480
+ <cx-spinner slot="prefix"></cx-spinner>
481
+ ) : (
482
+ <item.icon></item.icon>
483
+ )}
484
+ </cx-menu-item>
485
+ ))}
513
486
  <cx-divider></cx-divider>
514
487
  {Object.keys(sortOrders ?? {}).map((item) => (
515
488
  <cx-menu-item