From 2638bbd99a9155d1465d039114651130549b8ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Sun, 30 Mar 2025 22:48:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BASwatch=20Builder=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=8D=89=E7=A8=BF=E7=9A=84=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheme/swatch-scheme/Builder.module.css | 6 + .../scheme/swatch-scheme/Builder.tsx | 108 ++++++++++++------ 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/page-components/scheme/swatch-scheme/Builder.module.css b/src/page-components/scheme/swatch-scheme/Builder.module.css index caea7a2..35f2088 100644 --- a/src/page-components/scheme/swatch-scheme/Builder.module.css +++ b/src/page-components/scheme/swatch-scheme/Builder.module.css @@ -30,6 +30,12 @@ .parameter_input { max-width: 8em; } + .button_row { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--spacing-s); + } h5 { font-size: var(--font-size-m); line-height: 1.7em; diff --git a/src/page-components/scheme/swatch-scheme/Builder.tsx b/src/page-components/scheme/swatch-scheme/Builder.tsx index 2b61bbb..114d42b 100644 --- a/src/page-components/scheme/swatch-scheme/Builder.tsx +++ b/src/page-components/scheme/swatch-scheme/Builder.tsx @@ -2,6 +2,7 @@ import { ColorShifting, SwatchEntry, SwatchSchemeSetting } from 'color-module'; import { includes, isEmpty, isEqual, isNaN } from 'lodash-es'; import { useActionState, useCallback, useMemo, useState } from 'react'; import { useColorFunction } from '../../../ColorFunctionContext'; +import { NotificationType, useNotification } from '../../../components/Notifications'; import { ScrollArea } from '../../../components/ScrollArea'; import { Switch } from '../../../components/Switch'; import { SchemeContent } from '../../../models'; @@ -10,6 +11,7 @@ import { QSwatchEntry, QSwatchSchemeSetting, SwatchScheme, + SwatchSchemeSource, SwatchSchemeStorage, } from '../../../swatch_scheme'; import { mapToObject } from '../../../utls'; @@ -22,6 +24,7 @@ type SwatchSchemeBuilderProps = { }; export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBuilderProps) { + const { showToast } = useNotification(); const { colorFn } = useColorFunction(); const updateScheme = useUpdateScheme(scheme.id); const originalColors = useMemo(() => { @@ -64,63 +67,95 @@ export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBu } return null; }, [scheme.schemeStorage.source]); + const collectSchemeSource = (formData: FormData): SwatchSchemeSource => { + const swatchAmount = Number(formData.get('amount')); + const minLightness = Number(formData.get('min_lightness')); + const maxLightness = Number(formData.get('max_lightness')); + const includePrimary = isEqual(formData.get('include_primary'), 'true'); + const darkConvertChroma = Number(formData.get('dark_chroma')) / 100.0; + const darkConvertLightness = Number(formData.get('dark_lightness')) / 100.0; + const swatchSetting = new SwatchSchemeSetting( + swatchAmount, + minLightness / 100.0, + maxLightness / 100.0, + includePrimary, + new ColorShifting(darkConvertChroma, darkConvertLightness), + ); + const dumpedSettings = swatchSetting.toJsValue() as QSwatchSchemeSetting; + const entries: SwatchEntry[] = []; + for (const key of colorKeys) { + const name = String(formData.get(`name_${key}`)); + const color = String(formData.get(`color_${key}`)); + if (isEmpty(name) || isEmpty(color)) continue; + entries.push(new SwatchEntry(name, color)); + } + const dumpedEntries = entries.map((entry) => entry.toJsValue() as QSwatchEntry); + + return { + colors: dumpedEntries, + setting: dumpedSettings, + }; + }; + + const [, handleDraftAction] = useActionState, FormData>( + (_state, formData) => { + const errMsg = new Map(); + + const collected = collectSchemeSource(formData); + updateScheme((prev) => { + prev.schemeStorage.source = collected; + return prev; + }); + setNewColors([]); + + showToast(NotificationType.SUCCESS, 'Scheme draft saved!', 'tabler:device-floppy', 3000); + + return errMsg; + }, + new Map(), + ); const [errMsg, handleSubmitAction] = useActionState, FormData>( (_state, formData) => { const errMsg = new Map(); try { - const swatchAmount = Number(formData.get('amount')); - if (isNaN(swatchAmount) || swatchAmount <= 0) { + const collected = collectSchemeSource(formData); + if (isNaN(collected.setting.amount) || collected.setting.amount <= 0) { errMsg.set('amount', 'MUST be a positive number'); } - if (swatchAmount > 30) { + if (collected.setting.amount > 30) { errMsg.set('amount', 'MUST be less than 30'); } - const minLightness = Number(formData.get('min_lightness')); - if (isNaN(minLightness) || minLightness < 0 || minLightness > 100) { + if ( + isNaN(collected.setting.min_lightness) || + collected.setting.min_lightness < 0 || + collected.setting.min_lightness > 1.0 + ) { errMsg.set('min', 'MUST be a number between 0 and 100'); } - const maxLightness = Number(formData.get('max_lightness')); - if (isNaN(maxLightness) || maxLightness < 0 || maxLightness > 100) { + if ( + isNaN(collected.setting.max_lightness) || + collected.setting.max_lightness < 0 || + collected.setting.max_lightness > 1.0 + ) { errMsg.set('max', 'MUST be a number between 0 and 100'); } - const includePrimary = isEqual(formData.get('include_primary'), 'true'); - const darkConvertChroma = Number(formData.get('dark_chroma')) / 100.0; - const darkConvertLightness = Number(formData.get('dark_lightness')) / 100.0; - - const swatchSetting = new SwatchSchemeSetting( - swatchAmount, - minLightness / 100.0, - maxLightness / 100.0, - includePrimary, - new ColorShifting(darkConvertChroma, darkConvertLightness), - ); - const dumpedSettings = swatchSetting.toJsValue() as QSwatchSchemeSetting; - const entries: SwatchEntry[] = []; - for (const key of colorKeys) { - const name = String(formData.get(`name_${key}`)); - const color = String(formData.get(`color_${key}`)); - if (isEmpty(name) || isEmpty(color)) continue; - entries.push(new SwatchEntry(name, color)); - } - const dumpedEntries = entries.map((entry) => entry.toJsValue() as QSwatchEntry); - if (isEmpty(entries)) { + if (isEmpty(collected.colors)) { errMsg.set('color', 'At least one color is required'); } if (!isEmpty(errMsg)) return errMsg; - const generatedScheme = colorFn?.generate_swatch_scheme(entries, swatchSetting); - console.debug('[generated scheme]', generatedScheme); + const generatedScheme = colorFn?.generate_swatch_scheme( + collected.colors, + collected.setting, + ); updateScheme((prev) => { - prev.schemeStorage.source = { - colors: dumpedEntries, - setting: dumpedSettings, - }; + prev.schemeStorage.source = collected; prev.schemeStorage.scheme = mapToObject(generatedScheme[0]) as SwatchScheme; prev.schemeStorage.cssVariables = generatedScheme[1]; prev.schemeStorage.cssAutoSchemeVariables = generatedScheme[2]; @@ -237,10 +272,13 @@ export function SwatchSchemeBuilder({ scheme, onBuildCompleted }: SwatchSchemeBu {errMsg.get('color')} )} -
+
+