Merge branch 'feature/exports-mindmap' of https://bitbucket.org/wisemapping/wisemapping-frontend into feature/exports-mindmap

This commit is contained in:
Ezequiel-Vega 2022-02-11 18:35:52 -03:00
commit d8fbf4ecc9
227 changed files with 7144 additions and 4373 deletions

View File

@ -17,6 +17,7 @@ pipelines:
- cypress - cypress
script: script:
- export CYPRESS_imageSnaphots="true" - export CYPRESS_imageSnaphots="true"
- yarn install
- yarn bootstrap - yarn bootstrap
- yarn build - yarn build
- yarn lint - yarn lint

View File

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

View File

@ -1,87 +0,0 @@
{
"footer.aboutus": {
"defaultMessage": "About Us"
},
"footer.contactus": {
"defaultMessage": "Contact Us"
},
"footer.donations": {
"defaultMessage": "PayPal Donations"
},
"footer.faq": {
"defaultMessage": "F.A.Q."
},
"footer.feedback": {
"defaultMessage": "Feedback"
},
"footer.opensource": {
"defaultMessage": "Open Source"
},
"footer.termsandconditions": {
"defaultMessage": "Term And Conditions"
},
"header.donthaveaccount": {
"defaultMessage": "Don't have an account ?"
},
"header.haveaccount": {
"defaultMessage": "Already have an account?"
},
"login.email": {
"defaultMessage": "Email"
},
"login.error": {
"defaultMessage": "The login.email address or login.password you entered is not valid."
},
"login.forgotpwd": {
"defaultMessage": "Forgot Password ?"
},
"login.hsqldbcofig": {
"defaultMessage": "Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL",
"description": "Missing production database configured"
},
"login.loginto": {
"defaultMessage": "Log Into Your Account"
},
"login.password": {
"defaultMessage": "Password"
},
"login.remberme": {
"defaultMessage": "Remember me"
},
"login.signin": {
"defaultMessage": "Sign In"
},
"login.signup": {
"defaultMessage": "Sign Up"
},
"login.userinactive": {
"defaultMessage": "Sorry, your account has not been activated yet. You'll receive a notification login.email when it becomes active. Stay tuned!."
},
"login.welcome": {
"defaultMessage": "Welcome"
},
"registration.become": {
"defaultMessage": "Become a member of our comunity"
},
"registration.email": {
"defaultMessage": "Email"
},
"registration.firstname": {
"defaultMessage": "First Name"
},
"registration.lastname": {
"defaultMessage": "Last Name"
},
"registration.password": {
"defaultMessage": "Password"
},
"registration.register": {
"defaultMessage": "Register"
},
"registration.signup": {
"defaultMessage": "Signing up is free and just take a moment"
},
"registration.termandconditions": {
"defaultMessage": "Terms of Service: Please check the WiseMapping Account information you've entered above, and review the Terms of Service here. By clicking on 'Register' below you are agreeing to the Terms of Service above and the Privacy Policy"
}
}

View File

@ -28,5 +28,8 @@ module.exports = {
resolve: { resolve: {
extensions: ['.js'], extensions: ['.js'],
}, },
plugins: [new CleanWebpackPlugin()], plugins: [new CleanWebpackPlugin({
dangerouslyAllowCleanPatternsOutsideProject: true,
dry: false,
})],
}; };

28
packages/editor/README.md Normal file
View File

@ -0,0 +1,28 @@
# Editor
React Component for the wisemapping editor.
## Usage
This is a work in progress and for now mindplot needs to be instantiated using the initCallback prop. Check `test/playground/map-render` for some usage examples.
import Editor from `@wisemapping/editor`;
ReactDOM.render(
<Editor
mapId={1}
memoryPersistence={false}
readOnlyMode={false}
locale="en"
onAction={(action) => console.log('action called:', action)}
initCallback={initialization}
/>,
document.getElementById('root'),
);
## i18n
Messages are translated in the `lang` folder, and then compiled to `src/compiled-lang` using the following command:
yarn compile lang/de.json --ast --out-file src/compiled-lang/de.json

View File

@ -15,7 +15,7 @@ context('Edit Topic', () => {
it('Change Font Size', () => { it('Change Font Size', () => {
cy.get('#fontSizeTip').click(); cy.get('#fontSizeTip').click();
cy.get('.popover #small').click(); cy.get('.popover #small').click({ force: true });
cy.get('[test-id=1] > text').invoke('attr', 'font-size').should('eq', '8.0625'); cy.get('[test-id=1] > text').invoke('attr', 'font-size').should('eq', '8.0625');
cy.matchImageSnapshot('changeFontSizeSmall'); cy.matchImageSnapshot('changeFontSizeSmall');

View File

@ -7,7 +7,7 @@ context('Change Topic shape', () => {
it('change to square shape', () => { it('change to square shape', () => {
cy.get('#topicShapeTip').click(); cy.get('#topicShapeTip').click();
cy.get('#rectagle').click(); cy.get('#rectagle').click({ force: true });
cy.get('[test-id=11] > rect').eq(1).invoke('attr', 'rx').should('eq', '0'); cy.get('[test-id=11] > rect').eq(1).invoke('attr', 'rx').should('eq', '0');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

@ -0,0 +1,14 @@
{
"editor.try-welcome": {
"defaultMessage": "Dieser Ausgabebereich zeigt einige der Mindmap-Editor-Funktionen!"
},
"editor.try-welcome-description": {
"defaultMessage": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen."
},
"login.signup": {
"defaultMessage": "Anmeldung"
},
"action.share": {
"defaultMessage": "Teilen"
}
}

View File

@ -0,0 +1,14 @@
{
"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."
},
"login.signup": {
"defaultMessage": "Sign Up"
},
"action.share": {
"defaultMessage": "Share"
}
}

View File

@ -0,0 +1,14 @@
{
"editor.try-welcome": {
"defaultMessage": "¡Este espacio de edición muestra algunas de las capacidades del editor de mapas mentales!"
},
"editor.try-welcome-description": {
"defaultMessage": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita."
},
"login.signup": {
"defaultMessage": "Crear cuenta"
},
"action.share": {
"defaultMessage": "Compartir"
}
}

View File

@ -0,0 +1,14 @@
{
"editor.try-welcome": {
"defaultMessage": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales !"
},
"editor.try-welcome-description": {
"defaultMessage": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales."
},
"login.signup": {
"defaultMessage": "S'inscrire"
},
"action.share": {
"defaultMessage": "Partager"
}
}

View File

@ -7,7 +7,9 @@
"playground": "webpack serve --config webpack.playground.js", "playground": "webpack serve --config webpack.playground.js",
"cy:run": "cypress run", "cy:run": "cypress run",
"test:integration": "start-server-and-test 'yarn playground' http-get://localhost:8081 'yarn cy:run'", "test:integration": "start-server-and-test 'yarn playground' http-get://localhost:8081 'yarn cy:run'",
"test": "yarn test:integration" "test": "yarn test:integration",
"extract": "for lang in {'es','en','fr','de'};do formatjs extract 'src/**/*.ts*' --ignore 'src/@types/**/*' --out-file lang/${lang}.json;done",
"compile": "formatjs compile"
}, },
"repository": "http://www.wisemapping.com", "repository": "http://www.wisemapping.com",
"author": "Paulo Veiga <pveiga@gmail.com>, Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>", "author": "Paulo Veiga <pveiga@gmail.com>, Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>",
@ -16,6 +18,7 @@
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7", "@babel/preset-react": "^7.16.7",
"@formatjs/cli": "^4.8.1",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.8.1", "@typescript-eslint/eslint-plugin": "^4.8.1",

4
packages/editor/src/@types/index.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module "*.svg" {
const content: any;
export default content;
}

View File

@ -0,0 +1,26 @@
{
"action.share": [
{
"type": 0,
"value": "Teilen"
}
],
"editor.try-welcome": [
{
"type": 0,
"value": "Dieser Ausgabebereich zeigt einige der Mindmap-Editor-Funktionen!"
}
],
"editor.try-welcome-description": [
{
"type": 0,
"value": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen."
}
],
"login.signup": [
{
"type": 0,
"value": "Anmeldung"
}
]
}

View File

@ -0,0 +1,26 @@
{
"action.share": [
{
"type": 0,
"value": "Share"
}
],
"editor.try-welcome": [
{
"type": 0,
"value": "This edition space showcases some of the mindmap editor capabilities !"
}
],
"editor.try-welcome-description": [
{
"type": 0,
"value": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
}
],
"login.signup": [
{
"type": 0,
"value": "Sign Up"
}
]
}

View File

@ -0,0 +1,26 @@
{
"action.share": [
{
"type": 0,
"value": "Compartir"
}
],
"editor.try-welcome": [
{
"type": 0,
"value": "¡Este espacio de edición muestra algunas de las capacidades del editor de mapas mentales!"
}
],
"editor.try-welcome-description": [
{
"type": 0,
"value": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita."
}
],
"login.signup": [
{
"type": 0,
"value": "Crear cuenta"
}
]
}

View File

@ -0,0 +1,26 @@
{
"action.share": [
{
"type": 0,
"value": "Partager"
}
],
"editor.try-welcome": [
{
"type": 0,
"value": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales !"
}
],
"editor.try-welcome-description": [
{
"type": 0,
"value": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales."
}
],
"login.signup": [
{
"type": 0,
"value": "S'inscrire"
}
]
}

View File

@ -0,0 +1,25 @@
import styled from 'styled-components';
const ActionButton = styled.div`
cursor: pointer;
margin: 0px 10px;
font-family: Arial, Helvetica, sans-serif;
user-select: none;
vertical-align: middle;
justify-content: center;
padding: 10px 25px;
font-size: 15px;
min-width: 64px;
box-sizing: border-box;
font-weight: 600;
border-radius: 9px;
color: white;
background-color: #ffa800;
display: inline-block;
&:hover {
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
`;
export default ActionButton;

View File

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import { StyledLogo } from './styled'; import { StyledLogo, Notifier } from './styled';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import KeyboardSvg from '../../../images/keyboard.svg'; import KeyboardSvg from '../../../images/keyboard.svg';
import AddSvg from '../../../images/add.svg'; import AddSvg from '../../../images/add.svg';
import MinusSvg from '../../../images/minus.svg'; import MinusSvg from '../../../images/minus.svg';
import CenterFocusSvg from '../../../images/center_focus.svg'; import CenterFocusSvg from '../../../images/center_focus.svg';
import ActionButton from '../action-button';
export type FooterPropsType = { export type FooterPropsType = {
showTryPanel?: boolean; showTryPanel?: boolean;
@ -35,15 +36,15 @@ const Footer = ({ showTryPanel }: FooterPropsType): React.ReactElement => {
</div> </div>
</div> </div>
<StyledLogo id="bottom-logo"></StyledLogo> <StyledLogo id="bottom-logo"></StyledLogo>
<div id="headerNotifier"></div> <Notifier id="headerNotifier"></Notifier>
{showTryPanel && ( {showTryPanel && (
<div id="tryInfoPanel"> <div id="tryInfoPanel">
<p>{intl.formatMessage({ id: 'editor.try-welcome' })}</p> <p>{intl.formatMessage({ id: 'editor.try-welcome' })}</p>
<p>{intl.formatMessage({ id: 'editor.try-welcome-description' })}</p> <p>{intl.formatMessage({ id: 'editor.try-welcome-description' })}</p>
<a href="/c/registration"> <a href="/c/registration">
<div className="actionButton"> <ActionButton>
{intl.formatMessage({ id: 'login.signup', defaultMessage: 'Sign Up' })} {intl.formatMessage({ id: 'login.signup', defaultMessage: 'Sign Up' })}
</div> </ActionButton>
</a> </a>
</div> </div>
)} )}

View File

@ -15,4 +15,17 @@ export const StyledLogo = styled.div`
background: url(${LogoTextBlackSvg}) no-repeat; background: url(${LogoTextBlackSvg}) no-repeat;
width: 90px; width: 90px;
height: 40px; height: 40px;
` `;
export const Notifier = styled.div`
border: 1px solid rgb(241, 163, 39);
background-color: rgb(252, 235, 192);
border-radius: 3px;
position: fixed;
padding: 5px 9px;
color: back;
white-space: nowrap;
margin-top: 5px;
display: none;
bottom: 10px;
`;

View File

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

View File

@ -0,0 +1,50 @@
import styled from 'styled-components';
export const HeaderContainer = styled.div`
width: 100%;
height: 0px;
background: #202020;
z-index: 1000;
position: absolute;
top: 0;
display: flex;
`;
export const ToolbarContainer = styled.div`
display: flex;
flex-direction: column;
flex: 1;
`;
export const ToolbarButton = styled.div`
width: 28px;
height: 28px;
text-align: center;
z-index: 4;
margin-top: 3px;
padding-top: 2px;
padding-left: 2px;
margin-left: 3px;
display: inline-block;
`;
export const ToolbarButtonExt = styled(ToolbarButton)`
width: 40px;
text-align: left;
padding-left: 5px;
`;
export const AccountButton = styled.div`
display: inline-block;
margin-top: 3px;
`;
export const ToolbarRightContainer = styled.div`
flex-shrink: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
height: 100%;
overflow: hidden;
`;

View File

@ -1,30 +0,0 @@
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

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

View File

@ -2,7 +2,21 @@ import React from 'react';
import Toolbar, { ToolbarActionType } from './components/toolbar'; import Toolbar, { ToolbarActionType } from './components/toolbar';
import Footer from './components/footer'; import Footer from './components/footer';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import * as mindplot from '@wisemapping/mindplot'; import {
$notify,
buildDesigner,
LocalStorageManager,
PersistenceManager,
RESTPersistenceManager,
DesignerOptionsBuilder,
Designer
} from '@wisemapping/mindplot';
import FR from './compiled-lang/fr.json';
import ES from './compiled-lang/es.json';
import EN from './compiled-lang/en.json';
import DE from './compiled-lang/de.json';
declare global { declare global {
var memoryPersistence: boolean; var memoryPersistence: boolean;
var readOnly: boolean; var readOnly: boolean;
@ -15,26 +29,43 @@ declare global {
var locale: string; var locale: string;
var mindmapLocked: boolean; var mindmapLocked: boolean;
var mindmapLockedMsg: string; var mindmapLockedMsg: string;
var mapTitle: string;
// used in mindplot
var designer: Designer;
var accountEmail: string;
} }
export type EditorPropsType = { export type EditorPropsType = {
initCallback?: (m: typeof mindplot) => () => void; initCallback?: (locale: string) => void;
mapId: number; mapId?: number;
memoryPersistence: boolean; isTryMode: boolean;
readOnlyMode: boolean; readOnlyMode: boolean;
locale?: string; locale?: string;
onAction: (action: ToolbarActionType) => void; onAction: (action: ToolbarActionType) => void;
}; };
const initMindplot = ({ const loadLocaleData = (locale: string) => {
PersistenceManager, switch (locale) {
RESTPersistenceManager, case 'fr':
LocalStorageManager, return FR;
DesignerOptionsBuilder, case 'en':
buildDesigner, return EN;
$notify, case 'es':
}: typeof mindplot) => () => { return ES;
let persistence: typeof PersistenceManager; case 'de':
return DE;
default:
return EN;
}
}
const initMindplot = (locale: string) => {
// Change page title ...
document.title = `${global.mapTitle} | WiseMapping `;
// Configure persistence manager ...
let persistence: PersistenceManager;
if (!global.memoryPersistence && !global.readOnly) { if (!global.memoryPersistence && !global.readOnly) {
persistence = new RESTPersistenceManager({ persistence = new RESTPersistenceManager({
documentUrl: '/c/restful/maps/{id}/document', documentUrl: '/c/restful/maps/{id}/document',
@ -45,8 +76,7 @@ const initMindplot = ({
}); });
} else { } else {
persistence = new LocalStorageManager( persistence = new LocalStorageManager(
`/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${ `/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${!global.isAuth ? '-pub' : ''
!global.isAuth ? '-pub' : ''
}`, }`,
true true
); );
@ -58,10 +88,14 @@ const initMindplot = ({
const options = DesignerOptionsBuilder.buildOptions({ const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: persistence, persistenceManager: persistence,
readOnly: Boolean(global.readOnly || false), readOnly: Boolean(global.readOnly || false),
mapId: global.mapId, mapId: String(global.mapId),
container: 'mindplot', container: 'mindplot',
zoom: zoomParam || global.userOptions ? global.userOptions.zoom : 1, zoom:
locale: global.locale, zoomParam ||
(global.userOptions?.zoom != undefined
? Number.parseFloat(global.userOptions.zoom as string)
: 0.8),
locale: locale,
}); });
// Build designer ... // Build designer ...
@ -69,7 +103,7 @@ const initMindplot = ({
// Load map from XML file persisted on disk... // Load map from XML file persisted on disk...
const instance = PersistenceManager.getInstance(); const instance = PersistenceManager.getInstance();
const mindmap = instance.load(global.mapId); const mindmap = instance.load(String(global.mapId));
designer.loadMap(mindmap); designer.loadMap(mindmap);
if (global.mindmapLocked) { if (global.mindmapLocked) {
@ -77,28 +111,26 @@ const initMindplot = ({
} }
}; };
export default function Editor({ const Editor = ({
initCallback = initMindplot, initCallback = initMindplot,
mapId, mapId,
memoryPersistence, isTryMode: isTryMode,
readOnlyMode,
locale = 'en', locale = 'en',
onAction, onAction,
}: EditorPropsType): React.ReactElement { }: EditorPropsType): React.ReactElement => {
React.useEffect(() => {
React.useEffect(initCallback(mindplot), []); initCallback(locale);
}, []);
return ( return (
<IntlProvider locale={locale} defaultLocale="en" messages={{}}> <IntlProvider locale={locale} messages={loadLocaleData(locale)}>
<div id="header">
<Toolbar <Toolbar
memoryPersistence={memoryPersistence} isTryMode={isTryMode}
readOnlyMode={readOnlyMode}
onAction={onAction} onAction={onAction}
/> />
</div>
<div id="mindplot"></div> <div id="mindplot"></div>
<Footer showTryPanel={memoryPersistence} /> <Footer showTryPanel={isTryMode} />
</IntlProvider> </IntlProvider>
); );
} }
export default Editor;

View File

@ -136,6 +136,7 @@ div.shareModalDialog {
align-items: flex-end; align-items: flex-end;
bottom: 20px; bottom: 20px;
right: 20px; right: 20px;
align-items: stretch;
} }
div#position { div#position {

View File

@ -1,80 +1,6 @@
@import "toolbar.css"; @import "toolbar.css";
div#header {
width: 100%;
height: 0px;
background: #202020;
z-index: 1000;
position: absolute;
top: 0;
}
div#headerActions span {
border-bottom: 3px solid rgb(247, 201, 49);
}
div#headerActions a {
color: white;
text-decoration: none;
}
div#headerNotifier {
border: 1px solid rgb(241, 163, 39);
background-color: rgb(252, 235, 192);
border-radius: 3px;
position: fixed;
padding: 5px 9px;
color: back;
white-space: nowrap;
margin-top: 5px;
display: none;
bottom: 10px;
}
div#toolbarRight {
float: right;
white-space: nowrap;
vertical-align: middle;
justify-content: center;
margin: 6px 10px;
height: 100%;
}
#account {
float: right;
display: inline;
}
#account >img {
width: 36x;
height: 36px;
}
#accountSettingsPanel{ #accountSettingsPanel{
padding:10px 10px; padding:10px 10px;
} }
#share {
margin: 0 30px;
}
.actionButton {
float: right;
cursor: pointer;
margin: 0px 10px;
font-family: Arial, Helvetica, sans-serif;
user-select: none;
vertical-align: middle;
justify-content: center;
padding: 10px 25px;
font-size: 15px;
min-width: 64px;
box-sizing: border-box;
font-weight: 600;
border-radius: 9px;
color: white;
background-color: #ffa800;
}
.actionButton:hover {
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}

View File

@ -27,6 +27,7 @@ div#footer-logo {
#floating-panel { #floating-panel {
bottom: 80px; bottom: 80px;
align-items: stretch;
} }
div#mindplot { div#mindplot {

View File

@ -2,6 +2,7 @@ import '../css/editor.css';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Editor from '../../../../src/index'; import Editor from '../../../../src/index';
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
global.accountName = 'Test User'; global.accountName = 'Test User';
global.accountEmail = 'test@example.com'; global.accountEmail = 'test@example.com';
@ -11,12 +12,7 @@ global.mapId = 'welcome';
global.locale = 'en'; global.locale = 'en';
const initialization = ({ const initialization = () => {
LocalStorageManager,
DesignerOptionsBuilder,
buildDesigner,
PersistenceManager
}) => () => {
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({ const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: p persistenceManager: p

View File

@ -2,10 +2,10 @@ import '../css/embedded.css';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Editor from '../../../../src/index'; import Editor from '../../../../src/index';
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
const initialization =
({ LocalStorageManager, DesignerOptionsBuilder, buildDesigner, PersistenceManager }) => const initialization = () => {
() => {
// Options has been defined in by a external file ? // Options has been defined in by a external file ?
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p }); const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p });

View File

@ -2,13 +2,10 @@ import '../css/viewmode.css';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Editor from '../../../../src/index'; import Editor from '../../../../src/index';
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
const initialization = ({
LocalStorageManager, const initialization = () => {
DesignerOptionsBuilder,
buildDesigner,
PersistenceManager
}) => () => {
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false }); const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false });

View File

@ -1,13 +1,25 @@
{ {
"include": [
"src/**/*"
],
"compilerOptions": { "compilerOptions": {
"jsx": "react",
"outDir": "./dist/", "outDir": "./dist/",
"sourceMap": true, "sourceMap": true,
"noImplicitAny": true, "noImplicitAny": false,
"module": "commonjs", "module": "amd",
"target": "es5", "moduleResolution": "node",
"jsx": "react", "strict": false,
"target": "es6",
"allowJs": true, "allowJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"declaration": true,
"rootDirs": [
"src",
],
"resolveJsonModule": true
}, },
"exclude": ["node_modules"] "exclude": [
"node_modules"
]
} }

View File

@ -1,6 +1,4 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = { module.exports = {
output: { output: {
@ -9,7 +7,9 @@ module.exports = {
publicPath: '', publicPath: '',
library: { library: {
type: 'umd', type: 'umd',
}, }, },
stats:{
errorDetails: true
}, },
entry: { entry: {
"editor.bundle": path.join(__dirname, 'src', 'index.tsx') "editor.bundle": path.join(__dirname, 'src', 'index.tsx')
@ -38,7 +38,4 @@ module.exports = {
}, },
], ],
}, },
plugins: [
new CleanWebpackPlugin(),
]
} }

View File

@ -36,7 +36,10 @@ const playgroundConfig = {
], ],
}, },
plugins: [ plugins: [
new CleanWebpackPlugin(), new CleanWebpackPlugin({
dangerouslyAllowCleanPatternsOutsideProject: true,
dry: false,
}),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: 'test/playground/map-render/images/favicon.ico', to: 'favicon.ico' }, { from: 'test/playground/map-render/images/favicon.ico', to: 'favicon.ico' },

View File

@ -1,5 +1,6 @@
const { merge } = require('webpack-merge'); const { merge } = require('webpack-merge');
const common = require('./webpack.common'); const common = require('./webpack.common');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const prodConfig = { const prodConfig = {
optimization: { optimization: {
@ -8,9 +9,12 @@ const prodConfig = {
}, },
externals: { externals: {
react: 'react', react: 'react',
reactDOM: 'react-dom', "react-dom": 'react-dom',
reactIntl: 'react-intl', "react-intl": 'react-intl',
}, },
plugins: [
new CleanWebpackPlugin(),
]
}; };
module.exports = merge(common, prodConfig); module.exports = merge(common, prodConfig);

View File

@ -25,19 +25,26 @@
"no-underscore-dangle": "off", "no-underscore-dangle": "off",
"no-plusplus": "off", "no-plusplus": "off",
"no-param-reassign": "off", "no-param-reassign": "off",
"max-len": [1,300], "max-len": [
1,
300
],
"class-methods-use-this": "off", "class-methods-use-this": "off",
"no-console": "off", "no-console": "off",
"no-unused-vars": ["error", { "args": "none" }],
"import/extensions": ["error", {
"ts": "never"
}],
// codebase contains many this aliases, fix in the future? // codebase contains many this aliases, fix in the future?
"@typescript-eslint/no-this-alias": "off", "@typescript-eslint/no-this-alias": "off",
// no-unused-vars already used // Remove once migration is completed ...
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/ban-ts-comment": "warn", "import/extensions": [
"import/no-extraneous-dependencies": ["warn", {"packageDir": "./", "devDependencies": false, "optionalDependencies": false, "peerDependencies": false}] "error",
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
]
}, },
"settings": { "settings": {
"import/resolver": { "import/resolver": {
@ -45,7 +52,10 @@
"config": "./webpack.common.js" "config": "./webpack.common.js"
}, },
"node": { "node": {
"extensions": [".js",".ts"] "extensions": [
".js",
".ts"
]
} }
} }
} }

View File

@ -2,8 +2,6 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"module": "ES6", "module": "ES6",
"paths": {
"@libraries/*": ["../../libraries/*"] }
}, },
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

View File

@ -1,12 +0,0 @@
declare module "@wisemapping/mindplot" {
const mindplot: {
Mindmap,
PersistenceManager,
Designer,
LocalStorageManager,
Menu,
DesignerBuilder,
};
export default mindplot;
}

View File

@ -1,13 +1,13 @@
{ {
"name": "@wisemapping/mindplot", "name": "@wisemapping/mindplot",
"version": "5.0.2", "version": "5.0.3",
"description": "WiseMapping - Mindplot Canvas Library", "description": "WiseMapping - Mindplot Canvas Library",
"homepage": "http://www.wisemapping.org/", "homepage": "http://www.wisemapping.org/",
"main": "dist/mindplot.js",
"directories": { "directories": {
"lib": "src", "lib": "src",
"test": "__tests__" "test": "__tests__"
}, },
"main": "src/index.ts",
"files": [ "files": [
"src", "src",
"assets", "assets",

View File

@ -21,56 +21,57 @@ import { $assert } from '@wisemapping/core-js';
import Point from '@wisemapping/web2d'; import Point from '@wisemapping/web2d';
import { Mindmap } from '..'; import { Mindmap } from '..';
import CommandContext from './CommandContext'; import CommandContext from './CommandContext';
import ControlPoint from './ControlPoint';
import Events from './Events'; import Events from './Events';
import NodeModel from './model/NodeModel'; import NodeModel from './model/NodeModel';
import RelationshipModel from './model/RelationshipModel'; import RelationshipModel from './model/RelationshipModel';
import Topic from './Topic'; import Topic from './Topic';
abstract class ActionDispatcher extends Events { abstract class ActionDispatcher extends Events {
static _instance: ActionDispatcher; private static _instance: ActionDispatcher;
constructor(commandContext: CommandContext) { constructor(commandContext: CommandContext) {
$assert(commandContext, 'commandContext can not be null'); $assert(commandContext, 'commandContext can not be null');
super(); super();
} }
abstract addRelationship(model: RelationshipModel, mindmap: Mindmap); abstract addRelationship(model: RelationshipModel, mindmap: Mindmap): void;
abstract addTopics(models: NodeModel[], parentTopicId: any[]); abstract addTopics(models: NodeModel[], parentTopicId: number[]): void;
abstract deleteEntities(topicsIds: number[], relIds: number[]); abstract deleteEntities(topicsIds: number[], relIds: number[]): void;
abstract dragTopic(topicId: number, position: Point, order: number, parentTopic: Topic); abstract dragTopic(topicId: number, position: Point, order: number, parentTopic: Topic): void;
abstract moveTopic(topicId: number, position: any); abstract moveTopic(topicId: number, position: Point): void;
abstract moveControlPoint(ctrlPoint: this, point: any); abstract moveControlPoint(ctrlPoint: ControlPoint, point: Point): void;
abstract changeFontFamilyToTopic(topicIds: number[], fontFamily: string); abstract changeFontFamilyToTopic(topicIds: number[], fontFamily: string): void;
abstract changeFontStyleToTopic(topicsIds: number[]); abstract changeFontStyleToTopic(topicsIds: number[]): void;
abstract changeFontColorToTopic(topicsIds: number[], color: string); abstract changeFontColorToTopic(topicsIds: number[], color: string): void;
abstract changeFontSizeToTopic(topicsIds: number[], size: number); abstract changeFontSizeToTopic(topicsIds: number[], size: number): void;
abstract changeBackgroundColorToTopic(topicsIds: number[], color: string); abstract changeBackgroundColorToTopic(topicsIds: number[], color: string): void;
abstract changeBorderColorToTopic(topicsIds: number[], color: string); abstract changeBorderColorToTopic(topicsIds: number[], color: string): void;
abstract changeShapeTypeToTopic(topicsIds: number[], shapeType: string); abstract changeShapeTypeToTopic(topicsIds: number[], shapeType: string): void;
abstract changeFontWeightToTopic(topicsIds: number[]); abstract changeFontWeightToTopic(topicsIds: number[]): void;
abstract changeTextToTopic(topicsIds: number[], text: string); abstract changeTextToTopic(topicsIds: number[], text: string): void;
abstract shrinkBranch(topicsIds: number[], collapse: boolean); abstract shrinkBranch(topicsIds: number[], collapse: boolean): void;
abstract addFeatureToTopic(topicId: number, type: string, attributes: object); abstract addFeatureToTopic(topicId: number, type: string, attributes: object): void;
abstract changeFeatureToTopic(topicId: number, featureId: any, attributes: object); abstract changeFeatureToTopic(topicId: number, featureId: number, attributes: object): void;
abstract removeFeatureFromTopic(topicId: number, featureId: number); abstract removeFeatureFromTopic(topicId: number, featureId: number): void;
static setInstance = (dispatcher: ActionDispatcher) => { static setInstance = (dispatcher: ActionDispatcher) => {
this._instance = dispatcher; this._instance = dispatcher;

View File

@ -21,7 +21,11 @@ import Topic from './Topic';
import Shape from './util/Shape'; import Shape from './util/Shape';
class CentralTopic extends Topic { class CentralTopic extends Topic {
_registerEvents() { _buildDragShape() {
// Ignore ..
}
_registerEvents(): void {
super._registerEvents(); super._registerEvents();
// This disable the drag of the central topic. // This disable the drag of the central topic.
@ -31,17 +35,14 @@ class CentralTopic extends Topic {
}); });
} }
/** */ workoutIncomingConnectionPoint(): Point {
workoutIncomingConnectionPoint() {
return this.getPosition(); return this.getPosition();
} }
/** */ setCursor(type: string) {
setCursor(type) {
super.setCursor(type === 'move' ? 'default' : type); super.setCursor(type === 'move' ? 'default' : type);
} }
/** */
updateTopicShape() { updateTopicShape() {
// Overwite behaviour ... // Overwite behaviour ...
} }
@ -58,7 +59,7 @@ class CentralTopic extends Topic {
} }
/** */ /** */
workoutOutgoingConnectionPoint(targetPosition) { workoutOutgoingConnectionPoint(targetPosition: Point) {
$assert(targetPosition, 'targetPoint can not be null'); $assert(targetPosition, 'targetPoint can not be null');
const pos = this.getPosition(); const pos = this.getPosition();
const isAtRight = Shape.isAtRight(targetPosition, pos); const isAtRight = Shape.isAtRight(targetPosition, pos);

View File

@ -23,8 +23,11 @@ abstract class Command {
static _uuid: number; static _uuid: number;
private _discardDuplicated: string;
constructor() { constructor() {
this._id = Command._nextUUID(); this._id = Command._nextUUID();
this._discardDuplicated = undefined;
} }
abstract execute(commandContext: CommandContext): void; abstract execute(commandContext: CommandContext): void;
@ -42,6 +45,14 @@ abstract class Command {
this._uuid += 1; this._uuid += 1;
return this._uuid; return this._uuid;
} }
get discardDuplicated(): string {
return this._discardDuplicated;
}
set discardDuplicated(value: string) {
this._discardDuplicated = value;
}
} }
export default Command; export default Command;

View File

@ -20,8 +20,33 @@ import { $defined } from '@wisemapping/core-js';
import Shape from './util/Shape'; import Shape from './util/Shape';
import ActionDispatcher from './ActionDispatcher'; import ActionDispatcher from './ActionDispatcher';
import Workspace from './Workspace';
class ControlPoint { class ControlPoint {
private control1: Elipse;
private control2: Elipse;
private _controlPointsController: Elipse[];
private _controlLines: Line[];
private _isBinded: boolean;
_line: Line;
private _workspace: Workspace;
private _endPoint: any[];
private _orignalCtrlPoint: any;
private _controls: any;
private _mouseMoveFunction: (e: Event) => void;
private _mouseUpFunction: (e: Event) => void;
constructor() { constructor() {
this.control1 = new Elipse({ this.control1 = new Elipse({
width: 6, width: 6,
@ -70,7 +95,7 @@ class ControlPoint {
}); });
} }
setLine(line) { setLine(line: Line) {
if ($defined(this._line)) { if ($defined(this._line)) {
this._removeLine(); this._removeLine();
} }
@ -93,7 +118,7 @@ class ControlPoint {
if ($defined(this._line)) this._createControlPoint(); if ($defined(this._line)) this._createControlPoint();
} }
_createControlPoint() { private _createControlPoint() {
this._controls = this._line.getLine().getControlPoints(); this._controls = this._line.getLine().getControlPoints();
let pos = this._line.getLine().getFrom(); let pos = this._line.getLine().getFrom();
this._controlPointsController[0].setPosition( this._controlPointsController[0].setPosition(
@ -117,11 +142,11 @@ class ControlPoint {
); );
} }
_removeLine() { private _removeLine() {
// Overwrite default behaviour ... // Overwrite default behaviour ...
} }
_mouseDown(event, point, me) { private _mouseDown(event: Event, point, me) {
if (!this._isBinded) { if (!this._isBinded) {
this._isBinded = true; this._isBinded = true;
this._mouseMoveFunction = (e) => { this._mouseMoveFunction = (e) => {
@ -129,7 +154,7 @@ class ControlPoint {
}; };
this._workspace.getScreenManager().addEvent('mousemove', this._mouseMoveFunction); this._workspace.getScreenManager().addEvent('mousemove', this._mouseMoveFunction);
this._mouseUpFunction = (e) => { this._mouseUpFunction = (e: Event) => {
me._mouseUp(e, point, me); me._mouseUp(e, point, me);
}; };
this._workspace.getScreenManager().addEvent('mouseup', this._mouseUpFunction); this._workspace.getScreenManager().addEvent('mouseup', this._mouseUpFunction);
@ -139,7 +164,7 @@ class ControlPoint {
return false; return false;
} }
_mouseMoveEvent(event, point) { private _mouseMoveEvent(event: MouseEvent, point: Point) {
const screen = this._workspace.getScreenManager(); const screen = this._workspace.getScreenManager();
const pos = screen.getWorkspaceMousePosition(event); const pos = screen.getWorkspaceMousePosition(event);
@ -162,7 +187,7 @@ class ControlPoint {
this._line.getLine().updateLine(point); this._line.getLine().updateLine(point);
} }
_mouseUp(event, point) { private _mouseUp(event: MouseEvent, point: Point) {
this._workspace.getScreenManager().removeEvent('mousemove', this._mouseMoveFunction); this._workspace.getScreenManager().removeEvent('mousemove', this._mouseMoveFunction);
this._workspace.getScreenManager().removeEvent('mouseup', this._mouseUpFunction); this._workspace.getScreenManager().removeEvent('mouseup', this._mouseUpFunction);
@ -171,13 +196,13 @@ class ControlPoint {
this._isBinded = false; this._isBinded = false;
} }
_mouseClick(event) { _mouseClick(event: MouseEvent) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
return false; return false;
} }
setVisibility(visible) { setVisibility(visible: boolean) {
if (visible) { if (visible) {
this._controlLines[0].moveToFront(); this._controlLines[0].moveToFront();
this._controlLines[1].moveToFront(); this._controlLines[1].moveToFront();
@ -190,7 +215,7 @@ class ControlPoint {
this._controlLines[1].setVisibility(visible); this._controlLines[1].setVisibility(visible);
} }
addToWorkspace(workspace) { addToWorkspace(workspace: Workspace): void {
this._workspace = workspace; this._workspace = workspace;
workspace.append(this._controlPointsController[0]); workspace.append(this._controlPointsController[0]);
workspace.append(this._controlPointsController[1]); workspace.append(this._controlPointsController[1]);
@ -198,7 +223,7 @@ class ControlPoint {
workspace.append(this._controlLines[1]); workspace.append(this._controlLines[1]);
} }
removeFromWorkspace(workspace) { removeFromWorkspace(workspace: Workspace) {
this._workspace = null; this._workspace = null;
workspace.removeChild(this._controlPointsController[0]); workspace.removeChild(this._controlPointsController[0]);
workspace.removeChild(this._controlPointsController[1]); workspace.removeChild(this._controlPointsController[1]);
@ -206,20 +231,21 @@ class ControlPoint {
workspace.removeChild(this._controlLines[1]); workspace.removeChild(this._controlLines[1]);
} }
getControlPoint(index) { getControlPoint(index: number): ControlPoint {
return this._controls[index]; return this._controls[index];
} }
getOriginalEndPoint(index) { getOriginalEndPoint(index: number) {
return this._endPoint[index]; return this._endPoint[index];
} }
getOriginalCtrlPoint(index) { getOriginalCtrlPoint(index: number): ControlPoint {
return this._orignalCtrlPoint[index]; return this._orignalCtrlPoint[index];
} }
static FROM = 0;
static TO = 1;
} }
ControlPoint.FROM = 0;
ControlPoint.TO = 1;
export default ControlPoint; export default ControlPoint;

View File

@ -0,0 +1,21 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type CursorType = 'default' | 'move';
export default CursorType;

View File

@ -56,6 +56,7 @@ import { DesignerOptions } from './DesignerOptionsBuilder';
import MainTopic from './MainTopic'; import MainTopic from './MainTopic';
import DragTopic from './DragTopic'; import DragTopic from './DragTopic';
import CentralTopic from './CentralTopic'; import CentralTopic from './CentralTopic';
import FeatureType from './model/FeatureType';
class Designer extends Events { class Designer extends Events {
private _mindmap: Mindmap; private _mindmap: Mindmap;
@ -136,8 +137,8 @@ class Designer extends Events {
} }
private _registerWheelEvents(): void { private _registerWheelEvents(): void {
const zoomFactor = 1.006; const zoomFactor = 1.02;
document.addEventListener('wheel', (event) => { document.addEventListener('wheel', (event: WheelEvent) => {
if (event.deltaX > 0 || event.deltaY > 0) { if (event.deltaX > 0 || event.deltaY > 0) {
this.zoomOut(zoomFactor); this.zoomOut(zoomFactor);
} else { } else {
@ -151,14 +152,14 @@ class Designer extends Events {
return this._actionDispatcher; return this._actionDispatcher;
} }
// @ts-ignore addEvent(type: string, listener): Events {
addEvent(type: string, listener: any): void {
if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) { if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) {
const editor = TopicEventDispatcher.getInstance(); const editor = TopicEventDispatcher.getInstance();
editor.addEvent(type, listener); editor.addEvent(type, listener);
} else { } else {
super.addEvent(type, listener); super.addEvent(type, listener);
} }
return this;
} }
private _registerMouseEvents() { private _registerMouseEvents() {
@ -208,7 +209,7 @@ class Designer extends Events {
dragManager.addEvent('dragging', (event: MouseEvent, dragTopic: DragTopic) => { dragManager.addEvent('dragging', (event: MouseEvent, dragTopic: DragTopic) => {
dragTopic.updateFreeLayout(event); dragTopic.updateFreeLayout(event);
if (!dragTopic.isFreeLayoutOn(event)) { if (!dragTopic.isFreeLayoutOn()) {
// The node is being drag. Is the connection still valid ? // The node is being drag. Is the connection still valid ?
dragConnector.checkConnection(dragTopic); dragConnector.checkConnection(dragTopic);
@ -354,6 +355,20 @@ class Designer extends Events {
} }
} }
shrinkSelectedBranch() {
const nodes = this.getModel().filterSelectedTopics();
if (nodes.length <= 0 || nodes.length !== 1) {
// If there are more than one node selected,
$notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE'));
return;
}
// Execute event ...
const topic = nodes[0];
if (topic.getType() !== 'CentralTopic') {
this._actionDispatcher.shrinkBranch([topic.getId()], !topic.areChildrenShrunken());
}
}
copyToClipboard(): void { copyToClipboard(): void {
let topics = this.getModel().filterSelectedTopics(); let topics = this.getModel().filterSelectedTopics();
if (topics.length <= 0) { if (topics.length <= 0) {
@ -876,7 +891,7 @@ class Designer extends Events {
addIconType(iconType: string): void { addIconType(iconType: string): void {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id, { this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id as FeatureType, {
id: iconType, id: iconType,
}); });
} }

View File

@ -33,25 +33,35 @@ export function buildDesigner(options: DesignerOptions): Designer {
// Register load events ... // Register load events ...
designer = new Designer(options, divContainer); designer = new Designer(options, divContainer);
designer.addEvent('loadSuccess', () => { designer.addEvent('loadSuccess', () => {
// @ts-ignore globalThis.mindmapLoadReady = true;
window.mindmapLoadReady = true;
console.log('Map loadded successfully'); console.log('Map loadded successfully');
}); });
const onerrorFn = (message: string, url, lineNo) => { const onerrorFn = (msg: string, url: string, lineNo: number, columnNo: number, error: Error) => {
// Close loading dialog ... const message = [
// @ts-ignore `Message: ${msg}`,
if (window.waitDialog) { `URL: ${url}`,
// @ts-ignore `Line: ${lineNo}`,
window.waitDialog.close(); `Column: ${columnNo}`,
// @ts-ignore ].join(' - ');
window.waitDialog = null; console.log(message);
}
// Send error to server ...
$.ajax({
method: 'post',
url: '/c/restful/logger/editor',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
data: JSON.stringify({
jsErrorMsg: message,
jsStack: JSON.stringify(error),
userAgent: navigator.userAgent,
mapId: options.mapId,
}),
});
// Open error dialog only in case of mindmap loading errors. The rest of the error are reported but not display the dialog. // Open error dialog only in case of mindmap loading errors. The rest of the error are reported but not display the dialog.
// Remove this in the near future. // Remove this in the near future.
// @ts-ignore if (!globalThis.mindmapLoadReady) {
if (!window.mindmapLoadReady) {
$notifyModal($msg('UNEXPECTED_ERROR_LOADING')); $notifyModal($msg('UNEXPECTED_ERROR_LOADING'));
} }
}; };
@ -64,7 +74,7 @@ export function buildDesigner(options: DesignerOptions): Designer {
// Register toolbar event ... // Register toolbar event ...
if ($('#toolbar').length) { if ($('#toolbar').length) {
const menu = new Menu(designer, 'toolbar', options.mapId ? options.mapId : 'unknown'); const menu = new Menu(designer, 'toolbar');
// If a node has focus, focus can be move to another node using the keys. // If a node has focus, focus can be move to another node using the keys.
designer.cleanScreen = () => { designer.cleanScreen = () => {

View File

@ -18,41 +18,48 @@
import $ from 'jquery'; import $ from 'jquery';
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import Keyboard from './Keyboard'; import Keyboard from './Keyboard';
import { Designer } from '..';
import Topic from './Topic';
class DesignerKeyboard extends Keyboard { class DesignerKeyboard extends Keyboard {
constructor(designer) { // eslint-disable-next-line no-use-before-define
super(designer); static _instance: DesignerKeyboard;
constructor(designer: Designer) {
super();
$assert(designer, 'designer can not be null'); $assert(designer, 'designer can not be null');
this._registerEvents(designer); this._registerEvents(designer);
} }
_registerEvents(designer) { private _registerEvents(designer: Designer) {
// Try with the keyboard .. // Try with the keyboard ..
const model = designer.getModel(); const model = designer.getModel();
this.addShortcut( this.addShortcut(
['backspace'], (event) => { ['backspace'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.deleteSelectedEntities(); designer.deleteSelectedEntities();
}, },
); );
this.addShortcut( this.addShortcut(
['space'], () => { ['space'], (event: Event) => {
designer.shrinkSelectedBranch(); designer.shrinkSelectedBranch();
event.preventDefault();
event.stopPropagation();
}, },
); );
this.addShortcut( this.addShortcut(
['f2'], (event) => { ['f2'], (event: Event) => {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
const node = model.selectedTopic(); const node = model.selectedTopic();
if (node) { if (node) {
node.showTextEditor(); node.showTextEditor(node.getText());
} }
}, },
); );
this.addShortcut( this.addShortcut(
['del'], (event) => { ['del'], (event: Event) => {
designer.deleteSelectedEntities(); designer.deleteSelectedEntities();
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -64,63 +71,63 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
['insert'], (event) => { ['insert'], (event: Event) => {
designer.createChildForSelectedNode(); designer.createChildForSelectedNode();
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
}, },
); );
this.addShortcut( this.addShortcut(
['tab'], (event) => { ['tab'], (eventevent: Event) => {
designer.createChildForSelectedNode(); designer.createChildForSelectedNode();
event.preventDefault(); eventevent.preventDefault();
event.stopPropagation(); eventevent.stopPropagation();
}, },
); );
this.addShortcut( this.addShortcut(
['meta+enter'], (event) => { ['meta+enter'], (eventevent: Event) => {
event.preventDefault(); eventevent.preventDefault();
event.stopPropagation(); eventevent.stopPropagation();
designer.createChildForSelectedNode(); designer.createChildForSelectedNode();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+z', 'meta+z'], (event) => { ['ctrl+z', 'meta+z'], (event: Event) => {
event.preventDefault(event); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.undo(); designer.undo();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+c', 'meta+c'], (event) => { ['ctrl+c', 'meta+c'], (event: Event) => {
event.preventDefault(event); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.copyToClipboard(); designer.copyToClipboard();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+v', 'meta+v'], (event) => { ['ctrl+v', 'meta+v'], (event: Event) => {
event.preventDefault(event); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.pasteClipboard(); designer.pasteClipboard();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+shift+z', 'meta+shift+z', 'ctrl+y', 'meta+y'], (event) => { ['ctrl+shift+z', 'meta+shift+z', 'ctrl+y', 'meta+y'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.redo(); designer.redo();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+a', 'meta+a'], (event) => { ['ctrl+a', 'meta+a'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
designer.selectAll(); designer.selectAll();
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+b', 'meta+b'], (event) => { ['ctrl+b', 'meta+b'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -128,14 +135,14 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+s', 'meta+s'], (event) => { ['ctrl+s', 'meta+s'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
$(document).find('#save').trigger('click'); $(document).find('#save').trigger('click');
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+i', 'meta+i'], (event) => { ['ctrl+i', 'meta+i'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -143,7 +150,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
['ctrl+shift+a', 'meta+shift+a'], (event) => { ['ctrl+shift+a', 'meta+shift+a'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -151,7 +158,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
['meta+=', 'ctrl+='], (event) => { ['meta+=', 'ctrl+='], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -159,7 +166,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
['meta+-', 'ctrl+-'], (event) => { ['meta+-', 'ctrl+-'], (event: Event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -168,7 +175,7 @@ class DesignerKeyboard extends Keyboard {
); );
const me = this; const me = this;
this.addShortcut( this.addShortcut(
'right', (event) => { 'right', (event: Event) => {
const node = model.selectedTopic(); const node = model.selectedTopic();
if (node) { if (node) {
if (node.isCentralTopic()) { if (node.isCentralTopic()) {
@ -187,7 +194,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
'left', (event) => { 'left', (event: Event) => {
const node = model.selectedTopic(); const node = model.selectedTopic();
if (node) { if (node) {
if (node.isCentralTopic()) { if (node.isCentralTopic()) {
@ -206,7 +213,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
'up', (event) => { 'up', (event: Event) => {
const node = model.selectedTopic(); const node = model.selectedTopic();
if (node) { if (node) {
if (!node.isCentralTopic()) { if (!node.isCentralTopic()) {
@ -221,7 +228,7 @@ class DesignerKeyboard extends Keyboard {
}, },
); );
this.addShortcut( this.addShortcut(
'down', (event) => { 'down', (event: Event) => {
const node = model.selectedTopic(); const node = model.selectedTopic();
if (node) { if (node) {
if (!node.isCentralTopic()) { if (!node.isCentralTopic()) {
@ -238,7 +245,7 @@ class DesignerKeyboard extends Keyboard {
const excludes = ['esc', 'escape', 'f1', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12']; const excludes = ['esc', 'escape', 'f1', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'];
$(document).on('keypress', (event) => { $(document).on('keypress', (event) => {
let keyCode; let keyCode: number;
// Firefox doesn't skip special keys for keypress event... // Firefox doesn't skip special keys for keypress event...
if (event.key && excludes.includes(event.key.toLowerCase())) { if (event.key && excludes.includes(event.key.toLowerCase())) {
return; return;
@ -250,8 +257,10 @@ class DesignerKeyboard extends Keyboard {
keyCode = event.keyCode; keyCode = event.keyCode;
} }
const specialKey = $.hotkeys.specialKeys[keyCode]; // eslint-disable-next-line @typescript-eslint/no-explicit-any
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !$.hotkeys.shiftNums[keyCode]) { const jq: any = $;
const specialKey = jq.hotkeys.specialKeys[keyCode];
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !jq.hotkeys.shiftNums[keyCode]) {
const nodes = designer.getModel().filterSelectedTopics(); const nodes = designer.getModel().filterSelectedTopics();
if (nodes.length > 0) { if (nodes.length > 0) {
// If a modifier is press, the key selected must be ignored. // If a modifier is press, the key selected must be ignored.
@ -266,7 +275,7 @@ class DesignerKeyboard extends Keyboard {
}); });
} }
_goToBrother(designer, node, direction) { private _goToBrother(designer: Designer, node: Topic, direction) {
const parent = node.getParent(); const parent = node.getParent();
if (parent) { if (parent) {
const brothers = parent.getChildren(); const brothers = parent.getChildren();
@ -305,7 +314,7 @@ class DesignerKeyboard extends Keyboard {
} }
} }
_goToSideChild(designer, node, side) { private _goToSideChild(designer: Designer, node: Topic, side: 'LEFT' | 'RIGHT') {
const children = node.getChildren(); const children = node.getChildren();
if (children.length > 0) { if (children.length > 0) {
let target = children[0]; let target = children[0];
@ -331,14 +340,14 @@ class DesignerKeyboard extends Keyboard {
} }
} }
_goToParent(designer, node) { private _goToParent(designer: Designer, node: Topic) {
const parent = node.getParent(); const parent = node.getParent();
if (parent) { if (parent) {
this._goToNode(designer, parent); this._goToNode(designer, parent);
} }
} }
_goToChild(designer, node) { private _goToChild(designer, node) {
const children = node.getChildren(); const children = node.getChildren();
if (children.length > 0) { if (children.length > 0) {
let target = children[0]; let target = children[0];
@ -354,16 +363,19 @@ class DesignerKeyboard extends Keyboard {
} }
} }
_goToNode(designer, node) { private _goToNode(designer: Designer, node: Topic) {
// First deselect all the nodes ... // First deselect all the nodes ...
designer.deselectAll(); designer.deselectAll();
// Give focus to the selected node.... // Give focus to the selected node....
node.setOnFocus(true); node.setOnFocus(true);
} }
}
DesignerKeyboard.specialKeys = { static register = function register(designer: Designer) {
this._instance = new DesignerKeyboard(designer);
};
static specialKeys = {
8: 'backspace', 8: 'backspace',
9: 'tab', 9: 'tab',
10: 'return', 10: 'return',
@ -421,12 +433,9 @@ DesignerKeyboard.specialKeys = {
224: 'meta', 224: 'meta',
}; };
DesignerKeyboard.register = function register(designer) { static getInstance() {
this._instance = new DesignerKeyboard(designer);
};
DesignerKeyboard.getInstance = function getInstance() {
return this._instance; return this._instance;
}; }
}
export default DesignerKeyboard; export default DesignerKeyboard;

View File

@ -17,11 +17,11 @@
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import PersistenceManager from './PersistenceManager'; import PersistenceManager from './PersistenceManager';
import { Size } from './Size'; import SizeType from './SizeType';
export type DesignerOptions = { export type DesignerOptions = {
zoom: number, zoom: number,
containerSize?: Size, containerSize?: SizeType,
readOnly?: boolean, readOnly?: boolean,
mapId?: string, mapId?: string,
container: string, container: string,

View File

@ -16,15 +16,23 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import Command from './Command';
import CommandContext from './CommandContext';
class DesignerUndoManager { class DesignerUndoManager {
private _undoQueue: Command[];
private _redoQueue: Command[];
private _baseId: number;
constructor() { constructor() {
this._undoQueue = []; this._undoQueue = [];
this._redoQueue = []; this._redoQueue = [];
this._baseId = 0; this._baseId = 0;
} }
enqueue(command) { enqueue(command: Command) {
$assert(command, 'Command can not be null'); $assert(command, 'Command can not be null');
const { length } = this._undoQueue; const { length } = this._undoQueue;
if (command.discardDuplicated && length > 0) { if (command.discardDuplicated && length > 0) {
@ -39,7 +47,7 @@ class DesignerUndoManager {
this._redoQueue = []; this._redoQueue = [];
} }
execUndo(commandContext) { execUndo(commandContext: CommandContext) {
if (this._undoQueue.length > 0) { if (this._undoQueue.length > 0) {
const command = this._undoQueue.pop(); const command = this._undoQueue.pop();
this._redoQueue.push(command); this._redoQueue.push(command);

View File

@ -17,9 +17,25 @@
*/ */
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import DragTopic from './DragTopic'; import DragTopic from './DragTopic';
import EventBusDispatcher from './layout/EventBusDispatcher';
import Workspace from './Workspace';
class DragManager { class DragManager {
constructor(workspace, eventDispatcher) { private _workspace: Workspace;
private _designerModel: Workspace;
private _isDragInProcess: boolean;
private _eventDispatcher: EventBusDispatcher;
private _listeners;
private _mouseMoveListener;
private _mouseUpListener;
constructor(workspace: Workspace, eventDispatcher: EventBusDispatcher) {
this._workspace = workspace; this._workspace = workspace;
this._designerModel = workspace; this._designerModel = workspace;
this._listeners = {}; this._listeners = {};
@ -34,7 +50,7 @@ class DragManager {
const screen = workspace.getScreenManager(); const screen = workspace.getScreenManager();
const dragManager = this; const dragManager = this;
const me = this; const me = this;
const mouseDownListener = function mouseDownListener(event) { const mouseDownListener = function mouseDownListener() {
if (workspace.isWorkspaceEventsEnabled()) { if (workspace.isWorkspaceEventsEnabled()) {
// Disable double drag... // Disable double drag...
workspace.enableWorkspaceEvents(false); workspace.enableWorkspaceEvents(false);
@ -62,11 +78,11 @@ class DragManager {
node.addEvent('mousedown', mouseDownListener); node.addEvent('mousedown', mouseDownListener);
} }
remove(node) { remove() {
throw new Error('Not implemented: DragManager.prototype.remove'); throw new Error('Not implemented: DragManager.prototype.remove');
} }
_buildMouseMoveListener(workspace, dragNode, dragManager) { protected _buildMouseMoveListener(workspace: Workspace, dragNode, dragManager: DragManager) {
const screen = workspace.getScreenManager(); const screen = workspace.getScreenManager();
const me = this; const me = this;
const result = (event) => { const result = (event) => {
@ -98,7 +114,7 @@ class DragManager {
return result; return result;
} }
_buildMouseUpListener(workspace, node, dragNode, dragManager) { protected _buildMouseUpListener(workspace: Workspace, node, dragNode, dragManager: DragManager) {
const screen = workspace.getScreenManager(); const screen = workspace.getScreenManager();
const me = this; const me = this;
const result = (event) => { const result = (event) => {

View File

@ -210,9 +210,7 @@ class DragTopic {
return this.getConnectedToTopic() != null; return this.getConnectedToTopic() != null;
} }
isFreeLayoutOn(dragTopic) { isFreeLayoutOn() {
// return this._isFreeLayoutEnabled;
// Disable free layout ...
return false; return false;
} }
} }

View File

@ -17,15 +17,17 @@
*/ */
class Events { class Events {
private $events;
constructor() { constructor() {
this.$events = {}; this.$events = {};
} }
static _removeOn(string) { static _removeOn(string: string) {
return string.replace(/^on([A-Z])/, (full, first) => first.toLowerCase()); return string.replace(/^on([A-Z])/, (full, first) => first.toLowerCase());
} }
addEvent(typeName, fn, internal) { addEvent(typeName: string, fn?, internal?: boolean): Events {
const type = Events._removeOn(typeName); const type = Events._removeOn(typeName);
// Add function had not been added yet // Add function had not been added yet
@ -36,12 +38,11 @@ class Events {
} }
// Mark reference ... // Mark reference ...
// eslint-disable-next-line no-param-reassign
fn.internal = Boolean(internal); fn.internal = Boolean(internal);
return this; return this;
} }
fireEvent(typeName, eventArgs, delay) { fireEvent(typeName: string, eventArgs?, delay?: boolean): Events {
const type = Events._removeOn(typeName); const type = Events._removeOn(typeName);
const events = this.$events[type]; const events = this.$events[type];
if (!events) return this; if (!events) return this;
@ -57,7 +58,7 @@ class Events {
return this; return this;
} }
removeEvent(typeName, fn) { removeEvent(typeName: string, fn?): Events {
const type = Events._removeOn(typeName); const type = Events._removeOn(typeName);
const events = this.$events[type]; const events = this.$events[type];
if (events && !fn.internal) { if (events && !fn.internal) {

View File

@ -16,24 +16,29 @@
* limitations under the License. * limitations under the License.
*/ */
import $ from 'jquery'; import $ from 'jquery';
import { Mindmap } from '..';
import PersistenceManager from './PersistenceManager'; import PersistenceManager from './PersistenceManager';
class LocalStorageManager extends PersistenceManager { class LocalStorageManager extends PersistenceManager {
constructor(documentUrl, forceLoad) { private documentUrl: string;
private forceLoad: boolean;
constructor(documentUrl: string, forceLoad: boolean) {
super(); super();
this.documentUrl = documentUrl; this.documentUrl = documentUrl;
this.forceLoad = forceLoad; this.forceLoad = forceLoad;
} }
saveMapXml(mapId, mapXml, pref, saveHistory, events) { saveMapXml(mapId: string, mapXml: string) {
localStorage.setItem(`${mapId}-xml`, mapXml); localStorage.setItem(`${mapId}-xml`, mapXml);
} }
discardChanges(mapId) { discardChanges(mapId: string) {
localStorage.removeItem(`${mapId}-xml`); localStorage.removeItem(`${mapId}-xml`);
} }
loadMapDom(mapId) { loadMapDom(mapId: string) {
let xml = localStorage.getItem(`${mapId}-xml`); let xml = localStorage.getItem(`${mapId}-xml`);
if (xml == null || this.forceLoad) { if (xml == null || this.forceLoad) {
$.ajax({ $.ajax({
@ -58,7 +63,7 @@ class LocalStorageManager extends PersistenceManager {
return $.parseXML(xml); return $.parseXML(xml);
} }
unlockMap(mindmap) { unlockMap(mindmap: Mindmap) {
// Ignore, no implementation required ... // Ignore, no implementation required ...
} }
} }

View File

@ -16,25 +16,30 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import { Point, Group } from '@wisemapping/web2d'; import { Point, Group, ElementClass } from '@wisemapping/web2d';
import Topic from './Topic'; import Topic from './Topic';
import { TopicShape } from './model/INodeModel'; import { TopicShape } from './model/INodeModel';
import Shape from './util/Shape'; import Shape from './util/Shape';
import NodeModel from './model/NodeModel';
import Workspace from './Workspace';
import SizeType from './SizeType';
class MainTopic extends Topic { class MainTopic extends Topic {
private INNER_RECT_ATTRIBUTES: { stroke: string; };
/** /**
* @extends mindplot.Topic * @extends mindplot.Topic
* @constructs * @constructs
* @param model * @param model
* @param options * @param options
*/ */
constructor(model, options) { constructor(model: NodeModel, options) {
super(model, options); super(model, options);
this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' }; this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' };
} }
_buildDragShape() { _buildDragShape(): ElementClass {
const innerShape = this._buildShape(this.INNER_RECT_ATTRIBUTES, this.getShapeType()); const innerShape = this._buildShape(this.INNER_RECT_ATTRIBUTES, this.getShapeType());
const size = this.getSize(); const size = this.getSize();
innerShape.setSize(size.width, size.height); innerShape.setSize(size.width, size.height);
@ -70,9 +75,7 @@ class MainTopic extends Topic {
return group; return group;
} }
/** */ updateTopicShape(targetTopic: Topic) {
// eslint-disable-next-line no-unused-vars
updateTopicShape(targetTopic, workspace) {
// Change figure based on the connected topic ... // Change figure based on the connected topic ...
const model = this.getModel(); const model = this.getModel();
let shapeType = model.getShapeType(); let shapeType = model.getShapeType();
@ -86,7 +89,7 @@ class MainTopic extends Topic {
} }
/** */ /** */
disconnect(workspace) { disconnect(workspace: Workspace) {
super.disconnect(workspace); super.disconnect(workspace);
const model = this.getModel(); const model = this.getModel();
let shapeType = model.getShapeType(); let shapeType = model.getShapeType();
@ -99,7 +102,7 @@ class MainTopic extends Topic {
innerShape.setVisibility(true); innerShape.setVisibility(true);
} }
_updatePositionOnChangeSize(oldSize, newSize) { _updatePositionOnChangeSize(oldSize: SizeType, newSize: SizeType) {
const xOffset = Math.round((newSize.width - oldSize.width) / 2); const xOffset = Math.round((newSize.width - oldSize.width) / 2);
const pos = this.getPosition(); const pos = this.getPosition();
if ($defined(pos)) { if ($defined(pos)) {
@ -112,22 +115,20 @@ class MainTopic extends Topic {
} }
} }
/** */ workoutIncomingConnectionPoint(sourcePosition: Point) {
workoutIncomingConnectionPoint(sourcePosition) {
return Shape.workoutIncomingConnectionPoint(this, sourcePosition); return Shape.workoutIncomingConnectionPoint(this, sourcePosition);
} }
/** */ workoutOutgoingConnectionPoint(targetPosition: Point) {
workoutOutgoingConnectionPoint(targetPosition) {
$assert(targetPosition, 'targetPoint can not be null'); $assert(targetPosition, 'targetPoint can not be null');
const pos = this.getPosition(); const pos = this.getPosition();
const isAtRight = Shape.isAtRight(targetPosition, pos); const isAtRight = Shape.isAtRight(targetPosition, pos);
const size = this.getSize(); const size = this.getSize();
let result; let result: Point;
if (this.getShapeType() === TopicShape.LINE) { if (this.getShapeType() === TopicShape.LINE) {
result = new Point(); result = new Point();
const groupPosition = this._elem2d.getPosition(); const groupPosition = this.get2DElement().getPosition();
const innerShareSize = this.getInnerShape().getSize(); const innerShareSize = this.getInnerShape().getSize();
if (innerShareSize) { if (innerShareSize) {
@ -149,7 +150,7 @@ class MainTopic extends Topic {
result.y = pos.y + size.height / 2; result.y = pos.y + size.height / 2;
} }
} else { } else {
result = Shape.calculateRectConnectionPoint(pos, size, isAtRight, true); result = Shape.calculateRectConnectionPoint(pos, size, isAtRight);
} }
return result; return result;
} }

View File

@ -17,9 +17,8 @@
*/ */
import { $defined } from '@wisemapping/core-js'; import { $defined } from '@wisemapping/core-js';
import $ from 'jquery'; import $ from 'jquery';
// TODO: use jquery.hotkeys from npm or setup eslint-import-resolver-alias plugin
// eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
import initHotKeyPluggin from '@libraries/jquery.hotkeys';
import Events from './Events'; import Events from './Events';
import ActionDispatcher from './ActionDispatcher'; import ActionDispatcher from './ActionDispatcher';

View File

@ -16,17 +16,29 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import { ElementClass } from '@wisemapping/web2d';
import TopicConfig from './TopicConfig'; import TopicConfig from './TopicConfig';
import NodeModel from './model/NodeModel';
import Workspace from './Workspace';
import DragTopic from './DragTopic'; import DragTopic from './DragTopic';
import LayoutManager from './layout/LayoutManager';
import SizeType from './SizeType';
import PositionType from './PositionType';
class NodeGraph { abstract class NodeGraph {
/** private _mouseEvents: boolean;
* @constructs
* @param {mindplot.model.NodeModel} nodeModel private _options;
* @param {Object<Number, String, Boolean>} options
* @throws will throw an error if nodeModel is null or undefined private _onFocus: boolean;
*/
constructor(nodeModel, options) { private _size: SizeType;
private _model: NodeModel;
private _elem2d: ElementClass;
constructor(nodeModel: NodeModel, options) {
$assert(nodeModel, 'model can not be null'); $assert(nodeModel, 'model can not be null');
this._options = options; this._options = options;
@ -36,46 +48,33 @@ class NodeGraph {
this._size = { width: 50, height: 20 }; this._size = { width: 50, height: 20 };
} }
/** @return true if option is set to read-only */ isReadOnly(): boolean {
isReadOnly() {
return this._options.readOnly; return this._options.readOnly;
} }
/** @return model type */ getType(): string {
getType() {
const model = this.getModel(); const model = this.getModel();
return model.getType(); return model.getType();
} }
/** setId(id: number) {
* @param {String} id
* @throws will throw an error if the topic id is not a number
*/
setId(id) {
$assert(typeof id === 'number', `id is not a number:${id}`); $assert(typeof id === 'number', `id is not a number:${id}`);
this.getModel().setId(id); this.getModel().setId(id);
} }
_set2DElement(elem2d) { protected _set2DElement(elem2d: ElementClass) {
this._elem2d = elem2d; this._elem2d = elem2d;
} }
/** get2DElement(): ElementClass {
* @return 2D element
* @throws will throw an error if the element is null or undefined within node graph
*/
get2DElement() {
$assert(this._elem2d, 'NodeGraph has not been initialized properly'); $assert(this._elem2d, 'NodeGraph has not been initialized properly');
return this._elem2d; return this._elem2d;
} }
/** @abstract */ abstract setPosition(point, fireEvent): void;
setPosition(point, fireEvent) {
throw new Error('Unsupported operation');
}
/** */ /** */
addEvent(type, listener) { addEvent(type: string, listener) {
const elem = this.get2DElement(); const elem = this.get2DElement();
elem.addEvent(type, listener); elem.addEvent(type, listener);
} }
@ -102,41 +101,30 @@ class NodeGraph {
return this._mouseEvents; return this._mouseEvents;
} }
/** @return {Object<Number>} size */ getSize(): SizeType {
getSize() {
return this._size; return this._size;
} }
/** @param {Object<Number>} size */ setSize(size: SizeType, force?: boolean) {
setSize(size) { this._size.width = size.width;
this._size.width = parseInt(size.width, 10); this._size.height = size.height;
this._size.height = parseInt(size.height, 10);
} }
/** getModel(): NodeModel {
* @return {mindplot.model.NodeModel} the node model
*/
getModel() {
$assert(this._model, 'Model has not been initialized yet'); $assert(this._model, 'Model has not been initialized yet');
return this._model; return this._model;
} }
/** setModel(model: NodeModel) {
* @param {mindplot.NodeModel} model the node model
* @throws will throw an error if model is null or undefined
*/
setModel(model) {
$assert(model, 'Model can not be null'); $assert(model, 'Model can not be null');
this._model = model; this._model = model;
} }
/** */ getId(): number {
getId() {
return this._model.getId(); return this._model.getId();
} }
/** */ setOnFocus(focus: boolean) {
setOnFocus(focus) {
if (this._onFocus !== focus) { if (this._onFocus !== focus) {
this._onFocus = focus; this._onFocus = focus;
const outerShape = this.getOuterShape(); const outerShape = this.getOuterShape();
@ -157,29 +145,30 @@ class NodeGraph {
} }
} }
/** @return {Boolean} true if the node graph is on focus */ abstract closeEditors(): void;
isOnFocus() {
abstract setCursor(type: string): void;
abstract getOuterShape(): ElementClass;
isOnFocus(): boolean {
return this._onFocus; return this._onFocus;
} }
/** */ dispose(workspace: Workspace) {
dispose(workspace) {
this.setOnFocus(false); this.setOnFocus(false);
workspace.removeChild(this); workspace.removeChild(this);
} }
/** */ /** */
createDragNode(layoutManager) { createDragNode(layoutManager: LayoutManager) {
const dragShape = this._buildDragShape(); const dragShape = this._buildDragShape();
return new DragTopic(dragShape, this, layoutManager); return new DragTopic(dragShape, this, layoutManager);
} }
_buildDragShape() { abstract _buildDragShape();
$assert(false, '_buildDragShape must be implemented by all nodes.');
}
/** */ getPosition(): PositionType {
getPosition() {
const model = this.getModel(); const model = this.getModel();
return model.getPosition(); return model.getPosition();
} }

View File

@ -17,10 +17,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import { Mindmap } from '..';
import XMLSerializerFactory from './persistence/XMLSerializerFactory'; import XMLSerializerFactory from './persistence/XMLSerializerFactory';
class PersistenceManager { abstract class PersistenceManager {
save(mindmap, editorProperties, saveHistory, events, sync) { // eslint-disable-next-line no-use-before-define
static _instance: PersistenceManager;
save(mindmap: Mindmap, editorProperties, saveHistory: boolean, events, sync: boolean) {
$assert(mindmap, 'mindmap can not be null'); $assert(mindmap, 'mindmap can not be null');
$assert(editorProperties, 'editorProperties can not be null'); $assert(editorProperties, 'editorProperties can not be null');
@ -36,45 +40,39 @@ class PersistenceManager {
this.saveMapXml(mapId, mapXml, pref, saveHistory, events, sync); this.saveMapXml(mapId, mapXml, pref, saveHistory, events, sync);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
events.onError(this._buildError()); events.onError(e);
} }
} }
load(mapId) { load(mapId: string) {
$assert(mapId, 'mapId can not be null'); $assert(mapId, 'mapId can not be null');
const domDocument = this.loadMapDom(mapId); const domDocument = this.loadMapDom(mapId);
return PersistenceManager.loadFromDom(mapId, domDocument); return PersistenceManager.loadFromDom(mapId, domDocument);
} }
discardChanges(mapId) { abstract discardChanges(mapId: string): void;
throw new Error('Method must be implemented');
}
loadMapDom(mapId) { abstract loadMapDom(mapId: string): Document;
throw new Error('Method must be implemented');
}
saveMapXml(mapId, mapXml, pref, saveHistory, events, sync) { abstract saveMapXml(mapId: string, mapXml, pref, saveHistory, events, sync);
throw new Error('Method must be implemented');
}
unlockMap(mindmap) { abstract unlockMap(mindmap: Mindmap): void;
throw new Error('Method must be implemented');
}
}
PersistenceManager.init = (instance) => { static init = (instance: PersistenceManager) => {
PersistenceManager._instance = instance; this._instance = instance;
}; };
PersistenceManager.getInstance = () => PersistenceManager._instance; static getInstance(): PersistenceManager {
return this._instance;
}
PersistenceManager.loadFromDom = function loadFromDom(mapId, mapDom) { static loadFromDom(mapId: string, mapDom: Document) {
$assert(mapId, 'mapId can not be null'); $assert(mapId, 'mapId can not be null');
$assert(mapDom, 'mapDom can not be null'); $assert(mapDom, 'mapDom can not be null');
const serializer = XMLSerializerFactory.createInstanceFromDocument(mapDom); const serializer = XMLSerializerFactory.createInstanceFromDocument(mapDom);
return serializer.loadFromDom(mapDom, mapId); return serializer.loadFromDom(mapDom, mapId);
}; }
}
export default PersistenceManager; export default PersistenceManager;

View File

@ -0,0 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type PositionType = { x: number, y: number };
export default PositionType;

View File

@ -258,7 +258,7 @@ class Relationship extends ConnectionLine {
} }
// @typescript-eslint/ban-types // @typescript-eslint/ban-types
addEvent(eventType: string, listener: any) { addEvent(eventType: string, listener) {
let type = eventType; let type = eventType;
// Translate to web 2d events ... // Translate to web 2d events ...
if (type === 'onfocus') { if (type === 'onfocus') {

View File

@ -28,7 +28,7 @@ class RelationshipPivot {
private _designer: Designer; private _designer: Designer;
private _mouseMoveEvent: MouseEvent; private _mouseMoveEvent;
private _onClickEvent: (event: MouseEvent) => void; private _onClickEvent: (event: MouseEvent) => void;
@ -154,8 +154,8 @@ class RelationshipPivot {
const controlPoint = Shape.calculateDefaultControlPoints(sourcePosition, toPosition); const controlPoint = Shape.calculateDefaultControlPoints(sourcePosition, toPosition);
const spoint = new Point(); const spoint = new Point();
spoint.x = parseInt(controlPoint[0].x, 10) + parseInt(sourcePosition.x, 10); spoint.x = parseInt(controlPoint[0].x, 10) + sourcePosition.x;
spoint.y = parseInt(controlPoint[0].y, 10) + parseInt(sourcePosition.y, 10); spoint.y = parseInt(controlPoint[0].y, 10) + sourcePosition.y;
return Shape.calculateRelationShipPointCoordinates(this._sourceTopic, spoint); return Shape.calculateRelationShipPointCoordinates(this._sourceTopic, spoint);
} }

View File

@ -17,10 +17,25 @@
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import $ from 'jquery'; import $ from 'jquery';
import { Mindmap } from '..';
import { $msg } from './Messages'; import { $msg } from './Messages';
import PersistenceManager from './PersistenceManager'; import PersistenceManager from './PersistenceManager';
class RESTPersistenceManager extends PersistenceManager { class RESTPersistenceManager extends PersistenceManager {
private documentUrl: string;
private revertUrl: string;
private lockUrl: string;
private timestamp: string;
private session: string;
private onSave: boolean;
private clearTimeout;
constructor(options) { constructor(options) {
$assert(options.documentUrl, 'documentUrl can not be null'); $assert(options.documentUrl, 'documentUrl can not be null');
$assert(options.revertUrl, 'revertUrl can not be null'); $assert(options.revertUrl, 'revertUrl can not be null');
@ -36,41 +51,38 @@ class RESTPersistenceManager extends PersistenceManager {
this.session = options.session; this.session = options.session;
} }
saveMapXml(mapId, mapXml, pref, saveHistory, events, sync) { saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events, sync: boolean): void {
const data = { const data = {
id: mapId, id: mapId,
xml: mapXml, xml: mapXml,
properties: pref, properties: pref,
}; };
const persistence = this;
let query = `minor=${!saveHistory}`; let query = `minor=${!saveHistory}`;
query = `${query}&timestamp=${this.timestamp}`; query = `${query}&timestamp=${this.timestamp}`;
query = `${query}&session=${this.session}`; query = `${query}&session=${this.session}`;
if (!persistence.onSave) { if (!this.onSave) {
// Mark save in process and fire a event unlocking the save ... // Mark save in process and fire a event unlocking the save ...
persistence.onSave = true; this.onSave = true;
persistence.clearTimeout = setTimeout(() => { this.clearTimeout = setTimeout(() => {
persistence.clearTimeout = null; this.clearTimeout = null;
persistence.onSave = false; this.onSave = false;
}, 10000); }, 10000);
const persistence = this;
$.ajax({ $.ajax({
url: `${this.documentUrl.replace('{id}', mapId)}?${query}`,
type: 'put', type: 'put',
url: `${this.documentUrl.replace('{id}', mapId)}?${query}`,
dataType: 'json', dataType: 'json',
data: JSON.stringify(data), data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
async: !sync, async: !sync,
success(successData, textStatus, jqXHRresponseText) { success(successData) {
persistence.timestamp = successData; persistence.timestamp = successData;
events.onSuccess(); events.onSuccess();
}, },
error(jqXHR, textStatus, errorThrown) {
events.onError(persistence._buildError());
},
complete() { complete() {
// Clear event timeout ... // Clear event timeout ...
if (persistence.clearTimeout) { if (persistence.clearTimeout) {
@ -78,7 +90,7 @@ class RESTPersistenceManager extends PersistenceManager {
} }
persistence.onSave = false; persistence.onSave = false;
}, },
fail(xhr, textStatus) { error(xhr) {
const { responseText } = xhr; const { responseText } = xhr;
let userMsg = { severity: 'SEVERE', message: $msg('SAVE_COULD_NOT_BE_COMPLETED') }; let userMsg = { severity: 'SEVERE', message: $msg('SAVE_COULD_NOT_BE_COMPLETED') };
@ -102,7 +114,7 @@ class RESTPersistenceManager extends PersistenceManager {
} }
} }
discardChanges(mapId) { discardChanges(mapId: string) {
$.ajax({ $.ajax({
url: this.revertUrl.replace('{id}', mapId), url: this.revertUrl.replace('{id}', mapId),
async: false, async: false,
@ -114,7 +126,7 @@ class RESTPersistenceManager extends PersistenceManager {
}); });
} }
unlockMap(mindmap) { unlockMap(mindmap: Mindmap) {
const mapId = mindmap.getId(); const mapId = mindmap.getId();
$.ajax({ $.ajax({
url: this.lockUrl.replace('{id}', mapId), url: this.lockUrl.replace('{id}', mapId),
@ -128,7 +140,7 @@ class RESTPersistenceManager extends PersistenceManager {
}); });
} }
_buildError(jsonSeverResponse) { private _buildError(jsonSeverResponse) {
let message = jsonSeverResponse ? jsonSeverResponse.globalErrors[0] : null; let message = jsonSeverResponse ? jsonSeverResponse.globalErrors[0] : null;
let severity = jsonSeverResponse ? jsonSeverResponse.globalSeverity : null; let severity = jsonSeverResponse ? jsonSeverResponse.globalSeverity : null;
@ -142,9 +154,9 @@ class RESTPersistenceManager extends PersistenceManager {
return { severity, message }; return { severity, message };
} }
loadMapDom(mapId) { loadMapDom(mapId: string): Document {
// Let's try to open one from the local directory ... // Let's try to open one from the local directory ...
let xml; let xml: Document;
$.ajax({ $.ajax({
url: `${this.documentUrl.replace('{id}', mapId)}/xml`, url: `${this.documentUrl.replace('{id}', mapId)}/xml`,
method: 'get', method: 'get',

View File

@ -17,6 +17,7 @@
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import { Point } from '@wisemapping/web2d'; import { Point } from '@wisemapping/web2d';
import Icon from './Icon';
import Topic from './Topic'; import Topic from './Topic';
class ScreenManager { class ScreenManager {
@ -24,7 +25,7 @@ class ScreenManager {
private _padding: { x: number; y: number; }; private _padding: { x: number; y: number; };
private _clickEvents: ((event: UIEvent) => void)[]; private _clickEvents;
private _scale: number; private _scale: number;
@ -60,14 +61,13 @@ class ScreenManager {
this._scale = scale; this._scale = scale;
} }
addEvent(eventType: string, listener: any) { addEvent(eventType: string, listener) {
if (eventType === 'click') this._clickEvents.push(listener); if (eventType === 'click') this._clickEvents.push(listener);
else this._divContainer.bind(eventType, listener); else this._divContainer.bind(eventType, listener);
} }
removeEvent(event: string, listener: any) { removeEvent(event: string, listener) {
if (event === 'click') { if (event === 'click') {
// @ts-ignore @Todo: needs review ...
this._clickEvents.remove(listener); this._clickEvents.remove(listener);
} else { } else {
this._divContainer.unbind(event, listener); this._divContainer.unbind(event, listener);
@ -76,7 +76,7 @@ class ScreenManager {
fireEvent(type: string, event: UIEvent = null) { fireEvent(type: string, event: UIEvent = null) {
if (type === 'click') { if (type === 'click') {
this._clickEvents.forEach((listener: (arg0: any, arg1: any) => void) => { this._clickEvents.forEach((listener) => {
listener(type, event); listener(type, event);
}); });
} else { } else {
@ -102,7 +102,7 @@ class ScreenManager {
return { x, y }; return { x, y };
} }
getWorkspaceIconPosition(e: { getImage: () => any; getSize: () => any; getGroup: () => any; }) { getWorkspaceIconPosition(e: Icon) {
// Retrieve current icon position. // Retrieve current icon position.
const image = e.getImage(); const image = e.getImage();
const elementPosition = image.getPosition(); const elementPosition = image.getPosition();

View File

@ -19,16 +19,22 @@ import { Elipse } from '@wisemapping/web2d';
import TopicConfig from './TopicConfig'; import TopicConfig from './TopicConfig';
import ActionDispatcher from './ActionDispatcher'; import ActionDispatcher from './ActionDispatcher';
import Topic from './Topic';
class ShirinkConnector { class ShirinkConnector {
constructor(topic) { private _isShrink: boolean;
private _ellipse: Elipse;
constructor(topic: Topic) {
this._isShrink = false; this._isShrink = false;
const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES); const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES);
this._ellipse = ellipse; this._ellipse = ellipse;
ellipse.setFill('rgb(62,118,179)'); ellipse.setFill('rgb(62,118,179)');
ellipse.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH); ellipse.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH);
ellipse.addEvent('click', (event) => { ellipse.addEvent('click', (event: Event) => {
const model = topic.getModel(); const model = topic.getModel();
const collapse = !model.areChildrenShrunken(); const collapse = !model.areChildrenShrunken();
@ -39,12 +45,12 @@ class ShirinkConnector {
event.stopPropagation(); event.stopPropagation();
}); });
ellipse.addEvent('mousedown', (event) => { ellipse.addEvent('mousedown', (event: Event) => {
// Avoid node creation ... // Avoid node creation ...
event.stopPropagation(); event.stopPropagation();
}); });
ellipse.addEvent('dblclick', (event) => { ellipse.addEvent('dblclick', (event: Event) => {
// Avoid node creation ... // Avoid node creation ...
event.stopPropagation(); event.stopPropagation();
}); });
@ -58,12 +64,11 @@ class ShirinkConnector {
}); });
ellipse.setCursor('default'); ellipse.setCursor('default');
this._fillColor = '#f7f7f7';
const model = topic.getModel(); const model = topic.getModel();
this.changeRender(model.areChildrenShrunken()); this.changeRender(model.areChildrenShrunken());
} }
changeRender(isShrink) { changeRender(isShrink: boolean) {
const elipse = this._ellipse; const elipse = this._ellipse;
if (isShrink) { if (isShrink) {
elipse.setStroke('2', 'solid'); elipse.setStroke('2', 'solid');
@ -73,36 +78,35 @@ class ShirinkConnector {
this._isShrink = isShrink; this._isShrink = isShrink;
} }
setVisibility(value) { setVisibility(value: boolean): void {
this._ellipse.setVisibility(value); this._ellipse.setVisibility(value);
} }
setOpacity(opacity) { setOpacity(opacity: number): void {
this._ellipse.setOpacity(opacity); this._ellipse.setOpacity(opacity);
} }
setFill(color) { setFill(color: string): void {
this._fillColor = color;
this._ellipse.setFill(color); this._ellipse.setFill(color);
} }
setAttribute(name, value) { setAttribute(name: string, value) {
this._ellipse.setAttribute(name, value); this._ellipse.setAttribute(name, value);
} }
addToWorkspace(group) { addToWorkspace(group): void {
group.append(this._ellipse); group.append(this._ellipse);
} }
setPosition(x, y) { setPosition(x: number, y: number): void {
this._ellipse.setPosition(x, y); this._ellipse.setPosition(x, y);
} }
moveToBack() { moveToBack(): void {
this._ellipse.moveToBack(); this._ellipse.moveToBack();
} }
moveToFront() { moveToFront(): void {
this._ellipse.moveToFront(); this._ellipse.moveToFront();
} }
} }

View File

@ -1,4 +0,0 @@
export type Size = {
width: number;
height: number;
};

View File

@ -0,0 +1,23 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type SizeType = {
width: number;
height: number;
};
export default SizeType;

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