构建自动色板功能的基本页面结构。
This commit is contained in:
parent
7a8bbaa826
commit
b126858d8e
@ -10,6 +10,7 @@ import { LightenDarken } from './pages/LightenDarken';
|
|||||||
import { MainLayout } from './pages/MainLayout';
|
import { MainLayout } from './pages/MainLayout';
|
||||||
import { Mixer } from './pages/Mixer';
|
import { Mixer } from './pages/Mixer';
|
||||||
import { NewScheme } from './pages/NewScheme';
|
import { NewScheme } from './pages/NewScheme';
|
||||||
|
import { AutomaticPalette } from './pages/Palette';
|
||||||
import { SchemeDetail } from './pages/SchemeDetail';
|
import { SchemeDetail } from './pages/SchemeDetail';
|
||||||
import { SchemeNotFound } from './pages/SchemeNotFound';
|
import { SchemeNotFound } from './pages/SchemeNotFound';
|
||||||
import { Schemes } from './pages/Schemes';
|
import { Schemes } from './pages/Schemes';
|
||||||
@ -39,6 +40,7 @@ const routes = createBrowserRouter([
|
|||||||
{ path: 'tints-shades', element: <TintsShades /> },
|
{ path: 'tints-shades', element: <TintsShades /> },
|
||||||
{ path: 'lighten-darken', element: <LightenDarken /> },
|
{ path: 'lighten-darken', element: <LightenDarken /> },
|
||||||
{ path: 'mixer', element: <Mixer /> },
|
{ path: 'mixer', element: <Mixer /> },
|
||||||
|
{ path: 'palette', element: <AutomaticPalette /> },
|
||||||
{ path: 'wacg', element: <WACGCheck /> },
|
{ path: 'wacg', element: <WACGCheck /> },
|
||||||
{ path: 'compare', element: <ColorCompare /> },
|
{ path: 'compare', element: <ColorCompare /> },
|
||||||
{
|
{
|
||||||
|
47
src/pages/Palette.module.css
Normal file
47
src/pages/Palette.module.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
@layer pages {
|
||||||
|
.palette_workspace {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.palette_section {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.function_side {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
.mode_navigation {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
padding-block: var(--spacing-m);
|
||||||
|
font-size: var(--font-size-m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.palette_content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 var(--spacing-m);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
h5 {
|
||||||
|
padding-block: var(--spacing-m);
|
||||||
|
font-size: var(--font-size-m);
|
||||||
|
}
|
||||||
|
.color_value_mode {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/pages/Palette.tsx
Normal file
111
src/pages/Palette.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import cx from 'clsx';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { ColorPicker } from '../components/ColorPicker';
|
||||||
|
import { HSegmentedControl } from '../components/HSegmentedControl';
|
||||||
|
import { Labeled } from '../components/Labeled';
|
||||||
|
import { LabeledPicker } from '../components/LabeledPicker';
|
||||||
|
import { ScrollArea } from '../components/ScrollArea';
|
||||||
|
import { Switch } from '../components/Switch';
|
||||||
|
import { currentPickedColor } from '../stores/colors';
|
||||||
|
import styles from './Palette.module.css';
|
||||||
|
|
||||||
|
export function AutomaticPalette() {
|
||||||
|
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
|
||||||
|
const [useReferenceColor, setUseReferenceColor] = useState(false);
|
||||||
|
const [swatchAmount, setSwatchAmount] = useState(5);
|
||||||
|
const maxBias = useMemo(
|
||||||
|
() => (swatchAmount > 3 ? Math.floor(swatchAmount / 2) - 1 : 0),
|
||||||
|
[swatchAmount],
|
||||||
|
);
|
||||||
|
const [referenceBias, setReferenceBias] = useState(0);
|
||||||
|
const [minLightness, setMinLightness] = useState(10);
|
||||||
|
const [maxLightness, setMaxLightness] = useState(90);
|
||||||
|
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (useReferenceColor) {
|
||||||
|
setReferenceBias(0);
|
||||||
|
}
|
||||||
|
}, [swatchAmount, useReferenceColor]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('workspace', styles.palette_workspace)}>
|
||||||
|
<header>
|
||||||
|
<h3>Automatic Palette</h3>
|
||||||
|
<p>
|
||||||
|
Automatically generate a color palette suitable for UI color matching according to the
|
||||||
|
given color algorithm.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<ScrollArea enableY>
|
||||||
|
<section className={styles.palette_section}>
|
||||||
|
<aside className={styles.function_side}>
|
||||||
|
<div>
|
||||||
|
<h5>Reference Color</h5>
|
||||||
|
<ColorPicker color={selectedColor} onSelect={(color) => setSelectedColor(color)} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h5>Palette Setting</h5>
|
||||||
|
<Labeled label="Use Reference Color" inline>
|
||||||
|
<Switch checked={useReferenceColor} onChange={setUseReferenceColor} />
|
||||||
|
</Labeled>
|
||||||
|
<LabeledPicker
|
||||||
|
title="Swatch Amount"
|
||||||
|
min={3}
|
||||||
|
max={15}
|
||||||
|
step={1}
|
||||||
|
value={swatchAmount}
|
||||||
|
onChange={(value) => setSwatchAmount(value)}
|
||||||
|
/>
|
||||||
|
<LabeledPicker
|
||||||
|
title="Reference Bias"
|
||||||
|
min={0}
|
||||||
|
max={maxBias}
|
||||||
|
step={1}
|
||||||
|
value={referenceBias}
|
||||||
|
onChange={setReferenceBias}
|
||||||
|
disabled={!useReferenceColor || maxBias < 2}
|
||||||
|
/>
|
||||||
|
<LabeledPicker
|
||||||
|
title="Min Lightness"
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
value={minLightness}
|
||||||
|
onChange={setMinLightness}
|
||||||
|
unit="%"
|
||||||
|
/>
|
||||||
|
<LabeledPicker
|
||||||
|
title="Max Lightness"
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
value={maxLightness}
|
||||||
|
onChange={setMaxLightness}
|
||||||
|
unit="%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div className={styles.palette_content}>
|
||||||
|
<h5>Generated Palette</h5>
|
||||||
|
<div className={styles.color_value_mode}>
|
||||||
|
<label>Copy color value in</label>
|
||||||
|
<HSegmentedControl
|
||||||
|
options={[
|
||||||
|
{ label: 'HEX', value: 'hex' },
|
||||||
|
{ label: 'RGB', value: 'rgb' },
|
||||||
|
{ label: 'HSL', value: 'hsl' },
|
||||||
|
{ label: 'LAB', value: 'lab' },
|
||||||
|
{ label: 'OKLCH', value: 'oklch' },
|
||||||
|
]}
|
||||||
|
valu={mode}
|
||||||
|
onChange={setMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user