Merge remote-tracking branch 'origin/master' into HEAD

main
Jake Dexheimer 7 years ago
commit c90099f72a

@ -204,6 +204,60 @@
"code" "code"
] ]
}, },
{
"login": "dmmulroy",
"name": "Dillon Mulroy",
"avatar_url": "https://avatars1.githubusercontent.com/u/2755722?v=4",
"profile": "https://dillonmulroy.com",
"contributions": [
"code"
]
},
{
"login": "NARKOZ",
"name": "Nihad Abbasov",
"avatar_url": "https://avatars2.githubusercontent.com/u/253398?v=4",
"profile": "https://github.com/NARKOZ",
"contributions": [
"code"
]
},
{
"login": "TorzuoliH",
"name": "Hugo Torzuoli",
"avatar_url": "https://avatars1.githubusercontent.com/u/7328625?v=4",
"profile": "https://torzuolih.github.io",
"contributions": [
"doc"
]
},
{
"login": "bruno02221",
"name": "Bruno C. Couto",
"avatar_url": "https://avatars1.githubusercontent.com/u/1906977?v=4",
"profile": "https://github.com/bruno02221",
"contributions": [
"code"
]
},
{
"login": "molnarmark",
"name": "Mark Molnar",
"avatar_url": "https://avatars2.githubusercontent.com/u/13263073?v=4",
"profile": "https://github.com/molnarmark",
"contributions": [
"code"
]
},
{
"login": "TETRA2000",
"name": "Takahiko Inayama",
"avatar_url": "https://avatars1.githubusercontent.com/u/1459450?v=4",
"profile": "https://www.behance.net/tetra2000",
"contributions": [
"code"
]
},
{ {
"login": "martinfrancois", "login": "martinfrancois",
"name": "François Martin", "name": "François Martin",

@ -0,0 +1 @@
package-lock=false

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2017 Dawn Labs Copyright (c) 2018 Dawn Labs
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

@ -41,8 +41,12 @@ After you've customized your image you can either Tweet a link to the image, or
## Community ## Community
Check out these projects our awesome community has created: Check out these projects our awesome community has created:
- [IntelliJ IDEA `carbon-now-sh`](https://plugins.jetbrains.com/plugin/10469-carbon-now-sh) - Open up the selection in your current IntelliJ IDEA file in Carbon through a context menu
- [Atom `carbon-now-sh`](https://atom.io/packages/carbon-now-sh) - Open up your current Atom file in Carbon with `shift-cmd-A` - [Atom `carbon-now-sh`](https://atom.io/packages/carbon-now-sh) - Open up your current Atom file in Carbon with `shift-cmd-A`
- [VS Code `carbon-now-sh`](https://marketplace.visualstudio.com/items?itemName=ericadamski.carbon-now-sh) - Open up your current VS Code file in Carbon with command `carbon` - [VS Code `carbon-now-sh`](https://marketplace.visualstudio.com/items?itemName=ericadamski.carbon-now-sh) - Open up your current VS Code file in Carbon with command `carbon`
- [Sublime Text 3 `carbon-now-sh`](https://github.com/molnarmark/carbonSublime) - Open up the selection in your current Sublime Text 3 file with a custom bound key
- [Vim `carbon-now-sh`](https://github.com/kristijanhusak/vim-carbon-now-sh) - Open up the selection in your current Vim/Neovim using function `CarbonNowSh()`
- [Emacs `carbon-now-sh`](https://github.com/veelenga/carbon-now-sh.el) - Open up the selection in your current Emacs using interactive function `carbon-now-sh`
## Contribute ## Contribute
@ -73,5 +77,6 @@ Thanks goes out to all these wonderful people ([emoji key](https://github.com/ke
| [<img src="https://avatars0.githubusercontent.com/u/10078572?v=4" width="100px;"/><br /><sub><b>briandennis</b></sub>](https://github.com/briandennis)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=briandennis "Code") [📖](https://github.com/dawnlabs/carbon/commits?author=briandennis "Documentation") [🚇](#infra-briandennis "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-briandennis "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/8397708?v=4" width="100px;"/><br /><sub><b>mfix22</b></sub>](https://github.com/mfix22)<br />[💬](#question-mfix22 "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=mfix22 "Code") [🤔](#ideas-mfix22 "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/10369094?v=4" width="100px;"/><br /><sub><b>jakedex</b></sub>](https://github.com/jakedex)<br />[💬](#question-jakedex "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=jakedex "Code") [🎨](#design-jakedex "Design") [📹](#video-jakedex "Videos") | [<img src="https://avatars1.githubusercontent.com/u/10191084?v=4" width="100px;"/><br /><sub><b>andrewda</b></sub>](https://github.com/andrewda)<br />[💬](#question-andrewda "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=andrewda "Code") [🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Aandrewda "Bug reports") [👀](#review-andrewda "Reviewed Pull Requests") | [<img src="https://avatars2.githubusercontent.com/u/14703164?v=4" width="100px;"/><br /><sub><b>yeskunall</b></sub>](https://github.com/yeskunall)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=yeskunall "Code") [📖](https://github.com/dawnlabs/carbon/commits?author=yeskunall "Documentation") [🔧](#tool-yeskunall "Tools") [🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Ayeskunall "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/2652676?v=4" width="100px;"/><br /><sub><b>stoshfabricius</b></sub>](https://github.com/stoshfabricius)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=stoshfabricius "Code") | [<img src="https://avatars1.githubusercontent.com/u/11639896?v=4" width="100px;"/><br /><sub><b>jkling38</b></sub>](https://github.com/jkling38)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=jkling38 "Documentation") | | [<img src="https://avatars0.githubusercontent.com/u/10078572?v=4" width="100px;"/><br /><sub><b>briandennis</b></sub>](https://github.com/briandennis)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=briandennis "Code") [📖](https://github.com/dawnlabs/carbon/commits?author=briandennis "Documentation") [🚇](#infra-briandennis "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-briandennis "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/8397708?v=4" width="100px;"/><br /><sub><b>mfix22</b></sub>](https://github.com/mfix22)<br />[💬](#question-mfix22 "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=mfix22 "Code") [🤔](#ideas-mfix22 "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/10369094?v=4" width="100px;"/><br /><sub><b>jakedex</b></sub>](https://github.com/jakedex)<br />[💬](#question-jakedex "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=jakedex "Code") [🎨](#design-jakedex "Design") [📹](#video-jakedex "Videos") | [<img src="https://avatars1.githubusercontent.com/u/10191084?v=4" width="100px;"/><br /><sub><b>andrewda</b></sub>](https://github.com/andrewda)<br />[💬](#question-andrewda "Answering Questions") [💻](https://github.com/dawnlabs/carbon/commits?author=andrewda "Code") [🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Aandrewda "Bug reports") [👀](#review-andrewda "Reviewed Pull Requests") | [<img src="https://avatars2.githubusercontent.com/u/14703164?v=4" width="100px;"/><br /><sub><b>yeskunall</b></sub>](https://github.com/yeskunall)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=yeskunall "Code") [📖](https://github.com/dawnlabs/carbon/commits?author=yeskunall "Documentation") [🔧](#tool-yeskunall "Tools") [🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Ayeskunall "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/2652676?v=4" width="100px;"/><br /><sub><b>stoshfabricius</b></sub>](https://github.com/stoshfabricius)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=stoshfabricius "Code") | [<img src="https://avatars1.githubusercontent.com/u/11639896?v=4" width="100px;"/><br /><sub><b>jkling38</b></sub>](https://github.com/jkling38)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=jkling38 "Documentation") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars1.githubusercontent.com/u/225946?v=4" width="100px;"/><br /><sub><b>otobrglez</b></sub>](https://github.com/otobrglez)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=otobrglez "Code") | [<img src="https://avatars3.githubusercontent.com/u/11488612?v=4" width="100px;"/><br /><sub><b>darahak</b></sub>](https://github.com/darahak)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=darahak "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/246651?v=4" width="100px;"/><br /><sub><b>dom96</b></sub>](https://github.com/dom96)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=dom96 "Code") | [<img src="https://avatars3.githubusercontent.com/u/784056?v=4" width="100px;"/><br /><sub><b>elrumordelaluz</b></sub>](https://github.com/elrumordelaluz)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=elrumordelaluz "Code") | [<img src="https://avatars2.githubusercontent.com/u/21217?v=4" width="100px;"/><br /><sub><b>cjb</b></sub>](https://github.com/cjb)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=cjb "Code") | [<img src="https://avatars1.githubusercontent.com/u/5427083?v=4" width="100px;"/><br /><sub><b>Krzysztof-Cieslak</b></sub>](https://github.com/Krzysztof-Cieslak)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=Krzysztof-Cieslak "Code") | [<img src="https://avatars0.githubusercontent.com/u/2369851?v=4" width="100px;"/><br /><sub><b>fernahh</b></sub>](https://github.com/fernahh)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=fernahh "Documentation") | | [<img src="https://avatars1.githubusercontent.com/u/225946?v=4" width="100px;"/><br /><sub><b>otobrglez</b></sub>](https://github.com/otobrglez)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=otobrglez "Code") | [<img src="https://avatars3.githubusercontent.com/u/11488612?v=4" width="100px;"/><br /><sub><b>darahak</b></sub>](https://github.com/darahak)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=darahak "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/246651?v=4" width="100px;"/><br /><sub><b>dom96</b></sub>](https://github.com/dom96)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=dom96 "Code") | [<img src="https://avatars3.githubusercontent.com/u/784056?v=4" width="100px;"/><br /><sub><b>elrumordelaluz</b></sub>](https://github.com/elrumordelaluz)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=elrumordelaluz "Code") | [<img src="https://avatars2.githubusercontent.com/u/21217?v=4" width="100px;"/><br /><sub><b>cjb</b></sub>](https://github.com/cjb)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=cjb "Code") | [<img src="https://avatars1.githubusercontent.com/u/5427083?v=4" width="100px;"/><br /><sub><b>Krzysztof-Cieslak</b></sub>](https://github.com/Krzysztof-Cieslak)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=Krzysztof-Cieslak "Code") | [<img src="https://avatars0.githubusercontent.com/u/2369851?v=4" width="100px;"/><br /><sub><b>fernahh</b></sub>](https://github.com/fernahh)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=fernahh "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/10941033?v=4" width="100px;"/><br /><sub><b>g3r4n</b></sub>](https://github.com/g3r4n)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=g3r4n "Code") | [<img src="https://avatars0.githubusercontent.com/u/55830?v=4" width="100px;"/><br /><sub><b>Mat Gadd</b></sub>](http://drarok.com/)<br />[🐛](https://github.com/dawnlabs/carbon/issues?q=author%3ADrarok "Bug reports") [💻](https://github.com/dawnlabs/carbon/commits?author=Drarok "Code") | [<img src="https://avatars1.githubusercontent.com/u/11805775?v=4" width="100px;"/><br /><sub><b>Brad Davies</b></sub>](https://bradlab.co.uk)<br />[🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Avarbrad "Bug reports") [💻](https://github.com/dawnlabs/carbon/commits?author=varbrad "Code") | [<img src="https://avatars1.githubusercontent.com/u/9087886?v=4" width="100px;"/><br /><sub><b>Rafael Câmara</b></sub>](http://www.rafaelcamaram.com/)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=rafaelcamaram "Code") | [<img src="https://avatars1.githubusercontent.com/u/2212006?v=4" width="100px;"/><br /><sub><b>Gleb Bahmutov</b></sub>](https://glebbahmutov.com/)<br />[⚠️](https://github.com/dawnlabs/carbon/commits?author=bahmutov "Tests") [🔧](#tool-bahmutov "Tools") | [<img src="https://avatars2.githubusercontent.com/u/10677789?v=4" width="100px;"/><br /><sub><b>Iván Munguía</b></sub>](https://ivan-munguia.netlify.com)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=warborn "Code") | [<img src="https://avatars1.githubusercontent.com/u/14319020?v=4" width="100px;"/><br /><sub><b>François Martin</b></sub>](https://github.com/martinfrancois)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=martinfrancois "Code") | | [<img src="https://avatars1.githubusercontent.com/u/10941033?v=4" width="100px;"/><br /><sub><b>g3r4n</b></sub>](https://github.com/g3r4n)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=g3r4n "Code") | [<img src="https://avatars0.githubusercontent.com/u/55830?v=4" width="100px;"/><br /><sub><b>Mat Gadd</b></sub>](http://drarok.com/)<br />[🐛](https://github.com/dawnlabs/carbon/issues?q=author%3ADrarok "Bug reports") [💻](https://github.com/dawnlabs/carbon/commits?author=Drarok "Code") | [<img src="https://avatars1.githubusercontent.com/u/11805775?v=4" width="100px;"/><br /><sub><b>Brad Davies</b></sub>](https://bradlab.co.uk)<br />[🐛](https://github.com/dawnlabs/carbon/issues?q=author%3Avarbrad "Bug reports") [💻](https://github.com/dawnlabs/carbon/commits?author=varbrad "Code") | [<img src="https://avatars1.githubusercontent.com/u/9087886?v=4" width="100px;"/><br /><sub><b>Rafael Câmara</b></sub>](http://www.rafaelcamaram.com/)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=rafaelcamaram "Code") | [<img src="https://avatars1.githubusercontent.com/u/2212006?v=4" width="100px;"/><br /><sub><b>Gleb Bahmutov</b></sub>](https://glebbahmutov.com/)<br />[⚠️](https://github.com/dawnlabs/carbon/commits?author=bahmutov "Tests") [🔧](#tool-bahmutov "Tools") | [<img src="https://avatars2.githubusercontent.com/u/10677789?v=4" width="100px;"/><br /><sub><b>Iván Munguía</b></sub>](https://ivan-munguia.netlify.com)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=warborn "Code") | [<img src="https://avatars1.githubusercontent.com/u/2755722?v=4" width="100px;"/><br /><sub><b>Dillon Mulroy</b></sub>](https://dillonmulroy.com)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=dmmulroy "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/253398?v=4" width="100px;"/><br /><sub><b>Nihad Abbasov</b></sub>](https://github.com/NARKOZ)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=NARKOZ "Code") | [<img src="https://avatars1.githubusercontent.com/u/7328625?v=4" width="100px;"/><br /><sub><b>Hugo Torzuoli</b></sub>](https://torzuolih.github.io)<br />[📖](https://github.com/dawnlabs/carbon/commits?author=TorzuoliH "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/1906977?v=4" width="100px;"/><br /><sub><b>Bruno C. Couto</b></sub>](https://github.com/bruno02221)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=bruno02221 "Code") | [<img src="https://avatars2.githubusercontent.com/u/13263073?v=4" width="100px;"/><br /><sub><b>Mark Molnar</b></sub>](https://github.com/molnarmark)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=molnarmark "Code") | [<img src="https://avatars1.githubusercontent.com/u/1459450?v=4" width="100px;"/><br /><sub><b>Takahiko Inayama</b></sub>](https://www.behance.net/tetra2000)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=TETRA2000 "Code") | [<img src="https://avatars1.githubusercontent.com/u/14319020?v=4" width="100px;"/><br /><sub><b>François Martin</b></sub>](https://github.com/martinfrancois)<br />[💻](https://github.com/dawnlabs/carbon/commits?author=martinfrancois "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->

@ -1,10 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Don't deploy if commit contains WIP # Don't deploy if commit contains WIP
# TODO revisit this when deploy is stable
exit 0
set -e set -e
if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
if [[ -z ${NOW_TOKEN+x} ]]; then
if [[ $(git log -1 --pretty=%B --no-merges) != *"WIP"* ]]; then if [[ $(git log -1 --pretty=%B --no-merges) != *"WIP"* ]]; then
NOW_URL=$(now -e NODE_ENV=production --public --no-clipboard --team=dawn --token="$NOW_TOKEN") NOW_URL=$(now -e NODE_ENV=production --public --no-clipboard --team=dawn --token="$NOW_TOKEN") || exit 0
curl -X POST \ curl -X POST \
-H 'Content-type: application/json' \ -H 'Content-type: application/json' \
@ -12,4 +13,3 @@ if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
"$DAWN_SLACK_WEBHOOK" "$DAWN_SLACK_WEBHOOK"
fi fi
fi fi
fi

@ -3,7 +3,8 @@ import enhanceWithClickOutside from 'react-click-outside'
import { SketchPicker } from 'react-color' import { SketchPicker } from 'react-color'
import WindowPointer from './WindowPointer' import WindowPointer from './WindowPointer'
import ImagePicker from './ImagePicker' import ImagePicker from './ImagePicker'
import { COLORS } from '../lib/constants' import { COLORS, DEFAULT_BG_COLOR } from '../lib/constants'
import { validateColor } from '../lib/colors'
import { parseRGBA, capitalizeFirstLetter } from '../lib/util' import { parseRGBA, capitalizeFirstLetter } from '../lib/util'
class BackgroundSelect extends React.Component { class BackgroundSelect extends React.Component {
@ -46,6 +47,10 @@ class BackgroundSelect extends React.Component {
.replace(/\//g, '&#x2F;') .replace(/\//g, '&#x2F;')
: background : background
if (!validateColor(background)) {
background = DEFAULT_BG_COLOR
}
return ( return (
<div className="bg-select-container"> <div className="bg-select-container">
<div className="bg-select-display"> <div className="bg-select-display">

@ -13,25 +13,10 @@ import {
DEFAULT_LANGUAGE, DEFAULT_LANGUAGE,
LANGUAGES, LANGUAGES,
LANGUAGE_MODE_HASH, LANGUAGE_MODE_HASH,
LANGUAGE_NAME_HASH LANGUAGE_NAME_HASH,
DEFAULT_SETTINGS
} from '../lib/constants' } from '../lib/constants'
const DEFAULT_SETTINGS = {
paddingVertical: '50px',
paddingHorizontal: '50px',
marginVertical: '45px',
marginHorizontal: '45px',
backgroundMode: 'color',
backgroundColor: 'rgba(171, 184, 195, 1)',
dropShadowOffsetY: '20px',
dropShadowBlurRadius: '68px',
theme: 'seti',
windowTheme: 'none',
language: DEFAULT_LANGUAGE,
fontFamily: 'Hack',
fontSize: '14px'
}
class Carbon extends React.Component { class Carbon extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -42,6 +27,7 @@ class Carbon extends React.Component {
} }
this.handleLanguageChange = this.handleLanguageChange.bind(this) this.handleLanguageChange = this.handleLanguageChange.bind(this)
this.handleTitleBarChange = this.handleTitleBarChange.bind(this)
this.codeUpdated = this.codeUpdated.bind(this) this.codeUpdated = this.codeUpdated.bind(this)
} }
@ -68,6 +54,10 @@ class Carbon extends React.Component {
this.props.updateCode(newCode) this.props.updateCode(newCode)
} }
handleTitleBarChange(newTitle) {
this.props.updateTitleBar(newTitle)
}
handleLanguageChange = debounce( handleLanguageChange = debounce(
(newCode, config) => { (newCode, config) => {
const props = (config && config.customProps) || this.props const props = (config && config.customProps) || this.props
@ -119,7 +109,12 @@ class Carbon extends React.Component {
if (this.state.loading === false) { if (this.state.loading === false) {
content = ( content = (
<div id="container"> <div id="container">
{config.windowControls ? <WindowControls theme={config.windowTheme} /> : null} {config.windowControls ? (
<WindowControls
theme={config.windowTheme}
handleTitleBarChange={this.handleTitleBarChange}
/>
) : null}
<CodeMirror <CodeMirror
className={`CodeMirror__container window-theme__${config.windowTheme}`} className={`CodeMirror__container window-theme__${config.windowTheme}`}
onBeforeChange={(editor, meta, code) => this.codeUpdated(code)} onBeforeChange={(editor, meta, code) => this.codeUpdated(code)}

@ -1,145 +1,171 @@
import React from 'react' import React from 'react'
import enhanceWithClickOutside from 'react-click-outside' import Downshift from 'downshift'
import ArrowDown from './svg/Arrowdown' import ArrowDown from './svg/Arrowdown'
import Checkmark from './svg/Checkmark' import CheckMark from './svg/Checkmark'
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants'
class Dropdown extends React.Component { const Dropdown = ({ button, color, list, selected, onChange }) => {
constructor(props) { return (
super(props) <Downshift
this.state = { render={renderDropdown({ button, color, list, selected })}
isVisible: false selectedItem={selected}
} defaultHighlightedIndex={list.findIndex(it => it === selected)}
this.select = this.select.bind(this) itemToString={item => item.name}
this.toggle = this.toggle.bind(this) onChange={onChange}
stateReducer={reduceState(list)}
/>
)
} }
select(item) { const reduceState = list => (state, changes) => {
if (this.props.selected !== item) { switch (changes.type) {
this.props.onChange(item) case Downshift.stateChangeTypes.keyDownArrowUp:
case Downshift.stateChangeTypes.keyDownArrowDown:
return { ...changes, selectedItem: list[changes.highlightedIndex] }
default:
return changes
} }
} }
toggle() { const renderDropdown = ({ button, color, list, selected }) => ({
this.setState({ isVisible: !this.state.isVisible }) isOpen,
} highlightedIndex,
setHighlightedIndex,
handleClickOutside() { selectHighlightedItem,
this.setState({ isVisible: false }) selectedItem,
getRootProps,
getButtonProps,
getInputProps,
getItemProps
}) => {
return (
<DropdownContainer
{...getRootProps({ refKey: 'innerRef' })}
minWidth={minWidth(button, selected, list)}
>
<SelectedItem {...getButtonProps()} {...getInputProps()} isOpen={isOpen} color={color}>
{selectedItem.name}
</SelectedItem>
{isOpen ? (
<ListItems color={color}>
{list.map((item, index) => (
<ListItem
key={index}
color={color}
{...getItemProps({
item,
isSelected: selectedItem === item,
isHighlighted: highlightedIndex === index
})}
>
{item.name}
</ListItem>
))}
</ListItems>
) : null}
</DropdownContainer>
)
} }
renderListItems() { const DropdownContainer = ({ children, innerRef, minWidth, ...rest }) => {
return this.props.list.map((item, i) => ( return (
<div className="dropdown-list-item" key={i} onClick={this.select.bind(null, item)}> <div {...rest} ref={innerRef} className="dropdown-container">
<span>{item.name}</span> {children}
{this.props.selected === item ? <Checkmark /> : null}
<style jsx>{` <style jsx>{`
.dropdown-list-item { .dropdown-container {
display: flex; min-width: ${minWidth}px;
align-items: center; cursor: pointer;
justify-content: space-between;
background: ${COLORS.BLACK};
user-select: none;
padding: 8px 16px;
border-bottom: 1px solid ${this.props.color || COLORS.SECONDARY};
}
.dropdown-list-item span {
color: ${this.props.color || '#fff'};
}
.dropdown-list-item:hover {
background: ${COLORS.HOVER};
}
.dropdown-list-item:last-of-type {
border-bottom: none;
border-radius: 0px 0px 2px 2px;
} }
`}</style> `}</style>
</div> </div>
)) )
} }
render() { const SelectedItem = ({ children, isOpen, color, ...rest }) => {
const color = this.props.color || COLORS.SECONDARY const itemColor = color || COLORS.SECONDARY
// find longest list value in number of characters
const list = this.props.button ? [...this.props.list, this.props.selected] : this.props.list
const MIN_WIDTH = list.reduce(
(max, { name }) => (name && name.length > max ? name.length : max),
0
)
return ( return (
<div <span {...rest} tabIndex="0" className="dropdown-display">
className="dropdown-container" <span className="dropdown-display-text">{children}</span>
style={{ minWidth: MIN_WIDTH * 10 + 32 }} <div className={`dropdown-arrow ${isOpen ? 'is-reverse' : ''}`}>
onClick={this.toggle} <ArrowDown fill={itemColor} />
>
<div className={`dropdown-display ${this.state.isVisible ? 'is-visible' : ''}`}>
<span style={{ minWidth: '4rem' }}>{this.props.selected.name}</span>
<div className="arrow-down">
<ArrowDown fill={color} />
</div>
</div> </div>
<div className="dropdown-list">{this.renderListItems()}</div>
<style jsx>{` <style jsx>{`
.arrow-down {
position: absolute;
right: 16px;
}
.is-visible > .arrow-down {
transform: rotate(180deg);
}
.is-visible {
border-radius: 3px 3px 0px 0px !important;
}
.dropdown-container {
height: 100%;
cursor: pointer;
}
.dropdown-display { .dropdown-display {
display: flex;
align-items: center;
height: 100%; height: 100%;
border: 1px solid ${color}; border: 1px solid ${itemColor};
border-radius: 3px; border-radius: 3px;
user-select: none;
padding: 8px 16px; padding: 8px 16px;
display: flex; outline: none;
justify-content: flex-start;
align-items: center;
position: relative;
z-index: 1;
} }
.dropdown-display span {
color: ${color};
}
.dropdown-display:hover { .dropdown-display:hover {
background: ${COLORS.HOVER}; background: ${COLORS.HOVER};
} }
.dropdown-display-text {
.is-visible + .dropdown-list { flex-grow: 1;
display: block; color: ${itemColor};
}
.dropdown-arrow.is-reverse {
transform: rotate(180deg);
}
`}</style>
</span>
)
} }
const ListItems = ({ children, color }) => {
return (
<ul className="dropdown-list">
{children}
<style jsx>{`
.dropdown-list { .dropdown-list {
display: none;
margin-top: -1px; margin-top: -1px;
border: 1px solid ${color}; border: 1px solid ${color || COLORS.SECONDARY};
border-radius: 0px 0px 3px 3px; border-radius: 0 0 3px 3px;
max-height: 350px; max-height: 350px;
overflow-y: scroll; overflow-y: scroll;
} }
`}</style> `}</style>
</div> </ul>
) )
} }
const ListItem = ({ children, color, isHighlighted, isSelected, ...rest }) => {
const itemColor = color || COLORS.SECONDARY
return (
<li {...rest} className="dropdown-list-item">
<span className="dropdown-list-item-text">{children}</span>
{isSelected ? <CheckMark /> : null}
<style jsx>{`
.dropdown-list-item {
display: flex;
align-items: center;
background: ${isHighlighted ? COLORS.HOVER : COLORS.BLACK};
padding: 8px 16px;
border-bottom: 1px solid ${itemColor};
}
.dropdown-list-item:hover {
background: ${COLORS.HOVER};
}
.dropdown-list-item-text {
flex-grow: 1;
color: ${itemColor};
}
`}</style>
</li>
)
}
function minWidth(isButton, selected, list) {
const items = isButton ? [...list, selected] : list
return items.reduce((max, { name }) => {
const wordSize = name.length * 10 + 32
return wordSize > max ? wordSize : max
}, 0)
} }
export default enhanceWithClickOutside(Dropdown) export default Dropdown

@ -0,0 +1,88 @@
import React from 'react'
import { COLORS } from '../lib/constants'
import Checkmark from './svg/Checkmark'
import { EXPORT_SIZES } from '../lib/constants'
export default class extends React.Component {
constructor(props) {
super()
this.state = { isVisible: false }
this.select = this.select.bind(this)
this.toggle = this.toggle.bind(this)
}
select(exportSize) {
if (this.props.selected !== exportSize) {
this.props.onChange(exportSize)
}
}
toggle() {
this.setState({ isVisible: !this.state.isVisible })
}
renderExportSizes() {
return EXPORT_SIZES.map((exportSize, i) => {
return (
<div className="list-item" key={i} onClick={this.select.bind(null, exportSize.id)}>
<span style={{ ExportSize: exportSize.id }}>{exportSize.name}</span>
{this.props.selected === exportSize.id ? <Checkmark /> : null}
<style jsx>{`
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
user-select: none;
padding: 8px 16px;
border-bottom: 1px solid ${COLORS.SECONDARY};
background: rgba(255, 255, 255, 0.165);
}
.list-item:first-of-type {
border-top: 1px solid ${COLORS.SECONDARY};
}
.list-item:last-of-type {
border-bottom: none;
}
`}</style>
</div>
)
})
}
render() {
const selectedExportSize =
EXPORT_SIZES.filter(exportSize => exportSize.id === this.props.selected)[0] || {}
return (
<div className="export-size-select-container">
<div
onClick={this.toggle}
className={`display ${this.state.isVisible ? 'is-visible' : ''}`}
>
<span className="label">Export Size</span>
<span style={{ exportSize: selectedExportSize.id }}>{selectedExportSize.name}</span>
</div>
<div className="list">{this.renderExportSizes()}</div>
<style jsx>{`
.display {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
user-select: none;
padding: 8px;
}
.list {
display: none;
margin-top: -1px;
max-height: 80px;
overflow-y: scroll;
}
.is-visible + .list {
display: block;
}
`}</style>
</div>
)
}
}

@ -48,7 +48,7 @@ export default class extends React.Component {
} }
render() { render() {
const selectedFont = FONTS.filter(font => font.id === this.props.selected)[0] const selectedFont = FONTS.filter(font => font.id === this.props.selected)[0] || {}
return ( return (
<div className="font-select-container"> <div className="font-select-container">
<div <div

@ -3,8 +3,8 @@ import Link from 'next/link'
import { COLORS } from '../lib/constants' import { COLORS } from '../lib/constants'
const Footer = props => ( const Footer = props => (
<div className="mt3"> <footer role="contentinfo" className="mt3">
<div className="mt3"> <nav role="navigation" className="mt3">
<Link href="/about"> <Link href="/about">
<a className="link">about</a> <a className="link">about</a>
</Link> </Link>
@ -14,7 +14,7 @@ const Footer = props => (
<a className="link" href="https://github.com/dawnlabs/carbon"> <a className="link" href="https://github.com/dawnlabs/carbon">
source source
</a> </a>
</div> </nav>
<div className="mt3 mb2"> <div className="mt3 mb2">
a project by{' '} a project by{' '}
<a className="dawn-link" href="https://dawnlabs.io"> <a className="dawn-link" href="https://dawnlabs.io">
@ -23,9 +23,12 @@ const Footer = props => (
¬ ¬
</div> </div>
<style jsx>{` <style jsx>{`
div > div { footer {
text-align: center;
font-size: 14px; font-size: 14px;
}
footer > div {
text-align: center;
color: ${COLORS.GRAY}; color: ${COLORS.GRAY};
} }
@ -46,7 +49,7 @@ const Footer = props => (
color: #fff; color: #fff;
} }
`}</style> `}</style>
</div> </footer>
) )
export default Footer export default Footer

@ -2,7 +2,7 @@ import React from 'react'
import Logo from './svg/Logo' import Logo from './svg/Logo'
const Header = ({ enableHeroText }) => ( const Header = ({ enableHeroText }) => (
<div className="header mb4"> <header role="banner" className="header mb4">
<div className="header-content"> <div className="header-content">
<a id="link-home" href="/" aria-label="Home"> <a id="link-home" href="/" aria-label="Home">
<Logo /> <Logo />
@ -33,7 +33,7 @@ const Header = ({ enableHeroText }) => (
text-align: center; text-align: center;
} }
`}</style> `}</style>
</div> </header>
) )
export default Header export default Header

@ -4,7 +4,7 @@ import Header from './Header'
import Footer from './Footer' import Footer from './Footer'
export default ({ children, enableHeroText }) => ( export default ({ children, enableHeroText }) => (
<div className="main mt4 mb4"> <main className="main mt4 mb4">
<Meta /> <Meta />
<Header enableHeroText={enableHeroText} /> <Header enableHeroText={enableHeroText} />
<div className="page">{children}</div> <div className="page">{children}</div>
@ -19,5 +19,5 @@ export default ({ children, enableHeroText }) => (
align-items: center; align-items: center;
} }
`}</style> `}</style>
</div> </main>
) )

@ -3,6 +3,7 @@ import enhanceWithClickOutside from 'react-click-outside'
import SettingsIcon from './svg/Settings' import SettingsIcon from './svg/Settings'
import ThemeSelect from './ThemeSelect' import ThemeSelect from './ThemeSelect'
import FontSelect from './FontSelect' import FontSelect from './FontSelect'
import ExportSizeSelect from './ExportSizeSelect'
import Slider from './Slider' import Slider from './Slider'
import Toggle from './Toggle' import Toggle from './Toggle'
import WindowPointer from './WindowPointer' import WindowPointer from './WindowPointer'
@ -71,6 +72,7 @@ class Settings extends React.Component {
<Slider <Slider
label="Padding (vertical)" label="Padding (vertical)"
value={this.props.enabled.paddingVertical || 16} value={this.props.enabled.paddingVertical || 16}
maxValue={200}
onChange={this.props.onChange.bind(null, 'paddingVertical')} onChange={this.props.onChange.bind(null, 'paddingVertical')}
/> />
<Slider <Slider
@ -93,6 +95,20 @@ class Settings extends React.Component {
value={this.props.enabled.dropShadowBlurRadius || 68} value={this.props.enabled.dropShadowBlurRadius || 68}
onChange={this.props.onChange.bind(null, 'dropShadowBlurRadius')} onChange={this.props.onChange.bind(null, 'dropShadowBlurRadius')}
/> />
<Toggle
label="Squared image"
enabled={this.props.enabled.squaredImage}
onChange={this.props.onChange.bind(null, 'squaredImage')}
/>
<ExportSizeSelect
selected={this.props.enabled.exportSize || '2x'}
onChange={this.props.onChange.bind(null, 'exportSize')}
/>
<Toggle
label="Reset settings"
enabled={false}
onChange={this.props.resetDefaultSettings}
/>
</Collapse> </Collapse>
</div> </div>
<style jsx>{` <style jsx>{`

@ -1,9 +1,12 @@
import React from 'react' import React from 'react'
import { Controls, ControlsBW } from './svg/Controls' import { Controls, ControlsBW } from './svg/Controls'
export default ({ theme }) => ( export default ({ theme, handleTitleBarChange }) => (
<div className="window-controls"> <div className="window-controls">
{theme === 'bw' ? <ControlsBW /> : <Controls />} {theme === 'bw' ? <ControlsBW /> : <Controls />}
<div className="window-title-container">
<input type="text" spellCheck="false" onChange={e => handleTitleBarChange(e.target.value)} />
</div>
<style jsx> <style jsx>
{` {`
div { div {
@ -13,6 +16,25 @@ export default ({ theme }) => (
margin-left: 18px; margin-left: 18px;
z-index: 2; z-index: 2;
} }
.window-title-container {
position: absolute;
margin: 0px;
top: -3px;
left: -9px;
width: 100%;
text-align: center;
}
input {
width: 250px;
background: none;
outline: none;
border: none;
color: white;
text-align: center;
font-size: 14px;
}
`} `}
</style> </style>
</div> </div>

@ -1,5 +1,18 @@
export default () => ( export default () => (
<style jsx global>{` <style jsx global>{`
@font-face {
font-family: 'Iosevka';
src: url('//cdn.jsdelivr.net/npm/@typopro/web-iosevka@3.7.5/TypoPRO-iosevka-term-bold.eot');
src: url('//cdn.jsdelivr.net/npm/@typopro/web-iosevka@3.7.5/TypoPRO-iosevka-term-bold.eot')
format('embedded-opentype'),
url('//cdn.jsdelivr.net/npm/@typopro/web-iosevka@3.7.5/TypoPRO-iosevka-term-bold.woff')
format('woff'),
url('//cdn.jsdelivr.net/npm/@typopro/web-iosevka@3.7.5/TypoPRO-iosevka-term-bold.ttf')
format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face { @font-face {
font-family: 'Hack'; font-family: 'Hack';
src: url('//cdn.jsdelivr.net/font-hack/2.020/fonts/woff2/hack-regular-webfont.woff2?v=2.020') src: url('//cdn.jsdelivr.net/font-hack/2.020/fonts/woff2/hack-regular-webfont.woff2?v=2.020')

@ -13,9 +13,9 @@ export const Sharp = () => (
</defs> </defs>
<g fill="none" fillRule="evenodd"> <g fill="none" fillRule="evenodd">
<mask id="b1" fill="white"> <mask id="b1" fill="white">
<use xlinkHref="#a" /> <use xlinkHref="#a1" />
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a1" />
<g transform="translate(16 32)" mask="url(#b)"> <g transform="translate(16 32)" mask="url(#b)">
<path <path
fill="#000000" fill="#000000"
@ -62,9 +62,9 @@ export const BW = () => (
</defs> </defs>
<g fill="none" fillRule="evenodd"> <g fill="none" fillRule="evenodd">
<mask id="b2" fill="white"> <mask id="b2" fill="white">
<use xlinkHref="#a" /> <use xlinkHref="#a1" />
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a1" />
<g transform="translate(17 33)" mask="url(#b)"> <g transform="translate(17 33)" mask="url(#b)">
<path <path
fill="#000000" fill="#000000"
@ -94,9 +94,9 @@ export const None = () => (
</defs> </defs>
<g fill="none" fillRule="evenodd"> <g fill="none" fillRule="evenodd">
<mask id="b3" fill="white"> <mask id="b3" fill="white">
<use xlinkHref="#a" /> <use xlinkHref="#a1" />
</mask> </mask>
<use fill="#616161" xlinkHref="#a" /> <use fill="#616161" xlinkHref="#a1" />
<g transform="translate(16 32)" mask="url(#b)"> <g transform="translate(16 32)" mask="url(#b)">
<path <path
fill="#000000" fill="#000000"

@ -8,8 +8,8 @@ import { editorVisible } from '../support'
// so instead visit the desired url in each test // so instead visit the desired url in each test
describe('background color', () => { describe('background color', () => {
const bgColor = '.colorpicker-container .bg-color' const bgColor = '.bg-color-container .bg-color'
const picker = '.colorpicker-picker' const picker = '.bg-select-pickers'
const openPicker = () => { const openPicker = () => {
cy.get(bgColor).click() cy.get(bgColor).click()

@ -0,0 +1,13 @@
/* eslint-env mocha */
/* global cy */
import { editorVisible } from '../support'
describe('Gist', () => {
const themeDropdown = () => cy.get('#toolbar .dropdown-container').first()
it('Should pull text from the first Gist file', () => {
cy.visit('/3208813b324d82a9ebd197e4b1c3bae8')
editorVisible()
cy.contains('Y-Combinator implemented in JavaScript')
})
})

@ -0,0 +1,600 @@
const colors = new Set([
'indian red',
'crimson',
'lightpink',
'lightpink 1',
'lightpink 2',
'lightpink 3',
'lightpink 4',
'pink',
'pink 1',
'pink 2',
'pink 3',
'pink 4',
'palevioletred',
'palevioletred 1',
'palevioletred 2',
'palevioletred 3',
'palevioletred 4',
'lavenderblush 1',
'lavenderblush',
'lavenderblush 2',
'lavenderblush 3',
'lavenderblush 4',
'violetred 1',
'violetred 2',
'violetred 3',
'violetred 4',
'hotpink',
'hotpink 1',
'hotpink 2',
'hotpink 3',
'hotpink 4',
'raspberry',
'deeppink 1',
'deeppink',
'deeppink 2',
'deeppink 3',
'deeppink 4',
'maroon 1',
'maroon 2',
'maroon 3',
'maroon 4',
'mediumvioletred',
'violetred',
'orchid',
'orchid 1',
'orchid 2',
'orchid 3',
'orchid 4',
'thistle',
'thistle 1',
'thistle 2',
'thistle 3',
'thistle 4',
'plum 1',
'plum 2',
'plum 3',
'plum 4',
'plum',
'violet',
'magenta',
'fuchsia',
'magenta 2',
'magenta 3',
'magenta 4',
'darkmagenta',
'purple',
'mediumorchid',
'mediumorchid 1',
'mediumorchid 2',
'mediumorchid 3',
'mediumorchid 4',
'darkviolet',
'darkorchid',
'darkorchid 1',
'darkorchid 2',
'darkorchid 3',
'darkorchid 4',
'indigo',
'blueviolet',
'purple 1',
'purple 2',
'purple 3',
'purple 4',
'mediumpurple',
'mediumpurple 1',
'mediumpurple 2',
'mediumpurple 3',
'mediumpurple 4',
'darkslateblue',
'lightslateblue',
'mediumslateblue',
'slateblue',
'slateblue 1',
'slateblue 2',
'slateblue 3',
'slateblue 4',
'ghostwhite',
'lavender',
'blue',
'blue 2',
'blue 3',
'mediumblue',
'blue 4',
'darkblue',
'navy',
'midnightblue',
'cobalt',
'royalblue',
'royalblue 1',
'royalblue 2',
'royalblue 3',
'royalblue 4',
'cornflowerblue',
'lightsteelblue',
'lightsteelblue 1',
'lightsteelblue 2',
'lightsteelblue 3',
'lightsteelblue 4',
'lightslategray',
'slategray',
'slategray 1',
'slategray 2',
'slategray 3',
'slategray 4',
'dodgerblue 1',
'dodgerblue',
'dodgerblue 2',
'dodgerblue 3',
'dodgerblue 4',
'aliceblue',
'steelblue',
'steelblue 1',
'steelblue 2',
'steelblue 3',
'steelblue 4',
'lightskyblue',
'lightskyblue 1',
'lightskyblue 2',
'lightskyblue 3',
'lightskyblue 4',
'skyblue 1',
'skyblue 2',
'skyblue 3',
'skyblue 4',
'skyblue',
'deepskyblue 1',
'deepskyblue',
'deepskyblue 2',
'deepskyblue 3',
'deepskyblue 4',
'peacock',
'lightblue',
'lightblue 1',
'lightblue 2',
'lightblue 3',
'lightblue 4',
'powderblue',
'cadetblue 1',
'cadetblue 2',
'cadetblue 3',
'cadetblue 4',
'turquoise 1',
'turquoise 2',
'turquoise 3',
'turquoise 4',
'cadetblue',
'darkturquoise',
'azure 1',
'azure',
'azure 2',
'azure 3',
'azure 4',
'lightcyan 1',
'lightcyan',
'lightcyan 2',
'lightcyan 3',
'lightcyan 4',
'paleturquoise 1',
'paleturquoise 2',
'paleturquoise',
'paleturquoise 3',
'paleturquoise 4',
'darkslategray',
'darkslategray 1',
'darkslategray 2',
'darkslategray 3',
'darkslategray 4',
'cyan',
'aqua',
'cyan 2',
'cyan 3',
'cyan 4',
'darkcyan',
'teal',
'mediumturquoise',
'lightseagreen',
'manganeseblue',
'turquoise',
'coldgrey',
'turquoiseblue',
'aquamarine 1',
'aquamarine',
'aquamarine 2',
'aquamarine 3',
'mediumaquamarine',
'aquamarine 4',
'mediumspringgreen',
'mintcream',
'springgreen',
'springgreen 1',
'springgreen 2',
'springgreen 3',
'mediumseagreen',
'seagreen 1',
'seagreen 2',
'seagreen 3',
'seagreen 4',
'seagreen',
'emeraldgreen',
'mint',
'cobaltgreen',
'honeydew 1',
'honeydew',
'honeydew 2',
'honeydew 3',
'honeydew 4',
'darkseagreen',
'darkseagreen 1',
'darkseagreen 2',
'darkseagreen 3',
'darkseagreen 4',
'palegreen',
'palegreen 1',
'palegreen 2',
'lightgreen',
'palegreen 3',
'palegreen 4',
'limegreen',
'forestgreen',
'green 1',
'lime',
'green 2',
'green 3',
'green 4',
'green',
'darkgreen',
'sapgreen',
'lawngreen',
'chartreuse 1',
'chartreuse',
'chartreuse 2',
'chartreuse 3',
'chartreuse 4',
'greenyellow',
'darkolivegreen 1',
'darkolivegreen 2',
'darkolivegreen 3',
'darkolivegreen 4',
'darkolivegreen',
'olivedrab',
'olivedrab 1',
'olivedrab 2',
'olivedrab 3',
'yellowgreen',
'olivedrab 4',
'ivory 1',
'ivory',
'ivory 2',
'ivory 3',
'ivory 4',
'beige',
'lightyellow 1',
'lightyellow',
'lightyellow 2',
'lightyellow 3',
'lightyellow 4',
'lightgoldenrodyellow',
'yellow 1',
'yellow',
'yellow 2',
'yellow 3',
'yellow 4',
'warmgrey',
'olive',
'darkkhaki',
'khaki 1',
'khaki 2',
'khaki 3',
'khaki 4',
'khaki',
'palegoldenrod',
'lemonchiffon 1',
'lemonchiffon',
'lemonchiffon 2',
'lemonchiffon 3',
'lemonchiffon 4',
'lightgoldenrod 1',
'lightgoldenrod 2',
'lightgoldenrod 3',
'lightgoldenrod 4',
'banana',
'gold 1',
'gold',
'gold 2',
'gold 3',
'gold 4',
'cornsilk 1',
'cornsilk',
'cornsilk 2',
'cornsilk 3',
'cornsilk 4',
'goldenrod',
'goldenrod 1',
'goldenrod 2',
'goldenrod 3',
'goldenrod 4',
'darkgoldenrod',
'darkgoldenrod 1',
'darkgoldenrod 2',
'darkgoldenrod 3',
'darkgoldenrod 4',
'orange 1',
'orange',
'orange 2',
'orange 3',
'orange 4',
'floralwhite',
'oldlace',
'wheat',
'wheat 1',
'wheat 2',
'wheat 3',
'wheat 4',
'moccasin',
'papayawhip',
'blanchedalmond',
'navajowhite 1',
'navajowhite',
'navajowhite 2',
'navajowhite 3',
'navajowhite 4',
'eggshell',
'tan',
'brick',
'cadmiumyellow',
'antiquewhite',
'antiquewhite 1',
'antiquewhite 2',
'antiquewhite 3',
'antiquewhite 4',
'burlywood',
'burlywood 1',
'burlywood 2',
'burlywood 3',
'burlywood 4',
'bisque 1',
'bisque',
'bisque 2',
'bisque 3',
'bisque 4',
'melon',
'carrot',
'darkorange',
'darkorange 1',
'darkorange 2',
'darkorange 3',
'darkorange 4',
'tan 1',
'tan 2',
'tan 3',
'peru',
'tan 4',
'linen',
'peachpuff 1',
'peachpuff',
'peachpuff 2',
'peachpuff 3',
'peachpuff 4',
'seashell 1',
'seashell',
'seashell 2',
'seashell 3',
'seashell 4',
'sandybrown',
'rawsienna',
'chocolate',
'chocolate 1',
'chocolate 2',
'chocolate 3',
'chocolate 4',
'saddlebrown',
'ivoryblack',
'flesh',
'cadmiumorange',
'burntsienna',
'sienna',
'sienna 1',
'sienna 2',
'sienna 3',
'sienna 4',
'lightsalmon 1',
'lightsalmon',
'lightsalmon 2',
'lightsalmon 3',
'lightsalmon 4',
'coral',
'orangered 1',
'orangered',
'orangered 2',
'orangered 3',
'orangered 4',
'sepia',
'darksalmon',
'salmon 1',
'salmon 2',
'salmon 3',
'salmon 4',
'coral 1',
'coral 2',
'coral 3',
'coral 4',
'burntumber',
'tomato 1',
'tomato',
'tomato 2',
'tomato 3',
'tomato 4',
'salmon',
'mistyrose 1',
'mistyrose',
'mistyrose 2',
'mistyrose 3',
'mistyrose 4',
'snow 1',
'snow',
'snow 2',
'snow 3',
'snow 4',
'rosybrown',
'rosybrown 1',
'rosybrown 2',
'rosybrown 3',
'rosybrown 4',
'lightcoral',
'indianred',
'indianred 1',
'indianred 2',
'indianred 4',
'indianred 3',
'brown',
'brown 1',
'brown 2',
'brown 3',
'brown 4',
'firebrick',
'firebrick 1',
'firebrick 2',
'firebrick 3',
'firebrick 4',
'red 1',
'red',
'red 2',
'red 3',
'red 4',
'darkred',
'maroon',
'sgi beet',
'sgi slateblue',
'sgi lightblue',
'sgi teal',
'sgi chartreuse',
'sgi olivedrab',
'sgi brightgray',
'sgi salmon',
'sgi darkgray',
'sgi gray 12',
'sgi gray 16',
'sgi gray 32',
'sgi gray 36',
'sgi gray 52',
'sgi gray 56',
'sgi lightgray',
'sgi gray 72',
'sgi gray 76',
'sgi gray 92',
'sgi gray 96',
'white',
'white smoke',
'gray 96',
'gainsboro',
'lightgrey',
'silver',
'darkgray',
'gray',
'dimgray',
'gray 42',
'black',
'gray 99',
'gray 98',
'gray 97',
'gray 95',
'gray 94',
'gray 93',
'gray 92',
'gray 91',
'gray 90',
'gray 89',
'gray 88',
'gray 87',
'gray 86',
'gray 85',
'gray 84',
'gray 83',
'gray 82',
'gray 81',
'gray 80',
'gray 79',
'gray 78',
'gray 77',
'gray 76',
'gray 75',
'gray 74',
'gray 73',
'gray 72',
'gray 71',
'gray 70',
'gray 69',
'gray 68',
'gray 67',
'gray 66',
'gray 65',
'gray 64',
'gray 63',
'gray 62',
'gray 61',
'gray 60',
'gray 59',
'gray 58',
'gray 57',
'gray 56',
'gray 55',
'gray 54',
'gray 53',
'gray 52',
'gray 51',
'gray 50',
'gray 49',
'gray 48',
'gray 47',
'gray 46',
'gray 45',
'gray 44',
'gray 43',
'gray 40',
'gray 39',
'gray 38',
'gray 37',
'gray 36',
'gray 35',
'gray 34',
'gray 33',
'gray 32',
'gray 31',
'gray 30',
'gray 29',
'gray 28',
'gray 27',
'gray 26',
'gray 25',
'gray 24',
'gray 23',
'gray 22',
'gray 21',
'gray 20',
'gray 19',
'gray 18',
'gray 17',
'gray 16',
'gray 15',
'gray 14',
'gray 13',
'gray 12',
'gray 11',
'gray 10',
'gray 9',
'gray 8',
'gray 7',
'gray 6',
'gray 5',
'gray 4',
'gray 3',
'gray 2',
'gray 1',
'whitesmoke'
])
export const validateColor = (str = '') => {
return /#\d{3,6}|rgba{0,1}\(.*?\)/gi.test(str) || colors.has(str.toLowerCase())
}

@ -5,6 +5,7 @@ export const FONTS = [
{ id: 'Droid Sans Mono', name: 'Droid Sans Mono' }, { id: 'Droid Sans Mono', name: 'Droid Sans Mono' },
{ id: 'Fira Code', name: 'Fira Code' }, { id: 'Fira Code', name: 'Fira Code' },
{ id: 'Hack', name: 'Hack' }, { id: 'Hack', name: 'Hack' },
{ id: 'Iosevka', name: 'Iosevka' },
{ id: 'Inconsolata', name: 'Inconsolata' }, { id: 'Inconsolata', name: 'Inconsolata' },
{ id: 'Source Code Pro', name: 'Source Code Pro' }, { id: 'Source Code Pro', name: 'Source Code Pro' },
{ id: 'Ubuntu Mono', name: 'Ubuntu Mono' } { id: 'Ubuntu Mono', name: 'Ubuntu Mono' }
@ -77,6 +78,10 @@ export const THEMES = [
name: 'Solarized (Light)', name: 'Solarized (Light)',
hasStylesheet: false hasStylesheet: false
}, },
{
id: 'tomorrow-night-bright',
name: 'Tomorrow Night'
},
{ {
id: 'twilight', id: 'twilight',
name: 'Twilight' name: 'Twilight'
@ -98,6 +103,12 @@ export const LANGUAGES = [
name: 'Auto', name: 'Auto',
mode: 'auto' mode: 'auto'
}, },
{
name: 'Apache',
mode: 'apache',
mime: 'text/apache',
custom: true
},
{ {
name: 'Bash', name: 'Bash',
mode: 'shell', mode: 'shell',
@ -376,12 +387,22 @@ export const LANGUAGES = [
} }
] ]
export const EXPORT_SIZES = [
{ id: '1x', name: '1x', value: '1' },
{ id: '2x', name: '2x', value: '2' },
{ id: '4x', name: '4x', value: '4' }
]
export const EXPORT_SIZES_HASH = toHash(EXPORT_SIZES)
export const LANGUAGE_MIME_HASH = toHash(LANGUAGES, 'mime') export const LANGUAGE_MIME_HASH = toHash(LANGUAGES, 'mime')
export const LANGUAGE_MODE_HASH = toHash(LANGUAGES, 'mode') export const LANGUAGE_MODE_HASH = toHash(LANGUAGES, 'mode')
export const LANGUAGE_NAME_HASH = toHash(LANGUAGES, 'short') export const LANGUAGE_NAME_HASH = toHash(LANGUAGES, 'short')
export const DEFAULT_LANGUAGE = 'auto' export const DEFAULT_LANGUAGE = 'auto'
export const DEFAULT_THEME = THEMES_HASH.seti export const DEFAULT_THEME = THEMES_HASH.seti
export const DEFAULT_BG_COLOR = 'rgba(171, 184, 195, 1)'
export const DEFAULT_EXPORT_SIZE = EXPORT_SIZES_HASH['2x']
export const COLORS = { export const COLORS = {
BLACK: '#121212', BLACK: '#121212',
@ -417,3 +438,26 @@ if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
} }
) )
} }
export const DEFAULT_SETTINGS = {
paddingVertical: '48px',
paddingHorizontal: '32px',
marginVertical: '45px',
marginHorizontal: '45px',
backgroundImage: null,
backgroundImageSelection: null,
backgroundMode: 'color',
backgroundColor: DEFAULT_BG_COLOR,
dropShadow: true,
dropShadowOffsetY: '20px',
dropShadowBlurRadius: '68px',
theme: DEFAULT_THEME.id,
windowTheme: 'none',
language: DEFAULT_LANGUAGE,
fontFamily: 'Hack',
fontSize: '14px',
windowControls: true,
widthAdjustment: true,
lineNumbers: false,
exportSize: '2x'
}

@ -0,0 +1,136 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Apache mode by gloony
const CodeMirror = require('codemirror')
CodeMirror.defineMode('apache', function(config) {
return {
token: function(stream, state) {
var sol = stream.sol() || state.afterSection
var eol = stream.eol()
state.afterSection = false
if (sol) {
if (state.nextMultiline) {
state.inMultiline = true
state.nextMultiline = false
} else {
state.position = 'def'
}
}
if (eol && !state.nextMultiline) {
state.inMultiline = false
state.position = 'def'
}
if (sol) {
while (stream.eatSpace()) {}
}
var ch = stream.next()
if (sol && ch === '#') {
state.position = 'comment'
stream.skipToEnd()
return 'comment'
} else if (ch === '!' && stream.peek() !== ' ') {
return 'number'
} else if (ch === ' ') {
if (stream.peek() === '[') {
if (stream.skipTo(']')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'keyword'
} else if (stream.peek() === '(') {
if (stream.skipTo(')')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'string'
} else {
state.position = 'unit'
return 'unit'
}
} else if (ch === '"') {
if (stream.skipTo('"')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'quote'
} else if (sol && ch === '<') {
if (stream.skipTo('>')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'meta'
} else if (ch === '%') {
if (stream.peek() === '{') {
if (stream.skipTo('}')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'operator'
}
} else if (ch === '$') {
if (!isNaN(stream.peek()) && stream.peek() !== ' ') {
while (!isNaN(stream.peek()) && stream.peek() !== ' ') {
stream.next()
}
return 'operator'
}
} else if (ch === '\\') {
if (stream.peek() === '.') {
if (stream.skipTo(' ')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'string'
}
} else if (ch === '.') {
if (stream.peek() === '*') {
if (stream.skipTo(' ')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'string'
}
} else if (ch === '^') {
if (stream.skipTo(' ')) {
stream.next()
} else {
stream.skipToEnd()
}
return 'string'
}
return state.position
},
// electricInput: /<\/[\s\w:]+>$/,
lineComment: '#',
fold: 'brace',
startState: function() {
return {
position: 'def',
nextMultiline: false,
inMultiline: false,
afterSection: false
}
}
}
})
CodeMirror.defineMIME('text/apache', 'apache')
CodeMirror.defineMIME('text/htaccess', 'apache')

@ -10,6 +10,7 @@ if (typeof window !== 'undefined') {
const mapper = new Morph({ const mapper = new Morph({
types: { types: {
bool: v => { bool: v => {
if (v == null) return undefined
if (v === 'false') return false if (v === 'false') return false
return Boolean(v) return Boolean(v)
} }
@ -19,14 +20,21 @@ const mapper = new Morph({
const mappings = [ const mappings = [
{ field: 'bg:backgroundColor' }, { field: 'bg:backgroundColor' },
{ field: 't:theme' }, { field: 't:theme' },
{ field: 'wt:windowTheme' },
{ field: 'l:language' }, { field: 'l:language' },
{ field: 'ds:dropShadow', type: 'bool' }, { field: 'ds:dropShadow', type: 'bool' },
{ field: 'dsyoff:dropShadowOffsetY' },
{ field: 'dsblur:dropShadowBlurRadius' },
{ field: 'wc:windowControls', type: 'bool' }, { field: 'wc:windowControls', type: 'bool' },
{ field: 'wa:widthAdjustment', type: 'bool' }, { field: 'wa:widthAdjustment', type: 'bool' },
{ field: 'pv:paddingVertical' }, { field: 'pv:paddingVertical' },
{ field: 'ph:paddingHorizontal' }, { field: 'ph:paddingHorizontal' },
{ field: 'ln:lineNumbers', type: 'bool' }, { field: 'ln:lineNumbers', type: 'bool' },
{ field: 'code:code' } { field: 'fm:fontFamily' },
{ field: 'fs:fontSize' },
{ field: 'si:squaredImage', type: 'bool' },
{ field: 'code:code' },
{ field: 'es:exportSize' }
] ]
const reverseMappings = mappings.map(mapping => const reverseMappings = mappings.map(mapping =>
@ -43,8 +51,31 @@ const keysToQuery = keys =>
.map(key => `${key}=${keys[key]}`) .map(key => `${key}=${keys[key]}`)
.join('&')}` .join('&')}`
export const getQueryStringState = query => mapper.map(mappings, query) export const getQueryStringState = query => {
export const updateQueryString = state => const state = mapper.map(mappings, query)
deserializeCode(state)
Object.keys(state).forEach(key => {
if (state[key] === '') state[key] = undefined
})
return state
}
export const updateQueryString = state => {
let mappedState = mapper.map(reverseMappings, state)
serializeCode(mappedState)
history.replace({ history.replace({
search: encodeURI(keysToQuery(mapper.map(reverseMappings, state))) search: encodeURI(keysToQuery(mappedState))
}) })
}
// private
function serializeCode(state) {
if (state.code) state.code = encodeURIComponent(state.code)
}
function deserializeCode(state) {
if (state.code) state.code = decodeURIComponent(state.code)
}

@ -1,13 +0,0 @@
const Uglify = require('uglifyjs-webpack-plugin')
module.exports = {
// TODO remove `next.configs.js` when this closes: https://github.com/zeit/next.js/issues/1195
webpack: function(c, { dev }) {
if (!dev) {
c.plugins = c.plugins.filter(plugin => plugin.constructor.name !== 'UglifyJsPlugin')
c.plugins.push(new Uglify())
}
return c
}
}

9594
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -22,7 +22,9 @@
"codemirror": "^5.31.0", "codemirror": "^5.31.0",
"codemirror-graphql": "^0.6.12", "codemirror-graphql": "^0.6.12",
"codemirror-mode-elixir": "^1.1.1", "codemirror-mode-elixir": "^1.1.1",
"cross-env": "^5.1.3",
"dom-to-image": "^2.5.2", "dom-to-image": "^2.5.2",
"downshift": "^1.28.0",
"dropperx": "^0.1.0", "dropperx": "^0.1.0",
"express": "^4.16.2", "express": "^4.16.2",
"form-data": "^2.2.0", "form-data": "^2.2.0",
@ -32,7 +34,7 @@
"morgan": "^1.8.2", "morgan": "^1.8.2",
"morphmorph": "^0.0.2", "morphmorph": "^0.0.2",
"ms": "^2.0.0", "ms": "^2.0.0",
"next": "^4.2.3", "next": "^5.0.0",
"react": "^16.2.0", "react": "^16.2.0",
"react-click-outside": "^3.0.0", "react-click-outside": "^3.0.0",
"react-codemirror2": "^3.0.7", "react-codemirror2": "^3.0.7",
@ -50,14 +52,12 @@
}, },
"devDependencies": { "devDependencies": {
"all-contributors-cli": "^4.7.0", "all-contributors-cli": "^4.7.0",
"cross-env": "^5.1.1",
"cypress": "^1.4.1", "cypress": "^1.4.1",
"hex2rgb": "^2.2.0", "hex2rgb": "^2.2.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"lint-staged": "^6.0.0", "lint-staged": "^6.0.0",
"now": "^9.2.7", "now": "^9.2.7",
"prettier": "^1.8.1", "prettier": "^1.8.1",
"uglifyjs-webpack-plugin": "1.1.2",
"webpack": "^3.10.0" "webpack": "^3.10.0"
}, },
"now": { "now": {

@ -0,0 +1,14 @@
import Document, { Head, Main, NextScript } from 'next/document'
export default class extends Document {
render() {
return (
<html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}

@ -25,8 +25,13 @@ import {
LANGUAGE_NAME_HASH, LANGUAGE_NAME_HASH,
DEFAULT_LANGUAGE, DEFAULT_LANGUAGE,
DEFAULT_THEME, DEFAULT_THEME,
DEFAULT_EXPORT_SIZE,
COLORS, COLORS,
DEFAULT_CODE EXPORT_SIZES,
EXPORT_SIZES_HASH,
DEFAULT_CODE,
DEFAULT_BG_COLOR,
DEFAULT_SETTINGS
} from '../lib/constants' } from '../lib/constants'
import { getQueryStringState, updateQueryString } from '../lib/routing' import { getQueryStringState, updateQueryString } from '../lib/routing'
import { getState, saveState } from '../lib/util' import { getState, saveState } from '../lib/util'
@ -60,20 +65,7 @@ class Editor extends React.Component {
super(props) super(props)
this.state = Object.assign( this.state = Object.assign(
{ {
backgroundMode: 'color', ...DEFAULT_SETTINGS,
backgroundColor: 'rgba(171, 184, 195, 1)',
backgroundImage: null,
backgroundImageSelection: null,
theme: DEFAULT_THEME.id,
language: DEFAULT_LANGUAGE,
dropShadow: true,
dropShadowOffsetY: '20px',
dropShadowBlurRadius: '68px',
windowControls: true,
widthAdjustment: true,
lineNumbers: false,
paddingVertical: '48px',
paddingHorizontal: '32px',
uploading: false, uploading: false,
code: props.content, code: props.content,
_initialState: this.props.initialState _initialState: this.props.initialState
@ -84,7 +76,9 @@ class Editor extends React.Component {
this.save = this.save.bind(this) this.save = this.save.bind(this)
this.upload = this.upload.bind(this) this.upload = this.upload.bind(this)
this.updateCode = this.updateCode.bind(this) this.updateCode = this.updateCode.bind(this)
this.updateTitleBar = this.updateTitleBar.bind(this)
this.updateAspectRatio = this.updateAspectRatio.bind(this) this.updateAspectRatio = this.updateAspectRatio.bind(this)
this.resetDefaultSettings = this.resetDefaultSettings.bind(this)
} }
componentDidMount() { componentDidMount() {
@ -109,14 +103,18 @@ class Editor extends React.Component {
getCarbonImage({ format } = { format: 'png' }) { getCarbonImage({ format } = { format: 'png' }) {
const node = document.getElementById('export-container') const node = document.getElementById('export-container')
const exportSize = (EXPORT_SIZES_HASH[this.state.exportSize] || DEFAULT_EXPORT_SIZE).value
const config = { const config = {
style: { style: {
transform: 'scale(2)', transform: `scale(${exportSize})`,
'transform-origin': 'center' 'transform-origin': 'center',
background: this.state.squaredImage ? this.state.backgroundColor : 'none'
}, },
filter: n => (n.className ? String(n.className).indexOf('eliminateOnRender') < 0 : true), filter: n => (n.className ? String(n.className).indexOf('eliminateOnRender') < 0 : true),
width: node.offsetWidth * 2, width: node.offsetWidth * exportSize,
height: node.offsetHeight * 2 height: this.state.squaredImage
? node.offsetWidth * exportSize
: node.offsetHeight * exportSize
} }
return format.toLowerCase() === 'svg' return format.toLowerCase() === 'svg'
@ -132,6 +130,10 @@ class Editor extends React.Component {
this.setState({ aspectRatio }) this.setState({ aspectRatio })
} }
updateTitleBar(titleBar) {
this.setState({ titleBar })
}
save({ format } = { format: 'png' }) { save({ format } = { format: 'png' }) {
this.getCarbonImage({ format }).then(dataUrl => { this.getCarbonImage({ format }).then(dataUrl => {
const link = document.createElement('a') const link = document.createElement('a')
@ -143,6 +145,11 @@ class Editor extends React.Component {
}) })
} }
resetDefaultSettings() {
this.setState(DEFAULT_SETTINGS)
localStorage.clear()
}
upload() { upload() {
this.setState({ uploading: true }) this.setState({ uploading: true })
this.getCarbonImage({ format: 'png' }) this.getCarbonImage({ format: 'png' })
@ -181,6 +188,7 @@ class Editor extends React.Component {
<Settings <Settings
onChange={(key, value) => this.setState({ [key]: value })} onChange={(key, value) => this.setState({ [key]: value })}
enabled={this.state} enabled={this.state}
resetDefaultSettings={this.resetDefaultSettings}
/> />
<div className="buttons"> <div className="buttons">
<Button <Button
@ -215,7 +223,7 @@ class Editor extends React.Component {
backgroundMode: 'image' backgroundMode: 'image'
}) })
} else { } else {
this.setState({ code: file.content }) this.setState({ code: file.content, language: 'auto' })
} }
}} }}
> >
@ -228,6 +236,7 @@ class Editor extends React.Component {
config={this.state} config={this.state}
updateCode={code => this.updateCode(code)} updateCode={code => this.updateCode(code)}
onAspectRatioChange={this.updateAspectRatio} onAspectRatioChange={this.updateAspectRatio}
updateTitleBar={this.updateTitleBar}
> >
{this.state.code || DEFAULT_CODE} {this.state.code || DEFAULT_CODE}
</Carbon> </Carbon>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save