Integrate editor as part of webapp.

This commit is contained in:
Matias Arriola 2022-01-25 18:10:40 +00:00 committed by Paulo Veiga
parent 2b40f429b4
commit 8b6ffe2d3c
147 changed files with 2175 additions and 617 deletions

View File

@ -3,7 +3,7 @@ services:
e2e:
image: cypress/included:8.4.1
container_name: wisemapping-integration-tests
entrypoint: '/bin/sh -c "yarn install && yarn bootstrap && yarn test:integration"'
entrypoint: '/bin/sh -c "yarn bootstrap && yarn test:integration"'
working_dir: /e2e
environment:
- CYPRESS_imageSnaphots=true

View File

@ -3,7 +3,7 @@ services:
e2e:
image: cypress/included:8.4.1
container_name: wisemapping-integration-tests
entrypoint: '/bin/sh -c "yarn install && yarn bootstrap && yarn test:integration"'
entrypoint: '/bin/sh -c "yarn bootstrap && yarn test:integration"'
working_dir: /e2e
environment:
- CYPRESS_imageSnaphots=true

6
packages/editor/.babelrc Normal file
View File

@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}

4
packages/editor/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
cypress/screenshots
cypress/videos
cypress/downloads
cypress/snapshots/*/__diff_output__

View File

@ -0,0 +1,6 @@
{
"video": false,
"videoUploadOnPasses": false,
"baseUrl": "http://localhost:8081"
}

View File

@ -0,0 +1,22 @@
context('Playground', () => {
it('viewmode page should match its snapshot', () => {
['welcome', 'sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6', 'complex', 'img-support', 'icon-sample'].forEach((mapId) => {
cy.visit(`/viewmode.html?id=${mapId}`);
cy.get('#mindplot.ready').should('exist');
cy.matchImageSnapshot(`viewmode-${mapId}`);
});
});
it('the playground container.html page should match its snapshot', () => {
cy.visit('/container.html');
cy.getIframeBody()
.find('#mindplot.ready')
.should('exist');
cy.matchImageSnapshot('container');
});
it('the playground editor.html page should match its snapshot', () => {
cy.visit('/editor.html');
cy.get('#mindplot.ready').should('exist');
// TODO: why is the editor appearing twice in the snapshot?
cy.matchImageSnapshot('editor');
});
});

View File

@ -0,0 +1,24 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
addMatchImageSnapshotPlugin(on, config);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@ -0,0 +1,24 @@
import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command';
// make matchImageSnapshot() call the real implementation only if CYPRESS_imageSnaphots is set
// otherwise it calls a noop
if (Cypress.env('imageSnaphots')) {
addMatchImageSnapshotCommand({
failureThreshold: 0.001,
failureThresholdType: 'percent',
});
} else {
Cypress.Commands.add(
'matchImageSnapshot',
{
prevSubject: ['optional', 'element', 'window', 'document'],
},
() => Promise.resolve(),
);
}
// https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/
Cypress.Commands.add('getIframeBody', () => cy
.get('iframe')
.its('0.contentDocument.body').should('not.be.empty')
.then(cy.wrap));

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 382 B

View File

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 173 B

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 189 B

After

Width:  |  Height:  |  Size: 189 B

View File

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 440 B

View File

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

View File

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 254 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 205 B

After

Width:  |  Height:  |  Size: 205 B

View File

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

View File

Before

Width:  |  Height:  |  Size: 343 B

After

Width:  |  Height:  |  Size: 343 B

View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

View File

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 155 B

After

Width:  |  Height:  |  Size: 155 B

View File

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 302 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

View File

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 299 B

View File

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 257 B

View File

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 398 B

View File

Before

Width:  |  Height:  |  Size: 758 B

After

Width:  |  Height:  |  Size: 758 B

View File

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

View File

Before

Width:  |  Height:  |  Size: 510 B

After

Width:  |  Height:  |  Size: 510 B

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 280 B

View File

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 280 B

View File

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 341 B

View File

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

View File

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 299 B

View File

@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wisemapping</title>
</head>
<body>
<!-- React app root element -->
<div id="root"></div>
</body>
</html>

View File

@ -1,30 +1,53 @@
{
"name": "@wisemapping/editor",
"version": "1.0.0",
"main": "src/index.tsx",
"scripts": {},
"version": "0.1.0",
"main": "dist/editor.bundle.js",
"scripts": {
"build": "webpack --config webpack.prod.js",
"playground": "webpack serve --config webpack.playground.js",
"cy:run": "cypress run",
"test:integration": "start-server-and-test 'yarn playground' http-get://localhost:8081 'yarn cy:run'",
"test": "yarn test:integration"
},
"repository": "http://www.wisemapping.com",
"author": "Paulo Veiga <pveiga@gmail.com>, Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>",
"license": "MIT",
"private": false,
"devDependencies": {
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
"clean-webpack-plugin": "^4.0.0",
"compression-webpack-plugin": "^9.2.0",
"copy-webpack-plugin": "^10.2.1",
"cypress": "^8.4.1",
"cypress-image-snapshot": "^4.0.1",
"eslint": "^7.14.0",
"eslint-config-prettier": "^8.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"html-webpack-plugin": "^5.5.0",
"prettier": "^2.2.1",
"react": "^17.0.0",
"ts-loader": "^8.0.11",
"ts-node": "^9.0.0",
"typescript": "^4.1.2"
"typescript": "^4.1.2",
"webpack": "^5.67.0",
"webpack-dev-server": "^4.7.3",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@types/styled-components": "^5.1.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"@wisemapping/mindplot": "^0.4.15",
"styled-components": "^5.2.1"
},
"peerDependencies": {
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-intl": "^5.24.3"
}
}

View File

@ -1,6 +0,0 @@
import React from 'react';
import { StyledCanvas } from './styled';
const Canvas = (): React.ReactElement => <StyledCanvas>canvas</StyledCanvas>;
export default Canvas;

View File

@ -1,8 +0,0 @@
import styled from 'styled-components';
export const StyledCanvas = styled.div`
height: 100%
width: 100%;
flex: 1;
`;

View File

@ -1,6 +1,54 @@
import React from 'react';
import { StyledFooter } from './styled';
import { StyledLogo } from './styled';
import { useIntl } from 'react-intl';
const Footer = (): React.ReactElement => <StyledFooter>footer</StyledFooter>;
import KeyboardSvg from '../../../images/keyboard.svg';
import AddSvg from '../../../images/add.svg';
import MinusSvg from '../../../images/minus.svg';
import CenterFocusSvg from '../../../images/center_focus.svg';
export type FooterPropsType = {
showTryPanel?: boolean;
};
const Footer = ({ showTryPanel }: FooterPropsType): React.ReactElement => {
const intl = useIntl();
return (
<>
<div id="floating-panel">
<div id="keyboardShortcuts" className="buttonExtOn">
<img src={KeyboardSvg} />
</div>
<div id="zoom-button">
<button id="zoom-plus">
<img src={AddSvg} />
</button>
<button id="zoom-minus">
<img src={MinusSvg} />
</button>
</div>
<div id="position">
<button id="position-button">
<img src={CenterFocusSvg} />
</button>
</div>
</div>
<StyledLogo id="bottom-logo"></StyledLogo>
<div id="headerNotifier"></div>
{showTryPanel && (
<div id="tryInfoPanel">
<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>
)}
</>
);
};
export default Footer;

View File

@ -1,8 +1,18 @@
import styled from 'styled-components';
import { times } from '../../size';
import LogoTextBlackSvg from '../../../images/logo-text-black.svg';
export const StyledFooter = styled.div`
height: ${times(10)};
width: 100%;
border: 1px solid black;
`;
export const StyledLogo = styled.div`
position: fixed;
left: 20px;
bottom: 10px;
background: url(${LogoTextBlackSvg}) no-repeat;
width: 90px;
height: 40px;
`

View File

@ -1,15 +0,0 @@
import React from 'react';
import Footer from '../footer';
import TopBar from '../top-bar';
import Canvas from '../canvas';
import { StyledFrame } from './styled';
const Frame = (): React.ReactElement => (
<StyledFrame>
<TopBar />
<Canvas />
<Footer />
</StyledFrame>
);
export default Frame;

View File

@ -1,8 +0,0 @@
import styled from 'styled-components';
export const StyledFrame = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
`;

View File

@ -0,0 +1,143 @@
import React from 'react';
import { useIntl } from 'react-intl';
import BackIconSvg from '../../../images/back-icon.svg';
import SaveSvg from '../../../images/save.svg';
import DiscardSvg from '../../../images/discard.svg';
import UndoSvg from '../../../images/undo.svg';
import RedoSvg from '../../../images/redo.svg';
import TopicAddSvg from '../../../images/topic-add.svg';
import TopicDeleteSvg from '../../../images/topic-delete.svg';
import TopicBorderSvg from '../../../images/topic-border.svg';
import TopicColorSvg from '../../../images/topic-color.svg';
import TopicShapeSvg from '../../../images/topic-shape.svg';
import FontTypeSvg from '../../../images/font-type.svg';
import FontSizeSvg from '../../../images/font-size.svg';
import FontBoldSvg from '../../../images/font-bold.svg';
import FontItalicSvg from '../../../images/font-italic.svg';
import FontColorSvg from '../../../images/font-color.svg';
import TopicIconSvg from '../../../images/topic-icon.svg';
import TopicNoteSvg from '../../../images/topic-note.svg';
import TopicLinkSvg from '../../../images/topic-link.svg';
import TopicRelationSvg from '../../../images/topic-relation.svg';
import ExportSvg from '../../../images/export.svg';
import PublicSvg from '../../../images/public.svg';
import HistorySvg from '../../../images/history.svg';
import PrintSvg from '../../../images/print.svg';
import AccountSvg from '../../../images/account.svg';
export type ToolbarActionType = 'export' | 'publish' | 'history' | 'print' | 'share';
export type ToolbarPropsType = {
memoryPersistence: boolean;
readOnlyMode: boolean;
onAction: (action: ToolbarActionType) => void;
};
export default function Toolbar({
memoryPersistence,
readOnlyMode,
onAction,
}: ToolbarPropsType): React.ReactElement {
const intl = useIntl();
return (
<div id="toolbar">
<div id="backToList">
<img src={BackIconSvg} />
</div>
{!memoryPersistence && (
<div id="persist" className="buttonContainer">
<div id="save" className="buttonOn">
<img src={SaveSvg} />
</div>
<div id="discard" className="buttonOn">
<img src={DiscardSvg}/>
</div>
</div>
)}
{!readOnlyMode && (
<>
<div id="edit" className="buttonContainer">
<div id="undoEdition" className="buttonOn">
<img src={UndoSvg} />
</div>
<div id="redoEdition" className="buttonOn">
<img src={RedoSvg} />
</div>
</div>
<div id="nodeStyle" className="buttonContainer">
<div id="addTopic" className="buttonOn">
<img src={TopicAddSvg} />
</div>
<div id="deleteTopic" className="buttonOn">
<img src={TopicDeleteSvg} />
</div>
<div id="topicBorder" className="buttonExtOn">
<img src={TopicBorderSvg} />
</div>
<div id="topicColor" className="buttonExtOn">
<img src={TopicColorSvg} />
</div>
<div id="topicShape" className="buttonExtOn">
<img src={TopicShapeSvg} />
</div>
</div>
<div id="font" className="buttonContainer">
<div id="fontFamily" className="buttonOn">
<img src={FontTypeSvg} />
</div>
<div id="fontSize" className="buttonExtOn">
<img src={FontSizeSvg} />
</div>
<div id="fontBold" className="buttonOn">
<img src={FontBoldSvg} />
</div>
<div id="fontItalic" className="buttonOn">
<img src={FontItalicSvg} />
</div>
<div id="fontColor" className="buttonExtOn">
<img src={FontColorSvg} />
</div>
</div>
<div id="nodeContent" className="buttonContainer">
<div id="topicIcon" className="buttonExtOn">
<img src={TopicIconSvg} />
</div>
<div id="topicNote" className="buttonOn">
<img src={TopicNoteSvg} />
</div>
<div id="topicLink" className="buttonOn">
<img src={TopicLinkSvg} />
</div>
<div id="topicRelation" className="buttonOn">
<img src={TopicRelationSvg} />
</div>
</div>
<div id="separator" className="buttonContainer"></div>
</>
)}
{!memoryPersistence && (
<div id="toolbarRight">
<div id="export" className="buttonOn" onClick={() => onAction('export')}>
<img src={ExportSvg} />
</div>
<div id="publishIt" className="buttonOn" onClick={() => onAction('publish')}>
<img src={PublicSvg} />
</div>
<div id="history" className="buttonOn" onClick={() => onAction('history')}>
<img src={HistorySvg} />
</div>
<div id="print" className="buttonOn" onClick={() => onAction('print')}>
<img src={PrintSvg} />
</div>
<div id="account">
<img src={AccountSvg} />
</div>
<div id="share" className="actionButton" onClick={() => onAction('share')}>
{ intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' }) }
</div>
</div>
)}
</div>
);
}

View File

@ -1,6 +0,0 @@
import React from 'react';
import { StyledTopBar } from './styled';
const TopBar = (): React.ReactElement => <StyledTopBar>top bar</StyledTopBar>;
export default TopBar;

View File

@ -1,8 +0,0 @@
import styled from 'styled-components';
import { times } from '../../size';
export const StyledTopBar = styled.div`
height: ${times(10)};
width: 100%;
border: 1px solid black;
`;

30
packages/editor/src/custom.d.ts vendored Normal file
View File

@ -0,0 +1,30 @@
declare module "*.svg" {
const content: any;
export default content;
}
declare module "@wisemapping/mindplot" {
const mindplot: {
Mindmap: any,
PersistenceManager: any,
Designer: any,
LocalStorageManager: any,
Menu: any,
DesignerBuilder: any,
RESTPersistenceManager: any,
DesignerOptionsBuilder: any,
buildDesigner: any,
$notify: any
};
export var Mindmap: any;
export var PersistenceManager: any;
export var Designer: any;
export var LocalStorageManager: any;
export var Menu: any;
export var DesignerBuilder: any;
export var RESTPersistenceManager: any;
export var DesignerOptionsBuilder: any;
export var buildDesigner: any;
export var $notify: any;
export default mindplot;
}

View File

@ -0,0 +1,7 @@
export const Locales = {
EN: {},
ES: {},
DE: {},
FR: {}
};

View File

@ -1,3 +1,104 @@
import Editor from './components/frame';
import React from 'react';
import Toolbar, { ToolbarActionType } from './components/toolbar';
import Footer from './components/footer';
import { IntlProvider } from 'react-intl';
import * as mindplot from '@wisemapping/mindplot';
declare global {
var memoryPersistence: boolean;
var readOnly: boolean;
var lockTimestamp: string;
var lockSession: string;
var historyId: string;
var isAuth: boolean;
var mapId: number;
var userOptions: { zoom: string | number } | null;
var locale: string;
var mindmapLocked: boolean;
var mindmapLockedMsg: string;
}
export default Editor;
export type EditorPropsType = {
initCallback?: (m: typeof mindplot) => () => void;
mapId: number;
memoryPersistence: boolean;
readOnlyMode: boolean;
locale?: string;
onAction: (action: ToolbarActionType) => void;
};
const initMindplot = ({
PersistenceManager,
RESTPersistenceManager,
LocalStorageManager,
DesignerOptionsBuilder,
buildDesigner,
$notify,
}: typeof mindplot) => () => {
let persistence: typeof PersistenceManager;
if (!global.memoryPersistence && !global.readOnly) {
persistence = new RESTPersistenceManager({
documentUrl: '/c/restful/maps/{id}/document',
revertUrl: '/c/restful/maps/{id}/history/latest',
lockUrl: '/c/restful/maps/{id}/lock',
timestamp: global.lockTimestamp,
session: global.lockSession,
});
} else {
persistence = new LocalStorageManager(
`/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${
!global.isAuth ? '-pub' : ''
}`,
true
);
}
const params = new URLSearchParams(window.location.search.substring(1));
const zoomParam = Number.parseFloat(params.get('zoom'));
const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: persistence,
readOnly: Boolean(global.readOnly || false),
mapId: global.mapId,
container: 'mindplot',
zoom: zoomParam || global.userOptions ? global.userOptions.zoom : 1,
locale: global.locale,
});
// Build designer ...
const designer = buildDesigner(options);
// Load map from XML file persisted on disk...
const instance = PersistenceManager.getInstance();
const mindmap = instance.load(global.mapId);
designer.loadMap(mindmap);
if (global.mindmapLocked) {
$notify(global.mindmapLockedMsg);
}
};
export default function Editor({
initCallback = initMindplot,
mapId,
memoryPersistence,
readOnlyMode,
locale = 'en',
onAction,
}: EditorPropsType): React.ReactElement {
React.useEffect(initCallback(mindplot), []);
return (
<IntlProvider locale={locale} defaultLocale="en" messages={{}}>
<div id="header">
<Toolbar
memoryPersistence={memoryPersistence}
readOnlyMode={readOnlyMode}
onAction={onAction}
/>
</div>
<div id="mindplot"></div>
<Footer showTryPanel={memoryPersistence} />
</IntlProvider>
);
}

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@wisemapping/editor - Playground</title>
<style>
html * {
font-family: Arial !important;
}
tbody tr td:first-child {
width: 20%;
}
</style>
</head>
<body>
<h1>@wisemapping/editor - Playground</h1>
<p>You will find here a set of examples that shows how you can use integrate WiseMapping Editor</p>
<div>
<ul>
<li><a href="/viewmode.html">View mode:</a> Simple integration to load and render mindaps in read
only mode</li>
<li><a href="/editor.html">Editor mode:</a> Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.</li>
<li><a href="/container.html">Embedded:</a> Example on how to embeded editor in a iframe.</li>
</ul>
</div>
</body>
</html>

View File

@ -138,15 +138,6 @@ div.shareModalDialog {
right: 20px;
}
div#bottom-logo {
position: fixed;
left: 20px;
bottom: 10px;
background: url(../images/logo-text-black.svg) no-repeat;
width: 90px;
height: 40px;
}
div#position {
margin-top: 5px;
}

View File

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WiseMapping - Editor </title>
<meta name="viewport" content="initial-scale=1">
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WiseMapping - Editor </title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
</head>
<body>
<div id="root" onselectstart="return false;"></div>
</body>
</html>

View File

@ -10,7 +10,7 @@
</head>
<body>
<div id="mindplot" onselectstart="return false;"></div>
<div id="root" onselectstart="return false;"></div>
<div id="footer">
<div id="footer-logo"><img src="../images/logo-small.svg" /></div>
<div id="footer-desc">

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,48 @@
import '../css/editor.css';
import React from 'react';
import ReactDOM from 'react-dom';
import Editor from '../../../../src/index';
global.accountName = 'Test User';
global.accountEmail = 'test@example.com';
global.memoryPersistence = false;
global.readOnly = false;
global.mapId = 'welcome';
global.locale = 'en';
const initialization = ({
LocalStorageManager,
DesignerOptionsBuilder,
buildDesigner,
PersistenceManager
}) => () => {
const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: p
});
const designer = buildDesigner(options);
designer.addEvent('loadSuccess', () => {
// Hack for automation testing ...
document.getElementById('mindplot').classList.add('ready');
});
// Load map from XML file persisted on disk...
const mapId = 'welcome';
const persistence = PersistenceManager.getInstance();
const mindmap = persistence.load(mapId);
designer.loadMap(mindmap);
}
ReactDOM.render(
<Editor
mapId={global.mapId}
memoryPersistence={global.memoryPersistence}
readOnlyMode={global.readOnly}
locale={global.locale}
onAction={(action) => console.log('action called:', action)}
initCallback={initialization}
/>,
document.getElementById('root'),
);

View File

@ -0,0 +1,35 @@
import '../css/embedded.css';
import React from 'react';
import ReactDOM from 'react-dom';
import Editor from '../../../../src/index';
const initialization =
({ LocalStorageManager, DesignerOptionsBuilder, buildDesigner, PersistenceManager }) =>
() => {
// Options has been defined in by a external file ?
const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p });
const designer = buildDesigner(options);
designer.addEvent('loadSuccess', () => {
document.getElementById('mindplot').classList.add('ready');
});
// Load map from XML file persisted on disk...
const mapId = 'welcome';
const persistence = PersistenceManager.getInstance();
const mindmap = persistence.load(mapId);
designer.loadMap(mindmap);
};
ReactDOM.render(
<Editor
mapId={global.mapId}
memoryPersistence={global.memoryPersistence}
readOnlyMode={global.readOnly}
locale={global.locale}
onAction={(action) => console.log('action called:', action)}
initCallback={initialization}
/>,
document.getElementById('root')
);

View File

@ -0,0 +1,52 @@
import '../css/viewmode.css';
import React from 'react';
import ReactDOM from 'react-dom';
import Editor from '../../../../src/index';
const initialization = ({
LocalStorageManager,
DesignerOptionsBuilder,
buildDesigner,
PersistenceManager
}) => () => {
const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false });
// Obtain map id from query param
const params = new URLSearchParams(window.location.search.substring(1));
const mapId = params.get('id') || 'welcome';
const designer = buildDesigner(options);
designer.addEvent('loadSuccess', () => {
document.getElementById('mindplot').classList.add('ready');
});
// Load map from XML file persisted on disk...
const persistence = PersistenceManager.getInstance();
const mindmap = persistence.load(mapId);
designer.loadMap(mindmap);
// Code for selector of map.
const mapSelectElem = document.getElementById('map-select');
mapSelectElem.addEventListener('change', (e) => {
const selectMap = e.target.value;
window.location = `${window.location.pathname}?id=${selectMap}`;
});
Array.from(mapSelectElem.options).forEach((option) => {
option.selected = option.value === mapId;
});
};
ReactDOM.render(
<Editor
mapId={global.mapId}
memoryPersistence={global.memoryPersistence}
readOnlyMode={global.readOnly}
locale={global.locale}
onAction={(action) => console.log('action called:', action)}
initCallback={initialization}
/>,
document.getElementById('root'),
);

Some files were not shown because too many files have changed in this diff Show More