Skip to content

Commit

Permalink
feat: set custom workspaces at the level of the NMRium component (#1956)
Browse files Browse the repository at this point in the history
feat: set custom workspaces at the level of the NMRium component

close #1936

* chore: update demo to take a custom workspaces

* feat: set the custom workspaces at the level of the component

* chore: update demo to take a custom workspaces

* demo: Metabo workspace
  • Loading branch information
hamed-musallam committed Nov 29, 2022
1 parent 93158dd commit f3b3c65
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 32 deletions.
9 changes: 7 additions & 2 deletions src/component/NMRium.tsx
Expand Up @@ -68,6 +68,7 @@ import {
} from './reducer/types/Types';
import ToolBar from './toolbar/ToolBar';
import { BlobObject, getBlob } from './utility/export';
import { CustomWorkspaces } from './workspaces/Workspace';

const viewerContainerStyle = css`
border: 0.55px #e6e6e6 solid;
Expand Down Expand Up @@ -128,13 +129,15 @@ export type NMRiumWorkspace =
| 'default'
| 'prediction'
| 'embedded'
| 'assignment';
| 'assignment'
| `custom-${string}`;

export interface NMRiumProps {
data?: NMRiumData;
onDataChange?: (data: NMRiumDataReturn) => void;
onViewChange?: (view: ViewState) => void;
workspace?: NMRiumWorkspace;
customWorkspaces?: CustomWorkspaces;
preferences?: NMRiumPreferences;
emptyText?: ReactNode;
/**
Expand Down Expand Up @@ -184,6 +187,7 @@ const NMRium = forwardRef<NMRiumRef, NMRiumProps>(function NMRium(props, ref) {
function InnerNMRium({
data: dataProp = defaultData,
workspace,
customWorkspaces,
preferences = defaultPreferences,
getSpinner = defaultGetSpinner,
onDataChange,
Expand Down Expand Up @@ -248,10 +252,11 @@ function InnerNMRium({
payload: {
display: preferences,
workspace,
customWorkspaces,
dispatch: dispatchPreferences,
},
});
}, [preferences, workspace]);
}, [customWorkspaces, preferences, workspace]);

useImperativeHandle(
innerRef,
Expand Down
16 changes: 7 additions & 9 deletions src/component/context/PreferencesContext.tsx
Expand Up @@ -3,9 +3,7 @@ import { createContext, useContext, useMemo } from 'react';
import {
preferencesInitialState,
PreferencesState,
WORKSPACES,
} from '../reducer/preferences/preferencesReducer';
import Workspaces from '../workspaces';

export const PreferencesContext = createContext<PreferencesState>(
preferencesInitialState,
Expand All @@ -18,25 +16,25 @@ export function usePreferences() {
throw new Error('Preferences context was not found');
}

const { workspace, workspaces, dispatch } = context;
const { workspace, workspaces, customWorkspaces, dispatch } = context;

return useMemo(() => {
return {
current: workspaces[workspace.current] || {},
workspace,
workspaces,
customWorkspaces,
dispatch,
};
}, [dispatch, workspace, workspaces]);
}, [customWorkspaces, dispatch, workspace, workspaces]);
}

export function useWorkspacesList() {
const { workspaces } = usePreferences();
return useMemo(() => {
const currentWorkspaces = Object.keys(workspaces)
.filter((k) => !Workspaces[k])
.map((key) => ({ key, label: workspaces[key].label }));

return [...WORKSPACES, ...currentWorkspaces];
return Object.keys(workspaces).map((key) => ({
key,
label: workspaces[key].label,
}));
}, [workspaces]);
}
2 changes: 1 addition & 1 deletion src/component/modal/setting/DatabasesTabContent.tsx
Expand Up @@ -103,7 +103,7 @@ function DatabasesTabContent({ currentWorkspace }: DatabasesTabContentProps) {
</tr>
</thead>
<tbody>
{databases.data.map((item, index) => {
{databases?.data.map((item, index) => {
const num = index + 1;
return (
// eslint-disable-next-line react/no-array-index-key
Expand Down
37 changes: 36 additions & 1 deletion src/component/modal/setting/WorkspaceItem.tsx
@@ -1,6 +1,7 @@
import { CSSProperties, useState } from 'react';
import { FaTimes } from 'react-icons/fa';

import { usePreferences } from '../../context/PreferencesContext';
import Button from '../../elements/Button';
import workspaces from '../../workspaces';

Expand All @@ -10,6 +11,8 @@ const styles: Record<
> = {
container: {
display: 'flex',
alignItems: 'center',
padding: '0 5px',
},
item: {
flex: '1',
Expand All @@ -28,11 +31,13 @@ const styles: Record<
color: 'black',
outline: 'none',
backgroundColor: 'transparent',
flex: 1,
},
};

function WorkspaceItem({ item, onSave, onDelete }) {
const [name, setName] = useState<string>('');
const { customWorkspaces } = usePreferences();

// Add new workspace
function addHandler(e) {
Expand Down Expand Up @@ -72,8 +77,9 @@ function WorkspaceItem({ item, onSave, onDelete }) {
</div>
) : (
<div style={styles.container}>
<WorkSpaceIndicator workspaceKey={item.key} />
<span style={styles.item}>{item.label}</span>
{!workspaces[item.key] && (
{!workspaces[item.key] && !customWorkspaces[item.key] && (
<Button.Danger onClick={deleteHandler} size="xSmall" fill="clear">
<FaTimes />
</Button.Danger>
Expand All @@ -84,4 +90,33 @@ function WorkspaceItem({ item, onSave, onDelete }) {
);
}

const style = {
width: '20px',
height: '20px',
borderRadius: '50%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
};

const WorkSpaceIndicator = (props) => {
const { customWorkspaces } = usePreferences();
let letter = 'U';
let backgroundColor = '#ff6f00';

if (customWorkspaces[props.workspaceKey]) {
letter = 'C';
backgroundColor = '#ffbe05';
} else if (workspaces[props.workspaceKey]) {
letter = 'P';
backgroundColor = '#2dd36f';
}

return (
<div style={{ ...style, backgroundColor }}>
<span style={{ fontWeight: 'bolder' }}>{letter}</span>
</div>
);
};

export default WorkspaceItem;
33 changes: 27 additions & 6 deletions src/component/reducer/preferences/actions/initPreferences.ts
Expand Up @@ -5,14 +5,30 @@ import { getLocalStorage, storeData } from '../../../utility/LocalStorage';
import Workspaces from '../../../workspaces';
import { PreferencesState } from '../preferencesReducer';
import { checkKeysExists } from '../utilities/checkKeysExists';
import { filterCustomWorkspaces } from '../utilities/filterCustomWorkspaces';
import { filterObject } from '../utilities/filterObject';
import { getActiveWorkspace } from '../utilities/getActiveWorkspace';
import { getPreferencesByWorkspace } from '../utilities/getPreferencesByWorkspace';
import { mapWorkspaces } from '../utilities/mapWorkspaces';

export function initPreferences(draft: Draft<PreferencesState>, action) {
if (action.payload) {
const localData = getLocalStorage('nmr-general-settings');
const { dispatch, workspace, ...resProps } = action.payload;
const {
dispatch,
workspace,
customWorkspaces: cw,
...resProps
} = action.payload;
const customWorkspaces = mapWorkspaces(cw, 'custom');
const workspaces = mapWorkspaces(Workspaces as any, 'predefined');
draft.customWorkspaces = customWorkspaces;
draft.workspaces = {
...workspaces,
...customWorkspaces,
...draft.workspaces,
};

/**
* set the current workspace what the user-defined in the setting if the workspace is not defined at the level of component, otherwise
* use the default workspace
Expand Down Expand Up @@ -54,6 +70,7 @@ export function initPreferences(draft: Draft<PreferencesState>, action) {
workspaces,
version,
workspace: { current },
customWorkspaces,
} = draft || {};
const display = filterObject(workspacePreferences.display);

Expand All @@ -63,11 +80,15 @@ export function initPreferences(draft: Draft<PreferencesState>, action) {
currentWorkspace: localData?.currentWorkspace,
}),
workspaces: {
...workspaces,
[current]: {
...workspacePreferences,
display,
},
...filterCustomWorkspaces(workspaces),
...(!customWorkspaces[current]
? {
[current]: {
...workspacePreferences,
display,
},
}
: {}),
},
};

Expand Down
19 changes: 11 additions & 8 deletions src/component/reducer/preferences/actions/setPreferences.ts
Expand Up @@ -11,14 +11,17 @@ export function setPreferences(draft: Draft<PreferencesState>, action) {
formatting = mapNucleiFormatting(formatting);
let localData = getLocalStorage('nmr-general-settings');
localData.currentWorkspace = draft.workspace.current;
localData.workspaces = {
...localData.workspaces,
[draft.workspace.current]: {
...localData.workspaces[draft.workspace.current],
...restPreferences,
formatting,
},
};

if (!draft.customWorkspaces[draft.workspace.current]) {
localData.workspaces = {
...localData.workspaces,
[draft.workspace.current]: {
...localData.workspaces[draft.workspace.current],
...restPreferences,
formatting,
},
};
}

storeData('nmr-general-settings', JSON.stringify(localData));

Expand Down
9 changes: 8 additions & 1 deletion src/component/reducer/preferences/preferencesReducer.ts
Expand Up @@ -17,7 +17,12 @@ const LOCAL_STORAGE_VERSION = 11;

type InitPreferencesAction = ActionType<
'INIT_PREFERENCES',
{ display: NMRiumPreferences; workspace: NMRiumWorkspace; dispatch: any }
{
display: NMRiumPreferences;
workspace: NMRiumWorkspace;
customWorkspaces: Record<string, Workspace>;
dispatch: any;
}
>;
type SetPreferencesAction = ActionType<
'SET_PREFERENCES',
Expand Down Expand Up @@ -77,6 +82,7 @@ export const WORKSPACES: Array<{
export interface PreferencesState {
version: number;
workspaces: Record<string, Workspace>;
customWorkspaces: Record<string, Workspace>;
dispatch: (action?: PreferencesActions) => void;
workspace: {
current: NMRiumWorkspace;
Expand All @@ -87,6 +93,7 @@ export interface PreferencesState {
export const preferencesInitialState: PreferencesState = {
version: LOCAL_STORAGE_VERSION,
workspaces: {},
customWorkspaces: {},
dispatch: () => null,
workspace: {
current: 'default',
Expand Down
@@ -0,0 +1,11 @@
import { Workspace } from '../../../workspaces/Workspace';

export function filterCustomWorkspaces(workspaces: Record<string, Workspace>) {
const mapObject = {};
for (const key in workspaces) {
if (!key.startsWith('custom-')) {
mapObject[key] = workspaces[key];
}
}
return mapObject;
}
20 changes: 20 additions & 0 deletions src/component/reducer/preferences/utilities/mapWorkspaces.ts
@@ -0,0 +1,20 @@
import lodashMerge from 'lodash/merge';

import { Workspace, WorkSpaceSource } from '../../../workspaces/Workspace';
import { workspaceDefaultProperties } from '../../../workspaces/workspaceDefaultProperties';

export function mapWorkspaces(
customWorkspaces: Record<string, Workspace>,
source: WorkSpaceSource,
) {
const mapObject = {};
const prefix = source === 'custom' ? 'custom-' : '';
for (const key in customWorkspaces) {
mapObject[`${prefix}${key}`] = lodashMerge(
{},
workspaceDefaultProperties,
customWorkspaces[key],
);
}
return mapObject;
}
11 changes: 11 additions & 0 deletions src/component/workspaces/Workspace.ts
Expand Up @@ -162,6 +162,17 @@ export interface WorkspaceData {
nmrLoaders?: LoadersPreferences;
}

/**
* custom : workspace which come form the component level <NMRium customWorkspaces = {} />
* predefined : workspace which hardcoded in NMRium
* user: workspaces which the user create from the general settings
*/
export type WorkSpaceSource = 'custom' | 'predefined' | 'user';

export type InnerWorkspace = WorkspaceMeta & WorkspaceData;
export type CustomWorkspaces = Record<
string,
Omit<WorkspaceMeta, 'version'> & WorkspaceData
>;

export type Workspace = WorkspaceMeta & Required<WorkspaceData>;
5 changes: 5 additions & 0 deletions src/demo/samples.json
Expand Up @@ -193,6 +193,11 @@
"title": "empty",
"view": "Test"
},
{
"title": "Metabo workspace",
"view": "CustomWorkspace",
"file": "./data/xtc/XTC.json"
},
{
"file": "./data/cytisine/1H.json",
"title": "1H spectrum test",
Expand Down
19 changes: 19 additions & 0 deletions src/demo/views/CustomWorkspace.tsx
@@ -0,0 +1,19 @@
import { CustomWorkspaces } from '../../component/workspaces/Workspace';

import View from './View';

const customWorkspaces: CustomWorkspaces = {
test: {
label: 'Test Workspace',
display: {
panels: {
spectraPanel: { display: true, open: true },
multipleSpectraAnalysisPanel: { display: true, open: true },
},
},
},
};

export default function CustomWorkspace(props) {
return <View {...props} customWorkspaces={customWorkspaces} />;
}

0 comments on commit f3b3c65

Please sign in to comment.