sanity-plugin-iframe-pane 2.1.0 → 2.2.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.
- package/LICENSE +1 -1
- package/README.md +82 -29
- package/lib/index.esm.js +1 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/src/index.d.ts +2 -0
- package/package.json +5 -5
- package/src/Iframe.tsx +34 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> This is a **Sanity Studio v3** plugin.
|
|
4
4
|
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-iframe-pane/tree/studio-v2).
|
|
5
5
|
|
|
6
|
-
Display any URL in a View Pane, along with helpful buttons to Copy the URL or open in a new tab.
|
|
6
|
+
Display any URL in a View Pane, along with helpful buttons to Copy the URL or open it in a new tab.
|
|
7
7
|
|
|
8
8
|
Accepts either a string or an async function to resolve a URL based on the current document.
|
|
9
9
|
|
|
@@ -23,37 +23,90 @@ yarn add sanity-plugin-iframe-pane
|
|
|
23
23
|
|
|
24
24
|
## Usage
|
|
25
25
|
|
|
26
|
-
This is designed to be used as a [Component inside of a View](https://www.sanity.io/docs/structure-builder-reference#c0c8284844b7).
|
|
26
|
+
This is designed to be used as a [Component inside of a View](https://www.sanity.io/docs/structure-builder-reference#c0c8284844b7).
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
The simplest way to configure views is by customizing the `defaultDocumentNode` setting in the `deskTool()` plugin.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// ./sanity.config.ts
|
|
32
|
+
|
|
33
|
+
export default defineConfig({
|
|
34
|
+
// ...other config settings
|
|
35
|
+
plugins: [
|
|
36
|
+
deskTool({
|
|
37
|
+
defaultDocumentNode,
|
|
38
|
+
structure, // not required
|
|
39
|
+
}),
|
|
40
|
+
// ...other plugins
|
|
41
|
+
],
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A basic example of a custom `defaultDocumentNode` function, to only show the Iframe Pane on `movie` type documents.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
|
|
49
|
+
// ./src/defaultDocumentNode.ts
|
|
50
|
+
|
|
51
|
+
import {DefaultDocumentNodeResolver} from 'sanity/desk'
|
|
30
52
|
import Iframe from 'sanity-plugin-iframe-pane'
|
|
53
|
+
import {SanityDocument} from 'sanity'
|
|
54
|
+
|
|
55
|
+
// Customise this function to show the correct URL based on the current document
|
|
56
|
+
function getPreviewUrl(doc: SanityDocument) {
|
|
57
|
+
return doc?.slug?.current
|
|
58
|
+
? `${window.location.host}/${doc.slug.current}`
|
|
59
|
+
: `${window.location.host}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Import this into the deskTool() plugin
|
|
63
|
+
export const defaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
|
|
64
|
+
// Only show preview pane on `movie` schema type documents
|
|
65
|
+
switch (schemaType) {
|
|
66
|
+
case `movie`:
|
|
67
|
+
return S.document().views([
|
|
68
|
+
S.view.form(),
|
|
69
|
+
S.view
|
|
70
|
+
.component(Iframe)
|
|
71
|
+
.options({
|
|
72
|
+
url: (doc: SanityDocument) => getPreviewUrl(doc),
|
|
73
|
+
})
|
|
74
|
+
.title('Preview'),
|
|
75
|
+
])
|
|
76
|
+
default:
|
|
77
|
+
return S.document().views([S.view.form()])
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
31
81
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
82
|
+
## Options
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// Required: Accepts an async function
|
|
86
|
+
url: (doc) => resolveProductionUrl(doc),
|
|
87
|
+
|
|
88
|
+
// OR a string
|
|
89
|
+
url: `https://sanity.io`,
|
|
90
|
+
|
|
91
|
+
// Optional: Set the default size
|
|
92
|
+
defaultSize: `mobile`, // default `desktop`
|
|
93
|
+
|
|
94
|
+
// Optional: Add a reload button, or reload on new document revisions
|
|
95
|
+
reload: {
|
|
96
|
+
button: true, // default `undefined`
|
|
97
|
+
revision: true, // boolean | number. default `undefined`. If a number is provided, add a delay (in ms) before the automatic reload on document revision
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Optional: Display a spinner while the iframe is loading
|
|
101
|
+
loader: true // boolean | string. default `undefined`. If a string is provided, it will be display below the spinner (e.g. Loading…)
|
|
102
|
+
|
|
103
|
+
// Optional: Pass attributes to the underlying `iframe` element:
|
|
104
|
+
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
|
|
105
|
+
attributes: {
|
|
106
|
+
allow: 'fullscreen', // string, optional
|
|
107
|
+
referrerPolicy: 'strict-origin-when-cross-origin', // string, optional
|
|
108
|
+
sandbox: 'allow-same-origin', // string, optional
|
|
109
|
+
}
|
|
57
110
|
```
|
|
58
111
|
|
|
59
112
|
## License
|
package/lib/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function t(t){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){n(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}import{jsx as i,jsxs as r}from"react/jsx-runtime";import{useState as o,useRef as l,useEffect as c}from"react";import{ThemeProvider as a,Flex as d,Spinner as
|
|
1
|
+
function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function t(t){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){n(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}import{jsx as i,jsxs as r}from"react/jsx-runtime";import{useState as o,useRef as l,useEffect as c}from"react";import{ThemeProvider as a,Flex as d,Spinner as s,Card as u,Button as p,Box as f,Text as g}from"@sanity/ui";import{MobileDeviceIcon as y,UndoIcon as h,CopyIcon as b,LeaveIcon as m}from"@sanity/icons";import{useCopyToClipboard as v}from"usehooks-ts";const O={desktop:{width:"100%",height:"100%",maxHeight:"100%"},mobile:{width:414,height:"100%",maxHeight:736}},j="desktop";function w(e){const{document:n,options:w}=e,{url:k,defaultSize:x=j,reload:P,loader:z,attributes:C={}}=w,[S,D]=o(k&&"string"==typeof k?k:""),[L,E]=o((null==O?void 0:O[x])?x:j),[B,H]=o(!1),R=l(null),_=l(null),{displayed:I}=n,[,N]=v();function T(){(null==_?void 0:_.current)&&(_.current.src=_.current.src,H(!0))}return c((()=>{((null==P?void 0:P.revision)||0==(null==P?void 0:P.revision))&&setTimeout((()=>{T()}),Number(null==P?void 0:P.revision))}),[I._rev,null==P?void 0:P.revision]),c((()=>{"function"==typeof k&&(async()=>{H(!0);const e="function"==typeof k?await k(I):"";e!==S&&e&&"string"==typeof e&&D(e)})()}),[I._rev]),S&&"string"==typeof S?r(a,{children:[i("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:R,value:S,readOnly:!0,tabIndex:-1}),r(d,{direction:"column",style:{height:"100%"},children:[i(u,{padding:2,borderBottom:!0,children:r(d,{align:"center",gap:2,children:[i(d,{align:"center",gap:1,children:i(p,{fontSize:[1],padding:2,tone:"primary",mode:"mobile"===L?"default":"ghost",icon:y,onClick:()=>E("mobile"===L?"desktop":"mobile")})}),i(f,{flex:1,children:i(g,{size:0,textOverflow:"ellipsis",children:S})}),r(d,{align:"center",gap:1,children:[(null==P?void 0:P.button)?i(p,{fontSize:[1],padding:2,icon:h,title:"Reload","aria-label":"Reload",onClick:()=>T()}):null,i(p,{fontSize:[1],icon:b,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==R?void 0:R.current)?void 0:e.value)&&N(R.current.value)}}),i(p,{fontSize:[1],icon:m,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(S)})]})]})}),i(u,{tone:"transparent",padding:"mobile"===L?2:0,style:{height:"100%"},children:r(d,{align:"center",justify:"center",style:{height:"100%",position:"relative"},children:[z&&B&&i(d,{justify:"center",align:"center",style:{inset:"0",position:"absolute"},children:i(d,{style:t(t({},O[L]),{},{backgroundColor:"rgba(0,0,0,0.2)"}),justify:"center",align:"center",children:i(u,{padding:4,radius:2,shadow:1,children:r(d,{align:"center",direction:"column",gap:3,height:"fill",justify:"center",children:[i(s,{}),z&&"string"==typeof z&&i(g,{size:1,children:z})]})})})}),i("iframe",t(t({ref:_,title:"preview",style:O[L],frameBorder:"0",src:S},C),{},{onLoad:function(){H(!1),C.onLoad&&"function"==typeof C.onLoad&&C.onLoad()}}))]})})]})]}):i(a,{children:i(d,{padding:5,align:"center",justify:"center",children:i(s,{})})})}export{w as default};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/lib/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/Iframe.tsx"],"sourcesContent":["import React, {useEffect, useState, useRef} from 'react'\nimport {SanityDocumentLike} from 'sanity'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\nimport {HTMLAttributeReferrerPolicy} from 'react'\n\nimport {useCopyToClipboard} from 'usehooks-ts'\n\ntype Size = 'desktop' | 'mobile'\n\ntype SizeProps = {\n // eslint-disable-next-line no-unused-vars\n [key in Size]: {\n width: string | number\n height: string | number\n maxHeight: string | number\n }\n}\n\nconst sizes: SizeProps = {\n desktop: {\n width: `100%`,\n height: `100%`,\n maxHeight: `100%`,\n },\n mobile: {\n width: 414,\n height: `100%`,\n maxHeight: 736,\n },\n}\n\nexport type IframeOptions = {\n url: string | ((document: SanityDocumentLike) => unknown)\n defaultSize?: 'desktop' | 'mobile'\n reload: {\n revision: boolean | number\n button: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n }>\n}\n\nexport type IframeProps = {\n document: {\n displayed: SanityDocumentLike\n }\n options: IframeOptions\n}\n\nconst DEFAULT_SIZE = `desktop`\n\nfunction Iframe(props: IframeProps) {\n const {document: sanityDocument, options} = props\n const {url, defaultSize = DEFAULT_SIZE, reload, attributes = {}} = options\n const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n const input = useRef<HTMLTextAreaElement>(null)\n const iframe = useRef<HTMLIFrameElement>(null)\n const {displayed} = sanityDocument\n const [, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision || reload?.revision == 0) {\n setTimeout(() => {\n handleReload()\n }, Number(reload?.revision))\n }\n }, [displayed._rev, reload?.revision])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n const resolveUrl = typeof url === 'function' ? await url(displayed) : ``\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl && resolveUrl && typeof resolveUrl === 'string') {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url === 'function') {\n getUrl()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} align=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex={-1}\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`}}>\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n {...attributes}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nexport default Iframe\n"],"names":["sizes","desktop","width","height","maxHeight","mobile","DEFAULT_SIZE","Iframe","props","document","sanityDocument","options","url","defaultSize","reload","attributes","displayUrl","setDisplayUrl","useState","iframeSize","setIframeSize","input","useRef","iframe","displayed","copy","useCopyToClipboard","handleReload","current","src","useEffect","revision","setTimeout","Number","_rev","async","resolveUrl","getUrl","jsxs","ThemeProvider","children","jsx","style","position","pointerEvents","opacity","ref","value","readOnly","tabIndex","Flex","direction","Card","padding","borderBottom","align","gap","Button","fontSize","tone","mode","icon","MobileDeviceIcon","onClick","Box","flex","Text","size","textOverflow","button","UndoIcon","title","CopyIcon","_a","LeaveIcon","text","window","open","justify","_objectSpread","frameBorder","Spinner"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/Iframe.tsx"],"sourcesContent":["/* eslint-disable react/jsx-no-bind */\nimport React, {useEffect, useState, useRef} from 'react'\nimport {SanityDocumentLike} from 'sanity'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\nimport {HTMLAttributeReferrerPolicy} from 'react'\n\nimport {useCopyToClipboard} from 'usehooks-ts'\n\ntype Size = 'desktop' | 'mobile'\n\ntype SizeProps = {\n // eslint-disable-next-line no-unused-vars\n [key in Size]: {\n width: string | number\n height: string | number\n maxHeight: string | number\n }\n}\n\nconst sizes: SizeProps = {\n desktop: {\n width: `100%`,\n height: `100%`,\n maxHeight: `100%`,\n },\n mobile: {\n width: 414,\n height: `100%`,\n maxHeight: 736,\n },\n}\n\nexport type IframeOptions = {\n url: string | ((document: SanityDocumentLike) => unknown)\n defaultSize?: 'desktop' | 'mobile'\n loader?: boolean | string\n reload: {\n revision: boolean | number\n button: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nexport type IframeProps = {\n document: {\n displayed: SanityDocumentLike\n }\n options: IframeOptions\n}\n\nconst DEFAULT_SIZE = `desktop`\n\nfunction Iframe(props: IframeProps) {\n const {document: sanityDocument, options} = props\n const {url, defaultSize = DEFAULT_SIZE, reload, loader, attributes = {}} = options\n const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n const [loading, setLoading] = useState(false)\n const input = useRef<HTMLTextAreaElement>(null)\n const iframe = useRef<HTMLIFrameElement>(null)\n const {displayed} = sanityDocument\n const [, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setLoading(true)\n }\n\n function handleIframeLoad() {\n setLoading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision || reload?.revision == 0) {\n setTimeout(() => {\n handleReload()\n }, Number(reload?.revision))\n }\n }, [displayed._rev, reload?.revision])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n setLoading(true)\n const resolveUrl = typeof url === 'function' ? await url(displayed) : ``\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl && resolveUrl && typeof resolveUrl === 'string') {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url === 'function') {\n getUrl()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} align=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex={-1}\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`, position: `relative`}}>\n {loader && loading && (\n <Flex justify=\"center\" align=\"center\" style={{inset: `0`, position: `absolute`}}>\n <Flex\n style={{...sizes[iframeSize], backgroundColor: `rgba(0,0,0,0.2)`}}\n justify=\"center\"\n align=\"center\"\n >\n <Card padding={4} radius={2} shadow={1}>\n <Flex align=\"center\" direction=\"column\" gap={3} height=\"fill\" justify=\"center\">\n <Spinner />\n {loader && typeof loader === 'string' && <Text size={1}>{loader}</Text>}\n </Flex>\n </Card>\n </Flex>\n </Flex>\n )}\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nexport default Iframe\n"],"names":["sizes","desktop","width","height","maxHeight","mobile","DEFAULT_SIZE","Iframe","props","document","sanityDocument","options","url","defaultSize","reload","loader","attributes","displayUrl","setDisplayUrl","useState","iframeSize","setIframeSize","loading","setLoading","input","useRef","iframe","displayed","copy","useCopyToClipboard","handleReload","current","src","useEffect","revision","setTimeout","Number","_rev","async","resolveUrl","getUrl","jsxs","ThemeProvider","children","jsx","style","position","pointerEvents","opacity","ref","value","readOnly","tabIndex","Flex","direction","Card","padding","borderBottom","align","gap","Button","fontSize","tone","mode","icon","MobileDeviceIcon","onClick","Box","flex","Text","size","textOverflow","button","UndoIcon","title","CopyIcon","_a","LeaveIcon","text","window","open","justify","inset","backgroundColor","radius","shadow","Spinner","_objectSpread","frameBorder","onLoad"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var
|
|
1
|
+
"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function t(t){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){n(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var i=require("react/jsx-runtime"),r=require("react"),o=require("@sanity/ui"),l=require("@sanity/icons"),s=require("usehooks-ts");const c={desktop:{width:"100%",height:"100%",maxHeight:"100%"},mobile:{width:414,height:"100%",maxHeight:736}},a="desktop";module.exports=function(e){const{document:n,options:u}=e,{url:d,defaultSize:p=a,reload:f,loader:x,attributes:g={}}=u,[j,y]=r.useState(d&&"string"==typeof d?d:""),[h,b]=r.useState((null==c?void 0:c[p])?p:a),[v,m]=r.useState(!1),O=r.useRef(null),w=r.useRef(null),{displayed:C}=n,[,S]=s.useCopyToClipboard();function P(){(null==w?void 0:w.current)&&(w.current.src=w.current.src,m(!0))}return r.useEffect((()=>{((null==f?void 0:f.revision)||0==(null==f?void 0:f.revision))&&setTimeout((()=>{P()}),Number(null==f?void 0:f.revision))}),[C._rev,null==f?void 0:f.revision]),r.useEffect((()=>{"function"==typeof d&&(async()=>{m(!0);const e="function"==typeof d?await d(C):"";e!==j&&e&&"string"==typeof e&&y(e)})()}),[C._rev]),j&&"string"==typeof j?i.jsxs(o.ThemeProvider,{children:[i.jsx("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:O,value:j,readOnly:!0,tabIndex:-1}),i.jsxs(o.Flex,{direction:"column",style:{height:"100%"},children:[i.jsx(o.Card,{padding:2,borderBottom:!0,children:i.jsxs(o.Flex,{align:"center",gap:2,children:[i.jsx(o.Flex,{align:"center",gap:1,children:i.jsx(o.Button,{fontSize:[1],padding:2,tone:"primary",mode:"mobile"===h?"default":"ghost",icon:l.MobileDeviceIcon,onClick:()=>b("mobile"===h?"desktop":"mobile")})}),i.jsx(o.Box,{flex:1,children:i.jsx(o.Text,{size:0,textOverflow:"ellipsis",children:j})}),i.jsxs(o.Flex,{align:"center",gap:1,children:[(null==f?void 0:f.button)?i.jsx(o.Button,{fontSize:[1],padding:2,icon:l.UndoIcon,title:"Reload","aria-label":"Reload",onClick:()=>P()}):null,i.jsx(o.Button,{fontSize:[1],icon:l.CopyIcon,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==O?void 0:O.current)?void 0:e.value)&&S(O.current.value)}}),i.jsx(o.Button,{fontSize:[1],icon:l.LeaveIcon,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(j)})]})]})}),i.jsx(o.Card,{tone:"transparent",padding:"mobile"===h?2:0,style:{height:"100%"},children:i.jsxs(o.Flex,{align:"center",justify:"center",style:{height:"100%",position:"relative"},children:[x&&v&&i.jsx(o.Flex,{justify:"center",align:"center",style:{inset:"0",position:"absolute"},children:i.jsx(o.Flex,{style:t(t({},c[h]),{},{backgroundColor:"rgba(0,0,0,0.2)"}),justify:"center",align:"center",children:i.jsx(o.Card,{padding:4,radius:2,shadow:1,children:i.jsxs(o.Flex,{align:"center",direction:"column",gap:3,height:"fill",justify:"center",children:[i.jsx(o.Spinner,{}),x&&"string"==typeof x&&i.jsx(o.Text,{size:1,children:x})]})})})}),i.jsx("iframe",t(t({ref:w,title:"preview",style:c[h],frameBorder:"0",src:j},g),{},{onLoad:function(){m(!1),g.onLoad&&"function"==typeof g.onLoad&&g.onLoad()}}))]})})]})]}):i.jsx(o.ThemeProvider,{children:i.jsx(o.Flex,{padding:5,align:"center",justify:"center",children:i.jsx(o.Spinner,{})})})};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/Iframe.tsx"],"sourcesContent":["import React, {useEffect, useState, useRef} from 'react'\nimport {SanityDocumentLike} from 'sanity'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\nimport {HTMLAttributeReferrerPolicy} from 'react'\n\nimport {useCopyToClipboard} from 'usehooks-ts'\n\ntype Size = 'desktop' | 'mobile'\n\ntype SizeProps = {\n // eslint-disable-next-line no-unused-vars\n [key in Size]: {\n width: string | number\n height: string | number\n maxHeight: string | number\n }\n}\n\nconst sizes: SizeProps = {\n desktop: {\n width: `100%`,\n height: `100%`,\n maxHeight: `100%`,\n },\n mobile: {\n width: 414,\n height: `100%`,\n maxHeight: 736,\n },\n}\n\nexport type IframeOptions = {\n url: string | ((document: SanityDocumentLike) => unknown)\n defaultSize?: 'desktop' | 'mobile'\n reload: {\n revision: boolean | number\n button: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n }>\n}\n\nexport type IframeProps = {\n document: {\n displayed: SanityDocumentLike\n }\n options: IframeOptions\n}\n\nconst DEFAULT_SIZE = `desktop`\n\nfunction Iframe(props: IframeProps) {\n const {document: sanityDocument, options} = props\n const {url, defaultSize = DEFAULT_SIZE, reload, attributes = {}} = options\n const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n const input = useRef<HTMLTextAreaElement>(null)\n const iframe = useRef<HTMLIFrameElement>(null)\n const {displayed} = sanityDocument\n const [, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision || reload?.revision == 0) {\n setTimeout(() => {\n handleReload()\n }, Number(reload?.revision))\n }\n }, [displayed._rev, reload?.revision])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n const resolveUrl = typeof url === 'function' ? await url(displayed) : ``\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl && resolveUrl && typeof resolveUrl === 'string') {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url === 'function') {\n getUrl()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} align=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex={-1}\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`}}>\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n {...attributes}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nexport default Iframe\n"],"names":["sizes","desktop","width","height","maxHeight","mobile","DEFAULT_SIZE","props","document","sanityDocument","options","url","defaultSize","reload","attributes","displayUrl","setDisplayUrl","useState","iframeSize","setIframeSize","input","useRef","iframe","displayed","copy","useCopyToClipboard","handleReload","current","src","useEffect","revision","setTimeout","Number","_rev","async","resolveUrl","getUrl","jsxs","ThemeProvider","children","jsx","style","position","pointerEvents","opacity","ref","value","readOnly","tabIndex","Flex","direction","Card","padding","borderBottom","align","gap","Button","fontSize","tone","mode","icon","MobileDeviceIcon","onClick","Box","flex","Text","size","textOverflow","button","UndoIcon","title","CopyIcon","_a","LeaveIcon","text","window","open","justify","_objectSpread","frameBorder","Spinner"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/Iframe.tsx"],"sourcesContent":["/* eslint-disable react/jsx-no-bind */\nimport React, {useEffect, useState, useRef} from 'react'\nimport {SanityDocumentLike} from 'sanity'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\nimport {HTMLAttributeReferrerPolicy} from 'react'\n\nimport {useCopyToClipboard} from 'usehooks-ts'\n\ntype Size = 'desktop' | 'mobile'\n\ntype SizeProps = {\n // eslint-disable-next-line no-unused-vars\n [key in Size]: {\n width: string | number\n height: string | number\n maxHeight: string | number\n }\n}\n\nconst sizes: SizeProps = {\n desktop: {\n width: `100%`,\n height: `100%`,\n maxHeight: `100%`,\n },\n mobile: {\n width: 414,\n height: `100%`,\n maxHeight: 736,\n },\n}\n\nexport type IframeOptions = {\n url: string | ((document: SanityDocumentLike) => unknown)\n defaultSize?: 'desktop' | 'mobile'\n loader?: boolean | string\n reload: {\n revision: boolean | number\n button: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nexport type IframeProps = {\n document: {\n displayed: SanityDocumentLike\n }\n options: IframeOptions\n}\n\nconst DEFAULT_SIZE = `desktop`\n\nfunction Iframe(props: IframeProps) {\n const {document: sanityDocument, options} = props\n const {url, defaultSize = DEFAULT_SIZE, reload, loader, attributes = {}} = options\n const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n const [loading, setLoading] = useState(false)\n const input = useRef<HTMLTextAreaElement>(null)\n const iframe = useRef<HTMLIFrameElement>(null)\n const {displayed} = sanityDocument\n const [, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setLoading(true)\n }\n\n function handleIframeLoad() {\n setLoading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision || reload?.revision == 0) {\n setTimeout(() => {\n handleReload()\n }, Number(reload?.revision))\n }\n }, [displayed._rev, reload?.revision])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n setLoading(true)\n const resolveUrl = typeof url === 'function' ? await url(displayed) : ``\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl && resolveUrl && typeof resolveUrl === 'string') {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url === 'function') {\n getUrl()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} align=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex={-1}\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`, position: `relative`}}>\n {loader && loading && (\n <Flex justify=\"center\" align=\"center\" style={{inset: `0`, position: `absolute`}}>\n <Flex\n style={{...sizes[iframeSize], backgroundColor: `rgba(0,0,0,0.2)`}}\n justify=\"center\"\n align=\"center\"\n >\n <Card padding={4} radius={2} shadow={1}>\n <Flex align=\"center\" direction=\"column\" gap={3} height=\"fill\" justify=\"center\">\n <Spinner />\n {loader && typeof loader === 'string' && <Text size={1}>{loader}</Text>}\n </Flex>\n </Card>\n </Flex>\n </Flex>\n )}\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nexport default Iframe\n"],"names":["sizes","desktop","width","height","maxHeight","mobile","DEFAULT_SIZE","props","document","sanityDocument","options","url","defaultSize","reload","loader","attributes","displayUrl","setDisplayUrl","useState","iframeSize","setIframeSize","loading","setLoading","input","useRef","iframe","displayed","copy","useCopyToClipboard","handleReload","current","src","useEffect","revision","setTimeout","Number","_rev","async","resolveUrl","getUrl","jsxs","ThemeProvider","children","jsx","style","position","pointerEvents","opacity","ref","value","readOnly","tabIndex","Flex","direction","Card","padding","borderBottom","align","gap","Button","fontSize","tone","mode","icon","MobileDeviceIcon","onClick","Box","flex","Text","size","textOverflow","button","UndoIcon","title","CopyIcon","_a","LeaveIcon","text","window","open","justify","inset","backgroundColor","radius","shadow","Spinner","_objectSpread","frameBorder","onLoad"],"mappings":"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"}
|
package/lib/src/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare type IframeOptions = IframeOptions_2
|
|
|
9
9
|
declare type IframeOptions_2 = {
|
|
10
10
|
url: string | ((document: SanityDocumentLike) => unknown)
|
|
11
11
|
defaultSize?: 'desktop' | 'mobile'
|
|
12
|
+
loader?: boolean | string
|
|
12
13
|
reload: {
|
|
13
14
|
revision: boolean | number
|
|
14
15
|
button: boolean
|
|
@@ -17,6 +18,7 @@ declare type IframeOptions_2 = {
|
|
|
17
18
|
allow: string
|
|
18
19
|
referrerPolicy: HTMLAttributeReferrerPolicy | undefined
|
|
19
20
|
sandbox: string
|
|
21
|
+
onLoad: () => void
|
|
20
22
|
}>
|
|
21
23
|
}
|
|
22
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-iframe-pane",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Display any URL in a View Pane, along with helpful buttons to Copy the URL or open in a new tab",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/sanity-plugin-iframe-pane#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"watch": "pkg-utils watch"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@sanity/icons": "^
|
|
46
|
+
"@sanity/icons": "^2.0.0",
|
|
47
47
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
48
|
-
"@sanity/ui": "1.0.0
|
|
48
|
+
"@sanity/ui": "^1.0.0",
|
|
49
49
|
"usehooks-ts": "^2.6.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
@@ -68,12 +68,12 @@
|
|
|
68
68
|
"prettier-plugin-packagejson": "^2.3.0",
|
|
69
69
|
"react": "^18",
|
|
70
70
|
"rimraf": "^3.0.2",
|
|
71
|
-
"sanity": "3.0.0
|
|
71
|
+
"sanity": "^3.0.0",
|
|
72
72
|
"typescript": "^4.8.4"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"react": "^18",
|
|
76
|
-
"sanity": "
|
|
76
|
+
"sanity": "^3.0.0"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
79
|
"node": ">=14"
|
package/src/Iframe.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable react/jsx-no-bind */
|
|
1
2
|
import React, {useEffect, useState, useRef} from 'react'
|
|
2
3
|
import {SanityDocumentLike} from 'sanity'
|
|
3
4
|
import {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'
|
|
@@ -33,6 +34,7 @@ const sizes: SizeProps = {
|
|
|
33
34
|
export type IframeOptions = {
|
|
34
35
|
url: string | ((document: SanityDocumentLike) => unknown)
|
|
35
36
|
defaultSize?: 'desktop' | 'mobile'
|
|
37
|
+
loader?: boolean | string
|
|
36
38
|
reload: {
|
|
37
39
|
revision: boolean | number
|
|
38
40
|
button: boolean
|
|
@@ -41,6 +43,7 @@ export type IframeOptions = {
|
|
|
41
43
|
allow: string
|
|
42
44
|
referrerPolicy: HTMLAttributeReferrerPolicy | undefined
|
|
43
45
|
sandbox: string
|
|
46
|
+
onLoad: () => void
|
|
44
47
|
}>
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -55,9 +58,10 @@ const DEFAULT_SIZE = `desktop`
|
|
|
55
58
|
|
|
56
59
|
function Iframe(props: IframeProps) {
|
|
57
60
|
const {document: sanityDocument, options} = props
|
|
58
|
-
const {url, defaultSize = DEFAULT_SIZE, reload, attributes = {}} = options
|
|
61
|
+
const {url, defaultSize = DEFAULT_SIZE, reload, loader, attributes = {}} = options
|
|
59
62
|
const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)
|
|
60
63
|
const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)
|
|
64
|
+
const [loading, setLoading] = useState(false)
|
|
61
65
|
const input = useRef<HTMLTextAreaElement>(null)
|
|
62
66
|
const iframe = useRef<HTMLIFrameElement>(null)
|
|
63
67
|
const {displayed} = sanityDocument
|
|
@@ -77,6 +81,16 @@ function Iframe(props: IframeProps) {
|
|
|
77
81
|
// Funky way to reload an iframe without CORS issuies
|
|
78
82
|
// eslint-disable-next-line no-self-assign
|
|
79
83
|
iframe.current.src = iframe.current.src
|
|
84
|
+
|
|
85
|
+
setLoading(true)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleIframeLoad() {
|
|
89
|
+
setLoading(false)
|
|
90
|
+
// Run onLoad from attributes
|
|
91
|
+
if (attributes.onLoad && typeof attributes.onLoad === 'function') {
|
|
92
|
+
attributes.onLoad()
|
|
93
|
+
}
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
// Reload on new revisions
|
|
@@ -91,6 +105,7 @@ function Iframe(props: IframeProps) {
|
|
|
91
105
|
// Set initial URL and refresh on new revisions
|
|
92
106
|
useEffect(() => {
|
|
93
107
|
const getUrl = async () => {
|
|
108
|
+
setLoading(true)
|
|
94
109
|
const resolveUrl = typeof url === 'function' ? await url(displayed) : ``
|
|
95
110
|
|
|
96
111
|
// Only update state if URL has changed
|
|
@@ -173,7 +188,23 @@ function Iframe(props: IframeProps) {
|
|
|
173
188
|
</Flex>
|
|
174
189
|
</Card>
|
|
175
190
|
<Card tone="transparent" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>
|
|
176
|
-
<Flex align="center" justify="center" style={{height: `100
|
|
191
|
+
<Flex align="center" justify="center" style={{height: `100%`, position: `relative`}}>
|
|
192
|
+
{loader && loading && (
|
|
193
|
+
<Flex justify="center" align="center" style={{inset: `0`, position: `absolute`}}>
|
|
194
|
+
<Flex
|
|
195
|
+
style={{...sizes[iframeSize], backgroundColor: `rgba(0,0,0,0.2)`}}
|
|
196
|
+
justify="center"
|
|
197
|
+
align="center"
|
|
198
|
+
>
|
|
199
|
+
<Card padding={4} radius={2} shadow={1}>
|
|
200
|
+
<Flex align="center" direction="column" gap={3} height="fill" justify="center">
|
|
201
|
+
<Spinner />
|
|
202
|
+
{loader && typeof loader === 'string' && <Text size={1}>{loader}</Text>}
|
|
203
|
+
</Flex>
|
|
204
|
+
</Card>
|
|
205
|
+
</Flex>
|
|
206
|
+
</Flex>
|
|
207
|
+
)}
|
|
177
208
|
<iframe
|
|
178
209
|
ref={iframe}
|
|
179
210
|
title="preview"
|
|
@@ -181,6 +212,7 @@ function Iframe(props: IframeProps) {
|
|
|
181
212
|
frameBorder="0"
|
|
182
213
|
src={displayUrl}
|
|
183
214
|
{...attributes}
|
|
215
|
+
onLoad={handleIframeLoad}
|
|
184
216
|
/>
|
|
185
217
|
</Flex>
|
|
186
218
|
</Card>
|