sanity-plugin-iframe-pane 4.0.0 → 5.0.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/lib/index.d.cts DELETED
@@ -1,75 +0,0 @@
1
- import type {HTMLAttributeReferrerPolicy} from 'react'
2
- import {SanityDocument} from 'sanity'
3
- import {usePerspective} from 'sanity'
4
-
5
- export declare function Iframe(props: IframeProps): React.JSX.Element
6
-
7
- export declare interface IframeOptions {
8
- /**
9
- * If you have multiple iframe instances side-by-side you need to give each a unique key.
10
- */
11
- key?: string
12
- url:
13
- | string
14
- | UrlResolver
15
- | {
16
- /**
17
- * The URL origin of where the preview is hosted, for example `https://example.com`.
18
- * If it's an embedded Studio then set it to `'same-origin'`.
19
- */
20
- origin: 'same-origin' | string
21
- /**
22
- * The route to redirect to after enabling Draft Mode.
23
- * If you don't have enough data to build the URL, return an `Error` instance to show an error message.
24
- * @example `return new Error('Missing slug')`
25
- * To prolong the loading state, return `undefined`
26
- */
27
- preview: string | UrlResolver
28
- /**
29
- * The route that enables Draft Mode
30
- * @example '/api/draft'
31
- */
32
- draftMode: string
33
- }
34
- defaultSize?: IframeSizeKey
35
- showDisplayUrl?: boolean
36
- reload?: {
37
- button?: boolean
38
- }
39
- attributes?: Partial<{
40
- allow: string
41
- referrerPolicy: HTMLAttributeReferrerPolicy | undefined
42
- sandbox: string
43
- onLoad: () => void
44
- }>
45
- }
46
-
47
- export declare interface IframeProps {
48
- document: {
49
- displayed: SanityDocument
50
- draft: SanityDocument | null
51
- published: SanityDocument | null
52
- }
53
- options: IframeOptions
54
- }
55
-
56
- export declare type IframeSizeKey = keyof SizeProps
57
-
58
- export declare type Size = 'desktop' | 'mobile'
59
-
60
- export declare type SizeProps = {
61
- [key in Size]: {
62
- width: string | number
63
- height: string | number
64
- }
65
- }
66
-
67
- export declare type UrlResolver = (
68
- document: SanityDocument | null,
69
- perspective: Pick<
70
- ReturnType<typeof usePerspective>,
71
- 'selectedPerspectiveName' | 'perspectiveStack'
72
- >,
73
- ) => string | Error | undefined | Promise<string | Error | undefined>
74
-
75
- export {}
package/lib/index.d.ts DELETED
@@ -1,75 +0,0 @@
1
- import type {HTMLAttributeReferrerPolicy} from 'react'
2
- import {SanityDocument} from 'sanity'
3
- import {usePerspective} from 'sanity'
4
-
5
- export declare function Iframe(props: IframeProps): React.JSX.Element
6
-
7
- export declare interface IframeOptions {
8
- /**
9
- * If you have multiple iframe instances side-by-side you need to give each a unique key.
10
- */
11
- key?: string
12
- url:
13
- | string
14
- | UrlResolver
15
- | {
16
- /**
17
- * The URL origin of where the preview is hosted, for example `https://example.com`.
18
- * If it's an embedded Studio then set it to `'same-origin'`.
19
- */
20
- origin: 'same-origin' | string
21
- /**
22
- * The route to redirect to after enabling Draft Mode.
23
- * If you don't have enough data to build the URL, return an `Error` instance to show an error message.
24
- * @example `return new Error('Missing slug')`
25
- * To prolong the loading state, return `undefined`
26
- */
27
- preview: string | UrlResolver
28
- /**
29
- * The route that enables Draft Mode
30
- * @example '/api/draft'
31
- */
32
- draftMode: string
33
- }
34
- defaultSize?: IframeSizeKey
35
- showDisplayUrl?: boolean
36
- reload?: {
37
- button?: boolean
38
- }
39
- attributes?: Partial<{
40
- allow: string
41
- referrerPolicy: HTMLAttributeReferrerPolicy | undefined
42
- sandbox: string
43
- onLoad: () => void
44
- }>
45
- }
46
-
47
- export declare interface IframeProps {
48
- document: {
49
- displayed: SanityDocument
50
- draft: SanityDocument | null
51
- published: SanityDocument | null
52
- }
53
- options: IframeOptions
54
- }
55
-
56
- export declare type IframeSizeKey = keyof SizeProps
57
-
58
- export declare type Size = 'desktop' | 'mobile'
59
-
60
- export declare type SizeProps = {
61
- [key in Size]: {
62
- width: string | number
63
- height: string | number
64
- }
65
- }
66
-
67
- export declare type UrlResolver = (
68
- document: SanityDocument | null,
69
- perspective: Pick<
70
- ReturnType<typeof usePerspective>,
71
- 'selectedPerspectiveName' | 'perspectiveStack'
72
- >,
73
- ) => string | Error | undefined | Promise<string | Error | undefined>
74
-
75
- export {}
package/lib/index.js DELETED
@@ -1,384 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { MobileDeviceIcon, RefreshIcon, CopyIcon, LaunchIcon, WarningOutlineIcon } from "@sanity/icons";
3
- import { createPreviewSecret } from "@sanity/preview-url-secret/create-secret";
4
- import { definePreviewUrl } from "@sanity/preview-url-secret/define-preview-url";
5
- import { Text, useToast, Card, Flex, Tooltip, Button, Box, usePrefersReducedMotion, Spinner, Container, Stack } from "@sanity/ui";
6
- import { motion, MotionConfig, AnimatePresence } from "framer-motion";
7
- import { useMemo, useRef, useState, useCallback, memo, forwardRef, useEffect, useTransition, Suspense } from "react";
8
- import { useActiveWorkspace, useCurrentUser, useClient, usePerspective } from "sanity";
9
- import { suspend } from "suspend-react";
10
- import { getRedirectTo } from "@sanity/preview-url-secret/get-redirect-to";
11
- function DisplayUrl(props) {
12
- const truncatedUrl = useMemo(() => {
13
- const url = getRedirectTo(props.url);
14
- return `${url.origin === location.origin ? "" : url.origin}${url.pathname}${url.search}`;
15
- }, [props.url]);
16
- return /* @__PURE__ */ jsx(Text, { size: 0, textOverflow: "ellipsis", children: truncatedUrl });
17
- }
18
- const sizes = {
19
- desktop: {
20
- width: "100%",
21
- height: "100%"
22
- },
23
- mobile: {
24
- width: 414,
25
- height: 746
26
- }
27
- }, DEFAULT_SIZE = "desktop";
28
- function Toolbar(props) {
29
- const { url, iframeSize, setIframeSize, reloading, showUrl, reloadButton, handleReload } = props, validUrl = url instanceof URL, input = useRef(null), { push: pushToast } = useToast(), [, copy] = useCopyToClipboard();
30
- return /* @__PURE__ */ jsxs(Fragment, { children: [
31
- /* @__PURE__ */ jsx(
32
- "textarea",
33
- {
34
- style: { position: "absolute", pointerEvents: "none", opacity: 0 },
35
- ref: input,
36
- value: validUrl ? url.toString() : "",
37
- readOnly: !0,
38
- tabIndex: -1
39
- }
40
- ),
41
- /* @__PURE__ */ jsx(Card, { padding: 2, borderBottom: !0, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
42
- /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(
43
- Tooltip,
44
- {
45
- animate: !0,
46
- content: /* @__PURE__ */ jsx(Text, { size: 1, style: { whiteSpace: "nowrap" }, children: iframeSize === "mobile" ? "Exit mobile preview" : "Preview mobile viewport" }),
47
- padding: 2,
48
- placement: "bottom-start",
49
- children: /* @__PURE__ */ jsx(
50
- Button,
51
- {
52
- disabled: !validUrl,
53
- fontSize: [1],
54
- padding: 2,
55
- mode: iframeSize === "mobile" ? "default" : "ghost",
56
- icon: MobileDeviceIcon,
57
- onClick: () => setIframeSize(iframeSize === "mobile" ? "desktop" : "mobile")
58
- }
59
- )
60
- }
61
- ) }),
62
- /* @__PURE__ */ jsx(Box, { flex: 1, children: showUrl && validUrl && /* @__PURE__ */ jsx(DisplayUrl, { url }) }),
63
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 1, children: [
64
- reloadButton ? /* @__PURE__ */ jsx(
65
- Tooltip,
66
- {
67
- animate: !0,
68
- content: /* @__PURE__ */ jsx(Text, { size: 1, style: { whiteSpace: "nowrap" }, children: reloading ? "Reloading\u2026" : "Reload" }),
69
- padding: 2,
70
- children: /* @__PURE__ */ jsx(
71
- Button,
72
- {
73
- disabled: !validUrl,
74
- mode: "bleed",
75
- fontSize: [1],
76
- padding: 2,
77
- icon: RefreshIcon,
78
- loading: reloading,
79
- "aria-label": "Reload",
80
- onClick: () => handleReload()
81
- }
82
- )
83
- }
84
- ) : null,
85
- /* @__PURE__ */ jsx(
86
- Tooltip,
87
- {
88
- animate: !0,
89
- content: /* @__PURE__ */ jsx(Text, { size: 1, style: { whiteSpace: "nowrap" }, children: "Copy URL" }),
90
- padding: 2,
91
- children: /* @__PURE__ */ jsx(
92
- Button,
93
- {
94
- mode: "bleed",
95
- disabled: !validUrl,
96
- fontSize: [1],
97
- icon: CopyIcon,
98
- padding: [2],
99
- "aria-label": "Copy URL",
100
- onClick: () => {
101
- input?.current?.value && copy(input.current.value).then((copied) => {
102
- pushToast(copied ? {
103
- closable: !0,
104
- status: "success",
105
- title: "The URL is copied to the clipboard"
106
- } : {
107
- closable: !0,
108
- status: "error",
109
- title: "Failed to copy the URL to the clipboard"
110
- });
111
- });
112
- }
113
- }
114
- )
115
- }
116
- ),
117
- /* @__PURE__ */ jsx(
118
- Tooltip,
119
- {
120
- animate: !0,
121
- content: /* @__PURE__ */ jsx(Text, { size: 1, style: { whiteSpace: "nowrap" }, children: "Open URL in a new tab" }),
122
- padding: 2,
123
- placement: "bottom-end",
124
- children: /* @__PURE__ */ jsx(
125
- Button,
126
- {
127
- disabled: !validUrl,
128
- fontSize: [1],
129
- icon: LaunchIcon,
130
- mode: "ghost",
131
- paddingY: [2],
132
- text: "Open",
133
- "aria-label": "Open URL in a new tab",
134
- onClick: validUrl ? () => window.open(url.toString()) : void 0
135
- }
136
- )
137
- }
138
- )
139
- ] })
140
- ] }) })
141
- ] });
142
- }
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);
156
- function encodeStudioPerspective(studioPerspective) {
157
- return Array.isArray(studioPerspective) ? studioPerspective.join(",") : studioPerspective;
158
- }
159
- function Iframe(props) {
160
- 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 }));
161
- useEffect(() => {
162
- urlRef.current = options.url;
163
- }, [options.url]), useEffect(() => {
164
- JSON.stringify({ key, draft }) !== JSON.stringify(draftSnapshot) && startTransition(() => setDraftSnapshot({ key, draft }));
165
- }, [draft, draftSnapshot, key]);
166
- const currentUser = useCurrentUser(), client = useClient({ apiVersion: "2023-10-16" }), [expiresAt, setExpiresAt] = useState(), previewSecretRef = useRef(void 0), [isResolvingUrl, startTransition] = useTransition(), { perspectiveStack, selectedPerspectiveName } = usePerspective(), perspective = useMemo(
167
- () => ({
168
- perspectiveStack,
169
- selectedPerspectiveName
170
- }),
171
- [perspectiveStack, selectedPerspectiveName]
172
- ), url = useCallback(
173
- // eslint-disable-next-line @typescript-eslint/no-shadow
174
- async (draft2) => {
175
- if (typeof location > "u")
176
- return;
177
- const urlProp = urlRef.current;
178
- if (typeof urlProp == "string")
179
- return new URL(urlProp, location.origin);
180
- if (typeof urlProp == "function") {
181
- const url2 = await urlProp(draft2, perspective);
182
- return typeof url2 == "string" ? new URL(url2, location.origin) : url2;
183
- }
184
- if (typeof urlProp == "object") {
185
- const preview = typeof urlProp.preview == "function" ? await urlProp.preview(draft2, perspective) : urlProp.preview;
186
- if (typeof preview != "string")
187
- return preview;
188
- if (!previewSecretRef.current) {
189
- const { secret, expiresAt: expiresAt2 } = await createPreviewSecret(
190
- client,
191
- "sanity-plugin-iframe-pane",
192
- location.href,
193
- currentUser?.id
194
- );
195
- previewSecretRef.current = secret, startTransition(() => setExpiresAt(expiresAt2.getTime()));
196
- }
197
- const url2 = await definePreviewUrl({
198
- origin: urlProp.origin === "same-origin" ? location.origin : urlProp.origin,
199
- preview,
200
- draftMode: {
201
- enable: urlProp.draftMode
202
- }
203
- })({
204
- client,
205
- previewUrlSecret: previewSecretRef.current,
206
- previewSearchParam: null,
207
- studioBasePath: basePath,
208
- studioPreviewPerspective: encodeStudioPerspective(perspective.perspectiveStack)
209
- });
210
- return new URL(url2, location.origin);
211
- }
212
- },
213
- [basePath, client, currentUser?.id, perspective]
214
- );
215
- return useEffect(() => {
216
- if (expiresAt) {
217
- const timeout = setTimeout(
218
- () => {
219
- startTransition(() => setExpiresAt(void 0)), previewSecretRef.current = void 0;
220
- },
221
- Math.max(0, expiresAt - Date.now())
222
- );
223
- return () => clearTimeout(timeout);
224
- }
225
- }, [expiresAt]), /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, { iframeSize: "desktop" }), children: /* @__PURE__ */ jsx(
226
- IframeInner,
227
- {
228
- _key: draftSnapshot.key,
229
- draftSnapshot: draftSnapshot.draft,
230
- url,
231
- isResolvingUrl,
232
- attributes,
233
- perspective,
234
- defaultSize,
235
- reload,
236
- showDisplayUrl,
237
- userId: currentUser?.id
238
- },
239
- `${draftSnapshot.key}-${selectedPerspectiveName || "draft"}`
240
- ) });
241
- }
242
- const IframeInner = memo(function(props) {
243
- const {
244
- isResolvingUrl,
245
- defaultSize = DEFAULT_SIZE,
246
- reload,
247
- attributes = {},
248
- showDisplayUrl = !0,
249
- draftSnapshot,
250
- userId,
251
- expiresAt,
252
- perspective: { selectedPerspectiveName, perspectiveStack },
253
- _key
254
- } = props, [iframeSize, setIframeSize] = useState(sizes?.[defaultSize] ? defaultSize : DEFAULT_SIZE), prefersReducedMotion = usePrefersReducedMotion(), url = suspend(
255
- () => props.url(draftSnapshot),
256
- [
257
- // Cache based on a few specific conditions
258
- "sanity-plugin-iframe-pane",
259
- draftSnapshot,
260
- selectedPerspectiveName,
261
- perspectiveStack,
262
- userId,
263
- expiresAt,
264
- _key,
265
- resolveUUID
266
- ]
267
- ), [loading, setLoading] = useState(!0), [_reloading, setReloading] = useState(!1), reloading = _reloading || isResolvingUrl, iframe = useRef(null), handleReload = useCallback(() => {
268
- iframe?.current && (iframe.current.src = iframe.current.src, setReloading(!0));
269
- }, []);
270
- return /* @__PURE__ */ jsx(MotionConfig, { transition: prefersReducedMotion ? { duration: 0 } : void 0, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
271
- /* @__PURE__ */ jsx(
272
- Toolbar,
273
- {
274
- url,
275
- iframeSize,
276
- reloading,
277
- setIframeSize,
278
- showUrl: showDisplayUrl,
279
- reloadButton: !!reload?.button,
280
- handleReload
281
- }
282
- ),
283
- url instanceof Error ? /* @__PURE__ */ jsx(ErrorCard, { error: url }) : /* @__PURE__ */ jsx(Card, { tone: "transparent", style: { height: "100%" }, children: /* @__PURE__ */ jsx(
284
- Frame,
285
- {
286
- ref: iframe,
287
- loading,
288
- reloading,
289
- iframeSize,
290
- setReloading,
291
- setLoading,
292
- url,
293
- attributes
294
- }
295
- ) })
296
- ] }) });
297
- }), Frame = forwardRef(function(props, iframe) {
298
- const { loading, setLoading, iframeSize, attributes, reloading, url, setReloading } = props;
299
- function handleIframeLoad() {
300
- setLoading(!1), setReloading(!1), attributes.onLoad && typeof attributes.onLoad == "function" && attributes.onLoad();
301
- }
302
- return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "center", style: { height: "100%", position: "relative" }, children: [
303
- /* @__PURE__ */ jsx(AnimatePresence, { children: !url || loading && /* @__PURE__ */ jsx(
304
- MotionFlex,
305
- {
306
- initial: "initial",
307
- animate: "animate",
308
- exit: "exit",
309
- variants: spinnerVariants,
310
- justify: "center",
311
- align: "center",
312
- style: { inset: "0", position: "absolute" },
313
- children: /* @__PURE__ */ jsx(Loading, { iframeSize })
314
- }
315
- ) }),
316
- url && /* @__PURE__ */ jsx(
317
- motion.iframe,
318
- {
319
- ref: iframe,
320
- title: "preview",
321
- frameBorder: "0",
322
- style: { maxHeight: "100%" },
323
- src: url.toString(),
324
- initial: ["background", iframeSize],
325
- variants: iframeVariants,
326
- animate: [
327
- loading ? "background" : "active",
328
- reloading ? "reloading" : "idle",
329
- iframeSize
330
- ],
331
- ...attributes,
332
- onLoad: handleIframeLoad
333
- }
334
- )
335
- ] });
336
- }), spinnerVariants = {
337
- initial: { opacity: 1 },
338
- animate: { opacity: [0, 0, 1] },
339
- exit: { opacity: [1, 0, 0] }
340
- }, iframeVariants = {
341
- ...sizes,
342
- desktop: {
343
- ...sizes.desktop,
344
- boxShadow: "0 0 0 0px var(--card-shadow-outline-color)"
345
- },
346
- mobile: {
347
- ...sizes.mobile,
348
- boxShadow: "0 0 0 1px var(--card-shadow-outline-color)"
349
- },
350
- background: {
351
- opacity: 0,
352
- scale: 1
353
- },
354
- idle: {
355
- scale: 1
356
- },
357
- reloading: {
358
- scale: [1, 1, 1, 0.98]
359
- },
360
- active: {
361
- opacity: [0, 0, 1],
362
- scale: 1
363
- }
364
- };
365
- function Loading({ iframeSize }) {
366
- return /* @__PURE__ */ jsxs(Flex, { style: { ...sizes[iframeSize] }, justify: "center", align: "center", direction: "column", gap: 4, children: [
367
- /* @__PURE__ */ jsx(Spinner, { muted: !0 }),
368
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "Loading\u2026" })
369
- ] });
370
- }
371
- function ErrorCard({ error }) {
372
- return /* @__PURE__ */ jsx(Card, { height: "fill", children: /* @__PURE__ */ jsx(Flex, { align: "center", height: "fill", justify: "center", padding: 4, sizing: "border", children: /* @__PURE__ */ jsx(Container, { width: 0, children: /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsxs(Flex, { children: [
373
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, children: /* @__PURE__ */ jsx(WarningOutlineIcon, {}) }) }),
374
- /* @__PURE__ */ jsxs(Stack, { flex: 1, marginLeft: 3, space: 3, children: [
375
- /* @__PURE__ */ jsx(Text, { as: "h1", size: 1, weight: "bold", children: error.name }),
376
- /* @__PURE__ */ jsx(Text, { as: "p", muted: !0, size: 1, children: error.message })
377
- ] })
378
- ] }) }) }) }) });
379
- }
380
- const resolveUUID = Symbol();
381
- export {
382
- Iframe
383
- };
384
- //# sourceMappingURL=index.js.map
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
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 useMemo,\n useRef,\n useState,\n useTransition,\n} from 'react'\nimport {\n type SanityDocument,\n useActiveWorkspace,\n useClient,\n useCurrentUser,\n usePerspective,\n} 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 perspective: Pick<\n ReturnType<typeof usePerspective>,\n 'selectedPerspectiveName' | 'perspectiveStack'\n >,\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\nfunction encodeStudioPerspective(studioPerspective: string[] | string): string {\n return Array.isArray(studioPerspective) ? studioPerspective.join(',') : studioPerspective\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 {perspectiveStack, selectedPerspectiveName} = usePerspective()\n const perspective = useMemo(\n () => ({\n perspectiveStack,\n selectedPerspectiveName,\n }),\n [perspectiveStack, selectedPerspectiveName],\n )\n\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, perspective)\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'\n ? await urlProp.preview(draft, perspective)\n : 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: encodeStudioPerspective(perspective.perspectiveStack),\n })\n return new URL(url, location.origin)\n }\n return undefined\n },\n [basePath, client, currentUser?.id, perspective],\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}-${selectedPerspectiveName || 'draft'}`}\n _key={draftSnapshot.key}\n draftSnapshot={draftSnapshot.draft}\n url={url}\n isResolvingUrl={isResolvingUrl}\n attributes={attributes}\n perspective={perspective}\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 perspective: Parameters<UrlResolver>[1]\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 perspective: {selectedPerspectiveName, perspectiveStack},\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 selectedPerspectiveName,\n perspectiveStack,\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/sanity.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "parts": [
3
- {
4
- "implements": "part:@sanity/base/sanity-root",
5
- "path": "./v2-incompatible.js"
6
- }
7
- ]
8
- }
@@ -1,16 +0,0 @@
1
- import {getRedirectTo} from '@sanity/preview-url-secret/get-redirect-to'
2
- import {Text} from '@sanity/ui'
3
- import {useMemo} from 'react'
4
-
5
- export function DisplayUrl(props: {url: URL}) {
6
- const truncatedUrl = useMemo(() => {
7
- const url = getRedirectTo(props.url)
8
- return `${url.origin === location.origin ? '' : url.origin}${url.pathname}${url.search}`
9
- }, [props.url])
10
-
11
- return (
12
- <Text size={0} textOverflow="ellipsis">
13
- {truncatedUrl}
14
- </Text>
15
- )
16
- }