Skip to content

Commit acd4353

Browse files
feat: add general tab & header on general settings (#3975)
1 parent 9393149 commit acd4353

5 files changed

Lines changed: 181 additions & 51 deletions

File tree

src/component/context/LoggerContext.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ interface LoggerProviderProps {
3535
children: ReactNode;
3636
}
3737

38+
export type LoggerType =
39+
| 'fatal'
40+
| 'error'
41+
| 'warn'
42+
| 'info'
43+
| 'debug'
44+
| 'trace'
45+
| 'silent';
46+
3847
export const LOGGER_LEVELS = {
3948
fatal: 60,
4049
error: 50,

src/component/modal/setting/tanstack_general_settings/general_settings.tsx

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Dialog as BPDialog } from '@blueprintjs/core';
22
import styled from '@emotion/styled';
33
import { revalidateLogic } from '@tanstack/react-form';
4+
import type { Workspace } from '@zakodium/nmrium-core';
45
import { Form, useForm } from 'react-science/ui';
56
import type { z } from 'zod/v4';
67

@@ -28,50 +29,38 @@ export type GeneralSettingsFormType = z.input<typeof workspaceValidation>;
2829
export function GeneralSettings(props: GeneralSettingsProps) {
2930
const { isOpen, close, height } = props;
3031

31-
const { current: currentWorkspace } = usePreferences();
32+
const { current: currentWorkspace, dispatch } = usePreferences();
3233
const { saveSettings } = useSaveSettings();
3334

34-
const defaultValues: z.input<typeof workspaceValidation> = {
35-
general: {
36-
dimmedSpectraOpacity: currentWorkspace.general.dimmedSpectraOpacity,
37-
invertScroll: currentWorkspace.general.invertScroll,
38-
invertActions: currentWorkspace.general.invert,
39-
experimentalFeatures:
40-
currentWorkspace.display.general?.experimentalFeatures?.display ||
41-
false,
42-
},
43-
};
44-
4535
const form = useForm({
46-
validators: { onDynamic: workspaceValidation },
36+
validators: {
37+
onDynamic: workspaceValidation,
38+
},
4739
validationLogic: revalidateLogic({ mode: 'change' }),
48-
defaultValues,
40+
defaultValues: currentWorkspace as GeneralSettingsFormType,
4941
onSubmit: ({ value }) => {
50-
const parsedValues = workspaceValidation.parse(value);
42+
const safeParseResult = workspaceValidation.safeParse(value);
43+
44+
if (!safeParseResult.success) {
45+
throw new Error('Failed to parse workspace validation');
46+
}
5147

52-
saveSettings({
53-
display: {
54-
general: {
55-
experimentalFeatures: {
56-
display: parsedValues.general.experimentalFeatures,
57-
visible: true,
58-
},
59-
},
60-
},
61-
general: {
62-
invert: parsedValues.general.invertActions,
63-
invertScroll: parsedValues.general.invertScroll,
64-
dimmedSpectraOpacity: parsedValues.general.dimmedSpectraOpacity,
65-
spectraRendering: 'auto',
66-
verticalSplitterCloseThreshold: 0,
67-
verticalSplitterPosition: '1px',
68-
loggingLevel: 'info',
69-
popupLoggingLevel: 'info',
70-
},
71-
});
48+
saveSettings(value as Partial<Workspace>);
49+
close();
7250
},
7351
});
7452

53+
function onApply(values: GeneralSettingsFormType) {
54+
dispatch({
55+
type: 'APPLY_General_PREFERENCES',
56+
payload: {
57+
data: values as Omit<Workspace, 'label' | 'version'>,
58+
},
59+
});
60+
61+
close();
62+
}
63+
7564
return (
7665
<Dialog isOpen={isOpen} onClose={close} title="General settings" icon="cog">
7766
<Form
@@ -82,13 +71,21 @@ export function GeneralSettings(props: GeneralSettingsProps) {
8271
void form.handleSubmit();
8372
}}
8473
>
85-
<GeneralSettingsDialogHeader<GeneralSettingsFormType>
86-
reset={form.reset}
87-
currentValues={form.state.values}
88-
/>
74+
<form.Subscribe selector={(state) => state.values}>
75+
{(values) => (
76+
<GeneralSettingsDialogHeader
77+
reset={form.reset}
78+
currentValues={values}
79+
/>
80+
)}
81+
</form.Subscribe>
8982

9083
<GeneralSettingsDialogBody form={form} height={height} />
91-
<GeneralSettingsDialogFooter form={form} />
84+
<GeneralSettingsDialogFooter
85+
form={form}
86+
onCancel={close}
87+
onApply={onApply}
88+
/>
9289
</Form>
9390
</Dialog>
9491
);

src/component/modal/setting/tanstack_general_settings/general_settings_dialog_footer.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,45 @@
11
import { DialogFooter } from '@blueprintjs/core';
22
import styled from '@emotion/styled';
3-
import { withForm } from 'react-science/ui';
3+
import { Button, withForm } from 'react-science/ui';
44

5+
import type { GeneralSettingsFormType } from './general_settings.tsx';
56
import { defaultGeneralSettingsFormValues } from './validation.ts';
67

78
const Footer = styled.div`
89
display: flex;
9-
justify-content: flex-start;
10+
justify-content: flex-end;
11+
gap: 10px;
1012
`;
1113

1214
export const GeneralSettingsDialogFooter = withForm({
15+
props: {
16+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
17+
onApply: (values: GeneralSettingsFormType) => {
18+
/* empty */
19+
},
20+
onCancel: () => {
21+
/* empty */
22+
},
23+
},
1324
defaultValues: defaultGeneralSettingsFormValues,
14-
render: ({ form }) => {
25+
render: ({ form, onCancel, onApply }) => {
1526
return (
1627
<form.AppForm>
1728
<DialogFooter>
1829
<Footer>
19-
<form.SubmitButton>Save</form.SubmitButton>
30+
<Button variant="outlined" intent="danger" onClick={onCancel}>
31+
Cancel
32+
</Button>
33+
<form.SubmitButton intent="success">
34+
Apply and Save
35+
</form.SubmitButton>
36+
<form.Subscribe selector={(state) => state.values}>
37+
{(values) => (
38+
<Button intent="primary" onClick={() => onApply(values)}>
39+
Apply
40+
</Button>
41+
)}
42+
</form.Subscribe>
2043
</Footer>
2144
</DialogFooter>
2245
</form.AppForm>

src/component/modal/setting/tanstack_general_settings/tabs/general_tab.tsx

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
1+
import { Tag } from '@blueprintjs/core';
12
import { withForm } from 'react-science/ui';
23

4+
import { LOGGER_LEVELS } from '../../../../context/LoggerContext.tsx';
5+
import type { SelectDefaultItem } from '../../../../elements/Select2.tsx';
36
import { defaultGeneralSettingsFormValues } from '../validation.ts';
47

8+
const SHAPE_RENDERING: SelectDefaultItem[] = [
9+
{
10+
label: 'Auto',
11+
value: 'auto',
12+
},
13+
{
14+
label: 'Optimize speed',
15+
value: 'optimizeSpeed',
16+
},
17+
{
18+
label: 'Crisp edges',
19+
value: 'crispEdges',
20+
},
21+
{
22+
label: 'Geometric precision',
23+
value: 'geometricPrecision',
24+
},
25+
];
26+
27+
const LOGS_LEVELS = Object.keys(LOGGER_LEVELS).map((level) => ({
28+
label: level.replace(/^\w/, (c) => c.toUpperCase()),
29+
value: level,
30+
}));
31+
532
export const GeneralTab = withForm({
633
defaultValues: defaultGeneralSettingsFormValues,
734
render: ({ form }) => {
@@ -19,19 +46,48 @@ export const GeneralTab = withForm({
1946
/>
2047
)}
2148
</form.AppField>
22-
<form.AppField name="general.invertActions">
49+
<form.AppField name="general.invert">
2350
{(field) => <field.Switch label="Invert actions" />}
2451
</form.AppField>
2552
<form.AppField name="general.invertScroll">
2653
{(field) => <field.Switch label="Invert scroll" />}
2754
</form.AppField>
2855
</form.Section>
29-
3056
<form.Section title="Experimental features">
31-
<form.AppField name="general.experimentalFeatures">
57+
<form.AppField name="display.general.experimentalFeatures.display">
3258
{(field) => <field.Switch label="Enable experimental features" />}
3359
</form.AppField>
3460
</form.Section>
61+
<form.Section title="Rendering">
62+
<form.AppField name="general.spectraRendering">
63+
{(field) => (
64+
<field.Select label="Spectra rendering" items={SHAPE_RENDERING} />
65+
)}
66+
</form.AppField>
67+
</form.Section>
68+
<form.Section title="Logging settings">
69+
<form.AppField name="general.loggingLevel">
70+
{(field) => <field.Select label="Level" items={LOGS_LEVELS} />}
71+
</form.AppField>
72+
<form.AppField name="general.popupLoggingLevel">
73+
{(field) => (
74+
<field.Select label="Popup logging level" items={LOGS_LEVELS} />
75+
)}
76+
</form.AppField>
77+
</form.Section>
78+
<form.Section title="Peaks label">
79+
<form.AppField name="peaksLabel.marginTop">
80+
{(field) => (
81+
<field.NumericInput
82+
label="Margin top"
83+
min={0}
84+
max={1}
85+
stepSize={0.1}
86+
rightElement={<Tag>px</Tag>}
87+
/>
88+
)}
89+
</form.AppField>
90+
</form.Section>
3591
</>
3692
);
3793
},
Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,69 @@
11
import { z } from 'zod/v4';
22

3+
import type { LoggerType } from '../../../context/LoggerContext.tsx';
4+
5+
const loggingLevel: LoggerType[] = [
6+
'fatal',
7+
'error',
8+
'warn',
9+
'info',
10+
'debug',
11+
'trace',
12+
'silent',
13+
];
14+
15+
const peaksLabelValidation = z.object({
16+
marginTop: z.coerce.number().int().min(0),
17+
});
18+
319
const generalValidation = z.object({
420
dimmedSpectraOpacity: z.coerce.number().min(0).max(1),
5-
invertActions: z.boolean(),
21+
invert: z.boolean(),
622
invertScroll: z.boolean(),
7-
experimentalFeatures: z.boolean(),
23+
spectraRendering: z.enum([
24+
'auto',
25+
'optimizeSpeed',
26+
'crispEdges',
27+
'geometricPrecision',
28+
]),
29+
popupLoggingLevel: z.enum(loggingLevel).optional(),
30+
loggingLevel: z.enum(loggingLevel).optional(),
31+
});
32+
33+
const displayValidation = z.object({
34+
general: z.object({
35+
experimentalFeatures: z.object({
36+
display: z.boolean(),
37+
}),
38+
}),
839
});
940

1041
export const workspaceValidation = z.object({
42+
peaksLabel: peaksLabelValidation,
1143
general: generalValidation,
44+
display: displayValidation,
1245
});
1346

1447
// This object is used to define type not real values. Do not use it as values
1548
export const defaultGeneralSettingsFormValues: z.input<
1649
typeof workspaceValidation
1750
> = {
51+
peaksLabel: {
52+
marginTop: 0,
53+
},
1854
general: {
1955
dimmedSpectraOpacity: 0,
20-
invertActions: false,
56+
invert: false,
2157
invertScroll: false,
22-
experimentalFeatures: false,
58+
spectraRendering: 'auto',
59+
loggingLevel: 'info',
60+
popupLoggingLevel: 'info',
61+
},
62+
display: {
63+
general: {
64+
experimentalFeatures: {
65+
display: false,
66+
},
67+
},
2368
},
2469
};

0 commit comments

Comments
 (0)