sanity-plugin-iframe-pane 2.1.1 → 2.3.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,93 @@ 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: Display the Url in the pane
92
+ showDisplayUrl: true // boolean. default `true`.
93
+
94
+ // Optional: Set the default size
95
+ defaultSize: `mobile`, // default `desktop`
96
+
97
+ // Optional: Add a reload button, or reload on new document revisions
98
+ reload: {
99
+ button: true, // default `undefined`
100
+ revision: true, // boolean | number. default `undefined`. If a number is provided, add a delay (in ms) before the automatic reload on document revision
101
+ },
102
+
103
+ // Optional: Display a spinner while the iframe is loading
104
+ loader: true // boolean | string. default `undefined`. If a string is provided, it will be display below the spinner (e.g. Loading…)
105
+
106
+ // Optional: Pass attributes to the underlying `iframe` element:
107
+ // See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
108
+ attributes: {
109
+ allow: 'fullscreen', // string, optional
110
+ referrerPolicy: 'strict-origin-when-cross-origin', // string, optional
111
+ sandbox: 'allow-same-origin', // string, optional
112
+ }
57
113
  ```
58
114
 
59
115
  ## License
@@ -71,6 +127,6 @@ on how to run this plugin with hotreload in the studio.
71
127
  ### Release new version
72
128
 
73
129
  Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-iframe-pane/actions/workflows/main.yml).
74
- Make sure to select the studio-v3 branch and check "Release new version".
130
+ Make sure to select the `main` branch and check "Release new version".
75
131
 
76
132
  Semantic release will only release on configured branches, so it is safe to run release on any branch.
package/lib/index.esm.js CHANGED
@@ -1,2 +1 @@
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};
2
- //# sourceMappingURL=index.esm.js.map
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=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var i=n.call(e,t||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(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 u,Spinner as d,Card as s,Button as p,Box as f,Text as y}from"@sanity/ui";import{MobileDeviceIcon as g,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:P,defaultSize:k=j,reload:S,loader:x,attributes:z={},showDisplayUrl:C=!0}=w,[D,E]=o(P&&"string"==typeof P?P:""),[L,B]=o((null==O?void 0:O[k])?k:j),[H,N]=o(!1),R=l(null),T=l(null),{displayed:_}=n,[,I]=v();function U(){(null==T?void 0:T.current)&&(T.current.src=T.current.src,N(!0))}return c((()=>{((null==S?void 0:S.revision)||0==(null==S?void 0:S.revision))&&setTimeout((()=>{U()}),Number(null==S?void 0:S.revision))}),[_._rev,null==S?void 0:S.revision]),c((()=>{"function"==typeof P&&(async()=>{N(!0);const e="function"==typeof P?await P(_):"";e!==D&&e&&"string"==typeof e&&E(e)})()}),[_._rev]),D&&"string"==typeof D?r(a,{children:[i("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:R,value:D,readOnly:!0,tabIndex:-1}),r(u,{direction:"column",style:{height:"100%"},children:[i(s,{padding:2,borderBottom:!0,children:r(u,{align:"center",gap:2,children:[i(u,{align:"center",gap:1,children:i(p,{fontSize:[1],padding:2,tone:"primary",mode:"mobile"===L?"default":"ghost",icon:g,onClick:()=>B("mobile"===L?"desktop":"mobile")})}),i(f,{flex:1,children:C&&i(y,{size:0,textOverflow:"ellipsis",children:D})}),r(u,{align:"center",gap:1,children:[(null==S?void 0:S.button)?i(p,{fontSize:[1],padding:2,icon:h,title:"Reload","aria-label":"Reload",onClick:()=>U()}):null,i(p,{fontSize:[1],icon:m,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==R?void 0:R.current)?void 0:e.value)&&I(R.current.value)}}),i(p,{fontSize:[1],icon:b,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(D)})]})]})}),i(s,{tone:"transparent",padding:"mobile"===L?2:0,style:{height:"100%"},children:r(u,{align:"center",justify:"center",style:{height:"100%",position:"relative"},children:[x&&H&&i(u,{justify:"center",align:"center",style:{inset:"0",position:"absolute"},children:i(u,{style:t(t({},O[L]),{},{backgroundColor:"rgba(0,0,0,0.2)"}),justify:"center",align:"center",children:i(s,{padding:4,radius:2,shadow:1,children:r(u,{align:"center",direction:"column",gap:3,height:"fill",justify:"center",children:[i(d,{}),x&&"string"==typeof x&&i(y,{size:1,children:x})]})})})}),i("iframe",t(t({ref:T,title:"preview",style:O[L],frameBorder:"0",src:D},z),{},{onLoad:function(){N(!1),z.onLoad&&"function"==typeof z.onLoad&&z.onLoad()}}))]})})]})]}):i(a,{children:i(u,{padding:5,align:"center",justify:"center",children:i(d,{})})})}export{w as default};//# 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":"6hCAmBA,MAAMA,EAAmB,CACvBC,QAAS,CACPC,MAAO,OACPC,OAAQ,OACRC,UAAW,QAEbC,OAAQ,CACNH,MAAO,IACPC,OAAQ,OACRC,UAAW,MAyBTE,EAAe,UAErB,SAASC,EAAOC,GACd,MAAOC,SAAUC,EAAgBC,QAAAA,GAAWH,GACtCI,IAACA,cAAKC,EAAcP,EAAAQ,OAAcA,aAAQC,EAAa,CAAA,GAAMJ,GAC5DK,EAAYC,GAAiBC,EAASN,GAAsB,iBAARA,EAAmBA,OACvEO,EAAYC,GAAiBF,GAAiB,MAARlB,OAAQ,EAAAA,EAAAa,IAAeA,EAAcP,GAC5Ee,EAAQC,EAA4B,MACpCC,EAASD,EAA0B,OACnCE,UAACA,GAAad,GACXe,CAAAA,GAAQC,IAQjB,SAASC,WACFJ,WAAQK,WAMNL,EAAAK,QAAQC,IAAMN,EAAOK,QAAQC,IACtC,CA4BA,OAzBAC,GAAU,OACI,MAARhB,OAAQ,EAAAA,EAAAiB,WAAgC,IAAZ,MAARjB,OAAQ,EAAAA,EAAAiB,YAC9BC,YAAW,KACIL,GAAA,GACZM,OAAe,MAARnB,OAAQ,EAAAA,EAAAiB,UACpB,GACC,CAACP,EAAUU,KAAM,MAAApB,OAAA,EAAAA,EAAQiB,WAG5BD,GAAU,KAUW,mBAARlB,GATIuB,WACb,MAAMC,EAA4B,mBAARxB,QAA2BA,EAAIY,GAAa,GAGlEY,IAAepB,GAAcoB,GAAoC,iBAAfA,GACpDnB,EAAcmB,EAChB,EAIOC,EACT,GAEC,CAACb,EAAUU,OAETlB,GAAoC,iBAAfA,EAWvBsB,EAAAC,EAAA,CACCC,SAAA,CAACC,EAAA,WAAA,CACCC,MAAO,CAACC,oBAAsBC,cAAe,OAAQC,QAAS,GAC9DC,IAAKzB,EACL0B,MAAO/B,EACPgC,UAAQ,EACRC,UAAU,IAEXX,EAAAY,EAAA,CAAKC,UAAU,SAAST,MAAO,CAACvC,OAAA,QAC/BqC,SAAA,CAACC,EAAAW,EAAA,CAAKC,QAAS,EAAGC,cAAY,EAC5Bd,SAACF,EAAAY,EAAA,CAAKK,MAAM,SAASC,IAAK,EACxBhB,SAAA,CAACC,EAAAS,EAAA,CAAKK,MAAM,SAASC,IAAK,EACxBhB,SAACC,EAAAgB,EAAA,CACCC,SAAU,CAAC,GACXL,QAAS,EACTM,KAAK,UACLC,KAAqB,WAAfzC,EAA0B,UAAY,QAC5C0C,KAAMC,EACNC,QAAS,IAAM3C,EAA6B,WAAfD,EAA0B,UAAY,cAGtEsB,EAAAuB,EAAA,CAAIC,KAAM,EACTzB,SAACC,EAAAyB,EAAA,CAAKC,KAAM,EAAGC,aAAa,WACzB5B,SAAAxB,MAGJsB,EAAAY,EAAA,CAAKK,MAAM,SAASC,IAAK,EACvBhB,SAAA,EAAA,MAAA1B,OAAA,EAAAA,EAAQuD,QACN5B,EAAAgB,EAAA,CACCC,SAAU,CAAC,GACXL,QAAS,EACTQ,KAAMS,EACNC,MAAM,SACN,aAAW,SACXR,QAAS,IAAMpC,MAEf,KACHc,EAAAgB,EAAA,CACCC,SAAU,CAAC,GACXG,KAAMW,EACNnB,QAAS,CAAC,GACVkB,MAAM,OACN,aAAW,OACXR,QAAS,KAhGvB,IAjEFU,GAkES,OAAAA,EAAO,MAAApD,OAAA,EAAAA,EAAAO,cAAS,EAAA6C,EAAA1B,QAEhBtB,EAAAJ,EAAMO,QAAQmB,MA6FmB,IAE3BN,EAAAgB,EAAA,CACCC,SAAU,CAAC,GACXG,KAAMa,EACNrB,QAAS,CAAC,GACVsB,KAAK,OACLhB,KAAK,UACLI,QAAS,IAAMa,OAAOC,KAAK7D,aAKlCyB,EAAAW,EAAA,CAAKO,KAAK,cAAcN,QAAwB,WAAflC,EAA0B,EAAI,EAAGuB,MAAO,CAACvC,OAAA,QACzEqC,SAACC,EAAAS,EAAA,CAAKK,MAAM,SAASuB,QAAQ,SAASpC,MAAO,CAACvC,OAAA,QAC5CqC,SAACC,EAAA,SAAAsC,EAAA,CACCjC,IAAKvB,EACLgD,MAAM,UACN7B,MAAO1C,EAAMmB,GACb6D,YAAY,IACZnD,IAAKb,GACDD,cAzEX0B,EAAAF,EAAA,CACCC,SAACC,EAAAS,EAAA,CAAKG,QAAS,EAAGE,MAAM,SAASuB,QAAQ,SACvCtC,WAACyC,EAAQ,OA8EnB"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/lib/index.js CHANGED
@@ -1,2 +1 @@
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,{})})})};
2
- //# sourceMappingURL=index.js.map
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=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(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}},a="desktop";module.exports=function(e){const{document:n,options:u}=e,{url:d,defaultSize:p=a,reload:f,loader:y,attributes:g={},showDisplayUrl:x=!0}=u,[j,h]=i.useState(d&&"string"==typeof d?d:""),[b,v]=i.useState((null==c?void 0:c[p])?p:a),[m,O]=i.useState(!1),w=i.useRef(null),S=i.useRef(null),{displayed:C}=n,[,P]=s.useCopyToClipboard();function k(){(null==S?void 0:S.current)&&(S.current.src=S.current.src,O(!0))}return i.useEffect((()=>{((null==f?void 0:f.revision)||0==(null==f?void 0:f.revision))&&setTimeout((()=>{k()}),Number(null==f?void 0:f.revision))}),[C._rev,null==f?void 0:f.revision]),i.useEffect((()=>{"function"==typeof d&&(async()=>{O(!0);const e="function"==typeof d?await d(C):"";e!==j&&e&&"string"==typeof e&&h(e)})()}),[C._rev]),j&&"string"==typeof j?r.jsxs(o.ThemeProvider,{children:[r.jsx("textarea",{style:{position:"absolute",pointerEvents:"none",opacity:0},ref:w,value:j,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"===b?"default":"ghost",icon:l.MobileDeviceIcon,onClick:()=>v("mobile"===b?"desktop":"mobile")})}),r.jsx(o.Box,{flex:1,children:x&&r.jsx(o.Text,{size:0,textOverflow:"ellipsis",children:j})}),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:()=>k()}):null,r.jsx(o.Button,{fontSize:[1],icon:l.CopyIcon,padding:[2],title:"Copy","aria-label":"Copy",onClick:()=>{var e;(null==(e=null==w?void 0:w.current)?void 0:e.value)&&P(w.current.value)}}),r.jsx(o.Button,{fontSize:[1],icon:l.LeaveIcon,padding:[2],text:"Open",tone:"primary",onClick:()=>window.open(j)})]})]})}),r.jsx(o.Card,{tone:"transparent",padding:"mobile"===b?2:0,style:{height:"100%"},children:r.jsxs(o.Flex,{align:"center",justify:"center",style:{height:"100%",position:"relative"},children:[y&&m&&r.jsx(o.Flex,{justify:"center",align:"center",style:{inset:"0",position:"absolute"},children:r.jsx(o.Flex,{style:t(t({},c[b]),{},{backgroundColor:"rgba(0,0,0,0.2)"}),justify:"center",align:"center",children:r.jsx(o.Card,{padding:4,radius:2,shadow:1,children:r.jsxs(o.Flex,{align:"center",direction:"column",gap:3,height:"fill",justify:"center",children:[r.jsx(o.Spinner,{}),y&&"string"==typeof y&&r.jsx(o.Text,{size:1,children:y})]})})})}),r.jsx("iframe",t(t({ref:S,title:"preview",style:c[b],frameBorder:"0",src:j},g),{},{onLoad:function(){O(!1),g.onLoad&&"function"==typeof g.onLoad&&g.onLoad()}}))]})})]})]}):r.jsx(o.ThemeProvider,{children:r.jsx(o.Flex,{padding:5,align:"center",justify:"center",children:r.jsx(o.Spinner,{})})})};//# 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":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -9,6 +9,8 @@ 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
13
+ showDisplayUrl?: boolean
12
14
  reload: {
13
15
  revision: boolean | number
14
16
  button: boolean
@@ -17,6 +19,7 @@ declare type IframeOptions_2 = {
17
19
  allow: string
18
20
  referrerPolicy: HTMLAttributeReferrerPolicy | undefined
19
21
  sandbox: string
22
+ onLoad: () => void
20
23
  }>
21
24
  }
22
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-iframe-pane",
3
- "version": "2.1.1",
3
+ "version": "2.3.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,16 +43,16 @@
43
43
  "watch": "pkg-utils watch"
44
44
  },
45
45
  "dependencies": {
46
- "@sanity/icons": "^2.0.0",
46
+ "@sanity/icons": "^2.2.2",
47
47
  "@sanity/incompatible-plugin": "^1.0.4",
48
- "@sanity/ui": "^1.0.0",
49
- "usehooks-ts": "^2.6.0"
48
+ "@sanity/ui": "^1.3.0",
49
+ "usehooks-ts": "^2.9.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@commitlint/cli": "^17.2.0",
53
53
  "@commitlint/config-conventional": "^17.2.0",
54
- "@sanity/pkg-utils": "^1.17.2",
55
- "@sanity/plugin-kit": "^2.1.5",
54
+ "@sanity/pkg-utils": "^1.20.4",
55
+ "@sanity/plugin-kit": "^2.2.0",
56
56
  "@sanity/semantic-release-preset": "^2.0.2",
57
57
  "@typescript-eslint/eslint-plugin": "^5.42.0",
58
58
  "@typescript-eslint/parser": "^5.42.0",
@@ -65,11 +65,11 @@
65
65
  "husky": "^8.0.1",
66
66
  "lint-staged": "^13.0.3",
67
67
  "prettier": "^2.7.1",
68
- "prettier-plugin-packagejson": "^2.3.0",
68
+ "prettier-plugin-packagejson": "^2.4.3",
69
69
  "react": "^18",
70
70
  "rimraf": "^3.0.2",
71
71
  "sanity": "^3.0.0",
72
- "typescript": "^4.8.4"
72
+ "typescript": "^5.0.0"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "react": "^18",
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,8 @@ const sizes: SizeProps = {
33
34
  export type IframeOptions = {
34
35
  url: string | ((document: SanityDocumentLike) => unknown)
35
36
  defaultSize?: 'desktop' | 'mobile'
37
+ loader?: boolean | string
38
+ showDisplayUrl?: boolean
36
39
  reload: {
37
40
  revision: boolean | number
38
41
  button: boolean
@@ -41,6 +44,7 @@ export type IframeOptions = {
41
44
  allow: string
42
45
  referrerPolicy: HTMLAttributeReferrerPolicy | undefined
43
46
  sandbox: string
47
+ onLoad: () => void
44
48
  }>
45
49
  }
46
50
 
@@ -55,9 +59,17 @@ const DEFAULT_SIZE = `desktop`
55
59
 
56
60
  function Iframe(props: IframeProps) {
57
61
  const {document: sanityDocument, options} = props
58
- const {url, defaultSize = DEFAULT_SIZE, reload, attributes = {}} = options
62
+ const {
63
+ url,
64
+ defaultSize = DEFAULT_SIZE,
65
+ reload,
66
+ loader,
67
+ attributes = {},
68
+ showDisplayUrl = true,
69
+ } = options
59
70
  const [displayUrl, setDisplayUrl] = useState(url && typeof url === 'string' ? url : ``)
60
71
  const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)
72
+ const [loading, setLoading] = useState(false)
61
73
  const input = useRef<HTMLTextAreaElement>(null)
62
74
  const iframe = useRef<HTMLIFrameElement>(null)
63
75
  const {displayed} = sanityDocument
@@ -77,6 +89,16 @@ function Iframe(props: IframeProps) {
77
89
  // Funky way to reload an iframe without CORS issuies
78
90
  // eslint-disable-next-line no-self-assign
79
91
  iframe.current.src = iframe.current.src
92
+
93
+ setLoading(true)
94
+ }
95
+
96
+ function handleIframeLoad() {
97
+ setLoading(false)
98
+ // Run onLoad from attributes
99
+ if (attributes.onLoad && typeof attributes.onLoad === 'function') {
100
+ attributes.onLoad()
101
+ }
80
102
  }
81
103
 
82
104
  // Reload on new revisions
@@ -91,6 +113,7 @@ function Iframe(props: IframeProps) {
91
113
  // Set initial URL and refresh on new revisions
92
114
  useEffect(() => {
93
115
  const getUrl = async () => {
116
+ setLoading(true)
94
117
  const resolveUrl = typeof url === 'function' ? await url(displayed) : ``
95
118
 
96
119
  // Only update state if URL has changed
@@ -138,9 +161,11 @@ function Iframe(props: IframeProps) {
138
161
  />
139
162
  </Flex>
140
163
  <Box flex={1}>
141
- <Text size={0} textOverflow="ellipsis">
142
- {displayUrl}
143
- </Text>
164
+ {showDisplayUrl && (
165
+ <Text size={0} textOverflow="ellipsis">
166
+ {displayUrl}
167
+ </Text>
168
+ )}
144
169
  </Box>
145
170
  <Flex align="center" gap={1}>
146
171
  {reload?.button ? (
@@ -173,7 +198,23 @@ function Iframe(props: IframeProps) {
173
198
  </Flex>
174
199
  </Card>
175
200
  <Card tone="transparent" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>
176
- <Flex align="center" justify="center" style={{height: `100%`}}>
201
+ <Flex align="center" justify="center" style={{height: `100%`, position: `relative`}}>
202
+ {loader && loading && (
203
+ <Flex justify="center" align="center" style={{inset: `0`, position: `absolute`}}>
204
+ <Flex
205
+ style={{...sizes[iframeSize], backgroundColor: `rgba(0,0,0,0.2)`}}
206
+ justify="center"
207
+ align="center"
208
+ >
209
+ <Card padding={4} radius={2} shadow={1}>
210
+ <Flex align="center" direction="column" gap={3} height="fill" justify="center">
211
+ <Spinner />
212
+ {loader && typeof loader === 'string' && <Text size={1}>{loader}</Text>}
213
+ </Flex>
214
+ </Card>
215
+ </Flex>
216
+ </Flex>
217
+ )}
177
218
  <iframe
178
219
  ref={iframe}
179
220
  title="preview"
@@ -181,6 +222,7 @@ function Iframe(props: IframeProps) {
181
222
  frameBorder="0"
182
223
  src={displayUrl}
183
224
  {...attributes}
225
+ onLoad={handleIframeLoad}
184
226
  />
185
227
  </Flex>
186
228
  </Card>