@orangelogic/orange-dam-content-browser-sdk 1.0.1-test

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.
Files changed (173) hide show
  1. package/.env +1 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.json +82 -0
  4. package/.releaserc +17 -0
  5. package/.travis.yml +20 -0
  6. package/CBSDKdemo.html +315 -0
  7. package/GitVersion.yml +18 -0
  8. package/README.md +57 -0
  9. package/azure-pipeline.yaml +94 -0
  10. package/clientlib.config.js +36 -0
  11. package/config/env.js +105 -0
  12. package/config/getHttpsConfig.js +67 -0
  13. package/config/jest/babelTransform.js +30 -0
  14. package/config/jest/cssTransform.js +14 -0
  15. package/config/jest/fileTransform.js +41 -0
  16. package/config/modules.js +135 -0
  17. package/config/paths.js +79 -0
  18. package/config/webpack/persistentCache/createEnvironmentHash.js +10 -0
  19. package/config/webpack.config.js +762 -0
  20. package/config/webpackDevServer.config.js +128 -0
  21. package/config-overrides.js +8 -0
  22. package/gab_extension/GAB.html +85 -0
  23. package/gab_extension/GoogleChrome/manifest.json +28 -0
  24. package/gab_extension/GoogleChrome/src/assets/icon48.png +0 -0
  25. package/gab_extension/GoogleChrome/src/background/index.js +6 -0
  26. package/gab_extension/GoogleChrome/src/scripts/index.js +347 -0
  27. package/gab_extension/MozillaFirefox/manifest.json +20 -0
  28. package/gab_extension/MozillaFirefox/src/assets/icon.png +0 -0
  29. package/gab_extension/MozillaFirefox/src/background/index.js +5 -0
  30. package/gab_extension/MozillaFirefox/src/scripts/index.js +347 -0
  31. package/gab_extension/README.md +11 -0
  32. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.pbxproj +927 -0
  33. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  34. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  35. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/xcuserdata/oldevmac01.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  36. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/xcuserdata/oldevmac01.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
  37. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  38. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +63 -0
  39. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/Contents.json +6 -0
  40. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +20 -0
  41. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Base.lproj/Main.html +23 -0
  42. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Icon.png +0 -0
  43. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Script.js +24 -0
  44. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Style.css +61 -0
  45. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/ViewController.swift +81 -0
  46. package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (Extension)/SafariWebExtensionHandler.swift +26 -0
  47. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/AppDelegate.swift +24 -0
  48. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Base.lproj/LaunchScreen.storyboard +36 -0
  49. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Base.lproj/Main.storyboard +38 -0
  50. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Info.plist +27 -0
  51. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/SceneDelegate.swift +18 -0
  52. package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (Extension)/Info.plist +13 -0
  53. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/AppDelegate.swift +21 -0
  54. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Base.lproj/Main.storyboard +125 -0
  55. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Info.plist +8 -0
  56. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Orange DAM Asset Browser Extension.entitlements +12 -0
  57. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (Extension)/Info.plist +13 -0
  58. package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (Extension)/Orange DAM Asset Browser Extension.entitlements +10 -0
  59. package/package.json +192 -0
  60. package/public/favicon.ico +0 -0
  61. package/public/index.html +92 -0
  62. package/public/logo192.png +0 -0
  63. package/public/logo512.png +0 -0
  64. package/public/manifest.json +25 -0
  65. package/public/robots.txt +3 -0
  66. package/scripts/build.js +218 -0
  67. package/scripts/start.js +154 -0
  68. package/scripts/test.js +53 -0
  69. package/src/App.tsx +98 -0
  70. package/src/AppContext.ts +18 -0
  71. package/src/GlobalConfigContext.ts +46 -0
  72. package/src/components/ArrayClamp/ArrayClamp.styled.ts +42 -0
  73. package/src/components/ArrayClamp/ArrayClamp.tsx +167 -0
  74. package/src/components/ArrayClamp/index.ts +1 -0
  75. package/src/components/Browser/Browser.styled.ts +82 -0
  76. package/src/components/Browser/Browser.tsx +284 -0
  77. package/src/components/Browser/BrowserItem.tsx +98 -0
  78. package/src/components/Browser/index.ts +1 -0
  79. package/src/components/ControlBar/ControlBar.constants.tsx +66 -0
  80. package/src/components/ControlBar/ControlBar.styled.ts +82 -0
  81. package/src/components/ControlBar/ControlBar.tsx +528 -0
  82. package/src/components/ControlBar/Facet/Facet.tsx +113 -0
  83. package/src/components/ControlBar/Facet/index.ts +1 -0
  84. package/src/components/ControlBar/index.ts +1 -0
  85. package/src/components/FormatDialog/CropPreviewer/CropPreviewer.tsx +224 -0
  86. package/src/components/FormatDialog/CropPreviewer/index.ts +3 -0
  87. package/src/components/FormatDialog/CustomRendition/CustomRendition.constants.ts +24 -0
  88. package/src/components/FormatDialog/CustomRendition/CustomRendition.styled.ts +57 -0
  89. package/src/components/FormatDialog/CustomRendition/CustomRendition.tsx +178 -0
  90. package/src/components/FormatDialog/CustomRendition/index.ts +1 -0
  91. package/src/components/FormatDialog/CustomRendition/transformations/Crop.tsx +249 -0
  92. package/src/components/FormatDialog/CustomRendition/transformations/Extension.tsx +54 -0
  93. package/src/components/FormatDialog/CustomRendition/transformations/Format.tsx +86 -0
  94. package/src/components/FormatDialog/CustomRendition/transformations/Resize.tsx +176 -0
  95. package/src/components/FormatDialog/CustomRendition/transformations/Rotate.tsx +101 -0
  96. package/src/components/FormatDialog/CustomRendition/transformations/index.ts +5 -0
  97. package/src/components/FormatDialog/FormatDialog.styled.ts +137 -0
  98. package/src/components/FormatDialog/FormatDialog.tsx +1533 -0
  99. package/src/components/FormatDialog/Previewer/Previewer.styled.ts +31 -0
  100. package/src/components/FormatDialog/Previewer/Previewer.tsx +143 -0
  101. package/src/components/FormatDialog/Previewer/index.ts +1 -0
  102. package/src/components/FormatDialog/ProxyMenu/ProxyMenu.styled.ts +88 -0
  103. package/src/components/FormatDialog/ProxyMenu/ProxyMenu.tsx +74 -0
  104. package/src/components/FormatDialog/ProxyMenu/index.ts +1 -0
  105. package/src/components/FormatDialog/TrackingParameters/TrackingParameters.tsx +59 -0
  106. package/src/components/FormatDialog/TrackingParameters/index.ts +1 -0
  107. package/src/components/FormatDialog/index.ts +1 -0
  108. package/src/components/Header/Header.styled.ts +51 -0
  109. package/src/components/Header/Header.tsx +118 -0
  110. package/src/components/Header/index.ts +1 -0
  111. package/src/components/Loader/Loader.tsx +37 -0
  112. package/src/components/Loader/index.ts +1 -0
  113. package/src/components/NoResult/NoResult.tsx +37 -0
  114. package/src/components/NoResult/index.tsx +1 -0
  115. package/src/components/Result/AssetCard/AssetCard.styled.ts +120 -0
  116. package/src/components/Result/AssetCard/AssetCard.tsx +192 -0
  117. package/src/components/Result/AssetCard/AssetCardWrapper.styled.ts +35 -0
  118. package/src/components/Result/AssetCard/AssetCardWrapper.tsx +165 -0
  119. package/src/components/Result/AssetCard/index.ts +1 -0
  120. package/src/components/Result/AssetPreview/AssetPreview.styled.ts +108 -0
  121. package/src/components/Result/AssetPreview/AssetPreview.tsx +78 -0
  122. package/src/components/Result/AssetPreview/ImagePreview/ImagePreview.tsx +42 -0
  123. package/src/components/Result/AssetPreview/ImagePreview/index.ts +1 -0
  124. package/src/components/Result/AssetPreview/OtherPreview/OtherPreview.styled.ts +23 -0
  125. package/src/components/Result/AssetPreview/OtherPreview/OtherPreview.tsx +28 -0
  126. package/src/components/Result/AssetPreview/OtherPreview/index.ts +1 -0
  127. package/src/components/Result/AssetPreview/VideoPreview/VideoPreview.tsx +132 -0
  128. package/src/components/Result/AssetPreview/VideoPreview/index.ts +1 -0
  129. package/src/components/Result/AssetPreview/index.ts +1 -0
  130. package/src/consts/asset.ts +16 -0
  131. package/src/consts/data.ts +17 -0
  132. package/src/index.tsx +305 -0
  133. package/src/page/Authenticate/Authenticate.tsx +232 -0
  134. package/src/page/Authenticate/ConnectingBackground.tsx +44 -0
  135. package/src/page/Authenticate/index.tsx +94 -0
  136. package/src/page/Home/Home.styled.ts +46 -0
  137. package/src/page/Home/Home.tsx +941 -0
  138. package/src/page/Home/index.ts +1 -0
  139. package/src/react-web-component.d.ts +4617 -0
  140. package/src/store/assets/assets.api.ts +167 -0
  141. package/src/store/assets/assets.service.ts +223 -0
  142. package/src/store/assets/assets.slice.ts +104 -0
  143. package/src/store/auth/auth.service.ts +71 -0
  144. package/src/store/auth/auth.slice.ts +295 -0
  145. package/src/store/index.ts +27 -0
  146. package/src/store/search/search.api.ts +319 -0
  147. package/src/store/search/search.slice.ts +28 -0
  148. package/src/store/user/user.api.ts +29 -0
  149. package/src/styles.css +42 -0
  150. package/src/types/assets.ts +71 -0
  151. package/src/types/auth.ts +42 -0
  152. package/src/types/common.ts +11 -0
  153. package/src/types/download.ts +8 -0
  154. package/src/types/navigation.ts +3 -0
  155. package/src/types/search.ts +116 -0
  156. package/src/types/storage.ts +1 -0
  157. package/src/types/user.ts +6 -0
  158. package/src/utils/api.ts +186 -0
  159. package/src/utils/array.ts +25 -0
  160. package/src/utils/constants.ts +12 -0
  161. package/src/utils/fetch.ts +116 -0
  162. package/src/utils/getRequestUrl.ts +15 -0
  163. package/src/utils/hooks.ts +36 -0
  164. package/src/utils/icon.ts +22 -0
  165. package/src/utils/image.ts +157 -0
  166. package/src/utils/number.ts +11 -0
  167. package/src/utils/rotate.ts +23 -0
  168. package/src/utils/storage.ts +184 -0
  169. package/src/utils/string.ts +24 -0
  170. package/src/view/AssetsPicker.tsx +24 -0
  171. package/src/web-component.d.ts +8151 -0
  172. package/tsconfig.eslint.json +10 -0
  173. package/tsconfig.json +37 -0
@@ -0,0 +1,113 @@
1
+ import _capitalize from 'lodash-es/capitalize';
2
+ import { FC } from 'react';
3
+
4
+ type Props = {
5
+ facet: Record<string, number>;
6
+ type: string;
7
+ displayName: string;
8
+ collections: string[];
9
+ capitalize?: boolean;
10
+ loading?: boolean;
11
+ };
12
+
13
+ const Facet: FC<Props> = ({
14
+ facet,
15
+ type,
16
+ displayName,
17
+ collections,
18
+ capitalize = true,
19
+ loading = false,
20
+ }) => {
21
+ if (!facet || Object.values(facet).length === 0) {
22
+ return null;
23
+ }
24
+
25
+ /*
26
+ 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.
27
+ For example, "contact >> email", "contact >> phone", and "contact >> address" should be grouped as "contact" => { email: 10, phone: 5, address: 3 }
28
+ The "all" key will contain the total count of all subtypes for that parent type.
29
+ Sometimes there are some asset belongs to the parent type but not to any subtype. In that case, we need to add the parent type to the result as well.
30
+ 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
+
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] = {};
43
+ }
44
+ }
45
+ if (subtype) {
46
+ acc[parent][subtype] = value;
47
+ }
48
+ acc[parent].all = (acc[parent].all || 0) + value;
49
+
50
+ return acc;
51
+ }, {} as Record<string, Record<string, number> | number>);
52
+
53
+ return (
54
+ <cx-details open className="filter-details">
55
+ <cx-space
56
+ slot="summary"
57
+ align-items="center"
58
+ spacing="x-small"
59
+ wrap="nowrap"
60
+ >
61
+ <span>{displayName}</span>
62
+ {loading && <cx-spinner></cx-spinner>}
63
+ </cx-space>
64
+ <cx-space direction="vertical">
65
+ <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);
69
+
70
+ const { all, ...rest } = value;
71
+
72
+ return (
73
+ <cx-tree-item
74
+ key={key}
75
+ data-value={key}
76
+ data-type={type}
77
+ readonly={loading}
78
+ selected={selected}
79
+ >
80
+ {capitalize ? _capitalize(key) : key} {!!all && `(${all})`}
81
+ {Object.entries(rest).map(([subtype, count]) => (
82
+ <cx-tree-item
83
+ key={subtype}
84
+ data-value={`${key}>>${subtype}`}
85
+ data-type={type}
86
+ readonly={loading}
87
+ selected={collections.includes(`${key}>>${subtype}`)}
88
+ >
89
+ {capitalize ? _capitalize(subtype) : subtype} ({count})
90
+ </cx-tree-item>
91
+ ))}
92
+ </cx-tree-item>
93
+ );
94
+ }
95
+ return (
96
+ <cx-tree-item
97
+ key={key}
98
+ data-value={key}
99
+ data-type={type}
100
+ readonly={loading}
101
+ selected={collections.includes(key)}
102
+ >
103
+ {capitalize ? _capitalize(key) : key} ({value})
104
+ </cx-tree-item>
105
+ );
106
+ })}
107
+ </cx-tree>
108
+ </cx-space>
109
+ </cx-details>
110
+ );
111
+ };
112
+
113
+ export default Facet;
@@ -0,0 +1 @@
1
+ export { default } from './Facet';
@@ -0,0 +1 @@
1
+ export { default } from './ControlBar';
@@ -0,0 +1,224 @@
1
+ import _debounce from 'lodash-es/debounce';
2
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
+ import Cropper, { Area, Point } from 'react-easy-crop';
4
+
5
+ import { Unit } from '@/types/assets';
6
+ import { FORMAT_DIALOG_PREVIEW_SIZE } from '@/utils/constants';
7
+ import { calculateAspectRatioFit, cropImage, resizeImage, rotateImage } from '@/utils/image';
8
+
9
+ import { Container } from '../Previewer/Previewer.styled';
10
+
11
+ type Props = {
12
+ disabled: boolean;
13
+ loadable: boolean;
14
+ image: {
15
+ url: string;
16
+ originalUrl: string;
17
+ extension: string;
18
+ width: number;
19
+ height: number;
20
+ x: number;
21
+ y: number;
22
+ rotation: number;
23
+ };
24
+ selectedProxy: string;
25
+ resizer: {
26
+ width: number;
27
+ height: number;
28
+ };
29
+ cropper: {
30
+ width: number;
31
+ height: number;
32
+ percentageWidth: number;
33
+ percentageHeight: number;
34
+ x: number;
35
+ y: number;
36
+ unit: Unit;
37
+ };
38
+ rotation: number;
39
+ onCropComplete: (croppedArea: Area, croppedAreaPixels: Area) => void;
40
+ onLoadingChange: (loading: boolean) => void;
41
+ };
42
+
43
+ export type CropPreviewerHandle = {
44
+ applyResize: () => Promise<string>;
45
+ applyCrop: () => Promise<string>;
46
+ applyRotation: () => Promise<string>;
47
+ setZoom: (zoom: number) => void;
48
+ };
49
+
50
+ const CropPreviewer = forwardRef<CropPreviewerHandle, Props>(({
51
+ disabled,
52
+ loadable,
53
+ image,
54
+ resizer,
55
+ cropper,
56
+ rotation,
57
+ selectedProxy,
58
+ onCropComplete,
59
+ onLoadingChange,
60
+ }, ref) => {
61
+ const [isLoading, setIsLoading] = useState(true);
62
+ const [crop, setCrop] = useState({ x: 0, y: 0 });
63
+ const [zoom, setZoom] = useState(1);
64
+ const [resizedImage, setResizedImage] = useState<string>(image?.url ?? '');
65
+ const containerRef = useRef<HTMLDivElement>(null);
66
+ useEffect(() => {
67
+ const { url, originalUrl, extension } = image;
68
+ const { width, height } = resizer;
69
+ const resize = async () => {
70
+ if (loadable) {
71
+ onLoadingChange(true);
72
+ const { width: newWidth, height: newHeight } = calculateAspectRatioFit(image.width, image.height, width, height);
73
+ const { url: imageUrl } = await resizeImage(url,
74
+ newWidth,
75
+ newHeight,
76
+ containerRef.current?.clientWidth ?? window.innerWidth, FORMAT_DIALOG_PREVIEW_SIZE,
77
+ );
78
+ setResizedImage(extension === 'gif' ? originalUrl : imageUrl);
79
+ onLoadingChange(false);
80
+ } else {
81
+ setResizedImage(url);
82
+ }
83
+ };
84
+ const debounceResize = _debounce(resize, 300, { leading: true });
85
+ debounceResize();
86
+ }, [image, loadable, onLoadingChange, resizer, selectedProxy]);
87
+
88
+ const applyResize = useCallback(async () => {
89
+ return resizedImage;
90
+ }, [resizedImage]);
91
+
92
+ const applyCrop = useCallback(async () => {
93
+ const { width, height, x, y, unit } = cropper;
94
+ const options = { x, y, width, height };
95
+ options.x = (x / 100) * image.width;
96
+ options.y = (y / 100) * image.height;
97
+ if (loadable) {
98
+ onLoadingChange(true);
99
+ if (unit === Unit.AspectRatio) {
100
+ const { width: newWidth, height: newHeight } = calculateAspectRatioFit(image.width, image.height, width, height);
101
+ options.width = newWidth / zoom;
102
+ options.height = newHeight / zoom;
103
+ }
104
+ const result = await cropImage(
105
+ {
106
+ url: image.url,
107
+ width: image.width,
108
+ height: image.height,
109
+ },
110
+ options,
111
+ );
112
+ onLoadingChange(false);
113
+ return result;
114
+ } else {
115
+ return Promise.resolve(image.url);
116
+ }
117
+ }, [cropper, image.width, image.height, image.url, loadable, onLoadingChange, zoom]);
118
+
119
+ const applyRotation = useCallback(async () => {
120
+ if (loadable) {
121
+ onLoadingChange(true);
122
+ const result = await rotateImage(
123
+ {
124
+ url: image.url,
125
+ width: image.width,
126
+ height: image.height,
127
+ },
128
+ rotation,
129
+ );
130
+ onLoadingChange(false);
131
+ return result;
132
+ } else {
133
+ return Promise.resolve(image.url);
134
+ }
135
+ }, [image.height, image.url, image.width, loadable, onLoadingChange, rotation]);
136
+
137
+ useImperativeHandle(ref, () => ({
138
+ applyResize,
139
+ applyCrop,
140
+ applyRotation,
141
+ setZoom,
142
+ }));
143
+
144
+ const resetCropper = useCallback(() => {
145
+ setCrop({ x: 0, y: 0 });
146
+ setZoom(1);
147
+ }, []);
148
+
149
+ const onCropChange = useCallback(
150
+ (location: Point) => {
151
+ if (disabled) {
152
+ return;
153
+ }
154
+ setCrop(location);
155
+ },
156
+ [disabled],
157
+ );
158
+
159
+ const onMediaLoaded = useCallback(() => {
160
+ setIsLoading(false);
161
+ }, []);
162
+
163
+ useEffect(() => {
164
+ resetCropper();
165
+ }, [disabled, resetCropper]);
166
+
167
+
168
+ const objectFit = useMemo(() => {
169
+ const container = containerRef.current;
170
+ if (!container || isLoading) {
171
+ return 'contain';
172
+ }
173
+ const imgAspect = resizer.width / resizer.height;
174
+
175
+ const containerAspect = container.clientHeight ? (container.clientWidth) / (container.clientHeight) : 0;
176
+
177
+ return imgAspect > containerAspect ? 'horizontal-cover' : 'vertical-cover';
178
+ }, [isLoading, resizer]);
179
+
180
+ return (
181
+ <Container
182
+ ref={containerRef}
183
+ style={{
184
+ height: `${FORMAT_DIALOG_PREVIEW_SIZE}px`,
185
+ }}
186
+ >
187
+ {loadable && (
188
+ <Cropper
189
+ image={resizedImage}
190
+ crop={crop}
191
+ zoom={zoom}
192
+ aspect={cropper.width / cropper.height}
193
+ rotation={rotation}
194
+ objectFit={objectFit}
195
+ onCropChange={onCropChange}
196
+ onCropComplete={onCropComplete}
197
+ onMediaLoaded={onMediaLoaded}
198
+ onZoomChange={setZoom}
199
+ zoomWithScroll={cropper.unit === Unit.AspectRatio}
200
+ style={{
201
+ containerStyle: {
202
+ cursor: disabled ? 'default' : 'move',
203
+ pointerEvents: disabled ? 'none' : 'auto',
204
+ },
205
+ cropAreaStyle: {
206
+ display: disabled ? 'none' : 'block',
207
+ },
208
+ mediaStyle: {
209
+ opacity: isLoading ? 0 : 1,
210
+ },
211
+ }}
212
+ />
213
+ )}
214
+ {isLoading && (
215
+ <div className="loading">
216
+ <cx-skeleton className="loading__skeleton"></cx-skeleton>
217
+ <cx-spinner className="loading__spinner"></cx-spinner>
218
+ </div>
219
+ )}
220
+ </Container>
221
+ );
222
+ });
223
+
224
+ export default CropPreviewer;
@@ -0,0 +1,3 @@
1
+ import { CropPreviewerHandle } from './CropPreviewer';
2
+ export { default } from './CropPreviewer';
3
+ export type { CropPreviewerHandle };
@@ -0,0 +1,24 @@
1
+ export const cropModes = [{
2
+ value: 'free',
3
+ label: 'Freeform',
4
+ }, {
5
+ value: '16:9',
6
+ label: 'Widescreen (16:9)',
7
+ }, {
8
+ value: '9:16',
9
+ label: 'Phone (9:16)',
10
+ }, {
11
+ value: '4:3',
12
+ label: 'Presentation (4:3)',
13
+ }, {
14
+ value: '1:1',
15
+ label: 'Square (1:1)',
16
+ }, {
17
+ value: '3:2',
18
+ label: 'Landscape (3:2)',
19
+ }, {
20
+ value: '2:3',
21
+ label: 'Portrait (2:3)',
22
+ }];
23
+
24
+ export const INPUT_DEBOUNCE_DELAY = 1000;
@@ -0,0 +1,57 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ .details__summary__icon {
5
+ width: var(--cx-font-size-3x-large);
6
+ height: var(--cx-font-size-3x-large);
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ background-color: var(--cx-color-neutral-100);
11
+ border-radius: var(--cx-border-radius-large);
12
+ color: var(--cx-color-neutral-600);
13
+ font-size: var(--cx-font-size-large);
14
+ }
15
+
16
+ .details__summary__input-label {
17
+ color: var(--cx-color-neutral-400);
18
+ margin-inline-start: var(--cx-spacing-small);
19
+ }
20
+
21
+ cx-input::part(input) {
22
+ padding-left: var(--cx-spacing-small);
23
+ padding-right: var(--cx-spacing-small);
24
+ }
25
+
26
+ cx-space {
27
+ width: 100%;
28
+
29
+ cx-input {
30
+ flex: 1;
31
+ }
32
+ }
33
+
34
+ .resize__unit-select {
35
+ width: 120px;
36
+ }
37
+
38
+ .resize__input-group {
39
+ display: flex;
40
+ flex: 1;
41
+ }
42
+
43
+ .extension {
44
+ padding: var(--cx-spacing-small) var(--cx-spacing-medium);
45
+ border: solid 1px var(--cx-color-neutral-200);
46
+ background-color: var(--cx-color-neutral-0);
47
+
48
+ cx-select{
49
+ flex: 1;
50
+ width: 100%;
51
+ }
52
+ }
53
+
54
+ cx-details:not(:last-child)::part(base) {
55
+ border-bottom-width: 0;
56
+ }
57
+ `;
@@ -0,0 +1,178 @@
1
+ import _isEqualWith from 'lodash-es/isEqualWith';
2
+ import { FC, useEffect, useMemo, useRef } from 'react';
3
+
4
+ import { Unit } from '@/types/assets';
5
+ import { CxHideEvent, CxShowEvent } from '@/web-component';
6
+
7
+ import { Container } from './CustomRendition.styled';
8
+ import { Crop, Extension, Format, Resize, Rotate } from './transformations';
9
+ import { Proxy } from '@/types/search';
10
+
11
+ type Props = {
12
+ activeSetting: string;
13
+ extensions: { displayName: string; value: string }[];
14
+ availableProxies: Proxy[];
15
+ imageSize: {
16
+ width: number;
17
+ height: number;
18
+ }
19
+ resize: {
20
+ width: number;
21
+ height: number;
22
+ unit: Unit;
23
+ };
24
+ crop: {
25
+ width: number;
26
+ height: number;
27
+ unit: Unit;
28
+ percentageWidth: number;
29
+ percentageHeight: number;
30
+ };
31
+ extension: string;
32
+ lastAppliedResize: Record<Unit, {
33
+ width: number;
34
+ height: number;
35
+ unit: Unit;
36
+ }>;
37
+ lastAppliedCrop: Record<Unit, {
38
+ width: number;
39
+ height: number;
40
+ percentageWidth: number;
41
+ percentageHeight: number;
42
+ x: number;
43
+ y: number;
44
+ unit: Unit;
45
+ }>;
46
+ proxy: string;
47
+ rotation: number;
48
+ onCropChange: (
49
+ width: number,
50
+ height: number,
51
+ unit: Unit,
52
+ shouldApply: boolean
53
+ ) => void;
54
+ onExtensionChange: (extension: string) => void;
55
+ onFormatChange: (format: Proxy) => void;
56
+ onResizeChange: (
57
+ width: number,
58
+ height: number,
59
+ unit: Unit,
60
+ shouldApply: boolean
61
+ ) => void;
62
+ onRotateChange: (rotation: number, shouldApply: boolean) => void;
63
+ onViewChange: (view: string) => void;
64
+ };
65
+
66
+ const CustomRendition: FC<Props> = ({
67
+ activeSetting,
68
+ extensions,
69
+ availableProxies,
70
+ imageSize,
71
+ resize,
72
+ crop,
73
+ extension,
74
+ lastAppliedCrop,
75
+ lastAppliedResize,
76
+ proxy,
77
+ rotation,
78
+ onCropChange,
79
+ onExtensionChange,
80
+ onFormatChange,
81
+ onResizeChange,
82
+ onRotateChange,
83
+ onViewChange,
84
+ }: Props) => {
85
+ const containerRef = useRef<HTMLDivElement>(null);
86
+
87
+ useEffect(() => {
88
+ const container = containerRef.current;
89
+ if (!container) { return; }
90
+
91
+ const onDetailsShow = (e: CxShowEvent) => {
92
+ if ((e.target as HTMLElement).localName === 'cx-details') {
93
+ onViewChange((e.target as HTMLElement).dataset.value ?? '');
94
+ }
95
+ };
96
+ const onDetailsHide = (e: CxHideEvent) => {
97
+ const allDetails = container?.querySelectorAll('cx-details');
98
+
99
+ if ((e.target as HTMLElement).localName === 'cx-details') {
100
+ const allDetailsClosed = Array.from(allDetails).every((details) => {
101
+ if (details.open) {
102
+ return false;
103
+ }
104
+ return true;
105
+ });
106
+
107
+ if (allDetailsClosed) {
108
+ onViewChange('');
109
+ }
110
+ }
111
+ };
112
+ container.addEventListener('cx-show', onDetailsShow);
113
+ container.addEventListener('cx-hide', onDetailsHide);
114
+
115
+ return () => {
116
+ container.removeEventListener('cx-show', onDetailsShow);
117
+ container.removeEventListener('cx-hide', onDetailsHide);
118
+ };
119
+ }, [onViewChange]);
120
+
121
+ const disabledCropApply = useMemo(() => {
122
+ return _isEqualWith(crop, lastAppliedCrop[crop.unit], (a, b) => {
123
+ if (!isNaN(a) && !isNaN(b)) {
124
+ return Math.round(a) === Math.round(b);
125
+ }
126
+ return undefined;
127
+ });
128
+ }, [crop, lastAppliedCrop]);
129
+
130
+ const formats = useMemo(() => {
131
+ return availableProxies.filter(item => !item.cdnName);
132
+ }, [availableProxies]);
133
+
134
+ return (
135
+ <Container ref={containerRef}>
136
+ <Format
137
+ open={activeSetting === 'format'}
138
+ format={proxy}
139
+ formats={formats}
140
+ onApply={onFormatChange}
141
+ />
142
+ <Crop
143
+ open={activeSetting === 'crop'}
144
+ width={crop.width}
145
+ height={crop.height}
146
+ percentageWidth={crop.percentageWidth}
147
+ percentageHeight={crop.percentageHeight}
148
+ disabledCropApply={disabledCropApply}
149
+ lastAppliedSetting={lastAppliedCrop}
150
+ maxWidth={resize.width}
151
+ maxHeight={resize.height}
152
+ unit={crop.unit}
153
+ onChange={(width, height, unit) => onCropChange(width, height, unit, false)}
154
+ onApply={() => onCropChange(crop.width, crop.height, crop.unit, true)}
155
+ />
156
+ <Resize
157
+ open={activeSetting === 'resize'}
158
+ width={resize.width}
159
+ height={resize.height}
160
+ lastAppliedSetting={lastAppliedResize}
161
+ maxWidth={imageSize.width}
162
+ maxHeight={imageSize.height}
163
+ unit={resize.unit}
164
+ onChange={(width, height, unit) => onResizeChange(width, height, unit, false)}
165
+ onApply={() => onResizeChange(resize.width, resize.height, resize.unit, true)}
166
+ />
167
+ <Rotate
168
+ open={activeSetting === 'rotate'}
169
+ rotation={rotation}
170
+ onChange={(newRotation) => onRotateChange(newRotation, false)}
171
+ onApply={() => onRotateChange(rotation, true)}
172
+ />
173
+ <Extension extension={extension} extensions={extensions} onChange={onExtensionChange} />
174
+ </Container>
175
+ );
176
+ };
177
+
178
+ export default CustomRendition;
@@ -0,0 +1 @@
1
+ export { default } from './CustomRendition';