From 832f4608cd9d8484e0700e0be325908353a2c594 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 30 Jul 2022 01:46:18 +0800 Subject: [PATCH 1/2] Feat: bring up share function --- package.json | 2 ++ src/App.js | 8 ++++++-- src/Header.js | 23 +++++++++++++++++++++-- src/Repl.js | 37 ++++++++++++++++++++++++++++++++++++- yarn.lock | 10 ++++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3ce39b3..3572b3b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "dependencies": { "classnames": "^2.3.1", "codemirror": "^5.65.2", + "js-base64": "^3.7.2", "lodash-es": "^4.17.21", + "pako": "^2.0.4", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0" diff --git a/src/App.js b/src/App.js index f6e7722..b91ceac 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,7 @@ import styles from './App.module.css' export default class App extends React.Component { state = { error: null, + shareUrl: null } componentDidMount() { if (!window.Terser) { @@ -16,6 +17,9 @@ export default class App extends React.Component { }) } } + onReplShareUrlChange(shareUrl) { + this.setState({ shareUrl }) + } render() { const { error } = this.state @@ -27,13 +31,13 @@ export default class App extends React.Component { ) } else { - return + return } })() return (
-
+
{body}
) diff --git a/src/Header.js b/src/Header.js index 4b81664..d7ad106 100644 --- a/src/Header.js +++ b/src/Header.js @@ -1,8 +1,26 @@ -import React from 'react'; +import React, { useCallback, useState } from 'react'; import styles from './Header.module.css'; -const Header = () => { +const Header = ({ shareUrl = '' }) => { + const [buttonText, setButtonText] = useState('Share'); + + const handleShareButtonClick = useCallback(async () => { + // update addressbar with shareUrl + window.history.replaceState(null, '', shareUrl) + + if (!navigator.clipboard) { + prompt('Clipboard API is not supported in your environment. Please manually copy the following URL', shareUrl); + } else { + await navigator.clipboard.writeText(shareUrl) + setButtonText('URL is copied to clipboard!') + + setTimeout(() => { + setButtonText('Share') + }, 3000) + } + }, [shareUrl]); + return (
@@ -15,6 +33,7 @@ const Header = () => {

Terser REPL

Try Terser on this page

+
); diff --git a/src/Repl.js b/src/Repl.js index 143525b..5602553 100644 --- a/src/Repl.js +++ b/src/Repl.js @@ -5,6 +5,9 @@ import CodeMirrorPanel from './CodeMirrorPanel'; import { getCodeSizeInBytes } from './lib/helpers'; import terserOptions, { evalOptions } from './lib/terser-options'; +import { gzip, ungzip } from 'pako' +import { Base64 } from 'js-base64' + import styles from './Repl.module.css'; const DEBOUNCE_DELAY = 500; @@ -64,18 +67,47 @@ class Repl extends Component { ); } + componentDidMount() { + const url = new URL(window.location.href) + const encodedInput = url.searchParams.get('code') + const encodedOptionsCode = url.searchParams.get('options') + + if (encodedOptionsCode) { + const decodedOptionsCode = evalOptions(ungzip(Base64.toUint8Array(encodedOptionsCode), { to: 'string' })); + this.setState({ + optionsCode: decodedOptionsCode + }) + this._updateTerserOptions(decodedOptionsCode); + } + + if (encodedInput) { + const decodedCode = ungzip(Base64.toUint8Array(encodedInput), { to: 'string' }); + this._updateCode(decodedCode); + this._minify(); + } + } + + _getShareUrl = (code, optionsCode) => { + const url = new URL(window.location.href) + const encodedInput = Base64.fromUint8Array(gzip(code)) + url.searchParams.set('code', encodedInput) + const encodedConfig = Base64.fromUint8Array(gzip(JSON.stringify(optionsCode))) + url.searchParams.set('options', encodedConfig) + return url.toString() + } + _updateCode = code => { this.setState({ code, rawSize: getCodeSizeInBytes(code) }); this._minifyToState(code); + this.props.onReplShareUrlChange(this._getShareUrl(code, this.state.optionsCode)) }; _updateTerserOptions = options => { try { const parsedOptions = evalOptions(options); - this.setState({ terserOptions: parsedOptions, optionsErrorMessage: null @@ -84,6 +116,7 @@ class Repl extends Component { this.setState({ optionsErrorMessage: e.message }); } + this.props.onReplShareUrlChange(this._getShareUrl(this.state.code, options)) this._minify(this.state.code); }; @@ -110,6 +143,8 @@ class Repl extends Component { }); } } catch (e) { + console.error(e); + console.log(terserOpts); this.setState({ errorMessage: e.message }); } }; diff --git a/yarn.lock b/yarn.lock index 97fa871..bf919a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5936,6 +5936,11 @@ jest@^27.4.3: import-local "^3.0.2" jest-cli "^27.5.1" +js-base64@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745" + integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6786,6 +6791,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pako@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" + integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" From 95456ba42ef3670920fb634cb2cfe566016c7778 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 30 Jul 2022 01:56:42 +0800 Subject: [PATCH 2/2] Add corresponding style to share button --- src/Header.js | 20 +++++++++++--------- src/Header.module.css | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/Header.js b/src/Header.js index d7ad106..6adfae8 100644 --- a/src/Header.js +++ b/src/Header.js @@ -24,15 +24,17 @@ const Header = ({ shareUrl = '' }) => { return (
- - -

Terser REPL

-
-

Try Terser on this page

+
+ + +

Terser REPL

+
+

Try Terser on this page

+
diff --git a/src/Header.module.css b/src/Header.module.css index 41f185e..dba986c 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -11,6 +11,7 @@ display: -webkit-box; display: flex; -webkit-box-orient: horizontal; + justify-content: space-between; flex-flow: row nowrap; align-items: center; position: relative; @@ -19,6 +20,18 @@ user-select: none; } +.container header div { + display: -webkit-box; + display: flex; + -webkit-box-orient: horizontal; + justify-content: space-between; + flex-flow: row nowrap; + align-items: center; + position: relative; + text-align: left; + user-select: none; +} + .container header img { border-style: none; box-sizing: content-box; @@ -57,3 +70,28 @@ height: 34px; text-decoration: none; } + +.container button { + display: inline-flex; + appearance: none; + -webkit-box-align: center; + align-items: center; + user-select: none; + position: relative; + white-space: nowrap; + vertical-align: middle; + outline: transparent solid 2px; + outline-offset: 2px; + line-height: 1.2; + border-radius: 6px; + height: 28px; + margin-right: 1.20%; + font-weight: 600; + min-width: 28px; + font-size: 15px; + padding-inline-start: 8px; + padding-inline-end: 8px; + background: rgba(255, 255, 255, 0.8); + cursor: pointer; + border: 0; +}