Skip to content

Commit

Permalink
feat: " Export As " feature
Browse files Browse the repository at this point in the history
- option to change the file name.
- option to compress before exporting the file.
- option to force include the data,info and meta object.
- option to format the exported data to be easy to read.
  • Loading branch information
hamed-musallam committed May 3, 2021
1 parent 6b319ad commit 60e6430
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 16 deletions.
9 changes: 7 additions & 2 deletions src/component/EventsTrackers/KeysListenerTracker.js
Expand Up @@ -39,7 +39,11 @@ function KeysListenerTracker() {
changeDisplayViewModeHandler,
} = useToolsFunctions();

const { saveToClipboardHandler, saveAsJSONHandler } = useExport();
const {
saveToClipboardHandler,
saveAsJSONHandler,
saveAsHandler,
} = useExport();

const { highlight } = useHighlightData();
const assignmentData = useAssignmentData();
Expand Down Expand Up @@ -223,7 +227,7 @@ function KeysListenerTracker() {
if (e.shiftKey && (e.metaKey || e.ctrlKey)) {
switch (e.key) {
case 'S':
saveAsJSONHandler(2, false);
saveAsHandler();
e.preventDefault();
break;
default:
Expand All @@ -238,6 +242,7 @@ function KeysListenerTracker() {
handleChangeOption,
handleFullZoomOut,
loader,
saveAsHandler,
saveAsJSONHandler,
saveToClipboardHandler,
],
Expand Down
40 changes: 40 additions & 0 deletions src/component/hooks/useExport.js
Expand Up @@ -3,6 +3,8 @@ import { useCallback } from 'react';
import { toJSON } from '../../data/SpectraManager';
import { useChartData } from '../context/ChartContext';
import { useAlert } from '../elements/popup/Alert';
import { positions, useModal } from '../elements/popup/Modal';
import SaveAsModal from '../modal/SaveAsModal';
import {
copyPNGToClipboard,
exportAsJSON,
Expand All @@ -13,6 +15,7 @@ import {
import { toNmredata } from '../utility/toNmredata';

export default function useExport() {
const modal = useModal();
const alert = useAlert();
const state = useChartData();

Expand Down Expand Up @@ -87,11 +90,48 @@ export default function useExport() {
}
}, [alert, state.data]);

const saveHandler = useCallback(
async (options) => {
const { name, pretty, compressed, includeData } = options;
const hideLoading = await alert.showLoading(
`Exporting as ${name}.numrium process in progress`,
);
setTimeout(async () => {
const exportedData = toJSON(state, includeData);
const spaceIndent = pretty ? 2 : 0;
await exportAsJSON(exportedData, name, spaceIndent, compressed);
hideLoading();
}, 0);
},
[alert, state],
);
const saveAsHandler = useCallback(async () => {
if (state.data.length > 0) {
const fileName = state.data[0]?.display?.name;
modal.show(<SaveAsModal name={fileName} onSave={saveHandler} />, {
isBackgroundBlur: false,
position: positions.TOP_CENTER,
width: 400,
height: 280,
});

// const hideLoading = await alert.showLoading(
// 'Exporting as PNG process in progress',
// );
// setTimeout(() => {
// const fileName = state.data[0]?.display?.name;
// exportAsPng(fileName, 'nmrSVG');
// hideLoading();
// }, 0);
}
}, [modal, saveHandler, state.data]);

return {
saveToClipboardHandler,
saveAsJSONHandler,
saveAsNMREHandler,
saveAsSVGHandler,
saveAsPNGHandler,
saveAsHandler,
};
}
103 changes: 103 additions & 0 deletions src/component/modal/SaveAsModal.jsx
@@ -0,0 +1,103 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { useCallback, useRef } from 'react';

import CloseButton from '../elements/CloseButton';
import FormikCheckBox from '../elements/formik/FormikCheckBox';
import FormikForm from '../elements/formik/FormikForm';
import FormikInput from '../elements/formik/FormikInput';

import { ModalStyles } from './ModalStyle';

const styles = css`
.row {
align-items: center;
}
.inner-content {
flex: 1;
}
.custom-label {
width: 80px;
}
.name {
width: 100% !important;
text-align: left !important;
}
`;

const INITIAL_VALUE = {
name: '',
compressed: false,
pretty: false,
includeData: false,
};

function SaveAsModal({ onClose, onSave, name }) {
const refForm = useRef();

const handleSave = useCallback(() => {
refForm.current.submitForm();
}, []);

const submitHandler = useCallback(
(values) => {
onSave(values);
onClose();
},
[onClose, onSave],
);

return (
<div css={[ModalStyles, styles]}>
<div className="header handle">
<span>Save as ... </span>
<CloseButton onClick={onClose} className="close-bt" />
</div>
<div className="inner-content">
<FormikForm
ref={refForm}
initialValues={{ ...INITIAL_VALUE, name }}
onSubmit={submitHandler}
>
<div className="row margin-10">
<span className="custom-label">Name</span>
<FormikInput
label=""
name="name"
className="name"
style={{
container: { width: '100%' },
}}
/>
</div>

<div className="row margin-10">
<span className="custom-label">Compressed</span>
<FormikCheckBox name="compressed" />
</div>
<div className="row margin-10">
<span className="custom-label">Pretty Format</span>
<FormikCheckBox name="pretty" />
</div>
<div className="row margin-10">
<span className="custom-label">Include Data</span>
<FormikCheckBox name="includeData" />
</div>
</FormikForm>
</div>
<div className="footer-container">
<button type="button" onClick={handleSave} className="save-button">
Save
</button>
<button type="button" onClick={onClose} className="save-button">
Close
</button>
</div>
</div>
);
}

export default SaveAsModal;
10 changes: 10 additions & 0 deletions src/component/toolbar/BasicToolBar.jsx
Expand Up @@ -87,6 +87,11 @@ const EXPORT_MENU = [
icon: <FaFileDownload />,
label: 'Save data ( Press Ctrl + S )',
},
{
id: 'advance_save',
icon: <FaFileDownload />,
label: 'Save data as ( Press Ctrl + Shift + S )',
},
{
id: 'nmre',
icon: <FaFileDownload />,
Expand Down Expand Up @@ -120,6 +125,7 @@ function BasicToolBar({ info, verticalAlign, displayerMode }) {
saveAsJSONHandler,
saveAsNMREHandler,
saveToClipboardHandler,
saveAsHandler,
} = useExport();

const selectedSpectrumInfo = { isComplex: false, isFid: false, ...info };
Expand Down Expand Up @@ -190,6 +196,9 @@ function BasicToolBar({ info, verticalAlign, displayerMode }) {
case 'json':
saveAsJSONHandler();
break;
case 'advance_save':
saveAsHandler();
break;
case 'nmre':
saveAsNMREHandler();
break;
Expand All @@ -205,6 +214,7 @@ function BasicToolBar({ info, verticalAlign, displayerMode }) {
saveAsSVGHandler,
saveAsPNGHandler,
saveAsJSONHandler,
saveAsHandler,
saveAsNMREHandler,
saveToClipboardHandler,
],
Expand Down
6 changes: 4 additions & 2 deletions src/data/SpectraManager.js
Expand Up @@ -198,7 +198,7 @@ export async function addMolfileFromURL(molfileURL) {
*
* @param {object} state
*/
export function toJSON(state) {
export function toJSON(state, forceIncludeData = false) {
const {
data,
molecules,
Expand All @@ -215,7 +215,9 @@ export function toJSON(state) {
exclusionZones: {},
};
const spectra = data.map((ob) => {
return ob.info.dimension === 1 ? Datum1D.toJSON(ob) : Datum2D.toJSON(ob);
return ob.info.dimension === 1
? Datum1D.toJSON(ob, forceIncludeData)
: Datum2D.toJSON(ob, forceIncludeData);
});

return {
Expand Down
14 changes: 8 additions & 6 deletions src/data/data1d/Datum1D.ts
Expand Up @@ -205,18 +205,20 @@ function preprocessing(datum) {
}
}

export function toJSON(datum1D: Datum1D) {
export function toJSON(datum1D: Datum1D, forceIncludeData = false) {
return {
id: datum1D.id,
source: {
jcampURL: datum1D.source.jcampURL,
},
display: datum1D.display,
...(!datum1D.source.jcampURL && {
data: datum1D.originalData,
info: datum1D.originalInfo,
meta: datum1D.meta,
}),
...(!datum1D.source.jcampURL || forceIncludeData
? {
data: datum1D.originalData,
info: datum1D.originalInfo,
meta: datum1D.meta,
}
: {}),
peaks: datum1D.peaks,
integrals: datum1D.integrals,
ranges: datum1D.ranges,
Expand Down
14 changes: 8 additions & 6 deletions src/data/data2d/Datum2D.ts
Expand Up @@ -201,17 +201,19 @@ function getColor(options) {
return {};
}

export function toJSON(datum: Datum2D) {
export function toJSON(datum: Datum2D, forceIncludeData = false) {
return {
id: datum.id,
source: {
jcampURL: datum.source.jcampURL,
},
...(!datum.source.jcampURL && {
data: datum.originalData,
info: datum.originalInfo,
meta: datum.meta,
}),
...(!datum.source.jcampURL || forceIncludeData
? {
data: datum.originalData,
info: datum.originalInfo,
meta: datum.meta,
}
: {}),
zones: datum.zones,
filters: datum.filters,
display: datum.display,
Expand Down

0 comments on commit 60e6430

Please sign in to comment.