sanity-plugin-media 4.3.0 → 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.
Files changed (145) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +12 -12
  3. package/dist/index.d.mts +263 -195
  4. package/dist/index.d.ts +263 -195
  5. package/dist/index.js +83 -203
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +84 -203
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +42 -64
  10. package/src/__tests__/fixtures/createEpicTestStore.ts +5 -4
  11. package/src/__tests__/fixtures/mockSanityClient.ts +8 -8
  12. package/src/__tests__/fixtures/renderWithProviders.tsx +8 -7
  13. package/src/__tests__/fixtures/rootState.ts +4 -4
  14. package/src/components/AssetGridVirtualized/index.tsx +8 -7
  15. package/src/components/AssetMetadata/index.tsx +6 -5
  16. package/src/components/AssetTableVirtualized/index.tsx +7 -6
  17. package/src/components/AutoTagInputWrapper/index.tsx +9 -4
  18. package/src/components/Browser/Browser.test.tsx +9 -8
  19. package/src/components/Browser/index.tsx +2 -1
  20. package/src/components/Browser/useBrowserInit.ts +9 -9
  21. package/src/components/ButtonAssetCopy/index.tsx +1 -0
  22. package/src/components/ButtonViewGroup/index.tsx +4 -3
  23. package/src/components/CardAsset/CardAsset.test.tsx +53 -52
  24. package/src/components/CardAsset/index.tsx +52 -49
  25. package/src/components/CardUpload/index.tsx +7 -6
  26. package/src/components/Controls/index.tsx +7 -6
  27. package/src/components/DebugControls/index.tsx +5 -4
  28. package/src/components/DialogAssetEdit/Details.tsx +3 -2
  29. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +28 -27
  30. package/src/components/DialogAssetEdit/index.tsx +37 -37
  31. package/src/components/DialogConfirm/index.tsx +2 -1
  32. package/src/components/DialogSearchFacets/index.tsx +3 -2
  33. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +16 -15
  34. package/src/components/DialogTagCreate/index.tsx +11 -10
  35. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +28 -27
  36. package/src/components/DialogTagEdit/index.tsx +17 -16
  37. package/src/components/DialogTags/index.tsx +4 -3
  38. package/src/components/Dialogs/index.tsx +2 -3
  39. package/src/components/DocumentList/index.tsx +2 -3
  40. package/src/components/FileAssetPreview/index.tsx +2 -2
  41. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +12 -11
  42. package/src/components/FormBuilderTool/index.tsx +2 -1
  43. package/src/components/FormFieldInputLabel/index.tsx +1 -2
  44. package/src/components/FormFieldInputTags/index.tsx +4 -3
  45. package/src/components/FormSubmitButton/index.tsx +1 -1
  46. package/src/components/Header/index.tsx +3 -3
  47. package/src/components/Image/index.tsx +10 -4
  48. package/src/components/Items/index.tsx +5 -4
  49. package/src/components/Notifications/index.tsx +3 -2
  50. package/src/components/OrderSelect/index.tsx +4 -3
  51. package/src/components/PickedBar/index.tsx +2 -1
  52. package/src/components/Progress/index.tsx +3 -3
  53. package/src/components/ReduxProvider/index.tsx +15 -12
  54. package/src/components/SearchFacet/index.tsx +3 -2
  55. package/src/components/SearchFacetNumber/index.tsx +8 -8
  56. package/src/components/SearchFacetSelect/index.tsx +7 -8
  57. package/src/components/SearchFacetString/index.tsx +1 -1
  58. package/src/components/SearchFacetTags/index.tsx +13 -12
  59. package/src/components/SearchFacets/index.tsx +2 -3
  60. package/src/components/SearchFacetsControl/index.tsx +13 -12
  61. package/src/components/TableHeader/index.tsx +18 -17
  62. package/src/components/TableHeaderItem/index.tsx +4 -4
  63. package/src/components/TableRowAsset/index.tsx +37 -36
  64. package/src/components/TableRowUpload/index.tsx +7 -6
  65. package/src/components/Tag/index.tsx +8 -7
  66. package/src/components/TagView/index.tsx +2 -2
  67. package/src/components/TagViewHeader/index.tsx +5 -4
  68. package/src/components/TagsPanel/index.tsx +3 -3
  69. package/src/components/TagsVirtualized/index.tsx +25 -24
  70. package/src/components/TextInputSearch/index.tsx +3 -2
  71. package/src/components/UploadDropzone/UploadDropzone.test.tsx +8 -7
  72. package/src/components/UploadDropzone/index.tsx +14 -13
  73. package/src/config/orders.ts +6 -6
  74. package/src/config/searchFacets.ts +56 -55
  75. package/src/constants.ts +15 -14
  76. package/src/contexts/AssetSourceDispatchContext.tsx +1 -1
  77. package/src/contexts/ToolOptionsContext.tsx +6 -5
  78. package/src/formSchema/index.test.ts +6 -5
  79. package/src/formSchema/index.ts +5 -5
  80. package/src/hooks/useBreakpointIndex.ts +6 -6
  81. package/src/hooks/useKeyPress.ts +2 -2
  82. package/src/hooks/usePortalPopoverProps.ts +1 -1
  83. package/src/modules/assets/actions.ts +8 -7
  84. package/src/modules/assets/deleteAndUpdateEpics.test.ts +18 -17
  85. package/src/modules/assets/fetchEpic.test.ts +12 -11
  86. package/src/modules/assets/index.ts +134 -133
  87. package/src/modules/assets/reducer.test.ts +9 -8
  88. package/src/modules/assets/tagsAndListenerEpics.test.ts +36 -35
  89. package/src/modules/debug/index.ts +3 -3
  90. package/src/modules/dialog/actions.ts +2 -2
  91. package/src/modules/dialog/epics.test.ts +29 -28
  92. package/src/modules/dialog/index.ts +36 -35
  93. package/src/modules/dialog/reducer.test.ts +31 -30
  94. package/src/modules/index.ts +9 -9
  95. package/src/modules/notifications/epics.test.ts +71 -70
  96. package/src/modules/notifications/index.ts +50 -49
  97. package/src/modules/notifications/reducer.test.ts +8 -7
  98. package/src/modules/search/index.test.ts +2 -1
  99. package/src/modules/search/index.ts +22 -22
  100. package/src/modules/selected/index.ts +2 -2
  101. package/src/modules/selectors.test.ts +4 -3
  102. package/src/modules/selectors.ts +5 -5
  103. package/src/modules/tags/epics.test.ts +16 -15
  104. package/src/modules/tags/index.test.ts +2 -1
  105. package/src/modules/tags/index.ts +82 -81
  106. package/src/modules/uploads/actions.ts +3 -3
  107. package/src/modules/uploads/epics.test.ts +13 -12
  108. package/src/modules/uploads/index.test.ts +8 -7
  109. package/src/modules/uploads/index.ts +48 -47
  110. package/src/operators/checkTagName.test.ts +7 -6
  111. package/src/operators/checkTagName.ts +6 -5
  112. package/src/operators/debugThrottle.ts +4 -4
  113. package/src/plugin.tsx +18 -18
  114. package/src/schemas/tag.ts +7 -7
  115. package/src/styled/react-select/creatable.tsx +40 -39
  116. package/src/styled/react-select/single.tsx +39 -38
  117. package/src/types/index.ts +4 -3
  118. package/src/utils/applyMediaTags.ts +11 -10
  119. package/src/utils/blocksToText.test.ts +5 -4
  120. package/src/utils/blocksToText.ts +2 -2
  121. package/src/utils/constructFilter.test.ts +15 -14
  122. package/src/utils/constructFilter.ts +7 -7
  123. package/src/utils/generatePreviewBlobUrl.test.ts +6 -5
  124. package/src/utils/generatePreviewBlobUrl.ts +2 -2
  125. package/src/utils/getAssetResolution.test.ts +3 -2
  126. package/src/utils/getDocumentAssetIds.test.ts +7 -6
  127. package/src/utils/getDocumentAssetIds.ts +2 -2
  128. package/src/utils/getSchemeColor.test.ts +1 -0
  129. package/src/utils/getSchemeColor.ts +9 -9
  130. package/src/utils/getTagSelectOptions.test.ts +6 -5
  131. package/src/utils/getTagSelectOptions.ts +1 -1
  132. package/src/utils/getUniqueDocuments.test.ts +4 -3
  133. package/src/utils/getUniqueDocuments.ts +2 -2
  134. package/src/utils/imageDprUrl.test.ts +4 -3
  135. package/src/utils/imageDprUrl.ts +1 -1
  136. package/src/utils/isSupportedAssetType.test.ts +1 -0
  137. package/src/utils/mediaField.ts +4 -3
  138. package/src/utils/sanitizeFormData.test.ts +14 -13
  139. package/src/utils/typeGuards.test.ts +2 -1
  140. package/src/utils/uploadSanityAsset.test.ts +5 -4
  141. package/src/utils/uploadSanityAsset.ts +17 -16
  142. package/src/utils/withMaxConcurrency.test.ts +5 -4
  143. package/src/utils/withMaxConcurrency.ts +4 -4
  144. package/src/utils/zodFormResolver.ts +17 -0
  145. 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
  }
@@ -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
- import {getAssetFormSchema, tagFormSchema, tagOptionSchema} from '../formSchema'
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
- import blocksToText from './blocksToText'
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
- import getAssetResolution from './getAssetResolution'
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
  }
@@ -1,4 +1,5 @@
1
1
  import {describe, expect, it} from 'vitest'
2
+
2
3
  import {getSchemeColor} from './getSchemeColor'
3
4
 
4
5
  describe('getSchemeColor', () => {
@@ -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