sanity-plugin-mux-input 2.1.1 → 2.2.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.
Files changed (66) hide show
  1. package/lib/index.cjs +4037 -4
  2. package/lib/index.cjs.map +1 -1
  3. package/lib/index.d.ts +14 -1
  4. package/lib/index.js +4026 -2
  5. package/lib/index.js.map +1 -1
  6. package/package.json +27 -28
  7. package/src/actions/assets.ts +30 -2
  8. package/src/components/ConfigureApi.tsx +9 -1
  9. package/src/components/FormField.tsx +8 -10
  10. package/src/components/IconInfo.tsx +23 -0
  11. package/src/components/Input.styled.tsx +0 -8
  12. package/src/components/Input.tsx +4 -3
  13. package/src/components/InputBrowser.tsx +1 -8
  14. package/src/components/Player.styled.tsx +5 -144
  15. package/src/components/Player.tsx +23 -109
  16. package/src/components/PlayerActionsMenu.tsx +0 -4
  17. package/src/components/SelectAsset.tsx +18 -58
  18. package/src/components/SelectSortOptions.tsx +45 -0
  19. package/src/components/SpinnerBox.tsx +17 -0
  20. package/src/components/StudioTool.tsx +20 -0
  21. package/src/components/VideoDetails/DeleteDialog.tsx +156 -0
  22. package/src/components/VideoDetails/VideoDetails.tsx +298 -0
  23. package/src/components/VideoDetails/VideoReferences.tsx +70 -0
  24. package/src/components/VideoDetails/useVideoDetails.ts +85 -0
  25. package/src/components/VideoInBrowser.tsx +183 -0
  26. package/src/components/VideoMetadata.tsx +43 -0
  27. package/src/components/VideoPlayer.tsx +69 -0
  28. package/src/components/VideoThumbnail.tsx +106 -0
  29. package/src/components/VideosBrowser.tsx +83 -0
  30. package/src/components/__legacy__Uploader.tsx +2 -9
  31. package/src/components/documentPreview/DocumentPreview.tsx +107 -0
  32. package/src/components/documentPreview/DraftStatus.tsx +34 -0
  33. package/src/components/documentPreview/MissingSchemaType.tsx +33 -0
  34. package/src/components/documentPreview/PaneItemPreview.tsx +71 -0
  35. package/src/components/documentPreview/PublishedStatus.tsx +35 -0
  36. package/src/components/documentPreview/TimeAgo.tsx +13 -0
  37. package/src/components/documentPreview/paneItemTypes.ts +7 -0
  38. package/src/components/icons/Resolution.tsx +12 -0
  39. package/src/components/icons/StopWatch.tsx +20 -0
  40. package/src/components/icons/ToolIcon.tsx +21 -0
  41. package/src/hooks/useAssets.ts +61 -0
  42. package/src/hooks/useCancelUpload.ts +2 -2
  43. package/src/hooks/useClient.ts +3 -1
  44. package/src/hooks/useDocReferences.ts +21 -0
  45. package/src/hooks/useInView.ts +45 -0
  46. package/src/index.ts +2 -0
  47. package/src/plugin.tsx +1 -1
  48. package/src/util/constants.ts +7 -0
  49. package/src/util/createSearchFilter.ts +78 -0
  50. package/src/util/formatSeconds.ts +22 -0
  51. package/src/util/getAnimatedPosterSrc.ts +1 -1
  52. package/src/util/getPlaybackId.ts +1 -1
  53. package/src/util/getPlaybackPolicy.ts +1 -1
  54. package/src/util/getVideoMetadata.ts +18 -0
  55. package/src/util/types.ts +16 -1
  56. package/lib/_chunks/Player-547f8e2a.cjs +0 -474
  57. package/lib/_chunks/Player-547f8e2a.cjs.map +0 -1
  58. package/lib/_chunks/Player-bfdb96f6.js +0 -465
  59. package/lib/_chunks/Player-bfdb96f6.js.map +0 -1
  60. package/lib/_chunks/index-39e38243.cjs +0 -3251
  61. package/lib/_chunks/index-39e38243.cjs.map +0 -1
  62. package/lib/_chunks/index-71899191.js +0 -3229
  63. package/lib/_chunks/index-71899191.js.map +0 -1
  64. package/src/components/EditThumbnailDialog.tsx +0 -74
  65. package/src/components/VideoSource.styled.tsx +0 -235
  66. package/src/components/VideoSource.tsx +0 -318
@@ -0,0 +1,70 @@
1
+ import type {SanityDocument} from '@sanity/client'
2
+ import {Box, Card, Text} from '@sanity/ui'
3
+ import React from 'react'
4
+ import {collate, useSchema} from 'sanity'
5
+ import styled from 'styled-components'
6
+
7
+ import {PluginPlacement} from '../../util/types'
8
+ import {DocumentPreview} from '../documentPreview/DocumentPreview'
9
+ import SpinnerBox from '../SpinnerBox'
10
+
11
+ const Container = styled(Box)`
12
+ * {
13
+ color: ${(props: any) => props.theme.sanity.color.base.fg};
14
+ }
15
+ a {
16
+ text-decoration: none;
17
+ }
18
+ h2 {
19
+ font-size: ${(props: any) => props.theme.sanity.fonts.text.sizes[1]};
20
+ }
21
+ `
22
+
23
+ const FileReferences: React.FC<{
24
+ references?: SanityDocument[]
25
+ isLoaded: boolean
26
+ placement: PluginPlacement
27
+ }> = (props) => {
28
+ const schema = useSchema()
29
+ if (!props.isLoaded) {
30
+ return <SpinnerBox />
31
+ }
32
+
33
+ if (!props.references?.length) {
34
+ return (
35
+ <Text size={2} weight="bold" muted style={{marginTop: '1.5rem', textAlign: 'center'}}>
36
+ No documents are using this file
37
+ </Text>
38
+ )
39
+ }
40
+
41
+ const documentPairs = collate(props.references || [])
42
+ return (
43
+ <Container>
44
+ {documentPairs?.map((documentPair) => {
45
+ const schemaType = schema.get(documentPair.type)
46
+
47
+ return (
48
+ <Card
49
+ key={documentPair.id}
50
+ marginBottom={2}
51
+ padding={2}
52
+ radius={2}
53
+ shadow={1}
54
+ style={{overflow: 'hidden'}}
55
+ >
56
+ <Box>
57
+ <DocumentPreview
58
+ documentPair={documentPair}
59
+ schemaType={schemaType}
60
+ placement={props.placement}
61
+ />
62
+ </Box>
63
+ </Card>
64
+ )
65
+ })}
66
+ </Container>
67
+ )
68
+ }
69
+
70
+ export default FileReferences
@@ -0,0 +1,85 @@
1
+ import {useToast} from '@sanity/ui'
2
+ import {useState} from 'react'
3
+ import {useDocumentStore} from 'sanity'
4
+
5
+ import {useClient} from '../../hooks/useClient'
6
+ import useDocReferences from '../../hooks/useDocReferences'
7
+ import getVideoMetadata from '../../util/getVideoMetadata'
8
+ import {PluginPlacement, VideoAssetDocument} from '../../util/types'
9
+
10
+ export interface FileDetailsProps {
11
+ placement: PluginPlacement
12
+ closeDialog: () => void
13
+ asset: VideoAssetDocument & {autoPlay?: boolean}
14
+ }
15
+
16
+ export default function useFileDetails(props: FileDetailsProps) {
17
+ const documentStore = useDocumentStore()
18
+ const toast = useToast()
19
+ const client = useClient()
20
+ const [references, referencesLoading] = useDocReferences({
21
+ documentStore,
22
+ id: props.asset._id as string,
23
+ })
24
+
25
+ const [originalAsset, setOriginalAsset] = useState(() => props.asset)
26
+ const [filename, setFilename] = useState(props.asset.filename)
27
+ const modified = filename !== originalAsset.filename
28
+
29
+ const displayInfo = getVideoMetadata({...props.asset, filename})
30
+
31
+ const [state, setState] = useState<'deleting' | 'closing' | 'idle' | 'saving'>('idle')
32
+
33
+ function handleClose() {
34
+ if (state !== 'idle') return
35
+
36
+ if (modified) {
37
+ setState('closing')
38
+ return
39
+ }
40
+
41
+ props.closeDialog()
42
+ }
43
+
44
+ function confirmClose(shouldClose: boolean) {
45
+ if (state !== 'closing') return
46
+
47
+ if (shouldClose) props.closeDialog()
48
+
49
+ setState('idle')
50
+ }
51
+
52
+ async function saveChanges() {
53
+ if (state !== 'idle') return
54
+ setState('saving')
55
+
56
+ try {
57
+ await client.patch(props.asset._id).set({filename}).commit()
58
+ setOriginalAsset((prev) => ({...prev, filename}))
59
+ toast.push({title: 'File name updated', status: 'success'})
60
+ } catch (error) {
61
+ toast.push({
62
+ title: 'Failed updating file name',
63
+ status: 'error',
64
+ description: typeof error === 'string' ? error : 'Please try again',
65
+ })
66
+ setFilename(originalAsset.filename)
67
+ }
68
+
69
+ setState('idle')
70
+ }
71
+
72
+ return {
73
+ references,
74
+ referencesLoading,
75
+ modified,
76
+ filename,
77
+ setFilename,
78
+ displayInfo,
79
+ state,
80
+ setState,
81
+ handleClose,
82
+ confirmClose,
83
+ saveChanges,
84
+ }
85
+ }
@@ -0,0 +1,183 @@
1
+ import {CheckmarkIcon, EditIcon, LockIcon, PlayIcon} from '@sanity/icons'
2
+ import {Button, Card, Stack, Text, Tooltip} from '@sanity/ui'
3
+ import React, {useState} from 'react'
4
+ import styled from 'styled-components'
5
+
6
+ import {THUMBNAIL_ASPECT_RATIO} from '../util/constants'
7
+ import {getPlaybackPolicy} from '../util/getPlaybackPolicy'
8
+ import {VideoAssetDocument} from '../util/types'
9
+ import IconInfo from './IconInfo'
10
+ import VideoMetadata from './VideoMetadata'
11
+ import VideoPlayer from './VideoPlayer'
12
+ import VideoThumbnail from './VideoThumbnail'
13
+
14
+ const PlayButton = styled.button`
15
+ display: block;
16
+ padding: 0;
17
+ margin: 0;
18
+ border: none;
19
+ border-radius: 0.1875rem;
20
+ position: relative;
21
+ cursor: pointer;
22
+
23
+ &::after {
24
+ content: '';
25
+ background: var(--card-fg-color);
26
+ opacity: 0;
27
+ display: block;
28
+ position: absolute;
29
+ inset: 0;
30
+ z-index: 10;
31
+ transition: 0.15s ease-out;
32
+ border-radius: inherit;
33
+ }
34
+
35
+ > div[data-play] {
36
+ z-index: 11;
37
+ opacity: 0;
38
+ transition: 0.15s 0.05s ease-out;
39
+ position: absolute;
40
+ left: 50%;
41
+ top: 50%;
42
+ transform: translate(-50%, -50%);
43
+ color: var(--card-fg-color);
44
+ background: var(--card-bg-color);
45
+ width: auto;
46
+ height: 30%;
47
+ aspect-ratio: 1;
48
+ border-radius: 100%;
49
+ display: flex;
50
+ justify-content: center;
51
+ align-items: center;
52
+ box-sizing: border-box;
53
+ > svg {
54
+ display: block;
55
+ width: 70%;
56
+ height: auto;
57
+ // Visual balance to center-align the icon
58
+ transform: translateX(5%);
59
+ }
60
+ }
61
+
62
+ &:hover,
63
+ &:focus {
64
+ &::after {
65
+ opacity: 0.3;
66
+ }
67
+ > div[data-play] {
68
+ opacity: 1;
69
+ }
70
+ }
71
+ `
72
+
73
+ export default function VideoInBrowser({
74
+ onSelect,
75
+ onEdit,
76
+ asset,
77
+ }: {
78
+ onSelect?: (asset: VideoAssetDocument) => void
79
+ onEdit?: (asset: VideoAssetDocument) => void
80
+ asset: VideoAssetDocument
81
+ }) {
82
+ const [renderVideo, setRenderVideo] = useState(false)
83
+ const select = React.useCallback(() => onSelect?.(asset), [onSelect, asset])
84
+ const edit = React.useCallback(() => onEdit?.(asset), [onEdit, asset])
85
+
86
+ if (!asset) {
87
+ return null
88
+ }
89
+
90
+ const playbackPolicy = getPlaybackPolicy(asset)
91
+
92
+ return (
93
+ <Card
94
+ border
95
+ padding={2}
96
+ sizing="border"
97
+ radius={2}
98
+ style={{
99
+ position: 'relative',
100
+ }}
101
+ >
102
+ {playbackPolicy === 'signed' && (
103
+ <Tooltip
104
+ content={
105
+ <Card padding={2} radius={2}>
106
+ <IconInfo icon={LockIcon} text="Signed playback policy" size={2} />
107
+ </Card>
108
+ }
109
+ placement="right"
110
+ fallbackPlacements={['top', 'bottom']}
111
+ portal
112
+ >
113
+ <Card
114
+ tone="caution"
115
+ style={{
116
+ borderRadius: '100%',
117
+ position: 'absolute',
118
+ left: '1em',
119
+ top: '1em',
120
+ zIndex: 10,
121
+ }}
122
+ padding={2}
123
+ border
124
+ >
125
+ <Text muted size={1}>
126
+ <LockIcon />
127
+ </Text>
128
+ </Card>
129
+ </Tooltip>
130
+ )}
131
+ <Stack
132
+ space={3}
133
+ height="fill"
134
+ style={{
135
+ gridTemplateRows: 'min-content min-content 1fr',
136
+ }}
137
+ >
138
+ {renderVideo ? (
139
+ <VideoPlayer asset={asset} autoPlay forceAspectRatio={THUMBNAIL_ASPECT_RATIO} />
140
+ ) : (
141
+ <PlayButton onClick={() => setRenderVideo(true)}>
142
+ <div data-play>
143
+ <PlayIcon />
144
+ </div>
145
+ <VideoThumbnail asset={asset} />
146
+ </PlayButton>
147
+ )}
148
+ <VideoMetadata asset={asset} />
149
+ <div
150
+ style={{
151
+ display: 'flex',
152
+ width: '100%',
153
+ alignItems: 'flex-end',
154
+ justifyContent: 'flex-start',
155
+ gap: '.35rem',
156
+ }}
157
+ >
158
+ {onSelect && (
159
+ <Button
160
+ icon={CheckmarkIcon}
161
+ fontSize={2}
162
+ padding={2}
163
+ mode="ghost"
164
+ text="Select"
165
+ style={{flex: 1}}
166
+ tone="positive"
167
+ onClick={select}
168
+ />
169
+ )}
170
+ <Button
171
+ icon={EditIcon}
172
+ fontSize={2}
173
+ padding={2}
174
+ mode="ghost"
175
+ text="Details"
176
+ style={{flex: 1}}
177
+ onClick={edit}
178
+ />
179
+ </div>
180
+ </Stack>
181
+ </Card>
182
+ )
183
+ }
@@ -0,0 +1,43 @@
1
+ import {CalendarIcon, ClockIcon} from '@sanity/icons'
2
+ import {Inline, Stack, Text} from '@sanity/ui'
3
+ import React from 'react'
4
+
5
+ import getVideoMetadata from '../util/getVideoMetadata'
6
+ import {VideoAssetDocument} from '../util/types'
7
+ import IconInfo from './IconInfo'
8
+
9
+ const VideoMetadata = (props: {asset: VideoAssetDocument}) => {
10
+ if (!props.asset) {
11
+ return null
12
+ }
13
+
14
+ const displayInfo = getVideoMetadata(props.asset)
15
+ return (
16
+ <Stack space={2}>
17
+ {displayInfo.title && (
18
+ <Text
19
+ size={1}
20
+ weight="semibold"
21
+ style={{
22
+ wordWrap: 'break-word',
23
+ }}
24
+ >
25
+ {displayInfo.title}
26
+ </Text>
27
+ )}
28
+ <Inline space={3}>
29
+ {displayInfo?.duration && (
30
+ <IconInfo text={displayInfo.duration} icon={ClockIcon} size={1} muted />
31
+ )}
32
+ <IconInfo
33
+ text={displayInfo.createdAt.toISOString().split('T')[0]}
34
+ icon={CalendarIcon}
35
+ size={1}
36
+ muted
37
+ />
38
+ </Inline>
39
+ </Stack>
40
+ )
41
+ }
42
+
43
+ export default VideoMetadata
@@ -0,0 +1,69 @@
1
+ import MuxPlayer, {MuxPlayerProps} from '@mux/mux-player-react'
2
+ import {Card} from '@sanity/ui'
3
+ import React, {PropsWithChildren, useMemo} from 'react'
4
+
5
+ import {useClient} from '../hooks/useClient'
6
+ import {MIN_ASPECT_RATIO} from '../util/constants'
7
+ import {getVideoSrc} from '../util/getVideoSrc'
8
+ import type {VideoAssetDocument} from '../util/types'
9
+ import pluginPkg from './../../package.json'
10
+
11
+ export default function VideoPlayer({
12
+ asset,
13
+ children,
14
+ ...props
15
+ }: PropsWithChildren<
16
+ {asset: VideoAssetDocument; forceAspectRatio?: number} & Partial<Pick<MuxPlayerProps, 'autoPlay'>>
17
+ >) {
18
+ const client = useClient()
19
+
20
+ const videoSrc = useMemo(() => asset?.playbackId && getVideoSrc({client, asset}), [asset, client])
21
+
22
+ const signedToken = useMemo(() => {
23
+ try {
24
+ const url = new URL(videoSrc!)
25
+ return url.searchParams.get('token')
26
+ } catch {
27
+ return false
28
+ }
29
+ }, [videoSrc])
30
+
31
+ const [width, height] = (asset?.data?.aspect_ratio ?? '16:9').split(':').map(Number)
32
+ const targetAspectRatio =
33
+ props.forceAspectRatio || (Number.isNaN(width) ? 16 / 9 : width / height)
34
+ const aspectRatio = Math.max(MIN_ASPECT_RATIO, targetAspectRatio)
35
+
36
+ return (
37
+ <Card tone="transparent" style={{aspectRatio: aspectRatio, position: 'relative'}}>
38
+ {videoSrc && (
39
+ <>
40
+ <MuxPlayer
41
+ {...props}
42
+ playsInline
43
+ playbackId={asset.playbackId}
44
+ tokens={
45
+ signedToken
46
+ ? {playback: signedToken, thumbnail: signedToken, storyboard: signedToken}
47
+ : undefined
48
+ }
49
+ streamType="on-demand"
50
+ preload="metadata"
51
+ crossOrigin="anonymous"
52
+ metadata={{
53
+ player_name: 'Sanity Admin Dashboard',
54
+ player_version: pluginPkg.version,
55
+ page_type: 'Preview Player',
56
+ }}
57
+ style={{
58
+ height: '100%',
59
+ width: '100%',
60
+ display: 'block',
61
+ objectFit: 'contain',
62
+ }}
63
+ />
64
+ {children}
65
+ </>
66
+ )}
67
+ </Card>
68
+ )
69
+ }
@@ -0,0 +1,106 @@
1
+ import {ErrorOutlineIcon} from '@sanity/icons'
2
+ import {Card, CardTone, Stack, Text} from '@sanity/ui'
3
+ import React, {useMemo, useState} from 'react'
4
+ import styled from 'styled-components'
5
+
6
+ import {useClient} from '../hooks/useClient'
7
+ import useInView from '../hooks/useInView'
8
+ import {THUMBNAIL_ASPECT_RATIO} from '../util/constants'
9
+ import {getAnimatedPosterSrc} from '../util/getAnimatedPosterSrc'
10
+ import {VideoAssetDocument} from '../util/types'
11
+ import SpinnerBox from './SpinnerBox'
12
+
13
+ const Image = styled.img`
14
+ transition: opacity 0.175s ease-out 0s;
15
+ display: block;
16
+ width: 100%;
17
+ height: 100%;
18
+ object-fit: contain;
19
+ object-position: center center;
20
+ `
21
+
22
+ type ImageStatus = 'loading' | 'error' | 'loaded'
23
+
24
+ const STATUS_TO_TONE: Record<ImageStatus, CardTone> = {
25
+ loading: 'transparent',
26
+ error: 'critical',
27
+ loaded: 'default',
28
+ }
29
+
30
+ export default function VideoThumbnail({
31
+ asset,
32
+ width = 250,
33
+ }: {
34
+ asset: Partial<VideoAssetDocument>
35
+ width?: number
36
+ }) {
37
+ const {inView, ref} = useInView()
38
+
39
+ const [status, setStatus] = useState<ImageStatus>('loading')
40
+ const client = useClient()
41
+
42
+ const animatedSrc = useMemo(() => {
43
+ try {
44
+ return getAnimatedPosterSrc({asset, client, width})
45
+ } catch {
46
+ if (status !== 'error') setStatus('error')
47
+ return undefined
48
+ }
49
+ }, [asset, client, width, status, setStatus])
50
+
51
+ function handleLoad() {
52
+ setStatus('loaded')
53
+ }
54
+
55
+ function handleError() {
56
+ setStatus('error')
57
+ }
58
+
59
+ return (
60
+ <Card
61
+ style={{
62
+ aspectRatio: THUMBNAIL_ASPECT_RATIO,
63
+ position: 'relative',
64
+ }}
65
+ border
66
+ radius={2}
67
+ ref={ref as any}
68
+ tone={STATUS_TO_TONE[status]}
69
+ >
70
+ {inView ? (
71
+ <>
72
+ {status === 'loading' && <SpinnerBox />}
73
+ {status === 'error' && (
74
+ <Stack
75
+ space={4}
76
+ style={{
77
+ position: 'absolute',
78
+ width: '100%',
79
+ left: 0,
80
+ top: '50%',
81
+ transform: 'translateY(-50%)',
82
+ justifyItems: 'center',
83
+ }}
84
+ >
85
+ <Text size={4} muted>
86
+ <ErrorOutlineIcon style={{fontSize: '1.75em'}} />
87
+ </Text>
88
+ <Text muted align="center">
89
+ Failed loading thumbnail
90
+ </Text>
91
+ </Stack>
92
+ )}
93
+ <Image
94
+ src={animatedSrc}
95
+ alt={`Preview for video ${asset.filename || asset.assetId}`}
96
+ onLoad={handleLoad}
97
+ onError={handleError}
98
+ style={{
99
+ opacity: status === 'loaded' ? 1 : 0,
100
+ }}
101
+ />
102
+ </>
103
+ ) : null}
104
+ </Card>
105
+ )
106
+ }
@@ -0,0 +1,83 @@
1
+ import {SearchIcon} from '@sanity/icons'
2
+ import {Card, Flex, Grid, Label, Stack, Text, TextInput} from '@sanity/ui'
3
+ import React from 'react'
4
+
5
+ import useAssets from '../hooks/useAssets'
6
+ import type {VideoAssetDocument} from '../util/types'
7
+ import {SelectSortOptions} from './SelectSortOptions'
8
+ import SpinnerBox from './SpinnerBox'
9
+ import {FileDetailsProps} from './VideoDetails/useVideoDetails'
10
+ import VideoDetails from './VideoDetails/VideoDetails'
11
+ import VideoInBrowser from './VideoInBrowser'
12
+
13
+ export interface VideosBrowserProps {
14
+ onSelect?: (asset: VideoAssetDocument) => void
15
+ }
16
+
17
+ export default function VideosBrowser({onSelect}: VideosBrowserProps) {
18
+ const {assets, isLoading, searchQuery, setSearchQuery, setSort, sort} = useAssets()
19
+ const [editedAsset, setEditedAsset] = React.useState<FileDetailsProps['asset'] | null>(null)
20
+ const freshEditedAsset = React.useMemo(
21
+ () => assets.find((a) => a._id === editedAsset?._id) || editedAsset,
22
+ [editedAsset, assets]
23
+ )
24
+
25
+ return (
26
+ <>
27
+ <Stack padding={4} space={4}>
28
+ <Flex justify="space-between" align="center">
29
+ <Flex align="center" gap={3}>
30
+ <TextInput
31
+ value={searchQuery}
32
+ icon={SearchIcon}
33
+ onInput={(e: React.FormEvent<HTMLInputElement>) =>
34
+ setSearchQuery(e.currentTarget.value)
35
+ }
36
+ placeholder="Search files"
37
+ />
38
+ <SelectSortOptions setSort={setSort} sort={sort} />
39
+ </Flex>
40
+ </Flex>
41
+ <Stack space={3}>
42
+ {assets?.length > 0 && (
43
+ <Label muted>
44
+ {assets.length} video{assets.length > 1 ? 's' : null}{' '}
45
+ {searchQuery ? `matching "${searchQuery}"` : 'found'}
46
+ </Label>
47
+ )}
48
+ <Grid
49
+ gap={2}
50
+ style={{
51
+ gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
52
+ }}
53
+ >
54
+ {assets.map((asset) => (
55
+ <VideoInBrowser
56
+ key={asset._id}
57
+ asset={asset}
58
+ onEdit={setEditedAsset}
59
+ onSelect={onSelect}
60
+ />
61
+ ))}
62
+ </Grid>
63
+ </Stack>
64
+ {isLoading && <SpinnerBox />}
65
+
66
+ {!isLoading && assets.length === 0 && (
67
+ <Card padding={2} marginY={4} border radius={2}>
68
+ <Text align="center" muted>
69
+ {searchQuery ? `No videos found for "${searchQuery}"` : 'No videos in this dataset'}
70
+ </Text>
71
+ </Card>
72
+ )}
73
+ </Stack>
74
+ {freshEditedAsset && (
75
+ <VideoDetails
76
+ closeDialog={() => setEditedAsset(null)}
77
+ asset={freshEditedAsset}
78
+ placement={onSelect ? 'input' : 'tool'}
79
+ />
80
+ )}
81
+ </>
82
+ )
83
+ }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-nested-ternary */
2
2
  // This component needs to be refactored into a functional component
3
3
 
4
- import React, {lazy, Component} from 'react'
4
+ import React, {Component} from 'react'
5
5
  import {type Observable, Subject} from 'rxjs'
6
6
  import {takeUntil, tap} from 'rxjs/operators'
7
7
  import type {SanityClient} from 'sanity'
@@ -12,17 +12,12 @@ import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
12
12
  import {extractDroppedFiles} from '../util/extractFiles'
13
13
  import type {Config, MuxInputProps, Secrets, VideoAssetDocument} from '../util/types'
14
14
  import InputBrowser from './InputBrowser'
15
+ import Player from './Player'
15
16
  import PlayerActionsMenu from './PlayerActionsMenu'
16
17
  import {UploadCard} from './Uploader.styled'
17
18
  import UploadPlaceholder from './UploadPlaceholder'
18
19
  import {UploadProgress} from './UploadProgress'
19
20
 
20
- // TODO: Without this lazy load call a build error occurs in the Player component from the import
21
- // of media-chrome components. media-chrome ships separate ESM and CJS compatible modules in versions >=1.0.0.
22
- // Once the plugin is updated to media-chrome >= 1.0.0, remove this lazy load.
23
- // import Player from './Player'
24
- const Player = lazy(() => import('./Player'))
25
-
26
21
  interface Props extends Pick<MuxInputProps, 'onChange' | 'readOnly'> {
27
22
  config: Config
28
23
  client: SanityClient
@@ -249,8 +244,6 @@ class MuxVideoInputUploader extends Component<Props, State> {
249
244
  readOnly={this.props.readOnly}
250
245
  asset={this.props.asset}
251
246
  onChange={this.props.onChange}
252
- dialogState={this.props.dialogState}
253
- setDialogState={this.props.setDialogState}
254
247
  buttons={
255
248
  <PlayerActionsMenu
256
249
  asset={this.props.asset}