sanity-plugin-media 4.3.1 → 4.3.2
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/LICENSE +4 -4
- package/README.md +12 -12
- package/dist/index.d.mts +263 -195
- package/dist/index.d.ts +263 -195
- package/dist/index.js +83 -203
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -203
- package/dist/index.mjs.map +1 -1
- package/package.json +41 -63
- package/src/__tests__/fixtures/createEpicTestStore.ts +5 -4
- package/src/__tests__/fixtures/mockSanityClient.ts +8 -8
- package/src/__tests__/fixtures/renderWithProviders.tsx +8 -7
- package/src/__tests__/fixtures/rootState.ts +4 -4
- package/src/components/AssetGridVirtualized/index.tsx +8 -7
- package/src/components/AssetMetadata/index.tsx +6 -5
- package/src/components/AssetTableVirtualized/index.tsx +7 -6
- package/src/components/AutoTagInputWrapper/index.tsx +9 -4
- package/src/components/Browser/Browser.test.tsx +9 -8
- package/src/components/Browser/index.tsx +2 -1
- package/src/components/Browser/useBrowserInit.ts +9 -9
- package/src/components/ButtonAssetCopy/index.tsx +1 -0
- package/src/components/ButtonViewGroup/index.tsx +4 -3
- package/src/components/CardAsset/CardAsset.test.tsx +53 -52
- package/src/components/CardAsset/index.tsx +52 -49
- package/src/components/CardUpload/index.tsx +7 -6
- package/src/components/Controls/index.tsx +7 -6
- package/src/components/DebugControls/index.tsx +5 -4
- package/src/components/DialogAssetEdit/Details.tsx +3 -2
- package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +28 -27
- package/src/components/DialogAssetEdit/index.tsx +37 -37
- package/src/components/DialogConfirm/index.tsx +2 -1
- package/src/components/DialogSearchFacets/index.tsx +3 -2
- package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +16 -15
- package/src/components/DialogTagCreate/index.tsx +11 -10
- package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +28 -27
- package/src/components/DialogTagEdit/index.tsx +17 -16
- package/src/components/DialogTags/index.tsx +4 -3
- package/src/components/Dialogs/index.tsx +2 -3
- package/src/components/DocumentList/index.tsx +2 -3
- package/src/components/FileAssetPreview/index.tsx +2 -2
- package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +12 -11
- package/src/components/FormBuilderTool/index.tsx +2 -1
- package/src/components/FormFieldInputLabel/index.tsx +1 -2
- package/src/components/FormFieldInputTags/index.tsx +4 -3
- package/src/components/FormSubmitButton/index.tsx +1 -1
- package/src/components/Header/index.tsx +3 -3
- package/src/components/Image/index.tsx +10 -4
- package/src/components/Items/index.tsx +5 -4
- package/src/components/Notifications/index.tsx +3 -2
- package/src/components/OrderSelect/index.tsx +4 -3
- package/src/components/PickedBar/index.tsx +2 -1
- package/src/components/Progress/index.tsx +3 -3
- package/src/components/ReduxProvider/index.tsx +15 -12
- package/src/components/SearchFacet/index.tsx +3 -2
- package/src/components/SearchFacetNumber/index.tsx +8 -8
- package/src/components/SearchFacetSelect/index.tsx +7 -8
- package/src/components/SearchFacetString/index.tsx +1 -1
- package/src/components/SearchFacetTags/index.tsx +13 -12
- package/src/components/SearchFacets/index.tsx +2 -3
- package/src/components/SearchFacetsControl/index.tsx +13 -12
- package/src/components/TableHeader/index.tsx +18 -17
- package/src/components/TableHeaderItem/index.tsx +4 -4
- package/src/components/TableRowAsset/index.tsx +37 -36
- package/src/components/TableRowUpload/index.tsx +7 -6
- package/src/components/Tag/index.tsx +8 -7
- package/src/components/TagView/index.tsx +2 -2
- package/src/components/TagViewHeader/index.tsx +5 -4
- package/src/components/TagsPanel/index.tsx +3 -3
- package/src/components/TagsVirtualized/index.tsx +25 -24
- package/src/components/TextInputSearch/index.tsx +3 -2
- package/src/components/UploadDropzone/UploadDropzone.test.tsx +8 -7
- package/src/components/UploadDropzone/index.tsx +14 -13
- package/src/config/orders.ts +6 -6
- package/src/config/searchFacets.ts +56 -55
- package/src/constants.ts +15 -14
- package/src/contexts/AssetSourceDispatchContext.tsx +1 -1
- package/src/contexts/ToolOptionsContext.tsx +6 -5
- package/src/formSchema/index.test.ts +6 -5
- package/src/formSchema/index.ts +5 -5
- package/src/hooks/useBreakpointIndex.ts +6 -6
- package/src/hooks/useKeyPress.ts +2 -2
- package/src/hooks/usePortalPopoverProps.ts +1 -1
- package/src/modules/assets/actions.ts +8 -7
- package/src/modules/assets/deleteAndUpdateEpics.test.ts +18 -17
- package/src/modules/assets/fetchEpic.test.ts +12 -11
- package/src/modules/assets/index.ts +134 -133
- package/src/modules/assets/reducer.test.ts +9 -8
- package/src/modules/assets/tagsAndListenerEpics.test.ts +36 -35
- package/src/modules/debug/index.ts +3 -3
- package/src/modules/dialog/actions.ts +2 -2
- package/src/modules/dialog/epics.test.ts +29 -28
- package/src/modules/dialog/index.ts +36 -35
- package/src/modules/dialog/reducer.test.ts +31 -30
- package/src/modules/index.ts +9 -9
- package/src/modules/notifications/epics.test.ts +71 -70
- package/src/modules/notifications/index.ts +50 -49
- package/src/modules/notifications/reducer.test.ts +8 -7
- package/src/modules/search/index.test.ts +2 -1
- package/src/modules/search/index.ts +22 -22
- package/src/modules/selected/index.ts +2 -2
- package/src/modules/selectors.test.ts +4 -3
- package/src/modules/selectors.ts +5 -5
- package/src/modules/tags/epics.test.ts +16 -15
- package/src/modules/tags/index.test.ts +2 -1
- package/src/modules/tags/index.ts +82 -81
- package/src/modules/uploads/actions.ts +3 -3
- package/src/modules/uploads/epics.test.ts +13 -12
- package/src/modules/uploads/index.test.ts +8 -7
- package/src/modules/uploads/index.ts +48 -47
- package/src/operators/checkTagName.test.ts +7 -6
- package/src/operators/checkTagName.ts +6 -5
- package/src/operators/debugThrottle.ts +4 -4
- package/src/plugin.tsx +18 -18
- package/src/schemas/tag.ts +7 -7
- package/src/styled/react-select/creatable.tsx +40 -39
- package/src/styled/react-select/single.tsx +39 -38
- package/src/types/index.ts +4 -3
- package/src/utils/applyMediaTags.ts +11 -10
- package/src/utils/blocksToText.test.ts +5 -4
- package/src/utils/blocksToText.ts +2 -2
- package/src/utils/constructFilter.test.ts +15 -14
- package/src/utils/constructFilter.ts +7 -7
- package/src/utils/generatePreviewBlobUrl.test.ts +6 -5
- package/src/utils/generatePreviewBlobUrl.ts +2 -2
- package/src/utils/getAssetResolution.test.ts +3 -2
- package/src/utils/getDocumentAssetIds.test.ts +7 -6
- package/src/utils/getDocumentAssetIds.ts +2 -2
- package/src/utils/getSchemeColor.test.ts +1 -0
- package/src/utils/getSchemeColor.ts +9 -9
- package/src/utils/getTagSelectOptions.test.ts +6 -5
- package/src/utils/getTagSelectOptions.ts +1 -1
- package/src/utils/getUniqueDocuments.test.ts +4 -3
- package/src/utils/getUniqueDocuments.ts +2 -2
- package/src/utils/imageDprUrl.test.ts +4 -3
- package/src/utils/imageDprUrl.ts +1 -1
- package/src/utils/isSupportedAssetType.test.ts +1 -0
- package/src/utils/mediaField.ts +4 -3
- package/src/utils/sanitizeFormData.test.ts +14 -13
- package/src/utils/typeGuards.test.ts +2 -1
- package/src/utils/uploadSanityAsset.test.ts +5 -4
- package/src/utils/uploadSanityAsset.ts +17 -16
- package/src/utils/withMaxConcurrency.test.ts +5 -4
- package/src/utils/withMaxConcurrency.ts +4 -4
- package/src/utils/zodFormResolver.ts +17 -0
- package/v2-incompatible.js +2 -2
|
@@ -2,14 +2,15 @@ import {CloseIcon} from '@sanity/icons'
|
|
|
2
2
|
import {Box, Card, rem, studioTheme, Text, type ThemeColorSchemeKey} from '@sanity/ui'
|
|
3
3
|
import {components, type StylesConfig} from 'react-select'
|
|
4
4
|
import {Virtuoso} from 'react-virtuoso'
|
|
5
|
+
|
|
5
6
|
import {getSchemeColor} from '../../utils/getSchemeColor'
|
|
6
7
|
|
|
7
8
|
const {
|
|
8
9
|
fonts: {
|
|
9
|
-
text: {sizes: themeTextSizes}
|
|
10
|
+
text: {sizes: themeTextSizes},
|
|
10
11
|
},
|
|
11
12
|
radius: themeRadius,
|
|
12
|
-
space: themeSpace
|
|
13
|
+
space: themeSpace,
|
|
13
14
|
} = studioTheme
|
|
14
15
|
|
|
15
16
|
export const reactSelectStyles = (scheme: ThemeColorSchemeKey): StylesConfig => {
|
|
@@ -24,70 +25,70 @@ export const reactSelectStyles = (scheme: ThemeColorSchemeKey): StylesConfig =>
|
|
|
24
25
|
|
|
25
26
|
return {
|
|
26
27
|
...styles,
|
|
27
|
-
backgroundColor: 'var(--card-bg-color)',
|
|
28
|
-
color: 'inherit',
|
|
29
|
-
border: 'none',
|
|
30
|
-
borderRadius: themeRadius[2],
|
|
28
|
+
'backgroundColor': 'var(--card-bg-color)',
|
|
29
|
+
'color': 'inherit',
|
|
30
|
+
'border': 'none',
|
|
31
|
+
'borderRadius': themeRadius[2],
|
|
31
32
|
boxShadow,
|
|
32
|
-
fontSize: themeTextSizes[1].fontSize,
|
|
33
|
-
minHeight: '25px',
|
|
34
|
-
opacity: isDisabled ? 0.5 : 'inherit',
|
|
35
|
-
outline: 'none',
|
|
36
|
-
transition: 'none',
|
|
33
|
+
'fontSize': themeTextSizes[1].fontSize,
|
|
34
|
+
'minHeight': '25px',
|
|
35
|
+
'opacity': isDisabled ? 0.5 : 'inherit',
|
|
36
|
+
'outline': 'none',
|
|
37
|
+
'transition': 'none',
|
|
37
38
|
'&:hover': {
|
|
38
|
-
boxShadow: `inset 0 0 0 1px ${getSchemeColor(scheme, 'inputHoveredBorder')}
|
|
39
|
-
}
|
|
39
|
+
boxShadow: `inset 0 0 0 1px ${getSchemeColor(scheme, 'inputHoveredBorder')}`,
|
|
40
|
+
},
|
|
40
41
|
}
|
|
41
42
|
},
|
|
42
|
-
input: styles => ({
|
|
43
|
+
input: (styles) => ({
|
|
43
44
|
...styles,
|
|
44
45
|
color: 'var(--card-fg-color)',
|
|
45
46
|
fontFamily: studioTheme.fonts.text.family,
|
|
46
47
|
fontSize: themeTextSizes[1].fontSize,
|
|
47
|
-
marginLeft: rem(themeSpace[2])
|
|
48
|
+
marginLeft: rem(themeSpace[2]),
|
|
48
49
|
}),
|
|
49
|
-
menuList: styles => ({
|
|
50
|
+
menuList: (styles) => ({
|
|
50
51
|
...styles,
|
|
51
|
-
padding: 0
|
|
52
|
+
padding: 0,
|
|
52
53
|
}),
|
|
53
|
-
noOptionsMessage: styles => ({
|
|
54
|
+
noOptionsMessage: (styles) => ({
|
|
54
55
|
...styles,
|
|
55
56
|
fontFamily: studioTheme.fonts.text.family,
|
|
56
57
|
fontSize: themeTextSizes[1].fontSize,
|
|
57
|
-
lineHeight: '1em'
|
|
58
|
+
lineHeight: '1em',
|
|
58
59
|
}),
|
|
59
60
|
option: (styles, {isFocused}) => ({
|
|
60
61
|
...styles,
|
|
61
|
-
backgroundColor: isFocused ? getSchemeColor(scheme, 'spotBlue') : 'transparent',
|
|
62
|
-
borderRadius: themeRadius[2],
|
|
63
|
-
color: isFocused ? getSchemeColor(scheme, 'bg') : 'inherit',
|
|
64
|
-
fontSize: themeTextSizes[1].fontSize,
|
|
65
|
-
lineHeight: '1em',
|
|
66
|
-
margin: 0,
|
|
67
|
-
padding: rem(themeSpace[1]),
|
|
62
|
+
'backgroundColor': isFocused ? getSchemeColor(scheme, 'spotBlue') : 'transparent',
|
|
63
|
+
'borderRadius': themeRadius[2],
|
|
64
|
+
'color': isFocused ? getSchemeColor(scheme, 'bg') : 'inherit',
|
|
65
|
+
'fontSize': themeTextSizes[1].fontSize,
|
|
66
|
+
'lineHeight': '1em',
|
|
67
|
+
'margin': 0,
|
|
68
|
+
'padding': rem(themeSpace[1]),
|
|
68
69
|
'&:hover': {
|
|
69
70
|
backgroundColor: getSchemeColor(scheme, 'spotBlue'),
|
|
70
|
-
color: getSchemeColor(scheme, 'bg')
|
|
71
|
-
}
|
|
71
|
+
color: getSchemeColor(scheme, 'bg'),
|
|
72
|
+
},
|
|
72
73
|
}),
|
|
73
|
-
placeholder: styles => ({
|
|
74
|
+
placeholder: (styles) => ({
|
|
74
75
|
...styles,
|
|
75
76
|
fontSize: themeTextSizes[1].fontSize,
|
|
76
77
|
marginLeft: rem(themeSpace[2]),
|
|
77
|
-
paddingLeft: 0
|
|
78
|
+
paddingLeft: 0,
|
|
78
79
|
}),
|
|
79
|
-
singleValue: styles => ({
|
|
80
|
+
singleValue: (styles) => ({
|
|
80
81
|
...styles,
|
|
81
82
|
alignItems: 'center',
|
|
82
83
|
display: 'inline-flex',
|
|
83
84
|
height: '100%',
|
|
84
|
-
marginLeft: rem(themeSpace[2])
|
|
85
|
+
marginLeft: rem(themeSpace[2]),
|
|
85
86
|
}),
|
|
86
|
-
valueContainer: styles => ({
|
|
87
|
+
valueContainer: (styles) => ({
|
|
87
88
|
...styles,
|
|
88
89
|
margin: 0,
|
|
89
|
-
padding: 0
|
|
90
|
-
})
|
|
90
|
+
padding: 0,
|
|
91
|
+
}),
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -97,7 +98,7 @@ const ClearIndicator = (props: any) => {
|
|
|
97
98
|
<Box
|
|
98
99
|
paddingRight={1}
|
|
99
100
|
style={{
|
|
100
|
-
transform: 'scale(0.85)'
|
|
101
|
+
transform: 'scale(0.85)',
|
|
101
102
|
}}
|
|
102
103
|
>
|
|
103
104
|
<Text muted size={0}>
|
|
@@ -131,7 +132,7 @@ const MenuList = (props: any) => {
|
|
|
131
132
|
return (
|
|
132
133
|
<Virtuoso
|
|
133
134
|
className="media__custom-scrollbar"
|
|
134
|
-
itemContent={index => {
|
|
135
|
+
itemContent={(index) => {
|
|
135
136
|
const item = children[index]
|
|
136
137
|
return <Option {...item.props} />
|
|
137
138
|
}}
|
|
@@ -179,5 +180,5 @@ export const reactSelectComponents = {
|
|
|
179
180
|
MenuList,
|
|
180
181
|
NoOptionsMessage,
|
|
181
182
|
Option,
|
|
182
|
-
SingleValue
|
|
183
|
+
SingleValue,
|
|
183
184
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -3,15 +3,16 @@ import type {
|
|
|
3
3
|
SanityAssetDocument,
|
|
4
4
|
SanityClient,
|
|
5
5
|
SanityDocument,
|
|
6
|
-
SanityImageAssetDocument
|
|
6
|
+
SanityImageAssetDocument,
|
|
7
7
|
} from '@sanity/client'
|
|
8
8
|
import type {ComponentType, JSX} from 'react'
|
|
9
9
|
import type {Epic} from 'redux-observable'
|
|
10
10
|
import * as z from 'zod'
|
|
11
|
-
|
|
12
|
-
import type {RootReducerState} from '../modules/types'
|
|
11
|
+
|
|
13
12
|
import type {DetailsProps} from '../components/DialogAssetEdit/Details'
|
|
14
13
|
import type {SUPPORTED_ASSET_TYPES} from '../constants'
|
|
14
|
+
import {getAssetFormSchema, tagFormSchema, tagOptionSchema} from '../formSchema'
|
|
15
|
+
import type {RootReducerState} from '../modules/types'
|
|
15
16
|
|
|
16
17
|
export type AssetTypes = (typeof SUPPORTED_ASSET_TYPES)[number]
|
|
17
18
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {SanityClient} from '@sanity/client'
|
|
2
2
|
import groq from 'groq'
|
|
3
3
|
import {nanoid} from 'nanoid'
|
|
4
|
+
|
|
4
5
|
import {TAG_DOCUMENT_NAME} from '../constants'
|
|
5
6
|
import type {Tag} from '../types'
|
|
6
7
|
|
|
@@ -18,7 +19,7 @@ const pendingByAsset = new Map<string, Promise<void>>()
|
|
|
18
19
|
export function applyMediaTags(options: ApplyMediaTagsOptions): Promise<void> {
|
|
19
20
|
const {assetId} = options
|
|
20
21
|
const chain = (pendingByAsset.get(assetId) ?? Promise.resolve()).then(() =>
|
|
21
|
-
doApplyMediaTags(options)
|
|
22
|
+
doApplyMediaTags(options),
|
|
22
23
|
)
|
|
23
24
|
const cleanup = chain
|
|
24
25
|
.catch(() => {})
|
|
@@ -33,26 +34,26 @@ async function doApplyMediaTags({
|
|
|
33
34
|
client,
|
|
34
35
|
assetId,
|
|
35
36
|
mediaTags,
|
|
36
|
-
createTagsOnUpload = true
|
|
37
|
+
createTagsOnUpload = true,
|
|
37
38
|
}: ApplyMediaTagsOptions): Promise<void> {
|
|
38
39
|
if (!mediaTags || mediaTags.length === 0) return
|
|
39
40
|
|
|
40
41
|
const resolvedTags = await Promise.all(
|
|
41
|
-
mediaTags.map(async tagName => {
|
|
42
|
+
mediaTags.map(async (tagName) => {
|
|
42
43
|
const existingTag = await client.fetch<Tag | null>(
|
|
43
44
|
groq`*[_type == "${TAG_DOCUMENT_NAME}" && name.current == $tagName][0]`,
|
|
44
|
-
{tagName}
|
|
45
|
+
{tagName},
|
|
45
46
|
)
|
|
46
47
|
if (existingTag) return existingTag
|
|
47
48
|
if (createTagsOnUpload) {
|
|
48
49
|
const newTag = await client.create({
|
|
49
50
|
_type: TAG_DOCUMENT_NAME,
|
|
50
|
-
name: {_type: 'slug', current: tagName}
|
|
51
|
+
name: {_type: 'slug', current: tagName},
|
|
51
52
|
})
|
|
52
53
|
return newTag as Tag
|
|
53
54
|
}
|
|
54
55
|
return null
|
|
55
|
-
})
|
|
56
|
+
}),
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
const validTags = resolvedTags.filter((tag): tag is Tag => tag !== null)
|
|
@@ -61,17 +62,17 @@ async function doApplyMediaTags({
|
|
|
61
62
|
const existing = await client.fetch<{tagIds: string[]} | null>(
|
|
62
63
|
groq`*[_id == $assetId][0]{'tagIds': opt.media.tags[]._ref}`,
|
|
63
64
|
{assetId},
|
|
64
|
-
{useCdn: false} // bypass CDN cache so we see the latest committed tag refs
|
|
65
|
+
{useCdn: false}, // bypass CDN cache so we see the latest committed tag refs
|
|
65
66
|
)
|
|
66
67
|
const existingIds = new Set(existing?.tagIds ?? [])
|
|
67
68
|
|
|
68
69
|
const tagReferences = validTags
|
|
69
|
-
.filter(tag => !existingIds.has(tag._id))
|
|
70
|
-
.map(tag => ({
|
|
70
|
+
.filter((tag) => !existingIds.has(tag._id))
|
|
71
|
+
.map((tag) => ({
|
|
71
72
|
_key: nanoid(),
|
|
72
73
|
_ref: tag._id,
|
|
73
74
|
_type: 'reference' as const,
|
|
74
|
-
_weak: true
|
|
75
|
+
_weak: true,
|
|
75
76
|
}))
|
|
76
77
|
|
|
77
78
|
if (tagReferences.length === 0) return
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {describe, expect, it} from 'vitest'
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import type {Block} from '../types'
|
|
4
|
+
import blocksToText from './blocksToText'
|
|
4
5
|
|
|
5
6
|
describe('blocksToText', () => {
|
|
6
7
|
it('returns plain strings unchanged', () => {
|
|
@@ -18,14 +19,14 @@ describe('blocksToText', () => {
|
|
|
18
19
|
_type: 'block',
|
|
19
20
|
_key: 'a',
|
|
20
21
|
markDefs: [],
|
|
21
|
-
children: [{_key: 'a1', text: 'Line one', marks: []}]
|
|
22
|
+
children: [{_key: 'a1', text: 'Line one', marks: []}],
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
_type: 'block',
|
|
25
26
|
_key: 'b',
|
|
26
27
|
markDefs: [],
|
|
27
|
-
children: [{_key: 'b1', text: 'Line two', marks: []}]
|
|
28
|
-
}
|
|
28
|
+
children: [{_key: 'b1', text: 'Line two', marks: []}],
|
|
29
|
+
},
|
|
29
30
|
]
|
|
30
31
|
expect(blocksToText(blocks)).toBe('Line one\n\nLine two')
|
|
31
32
|
})
|
|
@@ -16,12 +16,12 @@ export default function (blocks: string | Block[] = '', opts = {}) {
|
|
|
16
16
|
|
|
17
17
|
const options = Object.assign({}, defaults, opts)
|
|
18
18
|
return blocks
|
|
19
|
-
.map(block => {
|
|
19
|
+
.map((block) => {
|
|
20
20
|
if (block._type !== 'block' || !block.children) {
|
|
21
21
|
return options.nonTextBehavior === 'remove' ? '' : `[${block._type} block]`
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
return block.children.map(child => child.text).join('')
|
|
24
|
+
return block.children.map((child) => child.text).join('')
|
|
25
25
|
})
|
|
26
26
|
.join('\n\n')
|
|
27
27
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @vitest-environment node
|
|
2
2
|
|
|
3
3
|
import {describe, expect, it} from 'vitest'
|
|
4
|
+
|
|
4
5
|
import {inputs} from '../config/searchFacets'
|
|
5
6
|
import type {SearchFacetInputProps} from '../types'
|
|
6
7
|
import constructFilter from './constructFilter'
|
|
@@ -10,7 +11,7 @@ describe('constructFilter', () => {
|
|
|
10
11
|
const q = constructFilter({
|
|
11
12
|
assetTypes: ['image', 'file'],
|
|
12
13
|
searchFacets: [],
|
|
13
|
-
searchQuery: undefined
|
|
14
|
+
searchQuery: undefined,
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
expect(q).toContain('_type in ["sanity.imageAsset","sanity.fileAsset"]')
|
|
@@ -21,7 +22,7 @@ describe('constructFilter', () => {
|
|
|
21
22
|
const q = constructFilter({
|
|
22
23
|
assetTypes: ['image'],
|
|
23
24
|
searchFacets: [],
|
|
24
|
-
searchQuery: undefined
|
|
25
|
+
searchQuery: undefined,
|
|
25
26
|
})
|
|
26
27
|
|
|
27
28
|
expect(q).toContain('_type in ["sanity.imageAsset"]')
|
|
@@ -31,11 +32,11 @@ describe('constructFilter', () => {
|
|
|
31
32
|
const q = constructFilter({
|
|
32
33
|
assetTypes: ['file', 'image'],
|
|
33
34
|
searchFacets: [],
|
|
34
|
-
searchQuery: ' hello '
|
|
35
|
+
searchQuery: ' hello ',
|
|
35
36
|
})
|
|
36
37
|
|
|
37
38
|
expect(q).toContain(
|
|
38
|
-
"[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*hello*'"
|
|
39
|
+
"[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*hello*'",
|
|
39
40
|
)
|
|
40
41
|
})
|
|
41
42
|
|
|
@@ -43,7 +44,7 @@ describe('constructFilter', () => {
|
|
|
43
44
|
const q = constructFilter({
|
|
44
45
|
assetTypes: ['image'],
|
|
45
46
|
searchFacets: [{...inputs.size, value: 500} as SearchFacetInputProps],
|
|
46
|
-
searchQuery: undefined
|
|
47
|
+
searchQuery: undefined,
|
|
47
48
|
})
|
|
48
49
|
|
|
49
50
|
expect(q.replace(/\s+/g, ' ')).toContain('round(size / 1000) > 500')
|
|
@@ -53,13 +54,13 @@ describe('constructFilter', () => {
|
|
|
53
54
|
const facet = {
|
|
54
55
|
...inputs.tag,
|
|
55
56
|
operatorType: 'references' as const,
|
|
56
|
-
value: {label: 'T', value: 'tag-id-1'}
|
|
57
|
+
value: {label: 'T', value: 'tag-id-1'},
|
|
57
58
|
} as SearchFacetInputProps
|
|
58
59
|
|
|
59
60
|
const q = constructFilter({
|
|
60
61
|
assetTypes: ['image', 'file'],
|
|
61
62
|
searchFacets: [facet],
|
|
62
|
-
searchQuery: undefined
|
|
63
|
+
searchQuery: undefined,
|
|
63
64
|
})
|
|
64
65
|
|
|
65
66
|
expect(q).toContain("references('tag-id-1')")
|
|
@@ -69,7 +70,7 @@ describe('constructFilter', () => {
|
|
|
69
70
|
const q = constructFilter({
|
|
70
71
|
assetTypes: ['image', 'file'],
|
|
71
72
|
searchFacets: [structuredClone(inputs.inUse)],
|
|
72
|
-
searchQuery: undefined
|
|
73
|
+
searchQuery: undefined,
|
|
73
74
|
})
|
|
74
75
|
|
|
75
76
|
expect(q).toContain('count(*[references(^._id)]) > 0')
|
|
@@ -79,7 +80,7 @@ describe('constructFilter', () => {
|
|
|
79
80
|
const q = constructFilter({
|
|
80
81
|
assetTypes: ['image', 'file'],
|
|
81
82
|
searchFacets: [{...inputs.title}, {...inputs.inUse}],
|
|
82
|
-
searchQuery: 'x'
|
|
83
|
+
searchQuery: 'x',
|
|
83
84
|
})
|
|
84
85
|
|
|
85
86
|
const parts = q.split(' && ')
|
|
@@ -94,16 +95,16 @@ describe('constructFilter', () => {
|
|
|
94
95
|
{
|
|
95
96
|
...inputs.tag,
|
|
96
97
|
operatorType: 'references',
|
|
97
|
-
value: {label: 'Example', value: 'abc123'}
|
|
98
|
-
} as SearchFacetInputProps
|
|
98
|
+
value: {label: 'Example', value: 'abc123'},
|
|
99
|
+
} as SearchFacetInputProps,
|
|
99
100
|
],
|
|
100
|
-
searchQuery: 'portrait'
|
|
101
|
+
searchQuery: 'portrait',
|
|
101
102
|
})
|
|
102
103
|
|
|
103
104
|
const normalized = q.replace(/\s+/g, ' ').trim()
|
|
104
105
|
|
|
105
106
|
expect(normalized).toBe(
|
|
106
|
-
'_type in ["sanity.fileAsset","sanity.imageAsset"] && !(_id in path("drafts.**")) && [_id, altText, assetId, creditLine, description, originalFilename, title, url] match \'*portrait*\' && round(size / 1000) > 100 && references(\'abc123\')'
|
|
107
|
+
'_type in ["sanity.fileAsset","sanity.imageAsset"] && !(_id in path("drafts.**")) && [_id, altText, assetId, creditLine, description, originalFilename, title, url] match \'*portrait*\' && round(size / 1000) > 100 && references(\'abc123\')',
|
|
107
108
|
)
|
|
108
109
|
})
|
|
109
110
|
|
|
@@ -111,7 +112,7 @@ describe('constructFilter', () => {
|
|
|
111
112
|
const q = constructFilter({
|
|
112
113
|
assetTypes: ['image', 'file'],
|
|
113
114
|
searchFacets: [],
|
|
114
|
-
searchQuery: undefined
|
|
115
|
+
searchQuery: undefined,
|
|
115
116
|
})
|
|
116
117
|
|
|
117
118
|
expect(q).not.toContain('match ')
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {AssetType, SearchFacetInputProps} from '../types'
|
|
2
1
|
import groq from 'groq'
|
|
3
2
|
|
|
4
3
|
import {operators} from '../config/searchFacets'
|
|
4
|
+
import type {AssetType, SearchFacetInputProps} from '../types'
|
|
5
5
|
|
|
6
6
|
const constructFilter = ({
|
|
7
7
|
assetTypes,
|
|
8
8
|
searchFacets,
|
|
9
|
-
searchQuery
|
|
9
|
+
searchQuery,
|
|
10
10
|
}: {
|
|
11
11
|
assetTypes: AssetType[]
|
|
12
12
|
searchFacets: SearchFacetInputProps[]
|
|
@@ -15,7 +15,7 @@ const constructFilter = ({
|
|
|
15
15
|
// Fetch asset types depending on current context.
|
|
16
16
|
// Either limit to a specific type (if being used as a custom asset source) or fetch both files and images (if being used as a tool)
|
|
17
17
|
// Sanity will crash if you try and insert incompatible asset types into fields!
|
|
18
|
-
const documentAssetTypes = assetTypes.map(type => `sanity.${type}Asset`)
|
|
18
|
+
const documentAssetTypes = assetTypes.map((type) => `sanity.${type}Asset`)
|
|
19
19
|
|
|
20
20
|
const baseFilter = groq`
|
|
21
21
|
_type in ${JSON.stringify(documentAssetTypes)} && !(_id in path("drafts.**"))
|
|
@@ -27,7 +27,7 @@ const constructFilter = ({
|
|
|
27
27
|
const operator = operators[operatorType]
|
|
28
28
|
|
|
29
29
|
// Get current modifier
|
|
30
|
-
const currentModifier = modifiers?.find(m => m.name === modifier)
|
|
30
|
+
const currentModifier = modifiers?.find((m) => m.name === modifier)
|
|
31
31
|
|
|
32
32
|
// Apply field modifier function (if present)
|
|
33
33
|
const facetField = currentModifier?.fieldModifier
|
|
@@ -54,7 +54,7 @@ const constructFilter = ({
|
|
|
54
54
|
const {field, operatorType, options, value} = facet
|
|
55
55
|
const operator = operators[operatorType]
|
|
56
56
|
|
|
57
|
-
const currentOptionValue = options?.find(l => l.name === value)?.value
|
|
57
|
+
const currentOptionValue = options?.find((l) => l.name === value)?.value
|
|
58
58
|
|
|
59
59
|
const fragment = operator.fn(currentOptionValue, field)
|
|
60
60
|
if (fragment) {
|
|
@@ -85,11 +85,11 @@ const constructFilter = ({
|
|
|
85
85
|
// references(*[_type == "media.tag" && name.current == "${searchQuery.trim()}"]._id)
|
|
86
86
|
...(searchQuery
|
|
87
87
|
? [
|
|
88
|
-
groq`[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*${searchQuery.trim()}*'
|
|
88
|
+
groq`[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*${searchQuery.trim()}*'`,
|
|
89
89
|
]
|
|
90
90
|
: []),
|
|
91
91
|
// Search facets
|
|
92
|
-
...searchFacetFragments
|
|
92
|
+
...searchFacetFragments,
|
|
93
93
|
].join(' && ')
|
|
94
94
|
|
|
95
95
|
return constructedQuery
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
1
|
import {firstValueFrom} from 'rxjs'
|
|
2
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
|
+
|
|
3
4
|
import {generatePreviewBlobUrl$} from './generatePreviewBlobUrl'
|
|
4
5
|
|
|
5
6
|
describe('generatePreviewBlobUrl$', () => {
|
|
@@ -25,7 +26,7 @@ describe('generatePreviewBlobUrl$', () => {
|
|
|
25
26
|
if (tagName === 'canvas') {
|
|
26
27
|
const el = origCreateElement('canvas')
|
|
27
28
|
vi.spyOn(el, 'getContext').mockReturnValue({
|
|
28
|
-
drawImage: vi.fn()
|
|
29
|
+
drawImage: vi.fn(),
|
|
29
30
|
} as unknown as CanvasRenderingContext2D)
|
|
30
31
|
/* eslint-disable callback-return, consistent-return -- HTMLCanvasElement#toBlob sync test stub */
|
|
31
32
|
el.toBlob = function toBlob(cb: ((blob: Blob | null) => void) | null | undefined) {
|
|
@@ -44,12 +45,12 @@ describe('generatePreviewBlobUrl$', () => {
|
|
|
44
45
|
Object.defineProperty(URL, 'createObjectURL', {
|
|
45
46
|
configurable: true,
|
|
46
47
|
writable: true,
|
|
47
|
-
value: createObjectURL
|
|
48
|
+
value: createObjectURL,
|
|
48
49
|
})
|
|
49
50
|
Object.defineProperty(URL, 'revokeObjectURL', {
|
|
50
51
|
configurable: true,
|
|
51
52
|
writable: true,
|
|
52
|
-
value: revokeObjectURL
|
|
53
|
+
value: revokeObjectURL,
|
|
53
54
|
})
|
|
54
55
|
})
|
|
55
56
|
|
|
@@ -62,7 +63,7 @@ describe('generatePreviewBlobUrl$', () => {
|
|
|
62
63
|
|
|
63
64
|
it('emits a blob URL when canvas preview succeeds', async () => {
|
|
64
65
|
const url = await firstValueFrom(
|
|
65
|
-
generatePreviewBlobUrl$(new File(['x'], 'photo.jpg', {type: 'image/jpeg'}))
|
|
66
|
+
generatePreviewBlobUrl$(new File(['x'], 'photo.jpg', {type: 'image/jpeg'})),
|
|
66
67
|
)
|
|
67
68
|
expect(url).toBe('blob:mock-preview')
|
|
68
69
|
})
|
|
@@ -4,7 +4,7 @@ import {mergeMap} from 'rxjs/operators'
|
|
|
4
4
|
const PREVIEW_WIDTH = 180 // px
|
|
5
5
|
|
|
6
6
|
const createBlob = (img: HTMLImageElement): Promise<Blob | null> => {
|
|
7
|
-
return new Promise(resolve => {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
8
|
const imageAspect = img.width / img.height
|
|
9
9
|
|
|
10
10
|
// Create a canvas element which we'll use to generate a low resolution preview.
|
|
@@ -26,7 +26,7 @@ const createBlob = (img: HTMLImageElement): Promise<Blob | null> => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const createImageEl = (file: File): Promise<HTMLImageElement> => {
|
|
29
|
-
return new Promise(resolve => {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
30
|
const blobUrlLarge = window.URL.createObjectURL(file)
|
|
31
31
|
const img = new Image()
|
|
32
32
|
img.onload = () => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {describe, expect, it} from 'vitest'
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import type {ImageAsset} from '../types'
|
|
4
|
+
import getAssetResolution from './getAssetResolution'
|
|
4
5
|
|
|
5
6
|
describe('getAssetResolution', () => {
|
|
6
7
|
it('formats width x height with px suffix', () => {
|
|
7
8
|
const asset = {
|
|
8
|
-
metadata: {dimensions: {width: 1920, height: 1080}}
|
|
9
|
+
metadata: {dimensions: {width: 1920, height: 1080}},
|
|
9
10
|
} as ImageAsset
|
|
10
11
|
expect(getAssetResolution(asset)).toBe('1920x1080px')
|
|
11
12
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @vitest-environment node
|
|
2
2
|
|
|
3
3
|
import {describe, expect, it} from 'vitest'
|
|
4
|
+
|
|
4
5
|
import getDocumentAssetIds from './getDocumentAssetIds'
|
|
5
6
|
|
|
6
7
|
describe('getDocumentAssetIds', () => {
|
|
@@ -15,9 +16,9 @@ describe('getDocumentAssetIds', () => {
|
|
|
15
16
|
body: [
|
|
16
17
|
{
|
|
17
18
|
_type: 'block',
|
|
18
|
-
asset: {_type: 'reference', _ref: 'image-asset-1'}
|
|
19
|
-
}
|
|
20
|
-
]
|
|
19
|
+
asset: {_type: 'reference', _ref: 'image-asset-1'},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
21
22
|
} as any
|
|
22
23
|
|
|
23
24
|
expect(getDocumentAssetIds(doc)).toEqual(['image-asset-1'])
|
|
@@ -30,8 +31,8 @@ describe('getDocumentAssetIds', () => {
|
|
|
30
31
|
modules: [
|
|
31
32
|
{image: {asset: {_type: 'reference', _ref: 'b'}}},
|
|
32
33
|
{image: {asset: {_type: 'reference', _ref: 'a'}}},
|
|
33
|
-
{image: {asset: {_type: 'reference', _ref: 'b'}}}
|
|
34
|
-
]
|
|
34
|
+
{image: {asset: {_type: 'reference', _ref: 'b'}}},
|
|
35
|
+
],
|
|
35
36
|
} as any
|
|
36
37
|
|
|
37
38
|
expect(getDocumentAssetIds(doc)).toEqual(['a', 'b'])
|
|
@@ -41,7 +42,7 @@ describe('getDocumentAssetIds', () => {
|
|
|
41
42
|
const doc = {
|
|
42
43
|
_id: 'doc1',
|
|
43
44
|
_type: 'post',
|
|
44
|
-
author: {_type: 'reference', _ref: 'person-1'}
|
|
45
|
+
author: {_type: 'reference', _ref: 'person-1'},
|
|
45
46
|
} as any
|
|
46
47
|
|
|
47
48
|
expect(getDocumentAssetIds(doc)).toEqual([])
|
|
@@ -6,7 +6,7 @@ const isPlainObject = (value: any) =>
|
|
|
6
6
|
// Recursively search node for any linked asset ids (`asset._type === 'reference'`)
|
|
7
7
|
const getAssetIds = (node: Record<string, any>, acc: string[] = []) => {
|
|
8
8
|
if (Array.isArray(node)) {
|
|
9
|
-
node.forEach(v => {
|
|
9
|
+
node.forEach((v) => {
|
|
10
10
|
getAssetIds(v, acc)
|
|
11
11
|
})
|
|
12
12
|
}
|
|
@@ -16,7 +16,7 @@ const getAssetIds = (node: Record<string, any>, acc: string[] = []) => {
|
|
|
16
16
|
acc.push(node.asset._ref)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
Object.values(node).forEach(val => {
|
|
19
|
+
Object.values(node).forEach((val) => {
|
|
20
20
|
getAssetIds(val, acc)
|
|
21
21
|
})
|
|
22
22
|
}
|
|
@@ -4,36 +4,36 @@ import {type ThemeColorSchemeKey, studioTheme} from '@sanity/ui'
|
|
|
4
4
|
const SCHEME_COLORS = {
|
|
5
5
|
bg: {
|
|
6
6
|
dark: hues.gray[950].hex,
|
|
7
|
-
light: hues.gray[50].hex
|
|
7
|
+
light: hues.gray[50].hex,
|
|
8
8
|
},
|
|
9
9
|
bg2: {
|
|
10
10
|
dark: hues.gray[900].hex,
|
|
11
|
-
light: hues.gray[100].hex
|
|
11
|
+
light: hues.gray[100].hex,
|
|
12
12
|
},
|
|
13
13
|
inputEnabledBorder: {
|
|
14
14
|
dark: studioTheme.color.dark.default.input.default.enabled.border,
|
|
15
|
-
light: studioTheme.color.light.default.input.default.enabled.border
|
|
15
|
+
light: studioTheme.color.light.default.input.default.enabled.border,
|
|
16
16
|
},
|
|
17
17
|
inputHoveredBorder: {
|
|
18
18
|
dark: studioTheme.color.dark.default.input.default.hovered.border,
|
|
19
|
-
light: studioTheme.color.light.default.input.default.hovered.border
|
|
19
|
+
light: studioTheme.color.light.default.input.default.hovered.border,
|
|
20
20
|
},
|
|
21
21
|
mutedHoveredBg: {
|
|
22
22
|
dark: studioTheme.color.dark.primary.muted.primary.hovered.bg,
|
|
23
|
-
light: studioTheme.color.light.primary.muted.primary.hovered.bg
|
|
23
|
+
light: studioTheme.color.light.primary.muted.primary.hovered.bg,
|
|
24
24
|
},
|
|
25
25
|
mutedHoveredFg: {
|
|
26
26
|
dark: studioTheme.color.dark.primary.muted.primary.hovered.fg,
|
|
27
|
-
light: studioTheme.color.light.primary.muted.primary.hovered.fg
|
|
27
|
+
light: studioTheme.color.light.primary.muted.primary.hovered.fg,
|
|
28
28
|
},
|
|
29
29
|
mutedSelectedBg: {
|
|
30
30
|
dark: studioTheme.color.dark.primary.muted.primary.selected.bg,
|
|
31
|
-
light: studioTheme.color.light.primary.muted.primary.selected.bg
|
|
31
|
+
light: studioTheme.color.light.primary.muted.primary.selected.bg,
|
|
32
32
|
},
|
|
33
33
|
spotBlue: {
|
|
34
34
|
dark: studioTheme.color.dark.primary.spot.blue,
|
|
35
|
-
light: studioTheme.color.light.primary.spot.blue
|
|
36
|
-
}
|
|
35
|
+
light: studioTheme.color.light.primary.spot.blue,
|
|
36
|
+
},
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
type SchemeColorKey = keyof typeof SCHEME_COLORS
|