基本形成色卡页面功能。
This commit is contained in:
42
src/page-components/cards-detail/ColorCard.module.css
Normal file
42
src/page-components/cards-detail/ColorCard.module.css
Normal file
@@ -0,0 +1,42 @@
|
||||
@layer pages {
|
||||
.card {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-n);
|
||||
font-size: var(--font-size-xxs);
|
||||
line-height: var(--font-size-xxs);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-xs);
|
||||
cursor: pointer;
|
||||
}
|
||||
.color_block {
|
||||
width: 100%;
|
||||
height: 5em;
|
||||
}
|
||||
.description_line {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: var(--spacing-xs) var(--spacing-s);
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-xxs);
|
||||
flex: 1;
|
||||
.name {
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: var(--font-size-xs);
|
||||
font-weight: bold;
|
||||
}
|
||||
.en_name {
|
||||
font-style: italic;
|
||||
color: var(--color-neutral-focus);
|
||||
}
|
||||
}
|
||||
.color_value {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
74
src/page-components/cards-detail/ColorCard.tsx
Normal file
74
src/page-components/cards-detail/ColorCard.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { capitalize } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useColorFunction } from '../../ColorFunctionContext';
|
||||
import { useCopyColor } from '../../hooks/useCopyColor';
|
||||
import { ColorDescription } from '../../models';
|
||||
import styles from './ColorCard.module.css';
|
||||
|
||||
type ColorCardProps = {
|
||||
color: ColorDescription;
|
||||
copyMode?: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
|
||||
};
|
||||
|
||||
export function ColorCard({ color, copyMode }: ColorCardProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const copytToClipboard = useCopyColor();
|
||||
const colorHex = useMemo(() => {
|
||||
const [r, g, b] = color.rgb;
|
||||
if (colorFn) {
|
||||
try {
|
||||
const hex = colorFn.rgb_to_hex(r, g, b);
|
||||
return hex;
|
||||
} catch (e) {
|
||||
console.error('[Convert RGB]', e);
|
||||
}
|
||||
}
|
||||
return `${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
||||
}, [colorFn, color]);
|
||||
const handleCopy = useCallback(() => {
|
||||
switch (copyMode) {
|
||||
case 'rgb':
|
||||
copytToClipboard(`rgb(${color.rgb[0]}, ${color.rgb[1]}, ${color.rgb[2]})`);
|
||||
break;
|
||||
case 'hsl':
|
||||
copytToClipboard(
|
||||
`hsl(${color.hsl[0].toFixed(1)}, ${(color.hsl[1] * 100).toFixed(2)}%, ${(
|
||||
color.hsl[2] * 100
|
||||
).toFixed(2)}%)`,
|
||||
);
|
||||
break;
|
||||
case 'lab':
|
||||
copytToClipboard(
|
||||
`lab(${color.lab[0].toFixed(1)}, ${color.lab[1].toFixed(2)}, ${color.lab[2].toFixed(2)})`,
|
||||
);
|
||||
break;
|
||||
case 'oklch':
|
||||
copytToClipboard(
|
||||
`oklch(${(color.oklch[0] * 100).toFixed(2)}%, ${color.oklch[1].toFixed(
|
||||
4,
|
||||
)}, ${color.oklch[2].toFixed(1)})`,
|
||||
);
|
||||
break;
|
||||
case 'hex':
|
||||
default:
|
||||
copytToClipboard(`#${colorHex}`);
|
||||
break;
|
||||
}
|
||||
}, [copytToClipboard, color, copyMode, colorHex]);
|
||||
|
||||
return (
|
||||
<div className={styles.card} onClick={handleCopy}>
|
||||
<div
|
||||
className={styles.color_block}
|
||||
style={{ backgroundColor: `rgb(${color.rgb[0]}, ${color.rgb[1]}, ${color.rgb[2]})` }}
|
||||
/>
|
||||
<div className={styles.description_line}>
|
||||
<div className={styles.title}>
|
||||
<span className={styles.name}>{color.name}</span>
|
||||
<span className={styles.en_name}>{color.pinyin.map(capitalize).join(' ')}</span>
|
||||
</div>
|
||||
<div className={styles.color_value}>#{colorHex}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
26
src/page-components/color-cards/CardNavigation.tsx
Normal file
26
src/page-components/color-cards/CardNavigation.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import cx from 'clsx';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import styles from './CardsNavigation.module.css';
|
||||
|
||||
export function CardsNavigation() {
|
||||
return (
|
||||
<div className={styles.cards_list}>
|
||||
<menu className={styles.nav_menu}>
|
||||
<li>
|
||||
<NavLink
|
||||
to="chinese"
|
||||
className={({ isActive }) => cx(styles.nav_link, isActive && styles.active)}>
|
||||
Chinese Traditional
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="japanese"
|
||||
className={({ isActive }) => cx(styles.nav_link, isActive && styles.active)}>
|
||||
Japanese Traditional
|
||||
</NavLink>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
);
|
||||
}
|
33
src/page-components/color-cards/CardsNavigation.module.css
Normal file
33
src/page-components/color-cards/CardsNavigation.module.css
Normal file
@@ -0,0 +1,33 @@
|
||||
@layer pages {
|
||||
.cards_list {
|
||||
max-width: calc(var(--spacing) * 125);
|
||||
flex: 1 1 calc(var(--spacing) * 125);
|
||||
padding: calc(var(--spacing) * 4) 0;
|
||||
box-shadow: 2px 0 8px oklch(from var(--color-black) l c h / 65%);
|
||||
z-index: 40;
|
||||
}
|
||||
.nav_menu {
|
||||
flex: 1 0;
|
||||
padding: var(--spacing-n);
|
||||
padding-block-start: var(--spacing-s);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-xs);
|
||||
li {
|
||||
list-style: none;
|
||||
a.nav_link {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding-inline: var(--spacing-l);
|
||||
padding-block: var(--spacing-s);
|
||||
&.active {
|
||||
background-color: var(--color-primary-active);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--color-primary-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user