修正大部分的编译错误。

This commit is contained in:
徐涛 2025-02-10 14:28:34 +08:00
parent 2144cd548a
commit 88e3d1f928
44 changed files with 429 additions and 381 deletions

View File

@ -1,6 +1,6 @@
import { Icon, IconProps } from '@iconify/react/dist/iconify.js';
import cx from 'clsx';
import { MouseEventHandler, useCallback } from 'react';
import { MouseEvent, MouseEventHandler, useCallback } from 'react';
import styles from './ActionIcon.module.css';
type ActionIconProps = {

View File

@ -87,7 +87,7 @@ export function ColorComponentInput({ color, onChange }: ColorComponentInputProp
}
};
const updateH = (evt: ChangeEvent<HTMLInputElement>) => {
const value = parseInt(evt.target.value, 10);
let value = parseInt(evt.target.value, 10);
if (value > 360) {
value -= 360;
}

View File

@ -28,7 +28,7 @@ export function ColorRangePicker({
}: ColorRangePickerProps) {
const [pickerValue, setPickerValue] = useState(value);
const handlePickerChange = (evt: ChangeEvent<HTMLInputElement>) => {
const value = evt.target.value as number;
const value = Number(evt.target.value);
setPickerValue(valueProcess(value));
onChange?.(valueProcess(value));
};

View File

@ -4,7 +4,7 @@ import { useRef, useState } from 'react';
import styles from './EditableTitle.module.css';
type EditableTitleProps = {
title: string;
title?: string;
onChange?: (newTitle: string) => void;
};

View File

@ -6,7 +6,7 @@ import styles from './FloatColorPicker.module.css';
type FloatColorPickerProps = {
name?: string;
color?: string;
color?: string | null;
onPick?: (color: string | null | undefined) => void;
};

View File

@ -52,6 +52,7 @@ export function HSegmentedControl({
<div
key={`${index}_${value}`}
className={cx(styles.option, isEqual(selected, value) && styles.selected)}
//@ts-expect-error TS2322
ref={(el) => (optionsRef.current[index] = el!)}
onClick={() => handleSelectAction(value, index)}>
{label}

View File

@ -26,7 +26,7 @@ export function LabeledPicker({
}: LabeledPickerProps) {
const [pickerValue, setPickerValue] = useState(value ?? min);
const handlePickerChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value as number;
const value = Number(event.target.value);
setPickerValue(value);
onChange?.(value);
};

View File

@ -118,7 +118,7 @@ type ToastProps = {
icon?: string;
duration?: ToastDuration;
ref: RefObject<HTMLDivElement>;
closeAction: () => void;
closeAction: (tid?: string) => void;
};
const Toast = ({
kind,
@ -157,7 +157,7 @@ export function useNotification() {
type NotificationElement = {
id: string;
element: ReactNode;
ref: RefObject<ReactNode>;
ref: RefObject<ReactNode | HTMLDivElement>;
};
type NotificationsProps = {
defaultDuration?: number;
@ -184,7 +184,7 @@ export function Notifications({
duration?: number,
) => {
const id = v4();
const ref = createRef(null);
const ref = createRef<ReactNode | HTMLDivElement>();
const newNotify = (
<Notification
kind={kind}
@ -207,14 +207,9 @@ export function Notifications({
setToasts((prev) => filter(prev, (n) => !isEqual(n.id, id)));
}, []);
const showToast = useCallback(
(
kind: NotificationType,
message?: string,
icon?: IconifyIconProps['icon'],
duration?: ToastDuration,
) => {
(kind: NotificationType, message?: string, icon?: string, duration?: ToastDuration) => {
const id = v4();
const ref = createRef(null);
const ref = createRef<HTMLDivElement>();
const newToast = (
<Toast
kind={kind}

View File

@ -1,5 +1,5 @@
import { clamp } from 'lodash-es';
import { RefObject, useEffect, useRef, useState } from 'react';
import { RefObject, useEffect, useRef, useState, WheelEvent } from 'react';
import styles from './ScrollArea.module.css';
type ScrollBarProps = {
@ -148,10 +148,10 @@ export function ScrollArea({
enableY = false,
normalizedScroll = false,
}: ScrollAreaProps) {
const scrollContainerRef = useRef<HTMLDivElement>(null);
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
const [xScrollNeeded, setXScrollNeeded] = useState(false);
const [yScrollNeeded, setYScrollNeeded] = useState(false);
const handleWheel = (evt: WheelEvent) => {
const handleWheel = (evt: WheelEvent<HTMLDivElement>) => {
const container = scrollContainerRef?.current;
if (enableY && container) {
const delta = evt.deltaY;
@ -177,7 +177,7 @@ export function ScrollArea({
return (
<div className={styles.scroll_area}>
<div className={styles.content} ref={scrollContainerRef} onWheel={handleWheel}>
<div className={styles.content} ref={scrollContainerRef} onWheel={(e) => handleWheel(e)}>
{children}
</div>
{enableY && yScrollNeeded && <VerticalScrollBar containerRef={scrollContainerRef} />}

View File

@ -18,7 +18,7 @@ const positionMap = {
export function Tooltip({ content, position = 'top', children }: TooltipProps) {
const [show, setShow] = useState(false);
const contentRef = useRef<HTMLDivElement>();
const contentRef = useRef<HTMLDivElement | null>(null);
return (
<div

View File

@ -52,6 +52,7 @@ export function VSegmentedControl({
<div
key={`${index}_${value}`}
className={cx(styles.option, isEqual(selected, value) && styles.selected)}
//@ts-expect-error TS2322
ref={(el) => (optionsRef.current[index] = el!)}
onClick={() => handleSelectAction(value, index)}>
{label}

View File

@ -6,7 +6,7 @@ import { NotificationType, useNotification } from '../components/Notifications';
export function useCopy() {
const { showToast } = useNotification();
const [cpState, copyToClipboard] = useCopyToClipboard();
const copyAction = useCallback((content: string) => {
const copyAction = useCallback((content?: string | null) => {
if (isNil(content) || isEmpty(content)) return;
copyToClipboard(content);
}, []);

View File

@ -4,12 +4,12 @@ import { MaterialDesign3SchemeStorage } from './material-3-scheme';
import { QSchemeStorage } from './q-scheme';
import { SwatchSchemeStorage } from './swatch_scheme';
export type Option =
export type Option<T = string | number | null> =
| {
label: string;
value: string | number | null;
value: T;
}
| Record<'label' | 'value', string | number | null>;
| Record<'label' | 'value', T>;
export type HarmonyColor = {
color: string;

View File

@ -23,18 +23,18 @@ export function Darkens({ color, darkens, mix, step, maximum, copyMode }: Darken
switch (mix) {
case 'progressive':
for (let i = 1; i <= darkens; i++) {
const darkenColor = colorFn.darken(last(darkenColors), step);
const darkenColor = colorFn.darken(last(darkenColors) ?? '', step ?? 0);
darkenColors.push(darkenColor);
}
break;
case 'linear':
for (let i = 1; i <= darkens; i++) {
const darkenColor = colorFn.darken(color, step * i);
const darkenColor = colorFn.darken(color, (step ?? 0) * i);
darkenColors.push(darkenColor);
}
break;
case 'average': {
const interval = maximum / darkens / 100;
const interval = (maximum ?? 0) / darkens / 100;
for (let i = 1; i <= darkens; i++) {
const darkenColor = colorFn.darken(color, interval * i);
darkenColors.push(darkenColor);

View File

@ -23,18 +23,18 @@ export function Lightens({ color, lightens, mix, step, maximum, copyMode }: Ligh
switch (mix) {
case 'progressive':
for (let i = 1; i <= lightens; i++) {
const lightenColor = colorFn.lighten(last(lightenColors), step);
const lightenColor = colorFn.lighten(last(lightenColors) ?? '', step ?? 0);
lightenColors.push(lightenColor);
}
break;
case 'linear':
for (let i = 1; i <= lightens; i++) {
const lightenColor = colorFn.lighten(color, step * i);
const lightenColor = colorFn.lighten(color, (step ?? 0) * i);
lightenColors.push(lightenColor);
}
break;
case 'average': {
const interval = maximum / lightens / 100;
const interval = (maximum ?? 0) / lightens / 100;
for (let i = 1; i <= lightens; i++) {
const lightenColor = colorFn.lighten(color, interval * i);
lightenColors.push(lightenColor);

View File

@ -1,6 +1,8 @@
import { isEqual, isNil } from 'lodash-es';
import { useState } from 'react';
import { Tab } from '../../components/Tab';
import { MaterialDesign2SchemeStorage } from '../../material-2-scheme';
import { SchemeContent } from '../../models';
import { SchemeExport } from './Export';
import { M2SchemeBuilder } from './m2-scheme/Builder';
import { M2SchemePreview } from './m2-scheme/Preview';
@ -11,18 +13,18 @@ const tabOptions = [
{ title: 'Exports', id: 'export' },
];
type M3SchemeProps = {
scheme: SchemeContent<MaterialDesign3SchemeStorage>;
type M2SchemeProps = {
scheme: SchemeContent<MaterialDesign2SchemeStorage>;
};
export function M2Scheme({ scheme }: M3SchemeProps) {
export function M2Scheme({ scheme }: M2SchemeProps) {
const [activeTab, setActiveTab] = useState<(typeof tabOptions)[number]['id']>(() =>
isNil(scheme.schemeStorage.scheme) ? 'builder' : 'overview',
);
return (
<>
<Tab tabs={tabOptions} activeTab={activeTab} onActive={setActiveTab} />
<Tab tabs={tabOptions} activeTab={activeTab} onActive={(v) => setActiveTab(v as string)} />
{isEqual(activeTab, 'overview') && <M2SchemePreview scheme={scheme} />}
{isEqual(activeTab, 'builder') && (
<M2SchemeBuilder scheme={scheme} onBuildComplete={() => setActiveTab('overview')} />

View File

@ -2,6 +2,7 @@ import { isEqual, isNil } from 'lodash-es';
import { useState } from 'react';
import { Tab } from '../../components/Tab';
import { MaterialDesign3SchemeStorage } from '../../material-3-scheme';
import { SchemeContent } from '../../models';
import { SchemeExport } from './Export';
import { M3SchemeBuilder } from './m3-scheme/Builder';
import { M3SchemePreview } from './m3-scheme/Preview';
@ -23,7 +24,7 @@ export function M3Scheme({ scheme }: M3SchemeProps) {
return (
<>
<Tab tabs={tabOptions} activeTab={activeTab} onActive={setActiveTab} />
<Tab tabs={tabOptions} activeTab={activeTab} onActive={(v) => setActiveTab(v as string)} />
{isEqual(activeTab, 'overview') && <M3SchemePreview scheme={scheme} />}
{isEqual(activeTab, 'builder') && (
<M3SchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />

View File

@ -24,7 +24,7 @@ export function QScheme({ scheme }: QSchemeProps) {
return (
<>
<Tab tabs={tabOptions} activeTab={activeTab} onActive={setActiveTab} />
<Tab tabs={tabOptions} activeTab={activeTab} onActive={(v) => setActiveTab(v as string)} />
{isEqual(activeTab, 'overview') && <QSchemePreview scheme={scheme} />}
{isEqual(activeTab, 'builder') && (
<QSchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />

View File

@ -24,7 +24,7 @@ export function SwatchScheme({ scheme }: SwatchSchemeProps) {
return (
<>
<Tab tabs={tabOptions} activeTab={activeTab} onActive={setActiveTab} />
<Tab tabs={tabOptions} activeTab={activeTab} onActive={(v) => setActiveTab(v as string)} />
{isEqual(activeTab, 'overview') && <SwatchSchemePreview scheme={scheme} />}
{isEqual(activeTab, 'builder') && (
<SwatchSchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />

View File

@ -36,18 +36,19 @@ export function M2SchemeBuilder({ scheme, onBuildComplete }: M2SchemeBuilderProp
[originalColors, newColors, deleted],
);
const [errMsg, handleSubmitAction] = useActionState((state, formData) => {
const [errMsg, handleSubmitAction] = useActionState<Map<string, string>, FormData>(
(_state, formData) => {
const errMsg = new Map<string, string>();
try {
const primaryColor = formData.get('primary');
const primaryColor = formData.get('primary') as string;
if (isNil(primaryColor) || isEmpty(primaryColor)) {
errMsg.set('primary', 'Primary color is required');
}
const secondaryColor = formData.get('secondary');
const secondaryColor = formData.get('secondary') as string;
if (isNil(secondaryColor) || isEmpty(secondaryColor)) {
errMsg.set('secondary', 'Secondary color is required');
}
const errorColor = formData.get('error');
const errorColor = formData.get('error') as string;
if (isNil(errorColor) || isEmpty(errorColor)) {
errMsg.set('error', 'Error color is required');
}
@ -89,7 +90,9 @@ export function M2SchemeBuilder({ scheme, onBuildComplete }: M2SchemeBuilderProp
}
return errMsg;
}, new Map<string, string>());
},
new Map<string, string>(),
);
return (
<ScrollArea enableY>

View File

@ -86,8 +86,8 @@ export function M2SchemePreview({ scheme }: M2SchemePreviewProps) {
return (
<ScrollArea enableY>
<div className={styles.preview_layout}>
<PreviewBlock title="Light" baseline={scheme.schemeStorage.scheme?.light} />
<PreviewBlock title="Dark" baseline={scheme.schemeStorage.scheme?.dark} />
<PreviewBlock title="Light" baseline={scheme.schemeStorage.scheme!.light} />
<PreviewBlock title="Dark" baseline={scheme.schemeStorage.scheme!.dark} />
</div>
</ScrollArea>
);

View File

@ -36,15 +36,16 @@ export function M3SchemeBuilder({ scheme, onBuildCompleted }: M3SchemeBuilderPro
[originalColors, newColors, deleted],
);
const [errMsg, handleSubmitAction] = useActionState((state, formData) => {
const [errMsg, handleSubmitAction] = useActionState<Map<string, string>, FormData>(
(_state, formData) => {
const errMsg = new Map<string, string>();
try {
const sourceColor = formData.get('source');
const sourceColor = formData.get('source') as string;
if (isNil(sourceColor) || isEmpty(sourceColor)) {
errMsg.set('source', 'Source color is required');
}
const errorColor = formData.get('error');
const errorColor = formData.get('error') as string;
if (isNil(errorColor) || isEmpty(errorColor)) {
errMsg.set('error', 'Error color is required');
}
@ -93,7 +94,9 @@ export function M3SchemeBuilder({ scheme, onBuildCompleted }: M3SchemeBuilderPro
}
return errMsg;
}, new Map<string, string>());
},
new Map<string, string>(),
);
return (
<ScrollArea enableY>

View File

@ -264,8 +264,8 @@ export function M3SchemePreview({ scheme }: M3SchemePreviewProps) {
return (
<ScrollArea enableY>
<div className={styles.preview_layout}>
<PreviewBlock title="Light Scheme" baseline={scheme.schemeStorage.scheme?.light_baseline} />
<PreviewBlock title="Dark Scheme" baseline={scheme.schemeStorage.scheme?.dark_baseline} />
<PreviewBlock title="Light Scheme" baseline={scheme.schemeStorage.scheme!.light_baseline} />
<PreviewBlock title="Dark Scheme" baseline={scheme.schemeStorage.scheme!.dark_baseline} />
</div>
</ScrollArea>
);

View File

@ -82,7 +82,8 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps
return [];
}, []);
const [errMsg, handleSubmitAction] = useActionState((state, formData) => {
const [errMsg, handleSubmitAction] = useActionState<Map<string, string>, FormData>(
(_state, formData) => {
const errMsg = new Map<string, string>();
const requiredFields = [
'primary',
@ -128,9 +129,9 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps
const source: QSchemeSource = {
primary: defaultEmptyFormData(formData, 'primary', null),
secondary: defaultEmptyFormData(formData, 'secondary', undefined),
tertiary: defaultEmptyFormData(formData, 'tertiary', undefined),
accent: defaultEmptyFormData(formData, 'accent', undefined),
secondary: defaultEmptyFormData(formData, 'secondary', null),
tertiary: defaultEmptyFormData(formData, 'tertiary', null),
accent: defaultEmptyFormData(formData, 'accent', null),
danger: defaultEmptyFormData(formData, 'danger', null),
success: defaultEmptyFormData(formData, 'success', null),
warning: defaultEmptyFormData(formData, 'warn', null),
@ -141,26 +142,26 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps
};
const generatedScheme = every([source.secondary, source.tertiary, source.accent], isNil)
? colorFn?.generate_q_scheme_automatically(
source.primary,
source.danger,
source.success,
source.warning,
source.info,
source.foreground,
source.background,
source.primary ?? '',
source.danger ?? '',
source.success ?? '',
source.warning ?? '',
source.info ?? '',
source.foreground ?? '',
source.background ?? '',
schemeSetting,
)
: colorFn?.generate_q_scheme_manually(
source.primary,
source.primary ?? '',
source.secondary ?? undefined,
source.tertiary ?? undefined,
source.accent ?? undefined,
source.danger,
source.success,
source.warning,
source.info,
source.foreground,
source.background,
source.danger ?? '',
source.success ?? '',
source.warning ?? '',
source.info ?? '',
source.foreground ?? '',
source.background ?? '',
schemeSetting,
);
updateScheme((prev) => {
@ -177,7 +178,9 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps
}
return errMsg;
}, new Map<string, string>());
},
new Map<string, string>(),
);
return (
<ScrollArea enableY>

View File

@ -10,7 +10,12 @@ import { ScrollArea } from '../../../components/ScrollArea';
import { Switch } from '../../../components/Switch';
import { SchemeContent } from '../../../models';
import { useUpdateScheme } from '../../../stores/schemes';
import { QSwatchEntry, QSwatchSchemeSetting, SwatchSchemeStorage } from '../../../swatch_scheme';
import {
QSwatchEntry,
QSwatchSchemeSetting,
SwatchScheme,
SwatchSchemeStorage,
} from '../../../swatch_scheme';
import { mapToObject } from '../../../utls';
import { ColorEntry, IdenticalColorEntry } from '../ColorEntry';
import styles from './Builder.module.css';
@ -64,7 +69,8 @@ export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBu
return null;
}, [scheme.schemeStorage.source]);
const [errMsg, handleSubmitAction] = useActionState((state, formData) => {
const [errMsg, handleSubmitAction] = useActionState<Map<string, string>, FormData>(
(_state, formData) => {
const errMsg = new Map<string, string>();
try {
@ -119,7 +125,7 @@ export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBu
colors: dumpedEntries,
setting: dumpedSettings,
};
prev.schemeStorage.scheme = mapToObject(generatedScheme[0]);
prev.schemeStorage.scheme = mapToObject(generatedScheme[0]) as SwatchScheme;
prev.schemeStorage.cssVariables = generatedScheme[1];
prev.schemeStorage.scssVariables = generatedScheme[2];
prev.schemeStorage.jsVariables = generatedScheme[3];
@ -132,7 +138,9 @@ export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBu
}
return errMsg;
}, new Map<string, string>());
},
new Map<string, string>(),
);
return (
<ScrollArea enableY>

View File

@ -54,12 +54,12 @@ export function SwatchSchemePreview({ scheme }: SwatchSchemePreviewProps) {
<h2>Light Scheme</h2>
<SchemeBlock
amount={scheme.schemeStorage.source?.setting?.amount ?? 0}
scheme={scheme.schemeStorage.scheme.light}
scheme={scheme.schemeStorage.scheme!.light}
/>
<h2>Dark Scheme</h2>
<SchemeBlock
amount={scheme.schemeStorage.source?.setting?.amount ?? 0}
scheme={scheme.schemeStorage.scheme.dark}
scheme={scheme.schemeStorage.scheme!.dark}
/>
</div>
</ScrollArea>

View File

@ -23,18 +23,18 @@ export function Shades({ color, shades, mix, step, maximum, copyMode }: ShadesLi
switch (mix) {
case 'progressive':
for (let i = 1; i <= shades; i++) {
const shade = colorFn!.shade(last(genColors), step);
const shade = colorFn!.shade(last(genColors) ?? '', step ?? 0);
genColors.push(shade);
}
break;
case 'linear':
for (let i = 1; i <= shades; i++) {
const shade = colorFn!.shade(color, step * i);
const shade = colorFn!.shade(color, (step ?? 0) * i);
genColors.push(shade);
}
break;
case 'average': {
const interval = maximum / shades / 100;
const interval = (maximum ?? 0) / shades / 100;
for (let i = 1; i <= shades; i++) {
const shade = colorFn!.shade(color, interval * i);
genColors.push(shade);

View File

@ -23,18 +23,18 @@ export function Tints({ color, tints, mix, step, maximum, copyMode }: TintsListP
switch (mix) {
case 'progressive':
for (let i = 1; i <= tints; i++) {
const tint = colorFn!.tint(last(genColors), step);
const tint = colorFn!.tint(last(genColors) ?? '', step ?? 0);
genColors.push(tint);
}
break;
case 'linear':
for (let i = 1; i <= tints; i++) {
const tint = colorFn!.tint(color, step * i);
const tint = colorFn!.tint(color, (step ?? 0) * i);
genColors.push(tint);
}
break;
case 'average': {
const interval = maximum / tints / 100;
const interval = (maximum ?? 0) / tints / 100;
for (let i = 1; i <= tints; i++) {
const tint = colorFn!.tint(color, interval * i);
genColors.push(tint);

View File

@ -8,6 +8,7 @@ import { ColorDescription } from '../models';
import { ColorCard } from '../page-components/cards-detail/ColorCard';
import styles from './CardsDetail.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
type CardsDetailProps = {
mainTag: string;
};
@ -20,7 +21,7 @@ export function CardsDetail({ mainTag }: CardsDetailProps) {
}
try {
const embededCategories = colorFn.color_categories() as { label: string; value: string }[];
return embededCategories.filter((cate) => !isEqual(cate.get('value'), 'unknown'));
return embededCategories.filter((cate) => !isEqual(cate.value, 'unknown'));
} catch (e) {
console.error('[Fetch color categories]', e);
}
@ -31,7 +32,7 @@ export function CardsDetail({ mainTag }: CardsDetailProps) {
const selectedValue = e.target.value;
setCategory(selectedValue);
};
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const [mode, setMode] = useState<ColorModes>('hex');
const colors = useMemo(() => {
if (!colorFn) {
return [];
@ -69,8 +70,8 @@ export function CardsDetail({ mainTag }: CardsDetailProps) {
onChange={handleSelectCategory}>
<option value="null">All</option>
{categories.map((cate, index) => (
<option key={`${cate.get('value')}-${index}`} value={cate.get('value')}>
{cate.get('label')}
<option key={`${cate.value}-${index}`} value={cate.value}>
{cate.label}
</option>
))}
</select>
@ -85,7 +86,7 @@ export function CardsDetail({ mainTag }: CardsDetailProps) {
{ label: 'OKLCH', value: 'oklch' },
]}
value={mode}
onChange={setMode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
<ScrollArea enableY>

View File

@ -44,7 +44,7 @@ export function ColorCompare() {
{ label: 'Relative', value: 'relative' },
]}
value={analysisMode}
onChange={setMode}
onChange={(v) => setMode(v as Parameters<typeof setMode>[0])}
/>
</Labeled>
<RGBCompare basic={basicColor} compare={compareColor} mode={analysisMode} />

View File

@ -114,7 +114,7 @@ export function Harmonies() {
<div className={styles.mode_navigation}>
<h5>Color selection method</h5>
<VSegmentedControl
onChange={setSelectedMode}
onChange={(v) => setSelectedMode(v as string)}
options={[
{ label: 'Complementary', value: 'complementary' },
{ label: 'Analogous', value: 'analogous' },

View File

@ -13,6 +13,8 @@ import { Lightens } from '../page-components/lighten-darken/lightens';
import { currentPickedColor } from '../stores/colors';
import styles from './LightenDarken.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
export function LightenDarken() {
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
const [lighten, setLighten] = useState(3);
@ -20,7 +22,7 @@ export function LightenDarken() {
const [steps, setSteps] = useState(10);
const [maximum, setMaximum] = useState(90);
const [mixMode, setMixMode] = useState<'progressive' | 'average'>('progressive');
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const [mode, setMode] = useState<ColorModes>('hex');
return (
<div className={cx('workspace', styles.lighten_workspace)}>
@ -123,8 +125,8 @@ export function LightenDarken() {
{ label: 'LAB', value: 'lab' },
{ label: 'OKLCH', value: 'oklch' },
]}
valu={mode}
onChange={setMode}
value={mode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
</div>

View File

@ -8,12 +8,14 @@ import { LabeledPicker } from '../components/LabeledPicker';
import { ScrollArea } from '../components/ScrollArea';
import styles from './Mixer.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
export function Mixer() {
const { colorFn } = useColorFunction();
const [basicColor, setBasicColor] = useState('000000');
const [mixColor, setMixColor] = useState('000000');
const [mixRatio, setMixRatio] = useState(0);
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const [mode, setMode] = useState<ColorModes>('hex');
const mixedColor = useMemo(() => {
try {
if (!colorFn) {
@ -68,7 +70,7 @@ export function Mixer() {
{ label: 'OKLCH', value: 'oklch' },
]}
value={mode}
onChange={setMode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
</div>

View File

@ -11,7 +11,8 @@ export function NewScheme() {
const createScheme = useCreateScheme();
const navigate = useNavigate();
const [schemeType, setSchemeType] = useState<SchemeTypeOption['value']>('q_scheme');
const [errors, formAction] = useActionState((prevState, formData) => {
const [errors, formAction] = useActionState<{ [key: string]: string }, FormData>(
(_prevState, formData): { [key: string]: string } => {
try {
const name = formData.get('name') as string;
if (isNil(name) || isEmpty(name)) {
@ -22,10 +23,12 @@ export function NewScheme() {
const newId = createScheme(name, schemeType, description);
navigate(`../${newId}`);
} catch (error) {
return error;
return error as { [key: string]: string };
}
return {};
}, {});
return {} as { [key: string]: string };
},
{},
);
return (
<form action={formAction} className={styles.create_scheme_form_layout}>
@ -37,7 +40,7 @@ export function NewScheme() {
options={SchemeTypeOptions}
extendClassName={styles.custom_segment}
value={schemeType}
onChange={setSchemeType}
onChange={(v) => setSchemeType(v as SchemeTypeOption['value'])}
/>
<input type="hidden" name="type" value={schemeType} />
</div>

View File

@ -11,6 +11,8 @@ import { PaletteColors } from '../page-components/auto-palette/PaletteColors';
import { currentPickedColor } from '../stores/colors';
import styles from './Palette.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
export function AutomaticPalette() {
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
const [useReferenceColor, setUseReferenceColor] = useState(false);
@ -22,7 +24,7 @@ export function AutomaticPalette() {
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');
const [mode, setMode] = useState<ColorModes>('hex');
useEffect(() => {
if (useReferenceColor) {
@ -111,8 +113,8 @@ export function AutomaticPalette() {
{ label: 'LAB', value: 'lab' },
{ label: 'OKLCH', value: 'oklch' },
]}
valu={mode}
onChange={setMode}
value={mode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
</div>

View File

@ -5,12 +5,17 @@ import { useNavigate, useParams } from 'react-router-dom';
import { EditableDescription } from '../components/EditableDescription';
import { EditableTitle } from '../components/EditableTitle';
import { SchemeSign } from '../components/SchemeSign';
import { MaterialDesign2SchemeStorage } from '../material-2-scheme';
import { MaterialDesign3SchemeStorage } from '../material-3-scheme';
import { SchemeContent } from '../models';
import { CorruptedScheme } from '../page-components/scheme/CorruptedScheme';
import { M2Scheme } from '../page-components/scheme/M2Scheme';
import { M3Scheme } from '../page-components/scheme/M3Scheme';
import { QScheme } from '../page-components/scheme/QScheme';
import { SwatchScheme } from '../page-components/scheme/SwatchScheme';
import { QSchemeStorage } from '../q-scheme';
import { useScheme, useUpdateScheme } from '../stores/schemes';
import { SwatchSchemeStorage } from '../swatch_scheme';
import styles from './SchemeDetail.module.css';
export function SchemeDetail() {
@ -40,13 +45,13 @@ export function SchemeDetail() {
const schemeContent = useMemo(() => {
switch (scheme?.type) {
case 'q_scheme':
return <QScheme scheme={scheme} />;
return <QScheme scheme={scheme as SchemeContent<QSchemeStorage>} />;
case 'swatch_scheme':
return <SwatchScheme scheme={scheme} />;
return <SwatchScheme scheme={scheme as SchemeContent<SwatchSchemeStorage>} />;
case 'material_2':
return <M2Scheme scheme={scheme} />;
return <M2Scheme scheme={scheme as SchemeContent<MaterialDesign2SchemeStorage>} />;
case 'material_3':
return <M3Scheme scheme={scheme} />;
return <M3Scheme scheme={scheme as SchemeContent<MaterialDesign3SchemeStorage>} />;
default:
return <CorruptedScheme />;
}

View File

@ -13,6 +13,8 @@ import { Tints } from '../page-components/tints-shades/tints';
import { currentPickedColor } from '../stores/colors';
import styles from './TintsShades.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
export function TintsShades() {
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
const [steps, setSteps] = useState(10);
@ -20,7 +22,7 @@ export function TintsShades() {
const [shades, setShades] = useState(3);
const [maximum, setMaximum] = useState(90);
const [mixMode, setMixMode] = useState<'progressive' | 'average'>('progressive');
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const [mode, setMode] = useState<ColorModes>('hex');
return (
<div className={cx('workspace', styles.tints_workspace)}>
@ -122,8 +124,8 @@ export function TintsShades() {
{ label: 'LAB', value: 'lab' },
{ label: 'OKLCH', value: 'oklch' },
]}
valu={mode}
onChange={setMode}
value={mode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
</div>

View File

@ -11,13 +11,15 @@ import { ScrollArea } from '../components/ScrollArea';
import { currentPickedColor } from '../stores/colors';
import styles from './Tones.module.css';
type ColorModes = 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
export function Tones() {
const { colorFn } = useColorFunction();
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
const [steps, setSteps] = useState(10);
const [tones, setTones] = useState(3);
const [seedBias, setSeedBias] = useState(0);
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const [mode, setMode] = useState<ColorModes>('hex');
const colors = useMemo(() => {
try {
const lightenColors = colorFn!.tonal_lighten_series(
@ -98,8 +100,8 @@ export function Tones() {
{ label: 'LAB', value: 'lab' },
{ label: 'OKLCH', value: 'oklch' },
]}
valu={mode}
onChange={setMode}
value={mode}
onChange={(v) => setMode(v as ColorModes)}
/>
</div>
</div>

View File

@ -9,9 +9,11 @@ import { ColorWheel } from '../page-components/wheels/ColorWheel';
import { currentPickedColor } from '../stores/colors';
import styles from './Wheels.module.css';
type HighlightMode = Parameters<typeof ColorWheel>[0]['highlightMode'];
export function Wheels() {
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
const [selectedMode, setSelectedMode] = useState('complementary');
const [selectedMode, setSelectedMode] = useState<HighlightMode>('complementary');
const [steps, setSteps] = useState(10);
const [tones, setTones] = useState(3);
@ -31,7 +33,7 @@ export function Wheels() {
<div className={styles.mode_navigation}>
<h5>Color selection method</h5>
<VSegmentedControl
onChange={setSelectedMode}
onChange={(v) => setSelectedMode(v as HighlightMode)}
options={[
{ label: 'Complementary', value: 'complementary' },
{ label: 'Analogous', value: 'analogous' },

View File

@ -47,7 +47,7 @@ export type QSchemeSource = {
warning: string | null;
info: string | null;
foreground: string | null;
background: strin | nullg;
background: string | null;
setting: QSchemeSetting | null;
};

View File

@ -42,7 +42,10 @@ export type SchemeSet = {
const schemesAtom = atomWithStorage<SchemeContent<SchemeStorage>[]>('schemes', []);
export const activeSchemeAtom = atomWithStorage<string | null>('activeScheme', null);
export function useSchemeList(): Pick<SchemeContent<SchemeStorage>, 'id' | 'name' | 'createdAt'>[] {
export function useSchemeList(): Pick<
SchemeContent<SchemeStorage>,
'id' | 'name' | 'createdAt' | 'type'
>[] {
const schemes = useAtomValue(schemesAtom);
const sortedSchemes = useMemo(
() =>
@ -56,9 +59,12 @@ export function useSchemeList(): Pick<SchemeContent<SchemeStorage>, 'id' | 'name
return sortedSchemes;
}
export function useScheme(id: string): SchemeContent<SchemeStorage> | null {
export function useScheme(id?: string | null): SchemeContent<SchemeStorage> | null {
const schemes = useAtomValue(schemesAtom);
const scheme = useMemo(() => schemes.find((s) => isEqual(id, s.id)) ?? null, [schemes, id]);
const scheme = useMemo(
() => schemes.find((s) => !isNil(id) && isEqual(id, s.id)) ?? null,
[schemes, id],
);
return scheme;
}
@ -71,11 +77,11 @@ export function useActiveScheme(): SchemeContent<SchemeStorage> | null {
export function useCreateScheme(): (
name: string,
type: SchemeType,
description?: string,
description?: string | null,
) => string {
const updateSchemes = useSetAtom(schemesAtom);
const createSchemeAction = useCallback(
(name: string, type: SchemeType, description?: string) => {
(name: string, type: SchemeType, description?: string | null) => {
const newId = v4();
updateSchemes((prev) => [
...prev.filter((s) => !isNil(s)),
@ -97,7 +103,7 @@ export function useCreateScheme(): (
}
export function useUpdateScheme(
id: string,
id?: string | null,
): (updater: (prev: SchemeContent<SchemeStorage>) => SchemeContent<SchemeStorage>) => void {
const updateSchemes = useSetAtom(schemesAtom);
const updateAction = useCallback(
@ -107,7 +113,7 @@ export function useUpdateScheme(
prev,
(acc, scheme) => {
if (!isNil(scheme)) {
if (isEqual(id, scheme.id)) {
if (!isNil(id) && isEqual(id, scheme.id)) {
acc.push(updater(scheme));
} else {
acc.push(scheme);

View File

@ -1,7 +1,7 @@
import { isEmpty, isNil } from 'lodash-es';
export function defaultEmptyFormData<D>(formData: FormData, param: string, defaultValue: D): D {
const value = formData.get(param);
const value = formData.get(param) as D;
if (isNil(value) || isEmpty(value)) {
return defaultValue;
}
@ -15,10 +15,8 @@ export function defaultEmptyValue<T, D>(value: T, defaultValue: D): T | D {
return value;
}
export function mapToObject<K extends string | number | symbol, V>(
map: Map<K, V>,
): Record<K, V extends Map<unknown, unknown> ? unknown : V> {
const obj: Record<K, V extends Map<unknown, unknown> ? unknown : V> = {};
export function mapToObject<K extends string | number | symbol, V>(map: Map<K, V>): Record<K, V> {
const obj = {} as Record<K, V>;
map.forEach((value, key) => {
if (value instanceof Map) {
obj[key] = mapToObject(value);

View File

@ -3,10 +3,13 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
@ -14,13 +17,14 @@
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"strict": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
"include": [
"src"
]
}

View File

@ -2,23 +2,25 @@
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"lib": [
"ES2023"
],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"strict": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
"include": [
"vite.config.ts"
]
}