构建自动色板功能的基本页面结构。
This commit is contained in:
		| @@ -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> | ||||||
|  |   ); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user