feat(方案构建器): 添加Q2方案构建器界面及功能
实现Q2方案构建器的完整界面,包括颜色选择、自定义颜色管理、自动化参数配置和方案设置 添加构建和保存草稿功能,支持生成完整的色彩方案 包含错误处理和表单验证逻辑
This commit is contained in:
parent
459b5ea1ab
commit
8a09806b8c
@ -5,6 +5,7 @@ import { SchemeContent } from '../../models';
|
||||
import { Q2SchemeStorage } from '../../q-2-scheme';
|
||||
import { isNilOrEmpty } from '../../utls';
|
||||
import { SchemeExport } from './Export';
|
||||
import { Q2SchemeBuilder } from './q-2-scheme/Builder';
|
||||
|
||||
const tabOptions = [
|
||||
{ title: 'Overview', id: 'overview' },
|
||||
@ -32,6 +33,9 @@ export function Q2Scheme({ scheme }: Q2SchemeProps) {
|
||||
export: isNilOrEmpty(scheme.schemeStorage?.cssVariables),
|
||||
}}
|
||||
/>
|
||||
{isEqual(activeTab, 'builder') && (
|
||||
<Q2SchemeBuilder scheme={scheme} onBuildCompleted={() => setActiveTab('overview')} />
|
||||
)}
|
||||
{isEqual(activeTab, 'export') && <SchemeExport scheme={scheme} />}
|
||||
</>
|
||||
);
|
||||
|
@ -0,0 +1,44 @@
|
||||
@layer pages {
|
||||
.builder_layout {
|
||||
padding: var(--spacing-s) var(--spacing-m);
|
||||
font-size: var(--font-size-s);
|
||||
line-height: 1.3em;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 200px);
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
.label {
|
||||
max-width: 200px;
|
||||
grid-column: 1;
|
||||
padding-inline-end: var(--spacing-m);
|
||||
text-align: right;
|
||||
}
|
||||
.color_picker_row {
|
||||
grid-column: 2 / span 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-s);
|
||||
.error_msg {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
}
|
||||
.segment_title {
|
||||
grid-column: 1 / span 2;
|
||||
text-align: center;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,17 @@
|
||||
import { ColorExpand, ColorShifting, SchemeSetting, WACGSetting } from 'color-module';
|
||||
import { includes, isEmpty, isNil } from 'lodash-es';
|
||||
import { useActionState, useCallback, useMemo, useState } 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';
|
||||
import { Q2SchemeStorage } from '../../../q-2-scheme';
|
||||
import { Q2SchemeSource, Q2SchemeStorage } from '../../../q-2-scheme';
|
||||
import { useUpdateScheme } from '../../../stores/schemes';
|
||||
import { isNilOrEmpty } from '../../../utls';
|
||||
import { ColorEntry, IdenticalColorEntry } from '../ColorEntry';
|
||||
import styles from './Builder.module.css';
|
||||
|
||||
type Q2SchemeBuilderProps = {
|
||||
scheme: SchemeContent<Q2SchemeStorage>;
|
||||
@ -8,5 +19,505 @@ type Q2SchemeBuilderProps = {
|
||||
};
|
||||
|
||||
export function Q2SchemeBuilder({ scheme, onBuildCompleted }: Q2SchemeBuilderProps) {
|
||||
return <ScrollArea enableY></ScrollArea>;
|
||||
const { showToast } = useNotification();
|
||||
const { colorFn } = useColorFunction();
|
||||
const updateScheme = useUpdateScheme(scheme.id);
|
||||
|
||||
// Load scheme setting and scheme default setting.
|
||||
const defaultSetting = useMemo(() => {
|
||||
try {
|
||||
if (!colorFn) throw 'Web Assembly functions is not available';
|
||||
const defaultValues = colorFn.q_scheme_default_settings();
|
||||
if (scheme.schemeStorage.source?.setting) {
|
||||
return new SchemeSetting(
|
||||
new ColorShifting(
|
||||
scheme.schemeStorage.source?.setting.hover.chroma ?? defaultValues.hover.chroma,
|
||||
scheme.schemeStorage.source?.setting.hover.lightness ?? defaultValues.hover.lightness,
|
||||
),
|
||||
new ColorShifting(
|
||||
scheme.schemeStorage.source?.setting?.active.chroma ?? defaultValues.active.chroma,
|
||||
scheme.schemeStorage.source?.setting?.active.lightness ??
|
||||
defaultValues.active.lightness,
|
||||
),
|
||||
new ColorShifting(
|
||||
scheme.schemeStorage.source?.setting?.focus.chroma ?? defaultValues.focus.chroma,
|
||||
scheme.schemeStorage.source?.setting?.focus.lightness ?? defaultValues.focus.lightness,
|
||||
),
|
||||
new ColorShifting(
|
||||
scheme.schemeStorage.source?.setting?.disabled.chroma ?? defaultValues.disabled.chroma,
|
||||
scheme.schemeStorage.source?.setting?.disabled.lightness ??
|
||||
defaultValues.disabled.lightness,
|
||||
),
|
||||
new ColorShifting(
|
||||
scheme.schemeStorage.source?.setting?.dark_convert.chroma ??
|
||||
defaultValues.dark_convert.chroma,
|
||||
scheme.schemeStorage.source?.setting?.dark_convert.lightness ??
|
||||
defaultValues.dark_convert.lightness,
|
||||
),
|
||||
scheme.schemeStorage.source?.setting?.expand_method ?? defaultValues.expand_method,
|
||||
scheme.schemeStorage.source?.setting?.wacg_follows ?? defaultValues.wacg_follows,
|
||||
);
|
||||
}
|
||||
return defaultValues;
|
||||
} catch (e) {
|
||||
console.error('[Q2 Scheme builder]', e);
|
||||
}
|
||||
}, [scheme]);
|
||||
|
||||
// Collect choices in color scheme settings
|
||||
const expandingMethods = useMemo(() => {
|
||||
try {
|
||||
if (!colorFn) throw 'Web Assembly functions is not available';
|
||||
return colorFn.q_scheme_color_expanding_methods();
|
||||
} catch (e) {
|
||||
console.error('[Q scheme builder]', e);
|
||||
}
|
||||
return [];
|
||||
}, []);
|
||||
const wacgFollowStrategies = useMemo(() => {
|
||||
try {
|
||||
if (!colorFn) throw 'Web Assembly functions is not available';
|
||||
return colorFn.q_scheme_wacg_settings();
|
||||
} catch (e) {
|
||||
console.error('[Q scheme builder]', e);
|
||||
}
|
||||
return [];
|
||||
}, []);
|
||||
|
||||
// Custom Colors processing
|
||||
const originalColors = useMemo(() => {
|
||||
return Object.entries(scheme.schemeStorage.source?.custom_colors ?? {}).map(
|
||||
([name, color], index) => ({ id: `oc_${index}`, name, color } as IdenticalColorEntry),
|
||||
);
|
||||
}, [scheme.schemeStorage.source]);
|
||||
const [newColors, setNewColors] = useState<IdenticalColorEntry[]>([]);
|
||||
const [deleted, setDeleted] = useState<string[]>([]);
|
||||
const addEntryAction = useCallback(() => {
|
||||
setNewColors((prev) => [...prev, { id: `nc_${prev.length}`, name: '', color: '' }]);
|
||||
}, []);
|
||||
const colorKeys = useMemo(
|
||||
() =>
|
||||
[...originalColors, ...newColors]
|
||||
.map((color) => color.id)
|
||||
.filter((c) => !includes(deleted, c)),
|
||||
[originalColors, newColors, deleted],
|
||||
);
|
||||
|
||||
// Collect scheme source
|
||||
const collectSchemeSource = (formData: FormData): [Q2SchemeSource, QSchemeSetting] => {
|
||||
const primaryColor = formData.get('primary')?.toString();
|
||||
const secondaryColor = formData.get('secondary')?.toString();
|
||||
const tertiaryColor = formData.get('tertiary')?.toString();
|
||||
const accentColor = formData.get('accent')?.toString();
|
||||
const dangerColor = formData.get('danger')?.toString();
|
||||
const successColor = formData.get('success')?.toString();
|
||||
const warnColor = formData.get('warn')?.toString();
|
||||
const infoColor = formData.get('info')?.toString();
|
||||
const foregroundColor = formData.get('foreground')?.toString();
|
||||
const backgroundColor = formData.get('background')?.toString();
|
||||
|
||||
const customColors: Record<string, string> = {};
|
||||
for (const key of colorKeys) {
|
||||
const name = formData.get(`name_${key}`)?.toString();
|
||||
const color = formData.get(`color_${key}`)?.toString();
|
||||
if (isNil(name) || isEmpty(name) || isNil(color) || isEmpty(color)) continue;
|
||||
customColors[name] = color;
|
||||
}
|
||||
|
||||
// collect scheme settings
|
||||
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: primaryColor,
|
||||
secondary: secondaryColor,
|
||||
tertiary: tertiaryColor,
|
||||
accent: accentColor,
|
||||
danger: dangerColor,
|
||||
success: successColor,
|
||||
warn: warnColor,
|
||||
info: infoColor,
|
||||
foreground: foregroundColor,
|
||||
background: backgroundColor,
|
||||
custom_colors: customColors,
|
||||
setting: dumpedSetting,
|
||||
} as Q2SchemeSource,
|
||||
schemeSetting,
|
||||
];
|
||||
};
|
||||
|
||||
// Scheme save actions
|
||||
const handleDraftAction = (formData: FormData) => {
|
||||
const [source] = collectSchemeSource(formData);
|
||||
updateScheme((prev) => {
|
||||
prev.schemeStorage.source = source;
|
||||
return prev;
|
||||
});
|
||||
showToast(NotificationType.SUCCESS, 'Scheme draft saved!', 'tabler:device-floppy', 3000);
|
||||
};
|
||||
const [errMsg, handleSubmitAction] = useActionState<Map<string, string>, FormData>(
|
||||
(_state, formData) => {
|
||||
const errMsg = new Map<string, string>();
|
||||
|
||||
// Check required color
|
||||
const requiredFields = [
|
||||
'primary',
|
||||
'danger',
|
||||
'success',
|
||||
'warn',
|
||||
'info',
|
||||
'foreground',
|
||||
'background',
|
||||
];
|
||||
for (const field of requiredFields) {
|
||||
if (!formData.get(field)) {
|
||||
errMsg.set(field, 'This color is required for scheme generating.');
|
||||
}
|
||||
}
|
||||
if (!isEmpty(errMsg)) return errMsg;
|
||||
|
||||
try {
|
||||
const [source, settings] = collectSchemeSource(formData);
|
||||
console.log('[Collected form data]', source, settings);
|
||||
const generatedScheme = colorFn?.generate_q_scheme_2_manually(
|
||||
source.primary ?? '',
|
||||
isEmpty(source.secondary) ? undefined : source.secondary,
|
||||
isEmpty(source.tertiary) ? undefined : source.tertiary,
|
||||
isEmpty(source.accent) ? undefined : source.accent,
|
||||
source.danger ?? '',
|
||||
source.success ?? '',
|
||||
source.warn ?? '',
|
||||
source.info ?? '',
|
||||
source.foreground ?? '',
|
||||
source.background ?? '',
|
||||
source.custom_colors,
|
||||
settings,
|
||||
);
|
||||
console.log('[Generated scheme]', generatedScheme);
|
||||
updateScheme((prev) => {
|
||||
prev.schemeStorage.source = source;
|
||||
prev.schemeStorage.scheme = generatedScheme[0];
|
||||
prev.schemeStorage.cssVariables = generatedScheme[1];
|
||||
prev.schemeStorage.cssAutoSchemeVariables = generatedScheme[2];
|
||||
prev.schemeStorage.scssVariables = generatedScheme[3];
|
||||
prev.schemeStorage.jsVariables = generatedScheme[4];
|
||||
return prev;
|
||||
});
|
||||
onBuildCompleted?.();
|
||||
} catch (e) {
|
||||
console.error('[build q2 scheme]', e);
|
||||
}
|
||||
|
||||
return errMsg;
|
||||
},
|
||||
new Map<string, string>(),
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollArea enableY>
|
||||
<form action={handleSubmitAction} className={styles.builder_layout}>
|
||||
<h5 className={styles.segment_title}>Original Colors</h5>
|
||||
<label className={styles.label}>Primary Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="primary"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.primary)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.primary
|
||||
}
|
||||
/>
|
||||
{errMsg.has('primary') && (
|
||||
<span className={styles.error_msg}>{errMsg.get('primary')}</span>
|
||||
)}
|
||||
</div>
|
||||
<label className={styles.label}>Secondary Color</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="secondary"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.secondary)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.secondary
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<label className={styles.label}>Tertiary Color</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="tertiary"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.tertiary)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.tertiary
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<label className={styles.label}>Accent Color</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="accent"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.accent)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.accent
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<label className={styles.label}>Danger Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="danger"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.danger)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.danger
|
||||
}
|
||||
/>
|
||||
{errMsg.has('danger') && <span className={styles.error_msg}>{errMsg.get('danger')}</span>}
|
||||
</div>
|
||||
<label className={styles.label}>Success Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="success"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.success)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.success
|
||||
}
|
||||
/>
|
||||
{errMsg.has('success') && (
|
||||
<span className={styles.error_msg}>{errMsg.get('success')}</span>
|
||||
)}
|
||||
</div>
|
||||
<label className={styles.label}>Warning Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="warn"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.warn)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.warn
|
||||
}
|
||||
/>
|
||||
{errMsg.has('warn') && <span className={styles.error_msg}>{errMsg.get('warn')}</span>}
|
||||
</div>
|
||||
<label className={styles.label}>Info Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="info"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.info)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.info
|
||||
}
|
||||
/>
|
||||
{errMsg.has('info') && <span className={styles.error_msg}>{errMsg.get('info')}</span>}
|
||||
</div>
|
||||
<label className={styles.label}>Foreground Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="foreground"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.foreground)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.foreground
|
||||
}
|
||||
/>
|
||||
{errMsg.has('foreground') && (
|
||||
<span className={styles.error_msg}>{errMsg.get('foreground')}</span>
|
||||
)}
|
||||
</div>
|
||||
<label className={styles.label}>Background Color*</label>
|
||||
<div className={styles.color_picker_row}>
|
||||
<FloatColorPicker
|
||||
name="background"
|
||||
color={
|
||||
isNilOrEmpty(scheme.schemeStorage.source?.background)
|
||||
? undefined
|
||||
: scheme.schemeStorage.source?.background
|
||||
}
|
||||
/>
|
||||
{errMsg.has('background') && (
|
||||
<span className={styles.error_msg}>{errMsg.get('background')}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h5 className={styles.segment_title}>Custom Colors</h5>
|
||||
<label style={{ gridColumn: 1 }}>Name</label>
|
||||
<label>Color</label>
|
||||
<div>
|
||||
<button type="button" className="small" onClick={addEntryAction}>
|
||||
Add Color
|
||||
</button>
|
||||
</div>
|
||||
{originalColors
|
||||
.filter((color) => !includes(deleted, color.id))
|
||||
.map((color) => (
|
||||
<ColorEntry
|
||||
key={color.id}
|
||||
entry={color}
|
||||
onDelete={(index) => setDeleted((prev) => [...prev, index])}
|
||||
/>
|
||||
))}
|
||||
{newColors
|
||||
.filter((color) => !includes(deleted, color.id))
|
||||
.map((color) => (
|
||||
<ColorEntry
|
||||
key={color.id}
|
||||
entry={color}
|
||||
onDelete={(index) => setDeleted((prev) => [...prev, index])}
|
||||
/>
|
||||
))}
|
||||
|
||||
<h5 className={styles.segment_title}>Automated parameters</h5>
|
||||
<label style={{ gridColumn: 2 }}>Chroma shifting</label>
|
||||
<label style={{ gridColumn: 3 }}>Lightness shifting</label>
|
||||
<label className={styles.label}>Hover</label>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="hover_chroma"
|
||||
defaultValue={((defaultSetting?.hover.chroma ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="hover_lightness"
|
||||
defaultValue={((defaultSetting?.hover.lightness ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<label className={styles.label}>Active</label>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="active_chroma"
|
||||
defaultValue={((defaultSetting?.active.chroma ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="active_lightness"
|
||||
defaultValue={((defaultSetting?.active.lightness ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<label className={styles.label}>Focus</label>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="focus_chroma"
|
||||
defaultValue={((defaultSetting?.focus.chroma ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="focus_lightness"
|
||||
defaultValue={((defaultSetting?.focus.lightness ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<label className={styles.label}>Disabled</label>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="disabled_chroma"
|
||||
defaultValue={((defaultSetting?.disabled.chroma ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="disabled_lightness"
|
||||
defaultValue={((defaultSetting?.disabled.lightness ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<label className={styles.label}>Convert to Dark scheme</label>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="dark_chroma"
|
||||
defaultValue={((defaultSetting?.dark_convert.chroma ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<div className="input_wrapper">
|
||||
<input
|
||||
type="number"
|
||||
name="dark_lightness"
|
||||
defaultValue={((defaultSetting?.dark_convert.lightness ?? 0) * 100).toFixed(2)}
|
||||
className={styles.parameter_input}
|
||||
/>
|
||||
<span>%</span>
|
||||
</div>
|
||||
<h5 className={styles.segment_title}>Settings</h5>
|
||||
<label className={styles.label}>Color Expanding Method</label>
|
||||
<div style={{ gridColumn: '2 / span 2' }}>
|
||||
<VSegmentedControl
|
||||
options={expandingMethods}
|
||||
name="expanding"
|
||||
defaultValue={defaultSetting?.expand_method}
|
||||
/>
|
||||
</div>
|
||||
<label className={styles.label}>Follow WACG Standard</label>
|
||||
<div style={{ gridColumn: '2 / span 2' }}>
|
||||
<VSegmentedControl
|
||||
options={wacgFollowStrategies}
|
||||
name="wacg"
|
||||
defaultValue={defaultSetting?.wacg_follows}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.button_row} style={{ gridColumn: '2 / span 2' }}>
|
||||
<button type="submit" className="primary">
|
||||
Build Scheme
|
||||
</button>
|
||||
<button type="submit" className="secondary" formAction={handleDraftAction}>
|
||||
Save Draft
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user