@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.
- package/.env +1 -0
- package/.eslintignore +2 -0
- package/.eslintrc.json +82 -0
- package/.releaserc +17 -0
- package/.travis.yml +20 -0
- package/CBSDKdemo.html +315 -0
- package/GitVersion.yml +18 -0
- package/README.md +57 -0
- package/azure-pipeline.yaml +94 -0
- package/clientlib.config.js +36 -0
- package/config/env.js +105 -0
- package/config/getHttpsConfig.js +67 -0
- package/config/jest/babelTransform.js +30 -0
- package/config/jest/cssTransform.js +14 -0
- package/config/jest/fileTransform.js +41 -0
- package/config/modules.js +135 -0
- package/config/paths.js +79 -0
- package/config/webpack/persistentCache/createEnvironmentHash.js +10 -0
- package/config/webpack.config.js +762 -0
- package/config/webpackDevServer.config.js +128 -0
- package/config-overrides.js +8 -0
- package/gab_extension/GAB.html +85 -0
- package/gab_extension/GoogleChrome/manifest.json +28 -0
- package/gab_extension/GoogleChrome/src/assets/icon48.png +0 -0
- package/gab_extension/GoogleChrome/src/background/index.js +6 -0
- package/gab_extension/GoogleChrome/src/scripts/index.js +347 -0
- package/gab_extension/MozillaFirefox/manifest.json +20 -0
- package/gab_extension/MozillaFirefox/src/assets/icon.png +0 -0
- package/gab_extension/MozillaFirefox/src/background/index.js +5 -0
- package/gab_extension/MozillaFirefox/src/scripts/index.js +347 -0
- package/gab_extension/README.md +11 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.pbxproj +927 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/project.xcworkspace/xcuserdata/oldevmac01.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Orange DAM Asset Browser Extension.xcodeproj/xcuserdata/oldevmac01.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +63 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/Contents.json +6 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +20 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Base.lproj/Main.html +23 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Icon.png +0 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Script.js +24 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/Resources/Style.css +61 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (App)/ViewController.swift +81 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/Shared (Extension)/SafariWebExtensionHandler.swift +26 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/AppDelegate.swift +24 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Base.lproj/LaunchScreen.storyboard +36 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Base.lproj/Main.storyboard +38 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/Info.plist +27 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (App)/SceneDelegate.swift +18 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/iOS (Extension)/Info.plist +13 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/AppDelegate.swift +21 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Base.lproj/Main.storyboard +125 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Info.plist +8 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (App)/Orange DAM Asset Browser Extension.entitlements +12 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (Extension)/Info.plist +13 -0
- package/gab_extension/Safari/Orange DAM Asset Browser Extension/macOS (Extension)/Orange DAM Asset Browser Extension.entitlements +10 -0
- package/package.json +192 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +92 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/scripts/build.js +218 -0
- package/scripts/start.js +154 -0
- package/scripts/test.js +53 -0
- package/src/App.tsx +98 -0
- package/src/AppContext.ts +18 -0
- package/src/GlobalConfigContext.ts +46 -0
- package/src/components/ArrayClamp/ArrayClamp.styled.ts +42 -0
- package/src/components/ArrayClamp/ArrayClamp.tsx +167 -0
- package/src/components/ArrayClamp/index.ts +1 -0
- package/src/components/Browser/Browser.styled.ts +82 -0
- package/src/components/Browser/Browser.tsx +284 -0
- package/src/components/Browser/BrowserItem.tsx +98 -0
- package/src/components/Browser/index.ts +1 -0
- package/src/components/ControlBar/ControlBar.constants.tsx +66 -0
- package/src/components/ControlBar/ControlBar.styled.ts +82 -0
- package/src/components/ControlBar/ControlBar.tsx +528 -0
- package/src/components/ControlBar/Facet/Facet.tsx +113 -0
- package/src/components/ControlBar/Facet/index.ts +1 -0
- package/src/components/ControlBar/index.ts +1 -0
- package/src/components/FormatDialog/CropPreviewer/CropPreviewer.tsx +224 -0
- package/src/components/FormatDialog/CropPreviewer/index.ts +3 -0
- package/src/components/FormatDialog/CustomRendition/CustomRendition.constants.ts +24 -0
- package/src/components/FormatDialog/CustomRendition/CustomRendition.styled.ts +57 -0
- package/src/components/FormatDialog/CustomRendition/CustomRendition.tsx +178 -0
- package/src/components/FormatDialog/CustomRendition/index.ts +1 -0
- package/src/components/FormatDialog/CustomRendition/transformations/Crop.tsx +249 -0
- package/src/components/FormatDialog/CustomRendition/transformations/Extension.tsx +54 -0
- package/src/components/FormatDialog/CustomRendition/transformations/Format.tsx +86 -0
- package/src/components/FormatDialog/CustomRendition/transformations/Resize.tsx +176 -0
- package/src/components/FormatDialog/CustomRendition/transformations/Rotate.tsx +101 -0
- package/src/components/FormatDialog/CustomRendition/transformations/index.ts +5 -0
- package/src/components/FormatDialog/FormatDialog.styled.ts +137 -0
- package/src/components/FormatDialog/FormatDialog.tsx +1533 -0
- package/src/components/FormatDialog/Previewer/Previewer.styled.ts +31 -0
- package/src/components/FormatDialog/Previewer/Previewer.tsx +143 -0
- package/src/components/FormatDialog/Previewer/index.ts +1 -0
- package/src/components/FormatDialog/ProxyMenu/ProxyMenu.styled.ts +88 -0
- package/src/components/FormatDialog/ProxyMenu/ProxyMenu.tsx +74 -0
- package/src/components/FormatDialog/ProxyMenu/index.ts +1 -0
- package/src/components/FormatDialog/TrackingParameters/TrackingParameters.tsx +59 -0
- package/src/components/FormatDialog/TrackingParameters/index.ts +1 -0
- package/src/components/FormatDialog/index.ts +1 -0
- package/src/components/Header/Header.styled.ts +51 -0
- package/src/components/Header/Header.tsx +118 -0
- package/src/components/Header/index.ts +1 -0
- package/src/components/Loader/Loader.tsx +37 -0
- package/src/components/Loader/index.ts +1 -0
- package/src/components/NoResult/NoResult.tsx +37 -0
- package/src/components/NoResult/index.tsx +1 -0
- package/src/components/Result/AssetCard/AssetCard.styled.ts +120 -0
- package/src/components/Result/AssetCard/AssetCard.tsx +192 -0
- package/src/components/Result/AssetCard/AssetCardWrapper.styled.ts +35 -0
- package/src/components/Result/AssetCard/AssetCardWrapper.tsx +165 -0
- package/src/components/Result/AssetCard/index.ts +1 -0
- package/src/components/Result/AssetPreview/AssetPreview.styled.ts +108 -0
- package/src/components/Result/AssetPreview/AssetPreview.tsx +78 -0
- package/src/components/Result/AssetPreview/ImagePreview/ImagePreview.tsx +42 -0
- package/src/components/Result/AssetPreview/ImagePreview/index.ts +1 -0
- package/src/components/Result/AssetPreview/OtherPreview/OtherPreview.styled.ts +23 -0
- package/src/components/Result/AssetPreview/OtherPreview/OtherPreview.tsx +28 -0
- package/src/components/Result/AssetPreview/OtherPreview/index.ts +1 -0
- package/src/components/Result/AssetPreview/VideoPreview/VideoPreview.tsx +132 -0
- package/src/components/Result/AssetPreview/VideoPreview/index.ts +1 -0
- package/src/components/Result/AssetPreview/index.ts +1 -0
- package/src/consts/asset.ts +16 -0
- package/src/consts/data.ts +17 -0
- package/src/index.tsx +305 -0
- package/src/page/Authenticate/Authenticate.tsx +232 -0
- package/src/page/Authenticate/ConnectingBackground.tsx +44 -0
- package/src/page/Authenticate/index.tsx +94 -0
- package/src/page/Home/Home.styled.ts +46 -0
- package/src/page/Home/Home.tsx +941 -0
- package/src/page/Home/index.ts +1 -0
- package/src/react-web-component.d.ts +4617 -0
- package/src/store/assets/assets.api.ts +167 -0
- package/src/store/assets/assets.service.ts +223 -0
- package/src/store/assets/assets.slice.ts +104 -0
- package/src/store/auth/auth.service.ts +71 -0
- package/src/store/auth/auth.slice.ts +295 -0
- package/src/store/index.ts +27 -0
- package/src/store/search/search.api.ts +319 -0
- package/src/store/search/search.slice.ts +28 -0
- package/src/store/user/user.api.ts +29 -0
- package/src/styles.css +42 -0
- package/src/types/assets.ts +71 -0
- package/src/types/auth.ts +42 -0
- package/src/types/common.ts +11 -0
- package/src/types/download.ts +8 -0
- package/src/types/navigation.ts +3 -0
- package/src/types/search.ts +116 -0
- package/src/types/storage.ts +1 -0
- package/src/types/user.ts +6 -0
- package/src/utils/api.ts +186 -0
- package/src/utils/array.ts +25 -0
- package/src/utils/constants.ts +12 -0
- package/src/utils/fetch.ts +116 -0
- package/src/utils/getRequestUrl.ts +15 -0
- package/src/utils/hooks.ts +36 -0
- package/src/utils/icon.ts +22 -0
- package/src/utils/image.ts +157 -0
- package/src/utils/number.ts +11 -0
- package/src/utils/rotate.ts +23 -0
- package/src/utils/storage.ts +184 -0
- package/src/utils/string.ts +24 -0
- package/src/view/AssetsPicker.tsx +24 -0
- package/src/web-component.d.ts +8151 -0
- package/tsconfig.eslint.json +10 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import _debounce from 'lodash-es/debounce';
|
|
2
|
+
import { FC, FormEvent, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Unit } from '@/types/assets';
|
|
5
|
+
import { calculateAspectRatioFit } from '@/utils/image';
|
|
6
|
+
import { CxChangeEvent, CxInput, CxSelect } from '@/web-component';
|
|
7
|
+
|
|
8
|
+
import { cropModes, INPUT_DEBOUNCE_DELAY } from '../CustomRendition.constants';
|
|
9
|
+
type Props = {
|
|
10
|
+
open: boolean;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
disabledCropApply: boolean;
|
|
14
|
+
lastAppliedSetting: Record<Unit, {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
percentageWidth: number;
|
|
18
|
+
percentageHeight: number;
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
unit: Unit;
|
|
22
|
+
}>;
|
|
23
|
+
maxWidth: number;
|
|
24
|
+
maxHeight: number;
|
|
25
|
+
percentageWidth: number;
|
|
26
|
+
percentageHeight: number;
|
|
27
|
+
unit: Unit;
|
|
28
|
+
onChange: (
|
|
29
|
+
width: number,
|
|
30
|
+
height: number,
|
|
31
|
+
unit: Unit,
|
|
32
|
+
) => void;
|
|
33
|
+
onApply: () => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const Crop: FC<Props> = ({
|
|
37
|
+
open,
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
disabledCropApply,
|
|
41
|
+
maxWidth,
|
|
42
|
+
maxHeight,
|
|
43
|
+
percentageHeight,
|
|
44
|
+
percentageWidth,
|
|
45
|
+
unit,
|
|
46
|
+
lastAppliedSetting,
|
|
47
|
+
onChange,
|
|
48
|
+
onApply,
|
|
49
|
+
}) => {
|
|
50
|
+
const [isDefined, setIsDefined] = useState(false);
|
|
51
|
+
const [mode, setMode] = useState<string>('free');
|
|
52
|
+
const [keepAspectRatio, setKeepAspectRatio] = useState(true);
|
|
53
|
+
const [invalidHeight, setInvalidHeight] = useState(false);
|
|
54
|
+
const [invalidWidth, setInvalidWidth] = useState(false);
|
|
55
|
+
|
|
56
|
+
const unitSelectRef = useRef<CxSelect>(null);
|
|
57
|
+
const formatSelectRef = useRef<CxSelect>(null);
|
|
58
|
+
const heightInputRef = useRef<CxInput>(null);
|
|
59
|
+
const widthInputRef = useRef<CxInput>(null);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setMode('free');
|
|
63
|
+
}, [open]);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!open) {
|
|
67
|
+
if (heightInputRef.current) {
|
|
68
|
+
heightInputRef.current.value = height.toString();
|
|
69
|
+
}
|
|
70
|
+
if (widthInputRef.current) {
|
|
71
|
+
widthInputRef.current.value = width.toString();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}, [height, open, width]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
Promise.all([
|
|
78
|
+
customElements.whenDefined('cx-input'),
|
|
79
|
+
customElements.whenDefined('cx-select'),
|
|
80
|
+
]).then(() => {
|
|
81
|
+
setIsDefined(true);
|
|
82
|
+
});
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const unitSelect = unitSelectRef.current;
|
|
87
|
+
if (!unitSelect || !isDefined) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const onUnitChange = (e: CxChangeEvent) => {
|
|
91
|
+
if ((e.target as HTMLOptionElement).value === Unit.AspectRatio) {
|
|
92
|
+
onChange(lastAppliedSetting[Unit.AspectRatio].width, lastAppliedSetting[Unit.AspectRatio].height, Unit.AspectRatio);
|
|
93
|
+
} else {
|
|
94
|
+
onChange(lastAppliedSetting[Unit.Pixel].width, lastAppliedSetting[Unit.Pixel].height, Unit.Pixel);
|
|
95
|
+
}
|
|
96
|
+
setMode('free');
|
|
97
|
+
};
|
|
98
|
+
unitSelect.addEventListener('cx-change', onUnitChange);
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
unitSelect.removeEventListener('cx-change', onUnitChange);
|
|
102
|
+
};
|
|
103
|
+
}, [isDefined, width, height, onChange, maxWidth, maxHeight, percentageWidth, percentageHeight, lastAppliedSetting]);
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const formatSelect = formatSelectRef.current;
|
|
107
|
+
if (!formatSelect || !isDefined) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const onModeChange = (e: CxChangeEvent) => {
|
|
111
|
+
const value = (e.target as HTMLOptionElement).value;
|
|
112
|
+
if (value === 'free') {
|
|
113
|
+
setKeepAspectRatio(false);
|
|
114
|
+
} else {
|
|
115
|
+
setKeepAspectRatio(true);
|
|
116
|
+
const [widthRatio, heightRatio] = value.split(':').map(Number);
|
|
117
|
+
if (unit === Unit.Pixel) {
|
|
118
|
+
// If image size is 1024x1024 and user selects 16:9, the new size should be 1024x576
|
|
119
|
+
const { width: newWidth, height: newHeight } = calculateAspectRatioFit(maxWidth, maxHeight, widthRatio, heightRatio);
|
|
120
|
+
onChange(newWidth, newHeight, unit);
|
|
121
|
+
} else {
|
|
122
|
+
onChange(widthRatio, heightRatio, unit);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
setMode((e.target as HTMLOptionElement).value);
|
|
126
|
+
};
|
|
127
|
+
formatSelect.addEventListener('cx-change', onModeChange);
|
|
128
|
+
|
|
129
|
+
return () => {
|
|
130
|
+
formatSelect.removeEventListener('cx-change', onModeChange);
|
|
131
|
+
};
|
|
132
|
+
}, [isDefined, maxWidth, maxHeight, unit, onChange]);
|
|
133
|
+
|
|
134
|
+
const aspectRatio = useMemo(() => {
|
|
135
|
+
return maxWidth / maxHeight;
|
|
136
|
+
}, [maxWidth, maxHeight]);
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
const handleWidthChange = _debounce((e: FormEvent<CxInput>) => {
|
|
140
|
+
if (!(e.target as HTMLInputElement).value) {
|
|
141
|
+
setInvalidWidth(true);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
setInvalidWidth(false);
|
|
145
|
+
const newWidth = Math.max(Math.min(Number((e.target as HTMLInputElement).value), maxWidth), 1);
|
|
146
|
+
(e.target as HTMLInputElement).value = newWidth.toString();
|
|
147
|
+
let newHeight = height;
|
|
148
|
+
if (keepAspectRatio) {
|
|
149
|
+
newHeight = Math.min(Math.round(newWidth / aspectRatio), maxHeight);
|
|
150
|
+
setInvalidHeight(false);
|
|
151
|
+
}
|
|
152
|
+
onChange(newWidth, newHeight, unit);
|
|
153
|
+
}, INPUT_DEBOUNCE_DELAY);
|
|
154
|
+
|
|
155
|
+
const handleHeightChange = _debounce((e: FormEvent<CxInput>) => {
|
|
156
|
+
if (!(e.target as HTMLInputElement).value) {
|
|
157
|
+
setInvalidHeight(true);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
setInvalidHeight(false);
|
|
161
|
+
const newHeight = Math.max(Math.min(Number((e.target as HTMLInputElement).value), maxHeight), 1);
|
|
162
|
+
(e.target as HTMLInputElement).value = newHeight.toString();
|
|
163
|
+
let newWidth = width;
|
|
164
|
+
if (keepAspectRatio) {
|
|
165
|
+
newWidth = Math.min(Math.round(newHeight * aspectRatio), maxWidth);
|
|
166
|
+
setInvalidWidth(false);
|
|
167
|
+
}
|
|
168
|
+
onChange(newWidth, newHeight, unit);
|
|
169
|
+
}, INPUT_DEBOUNCE_DELAY);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<cx-details open={open} data-value="crop">
|
|
173
|
+
<cx-space slot="summary" align-items='center'>
|
|
174
|
+
<div className="details__summary__icon">
|
|
175
|
+
<cx-icon name="crop"></cx-icon>
|
|
176
|
+
</div>
|
|
177
|
+
<cx-typography variant="body2">Crop</cx-typography>
|
|
178
|
+
</cx-space>
|
|
179
|
+
<cx-space direction="vertical" spacing="small">
|
|
180
|
+
<cx-select
|
|
181
|
+
ref={formatSelectRef}
|
|
182
|
+
value={isDefined ? mode : ''}
|
|
183
|
+
hoist
|
|
184
|
+
style={{
|
|
185
|
+
width: '100%',
|
|
186
|
+
}}
|
|
187
|
+
>
|
|
188
|
+
{cropModes.map(({ value, label }) => (
|
|
189
|
+
<cx-option key={value} value={value}>{label}</cx-option>
|
|
190
|
+
))}
|
|
191
|
+
</cx-select>
|
|
192
|
+
<cx-space spacing="small">
|
|
193
|
+
<div className="resize__input-group">
|
|
194
|
+
<cx-input
|
|
195
|
+
ref={widthInputRef}
|
|
196
|
+
placeholder="Width"
|
|
197
|
+
type="number"
|
|
198
|
+
step="1"
|
|
199
|
+
value={isDefined ? width.toString() : ''}
|
|
200
|
+
onInput={handleWidthChange}
|
|
201
|
+
required
|
|
202
|
+
>
|
|
203
|
+
<cx-typography slot="prefix" variant="body3" className="details__summary__input-label">W</cx-typography>
|
|
204
|
+
</cx-input>
|
|
205
|
+
<cx-icon-button
|
|
206
|
+
name={keepAspectRatio ? 'insert_link' : 'link_off'}
|
|
207
|
+
onClick={() => {
|
|
208
|
+
if (keepAspectRatio) {
|
|
209
|
+
setMode('free');
|
|
210
|
+
}
|
|
211
|
+
setKeepAspectRatio(!keepAspectRatio);
|
|
212
|
+
}}
|
|
213
|
+
style={{
|
|
214
|
+
color: keepAspectRatio ? 'var(--cx-color-primary)' : undefined,
|
|
215
|
+
}}
|
|
216
|
+
></cx-icon-button>
|
|
217
|
+
<cx-input
|
|
218
|
+
ref={heightInputRef}
|
|
219
|
+
placeholder="Height"
|
|
220
|
+
type="number"
|
|
221
|
+
step="1"
|
|
222
|
+
value={isDefined ? height.toString() : ''}
|
|
223
|
+
onInput={handleHeightChange}
|
|
224
|
+
required
|
|
225
|
+
>
|
|
226
|
+
<cx-typography slot="prefix" variant="body3" className="details__summary__input-label">H</cx-typography>
|
|
227
|
+
</cx-input>
|
|
228
|
+
</div>
|
|
229
|
+
<cx-select value={isDefined ? unit : ''} ref={unitSelectRef} hoist className="resize__unit-select">
|
|
230
|
+
<cx-option value="pixels">Pixels</cx-option>
|
|
231
|
+
<cx-option value="aspect-ratio">Aspect ratio</cx-option>
|
|
232
|
+
</cx-select>
|
|
233
|
+
</cx-space>
|
|
234
|
+
<cx-button
|
|
235
|
+
variant="primary"
|
|
236
|
+
disabled={disabledCropApply || invalidHeight || invalidWidth}
|
|
237
|
+
style={{
|
|
238
|
+
marginLeft: 'auto',
|
|
239
|
+
}}
|
|
240
|
+
onClick={onApply}
|
|
241
|
+
>
|
|
242
|
+
Apply
|
|
243
|
+
</cx-button>
|
|
244
|
+
</cx-space>
|
|
245
|
+
</cx-details>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export default Crop;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { CxSelect, CxChangeEvent } from '@/web-component';
|
|
2
|
+
import { useState, useRef, useEffect, FC } from 'react';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
extension: string,
|
|
6
|
+
extensions: { displayName: string; value: string }[],
|
|
7
|
+
onChange: (extension: string) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const Extension: FC<Props> = ({
|
|
11
|
+
extension,
|
|
12
|
+
extensions,
|
|
13
|
+
onChange,
|
|
14
|
+
}) => {
|
|
15
|
+
const [isDefined, setIsDefined] = useState(false);
|
|
16
|
+
const extensionSelectRef = useRef<CxSelect>(null);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
Promise.all([
|
|
20
|
+
customElements.whenDefined('cx-select'),
|
|
21
|
+
]).then(() => {
|
|
22
|
+
setIsDefined(true);
|
|
23
|
+
});
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const extensionSelect = extensionSelectRef.current;
|
|
28
|
+
if (!extensionSelect || !isDefined) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const onExtensionChange = (e: CxChangeEvent) => {
|
|
32
|
+
const newExtension = (e.target as HTMLOptionElement).value;
|
|
33
|
+
onChange(newExtension);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
extensionSelect.addEventListener('cx-change', onExtensionChange);
|
|
37
|
+
return () => {
|
|
38
|
+
extensionSelect.removeEventListener('cx-change', onExtensionChange);
|
|
39
|
+
};
|
|
40
|
+
}, [isDefined, onChange]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<cx-space direction="vertical" spacing="small" className="extension">
|
|
44
|
+
<cx-typography variant="body2">Extension</cx-typography>
|
|
45
|
+
<cx-select value={isDefined ? extension : ''} ref={extensionSelectRef} hoist>
|
|
46
|
+
{extensions.map((item) => (
|
|
47
|
+
<cx-option key={item.value} value={item.value}>{item.displayName}</cx-option>
|
|
48
|
+
))}
|
|
49
|
+
</cx-select>
|
|
50
|
+
</cx-space>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default Extension;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { CxSelect, CxChangeEvent } from '@/web-component';
|
|
2
|
+
import { useState, useRef, useEffect, FC } from 'react';
|
|
3
|
+
import { Proxy } from '@/types/search';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
format: string;
|
|
7
|
+
formats: Proxy[];
|
|
8
|
+
open: boolean;
|
|
9
|
+
onApply: (format: Proxy) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Extension: FC<Props> = ({ format, formats, open, onApply }) => {
|
|
13
|
+
const [value, setValue] = useState(format);
|
|
14
|
+
const [isDefined, setIsDefined] = useState(false);
|
|
15
|
+
const extensionSelectRef = useRef<CxSelect>(null);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
Promise.all([customElements.whenDefined('cx-select')]).then(() => {
|
|
19
|
+
setIsDefined(true);
|
|
20
|
+
});
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const extensionSelect = extensionSelectRef.current;
|
|
25
|
+
if (!extensionSelect || !isDefined) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const onExtensionChange = (e: CxChangeEvent) => {
|
|
29
|
+
const newFormatId = (e.target as HTMLOptionElement).value;
|
|
30
|
+
setValue(newFormatId);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
extensionSelect.addEventListener('cx-change', onExtensionChange);
|
|
34
|
+
return () => {
|
|
35
|
+
extensionSelect.removeEventListener('cx-change', onExtensionChange);
|
|
36
|
+
};
|
|
37
|
+
}, [formats, isDefined]);
|
|
38
|
+
|
|
39
|
+
useEffect(( ) => {
|
|
40
|
+
if (open) {
|
|
41
|
+
setValue(format);
|
|
42
|
+
}
|
|
43
|
+
}, [format, open]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<cx-details open={open} data-value="format" className="format">
|
|
47
|
+
<cx-space slot="summary" align-items="center">
|
|
48
|
+
<div className="details__summary__icon">
|
|
49
|
+
<cx-icon name="aspect_ratio"></cx-icon>
|
|
50
|
+
</div>
|
|
51
|
+
<cx-typography variant="body2">Format</cx-typography>
|
|
52
|
+
</cx-space>
|
|
53
|
+
<cx-space direction="vertical" spacing="small" className="format">
|
|
54
|
+
<cx-typography variant="body2">Format</cx-typography>
|
|
55
|
+
<cx-select
|
|
56
|
+
value={isDefined ? value : ''}
|
|
57
|
+
ref={extensionSelectRef}
|
|
58
|
+
hoist
|
|
59
|
+
>
|
|
60
|
+
{formats.map((item) => (
|
|
61
|
+
<cx-option key={item.id} value={item.id}>
|
|
62
|
+
{item.proxyLabel}
|
|
63
|
+
</cx-option>
|
|
64
|
+
))}
|
|
65
|
+
</cx-select>
|
|
66
|
+
<cx-button
|
|
67
|
+
variant="primary"
|
|
68
|
+
style={{
|
|
69
|
+
marginLeft: 'auto',
|
|
70
|
+
}}
|
|
71
|
+
onClick={() => {
|
|
72
|
+
const selectedProxy = formats.find((item) => item.id === value);
|
|
73
|
+
if (selectedProxy) {
|
|
74
|
+
onApply(selectedProxy);
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
disabled={value === format}
|
|
78
|
+
>
|
|
79
|
+
Apply
|
|
80
|
+
</cx-button>
|
|
81
|
+
</cx-space>
|
|
82
|
+
</cx-details>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default Extension;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import _debounce from 'lodash-es/debounce';
|
|
2
|
+
import { FC, FormEvent, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Unit } from '@/types/assets';
|
|
5
|
+
import { CxChangeEvent, CxInput, CxSelect } from '@/web-component';
|
|
6
|
+
import { INPUT_DEBOUNCE_DELAY } from '../CustomRendition.constants';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
open: boolean;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
lastAppliedSetting: Record<Unit, {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}>;
|
|
16
|
+
maxWidth: number;
|
|
17
|
+
maxHeight: number;
|
|
18
|
+
unit: Unit;
|
|
19
|
+
onChange: (
|
|
20
|
+
width: number,
|
|
21
|
+
height: number,
|
|
22
|
+
unit: Unit
|
|
23
|
+
) => void;
|
|
24
|
+
onApply: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const Resize: FC<Props> = ({ open, width, height, lastAppliedSetting, maxWidth, maxHeight, unit, onChange, onApply }) => {
|
|
28
|
+
const [isDefined, setIsDefined] = useState(false);
|
|
29
|
+
const [invalidHeight, setInvalidHeight] = useState(false);
|
|
30
|
+
const [invalidWidth, setInvalidWidth] = useState(false);
|
|
31
|
+
const [keepAspectRatio, setKeepAspectRatio] = useState(true);
|
|
32
|
+
const unitSelectRef = useRef<CxSelect>(null);
|
|
33
|
+
const heightInputRef = useRef<CxInput>(null);
|
|
34
|
+
const widthInputRef = useRef<CxInput>(null);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!open) {
|
|
38
|
+
if (heightInputRef.current) {
|
|
39
|
+
heightInputRef.current.value = height.toString();
|
|
40
|
+
}
|
|
41
|
+
if (widthInputRef.current) {
|
|
42
|
+
widthInputRef.current.value = width.toString();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [height, open, width]);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
Promise.all([
|
|
49
|
+
customElements.whenDefined('cx-input'),
|
|
50
|
+
customElements.whenDefined('cx-select'),
|
|
51
|
+
]).then(() => {
|
|
52
|
+
setIsDefined(true);
|
|
53
|
+
});
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const unitSelect = unitSelectRef.current;
|
|
58
|
+
if (!unitSelect || !isDefined) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const onUnitChange = (e: CxChangeEvent) => {
|
|
62
|
+
if ((e.target as HTMLOptionElement).value === Unit.AspectRatio) {
|
|
63
|
+
onChange(lastAppliedSetting[Unit.AspectRatio].width, lastAppliedSetting[Unit.AspectRatio].height, Unit.AspectRatio);
|
|
64
|
+
} else {
|
|
65
|
+
onChange(lastAppliedSetting[Unit.Pixel].width, lastAppliedSetting[Unit.Pixel].height, Unit.Pixel);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
unitSelect.addEventListener('cx-change', onUnitChange);
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
unitSelect.removeEventListener('cx-change', onUnitChange);
|
|
72
|
+
};
|
|
73
|
+
}, [isDefined, width, height, onChange, lastAppliedSetting, unit]);
|
|
74
|
+
|
|
75
|
+
const aspectRatio = useMemo(() => {
|
|
76
|
+
return maxWidth / maxHeight;
|
|
77
|
+
}, [maxWidth, maxHeight]);
|
|
78
|
+
|
|
79
|
+
const disabledApplyButton = useMemo(() => {
|
|
80
|
+
return (lastAppliedSetting[unit].width === width && lastAppliedSetting[unit].height === height);
|
|
81
|
+
}, [lastAppliedSetting, unit, width, height]);
|
|
82
|
+
|
|
83
|
+
const handleWidthChange = _debounce((e: FormEvent<CxInput>) => {
|
|
84
|
+
if (!(e.target as HTMLInputElement).value) {
|
|
85
|
+
setInvalidWidth(true);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
setInvalidWidth(false);
|
|
89
|
+
const newWidth = Math.max(Number((e.target as HTMLInputElement).value), 1);
|
|
90
|
+
(e.target as HTMLInputElement).value = newWidth.toString();
|
|
91
|
+
let newHeight = height;
|
|
92
|
+
if (keepAspectRatio) {
|
|
93
|
+
newHeight = Math.round(newWidth / aspectRatio);
|
|
94
|
+
setInvalidHeight(false);
|
|
95
|
+
}
|
|
96
|
+
onChange(newWidth, newHeight, unit);
|
|
97
|
+
}, INPUT_DEBOUNCE_DELAY);
|
|
98
|
+
|
|
99
|
+
const handleHeightChange = _debounce((e: FormEvent<CxInput>) => {
|
|
100
|
+
if (!(e.target as HTMLInputElement).value) {
|
|
101
|
+
setInvalidHeight(true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
setInvalidHeight(false);
|
|
105
|
+
const newHeight = Math.max(Number((e.target as HTMLInputElement).value), 1);
|
|
106
|
+
(e.target as HTMLInputElement).value = newHeight.toString();
|
|
107
|
+
let newWidth = width;
|
|
108
|
+
if (keepAspectRatio) {
|
|
109
|
+
newWidth = Math.round(newHeight * aspectRatio);
|
|
110
|
+
setInvalidWidth(false);
|
|
111
|
+
}
|
|
112
|
+
onChange(newWidth, newHeight, unit);
|
|
113
|
+
}, INPUT_DEBOUNCE_DELAY);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<cx-details open={open} data-value="resize" className="resize">
|
|
117
|
+
<cx-space slot="summary" align-items='center'>
|
|
118
|
+
<div className="details__summary__icon">
|
|
119
|
+
<cx-icon name="aspect_ratio"></cx-icon>
|
|
120
|
+
</div>
|
|
121
|
+
<cx-typography variant="body2">Resize</cx-typography>
|
|
122
|
+
</cx-space>
|
|
123
|
+
<cx-space direction="vertical" spacing="small">
|
|
124
|
+
<cx-space spacing="small">
|
|
125
|
+
<div className="resize__input-group">
|
|
126
|
+
<cx-input
|
|
127
|
+
ref={widthInputRef}
|
|
128
|
+
placeholder="Width"
|
|
129
|
+
type="number"
|
|
130
|
+
step="1"
|
|
131
|
+
value={isDefined ? width.toString() : ''}
|
|
132
|
+
onInput={handleWidthChange}
|
|
133
|
+
required
|
|
134
|
+
>
|
|
135
|
+
<cx-typography slot="prefix" variant="body3" className="details__summary__input-label">W</cx-typography>
|
|
136
|
+
</cx-input>
|
|
137
|
+
<cx-icon-button
|
|
138
|
+
name={keepAspectRatio ? 'insert_link' : 'link_off'}
|
|
139
|
+
onClick={() => setKeepAspectRatio(prevState => !prevState)}
|
|
140
|
+
style={{
|
|
141
|
+
color: keepAspectRatio ? 'var(--cx-color-primary)' : undefined,
|
|
142
|
+
}}
|
|
143
|
+
></cx-icon-button>
|
|
144
|
+
<cx-input
|
|
145
|
+
ref={heightInputRef}
|
|
146
|
+
placeholder="Height"
|
|
147
|
+
type="number"
|
|
148
|
+
step="1"
|
|
149
|
+
value={isDefined ? height.toString() : ''}
|
|
150
|
+
onInput={handleHeightChange}
|
|
151
|
+
required
|
|
152
|
+
>
|
|
153
|
+
<cx-typography slot="prefix" variant="body3" className="details__summary__input-label">H</cx-typography>
|
|
154
|
+
</cx-input>
|
|
155
|
+
</div>
|
|
156
|
+
<cx-select value={isDefined ? unit : ''} ref={unitSelectRef} hoist className="resize__unit-select">
|
|
157
|
+
<cx-option value="pixels">Pixels</cx-option>
|
|
158
|
+
<cx-option value="aspect-ratio">Aspect ratio</cx-option>
|
|
159
|
+
</cx-select>
|
|
160
|
+
</cx-space>
|
|
161
|
+
<cx-button
|
|
162
|
+
variant="primary"
|
|
163
|
+
disabled={disabledApplyButton || invalidHeight || invalidWidth}
|
|
164
|
+
style={{
|
|
165
|
+
marginLeft: 'auto',
|
|
166
|
+
}}
|
|
167
|
+
onClick={onApply}
|
|
168
|
+
>
|
|
169
|
+
Apply
|
|
170
|
+
</cx-button>
|
|
171
|
+
</cx-space>
|
|
172
|
+
</cx-details>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export default Resize;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import _debounce from 'lodash-es/debounce';
|
|
2
|
+
import { FormEvent, useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { CxInput } from '@/web-component';
|
|
5
|
+
import { INPUT_DEBOUNCE_DELAY } from '../CustomRendition.constants';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
open: boolean;
|
|
9
|
+
rotation: number;
|
|
10
|
+
onChange: (rotation: number) => void;
|
|
11
|
+
onApply: () => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const sanitizeRotation = (rotation: number) => {
|
|
15
|
+
if (rotation > 360) {
|
|
16
|
+
return 360;
|
|
17
|
+
} else if (rotation < 0) {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
return rotation;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const Rotate = ({ open, rotation, onChange, onApply }: Props) => {
|
|
24
|
+
const [isDefined, setIsDefined] = useState(false);
|
|
25
|
+
const [invalidRotation, setInvalidRotation] = useState(false);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
Promise.all([
|
|
29
|
+
customElements.whenDefined('cx-input'),
|
|
30
|
+
]).then(() => {
|
|
31
|
+
setIsDefined(true);
|
|
32
|
+
});
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const handleRotationChange = _debounce((e: FormEvent<CxInput>) => {
|
|
36
|
+
const value = (e.target as HTMLInputElement).value;
|
|
37
|
+
const newRotation = Number(value);
|
|
38
|
+
if (!value) {
|
|
39
|
+
setInvalidRotation(true);
|
|
40
|
+
} else {
|
|
41
|
+
setInvalidRotation(false);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// When the user change the value to a number >= 360 for the second time in a row,
|
|
45
|
+
// The input won't update the value back to 359, so we need to do it manually
|
|
46
|
+
const sanitizedRotation = sanitizeRotation(newRotation);
|
|
47
|
+
if (newRotation > 360 || newRotation < 0) {
|
|
48
|
+
(e.target as HTMLInputElement).value = sanitizedRotation.toString();
|
|
49
|
+
}
|
|
50
|
+
onChange(sanitizedRotation);
|
|
51
|
+
}, INPUT_DEBOUNCE_DELAY);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<cx-details open={open} data-value="rotate">
|
|
55
|
+
<cx-space slot="summary" align-items='center'>
|
|
56
|
+
<div className="details__summary__icon">
|
|
57
|
+
<cx-icon name="rotate_90_degrees_cw"></cx-icon>
|
|
58
|
+
</div>
|
|
59
|
+
<cx-typography variant="body2">Rotate</cx-typography>
|
|
60
|
+
</cx-space>
|
|
61
|
+
<cx-space spacing="small">
|
|
62
|
+
<cx-button-group label="History">
|
|
63
|
+
<cx-button
|
|
64
|
+
outline
|
|
65
|
+
onClick={() => onChange(sanitizeRotation(rotation - 90))}
|
|
66
|
+
>
|
|
67
|
+
<cx-icon name="rotate_90_degrees_ccw" label="Undo"></cx-icon>
|
|
68
|
+
</cx-button>
|
|
69
|
+
<cx-button
|
|
70
|
+
outline
|
|
71
|
+
onClick={() => onChange(sanitizeRotation(rotation + 90))}
|
|
72
|
+
>
|
|
73
|
+
<cx-icon name="rotate_90_degrees_cw" label="Redo"></cx-icon>
|
|
74
|
+
</cx-button>
|
|
75
|
+
</cx-button-group>
|
|
76
|
+
<cx-input
|
|
77
|
+
value={isDefined && !invalidRotation ? rotation.toString() : ''}
|
|
78
|
+
placeholder="0"
|
|
79
|
+
type="number"
|
|
80
|
+
min="0"
|
|
81
|
+
max="360"
|
|
82
|
+
style={{
|
|
83
|
+
width: '80px',
|
|
84
|
+
flex: 'none',
|
|
85
|
+
}}
|
|
86
|
+
onInput={handleRotationChange}
|
|
87
|
+
step='1'
|
|
88
|
+
></cx-input>
|
|
89
|
+
<cx-button
|
|
90
|
+
variant="primary"
|
|
91
|
+
disabled={rotation === 0 || rotation === 360 || invalidRotation}
|
|
92
|
+
onClick={onApply}
|
|
93
|
+
>
|
|
94
|
+
Apply
|
|
95
|
+
</cx-button>
|
|
96
|
+
</cx-space>
|
|
97
|
+
</cx-details>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default Rotate;
|