Merge branch 'feature/exports-mindmap' of https://bitbucket.org/wisemapping/wisemapping-frontend into feature/exports-mindmap
@ -17,6 +17,7 @@ pipelines:
|
||||
- cypress
|
||||
script:
|
||||
- export CYPRESS_imageSnaphots="true"
|
||||
- yarn install
|
||||
- yarn bootstrap
|
||||
- yarn build
|
||||
- yarn lint
|
||||
|
@ -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
|
||||
|
87
lang/en.json
@ -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"
|
||||
}
|
||||
}
|
@ -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
@ -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
|
@ -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');
|
||||
|
@ -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');
|
||||
|
||||
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 205 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 105 KiB |
14
packages/editor/lang/de.json
Normal 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"
|
||||
}
|
||||
}
|
14
packages/editor/lang/en.json
Normal 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"
|
||||
}
|
||||
}
|
14
packages/editor/lang/es.json
Normal 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"
|
||||
}
|
||||
}
|
14
packages/editor/lang/fr.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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
@ -0,0 +1,4 @@
|
||||
declare module "*.svg" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
26
packages/editor/src/compiled-lang/de.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
26
packages/editor/src/compiled-lang/en.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
26
packages/editor/src/compiled-lang/es.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
26
packages/editor/src/compiled-lang/fr.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
25
packages/editor/src/components/action-button/index.ts
Normal 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;
|
@ -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>
|
||||
)}
|
||||
|
@ -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;
|
||||
`;
|
||||
|
@ -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 (
|
||||
<HeaderContainer>
|
||||
<div id="toolbar">
|
||||
<div id="backToList">
|
||||
<img src={BackIconSvg} />
|
||||
</div>
|
||||
{!memoryPersistence && (
|
||||
{!isTryMode && (
|
||||
<div id="persist" className="buttonContainer">
|
||||
<div id="save" className="buttonOn">
|
||||
<ToolbarButton id="save" className="buttonOn">
|
||||
<img src={SaveSvg} />
|
||||
</div>
|
||||
<div id="discard" className="buttonOn">
|
||||
<img src={DiscardSvg}/>
|
||||
</div>
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
)}
|
||||
{!readOnlyMode && (
|
||||
<>
|
||||
<div id="edit" className="buttonContainer">
|
||||
<div id="undoEdition" className="buttonOn">
|
||||
<ToolbarButton id="undoEdition" className="buttonOn">
|
||||
<img src={UndoSvg} />
|
||||
</div>
|
||||
<div id="redoEdition" className="buttonOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="redoEdition" className="buttonOn">
|
||||
<img src={RedoSvg} />
|
||||
</div>
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<div id="nodeStyle" className="buttonContainer">
|
||||
<div id="addTopic" className="buttonOn">
|
||||
<ToolbarButton id="addTopic" className="buttonOn">
|
||||
<img src={TopicAddSvg} />
|
||||
</div>
|
||||
<div id="deleteTopic" className="buttonOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="deleteTopic" className="buttonOn">
|
||||
<img src={TopicDeleteSvg} />
|
||||
</div>
|
||||
<div id="topicBorder" className="buttonExtOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="topicBorder" className="buttonExtOn">
|
||||
<img src={TopicBorderSvg} />
|
||||
</div>
|
||||
<div id="topicColor" className="buttonExtOn">
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicColor" className="buttonExtOn">
|
||||
<img src={TopicColorSvg} />
|
||||
</div>
|
||||
<div id="topicShape" className="buttonExtOn">
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicShape" className="buttonExtOn">
|
||||
<img src={TopicShapeSvg} />
|
||||
</div>
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="font" className="buttonContainer">
|
||||
<div id="fontFamily" className="buttonOn">
|
||||
<ToolbarButton id="fontFamily" className="buttonOn">
|
||||
<img src={FontTypeSvg} />
|
||||
</div>
|
||||
<div id="fontSize" className="buttonExtOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontSize" className="buttonExtOn">
|
||||
<img src={FontSizeSvg} />
|
||||
</div>
|
||||
<div id="fontBold" className="buttonOn">
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="fontBold" className="buttonOn">
|
||||
<img src={FontBoldSvg} />
|
||||
</div>
|
||||
<div id="fontItalic" className="buttonOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="fontItalic" className="buttonOn">
|
||||
<img src={FontItalicSvg} />
|
||||
</div>
|
||||
<div id="fontColor" className="buttonExtOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontColor" className="buttonExtOn">
|
||||
<img src={FontColorSvg} />
|
||||
</div>
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="nodeContent" className="buttonContainer">
|
||||
<div id="topicIcon" className="buttonExtOn">
|
||||
<ToolbarButtonExt id="topicIcon" className="buttonExtOn">
|
||||
<img src={TopicIconSvg} />
|
||||
</div>
|
||||
<div id="topicNote" className="buttonOn">
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="topicNote" className="buttonOn">
|
||||
<img src={TopicNoteSvg} />
|
||||
</div>
|
||||
<div id="topicLink" className="buttonOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicLink" className="buttonOn">
|
||||
<img src={TopicLinkSvg} />
|
||||
</div>
|
||||
<div id="topicRelation" className="buttonOn">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicRelation" className="buttonOn">
|
||||
<img src={TopicRelationSvg} />
|
||||
</div>
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<div id="separator" className="buttonContainer"></div>
|
||||
</>
|
||||
)}
|
||||
{!memoryPersistence && (
|
||||
<div id="toolbarRight">
|
||||
<div id="export" className="buttonOn" onClick={() => onAction('export')}>
|
||||
{!isTryMode && (
|
||||
<ToolbarRightContainer>
|
||||
<ToolbarButton
|
||||
id="export"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('export')}
|
||||
>
|
||||
<img src={ExportSvg} />
|
||||
</div>
|
||||
<div id="publishIt" className="buttonOn" onClick={() => onAction('publish')}>
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="publishIt"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('publish')}
|
||||
>
|
||||
<img src={PublicSvg} />
|
||||
</div>
|
||||
<div id="history" className="buttonOn" onClick={() => onAction('history')}>
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="history"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('history')}
|
||||
>
|
||||
<img src={HistorySvg} />
|
||||
</div>
|
||||
<div id="print" className="buttonOn" onClick={() => onAction('print')}>
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="print"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('print')}
|
||||
>
|
||||
<img src={PrintSvg} />
|
||||
</div>
|
||||
<div id="account">
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="account">
|
||||
<img src={AccountSvg} />
|
||||
</div>
|
||||
<div id="share" className="actionButton" onClick={() => onAction('share')}>
|
||||
{ intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' }) }
|
||||
</div>
|
||||
</div>
|
||||
</ToolbarButton>
|
||||
<ActionButton onClick={() => onAction('share')}>
|
||||
{intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })}
|
||||
</ActionButton>
|
||||
</ToolbarRightContainer>
|
||||
)}
|
||||
</div>
|
||||
</HeaderContainer>
|
||||
);
|
||||
}
|
||||
|
50
packages/editor/src/components/toolbar/styled.ts
Normal 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;
|
||||
`;
|
30
packages/editor/src/custom.d.ts
vendored
@ -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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
export const Locales = {
|
||||
EN: {},
|
||||
ES: {},
|
||||
DE: {},
|
||||
FR: {}
|
||||
};
|
||||
|
@ -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">
|
||||
<IntlProvider locale={locale} messages={loadLocaleData(locale)}>
|
||||
<Toolbar
|
||||
memoryPersistence={memoryPersistence}
|
||||
readOnlyMode={readOnlyMode}
|
||||
isTryMode={isTryMode}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</div>
|
||||
<div id="mindplot"></div>
|
||||
<Footer showTryPanel={memoryPersistence} />
|
||||
<Footer showTryPanel={isTryMode} />
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
export default Editor;
|
||||
|
@ -136,6 +136,7 @@ div.shareModalDialog {
|
||||
align-items: flex-end;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
div#position {
|
||||
|
@ -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;
|
||||
}
|
@ -27,6 +27,7 @@ div#footer-logo {
|
||||
|
||||
#floating-panel {
|
||||
bottom: 80px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
div#mindplot {
|
||||
|
@ -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
|
||||
|
@ -2,10 +2,10 @@ 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 }) =>
|
||||
() => {
|
||||
|
||||
const initialization = () => {
|
||||
// Options has been defined in by a external file ?
|
||||
const p = new LocalStorageManager('samples/{id}.wxml');
|
||||
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p });
|
||||
@ -20,7 +20,7 @@ const initialization =
|
||||
const persistence = PersistenceManager.getInstance();
|
||||
const mindmap = persistence.load(mapId);
|
||||
designer.loadMap(mindmap);
|
||||
};
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<Editor
|
||||
|
@ -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 });
|
||||
|
||||
|
@ -1,13 +1,25 @@
|
||||
{
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"outDir": "./dist/",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"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"
|
||||
]
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
@ -9,7 +7,9 @@ module.exports = {
|
||||
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(),
|
||||
]
|
||||
}
|
||||
|
@ -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' },
|
||||
|
@ -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);
|
||||
|
@ -25,19 +25,26 @@
|
||||
"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": {
|
||||
@ -45,7 +52,10 @@
|
||||
"config": "./webpack.common.js"
|
||||
},
|
||||
"node": {
|
||||
"extensions": [".js",".ts"]
|
||||
"extensions": [
|
||||
".js",
|
||||
".ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "ES6",
|
||||
"paths": {
|
||||
"@libraries/*": ["../../libraries/*"] }
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
12
packages/mindplot/mindplot.d.ts
vendored
@ -1,12 +0,0 @@
|
||||
declare module "@wisemapping/mindplot" {
|
||||
const mindplot: {
|
||||
Mindmap,
|
||||
PersistenceManager,
|
||||
Designer,
|
||||
LocalStorageManager,
|
||||
Menu,
|
||||
DesignerBuilder,
|
||||
};
|
||||
|
||||
export default mindplot;
|
||||
}
|
@ -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",
|
||||
|
@ -21,56 +21,57 @@ 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;
|
||||
|
@ -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);
|
@ -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;
|
||||
|
@ -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;
|
21
packages/mindplot/src/components/CursorType.ts
Normal 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;
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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 = () => {
|
||||
|
@ -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,16 +363,19 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
DesignerKeyboard.specialKeys = {
|
||||
static register = function register(designer: Designer) {
|
||||
this._instance = new DesignerKeyboard(designer);
|
||||
};
|
||||
|
||||
static specialKeys = {
|
||||
8: 'backspace',
|
||||
9: 'tab',
|
||||
10: 'return',
|
||||
@ -419,14 +431,11 @@ DesignerKeyboard.specialKeys = {
|
||||
220: '\\',
|
||||
222: "'",
|
||||
224: 'meta',
|
||||
};
|
||||
};
|
||||
|
||||
DesignerKeyboard.register = function register(designer) {
|
||||
this._instance = new DesignerKeyboard(designer);
|
||||
};
|
||||
|
||||
DesignerKeyboard.getInstance = function getInstance() {
|
||||
static getInstance() {
|
||||
return this._instance;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default DesignerKeyboard;
|
@ -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,
|
||||
|
@ -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);
|
@ -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) => {
|
@ -210,9 +210,7 @@ class DragTopic {
|
||||
return this.getConnectedToTopic() != null;
|
||||
}
|
||||
|
||||
isFreeLayoutOn(dragTopic) {
|
||||
// return this._isFreeLayoutEnabled;
|
||||
// Disable free layout ...
|
||||
isFreeLayoutOn() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
@ -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 ...
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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';
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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');
|
||||
}
|
||||
|
||||
saveMapXml(mapId, mapXml, pref, saveHistory, events, sync) {
|
||||
throw new Error('Method must be implemented');
|
||||
}
|
||||
|
||||
unlockMap(mindmap) {
|
||||
throw new Error('Method must be implemented');
|
||||
}
|
||||
}
|
||||
|
||||
PersistenceManager.init = (instance) => {
|
||||
PersistenceManager._instance = instance;
|
||||
};
|
||||
|
||||
PersistenceManager.getInstance = () => PersistenceManager._instance;
|
||||
|
||||
PersistenceManager.loadFromDom = function loadFromDom(mapId, mapDom) {
|
||||
static loadFromDom(mapId: string, mapDom: Document) {
|
||||
$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;
|
20
packages/mindplot/src/components/PositionType.ts
Normal 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;
|
@ -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') {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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}×tamp=${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',
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export type Size = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
23
packages/mindplot/src/components/SizeType.ts
Normal 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;
|