sanity-plugin-media 4.3.6 → 5.0.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 (162) hide show
  1. package/package.json +6 -15
  2. package/dist/index.cjs +0 -4721
  3. package/dist/index.cjs.map +0 -1
  4. package/dist/index.d.cts +0 -239
  5. package/dist/index.d.cts.map +0 -1
  6. package/sanity.json +0 -8
  7. package/src/__tests__/fixtures/createEpicTestStore.ts +0 -28
  8. package/src/__tests__/fixtures/listenMock.ts +0 -9
  9. package/src/__tests__/fixtures/mockSanityClient.ts +0 -84
  10. package/src/__tests__/fixtures/renderWithProviders.tsx +0 -55
  11. package/src/__tests__/fixtures/rootState.ts +0 -27
  12. package/src/__tests__/fixtures/withinDialog.ts +0 -28
  13. package/src/components/AssetGridVirtualized/index.tsx +0 -94
  14. package/src/components/AssetMetadata/index.tsx +0 -122
  15. package/src/components/AssetTableVirtualized/index.tsx +0 -73
  16. package/src/components/AutoTagInputWrapper/index.tsx +0 -85
  17. package/src/components/Browser/Browser.test.tsx +0 -45
  18. package/src/components/Browser/index.tsx +0 -90
  19. package/src/components/Browser/useBrowserInit.ts +0 -126
  20. package/src/components/ButtonAssetCopy/index.tsx +0 -65
  21. package/src/components/ButtonViewGroup/index.tsx +0 -39
  22. package/src/components/CardAsset/CardAsset.test.tsx +0 -323
  23. package/src/components/CardAsset/index.tsx +0 -290
  24. package/src/components/CardUpload/index.tsx +0 -161
  25. package/src/components/Controls/index.tsx +0 -136
  26. package/src/components/DebugControls/index.tsx +0 -80
  27. package/src/components/Dialog/index.tsx +0 -11
  28. package/src/components/DialogAssetEdit/Details.tsx +0 -181
  29. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +0 -216
  30. package/src/components/DialogAssetEdit/index.tsx +0 -493
  31. package/src/components/DialogConfirm/index.tsx +0 -90
  32. package/src/components/DialogSearchFacets/index.tsx +0 -42
  33. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +0 -121
  34. package/src/components/DialogTagCreate/index.tsx +0 -111
  35. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +0 -165
  36. package/src/components/DialogTagEdit/index.tsx +0 -201
  37. package/src/components/DialogTags/index.tsx +0 -45
  38. package/src/components/Dialogs/index.tsx +0 -76
  39. package/src/components/DocumentList/index.tsx +0 -62
  40. package/src/components/FileAssetPreview/index.tsx +0 -37
  41. package/src/components/FileIcon/index.tsx +0 -43
  42. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +0 -63
  43. package/src/components/FormBuilderTool/index.tsx +0 -69
  44. package/src/components/FormFieldInputLabel/index.tsx +0 -66
  45. package/src/components/FormFieldInputTags/index.tsx +0 -98
  46. package/src/components/FormFieldInputText/index.tsx +0 -41
  47. package/src/components/FormFieldInputTextarea/index.tsx +0 -43
  48. package/src/components/FormSubmitButton/index.tsx +0 -59
  49. package/src/components/Header/index.tsx +0 -80
  50. package/src/components/Image/index.tsx +0 -41
  51. package/src/components/Items/index.tsx +0 -68
  52. package/src/components/Notifications/index.tsx +0 -24
  53. package/src/components/OrderSelect/index.tsx +0 -66
  54. package/src/components/PickedBar/index.tsx +0 -77
  55. package/src/components/Progress/index.tsx +0 -38
  56. package/src/components/ReduxProvider/index.tsx +0 -96
  57. package/src/components/SearchFacet/index.tsx +0 -66
  58. package/src/components/SearchFacetNumber/index.tsx +0 -133
  59. package/src/components/SearchFacetSelect/index.tsx +0 -110
  60. package/src/components/SearchFacetString/index.tsx +0 -88
  61. package/src/components/SearchFacetTags/index.tsx +0 -121
  62. package/src/components/SearchFacets/index.tsx +0 -72
  63. package/src/components/SearchFacetsControl/index.tsx +0 -140
  64. package/src/components/TableHeader/index.tsx +0 -110
  65. package/src/components/TableHeaderItem/index.tsx +0 -61
  66. package/src/components/TableRowAsset/index.tsx +0 -419
  67. package/src/components/TableRowUpload/index.tsx +0 -164
  68. package/src/components/Tag/index.tsx +0 -200
  69. package/src/components/TagIcon/index.tsx +0 -22
  70. package/src/components/TagView/index.tsx +0 -39
  71. package/src/components/TagViewHeader/index.tsx +0 -70
  72. package/src/components/TagsPanel/index.tsx +0 -40
  73. package/src/components/TagsVirtualized/index.tsx +0 -160
  74. package/src/components/TextInputNumber/index.tsx +0 -32
  75. package/src/components/TextInputSearch/index.tsx +0 -60
  76. package/src/components/Tool/index.tsx +0 -13
  77. package/src/components/UploadDropzone/UploadDropzone.test.tsx +0 -40
  78. package/src/components/UploadDropzone/index.tsx +0 -173
  79. package/src/config/orders.ts +0 -28
  80. package/src/config/searchFacets.ts +0 -312
  81. package/src/constants.ts +0 -87
  82. package/src/contexts/AssetSourceDispatchContext.tsx +0 -38
  83. package/src/contexts/DropzoneDispatchContext.tsx +0 -32
  84. package/src/contexts/ToolOptionsContext.tsx +0 -66
  85. package/src/formSchema/index.test.ts +0 -56
  86. package/src/formSchema/index.ts +0 -39
  87. package/src/hooks/useBreakpointIndex.ts +0 -50
  88. package/src/hooks/useKeyPress.ts +0 -39
  89. package/src/hooks/usePortalPopoverProps.ts +0 -13
  90. package/src/hooks/useTypedSelector.ts +0 -7
  91. package/src/hooks/useVersionedClient.ts +0 -6
  92. package/src/index.ts +0 -5
  93. package/src/modules/assets/actions.ts +0 -42
  94. package/src/modules/assets/deleteAndUpdateEpics.test.ts +0 -87
  95. package/src/modules/assets/fetchEpic.test.ts +0 -73
  96. package/src/modules/assets/index.ts +0 -782
  97. package/src/modules/assets/reducer.test.ts +0 -91
  98. package/src/modules/assets/tagsAndListenerEpics.test.ts +0 -206
  99. package/src/modules/debug/index.ts +0 -28
  100. package/src/modules/dialog/actions.ts +0 -10
  101. package/src/modules/dialog/epics.test.ts +0 -168
  102. package/src/modules/dialog/index.ts +0 -238
  103. package/src/modules/dialog/reducer.test.ts +0 -185
  104. package/src/modules/index.ts +0 -117
  105. package/src/modules/notifications/epics.test.ts +0 -374
  106. package/src/modules/notifications/index.ts +0 -199
  107. package/src/modules/notifications/reducer.test.ts +0 -54
  108. package/src/modules/search/index.test.ts +0 -36
  109. package/src/modules/search/index.ts +0 -167
  110. package/src/modules/selected/index.ts +0 -22
  111. package/src/modules/selectors.test.ts +0 -21
  112. package/src/modules/selectors.ts +0 -17
  113. package/src/modules/tags/epics.test.ts +0 -96
  114. package/src/modules/tags/index.test.ts +0 -42
  115. package/src/modules/tags/index.ts +0 -540
  116. package/src/modules/types.ts +0 -3
  117. package/src/modules/uploads/actions.ts +0 -13
  118. package/src/modules/uploads/epics.test.ts +0 -109
  119. package/src/modules/uploads/index.test.ts +0 -59
  120. package/src/modules/uploads/index.ts +0 -272
  121. package/src/operators/checkTagName.test.ts +0 -29
  122. package/src/operators/checkTagName.ts +0 -33
  123. package/src/operators/debugThrottle.ts +0 -25
  124. package/src/plugin.tsx +0 -54
  125. package/src/schemas/tag.ts +0 -28
  126. package/src/styled/GlobalStyles/index.tsx +0 -40
  127. package/src/styled/react-select/creatable.tsx +0 -184
  128. package/src/styled/react-select/single.tsx +0 -184
  129. package/src/types/index.ts +0 -346
  130. package/src/types/sanity-ui.d.ts +0 -5
  131. package/src/utils/applyMediaTags.ts +0 -87
  132. package/src/utils/blocksToText.test.ts +0 -43
  133. package/src/utils/blocksToText.ts +0 -27
  134. package/src/utils/constructFilter.test.ts +0 -120
  135. package/src/utils/constructFilter.ts +0 -98
  136. package/src/utils/generatePreviewBlobUrl.test.ts +0 -68
  137. package/src/utils/generatePreviewBlobUrl.ts +0 -53
  138. package/src/utils/getAssetResolution.test.ts +0 -13
  139. package/src/utils/getAssetResolution.ts +0 -7
  140. package/src/utils/getDocumentAssetIds.test.ts +0 -50
  141. package/src/utils/getDocumentAssetIds.ts +0 -35
  142. package/src/utils/getSchemeColor.test.ts +0 -12
  143. package/src/utils/getSchemeColor.ts +0 -43
  144. package/src/utils/getTagSelectOptions.test.ts +0 -44
  145. package/src/utils/getTagSelectOptions.ts +0 -16
  146. package/src/utils/getUniqueDocuments.test.ts +0 -26
  147. package/src/utils/getUniqueDocuments.ts +0 -15
  148. package/src/utils/imageDprUrl.test.ts +0 -46
  149. package/src/utils/imageDprUrl.ts +0 -27
  150. package/src/utils/isSupportedAssetType.test.ts +0 -16
  151. package/src/utils/isSupportedAssetType.ts +0 -15
  152. package/src/utils/mediaField.ts +0 -73
  153. package/src/utils/sanitizeFormData.test.ts +0 -59
  154. package/src/utils/sanitizeFormData.ts +0 -26
  155. package/src/utils/typeGuards.test.ts +0 -18
  156. package/src/utils/typeGuards.ts +0 -9
  157. package/src/utils/uploadSanityAsset.test.ts +0 -29
  158. package/src/utils/uploadSanityAsset.ts +0 -97
  159. package/src/utils/withMaxConcurrency.test.ts +0 -43
  160. package/src/utils/withMaxConcurrency.ts +0 -55
  161. package/src/utils/zodFormResolver.ts +0 -17
  162. package/v2-incompatible.js +0 -11
@@ -1,323 +0,0 @@
1
- import {screen} from '@testing-library/react'
2
- import userEvent from '@testing-library/user-event'
3
- import type {RefObject} from 'react'
4
- import {beforeEach, describe, expect, it, vi} from 'vitest'
5
-
6
- import {renderWithProviders} from '../../__tests__/fixtures/renderWithProviders'
7
- import {initialState as assetsInitialState} from '../../modules/assets'
8
- import type {AssetItem, AssetType, FileAsset, ImageAsset} from '../../types'
9
- import CardAsset from './index'
10
-
11
- const SHIFT_FLAG = '__CARD_ASSET_TEST_SHIFT__'
12
-
13
- function setShiftPressed(on: boolean) {
14
- const g = globalThis as unknown as Record<string, boolean | undefined>
15
- if (on) {
16
- g[SHIFT_FLAG] = true
17
- } else {
18
- delete g[SHIFT_FLAG]
19
- }
20
- }
21
-
22
- vi.mock('../../hooks/useKeyPress', () => ({
23
- default: (): RefObject<boolean> =>
24
- ({
25
- get current() {
26
- return Boolean((globalThis as unknown as Record<string, unknown>)[SHIFT_FLAG])
27
- },
28
- }) as RefObject<boolean>,
29
- }))
30
-
31
- vi.mock('../Image', () => ({
32
- default: () => <div data-testid="card-image" />,
33
- }))
34
-
35
- vi.mock('../FileIcon', () => ({
36
- default: ({extension}: {extension?: string}) => (
37
- <div data-testid="card-file-icon" data-extension={extension ?? ''} />
38
- ),
39
- }))
40
-
41
- vi.mock('sanity', async (importOriginal) => {
42
- const actual = await importOriginal<typeof import('sanity')>()
43
- return {
44
- ...actual,
45
- useColorSchemeValue: () => 'light',
46
- }
47
- })
48
-
49
- const imageAsset = {
50
- _id: 'img-1',
51
- _type: 'sanity.imageAsset',
52
- _createdAt: '',
53
- _updatedAt: '',
54
- _rev: 'r1',
55
- originalFilename: 'photo.png',
56
- size: 1,
57
- mimeType: 'image/png',
58
- url: 'https://example.com/photo.png',
59
- metadata: {dimensions: {width: 100, height: 100}, isOpaque: true},
60
- } as ImageAsset
61
-
62
- const fileAsset = {
63
- _id: 'file-1',
64
- _type: 'sanity.fileAsset',
65
- _createdAt: '',
66
- _updatedAt: '',
67
- _rev: 'r1',
68
- originalFilename: 'doc.pdf',
69
- extension: 'pdf',
70
- size: 1,
71
- mimeType: 'application/pdf',
72
- url: 'https://example.com/doc.pdf',
73
- } as FileAsset
74
-
75
- function assetItem(asset: ImageAsset | FileAsset, partial?: Partial<AssetItem>): AssetItem {
76
- return {
77
- _type: 'asset',
78
- asset,
79
- picked: false,
80
- updating: false,
81
- ...partial,
82
- }
83
- }
84
-
85
- function assetsState(byIds: Record<string, AssetItem>, extra?: Partial<typeof assetsInitialState>) {
86
- return {
87
- ...assetsInitialState,
88
- assetTypes: ['file', 'image'] as AssetType[],
89
- allIds: Object.keys(byIds),
90
- byIds,
91
- ...extra,
92
- }
93
- }
94
-
95
- function clickPreview() {
96
- const imgs = screen.getAllByTestId('card-image')
97
- const img = imgs.at(-1)
98
- if (!img) {
99
- throw new Error('card-image missing')
100
- }
101
- const target = img.parentElement
102
- if (!target) {
103
- throw new Error('preview wrapper missing')
104
- }
105
- return target
106
- }
107
-
108
- function clickFooterFilename(text: string) {
109
- const nodes = screen.getAllByText(text)
110
- const el = nodes.at(-1)
111
- if (!el) {
112
- throw new Error(`footer text missing: ${text}`)
113
- }
114
- return el
115
- }
116
-
117
- beforeEach(() => {
118
- setShiftPressed(false)
119
- })
120
-
121
- describe('CardAsset', () => {
122
- it('renders nothing when the asset id is not in the store', () => {
123
- renderWithProviders(<CardAsset id="missing" selected={false} />, {
124
- preloaded: {
125
- assets: assetsState({}),
126
- },
127
- })
128
- expect(screen.queryAllByTestId('card-image')).toHaveLength(0)
129
- expect(screen.queryAllByTestId('card-file-icon')).toHaveLength(0)
130
- })
131
-
132
- it('renders image preview and original filename for an image asset', () => {
133
- renderWithProviders(<CardAsset id="img-1" selected={false} />, {
134
- preloaded: {
135
- assets: assetsState({'img-1': assetItem(imageAsset)}),
136
- },
137
- })
138
- expect(screen.getAllByTestId('card-image').length).toBeGreaterThan(0)
139
- expect(screen.getAllByText('photo.png').length).toBeGreaterThan(0)
140
- })
141
-
142
- it('renders file icon with extension for a file asset', () => {
143
- renderWithProviders(<CardAsset id="file-1" selected={false} />, {
144
- preloaded: {
145
- assets: assetsState({'file-1': assetItem(fileAsset)}),
146
- },
147
- })
148
- const icon = screen.getAllByTestId('card-file-icon').at(-1)!
149
- expect(icon).toHaveAttribute('data-extension', 'pdf')
150
- expect(screen.getAllByText('doc.pdf').length).toBeGreaterThan(0)
151
- })
152
-
153
- it('opens the asset edit dialog when the preview is clicked in browse mode', async () => {
154
- const user = userEvent.setup()
155
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
156
- preloaded: {
157
- assets: assetsState({'img-1': assetItem(imageAsset)}),
158
- },
159
- })
160
-
161
- await user.click(clickPreview())
162
-
163
- expect(
164
- store.getState().dialog.items.some((d) => d.type === 'assetEdit' && d.assetId === 'img-1'),
165
- ).toBe(true)
166
- })
167
-
168
- it('calls onSelect with the asset document id when the preview is clicked in picker mode', async () => {
169
- const user = userEvent.setup()
170
- const onSelect = vi.fn()
171
- renderWithProviders(<CardAsset id="img-1" selected={false} />, {
172
- onSelect,
173
- preloaded: {
174
- assets: assetsState({'img-1': assetItem(imageAsset)}),
175
- },
176
- })
177
-
178
- await user.click(clickPreview())
179
-
180
- expect(onSelect).toHaveBeenCalledWith([
181
- {
182
- kind: 'assetDocumentId',
183
- value: 'img-1',
184
- },
185
- ])
186
- })
187
-
188
- it('toggles pick when the footer is clicked in browse mode', async () => {
189
- const user = userEvent.setup()
190
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
191
- preloaded: {
192
- assets: assetsState({'img-1': assetItem(imageAsset, {picked: false})}),
193
- },
194
- })
195
-
196
- await user.click(clickFooterFilename('photo.png'))
197
-
198
- expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
199
- })
200
-
201
- it('opens asset edit from the footer when in picker mode', async () => {
202
- const user = userEvent.setup()
203
- const onSelect = vi.fn()
204
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
205
- onSelect,
206
- preloaded: {
207
- assets: assetsState({'img-1': assetItem(imageAsset)}),
208
- },
209
- })
210
-
211
- await user.click(clickFooterFilename('photo.png'))
212
-
213
- expect(onSelect).not.toHaveBeenCalled()
214
- expect(
215
- store.getState().dialog.items.some((d) => d.type === 'assetEdit' && d.assetId === 'img-1'),
216
- ).toBe(true)
217
- })
218
-
219
- it('shift-clicks on preview to unpick when the asset is already picked', async () => {
220
- const user = userEvent.setup()
221
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
222
- preloaded: {
223
- assets: assetsState({'img-1': assetItem(imageAsset, {picked: true})}),
224
- },
225
- })
226
-
227
- setShiftPressed(true)
228
- await user.click(clickPreview())
229
- setShiftPressed(false)
230
-
231
- expect(store.getState().assets.byIds['img-1']!.picked).toBe(false)
232
- })
233
-
234
- it('shift-clicks on preview to pick a range when not picked and lastPicked is set', async () => {
235
- const user = userEvent.setup()
236
- const prevAsset = {...imageAsset, _id: 'prev-1', originalFilename: 'prev.png'} as ImageAsset
237
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
238
- preloaded: {
239
- assets: assetsState(
240
- {
241
- 'prev-1': assetItem(prevAsset),
242
- 'img-1': assetItem(imageAsset, {picked: false}),
243
- },
244
- {lastPicked: 'prev-1'},
245
- ),
246
- },
247
- })
248
-
249
- setShiftPressed(true)
250
- await user.click(clickPreview())
251
- setShiftPressed(false)
252
-
253
- expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
254
- expect(store.getState().assets.byIds['prev-1']!.picked).toBe(true)
255
- })
256
-
257
- it('shift-clicks on footer to pick a range when not picked', async () => {
258
- const user = userEvent.setup()
259
- const anchorAsset = {
260
- ...imageAsset,
261
- _id: 'anchor-9',
262
- originalFilename: 'anchor.png',
263
- } as ImageAsset
264
- const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
265
- preloaded: {
266
- assets: assetsState(
267
- {
268
- 'anchor-9': assetItem(anchorAsset),
269
- 'img-1': assetItem(imageAsset, {picked: false}),
270
- },
271
- {lastPicked: 'anchor-9'},
272
- ),
273
- },
274
- })
275
-
276
- setShiftPressed(true)
277
- await user.click(clickFooterFilename('photo.png'))
278
- setShiftPressed(false)
279
-
280
- expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
281
- expect(store.getState().assets.byIds['anchor-9']!.picked).toBe(true)
282
- })
283
-
284
- it('shows the selection checkmark when selected and not updating', () => {
285
- const {container} = renderWithProviders(<CardAsset id="img-1" selected />, {
286
- preloaded: {
287
- assets: assetsState({'img-1': assetItem(imageAsset, {updating: false})}),
288
- },
289
- })
290
- expect(
291
- container.querySelectorAll('[data-sanity-icon="checkmark-circle"]').length,
292
- ).toBeGreaterThan(0)
293
- })
294
-
295
- it('does not show the checkmark overlay while updating even if selected', () => {
296
- const {container} = renderWithProviders(<CardAsset id="img-1" selected />, {
297
- preloaded: {
298
- assets: assetsState({'img-1': assetItem(imageAsset, {updating: true})}),
299
- },
300
- })
301
- expect(container.querySelectorAll('[data-sanity-icon="checkmark-circle"]')).toHaveLength(0)
302
- })
303
-
304
- it('shows a spinner while updating', () => {
305
- renderWithProviders(<CardAsset id="img-1" selected={false} />, {
306
- preloaded: {
307
- assets: assetsState({'img-1': assetItem(imageAsset, {updating: true})}),
308
- },
309
- })
310
- expect(document.body.querySelectorAll('[data-ui="Spinner"]').length).toBeGreaterThan(0)
311
- })
312
-
313
- it('shows a warning icon when the asset item has an error', () => {
314
- const {container} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
315
- preloaded: {
316
- assets: assetsState({'img-1': assetItem(imageAsset, {error: 'Upload failed'})}),
317
- },
318
- })
319
- expect(
320
- container.querySelectorAll('[data-sanity-icon="warning-filled"]').length,
321
- ).toBeGreaterThan(0)
322
- })
323
- })
@@ -1,290 +0,0 @@
1
- import {CheckmarkCircleIcon, EditIcon, WarningFilledIcon} from '@sanity/icons'
2
- import {
3
- Box,
4
- Checkbox,
5
- Container,
6
- Flex,
7
- Spinner,
8
- Text,
9
- type Theme,
10
- type ThemeColorSchemeKey,
11
- Tooltip,
12
- } from '@sanity/ui'
13
- import {memo, type MouseEvent, type RefObject} from 'react'
14
- import {useDispatch} from 'react-redux'
15
- import {useColorSchemeValue} from 'sanity'
16
- import {styled, css} from 'styled-components'
17
-
18
- import {PANEL_HEIGHT} from '../../constants'
19
- import {useAssetSourceActions} from '../../contexts/AssetSourceDispatchContext'
20
- import useKeyPress from '../../hooks/useKeyPress'
21
- import useTypedSelector from '../../hooks/useTypedSelector'
22
- import {assetsActions, selectAssetById} from '../../modules/assets'
23
- import {dialogActions} from '../../modules/dialog'
24
- import {getSchemeColor} from '../../utils/getSchemeColor'
25
- import imageDprUrl from '../../utils/imageDprUrl'
26
- import {isFileAsset, isImageAsset} from '../../utils/typeGuards'
27
- import FileIcon from '../FileIcon'
28
- import Image from '../Image'
29
-
30
- type Props = {
31
- id: string
32
- selected: boolean
33
- }
34
-
35
- const CardWrapper = styled(Flex)`
36
- box-sizing: border-box;
37
- height: 100%;
38
- overflow: hidden;
39
- position: relative;
40
- width: 100%;
41
- `
42
-
43
- const CardContainer = styled(Flex)<{$picked?: boolean; theme: Theme; $updating?: boolean}>(({
44
- $picked,
45
- theme,
46
- $updating,
47
- }) => {
48
- return css`
49
- border: 1px solid transparent;
50
- height: 100%;
51
- pointer-events: ${$updating ? 'none' : 'auto'};
52
- position: relative;
53
- transition: all 300ms;
54
- user-select: none;
55
- width: 100%;
56
-
57
- border: ${$picked
58
- ? `1px solid ${theme.sanity.color.spot.orange} !important`
59
- : '1px solid inherit'};
60
-
61
- ${!$updating &&
62
- css`
63
- @media (hover: hover) and (pointer: fine) {
64
- &:hover {
65
- border: 1px solid var(--card-border-color);
66
- }
67
- }
68
- `}
69
- `
70
- })
71
-
72
- const ContextActionContainer = styled<typeof Flex, {$scheme: ThemeColorSchemeKey}>(Flex)(({
73
- $scheme,
74
- }) => {
75
- return css`
76
- cursor: pointer;
77
- height: ${PANEL_HEIGHT}px;
78
- transition: all 300ms;
79
- @media (hover: hover) and (pointer: fine) {
80
- &:hover {
81
- background: ${getSchemeColor($scheme, 'bg')};
82
- }
83
- }
84
- `
85
- })
86
-
87
- const StyledWarningOutlineIcon = styled(WarningFilledIcon)(({theme}) => {
88
- return {
89
- color: theme.sanity.color.spot.red,
90
- }
91
- })
92
-
93
- const CardAsset = (props: Props) => {
94
- const {id, selected} = props
95
-
96
- const scheme = useColorSchemeValue()
97
-
98
- // Refs
99
- const shiftPressed: RefObject<boolean> = useKeyPress('shift')
100
-
101
- // Redux
102
- const dispatch = useDispatch()
103
- const lastPicked = useTypedSelector((state) => state.assets.lastPicked)
104
- const item = useTypedSelector((state) => selectAssetById(state, id))
105
-
106
- const asset = item?.asset
107
- const error = item?.error
108
- const isOpaque = item?.asset?.metadata?.isOpaque
109
- const picked = item?.picked
110
- const updating = item?.updating
111
-
112
- const {onSelect} = useAssetSourceActions()
113
-
114
- // Short circuit if no asset is available
115
- if (!asset) {
116
- return null
117
- }
118
-
119
- // Callbacks
120
- const handleAssetClick = (e: MouseEvent<HTMLDivElement>) => {
121
- e.stopPropagation()
122
-
123
- if (onSelect) {
124
- onSelect([
125
- {
126
- kind: 'assetDocumentId',
127
- value: asset._id,
128
- },
129
- ])
130
- } else if (shiftPressed.current) {
131
- if (picked) {
132
- dispatch(assetsActions.pick({assetId: asset._id, picked: !picked}))
133
- } else {
134
- dispatch(assetsActions.pickRange({startId: lastPicked || asset._id, endId: asset._id}))
135
- }
136
- } else {
137
- dispatch(dialogActions.showAssetEdit({assetId: asset._id}))
138
- }
139
- }
140
-
141
- const handleContextActionClick = (e: MouseEvent) => {
142
- e.stopPropagation()
143
-
144
- if (onSelect) {
145
- dispatch(dialogActions.showAssetEdit({assetId: asset._id}))
146
- } else if (shiftPressed.current && !picked) {
147
- dispatch(assetsActions.pickRange({startId: lastPicked || asset._id, endId: asset._id}))
148
- } else {
149
- dispatch(assetsActions.pick({assetId: asset._id, picked: !picked}))
150
- }
151
- }
152
-
153
- const opacityContainer = updating ? 0.5 : 1
154
- const opacityPreview = selected || updating ? 0.25 : 1
155
-
156
- return (
157
- <CardWrapper padding={1}>
158
- <CardContainer direction="column" $picked={picked} $updating={item.updating}>
159
- {/* Image */}
160
- <Box
161
- flex={1}
162
- style={{
163
- cursor: selected ? 'default' : 'pointer',
164
- position: 'relative',
165
- }}
166
- >
167
- <div onClick={handleAssetClick} style={{height: '100%', opacity: opacityPreview}}>
168
- {/* File icon */}
169
- {isFileAsset(asset) && <FileIcon extension={asset.extension} width="80px" />}
170
-
171
- {/* Image */}
172
- {isImageAsset(asset) && (
173
- <Image
174
- draggable={false}
175
- $scheme={scheme}
176
- $showCheckerboard={!isOpaque}
177
- src={imageDprUrl(asset, {height: 250, width: 250})}
178
- style={{
179
- draggable: false,
180
- transition: 'opacity 1000ms',
181
- }}
182
- />
183
- )}
184
- </div>
185
-
186
- {/* Selected check icon */}
187
- {selected && !updating && (
188
- <Flex
189
- align="center"
190
- justify="center"
191
- style={{
192
- height: '100%',
193
- left: 0,
194
- opacity: opacityContainer,
195
- position: 'absolute',
196
- top: 0,
197
- width: '100%',
198
- }}
199
- >
200
- <Text size={2}>
201
- <CheckmarkCircleIcon />
202
- </Text>
203
- </Flex>
204
- )}
205
-
206
- {/* Spinner */}
207
- {updating && (
208
- <Flex
209
- align="center"
210
- justify="center"
211
- style={{
212
- height: '100%',
213
- left: 0,
214
- position: 'absolute',
215
- top: 0,
216
- width: '100%',
217
- }}
218
- >
219
- <Spinner />
220
- </Flex>
221
- )}
222
- </Box>
223
-
224
- {/* Footer */}
225
- <ContextActionContainer
226
- align="center"
227
- onClick={handleContextActionClick}
228
- paddingX={1}
229
- $scheme={scheme}
230
- style={{opacity: opacityContainer}}
231
- >
232
- {onSelect ? (
233
- <EditIcon
234
- style={{
235
- flexShrink: 0,
236
- opacity: 0.5,
237
- }}
238
- />
239
- ) : (
240
- <Checkbox
241
- checked={picked}
242
- readOnly
243
- style={{
244
- flexShrink: 0,
245
- pointerEvents: 'none',
246
- transform: 'scale(0.8)',
247
- }}
248
- />
249
- )}
250
-
251
- <Box marginLeft={2}>
252
- <Text muted size={0} textOverflow="ellipsis">
253
- {asset.originalFilename}
254
- </Text>
255
- </Box>
256
- </ContextActionContainer>
257
-
258
- {/* TODO: DRY */}
259
- {/* Error button */}
260
- {error && (
261
- <Box
262
- padding={3}
263
- style={{
264
- position: 'absolute',
265
- right: 0,
266
- top: 0,
267
- }}
268
- >
269
- <Tooltip
270
- animate
271
- content={
272
- <Container padding={2} width={0}>
273
- <Text size={1}>{error}</Text>
274
- </Container>
275
- }
276
- placement="left"
277
- portal
278
- >
279
- <Text size={1}>
280
- <StyledWarningOutlineIcon color="critical" />
281
- </Text>
282
- </Tooltip>
283
- </Box>
284
- )}
285
- </CardContainer>
286
- </CardWrapper>
287
- )
288
- }
289
-
290
- export default memo(CardAsset)