sanity-plugin-iframe-pane 1.0.15 → 1.1.1
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/README.md +1 -1
- package/lib/Iframe.js +19 -34
- package/lib/Iframe.js.map +1 -1
- package/lib/hooks/useCopytoClipboard.js +64 -0
- package/lib/hooks/useCopytoClipboard.js.map +1 -0
- package/package.json +15 -26
- package/src/{Iframe.js → Iframe.tsx} +31 -28
- package/src/hooks/useCopytoClipboard.ts +30 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Display any URL in a View Pane, along with helpful buttons to Copy the URL or op
|
|
|
4
4
|
|
|
5
5
|
Accepts either a string or an async function to resolve a URL based on the current document.
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
package/lib/Iframe.js
CHANGED
|
@@ -7,13 +7,11 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
|
|
10
|
-
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
11
|
-
|
|
12
10
|
var _ui = require("@sanity/ui");
|
|
13
11
|
|
|
14
12
|
var _icons = require("@sanity/icons");
|
|
15
13
|
|
|
16
|
-
var
|
|
14
|
+
var _useCopytoClipboard = _interopRequireDefault(require("./hooks/useCopytoClipboard"));
|
|
17
15
|
|
|
18
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
17
|
|
|
@@ -52,11 +50,12 @@ var sizes = {
|
|
|
52
50
|
}
|
|
53
51
|
};
|
|
54
52
|
|
|
55
|
-
function Iframe(
|
|
56
|
-
var sanityDocument =
|
|
57
|
-
options =
|
|
53
|
+
function Iframe(props) {
|
|
54
|
+
var sanityDocument = props.document,
|
|
55
|
+
options = props.options;
|
|
58
56
|
var url = options.url,
|
|
59
|
-
defaultSize = options.defaultSize,
|
|
57
|
+
_options$defaultSize = options.defaultSize,
|
|
58
|
+
defaultSize = _options$defaultSize === void 0 ? "desktop" : _options$defaultSize,
|
|
60
59
|
reload = options.reload;
|
|
61
60
|
|
|
62
61
|
var _useState = (0, _react.useState)(typeof url === 'string' ? url : ""),
|
|
@@ -64,7 +63,7 @@ function Iframe(_ref) {
|
|
|
64
63
|
displayUrl = _useState2[0],
|
|
65
64
|
setDisplayUrl = _useState2[1];
|
|
66
65
|
|
|
67
|
-
var _useState3 = (0, _react.useState)(defaultSize
|
|
66
|
+
var _useState3 = (0, _react.useState)(defaultSize),
|
|
68
67
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
69
68
|
iframeSize = _useState4[0],
|
|
70
69
|
setIframeSize = _useState4[1];
|
|
@@ -73,9 +72,8 @@ function Iframe(_ref) {
|
|
|
73
72
|
var iframe = (0, _react.useRef)();
|
|
74
73
|
var displayed = sanityDocument.displayed;
|
|
75
74
|
|
|
76
|
-
var _useCopyToClipboard = (0,
|
|
75
|
+
var _useCopyToClipboard = (0, _useCopytoClipboard.default)(),
|
|
77
76
|
_useCopyToClipboard2 = _slicedToArray(_useCopyToClipboard, 2),
|
|
78
|
-
value = _useCopyToClipboard2[0],
|
|
79
77
|
copy = _useCopyToClipboard2[1];
|
|
80
78
|
|
|
81
79
|
function handleCopy() {
|
|
@@ -89,6 +87,7 @@ function Iframe(_ref) {
|
|
|
89
87
|
if (!(iframe !== null && iframe !== void 0 && iframe.current)) {
|
|
90
88
|
return;
|
|
91
89
|
} // Funky way to reload an iframe without CORS issuies
|
|
90
|
+
// eslint-disable-next-line no-self-assign
|
|
92
91
|
|
|
93
92
|
|
|
94
93
|
iframe.current.src = iframe.current.src;
|
|
@@ -99,12 +98,12 @@ function Iframe(_ref) {
|
|
|
99
98
|
if (reload !== null && reload !== void 0 && reload.revision) {
|
|
100
99
|
handleReload();
|
|
101
100
|
}
|
|
102
|
-
}, [displayed._rev]); // Set initial URL and refresh on new revisions
|
|
101
|
+
}, [displayed._rev, reload === null || reload === void 0 ? void 0 : reload.revision]); // Set initial URL and refresh on new revisions
|
|
103
102
|
|
|
104
103
|
(0, _react.useEffect)(() => {
|
|
105
104
|
var getUrl = /*#__PURE__*/function () {
|
|
106
|
-
var
|
|
107
|
-
var resolveUrl = yield url(displayed); // Only update state if URL has changed
|
|
105
|
+
var _ref = _asyncToGenerator(function* () {
|
|
106
|
+
var resolveUrl = typeof url === 'function' ? yield url(displayed) : ""; // Only update state if URL has changed
|
|
108
107
|
|
|
109
108
|
if (resolveUrl !== displayUrl) {
|
|
110
109
|
setDisplayUrl(resolveUrl);
|
|
@@ -112,13 +111,14 @@ function Iframe(_ref) {
|
|
|
112
111
|
});
|
|
113
112
|
|
|
114
113
|
return function getUrl() {
|
|
115
|
-
return
|
|
114
|
+
return _ref.apply(this, arguments);
|
|
116
115
|
};
|
|
117
116
|
}();
|
|
118
117
|
|
|
119
|
-
if (typeof url
|
|
118
|
+
if (typeof url === 'function') {
|
|
120
119
|
getUrl();
|
|
121
|
-
}
|
|
120
|
+
} // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
121
|
+
|
|
122
122
|
}, [displayed._rev]);
|
|
123
123
|
|
|
124
124
|
if (!displayUrl || typeof displayUrl !== 'string') {
|
|
@@ -138,7 +138,7 @@ function Iframe(_ref) {
|
|
|
138
138
|
ref: input,
|
|
139
139
|
value: displayUrl,
|
|
140
140
|
readOnly: true,
|
|
141
|
-
tabIndex:
|
|
141
|
+
tabIndex: -1
|
|
142
142
|
}), /*#__PURE__*/_react.default.createElement(_ui.Flex, {
|
|
143
143
|
direction: "column",
|
|
144
144
|
style: {
|
|
@@ -171,16 +171,14 @@ function Iframe(_ref) {
|
|
|
171
171
|
}, reload !== null && reload !== void 0 && reload.button ? /*#__PURE__*/_react.default.createElement(_ui.Button, {
|
|
172
172
|
fontSize: [1],
|
|
173
173
|
padding: 2,
|
|
174
|
-
icon: _icons.UndoIcon
|
|
175
|
-
,
|
|
174
|
+
icon: _icons.UndoIcon,
|
|
176
175
|
title: "Reload",
|
|
177
176
|
"aria-label": "Reload",
|
|
178
177
|
onClick: () => handleReload()
|
|
179
178
|
}) : null, /*#__PURE__*/_react.default.createElement(_ui.Button, {
|
|
180
179
|
fontSize: [1],
|
|
181
180
|
icon: _icons.CopyIcon,
|
|
182
|
-
padding: [2]
|
|
183
|
-
,
|
|
181
|
+
padding: [2],
|
|
184
182
|
title: "Copy",
|
|
185
183
|
"aria-label": "Copy",
|
|
186
184
|
onClick: () => handleCopy()
|
|
@@ -212,19 +210,6 @@ function Iframe(_ref) {
|
|
|
212
210
|
})))));
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
Iframe.propTypes = {
|
|
216
|
-
document: _propTypes.default.shape({
|
|
217
|
-
displayed: _propTypes.default.shape({
|
|
218
|
-
_id: _propTypes.default.string.isRequired,
|
|
219
|
-
slug: _propTypes.default.shape({
|
|
220
|
-
current: _propTypes.default.string
|
|
221
|
-
})
|
|
222
|
-
})
|
|
223
|
-
}),
|
|
224
|
-
options: _propTypes.default.shape({
|
|
225
|
-
url: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func])
|
|
226
|
-
})
|
|
227
|
-
};
|
|
228
213
|
var _default = Iframe;
|
|
229
214
|
exports.default = _default;
|
|
230
215
|
//# sourceMappingURL=Iframe.js.map
|
package/lib/Iframe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Iframe.js"],"names":["sizes","desktop","backgroundColor","width","height","maxHeight","mobile","Iframe","sanityDocument","document","options","url","defaultSize","reload","displayUrl","setDisplayUrl","iframeSize","setIframeSize","input","iframe","displayed","value","copy","handleCopy","current","handleReload","src","revision","_rev","getUrl","resolveUrl","position","pointerEvents","opacity","MobileDeviceIcon","button","UndoIcon","CopyIcon","LeaveIcon","window","open","propTypes","PropTypes","shape","_id","string","isRequired","slug","oneOfType","func"],"mappings":"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","sourcesContent":["import React, {useEffect, useState, useRef} from 'react'\nimport PropTypes from 'prop-types'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\nimport {useCopyToClipboard} from 'usehooks-ts'\n\nconst sizes = {\n desktop: {backgroundColor: `white`, width: `100%`, height: `100%`, maxHeight: `100%`},\n mobile: {backgroundColor: `white`, width: 414, height: `100%`, maxHeight: 736},\n}\nfunction Iframe({document: sanityDocument, options}) {\n const {url, defaultSize, reload} = options\n const [displayUrl, setDisplayUrl] = useState(typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(\n defaultSize && sizes?.[defaultSize] ? defaultSize : `desktop`\n )\n const input = useRef()\n const iframe = useRef()\n const {displayed} = sanityDocument\n const [value, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n iframe.current.src = iframe.current.src\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision) {\n handleReload()\n }\n }, [displayed._rev])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n const resolveUrl = await url(displayed)\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl) {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url !== 'string') {\n getUrl()\n }\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} items=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex=\"-1\"\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom={1}>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n // text=\"Reload\"\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n // text=\"Copy\"\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`}}>\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nIframe.propTypes = {\n document: PropTypes.shape({\n displayed: PropTypes.shape({\n _id: PropTypes.string.isRequired,\n slug: PropTypes.shape({\n current: PropTypes.string,\n }),\n }),\n }),\n options: PropTypes.shape({\n url: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),\n }),\n}\n\nexport default Iframe\n"],"file":"Iframe.js"}
|
|
1
|
+
{"version":3,"file":"Iframe.js","names":["sizes","desktop","backgroundColor","width","height","maxHeight","mobile","Iframe","props","sanityDocument","document","options","url","defaultSize","reload","useState","displayUrl","setDisplayUrl","iframeSize","setIframeSize","input","useRef","iframe","displayed","useCopyToClipboard","copy","handleCopy","current","value","handleReload","src","useEffect","revision","_rev","getUrl","resolveUrl","position","pointerEvents","opacity","MobileDeviceIcon","button","UndoIcon","CopyIcon","LeaveIcon","window","open"],"sources":["../src/Iframe.tsx"],"sourcesContent":["import React, {useEffect, useState, useRef} from 'react'\nimport {SanityDocumentLike} from 'sanity'\nimport {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'\nimport {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'\n\nimport useCopyToClipboard from './hooks/useCopytoClipboard'\n\nconst sizes = {\n desktop: {backgroundColor: `white`, width: `100%`, height: `100%`, maxHeight: `100%`},\n mobile: {backgroundColor: `white`, width: 414, height: `100%`, maxHeight: 736},\n}\n\nexport type IframeOptions = {\n url: string | ((document: SanityDocumentLike) => unknown)\n defaultSize?: 'desktop' | 'mobile'\n reload: {\n revision: boolean\n button: boolean\n }\n}\n\nexport type IframeProps = {\n document: {\n displayed: SanityDocumentLike\n }\n options: IframeOptions\n}\n\nfunction Iframe(props: IframeProps) {\n const {document: sanityDocument, options} = props\n const {url, defaultSize = `desktop`, reload} = options\n const [displayUrl, setDisplayUrl] = useState(typeof url === 'string' ? url : ``)\n const [iframeSize, setIframeSize] = useState(defaultSize)\n const input = useRef()\n const iframe = useRef()\n const {displayed} = sanityDocument\n const [, copy] = useCopyToClipboard()\n\n function handleCopy() {\n if (!input?.current?.value) return\n\n copy(input.current.value)\n }\n\n function handleReload() {\n if (!iframe?.current) {\n return\n }\n\n // Funky way to reload an iframe without CORS issuies\n // eslint-disable-next-line no-self-assign\n iframe.current.src = iframe.current.src\n }\n\n // Reload on new revisions\n useEffect(() => {\n if (reload?.revision) {\n handleReload()\n }\n }, [displayed._rev, reload?.revision])\n\n // Set initial URL and refresh on new revisions\n useEffect(() => {\n const getUrl = async () => {\n const resolveUrl = typeof url === 'function' ? await url(displayed) : ``\n\n // Only update state if URL has changed\n if (resolveUrl !== displayUrl) {\n setDisplayUrl(resolveUrl)\n }\n }\n\n if (typeof url === 'function') {\n getUrl()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayed._rev])\n\n if (!displayUrl || typeof displayUrl !== 'string') {\n return (\n <ThemeProvider>\n <Flex padding={5} items=\"center\" justify=\"center\">\n <Spinner />\n </Flex>\n </ThemeProvider>\n )\n }\n\n return (\n <ThemeProvider>\n <textarea\n style={{position: `absolute`, pointerEvents: `none`, opacity: 0}}\n ref={input}\n value={displayUrl}\n readOnly\n tabIndex={-1}\n />\n <Flex direction=\"column\" style={{height: `100%`}}>\n <Card padding={2} borderBottom={1}>\n <Flex align=\"center\" gap={2}>\n <Flex align=\"center\" gap={1}>\n <Button\n fontSize={[1]}\n padding={2}\n tone=\"primary\"\n mode={iframeSize === 'mobile' ? 'default' : 'ghost'}\n icon={MobileDeviceIcon}\n onClick={() => setIframeSize(iframeSize === 'mobile' ? 'desktop' : 'mobile')}\n />\n </Flex>\n <Box flex={1}>\n <Text size={0} textOverflow=\"ellipsis\">\n {displayUrl}\n </Text>\n </Box>\n <Flex align=\"center\" gap={1}>\n {reload?.button ? (\n <Button\n fontSize={[1]}\n padding={2}\n icon={UndoIcon}\n title=\"Reload\"\n aria-label=\"Reload\"\n onClick={() => handleReload()}\n />\n ) : null}\n <Button\n fontSize={[1]}\n icon={CopyIcon}\n padding={[2]}\n title=\"Copy\"\n aria-label=\"Copy\"\n onClick={() => handleCopy()}\n />\n <Button\n fontSize={[1]}\n icon={LeaveIcon}\n padding={[2]}\n text=\"Open\"\n tone=\"primary\"\n onClick={() => window.open(displayUrl)}\n />\n </Flex>\n </Flex>\n </Card>\n <Card tone=\"transparent\" padding={iframeSize === 'mobile' ? 2 : 0} style={{height: `100%`}}>\n <Flex align=\"center\" justify=\"center\" style={{height: `100%`}}>\n <iframe\n ref={iframe}\n title=\"preview\"\n style={sizes[iframeSize]}\n frameBorder=\"0\"\n src={displayUrl}\n />\n </Flex>\n </Card>\n </Flex>\n </ThemeProvider>\n )\n}\n\nexport default Iframe\n"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
|
|
10
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
11
|
+
|
|
12
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
13
|
+
|
|
14
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
15
|
+
|
|
16
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
17
|
+
|
|
18
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
19
|
+
|
|
20
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
21
|
+
|
|
22
|
+
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
23
|
+
|
|
24
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
25
|
+
|
|
26
|
+
// Return success
|
|
27
|
+
function useCopyToClipboard() {
|
|
28
|
+
var _useState = (0, _react.useState)(null),
|
|
29
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
30
|
+
copiedText = _useState2[0],
|
|
31
|
+
setCopiedText = _useState2[1];
|
|
32
|
+
|
|
33
|
+
var copy = /*#__PURE__*/function () {
|
|
34
|
+
var _ref = _asyncToGenerator(function* (text) {
|
|
35
|
+
var _navigator;
|
|
36
|
+
|
|
37
|
+
if (!((_navigator = navigator) !== null && _navigator !== void 0 && _navigator.clipboard)) {
|
|
38
|
+
console.warn('Clipboard not supported');
|
|
39
|
+
return false;
|
|
40
|
+
} // Try to save to clipboard then save it in the state if worked
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
yield navigator.clipboard.writeText(text);
|
|
45
|
+
setCopiedText(text);
|
|
46
|
+
return true;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn('Copy failed', error);
|
|
49
|
+
setCopiedText(null);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return function copy(_x) {
|
|
55
|
+
return _ref.apply(this, arguments);
|
|
56
|
+
};
|
|
57
|
+
}();
|
|
58
|
+
|
|
59
|
+
return [copiedText, copy];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var _default = useCopyToClipboard;
|
|
63
|
+
exports.default = _default;
|
|
64
|
+
//# sourceMappingURL=useCopytoClipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCopytoClipboard.js","names":["useCopyToClipboard","useState","copiedText","setCopiedText","copy","text","navigator","clipboard","console","warn","writeText","error"],"sources":["../../src/hooks/useCopytoClipboard.ts"],"sourcesContent":["import {useState} from 'react'\n\ntype CopiedValue = string | null\ntype CopyFn = (text: string) => Promise<boolean> // Return success\n\nfunction useCopyToClipboard(): [CopiedValue, CopyFn] {\n const [copiedText, setCopiedText] = useState<CopiedValue>(null)\n\n const copy: CopyFn = 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\nexport default useCopyToClipboard\n"],"mappings":";;;;;;;AAAA;;;;;;;;;;;;;;;;;;AAGiD;AAEjD,SAASA,kBAAT,GAAqD;EACnD,gBAAoC,IAAAC,eAAA,EAAsB,IAAtB,CAApC;EAAA;EAAA,IAAOC,UAAP;EAAA,IAAmBC,aAAnB;;EAEA,IAAMC,IAAY;IAAA,6BAAG,WAAOC,IAAP,EAAgB;MAAA;;MACnC,IAAI,gBAACC,SAAD,uCAAC,WAAWC,SAAZ,CAAJ,EAA2B;QACzBC,OAAO,CAACC,IAAR,CAAa,yBAAb;QACA,OAAO,KAAP;MACD,CAJkC,CAMnC;;;MACA,IAAI;QACF,MAAMH,SAAS,CAACC,SAAV,CAAoBG,SAApB,CAA8BL,IAA9B,CAAN;QACAF,aAAa,CAACE,IAAD,CAAb;QACA,OAAO,IAAP;MACD,CAJD,CAIE,OAAOM,KAAP,EAAc;QACdH,OAAO,CAACC,IAAR,CAAa,aAAb,EAA4BE,KAA5B;QACAR,aAAa,CAAC,IAAD,CAAb;QACA,OAAO,KAAP;MACD;IACF,CAhBiB;;IAAA,gBAAZC,IAAY;MAAA;IAAA;EAAA,GAAlB;;EAkBA,OAAO,CAACF,UAAD,EAAaE,IAAb,CAAP;AACD;;eAEcJ,kB"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-iframe-pane",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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
|
"main": "lib/Iframe.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "sanipack build",
|
|
8
8
|
"watch": "sanipack build --watch",
|
|
9
|
-
"prepublishOnly": "sanipack build && sanipack verify"
|
|
9
|
+
"prepublishOnly": "sanipack build && sanipack verify",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"lint:fix": "eslint . --fix"
|
|
10
12
|
},
|
|
11
13
|
"repository": {
|
|
12
14
|
"type": "git",
|
|
13
|
-
"url": "
|
|
15
|
+
"url": "git+ssh://git@github.com/SimeonGriggs/sanity-plugin-iframe-pane.git"
|
|
14
16
|
},
|
|
15
17
|
"keywords": [
|
|
16
18
|
"sanity",
|
|
@@ -20,34 +22,21 @@
|
|
|
20
22
|
"license": "MIT",
|
|
21
23
|
"dependencies": {
|
|
22
24
|
"@sanity/icons": "^1.2.1",
|
|
23
|
-
"@sanity/ui": "^0.36.12"
|
|
24
|
-
"prop-types": "15.7.2",
|
|
25
|
-
"usehooks-ts": "^1.2.0"
|
|
25
|
+
"@sanity/ui": "^0.36.12"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"eslint": "
|
|
29
|
-
"eslint
|
|
30
|
-
"eslint-config-
|
|
31
|
-
"eslint-
|
|
28
|
+
"@sanity/eslint-config-studio": "^2.0.0",
|
|
29
|
+
"eslint": "8.19.0",
|
|
30
|
+
"eslint-config-prettier": "^8.5.0",
|
|
31
|
+
"eslint-config-sanity": "6.0.0",
|
|
32
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
33
|
+
"eslint-plugin-react": "^7.30.1",
|
|
32
34
|
"prettier": "2.4.1",
|
|
33
|
-
"
|
|
35
|
+
"sanity": "^2.29.3",
|
|
36
|
+
"sanipack": "2.0.1",
|
|
37
|
+
"typescript": "^4.7.4"
|
|
34
38
|
},
|
|
35
39
|
"peerDependencies": {
|
|
36
40
|
"react": "^17.0.2"
|
|
37
|
-
},
|
|
38
|
-
"prettier": {
|
|
39
|
-
"semi": false,
|
|
40
|
-
"printWidth": 100,
|
|
41
|
-
"bracketSpacing": false,
|
|
42
|
-
"singleQuote": true
|
|
43
|
-
},
|
|
44
|
-
"eslintConfig": {
|
|
45
|
-
"parser": "sanipack/babel/eslint-parser",
|
|
46
|
-
"extends": [
|
|
47
|
-
"sanity",
|
|
48
|
-
"sanity/react",
|
|
49
|
-
"prettier",
|
|
50
|
-
"prettier/react"
|
|
51
|
-
]
|
|
52
41
|
}
|
|
53
42
|
}
|
|
@@ -1,23 +1,40 @@
|
|
|
1
1
|
import React, {useEffect, useState, useRef} from 'react'
|
|
2
|
-
import
|
|
2
|
+
import {SanityDocumentLike} from 'sanity'
|
|
3
3
|
import {Box, Flex, Text, Button, ThemeProvider, Card, Spinner} from '@sanity/ui'
|
|
4
4
|
import {UndoIcon, CopyIcon, LeaveIcon, MobileDeviceIcon} from '@sanity/icons'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
import useCopyToClipboard from './hooks/useCopytoClipboard'
|
|
6
7
|
|
|
7
8
|
const sizes = {
|
|
8
9
|
desktop: {backgroundColor: `white`, width: `100%`, height: `100%`, maxHeight: `100%`},
|
|
9
10
|
mobile: {backgroundColor: `white`, width: 414, height: `100%`, maxHeight: 736},
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
export type IframeOptions = {
|
|
14
|
+
url: string | ((document: SanityDocumentLike) => unknown)
|
|
15
|
+
defaultSize?: 'desktop' | 'mobile'
|
|
16
|
+
reload: {
|
|
17
|
+
revision: boolean
|
|
18
|
+
button: boolean
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type IframeProps = {
|
|
23
|
+
document: {
|
|
24
|
+
displayed: SanityDocumentLike
|
|
25
|
+
}
|
|
26
|
+
options: IframeOptions
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Iframe(props: IframeProps) {
|
|
30
|
+
const {document: sanityDocument, options} = props
|
|
31
|
+
const {url, defaultSize = `desktop`, reload} = options
|
|
13
32
|
const [displayUrl, setDisplayUrl] = useState(typeof url === 'string' ? url : ``)
|
|
14
|
-
const [iframeSize, setIframeSize] = useState(
|
|
15
|
-
defaultSize && sizes?.[defaultSize] ? defaultSize : `desktop`
|
|
16
|
-
)
|
|
33
|
+
const [iframeSize, setIframeSize] = useState(defaultSize)
|
|
17
34
|
const input = useRef()
|
|
18
35
|
const iframe = useRef()
|
|
19
36
|
const {displayed} = sanityDocument
|
|
20
|
-
const [
|
|
37
|
+
const [, copy] = useCopyToClipboard()
|
|
21
38
|
|
|
22
39
|
function handleCopy() {
|
|
23
40
|
if (!input?.current?.value) return
|
|
@@ -31,6 +48,7 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
31
48
|
}
|
|
32
49
|
|
|
33
50
|
// Funky way to reload an iframe without CORS issuies
|
|
51
|
+
// eslint-disable-next-line no-self-assign
|
|
34
52
|
iframe.current.src = iframe.current.src
|
|
35
53
|
}
|
|
36
54
|
|
|
@@ -39,12 +57,12 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
39
57
|
if (reload?.revision) {
|
|
40
58
|
handleReload()
|
|
41
59
|
}
|
|
42
|
-
}, [displayed._rev])
|
|
60
|
+
}, [displayed._rev, reload?.revision])
|
|
43
61
|
|
|
44
62
|
// Set initial URL and refresh on new revisions
|
|
45
63
|
useEffect(() => {
|
|
46
64
|
const getUrl = async () => {
|
|
47
|
-
const resolveUrl = await url(displayed)
|
|
65
|
+
const resolveUrl = typeof url === 'function' ? await url(displayed) : ``
|
|
48
66
|
|
|
49
67
|
// Only update state if URL has changed
|
|
50
68
|
if (resolveUrl !== displayUrl) {
|
|
@@ -52,9 +70,10 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
52
70
|
}
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
if (typeof url
|
|
73
|
+
if (typeof url === 'function') {
|
|
56
74
|
getUrl()
|
|
57
75
|
}
|
|
76
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
77
|
}, [displayed._rev])
|
|
59
78
|
|
|
60
79
|
if (!displayUrl || typeof displayUrl !== 'string') {
|
|
@@ -74,7 +93,7 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
74
93
|
ref={input}
|
|
75
94
|
value={displayUrl}
|
|
76
95
|
readOnly
|
|
77
|
-
tabIndex=
|
|
96
|
+
tabIndex={-1}
|
|
78
97
|
/>
|
|
79
98
|
<Flex direction="column" style={{height: `100%`}}>
|
|
80
99
|
<Card padding={2} borderBottom={1}>
|
|
@@ -100,7 +119,6 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
100
119
|
fontSize={[1]}
|
|
101
120
|
padding={2}
|
|
102
121
|
icon={UndoIcon}
|
|
103
|
-
// text="Reload"
|
|
104
122
|
title="Reload"
|
|
105
123
|
aria-label="Reload"
|
|
106
124
|
onClick={() => handleReload()}
|
|
@@ -110,7 +128,6 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
110
128
|
fontSize={[1]}
|
|
111
129
|
icon={CopyIcon}
|
|
112
130
|
padding={[2]}
|
|
113
|
-
// text="Copy"
|
|
114
131
|
title="Copy"
|
|
115
132
|
aria-label="Copy"
|
|
116
133
|
onClick={() => handleCopy()}
|
|
@@ -142,18 +159,4 @@ function Iframe({document: sanityDocument, options}) {
|
|
|
142
159
|
)
|
|
143
160
|
}
|
|
144
161
|
|
|
145
|
-
Iframe.propTypes = {
|
|
146
|
-
document: PropTypes.shape({
|
|
147
|
-
displayed: PropTypes.shape({
|
|
148
|
-
_id: PropTypes.string.isRequired,
|
|
149
|
-
slug: PropTypes.shape({
|
|
150
|
-
current: PropTypes.string,
|
|
151
|
-
}),
|
|
152
|
-
}),
|
|
153
|
-
}),
|
|
154
|
-
options: PropTypes.shape({
|
|
155
|
-
url: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
156
|
-
}),
|
|
157
|
-
}
|
|
158
|
-
|
|
159
162
|
export default Iframe
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {useState} from 'react'
|
|
2
|
+
|
|
3
|
+
type CopiedValue = string | null
|
|
4
|
+
type CopyFn = (text: string) => Promise<boolean> // Return success
|
|
5
|
+
|
|
6
|
+
function useCopyToClipboard(): [CopiedValue, CopyFn] {
|
|
7
|
+
const [copiedText, setCopiedText] = useState<CopiedValue>(null)
|
|
8
|
+
|
|
9
|
+
const copy: CopyFn = async (text) => {
|
|
10
|
+
if (!navigator?.clipboard) {
|
|
11
|
+
console.warn('Clipboard not supported')
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Try to save to clipboard then save it in the state if worked
|
|
16
|
+
try {
|
|
17
|
+
await navigator.clipboard.writeText(text)
|
|
18
|
+
setCopiedText(text)
|
|
19
|
+
return true
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn('Copy failed', error)
|
|
22
|
+
setCopiedText(null)
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return [copiedText, copy]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default useCopyToClipboard
|