sanity-plugin-cloudinary 0.2.2 → 1.0.0-v3-studio.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 (51) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +160 -66
  3. package/lib/cjs/index.js +708 -0
  4. package/lib/cjs/index.js.map +1 -0
  5. package/lib/esm/index.js +697 -0
  6. package/lib/esm/index.js.map +1 -0
  7. package/lib/types/index.d.ts +22 -0
  8. package/lib/types/index.d.ts.map +1 -0
  9. package/package.json +66 -41
  10. package/sanity.json +3 -15
  11. package/src/arrayFunctions.tsx +71 -0
  12. package/src/components/AssetDiff.tsx +44 -0
  13. package/src/components/AssetPreview.tsx +41 -0
  14. package/src/components/CloudinaryInput.tsx +61 -0
  15. package/src/components/SecretsConfigView.tsx +39 -0
  16. package/src/components/VideoPlayer.tsx +62 -0
  17. package/src/components/WidgetInput.tsx +66 -0
  18. package/src/components/asset-source/CloudinaryAssetSource.tsx +155 -0
  19. package/src/components/asset-source/Icon.tsx +93 -0
  20. package/src/index.ts +30 -0
  21. package/src/schema/cloudinaryAsset.ts +99 -0
  22. package/src/schema/cloudinaryAssetDerived.ts +26 -0
  23. package/src/typings.d.ts +74 -0
  24. package/src/utils.ts +115 -0
  25. package/v2-incompatible.js +11 -0
  26. package/dist/arrayFunctions.js +0 -82
  27. package/dist/arrayFunctions.js.map +0 -1
  28. package/dist/components/AssetDiff.js +0 -62
  29. package/dist/components/AssetDiff.js.map +0 -1
  30. package/dist/components/AssetPreview.js +0 -46
  31. package/dist/components/AssetPreview.js.map +0 -1
  32. package/dist/components/CloudinaryInput.js +0 -90
  33. package/dist/components/CloudinaryInput.js.map +0 -1
  34. package/dist/components/SecretsConfigView.js +0 -37
  35. package/dist/components/SecretsConfigView.js.map +0 -1
  36. package/dist/components/VideoPlayer.js +0 -70
  37. package/dist/components/VideoPlayer.js.map +0 -1
  38. package/dist/components/WidgetInput.js +0 -87
  39. package/dist/components/WidgetInput.js.map +0 -1
  40. package/dist/index.js +0 -24
  41. package/dist/index.js.map +0 -1
  42. package/dist/schema/cloudinary.js +0 -19
  43. package/dist/schema/cloudinary.js.map +0 -1
  44. package/dist/schema/cloudinaryAsset.js +0 -100
  45. package/dist/schema/cloudinaryAsset.js.map +0 -1
  46. package/dist/schema/cloudinaryAssetDerived.js +0 -22
  47. package/dist/schema/cloudinaryAssetDerived.js.map +0 -1
  48. package/dist/typings.d.js +0 -8
  49. package/dist/typings.d.js.map +0 -1
  50. package/dist/utils.js +0 -92
  51. package/dist/utils.js.map +0 -1
@@ -0,0 +1,66 @@
1
+ import React, {useCallback} from 'react'
2
+ import {ObjectInputProps, PatchEvent, unset} from 'sanity'
3
+ import {Button, Flex, Grid, Stack} from '@sanity/ui'
4
+ import {PlugIcon} from '@sanity/icons'
5
+ import styled from 'styled-components'
6
+ import AssetPreview from './AssetPreview'
7
+ import {CloudinaryAsset} from '../typings'
8
+
9
+ const SetupButtonContainer = styled.div`
10
+ position: relative;
11
+ display: block;
12
+ font-size: 0.8em;
13
+ transform: translate(0%, -10%);
14
+ `
15
+
16
+ type WidgetInputProps = ObjectInputProps & {openMediaSelector: () => void; onSetup: () => void}
17
+
18
+ const WidgetInput = (props: WidgetInputProps) => {
19
+ const {onChange, readOnly, value, openMediaSelector} = props
20
+
21
+ const removeValue = useCallback(() => {
22
+ onChange(PatchEvent.from([unset()]))
23
+ }, [onChange])
24
+
25
+ return (
26
+ <Stack>
27
+ <SetupButtonContainer>
28
+ <Flex flex={1} justify="flex-end">
29
+ <Button
30
+ color="primary"
31
+ icon={PlugIcon}
32
+ mode="bleed"
33
+ title="Configure"
34
+ onClick={props.onSetup}
35
+ tabIndex={1}
36
+ />
37
+ </Flex>
38
+ </SetupButtonContainer>
39
+
40
+ <Flex style={{textAlign: 'center'}} marginBottom={2}>
41
+ <AssetPreview value={value as CloudinaryAsset} />
42
+ </Flex>
43
+
44
+ <Grid gap={1} style={{gridTemplateColumns: 'repeat(auto-fit, minmax(100px, 1fr))'}}>
45
+ <Button
46
+ disabled={readOnly}
47
+ mode="ghost"
48
+ title="Select an asset"
49
+ tone="default"
50
+ onClick={openMediaSelector}
51
+ text="Select…"
52
+ />
53
+ <Button
54
+ disabled={readOnly || !value}
55
+ tone="critical"
56
+ mode="ghost"
57
+ title="Remove asset"
58
+ text="Remove"
59
+ onClick={removeValue}
60
+ />
61
+ </Grid>
62
+ </Stack>
63
+ )
64
+ }
65
+
66
+ export default WidgetInput
@@ -0,0 +1,155 @@
1
+ /* eslint-disable camelcase */
2
+ import React, {useCallback, useEffect, useRef, useState} from 'react'
3
+ import {Box, Button, Dialog, Flex, Spinner, Stack, Text} from '@sanity/ui'
4
+ import {CloudinaryMediaLibrary, InsertHandlerParams} from '../../typings'
5
+ import {createMediaLibrary, decodeSourceId, encodeFilename, encodeSourceId} from '../../utils'
6
+ import styled from 'styled-components'
7
+ import {useSecrets} from '@sanity/studio-secrets'
8
+ import SecretsConfigView, {namespace, Secrets} from '../SecretsConfigView'
9
+ import {AssetSourceComponentProps, ImageAsset} from 'sanity'
10
+ import {PlugIcon} from '@sanity/icons'
11
+
12
+ export const Widget = styled.div`
13
+ height: 70vh;
14
+ `
15
+
16
+ export function CloudinaryAssetSource(props: AssetSourceComponentProps) {
17
+ const {onClose} = props
18
+
19
+ const [loadingMessage, setLoadingMessage] = useState<string | undefined>(
20
+ 'Loading Cloudinary Media Libary'
21
+ )
22
+ const library = useRef<CloudinaryMediaLibrary | undefined>()
23
+ const contentRef = useRef<HTMLDivElement | null>(null)
24
+ const {secrets} = useSecrets<Secrets>(namespace)
25
+ const cloudName = secrets?.cloudName
26
+ const apiKey = secrets?.apiKey
27
+ const [widgetId] = useState(() => `cloundinaryWidget-${Date.now()}`)
28
+ const [showSettings, setShowSettings] = useState(false)
29
+
30
+ const propsRef = useRef(props)
31
+
32
+ useEffect(() => {
33
+ // because we have to access props after loading js in a callback,
34
+ // we cannot pass props as dependecnies as that will cause infinite updates
35
+ // this takes a snapshot of props, so we can access them later
36
+ propsRef.current = props
37
+ }, [props])
38
+
39
+ const handleClose = useCallback(() => {
40
+ if (library.current) {
41
+ library.current.hide()
42
+ }
43
+ onClose()
44
+ }, [onClose, library])
45
+
46
+ useEffect(() => {
47
+ if (!cloudName || !apiKey) {
48
+ return
49
+ }
50
+
51
+ createMediaLibrary({
52
+ cloudName,
53
+ apiKey,
54
+ inlineContainer: `#${widgetId}`,
55
+ libraryCreated: (lib: CloudinaryMediaLibrary) => {
56
+ library.current = lib
57
+ const selectedAssets = propsRef.current.selectedAssets
58
+ const firstSelectedAsset = selectedAssets ? selectedAssets[0] : null
59
+
60
+ // eslint-disable-next-line no-undef
61
+ const iframe: ChildNode | null | undefined =
62
+ contentRef.current && contentRef.current.firstChild
63
+ if (iframe && iframe instanceof HTMLIFrameElement) {
64
+ setLoadingMessage(undefined)
65
+ let asset
66
+ if (
67
+ propsRef.current.selectionType === 'single' &&
68
+ firstSelectedAsset &&
69
+ firstSelectedAsset.source &&
70
+ firstSelectedAsset.source.id
71
+ ) {
72
+ asset = decodeSourceId(firstSelectedAsset.source.id)
73
+ }
74
+ const folder = asset
75
+ ? {
76
+ path: asset.public_id.split('/').slice(0, -1).join('/'),
77
+ resource_type: 'image',
78
+ }
79
+ : {path: '', resource_type: 'image'}
80
+ if (lib && contentRef.current) {
81
+ lib.show({folder, asset})
82
+ contentRef.current.style.visibility = 'visible'
83
+ }
84
+ }
85
+ },
86
+ insertHandler: ({assets}: InsertHandlerParams) => {
87
+ if (!library.current) {
88
+ return
89
+ }
90
+ const imageAssets = assets.filter((asset) => asset.resource_type === 'image')
91
+ if (imageAssets.length === 0) {
92
+ throw new Error('The selection did not contain any images.')
93
+ }
94
+ library.current.hide()
95
+ propsRef.current.onSelect(
96
+ imageAssets.map((asset) => {
97
+ const url =
98
+ asset.derived && asset.derived[0] ? asset.derived[0].secure_url : asset.secure_url
99
+ return {
100
+ kind: 'url',
101
+ value: url,
102
+ assetDocumentProps: {
103
+ _type: 'sanity.imageAsset',
104
+ originalFilename: encodeFilename(asset),
105
+ source: {
106
+ id: encodeSourceId(asset),
107
+ name: `cloudinary:${cloudName}`,
108
+ },
109
+ } as ImageAsset,
110
+ }
111
+ })
112
+ )
113
+ },
114
+ })
115
+ }, [cloudName, apiKey, widgetId])
116
+
117
+ const hasConfig = apiKey && cloudName
118
+ return (
119
+ <Dialog
120
+ id="cloudinary-asset-source"
121
+ header="Select image from Cloudinary"
122
+ onClose={handleClose}
123
+ open
124
+ width={4}
125
+ >
126
+ <Box padding={4}>
127
+ {showSettings && <SecretsConfigView onClose={() => setShowSettings(false)} />}
128
+ <Flex flex={1} justify="flex-end">
129
+ <Button
130
+ color="primary"
131
+ icon={PlugIcon}
132
+ mode="bleed"
133
+ title="Configure"
134
+ onClick={() => setShowSettings(true)}
135
+ tabIndex={1}
136
+ text={hasConfig ? undefined : 'Configure Cloudinary plugin'}
137
+ />
138
+ </Flex>
139
+
140
+ {hasConfig && loadingMessage && (
141
+ <Stack space={3}>
142
+ <Flex align="center" justify="center">
143
+ <Spinner muted />
144
+ </Flex>
145
+ <Text size={1} muted align="center">
146
+ {loadingMessage}
147
+ </Text>
148
+ </Stack>
149
+ )}
150
+
151
+ <Widget style={{visibility: 'hidden'}} ref={contentRef} id={widgetId} />
152
+ </Box>
153
+ </Dialog>
154
+ )
155
+ }
@@ -0,0 +1,93 @@
1
+ import React from 'react'
2
+
3
+ export function CloudinaryIcon() {
4
+ return (
5
+ <svg
6
+ version="1.1"
7
+ id="Layer_1"
8
+ x="0px"
9
+ y="0px"
10
+ width="1em"
11
+ height="1em"
12
+ viewBox="0 0 141.732 141.747"
13
+ enableBackground="new 0 0 141.732 141.747"
14
+ >
15
+ <g>
16
+ <path
17
+ fill="#0071CE"
18
+ d="M115.585,109.242c-1.609,0-3.107-1.024-3.635-2.637c-0.657-2.008,0.438-4.169,2.447-4.826
19
+ c7.278-2.382,11.98-8.761,11.98-16.252c0-9.487-7.718-17.206-17.205-17.206c-0.659,0-1.368,0.052-2.231,0.164l-3.741,0.485
20
+ l-0.537-3.735c-2.299-16.016-16.251-28.094-32.454-28.094c-13.395,0-25.32,8.019-30.377,20.43l-0.952,2.335l-2.52,0.046
21
+ c-11.581,0.213-21.003,9.804-21.003,21.379c0,8.45,4.906,16.156,12.498,19.631c1.921,0.88,2.766,3.15,1.886,5.071
22
+ c-0.88,1.921-3.149,2.764-5.07,1.887C14.363,103.202,7.703,92.766,7.703,81.331c0-14.88,11.465-27.345,26.028-28.876
23
+ c6.71-14.03,20.773-22.965,36.477-22.965c18.796,0,35.135,13.178,39.372,31.184c13.519,0.219,24.45,11.284,24.45,24.854
24
+ c0,10.693-6.934,20.146-17.253,23.523C116.382,109.18,115.98,109.242,115.585,109.242z"
25
+ />
26
+ <path
27
+ fill="#DC8327"
28
+ d="M57.12,111.02c-0.001-0.001-0.001-0.001-0.002-0.001c-0.001,0-0.002-0.001-0.003-0.001h-0.001
29
+ c0,0-0.001-0.001-0.001-0.001l-0.001-0.001c0,0-0.001,0-0.001-0.001h-0.001l-0.001-0.001c0.001-0.001-0.001-0.001-0.001-0.001
30
+ l-0.001-0.001c0,0-0.001,0-0.001,0l-0.001-0.001c0.001,0.001-0.001-0.001-0.001-0.001s-0.002-0.001-0.003-0.001l-0.001-0.001H57.1
31
+ c-0.001-0.001-0.001-0.001-0.001-0.001l-0.001-0.001c-0.003-0.001-0.002-0.001-0.003-0.001c-0.001,0.001-0.001-0.001-0.002-0.001
32
+ l-0.001-0.001c0,0-0.001-0.001-0.002-0.001l-0.001-0.001c-0.001-0.001-0.003-0.001-0.004-0.001s-0.003-0.001-0.004-0.001
33
+ c-0.001-0.001-0.001-0.001-0.002-0.001h-0.001c-0.001-0.001-0.002-0.001-0.003-0.001c-0.001-0.001-0.003-0.001-0.003-0.001
34
+ c-0.001,0-0.001,0-0.001,0l-0.002-0.001c-0.001,0-0.001-0.001-0.001-0.001h-0.001c-0.059-0.021-0.122-0.034-0.188-0.037h-0.002
35
+ h-0.002c-0.001,0-0.001,0-0.002,0c0,0,0,0-0.001,0c0,0-0.001,0-0.001,0h-0.001c-0.001-0.001-0.001-0.001-0.001-0.001
36
+ c-0.001,0-0.002,0-0.002,0c-0.001,0-0.002,0-0.002,0h-0.001h-0.001h-0.001c-0.001,0-0.002,0-0.002,0h-0.001H56.86h-0.001h-0.001
37
+ c-0.001,0-0.001,0-0.001,0h-0.001h-0.001h-0.001h-0.001c-0.001,0-0.001,0-0.001,0c-1.656,0-3.011-1.348-3.021-3V74.29h2.567
38
+ c0.004,0,0.009,0,0.013,0c0.393,0.017,0.661-0.285,0.661-0.648c0-0.271-0.166-0.503-0.402-0.6l-12.379-8.544
39
+ c-0.222-0.153-0.515-0.153-0.737,0l-12.476,8.611c-0.234,0.161-0.335,0.456-0.251,0.727c0.085,0.271,0.335,0.455,0.619,0.455h2.58
40
+ l0.002,33.674c0.013,2.328,1.883,4.228,4.262,4.288c0.027,0.003,0.053,0.005,0.08,0.005h18.481c0.004,0,0.007,0,0.011,0
41
+ c0.17-0.003,0.324-0.071,0.438-0.18c0,0,0,0,0.001-0.001c0.002-0.002,0.004-0.004,0.005-0.005c0.001-0.001,0.002-0.001,0.003-0.003
42
+ c0,0,0.001-0.001,0.001-0.001l0.001-0.001l0.001-0.001l0.001-0.001l0.001-0.001c0.001-0.001,0.001-0.001,0.001-0.001
43
+ c0.002-0.001,0.001-0.001,0.002-0.002c0,0,0,0,0.001-0.001l0.001-0.001c0,0,0,0,0.001-0.001c0.112-0.116,0.182-0.273,0.183-0.447
44
+ v-0.002v-0.001v-0.001v-0.001v-0.001v-0.001v-0.001v-0.002C57.498,111.345,57.343,111.121,57.12,111.02z"
45
+ />
46
+ <path
47
+ fill="#F4B21B"
48
+ d="M83.889,111.02c0,0-0.001-0.001-0.002-0.001c-0.001,0-0.002-0.001-0.003-0.001h-0.001
49
+ c-0.001-0.001-0.001-0.001-0.001-0.001l-0.001-0.001h-0.001c0,0-0.001-0.001-0.001-0.001c0,0-0.001-0.001-0.001-0.001
50
+ c0.001-0.001-0.001-0.001-0.002-0.001l-0.001-0.001h-0.001c-0.001,0-0.001-0.001-0.001-0.001c-0.002,0.001-0.002-0.001-0.002-0.001
51
+ l-0.002-0.001l-0.001-0.001h-0.001c0,0-0.001-0.001-0.001-0.001l-0.001-0.001c-0.001-0.001-0.002-0.001-0.002-0.001
52
+ c-0.001,0.001-0.001-0.001-0.003-0.001l-0.001-0.001l-0.001-0.001c0,0-0.001,0-0.002-0.001c-0.001-0.001-0.003-0.001-0.004-0.001
53
+ s-0.003-0.001-0.004-0.001c-0.001-0.001-0.001-0.001-0.002-0.001c-0.001,0-0.001,0-0.002-0.001c0,0-0.001,0-0.002-0.001
54
+ c-0.003-0.001-0.001-0.001-0.002-0.001c-0.003,0-0.001,0-0.002-0.001c0,0-0.001-0.001-0.002-0.001l-0.001-0.001h-0.001
55
+ c-0.059-0.021-0.122-0.034-0.188-0.037h-0.002c-0.001,0-0.001,0-0.001,0c-0.001,0-0.002,0-0.002,0s-0.001,0-0.001,0h-0.001h-0.001
56
+ l-0.001-0.001c-0.001,0-0.002,0-0.002,0c-0.001,0-0.002,0-0.002,0h-0.001c-0.001,0-0.001,0-0.001,0h-0.001
57
+ c-0.001,0-0.002,0-0.002,0s-0.001,0-0.002,0h-0.001c0,0-0.001,0-0.001,0h-0.001c-0.001,0-0.001,0-0.001,0h-0.001h-0.001h-0.001
58
+ h-0.001c-0.001,0-0.001,0-0.001,0c-1.655,0-3.01-1.348-3.02-3V81.829h2.579c0.009-0.001,0.016-0.001,0.026,0
59
+ c0.358,0,0.648-0.29,0.648-0.648c0-0.271-0.166-0.503-0.402-0.6l-12.38-8.544c-0.222-0.153-0.515-0.153-0.737,0L57.86,80.647
60
+ c-0.234,0.161-0.335,0.456-0.251,0.727c0.085,0.271,0.335,0.455,0.619,0.455h2.568l0.002,26.135
61
+ c0.011,2.329,1.884,4.23,4.264,4.289c0.026,0.003,0.052,0.004,0.078,0.004h18.481c0.004,0,0.007,0,0.011,0
62
+ c0.17-0.003,0.324-0.071,0.438-0.18c0,0,0,0,0.001-0.001c0.002-0.002,0.006-0.004,0.005-0.005c0.001-0.001,0.002-0.001,0.003-0.003
63
+ c0.001-0.001,0.001-0.001,0.001-0.001l0.001-0.001l0.001-0.001c0,0,0.001,0,0.001-0.001l0.001-0.001
64
+ c0.001,0,0.001-0.001,0.001-0.001c0.003-0.001,0.002-0.001,0.002-0.002c0,0,0,0,0.001-0.001c0,0,0,0,0.001-0.001
65
+ c0,0,0,0,0.001-0.001c0.112-0.116,0.182-0.273,0.183-0.447v-0.002v-0.001v-0.001v-0.001v-0.001v-0.001v-0.001v-0.002
66
+ C84.267,111.345,84.112,111.121,83.889,111.02z"
67
+ />
68
+ <path
69
+ fill="#F2D864"
70
+ d="M110.667,111.02l-0.002-0.001c-0.001,0-0.002-0.001-0.003-0.001h-0.001
71
+ c-0.001-0.001-0.001-0.001-0.001-0.001l-0.001-0.001c-0.001,0-0.001,0-0.001-0.001h-0.001l-0.001-0.001
72
+ c-0.001-0.001-0.001-0.001-0.001-0.001s-0.001,0-0.001-0.001h-0.001c0,0-0.001-0.001-0.001-0.001
73
+ c-0.001,0.001-0.001-0.001-0.002-0.001c0.001-0.001-0.001-0.001-0.002-0.001l-0.001-0.001c-0.001,0-0.001,0-0.001,0
74
+ c-0.001-0.001-0.001-0.001-0.001-0.001l-0.001-0.001c-0.003-0.001-0.002-0.001-0.002-0.001c-0.001,0.001-0.001-0.001-0.003-0.001
75
+ l-0.001-0.001c0.001-0.001-0.001-0.001-0.002-0.001c0,0-0.001,0-0.001-0.001c-0.001-0.001-0.003-0.001-0.004-0.001
76
+ s-0.003-0.001-0.004-0.001l-0.002-0.001c0,0-0.001,0-0.002-0.001l-0.002-0.001c-0.003-0.001-0.003-0.001-0.002-0.001
77
+ c-0.003,0-0.003,0-0.002-0.001c-0.001,0-0.001-0.001-0.001-0.001c-0.001,0-0.002-0.001-0.002-0.001h-0.001
78
+ c-0.059-0.021-0.122-0.034-0.188-0.037h-0.001c-0.001,0-0.002,0-0.002,0c-0.001,0-0.002,0-0.003,0h-0.001h-0.001h-0.001
79
+ c-0.001-0.001-0.001-0.001-0.002-0.001c0,0-0.001,0-0.002,0c0,0-0.001,0-0.002,0h-0.001c0,0-0.001,0-0.001,0h-0.001
80
+ c-0.001,0-0.002,0-0.002,0h-0.002c0,0,0,0-0.001,0c0,0-0.001,0-0.001,0h-0.001c0,0-0.001,0-0.001,0h-0.001h-0.001h-0.001H110.4
81
+ c0,0-0.001,0-0.001,0c-1.655,0-3.01-1.348-3.02-3V89.365h2.573c0.004,0,0.009,0,0.013,0c0.365-0.009,0.661-0.285,0.661-0.648
82
+ c0-0.271-0.166-0.503-0.402-0.6l-12.38-8.544c-0.221-0.153-0.515-0.153-0.737,0l-12.476,8.61c-0.234,0.161-0.335,0.456-0.251,0.727
83
+ c0.085,0.271,0.335,0.455,0.619,0.455h2.573l0.002,18.599c0.013,2.329,1.885,4.231,4.264,4.289
84
+ c0.026,0.003,0.052,0.004,0.078,0.004h18.481c0.004,0,0.007,0,0.011,0c0.17-0.003,0.324-0.071,0.438-0.18l0.001-0.001
85
+ c0.002-0.002,0.005-0.004,0.005-0.005c0.001-0.001,0.002-0.001,0.003-0.003c0,0,0.001-0.001,0.001-0.001l0.001-0.001l0.001-0.001
86
+ l0.001-0.001l0.001-0.001l0.001-0.001c0.003-0.001,0.001-0.001,0.002-0.002c0,0,0,0,0.001-0.001l0.001-0.001c0,0,0,0,0.001-0.001
87
+ c0.112-0.116,0.182-0.273,0.183-0.447v-0.002v-0.001v-0.001v-0.001v-0.001v-0.001v-0.001v-0.002
88
+ C111.045,111.345,110.889,111.121,110.667,111.02z"
89
+ />
90
+ </g>
91
+ </svg>
92
+ )
93
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ import {cloudinaryAssetSchema} from './schema/cloudinaryAsset'
2
+ import {cloudinaryAssetDerivedSchema} from './schema/cloudinaryAssetDerived'
3
+ import {createPlugin, AssetSource} from 'sanity'
4
+ import {CloudinaryIcon} from './components/asset-source/Icon'
5
+ import {CloudinaryAssetSource} from './components/asset-source/CloudinaryAssetSource'
6
+
7
+ export {cloudinaryAssetSchema, cloudinaryAssetDerivedSchema}
8
+
9
+ export const cloudinarySchemaPlugin = createPlugin({
10
+ name: 'cloudinary-schema',
11
+ schema: {
12
+ types: [cloudinaryAssetSchema, cloudinaryAssetDerivedSchema],
13
+ },
14
+ })
15
+
16
+ export const cloudinaryImageSource: AssetSource = {
17
+ name: 'cloudinary-image',
18
+ title: 'Cloudinary',
19
+ icon: CloudinaryIcon,
20
+ component: CloudinaryAssetSource,
21
+ }
22
+
23
+ export const cloudinaryAssetSourcePlugin = createPlugin({
24
+ name: 'cloudinart-asset-source',
25
+ form: {
26
+ image: {
27
+ assetSources: [cloudinaryImageSource],
28
+ },
29
+ },
30
+ })
@@ -0,0 +1,99 @@
1
+ /* eslint-disable */
2
+ import CloudinaryInput from '../components/CloudinaryInput'
3
+ import AssetDiff from '../components/AssetDiff'
4
+ import AssetPreview from '../components/AssetPreview'
5
+ import {defineType} from 'sanity'
6
+
7
+ export const cloudinaryAssetSchema = defineType({
8
+ type: 'object',
9
+ name: 'cloudinary.asset',
10
+ fields: [
11
+ {
12
+ type: 'string',
13
+ name: 'public_id',
14
+ },
15
+ {
16
+ type: 'string',
17
+ name: 'resource_type',
18
+ // "image", "?"
19
+ },
20
+ {
21
+ type: 'string',
22
+ name: 'type',
23
+ // "upload", "?"
24
+ },
25
+ {
26
+ type: 'string',
27
+ name: 'format',
28
+ // "jpg"
29
+ },
30
+ {
31
+ type: 'number',
32
+ name: 'version',
33
+ },
34
+ {
35
+ type: 'url',
36
+ name: 'url',
37
+ },
38
+ {
39
+ type: 'url',
40
+ name: 'secure_url',
41
+ },
42
+ {
43
+ type: 'number',
44
+ name: 'width',
45
+ },
46
+ {
47
+ type: 'number',
48
+ name: 'height',
49
+ },
50
+ {
51
+ type: 'number',
52
+ name: 'bytes',
53
+ },
54
+ {
55
+ type: 'number',
56
+ name: 'duration',
57
+ // can be null
58
+ },
59
+ {
60
+ type: 'array',
61
+ name: 'tags',
62
+ of: [{type: 'string'}],
63
+ },
64
+ {
65
+ type: 'datetime',
66
+ name: 'created_at',
67
+ },
68
+ {
69
+ type: 'array',
70
+ name: 'derived',
71
+ of: [{type: 'cloudinary.assetDerived'}],
72
+ },
73
+ {
74
+ type: 'string',
75
+ name: 'access_mode',
76
+ },
77
+ // context array of unknown content
78
+ // metadata array of unknown content
79
+ ],
80
+ components: {
81
+ input: CloudinaryInput,
82
+ diff: AssetDiff,
83
+ preview: AssetPreview,
84
+ },
85
+ preview: {
86
+ select: {
87
+ url: 'url',
88
+ resource_type: 'resource_type',
89
+ derived: 'derived.0.url',
90
+ },
91
+ prepare({url, derived, resource_type}) {
92
+ return {
93
+ title: url,
94
+ resource_type,
95
+ url: derived || url,
96
+ }
97
+ },
98
+ },
99
+ })
@@ -0,0 +1,26 @@
1
+ import {defineType} from 'sanity'
2
+
3
+ export type CloudinaryAssetDerived = {
4
+ raw_transformation: string
5
+ url: string
6
+ secure_url: string
7
+ }
8
+
9
+ export const cloudinaryAssetDerivedSchema = defineType({
10
+ type: 'object',
11
+ name: 'cloudinary.assetDerived',
12
+ fields: [
13
+ {
14
+ type: 'string',
15
+ name: 'raw_transformation',
16
+ },
17
+ {
18
+ type: 'url',
19
+ name: 'url',
20
+ },
21
+ {
22
+ type: 'url',
23
+ name: 'secure_url',
24
+ },
25
+ ],
26
+ })
@@ -0,0 +1,74 @@
1
+ import {CSSProperties} from 'react'
2
+ import {CloudinaryAssetDerived} from './schema/cloudinaryAssetDerived'
3
+
4
+ declare module '*.css' {
5
+ const content: {[className: string]: CSSProperties}
6
+ export default content
7
+ }
8
+
9
+ export type CloudinaryDerivative = {
10
+ url: string
11
+ secure_url: string
12
+ raw_transformation: string
13
+ }
14
+
15
+ export type CloudinaryAssetResponse = {
16
+ public_id: string
17
+ resource_type: string
18
+ type: string
19
+ url: string
20
+ tags: string[]
21
+ secure_url: string
22
+ format: string
23
+ width: number
24
+ height: number
25
+ bytes: number
26
+ derived?: CloudinaryDerivative[]
27
+ }
28
+
29
+ export type InsertHandlerParams = {
30
+ assets: CloudinaryAssetResponse[]
31
+ }
32
+
33
+ export interface CloudinaryMediaLibrary {
34
+ show: (config?: {asset: any; folder: any}) => void
35
+ hide: () => void
36
+ }
37
+
38
+ export type CloudinaryAsset = {
39
+ _type: string
40
+ _key?: string
41
+ _version: number
42
+ public_id: string
43
+ resource_type: string
44
+ type: string
45
+ format: string
46
+ version: number
47
+ url: string
48
+ secure_url: string
49
+ derived?: CloudinaryAssetDerived[]
50
+ }
51
+
52
+ export type AssetDocument = {
53
+ _id: string
54
+ label?: string
55
+ title?: string
56
+ description?: string
57
+ source?: {
58
+ id: string
59
+ name: string
60
+ url?: string
61
+ }
62
+ creditLine?: string
63
+ originalFilename?: string
64
+ }
65
+
66
+ declare global {
67
+ // eslint-disable-next-line no-unused-vars
68
+ interface Window {
69
+ cloudinary: {
70
+ openMediaLibrary: (config: any, callbacks: any) => void
71
+ createMediaLibrary: (config: any, callbacks?: any) => CloudinaryMediaLibrary
72
+ }
73
+ }
74
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,115 @@
1
+ /* eslint-disable camelcase */
2
+ import {
3
+ CloudinaryAsset,
4
+ CloudinaryAssetResponse,
5
+ CloudinaryMediaLibrary,
6
+ InsertHandlerParams,
7
+ } from './typings'
8
+
9
+ const widgetSrc = 'https://media-library.cloudinary.com/global/all.js'
10
+
11
+ export function assetUrl(asset: CloudinaryAsset) {
12
+ if (asset.derived && asset.derived.length > 0) {
13
+ const [derived] = asset.derived
14
+ if (derived.secure_url) {
15
+ return derived.secure_url
16
+ }
17
+ return derived.url
18
+ }
19
+ if (asset.secure_url) {
20
+ return asset.secure_url
21
+ }
22
+ return asset.url
23
+ }
24
+
25
+ export const openMediaSelector = (
26
+ cloudName: string,
27
+ apiKey: string,
28
+ multiple: boolean,
29
+ insertHandler: (params: InsertHandlerParams) => void,
30
+ selectedAsset?: CloudinaryAsset
31
+ ) => {
32
+ loadJS(widgetSrc, () => {
33
+ const options: Record<string, any> = {
34
+ cloud_name: cloudName,
35
+ api_key: apiKey,
36
+ insert_caption: 'Select',
37
+ multiple,
38
+ }
39
+
40
+ if (selectedAsset) {
41
+ options.asset = {
42
+ public_id: selectedAsset.public_id,
43
+ type: selectedAsset.type,
44
+ resource_type: selectedAsset.resource_type,
45
+ }
46
+ }
47
+
48
+ window.cloudinary.openMediaLibrary(options, {insertHandler})
49
+ })
50
+ }
51
+
52
+ export const createMediaLibrary = ({
53
+ cloudName,
54
+ apiKey,
55
+ inlineContainer,
56
+ libraryCreated,
57
+ insertHandler,
58
+ }: {
59
+ cloudName: string
60
+ apiKey: string
61
+ inlineContainer: string
62
+ libraryCreated: (library: CloudinaryMediaLibrary) => void
63
+ insertHandler: (params: InsertHandlerParams) => void
64
+ }) => {
65
+ loadJS(widgetSrc, () => {
66
+ const options: Record<string, any> = {
67
+ cloud_name: cloudName,
68
+ api_key: apiKey,
69
+ insert_caption: 'Select',
70
+ inline_container: inlineContainer,
71
+ remove_header: true,
72
+ }
73
+
74
+ libraryCreated(window.cloudinary.createMediaLibrary(options, {insertHandler}))
75
+ })
76
+ }
77
+
78
+ export function loadJS(url: string, callback: () => void) {
79
+ const existingScript = document.getElementById('damWidget')
80
+ if (!existingScript) {
81
+ const script = document.createElement('script')
82
+ script.src = url
83
+ script.id = 'damWidget'
84
+ document.body.appendChild(script)
85
+ script.onload = () => {
86
+ if (callback) {
87
+ return callback()
88
+ }
89
+ return true
90
+ }
91
+ }
92
+ if (existingScript && callback) {
93
+ return callback()
94
+ }
95
+ return true
96
+ }
97
+
98
+ export function encodeSourceId(asset: CloudinaryAssetResponse): string {
99
+ const {resource_type, public_id, type} = asset
100
+ return btoa(JSON.stringify({public_id, resource_type, type})) // Sort keys alphabetically!
101
+ }
102
+
103
+ export function encodeFilename(asset: CloudinaryAssetResponse) {
104
+ return `${asset.public_id.split('/').slice(-1)[0]}.${asset.format}`
105
+ }
106
+
107
+ export function decodeSourceId(sourceId: string): CloudinaryAssetResponse | undefined {
108
+ let sourceIdDecoded: any
109
+ try {
110
+ sourceIdDecoded = JSON.parse(atob(sourceId))
111
+ } catch (err) {
112
+ // Do nothing
113
+ }
114
+ return sourceIdDecoded
115
+ }
@@ -0,0 +1,11 @@
1
+ const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
+ const {name, version, sanityExchangeUrl} = require('./package.json')
3
+
4
+ export default showIncompatiblePluginDialog({
5
+ name: name,
6
+ versions: {
7
+ v3: version,
8
+ v2: undefined,
9
+ },
10
+ sanityExchangeUrl,
11
+ })