Plumbing for wallpaper previews.

This commit is contained in:
Matt Swensen 2018-11-24 11:29:07 -07:00
parent 84961f5dad
commit cd123ab971
No known key found for this signature in database
GPG Key ID: 3F9E482BFC526F35
14 changed files with 104 additions and 20 deletions

@ -5,6 +5,7 @@ import { UrlStateProvider } from './UrlState';
import ColorState from './ColorState'; import ColorState from './ColorState';
import ColorSetInputs from './ColorSetInputs'; import ColorSetInputs from './ColorSetInputs';
import TextPreviews from './TextPreviews'; import TextPreviews from './TextPreviews';
import WallpaperPreview from './WallpaperPreview';
export default class App extends PureComponent { export default class App extends PureComponent {
render() { render() {
@ -35,7 +36,10 @@ export default class App extends PureComponent {
<h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>1. Define colors</h2> <h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>1. Define colors</h2>
<ColorSetInputs /> <ColorSetInputs />
<h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>2. Preview</h2> <h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>2. Preview</h2>
<TextPreviews /> <div className={ styles.previewsContainer }>
<TextPreviews />
<WallpaperPreview />
</div>
</div> </div>
</div> </div>
) } ) }

@ -23,4 +23,10 @@
.h2 { .h2 {
font-size: var(--size-large-1); font-size: var(--size-large-1);
margin: var(--size-regular) 0; margin: var(--size-regular) 0;
} }
.previewsContainer {
display: grid;
grid-template-columns: calc((100% - var(--size-large-1)) * 2 / 3) calc((100% - var(--size-large-1)) / 3);
grid-column-gap: var(--size-large-1);
}

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Check } from './Icons'; import { CheckIcon } from './Icons';
import styles from './Checkbox.module.css'; import styles from './Checkbox.module.css';
import ColorState from './ColorState'; import ColorState from './ColorState';
@ -18,7 +18,7 @@ export default class Checkbox extends PureComponent {
checked={ this.props.value } checked={ this.props.value }
onChange={ evt => this.props.onChange(evt.target.checked) } onChange={ evt => this.props.onChange(evt.target.checked) }
/> />
<Check <CheckIcon
backgroundColor={ this.props.value ? getColor('shade7') : 'transparent' } backgroundColor={ this.props.value ? getColor('shade7') : 'transparent' }
outlineColor={ this.props.value ? 'transparent' : getColor('shade7') } outlineColor={ this.props.value ? 'transparent' : getColor('shade7') }
checkColor={ this.props.value ? getColor('shade0') : 'transparent' } checkColor={ this.props.value ? getColor('shade0') : 'transparent' }

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import ColorState from './ColorState'; import ColorState from './ColorState';
import { Droplet } from './Icons'; import { DropletIcon } from './Icons';
import styles from './ColorInput.module.css'; import styles from './ColorInput.module.css';
import getBestForeground from './getBestForeground'; import getBestForeground from './getBestForeground';
@ -36,7 +36,7 @@ export default class ColorInput extends PureComponent {
}} }}
tabIndex="0" tabIndex="0"
> >
<Droplet /> <DropletIcon />
<input <input
type="color" type="color"
className={ styles.colorInput } className={ styles.colorInput }

@ -1,18 +1,18 @@
import React from 'react'; import React from 'react';
export const Droplet = () => ( export const DropletIcon = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M8 3.004c.25.386.525.772.827 1.172.297.395 1.308 1.664 1.495 1.91C11.522 7.663 12 8.802 12 10.5c0 2.002-1.613 3.5-4 3.5s-4-1.498-4-3.5c0-1.698.48-2.837 1.678-4.414.187-.246 1.198-1.515 1.495-1.91.302-.4.576-.786.827-1.172z" stroke="currentColor" strokeWidth="2" fill="none" fillRule="evenodd"/> <path d="M8 3.004c.25.386.525.772.827 1.172.297.395 1.308 1.664 1.495 1.91C11.522 7.663 12 8.802 12 10.5c0 2.002-1.613 3.5-4 3.5s-4-1.498-4-3.5c0-1.698.48-2.837 1.678-4.414.187-.246 1.198-1.515 1.495-1.91.302-.4.576-.786.827-1.172z" stroke="currentColor" strokeWidth="2" fill="none" fillRule="evenodd"/>
</svg> </svg>
); );
export const Check = ({ backgroundColor, outlineColor, checkColor }) => ( export const CheckIcon = ({ backgroundColor, outlineColor, checkColor }) => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" style={{ backgroundColor, boxShadow: `inset 0 0 0 1px ${outlineColor}`, borderRadius: '2px' }}> <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" style={{ backgroundColor, boxShadow: `inset 0 0 0 1px ${outlineColor}`, borderRadius: '2px' }}>
<path d="M4 9l2 2M6 11l6-6" stroke={ checkColor } strokeWidth="2" fill="none" fillRule="evenodd" strokeLinecap="square"/> <path d="M4 9l2 2M6 11l6-6" stroke={ checkColor } strokeWidth="2" fill="none" fillRule="evenodd" strokeLinecap="square"/>
</svg> </svg>
); );
export const Radio = ({ selected }) => ( export const RadioIcon = ({ selected }) => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd"> <g fill="none" fillRule="evenodd">
<circle stroke="currentColor" cx="8" cy="8" r="7.5"/> <circle stroke="currentColor" cx="8" cy="8" r="7.5"/>

28
web/src/Radio.js Normal file

@ -0,0 +1,28 @@
import React, { PureComponent } from 'react';
import { RadioIcon } from './Icons';
import ColorState from './ColorState';
import styles from './Radio.module.css';
export default class Radio extends PureComponent {
render() {
return (
<ColorState>
{ ({ getColor }) => (
<label
className={ styles.wrapper }
style={{ color: getColor('shade7') }}
>
<input
type="radio"
className={ styles.input }
checked={ this.props.value }
onChange={ evt => this.props.onChange(evt.target.checked) }
/>
<RadioIcon selected={ this.props.value } />
<span className={ styles.label }>{ this.props.label }</span>
</label>
) }
</ColorState>
);
}
}

13
web/src/Radio.module.css Normal file

@ -0,0 +1,13 @@
.wrapper {
display: flex;
align-items: center;
}
.input {
display: none;
}
.label {
margin-left: var(--size-small-2);
flex-grow: 1;
}

@ -17,7 +17,10 @@ export default class Tabs extends PureComponent {
borderLeftColor: getColor('shade7'), borderLeftColor: getColor('shade7'),
}), }),
contentClassName: styles.tabContent, contentClassName: styles.tabContent,
contentStyle: { borderColor: getColor('shade7') }, contentStyle: {
borderColor: getColor('shade7'),
backgroundColor: getColor('shade0'),
},
}) } }) }
</ColorState> </ColorState>
); );

@ -1,5 +1,7 @@
.tab { .tab {
display: inline-block;
font-size: var(--size-regular); font-size: var(--size-regular);
line-height: var(--line-height);
font-family: 'Fira Code', monospace; font-family: 'Fira Code', monospace;
padding: var(--size-small-4) var(--size-small-1); padding: var(--size-small-4) var(--size-small-1);
border-width: var(--border-size); border-width: var(--border-size);
@ -19,4 +21,5 @@
border-top-right-radius: var(--border-radius-size); border-top-right-radius: var(--border-radius-size);
border-bottom-right-radius: var(--border-radius-size); border-bottom-right-radius: var(--border-radius-size);
border-bottom-left-radius: var(--border-radius-size); border-bottom-left-radius: var(--border-radius-size);
padding: var(--size-large-1);
} }

@ -2,7 +2,6 @@ import React, { PureComponent } from 'react';
import CodePreview from './CodePreview'; import CodePreview from './CodePreview';
import TerminalPreview from './TerminalPreview'; import TerminalPreview from './TerminalPreview';
import Tabs from './Tabs'; import Tabs from './Tabs';
import styles from './TextPreviews.module.css';
export default class TextPreviews extends PureComponent { export default class TextPreviews extends PureComponent {
@ -12,7 +11,7 @@ export default class TextPreviews extends PureComponent {
return ( return (
<Tabs> <Tabs>
{ ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => ( { ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => (
<> <div>
<div> <div>
<button <button
className={ tabClassName } className={ tabClassName }
@ -25,7 +24,7 @@ export default class TextPreviews extends PureComponent {
onClick={ () => this.setState({ activePreview: 'terminal' }) } onClick={ () => this.setState({ activePreview: 'terminal' }) }
>Terminal</button> >Terminal</button>
</div> </div>
<div className={ `${styles.previewContainer} ${contentClassName}` } style={ contentStyle }> <div className={ contentClassName } style={ contentStyle }>
{ this.state.activePreview === 'code' ? ( { this.state.activePreview === 'code' ? (
<CodePreview /> <CodePreview />
) : null } ) : null }
@ -33,7 +32,7 @@ export default class TextPreviews extends PureComponent {
<TerminalPreview /> <TerminalPreview />
) : null } ) : null }
</div> </div>
</> </div>
) } ) }
</Tabs> </Tabs>
); );

@ -1,3 +0,0 @@
.previewContainer {
padding: var(--size-large-1);
}

@ -1,4 +1,4 @@
import React, { Component } from 'react'; import React, { PureComponent } from 'react';
import qs from 'qs'; import qs from 'qs';
import { merge } from 'lodash'; import { merge } from 'lodash';
import getValueOrFallback from './getValueOrFallback'; import getValueOrFallback from './getValueOrFallback';
@ -52,9 +52,10 @@ const fallbackState = {
dark: true, dark: true,
light: true, light: true,
}, },
activeWallpaper: 'none',
}; };
export class UrlStateProvider extends Component { export class UrlStateProvider extends PureComponent {
constructor(props, ...args) { constructor(props, ...args) {
super(props, ...args); super(props, ...args);
this.state = stateFromParams(props.history.location.search); this.state = stateFromParams(props.history.location.search);

@ -0,0 +1,28 @@
import React, { PureComponent } from 'react';
import { UrlStateConsumer } from './UrlState';
import Tabs from './Tabs';
import Radio from './Radio';
export default class WallpaperPreview extends PureComponent {
render() {
return (
<UrlStateConsumer>
{ ({ getValueOrFallback, mergeState }) => (
<Tabs>
{ ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => (
<div>
<span
className={ tabClassName }
style={ getTabStyle(true) }
>Wallpaper</span>
<div className={ contentClassName } style={ contentStyle }>
<Radio value={ getValueOrFallback([['activeWallpaper']]) === 'none' } label="None" />
</div>
</div>
) }
</Tabs>
) }
</UrlStateConsumer>
);
}
}

@ -38,10 +38,12 @@ body,
--border-size: var(--size-small-6); --border-size: var(--size-small-6);
--border-radius-size: var(--size-small-3); --border-radius-size: var(--size-small-3);
--line-height: calc(1 / var(--size-ratio));
height: 100%; height: 100%;
font-family: 'Fira Code', monospace; font-family: 'Fira Code', monospace;
font-size: 16px; font-size: 16px;
line-height: calc(1 / var(--size-ratio)); line-height: var(--line-height);
} }
* { * {