@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.
- package/CBSDKdemo.html +2 -2
- package/gab_extension/GAB.html +2 -2
- package/package.json +1 -1
- package/src/components/ControlBar/ControlBar.cy.tsx +55 -27
- package/src/components/ControlBar/ControlBar.tsx +180 -207
- package/src/components/ControlBar/Facet/Facet.cy.tsx +175 -24
- package/src/components/ControlBar/Facet/Facet.tsx +27 -13
- package/src/consts/data.ts +2 -0
- package/src/page/Home/Home.tsx +103 -79
- package/src/store/assets/assets.api.ts +30 -8
- package/src/store/search/search.api.ts +51 -68
- package/src/types/search.ts +14 -5
|
@@ -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 {
|
|
4
|
+
import { Facet as FacetType, GridView, SortDirection } from '@/types/search';
|
|
5
5
|
import type {
|
|
6
|
-
CxChangeEvent,
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
211
|
+
}, [isDefined, selectedFacets, onSettingChange]);
|
|
232
212
|
|
|
233
|
-
const selectedView = useMemo(
|
|
213
|
+
const selectedView = useMemo(
|
|
214
|
+
() => views.find((item) => item.value === view),
|
|
215
|
+
[view],
|
|
216
|
+
);
|
|
234
217
|
|
|
235
218
|
const appliedFilersCount = useMemo(() => {
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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 ${
|
|
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>
|
|
258
|
-
|
|
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
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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=
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
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
|
|
483
|
-
.map(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
)
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|