Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/component/context/LoggerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ interface LoggerProviderProps {
children: ReactNode;
}

export type LoggerType =
| 'fatal'
| 'error'
| 'warn'
| 'info'
| 'debug'
| 'trace'
| 'silent';

export const LOGGER_LEVELS = {
fatal: 60,
error: 50,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Dialog as BPDialog } from '@blueprintjs/core';
import styled from '@emotion/styled';
import { revalidateLogic } from '@tanstack/react-form';
import type { Workspace } from '@zakodium/nmrium-core';
import { Form, useForm } from 'react-science/ui';
import type { z } from 'zod/v4';

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

const { current: currentWorkspace } = usePreferences();
const { current: currentWorkspace, dispatch } = usePreferences();
const { saveSettings } = useSaveSettings();

const defaultValues: z.input<typeof workspaceValidation> = {
general: {
dimmedSpectraOpacity: currentWorkspace.general.dimmedSpectraOpacity,
invertScroll: currentWorkspace.general.invertScroll,
invertActions: currentWorkspace.general.invert,
experimentalFeatures:
currentWorkspace.display.general?.experimentalFeatures?.display ||
false,
},
};

const form = useForm({
validators: { onDynamic: workspaceValidation },
validators: {
onDynamic: workspaceValidation,
},
validationLogic: revalidateLogic({ mode: 'change' }),
defaultValues,
defaultValues: currentWorkspace as GeneralSettingsFormType,
onSubmit: ({ value }) => {
const parsedValues = workspaceValidation.parse(value);
const safeParseResult = workspaceValidation.safeParse(value);

if (!safeParseResult.success) {
throw new Error('Failed to parse workspace validation');
}

saveSettings({
display: {
general: {
experimentalFeatures: {
display: parsedValues.general.experimentalFeatures,
visible: true,
},
},
},
general: {
invert: parsedValues.general.invertActions,
invertScroll: parsedValues.general.invertScroll,
dimmedSpectraOpacity: parsedValues.general.dimmedSpectraOpacity,
spectraRendering: 'auto',
verticalSplitterCloseThreshold: 0,
verticalSplitterPosition: '1px',
loggingLevel: 'info',
popupLoggingLevel: 'info',
},
});
saveSettings(value as Partial<Workspace>);
close();
},
});

function onApply(values: GeneralSettingsFormType) {
dispatch({
type: 'APPLY_General_PREFERENCES',
payload: {
data: values as Omit<Workspace, 'label' | 'version'>,
},
});

close();
}

return (
<Dialog isOpen={isOpen} onClose={close} title="General settings" icon="cog">
<Form
Expand All @@ -82,13 +71,21 @@ export function GeneralSettings(props: GeneralSettingsProps) {
void form.handleSubmit();
}}
>
<GeneralSettingsDialogHeader<GeneralSettingsFormType>
reset={form.reset}
currentValues={form.state.values}
/>
<form.Subscribe selector={(state) => state.values}>
{(values) => (
<GeneralSettingsDialogHeader
reset={form.reset}
currentValues={values}
/>
)}
</form.Subscribe>

<GeneralSettingsDialogBody form={form} height={height} />
<GeneralSettingsDialogFooter form={form} />
<GeneralSettingsDialogFooter
form={form}
onCancel={close}
onApply={onApply}
/>
</Form>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import { DialogFooter } from '@blueprintjs/core';
import styled from '@emotion/styled';
import { withForm } from 'react-science/ui';
import { Button, withForm } from 'react-science/ui';

import type { GeneralSettingsFormType } from './general_settings.tsx';
import { defaultGeneralSettingsFormValues } from './validation.ts';

const Footer = styled.div`
display: flex;
justify-content: flex-start;
justify-content: flex-end;
gap: 10px;
`;

export const GeneralSettingsDialogFooter = withForm({
props: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onApply: (values: GeneralSettingsFormType) => {
/* empty */
},
onCancel: () => {
/* empty */
},
},
defaultValues: defaultGeneralSettingsFormValues,
render: ({ form }) => {
render: ({ form, onCancel, onApply }) => {
return (
<form.AppForm>
<DialogFooter>
<Footer>
<form.SubmitButton>Save</form.SubmitButton>
<Button variant="outlined" intent="danger" onClick={onCancel}>
Cancel
</Button>
<form.SubmitButton intent="success">
Apply and Save
</form.SubmitButton>
<form.Subscribe selector={(state) => state.values}>
{(values) => (
<Button intent="primary" onClick={() => onApply(values)}>
Apply
</Button>
)}
</form.Subscribe>
</Footer>
</DialogFooter>
</form.AppForm>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import { Tag } from '@blueprintjs/core';
import { withForm } from 'react-science/ui';

import { LOGGER_LEVELS } from '../../../../context/LoggerContext.tsx';
import type { SelectDefaultItem } from '../../../../elements/Select2.tsx';
import { defaultGeneralSettingsFormValues } from '../validation.ts';

const SHAPE_RENDERING: SelectDefaultItem[] = [
{
label: 'Auto',
value: 'auto',
},
{
label: 'Optimize speed',
value: 'optimizeSpeed',
},
{
label: 'Crisp edges',
value: 'crispEdges',
},
{
label: 'Geometric precision',
value: 'geometricPrecision',
},
];

const LOGS_LEVELS = Object.keys(LOGGER_LEVELS).map((level) => ({
label: level.replace(/^\w/, (c) => c.toUpperCase()),
value: level,
}));

export const GeneralTab = withForm({
defaultValues: defaultGeneralSettingsFormValues,
render: ({ form }) => {
Expand All @@ -19,19 +46,48 @@ export const GeneralTab = withForm({
/>
)}
</form.AppField>
<form.AppField name="general.invertActions">
<form.AppField name="general.invert">
{(field) => <field.Switch label="Invert actions" />}
</form.AppField>
<form.AppField name="general.invertScroll">
{(field) => <field.Switch label="Invert scroll" />}
</form.AppField>
</form.Section>

<form.Section title="Experimental features">
<form.AppField name="general.experimentalFeatures">
<form.AppField name="display.general.experimentalFeatures.display">
{(field) => <field.Switch label="Enable experimental features" />}
</form.AppField>
</form.Section>
<form.Section title="Rendering">
<form.AppField name="general.spectraRendering">
{(field) => (
<field.Select label="Spectra rendering" items={SHAPE_RENDERING} />
)}
</form.AppField>
</form.Section>
<form.Section title="Logging settings">
<form.AppField name="general.loggingLevel">
{(field) => <field.Select label="Level" items={LOGS_LEVELS} />}
</form.AppField>
<form.AppField name="general.popupLoggingLevel">
{(field) => (
<field.Select label="Popup logging level" items={LOGS_LEVELS} />
)}
</form.AppField>
</form.Section>
<form.Section title="Peaks label">
<form.AppField name="peaksLabel.marginTop">
{(field) => (
<field.NumericInput
label="Margin top"
min={0}
max={1}
stepSize={0.1}
rightElement={<Tag>px</Tag>}
/>
)}
</form.AppField>
</form.Section>
</>
);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,69 @@
import { z } from 'zod/v4';

import type { LoggerType } from '../../../context/LoggerContext.tsx';

const loggingLevel: LoggerType[] = [
'fatal',
'error',
'warn',
'info',
'debug',
'trace',
'silent',
];

const peaksLabelValidation = z.object({
marginTop: z.coerce.number().int().min(0),
});

const generalValidation = z.object({
dimmedSpectraOpacity: z.coerce.number().min(0).max(1),
invertActions: z.boolean(),
invert: z.boolean(),
invertScroll: z.boolean(),
experimentalFeatures: z.boolean(),
spectraRendering: z.enum([
'auto',
'optimizeSpeed',
'crispEdges',
'geometricPrecision',
]),
popupLoggingLevel: z.enum(loggingLevel).optional(),
loggingLevel: z.enum(loggingLevel).optional(),
});

const displayValidation = z.object({
general: z.object({
experimentalFeatures: z.object({
display: z.boolean(),
}),
}),
});

export const workspaceValidation = z.object({
peaksLabel: peaksLabelValidation,
general: generalValidation,
display: displayValidation,
});

// This object is used to define type not real values. Do not use it as values
export const defaultGeneralSettingsFormValues: z.input<
typeof workspaceValidation
> = {
peaksLabel: {
marginTop: 0,
},
general: {
dimmedSpectraOpacity: 0,
invertActions: false,
invert: false,
invertScroll: false,
experimentalFeatures: false,
spectraRendering: 'auto',
loggingLevel: 'info',
popupLoggingLevel: 'info',
},
display: {
general: {
experimentalFeatures: {
display: false,
},
},
},
};
Loading