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
script:
- export CYPRESS_imageSnaphots="true"
- yarn install
- yarn bootstrap
- yarn build
- yarn lint

View File

@ -3,7 +3,7 @@ services:
e2e:
image: cypress/included:8.4.1
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
environment:
- 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: {
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', () => {
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.matchImageSnapshot('changeFontSizeSmall');

View File

@ -7,7 +7,7 @@ context('Change Topic shape', () => {
it('change to square shape', () => {
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');

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",
"cy:run": "cypress 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",
"author": "Paulo Veiga <pveiga@gmail.com>, Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>",
@ -16,6 +18,7 @@
"devDependencies": {
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@formatjs/cli": "^4.8.1",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@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 { StyledLogo } from './styled';
import { StyledLogo, Notifier } from './styled';
import { useIntl } from 'react-intl';
import KeyboardSvg from '../../../images/keyboard.svg';
import AddSvg from '../../../images/add.svg';
import MinusSvg from '../../../images/minus.svg';
import CenterFocusSvg from '../../../images/center_focus.svg';
import ActionButton from '../action-button';
export type FooterPropsType = {
showTryPanel?: boolean;
@ -35,15 +36,15 @@ const Footer = ({ showTryPanel }: FooterPropsType): React.ReactElement => {
</div>
</div>
<StyledLogo id="bottom-logo"></StyledLogo>
<div id="headerNotifier"></div>
<Notifier id="headerNotifier"></Notifier>
{showTryPanel && (
<div id="tryInfoPanel">
<p>{intl.formatMessage({ id: 'editor.try-welcome' })}</p>
<p>{intl.formatMessage({ id: 'editor.try-welcome-description' })}</p>
<a href="/c/registration">
<div className="actionButton">
<ActionButton>
{intl.formatMessage({ id: 'login.signup', defaultMessage: 'Sign Up' })}
</div>
</ActionButton>
</a>
</div>
)}

View File

@ -15,4 +15,17 @@ export const StyledLogo = styled.div`
background: url(${LogoTextBlackSvg}) no-repeat;
width: 90px;
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 SaveSvg from '../../../images/save.svg';
import DiscardSvg from '../../../images/discard.svg';
import UndoSvg from '../../../images/undo.svg';
import RedoSvg from '../../../images/redo.svg';
import TopicAddSvg from '../../../images/topic-add.svg';
@ -26,118 +25,130 @@ import HistorySvg from '../../../images/history.svg';
import PrintSvg from '../../../images/print.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 ToolbarPropsType = {
memoryPersistence: boolean;
readOnlyMode: boolean;
isTryMode: boolean;
onAction: (action: ToolbarActionType) => void;
};
export default function Toolbar({
memoryPersistence,
readOnlyMode,
isTryMode: isTryMode,
onAction,
}: ToolbarPropsType): React.ReactElement {
const intl = useIntl();
return (
<div id="toolbar">
<div id="backToList">
<img src={BackIconSvg} />
<HeaderContainer>
<div id="toolbar">
<div id="backToList">
<img src={BackIconSvg} />
</div>
{!isTryMode && (
<div id="persist" className="buttonContainer">
<ToolbarButton id="save" className="buttonOn">
<img src={SaveSvg} />
</ToolbarButton>
</div>
)}
<div id="edit" className="buttonContainer">
<ToolbarButton id="undoEdition" className="buttonOn">
<img src={UndoSvg} />
</ToolbarButton>
<ToolbarButton id="redoEdition" className="buttonOn">
<img src={RedoSvg} />
</ToolbarButton>
</div>
<div id="nodeStyle" className="buttonContainer">
<ToolbarButton id="addTopic" className="buttonOn">
<img src={TopicAddSvg} />
</ToolbarButton>
<ToolbarButton id="deleteTopic" className="buttonOn">
<img src={TopicDeleteSvg} />
</ToolbarButton>
<ToolbarButtonExt id="topicBorder" className="buttonExtOn">
<img src={TopicBorderSvg} />
</ToolbarButtonExt>
<ToolbarButtonExt id="topicColor" className="buttonExtOn">
<img src={TopicColorSvg} />
</ToolbarButtonExt>
<ToolbarButtonExt id="topicShape" className="buttonExtOn">
<img src={TopicShapeSvg} />
</ToolbarButtonExt>
</div>
<div id="font" className="buttonContainer">
<ToolbarButton id="fontFamily" className="buttonOn">
<img src={FontTypeSvg} />
</ToolbarButton>
<ToolbarButtonExt id="fontSize" className="buttonExtOn">
<img src={FontSizeSvg} />
</ToolbarButtonExt>
<ToolbarButton id="fontBold" className="buttonOn">
<img src={FontBoldSvg} />
</ToolbarButton>
<ToolbarButton id="fontItalic" className="buttonOn">
<img src={FontItalicSvg} />
</ToolbarButton>
<ToolbarButtonExt id="fontColor" className="buttonExtOn">
<img src={FontColorSvg} />
</ToolbarButtonExt>
</div>
<div id="nodeContent" className="buttonContainer">
<ToolbarButtonExt id="topicIcon" className="buttonExtOn">
<img src={TopicIconSvg} />
</ToolbarButtonExt>
<ToolbarButton id="topicNote" className="buttonOn">
<img src={TopicNoteSvg} />
</ToolbarButton>
<ToolbarButton id="topicLink" className="buttonOn">
<img src={TopicLinkSvg} />
</ToolbarButton>
<ToolbarButton id="topicRelation" className="buttonOn">
<img src={TopicRelationSvg} />
</ToolbarButton>
</div>
<div id="separator" className="buttonContainer"></div>
{!isTryMode && (
<ToolbarRightContainer>
<ToolbarButton
id="export"
className="buttonOn"
onClick={() => onAction('export')}
>
<img src={ExportSvg} />
</ToolbarButton>
<ToolbarButton
id="publishIt"
className="buttonOn"
onClick={() => onAction('publish')}
>
<img src={PublicSvg} />
</ToolbarButton>
<ToolbarButton
id="history"
className="buttonOn"
onClick={() => onAction('history')}
>
<img src={HistorySvg} />
</ToolbarButton>
<ToolbarButton
id="print"
className="buttonOn"
onClick={() => onAction('print')}
>
<img src={PrintSvg} />
</ToolbarButton>
<ToolbarButton id="account">
<img src={AccountSvg} />
</ToolbarButton>
<ActionButton onClick={() => onAction('share')}>
{intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })}
</ActionButton>
</ToolbarRightContainer>
)}
</div>
{!memoryPersistence && (
<div id="persist" className="buttonContainer">
<div id="save" className="buttonOn">
<img src={SaveSvg} />
</div>
<div id="discard" className="buttonOn">
<img src={DiscardSvg}/>
</div>
</div>
)}
{!readOnlyMode && (
<>
<div id="edit" className="buttonContainer">
<div id="undoEdition" className="buttonOn">
<img src={UndoSvg} />
</div>
<div id="redoEdition" className="buttonOn">
<img src={RedoSvg} />
</div>
</div>
<div id="nodeStyle" className="buttonContainer">
<div id="addTopic" className="buttonOn">
<img src={TopicAddSvg} />
</div>
<div id="deleteTopic" className="buttonOn">
<img src={TopicDeleteSvg} />
</div>
<div id="topicBorder" className="buttonExtOn">
<img src={TopicBorderSvg} />
</div>
<div id="topicColor" className="buttonExtOn">
<img src={TopicColorSvg} />
</div>
<div id="topicShape" className="buttonExtOn">
<img src={TopicShapeSvg} />
</div>
</div>
<div id="font" className="buttonContainer">
<div id="fontFamily" className="buttonOn">
<img src={FontTypeSvg} />
</div>
<div id="fontSize" className="buttonExtOn">
<img src={FontSizeSvg} />
</div>
<div id="fontBold" className="buttonOn">
<img src={FontBoldSvg} />
</div>
<div id="fontItalic" className="buttonOn">
<img src={FontItalicSvg} />
</div>
<div id="fontColor" className="buttonExtOn">
<img src={FontColorSvg} />
</div>
</div>
<div id="nodeContent" className="buttonContainer">
<div id="topicIcon" className="buttonExtOn">
<img src={TopicIconSvg} />
</div>
<div id="topicNote" className="buttonOn">
<img src={TopicNoteSvg} />
</div>
<div id="topicLink" className="buttonOn">
<img src={TopicLinkSvg} />
</div>
<div id="topicRelation" className="buttonOn">
<img src={TopicRelationSvg} />
</div>
</div>
<div id="separator" className="buttonContainer"></div>
</>
)}
{!memoryPersistence && (
<div id="toolbarRight">
<div id="export" className="buttonOn" onClick={() => onAction('export')}>
<img src={ExportSvg} />
</div>
<div id="publishIt" className="buttonOn" onClick={() => onAction('publish')}>
<img src={PublicSvg} />
</div>
<div id="history" className="buttonOn" onClick={() => onAction('history')}>
<img src={HistorySvg} />
</div>
<div id="print" className="buttonOn" onClick={() => onAction('print')}>
<img src={PrintSvg} />
</div>
<div id="account">
<img src={AccountSvg} />
</div>
<div id="share" className="actionButton" onClick={() => onAction('share')}>
{ intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' }) }
</div>
</div>
)}
</div>
</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 Footer from './components/footer';
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 {
var memoryPersistence: boolean;
var readOnly: boolean;
@ -15,26 +29,43 @@ declare global {
var locale: string;
var mindmapLocked: boolean;
var mindmapLockedMsg: string;
var mapTitle: string;
// used in mindplot
var designer: Designer;
var accountEmail: string;
}
export type EditorPropsType = {
initCallback?: (m: typeof mindplot) => () => void;
mapId: number;
memoryPersistence: boolean;
initCallback?: (locale: string) => void;
mapId?: number;
isTryMode: boolean;
readOnlyMode: boolean;
locale?: string;
onAction: (action: ToolbarActionType) => void;
};
const initMindplot = ({
PersistenceManager,
RESTPersistenceManager,
LocalStorageManager,
DesignerOptionsBuilder,
buildDesigner,
$notify,
}: typeof mindplot) => () => {
let persistence: typeof PersistenceManager;
const loadLocaleData = (locale: string) => {
switch (locale) {
case 'fr':
return FR;
case 'en':
return EN;
case 'es':
return ES;
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) {
persistence = new RESTPersistenceManager({
documentUrl: '/c/restful/maps/{id}/document',
@ -45,8 +76,7 @@ const initMindplot = ({
});
} else {
persistence = new LocalStorageManager(
`/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${
!global.isAuth ? '-pub' : ''
`/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${!global.isAuth ? '-pub' : ''
}`,
true
);
@ -58,10 +88,14 @@ const initMindplot = ({
const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: persistence,
readOnly: Boolean(global.readOnly || false),
mapId: global.mapId,
mapId: String(global.mapId),
container: 'mindplot',
zoom: zoomParam || global.userOptions ? global.userOptions.zoom : 1,
locale: global.locale,
zoom:
zoomParam ||
(global.userOptions?.zoom != undefined
? Number.parseFloat(global.userOptions.zoom as string)
: 0.8),
locale: locale,
});
// Build designer ...
@ -69,7 +103,7 @@ const initMindplot = ({
// Load map from XML file persisted on disk...
const instance = PersistenceManager.getInstance();
const mindmap = instance.load(global.mapId);
const mindmap = instance.load(String(global.mapId));
designer.loadMap(mindmap);
if (global.mindmapLocked) {
@ -77,28 +111,26 @@ const initMindplot = ({
}
};
export default function Editor({
const Editor = ({
initCallback = initMindplot,
mapId,
memoryPersistence,
readOnlyMode,
isTryMode: isTryMode,
locale = 'en',
onAction,
}: EditorPropsType): React.ReactElement {
React.useEffect(initCallback(mindplot), []);
}: EditorPropsType): React.ReactElement => {
React.useEffect(() => {
initCallback(locale);
}, []);
return (
<IntlProvider locale={locale} defaultLocale="en" messages={{}}>
<div id="header">
<Toolbar
memoryPersistence={memoryPersistence}
readOnlyMode={readOnlyMode}
onAction={onAction}
/>
</div>
<IntlProvider locale={locale} messages={loadLocaleData(locale)}>
<Toolbar
isTryMode={isTryMode}
onAction={onAction}
/>
<div id="mindplot"></div>
<Footer showTryPanel={memoryPersistence} />
<Footer showTryPanel={isTryMode} />
</IntlProvider>
);
}
export default Editor;

View File

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

View File

@ -1,80 +1,6 @@
@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{
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 {
bottom: 80px;
align-items: stretch;
}
div#mindplot {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,15 @@
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '',
publicPath: '',
library: {
type: 'umd',
},
}, },
stats:{
errorDetails: true
},
entry: {
"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: [
new CleanWebpackPlugin(),
new CleanWebpackPlugin({
dangerouslyAllowCleanPatternsOutsideProject: true,
dry: false,
}),
new CopyPlugin({
patterns: [
{ from: 'test/playground/map-render/images/favicon.ico', to: 'favicon.ico' },

View File

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

View File

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

View File

@ -2,8 +2,6 @@
"compilerOptions": {
"baseUrl": ".",
"module": "ES6",
"paths": {
"@libraries/*": ["../../libraries/*"] }
},
"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",
"version": "5.0.2",
"version": "5.0.3",
"description": "WiseMapping - Mindplot Canvas Library",
"homepage": "http://www.wisemapping.org/",
"main": "dist/mindplot.js",
"directories": {
"lib": "src",
"test": "__tests__"
},
"main": "src/index.ts",
"files": [
"src",
"assets",

View File

@ -21,62 +21,63 @@ import { $assert } from '@wisemapping/core-js';
import Point from '@wisemapping/web2d';
import { Mindmap } from '..';
import CommandContext from './CommandContext';
import ControlPoint from './ControlPoint';
import Events from './Events';
import NodeModel from './model/NodeModel';
import RelationshipModel from './model/RelationshipModel';
import Topic from './Topic';
abstract class ActionDispatcher extends Events {
static _instance: ActionDispatcher;
private static _instance: ActionDispatcher;
constructor(commandContext: CommandContext) {
$assert(commandContext, 'commandContext can not be null');
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) => {
this._instance = dispatcher;
};
static setInstance = (dispatcher: ActionDispatcher) => {
this._instance = dispatcher;
};
static getInstance = (): ActionDispatcher => ActionDispatcher._instance;
static getInstance = (): ActionDispatcher => ActionDispatcher._instance;
}
export default ActionDispatcher;

View File

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

View File

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

View File

@ -20,8 +20,33 @@ import { $defined } from '@wisemapping/core-js';
import Shape from './util/Shape';
import ActionDispatcher from './ActionDispatcher';
import Workspace from './Workspace';
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() {
this.control1 = new Elipse({
width: 6,
@ -70,7 +95,7 @@ class ControlPoint {
});
}
setLine(line) {
setLine(line: Line) {
if ($defined(this._line)) {
this._removeLine();
}
@ -93,7 +118,7 @@ class ControlPoint {
if ($defined(this._line)) this._createControlPoint();
}
_createControlPoint() {
private _createControlPoint() {
this._controls = this._line.getLine().getControlPoints();
let pos = this._line.getLine().getFrom();
this._controlPointsController[0].setPosition(
@ -117,11 +142,11 @@ class ControlPoint {
);
}
_removeLine() {
private _removeLine() {
// Overwrite default behaviour ...
}
_mouseDown(event, point, me) {
private _mouseDown(event: Event, point, me) {
if (!this._isBinded) {
this._isBinded = true;
this._mouseMoveFunction = (e) => {
@ -129,7 +154,7 @@ class ControlPoint {
};
this._workspace.getScreenManager().addEvent('mousemove', this._mouseMoveFunction);
this._mouseUpFunction = (e) => {
this._mouseUpFunction = (e: Event) => {
me._mouseUp(e, point, me);
};
this._workspace.getScreenManager().addEvent('mouseup', this._mouseUpFunction);
@ -139,7 +164,7 @@ class ControlPoint {
return false;
}
_mouseMoveEvent(event, point) {
private _mouseMoveEvent(event: MouseEvent, point: Point) {
const screen = this._workspace.getScreenManager();
const pos = screen.getWorkspaceMousePosition(event);
@ -162,7 +187,7 @@ class ControlPoint {
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('mouseup', this._mouseUpFunction);
@ -171,13 +196,13 @@ class ControlPoint {
this._isBinded = false;
}
_mouseClick(event) {
_mouseClick(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
return false;
}
setVisibility(visible) {
setVisibility(visible: boolean) {
if (visible) {
this._controlLines[0].moveToFront();
this._controlLines[1].moveToFront();
@ -190,7 +215,7 @@ class ControlPoint {
this._controlLines[1].setVisibility(visible);
}
addToWorkspace(workspace) {
addToWorkspace(workspace: Workspace): void {
this._workspace = workspace;
workspace.append(this._controlPointsController[0]);
workspace.append(this._controlPointsController[1]);
@ -198,7 +223,7 @@ class ControlPoint {
workspace.append(this._controlLines[1]);
}
removeFromWorkspace(workspace) {
removeFromWorkspace(workspace: Workspace) {
this._workspace = null;
workspace.removeChild(this._controlPointsController[0]);
workspace.removeChild(this._controlPointsController[1]);
@ -206,20 +231,21 @@ class ControlPoint {
workspace.removeChild(this._controlLines[1]);
}
getControlPoint(index) {
getControlPoint(index: number): ControlPoint {
return this._controls[index];
}
getOriginalEndPoint(index) {
getOriginalEndPoint(index: number) {
return this._endPoint[index];
}
getOriginalCtrlPoint(index) {
getOriginalCtrlPoint(index: number): ControlPoint {
return this._orignalCtrlPoint[index];
}
static FROM = 0;
static TO = 1;
}
ControlPoint.FROM = 0;
ControlPoint.TO = 1;
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 DragTopic from './DragTopic';
import CentralTopic from './CentralTopic';
import FeatureType from './model/FeatureType';
class Designer extends Events {
private _mindmap: Mindmap;
@ -136,8 +137,8 @@ class Designer extends Events {
}
private _registerWheelEvents(): void {
const zoomFactor = 1.006;
document.addEventListener('wheel', (event) => {
const zoomFactor = 1.02;
document.addEventListener('wheel', (event: WheelEvent) => {
if (event.deltaX > 0 || event.deltaY > 0) {
this.zoomOut(zoomFactor);
} else {
@ -151,14 +152,14 @@ class Designer extends Events {
return this._actionDispatcher;
}
// @ts-ignore
addEvent(type: string, listener: any): void {
addEvent(type: string, listener): Events {
if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) {
const editor = TopicEventDispatcher.getInstance();
editor.addEvent(type, listener);
} else {
super.addEvent(type, listener);
}
return this;
}
private _registerMouseEvents() {
@ -186,7 +187,7 @@ class Designer extends Events {
screenManager.addEvent('dblclick', (event: MouseEvent) => {
if (workspace.isWorkspaceEventsEnabled()) {
const mousePos = screenManager.getWorkspaceMousePosition(event);
const centralTopic:CentralTopic = me.getModel()
const centralTopic: CentralTopic = me.getModel()
.getCentralTopic();
const model = me._createChildModel(centralTopic, mousePos);
@ -208,7 +209,7 @@ class Designer extends Events {
dragManager.addEvent('dragging', (event: MouseEvent, dragTopic: DragTopic) => {
dragTopic.updateFreeLayout(event);
if (!dragTopic.isFreeLayoutOn(event)) {
if (!dragTopic.isFreeLayoutOn()) {
// The node is being drag. Is the connection still valid ?
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 {
let topics = this.getModel().filterSelectedTopics();
if (topics.length <= 0) {
@ -876,7 +891,7 @@ class Designer extends Events {
addIconType(iconType: string): void {
const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) {
this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id, {
this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id as FeatureType, {
id: iconType,
});
}

View File

@ -33,25 +33,35 @@ export function buildDesigner(options: DesignerOptions): Designer {
// Register load events ...
designer = new Designer(options, divContainer);
designer.addEvent('loadSuccess', () => {
// @ts-ignore
window.mindmapLoadReady = true;
globalThis.mindmapLoadReady = true;
console.log('Map loadded successfully');
});
const onerrorFn = (message: string, url, lineNo) => {
// Close loading dialog ...
// @ts-ignore
if (window.waitDialog) {
// @ts-ignore
window.waitDialog.close();
// @ts-ignore
window.waitDialog = null;
}
const onerrorFn = (msg: string, url: string, lineNo: number, columnNo: number, error: Error) => {
const message = [
`Message: ${msg}`,
`URL: ${url}`,
`Line: ${lineNo}`,
`Column: ${columnNo}`,
].join(' - ');
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.
// Remove this in the near future.
// @ts-ignore
if (!window.mindmapLoadReady) {
if (!globalThis.mindmapLoadReady) {
$notifyModal($msg('UNEXPECTED_ERROR_LOADING'));
}
};
@ -64,7 +74,7 @@ export function buildDesigner(options: DesignerOptions): Designer {
// Register toolbar event ...
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.
designer.cleanScreen = () => {

View File

@ -18,41 +18,48 @@
import $ from 'jquery';
import { $assert } from '@wisemapping/core-js';
import Keyboard from './Keyboard';
import { Designer } from '..';
import Topic from './Topic';
class DesignerKeyboard extends Keyboard {
constructor(designer) {
super(designer);
// eslint-disable-next-line no-use-before-define
static _instance: DesignerKeyboard;
constructor(designer: Designer) {
super();
$assert(designer, 'designer can not be null');
this._registerEvents(designer);
}
_registerEvents(designer) {
private _registerEvents(designer: Designer) {
// Try with the keyboard ..
const model = designer.getModel();
this.addShortcut(
['backspace'], (event) => {
['backspace'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.deleteSelectedEntities();
},
);
this.addShortcut(
['space'], () => {
['space'], (event: Event) => {
designer.shrinkSelectedBranch();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['f2'], (event) => {
['f2'], (event: Event) => {
event.stopPropagation();
event.preventDefault();
const node = model.selectedTopic();
if (node) {
node.showTextEditor();
node.showTextEditor(node.getText());
}
},
);
this.addShortcut(
['del'], (event) => {
['del'], (event: Event) => {
designer.deleteSelectedEntities();
event.preventDefault();
event.stopPropagation();
@ -64,63 +71,63 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
['insert'], (event) => {
['insert'], (event: Event) => {
designer.createChildForSelectedNode();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['tab'], (event) => {
['tab'], (eventevent: Event) => {
designer.createChildForSelectedNode();
event.preventDefault();
event.stopPropagation();
eventevent.preventDefault();
eventevent.stopPropagation();
},
);
this.addShortcut(
['meta+enter'], (event) => {
event.preventDefault();
event.stopPropagation();
['meta+enter'], (eventevent: Event) => {
eventevent.preventDefault();
eventevent.stopPropagation();
designer.createChildForSelectedNode();
},
);
this.addShortcut(
['ctrl+z', 'meta+z'], (event) => {
event.preventDefault(event);
['ctrl+z', 'meta+z'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.undo();
},
);
this.addShortcut(
['ctrl+c', 'meta+c'], (event) => {
event.preventDefault(event);
['ctrl+c', 'meta+c'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.copyToClipboard();
},
);
this.addShortcut(
['ctrl+v', 'meta+v'], (event) => {
event.preventDefault(event);
['ctrl+v', 'meta+v'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.pasteClipboard();
},
);
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.stopPropagation();
designer.redo();
},
);
this.addShortcut(
['ctrl+a', 'meta+a'], (event) => {
['ctrl+a', 'meta+a'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.selectAll();
},
);
this.addShortcut(
['ctrl+b', 'meta+b'], (event) => {
['ctrl+b', 'meta+b'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
@ -128,14 +135,14 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
['ctrl+s', 'meta+s'], (event) => {
['ctrl+s', 'meta+s'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
$(document).find('#save').trigger('click');
},
);
this.addShortcut(
['ctrl+i', 'meta+i'], (event) => {
['ctrl+i', 'meta+i'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
@ -143,7 +150,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
['ctrl+shift+a', 'meta+shift+a'], (event) => {
['ctrl+shift+a', 'meta+shift+a'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
@ -151,7 +158,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
['meta+=', 'ctrl+='], (event) => {
['meta+=', 'ctrl+='], (event: Event) => {
event.preventDefault();
event.stopPropagation();
@ -159,7 +166,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
['meta+-', 'ctrl+-'], (event) => {
['meta+-', 'ctrl+-'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
@ -168,7 +175,7 @@ class DesignerKeyboard extends Keyboard {
);
const me = this;
this.addShortcut(
'right', (event) => {
'right', (event: Event) => {
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
@ -187,7 +194,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
'left', (event) => {
'left', (event: Event) => {
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
@ -206,7 +213,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
'up', (event) => {
'up', (event: Event) => {
const node = model.selectedTopic();
if (node) {
if (!node.isCentralTopic()) {
@ -221,7 +228,7 @@ class DesignerKeyboard extends Keyboard {
},
);
this.addShortcut(
'down', (event) => {
'down', (event: Event) => {
const node = model.selectedTopic();
if (node) {
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'];
$(document).on('keypress', (event) => {
let keyCode;
let keyCode: number;
// Firefox doesn't skip special keys for keypress event...
if (event.key && excludes.includes(event.key.toLowerCase())) {
return;
@ -250,8 +257,10 @@ class DesignerKeyboard extends Keyboard {
keyCode = event.keyCode;
}
const specialKey = $.hotkeys.specialKeys[keyCode];
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !$.hotkeys.shiftNums[keyCode]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const jq: any = $;
const specialKey = jq.hotkeys.specialKeys[keyCode];
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !jq.hotkeys.shiftNums[keyCode]) {
const nodes = designer.getModel().filterSelectedTopics();
if (nodes.length > 0) {
// 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();
if (parent) {
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();
if (children.length > 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();
if (parent) {
this._goToNode(designer, parent);
}
}
_goToChild(designer, node) {
private _goToChild(designer, node) {
const children = node.getChildren();
if (children.length > 0) {
let target = children[0];
@ -354,79 +363,79 @@ class DesignerKeyboard extends Keyboard {
}
}
_goToNode(designer, node) {
private _goToNode(designer: Designer, node: Topic) {
// First deselect all the nodes ...
designer.deselectAll();
// Give focus to the selected node....
node.setOnFocus(true);
}
static register = function register(designer: Designer) {
this._instance = new DesignerKeyboard(designer);
};
static specialKeys = {
8: 'backspace',
9: 'tab',
10: 'return',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
19: 'pause',
20: 'capslock',
27: 'esc',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'insert',
46: 'del',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111: '/',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
144: 'numlock',
145: 'scroll',
186: ';',
191: '/',
220: '\\',
222: "'",
224: 'meta',
};
static getInstance() {
return this._instance;
}
}
DesignerKeyboard.specialKeys = {
8: 'backspace',
9: 'tab',
10: 'return',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
19: 'pause',
20: 'capslock',
27: 'esc',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'insert',
46: 'del',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111: '/',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
144: 'numlock',
145: 'scroll',
186: ';',
191: '/',
220: '\\',
222: "'",
224: 'meta',
};
DesignerKeyboard.register = function register(designer) {
this._instance = new DesignerKeyboard(designer);
};
DesignerKeyboard.getInstance = function getInstance() {
return this._instance;
};
export default DesignerKeyboard;

View File

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

View File

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

View File

@ -17,9 +17,25 @@
*/
import { $assert, $defined } from '@wisemapping/core-js';
import DragTopic from './DragTopic';
import EventBusDispatcher from './layout/EventBusDispatcher';
import Workspace from './Workspace';
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._designerModel = workspace;
this._listeners = {};
@ -34,7 +50,7 @@ class DragManager {
const screen = workspace.getScreenManager();
const dragManager = this;
const me = this;
const mouseDownListener = function mouseDownListener(event) {
const mouseDownListener = function mouseDownListener() {
if (workspace.isWorkspaceEventsEnabled()) {
// Disable double drag...
workspace.enableWorkspaceEvents(false);
@ -62,11 +78,11 @@ class DragManager {
node.addEvent('mousedown', mouseDownListener);
}
remove(node) {
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 me = this;
const result = (event) => {
@ -98,7 +114,7 @@ class DragManager {
return result;
}
_buildMouseUpListener(workspace, node, dragNode, dragManager) {
protected _buildMouseUpListener(workspace: Workspace, node, dragNode, dragManager: DragManager) {
const screen = workspace.getScreenManager();
const me = this;
const result = (event) => {

View File

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

View File

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

View File

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

View File

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

View File

@ -17,9 +17,8 @@
*/
import { $defined } from '@wisemapping/core-js';
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 ActionDispatcher from './ActionDispatcher';

View File

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

View File

@ -17,10 +17,14 @@
* limitations under the License.
*/
import { $assert } from '@wisemapping/core-js';
import { Mindmap } from '..';
import XMLSerializerFactory from './persistence/XMLSerializerFactory';
class PersistenceManager {
save(mindmap, editorProperties, saveHistory, events, sync) {
abstract class PersistenceManager {
// 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(editorProperties, 'editorProperties can not be null');
@ -36,45 +40,39 @@ class PersistenceManager {
this.saveMapXml(mapId, mapXml, pref, saveHistory, events, sync);
} catch (e) {
console.error(e);
events.onError(this._buildError());
events.onError(e);
}
}
load(mapId) {
load(mapId: string) {
$assert(mapId, 'mapId can not be null');
const domDocument = this.loadMapDom(mapId);
return PersistenceManager.loadFromDom(mapId, domDocument);
}
discardChanges(mapId) {
throw new Error('Method must be implemented');
abstract discardChanges(mapId: string): void;
abstract loadMapDom(mapId: string): Document;
abstract saveMapXml(mapId: string, mapXml, pref, saveHistory, events, sync);
abstract unlockMap(mindmap: Mindmap): void;
static init = (instance: PersistenceManager) => {
this._instance = instance;
};
static getInstance(): PersistenceManager {
return this._instance;
}
loadMapDom(mapId) {
throw new Error('Method must be implemented');
}
static loadFromDom(mapId: string, mapDom: Document) {
$assert(mapId, 'mapId can not be null');
$assert(mapDom, 'mapDom can not be null');
saveMapXml(mapId, mapXml, pref, saveHistory, events, sync) {
throw new Error('Method must be implemented');
}
unlockMap(mindmap) {
throw new Error('Method must be implemented');
const serializer = XMLSerializerFactory.createInstanceFromDocument(mapDom);
return serializer.loadFromDom(mapDom, mapId);
}
}
PersistenceManager.init = (instance) => {
PersistenceManager._instance = instance;
};
PersistenceManager.getInstance = () => PersistenceManager._instance;
PersistenceManager.loadFromDom = function loadFromDom(mapId, mapDom) {
$assert(mapId, 'mapId can not be null');
$assert(mapDom, 'mapDom can not be null');
const serializer = XMLSerializerFactory.createInstanceFromDocument(mapDom);
return serializer.loadFromDom(mapDom, mapId);
};
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
addEvent(eventType: string, listener: any) {
addEvent(eventType: string, listener) {
let type = eventType;
// Translate to web 2d events ...
if (type === 'onfocus') {

View File

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

View File

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

View File

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

View File

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