sanity-plugin-iframe-pane 1.0.14 → 1.1.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/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
- ![Iframe View Pane](https://user-images.githubusercontent.com/9684022/121473207-3548de00-c9ba-11eb-88a0-7d6c748b3f00.png)
7
+ ![Iframe View Pane](https://user-images.githubusercontent.com/9684022/144389599-496e1e50-62a7-4d5c-903a-889885eb8aab.png)
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 _usehooksTs = require("usehooks-ts");
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(_ref) {
56
- var sanityDocument = _ref.document,
57
- options = _ref.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 && sizes !== null && sizes !== void 0 && sizes[defaultSize] ? defaultSize : "desktop"),
66
+ var _useState3 = (0, _react.useState)(sizes === null || sizes === void 0 ? void 0 : sizes[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, _usehooksTs.useCopyToClipboard)(),
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,21 +98,28 @@ function Iframe(_ref) {
99
98
  if (reload !== null && reload !== void 0 && reload.revision) {
100
99
  handleReload();
101
100
  }
102
- }, [displayed === null || displayed === void 0 ? void 0 : displayed._rev]);
101
+ }, [displayed._rev, reload === null || reload === void 0 ? void 0 : reload.revision]); // Set initial URL and refresh on new revisions
102
+
103
103
  (0, _react.useEffect)(() => {
104
104
  var getUrl = /*#__PURE__*/function () {
105
- var _ref2 = _asyncToGenerator(function* () {
106
- var resolveUrl = yield url(displayed);
107
- setDisplayUrl(resolveUrl);
105
+ var _ref = _asyncToGenerator(function* () {
106
+ var resolveUrl = typeof url === 'function' ? yield url(displayed) : ""; // Only update state if URL has changed
107
+
108
+ if (resolveUrl !== displayUrl) {
109
+ setDisplayUrl(resolveUrl);
110
+ }
108
111
  });
109
112
 
110
113
  return function getUrl() {
111
- return _ref2.apply(this, arguments);
114
+ return _ref.apply(this, arguments);
112
115
  };
113
116
  }();
114
117
 
115
- if (!displayUrl && displayed !== null && displayed !== void 0 && displayed._id) getUrl();
116
- }, [displayed]);
118
+ if (typeof url === 'function') {
119
+ getUrl();
120
+ } // eslint-disable-next-line react-hooks/exhaustive-deps
121
+
122
+ }, [displayed._rev]);
117
123
 
118
124
  if (!displayUrl || typeof displayUrl !== 'string') {
119
125
  return /*#__PURE__*/_react.default.createElement(_ui.ThemeProvider, null, /*#__PURE__*/_react.default.createElement(_ui.Flex, {
@@ -132,7 +138,7 @@ function Iframe(_ref) {
132
138
  ref: input,
133
139
  value: displayUrl,
134
140
  readOnly: true,
135
- tabIndex: "-1"
141
+ tabIndex: -1
136
142
  }), /*#__PURE__*/_react.default.createElement(_ui.Flex, {
137
143
  direction: "column",
138
144
  style: {
@@ -165,16 +171,14 @@ function Iframe(_ref) {
165
171
  }, reload !== null && reload !== void 0 && reload.button ? /*#__PURE__*/_react.default.createElement(_ui.Button, {
166
172
  fontSize: [1],
167
173
  padding: 2,
168
- icon: _icons.UndoIcon // text="Reload"
169
- ,
174
+ icon: _icons.UndoIcon,
170
175
  title: "Reload",
171
176
  "aria-label": "Reload",
172
177
  onClick: () => handleReload()
173
178
  }) : null, /*#__PURE__*/_react.default.createElement(_ui.Button, {
174
179
  fontSize: [1],
175
180
  icon: _icons.CopyIcon,
176
- padding: [2] // text="Copy"
177
- ,
181
+ padding: [2],
178
182
  title: "Copy",
179
183
  "aria-label": "Copy",
180
184
  onClick: () => handleCopy()
@@ -206,19 +210,6 @@ function Iframe(_ref) {
206
210
  })))));
207
211
  }
208
212
 
209
- Iframe.propTypes = {
210
- document: _propTypes.default.shape({
211
- displayed: _propTypes.default.shape({
212
- _id: _propTypes.default.string.isRequired,
213
- slug: _propTypes.default.shape({
214
- current: _propTypes.default.string
215
- })
216
- })
217
- }),
218
- options: _propTypes.default.shape({
219
- url: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func])
220
- })
221
- };
222
213
  var _default = Iframe;
223
214
  exports.default = _default;
224
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","_id","position","pointerEvents","opacity","MobileDeviceIcon","button","UndoIcon","CopyIcon","LeaveIcon","window","open","propTypes","PropTypes","shape","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 useEffect(() => {\n const getUrl = async () => {\n const resolveUrl = await url(displayed)\n\n setDisplayUrl(resolveUrl)\n }\n\n if (!displayUrl && displayed?._id) getUrl()\n }, [displayed])\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(sizes?.[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.0.14",
3
+ "version": "1.1.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
  "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": "https://raw.githubusercontent.com/SimeonGriggs/sanity-plugin-iframe-pane"
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": "7.32.0",
29
- "eslint-config-prettier": "8.3.0",
30
- "eslint-config-sanity": "5.1.0",
31
- "eslint-plugin-react": "7.26.0",
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
- "sanipack": "2.0.1"
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 PropTypes from 'prop-types'
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
- import { useCopyToClipboard } from 'usehooks-ts'
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
- function Iframe({document: sanityDocument, options}) {
12
- const {url, defaultSize, reload} = options
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(sizes?.[defaultSize])
17
34
  const input = useRef()
18
35
  const iframe = useRef()
19
36
  const {displayed} = sanityDocument
20
- const [value, copy] = useCopyToClipboard()
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,17 +57,24 @@ 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
 
62
+ // Set initial URL and refresh on new revisions
44
63
  useEffect(() => {
45
64
  const getUrl = async () => {
46
- const resolveUrl = await url(displayed)
65
+ const resolveUrl = typeof url === 'function' ? await url(displayed) : ``
47
66
 
48
- setDisplayUrl(resolveUrl)
67
+ // Only update state if URL has changed
68
+ if (resolveUrl !== displayUrl) {
69
+ setDisplayUrl(resolveUrl)
70
+ }
49
71
  }
50
72
 
51
- if (!displayUrl && displayed?._id) getUrl()
52
- }, [displayed])
73
+ if (typeof url === 'function') {
74
+ getUrl()
75
+ }
76
+ // eslint-disable-next-line react-hooks/exhaustive-deps
77
+ }, [displayed._rev])
53
78
 
54
79
  if (!displayUrl || typeof displayUrl !== 'string') {
55
80
  return (
@@ -68,7 +93,7 @@ function Iframe({document: sanityDocument, options}) {
68
93
  ref={input}
69
94
  value={displayUrl}
70
95
  readOnly
71
- tabIndex="-1"
96
+ tabIndex={-1}
72
97
  />
73
98
  <Flex direction="column" style={{height: `100%`}}>
74
99
  <Card padding={2} borderBottom={1}>
@@ -94,7 +119,6 @@ function Iframe({document: sanityDocument, options}) {
94
119
  fontSize={[1]}
95
120
  padding={2}
96
121
  icon={UndoIcon}
97
- // text="Reload"
98
122
  title="Reload"
99
123
  aria-label="Reload"
100
124
  onClick={() => handleReload()}
@@ -104,7 +128,6 @@ function Iframe({document: sanityDocument, options}) {
104
128
  fontSize={[1]}
105
129
  icon={CopyIcon}
106
130
  padding={[2]}
107
- // text="Copy"
108
131
  title="Copy"
109
132
  aria-label="Copy"
110
133
  onClick={() => handleCopy()}
@@ -136,18 +159,4 @@ function Iframe({document: sanityDocument, options}) {
136
159
  )
137
160
  }
138
161
 
139
- Iframe.propTypes = {
140
- document: PropTypes.shape({
141
- displayed: PropTypes.shape({
142
- _id: PropTypes.string.isRequired,
143
- slug: PropTypes.shape({
144
- current: PropTypes.string,
145
- }),
146
- }),
147
- }),
148
- options: PropTypes.shape({
149
- url: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
150
- }),
151
- }
152
-
153
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