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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Simeon Griggs
3
+ Copyright (c) 2023 Simeon Griggs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- ```js
29
- // ./src/deskStructure.js
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
- // ...all other list items
33
-
34
- S.view
35
- .component(Iframe)
36
- .options({
37
- // Required: Accepts an async function
38
- url: (doc) => resolveProductionUrl(doc),
39
- // OR a string
40
- url: `https://sanity.io`,
41
- // Optional: Set the default size
42
- defaultSize: `mobile`, // default `desktop`
43
- // Optional: Add a reload button, or reload on new document revisions
44
- reload: {
45
- button: true, // default `undefined`
46
- revision: true, // boolean | number. default `undefined`. If a number is provided, add a delay (in ms) before the automatic reload on document revision
47
- },
48
- // Optional: Pass attributes to the underlying `iframe` element:
49
- // See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
50
- attributes: {
51
- allow: 'fullscreen' // string, optional
52
- referrerPolicy: 'strict-origin-when-cross-origin' // string, optional
53
- sandbox: 'allow-same-origin' // string, optional
54
- }
55
- })
56
- .title('Preview')
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 u,Card as s,Button as p,Box as f,Text as g}from"@sanity/ui";import{MobileDeviceIcon as y,UndoIcon as h,CopyIcon as m,LeaveIcon as b}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,attributes:S={}}=w,[z,C]=o(k&&"string"==typeof k?k:""),[D,E]=o((null==O?void 0:O[x])?x:j),B=l(null),H=l(null),{displayed:R}=n,[,_]=v();function I(){(null==H?void 0:H.current)&&(H.current.src=H.current.src)}return c((()=>{((null==P?void 0:P.revision)||0==(null==P?void 0:P.revision))&&setTimeout((()=>{I()}),Number(null==P?void 0:P.revision))}),[R._rev,null==P?void 0:P.revision]),c((()=>{"function"==typeof k&&(async()=>{const e="function"==typeof k?await k(R):"";e!==z&&e&&"string"==typeof e&&C(e)})()}),[R._rev]),z&&"string"==typeof z?r(a,{children:[i("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:B,value:z,readOnly:!0,tabIndex:-1}),r(d,{direction:"column",style:{height:"100%"},children:[i(s,{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"===D?"default":"ghost",icon:y,onClick:()=>E("mobile"===D?"desktop":"mobile")})}),i(f,{flex:1,children:i(g,{size:0,textOverflow:"ellipsis",children:z})}),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:()=>I()}):null,i(p,{fontSize:[1],icon:m,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==B?void 0:B.current)?void 0:e.value)&&_(B.current.value)}}),i(p,{fontSize:[1],icon:b,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(z)})]})]})}),i(s,{tone:"transparent",padding:"mobile"===D?2:0,style:{height:"100%"},children:i(d,{align:"center",justify:"center",style:{height:"100%"},children:i("iframe",t({ref:H,title:"preview",style:O[D],frameBorder:"0",src:z},S))})})]})]}):i(a,{children:i(d,{padding:5,align:"center",justify:"center",children:i(u,{})})})}export{w as default};
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
@@ -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 r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?e(Object(i),!0).forEach((function(e){n(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,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 r=require("react/jsx-runtime"),i=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}},u="desktop";module.exports=function(e){const{document:n,options:a}=e,{url:d,defaultSize:p=u,reload:f,attributes:x={}}=a,[y,g]=i.useState(d&&"string"==typeof d?d:""),[h,j]=i.useState((null==c?void 0:c[p])?p:u),b=i.useRef(null),v=i.useRef(null),{displayed:m}=n,[,O]=s.useCopyToClipboard();function w(){(null==v?void 0:v.current)&&(v.current.src=v.current.src)}return i.useEffect((()=>{((null==f?void 0:f.revision)||0==(null==f?void 0:f.revision))&&setTimeout((()=>{w()}),Number(null==f?void 0:f.revision))}),[m._rev,null==f?void 0:f.revision]),i.useEffect((()=>{"function"==typeof d&&(async()=>{const e="function"==typeof d?await d(m):"";e!==y&&e&&"string"==typeof e&&g(e)})()}),[m._rev]),y&&"string"==typeof y?r.jsxs(o.ThemeProvider,{children:[r.jsx("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:b,value:y,readOnly:!0,tabIndex:-1}),r.jsxs(o.Flex,{direction:"column",style:{height:"100%"},children:[r.jsx(o.Card,{padding:2,borderBottom:!0,children:r.jsxs(o.Flex,{align:"center",gap:2,children:[r.jsx(o.Flex,{align:"center",gap:1,children:r.jsx(o.Button,{fontSize:[1],padding:2,tone:"primary",mode:"mobile"===h?"default":"ghost",icon:l.MobileDeviceIcon,onClick:()=>j("mobile"===h?"desktop":"mobile")})}),r.jsx(o.Box,{flex:1,children:r.jsx(o.Text,{size:0,textOverflow:"ellipsis",children:y})}),r.jsxs(o.Flex,{align:"center",gap:1,children:[(null==f?void 0:f.button)?r.jsx(o.Button,{fontSize:[1],padding:2,icon:l.UndoIcon,title:"Reload","aria-label":"Reload",onClick:()=>w()}):null,r.jsx(o.Button,{fontSize:[1],icon:l.CopyIcon,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==b?void 0:b.current)?void 0:e.value)&&O(b.current.value)}}),r.jsx(o.Button,{fontSize:[1],icon:l.LeaveIcon,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(y)})]})]})}),r.jsx(o.Card,{tone:"transparent",padding:"mobile"===h?2:0,style:{height:"100%"},children:r.jsx(o.Flex,{align:"center",justify:"center",style:{height:"100%"},children:r.jsx("iframe",t({ref:v,title:"preview",style:c[h],frameBorder:"0",src:y},x))})})]})]}):r.jsx(o.ThemeProvider,{children:r.jsx(o.Flex,{padding:5,align:"center",justify:"center",children:r.jsx(o.Spinner,{})})})};
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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"}
@@ -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.1.0",
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": "^1.3.1",
46
+ "@sanity/icons": "^2.0.0",
47
47
  "@sanity/incompatible-plugin": "^1.0.4",
48
- "@sanity/ui": "1.0.0-beta.32",
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-rc.2",
71
+ "sanity": "^3.0.0",
72
72
  "typescript": "^4.8.4"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "react": "^18",
76
- "sanity": "dev-preview || 3.0.0-rc.2"
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>