构建自动色板功能的基本页面结构。
This commit is contained in:
parent
7a8bbaa826
commit
b126858d8e
@ -10,6 +10,7 @@ import { LightenDarken } from './pages/LightenDarken';
|
||||
import { MainLayout } from './pages/MainLayout';
|
||||
import { Mixer } from './pages/Mixer';
|
||||
import { NewScheme } from './pages/NewScheme';
|
||||
import { AutomaticPalette } from './pages/Palette';
|
||||
import { SchemeDetail } from './pages/SchemeDetail';
|
||||
import { SchemeNotFound } from './pages/SchemeNotFound';
|
||||
import { Schemes } from './pages/Schemes';
|
||||
@ -39,6 +40,7 @@ const routes = createBrowserRouter([
|
||||
{ path: 'tints-shades', element: <TintsShades /> },
|
||||
{ path: 'lighten-darken', element: <LightenDarken /> },
|
||||
{ path: 'mixer', element: <Mixer /> },
|
||||
{ path: 'palette', element: <AutomaticPalette /> },
|
||||
{ path: 'wacg', element: <WACGCheck /> },
|
||||
{ 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