sanity-plugin-mux-input 2.2.4 → 2.3.0
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/README.md +148 -16
- package/lib/index.cjs +3995 -3676
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +210 -0
- package/lib/index.d.ts +109 -25
- package/lib/index.esm.js +4384 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +3957 -3625
- package/lib/index.js.map +1 -1
- package/package.json +47 -51
- package/src/_exports/index.ts +32 -0
- package/src/actions/upload.ts +35 -40
- package/src/clients/upChunkObservable.ts +5 -1
- package/src/components/ConfigureApi.tsx +0 -1
- package/src/components/FileInputArea.tsx +92 -0
- package/src/components/FileInputButton.tsx +3 -2
- package/src/components/FileInputMenuItem.styled.tsx +2 -2
- package/src/components/FileInputMenuItem.tsx +2 -10
- package/src/components/ImportVideosFromMux.tsx +317 -0
- package/src/components/Input.tsx +3 -3
- package/src/components/PlayerActionsMenu.tsx +14 -12
- package/src/components/SelectAsset.tsx +1 -1
- package/src/components/StudioTool.tsx +11 -6
- package/src/components/TextTracksEditor.tsx +214 -0
- package/src/components/UploadConfiguration.tsx +390 -0
- package/src/components/UploadPlaceholder.tsx +41 -55
- package/src/components/Uploader.styled.tsx +0 -1
- package/src/components/Uploader.tsx +384 -0
- package/src/components/VideoDetails/DeleteDialog.tsx +20 -24
- package/src/components/VideoPlayer.tsx +33 -5
- package/src/components/VideoThumbnail.tsx +21 -7
- package/src/components/VideosBrowser.tsx +6 -3
- package/src/components/withFocusRing/withFocusRing.ts +20 -22
- package/src/hooks/useClient.ts +1 -1
- package/src/hooks/useImportMuxAssets.ts +127 -0
- package/src/hooks/useMuxAssets.ts +168 -0
- package/src/plugin.tsx +5 -5
- package/src/util/asserters.ts +9 -0
- package/src/util/createSearchFilter.ts +1 -1
- package/src/util/formatBytes.ts +32 -0
- package/src/util/generateJwt.ts +1 -0
- package/src/util/getAnimatedPosterSrc.ts +1 -1
- package/src/util/getPlaybackId.ts +1 -1
- package/src/util/getPlaybackPolicy.ts +1 -1
- package/src/util/parsers.ts +5 -0
- package/src/util/types.ts +195 -12
- package/lib/index.cjs.js +0 -5
- package/src/components/__legacy__Uploader.tsx +0 -280
- package/src/index.ts +0 -29
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {DocumentVideoIcon, UploadIcon} from '@sanity/icons'
|
|
2
|
+
import {Button, Card, Checkbox, Dialog, Flex, Label, Radio, Stack, Text} from '@sanity/ui'
|
|
3
|
+
import {uuid} from '@sanity/uuid'
|
|
4
|
+
import LanguagesList from 'iso-639-1'
|
|
5
|
+
import {useEffect, useId, useReducer, useRef} from 'react'
|
|
6
|
+
import {FormField} from 'sanity'
|
|
7
|
+
|
|
8
|
+
import formatBytes from '../util/formatBytes'
|
|
9
|
+
import {
|
|
10
|
+
type AutogeneratedTextTrack,
|
|
11
|
+
type CustomTextTrack,
|
|
12
|
+
isAutogeneratedTrack,
|
|
13
|
+
isCustomTextTrack,
|
|
14
|
+
type MuxNewAssetSettings,
|
|
15
|
+
type PluginConfig,
|
|
16
|
+
type Secrets,
|
|
17
|
+
type SupportedMuxLanguage,
|
|
18
|
+
type UploadConfig,
|
|
19
|
+
type UploadTextTrack,
|
|
20
|
+
} from '../util/types'
|
|
21
|
+
import TextTracksEditor, {type TrackAction} from './TextTracksEditor'
|
|
22
|
+
import type {StagedUpload} from './Uploader'
|
|
23
|
+
|
|
24
|
+
export type UploadConfigurationStateAction =
|
|
25
|
+
| {action: 'encoding_tier'; value: UploadConfig['encoding_tier']}
|
|
26
|
+
| {action: 'max_resolution_tier'; value: UploadConfig['max_resolution_tier']}
|
|
27
|
+
| {action: 'mp4_support'; value: UploadConfig['mp4_support']}
|
|
28
|
+
| {action: 'normalize_audio'; value: UploadConfig['normalize_audio']}
|
|
29
|
+
| {action: 'signed'; value: UploadConfig['signed']}
|
|
30
|
+
| TrackAction
|
|
31
|
+
|
|
32
|
+
const ENCODING_OPTIONS = [
|
|
33
|
+
{value: 'smart', label: 'Smart'},
|
|
34
|
+
{value: 'baseline', label: 'Baseline'},
|
|
35
|
+
] as const satisfies {value: UploadConfig['encoding_tier']; label: string}[]
|
|
36
|
+
|
|
37
|
+
const RESOLUTION_TIERS = [
|
|
38
|
+
{value: '1080p', label: '1080p'},
|
|
39
|
+
{value: '1440p', label: '1440p (2k)'},
|
|
40
|
+
{value: '2160p', label: '2160p (4k)'},
|
|
41
|
+
] as const satisfies {value: UploadConfig['max_resolution_tier']; label: string}[]
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The modal for configuring a staged upload. Handles triggering of the asset
|
|
45
|
+
* upload, even if no modal needs to be shown.
|
|
46
|
+
*
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
export default function UploadConfiguration({
|
|
50
|
+
stagedUpload,
|
|
51
|
+
secrets,
|
|
52
|
+
pluginConfig,
|
|
53
|
+
startUpload,
|
|
54
|
+
onClose,
|
|
55
|
+
}: {
|
|
56
|
+
stagedUpload: StagedUpload
|
|
57
|
+
secrets: Secrets
|
|
58
|
+
pluginConfig: PluginConfig
|
|
59
|
+
startUpload: (settings: MuxNewAssetSettings) => void
|
|
60
|
+
onClose: () => void
|
|
61
|
+
}) {
|
|
62
|
+
const id = useId()
|
|
63
|
+
const autoTextTracks = useRef<NonNullable<UploadConfig['text_tracks']>>(
|
|
64
|
+
(pluginConfig.encoding_tier === 'smart' &&
|
|
65
|
+
pluginConfig.defaultAutogeneratedSubtitleLangs?.map(
|
|
66
|
+
(language_code) =>
|
|
67
|
+
({
|
|
68
|
+
_id: uuid(),
|
|
69
|
+
type: 'autogenerated',
|
|
70
|
+
language_code,
|
|
71
|
+
name: LanguagesList.getNativeName(language_code),
|
|
72
|
+
}) satisfies AutogeneratedTextTrack
|
|
73
|
+
)) ||
|
|
74
|
+
[]
|
|
75
|
+
).current
|
|
76
|
+
|
|
77
|
+
const [config, dispatch] = useReducer(
|
|
78
|
+
(prev: UploadConfig, action: UploadConfigurationStateAction) => {
|
|
79
|
+
switch (action.action) {
|
|
80
|
+
case 'encoding_tier':
|
|
81
|
+
// If encoding tier switches to baseline, remove smart-only features
|
|
82
|
+
if (action.value === 'baseline') {
|
|
83
|
+
return Object.assign({}, prev, {
|
|
84
|
+
encoding_tier: action.value,
|
|
85
|
+
mp4_support: 'none',
|
|
86
|
+
max_resolution_tier: '1080p',
|
|
87
|
+
text_tracks: prev.text_tracks?.filter(({type}) => type !== 'autogenerated'),
|
|
88
|
+
})
|
|
89
|
+
// If encoding tier switches to smart, add back in default smart features
|
|
90
|
+
}
|
|
91
|
+
return Object.assign({}, prev, {
|
|
92
|
+
encoding_tier: action.value,
|
|
93
|
+
mp4_support: pluginConfig.mp4_support,
|
|
94
|
+
max_resolution_tier: pluginConfig.max_resolution_tier,
|
|
95
|
+
text_tracks: [...autoTextTracks, ...(prev.text_tracks || [])],
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
case 'mp4_support':
|
|
99
|
+
case 'max_resolution_tier':
|
|
100
|
+
case 'normalize_audio':
|
|
101
|
+
case 'signed':
|
|
102
|
+
return Object.assign({}, prev, {[action.action]: action.value})
|
|
103
|
+
// Updating individual tracks
|
|
104
|
+
case 'track': {
|
|
105
|
+
const text_tracks = [...prev.text_tracks]
|
|
106
|
+
const target_track_i = text_tracks.findIndex(({_id}) => _id === action.id)
|
|
107
|
+
// eslint-disable-next-line default-case
|
|
108
|
+
switch (action.subAction) {
|
|
109
|
+
case 'add':
|
|
110
|
+
// Exit early if track already exists
|
|
111
|
+
if (target_track_i !== -1) break
|
|
112
|
+
text_tracks.push(
|
|
113
|
+
(prev.encoding_tier === 'smart'
|
|
114
|
+
? {_id: action.id, type: 'autogenerated'}
|
|
115
|
+
: {_id: action.id, type: 'subtitles'}) as UploadTextTrack
|
|
116
|
+
)
|
|
117
|
+
break
|
|
118
|
+
case 'update':
|
|
119
|
+
if (target_track_i === -1) break
|
|
120
|
+
text_tracks[target_track_i] = {
|
|
121
|
+
...text_tracks[target_track_i],
|
|
122
|
+
...action.value,
|
|
123
|
+
} as UploadTextTrack
|
|
124
|
+
break
|
|
125
|
+
case 'delete':
|
|
126
|
+
if (target_track_i === -1) break
|
|
127
|
+
text_tracks.splice(target_track_i, 1)
|
|
128
|
+
break
|
|
129
|
+
}
|
|
130
|
+
return Object.assign({}, prev, {text_tracks})
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
return prev
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
encoding_tier: pluginConfig.encoding_tier,
|
|
138
|
+
max_resolution_tier: pluginConfig.max_resolution_tier,
|
|
139
|
+
mp4_support: pluginConfig.mp4_support,
|
|
140
|
+
signed: secrets.enableSignedUrls && pluginConfig.defaultSigned,
|
|
141
|
+
normalize_audio: pluginConfig.normalize_audio,
|
|
142
|
+
text_tracks: autoTextTracks,
|
|
143
|
+
} as UploadConfig
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// If user-provided config is disabled, begin the upload immediately with
|
|
147
|
+
// the developer-specified values from the schema or config or defaults.
|
|
148
|
+
// This can include auto-generated subtitles!
|
|
149
|
+
const {disableTextTrackConfig, disableUploadConfig} = pluginConfig
|
|
150
|
+
const skipConfig = disableTextTrackConfig && disableUploadConfig
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (skipConfig) startUpload(formatUploadConfig(config))
|
|
153
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
154
|
+
}, [])
|
|
155
|
+
if (skipConfig) return null
|
|
156
|
+
|
|
157
|
+
const maxSupportedResolution = RESOLUTION_TIERS.findIndex(
|
|
158
|
+
(rt) => rt.value === pluginConfig.max_resolution_tier
|
|
159
|
+
)
|
|
160
|
+
return (
|
|
161
|
+
<Dialog
|
|
162
|
+
open
|
|
163
|
+
id="upload-configuration"
|
|
164
|
+
zOffset={1000}
|
|
165
|
+
width={1}
|
|
166
|
+
header="Configure Mux Upload"
|
|
167
|
+
onClose={onClose}
|
|
168
|
+
>
|
|
169
|
+
<Stack padding={4} space={2}>
|
|
170
|
+
<Label size={3}>FILE TO UPLOAD</Label>
|
|
171
|
+
<Card
|
|
172
|
+
tone="transparent"
|
|
173
|
+
border
|
|
174
|
+
padding={3}
|
|
175
|
+
paddingY={4}
|
|
176
|
+
style={{borderRadius: '0.1865rem'}}
|
|
177
|
+
>
|
|
178
|
+
<Flex gap={2}>
|
|
179
|
+
<DocumentVideoIcon fontSize="2em" />
|
|
180
|
+
<Stack space={2}>
|
|
181
|
+
<Text textOverflow="ellipsis" as="h2" size={3}>
|
|
182
|
+
{stagedUpload.type === 'file' ? stagedUpload.files[0].name : stagedUpload.url}
|
|
183
|
+
</Text>
|
|
184
|
+
<Text as="p" size={1} muted>
|
|
185
|
+
{stagedUpload.type === 'file'
|
|
186
|
+
? `Direct File Upload (${formatBytes(stagedUpload.files[0].size)})`
|
|
187
|
+
: 'File From URL (Unknown size)'}
|
|
188
|
+
</Text>
|
|
189
|
+
</Stack>
|
|
190
|
+
</Flex>
|
|
191
|
+
</Card>
|
|
192
|
+
{!disableUploadConfig && (
|
|
193
|
+
<Stack space={3} paddingBottom={2}>
|
|
194
|
+
<FormField
|
|
195
|
+
title="Encoding Tier"
|
|
196
|
+
description={
|
|
197
|
+
<>
|
|
198
|
+
The encoding tier informs the cost, quality, and available platform features for
|
|
199
|
+
the asset.{' '}
|
|
200
|
+
<a
|
|
201
|
+
href="https://docs.mux.com/guides/use-encoding-tiers"
|
|
202
|
+
target="_blank"
|
|
203
|
+
rel="noopener noreferrer"
|
|
204
|
+
>
|
|
205
|
+
See the Mux guide for more details.
|
|
206
|
+
</a>
|
|
207
|
+
</>
|
|
208
|
+
}
|
|
209
|
+
>
|
|
210
|
+
<Flex gap={3}>
|
|
211
|
+
{ENCODING_OPTIONS.map(({value, label}) => {
|
|
212
|
+
const inputId = `${id}--encodingtier-${value}`
|
|
213
|
+
return (
|
|
214
|
+
<Flex key={value} align="center" gap={2}>
|
|
215
|
+
<Radio
|
|
216
|
+
checked={config.encoding_tier === value}
|
|
217
|
+
name="asset-encodingtier"
|
|
218
|
+
onChange={(e) =>
|
|
219
|
+
dispatch({
|
|
220
|
+
action: 'encoding_tier' as const,
|
|
221
|
+
value: e.currentTarget.value as UploadConfig['encoding_tier'],
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
value={value}
|
|
225
|
+
id={inputId}
|
|
226
|
+
/>
|
|
227
|
+
<Text as="label" htmlFor={inputId}>
|
|
228
|
+
{label}
|
|
229
|
+
</Text>
|
|
230
|
+
</Flex>
|
|
231
|
+
)
|
|
232
|
+
})}
|
|
233
|
+
</Flex>
|
|
234
|
+
</FormField>
|
|
235
|
+
|
|
236
|
+
{config.encoding_tier === 'smart' && maxSupportedResolution > 0 && (
|
|
237
|
+
<FormField
|
|
238
|
+
title="Resolution Tier"
|
|
239
|
+
description={
|
|
240
|
+
<>
|
|
241
|
+
The maximum{' '}
|
|
242
|
+
<a
|
|
243
|
+
href="https://docs.mux.com/api-reference#video/operation/create-direct-upload"
|
|
244
|
+
target="_blank"
|
|
245
|
+
rel="noopener noreferrer"
|
|
246
|
+
>
|
|
247
|
+
resolution_tier
|
|
248
|
+
</a>{' '}
|
|
249
|
+
your asset is encoded, stored, and streamed at.
|
|
250
|
+
</>
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
<Flex gap={3} wrap={'wrap'}>
|
|
254
|
+
{RESOLUTION_TIERS.map(({value, label}, index) => {
|
|
255
|
+
const inputId = `${id}--type-${value}`
|
|
256
|
+
|
|
257
|
+
if (index > maxSupportedResolution) return null
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<Flex key={value} align="center" gap={2}>
|
|
261
|
+
<Radio
|
|
262
|
+
checked={config.max_resolution_tier === value}
|
|
263
|
+
name="asset-resolutiontier"
|
|
264
|
+
onChange={(e) =>
|
|
265
|
+
dispatch({
|
|
266
|
+
action: 'max_resolution_tier',
|
|
267
|
+
value: e.currentTarget.value as UploadConfig['max_resolution_tier'],
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
value={value}
|
|
271
|
+
id={inputId}
|
|
272
|
+
/>
|
|
273
|
+
<Text as="label" htmlFor={inputId}>
|
|
274
|
+
{label}
|
|
275
|
+
</Text>
|
|
276
|
+
</Flex>
|
|
277
|
+
)
|
|
278
|
+
})}
|
|
279
|
+
</Flex>
|
|
280
|
+
</FormField>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{(secrets.enableSignedUrls || config.encoding_tier === 'smart') && (
|
|
284
|
+
<FormField title="Additional Configuration">
|
|
285
|
+
<Stack space={2}>
|
|
286
|
+
{secrets.enableSignedUrls && (
|
|
287
|
+
<Flex align="center" gap={2}>
|
|
288
|
+
<Checkbox
|
|
289
|
+
id={`${id}--signed`}
|
|
290
|
+
style={{display: 'block'}}
|
|
291
|
+
name="signed"
|
|
292
|
+
required
|
|
293
|
+
checked={config.signed}
|
|
294
|
+
onChange={(e) =>
|
|
295
|
+
dispatch({
|
|
296
|
+
action: 'signed',
|
|
297
|
+
value: e.currentTarget.checked,
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
/>
|
|
301
|
+
<Text>
|
|
302
|
+
<label htmlFor={`${id}--signed`}>Signed playback URL</label>
|
|
303
|
+
</Text>
|
|
304
|
+
</Flex>
|
|
305
|
+
)}
|
|
306
|
+
{config.encoding_tier === 'smart' && (
|
|
307
|
+
<Flex align="center" gap={2}>
|
|
308
|
+
<Checkbox
|
|
309
|
+
id={`${id}--mp4_support`}
|
|
310
|
+
style={{display: 'block'}}
|
|
311
|
+
name="mp4_support"
|
|
312
|
+
required
|
|
313
|
+
checked={config.mp4_support === 'standard'}
|
|
314
|
+
onChange={(e) =>
|
|
315
|
+
dispatch({
|
|
316
|
+
action: 'mp4_support',
|
|
317
|
+
value: e.currentTarget.checked ? 'standard' : 'none',
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
321
|
+
<Text>
|
|
322
|
+
<label htmlFor={`${id}--mp4_support`}>
|
|
323
|
+
MP4 support (allow downloading)
|
|
324
|
+
</label>
|
|
325
|
+
</Text>
|
|
326
|
+
</Flex>
|
|
327
|
+
)}
|
|
328
|
+
</Stack>
|
|
329
|
+
</FormField>
|
|
330
|
+
)}
|
|
331
|
+
</Stack>
|
|
332
|
+
)}
|
|
333
|
+
|
|
334
|
+
{!disableTextTrackConfig && (
|
|
335
|
+
<TextTracksEditor
|
|
336
|
+
canAutoGenerate={config.encoding_tier === 'smart'}
|
|
337
|
+
tracks={config.text_tracks}
|
|
338
|
+
dispatch={dispatch}
|
|
339
|
+
/>
|
|
340
|
+
)}
|
|
341
|
+
|
|
342
|
+
<Button
|
|
343
|
+
icon={UploadIcon}
|
|
344
|
+
text="Upload"
|
|
345
|
+
tone="positive"
|
|
346
|
+
onClick={() => startUpload(formatUploadConfig(config))}
|
|
347
|
+
/>
|
|
348
|
+
</Stack>
|
|
349
|
+
</Dialog>
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function formatUploadConfig(config: UploadConfig): MuxNewAssetSettings {
|
|
354
|
+
const generated_subtitles = config.text_tracks
|
|
355
|
+
.filter<AutogeneratedTextTrack>(isAutogeneratedTrack)
|
|
356
|
+
.map<{name: string; language_code: SupportedMuxLanguage}>((track) => ({
|
|
357
|
+
name: track.name,
|
|
358
|
+
language_code: track.language_code,
|
|
359
|
+
}))
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
input: [
|
|
363
|
+
{
|
|
364
|
+
type: 'video',
|
|
365
|
+
generated_subtitles: generated_subtitles.length > 0 ? generated_subtitles : undefined,
|
|
366
|
+
},
|
|
367
|
+
...config.text_tracks.filter<CustomTextTrack>(isCustomTextTrack).reduce(
|
|
368
|
+
(acc, track) => {
|
|
369
|
+
if (track.language_code && track.file && track.name) {
|
|
370
|
+
acc.push({
|
|
371
|
+
url: track.file.contents,
|
|
372
|
+
type: 'text',
|
|
373
|
+
text_type: track.type === 'subtitles' ? 'subtitles' : undefined,
|
|
374
|
+
language_code: track.language_code,
|
|
375
|
+
name: track.name,
|
|
376
|
+
closed_captions: track.type === 'captions',
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
return acc
|
|
380
|
+
},
|
|
381
|
+
[] as NonNullable<MuxNewAssetSettings['input']>
|
|
382
|
+
),
|
|
383
|
+
],
|
|
384
|
+
mp4_support: config.mp4_support,
|
|
385
|
+
playback_policy: config.signed ? ['public', 'signed'] : ['public'],
|
|
386
|
+
max_resolution_tier: config.max_resolution_tier,
|
|
387
|
+
encoding_tier: config.encoding_tier,
|
|
388
|
+
normalize_audio: config.normalize_audio,
|
|
389
|
+
}
|
|
390
|
+
}
|
|
@@ -7,18 +7,6 @@ import styled from 'styled-components'
|
|
|
7
7
|
import type {SetDialogState} from '../hooks/useDialogState'
|
|
8
8
|
import {FileInputButton, type FileInputButtonProps} from './FileInputButton'
|
|
9
9
|
|
|
10
|
-
const UploadCard = styled(Card)`
|
|
11
|
-
&& {
|
|
12
|
-
border-style: dashed;
|
|
13
|
-
}
|
|
14
|
-
`
|
|
15
|
-
|
|
16
|
-
const ConfigureApiBox = styled(Box)`
|
|
17
|
-
position: absolute;
|
|
18
|
-
top: 0;
|
|
19
|
-
right: 0;
|
|
20
|
-
`
|
|
21
|
-
|
|
22
10
|
interface UploadPlaceholderProps {
|
|
23
11
|
setDialogState: SetDialogState
|
|
24
12
|
readOnly: boolean
|
|
@@ -32,16 +20,45 @@ export default function UploadPlaceholder(props: UploadPlaceholderProps) {
|
|
|
32
20
|
const handleConfigureApi = useCallback(() => setDialogState('secrets'), [setDialogState])
|
|
33
21
|
|
|
34
22
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
23
|
+
<Card
|
|
24
|
+
sizing="border"
|
|
25
|
+
tone={readOnly ? 'transparent' : 'inherit'}
|
|
26
|
+
border
|
|
27
|
+
radius={2}
|
|
28
|
+
paddingX={3}
|
|
29
|
+
paddingY={1}
|
|
30
|
+
style={hovering ? {borderColor: 'transparent'} : undefined}
|
|
31
|
+
>
|
|
32
|
+
<Flex
|
|
33
|
+
align="center"
|
|
34
|
+
justify="space-between"
|
|
35
|
+
gap={4}
|
|
36
|
+
direction={['column', 'column', 'row']}
|
|
37
|
+
paddingY={2}
|
|
37
38
|
sizing="border"
|
|
38
|
-
height="fill"
|
|
39
|
-
tone={readOnly ? 'transparent' : 'inherit'}
|
|
40
|
-
border
|
|
41
|
-
padding={3}
|
|
42
|
-
style={hovering ? {borderColor: 'transparent'} : undefined}
|
|
43
39
|
>
|
|
44
|
-
<
|
|
40
|
+
<Flex align="center" justify="flex-start" gap={2} flex={1}>
|
|
41
|
+
<Flex justify="center">
|
|
42
|
+
<Text muted>
|
|
43
|
+
<DocumentVideoIcon />
|
|
44
|
+
</Text>
|
|
45
|
+
</Flex>
|
|
46
|
+
<Flex justify="center">
|
|
47
|
+
<Text size={1} muted>
|
|
48
|
+
Drag video or paste URL here
|
|
49
|
+
</Text>
|
|
50
|
+
</Flex>
|
|
51
|
+
</Flex>
|
|
52
|
+
<Inline space={2}>
|
|
53
|
+
<FileInputButton
|
|
54
|
+
mode="bleed"
|
|
55
|
+
tone="default"
|
|
56
|
+
icon={UploadIcon}
|
|
57
|
+
text="Upload"
|
|
58
|
+
onSelect={onSelect}
|
|
59
|
+
/>
|
|
60
|
+
<Button mode="bleed" icon={SearchIcon} text="Select" onClick={handleBrowse} />
|
|
61
|
+
|
|
45
62
|
<Button
|
|
46
63
|
padding={3}
|
|
47
64
|
radius={3}
|
|
@@ -49,41 +66,10 @@ export default function UploadPlaceholder(props: UploadPlaceholderProps) {
|
|
|
49
66
|
onClick={handleConfigureApi}
|
|
50
67
|
icon={PlugIcon}
|
|
51
68
|
mode="bleed"
|
|
69
|
+
title="Configure plugin credentials"
|
|
52
70
|
/>
|
|
53
|
-
</
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
justify="space-between"
|
|
57
|
-
gap={4}
|
|
58
|
-
direction={['column', 'column', 'row']}
|
|
59
|
-
paddingY={[2, 2, 0]}
|
|
60
|
-
sizing="border"
|
|
61
|
-
height="fill"
|
|
62
|
-
>
|
|
63
|
-
<Flex align="center" justify="center" gap={2} flex={1}>
|
|
64
|
-
<Flex justify="center">
|
|
65
|
-
<Text muted>
|
|
66
|
-
<DocumentVideoIcon />
|
|
67
|
-
</Text>
|
|
68
|
-
</Flex>
|
|
69
|
-
<Flex justify="center">
|
|
70
|
-
<Text size={1} muted>
|
|
71
|
-
Drag video or paste URL here
|
|
72
|
-
</Text>
|
|
73
|
-
</Flex>
|
|
74
|
-
</Flex>
|
|
75
|
-
<Inline space={2}>
|
|
76
|
-
<FileInputButton
|
|
77
|
-
mode="ghost"
|
|
78
|
-
tone="default"
|
|
79
|
-
icon={UploadIcon}
|
|
80
|
-
text="Upload"
|
|
81
|
-
onSelect={onSelect}
|
|
82
|
-
/>
|
|
83
|
-
<Button mode="ghost" icon={SearchIcon} text="Select" onClick={handleBrowse} />
|
|
84
|
-
</Inline>
|
|
85
|
-
</Flex>
|
|
86
|
-
</UploadCard>
|
|
87
|
-
</Box>
|
|
71
|
+
</Inline>
|
|
72
|
+
</Flex>
|
|
73
|
+
</Card>
|
|
88
74
|
)
|
|
89
75
|
}
|