sanity-plugin-media 4.3.5 → 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 (166) hide show
  1. package/dist/index.d.ts +190 -413
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +2532 -3564
  4. package/dist/index.js.map +1 -1
  5. package/package.json +29 -35
  6. package/dist/index.cjs +0 -5753
  7. package/dist/index.cjs.map +0 -1
  8. package/dist/index.d.cts +0 -462
  9. package/sanity.json +0 -8
  10. package/src/__tests__/fixtures/createEpicTestStore.ts +0 -28
  11. package/src/__tests__/fixtures/listenMock.ts +0 -9
  12. package/src/__tests__/fixtures/mockSanityClient.ts +0 -84
  13. package/src/__tests__/fixtures/renderWithProviders.tsx +0 -55
  14. package/src/__tests__/fixtures/rootState.ts +0 -27
  15. package/src/__tests__/fixtures/withinDialog.ts +0 -28
  16. package/src/components/AssetGridVirtualized/index.tsx +0 -94
  17. package/src/components/AssetMetadata/index.tsx +0 -122
  18. package/src/components/AssetTableVirtualized/index.tsx +0 -73
  19. package/src/components/AutoTagInputWrapper/index.tsx +0 -87
  20. package/src/components/Browser/Browser.test.tsx +0 -45
  21. package/src/components/Browser/index.tsx +0 -90
  22. package/src/components/Browser/useBrowserInit.ts +0 -126
  23. package/src/components/ButtonAssetCopy/index.tsx +0 -65
  24. package/src/components/ButtonViewGroup/index.tsx +0 -39
  25. package/src/components/CardAsset/CardAsset.test.tsx +0 -323
  26. package/src/components/CardAsset/index.tsx +0 -290
  27. package/src/components/CardUpload/index.tsx +0 -161
  28. package/src/components/Controls/index.tsx +0 -136
  29. package/src/components/DebugControls/index.tsx +0 -80
  30. package/src/components/Dialog/index.tsx +0 -11
  31. package/src/components/DialogAssetEdit/Details.tsx +0 -181
  32. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +0 -216
  33. package/src/components/DialogAssetEdit/index.tsx +0 -492
  34. package/src/components/DialogConfirm/index.tsx +0 -88
  35. package/src/components/DialogSearchFacets/index.tsx +0 -42
  36. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +0 -121
  37. package/src/components/DialogTagCreate/index.tsx +0 -103
  38. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +0 -165
  39. package/src/components/DialogTagEdit/index.tsx +0 -190
  40. package/src/components/DialogTags/index.tsx +0 -45
  41. package/src/components/Dialogs/index.tsx +0 -76
  42. package/src/components/DocumentList/index.tsx +0 -62
  43. package/src/components/FileAssetPreview/index.tsx +0 -37
  44. package/src/components/FileIcon/index.tsx +0 -43
  45. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +0 -63
  46. package/src/components/FormBuilderTool/index.tsx +0 -69
  47. package/src/components/FormFieldInputLabel/index.tsx +0 -66
  48. package/src/components/FormFieldInputTags/index.tsx +0 -98
  49. package/src/components/FormFieldInputText/index.tsx +0 -41
  50. package/src/components/FormFieldInputTextarea/index.tsx +0 -43
  51. package/src/components/FormSubmitButton/index.tsx +0 -59
  52. package/src/components/Header/index.tsx +0 -80
  53. package/src/components/Image/index.tsx +0 -41
  54. package/src/components/Items/index.tsx +0 -68
  55. package/src/components/Notifications/index.tsx +0 -24
  56. package/src/components/OrderSelect/index.tsx +0 -66
  57. package/src/components/PickedBar/index.tsx +0 -77
  58. package/src/components/Progress/index.tsx +0 -38
  59. package/src/components/ReduxProvider/index.tsx +0 -96
  60. package/src/components/SearchFacet/index.tsx +0 -66
  61. package/src/components/SearchFacetNumber/index.tsx +0 -133
  62. package/src/components/SearchFacetSelect/index.tsx +0 -110
  63. package/src/components/SearchFacetString/index.tsx +0 -88
  64. package/src/components/SearchFacetTags/index.tsx +0 -121
  65. package/src/components/SearchFacets/index.tsx +0 -72
  66. package/src/components/SearchFacetsControl/index.tsx +0 -140
  67. package/src/components/TableHeader/index.tsx +0 -110
  68. package/src/components/TableHeaderItem/index.tsx +0 -61
  69. package/src/components/TableRowAsset/index.tsx +0 -420
  70. package/src/components/TableRowUpload/index.tsx +0 -164
  71. package/src/components/Tag/index.tsx +0 -200
  72. package/src/components/TagIcon/index.tsx +0 -22
  73. package/src/components/TagView/index.tsx +0 -39
  74. package/src/components/TagViewHeader/index.tsx +0 -70
  75. package/src/components/TagsPanel/index.tsx +0 -40
  76. package/src/components/TagsVirtualized/index.tsx +0 -160
  77. package/src/components/TextInputNumber/index.tsx +0 -32
  78. package/src/components/TextInputSearch/index.tsx +0 -60
  79. package/src/components/Tool/index.tsx +0 -13
  80. package/src/components/UploadDropzone/UploadDropzone.test.tsx +0 -40
  81. package/src/components/UploadDropzone/index.tsx +0 -173
  82. package/src/config/orders.ts +0 -28
  83. package/src/config/searchFacets.ts +0 -312
  84. package/src/constants.ts +0 -87
  85. package/src/contexts/AssetSourceDispatchContext.tsx +0 -37
  86. package/src/contexts/DropzoneDispatchContext.tsx +0 -34
  87. package/src/contexts/ToolOptionsContext.tsx +0 -65
  88. package/src/formSchema/index.test.ts +0 -56
  89. package/src/formSchema/index.ts +0 -39
  90. package/src/hooks/useBreakpointIndex.ts +0 -49
  91. package/src/hooks/useKeyPress.ts +0 -39
  92. package/src/hooks/useOnScreen.ts +0 -34
  93. package/src/hooks/usePortalPopoverProps.ts +0 -13
  94. package/src/hooks/useTypedSelector.ts +0 -7
  95. package/src/hooks/useVersionedClient.ts +0 -6
  96. package/src/index.ts +0 -5
  97. package/src/modules/assets/actions.ts +0 -42
  98. package/src/modules/assets/deleteAndUpdateEpics.test.ts +0 -87
  99. package/src/modules/assets/fetchEpic.test.ts +0 -73
  100. package/src/modules/assets/index.ts +0 -825
  101. package/src/modules/assets/reducer.test.ts +0 -91
  102. package/src/modules/assets/tagsAndListenerEpics.test.ts +0 -206
  103. package/src/modules/debug/index.ts +0 -28
  104. package/src/modules/dialog/actions.ts +0 -10
  105. package/src/modules/dialog/epics.test.ts +0 -168
  106. package/src/modules/dialog/index.ts +0 -238
  107. package/src/modules/dialog/reducer.test.ts +0 -185
  108. package/src/modules/index.ts +0 -124
  109. package/src/modules/notifications/epics.test.ts +0 -374
  110. package/src/modules/notifications/index.ts +0 -199
  111. package/src/modules/notifications/reducer.test.ts +0 -54
  112. package/src/modules/search/index.test.ts +0 -36
  113. package/src/modules/search/index.ts +0 -167
  114. package/src/modules/selected/index.ts +0 -22
  115. package/src/modules/selectors.test.ts +0 -21
  116. package/src/modules/selectors.ts +0 -17
  117. package/src/modules/tags/epics.test.ts +0 -96
  118. package/src/modules/tags/index.test.ts +0 -42
  119. package/src/modules/tags/index.ts +0 -540
  120. package/src/modules/types.ts +0 -3
  121. package/src/modules/uploads/actions.ts +0 -13
  122. package/src/modules/uploads/epics.test.ts +0 -109
  123. package/src/modules/uploads/index.test.ts +0 -59
  124. package/src/modules/uploads/index.ts +0 -282
  125. package/src/operators/checkTagName.test.ts +0 -29
  126. package/src/operators/checkTagName.ts +0 -33
  127. package/src/operators/debugThrottle.ts +0 -25
  128. package/src/plugin.tsx +0 -54
  129. package/src/schemas/tag.ts +0 -28
  130. package/src/styled/GlobalStyles/index.tsx +0 -40
  131. package/src/styled/react-select/creatable.tsx +0 -184
  132. package/src/styled/react-select/single.tsx +0 -184
  133. package/src/types/index.ts +0 -379
  134. package/src/types/sanity-ui.d.ts +0 -6
  135. package/src/utils/applyMediaTags.ts +0 -87
  136. package/src/utils/blocksToText.test.ts +0 -43
  137. package/src/utils/blocksToText.ts +0 -27
  138. package/src/utils/constructFilter.test.ts +0 -120
  139. package/src/utils/constructFilter.ts +0 -98
  140. package/src/utils/generatePreviewBlobUrl.test.ts +0 -70
  141. package/src/utils/generatePreviewBlobUrl.ts +0 -53
  142. package/src/utils/getAssetResolution.test.ts +0 -13
  143. package/src/utils/getAssetResolution.ts +0 -7
  144. package/src/utils/getDocumentAssetIds.test.ts +0 -50
  145. package/src/utils/getDocumentAssetIds.ts +0 -35
  146. package/src/utils/getSchemeColor.test.ts +0 -12
  147. package/src/utils/getSchemeColor.ts +0 -43
  148. package/src/utils/getTagSelectOptions.test.ts +0 -44
  149. package/src/utils/getTagSelectOptions.ts +0 -16
  150. package/src/utils/getUniqueDocuments.test.ts +0 -26
  151. package/src/utils/getUniqueDocuments.ts +0 -15
  152. package/src/utils/imageDprUrl.test.ts +0 -46
  153. package/src/utils/imageDprUrl.ts +0 -27
  154. package/src/utils/isSupportedAssetType.test.ts +0 -16
  155. package/src/utils/isSupportedAssetType.ts +0 -15
  156. package/src/utils/mediaField.ts +0 -73
  157. package/src/utils/sanitizeFormData.test.ts +0 -59
  158. package/src/utils/sanitizeFormData.ts +0 -26
  159. package/src/utils/typeGuards.test.ts +0 -18
  160. package/src/utils/typeGuards.ts +0 -9
  161. package/src/utils/uploadSanityAsset.test.ts +0 -29
  162. package/src/utils/uploadSanityAsset.ts +0 -97
  163. package/src/utils/withMaxConcurrency.test.ts +0 -43
  164. package/src/utils/withMaxConcurrency.ts +0 -55
  165. package/src/utils/zodFormResolver.ts +0 -17
  166. package/v2-incompatible.js +0 -11
@@ -1,98 +0,0 @@
1
- import groq from 'groq'
2
-
3
- import {operators} from '../config/searchFacets'
4
- import type {AssetType, SearchFacetInputProps} from '../types'
5
-
6
- const constructFilter = ({
7
- assetTypes,
8
- searchFacets,
9
- searchQuery,
10
- }: {
11
- assetTypes: AssetType[]
12
- searchFacets: SearchFacetInputProps[]
13
- searchQuery?: string
14
- }): string => {
15
- // Fetch asset types depending on current context.
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
- // Sanity will crash if you try and insert incompatible asset types into fields!
18
- const documentAssetTypes = assetTypes.map((type) => `sanity.${type}Asset`)
19
-
20
- const baseFilter = groq`
21
- _type in ${JSON.stringify(documentAssetTypes)} && !(_id in path("drafts.**"))
22
- `
23
-
24
- const searchFacetFragments = searchFacets.reduce((acc: string[], facet) => {
25
- if (facet.type === 'number') {
26
- const {field, modifier, modifiers, operatorType, value} = facet
27
- const operator = operators[operatorType]
28
-
29
- // Get current modifier
30
- const currentModifier = modifiers?.find((m) => m.name === modifier)
31
-
32
- // Apply field modifier function (if present)
33
- const facetField = currentModifier?.fieldModifier
34
- ? currentModifier.fieldModifier(field)
35
- : field
36
-
37
- const fragment = operator.fn(value, facetField)
38
- if (fragment) {
39
- acc.push(fragment)
40
- }
41
- }
42
-
43
- if (facet.type === 'searchable') {
44
- const {field, operatorType, value} = facet
45
- const operator = operators[operatorType]
46
-
47
- const fragment = operator.fn(value?.value, field)
48
- if (fragment) {
49
- acc.push(fragment)
50
- }
51
- }
52
-
53
- if (facet.type === 'select') {
54
- const {field, operatorType, options, value} = facet
55
- const operator = operators[operatorType]
56
-
57
- const currentOptionValue = options?.find((l) => l.name === value)?.value
58
-
59
- const fragment = operator.fn(currentOptionValue, field)
60
- if (fragment) {
61
- acc.push(fragment)
62
- }
63
- }
64
-
65
- if (facet.type === 'string') {
66
- const {field, operatorType, value} = facet
67
- const operator = operators[operatorType]
68
-
69
- const fragment = operator.fn(value, field)
70
- if (fragment) {
71
- acc.push(fragment)
72
- }
73
- }
74
-
75
- return acc
76
- }, [])
77
-
78
- // Join separate filter fragments
79
- const constructedQuery = [
80
- // Base filter
81
- baseFilter,
82
- // Search query (if present)
83
- // NOTE: Currently this only searches direct fields on sanity.fileAsset/sanity.imageAsset and NOT referenced tags
84
- // It's possible to add this by adding the following line to the searchQuery, but it's quite slow
85
- // references(*[_type == "media.tag" && name.current == "${searchQuery.trim()}"]._id)
86
- ...(searchQuery
87
- ? [
88
- groq`[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*${searchQuery.trim()}*'`,
89
- ]
90
- : []),
91
- // Search facets
92
- ...searchFacetFragments,
93
- ].join(' && ')
94
-
95
- return constructedQuery
96
- }
97
-
98
- export default constructFilter
@@ -1,70 +0,0 @@
1
- import {firstValueFrom} from 'rxjs'
2
- import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
3
-
4
- import {generatePreviewBlobUrl$} from './generatePreviewBlobUrl'
5
-
6
- describe('generatePreviewBlobUrl$', () => {
7
- const origCreateElement = document.createElement.bind(document)
8
-
9
- beforeEach(() => {
10
- class MockImage {
11
- onload: (() => void) | null = null
12
- width = 400
13
- height = 200
14
- private _src = ''
15
- get src() {
16
- return this._src
17
- }
18
- set src(v: string) {
19
- this._src = v
20
- queueMicrotask(() => this.onload?.())
21
- }
22
- }
23
- vi.stubGlobal('Image', MockImage)
24
-
25
- vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
26
- if (tagName === 'canvas') {
27
- const el = origCreateElement('canvas')
28
- vi.spyOn(el, 'getContext').mockReturnValue({
29
- drawImage: vi.fn(),
30
- } as unknown as CanvasRenderingContext2D)
31
- /* eslint-disable callback-return, consistent-return -- HTMLCanvasElement#toBlob sync test stub */
32
- el.toBlob = function toBlob(cb: ((blob: Blob | null) => void) | null | undefined) {
33
- if (cb) {
34
- cb(new Blob(['x'], {type: 'image/jpeg'}))
35
- }
36
- }
37
- /* eslint-enable callback-return, consistent-return */
38
- return el
39
- }
40
- return origCreateElement(tagName)
41
- })
42
-
43
- const createObjectURL = vi.fn(() => 'blob:mock-preview')
44
- const revokeObjectURL = vi.fn()
45
- Object.defineProperty(URL, 'createObjectURL', {
46
- configurable: true,
47
- writable: true,
48
- value: createObjectURL,
49
- })
50
- Object.defineProperty(URL, 'revokeObjectURL', {
51
- configurable: true,
52
- writable: true,
53
- value: revokeObjectURL,
54
- })
55
- })
56
-
57
- afterEach(() => {
58
- vi.unstubAllGlobals()
59
- vi.restoreAllMocks()
60
- delete (URL as Partial<typeof URL> & {createObjectURL?: unknown}).createObjectURL
61
- delete (URL as Partial<typeof URL> & {revokeObjectURL?: unknown}).revokeObjectURL
62
- })
63
-
64
- it('emits a blob URL when canvas preview succeeds', async () => {
65
- const url = await firstValueFrom(
66
- generatePreviewBlobUrl$(new File(['x'], 'photo.jpg', {type: 'image/jpeg'})),
67
- )
68
- expect(url).toBe('blob:mock-preview')
69
- })
70
- })
@@ -1,53 +0,0 @@
1
- import {Observable, from, of} from 'rxjs'
2
- import {mergeMap} from 'rxjs/operators'
3
-
4
- const PREVIEW_WIDTH = 180 // px
5
-
6
- const createBlob = (img: HTMLImageElement): Promise<Blob | null> => {
7
- return new Promise((resolve) => {
8
- const imageAspect = img.width / img.height
9
-
10
- // Create a canvas element which we'll use to generate a low resolution preview.
11
- // Ensure that the canvas is at least 1 pixel high as blob generation will fail otherwise.
12
- const canvas: HTMLCanvasElement = document.createElement('canvas')
13
- canvas.width = PREVIEW_WIDTH
14
- canvas.height = Math.max(PREVIEW_WIDTH / imageAspect, 1)
15
-
16
- // Fail silently if we're unable to generate a preview image.
17
- // This can often be the case when trying to render SVGs containing `<foreignObject>` elements.
18
- try {
19
- const ctx = canvas.getContext('2d')
20
- ctx?.drawImage(img, 0, 0, PREVIEW_WIDTH, PREVIEW_WIDTH / imageAspect)
21
- canvas.toBlob(resolve, 'image/jpeg')
22
- } catch (err) {
23
- console.warn(`Unable to generate preview image:`, err)
24
- }
25
- })
26
- }
27
-
28
- const createImageEl = (file: File): Promise<HTMLImageElement> => {
29
- return new Promise((resolve) => {
30
- const blobUrlLarge = window.URL.createObjectURL(file)
31
- const img = new Image()
32
- img.onload = () => {
33
- window.URL.revokeObjectURL(blobUrlLarge)
34
- resolve(img)
35
- }
36
- img.src = blobUrlLarge
37
- })
38
- }
39
-
40
- const generatePreviewBlobUrl = async (file: File): Promise<string> => {
41
- const imageEl = await createImageEl(file)
42
- const blob = await createBlob(imageEl)
43
-
44
- if (!blob) {
45
- throw Error('Unable to generate file Blob')
46
- }
47
-
48
- return window.URL.createObjectURL(blob)
49
- }
50
-
51
- export const generatePreviewBlobUrl$ = (file: File): Observable<string> => {
52
- return of(null).pipe(mergeMap(() => from(generatePreviewBlobUrl(file))))
53
- }
@@ -1,13 +0,0 @@
1
- import {describe, expect, it} from 'vitest'
2
-
3
- import type {ImageAsset} from '../types'
4
- import getAssetResolution from './getAssetResolution'
5
-
6
- describe('getAssetResolution', () => {
7
- it('formats width x height with px suffix', () => {
8
- const asset = {
9
- metadata: {dimensions: {width: 1920, height: 1080}},
10
- } as ImageAsset
11
- expect(getAssetResolution(asset)).toBe('1920x1080px')
12
- })
13
- })
@@ -1,7 +0,0 @@
1
- import type {ImageAsset} from '../types'
2
-
3
- const getAssetResolution = (asset: ImageAsset) => {
4
- return `${asset.metadata.dimensions.width}x${asset.metadata.dimensions.height}px`
5
- }
6
-
7
- export default getAssetResolution
@@ -1,50 +0,0 @@
1
- // @vitest-environment node
2
-
3
- import {describe, expect, it} from 'vitest'
4
-
5
- import getDocumentAssetIds from './getDocumentAssetIds'
6
-
7
- describe('getDocumentAssetIds', () => {
8
- it('returns empty array for document without asset refs', () => {
9
- expect(getDocumentAssetIds({_id: 'doc1', _type: 'post'} as any)).toEqual([])
10
- })
11
-
12
- it('collects asset _ref from nested portable text–like structures', () => {
13
- const doc = {
14
- _id: 'doc1',
15
- _type: 'post',
16
- body: [
17
- {
18
- _type: 'block',
19
- asset: {_type: 'reference', _ref: 'image-asset-1'},
20
- },
21
- ],
22
- } as any
23
-
24
- expect(getDocumentAssetIds(doc)).toEqual(['image-asset-1'])
25
- })
26
-
27
- it('dedupes and sorts refs', () => {
28
- const doc = {
29
- _id: 'doc1',
30
- _type: 'post',
31
- modules: [
32
- {image: {asset: {_type: 'reference', _ref: 'b'}}},
33
- {image: {asset: {_type: 'reference', _ref: 'a'}}},
34
- {image: {asset: {_type: 'reference', _ref: 'b'}}},
35
- ],
36
- } as any
37
-
38
- expect(getDocumentAssetIds(doc)).toEqual(['a', 'b'])
39
- })
40
-
41
- it('ignores reference nodes that are not asset references', () => {
42
- const doc = {
43
- _id: 'doc1',
44
- _type: 'post',
45
- author: {_type: 'reference', _ref: 'person-1'},
46
- } as any
47
-
48
- expect(getDocumentAssetIds(doc)).toEqual([])
49
- })
50
- })
@@ -1,35 +0,0 @@
1
- import type {SanityDocument} from '@sanity/client'
2
-
3
- const isPlainObject = (value: any) =>
4
- value !== null && typeof value === 'object' && !Array.isArray(value)
5
-
6
- // Recursively search node for any linked asset ids (`asset._type === 'reference'`)
7
- const getAssetIds = (node: Record<string, any>, acc: string[] = []) => {
8
- if (Array.isArray(node)) {
9
- node.forEach((v) => {
10
- getAssetIds(v, acc)
11
- })
12
- }
13
-
14
- if (isPlainObject(node)) {
15
- if (node?.asset?._type === 'reference' && node?.asset?._ref) {
16
- acc.push(node.asset._ref)
17
- }
18
-
19
- Object.values(node).forEach((val) => {
20
- getAssetIds(val, acc)
21
- })
22
- }
23
-
24
- return acc
25
- }
26
-
27
- // Retrieve all linked asset ids from a Sanity document
28
- const getDocumentAssetIds = (document: SanityDocument): string[] => {
29
- const assetIds = getAssetIds(document)
30
-
31
- // Sort and dedupe
32
- return [...new Set(assetIds.sort())]
33
- }
34
-
35
- export default getDocumentAssetIds
@@ -1,12 +0,0 @@
1
- import {describe, expect, it} from 'vitest'
2
-
3
- import {getSchemeColor} from './getSchemeColor'
4
-
5
- describe('getSchemeColor', () => {
6
- it('returns a hex or theme string for light and dark schemes', () => {
7
- expect(getSchemeColor('light', 'bg')).toMatch(/^#/)
8
- expect(getSchemeColor('dark', 'bg')).toMatch(/^#/)
9
- expect(getSchemeColor('light', 'spotBlue')).toBeTruthy()
10
- expect(getSchemeColor('dark', 'inputEnabledBorder')).toBeTruthy()
11
- })
12
- })
@@ -1,43 +0,0 @@
1
- import {hues} from '@sanity/color'
2
- import {type ThemeColorSchemeKey, studioTheme} from '@sanity/ui'
3
-
4
- const SCHEME_COLORS = {
5
- bg: {
6
- dark: hues.gray[950].hex,
7
- light: hues.gray[50].hex,
8
- },
9
- bg2: {
10
- dark: hues.gray[900].hex,
11
- light: hues.gray[100].hex,
12
- },
13
- inputEnabledBorder: {
14
- dark: studioTheme.color.dark.default.input.default.enabled.border,
15
- light: studioTheme.color.light.default.input.default.enabled.border,
16
- },
17
- inputHoveredBorder: {
18
- dark: studioTheme.color.dark.default.input.default.hovered.border,
19
- light: studioTheme.color.light.default.input.default.hovered.border,
20
- },
21
- mutedHoveredBg: {
22
- dark: studioTheme.color.dark.primary.muted.primary.hovered.bg,
23
- light: studioTheme.color.light.primary.muted.primary.hovered.bg,
24
- },
25
- mutedHoveredFg: {
26
- dark: studioTheme.color.dark.primary.muted.primary.hovered.fg,
27
- light: studioTheme.color.light.primary.muted.primary.hovered.fg,
28
- },
29
- mutedSelectedBg: {
30
- dark: studioTheme.color.dark.primary.muted.primary.selected.bg,
31
- light: studioTheme.color.light.primary.muted.primary.selected.bg,
32
- },
33
- spotBlue: {
34
- dark: studioTheme.color.dark.primary.spot.blue,
35
- light: studioTheme.color.light.primary.spot.blue,
36
- },
37
- }
38
-
39
- type SchemeColorKey = keyof typeof SCHEME_COLORS
40
-
41
- export function getSchemeColor(scheme: ThemeColorSchemeKey, colorKey: SchemeColorKey): string {
42
- return SCHEME_COLORS[colorKey]?.[scheme]
43
- }
@@ -1,44 +0,0 @@
1
- import {describe, expect, it} from 'vitest'
2
-
3
- import type {Tag, TagItem} from '../types'
4
- import getTagSelectOptions from './getTagSelectOptions'
5
-
6
- function tagItem(partial: Partial<TagItem> & Pick<TagItem, 'tag'>): TagItem {
7
- return {
8
- _type: 'tag',
9
- picked: false,
10
- updating: false,
11
- ...partial,
12
- }
13
- }
14
-
15
- const makeTag = (id: string, name: string): Tag => ({
16
- _id: id,
17
- _type: 'media.tag',
18
- _createdAt: '',
19
- _updatedAt: '',
20
- _rev: 'r1',
21
- name: {_type: 'slug', current: name},
22
- })
23
-
24
- describe('getTagSelectOptions', () => {
25
- it('maps tag items to label/value options', () => {
26
- const tags = [tagItem({tag: makeTag('t1', 'alpha')}), tagItem({tag: makeTag('t2', 'beta')})]
27
- expect(getTagSelectOptions(tags)).toEqual([
28
- {label: 'alpha', value: 't1'},
29
- {label: 'beta', value: 't2'},
30
- ])
31
- })
32
-
33
- it('returns an empty array for an empty list', () => {
34
- expect(getTagSelectOptions([])).toEqual([])
35
- })
36
-
37
- it('skips items without a tag', () => {
38
- const tags = [
39
- tagItem({tag: makeTag('t1', 'ok')}),
40
- {_type: 'tag', tag: undefined, picked: false, updating: false} as unknown as TagItem,
41
- ]
42
- expect(getTagSelectOptions(tags)).toEqual([{label: 'ok', value: 't1'}])
43
- })
44
- })
@@ -1,16 +0,0 @@
1
- import type {TagSelectOption, TagItem} from '../types'
2
-
3
- const getTagSelectOptions = (tags: TagItem[]): TagSelectOption[] => {
4
- return tags.reduce((acc: TagSelectOption[], val) => {
5
- const tag = val?.tag
6
- if (tag) {
7
- acc.push({
8
- label: tag?.name?.current,
9
- value: tag?._id,
10
- })
11
- }
12
- return acc
13
- }, [])
14
- }
15
-
16
- export default getTagSelectOptions
@@ -1,26 +0,0 @@
1
- import type {SanityDocument} from '@sanity/client'
2
- import {describe, expect, it} from 'vitest'
3
-
4
- import {getUniqueDocuments} from './getUniqueDocuments'
5
-
6
- describe('getUniqueDocuments', () => {
7
- it('drops published documents when a drafts.* sibling exists', () => {
8
- const docs: SanityDocument[] = [
9
- {_id: 'drafts.post1', _type: 'post'} as SanityDocument,
10
- {_id: 'post1', _type: 'post'} as SanityDocument,
11
- ]
12
- expect(getUniqueDocuments(docs)).toEqual([{_id: 'drafts.post1', _type: 'post'}])
13
- })
14
-
15
- it('keeps published-only and draft-only ids', () => {
16
- const docs: SanityDocument[] = [
17
- {_id: 'onlyPub', _type: 'x'} as SanityDocument,
18
- {_id: 'drafts.onlyDraft', _type: 'x'} as SanityDocument,
19
- ]
20
- expect(getUniqueDocuments(docs)).toEqual(docs)
21
- })
22
-
23
- it('returns an empty array for an empty list', () => {
24
- expect(getUniqueDocuments([])).toEqual([])
25
- })
26
- })
@@ -1,15 +0,0 @@
1
- import type {SanityDocument} from '@sanity/client'
2
-
3
- export function getUniqueDocuments(documents: SanityDocument[]): SanityDocument[] {
4
- const draftIds = documents.reduce(
5
- (acc: string[], doc: SanityDocument) =>
6
- doc._id.startsWith('drafts.') ? acc.concat(doc._id.slice(7)) : acc,
7
- [],
8
- )
9
-
10
- const filteredDocuments: SanityDocument[] = documents.filter(
11
- (doc: SanityDocument) => !draftIds.includes(doc._id),
12
- )
13
-
14
- return filteredDocuments
15
- }
@@ -1,46 +0,0 @@
1
- import {afterEach, describe, expect, it} from 'vitest'
2
-
3
- import type {ImageAsset} from '../types'
4
- import imageDprUrl from './imageDprUrl'
5
-
6
- const asset = {
7
- _id: 'a1',
8
- _type: 'sanity.imageAsset',
9
- _createdAt: '',
10
- _updatedAt: '',
11
- _rev: 'r1',
12
- originalFilename: 'x.png',
13
- size: 1,
14
- mimeType: 'image/png',
15
- url: 'https://cdn.test/image.png',
16
- metadata: {dimensions: {width: 100, height: 100}, isOpaque: true},
17
- } as ImageAsset
18
-
19
- describe('imageDprUrl', () => {
20
- const dpr = window.devicePixelRatio
21
-
22
- afterEach(() => {
23
- Object.defineProperty(window, 'devicePixelRatio', {value: dpr, configurable: true})
24
- })
25
-
26
- it('scales width by devicePixelRatio and sets fit=max', () => {
27
- Object.defineProperty(window, 'devicePixelRatio', {value: 2, configurable: true})
28
- const url = imageDprUrl(asset, {width: 400})
29
- expect(url).toBe('https://cdn.test/image.png?fit=max&w=800')
30
- })
31
-
32
- it('includes height when provided, scaled by dpr', () => {
33
- Object.defineProperty(window, 'devicePixelRatio', {value: 2, configurable: true})
34
- const url = imageDprUrl(asset, {width: 300, height: 200})
35
- expect(url).toBe('https://cdn.test/image.png?fit=max&w=600&h=400')
36
- })
37
-
38
- it('uses multiplier 1 when devicePixelRatio is missing', () => {
39
- Object.defineProperty(window, 'devicePixelRatio', {
40
- value: undefined as unknown as number,
41
- configurable: true,
42
- })
43
- const url = imageDprUrl(asset, {width: 100})
44
- expect(url).toBe('https://cdn.test/image.png?fit=max&w=100')
45
- })
46
- })
@@ -1,27 +0,0 @@
1
- import type {ImageAsset} from '../types'
2
-
3
- const imageDprUrl = (
4
- asset: ImageAsset,
5
- options: {
6
- width: number
7
- height?: number
8
- },
9
- ): string => {
10
- const dpi =
11
- typeof window === 'undefined' || !window.devicePixelRatio
12
- ? 1
13
- : Math.round(window.devicePixelRatio)
14
- const imgH = options?.height ? options?.height * Math.max(1, dpi) : undefined
15
- const imgW = options.width * Math.max(1, dpi)
16
-
17
- const urlParams = new URLSearchParams()
18
- urlParams.append('fit', 'max')
19
- urlParams.append('w', imgW.toString())
20
- if (imgH) {
21
- urlParams.append('h', imgH.toString())
22
- }
23
-
24
- return `${asset.url}?${urlParams.toString()}`
25
- }
26
-
27
- export default imageDprUrl
@@ -1,16 +0,0 @@
1
- import {describe, expect, it} from 'vitest'
2
-
3
- import {isSupportedAssetType} from './isSupportedAssetType'
4
-
5
- describe('isSupportedAssetType', () => {
6
- it('returns true for file and image', () => {
7
- expect(isSupportedAssetType('file')).toBe(true)
8
- expect(isSupportedAssetType('image')).toBe(true)
9
- })
10
-
11
- it('returns false for unsupported or missing types', () => {
12
- expect(isSupportedAssetType('video')).toBe(false)
13
- expect(isSupportedAssetType('')).toBe(false)
14
- expect(isSupportedAssetType(undefined)).toBe(false)
15
- })
16
- })
@@ -1,15 +0,0 @@
1
- import {SUPPORTED_ASSET_TYPES} from '../constants'
2
- import type {AssetType} from '../types'
3
-
4
- /**
5
- * Determines whether or not the provided asset type (eg 'image', 'file', 'arbitrary')
6
- * is a supported asset type for this plugin.
7
- *
8
- * @param assetType - The asset type to check.
9
- * @returns True if the asset type is supported, false otherwise.
10
- * @internal
11
- */
12
- export function isSupportedAssetType(assetType?: string): assetType is AssetType {
13
- const supported: string[] = SUPPORTED_ASSET_TYPES
14
- return assetType ? supported.includes(assetType) : false
15
- }
@@ -1,73 +0,0 @@
1
- import type {
2
- FieldDefinitionBase,
3
- FileDefinition,
4
- ImageDefinition,
5
- WidenInitialValue,
6
- WidenValidation,
7
- } from 'sanity'
8
-
9
- import {AutoTagInput} from '../components/AutoTagInputWrapper'
10
-
11
- type ImageMediaFieldConfig = Omit<ImageDefinition, 'options'> &
12
- FieldDefinitionBase & {
13
- name: string
14
- mediaTags: string[]
15
- options?: ImageDefinition['options']
16
- }
17
-
18
- type FileMediaFieldConfig = Omit<FileDefinition, 'options'> &
19
- FieldDefinitionBase & {
20
- name: string
21
- mediaTags: string[]
22
- options?: FileDefinition['options']
23
- }
24
-
25
- type ImageMediaFieldResult = Omit<ImageDefinition, 'options'> &
26
- FieldDefinitionBase & {
27
- options?: ImageDefinition['options'] & {mediaTags: string[]}
28
- components: {input: typeof AutoTagInput}
29
- } & WidenValidation &
30
- WidenInitialValue
31
-
32
- type FileMediaFieldResult = Omit<FileDefinition, 'options'> &
33
- FieldDefinitionBase & {
34
- options?: FileDefinition['options'] & {mediaTags: string[]}
35
- components: {input: typeof AutoTagInput}
36
- } & WidenValidation &
37
- WidenInitialValue
38
-
39
- /**
40
- * Defines an image or file field with automatic media tag application when an asset is selected.
41
- *
42
- * Pass `mediaTags` at the top level — they are moved into `options.mediaTags` (for media browser
43
- * pre-filtering) and wire up {@link AutoTagInput} as the field component automatically:
44
- * ```ts
45
- * import {mediaField} from 'sanity-plugin-media'
46
- *
47
- * mediaField({
48
- * name: 'coverImage',
49
- * type: 'image',
50
- * mediaTags: ['product-cover'],
51
- * options: { hotspot: true },
52
- * })
53
- * ```
54
- *
55
- * For file fields, set `type: 'file'`:
56
- * ```ts
57
- * mediaField({ name: 'drawing', type: 'file', mediaTags: ['model-drawing'] })
58
- * ```
59
- */
60
- export function mediaField(config: ImageMediaFieldConfig): ImageMediaFieldResult
61
- export function mediaField(config: FileMediaFieldConfig): FileMediaFieldResult
62
- export function mediaField(
63
- config: ImageMediaFieldConfig | FileMediaFieldConfig,
64
- ): ImageMediaFieldResult | FileMediaFieldResult {
65
- const {mediaTags, options, components, ...rest} = config as ImageMediaFieldConfig & {
66
- components?: Record<string, unknown>
67
- }
68
- return {
69
- ...rest,
70
- options: {...options, mediaTags},
71
- components: {...components, input: AutoTagInput},
72
- } as unknown as ImageMediaFieldResult
73
- }