add color palette generation frontend (beta)

main
Mike Fix 5 years ago
parent 79e648844e
commit 1f1dbade1d

@ -23,7 +23,16 @@ class BackgroundSelect extends React.PureComponent {
handlePickColor = ({ rgb }) => this.props.onChange({ backgroundColor: stringifyRGBA(rgb) }) handlePickColor = ({ rgb }) => this.props.onChange({ backgroundColor: stringifyRGBA(rgb) })
render() { render() {
const { color, mode, image, onChange, isVisible, toggleVisibility, carbonRef } = this.props const {
color,
mode,
image,
onChange,
isVisible,
toggleVisibility,
carbonRef,
updateHighlights
} = this.props
const background = validateColor(color) ? color : DEFAULT_BG_COLOR const background = validateColor(color) ? color : DEFAULT_BG_COLOR
@ -70,7 +79,12 @@ class BackgroundSelect extends React.PureComponent {
<ColorPicker color={color} onChange={this.handlePickColor} /> <ColorPicker color={color} onChange={this.handlePickColor} />
</div> </div>
<div hidden={mode !== 'image'}> <div hidden={mode !== 'image'}>
<ImagePicker onChange={onChange} imageDataURL={image} aspectRatio={aspectRatio} /> <ImagePicker
onChange={onChange}
imageDataURL={image}
aspectRatio={aspectRatio}
updateHighlights={updateHighlights}
/>
</div> </div>
</div> </div>
</Popout> </Popout>

@ -377,6 +377,7 @@ class Editor extends React.Component {
/> />
<BackgroundSelect <BackgroundSelect
onChange={this.updateBackground} onChange={this.updateBackground}
updateHighlights={this.updateHighlights}
mode={backgroundMode} mode={backgroundMode}
color={backgroundColor} color={backgroundColor}
image={backgroundImage} image={backgroundImage}

@ -1,9 +1,11 @@
import React from 'react' import React from 'react'
import ReactCrop, { makeAspectCrop } from 'react-image-crop' import ReactCrop, { makeAspectCrop } from 'react-image-crop'
import { useLocalStorage } from '@dawnlabs/tacklebox'
import RandomImage from './RandomImage' import RandomImage from './RandomImage'
import PhotoCredit from './PhotoCredit' import PhotoCredit from './PhotoCredit'
import Input from './Input' import Input from './Input'
import Toggle from './Toggle'
import { Link } from './Meta' import { Link } from './Meta'
import { fileToDataURL } from '../lib/util' import { fileToDataURL } from '../lib/util'
import ApiContext from './ApiContext' import ApiContext from './ApiContext'
@ -140,10 +142,37 @@ export default class ImagePicker extends React.Component {
return this.handleImageChange(dataURL, dataURL) return this.handleImageChange(dataURL, dataURL)
} }
async selectImage(url, { photographer } = {}) { async selectImage(image) {
// TODO use React suspense for loading this asset // TODO use React suspense for loading this asset
const { dataURL } = await this.context.downloadThumbnailImage({ url }) const { dataURL } = await this.context.downloadThumbnailImage(image)
return this.handleImageChange(url, dataURL, photographer)
this.handleImageChange(image.url, dataURL, image.photographer)
if (image.palette && image.palette.length && this.generateColorPalette) {
/*
* Background is first, which is either the lightest or darkest color
* and the rest are sorted by highest contrast w/ the background
*/
const palette = image.palette.map(c => c.hex)
/*
* Contributors, please feel free to adjust this algorithm to create the most
* readible or aesthetically pleasing syntax highlighting.
*/
this.props.updateHighlights({
background: palette[0],
text: palette[1],
variable: palette[2],
attribute: palette[3],
definition: palette[4],
keyword: palette[5],
property: palette[6],
string: palette[7],
number: palette[8],
operator: palette[9],
meta: palette[10],
tag: palette[11],
comment: palette[12]
})
}
} }
removeImage() { removeImage() {
@ -192,6 +221,7 @@ export default class ImagePicker extends React.Component {
Or use a random <a href="https://unsplash.com/">Unsplash</a> image: Or use a random <a href="https://unsplash.com/">Unsplash</a> image:
</span> </span>
<RandomImage onChange={this.selectImage} /> <RandomImage onChange={this.selectImage} />
<GeneratePaletteSetting onChange={value => (this.generateColorPalette = value)} />
</div> </div>
<style jsx> <style jsx>
{` {`
@ -333,3 +363,17 @@ export default class ImagePicker extends React.Component {
) )
} }
} }
function GeneratePaletteSetting({ onChange }) {
const [enabled, setEnabled] = useLocalStorage('CARBON_GENERATE_COLOR_PALETTE')
React.useEffect(() => void onChange(enabled), [enabled, onChange])
return (
<Toggle
label="Generate color palette (beta)"
enabled={enabled}
onChange={setEnabled}
padding="8px 0 0"
/>
)
}

@ -13,7 +13,7 @@ function RandomImage(props) {
const [selectImage, { loading: selecting }] = useAsyncCallback(() => { const [selectImage, { loading: selecting }] = useAsyncCallback(() => {
const image = cacheRef.current[cacheIndex] const image = cacheRef.current[cacheIndex]
return api.unsplash.download(image.id).then(url => props.onChange(url, image)) return api.unsplash.download(image.id).then(data => props.onChange({ ...image, ...data }))
}) })
const [updateCache, { loading: updating, error, data: imgs }] = useAsyncCallback( const [updateCache, { loading: updating, error, data: imgs }] = useAsyncCallback(

@ -111,7 +111,7 @@ const WindowSettings = React.memo(
} }
.settings-content :global(.settings-row:focus-within) { .settings-content :global(.settings-row:focus-within) {
outline: -webkit-focus-ring-color auto 5px; outline: -webkit-focus-ring-color auto 4px;
} }
.first-line-number-row { .first-line-number-row {

@ -10,7 +10,16 @@ class Toggle extends React.PureComponent {
toggle = () => this.props.onChange(!this.props.enabled) toggle = () => this.props.onChange(!this.props.enabled)
handleKeydown = e => {
if (e.key === 'Enter') this.toggle()
if (e.key === ' ') {
e.preventDefault()
this.toggle()
}
}
render() { render() {
// TODO use input[type=["checkbox"] /> instead of div
return ( return (
<div <div
role="checkbox" role="checkbox"
@ -18,8 +27,9 @@ class Toggle extends React.PureComponent {
className={`toggle ${this.props.className}`} className={`toggle ${this.props.className}`}
onClick={this.toggle} onClick={this.toggle}
aria-checked={this.props.enabled} aria-checked={this.props.enabled}
onKeyDown={this.handleKeydown}
> >
<span className="label">{this.props.label}</span> <label className="label">{this.props.label}</label>
{this.props.enabled ? <Checkmark /> : <div className="checkmark-disabled" />} {this.props.enabled ? <Checkmark /> : <div className="checkmark-disabled" />}
<style jsx> <style jsx>
{` {`
@ -29,7 +39,16 @@ class Toggle extends React.PureComponent {
justify-content: ${this.props.center ? 'center' : 'space-between'}; justify-content: ${this.props.center ? 'center' : 'space-between'};
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
padding: 8px 12px 8px 8px; padding: ${this.props.padding || '8px 12px 8px 8px'};
}
.toggle {
outline: none;
}
.toggle:focus-within :global(svg),
.toggle:focus-within .checkmark-disabled {
outline: 4px auto -webkit-focus-ring-color;
} }
.checkmark-disabled { .checkmark-disabled {

@ -65,7 +65,7 @@ const downloadThumbnailImage = img => {
const unsplash = { const unsplash = {
download(id) { download(id) {
return client.get(`/unsplash/download/${id}`).then(res => res.data.url) return client.get(`/unsplash/download/${id}`).then(res => res.data)
}, },
async random() { async random() {
const imageUrls = await client.get('/unsplash/random') const imageUrls = await client.get('/unsplash/random')

Loading…
Cancel
Save