sanity-plugin-iframe-pane 3.1.6 → 3.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 +4 -4
- package/lib/index.cjs +27 -15
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +70 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.js +27 -16
- package/lib/index.js.map +1 -1
- package/package.json +28 -31
- package/src/Iframe.tsx +9 -5
- package/src/Toolbar.tsx +29 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ yarn add sanity-plugin-iframe-pane
|
|
|
25
25
|
|
|
26
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
|
-
The simplest way to configure views is by customizing the `defaultDocumentNode` setting in the `
|
|
28
|
+
The simplest way to configure views is by customizing the `defaultDocumentNode` setting in the `structureTool()` plugin.
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
// ./sanity.config.ts
|
|
@@ -33,7 +33,7 @@ The simplest way to configure views is by customizing the `defaultDocumentNode`
|
|
|
33
33
|
export default defineConfig({
|
|
34
34
|
// ...other config settings
|
|
35
35
|
plugins: [
|
|
36
|
-
|
|
36
|
+
structureTool({
|
|
37
37
|
defaultDocumentNode,
|
|
38
38
|
structure, // not required
|
|
39
39
|
}),
|
|
@@ -47,9 +47,9 @@ A basic example of a custom `defaultDocumentNode` function, to only show the Ifr
|
|
|
47
47
|
```ts
|
|
48
48
|
// ./src/defaultDocumentNode.ts
|
|
49
49
|
|
|
50
|
-
import {DefaultDocumentNodeResolver} from 'sanity/
|
|
50
|
+
import {type DefaultDocumentNodeResolver} from 'sanity/structure'
|
|
51
51
|
import {Iframe} from 'sanity-plugin-iframe-pane'
|
|
52
|
-
import {SanityDocument} from 'sanity'
|
|
52
|
+
import {type SanityDocument} from 'sanity'
|
|
53
53
|
|
|
54
54
|
// Customise this function to show the correct URL based on the current document
|
|
55
55
|
function getPreviewUrl(doc: SanityDocument) {
|
package/lib/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: !0 });
|
|
3
|
-
var jsxRuntime = require("react/jsx-runtime"), icons = require("@sanity/icons"), createSecret = require("@sanity/preview-url-secret/create-secret"), definePreviewUrl = require("@sanity/preview-url-secret/define-preview-url"), ui = require("@sanity/ui"), framerMotion = require("framer-motion"), react = require("react"), sanity = require("sanity"), suspendReact = require("suspend-react"),
|
|
3
|
+
var jsxRuntime = require("react/jsx-runtime"), icons = require("@sanity/icons"), createSecret = require("@sanity/preview-url-secret/create-secret"), definePreviewUrl = require("@sanity/preview-url-secret/define-preview-url"), ui = require("@sanity/ui"), framerMotion = require("framer-motion"), react = require("react"), sanity = require("sanity"), suspendReact = require("suspend-react"), getRedirectTo = require("@sanity/preview-url-secret/get-redirect-to");
|
|
4
4
|
function DisplayUrl(props) {
|
|
5
5
|
const truncatedUrl = react.useMemo(() => {
|
|
6
6
|
const url = getRedirectTo.getRedirectTo(props.url);
|
|
@@ -19,7 +19,7 @@ const sizes = {
|
|
|
19
19
|
}
|
|
20
20
|
}, DEFAULT_SIZE = "desktop";
|
|
21
21
|
function Toolbar(props) {
|
|
22
|
-
const { url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload } = props, validUrl = url instanceof URL, input = react.useRef(null), { push: pushToast } = ui.useToast(), [, copy] =
|
|
22
|
+
const { url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload } = props, validUrl = url instanceof URL, input = react.useRef(null), { push: pushToast } = ui.useToast(), [, copy] = useCopyToClipboard();
|
|
23
23
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
24
24
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25
25
|
"textarea",
|
|
@@ -91,8 +91,7 @@ function Toolbar(props) {
|
|
|
91
91
|
padding: [2],
|
|
92
92
|
"aria-label": "Copy URL",
|
|
93
93
|
onClick: () => {
|
|
94
|
-
|
|
95
|
-
(_a = input == null ? void 0 : input.current) != null && _a.value && copy(input.current.value).then((copied) => {
|
|
94
|
+
input?.current?.value && copy(input.current.value).then((copied) => {
|
|
96
95
|
pushToast(copied ? {
|
|
97
96
|
closable: !0,
|
|
98
97
|
status: "success",
|
|
@@ -134,15 +133,27 @@ function Toolbar(props) {
|
|
|
134
133
|
] }) })
|
|
135
134
|
] });
|
|
136
135
|
}
|
|
137
|
-
|
|
136
|
+
function useCopyToClipboard() {
|
|
137
|
+
const [copiedText, setCopiedText] = react.useState(null), copy = react.useCallback(async (text) => {
|
|
138
|
+
if (!navigator?.clipboard)
|
|
139
|
+
return console.warn("Clipboard not supported"), !1;
|
|
140
|
+
try {
|
|
141
|
+
return await navigator.clipboard.writeText(text), setCopiedText(text), !0;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return console.warn("Copy failed", error), setCopiedText(null), !1;
|
|
144
|
+
}
|
|
145
|
+
}, []);
|
|
146
|
+
return [copiedText, copy];
|
|
147
|
+
}
|
|
148
|
+
const MotionFlex = framerMotion.motion.create(ui.Flex);
|
|
138
149
|
function Iframe(props) {
|
|
139
|
-
const { document, options } = props, draft = document.draft || document.published || document.displayed, { defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = !0, key } = options, urlRef = react.useRef(options.url), [draftSnapshot, setDraftSnapshot] = react.useState(() => ({ key, draft }));
|
|
150
|
+
const { document, options } = props, draft = document.draft || document.published || document.displayed, { defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = !0, key } = options, basePath = sanity.useActiveWorkspace()?.activeWorkspace?.basePath || "/", urlRef = react.useRef(options.url), [draftSnapshot, setDraftSnapshot] = react.useState(() => ({ key, draft }));
|
|
140
151
|
react.useEffect(() => {
|
|
141
152
|
urlRef.current = options.url;
|
|
142
153
|
}, [options.url]), react.useEffect(() => {
|
|
143
154
|
JSON.stringify({ key, draft }) !== JSON.stringify(draftSnapshot) && startTransition(() => setDraftSnapshot({ key, draft }));
|
|
144
155
|
}, [draft, draftSnapshot, key]);
|
|
145
|
-
const currentUser = sanity.useCurrentUser(), client = sanity.useClient({ apiVersion: "2023-10-16" }), [expiresAt, setExpiresAt] = react.useState(), previewSecretRef = react.useRef(), [isResolvingUrl, startTransition] = react.useTransition(), url = react.useCallback(
|
|
156
|
+
const currentUser = sanity.useCurrentUser(), client = sanity.useClient({ apiVersion: "2023-10-16" }), [expiresAt, setExpiresAt] = react.useState(), previewSecretRef = react.useRef(void 0), [isResolvingUrl, startTransition] = react.useTransition(), url = react.useCallback(
|
|
146
157
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
147
158
|
async (draft2) => {
|
|
148
159
|
if (typeof location > "u")
|
|
@@ -163,7 +174,7 @@ function Iframe(props) {
|
|
|
163
174
|
client,
|
|
164
175
|
"sanity-plugin-iframe-pane",
|
|
165
176
|
location.href,
|
|
166
|
-
currentUser
|
|
177
|
+
currentUser?.id
|
|
167
178
|
);
|
|
168
179
|
previewSecretRef.current = secret, startTransition(() => setExpiresAt(expiresAt2.getTime()));
|
|
169
180
|
}
|
|
@@ -176,12 +187,14 @@ function Iframe(props) {
|
|
|
176
187
|
})({
|
|
177
188
|
client,
|
|
178
189
|
previewUrlSecret: previewSecretRef.current,
|
|
179
|
-
previewSearchParam: null
|
|
190
|
+
previewSearchParam: null,
|
|
191
|
+
studioBasePath: basePath,
|
|
192
|
+
studioPreviewPerspective: "previewDrafts"
|
|
180
193
|
});
|
|
181
194
|
return new URL(url2, location.origin);
|
|
182
195
|
}
|
|
183
196
|
},
|
|
184
|
-
[client, currentUser
|
|
197
|
+
[client, currentUser?.id, basePath]
|
|
185
198
|
);
|
|
186
199
|
return react.useEffect(() => {
|
|
187
200
|
if (expiresAt) {
|
|
@@ -204,13 +217,12 @@ function Iframe(props) {
|
|
|
204
217
|
defaultSize,
|
|
205
218
|
reload,
|
|
206
219
|
showDisplayUrl,
|
|
207
|
-
userId: currentUser
|
|
220
|
+
userId: currentUser?.id
|
|
208
221
|
},
|
|
209
222
|
draftSnapshot.key
|
|
210
223
|
) });
|
|
211
224
|
}
|
|
212
225
|
const IframeInner = react.memo(function(props) {
|
|
213
|
-
var _a;
|
|
214
226
|
const {
|
|
215
227
|
isResolvingUrl,
|
|
216
228
|
defaultSize = DEFAULT_SIZE,
|
|
@@ -221,7 +233,7 @@ const IframeInner = react.memo(function(props) {
|
|
|
221
233
|
userId,
|
|
222
234
|
expiresAt,
|
|
223
235
|
_key
|
|
224
|
-
} = props, [iframeSize, setIframeSize] = react.useState(
|
|
236
|
+
} = props, [iframeSize, setIframeSize] = react.useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE), prefersReducedMotion = ui.usePrefersReducedMotion(), url = suspendReact.suspend(
|
|
225
237
|
() => props.url(draftSnapshot),
|
|
226
238
|
[
|
|
227
239
|
// Cache based on a few specific conditions
|
|
@@ -233,7 +245,7 @@ const IframeInner = react.memo(function(props) {
|
|
|
233
245
|
resolveUUID
|
|
234
246
|
]
|
|
235
247
|
), [loading, setLoading] = react.useState(!0), [_reloading, setReloading] = react.useState(!1), reloading = _reloading || isResolvingUrl, iframe = react.useRef(null), handleReload = react.useCallback(() => {
|
|
236
|
-
iframe
|
|
248
|
+
iframe?.current && (iframe.current.src = iframe.current.src, setReloading(!0));
|
|
237
249
|
}, []);
|
|
238
250
|
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.MotionConfig, { transition: prefersReducedMotion ? { duration: 0 } : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", style: { height: "100%" }, children: [
|
|
239
251
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -244,7 +256,7 @@ const IframeInner = react.memo(function(props) {
|
|
|
244
256
|
reloading,
|
|
245
257
|
setIframeSize,
|
|
246
258
|
showUrl: showDisplayUrl,
|
|
247
|
-
reloadButton: !!
|
|
259
|
+
reloadButton: !!reload?.button,
|
|
248
260
|
handleReload
|
|
249
261
|
}
|
|
250
262
|
),
|
package/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/DisplayUrl.tsx","../src/Toolbar.tsx","../src/Iframe.tsx"],"sourcesContent":["import {getRedirectTo} from '@sanity/preview-url-secret/get-redirect-to'\nimport {Text} from '@sanity/ui'\nimport {useMemo} from 'react'\n\nexport function DisplayUrl(props: {url: URL}) {\n const truncatedUrl = useMemo(() => {\n const url = getRedirectTo(props.url)\n return `${url.origin === location.origin ? '' : url.origin}${url.pathname}${url.search}`\n }, [props.url])\n\n return (\n <Text size={0} textOverflow=\"ellipsis\">\n {truncatedUrl}\n </Text>\n )\n}\n","import {CopyIcon, LaunchIcon, MobileDeviceIcon, RefreshIcon} from '@sanity/icons'\nimport {Box, Button, Card, Flex, Text, Tooltip, useToast} from '@sanity/ui'\nimport {useRef} from 'react'\nimport {useCopyToClipboard} from 'usehooks-ts'\n\nimport {DisplayUrl} from './DisplayUrl'\nimport type {IframeSizeKey, SizeProps} from './types'\n\nexport const sizes: SizeProps = {\n desktop: {\n width: '100%',\n height: '100%',\n },\n mobile: {\n width: 414,\n height: 746,\n },\n}\n\nexport const DEFAULT_SIZE = 'desktop'\n\nexport interface ToolbarProps {\n url: URL | Error | undefined\n iframeSize: IframeSizeKey\n setIframeSize: (size: IframeSizeKey) => void\n showUrl: boolean\n reloading: boolean\n reloadButton: boolean\n handleReload: () => void\n}\nexport function Toolbar(props: ToolbarProps) {\n const {url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload} = props\n const validUrl = url instanceof URL\n\n const input = useRef<HTMLTextAreaElement>(null)\n const {push: pushToast} = useToast()\n const [, copy] = useCopyToClipboard()\n\n return (\n <>\n <textarea\n style={{position: 'absolute', pointerEvents: 'none', opacity: 0}}\n ref={input}\n value={validUrl ? url.toString() : ''}\n readOnly\n tabIndex={-1}\n />\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {iframeSize === 'mobile' ? 'Exit mobile preview' : 'Preview mobile viewport'}\n </Text>\n }\n padding={2}\n placement=\"bottom-start\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n padding={2}\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Tooltip>\n </Flex>\n <Box flex={1}>{showUrl && validUrl && <DisplayUrl url={url} />}</Box>\n <Flex align=\"center\" gap={1}>\n {reloadButton ? (\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {reloading ? 'Reloading…' : 'Reload'}\n </Text>\n }\n padding={2}\n >\n <Button\n disabled={!validUrl}\n mode=\"bleed\"\n fontSize={[1]}\n padding={2}\n icon={RefreshIcon}\n loading={reloading}\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n </Tooltip>\n ) : null}\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Copy URL\n </Text>\n }\n padding={2}\n >\n <Button\n mode=\"bleed\"\n disabled={!validUrl}\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n aria-label=\"Copy URL\"\n onClick={() => {\n if (!input?.current?.value) return\n\n copy(input.current.value).then((copied) => {\n if (copied) {\n pushToast({\n closable: true,\n status: 'success',\n title: 'The URL is copied to the clipboard',\n })\n } else {\n pushToast({\n closable: true,\n status: 'error',\n title: 'Failed to copy the URL to the clipboard',\n })\n }\n })\n }}\n />\n </Tooltip>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Open URL in a new tab\n </Text>\n }\n padding={2}\n placement=\"bottom-end\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n icon={LaunchIcon}\n mode=\"ghost\"\n paddingY={[2]}\n text=\"Open\"\n aria-label=\"Open URL in a new tab\"\n onClick={validUrl ? () => window.open(url.toString()) : undefined}\n />\n </Tooltip>\n </Flex>\n </Flex>\n </Card>\n </>\n )\n}\n","import {WarningOutlineIcon} from '@sanity/icons'\nimport {createPreviewSecret} from '@sanity/preview-url-secret/create-secret'\nimport {definePreviewUrl} from '@sanity/preview-url-secret/define-preview-url'\nimport {Box, Card, Container, Flex, Spinner, Stack, Text, usePrefersReducedMotion} from '@sanity/ui'\nimport {AnimatePresence, motion, MotionConfig} from 'framer-motion'\nimport type {HTMLAttributeReferrerPolicy} from 'react'\nimport {\n forwardRef,\n memo,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n useTransition,\n} from 'react'\nimport {type SanityDocument, useClient, useCurrentUser} from 'sanity'\nimport {suspend} from 'suspend-react'\n\nimport {DEFAULT_SIZE, sizes, Toolbar} from './Toolbar'\nimport type {IframeSizeKey} from './types'\n\nexport type UrlResolver = (\n document: SanityDocument | null,\n) => string | Error | undefined | Promise<string | Error | undefined>\n\nexport type {IframeSizeKey}\n\nexport interface IframeOptions {\n /**\n * If you have multiple iframe instances side-by-side you need to give each a unique key.\n */\n key?: string\n url:\n | string\n | UrlResolver\n | {\n /**\n * The URL origin of where the preview is hosted, for example `https://example.com`.\n * If it's an embedded Studio then set it to `'same-origin'`.\n */\n origin: 'same-origin' | string\n /**\n * The route to redirect to after enabling Draft Mode.\n * If you don't have enough data to build the URL, return an `Error` instance to show an error message.\n * @example `return new Error('Missing slug')`\n * To prolong the loading state, return `undefined`\n */\n preview: string | UrlResolver\n /**\n * The route that enables Draft Mode\n * @example '/api/draft'\n */\n draftMode: string\n }\n defaultSize?: IframeSizeKey\n showDisplayUrl?: boolean\n reload?: {\n button?: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nconst MotionFlex = motion(Flex)\n\nexport interface IframeProps {\n document: {\n displayed: SanityDocument\n draft: SanityDocument | null\n published: SanityDocument | null\n }\n options: IframeOptions\n}\n\nexport function Iframe(props: IframeProps) {\n const {document, options} = props\n const draft = document.draft || document.published || document.displayed\n\n const {defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = true, key} = options\n\n const urlRef = useRef(options.url)\n const [draftSnapshot, setDraftSnapshot] = useState(() => ({key, draft}))\n useEffect(() => {\n urlRef.current = options.url\n }, [options.url])\n useEffect(() => {\n if (JSON.stringify({key, draft}) !== JSON.stringify(draftSnapshot)) {\n startTransition(() => setDraftSnapshot({key, draft}))\n }\n }, [draft, draftSnapshot, key])\n const currentUser = useCurrentUser()\n const client = useClient({apiVersion: '2023-10-16'})\n const [expiresAt, setExpiresAt] = useState<number | undefined>()\n const previewSecretRef = useRef<string | undefined>()\n const [isResolvingUrl, startTransition] = useTransition()\n const url = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-shadow\n async (draft: SanityDocument | null) => {\n if (typeof location === 'undefined') {\n return undefined\n }\n const urlProp = urlRef.current\n if (typeof urlProp === 'string') {\n return new URL(urlProp, location.origin)\n }\n if (typeof urlProp === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await urlProp(draft)\n return typeof url === 'string' ? new URL(url, location.origin) : url\n }\n if (typeof urlProp === 'object') {\n const preview =\n typeof urlProp.preview === 'function' ? await urlProp.preview(draft) : urlProp.preview\n if (typeof preview !== 'string') {\n return preview\n }\n if (!previewSecretRef.current) {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const {secret, expiresAt} = await createPreviewSecret(\n client,\n 'sanity-plugin-iframe-pane',\n location.href,\n currentUser?.id,\n )\n previewSecretRef.current = secret\n startTransition(() => setExpiresAt(expiresAt.getTime()))\n }\n\n const resolvePreviewUrl = definePreviewUrl({\n origin: urlProp.origin === 'same-origin' ? location.origin : urlProp.origin,\n preview,\n draftMode: {\n enable: urlProp.draftMode,\n },\n })\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await resolvePreviewUrl({\n client,\n previewUrlSecret: previewSecretRef.current,\n previewSearchParam: null,\n })\n return new URL(url, location.origin)\n }\n return undefined\n },\n [client, currentUser?.id],\n )\n useEffect(() => {\n if (expiresAt) {\n const timeout = setTimeout(\n () => {\n startTransition(() => setExpiresAt(undefined))\n previewSecretRef.current = undefined\n },\n Math.max(0, expiresAt - Date.now()),\n )\n return () => clearTimeout(timeout)\n }\n return undefined\n }, [expiresAt])\n\n return (\n <Suspense fallback={<Loading iframeSize=\"desktop\" />}>\n <IframeInner\n key={draftSnapshot.key}\n _key={draftSnapshot.key}\n draftSnapshot={draftSnapshot.draft}\n url={url}\n isResolvingUrl={isResolvingUrl}\n attributes={attributes}\n defaultSize={defaultSize}\n reload={reload}\n showDisplayUrl={showDisplayUrl}\n userId={currentUser?.id}\n />\n </Suspense>\n )\n}\n\nexport interface IframeInnerProps extends Omit<IframeOptions, 'url'> {\n url: (draftSnapshot: SanityDocument | null) => Promise<URL | Error | undefined>\n isResolvingUrl: boolean\n draftSnapshot: SanityDocument | null\n userId?: string\n expiresAt?: number\n _key?: string\n}\nconst IframeInner = memo(function IframeInner(props: IframeInnerProps) {\n const {\n isResolvingUrl,\n defaultSize = DEFAULT_SIZE,\n reload,\n attributes = {},\n showDisplayUrl = true,\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n } = props\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const url = suspend(\n () => props.url(draftSnapshot),\n [\n // Cache based on a few specific conditions\n 'sanity-plugin-iframe-pane',\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n resolveUUID,\n ],\n )\n\n const [loading, setLoading] = useState(true)\n const [_reloading, setReloading] = useState(false)\n const reloading = _reloading || isResolvingUrl\n\n const iframe = useRef<HTMLIFrameElement>(null)\n\n const handleReload = useCallback(() => {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issues\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setReloading(true)\n }, [])\n\n return (\n <MotionConfig transition={prefersReducedMotion ? {duration: 0} : undefined}>\n <Flex direction=\"column\" style={{height: '100%'}}>\n <Toolbar\n url={url}\n iframeSize={iframeSize}\n reloading={reloading}\n setIframeSize={setIframeSize}\n showUrl={showDisplayUrl}\n reloadButton={!!reload?.button}\n handleReload={handleReload}\n />\n {url instanceof Error ? (\n <ErrorCard error={url} />\n ) : (\n <Card tone=\"transparent\" style={{height: '100%'}}>\n <Frame\n ref={iframe}\n loading={loading}\n reloading={reloading}\n iframeSize={iframeSize}\n setReloading={setReloading}\n setLoading={setLoading}\n url={url}\n attributes={attributes}\n />\n </Card>\n )}\n </Flex>\n </MotionConfig>\n )\n})\n\ninterface FrameProps extends Required<Pick<IframeOptions, 'attributes'>> {\n loading: boolean\n reloading: boolean\n setLoading: (loading: boolean) => void\n setReloading: (reloading: boolean) => void\n iframeSize: IframeSizeKey\n url: URL | undefined\n}\nconst Frame = forwardRef(function Frame(\n props: FrameProps,\n iframe: React.ForwardedRef<HTMLIFrameElement>,\n) {\n const {loading, setLoading, iframeSize, attributes, reloading, url, setReloading} = props\n\n function handleIframeLoad() {\n setLoading(false)\n setReloading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" style={{height: '100%', position: 'relative'}}>\n <AnimatePresence>\n {!url ||\n (loading && (\n <MotionFlex\n initial=\"initial\"\n animate=\"animate\"\n exit=\"exit\"\n variants={spinnerVariants}\n justify=\"center\"\n align=\"center\"\n style={{inset: '0', position: 'absolute'}}\n >\n <Loading iframeSize={iframeSize} />\n </MotionFlex>\n ))}\n </AnimatePresence>\n {url && (\n <motion.iframe\n ref={iframe}\n title=\"preview\"\n frameBorder=\"0\"\n style={{maxHeight: '100%'}}\n src={url.toString()}\n initial={['background', iframeSize]}\n variants={iframeVariants}\n animate={[\n loading ? 'background' : 'active',\n reloading ? 'reloading' : 'idle',\n iframeSize,\n ]}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n )}\n </Flex>\n )\n})\n\nconst spinnerVariants = {\n initial: {opacity: 1},\n animate: {opacity: [0, 0, 1]},\n exit: {opacity: [1, 0, 0]},\n}\n\nconst iframeVariants = {\n ...sizes,\n desktop: {\n ...sizes.desktop,\n boxShadow: '0 0 0 0px var(--card-shadow-outline-color)',\n },\n mobile: {\n ...sizes.mobile,\n boxShadow: '0 0 0 1px var(--card-shadow-outline-color)',\n },\n background: {\n opacity: 0,\n scale: 1,\n },\n idle: {\n scale: 1,\n },\n reloading: {\n scale: [1, 1, 1, 0.98],\n },\n active: {\n opacity: [0, 0, 1],\n scale: 1,\n },\n}\n\nfunction Loading({iframeSize}: {iframeSize: IframeSizeKey}) {\n return (\n <Flex style={{...sizes[iframeSize]}} justify=\"center\" align=\"center\" direction=\"column\" gap={4}>\n <Spinner muted />\n <Text muted size={1}>\n Loading…\n </Text>\n </Flex>\n )\n}\n\nexport function ErrorCard({error}: {error: Error}) {\n return (\n <Card height=\"fill\">\n <Flex align=\"center\" height=\"fill\" justify=\"center\" padding={4} sizing=\"border\">\n <Container width={0}>\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Flex>\n <Box>\n <Text size={1}>\n <WarningOutlineIcon />\n </Text>\n </Box>\n <Stack flex={1} marginLeft={3} space={3}>\n <Text as=\"h1\" size={1} weight=\"bold\">\n {error.name}\n </Text>\n <Text as=\"p\" muted size={1}>\n {error.message}\n </Text>\n </Stack>\n </Flex>\n </Card>\n </Container>\n </Flex>\n </Card>\n )\n}\n\n// https://github.com/pmndrs/suspend-react?tab=readme-ov-file#making-cache-keys-unique\nconst resolveUUID = Symbol()\n"],"names":["useMemo","getRedirectTo","Text","useRef","useToast","useCopyToClipboard","jsxs","Fragment","jsx","Card","Flex","Tooltip","Button","MobileDeviceIcon","Box","RefreshIcon","CopyIcon","LaunchIcon","motion","useState","useEffect","useCurrentUser","useClient","useTransition","useCallback","draft","url","expiresAt","createPreviewSecret","definePreviewUrl","Suspense","memo","usePrefersReducedMotion","suspend","MotionConfig","forwardRef","AnimatePresence","Spinner","Container","WarningOutlineIcon","Stack"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/DisplayUrl.tsx","../src/Toolbar.tsx","../src/Iframe.tsx"],"sourcesContent":["import {getRedirectTo} from '@sanity/preview-url-secret/get-redirect-to'\nimport {Text} from '@sanity/ui'\nimport {useMemo} from 'react'\n\nexport function DisplayUrl(props: {url: URL}) {\n const truncatedUrl = useMemo(() => {\n const url = getRedirectTo(props.url)\n return `${url.origin === location.origin ? '' : url.origin}${url.pathname}${url.search}`\n }, [props.url])\n\n return (\n <Text size={0} textOverflow=\"ellipsis\">\n {truncatedUrl}\n </Text>\n )\n}\n","import {CopyIcon, LaunchIcon, MobileDeviceIcon, RefreshIcon} from '@sanity/icons'\nimport {Box, Button, Card, Flex, Text, Tooltip, useToast} from '@sanity/ui'\nimport {useCallback, useRef, useState} from 'react'\n\nimport {DisplayUrl} from './DisplayUrl'\nimport type {IframeSizeKey, SizeProps} from './types'\n\nexport const sizes: SizeProps = {\n desktop: {\n width: '100%',\n height: '100%',\n },\n mobile: {\n width: 414,\n height: 746,\n },\n}\n\nexport const DEFAULT_SIZE = 'desktop'\n\nexport interface ToolbarProps {\n url: URL | Error | undefined\n iframeSize: IframeSizeKey\n setIframeSize: (size: IframeSizeKey) => void\n showUrl: boolean\n reloading: boolean\n reloadButton: boolean\n handleReload: () => void\n}\nexport function Toolbar(props: ToolbarProps) {\n const {url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload} = props\n const validUrl = url instanceof URL\n\n const input = useRef<HTMLTextAreaElement>(null)\n const {push: pushToast} = useToast()\n const [, copy] = useCopyToClipboard()\n\n return (\n <>\n <textarea\n style={{position: 'absolute', pointerEvents: 'none', opacity: 0}}\n ref={input}\n value={validUrl ? url.toString() : ''}\n readOnly\n tabIndex={-1}\n />\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {iframeSize === 'mobile' ? 'Exit mobile preview' : 'Preview mobile viewport'}\n </Text>\n }\n padding={2}\n placement=\"bottom-start\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n padding={2}\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Tooltip>\n </Flex>\n <Box flex={1}>{showUrl && validUrl && <DisplayUrl url={url} />}</Box>\n <Flex align=\"center\" gap={1}>\n {reloadButton ? (\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {reloading ? 'Reloading…' : 'Reload'}\n </Text>\n }\n padding={2}\n >\n <Button\n disabled={!validUrl}\n mode=\"bleed\"\n fontSize={[1]}\n padding={2}\n icon={RefreshIcon}\n loading={reloading}\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n </Tooltip>\n ) : null}\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Copy URL\n </Text>\n }\n padding={2}\n >\n <Button\n mode=\"bleed\"\n disabled={!validUrl}\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n aria-label=\"Copy URL\"\n onClick={() => {\n if (!input?.current?.value) return\n\n copy(input.current.value).then((copied) => {\n if (copied) {\n pushToast({\n closable: true,\n status: 'success',\n title: 'The URL is copied to the clipboard',\n })\n } else {\n pushToast({\n closable: true,\n status: 'error',\n title: 'Failed to copy the URL to the clipboard',\n })\n }\n })\n }}\n />\n </Tooltip>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Open URL in a new tab\n </Text>\n }\n padding={2}\n placement=\"bottom-end\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n icon={LaunchIcon}\n mode=\"ghost\"\n paddingY={[2]}\n text=\"Open\"\n aria-label=\"Open URL in a new tab\"\n onClick={validUrl ? () => window.open(url.toString()) : undefined}\n />\n </Tooltip>\n </Flex>\n </Flex>\n </Card>\n </>\n )\n}\n\ntype CopiedValue = string | null\n\ntype CopyFn = (text: string) => Promise<boolean>\n\nfunction useCopyToClipboard(): [CopiedValue, CopyFn] {\n const [copiedText, setCopiedText] = useState<CopiedValue>(null)\n\n const copy: CopyFn = useCallback(async (text) => {\n if (!navigator?.clipboard) {\n console.warn('Clipboard not supported')\n return false\n }\n\n // Try to save to clipboard then save it in the state if worked\n try {\n await navigator.clipboard.writeText(text)\n setCopiedText(text)\n return true\n } catch (error) {\n console.warn('Copy failed', error)\n setCopiedText(null)\n return false\n }\n }, [])\n\n return [copiedText, copy]\n}\n","import {WarningOutlineIcon} from '@sanity/icons'\nimport {createPreviewSecret} from '@sanity/preview-url-secret/create-secret'\nimport {definePreviewUrl} from '@sanity/preview-url-secret/define-preview-url'\nimport {Box, Card, Container, Flex, Spinner, Stack, Text, usePrefersReducedMotion} from '@sanity/ui'\nimport {AnimatePresence, motion, MotionConfig} from 'framer-motion'\nimport type {HTMLAttributeReferrerPolicy} from 'react'\nimport {\n forwardRef,\n memo,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n useTransition,\n} from 'react'\nimport {type SanityDocument, useActiveWorkspace, useClient, useCurrentUser} from 'sanity'\nimport {suspend} from 'suspend-react'\n\nimport {DEFAULT_SIZE, sizes, Toolbar} from './Toolbar'\nimport type {IframeSizeKey} from './types'\n\nexport type UrlResolver = (\n document: SanityDocument | null,\n) => string | Error | undefined | Promise<string | Error | undefined>\n\nexport type {IframeSizeKey}\n\nexport interface IframeOptions {\n /**\n * If you have multiple iframe instances side-by-side you need to give each a unique key.\n */\n key?: string\n url:\n | string\n | UrlResolver\n | {\n /**\n * The URL origin of where the preview is hosted, for example `https://example.com`.\n * If it's an embedded Studio then set it to `'same-origin'`.\n */\n origin: 'same-origin' | string\n /**\n * The route to redirect to after enabling Draft Mode.\n * If you don't have enough data to build the URL, return an `Error` instance to show an error message.\n * @example `return new Error('Missing slug')`\n * To prolong the loading state, return `undefined`\n */\n preview: string | UrlResolver\n /**\n * The route that enables Draft Mode\n * @example '/api/draft'\n */\n draftMode: string\n }\n defaultSize?: IframeSizeKey\n showDisplayUrl?: boolean\n reload?: {\n button?: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nconst MotionFlex = motion.create(Flex)\n\nexport interface IframeProps {\n document: {\n displayed: SanityDocument\n draft: SanityDocument | null\n published: SanityDocument | null\n }\n options: IframeOptions\n}\n\nexport function Iframe(props: IframeProps): React.JSX.Element {\n const {document, options} = props\n const draft = document.draft || document.published || document.displayed\n\n const {defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = true, key} = options\n\n const workspace = useActiveWorkspace()\n const basePath = workspace?.activeWorkspace?.basePath || '/'\n const urlRef = useRef(options.url)\n const [draftSnapshot, setDraftSnapshot] = useState(() => ({key, draft}))\n useEffect(() => {\n urlRef.current = options.url\n }, [options.url])\n useEffect(() => {\n if (JSON.stringify({key, draft}) !== JSON.stringify(draftSnapshot)) {\n startTransition(() => setDraftSnapshot({key, draft}))\n }\n }, [draft, draftSnapshot, key])\n const currentUser = useCurrentUser()\n const client = useClient({apiVersion: '2023-10-16'})\n const [expiresAt, setExpiresAt] = useState<number | undefined>()\n const previewSecretRef = useRef<string | undefined>(undefined)\n const [isResolvingUrl, startTransition] = useTransition()\n const url = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-shadow\n async (draft: SanityDocument | null) => {\n if (typeof location === 'undefined') {\n return undefined\n }\n const urlProp = urlRef.current\n if (typeof urlProp === 'string') {\n return new URL(urlProp, location.origin)\n }\n if (typeof urlProp === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await urlProp(draft)\n return typeof url === 'string' ? new URL(url, location.origin) : url\n }\n if (typeof urlProp === 'object') {\n const preview =\n typeof urlProp.preview === 'function' ? await urlProp.preview(draft) : urlProp.preview\n if (typeof preview !== 'string') {\n return preview\n }\n if (!previewSecretRef.current) {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const {secret, expiresAt} = await createPreviewSecret(\n client,\n 'sanity-plugin-iframe-pane',\n location.href,\n currentUser?.id,\n )\n previewSecretRef.current = secret\n startTransition(() => setExpiresAt(expiresAt.getTime()))\n }\n\n const resolvePreviewUrl = definePreviewUrl({\n origin: urlProp.origin === 'same-origin' ? location.origin : urlProp.origin,\n preview,\n draftMode: {\n enable: urlProp.draftMode,\n },\n })\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await resolvePreviewUrl({\n client,\n previewUrlSecret: previewSecretRef.current,\n previewSearchParam: null,\n studioBasePath: basePath,\n studioPreviewPerspective: 'previewDrafts',\n })\n return new URL(url, location.origin)\n }\n return undefined\n },\n [client, currentUser?.id, basePath],\n )\n useEffect(() => {\n if (expiresAt) {\n const timeout = setTimeout(\n () => {\n startTransition(() => setExpiresAt(undefined))\n previewSecretRef.current = undefined\n },\n Math.max(0, expiresAt - Date.now()),\n )\n return () => clearTimeout(timeout)\n }\n return undefined\n }, [expiresAt])\n\n return (\n <Suspense fallback={<Loading iframeSize=\"desktop\" />}>\n <IframeInner\n key={draftSnapshot.key}\n _key={draftSnapshot.key}\n draftSnapshot={draftSnapshot.draft}\n url={url}\n isResolvingUrl={isResolvingUrl}\n attributes={attributes}\n defaultSize={defaultSize}\n reload={reload}\n showDisplayUrl={showDisplayUrl}\n userId={currentUser?.id}\n />\n </Suspense>\n )\n}\n\nexport interface IframeInnerProps extends Omit<IframeOptions, 'url'> {\n url: (draftSnapshot: SanityDocument | null) => Promise<URL | Error | undefined>\n isResolvingUrl: boolean\n draftSnapshot: SanityDocument | null\n userId?: string\n expiresAt?: number\n _key?: string\n}\nconst IframeInner = memo(function IframeInner(props: IframeInnerProps) {\n const {\n isResolvingUrl,\n defaultSize = DEFAULT_SIZE,\n reload,\n attributes = {},\n showDisplayUrl = true,\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n } = props\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const url = suspend(\n () => props.url(draftSnapshot),\n [\n // Cache based on a few specific conditions\n 'sanity-plugin-iframe-pane',\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n resolveUUID,\n ],\n )\n\n const [loading, setLoading] = useState(true)\n const [_reloading, setReloading] = useState(false)\n const reloading = _reloading || isResolvingUrl\n\n const iframe = useRef<HTMLIFrameElement>(null)\n\n const handleReload = useCallback(() => {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issues\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setReloading(true)\n }, [])\n\n return (\n <MotionConfig transition={prefersReducedMotion ? {duration: 0} : undefined}>\n <Flex direction=\"column\" style={{height: '100%'}}>\n <Toolbar\n url={url}\n iframeSize={iframeSize}\n reloading={reloading}\n setIframeSize={setIframeSize}\n showUrl={showDisplayUrl}\n reloadButton={!!reload?.button}\n handleReload={handleReload}\n />\n {url instanceof Error ? (\n <ErrorCard error={url} />\n ) : (\n <Card tone=\"transparent\" style={{height: '100%'}}>\n <Frame\n ref={iframe}\n loading={loading}\n reloading={reloading}\n iframeSize={iframeSize}\n setReloading={setReloading}\n setLoading={setLoading}\n url={url}\n attributes={attributes}\n />\n </Card>\n )}\n </Flex>\n </MotionConfig>\n )\n})\n\ninterface FrameProps extends Required<Pick<IframeOptions, 'attributes'>> {\n loading: boolean\n reloading: boolean\n setLoading: (loading: boolean) => void\n setReloading: (reloading: boolean) => void\n iframeSize: IframeSizeKey\n url: URL | undefined\n}\nconst Frame = forwardRef(function Frame(\n props: FrameProps,\n iframe: React.ForwardedRef<HTMLIFrameElement>,\n) {\n const {loading, setLoading, iframeSize, attributes, reloading, url, setReloading} = props\n\n function handleIframeLoad() {\n setLoading(false)\n setReloading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" style={{height: '100%', position: 'relative'}}>\n <AnimatePresence>\n {!url ||\n (loading && (\n <MotionFlex\n initial=\"initial\"\n animate=\"animate\"\n exit=\"exit\"\n variants={spinnerVariants}\n justify=\"center\"\n align=\"center\"\n style={{inset: '0', position: 'absolute'}}\n >\n <Loading iframeSize={iframeSize} />\n </MotionFlex>\n ))}\n </AnimatePresence>\n {url && (\n <motion.iframe\n ref={iframe}\n title=\"preview\"\n frameBorder=\"0\"\n style={{maxHeight: '100%'}}\n src={url.toString()}\n initial={['background', iframeSize]}\n variants={iframeVariants}\n animate={[\n loading ? 'background' : 'active',\n reloading ? 'reloading' : 'idle',\n iframeSize,\n ]}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n )}\n </Flex>\n )\n})\n\nconst spinnerVariants = {\n initial: {opacity: 1},\n animate: {opacity: [0, 0, 1]},\n exit: {opacity: [1, 0, 0]},\n}\n\nconst iframeVariants = {\n ...sizes,\n desktop: {\n ...sizes.desktop,\n boxShadow: '0 0 0 0px var(--card-shadow-outline-color)',\n },\n mobile: {\n ...sizes.mobile,\n boxShadow: '0 0 0 1px var(--card-shadow-outline-color)',\n },\n background: {\n opacity: 0,\n scale: 1,\n },\n idle: {\n scale: 1,\n },\n reloading: {\n scale: [1, 1, 1, 0.98],\n },\n active: {\n opacity: [0, 0, 1],\n scale: 1,\n },\n}\n\nfunction Loading({iframeSize}: {iframeSize: IframeSizeKey}) {\n return (\n <Flex style={{...sizes[iframeSize]}} justify=\"center\" align=\"center\" direction=\"column\" gap={4}>\n <Spinner muted />\n <Text muted size={1}>\n Loading…\n </Text>\n </Flex>\n )\n}\n\nexport function ErrorCard({error}: {error: Error}) {\n return (\n <Card height=\"fill\">\n <Flex align=\"center\" height=\"fill\" justify=\"center\" padding={4} sizing=\"border\">\n <Container width={0}>\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Flex>\n <Box>\n <Text size={1}>\n <WarningOutlineIcon />\n </Text>\n </Box>\n <Stack flex={1} marginLeft={3} space={3}>\n <Text as=\"h1\" size={1} weight=\"bold\">\n {error.name}\n </Text>\n <Text as=\"p\" muted size={1}>\n {error.message}\n </Text>\n </Stack>\n </Flex>\n </Card>\n </Container>\n </Flex>\n </Card>\n )\n}\n\n// https://github.com/pmndrs/suspend-react?tab=readme-ov-file#making-cache-keys-unique\nconst resolveUUID = Symbol()\n"],"names":["useMemo","getRedirectTo","Text","useRef","useToast","jsxs","Fragment","jsx","Card","Flex","Tooltip","Button","MobileDeviceIcon","Box","RefreshIcon","CopyIcon","LaunchIcon","useState","useCallback","motion","useActiveWorkspace","useEffect","useCurrentUser","useClient","useTransition","draft","url","expiresAt","createPreviewSecret","definePreviewUrl","Suspense","memo","usePrefersReducedMotion","suspend","MotionConfig","forwardRef","AnimatePresence","Spinner","Container","WarningOutlineIcon","Stack"],"mappings":"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"}
|
package/lib/index.d.cts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type {HTMLAttributeReferrerPolicy} from 'react'
|
|
2
|
+
import {SanityDocument} from 'sanity'
|
|
3
|
+
|
|
4
|
+
export declare function Iframe(props: IframeProps): React.JSX.Element
|
|
5
|
+
|
|
6
|
+
export declare interface IframeOptions {
|
|
7
|
+
/**
|
|
8
|
+
* If you have multiple iframe instances side-by-side you need to give each a unique key.
|
|
9
|
+
*/
|
|
10
|
+
key?: string
|
|
11
|
+
url:
|
|
12
|
+
| string
|
|
13
|
+
| UrlResolver
|
|
14
|
+
| {
|
|
15
|
+
/**
|
|
16
|
+
* The URL origin of where the preview is hosted, for example `https://example.com`.
|
|
17
|
+
* If it's an embedded Studio then set it to `'same-origin'`.
|
|
18
|
+
*/
|
|
19
|
+
origin: 'same-origin' | string
|
|
20
|
+
/**
|
|
21
|
+
* The route to redirect to after enabling Draft Mode.
|
|
22
|
+
* If you don't have enough data to build the URL, return an `Error` instance to show an error message.
|
|
23
|
+
* @example `return new Error('Missing slug')`
|
|
24
|
+
* To prolong the loading state, return `undefined`
|
|
25
|
+
*/
|
|
26
|
+
preview: string | UrlResolver
|
|
27
|
+
/**
|
|
28
|
+
* The route that enables Draft Mode
|
|
29
|
+
* @example '/api/draft'
|
|
30
|
+
*/
|
|
31
|
+
draftMode: string
|
|
32
|
+
}
|
|
33
|
+
defaultSize?: IframeSizeKey
|
|
34
|
+
showDisplayUrl?: boolean
|
|
35
|
+
reload?: {
|
|
36
|
+
button?: boolean
|
|
37
|
+
}
|
|
38
|
+
attributes?: Partial<{
|
|
39
|
+
allow: string
|
|
40
|
+
referrerPolicy: HTMLAttributeReferrerPolicy | undefined
|
|
41
|
+
sandbox: string
|
|
42
|
+
onLoad: () => void
|
|
43
|
+
}>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export declare interface IframeProps {
|
|
47
|
+
document: {
|
|
48
|
+
displayed: SanityDocument
|
|
49
|
+
draft: SanityDocument | null
|
|
50
|
+
published: SanityDocument | null
|
|
51
|
+
}
|
|
52
|
+
options: IframeOptions
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export declare type IframeSizeKey = keyof SizeProps
|
|
56
|
+
|
|
57
|
+
export declare type Size = 'desktop' | 'mobile'
|
|
58
|
+
|
|
59
|
+
export declare type SizeProps = {
|
|
60
|
+
[key in Size]: {
|
|
61
|
+
width: string | number
|
|
62
|
+
height: string | number
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export declare type UrlResolver = (
|
|
67
|
+
document: SanityDocument | null,
|
|
68
|
+
) => string | Error | undefined | Promise<string | Error | undefined>
|
|
69
|
+
|
|
70
|
+
export {}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type {HTMLAttributeReferrerPolicy} from 'react'
|
|
2
|
-
import {JSX as JSX_2} from 'react/jsx-runtime'
|
|
3
2
|
import {SanityDocument} from 'sanity'
|
|
4
3
|
|
|
5
|
-
export declare function Iframe(props: IframeProps):
|
|
4
|
+
export declare function Iframe(props: IframeProps): React.JSX.Element
|
|
6
5
|
|
|
7
6
|
export declare interface IframeOptions {
|
|
8
7
|
/**
|
package/lib/index.js
CHANGED
|
@@ -4,10 +4,9 @@ import { createPreviewSecret } from "@sanity/preview-url-secret/create-secret";
|
|
|
4
4
|
import { definePreviewUrl } from "@sanity/preview-url-secret/define-preview-url";
|
|
5
5
|
import { Text, useToast, Card, Flex, Tooltip, Button, Box, usePrefersReducedMotion, Spinner, Container, Stack } from "@sanity/ui";
|
|
6
6
|
import { motion, MotionConfig, AnimatePresence } from "framer-motion";
|
|
7
|
-
import { useMemo, useRef,
|
|
8
|
-
import { useCurrentUser, useClient } from "sanity";
|
|
7
|
+
import { useMemo, useRef, useState, useCallback, memo, forwardRef, useEffect, useTransition, Suspense } from "react";
|
|
8
|
+
import { useActiveWorkspace, useCurrentUser, useClient } from "sanity";
|
|
9
9
|
import { suspend } from "suspend-react";
|
|
10
|
-
import { useCopyToClipboard } from "usehooks-ts";
|
|
11
10
|
import { getRedirectTo } from "@sanity/preview-url-secret/get-redirect-to";
|
|
12
11
|
function DisplayUrl(props) {
|
|
13
12
|
const truncatedUrl = useMemo(() => {
|
|
@@ -99,8 +98,7 @@ function Toolbar(props) {
|
|
|
99
98
|
padding: [2],
|
|
100
99
|
"aria-label": "Copy URL",
|
|
101
100
|
onClick: () => {
|
|
102
|
-
|
|
103
|
-
(_a = input == null ? void 0 : input.current) != null && _a.value && copy(input.current.value).then((copied) => {
|
|
101
|
+
input?.current?.value && copy(input.current.value).then((copied) => {
|
|
104
102
|
pushToast(copied ? {
|
|
105
103
|
closable: !0,
|
|
106
104
|
status: "success",
|
|
@@ -142,15 +140,27 @@ function Toolbar(props) {
|
|
|
142
140
|
] }) })
|
|
143
141
|
] });
|
|
144
142
|
}
|
|
145
|
-
|
|
143
|
+
function useCopyToClipboard() {
|
|
144
|
+
const [copiedText, setCopiedText] = useState(null), copy = useCallback(async (text) => {
|
|
145
|
+
if (!navigator?.clipboard)
|
|
146
|
+
return console.warn("Clipboard not supported"), !1;
|
|
147
|
+
try {
|
|
148
|
+
return await navigator.clipboard.writeText(text), setCopiedText(text), !0;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return console.warn("Copy failed", error), setCopiedText(null), !1;
|
|
151
|
+
}
|
|
152
|
+
}, []);
|
|
153
|
+
return [copiedText, copy];
|
|
154
|
+
}
|
|
155
|
+
const MotionFlex = motion.create(Flex);
|
|
146
156
|
function Iframe(props) {
|
|
147
|
-
const { document, options } = props, draft = document.draft || document.published || document.displayed, { defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = !0, key } = options, urlRef = useRef(options.url), [draftSnapshot, setDraftSnapshot] = useState(() => ({ key, draft }));
|
|
157
|
+
const { document, options } = props, draft = document.draft || document.published || document.displayed, { defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = !0, key } = options, basePath = useActiveWorkspace()?.activeWorkspace?.basePath || "/", urlRef = useRef(options.url), [draftSnapshot, setDraftSnapshot] = useState(() => ({ key, draft }));
|
|
148
158
|
useEffect(() => {
|
|
149
159
|
urlRef.current = options.url;
|
|
150
160
|
}, [options.url]), useEffect(() => {
|
|
151
161
|
JSON.stringify({ key, draft }) !== JSON.stringify(draftSnapshot) && startTransition(() => setDraftSnapshot({ key, draft }));
|
|
152
162
|
}, [draft, draftSnapshot, key]);
|
|
153
|
-
const currentUser = useCurrentUser(), client = useClient({ apiVersion: "2023-10-16" }), [expiresAt, setExpiresAt] = useState(), previewSecretRef = useRef(), [isResolvingUrl, startTransition] = useTransition(), url = useCallback(
|
|
163
|
+
const currentUser = useCurrentUser(), client = useClient({ apiVersion: "2023-10-16" }), [expiresAt, setExpiresAt] = useState(), previewSecretRef = useRef(void 0), [isResolvingUrl, startTransition] = useTransition(), url = useCallback(
|
|
154
164
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
155
165
|
async (draft2) => {
|
|
156
166
|
if (typeof location > "u")
|
|
@@ -171,7 +181,7 @@ function Iframe(props) {
|
|
|
171
181
|
client,
|
|
172
182
|
"sanity-plugin-iframe-pane",
|
|
173
183
|
location.href,
|
|
174
|
-
currentUser
|
|
184
|
+
currentUser?.id
|
|
175
185
|
);
|
|
176
186
|
previewSecretRef.current = secret, startTransition(() => setExpiresAt(expiresAt2.getTime()));
|
|
177
187
|
}
|
|
@@ -184,12 +194,14 @@ function Iframe(props) {
|
|
|
184
194
|
})({
|
|
185
195
|
client,
|
|
186
196
|
previewUrlSecret: previewSecretRef.current,
|
|
187
|
-
previewSearchParam: null
|
|
197
|
+
previewSearchParam: null,
|
|
198
|
+
studioBasePath: basePath,
|
|
199
|
+
studioPreviewPerspective: "previewDrafts"
|
|
188
200
|
});
|
|
189
201
|
return new URL(url2, location.origin);
|
|
190
202
|
}
|
|
191
203
|
},
|
|
192
|
-
[client, currentUser
|
|
204
|
+
[client, currentUser?.id, basePath]
|
|
193
205
|
);
|
|
194
206
|
return useEffect(() => {
|
|
195
207
|
if (expiresAt) {
|
|
@@ -212,13 +224,12 @@ function Iframe(props) {
|
|
|
212
224
|
defaultSize,
|
|
213
225
|
reload,
|
|
214
226
|
showDisplayUrl,
|
|
215
|
-
userId: currentUser
|
|
227
|
+
userId: currentUser?.id
|
|
216
228
|
},
|
|
217
229
|
draftSnapshot.key
|
|
218
230
|
) });
|
|
219
231
|
}
|
|
220
232
|
const IframeInner = memo(function(props) {
|
|
221
|
-
var _a;
|
|
222
233
|
const {
|
|
223
234
|
isResolvingUrl,
|
|
224
235
|
defaultSize = DEFAULT_SIZE,
|
|
@@ -229,7 +240,7 @@ const IframeInner = memo(function(props) {
|
|
|
229
240
|
userId,
|
|
230
241
|
expiresAt,
|
|
231
242
|
_key
|
|
232
|
-
} = props, [iframeSize, setIframeSize] = useState(
|
|
243
|
+
} = props, [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE), prefersReducedMotion = usePrefersReducedMotion(), url = suspend(
|
|
233
244
|
() => props.url(draftSnapshot),
|
|
234
245
|
[
|
|
235
246
|
// Cache based on a few specific conditions
|
|
@@ -241,7 +252,7 @@ const IframeInner = memo(function(props) {
|
|
|
241
252
|
resolveUUID
|
|
242
253
|
]
|
|
243
254
|
), [loading, setLoading] = useState(!0), [_reloading, setReloading] = useState(!1), reloading = _reloading || isResolvingUrl, iframe = useRef(null), handleReload = useCallback(() => {
|
|
244
|
-
iframe
|
|
255
|
+
iframe?.current && (iframe.current.src = iframe.current.src, setReloading(!0));
|
|
245
256
|
}, []);
|
|
246
257
|
return /* @__PURE__ */ jsx(MotionConfig, { transition: prefersReducedMotion ? { duration: 0 } : void 0, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
|
|
247
258
|
/* @__PURE__ */ jsx(
|
|
@@ -252,7 +263,7 @@ const IframeInner = memo(function(props) {
|
|
|
252
263
|
reloading,
|
|
253
264
|
setIframeSize,
|
|
254
265
|
showUrl: showDisplayUrl,
|
|
255
|
-
reloadButton: !!
|
|
266
|
+
reloadButton: !!reload?.button,
|
|
256
267
|
handleReload
|
|
257
268
|
}
|
|
258
269
|
),
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/DisplayUrl.tsx","../src/Toolbar.tsx","../src/Iframe.tsx"],"sourcesContent":["import {getRedirectTo} from '@sanity/preview-url-secret/get-redirect-to'\nimport {Text} from '@sanity/ui'\nimport {useMemo} from 'react'\n\nexport function DisplayUrl(props: {url: URL}) {\n const truncatedUrl = useMemo(() => {\n const url = getRedirectTo(props.url)\n return `${url.origin === location.origin ? '' : url.origin}${url.pathname}${url.search}`\n }, [props.url])\n\n return (\n <Text size={0} textOverflow=\"ellipsis\">\n {truncatedUrl}\n </Text>\n )\n}\n","import {CopyIcon, LaunchIcon, MobileDeviceIcon, RefreshIcon} from '@sanity/icons'\nimport {Box, Button, Card, Flex, Text, Tooltip, useToast} from '@sanity/ui'\nimport {useRef} from 'react'\nimport {useCopyToClipboard} from 'usehooks-ts'\n\nimport {DisplayUrl} from './DisplayUrl'\nimport type {IframeSizeKey, SizeProps} from './types'\n\nexport const sizes: SizeProps = {\n desktop: {\n width: '100%',\n height: '100%',\n },\n mobile: {\n width: 414,\n height: 746,\n },\n}\n\nexport const DEFAULT_SIZE = 'desktop'\n\nexport interface ToolbarProps {\n url: URL | Error | undefined\n iframeSize: IframeSizeKey\n setIframeSize: (size: IframeSizeKey) => void\n showUrl: boolean\n reloading: boolean\n reloadButton: boolean\n handleReload: () => void\n}\nexport function Toolbar(props: ToolbarProps) {\n const {url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload} = props\n const validUrl = url instanceof URL\n\n const input = useRef<HTMLTextAreaElement>(null)\n const {push: pushToast} = useToast()\n const [, copy] = useCopyToClipboard()\n\n return (\n <>\n <textarea\n style={{position: 'absolute', pointerEvents: 'none', opacity: 0}}\n ref={input}\n value={validUrl ? url.toString() : ''}\n readOnly\n tabIndex={-1}\n />\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {iframeSize === 'mobile' ? 'Exit mobile preview' : 'Preview mobile viewport'}\n </Text>\n }\n padding={2}\n placement=\"bottom-start\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n padding={2}\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Tooltip>\n </Flex>\n <Box flex={1}>{showUrl && validUrl && <DisplayUrl url={url} />}</Box>\n <Flex align=\"center\" gap={1}>\n {reloadButton ? (\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {reloading ? 'Reloading…' : 'Reload'}\n </Text>\n }\n padding={2}\n >\n <Button\n disabled={!validUrl}\n mode=\"bleed\"\n fontSize={[1]}\n padding={2}\n icon={RefreshIcon}\n loading={reloading}\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n </Tooltip>\n ) : null}\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Copy URL\n </Text>\n }\n padding={2}\n >\n <Button\n mode=\"bleed\"\n disabled={!validUrl}\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n aria-label=\"Copy URL\"\n onClick={() => {\n if (!input?.current?.value) return\n\n copy(input.current.value).then((copied) => {\n if (copied) {\n pushToast({\n closable: true,\n status: 'success',\n title: 'The URL is copied to the clipboard',\n })\n } else {\n pushToast({\n closable: true,\n status: 'error',\n title: 'Failed to copy the URL to the clipboard',\n })\n }\n })\n }}\n />\n </Tooltip>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Open URL in a new tab\n </Text>\n }\n padding={2}\n placement=\"bottom-end\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n icon={LaunchIcon}\n mode=\"ghost\"\n paddingY={[2]}\n text=\"Open\"\n aria-label=\"Open URL in a new tab\"\n onClick={validUrl ? () => window.open(url.toString()) : undefined}\n />\n </Tooltip>\n </Flex>\n </Flex>\n </Card>\n </>\n )\n}\n","import {WarningOutlineIcon} from '@sanity/icons'\nimport {createPreviewSecret} from '@sanity/preview-url-secret/create-secret'\nimport {definePreviewUrl} from '@sanity/preview-url-secret/define-preview-url'\nimport {Box, Card, Container, Flex, Spinner, Stack, Text, usePrefersReducedMotion} from '@sanity/ui'\nimport {AnimatePresence, motion, MotionConfig} from 'framer-motion'\nimport type {HTMLAttributeReferrerPolicy} from 'react'\nimport {\n forwardRef,\n memo,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n useTransition,\n} from 'react'\nimport {type SanityDocument, useClient, useCurrentUser} from 'sanity'\nimport {suspend} from 'suspend-react'\n\nimport {DEFAULT_SIZE, sizes, Toolbar} from './Toolbar'\nimport type {IframeSizeKey} from './types'\n\nexport type UrlResolver = (\n document: SanityDocument | null,\n) => string | Error | undefined | Promise<string | Error | undefined>\n\nexport type {IframeSizeKey}\n\nexport interface IframeOptions {\n /**\n * If you have multiple iframe instances side-by-side you need to give each a unique key.\n */\n key?: string\n url:\n | string\n | UrlResolver\n | {\n /**\n * The URL origin of where the preview is hosted, for example `https://example.com`.\n * If it's an embedded Studio then set it to `'same-origin'`.\n */\n origin: 'same-origin' | string\n /**\n * The route to redirect to after enabling Draft Mode.\n * If you don't have enough data to build the URL, return an `Error` instance to show an error message.\n * @example `return new Error('Missing slug')`\n * To prolong the loading state, return `undefined`\n */\n preview: string | UrlResolver\n /**\n * The route that enables Draft Mode\n * @example '/api/draft'\n */\n draftMode: string\n }\n defaultSize?: IframeSizeKey\n showDisplayUrl?: boolean\n reload?: {\n button?: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nconst MotionFlex = motion(Flex)\n\nexport interface IframeProps {\n document: {\n displayed: SanityDocument\n draft: SanityDocument | null\n published: SanityDocument | null\n }\n options: IframeOptions\n}\n\nexport function Iframe(props: IframeProps) {\n const {document, options} = props\n const draft = document.draft || document.published || document.displayed\n\n const {defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = true, key} = options\n\n const urlRef = useRef(options.url)\n const [draftSnapshot, setDraftSnapshot] = useState(() => ({key, draft}))\n useEffect(() => {\n urlRef.current = options.url\n }, [options.url])\n useEffect(() => {\n if (JSON.stringify({key, draft}) !== JSON.stringify(draftSnapshot)) {\n startTransition(() => setDraftSnapshot({key, draft}))\n }\n }, [draft, draftSnapshot, key])\n const currentUser = useCurrentUser()\n const client = useClient({apiVersion: '2023-10-16'})\n const [expiresAt, setExpiresAt] = useState<number | undefined>()\n const previewSecretRef = useRef<string | undefined>()\n const [isResolvingUrl, startTransition] = useTransition()\n const url = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-shadow\n async (draft: SanityDocument | null) => {\n if (typeof location === 'undefined') {\n return undefined\n }\n const urlProp = urlRef.current\n if (typeof urlProp === 'string') {\n return new URL(urlProp, location.origin)\n }\n if (typeof urlProp === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await urlProp(draft)\n return typeof url === 'string' ? new URL(url, location.origin) : url\n }\n if (typeof urlProp === 'object') {\n const preview =\n typeof urlProp.preview === 'function' ? await urlProp.preview(draft) : urlProp.preview\n if (typeof preview !== 'string') {\n return preview\n }\n if (!previewSecretRef.current) {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const {secret, expiresAt} = await createPreviewSecret(\n client,\n 'sanity-plugin-iframe-pane',\n location.href,\n currentUser?.id,\n )\n previewSecretRef.current = secret\n startTransition(() => setExpiresAt(expiresAt.getTime()))\n }\n\n const resolvePreviewUrl = definePreviewUrl({\n origin: urlProp.origin === 'same-origin' ? location.origin : urlProp.origin,\n preview,\n draftMode: {\n enable: urlProp.draftMode,\n },\n })\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await resolvePreviewUrl({\n client,\n previewUrlSecret: previewSecretRef.current,\n previewSearchParam: null,\n })\n return new URL(url, location.origin)\n }\n return undefined\n },\n [client, currentUser?.id],\n )\n useEffect(() => {\n if (expiresAt) {\n const timeout = setTimeout(\n () => {\n startTransition(() => setExpiresAt(undefined))\n previewSecretRef.current = undefined\n },\n Math.max(0, expiresAt - Date.now()),\n )\n return () => clearTimeout(timeout)\n }\n return undefined\n }, [expiresAt])\n\n return (\n <Suspense fallback={<Loading iframeSize=\"desktop\" />}>\n <IframeInner\n key={draftSnapshot.key}\n _key={draftSnapshot.key}\n draftSnapshot={draftSnapshot.draft}\n url={url}\n isResolvingUrl={isResolvingUrl}\n attributes={attributes}\n defaultSize={defaultSize}\n reload={reload}\n showDisplayUrl={showDisplayUrl}\n userId={currentUser?.id}\n />\n </Suspense>\n )\n}\n\nexport interface IframeInnerProps extends Omit<IframeOptions, 'url'> {\n url: (draftSnapshot: SanityDocument | null) => Promise<URL | Error | undefined>\n isResolvingUrl: boolean\n draftSnapshot: SanityDocument | null\n userId?: string\n expiresAt?: number\n _key?: string\n}\nconst IframeInner = memo(function IframeInner(props: IframeInnerProps) {\n const {\n isResolvingUrl,\n defaultSize = DEFAULT_SIZE,\n reload,\n attributes = {},\n showDisplayUrl = true,\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n } = props\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const url = suspend(\n () => props.url(draftSnapshot),\n [\n // Cache based on a few specific conditions\n 'sanity-plugin-iframe-pane',\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n resolveUUID,\n ],\n )\n\n const [loading, setLoading] = useState(true)\n const [_reloading, setReloading] = useState(false)\n const reloading = _reloading || isResolvingUrl\n\n const iframe = useRef<HTMLIFrameElement>(null)\n\n const handleReload = useCallback(() => {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issues\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setReloading(true)\n }, [])\n\n return (\n <MotionConfig transition={prefersReducedMotion ? {duration: 0} : undefined}>\n <Flex direction=\"column\" style={{height: '100%'}}>\n <Toolbar\n url={url}\n iframeSize={iframeSize}\n reloading={reloading}\n setIframeSize={setIframeSize}\n showUrl={showDisplayUrl}\n reloadButton={!!reload?.button}\n handleReload={handleReload}\n />\n {url instanceof Error ? (\n <ErrorCard error={url} />\n ) : (\n <Card tone=\"transparent\" style={{height: '100%'}}>\n <Frame\n ref={iframe}\n loading={loading}\n reloading={reloading}\n iframeSize={iframeSize}\n setReloading={setReloading}\n setLoading={setLoading}\n url={url}\n attributes={attributes}\n />\n </Card>\n )}\n </Flex>\n </MotionConfig>\n )\n})\n\ninterface FrameProps extends Required<Pick<IframeOptions, 'attributes'>> {\n loading: boolean\n reloading: boolean\n setLoading: (loading: boolean) => void\n setReloading: (reloading: boolean) => void\n iframeSize: IframeSizeKey\n url: URL | undefined\n}\nconst Frame = forwardRef(function Frame(\n props: FrameProps,\n iframe: React.ForwardedRef<HTMLIFrameElement>,\n) {\n const {loading, setLoading, iframeSize, attributes, reloading, url, setReloading} = props\n\n function handleIframeLoad() {\n setLoading(false)\n setReloading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" style={{height: '100%', position: 'relative'}}>\n <AnimatePresence>\n {!url ||\n (loading && (\n <MotionFlex\n initial=\"initial\"\n animate=\"animate\"\n exit=\"exit\"\n variants={spinnerVariants}\n justify=\"center\"\n align=\"center\"\n style={{inset: '0', position: 'absolute'}}\n >\n <Loading iframeSize={iframeSize} />\n </MotionFlex>\n ))}\n </AnimatePresence>\n {url && (\n <motion.iframe\n ref={iframe}\n title=\"preview\"\n frameBorder=\"0\"\n style={{maxHeight: '100%'}}\n src={url.toString()}\n initial={['background', iframeSize]}\n variants={iframeVariants}\n animate={[\n loading ? 'background' : 'active',\n reloading ? 'reloading' : 'idle',\n iframeSize,\n ]}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n )}\n </Flex>\n )\n})\n\nconst spinnerVariants = {\n initial: {opacity: 1},\n animate: {opacity: [0, 0, 1]},\n exit: {opacity: [1, 0, 0]},\n}\n\nconst iframeVariants = {\n ...sizes,\n desktop: {\n ...sizes.desktop,\n boxShadow: '0 0 0 0px var(--card-shadow-outline-color)',\n },\n mobile: {\n ...sizes.mobile,\n boxShadow: '0 0 0 1px var(--card-shadow-outline-color)',\n },\n background: {\n opacity: 0,\n scale: 1,\n },\n idle: {\n scale: 1,\n },\n reloading: {\n scale: [1, 1, 1, 0.98],\n },\n active: {\n opacity: [0, 0, 1],\n scale: 1,\n },\n}\n\nfunction Loading({iframeSize}: {iframeSize: IframeSizeKey}) {\n return (\n <Flex style={{...sizes[iframeSize]}} justify=\"center\" align=\"center\" direction=\"column\" gap={4}>\n <Spinner muted />\n <Text muted size={1}>\n Loading…\n </Text>\n </Flex>\n )\n}\n\nexport function ErrorCard({error}: {error: Error}) {\n return (\n <Card height=\"fill\">\n <Flex align=\"center\" height=\"fill\" justify=\"center\" padding={4} sizing=\"border\">\n <Container width={0}>\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Flex>\n <Box>\n <Text size={1}>\n <WarningOutlineIcon />\n </Text>\n </Box>\n <Stack flex={1} marginLeft={3} space={3}>\n <Text as=\"h1\" size={1} weight=\"bold\">\n {error.name}\n </Text>\n <Text as=\"p\" muted size={1}>\n {error.message}\n </Text>\n </Stack>\n </Flex>\n </Card>\n </Container>\n </Flex>\n </Card>\n )\n}\n\n// https://github.com/pmndrs/suspend-react?tab=readme-ov-file#making-cache-keys-unique\nconst resolveUUID = Symbol()\n"],"names":["draft","url","expiresAt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/DisplayUrl.tsx","../src/Toolbar.tsx","../src/Iframe.tsx"],"sourcesContent":["import {getRedirectTo} from '@sanity/preview-url-secret/get-redirect-to'\nimport {Text} from '@sanity/ui'\nimport {useMemo} from 'react'\n\nexport function DisplayUrl(props: {url: URL}) {\n const truncatedUrl = useMemo(() => {\n const url = getRedirectTo(props.url)\n return `${url.origin === location.origin ? '' : url.origin}${url.pathname}${url.search}`\n }, [props.url])\n\n return (\n <Text size={0} textOverflow=\"ellipsis\">\n {truncatedUrl}\n </Text>\n )\n}\n","import {CopyIcon, LaunchIcon, MobileDeviceIcon, RefreshIcon} from '@sanity/icons'\nimport {Box, Button, Card, Flex, Text, Tooltip, useToast} from '@sanity/ui'\nimport {useCallback, useRef, useState} from 'react'\n\nimport {DisplayUrl} from './DisplayUrl'\nimport type {IframeSizeKey, SizeProps} from './types'\n\nexport const sizes: SizeProps = {\n desktop: {\n width: '100%',\n height: '100%',\n },\n mobile: {\n width: 414,\n height: 746,\n },\n}\n\nexport const DEFAULT_SIZE = 'desktop'\n\nexport interface ToolbarProps {\n url: URL | Error | undefined\n iframeSize: IframeSizeKey\n setIframeSize: (size: IframeSizeKey) => void\n showUrl: boolean\n reloading: boolean\n reloadButton: boolean\n handleReload: () => void\n}\nexport function Toolbar(props: ToolbarProps) {\n const {url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload} = props\n const validUrl = url instanceof URL\n\n const input = useRef<HTMLTextAreaElement>(null)\n const {push: pushToast} = useToast()\n const [, copy] = useCopyToClipboard()\n\n return (\n <>\n <textarea\n style={{position: 'absolute', pointerEvents: 'none', opacity: 0}}\n ref={input}\n value={validUrl ? url.toString() : ''}\n readOnly\n tabIndex={-1}\n />\n <Card padding={2} borderBottom>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {iframeSize === 'mobile' ? 'Exit mobile preview' : 'Preview mobile viewport'}\n </Text>\n }\n padding={2}\n placement=\"bottom-start\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n padding={2}\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Tooltip>\n </Flex>\n <Box flex={1}>{showUrl && validUrl && <DisplayUrl url={url} />}</Box>\n <Flex align=\"center\" gap={1}>\n {reloadButton ? (\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n {reloading ? 'Reloading…' : 'Reload'}\n </Text>\n }\n padding={2}\n >\n <Button\n disabled={!validUrl}\n mode=\"bleed\"\n fontSize={[1]}\n padding={2}\n icon={RefreshIcon}\n loading={reloading}\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n </Tooltip>\n ) : null}\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Copy URL\n </Text>\n }\n padding={2}\n >\n <Button\n mode=\"bleed\"\n disabled={!validUrl}\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n aria-label=\"Copy URL\"\n onClick={() => {\n if (!input?.current?.value) return\n\n copy(input.current.value).then((copied) => {\n if (copied) {\n pushToast({\n closable: true,\n status: 'success',\n title: 'The URL is copied to the clipboard',\n })\n } else {\n pushToast({\n closable: true,\n status: 'error',\n title: 'Failed to copy the URL to the clipboard',\n })\n }\n })\n }}\n />\n </Tooltip>\n <Tooltip\n animate\n content={\n <Text size={1} style={{whiteSpace: 'nowrap'}}>\n Open URL in a new tab\n </Text>\n }\n padding={2}\n placement=\"bottom-end\"\n >\n <Button\n disabled={!validUrl}\n fontSize={[1]}\n icon={LaunchIcon}\n mode=\"ghost\"\n paddingY={[2]}\n text=\"Open\"\n aria-label=\"Open URL in a new tab\"\n onClick={validUrl ? () => window.open(url.toString()) : undefined}\n />\n </Tooltip>\n </Flex>\n </Flex>\n </Card>\n </>\n )\n}\n\ntype CopiedValue = string | null\n\ntype CopyFn = (text: string) => Promise<boolean>\n\nfunction useCopyToClipboard(): [CopiedValue, CopyFn] {\n const [copiedText, setCopiedText] = useState<CopiedValue>(null)\n\n const copy: CopyFn = useCallback(async (text) => {\n if (!navigator?.clipboard) {\n console.warn('Clipboard not supported')\n return false\n }\n\n // Try to save to clipboard then save it in the state if worked\n try {\n await navigator.clipboard.writeText(text)\n setCopiedText(text)\n return true\n } catch (error) {\n console.warn('Copy failed', error)\n setCopiedText(null)\n return false\n }\n }, [])\n\n return [copiedText, copy]\n}\n","import {WarningOutlineIcon} from '@sanity/icons'\nimport {createPreviewSecret} from '@sanity/preview-url-secret/create-secret'\nimport {definePreviewUrl} from '@sanity/preview-url-secret/define-preview-url'\nimport {Box, Card, Container, Flex, Spinner, Stack, Text, usePrefersReducedMotion} from '@sanity/ui'\nimport {AnimatePresence, motion, MotionConfig} from 'framer-motion'\nimport type {HTMLAttributeReferrerPolicy} from 'react'\nimport {\n forwardRef,\n memo,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n useTransition,\n} from 'react'\nimport {type SanityDocument, useActiveWorkspace, useClient, useCurrentUser} from 'sanity'\nimport {suspend} from 'suspend-react'\n\nimport {DEFAULT_SIZE, sizes, Toolbar} from './Toolbar'\nimport type {IframeSizeKey} from './types'\n\nexport type UrlResolver = (\n document: SanityDocument | null,\n) => string | Error | undefined | Promise<string | Error | undefined>\n\nexport type {IframeSizeKey}\n\nexport interface IframeOptions {\n /**\n * If you have multiple iframe instances side-by-side you need to give each a unique key.\n */\n key?: string\n url:\n | string\n | UrlResolver\n | {\n /**\n * The URL origin of where the preview is hosted, for example `https://example.com`.\n * If it's an embedded Studio then set it to `'same-origin'`.\n */\n origin: 'same-origin' | string\n /**\n * The route to redirect to after enabling Draft Mode.\n * If you don't have enough data to build the URL, return an `Error` instance to show an error message.\n * @example `return new Error('Missing slug')`\n * To prolong the loading state, return `undefined`\n */\n preview: string | UrlResolver\n /**\n * The route that enables Draft Mode\n * @example '/api/draft'\n */\n draftMode: string\n }\n defaultSize?: IframeSizeKey\n showDisplayUrl?: boolean\n reload?: {\n button?: boolean\n }\n attributes?: Partial<{\n allow: string\n referrerPolicy: HTMLAttributeReferrerPolicy | undefined\n sandbox: string\n onLoad: () => void\n }>\n}\n\nconst MotionFlex = motion.create(Flex)\n\nexport interface IframeProps {\n document: {\n displayed: SanityDocument\n draft: SanityDocument | null\n published: SanityDocument | null\n }\n options: IframeOptions\n}\n\nexport function Iframe(props: IframeProps): React.JSX.Element {\n const {document, options} = props\n const draft = document.draft || document.published || document.displayed\n\n const {defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = true, key} = options\n\n const workspace = useActiveWorkspace()\n const basePath = workspace?.activeWorkspace?.basePath || '/'\n const urlRef = useRef(options.url)\n const [draftSnapshot, setDraftSnapshot] = useState(() => ({key, draft}))\n useEffect(() => {\n urlRef.current = options.url\n }, [options.url])\n useEffect(() => {\n if (JSON.stringify({key, draft}) !== JSON.stringify(draftSnapshot)) {\n startTransition(() => setDraftSnapshot({key, draft}))\n }\n }, [draft, draftSnapshot, key])\n const currentUser = useCurrentUser()\n const client = useClient({apiVersion: '2023-10-16'})\n const [expiresAt, setExpiresAt] = useState<number | undefined>()\n const previewSecretRef = useRef<string | undefined>(undefined)\n const [isResolvingUrl, startTransition] = useTransition()\n const url = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-shadow\n async (draft: SanityDocument | null) => {\n if (typeof location === 'undefined') {\n return undefined\n }\n const urlProp = urlRef.current\n if (typeof urlProp === 'string') {\n return new URL(urlProp, location.origin)\n }\n if (typeof urlProp === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await urlProp(draft)\n return typeof url === 'string' ? new URL(url, location.origin) : url\n }\n if (typeof urlProp === 'object') {\n const preview =\n typeof urlProp.preview === 'function' ? await urlProp.preview(draft) : urlProp.preview\n if (typeof preview !== 'string') {\n return preview\n }\n if (!previewSecretRef.current) {\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const {secret, expiresAt} = await createPreviewSecret(\n client,\n 'sanity-plugin-iframe-pane',\n location.href,\n currentUser?.id,\n )\n previewSecretRef.current = secret\n startTransition(() => setExpiresAt(expiresAt.getTime()))\n }\n\n const resolvePreviewUrl = definePreviewUrl({\n origin: urlProp.origin === 'same-origin' ? location.origin : urlProp.origin,\n preview,\n draftMode: {\n enable: urlProp.draftMode,\n },\n })\n // eslint-disable-next-line @typescript-eslint/no-shadow\n const url = await resolvePreviewUrl({\n client,\n previewUrlSecret: previewSecretRef.current,\n previewSearchParam: null,\n studioBasePath: basePath,\n studioPreviewPerspective: 'previewDrafts',\n })\n return new URL(url, location.origin)\n }\n return undefined\n },\n [client, currentUser?.id, basePath],\n )\n useEffect(() => {\n if (expiresAt) {\n const timeout = setTimeout(\n () => {\n startTransition(() => setExpiresAt(undefined))\n previewSecretRef.current = undefined\n },\n Math.max(0, expiresAt - Date.now()),\n )\n return () => clearTimeout(timeout)\n }\n return undefined\n }, [expiresAt])\n\n return (\n <Suspense fallback={<Loading iframeSize=\"desktop\" />}>\n <IframeInner\n key={draftSnapshot.key}\n _key={draftSnapshot.key}\n draftSnapshot={draftSnapshot.draft}\n url={url}\n isResolvingUrl={isResolvingUrl}\n attributes={attributes}\n defaultSize={defaultSize}\n reload={reload}\n showDisplayUrl={showDisplayUrl}\n userId={currentUser?.id}\n />\n </Suspense>\n )\n}\n\nexport interface IframeInnerProps extends Omit<IframeOptions, 'url'> {\n url: (draftSnapshot: SanityDocument | null) => Promise<URL | Error | undefined>\n isResolvingUrl: boolean\n draftSnapshot: SanityDocument | null\n userId?: string\n expiresAt?: number\n _key?: string\n}\nconst IframeInner = memo(function IframeInner(props: IframeInnerProps) {\n const {\n isResolvingUrl,\n defaultSize = DEFAULT_SIZE,\n reload,\n attributes = {},\n showDisplayUrl = true,\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n } = props\n const [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE)\n\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const url = suspend(\n () => props.url(draftSnapshot),\n [\n // Cache based on a few specific conditions\n 'sanity-plugin-iframe-pane',\n draftSnapshot,\n userId,\n expiresAt,\n _key,\n resolveUUID,\n ],\n )\n\n const [loading, setLoading] = useState(true)\n const [_reloading, setReloading] = useState(false)\n const reloading = _reloading || isResolvingUrl\n\n const iframe = useRef<HTMLIFrameElement>(null)\n\n const handleReload = useCallback(() => {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issues\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n\n setReloading(true)\n }, [])\n\n return (\n <MotionConfig transition={prefersReducedMotion ? {duration: 0} : undefined}>\n <Flex direction=\"column\" style={{height: '100%'}}>\n <Toolbar\n url={url}\n iframeSize={iframeSize}\n reloading={reloading}\n setIframeSize={setIframeSize}\n showUrl={showDisplayUrl}\n reloadButton={!!reload?.button}\n handleReload={handleReload}\n />\n {url instanceof Error ? (\n <ErrorCard error={url} />\n ) : (\n <Card tone=\"transparent\" style={{height: '100%'}}>\n <Frame\n ref={iframe}\n loading={loading}\n reloading={reloading}\n iframeSize={iframeSize}\n setReloading={setReloading}\n setLoading={setLoading}\n url={url}\n attributes={attributes}\n />\n </Card>\n )}\n </Flex>\n </MotionConfig>\n )\n})\n\ninterface FrameProps extends Required<Pick<IframeOptions, 'attributes'>> {\n loading: boolean\n reloading: boolean\n setLoading: (loading: boolean) => void\n setReloading: (reloading: boolean) => void\n iframeSize: IframeSizeKey\n url: URL | undefined\n}\nconst Frame = forwardRef(function Frame(\n props: FrameProps,\n iframe: React.ForwardedRef<HTMLIFrameElement>,\n) {\n const {loading, setLoading, iframeSize, attributes, reloading, url, setReloading} = props\n\n function handleIframeLoad() {\n setLoading(false)\n setReloading(false)\n // Run onLoad from attributes\n if (attributes.onLoad && typeof attributes.onLoad === 'function') {\n attributes.onLoad()\n }\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" style={{height: '100%', position: 'relative'}}>\n <AnimatePresence>\n {!url ||\n (loading && (\n <MotionFlex\n initial=\"initial\"\n animate=\"animate\"\n exit=\"exit\"\n variants={spinnerVariants}\n justify=\"center\"\n align=\"center\"\n style={{inset: '0', position: 'absolute'}}\n >\n <Loading iframeSize={iframeSize} />\n </MotionFlex>\n ))}\n </AnimatePresence>\n {url && (\n <motion.iframe\n ref={iframe}\n title=\"preview\"\n frameBorder=\"0\"\n style={{maxHeight: '100%'}}\n src={url.toString()}\n initial={['background', iframeSize]}\n variants={iframeVariants}\n animate={[\n loading ? 'background' : 'active',\n reloading ? 'reloading' : 'idle',\n iframeSize,\n ]}\n {...attributes}\n onLoad={handleIframeLoad}\n />\n )}\n </Flex>\n )\n})\n\nconst spinnerVariants = {\n initial: {opacity: 1},\n animate: {opacity: [0, 0, 1]},\n exit: {opacity: [1, 0, 0]},\n}\n\nconst iframeVariants = {\n ...sizes,\n desktop: {\n ...sizes.desktop,\n boxShadow: '0 0 0 0px var(--card-shadow-outline-color)',\n },\n mobile: {\n ...sizes.mobile,\n boxShadow: '0 0 0 1px var(--card-shadow-outline-color)',\n },\n background: {\n opacity: 0,\n scale: 1,\n },\n idle: {\n scale: 1,\n },\n reloading: {\n scale: [1, 1, 1, 0.98],\n },\n active: {\n opacity: [0, 0, 1],\n scale: 1,\n },\n}\n\nfunction Loading({iframeSize}: {iframeSize: IframeSizeKey}) {\n return (\n <Flex style={{...sizes[iframeSize]}} justify=\"center\" align=\"center\" direction=\"column\" gap={4}>\n <Spinner muted />\n <Text muted size={1}>\n Loading…\n </Text>\n </Flex>\n )\n}\n\nexport function ErrorCard({error}: {error: Error}) {\n return (\n <Card height=\"fill\">\n <Flex align=\"center\" height=\"fill\" justify=\"center\" padding={4} sizing=\"border\">\n <Container width={0}>\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Flex>\n <Box>\n <Text size={1}>\n <WarningOutlineIcon />\n </Text>\n </Box>\n <Stack flex={1} marginLeft={3} space={3}>\n <Text as=\"h1\" size={1} weight=\"bold\">\n {error.name}\n </Text>\n <Text as=\"p\" muted size={1}>\n {error.message}\n </Text>\n </Stack>\n </Flex>\n </Card>\n </Container>\n </Flex>\n </Card>\n )\n}\n\n// https://github.com/pmndrs/suspend-react?tab=readme-ov-file#making-cache-keys-unique\nconst resolveUUID = Symbol()\n"],"names":["draft","url","expiresAt"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-iframe-pane",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.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": {
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"type": "module",
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
19
|
-
"types": "./lib/index.d.ts",
|
|
20
19
|
"source": "./src/index.ts",
|
|
21
20
|
"import": "./lib/index.js",
|
|
22
21
|
"require": "./lib/index.cjs",
|
|
@@ -26,7 +25,6 @@
|
|
|
26
25
|
},
|
|
27
26
|
"main": "./lib/index.cjs",
|
|
28
27
|
"module": "./lib/index.js",
|
|
29
|
-
"source": "./src/index.ts",
|
|
30
28
|
"types": "./lib/index.d.ts",
|
|
31
29
|
"files": [
|
|
32
30
|
"src",
|
|
@@ -35,67 +33,66 @@
|
|
|
35
33
|
"v2-incompatible.js"
|
|
36
34
|
],
|
|
37
35
|
"scripts": {
|
|
38
|
-
"build": "
|
|
36
|
+
"build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
|
|
39
37
|
"clean": "rimraf lib",
|
|
40
38
|
"format": "prettier --write --cache --ignore-unknown .",
|
|
41
39
|
"link-watch": "plugin-kit link-watch",
|
|
42
40
|
"lint": "eslint .",
|
|
43
41
|
"prepare": "husky install || true",
|
|
44
|
-
"prepublishOnly": "run
|
|
42
|
+
"prepublishOnly": "npm run build",
|
|
45
43
|
"test": "npm run lint && npm run type-check && npm run build",
|
|
46
44
|
"type-check": "tsc --noEmit",
|
|
47
45
|
"watch": "pkg-utils watch --strict"
|
|
48
46
|
},
|
|
49
47
|
"browserslist": "extends @sanity/browserslist-config",
|
|
48
|
+
"prettier": "@sanity/prettier-config",
|
|
50
49
|
"dependencies": {
|
|
51
|
-
"@sanity/icons": "^
|
|
52
|
-
"@sanity/incompatible-plugin": "^1.0.
|
|
53
|
-
"@sanity/preview-url-secret": "^1.
|
|
54
|
-
"@sanity/ui": "^2.
|
|
55
|
-
"framer-motion": "
|
|
56
|
-
"suspend-react": "0.1.3"
|
|
57
|
-
"usehooks-ts": "3.0.1"
|
|
50
|
+
"@sanity/icons": "^3.5.7",
|
|
51
|
+
"@sanity/incompatible-plugin": "^1.0.5",
|
|
52
|
+
"@sanity/preview-url-secret": "^2.1.3",
|
|
53
|
+
"@sanity/ui": "^2.11.6",
|
|
54
|
+
"framer-motion": "^12.0.6",
|
|
55
|
+
"suspend-react": "0.1.3"
|
|
58
56
|
},
|
|
59
57
|
"devDependencies": {
|
|
60
58
|
"@commitlint/cli": "^19.2.0",
|
|
61
59
|
"@commitlint/config-conventional": "^19.1.0",
|
|
62
|
-
"@sanity/pkg-utils": "^
|
|
63
|
-
"@sanity/plugin-kit": "^
|
|
60
|
+
"@sanity/pkg-utils": "^7.0.3",
|
|
61
|
+
"@sanity/plugin-kit": "^4.0.19",
|
|
62
|
+
"@sanity/prettier-config": "^1.0.3",
|
|
64
63
|
"@sanity/semantic-release-preset": "^4.1.7",
|
|
64
|
+
"@types/react": "^19.0.8",
|
|
65
65
|
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
|
66
66
|
"@typescript-eslint/parser": "^7.3.1",
|
|
67
|
+
"eslint": "^8.57.0",
|
|
67
68
|
"eslint-config-prettier": "^9.1.0",
|
|
68
69
|
"eslint-config-react-app": "^7.0.1",
|
|
69
70
|
"eslint-config-sanity": "^7.1.2",
|
|
70
71
|
"eslint-plugin-import": "^2.29.1",
|
|
71
72
|
"eslint-plugin-prettier": "^5.1.3",
|
|
72
|
-
"eslint-plugin-react-hooks": "^4.6.0",
|
|
73
73
|
"eslint-plugin-react": "^7.34.1",
|
|
74
|
-
"eslint": "^
|
|
74
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
75
75
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
|
76
76
|
"husky": "^8.0.3",
|
|
77
77
|
"lint-staged": "^15.0.1",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"react": "^18.
|
|
82
|
-
"react-dom": "^18.2.0",
|
|
83
|
-
"react-is": "^18.2.0",
|
|
78
|
+
"prettier": "^3.4.2",
|
|
79
|
+
"react": "^18.3.0",
|
|
80
|
+
"react-dom": "^18.3.0",
|
|
81
|
+
"react-is": "^18.3.0",
|
|
84
82
|
"rimraf": "^5.0.1",
|
|
85
|
-
"sanity": "^3.
|
|
86
|
-
"styled-components": "^6.1.
|
|
87
|
-
"typescript": "5.
|
|
83
|
+
"sanity": "^3.71.2",
|
|
84
|
+
"styled-components": "^6.1.14",
|
|
85
|
+
"typescript": "5.7.3"
|
|
88
86
|
},
|
|
89
87
|
"peerDependencies": {
|
|
90
|
-
"react": "^18.
|
|
91
|
-
"sanity": "^3.
|
|
92
|
-
"styled-components": "^5.2 || ^6
|
|
88
|
+
"react": "^18.3 || ^19",
|
|
89
|
+
"sanity": "^3.71",
|
|
90
|
+
"styled-components": "^5.2 || ^6"
|
|
93
91
|
},
|
|
94
92
|
"engines": {
|
|
95
|
-
"node": ">=
|
|
93
|
+
"node": ">=18"
|
|
96
94
|
},
|
|
97
95
|
"publishConfig": {
|
|
98
|
-
"access": "public"
|
|
99
|
-
"provenance": true
|
|
96
|
+
"access": "public"
|
|
100
97
|
}
|
|
101
98
|
}
|
package/src/Iframe.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
useState,
|
|
15
15
|
useTransition,
|
|
16
16
|
} from 'react'
|
|
17
|
-
import {type SanityDocument, useClient, useCurrentUser} from 'sanity'
|
|
17
|
+
import {type SanityDocument, useActiveWorkspace, useClient, useCurrentUser} from 'sanity'
|
|
18
18
|
import {suspend} from 'suspend-react'
|
|
19
19
|
|
|
20
20
|
import {DEFAULT_SIZE, sizes, Toolbar} from './Toolbar'
|
|
@@ -66,7 +66,7 @@ export interface IframeOptions {
|
|
|
66
66
|
}>
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const MotionFlex = motion(Flex)
|
|
69
|
+
const MotionFlex = motion.create(Flex)
|
|
70
70
|
|
|
71
71
|
export interface IframeProps {
|
|
72
72
|
document: {
|
|
@@ -77,12 +77,14 @@ export interface IframeProps {
|
|
|
77
77
|
options: IframeOptions
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export function Iframe(props: IframeProps) {
|
|
80
|
+
export function Iframe(props: IframeProps): React.JSX.Element {
|
|
81
81
|
const {document, options} = props
|
|
82
82
|
const draft = document.draft || document.published || document.displayed
|
|
83
83
|
|
|
84
84
|
const {defaultSize = DEFAULT_SIZE, reload, attributes, showDisplayUrl = true, key} = options
|
|
85
85
|
|
|
86
|
+
const workspace = useActiveWorkspace()
|
|
87
|
+
const basePath = workspace?.activeWorkspace?.basePath || '/'
|
|
86
88
|
const urlRef = useRef(options.url)
|
|
87
89
|
const [draftSnapshot, setDraftSnapshot] = useState(() => ({key, draft}))
|
|
88
90
|
useEffect(() => {
|
|
@@ -96,7 +98,7 @@ export function Iframe(props: IframeProps) {
|
|
|
96
98
|
const currentUser = useCurrentUser()
|
|
97
99
|
const client = useClient({apiVersion: '2023-10-16'})
|
|
98
100
|
const [expiresAt, setExpiresAt] = useState<number | undefined>()
|
|
99
|
-
const previewSecretRef = useRef<string | undefined>()
|
|
101
|
+
const previewSecretRef = useRef<string | undefined>(undefined)
|
|
100
102
|
const [isResolvingUrl, startTransition] = useTransition()
|
|
101
103
|
const url = useCallback(
|
|
102
104
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
@@ -143,12 +145,14 @@ export function Iframe(props: IframeProps) {
|
|
|
143
145
|
client,
|
|
144
146
|
previewUrlSecret: previewSecretRef.current,
|
|
145
147
|
previewSearchParam: null,
|
|
148
|
+
studioBasePath: basePath,
|
|
149
|
+
studioPreviewPerspective: 'previewDrafts',
|
|
146
150
|
})
|
|
147
151
|
return new URL(url, location.origin)
|
|
148
152
|
}
|
|
149
153
|
return undefined
|
|
150
154
|
},
|
|
151
|
-
[client, currentUser?.id],
|
|
155
|
+
[client, currentUser?.id, basePath],
|
|
152
156
|
)
|
|
153
157
|
useEffect(() => {
|
|
154
158
|
if (expiresAt) {
|
package/src/Toolbar.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {CopyIcon, LaunchIcon, MobileDeviceIcon, RefreshIcon} from '@sanity/icons'
|
|
2
2
|
import {Box, Button, Card, Flex, Text, Tooltip, useToast} from '@sanity/ui'
|
|
3
|
-
import {useRef} from 'react'
|
|
4
|
-
import {useCopyToClipboard} from 'usehooks-ts'
|
|
3
|
+
import {useCallback, useRef, useState} from 'react'
|
|
5
4
|
|
|
6
5
|
import {DisplayUrl} from './DisplayUrl'
|
|
7
6
|
import type {IframeSizeKey, SizeProps} from './types'
|
|
@@ -156,3 +155,31 @@ export function Toolbar(props: ToolbarProps) {
|
|
|
156
155
|
</>
|
|
157
156
|
)
|
|
158
157
|
}
|
|
158
|
+
|
|
159
|
+
type CopiedValue = string | null
|
|
160
|
+
|
|
161
|
+
type CopyFn = (text: string) => Promise<boolean>
|
|
162
|
+
|
|
163
|
+
function useCopyToClipboard(): [CopiedValue, CopyFn] {
|
|
164
|
+
const [copiedText, setCopiedText] = useState<CopiedValue>(null)
|
|
165
|
+
|
|
166
|
+
const copy: CopyFn = useCallback(async (text) => {
|
|
167
|
+
if (!navigator?.clipboard) {
|
|
168
|
+
console.warn('Clipboard not supported')
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Try to save to clipboard then save it in the state if worked
|
|
173
|
+
try {
|
|
174
|
+
await navigator.clipboard.writeText(text)
|
|
175
|
+
setCopiedText(text)
|
|
176
|
+
return true
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.warn('Copy failed', error)
|
|
179
|
+
setCopiedText(null)
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
}, [])
|
|
183
|
+
|
|
184
|
+
return [copiedText, copy]
|
|
185
|
+
}
|