From c0ff116de863ec86f81de065b02dbf1ff28d920c Mon Sep 17 00:00:00 2001 From: Mike Fix Date: Tue, 21 Aug 2018 16:24:52 -0700 Subject: [PATCH] embeds expose CodeMirrorLink, load only necessary theme in embed implement copy button in Carbon window controls add copy to queryParam use next/head and metatags in /embed make editor have router prop fix now.json rewrites allow local stylesheets in embed --- components/Carbon.js | 7 ++++ components/CopyButton.js | 36 ++++++++++++++++++ components/Dropdown.js | 15 +++++--- components/Editor.js | 18 ++++++--- components/ExportButton.js | 46 +++++++++++++++++++++++ components/Meta.js | 66 +++++++++++++++++++-------------- components/WindowControls.js | 47 +++++++++++++++++++++++- components/svg/Checkmark.js | 6 +-- components/svg/Copy.js | 29 +++++++++++++++ lib/routing.js | 3 +- next.config.js | 1 + now.json | 2 +- package.json | 1 + pages/embed.js | 71 ++++++++++++++++++++++++++++++++++++ pages/index.js | 7 +++- yarn.lock | 19 +++++++++- 16 files changed, 327 insertions(+), 47 deletions(-) create mode 100644 components/CopyButton.js create mode 100644 components/ExportButton.js create mode 100644 components/svg/Copy.js create mode 100644 pages/embed.js diff --git a/components/Carbon.js b/components/Carbon.js index fbffbf6..e8c94a8 100644 --- a/components/Carbon.js +++ b/components/Carbon.js @@ -10,6 +10,11 @@ import Watermark from '../components/svg/Watermark' import { COLORS, LANGUAGE_MODE_HASH, LANGUAGE_NAME_HASH, DEFAULT_SETTINGS } from '../lib/constants' class Carbon extends React.PureComponent { + static defaultProps = { + onAspectRatioChange: () => {}, + updateCode: () => {} + } + componentDidMount() { const ro = new ResizeObserver(entries => { const cr = entries[0].contentRect @@ -69,6 +74,8 @@ class Carbon extends React.PureComponent { titleBar={this.props.titleBar} theme={config.windowTheme} handleTitleBarChange={this.props.updateTitleBar} + code={this.props.children} + copyable={this.props.copyable} /> ) : null} component.setState({ copied: false }), + this.props.interval == null ? 1000 : this.props.interval + ) + } + + render() { + return ( + + {this.props.children({ + copied: this.state.copied + })} + + ) + } +} + +export default CopyButton diff --git a/components/Dropdown.js b/components/Dropdown.js index df3e6df..58504db 100644 --- a/components/Dropdown.js +++ b/components/Dropdown.js @@ -58,7 +58,7 @@ class Dropdown extends React.PureComponent { userInputtedValue = '' render() { - const { button, color, selected, onChange } = this.props + const { button, color, selected, onChange, itemWrapper } = this.props const { itemsToShow, inputValue } = this.state const minWidth = calcMinWidth(button, selected, itemsToShow) @@ -72,13 +72,13 @@ class Dropdown extends React.PureComponent { onChange={onChange} onUserAction={this.onUserAction} > - {renderDropdown({ button, color, list: itemsToShow, selected, minWidth })} + {renderDropdown({ button, color, list: itemsToShow, selected, minWidth, itemWrapper })} ) } } -const renderDropdown = ({ button, color, list, minWidth }) => ({ +const renderDropdown = ({ button, color, list, minWidth, itemWrapper }) => ({ isOpen, highlightedIndex, selectedItem, @@ -104,6 +104,7 @@ const renderDropdown = ({ button, color, list, minWidth }) => ({ { ) } -const ListItem = ({ children, color, isHighlighted, isSelected, ...rest }) => { +const ListItem = ({ children, color, isHighlighted, isSelected, itemWrapper, ...rest }) => { const itemColor = color || COLORS.SECONDARY return (
  • - {children} + {itemWrapper ? ( + itemWrapper({ children, color: itemColor }) + ) : ( + {children} + )} {isSelected ? : null} + + ) +} + +export default withRouter(ExportButton) diff --git a/components/Meta.js b/components/Meta.js index 070c838..59c1f13 100644 --- a/components/Meta.js +++ b/components/Meta.js @@ -4,12 +4,46 @@ import Reset from './style/Reset' import Font from './style/Font' import Typography from './style/Typography' -const LOCAL_STYLESHEETS = ['one-dark', 'verminal', 'night-owl', 'nord'] +export const LOCAL_STYLESHEETS = ['one-dark', 'verminal', 'night-owl', 'nord'] const CDN_STYLESHEETS = THEMES.filter( t => t.hasStylesheet !== false && LOCAL_STYLESHEETS.indexOf(t.id) < 0 ) +export const CodeMirrorLink = () => ( + +) + +export const MetaTags = () => ( + <> + + + + + + + + + + + + Carbon + + +) + /* * Before supporting verify that it is widely supported in FireFox * with out a flag here: https://caniuse.com/#feat=link-rel-preload @@ -19,38 +53,14 @@ export default function Meta() { return (
    - - - - - - - - - - - - Carbon - - + + - + + {copied ? ( + + ) : ( + + )} + + + ) +} -export default ({ titleBar, theme, handleTitleBarChange }) => ( +export default ({ titleBar, theme, handleTitleBarChange, copyable, code }) => (
    {theme === 'bw' ? : }
    @@ -12,6 +45,11 @@ export default ({ titleBar, theme, handleTitleBarChange }) => ( onChange={e => handleTitleBarChange(e.target.value)} />
    + {copyable && ( +
    + {renderCopyButton} +
    + )}
    diff --git a/components/svg/Checkmark.js b/components/svg/Checkmark.js index a00ce89..e441b9e 100644 --- a/components/svg/Checkmark.js +++ b/components/svg/Checkmark.js @@ -1,9 +1,9 @@ import React from 'react' -export default () => ( - +export default ({ width = 9, height = 8, color = '#FFFFFF' }) => ( + diff --git a/components/svg/Copy.js b/components/svg/Copy.js new file mode 100644 index 0000000..2b7a1f5 --- /dev/null +++ b/components/svg/Copy.js @@ -0,0 +1,29 @@ +import React from 'react' + +const SVG_RATIO = 0.81 + +const Copy = ({ size, color }) => { + const width = size * SVG_RATIO + const height = size + + return ( + + + + ) +} + +Copy.defaultProps = { + size: 16 +} + +export default Copy diff --git a/lib/routing.js b/lib/routing.js index 3439482..48e1e0a 100644 --- a/lib/routing.js +++ b/lib/routing.js @@ -30,7 +30,8 @@ const mappings = [ { field: 'code:code' }, { field: 'es:exportSize' }, { field: 'wm:watermark', type: 'bool' }, - { field: 'ts:timestamp', type: 'bool' } + { field: 'ts:timestamp', type: 'bool' }, + { field: 'copy', type: 'bool' } ] const reverseMappings = mappings.map(mapping => diff --git a/next.config.js b/next.config.js index f04ff50..9b61716 100644 --- a/next.config.js +++ b/next.config.js @@ -6,6 +6,7 @@ module.exports = (/* phase, { defaultConfig } */) => { exportPathMap() { return { '/about': { page: '/about' }, + '/embed': { page: '/embed' }, '/index': { page: '/index' }, '/': { page: '/' } } diff --git a/now.json b/now.json index 688ecef..a83f7ea 100644 --- a/now.json +++ b/now.json @@ -5,7 +5,7 @@ "static": { "rewrites": [ { - "source": "!/about", + "source": "!/?(about)?(embed)", "destination": "/index.html" } ] diff --git a/package.json b/package.json index 8a26924..609ee8e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react-click-outside": "^3.0.0", "react-codemirror2": "^5.1.0", "react-color": "^2.13.8", + "react-copy-to-clipboard": "^5.0.1", "react-dnd": "^5.0.0", "react-dnd-html5-backend": "^5.0.0", "react-dom": "16.3.*", diff --git a/pages/embed.js b/pages/embed.js new file mode 100644 index 0000000..40f548d --- /dev/null +++ b/pages/embed.js @@ -0,0 +1,71 @@ +// Theirs +import React from 'react' +import Head from 'next/head' +import { withRouter } from 'next/router' +import url from 'url' + +// Ours +import { LOCAL_STYLESHEETS, CodeMirrorLink, MetaTags } from '../components/Meta' +import Carbon from '../components/Carbon' +import { DEFAULT_CODE, DEFAULT_SETTINGS } from '../lib/constants' + +import { getQueryStringState } from '../lib/routing' + +const Page = props => ( +
    + + + {LOCAL_STYLESHEETS.indexOf(props.theme) > -1 ? ( + + ) : ( + + )} + + + {props.children} + +
    +) + +class Embed extends React.Component { + state = { + ...DEFAULT_SETTINGS, + code: DEFAULT_CODE, + mounted: false + } + + componentDidMount() { + const { asPath = '' } = this.props.router + const { query } = url.parse(asPath, true) + const queryParams = getQueryStringState(query) + const initialState = Object.keys(queryParams).length ? queryParams : {} + + this.setState({ ...initialState, copyable: queryParams.copy !== false, mounted: true }) + } + + render() { + return ( + + {this.state.mounted && ( + + {this.state.code} + + )} + + ) + } +} + +export default withRouter(Embed) diff --git a/pages/index.js b/pages/index.js index 6fa2ddc..229c264 100644 --- a/pages/index.js +++ b/pages/index.js @@ -22,7 +22,12 @@ class Index extends React.Component { render() { return ( - + ) } diff --git a/yarn.lock b/yarn.lock index 4c25d2b..c5df462 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1577,6 +1577,12 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +copy-to-clipboard@^3: + version "3.0.8" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9" + dependencies: + toggle-selection "^1.0.3" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -4579,7 +4585,7 @@ prop-types@15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: @@ -4712,6 +4718,13 @@ react-color@^2.13.8: reactcss "^1.2.0" tinycolor2 "^1.4.1" +react-copy-to-clipboard@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz#8eae107bb400be73132ed3b6a7b4fb156090208e" + dependencies: + copy-to-clipboard "^3" + prop-types "^15.5.8" + "react-dnd-html5-backend@^4.0.2 || ^5.0.0", react-dnd-html5-backend@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-5.0.1.tgz#0b578d79c5c01317c70414c8d717f632b919d4f1" @@ -5635,6 +5648,10 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + tohash@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/tohash/-/tohash-1.0.2.tgz#9e66e497da0cfd77ba85f9663065adf2d8c99981"