Clean up state derivation abstractions.
This commit is contained in:
parent
4745429549
commit
8f95f562b1
116
web/src/App.js
116
web/src/App.js
@ -1,109 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './App.module.css';
|
||||
import { UrlStateProvider } from './UrlState';
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from './ThemeContext';
|
||||
import Main from './Main';
|
||||
|
||||
import ColorState from './ColorState';
|
||||
import ColorSetInputs from './ColorSetInputs';
|
||||
import TextPreviews from './TextPreviews';
|
||||
import WallpaperPreview from './WallpaperPreview';
|
||||
import PreBuiltList from './PreBuiltList';
|
||||
import Download from './Download';
|
||||
import Link from './Link';
|
||||
import CopyUrl from './CopyUrl';
|
||||
import StarCount from './StarCount';
|
||||
|
||||
export default ({ history }) => {
|
||||
const [keyboarding, setKeyboarding] = useState(false);
|
||||
const onKeyDown = (evt) => {
|
||||
if (evt.key === 'Tab') {
|
||||
setKeyboarding(true);
|
||||
}
|
||||
};
|
||||
const onMouseDown = () => {
|
||||
setKeyboarding(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
window.document.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
window.document.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
window.document.addEventListener('mousedown', onMouseDown);
|
||||
return () => {
|
||||
window.document.removeEventListener('mousedown', onMouseDown);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<UrlStateProvider history={ history }>
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<div
|
||||
className={ `${styles.app} ${keyboarding ? styles.keyboarding : ''}` }
|
||||
style={{
|
||||
backgroundColor: getColor('shade0'),
|
||||
'--selection-foreground-color': getColor('shade0'),
|
||||
'--selection-background-color': getColor('accent5'),
|
||||
'--focus-outline-color': getColor('accent6'),
|
||||
}}
|
||||
>
|
||||
<div className={ styles.container }>
|
||||
<header className={ styles.header }>
|
||||
<h1 className={ styles.h1 } style={{ color: getColor('shade7') }}>themer</h1>
|
||||
<StarCount />
|
||||
</header>
|
||||
<hr
|
||||
className={ styles.hr }
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(
|
||||
to right,
|
||||
${getColor('accent0', 'shade2', 'shade7')},
|
||||
${getColor('accent1', 'shade2', 'shade7')},
|
||||
${getColor('accent2', 'shade2', 'shade7')},
|
||||
${getColor('accent3', 'shade2', 'shade7')},
|
||||
${getColor('accent4', 'shade2', 'shade7')},
|
||||
${getColor('accent4', 'shade2', 'shade7')},
|
||||
${getColor('accent5', 'shade2', 'shade7')},
|
||||
${getColor('accent6', 'shade2', 'shade7')},
|
||||
${getColor('accent7', 'shade2', 'shade7')}
|
||||
)
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
<p style={{ color: getColor('shade6', 'shade7')}}>themer takes a set of colors and generates themes for your apps (editors, terminals, wallpapers, and more).</p>
|
||||
<h2 className={ styles.h2 } style={{ color: getColor('shade7') }}>1. Define colors</h2>
|
||||
<p className={ styles.help } style={{ color: getColor('shade6', 'shade7') }}>Input your colors using any CSS format (keyword, hsl, rgb, etc.).</p>
|
||||
<ColorSetInputs />
|
||||
<p className={ styles.preBuilt } style={{ color: getColor('shade6', 'shade7') }}>
|
||||
Or start with a pre-built color set:
|
||||
</p>
|
||||
<PreBuiltList />
|
||||
<h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>2. Preview</h2>
|
||||
<div className={ styles.previewsContainer }>
|
||||
<TextPreviews />
|
||||
<WallpaperPreview />
|
||||
</div>
|
||||
<h2 className={ styles.h2 } style={{ color: getColor('shade7')}}>3. Download</h2>
|
||||
<p className={ styles.help } style={{ color: getColor('shade6', 'shade7') }}>Select which themes you'd like to generate from your color set.</p>
|
||||
<Download />
|
||||
</div>
|
||||
<div className={ styles.shape } style={{ '--shape-color': getColor('shade1', 'shade0') }}>
|
||||
<div className={ styles.container }>
|
||||
<p style={{ color: getColor('shade7') }}>
|
||||
<span style={{ color: getColor('accent1', 'shade7') }}>Pro tip:</span>
|
||||
{' '}
|
||||
The current URL uniquely identifies your current theme. Bookmark it, email it, or share it however you like.
|
||||
<CopyUrl className={ styles.copyUrl }/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer className={ styles.footer } style={{ color: getColor('shade3', 'shade7') }}>
|
||||
themer is free and open source software, made by <Link href="https://mjswensen.com">mjswensen</Link> with <Link href="https://github.com/mjswensen/themer/graphs/contributors">contributors</Link>, and is released under the MIT license
|
||||
</footer>
|
||||
</div>
|
||||
) }
|
||||
</ColorState>
|
||||
</UrlStateProvider>
|
||||
);
|
||||
}
|
||||
export default ({ history }) => (
|
||||
<ThemeProvider history={ history }>
|
||||
<Main />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import React, { forwardRef, useContext } from 'react';
|
||||
import styles from './Button.module.css';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default forwardRef(({
|
||||
small,
|
||||
@ -10,39 +10,36 @@ export default forwardRef(({
|
||||
disabled,
|
||||
children,
|
||||
}, buttonRef) => {
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
const getColorKeys = () => {
|
||||
if (disabled) {
|
||||
return ['shade3', 'shade0'];
|
||||
return ['shade3'];
|
||||
} else if (small || special) {
|
||||
return ['shade7'];
|
||||
} else {
|
||||
return ['accent4', 'shade6', 'shade7'];
|
||||
return ['accent4', 'shade6'];
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<button
|
||||
className={ [
|
||||
styles.button,
|
||||
small && styles.small,
|
||||
special && styles.special,
|
||||
className,
|
||||
].filter(Boolean).join(' ') }
|
||||
data-text="Download"
|
||||
style={{
|
||||
color: getColor(...getColorKeys()),
|
||||
'--button-resting-background-color': getColor('shade1', 'shade0'),
|
||||
'--button-hover-background-color': getColor('shade2', 'shade0'),
|
||||
'--button-active-background-color': getColor('shade0'),
|
||||
'--button-special-color-1': getColor('accent1', 'shade7'),
|
||||
'--button-special-color-2': getColor('accent7', 'shade7'),
|
||||
}}
|
||||
onClick={ onClick }
|
||||
disabled={ disabled }
|
||||
ref={ buttonRef }
|
||||
>{ children }</button>
|
||||
) }
|
||||
</ColorState>
|
||||
<button
|
||||
className={ [
|
||||
styles.button,
|
||||
small && styles.small,
|
||||
special && styles.special,
|
||||
className,
|
||||
].filter(Boolean).join(' ') }
|
||||
data-text="Download"
|
||||
style={{
|
||||
color: getActiveColorOrFallback(getColorKeys()),
|
||||
'--button-resting-background-color': getActiveColorOrFallback(['shade1'], true),
|
||||
'--button-hover-background-color': getActiveColorOrFallback(['shade2'], true),
|
||||
'--button-active-background-color': getActiveColorOrFallback(['shade0'], true),
|
||||
'--button-special-color-1': getActiveColorOrFallback(['accent1']),
|
||||
'--button-special-color-2': getActiveColorOrFallback(['accent7']),
|
||||
}}
|
||||
onClick={ onClick }
|
||||
disabled={ disabled }
|
||||
ref={ buttonRef }
|
||||
>{ children }</button>
|
||||
);
|
||||
});
|
||||
|
@ -1,29 +0,0 @@
|
||||
import React from "react";
|
||||
import { UrlStateConsumer } from "./UrlState";
|
||||
|
||||
export default ({ children }) => {
|
||||
return (
|
||||
<UrlStateConsumer>
|
||||
{({ getValueOrFallback, mergeState }) =>
|
||||
children({
|
||||
getValue: () =>
|
||||
getValueOrFallback(
|
||||
[
|
||||
[
|
||||
"calculateIntermediaryShades",
|
||||
getValueOrFallback([["activeColorSet"]])
|
||||
]
|
||||
],
|
||||
v => v === "true"
|
||||
),
|
||||
setValue: v =>
|
||||
mergeState({
|
||||
calculateIntermediaryShades: {
|
||||
[getValueOrFallback([["activeColorSet"]])]: v
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</UrlStateConsumer>
|
||||
);
|
||||
};
|
@ -1,43 +1,44 @@
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { CheckIcon } from './Icons';
|
||||
import styles from './Checkbox.module.css';
|
||||
import ColorState from './ColorState';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default ({ className, value, accentSelected, onChange, label }) => (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<label
|
||||
className={ [styles.wrapper, className].join(' ') }
|
||||
style={{
|
||||
color: value && accentSelected
|
||||
? getColor('accent3', 'shade7')
|
||||
: getColor('shade7'),
|
||||
}}
|
||||
tabIndex="0"
|
||||
onKeyDown={ evt => {
|
||||
if (evt.key === ' ' || evt.key === 'Enter') {
|
||||
evt.preventDefault();
|
||||
onChange(!value);
|
||||
}
|
||||
} }
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className={ styles.input }
|
||||
checked={ value }
|
||||
onChange={ evt => onChange(evt.target.checked) }
|
||||
/>
|
||||
<CheckIcon
|
||||
backgroundColor={
|
||||
value
|
||||
? ( accentSelected ? getColor('accent3', 'shade7')
|
||||
: getColor('shade7')) : 'transparent'
|
||||
}
|
||||
outlineColor={ value ? 'transparent' : getColor('shade7') }
|
||||
checkColor={ value ? getColor('shade0') : 'transparent' }
|
||||
/>
|
||||
<span className={ styles.label }>{ label }</span>
|
||||
</label>
|
||||
) }
|
||||
</ColorState>
|
||||
);
|
||||
export default ({ className, value, accentSelected, onChange, label }) => {
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
return (
|
||||
<label
|
||||
className={ [styles.wrapper, className].join(' ') }
|
||||
style={{
|
||||
color: value && accentSelected
|
||||
? getActiveColorOrFallback(['accent3'])
|
||||
: getActiveColorOrFallback(['shade7']),
|
||||
}}
|
||||
tabIndex="0"
|
||||
onKeyDown={ evt => {
|
||||
if (evt.key === ' ' || evt.key === 'Enter') {
|
||||
evt.preventDefault();
|
||||
onChange(!value);
|
||||
}
|
||||
} }
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className={ styles.input }
|
||||
checked={ value }
|
||||
onChange={ evt => onChange(evt.target.checked) }
|
||||
/>
|
||||
<CheckIcon
|
||||
backgroundColor={
|
||||
value
|
||||
? ( accentSelected
|
||||
? getActiveColorOrFallback(['accent3'])
|
||||
: getActiveColorOrFallback('shade7')
|
||||
) : 'transparent'
|
||||
}
|
||||
outlineColor={ value ? 'transparent' : getActiveColorOrFallback(['shade7']) }
|
||||
checkColor={ value ? getActiveColorOrFallback(['shade0'], true) : 'transparent' }
|
||||
/>
|
||||
<span className={ styles.label }>{ label }</span>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
@ -1,88 +1,85 @@
|
||||
import React from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import React, { useContext } from 'react';
|
||||
import styles from './CodePreview.module.css';
|
||||
import { cursor } from './cursor.module.css';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => {
|
||||
const { activePreparedColorSet } = useContext(ThemeContext);
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<pre className={ styles.pre }>
|
||||
<code style={{ color: getColor('shade7') }}>
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 1 </span>
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>import</span>
|
||||
{' map '}
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>from </span>
|
||||
<span style={{ color: getColor('accent3', 'shade7') }}>'map.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 2 </span>
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>import</span>
|
||||
{' basePickBy '}
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>from </span>
|
||||
<span style={{ color: getColor('accent3', 'shade7') }}>'./.internal/basePickBy.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 3 </span>
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>import</span>
|
||||
{' getAllKeysIn '}
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>from </span>
|
||||
<span style={{ color: getColor('accent3', 'shade7') }}>'./.internal/getAllKeysIn.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 4 </span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 5 </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}>{'/**\n'}</span>
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 6 </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> * Creates an object composed of the `object` properties `predicate` returns{'\n'}</span>
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 7 </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> * truthy for. The predicate is invoked with two arguments: (value, key).{'\n'}</span>
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 8 </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> */{'\n'}</span>
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }> 9 </span>
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}>function </span>
|
||||
<span style={{ color: getColor('accent2', 'shade7') }}>pickBy</span>
|
||||
(object, predicate)
|
||||
{' {\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>10 </span>
|
||||
<span style={{ color: getColor('accent5', 'shade7') }}> if </span>
|
||||
(object
|
||||
<span style={{ color: getColor('accent5', 'shade7') }}> == </span>
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}>null</span>
|
||||
{') {\n'}
|
||||
<span style={{ color: getColor('shade5', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>11 </span>
|
||||
<span style={{ color: getColor('accent5', 'shade7') }}> return </span>
|
||||
{'{};'}
|
||||
<span style={{ borderLeftColor: getColor('accent6', 'shade7') }} className={ [styles.cursor, cursor].join(' ') }></span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>12 </span>
|
||||
{' }\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>13 </span>
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}> const </span>
|
||||
props
|
||||
<span style={{ color: getColor('accent5', 'shade7') }}> = </span>
|
||||
map(getAllKeysIn(object), prop
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}> => </span>
|
||||
{'[prop]);\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>14 </span>
|
||||
<span style={{ color: getColor('accent5', 'shade7') }}> return </span>
|
||||
basePickBy(object, props, (value, path)
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}> => </span>
|
||||
predicate(value, path[
|
||||
<span style={{ color: getColor('accent0', 'shade7') }}>0</span>
|
||||
{']));\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>15 </span>
|
||||
{'}\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>16 </span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade1', 'shade7'), backgroundColor: getColor('shade0') }} className={ styles.lineNumber }>17 </span>
|
||||
<span style={{ color: getColor('accent6', 'shade7') }}>export </span>
|
||||
<span style={{ color: getColor('accent7', 'shade7') }}>default </span>
|
||||
pickBy;
|
||||
</code>
|
||||
</pre>
|
||||
) }
|
||||
</ColorState>
|
||||
<pre className={ styles.pre }>
|
||||
<code style={{ color: activePreparedColorSet['shade7'] }}>
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 1 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>import</span>
|
||||
{' map '}
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>from </span>
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>'map.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 2 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>import</span>
|
||||
{' basePickBy '}
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>from </span>
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>'./.internal/basePickBy.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 3 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>import</span>
|
||||
{' getAllKeysIn '}
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>from </span>
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>'./.internal/getAllKeysIn.js'</span>
|
||||
;
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 4 </span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 5 </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}>{'/**\n'}</span>
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 6 </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> * Creates an object composed of the `object` properties `predicate` returns{'\n'}</span>
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 7 </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> * truthy for. The predicate is invoked with two arguments: (value, key).{'\n'}</span>
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 8 </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> */{'\n'}</span>
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }> 9 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}>function </span>
|
||||
<span style={{ color: activePreparedColorSet['accent2'] }}>pickBy</span>
|
||||
(object, predicate)
|
||||
{' {\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>10 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent5'] }}> if </span>
|
||||
(object
|
||||
<span style={{ color: activePreparedColorSet['accent5'] }}> == </span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}>null</span>
|
||||
{') {\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade5'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>11 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent5'] }}> return </span>
|
||||
{'{};'}
|
||||
<span style={{ borderLeftColor: activePreparedColorSet['accent6'] }} className={ [styles.cursor, cursor].join(' ') }></span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>12 </span>
|
||||
{' }\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>13 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}> const </span>
|
||||
props
|
||||
<span style={{ color: activePreparedColorSet['accent5'] }}> = </span>
|
||||
map(getAllKeysIn(object), prop
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}> => </span>
|
||||
{'[prop]);\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>14 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent5'] }}> return </span>
|
||||
basePickBy(object, props, (value, path)
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}> => </span>
|
||||
predicate(value, path[
|
||||
<span style={{ color: activePreparedColorSet['accent0'] }}>0</span>
|
||||
{']));\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>15 </span>
|
||||
{'}\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>16 </span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade1'], backgroundColor: activePreparedColorSet['shade0'] }} className={ styles.lineNumber }>17 </span>
|
||||
<span style={{ color: activePreparedColorSet['accent6'] }}>export </span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}>default </span>
|
||||
pickBy;
|
||||
</code>
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
@ -1,62 +1,63 @@
|
||||
import React, { useRef } from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import React, { useRef, useContext } from 'react';
|
||||
import { DropletIcon } from './Icons';
|
||||
import styles from './ColorInput.module.css';
|
||||
import getBestForeground from './getBestForeground';
|
||||
import colorSupport from './colorInputSupport';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default ({ className, style, colorKey, help }) => {
|
||||
const textInput = useRef(null);
|
||||
const {
|
||||
getActiveColorOrFallback,
|
||||
getActiveRawColor,
|
||||
setActiveRawColor,
|
||||
} = useContext(ThemeContext);
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor, getRawColor, setColor }) => (
|
||||
<div className={ [styles.outerWrapper, className].join(' ') } style={ style }>
|
||||
<div className={ styles.inputsWrapper }>
|
||||
<label className={ styles.textInputWrapper } style={{ color: getColor('shade7') }}>
|
||||
<span className={ styles.label }>{ colorKey }:</span>
|
||||
<input
|
||||
type="text"
|
||||
className={ styles.textInput }
|
||||
style={{
|
||||
color: getColor('shade7'),
|
||||
borderBottomColor: getColor(colorKey, 'shade7'),
|
||||
}}
|
||||
value={ getRawColor(colorKey) }
|
||||
onChange={ evt => setColor(colorKey, evt.target.value) }
|
||||
ref={ textInput }
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
className={ styles.swatch }
|
||||
style={{
|
||||
color: getBestForeground(
|
||||
getColor('shade7'),
|
||||
getColor('shade0'),
|
||||
getColor(colorKey, 'shade7'),
|
||||
),
|
||||
backgroundColor: getColor(colorKey, 'shade7'),
|
||||
}}
|
||||
tabIndex="-1"
|
||||
onClick={ evt => {
|
||||
if(!colorSupport) {
|
||||
evt.preventDefault();
|
||||
textInput.current.focus()
|
||||
}
|
||||
} }
|
||||
>
|
||||
<DropletIcon />
|
||||
<input
|
||||
type="color"
|
||||
className={ styles.colorInput }
|
||||
value={ getColor(colorKey) }
|
||||
onChange={ evt => setColor(colorKey, evt.target.value) }
|
||||
tabIndex="-1"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className={ styles.help } style={{ color: getColor('shade4', 'shade7') }}>{ help }</div>
|
||||
</div>
|
||||
) }
|
||||
</ColorState>
|
||||
<div className={ [styles.outerWrapper, className].join(' ') } style={ style }>
|
||||
<div className={ styles.inputsWrapper }>
|
||||
<label className={ styles.textInputWrapper } style={{ color: getActiveColorOrFallback(['shade7']) }}>
|
||||
<span className={ styles.label }>{ colorKey }:</span>
|
||||
<input
|
||||
type="text"
|
||||
className={ styles.textInput }
|
||||
style={{
|
||||
color: getActiveColorOrFallback(['shade7']),
|
||||
borderBottomColor: getActiveColorOrFallback([colorKey]),
|
||||
}}
|
||||
value={ getActiveRawColor(colorKey) }
|
||||
onChange={ evt => setActiveRawColor(colorKey, evt.target.value) }
|
||||
ref={ textInput }
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
className={ styles.swatch }
|
||||
style={{
|
||||
color: getBestForeground(
|
||||
getActiveColorOrFallback(['shade7']),
|
||||
getActiveColorOrFallback(['shade0'], true),
|
||||
getActiveColorOrFallback([colorKey]),
|
||||
),
|
||||
backgroundColor: getActiveColorOrFallback([colorKey]),
|
||||
}}
|
||||
tabIndex="-1"
|
||||
onClick={ evt => {
|
||||
if(!colorSupport) {
|
||||
evt.preventDefault();
|
||||
textInput.current.focus()
|
||||
}
|
||||
} }
|
||||
>
|
||||
<DropletIcon />
|
||||
<input
|
||||
type="color"
|
||||
className={ styles.colorInput }
|
||||
value={ getActiveColorOrFallback([colorKey], colorKey === 'shade0') }
|
||||
onChange={ evt => setActiveRawColor(colorKey, evt.target.value) }
|
||||
tabIndex="-1"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className={ styles.help } style={{ color: getActiveColorOrFallback(['shade4']) }}>{ help }</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
@ -1,78 +1,68 @@
|
||||
import React from 'react';
|
||||
import { UrlStateConsumer } from './UrlState';
|
||||
import React, { useContext } from 'react';
|
||||
import styles from './ColorSetInputs.module.css';
|
||||
import ColorState from './ColorState';
|
||||
import ColorInput from './ColorInput';
|
||||
import Checkbox from './Checkbox';
|
||||
import CalculateIntermediaryShadesState from './CalculateIntermediaryShadesState';
|
||||
import Tabs from './Tabs';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => (
|
||||
<UrlStateConsumer>
|
||||
{ ({ getValueOrFallback, mergeState }) => {
|
||||
const isDark = getValueOrFallback([['activeColorSet']]) === 'dark';
|
||||
return (
|
||||
<Tabs>
|
||||
{ ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => (
|
||||
<ColorState>
|
||||
{ () => (
|
||||
<>
|
||||
<div>
|
||||
<button
|
||||
className={ tabClassName }
|
||||
style={ getTabStyle(isDark) }
|
||||
onClick={ () => mergeState({ activeColorSet: 'dark' }) }
|
||||
>Dark Variant</button>
|
||||
<button
|
||||
className={ tabClassName }
|
||||
style={ getTabStyle(!isDark) }
|
||||
onClick={ () => mergeState({ activeColorSet: 'light' }) }
|
||||
>Light Variant</button>
|
||||
</div>
|
||||
<div className={ `${styles.inputContainer} ${contentClassName}` } style={ contentStyle }>
|
||||
<ColorInput className={ styles.shade0 } colorKey="shade0" help="background color" />
|
||||
<CalculateIntermediaryShadesState>
|
||||
{ ({ getValue, setValue }) => (
|
||||
<>
|
||||
{ !getValue() && (
|
||||
<>
|
||||
<ColorInput className={ styles.shade1 } colorKey="shade1" help="UI" />
|
||||
<ColorInput className={ styles.shade2 } colorKey="shade2" help="UI, text selection" />
|
||||
<ColorInput className={ styles.shade3 } colorKey="shade3" help="UI, code comments" />
|
||||
<ColorInput className={ styles.shade4 } colorKey="shade4" help="UI" />
|
||||
<ColorInput className={ styles.shade5 } colorKey="shade5" help="UI" />
|
||||
<ColorInput className={ styles.shade6 } colorKey="shade6" help="foreground text" />
|
||||
</>
|
||||
) }
|
||||
<ColorInput
|
||||
className={ [styles.shade7, getValue() ? styles.collapsed : ''].join(' ') }
|
||||
colorKey="shade7"
|
||||
help="foreground text"
|
||||
/>
|
||||
<Checkbox
|
||||
className={ styles.checkbox }
|
||||
label="calculate intermediary shades"
|
||||
value={ getValue() }
|
||||
onChange={ setValue }
|
||||
/>
|
||||
</>
|
||||
) }
|
||||
</CalculateIntermediaryShadesState>
|
||||
<ColorInput className={ styles.accent0 } colorKey="accent0" help="error, vcs deletion" />
|
||||
<ColorInput className={ styles.accent1 } colorKey="accent1" help="syntax" />
|
||||
<ColorInput className={ styles.accent2 } colorKey="accent2" help="warning, vcs modification" />
|
||||
<ColorInput className={ styles.accent3 } colorKey="accent3" help="success, vcs addition" />
|
||||
<ColorInput className={ styles.accent4 } colorKey="accent4" help="syntax" />
|
||||
<ColorInput className={ styles.accent5 } colorKey="accent5" help="syntax" />
|
||||
<ColorInput className={ styles.accent6 } colorKey="accent6" help="syntax, caret/cursor" />
|
||||
<ColorInput className={ styles.accent7 } colorKey="accent7" help="syntax, special" />
|
||||
</div>
|
||||
</>
|
||||
) }
|
||||
</ColorState>
|
||||
) }
|
||||
</Tabs>
|
||||
);
|
||||
} }
|
||||
</UrlStateConsumer>
|
||||
);
|
||||
export default () => {
|
||||
const {
|
||||
activeCalculateIntermediaryShades,
|
||||
setActiveCalculateIntermediaryShades,
|
||||
activeColorSet,
|
||||
setActiveColorSet,
|
||||
} = useContext(ThemeContext);
|
||||
const isDark = activeColorSet === 'dark';
|
||||
return (
|
||||
<Tabs>
|
||||
{ ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => (
|
||||
<>
|
||||
<div>
|
||||
<button
|
||||
className={ tabClassName }
|
||||
style={ getTabStyle(isDark) }
|
||||
onClick={ () => setActiveColorSet('dark') }
|
||||
>Dark Variant</button>
|
||||
<button
|
||||
className={ tabClassName }
|
||||
style={ getTabStyle(!isDark) }
|
||||
onClick={ () => setActiveColorSet('light') }
|
||||
>Light Variant</button>
|
||||
</div>
|
||||
<div className={ `${styles.inputContainer} ${contentClassName}` } style={ contentStyle }>
|
||||
<ColorInput className={ styles.shade0 } colorKey="shade0" help="background color" />
|
||||
{ !activeCalculateIntermediaryShades && (
|
||||
<>
|
||||
<ColorInput className={ styles.shade1 } colorKey="shade1" help="UI" />
|
||||
<ColorInput className={ styles.shade2 } colorKey="shade2" help="UI, text selection" />
|
||||
<ColorInput className={ styles.shade3 } colorKey="shade3" help="UI, code comments" />
|
||||
<ColorInput className={ styles.shade4 } colorKey="shade4" help="UI" />
|
||||
<ColorInput className={ styles.shade5 } colorKey="shade5" help="UI" />
|
||||
<ColorInput className={ styles.shade6 } colorKey="shade6" help="foreground text" />
|
||||
</>
|
||||
) }
|
||||
<ColorInput
|
||||
className={ [styles.shade7, activeCalculateIntermediaryShades ? styles.collapsed : ''].join(' ') }
|
||||
colorKey="shade7"
|
||||
help="foreground text"
|
||||
/>
|
||||
<Checkbox
|
||||
className={ styles.checkbox }
|
||||
label="calculate intermediary shades"
|
||||
value={ activeCalculateIntermediaryShades }
|
||||
onChange={ setActiveCalculateIntermediaryShades }
|
||||
/>
|
||||
<ColorInput className={ styles.accent0 } colorKey="accent0" help="error, vcs deletion" />
|
||||
<ColorInput className={ styles.accent1 } colorKey="accent1" help="syntax" />
|
||||
<ColorInput className={ styles.accent2 } colorKey="accent2" help="warning, vcs modification" />
|
||||
<ColorInput className={ styles.accent3 } colorKey="accent3" help="success, vcs addition" />
|
||||
<ColorInput className={ styles.accent4 } colorKey="accent4" help="syntax" />
|
||||
<ColorInput className={ styles.accent5 } colorKey="accent5" help="syntax" />
|
||||
<ColorInput className={ styles.accent6 } colorKey="accent6" help="syntax, caret/cursor" />
|
||||
<ColorInput className={ styles.accent7 } colorKey="accent7" help="syntax, special" />
|
||||
</div>
|
||||
</>
|
||||
) }
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
@ -1,61 +0,0 @@
|
||||
import React from 'react';
|
||||
import { UrlStateConsumer } from './UrlState';
|
||||
import { get } from 'lodash';
|
||||
import colorSteps from 'color-steps';
|
||||
import Color from 'color';
|
||||
import CalculateIntermediaryShadesState from './CalculateIntermediaryShadesState';
|
||||
|
||||
export default ({ children }) => (
|
||||
<CalculateIntermediaryShadesState>
|
||||
{ ({ getValue: shouldCalculateIntermediaryShades }) => (
|
||||
<UrlStateConsumer>
|
||||
{ ({ getValueOrFallback, mergeState, rawState }) => children({
|
||||
getColor: (...args) => {
|
||||
const parse = v => Color(v).hex();
|
||||
const calculatedState = (() => {
|
||||
if (shouldCalculateIntermediaryShades()) {
|
||||
try {
|
||||
const calculatedColors = colorSteps(
|
||||
parse(get(rawState, ['colors', getValueOrFallback([['activeColorSet']]), 'shade0'], '')),
|
||||
parse(get(rawState, ['colors', getValueOrFallback([['activeColorSet']]), 'shade7'], '')),
|
||||
);
|
||||
return {
|
||||
colors: {
|
||||
[getValueOrFallback([['activeColorSet']])]: calculatedColors.reduce(
|
||||
(shades, color, idx) => ({
|
||||
...shades,
|
||||
[`shade${idx+1}`]: color,
|
||||
}),
|
||||
{},
|
||||
)
|
||||
},
|
||||
};
|
||||
} catch {}
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
return getValueOrFallback(
|
||||
args.map(colorKey => [
|
||||
'colors',
|
||||
getValueOrFallback([['activeColorSet']]),
|
||||
colorKey
|
||||
]),
|
||||
parse,
|
||||
calculatedState,
|
||||
);
|
||||
},
|
||||
setColor: (key, value) => {
|
||||
mergeState({
|
||||
colors: {
|
||||
[getValueOrFallback([['activeColorSet']])]: {
|
||||
[key]: value,
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
getRawColor: key => get(rawState, ['colors', getValueOrFallback([['activeColorSet']]), key], ''),
|
||||
}) }
|
||||
</UrlStateConsumer>
|
||||
) }
|
||||
</CalculateIntermediaryShadesState>
|
||||
);
|
@ -1,11 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useContext } from 'react';
|
||||
import Checkbox from './Checkbox';
|
||||
import styles from './Download.module.css';
|
||||
import ColorState from './ColorState';
|
||||
import Button from './Button';
|
||||
import generateZip from './generateZip';
|
||||
import saveAs from 'file-saver';
|
||||
import { UrlStateConsumer } from './UrlState';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => {
|
||||
const [hyper, setHyper] = useState(false);
|
||||
@ -35,243 +34,238 @@ export default () => {
|
||||
const [sketchPalettes, setSketchPalettes] = useState(false);
|
||||
const [tmux, setTmux] = useState(false);
|
||||
|
||||
const { getActiveColorOrFallback, preparedColorSet } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<>
|
||||
<div className={ styles.fieldsetWrapper }>
|
||||
<fieldset style={{ borderColor: getColor('shade2', 'shade7') }}>
|
||||
<legend style={{ color: getColor('shade5', 'shade7') }}>Terminals</legend>
|
||||
<Checkbox
|
||||
value={ hyper }
|
||||
onChange={ () => setHyper(!hyper) }
|
||||
label="Hyper"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ iterm }
|
||||
onChange={ () => setIterm(!iterm) }
|
||||
label="iTerm"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ gnomeTerminal }
|
||||
onChange={ () => setGnomeTerminal(!gnomeTerminal) }
|
||||
label="GNOME Terminal"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ conemu }
|
||||
onChange={ () => setConemu(!conemu) }
|
||||
label="ConEmu"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ cmd }
|
||||
onChange={ () => setCmd(!cmd) }
|
||||
label="CMD.exe"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ termite }
|
||||
onChange={ () => setTermite(!termite) }
|
||||
label="Termite"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ kitty }
|
||||
onChange={ () => setKitty(!kitty) }
|
||||
label="kitty"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getColor('shade2', 'shade7') }}>
|
||||
<legend style={{ color: getColor('shade5', 'shade7') }}>Editors / IDEs</legend>
|
||||
<Checkbox
|
||||
value={ atomSyntax }
|
||||
onChange={ () => setAtomSyntax(!atomSyntax) }
|
||||
label="Atom (syntax)"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ atomUi }
|
||||
onChange={ () => setAtomUi(!atomUi) }
|
||||
label="Atom (UI)"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ sublimeText }
|
||||
onChange={ () => setSublimeText(!sublimeText) }
|
||||
label="Sublime Text"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vim }
|
||||
onChange={ () => setVim(!vim) }
|
||||
label="Vim"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vimLightline }
|
||||
onChange={ () => setVimLightline(!vimLightline) }
|
||||
label="lightline.vim"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vscode }
|
||||
onChange={ () => setVscode(!vscode) }
|
||||
label="VS Code"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ xcode }
|
||||
onChange={ () => setXcode(!xcode) }
|
||||
label="Xcode"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ bbedit }
|
||||
onChange={ () => setBbedit(!bbedit) }
|
||||
label="BBEdit"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ jetbrains }
|
||||
onChange={ () => setJetbrains(!jetbrains) }
|
||||
label="JetBrains"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getColor('shade2', 'shade7') }}>
|
||||
<legend style={{ color: getColor('shade5', 'shade7') }}>Wallpapers</legend>
|
||||
<Checkbox
|
||||
value={ wallpaperBlockWave }
|
||||
onChange={ () => setWallpaperBlockWave(!wallpaperBlockWave) }
|
||||
label="“Block Wave”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperOctagon }
|
||||
onChange={ () => setWallpaperOctagon(!wallpaperOctagon) }
|
||||
label="“Octagon”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperTriangles }
|
||||
onChange={ () => setWallpaperTriangles(!wallpaperTriangles) }
|
||||
label="“Triangles”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperTrianglify }
|
||||
onChange={ () => setWallpaperTrianglify(!wallpaperTrianglify) }
|
||||
label="“Trianglify”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperShirts }
|
||||
onChange={ () => setWallpaperShirts(!wallpaperShirts) }
|
||||
label="“Shirts”"
|
||||
accentSelected
|
||||
/>
|
||||
<div
|
||||
className={ styles.wallpaperHint }
|
||||
style={{ color: getColor('shade3', 'shade7') }}
|
||||
>
|
||||
Wallpapers will be rendered at the browser viewport's resolution.
|
||||
</div>
|
||||
{ window.document.fullscreenEnabled ? (
|
||||
<Button
|
||||
className={ styles.fullscreen }
|
||||
small
|
||||
onClick={
|
||||
() => window.document.documentElement.requestFullscreen()
|
||||
}
|
||||
>Go fullscreen</Button>
|
||||
) : null }
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getColor('shade2', 'shade7') }}>
|
||||
<legend style={{ color: getColor('shade5', 'shade7') }}>Other</legend>
|
||||
<Checkbox
|
||||
value={ slack }
|
||||
onChange={ () => setSlack(!slack) }
|
||||
label="Slack sidebar"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ alfred }
|
||||
onChange={ () => setAlfred(!alfred) }
|
||||
label="Alfred.app"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ chrome }
|
||||
onChange={ () => setChrome(!chrome) }
|
||||
label="Chrome"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ sketchPalettes }
|
||||
onChange={ () => setSketchPalettes(!sketchPalettes) }
|
||||
label="Sketch palettes"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ tmux }
|
||||
onChange={ () => setTmux(!tmux) }
|
||||
label="tmux"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
<>
|
||||
<div className={ styles.fieldsetWrapper }>
|
||||
<fieldset style={{ borderColor: getActiveColorOrFallback(['shade2']) }}>
|
||||
<legend style={{ color: getActiveColorOrFallback(['shade5']) }}>Terminals</legend>
|
||||
<Checkbox
|
||||
value={ hyper }
|
||||
onChange={ () => setHyper(!hyper) }
|
||||
label="Hyper"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ iterm }
|
||||
onChange={ () => setIterm(!iterm) }
|
||||
label="iTerm"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ gnomeTerminal }
|
||||
onChange={ () => setGnomeTerminal(!gnomeTerminal) }
|
||||
label="GNOME Terminal"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ conemu }
|
||||
onChange={ () => setConemu(!conemu) }
|
||||
label="ConEmu"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ cmd }
|
||||
onChange={ () => setCmd(!cmd) }
|
||||
label="CMD.exe"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ termite }
|
||||
onChange={ () => setTermite(!termite) }
|
||||
label="Termite"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ kitty }
|
||||
onChange={ () => setKitty(!kitty) }
|
||||
label="kitty"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getActiveColorOrFallback(['shade2']) }}>
|
||||
<legend style={{ color: getActiveColorOrFallback(['shade5']) }}>Editors / IDEs</legend>
|
||||
<Checkbox
|
||||
value={ atomSyntax }
|
||||
onChange={ () => setAtomSyntax(!atomSyntax) }
|
||||
label="Atom (syntax)"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ atomUi }
|
||||
onChange={ () => setAtomUi(!atomUi) }
|
||||
label="Atom (UI)"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ sublimeText }
|
||||
onChange={ () => setSublimeText(!sublimeText) }
|
||||
label="Sublime Text"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vim }
|
||||
onChange={ () => setVim(!vim) }
|
||||
label="Vim"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vimLightline }
|
||||
onChange={ () => setVimLightline(!vimLightline) }
|
||||
label="lightline.vim"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ vscode }
|
||||
onChange={ () => setVscode(!vscode) }
|
||||
label="VS Code"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ xcode }
|
||||
onChange={ () => setXcode(!xcode) }
|
||||
label="Xcode"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ bbedit }
|
||||
onChange={ () => setBbedit(!bbedit) }
|
||||
label="BBEdit"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ jetbrains }
|
||||
onChange={ () => setJetbrains(!jetbrains) }
|
||||
label="JetBrains"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getActiveColorOrFallback(['shade2']) }}>
|
||||
<legend style={{ color: getActiveColorOrFallback(['shade5']) }}>Wallpapers</legend>
|
||||
<Checkbox
|
||||
value={ wallpaperBlockWave }
|
||||
onChange={ () => setWallpaperBlockWave(!wallpaperBlockWave) }
|
||||
label="“Block Wave”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperOctagon }
|
||||
onChange={ () => setWallpaperOctagon(!wallpaperOctagon) }
|
||||
label="“Octagon”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperTriangles }
|
||||
onChange={ () => setWallpaperTriangles(!wallpaperTriangles) }
|
||||
label="“Triangles”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperTrianglify }
|
||||
onChange={ () => setWallpaperTrianglify(!wallpaperTrianglify) }
|
||||
label="“Trianglify”"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ wallpaperShirts }
|
||||
onChange={ () => setWallpaperShirts(!wallpaperShirts) }
|
||||
label="“Shirts”"
|
||||
accentSelected
|
||||
/>
|
||||
<div
|
||||
className={ styles.wallpaperHint }
|
||||
style={{ color: getActiveColorOrFallback(['shade3']) }}
|
||||
>
|
||||
Wallpapers will be rendered at the browser viewport's resolution.
|
||||
</div>
|
||||
<UrlStateConsumer>
|
||||
{ ({ rawState }) => (
|
||||
<Button
|
||||
special
|
||||
onClick={ async () => {
|
||||
const zip = await generateZip(
|
||||
{
|
||||
hyper,
|
||||
iterm,
|
||||
gnomeTerminal,
|
||||
conemu,
|
||||
cmd,
|
||||
termite,
|
||||
kitty,
|
||||
atomSyntax,
|
||||
atomUi,
|
||||
sublimeText,
|
||||
vim,
|
||||
vimLightline,
|
||||
vscode,
|
||||
xcode,
|
||||
bbedit,
|
||||
jetbrains,
|
||||
wallpaperBlockWave,
|
||||
wallpaperOctagon,
|
||||
wallpaperTriangles,
|
||||
wallpaperTrianglify,
|
||||
wallpaperShirts,
|
||||
slack,
|
||||
alfred,
|
||||
chrome,
|
||||
sketchPalettes,
|
||||
tmux,
|
||||
},
|
||||
rawState.colors,
|
||||
window.innerWidth * window.devicePixelRatio,
|
||||
window.innerHeight * window.devicePixelRatio,
|
||||
window.location.href,
|
||||
);
|
||||
zip.generateAsync({ type: 'blob' }).then(contents => {
|
||||
saveAs(contents, 'themer.zip');
|
||||
});
|
||||
} }
|
||||
>Download</Button>
|
||||
) }
|
||||
</UrlStateConsumer>
|
||||
</>
|
||||
) }
|
||||
</ColorState>
|
||||
{ window.document.fullscreenEnabled ? (
|
||||
<Button
|
||||
className={ styles.fullscreen }
|
||||
small
|
||||
onClick={
|
||||
() => window.document.documentElement.requestFullscreen()
|
||||
}
|
||||
>Go fullscreen</Button>
|
||||
) : null }
|
||||
</fieldset>
|
||||
<fieldset style={{ borderColor: getActiveColorOrFallback(['shade2']) }}>
|
||||
<legend style={{ color: getActiveColorOrFallback(['shade5']) }}>Other</legend>
|
||||
<Checkbox
|
||||
value={ slack }
|
||||
onChange={ () => setSlack(!slack) }
|
||||
label="Slack sidebar"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ alfred }
|
||||
onChange={ () => setAlfred(!alfred) }
|
||||
label="Alfred.app"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ chrome }
|
||||
onChange={ () => setChrome(!chrome) }
|
||||
label="Chrome"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ sketchPalettes }
|
||||
onChange={ () => setSketchPalettes(!sketchPalettes) }
|
||||
label="Sketch palettes"
|
||||
accentSelected
|
||||
/>
|
||||
<Checkbox
|
||||
value={ tmux }
|
||||
onChange={ () => setTmux(!tmux) }
|
||||
label="tmux"
|
||||
accentSelected
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
<Button
|
||||
special
|
||||
disabled={ !preparedColorSet.dark && !preparedColorSet.light }
|
||||
onClick={ async () => {
|
||||
const zip = await generateZip(
|
||||
{
|
||||
hyper,
|
||||
iterm,
|
||||
gnomeTerminal,
|
||||
conemu,
|
||||
cmd,
|
||||
termite,
|
||||
kitty,
|
||||
atomSyntax,
|
||||
atomUi,
|
||||
sublimeText,
|
||||
vim,
|
||||
vimLightline,
|
||||
vscode,
|
||||
xcode,
|
||||
bbedit,
|
||||
jetbrains,
|
||||
wallpaperBlockWave,
|
||||
wallpaperOctagon,
|
||||
wallpaperTriangles,
|
||||
wallpaperTrianglify,
|
||||
wallpaperShirts,
|
||||
slack,
|
||||
alfred,
|
||||
chrome,
|
||||
sketchPalettes,
|
||||
tmux,
|
||||
},
|
||||
preparedColorSet,
|
||||
window.innerWidth * window.devicePixelRatio,
|
||||
window.innerHeight * window.devicePixelRatio,
|
||||
window.location.href,
|
||||
);
|
||||
zip.generateAsync({ type: 'blob' }).then(contents => {
|
||||
saveAs(contents, 'themer.zip');
|
||||
});
|
||||
} }
|
||||
>Download</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React from "react";
|
||||
import ColorState from "./ColorState";
|
||||
import React, { useContext } from "react";
|
||||
import ThemeContext from "./ThemeContext";
|
||||
|
||||
export default props => (
|
||||
<ColorState>
|
||||
{({ getColor }) => (
|
||||
<a style={{ color: getColor("accent5", "shade7") }} { ...props }>
|
||||
{ props.children }
|
||||
</a>
|
||||
)}
|
||||
</ColorState>
|
||||
);
|
||||
export default props => {
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
return (
|
||||
<a style={{ color: getActiveColorOrFallback(['accent5']) }} { ...props }>
|
||||
{ props.children }
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
103
web/src/Main.js
Normal file
103
web/src/Main.js
Normal file
@ -0,0 +1,103 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import styles from './Main.module.css';
|
||||
|
||||
import ColorSetInputs from './ColorSetInputs';
|
||||
import TextPreviews from './TextPreviews';
|
||||
import WallpaperPreview from './WallpaperPreview';
|
||||
import PreBuiltList from './PreBuiltList';
|
||||
import Download from './Download';
|
||||
import Link from './Link';
|
||||
import CopyUrl from './CopyUrl';
|
||||
import StarCount from './StarCount';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => {
|
||||
const [keyboarding, setKeyboarding] = useState(false);
|
||||
const onKeyDown = (evt) => {
|
||||
if (evt.key === 'Tab') {
|
||||
setKeyboarding(true);
|
||||
}
|
||||
};
|
||||
const onMouseDown = () => {
|
||||
setKeyboarding(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
window.document.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
window.document.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
window.document.addEventListener('mousedown', onMouseDown);
|
||||
return () => {
|
||||
window.document.removeEventListener('mousedown', onMouseDown);
|
||||
}
|
||||
});
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
return (
|
||||
<div
|
||||
className={ `${styles.app} ${keyboarding ? styles.keyboarding : ''}` }
|
||||
style={{
|
||||
backgroundColor: getActiveColorOrFallback(['shade0'], true),
|
||||
'--selection-foreground-color': getActiveColorOrFallback(['shade0'], true),
|
||||
'--selection-background-color': getActiveColorOrFallback(['accent5']),
|
||||
'--focus-outline-color': getActiveColorOrFallback(['accent6']),
|
||||
}}
|
||||
>
|
||||
<div className={ styles.container }>
|
||||
<header className={ styles.header }>
|
||||
<h1 className={ styles.h1 } style={{ color: getActiveColorOrFallback(['shade7']) }}>themer</h1>
|
||||
<StarCount />
|
||||
</header>
|
||||
<hr
|
||||
className={ styles.hr }
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(
|
||||
to right,
|
||||
${getActiveColorOrFallback(['accent0', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent1', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent2', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent3', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent4', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent4', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent5', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent6', 'shade2'])},
|
||||
${getActiveColorOrFallback(['accent7', 'shade2'])}
|
||||
)
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
<p style={{ color: getActiveColorOrFallback(['shade6'])}}>themer takes a set of colors and generates themes for your apps (editors, terminals, wallpapers, and more).</p>
|
||||
<h2 className={ styles.h2 } style={{ color: getActiveColorOrFallback(['shade7']) }}>1. Define colors</h2>
|
||||
<p className={ styles.help } style={{ color: getActiveColorOrFallback(['shade6']) }}>Input your colors using any CSS format (keyword, hsl, rgb, etc.).</p>
|
||||
<ColorSetInputs />
|
||||
<p className={ styles.preBuilt } style={{ color: getActiveColorOrFallback(['shade6']) }}>
|
||||
Or start with a pre-built color set:
|
||||
</p>
|
||||
<PreBuiltList />
|
||||
<h2 className={ styles.h2 } style={{ color: getActiveColorOrFallback(['shade7'])}}>2. Preview</h2>
|
||||
<div className={ styles.previewsContainer }>
|
||||
<TextPreviews />
|
||||
<WallpaperPreview />
|
||||
</div>
|
||||
<h2 className={ styles.h2 } style={{ color: getActiveColorOrFallback(['shade7'])}}>3. Download</h2>
|
||||
<p className={ styles.help } style={{ color: getActiveColorOrFallback(['shade6']) }}>Select which themes you'd like to generate from your color set.</p>
|
||||
<Download />
|
||||
</div>
|
||||
<div className={ styles.shape } style={{ '--shape-color': getActiveColorOrFallback(['shade1'], true) }}>
|
||||
<div className={ styles.container }>
|
||||
<p style={{ color: getActiveColorOrFallback(['shade7']) }}>
|
||||
<span style={{ color: getActiveColorOrFallback(['accent1']) }}>Pro tip:</span>
|
||||
{' '}
|
||||
The current URL uniquely identifies your current theme. Bookmark it, email it, or share it however you like.
|
||||
<CopyUrl className={ styles.copyUrl }/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer className={ styles.footer } style={{ color: getActiveColorOrFallback(['shade3']) }}>
|
||||
themer is free and open source software, made by <Link href="https://mjswensen.com">mjswensen</Link> with <Link href="https://github.com/mjswensen/themer/graphs/contributors">contributors</Link>, and is released under the MIT license
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { UrlStateConsumer, paramsFromState } from './UrlState';
|
||||
import ColorState from './ColorState';
|
||||
import React, { useContext } from 'react';
|
||||
import Link from './Link';
|
||||
import { has } from 'lodash';
|
||||
import styles from './PreBuiltList.module.css';
|
||||
import ThemeContext, { paramsFromState } from './ThemeContext';
|
||||
|
||||
import { colors as defaultColors } from 'themer-colors-default';
|
||||
import { colors as nightSkyColors } from 'themer-colors-night-sky';
|
||||
@ -16,51 +15,46 @@ import { colors as solarizedColors } from 'themer-colors-solarized';
|
||||
import { colors as githubUniverseColors } from 'themer-colors-github-universe';
|
||||
import { colors as novaColors } from 'themer-colors-nova';
|
||||
|
||||
const PreBuiltLink = ({ colors, children }) => (
|
||||
<UrlStateConsumer>
|
||||
{ ({ getValueOrFallback }) => {
|
||||
const activeColorSet = getValueOrFallback([['activeColorSet']]);
|
||||
const oppositeColorSet = activeColorSet === 'dark' ? 'light' : 'dark';
|
||||
const preparedState = {
|
||||
colors,
|
||||
activeColorSet: has(colors, activeColorSet) ? activeColorSet : oppositeColorSet,
|
||||
calculateIntermediaryShades: {
|
||||
dark: !has(colors, 'dark.shade1'),
|
||||
light: !has(colors, 'light.shade1'),
|
||||
},
|
||||
};
|
||||
return (<Link href={ paramsFromState(preparedState) }>{ children }</Link>);
|
||||
} }
|
||||
</UrlStateConsumer>
|
||||
);
|
||||
const PreBuiltLink = ({ colors, children }) => {
|
||||
const { activeColorSet } = useContext(ThemeContext);
|
||||
const oppositeColorSet = activeColorSet === 'dark' ? 'light' : 'dark';
|
||||
const preparedState = {
|
||||
colors,
|
||||
activeColorSet: has(colors, activeColorSet) ? activeColorSet : oppositeColorSet,
|
||||
calculateIntermediaryShades: {
|
||||
dark: !has(colors, 'dark.shade1'),
|
||||
light: !has(colors, 'light.shade1'),
|
||||
},
|
||||
};
|
||||
return (<Link href={ paramsFromState(preparedState) }>{ children }</Link>);
|
||||
};
|
||||
|
||||
export default () => (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<ul className={ styles.linksets } style={{ color: getColor('shade5', 'shade7') }}>
|
||||
<li>
|
||||
Original color sets:
|
||||
{' '}
|
||||
<ul className={ styles.links } >
|
||||
<li><PreBuiltLink colors={ defaultColors }>Default</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ nightSkyColors }>Night Sky</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ polarIceColors }>Polar Ice</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ fingerPaintColors }>Finger Paint</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ monkeyColors }>Monkey</PreBuiltLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Ports from third party themes:
|
||||
{' '}
|
||||
<ul className={ styles.links }>
|
||||
<li><PreBuiltLink colors={ oneColors }>One</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ lucidColors }>Lucid</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ solarizedColors }>Solarized</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ githubUniverseColors }>GitHub Universe</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ novaColors }>Nova</PreBuiltLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
) }
|
||||
</ColorState>
|
||||
);
|
||||
export default () => {
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
return (
|
||||
<ul className={ styles.linksets } style={{ color: getActiveColorOrFallback(['shade5']) }}>
|
||||
<li>
|
||||
Original color sets:
|
||||
{' '}
|
||||
<ul className={ styles.links } >
|
||||
<li><PreBuiltLink colors={ defaultColors }>Default</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ nightSkyColors }>Night Sky</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ polarIceColors }>Polar Ice</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ fingerPaintColors }>Finger Paint</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ monkeyColors }>Monkey</PreBuiltLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Ports from third party themes:
|
||||
{' '}
|
||||
<ul className={ styles.links }>
|
||||
<li><PreBuiltLink colors={ oneColors }>One</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ lucidColors }>Lucid</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ solarizedColors }>Solarized</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ githubUniverseColors }>GitHub Universe</PreBuiltLink></li>
|
||||
<li><PreBuiltLink colors={ novaColors }>Nova</PreBuiltLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import numeral from 'numeral';
|
||||
import styles from './StarCount.module.css';
|
||||
import ColorState from './ColorState';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => {
|
||||
const [count, setCount] = useState('');
|
||||
@ -16,34 +16,32 @@ export default () => {
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<span
|
||||
style={{
|
||||
'--star-count-resting-background-color': getColor('shade1', 'shade0'),
|
||||
'--star-count-hover-background-color': getColor('shade2', 'shade0'),
|
||||
'--star-count-active-background-color': getColor('shade0'),
|
||||
}}
|
||||
>
|
||||
<a
|
||||
className={ styles.star }
|
||||
style={{ color: getColor('shade7') }}
|
||||
href="https://github.com/mjswensen/themer"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Star on GitHub</a>
|
||||
{ count ? (
|
||||
<a
|
||||
className={ styles.stargazers }
|
||||
style={{ color: getColor('shade7') }}
|
||||
href="https://github.com/mjswensen/themer/stargazers"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{ count }</a>
|
||||
) : null }
|
||||
</span>
|
||||
) }
|
||||
</ColorState>
|
||||
<span
|
||||
style={{
|
||||
'--star-count-resting-background-color': getActiveColorOrFallback(['shade1'], true),
|
||||
'--star-count-hover-background-color': getActiveColorOrFallback(['shade2'], true),
|
||||
'--star-count-active-background-color': getActiveColorOrFallback(['shade0'], true),
|
||||
}}
|
||||
>
|
||||
<a
|
||||
className={ styles.star }
|
||||
style={{ color: getActiveColorOrFallback(['shade7']) }}
|
||||
href="https://github.com/mjswensen/themer"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Star on GitHub</a>
|
||||
{ count ? (
|
||||
<a
|
||||
className={ styles.stargazers }
|
||||
style={{ color: getActiveColorOrFallback(['shade7']) }}
|
||||
href="https://github.com/mjswensen/themer/stargazers"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{ count }</a>
|
||||
) : null }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -1,23 +1,26 @@
|
||||
import React from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import { useContext } from 'react';
|
||||
import styles from './Tabs.module.css';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default ({ children }) => (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => children({
|
||||
tabClassName: styles.tab,
|
||||
getTabStyle: active => ({
|
||||
backgroundColor: active ? getColor('shade0') : getColor('shade2', 'shade0'),
|
||||
color: getColor('shade7'),
|
||||
borderColor: getColor('shade7'),
|
||||
'--tab-bottom-overlap-color': active ? getColor('shade0') : getColor('shade2', 'shade0'),
|
||||
'--tab-bottom-overlap-size': active ? 'calc(var(--border-size) * 2)' : 'var(--border-size)',
|
||||
}),
|
||||
contentClassName: styles.tabContent,
|
||||
contentStyle: {
|
||||
borderColor: getColor('shade7'),
|
||||
backgroundColor: getColor('shade0'),
|
||||
},
|
||||
}) }
|
||||
</ColorState>
|
||||
);
|
||||
export default ({ children }) => {
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
return children({
|
||||
tabClassName: styles.tab,
|
||||
getTabStyle: active => ({
|
||||
backgroundColor: active ? getActiveColorOrFallback(['shade0'], true) : getActiveColorOrFallback(['shade2'], true),
|
||||
color: getActiveColorOrFallback(['shade7']),
|
||||
borderColor: getActiveColorOrFallback(['shade7']),
|
||||
'--tab-bottom-overlap-color': active
|
||||
? getActiveColorOrFallback(['shade0'], true)
|
||||
: getActiveColorOrFallback(['shade2'], true),
|
||||
'--tab-bottom-overlap-size': active
|
||||
? 'calc(var(--border-size) * 2)'
|
||||
: 'var(--border-size)',
|
||||
}),
|
||||
contentClassName: styles.tabContent,
|
||||
contentStyle: {
|
||||
borderColor: getActiveColorOrFallback(['shade7']),
|
||||
backgroundColor: getActiveColorOrFallback(['shade0'], true),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -1,73 +1,72 @@
|
||||
import React from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import React, { useContext } from 'react';
|
||||
import styles from './TerminalPreview.module.css';
|
||||
import { cursor } from './cursor.module.css';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default () => (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<pre className={ styles.pre }>
|
||||
<code>
|
||||
<span style={{ color: getColor('accent4') }}>~/project</span>
|
||||
<span style={{ color: getColor('accent7') }}>(branch*) </span>
|
||||
<span style={{ color: getColor('accent3') }}>|> </span>
|
||||
<span style={{ color: getColor('shade5', 'shade7') }}>yarn test</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}>$ jest</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>prepare.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-wallpaper-triangles/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-vscode/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-utils/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-wallpaper-octagon/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-atom-syntax/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade0'), backgroundColor: getColor('accent3', 'shade7') }}> PASS </span>
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}> packages/themer-chrome/lib/</span>
|
||||
<span style={{ color: getColor('shade7') }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('accent3', 'shade7') }}>...</span>
|
||||
{'\n\n'}
|
||||
<span style={{ color: getColor('shade7') }}>Test Suites: </span>
|
||||
<span style={{ color: getColor('accent4', 'shade7') }}>42 passed</span>
|
||||
<span style={{ color: getColor('shade6', 'shade7') }}>, 42 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade7') }}>Tests: </span>
|
||||
<span style={{ color: getColor('accent4', 'shade7') }}>145 passed</span>
|
||||
<span style={{ color: getColor('shade6', 'shade7') }}>, 145 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade7') }}>Snapshots: </span>
|
||||
<span style={{ color: getColor('accent4', 'shade7') }}>102 passed</span>
|
||||
<span style={{ color: getColor('shade6', 'shade7') }}>, 102 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade7') }}>Time: </span>
|
||||
<span style={{ color: getColor('shade6', 'shade7') }}>5.626s</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('shade3', 'shade7') }}>Ran all test suites.</span>
|
||||
{'\n'}
|
||||
<span style={{ color: getColor('accent4') }}>~/project</span>
|
||||
<span style={{ color: getColor('accent7') }}>(branch*) </span>
|
||||
<span style={{ color: getColor('accent3') }}>|> </span>
|
||||
<span style={{ backgroundColor: getColor('shade5', 'shade7') }} className={ cursor }> </span>
|
||||
</code>
|
||||
</pre>
|
||||
) }
|
||||
</ColorState>
|
||||
);
|
||||
export default () => {
|
||||
const { activePreparedColorSet } = useContext(ThemeContext);
|
||||
return (
|
||||
<pre className={ styles.pre }>
|
||||
<code>
|
||||
<span style={{ color: activePreparedColorSet['accent4'] }}>~/project</span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}>(branch*) </span>
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>|> </span>
|
||||
<span style={{ color: activePreparedColorSet['shade5'] }}>yarn test</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}>$ jest</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>prepare.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-wallpaper-triangles/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-vscode/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-utils/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-wallpaper-octagon/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-atom-syntax/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade0'], backgroundColor: activePreparedColorSet['accent3'] }}> PASS </span>
|
||||
<span style={{ color: activePreparedColorSet['shade2'] }}> packages/themer-chrome/lib/</span>
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>index.spec.js</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>...</span>
|
||||
{'\n\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>Test Suites: </span>
|
||||
<span style={{ color: activePreparedColorSet['accent4'] }}>42 passed</span>
|
||||
<span style={{ color: activePreparedColorSet['shade6'] }}>, 42 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>Tests: </span>
|
||||
<span style={{ color: activePreparedColorSet['accent4'] }}>145 passed</span>
|
||||
<span style={{ color: activePreparedColorSet['shade6'] }}>, 145 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>Snapshots: </span>
|
||||
<span style={{ color: activePreparedColorSet['accent4'] }}>102 passed</span>
|
||||
<span style={{ color: activePreparedColorSet['shade6'] }}>, 102 total</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade7'] }}>Time: </span>
|
||||
<span style={{ color: activePreparedColorSet['shade6'] }}>5.626s</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['shade3'] }}>Ran all test suites.</span>
|
||||
{'\n'}
|
||||
<span style={{ color: activePreparedColorSet['accent4'] }}>~/project</span>
|
||||
<span style={{ color: activePreparedColorSet['accent7'] }}>(branch*) </span>
|
||||
<span style={{ color: activePreparedColorSet['accent3'] }}>|> </span>
|
||||
<span style={{ backgroundColor: activePreparedColorSet['shade5'] }} className={ cursor }> </span>
|
||||
</code>
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
275
web/src/ThemeContext.js
Normal file
275
web/src/ThemeContext.js
Normal file
@ -0,0 +1,275 @@
|
||||
import React, { createContext, useState, useEffect } from "react";
|
||||
import qs from 'qs';
|
||||
import { get, merge } from 'lodash';
|
||||
import Color from 'color';
|
||||
import colorSteps from 'color-steps';
|
||||
|
||||
const stateFromParams = search =>
|
||||
qs.parse(search, { allowDots: true, ignoreQueryPrefix: true });
|
||||
export const paramsFromState = state =>
|
||||
qs.stringify(state, { allowDots: true, addQueryPrefix: true });
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
colors: {
|
||||
dark: {
|
||||
shade0: '#000000',
|
||||
shade7: '#FFFFFF',
|
||||
},
|
||||
light: {
|
||||
shade0: '#FFFFFF',
|
||||
shade7: '#000000',
|
||||
},
|
||||
},
|
||||
activeColorSet: 'dark',
|
||||
calculateIntermediaryShades: {
|
||||
dark: true,
|
||||
light: true,
|
||||
},
|
||||
};
|
||||
|
||||
const stringToBooleanOrDefault = (value, defaultValue) => {
|
||||
switch (value) {
|
||||
case 'true':
|
||||
return true;
|
||||
case 'false':
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const ThemeContext = createContext();
|
||||
|
||||
export const ThemeProvider = ({ history, children }) => {
|
||||
const [rawState, setRawState] = useState(stateFromParams(history.location.search));
|
||||
useEffect(() => {
|
||||
return history.listen(location => {
|
||||
setRawState(stateFromParams(location.search));
|
||||
});
|
||||
});
|
||||
|
||||
const mergeState = newState => history.replace(paramsFromState(merge({}, rawState, newState)));
|
||||
|
||||
const activeColorSet = ['dark', 'light'].includes(rawState.activeColorSet)
|
||||
? rawState.activeColorSet
|
||||
: DEFAULT_STATE.activeColorSet;
|
||||
|
||||
const setActiveColorSet = value => mergeState({
|
||||
activeColorSet: value,
|
||||
});
|
||||
|
||||
const calculateIntermediaryDarkShades = stringToBooleanOrDefault(
|
||||
get(rawState, 'calculateIntermediaryShades.dark'),
|
||||
DEFAULT_STATE.calculateIntermediaryShades.dark,
|
||||
)
|
||||
|
||||
const calculateIntermediaryLightShades = stringToBooleanOrDefault(
|
||||
get(rawState, 'calculateIntermediaryShades.light'),
|
||||
DEFAULT_STATE.calculateIntermediaryShades.light,
|
||||
);
|
||||
|
||||
const activeCalculateIntermediaryShades = activeColorSet === 'dark'
|
||||
? calculateIntermediaryDarkShades
|
||||
: calculateIntermediaryLightShades;
|
||||
|
||||
const setActiveCalculateIntermediaryShades = value => mergeState({
|
||||
calculateIntermediaryShades: {
|
||||
[activeColorSet]: value,
|
||||
},
|
||||
});
|
||||
|
||||
const parseSafe = v => {
|
||||
try {
|
||||
if (v === undefined) return;
|
||||
return Color(v).hex();
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const darkShade0 = parseSafe(get(rawState, 'colors.dark.shade0'));
|
||||
const darkShade7 = parseSafe(get(rawState, 'colors.dark.shade7'));
|
||||
const lightShade0 = parseSafe(get(rawState, 'colors.light.shade0'));
|
||||
const lightShade7 = parseSafe(get(rawState, 'colors.light.shade7'));
|
||||
|
||||
const preparedColors = {
|
||||
dark: {
|
||||
shade0: darkShade0,
|
||||
...(calculateIntermediaryDarkShades && darkShade0 && darkShade7
|
||||
? colorSteps(darkShade0, darkShade7).reduce(
|
||||
(shades, color, idx) => ({
|
||||
...shades,
|
||||
[`shade${idx+1}`]: Color(color).hex(),
|
||||
}),
|
||||
{},
|
||||
)
|
||||
: {
|
||||
shade1: parseSafe(get(rawState, 'colors.dark.shade1')),
|
||||
shade2: parseSafe(get(rawState, 'colors.dark.shade2')),
|
||||
shade3: parseSafe(get(rawState, 'colors.dark.shade3')),
|
||||
shade4: parseSafe(get(rawState, 'colors.dark.shade4')),
|
||||
shade5: parseSafe(get(rawState, 'colors.dark.shade5')),
|
||||
shade6: parseSafe(get(rawState, 'colors.dark.shade6')),
|
||||
}
|
||||
),
|
||||
shade7: darkShade7,
|
||||
accent0: parseSafe(get(rawState, 'colors.dark.accent0')),
|
||||
accent1: parseSafe(get(rawState, 'colors.dark.accent1')),
|
||||
accent2: parseSafe(get(rawState, 'colors.dark.accent2')),
|
||||
accent3: parseSafe(get(rawState, 'colors.dark.accent3')),
|
||||
accent4: parseSafe(get(rawState, 'colors.dark.accent4')),
|
||||
accent5: parseSafe(get(rawState, 'colors.dark.accent5')),
|
||||
accent6: parseSafe(get(rawState, 'colors.dark.accent6')),
|
||||
accent7: parseSafe(get(rawState, 'colors.dark.accent7')),
|
||||
},
|
||||
light: {
|
||||
shade0: lightShade0,
|
||||
...(calculateIntermediaryLightShades && lightShade0 && lightShade7
|
||||
? colorSteps(lightShade0, lightShade7).reduce(
|
||||
(shades, color, idx) => ({
|
||||
...shades,
|
||||
[`shade${idx+1}`]: color,
|
||||
}),
|
||||
{},
|
||||
)
|
||||
: {
|
||||
shade1: parseSafe(get(rawState, 'colors.light.shade1')),
|
||||
shade2: parseSafe(get(rawState, 'colors.light.shade2')),
|
||||
shade3: parseSafe(get(rawState, 'colors.light.shade3')),
|
||||
shade4: parseSafe(get(rawState, 'colors.light.shade4')),
|
||||
shade5: parseSafe(get(rawState, 'colors.light.shade5')),
|
||||
shade6: parseSafe(get(rawState, 'colors.light.shade6')),
|
||||
}
|
||||
),
|
||||
shade7: lightShade7,
|
||||
accent0: parseSafe(get(rawState, 'colors.light.accent0')),
|
||||
accent1: parseSafe(get(rawState, 'colors.light.accent1')),
|
||||
accent2: parseSafe(get(rawState, 'colors.light.accent2')),
|
||||
accent3: parseSafe(get(rawState, 'colors.light.accent3')),
|
||||
accent4: parseSafe(get(rawState, 'colors.light.accent4')),
|
||||
accent5: parseSafe(get(rawState, 'colors.light.accent5')),
|
||||
accent6: parseSafe(get(rawState, 'colors.light.accent6')),
|
||||
accent7: parseSafe(get(rawState, 'colors.light.accent7')),
|
||||
},
|
||||
}
|
||||
|
||||
const getPreparedColor = (variant, keys, fallbackKey) => {
|
||||
for (const key of keys) {
|
||||
const preparedColor = get(preparedColors, [variant, key]);
|
||||
if (preparedColor !== undefined) {
|
||||
return preparedColor;
|
||||
}
|
||||
}
|
||||
const preparedFallback = get(preparedColors, [variant, fallbackKey]);
|
||||
if (preparedFallback !== undefined) {
|
||||
return preparedFallback;
|
||||
} else {
|
||||
return get(DEFAULT_STATE, ['colors', variant, fallbackKey]);
|
||||
}
|
||||
}
|
||||
|
||||
const getColorOrFallback = (variant, keys, background = false) =>
|
||||
getPreparedColor(variant, keys, background ? 'shade0' : 'shade7');
|
||||
const getActiveColorOrFallback = (keys, background) =>
|
||||
getColorOrFallback(activeColorSet, keys, background);
|
||||
|
||||
const preparedFullColorSet = {
|
||||
dark: {
|
||||
shade0: getColorOrFallback('dark', ['shade0'], true),
|
||||
shade1: getColorOrFallback('dark', ['shade1']),
|
||||
shade2: getColorOrFallback('dark', ['shade2']),
|
||||
shade3: getColorOrFallback('dark', ['shade3']),
|
||||
shade4: getColorOrFallback('dark', ['shade4']),
|
||||
shade5: getColorOrFallback('dark', ['shade5']),
|
||||
shade6: getColorOrFallback('dark', ['shade6']),
|
||||
shade7: getColorOrFallback('dark', ['shade7']),
|
||||
accent0: getColorOrFallback('dark', ['accent0']),
|
||||
accent1: getColorOrFallback('dark', ['accent1']),
|
||||
accent2: getColorOrFallback('dark', ['accent2']),
|
||||
accent3: getColorOrFallback('dark', ['accent3']),
|
||||
accent4: getColorOrFallback('dark', ['accent4']),
|
||||
accent5: getColorOrFallback('dark', ['accent5']),
|
||||
accent6: getColorOrFallback('dark', ['accent6']),
|
||||
accent7: getColorOrFallback('dark', ['accent7']),
|
||||
},
|
||||
light: {
|
||||
shade0: getColorOrFallback('light', ['shade0'], true),
|
||||
shade1: getColorOrFallback('light', ['shade1']),
|
||||
shade2: getColorOrFallback('light', ['shade2']),
|
||||
shade3: getColorOrFallback('light', ['shade3']),
|
||||
shade4: getColorOrFallback('light', ['shade4']),
|
||||
shade5: getColorOrFallback('light', ['shade5']),
|
||||
shade6: getColorOrFallback('light', ['shade6']),
|
||||
shade7: getColorOrFallback('light', ['shade7']),
|
||||
accent0: getColorOrFallback('light', ['accent0']),
|
||||
accent1: getColorOrFallback('light', ['accent1']),
|
||||
accent2: getColorOrFallback('light', ['accent2']),
|
||||
accent3: getColorOrFallback('light', ['accent3']),
|
||||
accent4: getColorOrFallback('light', ['accent4']),
|
||||
accent5: getColorOrFallback('light', ['accent5']),
|
||||
accent6: getColorOrFallback('light', ['accent6']),
|
||||
accent7: getColorOrFallback('light', ['accent7']),
|
||||
}
|
||||
};
|
||||
|
||||
const activePreparedColorSet = preparedFullColorSet[activeColorSet];
|
||||
|
||||
const colorKeys = [
|
||||
'shade0',
|
||||
'shade1',
|
||||
'shade2',
|
||||
'shade3',
|
||||
'shade4',
|
||||
'shade5',
|
||||
'shade6',
|
||||
'shade7',
|
||||
'accent0',
|
||||
'accent1',
|
||||
'accent2',
|
||||
'accent3',
|
||||
'accent4',
|
||||
'accent5',
|
||||
'accent6',
|
||||
'accent7',
|
||||
];
|
||||
const preparedColorSet = {
|
||||
...(
|
||||
colorKeys.some(key => !!get(rawState, ['colors', 'dark', key]))
|
||||
? { dark: preparedFullColorSet.dark }
|
||||
: null
|
||||
),
|
||||
...(
|
||||
colorKeys.some(key => !!get(rawState, ['colors', 'light', key]))
|
||||
? { light: preparedFullColorSet.light }
|
||||
: null
|
||||
),
|
||||
};
|
||||
|
||||
const getActiveRawColor = key => get(rawState, ['colors', activeColorSet, key], '');
|
||||
|
||||
const setActiveRawColor = (key, value) => mergeState({
|
||||
colors: {
|
||||
[activeColorSet]: {
|
||||
[key]: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{
|
||||
activeColorSet,
|
||||
setActiveColorSet,
|
||||
|
||||
activeCalculateIntermediaryShades,
|
||||
setActiveCalculateIntermediaryShades,
|
||||
|
||||
getActiveRawColor,
|
||||
getActiveColorOrFallback,
|
||||
preparedColorSet,
|
||||
activePreparedColorSet,
|
||||
setActiveRawColor,
|
||||
}}>
|
||||
{ children }
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default ThemeContext;
|
@ -1,83 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import qs from 'qs';
|
||||
import { merge } from 'lodash';
|
||||
import getValueOrFallback from './getValueOrFallback';
|
||||
|
||||
const UrlStateContext = React.createContext();
|
||||
|
||||
const stateFromParams = search => qs.parse(search, { allowDots: true, ignoreQueryPrefix: true });
|
||||
export const paramsFromState = state => qs.stringify(state, { allowDots: true, addQueryPrefix: true });
|
||||
|
||||
const fallbackState = {
|
||||
colors: {
|
||||
dark: {
|
||||
shade0: '#000000',
|
||||
shade1: '#000000',
|
||||
shade2: '#000000',
|
||||
shade3: '#000000',
|
||||
shade4: '#000000',
|
||||
shade5: '#000000',
|
||||
shade6: '#000000',
|
||||
shade7: '#FFFFFF',
|
||||
accent0: '#FFFFFF',
|
||||
accent1: '#FFFFFF',
|
||||
accent2: '#FFFFFF',
|
||||
accent3: '#FFFFFF',
|
||||
accent4: '#FFFFFF',
|
||||
accent5: '#FFFFFF',
|
||||
accent6: '#FFFFFF',
|
||||
accent7: '#FFFFFF',
|
||||
},
|
||||
light: {
|
||||
shade0: '#FFFFFF',
|
||||
shade1: '#FFFFFF',
|
||||
shade2: '#FFFFFF',
|
||||
shade3: '#FFFFFF',
|
||||
shade4: '#FFFFFF',
|
||||
shade5: '#FFFFFF',
|
||||
shade6: '#FFFFFF',
|
||||
shade7: '#000000',
|
||||
accent0: '#000000',
|
||||
accent1: '#000000',
|
||||
accent2: '#000000',
|
||||
accent3: '#000000',
|
||||
accent4: '#000000',
|
||||
accent5: '#000000',
|
||||
accent6: '#000000',
|
||||
accent7: '#000000',
|
||||
},
|
||||
},
|
||||
activeColorSet: 'dark',
|
||||
calculateIntermediaryShades: {
|
||||
dark: true,
|
||||
light: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const UrlStateProvider = ({ history, children }) => {
|
||||
const [state, setState] = useState(stateFromParams(history.location.search));
|
||||
|
||||
useEffect(() => {
|
||||
return history.listen(location => {
|
||||
setState(stateFromParams(location.search));
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<UrlStateContext.Provider value={{
|
||||
rawState: state,
|
||||
getValueOrFallback: (paths, parse, calculatedState) => getValueOrFallback(
|
||||
state,
|
||||
calculatedState,
|
||||
fallbackState,
|
||||
paths,
|
||||
parse,
|
||||
),
|
||||
mergeState: newState => history.replace(paramsFromState(merge({}, state, newState))),
|
||||
}}>
|
||||
{ children }
|
||||
</UrlStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const UrlStateConsumer = UrlStateContext.Consumer;
|
@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import ColorState from './ColorState';
|
||||
import React, { useState, useEffect, useRef, useContext } from 'react';
|
||||
import Button from './Button';
|
||||
import styles from './WallpaperModal.module.css';
|
||||
|
||||
@ -8,6 +7,7 @@ import { render as octagonRender } from 'themer-wallpaper-octagon';
|
||||
import { render as shirtsRender } from 'themer-wallpaper-shirts';
|
||||
import { render as trianglesRender } from 'themer-wallpaper-triangles';
|
||||
import { render as trianglifyRender } from 'themer-wallpaper-trianglify';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
const getImagePromises = (pkg, colors, width, height) => {
|
||||
const options = { [`${pkg}-size`]: `${width}x${height}` };
|
||||
@ -42,6 +42,8 @@ export default ({ onClose, wallpaper, colors }) => {
|
||||
};
|
||||
});
|
||||
|
||||
const { getActiveColorOrFallback } = useContext(ThemeContext);
|
||||
|
||||
const node = useRef(null);
|
||||
const button = useRef(null);
|
||||
|
||||
@ -72,28 +74,24 @@ export default ({ onClose, wallpaper, colors }) => {
|
||||
}, [button]);
|
||||
|
||||
return (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<div
|
||||
className={ styles.scrim }
|
||||
style={{ backgroundColor: getActiveColorOrFallback(['shade0'], true) }}
|
||||
ref={ node }
|
||||
>
|
||||
{ image ? (
|
||||
<div
|
||||
className={ styles.scrim }
|
||||
style={{ backgroundColor: getColor('shade0') }}
|
||||
ref={ node }
|
||||
>
|
||||
{ image ? (
|
||||
<div
|
||||
className={ styles.image }
|
||||
style={{ backgroundImage: image }}
|
||||
/>
|
||||
) : (
|
||||
<span style={{ color: getColor('shade2', 'shade7') }}>loading...</span>
|
||||
) }
|
||||
<Button
|
||||
className={ styles.close }
|
||||
onClick={ onClose }
|
||||
ref={ button }
|
||||
>close</Button>
|
||||
</div>
|
||||
className={ styles.image }
|
||||
style={{ backgroundImage: image }}
|
||||
/>
|
||||
) : (
|
||||
<span style={{ color: getActiveColorOrFallback(['shade2']) }}>loading...</span>
|
||||
) }
|
||||
</ColorState>
|
||||
<Button
|
||||
className={ styles.close }
|
||||
onClick={ onClose }
|
||||
ref={ button }
|
||||
>close</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useRef, useContext } from 'react';
|
||||
import Tabs from './Tabs';
|
||||
import WallpaperModal from './WallpaperModal';
|
||||
import Button from './Button';
|
||||
import styles from './WallpaperPreview.module.css';
|
||||
import ColorState from './ColorState';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
const wallpaperOptions = [
|
||||
{ value: 'themer-wallpaper-block-wave', label: '"Block Wave"'},
|
||||
@ -26,6 +26,8 @@ export default () => {
|
||||
setActivePreview(null);
|
||||
}
|
||||
|
||||
const { activePreparedColorSet } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
{ ({ tabClassName, getTabStyle, contentClassName, contentStyle }) => (
|
||||
@ -45,34 +47,13 @@ export default () => {
|
||||
)) }
|
||||
</div>
|
||||
{ activePreview ? (
|
||||
<ColorState>
|
||||
{ ({ getColor }) => (
|
||||
<WallpaperModal
|
||||
wallpaper={ activePreview }
|
||||
colors={{
|
||||
current: {
|
||||
shade0: getColor('shade0'),
|
||||
shade1: getColor('shade1'),
|
||||
shade2: getColor('shade2'),
|
||||
shade3: getColor('shade3'),
|
||||
shade4: getColor('shade4'),
|
||||
shade5: getColor('shade5'),
|
||||
shade6: getColor('shade6'),
|
||||
shade7: getColor('shade7'),
|
||||
accent0: getColor('accent0'),
|
||||
accent1: getColor('accent1'),
|
||||
accent2: getColor('accent2'),
|
||||
accent3: getColor('accent3'),
|
||||
accent4: getColor('accent4'),
|
||||
accent5: getColor('accent5'),
|
||||
accent6: getColor('accent6'),
|
||||
accent7: getColor('accent7'),
|
||||
}
|
||||
}}
|
||||
onClose={ onModalClose }
|
||||
/>
|
||||
) }
|
||||
</ColorState>
|
||||
<WallpaperModal
|
||||
wallpaper={ activePreview }
|
||||
colors={{
|
||||
current: activePreparedColorSet
|
||||
}}
|
||||
onClose={ onModalClose }
|
||||
/>
|
||||
) : null }
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user