mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-22 06:37:56 +01:00
Integrate export and improve editor integration
This commit is contained in:
parent
f84ccfdcbd
commit
2faeeedd1b
@ -58,6 +58,8 @@ import { DesignerOptions } from './DesignerOptionsBuilder';
|
||||
import MainTopic from './MainTopic';
|
||||
import DragTopic from './DragTopic';
|
||||
|
||||
export type ExportFormat = 'png' | 'svg' | 'jpg' | 'wxml';
|
||||
|
||||
class Designer extends Events {
|
||||
private _mindmap: Mindmap;
|
||||
|
||||
@ -80,11 +82,11 @@ class Designer extends Events {
|
||||
private _cleanScreen: any;
|
||||
|
||||
constructor(options: DesignerOptions, divElement: JQuery) {
|
||||
super();
|
||||
$assert(options, 'options must be defined');
|
||||
$assert(options.zoom, 'zoom must be defined');
|
||||
$assert(options.containerSize, 'size must be defined');
|
||||
$assert(divElement, 'divElement must be defined');
|
||||
super();
|
||||
|
||||
// Set up i18n location ...
|
||||
Messages.init(options.locale);
|
||||
@ -342,7 +344,9 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
export(formatType: 'png' | 'svg' | 'jpg' | 'wxml'): Promise<string> {
|
||||
EXPORT_SUPPORTED_FORMATS: ExportFormat[] = ['png', 'svg', 'jpg', 'wxml'];
|
||||
|
||||
export(formatType: ExportFormat): Promise<string> {
|
||||
const workspace = this._workspace;
|
||||
const svgElement = workspace.getSVGElement();
|
||||
const size = workspace.getSize();
|
||||
|
@ -57,8 +57,8 @@ class AccountSettingsPanel extends ListToolbarPanel {
|
||||
const content = $("<div class='toolbarPanel' id='accountSettingsPanel'></div>");
|
||||
content[0].innerHTML = `
|
||||
<p style='text-align:center;font-weight:bold;'>${global.accountName}</p>
|
||||
<p>pveiga@wisemapping.com</p>
|
||||
<div id="${global.accountMail}" model='logout' style='text-align:center'>
|
||||
<p>${global.accountEmail}</p>
|
||||
<div id="account-logout" model='logout' style='text-align:center'>
|
||||
Logout
|
||||
</div>
|
||||
`;
|
||||
|
@ -467,5 +467,11 @@
|
||||
},
|
||||
"share.message": {
|
||||
"defaultMessage": "Message"
|
||||
},
|
||||
"editor.try-welcome": {
|
||||
"defaultMessage": "This edition space showcases some of the mindmap editor capabilities !"
|
||||
},
|
||||
"editor.try-welcome-description": {
|
||||
"defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
|
||||
}
|
||||
}
|
@ -467,5 +467,11 @@
|
||||
},
|
||||
"share.message": {
|
||||
"defaultMessage": "Message"
|
||||
},
|
||||
"editor.try-welcome": {
|
||||
"defaultMessage": "This edition space showcases some of the mindmap editor capabilities !"
|
||||
},
|
||||
"editor.try-welcome-description": {
|
||||
"defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
|
||||
}
|
||||
}
|
@ -467,5 +467,11 @@
|
||||
},
|
||||
"share.message": {
|
||||
"defaultMessage": "Message"
|
||||
},
|
||||
"editor.try-welcome": {
|
||||
"defaultMessage": "This edition space showcases some of the mindmap editor capabilities !"
|
||||
},
|
||||
"editor.try-welcome-description": {
|
||||
"defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
|
||||
}
|
||||
}
|
@ -467,5 +467,11 @@
|
||||
},
|
||||
"share.message": {
|
||||
"defaultMessage": "Message"
|
||||
},
|
||||
"editor.try-welcome": {
|
||||
"defaultMessage": "This edition space showcases some of the mindmap editor capabilities !"
|
||||
},
|
||||
"editor.try-welcome-description": {
|
||||
"defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
|
||||
}
|
||||
}
|
@ -32,6 +32,12 @@ const queryClient = new QueryClient({
|
||||
const App = (): ReactElement => {
|
||||
const appi18n = new AppI18n();
|
||||
const locale = appi18n.getBrowserLocale();
|
||||
|
||||
// global variables set server-side
|
||||
const memoryPersistence = global.memoryPersistence;
|
||||
const readOnlyMode = global.readOnly;
|
||||
const mapId = parseInt(global.mapId, 10);
|
||||
|
||||
return locale.message ? (
|
||||
<Provider store={store}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
@ -66,7 +72,8 @@ const App = (): ReactElement => {
|
||||
<MapsPage />
|
||||
</Route>
|
||||
<Route exact path="/c/maps/:id/edit">
|
||||
<Editor />
|
||||
<Editor memoryPersistence={memoryPersistence}
|
||||
readOnlyMode={readOnlyMode} mapId={mapId} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
|
@ -74,13 +74,30 @@ const ExportDialog = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (submit) {
|
||||
// TODO: Remove usage of global "designer"
|
||||
const designer = global.designer;
|
||||
// Depending on the type of export. It will require differt POST.
|
||||
if (
|
||||
exportFormat == 'pdf' ||
|
||||
exportFormat == 'svg' ||
|
||||
exportFormat == 'jpg' ||
|
||||
exportFormat == 'png'
|
||||
designer &&
|
||||
designer.EXPORT_SUPPORTED_FORMATS.includes(exportFormat)
|
||||
) {
|
||||
designer.export(exportFormat)
|
||||
.then((url: string) => {
|
||||
// Create hidden anchor to force download ...
|
||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||
anchor.style.display = 'display: none';
|
||||
anchor.download = `${mapId}.${exportFormat}`;
|
||||
anchor.href = url;
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
// Trigger click ...
|
||||
anchor.click();
|
||||
|
||||
// Clean up ...
|
||||
URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
});
|
||||
} else if (exportFormat === 'pdf') {
|
||||
formTransformtRef?.submit();
|
||||
} else {
|
||||
formExportRef?.submit();
|
||||
@ -99,13 +116,15 @@ const ExportDialog = ({
|
||||
description={intl.formatMessage({ id: 'export.desc', defaultMessage: 'Export this map in the format that you want and start using it in your presentations or sharing by email' })}
|
||||
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
>
|
||||
{
|
||||
!enableImgExport &&
|
||||
<Alert severity="info">
|
||||
<FormattedMessage
|
||||
id="export.warning"
|
||||
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
||||
/>
|
||||
</Alert>
|
||||
|
||||
}
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
||||
<FormControl>
|
||||
@ -124,7 +143,7 @@ const ExportDialog = ({
|
||||
/>
|
||||
{exportGroup == 'image' && (
|
||||
<Select
|
||||
onSelect={handleOnExportFormatChange}
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
value={exportFormat}
|
||||
className={classes.label}
|
||||
@ -132,7 +151,7 @@ const ExportDialog = ({
|
||||
<MenuItem value="svg" className={classes.menu}>
|
||||
Scalable Vector Graphics (SVG)
|
||||
</MenuItem>
|
||||
<MenuItem value="pdf" className={classes.select}>
|
||||
<MenuItem value="pdf" className={classes.menu}>
|
||||
Portable Document Format (PDF)
|
||||
</MenuItem>
|
||||
<MenuItem value="png" className={classes.menu}>
|
||||
|
@ -22,9 +22,10 @@ type ActionDialogProps = {
|
||||
action?: ActionType;
|
||||
mapsId: number[];
|
||||
onClose: () => void;
|
||||
fromEditor: boolean;
|
||||
};
|
||||
|
||||
const ActionDispatcher = ({ mapsId, action, onClose }: ActionDialogProps): React.ReactElement => {
|
||||
const ActionDispatcher = ({ mapsId, action, onClose, fromEditor }: ActionDialogProps): React.ReactElement => {
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
@ -57,12 +58,17 @@ const ActionDispatcher = ({ mapsId, action, onClose }: ActionDialogProps): React
|
||||
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||
{action === 'export' && (
|
||||
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={false} />
|
||||
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={fromEditor} />
|
||||
)}
|
||||
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
ActionDispatcher.defaultProps = {
|
||||
fromEditor: false,
|
||||
};
|
||||
|
||||
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
||||
queryClient.invalidateQueries('maps');
|
||||
onClose();
|
||||
|
@ -2,14 +2,19 @@ import React from 'react';
|
||||
import Toolbar from './toolbar';
|
||||
import ActionDispatcher from '../action-dispatcher';
|
||||
import { ActionType } from '../action-chooser';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
|
||||
// this component is a hack. In order to work, we need to load externally the "loader.js" script from mindplot
|
||||
export type EditorPropsType = {
|
||||
mapId: number;
|
||||
memoryPersistence: boolean;
|
||||
readOnlyMode: boolean;
|
||||
};
|
||||
|
||||
// HACK. In order to work, we need to load externally the "loader.js" script from mindplot
|
||||
// TODO: create the Editor component in the editor package, with its own build and include it instead
|
||||
export default function Editor(): React.ReactElement {
|
||||
const memoryPersistence = false;
|
||||
const readOnlyMode = false;
|
||||
const mapId = 1;
|
||||
export default function Editor({ mapId, memoryPersistence, readOnlyMode } : EditorPropsType): React.ReactElement {
|
||||
const intl = useIntl();
|
||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
||||
|
||||
return <>
|
||||
@ -42,10 +47,14 @@ export default function Editor(): React.ReactElement {
|
||||
<div id="headerNotifier"></div>
|
||||
{
|
||||
memoryPersistence && <div id="tryInfoPanel">
|
||||
<p>TRY_WELCOME</p>
|
||||
<p>TRY_WELCOME_DESC</p>
|
||||
<a href="/c/registration"><div className="actionButton">
|
||||
SIGN_UP</div>
|
||||
<p>
|
||||
{ intl.formatMessage({ id: 'editor.try-welcome' }) }
|
||||
</p>
|
||||
<p>{ intl.formatMessage({ id: 'editor.try-welcome-description' }) }</p>
|
||||
<a href="/c/registration">
|
||||
<div className="actionButton">
|
||||
{ intl.formatMessage({ id: 'login.signup', defaultMessage: 'Sign Up' }) }
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@ -55,6 +64,7 @@ export default function Editor(): React.ReactElement {
|
||||
action={activeDialog}
|
||||
onClose={() => setActiveDialog(null)}
|
||||
mapsId={[mapId]}
|
||||
fromEditor
|
||||
/>
|
||||
}
|
||||
</>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { ActionType } from '../action-chooser';
|
||||
|
||||
export type ToolbarPropsType = {
|
||||
@ -12,6 +13,7 @@ export default function Toolbar({
|
||||
readOnlyMode,
|
||||
onAction,
|
||||
}: ToolbarPropsType): React.ReactElement {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div id="toolbar">
|
||||
<div id="backToList">
|
||||
@ -22,6 +24,9 @@ export default function Toolbar({
|
||||
<div id="save" className="buttonOn">
|
||||
<img src="../../images/editor/save.svg" />
|
||||
</div>
|
||||
<div id="discard" className="buttonOn">
|
||||
<img src="../../images/editor/discard.svg" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!readOnlyMode && (
|
||||
@ -103,7 +108,7 @@ export default function Toolbar({
|
||||
<img src="../../images/editor/account.svg" />
|
||||
</div>
|
||||
<div id="share" className="actionButton" onClick={() => onAction('share')}>
|
||||
SHARE
|
||||
{ intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' }) }
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -197,6 +197,7 @@ const MapsPage = (): ReactElement => {
|
||||
action={activeDialog}
|
||||
onClose={() => setActiveDialog(undefined)}
|
||||
mapsId={[]}
|
||||
fromEditor
|
||||
/>
|
||||
|
||||
<div className={classes.rightButtonGroup}>
|
||||
|
Loading…
Reference in New Issue
Block a user