feat(q-2-scheme): 添加颜色方案预览组件并优化类型定义
新增 Q2SchemePreview 组件用于展示颜色方案的预览效果 将 Map 类型改为 Record 以简化数据结构
This commit is contained in:
parent
a7ef8eb576
commit
a77fb3f18b
@ -6,6 +6,7 @@ import { Q2SchemeStorage } from '../../q-2-scheme';
|
|||||||
import { isNilOrEmpty } from '../../utls';
|
import { isNilOrEmpty } from '../../utls';
|
||||||
import { SchemeExport } from './Export';
|
import { SchemeExport } from './Export';
|
||||||
import { Q2SchemeBuilder } from './q-2-scheme/Builder';
|
import { Q2SchemeBuilder } from './q-2-scheme/Builder';
|
||||||
|
import Q2SchemePreview from './q-2-scheme/Preview';
|
||||||
|
|
||||||
const tabOptions = [
|
const tabOptions = [
|
||||||
{ title: 'Overview', id: 'overview' },
|
{ title: 'Overview', id: 'overview' },
|
||||||
@ -33,6 +34,7 @@ export function Q2Scheme({ scheme }: Q2SchemeProps) {
|
|||||||
export: isNilOrEmpty(scheme.schemeStorage?.cssVariables),
|
export: isNilOrEmpty(scheme.schemeStorage?.cssVariables),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{isEqual(activeTab, 'overview') && <Q2SchemePreview scheme={scheme} />}
|
||||||
{isEqual(activeTab, 'builder') && (
|
{isEqual(activeTab, 'builder') && (
|
||||||
<Q2SchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />
|
<Q2SchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />
|
||||||
)}
|
)}
|
||||||
|
58
src/page-components/scheme/q-2-scheme/Preview.module.css
Normal file
58
src/page-components/scheme/q-2-scheme/Preview.module.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@layer pages {
|
||||||
|
.preview_layout {
|
||||||
|
padding: var(--spacing-s) var(--spacing-m);
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.preview_block {
|
||||||
|
width: inherit;
|
||||||
|
padding: var(--spacing-xl) var(--spacing-m);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
h2 {
|
||||||
|
font-size: var(--font-size-xl);
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.preview_unit {
|
||||||
|
width: inherit;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
.preview_indi_block {
|
||||||
|
width: inherit;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
.preview_swatch {
|
||||||
|
width: inherit;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(16, 1fr);
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
.preview_swatch_cell {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.preview_cell {
|
||||||
|
padding: var(--spacing-xs) var(--spacing-s);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--spacing-xxs);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
line-height: 1.5em;
|
||||||
|
.wacg {
|
||||||
|
font-size: var(--font-size-xxs);
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
169
src/page-components/scheme/q-2-scheme/Preview.tsx
Normal file
169
src/page-components/scheme/q-2-scheme/Preview.tsx
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import { capitalize, keys } from 'lodash-es';
|
||||||
|
import { FC, ReactNode, useMemo } from 'react';
|
||||||
|
import { useColorFunction } from '../../../ColorFunctionContext';
|
||||||
|
import { ScrollArea } from '../../../components/ScrollArea';
|
||||||
|
import { SchemeContent } from '../../../models';
|
||||||
|
import { Q2Baseline, Q2ColorSet, Q2ColorUnit, Q2SchemeStorage } from '../../../q-2-scheme';
|
||||||
|
import styles from './Preview.module.css';
|
||||||
|
|
||||||
|
interface PreviewCellProps {
|
||||||
|
bg: string;
|
||||||
|
fg: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewCell: FC<PreviewCellProps> = ({ bg, fg, children }) => {
|
||||||
|
const { colorFn } = useColorFunction();
|
||||||
|
const wacgRatio = useMemo(() => {
|
||||||
|
try {
|
||||||
|
if (!colorFn) return null;
|
||||||
|
return colorFn.wacg_relative_contrast(fg, bg);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Error on calc WACG Ratio]', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [bg, fg]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.preview_cell} style={{ backgroundColor: `#${bg}`, color: `#${fg}` }}>
|
||||||
|
<span>{children}</span>
|
||||||
|
{wacgRatio && <span className={styles.wacg}>WACG {wacgRatio?.toFixed(2)}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewLineProps {
|
||||||
|
name: string;
|
||||||
|
unit: Q2ColorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewLine: FC<PreviewLineProps> = ({ name, unit }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.preview_unit}>
|
||||||
|
<PreviewCell bg={unit.root} fg={unit.onRoot}>
|
||||||
|
{name}
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={unit.hover} fg={unit.onRoot}>
|
||||||
|
{name} Hover
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={unit.active} fg={unit.onRoot}>
|
||||||
|
{name} Active
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={unit.focus} fg={unit.onRoot}>
|
||||||
|
{name} Focus
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={unit.disabled} fg={unit.onDisabled}>
|
||||||
|
{name} Disabled
|
||||||
|
</PreviewCell>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewSwatchLineProps {
|
||||||
|
swatch: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewSwatchLine: FC<PreviewSwatchLineProps> = ({ swatch }) => {
|
||||||
|
const cells = useMemo(() => {
|
||||||
|
const collection: ReactNode[] = [];
|
||||||
|
for (const key of keys(swatch)) {
|
||||||
|
const color = swatch[key];
|
||||||
|
collection.push(
|
||||||
|
<div className={styles.preview_swatch_cell} style={{ backgroundColor: `#${color}` }} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}, [swatch]);
|
||||||
|
|
||||||
|
return <div className={styles.preview_swatch}>{cells}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewSetProps {
|
||||||
|
name: string;
|
||||||
|
colorUnit: Q2ColorUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewSet: FC<PreviewSetProps> = ({ name, colorUnit }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PreviewLine name={name} unit={colorUnit.root} />
|
||||||
|
<PreviewLine name={`${name} Surface`} unit={colorUnit.surface} />
|
||||||
|
<PreviewSwatchLine name={name} swatch={colorUnit.swatch} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewBlockProps {
|
||||||
|
baseline: Q2Baseline;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewBlock: FC<PreviewBlockProps> = ({ baseline, title }) => {
|
||||||
|
const customSets = useMemo(() => {
|
||||||
|
const colors = keys(baseline.custom);
|
||||||
|
const elements: ReactNode[] = [];
|
||||||
|
|
||||||
|
for (const key of colors) {
|
||||||
|
const color = baseline.custom[key];
|
||||||
|
elements.push(<PreviewSet name={capitalize(key)} colorUnit={color} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}, [baseline.custom]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.preview_block} style={{ backgroundColor: `#${baseline.surface.root}` }}>
|
||||||
|
<h2 style={{ color: `#${baseline.surface.onRoot}` }}>{title}</h2>
|
||||||
|
<PreviewSet name="Primary" colorUnit={baseline.primary} />
|
||||||
|
<PreviewSet name="Secondary" colorUnit={baseline.secondary} />
|
||||||
|
<PreviewSet name="Tertiary" colorUnit={baseline.tertiary} />
|
||||||
|
<PreviewSet name="Accent" colorUnit={baseline.accent} />
|
||||||
|
<PreviewSet name="Danger" colorUnit={baseline.danger} />
|
||||||
|
<PreviewSet name="Success" colorUnit={baseline.success} />
|
||||||
|
<PreviewSet name="Warn" colorUnit={baseline.warn} />
|
||||||
|
<PreviewSet name="Info" colorUnit={baseline.info} />
|
||||||
|
<PreviewLine name="Neutral" unit={baseline.neutral} />
|
||||||
|
<PreviewLine name="Neutral Variant" unit={baseline.neutralVariant} />
|
||||||
|
<PreviewLine name="Surface" unit={baseline.surface} />
|
||||||
|
<PreviewLine name="Surface Variant" unit={baseline.surfaceVariant} />
|
||||||
|
<div className={styles.preview_indi_block}>
|
||||||
|
<PreviewCell bg={baseline.shadow} fg={baseline.surface.root.onRoot}>
|
||||||
|
Shadow
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={baseline.overlay} fg={baseline.surface.root.onRoot}>
|
||||||
|
Overlay
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={baseline.outline} fg={baseline.surface.root.onRoot}>
|
||||||
|
Outline
|
||||||
|
</PreviewCell>
|
||||||
|
<PreviewCell bg={baseline.outlineVariant} fg={baseline.surface.root.onRoot}>
|
||||||
|
Outline Variant
|
||||||
|
</PreviewCell>
|
||||||
|
</div>
|
||||||
|
{customSets}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewProps {
|
||||||
|
scheme: SchemeContent<Q2SchemeStorage>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Q2SchemePreview: FC<PreviewProps> = ({ scheme }) => {
|
||||||
|
return (
|
||||||
|
<ScrollArea enableY>
|
||||||
|
<div className={styles.preview_layout}>
|
||||||
|
<div className={styles.preview_layout}>
|
||||||
|
{scheme.schemeStorage.scheme?.light && (
|
||||||
|
<PreviewBlock baseline={scheme.schemeStorage.scheme.light} title="Light Scheme" />
|
||||||
|
)}
|
||||||
|
{scheme.schemeStorage.scheme?.dark && (
|
||||||
|
<PreviewBlock baseline={scheme.schemeStorage.scheme.dark} title="Dark Scheme" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Q2SchemePreview;
|
@ -13,7 +13,7 @@ export type Q2ColorSet = {
|
|||||||
export type Q2ColorUnit = {
|
export type Q2ColorUnit = {
|
||||||
root: Q2ColorSet;
|
root: Q2ColorSet;
|
||||||
surface: Q2ColorSet;
|
surface: Q2ColorSet;
|
||||||
swatch: Map<string, string>;
|
swatch: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Q2Baseline = {
|
export type Q2Baseline = {
|
||||||
@ -33,7 +33,7 @@ export type Q2Baseline = {
|
|||||||
overlay: string;
|
overlay: string;
|
||||||
outline: string;
|
outline: string;
|
||||||
outlineVariant: string;
|
outlineVariant: string;
|
||||||
custom: Map<string, Q2ColorUnit>;
|
custom: Record<string, Q2ColorUnit>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Q2Scheme = {
|
export type Q2Scheme = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user