@orangelogic/orange-dam-content-browser-sdk 2.1.15 → 2.1.16

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 CHANGED
@@ -6,9 +6,9 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
8
8
 
9
- <script src="https://downloads.orangelogic.com/ContentBrowserSDK/v2.1.15/OrangeDAMContentBrowserSDK.min.js"></script>
9
+ <script src="https://downloads.orangelogic.com/ContentBrowserSDK/v2.1.16/OrangeDAMContentBrowserSDK.min.js"></script>
10
10
  <link rel="stylesheet" type="text/css"
11
- href="https://downloads.orangelogic.com/ContentBrowserSDK/v2.1.15/OrangeDAMContentBrowserSDK.min.css">
11
+ href="https://downloads.orangelogic.com/ContentBrowserSDK/v2.1.16/OrangeDAMContentBrowserSDK.min.css">
12
12
 
13
13
  <style>
14
14
  * {
@@ -3,9 +3,9 @@
3
3
 
4
4
  <head>
5
5
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap">
6
- <script src="https://downloads.orangelogic.com/Cortex/AssetBrowser/v2.1.15/OrangeDamAssetBrowser.min.js"></script>
6
+ <script src="https://downloads.orangelogic.com/Cortex/AssetBrowser/v2.1.16/OrangeDamAssetBrowser.min.js"></script>
7
7
  <link rel="stylesheet" type="text/css"
8
- href="https://downloads.orangelogic.com/Cortex/AssetBrowser/v2.1.15/OrangeDamAssetBrowser.min.css">
8
+ href="https://downloads.orangelogic.com/Cortex/AssetBrowser/v2.1.16/OrangeDamAssetBrowser.min.css">
9
9
  <style>
10
10
  #orangelogic-GAB-browser-wrapper {
11
11
  min-width: 382px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangelogic/orange-dam-content-browser-sdk",
3
- "version": "2.1.15",
3
+ "version": "2.1.16",
4
4
  "description": "OrangeDAM Content Browser SDK source code",
5
5
  "main": "index.js",
6
6
  "homepage": "/",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@babel/core": "^7.16.0",
70
- "@orangelogic-private/design-system": "^1.0.261",
70
+ "@orangelogic-private/design-system": "^1.0.269",
71
71
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
72
72
  "@reduxjs/toolkit": "^1.9.5",
73
73
  "@svgr/webpack": "^8.1.0",
package/src/App.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import '@orangelogic-private/design-system/assets/styles.css';
1
+ import '@orangelogic-private/design-system/assets/style.css';
2
2
  import '@orangelogic-private/design-system/components/alert';
3
3
  import '@orangelogic-private/design-system/components/avatar';
4
4
  import '@orangelogic-private/design-system/components/badge';
package/src/AppContext.ts CHANGED
@@ -4,7 +4,7 @@ import { GetAssetLinkResponse } from './types/search';
4
4
  export type AppContextType = {
5
5
  extraFields: string[];
6
6
  onAssetAction: (action: string, recordID: string) => void;
7
- onAssetSelected: (asset: GetAssetLinkResponse[]) => void;
7
+ onAssetSelected: (asset: GetAssetLinkResponse[]) => void | Promise<void>;
8
8
  onImageSelected: (image: GetAssetLinkResponse[]) => void;
9
9
  onError: (errorMessage?: string, error?: Error) => void;
10
10
  onClose: () => void;
@@ -17,12 +17,15 @@ const LoadMoreButton = ({
17
17
  <cx-space direction="horizontal" slot={slot}>
18
18
  {!disabledIndentation && <div className="browser__load-more__indentation"></div>}
19
19
  <cx-button
20
+ variant="text"
21
+ size="small"
20
22
  onClick={loadMore}
21
23
  disabled={disabled}
22
24
  loading={isLoading}
23
25
  className="browser__load-more"
24
26
  >
25
- Load more
27
+ <cx-icon name="add" slot="prefix"></cx-icon>
28
+ More
26
29
  </cx-button>
27
30
  </cx-space>
28
31
  );
@@ -1,5 +1,8 @@
1
+ import LoadMoreButton from '@/components/Browser/LoadMoreButton';
1
2
  import _capitalize from 'lodash-es/capitalize';
2
- import { FC } from 'react';
3
+ import { FC, useMemo, useState } from 'react';
4
+
5
+ const ITEMS_PER_PAGE = 20;
3
6
 
4
7
  type Props = {
5
8
  facet: Record<string, number>;
@@ -18,9 +21,7 @@ const Facet: FC<Props> = ({
18
21
  capitalize = true,
19
22
  loading = false,
20
23
  }) => {
21
- if (!facet || Object.values(facet).length === 0) {
22
- return null;
23
- }
24
+ const [page, setPage] = useState(1);
24
25
 
25
26
  /*
26
27
  The current facet value is flat. If a facet includes the ">>" character, it means it's a subtype, and the parent type is the value before ">>". We need to group them.
@@ -30,26 +31,37 @@ const Facet: FC<Props> = ({
30
31
  For example, "contact" => { all: 12, email: 5, phone: 3 } means that 5 of them are emails, 3 are phones, and 2 are just contacts that are directly of the parent's type.
31
32
  */
32
33
 
33
- const mappedSubtypes = Object.entries(facet).reduce((acc, [key, value]) => {
34
- const [parent, subtype] = key.split('>>');
35
-
36
- if (!acc[parent] || typeof acc[parent] !== 'object') {
37
- if (acc[parent]) {
38
- acc[parent] = {
39
- 'all': acc[parent],
40
- };
41
- } else {
42
- acc[parent] = {};
34
+ const mappedSubtypes = useMemo(() => {
35
+ return Object.entries(facet).reduce((acc, [key, value]) => {
36
+ const [parent, subtype] = key.split('>>');
37
+
38
+ if (!acc[parent] || typeof acc[parent] !== 'object') {
39
+ if (acc[parent]) {
40
+ acc[parent] = {
41
+ all: acc[parent],
42
+ };
43
+ } else {
44
+ acc[parent] = {};
45
+ }
46
+ }
47
+ if (subtype) {
48
+ acc[parent][subtype] = value;
43
49
  }
44
- }
45
- if (subtype) {
46
- acc[parent][subtype] = value;
47
- }
48
- acc[parent].all = (acc[parent].all || 0) + value;
50
+ acc[parent].all = (acc[parent].all || 0) + value;
51
+
52
+ return acc;
53
+ }, {} as Record<string, Record<string, number> | number>);
54
+ }, [facet]);
55
+
56
+ const hasNextPage = useMemo(
57
+ () => Object.keys(mappedSubtypes).length > page * ITEMS_PER_PAGE,
58
+ [mappedSubtypes, page],
59
+ );
60
+
61
+ if (!facet || Object.values(facet).length === 0) {
62
+ return null;
63
+ }
49
64
 
50
- return acc;
51
- }, {} as Record<string, Record<string, number> | number>);
52
-
53
65
  return (
54
66
  <cx-details open className="filter-details">
55
67
  <cx-space
@@ -63,52 +75,61 @@ const Facet: FC<Props> = ({
63
75
  </cx-space>
64
76
  <cx-space direction="vertical">
65
77
  <cx-tree selection="multiple" data-facet={type}>
66
- {Object.entries(mappedSubtypes).map(([key, value]) => {
67
- if (typeof value === 'object') {
68
- const selected = collections.includes(key);
78
+ {Object.entries(mappedSubtypes)
79
+ .slice(0, page * ITEMS_PER_PAGE)
80
+ .map(([key, value]) => {
81
+ if (typeof value === 'object') {
82
+ const selected = collections.includes(key);
69
83
 
70
- const { all, ...rest } = value;
84
+ const { all, ...rest } = value;
71
85
 
86
+ return (
87
+ <cx-tree-item
88
+ key={key}
89
+ data-value={key}
90
+ data-type={type}
91
+ readonly={loading}
92
+ selected={selected}
93
+ disabled-sync-checkboxes
94
+ >
95
+ {capitalize ? _capitalize(key) : key} {!!all && `(${all})`}
96
+ {Object.entries(rest).map(([subtype, count]) => (
97
+ <cx-tree-item
98
+ key={subtype}
99
+ data-value={`${key}>>${subtype}`}
100
+ data-type={type}
101
+ readonly={loading}
102
+ selected={collections.includes(`${key}>>${subtype}`)}
103
+ >
104
+ {capitalize ? _capitalize(subtype) : subtype} ({count})
105
+ </cx-tree-item>
106
+ ))}
107
+ </cx-tree-item>
108
+ );
109
+ }
72
110
  return (
73
111
  <cx-tree-item
74
112
  key={key}
75
113
  data-value={key}
76
114
  data-type={type}
77
115
  readonly={loading}
78
- selected={selected}
79
- disabled-sync-checkboxes
116
+ selected={collections.includes(key)}
80
117
  >
81
- {capitalize ? _capitalize(key) : key} {!!all && `(${all})`}
82
- {Object.entries(rest).map(([subtype, count]) => (
83
- <cx-tree-item
84
- key={subtype}
85
- data-value={`${key}>>${subtype}`}
86
- data-type={type}
87
- readonly={loading}
88
- selected={collections.includes(`${key}>>${subtype}`)}
89
- >
90
- {capitalize ? _capitalize(subtype) : subtype} ({count})
91
- </cx-tree-item>
92
- ))}
118
+ {capitalize ? _capitalize(key) : key} ({value})
93
119
  </cx-tree-item>
94
120
  );
95
- }
96
- return (
97
- <cx-tree-item
98
- key={key}
99
- data-value={key}
100
- data-type={type}
101
- readonly={loading}
102
- selected={collections.includes(key)}
103
- >
104
- {capitalize ? _capitalize(key) : key} ({value})
105
- </cx-tree-item>
106
- );
107
- })}
121
+ })}
122
+ {hasNextPage && (
123
+ <LoadMoreButton
124
+ loadMore={() => setPage((prev) => prev + 1)}
125
+ isLoading={loading}
126
+ disabled={loading}
127
+ />
128
+ )}
108
129
  </cx-tree>
109
130
  </cx-space>
110
131
  </cx-details>
111
132
  );
112
133
  };
113
134
 
114
- export default Facet;
135
+ export default Facet;
@@ -252,8 +252,8 @@ const FormatDialogProps = {
252
252
  recordId: 'Q0LDO000001895618',
253
253
  },
254
254
  onClose: () => {},
255
- onProxyConfirm: () => {},
256
- onFormatConfirm: () => {},
255
+ onProxyConfirm: () => Promise.resolve(),
256
+ onFormatConfirm: () => Promise.resolve(),
257
257
  onOpenInDriveConfirm: () => {},
258
258
  allowTracking: true,
259
259
  autoExtension: '.auto',
@@ -43,13 +43,13 @@ type Props = {
43
43
  parameters?: TrackingParameter[];
44
44
  useRepresentative?: boolean;
45
45
  value: string;
46
- }) => void;
46
+ }) => Promise<void>;
47
47
  onFormatConfirm: (value: {
48
48
  value: Transformation[];
49
49
  parameters?: TrackingParameter[];
50
50
  proxiesPreference?: string;
51
51
  extension?: string;
52
- }) => void;
52
+ }) => Promise<void>;
53
53
  onUnFavorite: () => Promise<boolean>;
54
54
  };
55
55
 
@@ -94,6 +94,7 @@ type State = {
94
94
  y: number;
95
95
  unit: Unit;
96
96
  }>;
97
+ isLoadingConfirm: boolean;
97
98
  isLoadingFavorites: boolean;
98
99
  rotation: number;
99
100
  quality: number;
@@ -121,6 +122,7 @@ type Action =
121
122
  | { type: 'SET_LAST_RESIZE_SIZE'; payload: Partial<State['lastResizeSize']> }
122
123
  | { type: 'SET_LAST_CROP_SIZE'; payload: Partial<State['lastCropSize']> }
123
124
  | { type: 'SET_LOADING'; payload: boolean }
125
+ | { type: 'SET_LOADING_CONFIRM'; payload: boolean }
124
126
  | { type: 'SET_LOADING_FAVORITES'; payload: boolean }
125
127
  | { type: 'SET_PREVIEW_LOADABLE'; payload: boolean }
126
128
  | { type: 'SET_QUALITY'; payload: number }
@@ -197,6 +199,7 @@ const initialState: State = {
197
199
  unit: Unit.Pixel,
198
200
  },
199
201
  },
202
+ isLoadingConfirm: false,
200
203
  isLoadingFavorites: false,
201
204
  rotation: 0,
202
205
  quality: 100,
@@ -321,6 +324,11 @@ const reducer = (state: State, action: Action): State => {
321
324
  ...state,
322
325
  isLoading: action.payload,
323
326
  };
327
+ case 'SET_LOADING_CONFIRM':
328
+ return {
329
+ ...state,
330
+ isLoadingConfirm: action.payload,
331
+ };
324
332
  case 'SET_LOADING_FAVORITES':
325
333
  return {
326
334
  ...state,
@@ -1235,7 +1243,7 @@ const FormatDialog: FC<Props> = ({
1235
1243
  {allowFavorites && (
1236
1244
  <cx-tooltip
1237
1245
  slot="header-actions"
1238
- content={isFavorite ? 'Unmark favorite' : 'Mark favorite'}
1246
+ content={isFavorite ? 'Favorite' : 'Unfavorite'}
1239
1247
  placement="bottom"
1240
1248
  >
1241
1249
  {state.isLoadingFavorites ? (
@@ -1588,16 +1596,19 @@ const FormatDialog: FC<Props> = ({
1588
1596
  content = (
1589
1597
  <cx-button
1590
1598
  className="dialog__footer__button"
1591
- onClick={() => {
1599
+ loading={state.isLoadingConfirm}
1600
+ onClick={async () => {
1592
1601
  if (!selectedAsset?.docType) {
1593
1602
  return;
1594
1603
  }
1595
1604
 
1596
- onProxyConfirm({
1605
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: true });
1606
+ await onProxyConfirm({
1597
1607
  extension: selectedAsset.extension,
1598
1608
  useRepresentative: true,
1599
1609
  value: '',
1600
1610
  });
1611
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: false });
1601
1612
  }}
1602
1613
  variant="primary"
1603
1614
  >
@@ -1640,9 +1651,10 @@ const FormatDialog: FC<Props> = ({
1640
1651
  <cx-button
1641
1652
  className="dialog__footer__button"
1642
1653
  disabled={disabledInsert}
1654
+ loading={state.isLoadingConfirm}
1643
1655
  variant="primary"
1644
1656
  style={{ flex: 1 }}
1645
- onClick={() => {
1657
+ onClick={async () => {
1646
1658
  const selectedProxy = availableProxies?.find((proxy) => {
1647
1659
  return proxy.id === state.selectedProxy;
1648
1660
  });
@@ -1656,7 +1668,8 @@ const FormatDialog: FC<Props> = ({
1656
1668
  return;
1657
1669
  }
1658
1670
 
1659
- onProxyConfirm({
1671
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: true });
1672
+ await onProxyConfirm({
1660
1673
  extension: selectedProxy.extension ?? selectedAsset.extension,
1661
1674
  value: selectedProxy.proxyName,
1662
1675
  permanentLink: selectedProxy.permanentLink ?? undefined,
@@ -1665,8 +1678,10 @@ const FormatDialog: FC<Props> = ({
1665
1678
  : undefined,
1666
1679
  useRepresentative: state.useRepresentative,
1667
1680
  });
1681
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: false });
1668
1682
  } else {
1669
- onFormatConfirm({
1683
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: true });
1684
+ await onFormatConfirm({
1670
1685
  value: state.transformations,
1671
1686
  parameters: state.enabledTracking
1672
1687
  ? state.trackingParameters
@@ -1674,6 +1689,7 @@ const FormatDialog: FC<Props> = ({
1674
1689
  proxiesPreference: selectedProxy?.proxyName,
1675
1690
  extension: state.selectedFormat.extension,
1676
1691
  });
1692
+ dispatch({ type: 'SET_LOADING_CONFIRM', payload: false });
1677
1693
  }
1678
1694
  dispatch({ type: 'RESET_DATA' });
1679
1695
  onClose();
@@ -36,6 +36,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
36
36
 
37
37
  import { Container } from './Home.styled';
38
38
  import { COMPUTED_FIELDS } from '@/consts/data';
39
+ import { isPromise } from '@/utils/function';
39
40
 
40
41
  type Props = {
41
42
  multiSelect?: boolean;
@@ -225,7 +226,7 @@ const HomePage: FC<Props> = () => {
225
226
  showFavoriteFolder,
226
227
  showVersions,
227
228
  } = useContext(GlobalConfigContext);
228
- const { extraFields, onAssetAction } = useContext(AppContext);
229
+ const { extraFields, onAssetAction, onAssetSelected, onClose } = useContext(AppContext);
229
230
 
230
231
  const { data: selectedAssetData, isFetching: isFetchingSelectedAsset, isError: isErrorSelectedAsset } = useGetAssetByIdQuery(selectedAssetId ? {
231
232
  id: selectedAssetId,
@@ -681,7 +682,7 @@ const HomePage: FC<Props> = () => {
681
682
  }
682
683
  }, []);
683
684
 
684
- const handleSelectedAsset = useCallback((images: GetAssetLinkResponse[]) => {
685
+ const handleSelectedAsset = useCallback(async (images: GetAssetLinkResponse[]) => {
685
686
  const payload = [...images];
686
687
  COMPUTED_FIELDS.forEach((item) => {
687
688
  const key = _camelCase(item) as keyof typeof selectedAsset;
@@ -692,13 +693,25 @@ const HomePage: FC<Props> = () => {
692
693
  };
693
694
  }
694
695
  });
695
-
696
- window.OrangeDAMContentBrowser._onAssetSelected?.(payload);
696
+
697
+ const result = onAssetSelected(payload);
698
+
699
+ if (isPromise(result)) {
700
+ try {
701
+ await result;
702
+ } catch (error) {
703
+ console.error('Error in onAssetSelected:', error);
704
+ return;
705
+ }
706
+ } else {
707
+ onAssetSelected?.(payload);
708
+ }
709
+
697
710
  if (persistMode) {
698
711
  return;
699
712
  }
700
- window.OrangeDAMContentBrowser._onClose?.();
701
- }, [extraFields, persistMode, selectedAsset]);
713
+ onClose?.();
714
+ }, [extraFields, onAssetSelected, onClose, persistMode, selectedAsset]);
702
715
 
703
716
  const hasNextPage = useMemo(
704
717
  () => (data ? state.start + state.pageSize < state.totalCount : false),
@@ -965,7 +978,7 @@ const HomePage: FC<Props> = () => {
965
978
  );
966
979
 
967
980
  if (importAssets.fulfilled.match(images)) {
968
- handleSelectedAsset(images.payload);
981
+ await handleSelectedAsset(images.payload);
969
982
  }
970
983
  }}
971
984
  onFormatConfirm={async ({ value, parameters, proxiesPreference, extension }) => {
@@ -994,7 +1007,7 @@ const HomePage: FC<Props> = () => {
994
1007
  );
995
1008
 
996
1009
  if (importAssets.fulfilled.match(images)) {
997
- handleSelectedAsset(images.payload);
1010
+ await handleSelectedAsset(images.payload);
998
1011
  }
999
1012
  }}
1000
1013
  onUnFavorite={async () => {
@@ -176,7 +176,7 @@ export const assetsApi = createApi({
176
176
  fileImportDate: GetValueByKeyCaseInsensitive(version, 'FileImportDate'),
177
177
  scrubUrl: GetValueByKeyCaseInsensitive(version, 'ScrubUrl'),
178
178
  versionCreateDate: GetValueByKeyCaseInsensitive(version, 'VersionCreateDate'),
179
- versionFileName: GetValueByKeyCaseInsensitive(version, 'VersionFileName'),
179
+ versionFileName: GetValueByKeyCaseInsensitive(version, 'UpdatedFileName'),
180
180
  versionFileUrl: GetValueByKeyCaseInsensitive(version, 'PreviewUrl'),
181
181
  versionId: GetValueByKeyCaseInsensitive(version, 'VersionID'),
182
182
  versionNumber: GetValueByKeyCaseInsensitive(version, 'VersionNumber'),
@@ -0,0 +1,7 @@
1
+ export function isPromise(value: unknown): value is Promise<unknown> {
2
+ return (
3
+ typeof value === 'object' &&
4
+ value !== null &&
5
+ typeof (value as { then?: unknown }).then === 'function'
6
+ );
7
+ }
@@ -10,6 +10,7 @@ import { rotateBox } from './rotate';
10
10
  import { deleteData, getData, storeData } from './storage';
11
11
  import { generateRandomString, isNullOrWhiteSpace } from './string';
12
12
  import { calculateAspectRatioFit, cropImage, resizeImage, rotateImage } from './image';
13
+ import { isPromise } from './function';
13
14
 
14
15
  describe('Utils - array', () => {
15
16
  it('Should check if an array has elements', () => {
@@ -251,3 +252,13 @@ describe('Utils - image', () => {
251
252
  expect(result2.height).to.equal(640);
252
253
  });
253
254
  });
255
+
256
+ describe('Utils - function', () => {
257
+ it('Should check if a value is a promise', () => {
258
+ const promise = Promise.resolve('test');
259
+ const notPromise = 'test';
260
+
261
+ expect(isPromise(promise)).toBe(true);
262
+ expect(isPromise(notPromise)).toBe(false);
263
+ });
264
+ });