diff --git a/src/page-components/scheme/q-scheme/Builder.module.css b/src/page-components/scheme/q-scheme/Builder.module.css index bb7bce3..80bf3e2 100644 --- a/src/page-components/scheme/q-scheme/Builder.module.css +++ b/src/page-components/scheme/q-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/q-scheme/Builder.tsx b/src/page-components/scheme/q-scheme/Builder.tsx index 157b367..a8c4184 100644 --- a/src/page-components/scheme/q-scheme/Builder.tsx +++ b/src/page-components/scheme/q-scheme/Builder.tsx @@ -3,6 +3,7 @@ import { every, isEmpty, isNil } from 'lodash-es'; import { useActionState, useMemo } from 'react'; import { useColorFunction } from '../../../ColorFunctionContext'; import { FloatColorPicker } from '../../../components/FloatColorPicker'; +import { NotificationType, useNotification } from '../../../components/Notifications'; import { ScrollArea } from '../../../components/ScrollArea'; import { VSegmentedControl } from '../../../components/VSegmentedControl'; import { SchemeContent } from '../../../models'; @@ -17,6 +18,7 @@ type QSchemeBuilderProps = { }; export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps) { + const { showToast } = useNotification(); const { colorFn } = useColorFunction(); const updateScheme = useUpdateScheme(scheme.id); const defaultSetting = useMemo(() => { @@ -77,6 +79,62 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps return []; }, []); + const collectSchemeSource = (formData: FormData): QSchemeSource => { + const schemeSetting = new SchemeSetting( + new ColorShifting( + Number(formData.get('hover_chroma')) / 100, + Number(formData.get('hover_lightness')) / 100, + ), + new ColorShifting( + Number(formData.get('active_chroma')) / 100, + Number(formData.get('active_lightness')) / 100, + ), + new ColorShifting( + Number(formData.get('focus_chroma')) / 100, + Number(formData.get('focus_lightness')) / 100, + ), + new ColorShifting( + Number(formData.get('disabled_chroma')) / 100, + Number(formData.get('disabled_lightness')) / 100, + ), + new ColorShifting( + Number(formData.get('dark_chroma')) / 100, + Number(formData.get('dark_lightness')) / 100, + ), + Number(formData.get('expanding')) as ColorExpand, + Number(formData.get('wacg')) as WACGSetting, + ); + const dumpedSetting = schemeSetting.toJsValue() as QSchemeSetting; + return { + primary: defaultEmptyFormData(formData, 'primary', null), + 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), + info: defaultEmptyFormData(formData, 'info', null), + foreground: defaultEmptyFormData(formData, 'foreground', null), + background: defaultEmptyFormData(formData, 'background', null), + setting: dumpedSetting, + }; + }; + + const [, handleDraftAction] = useActionState, FormData>( + (_state, formData) => { + const errMsg = new Map(); + + const source = collectSchemeSource(formData); + updateScheme((prev) => { + prev.schemeStorage.source = source; + return prev; + }); + 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(); @@ -96,45 +154,7 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps } if (!isEmpty(errMsg)) return errMsg; try { - const schemeSetting = new SchemeSetting( - new ColorShifting( - Number(formData.get('hover_chroma')) / 100, - Number(formData.get('hover_lightness')) / 100, - ), - new ColorShifting( - Number(formData.get('active_chroma')) / 100, - Number(formData.get('active_lightness')) / 100, - ), - new ColorShifting( - Number(formData.get('focus_chroma')) / 100, - Number(formData.get('focus_lightness')) / 100, - ), - new ColorShifting( - Number(formData.get('disabled_chroma')) / 100, - Number(formData.get('disabled_lightness')) / 100, - ), - new ColorShifting( - Number(formData.get('dark_chroma')) / 100, - Number(formData.get('dark_lightness')) / 100, - ), - Number(formData.get('expanding')) as ColorExpand, - Number(formData.get('wacg')) as WACGSetting, - ); - const dumpedSetting = schemeSetting.toJsValue() as QSchemeSetting; - - const source: QSchemeSource = { - primary: defaultEmptyFormData(formData, 'primary', null), - 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), - info: defaultEmptyFormData(formData, 'info', null), - foreground: defaultEmptyFormData(formData, 'foreground', null), - background: defaultEmptyFormData(formData, 'background', null), - setting: dumpedSetting, - }; + const source = collectSchemeSource(formData); const generatedScheme = every([source.secondary, source.tertiary, source.accent], isNil) ? colorFn?.generate_q_scheme_automatically( source.primary ?? '', @@ -352,10 +372,13 @@ export function QSchemeBuilder({ scheme, onBuildCompleted }: QSchemeBuilderProps defaultValue={defaultSetting?.wacg_follows} /> -
+
+