mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-25 15:47:55 +01:00
Add support for emoji
Update core libraries.
This commit is contained in:
parent
906c284c24
commit
c60254aca1
@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
e2e:
|
||||
image: cypress/included:10.8.0
|
||||
image: cypress/included:10.11.0
|
||||
container_name: wisemapping-integration-tests
|
||||
entrypoint: '/bin/sh -c "yarn install && yarn bootstrap && yarn build && yarn test:integration"'
|
||||
working_dir: /e2e
|
||||
|
@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
e2e:
|
||||
image: cypress/included:10.8.0
|
||||
image: cypress/included:10.11.0
|
||||
container_name: wisemapping-integration-tests
|
||||
entrypoint: '/bin/sh -c "yarn bootstrap && yarn build && yarn test:integration"'
|
||||
working_dir: /e2e
|
||||
|
35
packages/editor/.eslintrc.json
Normal file
35
packages/editor/.eslintrc.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"prettier",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"**/dist/**/*"
|
||||
],
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint",
|
||||
"react-hooks"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "error",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
|
||||
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
|
||||
}
|
||||
}
|
@ -17,52 +17,41 @@
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.18.6",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.16.5",
|
||||
"@formatjs/cli": "^4.8.1",
|
||||
"@babel/preset-env": "^7.19.4",
|
||||
"@formatjs/cli": "^5.1.3",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
||||
"@typescript-eslint/parser": "^4.8.1",
|
||||
"babel-plugin-transform-require-context": "^0.1.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"compression-webpack-plugin": "^9.2.0",
|
||||
"copy-webpack-plugin": "^10.2.1",
|
||||
"css-loader": "^6.7.1",
|
||||
"cypress": "^8.4.1",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"eslint": "^7.14.0",
|
||||
"eslint-config-prettier": "^8.0.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"jest-transform-stub": "^2.0.0",
|
||||
"prettier": "^2.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.1.0",
|
||||
"ts-loader": "^8.0.11",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.1.2",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-dev-server": "^4.7.3",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wisemapping/mindplot": "^5.0.1",
|
||||
"react-color": "^2.19.3"
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@wisemapping/mindplot": "^5.0.15",
|
||||
"emoji-picker-react": "^4.4.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"styled-components": "^5.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@mui/icons-material": "^5.9.3",
|
||||
"@mui/material": "^5.9.3",
|
||||
"@types/styled-components": "^5.1.26",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "^5.24.3",
|
||||
"styled-components": "^5.3.5"
|
||||
"@mui/material": "^5.10.11",
|
||||
"react": "^18.2.0",
|
||||
"react-intl": "^5.25.1",
|
||||
"styled-components": "^5.3.6"
|
||||
}
|
||||
}
|
||||
|
@ -15,30 +15,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
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';
|
||||
import RU from './../../compiled-lang/ru.json';
|
||||
import ZH from './../../compiled-lang/zh.json';
|
||||
|
||||
class I18nMsg {
|
||||
static loadLocaleData(locale: string) {
|
||||
static loadLocaleData(locale: string): Record<string, string> {
|
||||
switch (locale) {
|
||||
case 'fr':
|
||||
return FR;
|
||||
return require('./../../compiled-lang/fr.json');
|
||||
case 'en':
|
||||
return EN;
|
||||
return require('./../../compiled-lang/en.json');
|
||||
case 'es':
|
||||
return ES;
|
||||
return require('./../../compiled-lang/es.json');
|
||||
case 'de':
|
||||
return DE;
|
||||
return require('./../../compiled-lang/de.json');
|
||||
case 'ru':
|
||||
return RU;
|
||||
return require('./../../compiled-lang/ru.json');
|
||||
case 'zh':
|
||||
return ZH;
|
||||
return require('./../../compiled-lang/zh.json');
|
||||
default:
|
||||
return EN;
|
||||
return require('./../../compiled-lang/en.json');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Designer, MindplotWebComponent, PersistenceManager } from '@wisemapping/mindplot';
|
||||
import {
|
||||
Designer,
|
||||
MindplotWebComponent,
|
||||
PersistenceManager,
|
||||
DesignerModel,
|
||||
} from '@wisemapping/mindplot';
|
||||
import Capability from '../../action/capability';
|
||||
|
||||
class Editor {
|
||||
@ -43,15 +48,19 @@ class Editor {
|
||||
return this.component?.getDesigner();
|
||||
}
|
||||
|
||||
getDesignerModel(): DesignerModel | undefined {
|
||||
return this.getDesigner().getModel();
|
||||
}
|
||||
|
||||
loadMindmap(mapId: string, persistenceManager: PersistenceManager, widgetManager): void {
|
||||
this.component.buildDesigner(persistenceManager, widgetManager);
|
||||
this.component.loadMap(mapId);
|
||||
}
|
||||
|
||||
registerEvents(canvasUpdate: (timestamp: number) => void, capability: Capability) {
|
||||
const desiger = this.component.getDesigner();
|
||||
const designer = this.component.getDesigner();
|
||||
const onNodeBlurHandler = () => {
|
||||
if (!desiger.getModel().selectedTopic()) {
|
||||
if (!designer.getModel().selectedTopic()) {
|
||||
canvasUpdate(Date.now());
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Designer } from '@wisemapping/mindplot';
|
||||
import { Designer, Topic } from '@wisemapping/mindplot';
|
||||
import NodeProperty from '../node-property';
|
||||
import {
|
||||
getTheUniqueValueOrNull,
|
||||
@ -8,10 +8,6 @@ import {
|
||||
getNextValue,
|
||||
} from '../../../components/toolbar/ToolbarValueModelBuilder';
|
||||
|
||||
/**
|
||||
* Given a designer build NodePropertyValueModel instances for the mindplot node properties
|
||||
*/
|
||||
|
||||
class NodePropertyBuilder {
|
||||
designer: Designer;
|
||||
|
||||
@ -26,24 +22,20 @@ class NodePropertyBuilder {
|
||||
noteModel: NodeProperty;
|
||||
linkModel: NodeProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param designer designer to change node properties values
|
||||
*/
|
||||
constructor(designer: Designer) {
|
||||
this.designer = designer;
|
||||
}
|
||||
|
||||
private selectedTopic() {
|
||||
return designer.getModel().selectedTopic();
|
||||
return this.designer.getModel().selectedTopic();
|
||||
}
|
||||
|
||||
private getFontSize() {
|
||||
return designer.getModel().selectedTopic()?.getFontSize();
|
||||
return this.designer.getModel().selectedTopic()?.getFontSize();
|
||||
}
|
||||
|
||||
private uniqueOrNull(propertyGetter: (Topic) => any | null) {
|
||||
const nodes = designer.getModel().filterSelectedTopics();
|
||||
private uniqueOrNull(propertyGetter: (Topic: Topic) => any | null) {
|
||||
const nodes = this.designer.getModel().filterSelectedTopics();
|
||||
return getTheUniqueValueOrNull(nodes, propertyGetter);
|
||||
}
|
||||
|
||||
@ -53,8 +45,8 @@ class NodePropertyBuilder {
|
||||
*/
|
||||
fontWeigthModel(): NodeProperty {
|
||||
return {
|
||||
getValue: () => designer.getModel().selectedTopic()?.getFontWeight(),
|
||||
switchValue: () => designer.changeFontWeight(),
|
||||
getValue: () => this.designer.getModel().selectedTopic()?.getFontWeight(),
|
||||
switchValue: () => this.designer.changeFontWeight(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -74,7 +66,7 @@ class NodePropertyBuilder {
|
||||
if (direction === SwitchValueDirection.up) {
|
||||
newValue = getNextValue(fontSizes, this.getFontSize());
|
||||
}
|
||||
designer.changeFontSize(newValue);
|
||||
this.designer.changeFontSize(newValue);
|
||||
},
|
||||
};
|
||||
return this.fontSizeModel;
|
||||
@ -87,8 +79,8 @@ class NodePropertyBuilder {
|
||||
getSelectedTopicColorModel(): NodeProperty {
|
||||
if (!this.selectedTopicColorModel)
|
||||
this.selectedTopicColorModel = {
|
||||
getValue: () => designer.getModel().selectedTopic()?.getBackgroundColor(),
|
||||
setValue: (color) => designer.changeBackgroundColor(color),
|
||||
getValue: () => this.designer.getModel().selectedTopic()?.getBackgroundColor(),
|
||||
setValue: (color) => this.designer.changeBackgroundColor(color),
|
||||
};
|
||||
|
||||
return this.selectedTopicColorModel;
|
||||
@ -122,7 +114,7 @@ class NodePropertyBuilder {
|
||||
if (!this.borderColorModel)
|
||||
this.borderColorModel = {
|
||||
getValue: () => this.uniqueOrNull((node) => node.getBorderColor()),
|
||||
setValue: (hex: string) => designer.changeBorderColor(hex),
|
||||
setValue: (hex: string) => this.designer.changeBorderColor(hex),
|
||||
};
|
||||
return this.borderColorModel;
|
||||
}
|
||||
@ -135,7 +127,7 @@ class NodePropertyBuilder {
|
||||
if (!this.fontColorModel)
|
||||
this.fontColorModel = {
|
||||
getValue: () => this.uniqueOrNull((node) => node.getFontColor()),
|
||||
setValue: (hex: string) => designer.changeFontColor(hex),
|
||||
setValue: (hex: string) => this.designer.changeFontColor(hex),
|
||||
};
|
||||
return this.fontColorModel;
|
||||
}
|
||||
@ -148,7 +140,10 @@ class NodePropertyBuilder {
|
||||
if (!this.topicIconModel)
|
||||
this.topicIconModel = {
|
||||
getValue: () => null,
|
||||
setValue: (value: string) => designer.addIconType(value),
|
||||
setValue: (value: string) => {
|
||||
const values = value.split(':');
|
||||
this.designer.addIconType(values[0] as 'image' | 'emoji', values[1]);
|
||||
},
|
||||
};
|
||||
return this.topicIconModel;
|
||||
}
|
||||
@ -177,7 +172,7 @@ class NodePropertyBuilder {
|
||||
if (!this.fontFamilyModel)
|
||||
this.fontFamilyModel = {
|
||||
getValue: () => this.uniqueOrNull((node) => node.getFontFamily()),
|
||||
setValue: (value: string) => designer.changeFontFamily(value),
|
||||
setValue: (value: string) => this.designer.changeFontFamily(value),
|
||||
};
|
||||
return this.fontFamilyModel;
|
||||
}
|
||||
@ -190,7 +185,7 @@ class NodePropertyBuilder {
|
||||
if (!this.fontStyleModel)
|
||||
this.fontStyleModel = {
|
||||
getValue: () => this.selectedTopic()?.getFontStyle(),
|
||||
switchValue: () => designer.changeFontStyle(),
|
||||
switchValue: () => this.designer.changeFontStyle(),
|
||||
};
|
||||
return this.fontStyleModel;
|
||||
}
|
||||
@ -203,7 +198,7 @@ class NodePropertyBuilder {
|
||||
if (!this.topicShapeModel)
|
||||
this.topicShapeModel = {
|
||||
getValue: () => this.uniqueOrNull((node) => node.getShapeType()),
|
||||
setValue: (value: string) => designer.changeTopicShape(value),
|
||||
setValue: (value: string) => this.designer.changeTopicShape(value),
|
||||
};
|
||||
return this.topicShapeModel;
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ const UndoAndRedo = ({ configuration, disabledCondition, model }: UndoAndRedo) =
|
||||
setDisabled(!isDisabled);
|
||||
|
||||
return () => {
|
||||
designer.removeEvent('modelUpdate', handleUpdate);
|
||||
model.getDesigner().removeEvent('modelUpdate', handleUpdate);
|
||||
};
|
||||
};
|
||||
|
||||
if (model.getDesigner()) {
|
||||
designer.addEvent('modelUpdate', handleUpdate);
|
||||
model.getDesigner().addEvent('modelUpdate', handleUpdate);
|
||||
}
|
||||
}
|
||||
}, [model?.isMapLoadded()]);
|
||||
@ -52,7 +52,7 @@ const UndoAndRedo = ({ configuration, disabledCondition, model }: UndoAndRedo) =
|
||||
...configuration,
|
||||
disabled: () => disabled,
|
||||
}}
|
||||
></ToolbarMenuItem>
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default UndoAndRedo;
|
||||
|
@ -0,0 +1,30 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import React from 'react';
|
||||
import iconGroups from './iconGroups.json';
|
||||
import { SvgImageIcon } from '@wisemapping/mindplot';
|
||||
import NodeProperty from '../../../../../classes/model/node-property';
|
||||
|
||||
type IconImageTab = {
|
||||
iconModel: NodeProperty;
|
||||
};
|
||||
const IconImageTab = ({ iconModel }: IconImageTab) => {
|
||||
return (
|
||||
<Box sx={{ width: '350px' }}>
|
||||
{iconGroups.map((family, i) => (
|
||||
<span>
|
||||
{family.icons.map((icon) => (
|
||||
<img
|
||||
className="panelIcon"
|
||||
key={icon}
|
||||
src={SvgImageIcon.getImageUrl(icon)}
|
||||
onClick={() => {
|
||||
iconModel.setValue(`image:${icon}`);
|
||||
}}
|
||||
></img>
|
||||
))}
|
||||
</span>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default IconImageTab;
|
@ -15,78 +15,57 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Box from '@mui/material/Box';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import React from 'react';
|
||||
import iconGroups from './iconGroups.json';
|
||||
import { ImageIcon } from '@wisemapping/mindplot';
|
||||
import React, { useEffect } from 'react';
|
||||
import NodeProperty from '../../../../classes/model/node-property';
|
||||
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react';
|
||||
import DesignerKeyboard from '@wisemapping/mindplot/src/components/DesignerKeyboard';
|
||||
import IconImageTab from './image-icon-tab';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import FormGroup from '@mui/material/FormGroup';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
|
||||
/**
|
||||
* emoji picker for editor toolbar
|
||||
*/
|
||||
const IconPicker = (props: { closeModal: () => void; iconModel: NodeProperty }) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
type IconPickerProp = {
|
||||
closeModal: () => void;
|
||||
iconModel: NodeProperty;
|
||||
};
|
||||
|
||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setValue(newValue);
|
||||
const IconPicker = ({ closeModal, iconModel }: IconPickerProp) => {
|
||||
const [checked, setChecked] = React.useState(true);
|
||||
|
||||
const handleCheck = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setChecked(!checked);
|
||||
};
|
||||
|
||||
// Review ...
|
||||
useEffect(() => {
|
||||
DesignerKeyboard.pause();
|
||||
return () => { DesignerKeyboard.resume(); }
|
||||
}, []);
|
||||
|
||||
|
||||
const handleEmojiSelect = (emoji: EmojiClickData) => {
|
||||
const emojiChar = emoji.emoji;
|
||||
iconModel.setValue(`emoji:${emojiChar}`);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '250px' }}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="Icons tabs">
|
||||
{iconGroups.map((family, i) => (
|
||||
<Tab
|
||||
key={family.id}
|
||||
icon={<img className="panelIcon" src={ImageIcon.getImageUrl(family.icons[0])} />}
|
||||
{...a11yProps(i)}
|
||||
<div style={{ padding: '5px' }}>
|
||||
<FormGroup>
|
||||
<FormControlLabel label="Show Images" control={<Switch onChange={handleCheck} />} />
|
||||
</FormGroup>
|
||||
|
||||
{checked && (
|
||||
<EmojiPicker
|
||||
onEmojiClick={handleEmojiSelect}
|
||||
lazyLoadEmojis={true}
|
||||
autoFocusSearch={true}
|
||||
previewConfig={{ showPreview: false }}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{iconGroups.map((family, i) => (
|
||||
<TabPanel key={family.id} value={value} index={i}>
|
||||
{family.icons.map((icon) => (
|
||||
<img
|
||||
className="panelIcon"
|
||||
key={icon}
|
||||
src={ImageIcon.getImageUrl(icon)}
|
||||
onClick={() => {
|
||||
props.iconModel.setValue(icon);
|
||||
props.closeModal();
|
||||
}}
|
||||
></img>
|
||||
))}
|
||||
</TabPanel>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)}
|
||||
|
||||
/**
|
||||
* tab panel used for display icon families in tabs
|
||||
*/
|
||||
const TabPanel = (props: { children?: React.ReactNode; index: number; value: number }) => {
|
||||
const { children, value, index } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
role="tabpanel"
|
||||
hidden={value !== index}
|
||||
id={`simple-tabpanel-${index}`}
|
||||
aria-labelledby={`simple-tab-${index}`}
|
||||
>
|
||||
{value === index && <Box>{children}</Box>}
|
||||
{!checked && <IconImageTab iconModel={iconModel} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const a11yProps = (index: number) => {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
'aria-controls': `simple-tabpanel-${index}`,
|
||||
};
|
||||
};
|
||||
export default IconPicker;
|
||||
|
@ -15,7 +15,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { $msg } from '@wisemapping/mindplot';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
|
@ -116,7 +116,7 @@ const AppBar = ({ model, mapInfo, capability, onAction, accountConfig }: AppBarP
|
||||
intl.formatMessage({ id: 'appbar.tooltip-undo', defaultMessage: 'Undo' }),
|
||||
'Z',
|
||||
),
|
||||
onClick: () => designer.undo(),
|
||||
onClick: () => model.getDesigner().undo(),
|
||||
}}
|
||||
disabledCondition={(event) => event.undoSteps > 0}
|
||||
model={model}
|
||||
@ -134,7 +134,7 @@ const AppBar = ({ model, mapInfo, capability, onAction, accountConfig }: AppBarP
|
||||
intl.formatMessage({ id: 'appbar.tooltip-redo', defaultMessage: 'Redo' }),
|
||||
'Shift + Z',
|
||||
),
|
||||
onClick: () => designer.redo(),
|
||||
onClick: () => model.getDesigner().redo(),
|
||||
}}
|
||||
disabledCondition={(event) => event.redoSteps > 0}
|
||||
model={model}
|
||||
|
@ -146,7 +146,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
],
|
||||
},
|
||||
],
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -230,7 +230,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
],
|
||||
},
|
||||
],
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -243,9 +243,9 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
defaultMessage: 'Add Relationship',
|
||||
}),
|
||||
onClick: (e) => {
|
||||
designer.showRelPivot(e);
|
||||
model.getDesigner().showRelPivot(e);
|
||||
},
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -268,7 +268,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
),
|
||||
},
|
||||
],
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -292,7 +292,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
),
|
||||
},
|
||||
],
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -304,6 +304,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
id: 'editor-panel.tooltip-add-icon',
|
||||
defaultMessage: 'Add Icon',
|
||||
}),
|
||||
useClickToClose: true,
|
||||
options: [
|
||||
{
|
||||
tooltip: 'Node icon',
|
||||
@ -315,7 +316,7 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
),
|
||||
},
|
||||
],
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
const addNodeToolbarConfiguration = {
|
||||
@ -323,9 +324,10 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
tooltip:
|
||||
intl.formatMessage({ id: 'editor-panel.tooltip-add-topic', defaultMessage: 'Add Topic' }) +
|
||||
' (Enter)',
|
||||
onClick: () => designer.createSiblingForSelectedNode(),
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
onClick: () => model.getDesigner().createSiblingForSelectedNode(),
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
const deleteNodeToolbarConfiguration = {
|
||||
icon: <RemoveCircleOutlineIcon />,
|
||||
tooltip:
|
||||
@ -333,9 +335,10 @@ export function buildEditorPanelConfig(model: Editor): ActionConfig[] {
|
||||
id: 'editor-panel.tooltip-delete-topic',
|
||||
defaultMessage: 'Delete Topic',
|
||||
}) + ' (Delete)',
|
||||
onClick: () => designer.deleteSelectedEntities(),
|
||||
disabled: () => designer.getModel().filterSelectedTopics().length === 0,
|
||||
onClick: () => model.getDesigner().deleteSelectedEntities(),
|
||||
disabled: () => model.getDesigner().getModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
return [
|
||||
addNodeToolbarConfiguration,
|
||||
deleteNodeToolbarConfiguration,
|
||||
|
@ -93,7 +93,6 @@ const Editor = ({
|
||||
// Initialize locate ...
|
||||
const locale = options.locale;
|
||||
const msg = I18nMsg.loadLocaleData(locale);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={editorTheme}>
|
||||
<IntlProvider locale={locale} messages={msg}>
|
||||
|
@ -53,7 +53,7 @@ export function buildZoomToolbarConfig(model: Editor, capability: Capability): A
|
||||
%
|
||||
{!model?.isMapLoadded()
|
||||
? 100
|
||||
: Math.floor((1 / designer.getWorkSpace()?.getZoom()) * 100)}
|
||||
: Math.floor((1 / model.getDesigner().getWorkSpace()?.getZoom()) * 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
),
|
||||
|
@ -19,8 +19,8 @@ body {
|
||||
height: 100%;
|
||||
}
|
||||
.panelIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
margin-left: 4px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
Exporter,
|
||||
Importer,
|
||||
TextImporterFactory,
|
||||
XMLSerializerFactory,
|
||||
} from '@wisemapping/mindplot';
|
||||
|
||||
import Editor from './components';
|
||||
@ -41,7 +42,6 @@ import MapInfo from './classes/model/map-info';
|
||||
|
||||
declare global {
|
||||
// used in mindplot
|
||||
var designer: Designer;
|
||||
var accountEmail: string;
|
||||
}
|
||||
|
||||
@ -70,6 +70,7 @@ export {
|
||||
TextImporterFactory,
|
||||
EditorOptions,
|
||||
MapInfo,
|
||||
XMLSerializerFactory,
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
|
@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
import MapInfoImpl from './MapInfoImpl';
|
||||
@ -37,13 +37,14 @@ const options: EditorOptions = {
|
||||
enableKeyboardEvents: true,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container!);
|
||||
root.render(
|
||||
<Editor
|
||||
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
|
||||
options={options}
|
||||
persistenceManager={persistence}
|
||||
onAction={(action) => console.log('action called:', action)}
|
||||
onLoad={initialization}
|
||||
/>,
|
||||
document.getElementById('root'),
|
||||
/>
|
||||
);
|
||||
|
@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
import MapInfoImpl from './MapInfoImpl';
|
||||
@ -38,13 +38,14 @@ const options: EditorOptions = {
|
||||
};
|
||||
|
||||
const mapInfo = new MapInfoImpl('welcome', 'Develop WiseMapping', true);
|
||||
ReactDOM.render(
|
||||
<Editor
|
||||
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container!);
|
||||
root.render(<Editor
|
||||
mapInfo={mapInfo}
|
||||
options={options}
|
||||
persistenceManager={persistence}
|
||||
onAction={(action) => console.log('action called:', action)}
|
||||
onLoad={initialization}
|
||||
/>,
|
||||
document.getElementById('root'),
|
||||
/>
|
||||
);
|
||||
|
@ -16,8 +16,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
import MapInfoImpl from './MapInfoImpl';
|
||||
|
||||
@ -38,7 +38,9 @@ const options: EditorOptions = {
|
||||
enableKeyboardEvents: true,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById('app');
|
||||
const root = createRoot(container!);
|
||||
root.render(
|
||||
<Editor
|
||||
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
|
||||
options={options}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import '../css/viewmode.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
import MapInfoImpl from './MapInfoImpl';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
const initialization = (designer: Designer) => {
|
||||
designer.addEvent('loadSuccess', () => {
|
||||
@ -38,13 +38,13 @@ const options: EditorOptions = {
|
||||
enableKeyboardEvents: true,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container!);
|
||||
root.render(
|
||||
<Editor
|
||||
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
|
||||
options={options}
|
||||
persistenceManager={persistence}
|
||||
onAction={(action) => console.log('action called:', action)}
|
||||
onLoad={initialization}
|
||||
/>,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
/>);
|
||||
|
@ -9,7 +9,6 @@ const prodConfig = {
|
||||
},
|
||||
externals: {
|
||||
react: 'react',
|
||||
'react-dom': 'react-dom',
|
||||
'react-intl': 'react-intl',
|
||||
},
|
||||
plugins: [new CleanWebpackPlugin()],
|
||||
|
@ -35,6 +35,7 @@
|
||||
"@types/jquery": "^3.5.11",
|
||||
"@wisemapping/core-js": "^0.4.0",
|
||||
"@wisemapping/web2d": "^0.4.0",
|
||||
"emoji-picker-react": "^4.4.3",
|
||||
"jest": "^27.4.5",
|
||||
"jquery": "3.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
@ -873,16 +873,16 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
addIconType(iconType: string): void {
|
||||
addIconType(type: 'image' | 'emoji', iconType: string): void {
|
||||
const topicsIds = this.getModel().filterTopicsIds();
|
||||
|
||||
const featureType: FeatureType = (
|
||||
type === 'emoji' ? TopicFeatureFactory.EmojiIcon.id : TopicFeatureFactory.SvgIcon.id
|
||||
) as FeatureType;
|
||||
if (topicsIds.length > 0) {
|
||||
this._actionDispatcher.addFeatureToTopic(
|
||||
topicsIds[0],
|
||||
TopicFeatureFactory.Icon.id as FeatureType,
|
||||
{
|
||||
this._actionDispatcher.addFeatureToTopic(topicsIds[0], featureType, {
|
||||
id: iconType,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
99
packages/mindplot/src/components/EmojiCharIcon.ts
Normal file
99
packages/mindplot/src/components/EmojiCharIcon.ts
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { Text, Group, ElementClass, Point } from '@wisemapping/web2d';
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
|
||||
import Icon from './Icon';
|
||||
import IconGroup from './IconGroup';
|
||||
import SvgIconModel from './model/SvgIconModel';
|
||||
import SizeType from './SizeType';
|
||||
import Topic from './Topic';
|
||||
import ActionDispatcher from './ActionDispatcher';
|
||||
|
||||
class EmojiCharIcon implements Icon {
|
||||
private char: string;
|
||||
|
||||
private element: ElementClass;
|
||||
|
||||
private group: IconGroup;
|
||||
|
||||
private iconModel: SvgIconModel;
|
||||
|
||||
private topic: Topic;
|
||||
|
||||
constructor(topic: Topic, iconModel: SvgIconModel, readOnly: boolean) {
|
||||
$assert(iconModel, 'iconModel can not be null');
|
||||
$assert(topic, 'topic can not be null');
|
||||
this.iconModel = iconModel;
|
||||
this.topic = topic;
|
||||
|
||||
this.element = new Group({
|
||||
width: 90,
|
||||
height: 90,
|
||||
x: 0,
|
||||
y: 0,
|
||||
coordSizeWidth: 15,
|
||||
coordSizeHeight: 15,
|
||||
coordOriginY: 2,
|
||||
});
|
||||
const iconText = new Text();
|
||||
iconText.setText(iconModel.getIconType());
|
||||
this.element.append(iconText);
|
||||
|
||||
// Add events ...
|
||||
if (!readOnly) {
|
||||
this.element.setCursor('pointer');
|
||||
}
|
||||
}
|
||||
|
||||
getElement(): ElementClass {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
setGroup(group: IconGroup) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
getGroup(): IconGroup {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
getSize(): SizeType {
|
||||
return this.group.getSize();
|
||||
}
|
||||
|
||||
getPosition(): Point {
|
||||
return this.group.getPosition();
|
||||
}
|
||||
|
||||
addEvent(type: string, fnc: any): void {
|
||||
this.element.addEvent(type, fnc);
|
||||
}
|
||||
|
||||
remove() {
|
||||
const actionDispatcher = ActionDispatcher.getInstance();
|
||||
const featureId = this.iconModel.getId();
|
||||
actionDispatcher.removeFeatureFromTopic(this.topic.getId(), featureId);
|
||||
}
|
||||
|
||||
getModel(): SvgIconModel {
|
||||
return this.iconModel;
|
||||
}
|
||||
}
|
||||
|
||||
export default EmojiCharIcon;
|
@ -1,70 +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.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import { Image, Point } from '@wisemapping/web2d';
|
||||
import { Point, ElementClass } from '@wisemapping/web2d';
|
||||
import IconGroup from './IconGroup';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
|
||||
abstract class Icon {
|
||||
protected _image: Image;
|
||||
interface Icon {
|
||||
getElement(): ElementClass;
|
||||
|
||||
protected _group: IconGroup;
|
||||
setGroup(group: IconGroup);
|
||||
|
||||
constructor(url: string) {
|
||||
$assert(url, 'image url can not be null');
|
||||
this._image = new Image();
|
||||
this._image.setHref(url);
|
||||
this._image.setSize(Icon.SIZE, Icon.SIZE);
|
||||
}
|
||||
getGroup(): IconGroup;
|
||||
|
||||
getImage(): Image {
|
||||
return this._image;
|
||||
}
|
||||
getSize(): SizeType;
|
||||
|
||||
setGroup(group: IconGroup) {
|
||||
this._group = group;
|
||||
}
|
||||
getPosition(): Point;
|
||||
|
||||
getGroup(): IconGroup {
|
||||
return this._group;
|
||||
}
|
||||
addEvent(type: string, fnc): void;
|
||||
|
||||
getSize(): SizeType {
|
||||
return this._image.getSize();
|
||||
}
|
||||
remove(): void;
|
||||
|
||||
getPosition(): Point {
|
||||
return this._image.getPosition();
|
||||
}
|
||||
|
||||
addEvent(type: string, fnc): void {
|
||||
this._image.addEvent(type, fnc);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
remove() {
|
||||
throw new Error('Unsupported operation');
|
||||
}
|
||||
|
||||
abstract getModel(): FeatureModel;
|
||||
|
||||
static SIZE = 90;
|
||||
getModel();
|
||||
}
|
||||
|
||||
export default Icon;
|
||||
|
@ -19,7 +19,7 @@
|
||||
import { $assert, $defined } from '@wisemapping/core-js';
|
||||
import { Group, ElementClass, Point } from '@wisemapping/web2d';
|
||||
import IconGroupRemoveTip from './IconGroupRemoveTip';
|
||||
import Icon from './Icon';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
|
||||
@ -29,7 +29,7 @@ ORDER_BY_TYPE.set('note', 1);
|
||||
ORDER_BY_TYPE.set('link', 2);
|
||||
|
||||
class IconGroup {
|
||||
private _icons: Icon[];
|
||||
private _icons: ImageIcon[];
|
||||
|
||||
private _group: any;
|
||||
|
||||
@ -84,7 +84,7 @@ class IconGroup {
|
||||
this._resize(this._icons.length);
|
||||
}
|
||||
|
||||
addIcon(icon: Icon, remove: boolean) {
|
||||
addIcon(icon: ImageIcon, remove: boolean) {
|
||||
$defined(icon, 'icon is not defined');
|
||||
|
||||
// Order could have change, need to re-add all.
|
||||
@ -104,7 +104,7 @@ class IconGroup {
|
||||
this._resize(this._icons.length);
|
||||
this._icons.forEach((i, index) => {
|
||||
this._positionIcon(i, index);
|
||||
const imageShape = i.getImage();
|
||||
const imageShape = i.getElement();
|
||||
this._group.append(imageShape);
|
||||
});
|
||||
|
||||
@ -140,11 +140,11 @@ class IconGroup {
|
||||
this._removeIcon(icon);
|
||||
}
|
||||
|
||||
private _removeIcon(icon: Icon) {
|
||||
private _removeIcon(icon: ImageIcon) {
|
||||
$assert(icon, 'icon can not be null');
|
||||
|
||||
this._removeTip.close(0);
|
||||
this._group.removeChild(icon.getImage());
|
||||
this._group.removeChild(icon.getElement());
|
||||
|
||||
this._icons = this._icons.filter((i) => i !== icon);
|
||||
this._resize(this._icons.length);
|
||||
@ -175,13 +175,15 @@ class IconGroup {
|
||||
private _resize(iconsLength: number) {
|
||||
this._group.setSize(iconsLength * this._iconSize.width, this._iconSize.height);
|
||||
|
||||
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||
const iconSize = ImageIcon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||
this._group.setCoordSize(iconsLength * iconSize, iconSize);
|
||||
}
|
||||
|
||||
private _positionIcon(icon: Icon, order: number) {
|
||||
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||
icon.getImage().setPosition(iconSize * order + IconGroup.ICON_PADDING, IconGroup.ICON_PADDING);
|
||||
private _positionIcon(icon: ImageIcon, order: number) {
|
||||
const iconSize = ImageIcon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||
icon
|
||||
.getElement()
|
||||
.setPosition(iconSize * order + IconGroup.ICON_PADDING, IconGroup.ICON_PADDING);
|
||||
}
|
||||
|
||||
static ICON_PADDING = 5;
|
||||
|
@ -1,338 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import Icon from './Icon';
|
||||
import ActionDispatcher from './ActionDispatcher';
|
||||
|
||||
function importAll(r) {
|
||||
const images = {};
|
||||
r.keys().forEach((item) => {
|
||||
images[item.replace('./', '')] = r(item);
|
||||
});
|
||||
return images;
|
||||
}
|
||||
|
||||
const images = importAll(require.context('../../assets/icons', false, /\.(png|svg)$/));
|
||||
|
||||
class ImageIcon extends Icon {
|
||||
constructor(topic, iconModel, readOnly) {
|
||||
$assert(iconModel, 'iconModel can not be null');
|
||||
$assert(topic, 'topic can not be null');
|
||||
|
||||
// Build graph image representation ...
|
||||
let iconType = iconModel.getIconType();
|
||||
|
||||
// Hack for overwrite wrong icon. Remove in couple of months ...
|
||||
if (iconType === 'meeetapps_facebook-messenger') {
|
||||
iconType = 'meetapps_facebook-messenger';
|
||||
}
|
||||
|
||||
const imgUrl = ImageIcon.getImageUrl(iconType);
|
||||
super(imgUrl);
|
||||
|
||||
this._topicId = topic.getId();
|
||||
this._featureModel = iconModel;
|
||||
|
||||
if (!readOnly) {
|
||||
// Icon
|
||||
const image = this.getImage();
|
||||
const me = this;
|
||||
image.addEvent('click', () => {
|
||||
const iconTypeClick = iconModel.getIconType();
|
||||
const newIconType = ImageIcon._getNextFamilyIconId(iconTypeClick);
|
||||
iconModel.setIconType(newIconType);
|
||||
|
||||
me._image.setHref(ImageIcon.getImageUrl(newIconType));
|
||||
});
|
||||
this._image.setCursor('pointer');
|
||||
}
|
||||
}
|
||||
|
||||
static getImageUrl(iconId) {
|
||||
let result = images[`${iconId}.svg`];
|
||||
if (!result) {
|
||||
result = images[`${iconId}.png`];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getModel() {
|
||||
return this._featureModel;
|
||||
}
|
||||
|
||||
static _getNextFamilyIconId(iconId) {
|
||||
const familyIcons = ImageIcon._getFamilyIcons(iconId);
|
||||
$assert(familyIcons !== null, `Family Icon not found: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
for (let i = 0; i < familyIcons.length && result == null; i++) {
|
||||
if (familyIcons[i] === iconId) {
|
||||
// Is last one?
|
||||
if (i === familyIcons.length - 1) {
|
||||
[result] = familyIcons;
|
||||
} else {
|
||||
result = familyIcons[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static _getFamilyIcons(iconId) {
|
||||
$assert(iconId != null, 'id must not be null');
|
||||
$assert(iconId.indexOf('_') !== -1, `Invalid icon id (it must contain '_'). Id: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
for (let i = 0; i < ImageIcon.prototype.ICON_FAMILIES.length; i++) {
|
||||
const family = ImageIcon.prototype.ICON_FAMILIES[i];
|
||||
const iconFamilyId = iconId.substr(0, iconId.indexOf('_'));
|
||||
|
||||
if (family.id === iconFamilyId) {
|
||||
result = family.icons;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove() {
|
||||
const actionDispatcher = ActionDispatcher.getInstance();
|
||||
const featureId = this._featureModel.getId();
|
||||
const topicId = this._topicId;
|
||||
actionDispatcher.removeFeatureFromTopic(topicId, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
ImageIcon.prototype.ICON_FAMILIES = [
|
||||
{
|
||||
id: 'face',
|
||||
icons: ['face_plain', 'face_sad', 'face_crying', 'face_smile', 'face_surprise', 'face_wink'],
|
||||
},
|
||||
{
|
||||
id: 'funy',
|
||||
icons: ['funy_angel', 'funy_devilish', 'funy_glasses', 'funy_grin', 'funy_kiss', 'funy_monkey'],
|
||||
},
|
||||
{
|
||||
id: 'sport',
|
||||
icons: [
|
||||
'sport_basketball',
|
||||
'sport_football',
|
||||
'sport_golf',
|
||||
'sport_raquet',
|
||||
'sport_shuttlecock',
|
||||
'sport_soccer',
|
||||
'sport_tennis',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'bulb',
|
||||
icons: ['bulb_light_on', 'bulb_light_off'],
|
||||
},
|
||||
{
|
||||
id: 'thumb',
|
||||
icons: ['thumb_thumb_up', 'thumb_thumb_down'],
|
||||
},
|
||||
{
|
||||
id: 'tick',
|
||||
icons: ['tick_tick', 'tick_cross'],
|
||||
},
|
||||
{
|
||||
id: 'onoff',
|
||||
icons: [
|
||||
'onoff_clock',
|
||||
'onoff_clock_red',
|
||||
'onoff_add',
|
||||
'onoff_delete',
|
||||
'onoff_status_offline',
|
||||
'onoff_status_online',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'money',
|
||||
icons: [
|
||||
'money_money',
|
||||
'money_dollar',
|
||||
'money_euro',
|
||||
'money_pound',
|
||||
'money_yen',
|
||||
'money_coins',
|
||||
'money_ruby',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'time',
|
||||
icons: ['time_calendar', 'time_clock', 'time_hourglass'],
|
||||
},
|
||||
{
|
||||
id: 'number',
|
||||
icons: [
|
||||
'number_1',
|
||||
'number_2',
|
||||
'number_3',
|
||||
'number_4',
|
||||
'number_5',
|
||||
'number_6',
|
||||
'number_7',
|
||||
'number_8',
|
||||
'number_9',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'chart',
|
||||
icons: ['chart_bar', 'chart_line', 'chart_curve', 'chart_pie', 'chart_organisation'],
|
||||
},
|
||||
{
|
||||
id: 'sign',
|
||||
icons: ['sign_warning', 'sign_info', 'sign_stop', 'sign_help', 'sign_cancel'],
|
||||
},
|
||||
{
|
||||
id: 'hard',
|
||||
icons: [
|
||||
'hard_cd',
|
||||
'hard_computer',
|
||||
'hard_controller',
|
||||
'hard_driver_disk',
|
||||
'hard_ipod',
|
||||
'hard_keyboard',
|
||||
'hard_mouse',
|
||||
'hard_printer',
|
||||
'hard_webcam',
|
||||
'hard_microphone',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'things',
|
||||
icons: [
|
||||
'things_address_book',
|
||||
'things_wrench',
|
||||
'things_pin',
|
||||
'things_window-layout',
|
||||
'things_bubbles',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'soft',
|
||||
icons: [
|
||||
'soft_bug',
|
||||
'soft_cursor',
|
||||
'soft_database_table',
|
||||
'soft_database',
|
||||
'soft_feed',
|
||||
'soft_folder_explore',
|
||||
'soft_rss',
|
||||
'soft_penguin',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'arrow',
|
||||
icons: ['arrow_up', 'arrow_down', 'arrow_left', 'arrow_right'],
|
||||
},
|
||||
{
|
||||
id: 'arrowc',
|
||||
icons: [
|
||||
'arrowc_rotate_anticlockwise',
|
||||
'arrowc_rotate_clockwise',
|
||||
'arrowc_turn_left',
|
||||
'arrowc_turn_right',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'people',
|
||||
icons: ['people_group', 'people_male1', 'people_male2', 'people_female1', 'people_female2'],
|
||||
},
|
||||
{
|
||||
id: 'mail',
|
||||
icons: ['mail_envelop', 'mail_mailbox', 'mail_edit', 'mail_list'],
|
||||
},
|
||||
{
|
||||
id: 'flag',
|
||||
icons: ['flag_blue', 'flag_green', 'flag_orange', 'flag_pink', 'flag_purple', 'flag_yellow'],
|
||||
},
|
||||
{
|
||||
id: 'social',
|
||||
icons: [
|
||||
'social_facebook',
|
||||
'social_twitter',
|
||||
'social_redit',
|
||||
'social_instagram',
|
||||
'social_google-plus',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'meetapps',
|
||||
icons: [
|
||||
'meetapps_slack',
|
||||
'meetapps_google-meet',
|
||||
'meetapps_whatapp',
|
||||
'meetapps_ms-teams',
|
||||
'meetapps_zoom',
|
||||
'meetapps_facebook-messenger',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'appsgoogle',
|
||||
icons: ['appsgoogle_youtube', 'appsgoogle_gmail', 'appsgoogle_maps'],
|
||||
},
|
||||
{
|
||||
id: 'tag',
|
||||
icons: ['tag_blue', 'tag_green', 'tag_orange', 'tag_red', 'tag_pink', 'tag_yellow'],
|
||||
},
|
||||
{
|
||||
id: 'object',
|
||||
icons: [
|
||||
'object_bell',
|
||||
'object_clanbomber',
|
||||
'object_key',
|
||||
'object_pencil',
|
||||
'object_phone',
|
||||
'object_magnifier',
|
||||
'object_clip',
|
||||
'object_music',
|
||||
'object_star',
|
||||
'object_wizard',
|
||||
'object_house',
|
||||
'object_cake',
|
||||
'object_camera',
|
||||
'object_palette',
|
||||
'object_rainbow',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'weather',
|
||||
icons: [
|
||||
'weather_clear-night',
|
||||
'weather_clear',
|
||||
'weather_few-clouds-night',
|
||||
'weather_few-clouds',
|
||||
'weather_overcast',
|
||||
'weather_severe-alert',
|
||||
'weather_showers-scattered',
|
||||
'weather_showers',
|
||||
'weather_snow',
|
||||
'weather_storm',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'task',
|
||||
icons: ['task_0', 'task_25', 'task_50', 'task_75', 'task_100'],
|
||||
},
|
||||
];
|
||||
|
||||
export default ImageIcon;
|
71
packages/mindplot/src/components/ImageIcon.ts
Normal file
71
packages/mindplot/src/components/ImageIcon.ts
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import { Image, Point, ElementClass } from '@wisemapping/web2d';
|
||||
import IconGroup from './IconGroup';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import Icon from './Icon';
|
||||
|
||||
abstract class ImageIcon implements Icon {
|
||||
protected _image: Image;
|
||||
|
||||
protected _group: IconGroup;
|
||||
|
||||
constructor(url: string) {
|
||||
$assert(url, 'image url can not be null');
|
||||
this._image = new Image();
|
||||
this._image.setHref(url);
|
||||
this._image.setSize(ImageIcon.SIZE, ImageIcon.SIZE);
|
||||
}
|
||||
|
||||
getElement(): ElementClass {
|
||||
return this._image;
|
||||
}
|
||||
|
||||
setGroup(group: IconGroup) {
|
||||
this._group = group;
|
||||
}
|
||||
|
||||
getGroup(): IconGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
getSize(): SizeType {
|
||||
return this._image.getSize();
|
||||
}
|
||||
|
||||
getPosition(): Point {
|
||||
return this._image.getPosition();
|
||||
}
|
||||
|
||||
addEvent(type: string, fnc): void {
|
||||
this._image.addEvent(type, fnc);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
remove() {
|
||||
throw new Error('Unsupported operation');
|
||||
}
|
||||
|
||||
abstract getModel(): FeatureModel;
|
||||
|
||||
static SIZE = 90;
|
||||
}
|
||||
|
||||
export default ImageIcon;
|
@ -16,14 +16,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import Icon from './Icon';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import LinksImage from '../../assets/icons/links.svg';
|
||||
import LinkModel from './model/LinkModel';
|
||||
import Topic from './Topic';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import WidgetManager from './WidgetManager';
|
||||
|
||||
class LinkIcon extends Icon {
|
||||
class LinkIcon extends ImageIcon {
|
||||
private _linksModel: FeatureModel;
|
||||
|
||||
private _topic: Topic;
|
||||
|
@ -16,14 +16,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import Icon from './Icon';
|
||||
import NotesImage from '../../assets/icons/notes.svg';
|
||||
import Topic from './Topic';
|
||||
import NoteModel from './model/NoteModel';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import WidgetManager from './WidgetManager';
|
||||
import ImageIcon from './ImageIcon';
|
||||
|
||||
class NoteIcon extends Icon {
|
||||
class NoteIcon extends ImageIcon {
|
||||
private _linksModel: NoteModel;
|
||||
|
||||
private _topic: Topic;
|
||||
|
132
packages/mindplot/src/components/SvgImageIcon.js
Normal file
132
packages/mindplot/src/components/SvgImageIcon.js
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import ActionDispatcher from './ActionDispatcher';
|
||||
import iconFamily from './model/SvgIconFamily.json';
|
||||
|
||||
function importAll(r) {
|
||||
const images = {};
|
||||
r.keys().forEach((item) => {
|
||||
images[item.replace('./', '')] = r(item);
|
||||
});
|
||||
return images;
|
||||
}
|
||||
|
||||
const images = importAll(require.context('../../assets/icons', false, /\.(png|svg)$/));
|
||||
|
||||
class SvgImageIcon extends ImageIcon {
|
||||
constructor(topic, iconModel, readOnly) {
|
||||
$assert(iconModel, 'iconModel can not be null');
|
||||
$assert(topic, 'topic can not be null');
|
||||
|
||||
// Build graph image representation ...
|
||||
const iconType = iconModel.getIconType();
|
||||
const imgUrl = SvgImageIcon.getImageUrl(iconType);
|
||||
super(imgUrl);
|
||||
|
||||
this._topicId = topic.getId();
|
||||
this._featureModel = iconModel;
|
||||
|
||||
if (!readOnly) {
|
||||
// Icon
|
||||
const image = this.getElement();
|
||||
const me = this;
|
||||
image.addEvent('click', () => {
|
||||
const iconTypeClick = iconModel.getIconType();
|
||||
const newIconType = SvgImageIcon._getNextFamilyIconId(iconTypeClick);
|
||||
iconModel.setIconType(newIconType);
|
||||
|
||||
me._image.setHref(SvgImageIcon.getImageUrl(newIconType));
|
||||
});
|
||||
this._image.setCursor('pointer');
|
||||
}
|
||||
}
|
||||
|
||||
static getImageUrl(iconId) {
|
||||
let result = images[`${iconId}.svg`];
|
||||
if (!result) {
|
||||
result = images[`${iconId}.png`];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getModel() {
|
||||
return this._featureModel;
|
||||
}
|
||||
|
||||
static _getNextFamilyIconId(iconId) {
|
||||
const familyIcons = SvgImageIcon._getFamilyIcons(iconId);
|
||||
$assert(familyIcons !== null, `Family Icon not found: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
for (let i = 0; i < familyIcons.length && result == null; i++) {
|
||||
if (familyIcons[i] === iconId) {
|
||||
// Is last one?
|
||||
if (i === familyIcons.length - 1) {
|
||||
[result] = familyIcons;
|
||||
} else {
|
||||
result = familyIcons[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static _getNextUnicode(iconId) {
|
||||
let result = null;
|
||||
for (let i = 0; i < iconFamily.length; i++) {
|
||||
const family = iconFamily[i];
|
||||
const iconFamilyId = iconId.substr(0, iconId.indexOf('_'));
|
||||
|
||||
if (family.id === iconFamilyId) {
|
||||
result = family.icons;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static _getFamilyIcons(iconId) {
|
||||
$assert(iconId != null, 'id must not be null');
|
||||
$assert(iconId.indexOf('_') !== -1, `Invalid icon id (it must contain '_'). Id: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
for (let i = 0; i < iconFamily.length; i++) {
|
||||
const family = iconFamily[i];
|
||||
const iconFamilyId = iconId.substr(0, iconId.indexOf('_'));
|
||||
|
||||
if (family.id === iconFamilyId) {
|
||||
result = family.icons;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove() {
|
||||
const actionDispatcher = ActionDispatcher.getInstance();
|
||||
const featureId = this._featureModel.getId();
|
||||
const topicId = this._topicId;
|
||||
actionDispatcher.removeFeatureFromTopic(topicId, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
export default SvgImageIcon;
|
@ -40,7 +40,7 @@ import NoteModel from './model/NoteModel';
|
||||
import LinkModel from './model/LinkModel';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import Icon from './Icon';
|
||||
import ImageIcon from './ImageIcon';
|
||||
|
||||
const ICON_SCALING_FACTOR = 1.3;
|
||||
|
||||
@ -322,13 +322,18 @@ abstract class Topic extends NodeGraph {
|
||||
const featuresModel = model.getFeatures();
|
||||
featuresModel.forEach((f) => {
|
||||
const icon = TopicFeatureFactory.createIcon(this, f, this.isReadOnly());
|
||||
result.addIcon(icon, f.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly());
|
||||
|
||||
const type = f.getType();
|
||||
const addRemoveAction =
|
||||
type === TopicFeatureFactory.SvgIcon.id || type === TopicFeatureFactory.EmojiIcon.id;
|
||||
|
||||
result.addIcon(icon, addRemoveAction && !this.isReadOnly());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
addFeature(featureModel: FeatureModel): Icon {
|
||||
addFeature(featureModel: FeatureModel): ImageIcon {
|
||||
const iconGroup = this.getOrBuildIconGroup();
|
||||
this.closeEditors();
|
||||
|
||||
@ -336,11 +341,11 @@ abstract class Topic extends NodeGraph {
|
||||
const model = this.getModel();
|
||||
model.addFeature(featureModel);
|
||||
|
||||
const result: Icon = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly());
|
||||
iconGroup.addIcon(
|
||||
result,
|
||||
featureModel.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly(),
|
||||
);
|
||||
const result: ImageIcon = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly());
|
||||
const isIcon =
|
||||
featureModel.getType() === TopicFeatureFactory.SvgIcon.id ||
|
||||
featureModel.getType() === TopicFeatureFactory.EmojiIcon.id;
|
||||
iconGroup.addIcon(result, isIcon && !this.isReadOnly());
|
||||
|
||||
this.adjustShapes();
|
||||
return result;
|
||||
@ -533,7 +538,7 @@ abstract class Topic extends NodeGraph {
|
||||
return result;
|
||||
}
|
||||
|
||||
setBackgroundColor(color: string) {
|
||||
setBackgroundColor(color: string): void {
|
||||
this._setBackgroundColor(color, true);
|
||||
}
|
||||
|
||||
@ -552,7 +557,6 @@ abstract class Topic extends NodeGraph {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
getBackgroundColor(): string {
|
||||
const model = this.getModel();
|
||||
let result = model.getBackgroundColor();
|
||||
|
@ -17,15 +17,21 @@
|
||||
*/
|
||||
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import EmojiCharIcon from './EmojiCharIcon';
|
||||
import SvgImageIcon from './SvgImageIcon';
|
||||
import LinkIcon from './LinkIcon';
|
||||
import NoteIcon from './NoteIcon';
|
||||
|
||||
const TopicFeatureFactory = {
|
||||
/** the icon object */
|
||||
Icon: {
|
||||
SvgIcon: {
|
||||
id: 'icon',
|
||||
icon: ImageIcon,
|
||||
icon: SvgImageIcon,
|
||||
},
|
||||
|
||||
EmojiIcon: {
|
||||
id: 'eicon',
|
||||
icon: EmojiCharIcon,
|
||||
},
|
||||
|
||||
/** the link object */
|
||||
@ -52,7 +58,8 @@ const TopicFeatureFactory = {
|
||||
};
|
||||
|
||||
TopicFeatureFactory._featuresMetadataById = [
|
||||
TopicFeatureFactory.Icon,
|
||||
TopicFeatureFactory.SvgIcon,
|
||||
TopicFeatureFactory.EmojiIcon,
|
||||
TopicFeatureFactory.Link,
|
||||
TopicFeatureFactory.Note,
|
||||
];
|
||||
|
@ -72,15 +72,15 @@ class WidgetManager {
|
||||
}
|
||||
|
||||
createTooltipForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
|
||||
this.createTooltip(linkIcon.getImage().peer, $msg('LINK'), linkModel, undefined);
|
||||
this.createTooltip(linkIcon.getElement().peer, $msg('LINK'), linkModel, undefined);
|
||||
}
|
||||
|
||||
createTooltipForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
|
||||
this.createTooltip(noteIcon.getImage().peer, $msg('NOTE'), undefined, noteModel);
|
||||
this.createTooltip(noteIcon.getElement().peer, $msg('NOTE'), undefined, noteModel);
|
||||
}
|
||||
|
||||
configureEditorForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
|
||||
const htmlImage = linkIcon.getImage().peer;
|
||||
const htmlImage = linkIcon.getElement().peer;
|
||||
htmlImage.addEvent('click', (evt) => {
|
||||
this.showEditorForLink(topic, linkModel, linkIcon);
|
||||
evt.stopPropagation();
|
||||
@ -88,7 +88,7 @@ class WidgetManager {
|
||||
}
|
||||
|
||||
configureEditorForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
|
||||
const htmlImage = noteIcon.getImage().peer;
|
||||
const htmlImage = noteIcon.getElement().peer;
|
||||
htmlImage.addEvent('click', (evt) => {
|
||||
this.showEditorForNote(topic, noteModel, noteIcon);
|
||||
evt.stopPropagation();
|
||||
|
@ -2,7 +2,7 @@ import xmlFormatter from 'xml-formatter';
|
||||
import { Mindmap } from '../..';
|
||||
import INodeModel, { TopicShape } from '../model/INodeModel';
|
||||
import RelationshipModel from '../model/RelationshipModel';
|
||||
import IconModel from '../model/IconModel';
|
||||
import SvgIconModel from '../model/SvgIconModel';
|
||||
import FeatureModel from '../model/FeatureModel';
|
||||
import LinkModel from '../model/LinkModel';
|
||||
import NoteModel from '../model/NoteModel';
|
||||
@ -228,7 +228,7 @@ class FreemindExporter extends Exporter {
|
||||
}
|
||||
|
||||
if (type === 'icon') {
|
||||
const icon = feature as IconModel;
|
||||
const icon = feature as SvgIconModel;
|
||||
const freemindIcon: Icon = new Icon();
|
||||
freemindIcon.setBuiltin(icon.getIconType());
|
||||
freemindNode.setArrowlinkOrCloudOrEdge(freemindIcon);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import IconModel from '../model/IconModel';
|
||||
import SvgIconModel from '../model/SvgIconModel';
|
||||
|
||||
export default class FreemindIconConverter {
|
||||
private static freeIdToIcon: Map<string, IconModel> = new Map<string, IconModel>();
|
||||
private static freeIdToIcon: Map<string, SvgIconModel> = new Map<string, SvgIconModel>();
|
||||
|
||||
public static toWiseId(iconId: string): number | null {
|
||||
const result: IconModel = this.freeIdToIcon.get(iconId);
|
||||
const result: SvgIconModel = this.freeIdToIcon.get(iconId);
|
||||
return result ? result.getId() : null;
|
||||
}
|
||||
}
|
||||
|
36
packages/mindplot/src/components/model/EmojiIconModel.ts
Normal file
36
packages/mindplot/src/components/model/EmojiIconModel.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import FeatureModel from './FeatureModel';
|
||||
|
||||
class EmojiIconModel extends FeatureModel {
|
||||
constructor(attributes) {
|
||||
super('eicon');
|
||||
this.setIconType(attributes.id);
|
||||
}
|
||||
|
||||
getIconType(): string {
|
||||
return this.getAttribute('id') as string;
|
||||
}
|
||||
|
||||
setIconType(iconType: string): void {
|
||||
$assert(iconType, 'iconType id can not be null');
|
||||
this.setAttribute('id', iconType);
|
||||
}
|
||||
}
|
||||
export default EmojiIconModel;
|
@ -1,9 +1,10 @@
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import IconModel from './IconModel';
|
||||
import SvgIconModel from './SvgIconModel';
|
||||
import LinkModel from './LinkModel';
|
||||
import NoteModel from './NoteModel';
|
||||
import FeatureModel from './FeatureModel';
|
||||
import FeatureType from './FeatureType';
|
||||
import EmojiIconModel from './EmojiIconModel';
|
||||
|
||||
interface NodeById {
|
||||
id: FeatureType;
|
||||
@ -14,7 +15,11 @@ class FeatureModelFactory {
|
||||
static modelById: Array<NodeById> = [
|
||||
{
|
||||
id: 'icon',
|
||||
model: IconModel,
|
||||
model: SvgIconModel,
|
||||
},
|
||||
{
|
||||
id: 'eicon',
|
||||
model: EmojiIconModel,
|
||||
},
|
||||
{
|
||||
id: 'link',
|
||||
|
@ -15,6 +15,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
type FeatureType = 'note' | 'link' | 'icon';
|
||||
type FeatureType = 'note' | 'link' | 'icon' | 'eicon';
|
||||
|
||||
export default FeatureType;
|
||||
|
293
packages/mindplot/src/components/model/SvgIconFamily.json
Normal file
293
packages/mindplot/src/components/model/SvgIconFamily.json
Normal file
@ -0,0 +1,293 @@
|
||||
[
|
||||
{
|
||||
"id": "face",
|
||||
"icons": [
|
||||
"face_plain",
|
||||
"face_sad",
|
||||
"face_crying",
|
||||
"face_smile",
|
||||
"face_surprise",
|
||||
"face_wink"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "funy",
|
||||
"icons": [
|
||||
"funy_angel",
|
||||
"funy_devilish",
|
||||
"funy_glasses",
|
||||
"funy_grin",
|
||||
"funy_kiss",
|
||||
"funy_monkey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sport",
|
||||
"icons": [
|
||||
"sport_basketball",
|
||||
"sport_football",
|
||||
"sport_golf",
|
||||
"sport_raquet",
|
||||
"sport_shuttlecock",
|
||||
"sport_soccer",
|
||||
"sport_tennis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bulb",
|
||||
"icons": [
|
||||
"",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "thumb",
|
||||
"icons": [
|
||||
"thumb_thumb_up",
|
||||
"thumb_thumb_down"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tick",
|
||||
"icons": [
|
||||
"tick_tick",
|
||||
"tick_cross"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "onoff",
|
||||
"icons": [
|
||||
"onoff_clock",
|
||||
"onoff_clock_red",
|
||||
"onoff_add",
|
||||
"onoff_delete",
|
||||
"onoff_status_offline",
|
||||
"onoff_status_online"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "money",
|
||||
"icons": [
|
||||
"money_money",
|
||||
"money_dollar",
|
||||
"money_euro",
|
||||
"money_pound",
|
||||
"money_yen",
|
||||
"money_coins",
|
||||
"money_ruby"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "time",
|
||||
"icons": [
|
||||
"time_calendar",
|
||||
"time_clock",
|
||||
"time_hourglass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "number",
|
||||
"icons": [
|
||||
"number_1",
|
||||
"number_2",
|
||||
"number_3",
|
||||
"number_4",
|
||||
"number_5",
|
||||
"number_6",
|
||||
"number_7",
|
||||
"number_8",
|
||||
"number_9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "chart",
|
||||
"icons": [
|
||||
"chart_bar",
|
||||
"chart_line",
|
||||
"chart_curve",
|
||||
"chart_pie",
|
||||
"chart_organisation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sign",
|
||||
"icons": [
|
||||
"sign_warning",
|
||||
"sign_info",
|
||||
"sign_stop",
|
||||
"sign_help",
|
||||
"sign_cancel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "hard",
|
||||
"icons": [
|
||||
"hard_cd",
|
||||
"hard_computer",
|
||||
"hard_controller",
|
||||
"hard_driver_disk",
|
||||
"hard_ipod",
|
||||
"hard_keyboard",
|
||||
"hard_mouse",
|
||||
"hard_printer",
|
||||
"hard_webcam",
|
||||
"hard_microphone"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "things",
|
||||
"icons": [
|
||||
"things_address_book",
|
||||
"things_wrench",
|
||||
"things_pin",
|
||||
"things_window-layout",
|
||||
"things_bubbles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "soft",
|
||||
"icons": [
|
||||
"soft_bug",
|
||||
"soft_cursor",
|
||||
"soft_database_table",
|
||||
"soft_database",
|
||||
"soft_feed",
|
||||
"soft_folder_explore",
|
||||
"soft_rss",
|
||||
"soft_penguin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arrow",
|
||||
"icons": [
|
||||
"arrow_up",
|
||||
"arrow_down",
|
||||
"arrow_left",
|
||||
"arrow_right"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arrowc",
|
||||
"icons": [
|
||||
"arrowc_rotate_anticlockwise",
|
||||
"arrowc_rotate_clockwise",
|
||||
"arrowc_turn_left",
|
||||
"arrowc_turn_right"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "people",
|
||||
"icons": [
|
||||
"people_group",
|
||||
"people_male1",
|
||||
"people_male2",
|
||||
"people_female1",
|
||||
"people_female2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mail",
|
||||
"icons": [
|
||||
"mail_envelop",
|
||||
"mail_mailbox",
|
||||
"mail_edit",
|
||||
"mail_list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "flag",
|
||||
"icons": [
|
||||
"flag_blue",
|
||||
"flag_green",
|
||||
"flag_orange",
|
||||
"flag_pink",
|
||||
"flag_purple",
|
||||
"flag_yellow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "social",
|
||||
"icons": [
|
||||
"social_facebook",
|
||||
"social_twitter",
|
||||
"social_redit",
|
||||
"social_instagram",
|
||||
"social_google-plus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "meetapps",
|
||||
"icons": [
|
||||
"meetapps_slack",
|
||||
"meetapps_google-meet",
|
||||
"meetapps_whatapp",
|
||||
"meetapps_ms-teams",
|
||||
"meetapps_zoom",
|
||||
"meetapps_facebook-messenger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "appsgoogle",
|
||||
"icons": [
|
||||
"appsgoogle_youtube",
|
||||
"appsgoogle_gmail",
|
||||
"appsgoogle_maps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tag",
|
||||
"icons": [
|
||||
"tag_blue",
|
||||
"tag_green",
|
||||
"tag_orange",
|
||||
"tag_red",
|
||||
"tag_pink",
|
||||
"tag_yellow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "object",
|
||||
"icons": [
|
||||
"object_bell",
|
||||
"object_clanbomber",
|
||||
"object_key",
|
||||
"object_pencil",
|
||||
"object_phone",
|
||||
"object_magnifier",
|
||||
"object_clip",
|
||||
"object_music",
|
||||
"object_star",
|
||||
"object_wizard",
|
||||
"object_house",
|
||||
"object_cake",
|
||||
"object_camera",
|
||||
"object_palette",
|
||||
"object_rainbow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "weather",
|
||||
"icons": [
|
||||
"weather_clear-night",
|
||||
"weather_clear",
|
||||
"weather_few-clouds-night",
|
||||
"weather_few-clouds",
|
||||
"weather_overcast",
|
||||
"weather_severe-alert",
|
||||
"weather_showers-scattered",
|
||||
"weather_showers",
|
||||
"weather_snow",
|
||||
"weather_storm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "task",
|
||||
"icons": [
|
||||
"task_0",
|
||||
"task_25",
|
||||
"task_50",
|
||||
"task_75",
|
||||
"task_100"
|
||||
]
|
||||
}
|
||||
]
|
@ -18,7 +18,7 @@
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import FeatureModel from './FeatureModel';
|
||||
|
||||
class IconModel extends FeatureModel {
|
||||
class SvgIconModel extends FeatureModel {
|
||||
constructor(attributes) {
|
||||
super('icon');
|
||||
this.setIconType(attributes.id);
|
||||
@ -33,4 +33,5 @@ class IconModel extends FeatureModel {
|
||||
this.setAttribute('id', iconType);
|
||||
}
|
||||
}
|
||||
export default IconModel;
|
||||
|
||||
export default SvgIconModel;
|
@ -172,7 +172,7 @@ class XMLSerializerTango implements XMLMindmapSerializer {
|
||||
const cdata = document.createCDATASection(this._rmXmlInv(value));
|
||||
featureDom.appendChild(cdata);
|
||||
} else {
|
||||
featureDom.setAttribute(key, this._rmXmlInv(value));
|
||||
featureDom.setAttribute(key, value);
|
||||
}
|
||||
}
|
||||
parentTopic.appendChild(featureDom);
|
||||
|
@ -31,8 +31,10 @@ import Exporter from './components/export/Exporter';
|
||||
import Importer from './components/import/Importer';
|
||||
import DesignerKeyboard from './components/DesignerKeyboard';
|
||||
import EditorRenderMode from './components/EditorRenderMode';
|
||||
import DesignerModel from './components/DesignerModel';
|
||||
|
||||
import SvgImageIcon from './components/SvgImageIcon';
|
||||
|
||||
import ImageIcon from './components/ImageIcon';
|
||||
import MindplotWebComponent, {
|
||||
MindplotWebComponentInterface,
|
||||
} from './components/MindplotWebComponent';
|
||||
@ -48,8 +50,13 @@ import WidgetManager from './components/WidgetManager';
|
||||
import { buildDesigner } from './components/DesignerBuilder';
|
||||
|
||||
import { $notify } from './components/widget/ToolbarNotifier';
|
||||
import XMLSerializerFactory from './components/persistence/XMLSerializerFactory';
|
||||
|
||||
import { $msg } from './components/Messages';
|
||||
declare global {
|
||||
// Todo: There are some global references that needs to be removed inside mindplot.
|
||||
// eslint-disable-next-line vars-on-top, no-var
|
||||
var designer: Designer;
|
||||
}
|
||||
|
||||
const globalAny: any = global;
|
||||
globalAny.jQuery = jquery;
|
||||
@ -62,6 +69,7 @@ if (!customElements.get('mindplot-component')) {
|
||||
export {
|
||||
Mindmap,
|
||||
Designer,
|
||||
DesignerModel,
|
||||
DesignerBuilder,
|
||||
PersistenceManager,
|
||||
RESTPersistenceManager,
|
||||
@ -74,10 +82,9 @@ export {
|
||||
ImageExporterFactory,
|
||||
TextImporterFactory,
|
||||
Exporter,
|
||||
SvgImageIcon,
|
||||
Importer,
|
||||
ImageIcon,
|
||||
$notify,
|
||||
$msg,
|
||||
DesignerKeyboard,
|
||||
MindplotWebComponent,
|
||||
MindplotWebComponentInterface,
|
||||
@ -87,4 +94,5 @@ export {
|
||||
NoteModel,
|
||||
WidgetManager,
|
||||
Topic,
|
||||
XMLSerializerFactory,
|
||||
};
|
||||
|
@ -29,7 +29,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.13",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/preset-env": "^7.19.4",
|
||||
"babel-loader": "^8.2.2",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"core-js": "^3.15.2",
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-alert */
|
||||
import $ from 'jquery';
|
||||
import { Toolkit, Workspace, Line, Group, Elipse } from '../../src';
|
||||
import { Workspace, Line, Group, Elipse } from '../../src';
|
||||
|
||||
global.$ = $;
|
||||
|
||||
|
@ -23,11 +23,14 @@
|
||||
],
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint"
|
||||
"@typescript-eslint",
|
||||
"react-hooks"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "error",
|
||||
"@typescript-eslint/no-unused-vars": "error"
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"react-hooks/rules-of-hooks": "warn" // Checks rules of Hooks
|
||||
// "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@
|
||||
"accountinfo.firstname": {
|
||||
"defaultMessage": "Primer nombre"
|
||||
},
|
||||
"registration.page-title": {
|
||||
"defaultMessage": "Registrarse | WiseMapping"
|
||||
},
|
||||
"accountinfo.lastname": {
|
||||
"defaultMessage": "Apellido"
|
||||
},
|
||||
|
@ -14,6 +14,9 @@
|
||||
"accountinfo.lastname": {
|
||||
"defaultMessage": "Nom de famille"
|
||||
},
|
||||
"registration.page-title": {
|
||||
"defaultMessage": "Inscription | WiseMapping"
|
||||
},
|
||||
"accountinfo.title": {
|
||||
"defaultMessage": "Informations de compte"
|
||||
},
|
||||
|
@ -25,57 +25,40 @@
|
||||
"@formatjs/cli": "^2.13.15",
|
||||
"@testing-library/cypress": "^7.0.3",
|
||||
"@types/testing-library__cypress": "^5.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
||||
"@typescript-eslint/parser": "^4.8.1",
|
||||
"brotli-webpack-plugin": "^1.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"compression-webpack-plugin": "^7.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||
"@typescript-eslint/parser": "^5.41.0",
|
||||
"clean-webpack-plugin": "^3.0.05.10.11",
|
||||
"copy-webpack-plugin": "^7.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^5.0.1",
|
||||
"cypress": "^8.4.1",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"eslint": "^7.14.0",
|
||||
"eslint-config-prettier": "^8.0.0",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-dynamic-env-plugin": "^0.0.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"html-webpack-plugin": "^5.1.0",
|
||||
"prettier": "^2.2.1",
|
||||
"sass-loader": "^10.1.0",
|
||||
"start-server-and-test": "^1.12.0",
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-loader": "^8.0.11",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.1.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-cli": "^4.2.0",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.4",
|
||||
"@mui/icons-material": "^5.9.3",
|
||||
"@mui/lab": "^5.0.0-alpha.98",
|
||||
"@mui/material": "^5.9.3",
|
||||
"@mui/material": "^5.10.11",
|
||||
"@mui/styles": "^5.9.3",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@wisemapping/editor": "^0.4.0",
|
||||
"axios": "^0.27.2",
|
||||
"dayjs": "^1.10.7",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-ga4": "^1.4.1",
|
||||
"react-google-recaptcha": "^2.1.0",
|
||||
"react-intl": "^4.7.6",
|
||||
"react-intl": "^5.25.1",
|
||||
"react-query": "^3.39.1",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router": "^5.1.8",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"react-router-dom": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { fetchAccount } from './../../redux/clientSlice';
|
||||
import { useFetchAccount } from './../../redux/clientSlice';
|
||||
import 'dayjs/locale/fr';
|
||||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/es';
|
||||
@ -26,7 +26,7 @@ export default abstract class AppI18n {
|
||||
const isTryPage = window.location.pathname.endsWith('/try');
|
||||
let result: Locale;
|
||||
if (!isTryPage) {
|
||||
const account = fetchAccount();
|
||||
const account = useFetchAccount();
|
||||
result = account?.locale ? account.locale : this.getDefaultLocale();
|
||||
|
||||
// If the local storage value is different, update ...
|
||||
|
@ -873,6 +873,12 @@
|
||||
"value": "Apellido"
|
||||
}
|
||||
],
|
||||
"registration.page-title": [
|
||||
{
|
||||
"type": 0,
|
||||
"value": "Registrarse | WiseMapping"
|
||||
}
|
||||
],
|
||||
"registration.password": [
|
||||
{
|
||||
"type": 0,
|
||||
|
@ -919,6 +919,12 @@
|
||||
"value": "Nom de famille"
|
||||
}
|
||||
],
|
||||
"registration.page-title": [
|
||||
{
|
||||
"type": 0,
|
||||
"value": "Inscription | WiseMapping"
|
||||
}
|
||||
],
|
||||
"registration.password": [
|
||||
{
|
||||
"type": 0,
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
LocalStorageManager,
|
||||
Mindmap,
|
||||
MockPersistenceManager,
|
||||
XMLSerializerTango,
|
||||
XMLSerializerFactory,
|
||||
} from '@wisemapping/editor';
|
||||
|
||||
export const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
|
||||
@ -54,7 +54,7 @@ export const getMindmapFromPersistence = (mapId: string): Mindmap => {
|
||||
'text/xml',
|
||||
);
|
||||
|
||||
const serializer = new XMLSerializerTango();
|
||||
const serializer = XMLSerializerFactory.getSerializer('tango');
|
||||
mindmap = serializer.loadFromDom(xmlDoc, String(mapId));
|
||||
}
|
||||
return mindmap;
|
||||
|
@ -25,7 +25,7 @@ import AppI18n, { Locales } from '../../classes/app-i18n';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { hotkeysEnabled } from '../../redux/editorSlice';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { fetchAccount, fetchMapById } from '../../redux/clientSlice';
|
||||
import { useFetchAccount, useFetchMapById } from '../../redux/clientSlice';
|
||||
import EditorOptionsBuilder from './EditorOptionsBuilder';
|
||||
import { buildPersistenceManagerForEditor } from './PersistenceManagerUtils';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
@ -50,14 +50,14 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
||||
}, []);
|
||||
|
||||
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
||||
const useFindEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
||||
let result: EditorRenderMode = null;
|
||||
if (isTryMode) {
|
||||
result = 'showcase';
|
||||
} else if (global.mindmapLocked) {
|
||||
result = 'viewonly';
|
||||
} else {
|
||||
const fetchResult = fetchMapById(mapId);
|
||||
const fetchResult = useFetchMapById(mapId);
|
||||
if (!fetchResult.isLoading) {
|
||||
if (fetchResult.error) {
|
||||
throw new Error(`Map info could not be loaded: ${JSON.stringify(fetchResult.error)}`);
|
||||
@ -76,11 +76,11 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
||||
|
||||
// What is the role ?
|
||||
const mapId = EditorOptionsBuilder.loadMapId();
|
||||
const mode = findEditorMode(isTryMode, mapId);
|
||||
const mode = useFindEditorMode(isTryMode, mapId);
|
||||
|
||||
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
||||
// at the i18n resource loading.
|
||||
const isAccountLoaded = mode === 'showcase' || fetchAccount;
|
||||
const isAccountLoaded = mode === 'showcase' || useFetchAccount;
|
||||
const loadCompleted = mode && isAccountLoaded;
|
||||
|
||||
let options, persistence: PersistenceManager;
|
||||
@ -99,10 +99,10 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (options?.mapTitle) {
|
||||
document.title = `${options.mapTitle} | WiseMapping `;
|
||||
if (mapInfo) {
|
||||
document.title = `${mapInfo.getTitle()} | WiseMapping `;
|
||||
}
|
||||
}, [loadCompleted]);
|
||||
}, [mapInfo]);
|
||||
|
||||
return loadCompleted ? (
|
||||
<IntlProvider
|
||||
|
@ -13,32 +13,6 @@ import Link from '@mui/material/Link';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
||||
|
||||
type ConfigStatusProps = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
||||
let result;
|
||||
if (enabled === true) {
|
||||
result = (
|
||||
<div className="db-warn-msg">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="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"
|
||||
/>
|
||||
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
||||
{' '}
|
||||
here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return result || null;
|
||||
};
|
||||
|
||||
const LoginError = () => {
|
||||
// @Todo: This must be reviewed to be based on navigation state.
|
||||
// Login error example: http://localhost:8080/c/login?login.error=2
|
||||
@ -132,7 +106,6 @@ const LoginPage = (): React.ReactElement => {
|
||||
<Link component={RouterLink} to="/c/forgot-password">
|
||||
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
||||
</Link>
|
||||
<ConfigStatusMessage />
|
||||
</FormContainer>
|
||||
|
||||
<Footer />
|
||||
|
@ -5,7 +5,7 @@ import Client, { ErrorInfo } from '../../../../classes/client';
|
||||
import Input from '../../../form/input';
|
||||
import BaseDialog from '../../action-dispatcher/base-dialog';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { activeInstance, fetchAccount } from '../../../../redux/clientSlice';
|
||||
import { activeInstance, useFetchAccount } from '../../../../redux/clientSlice';
|
||||
|
||||
import Alert from '@mui/material/Alert';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
@ -63,7 +63,7 @@ const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps): React.ReactElem
|
||||
},
|
||||
);
|
||||
|
||||
const account = fetchAccount();
|
||||
const account = useFetchAccount();
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
setModel({
|
||||
|
@ -7,7 +7,7 @@ import SettingsApplicationsOutlined from '@mui/icons-material/SettingsApplicatio
|
||||
import AccountCircle from '@mui/icons-material/AccountCircle';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchAccount } from '../../../redux/clientSlice';
|
||||
import { useFetchAccount } from '../../../redux/clientSlice';
|
||||
import AccountInfoDialog from './account-info-dialog';
|
||||
import ChangePasswordDialog from './change-password-dialog';
|
||||
import LockOpenOutlined from '@mui/icons-material/LockOpenOutlined';
|
||||
@ -34,7 +34,7 @@ const AccountMenu = (): React.ReactElement => {
|
||||
elem.submit();
|
||||
};
|
||||
|
||||
const account = fetchAccount();
|
||||
const account = useFetchAccount();
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
|
@ -12,7 +12,7 @@ import LabelOutlined from '@mui/icons-material/LabelOutlined';
|
||||
import HistoryOutlined from '@mui/icons-material/HistoryOutlined';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchMapById } from '../../../redux/clientSlice';
|
||||
import { useFetchMapById } from '../../../redux/clientSlice';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
@ -52,7 +52,7 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
||||
};
|
||||
};
|
||||
|
||||
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
||||
const role = mapId ? useFetchMapById(mapId)?.map?.role : undefined;
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchor}
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Client, { ErrorInfo } from '../../../../classes/client';
|
||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { activeInstance, useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import { SimpleDialogProps, handleOnMutationSuccess } from '..';
|
||||
import BaseDialog from '../base-dialog';
|
||||
import Alert from '@mui/material/Alert';
|
||||
@ -30,7 +30,7 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
||||
mutation.mutate(mapId);
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
const alertTitle = `${intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
|
@ -5,7 +5,7 @@ import FormControl from '@mui/material/FormControl';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client';
|
||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { activeInstance, useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import Input from '../../../form/input';
|
||||
import { SimpleDialogProps } from '..';
|
||||
import BaseDialog from '../base-dialog';
|
||||
@ -56,7 +56,7 @@ const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElem
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import BaseDialog from '../base-dialog';
|
||||
import { useStyles } from './style';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import { fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import RadioGroup from '@mui/material/RadioGroup';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
@ -39,7 +39,7 @@ const ExportDialog = ({
|
||||
}: ExportDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
|
||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||
enableImgExport ? 'image' : 'document',
|
||||
|
@ -34,11 +34,13 @@ const ActionDispatcher = ({
|
||||
fromEditor,
|
||||
}: ActionDialogProps): React.ReactElement => {
|
||||
useEffect(() => {
|
||||
if (action) {
|
||||
ReactGA.event({
|
||||
category: 'map metadata',
|
||||
action: action,
|
||||
nonInteraction: true,
|
||||
});
|
||||
}
|
||||
}, [action]);
|
||||
|
||||
const handleOnClose = (success?: boolean): void => {
|
||||
|
@ -6,7 +6,7 @@ import BaseDialog from '../base-dialog';
|
||||
import { SimpleDialogProps } from '..';
|
||||
import { useStyles } from './style';
|
||||
import dayjs from 'dayjs';
|
||||
import { fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Card from '@mui/material/Card';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
@ -18,7 +18,7 @@ import LocalizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
dayjs.extend(LocalizedFormat);
|
||||
|
||||
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const intl = useIntl();
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Client, { ErrorInfo } from '../../../../classes/client';
|
||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { activeInstance, useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import BaseDialog from '../base-dialog';
|
||||
import { handleOnMutationSuccess, SimpleDialogProps } from '..';
|
||||
import { useStyles } from './style';
|
||||
@ -17,11 +17,11 @@ import Tab from '@mui/material/Tab';
|
||||
import TabPanel from '@mui/lab/TabPanel';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import TextareaAutosize from '@mui/material/TextareaAutosize';
|
||||
import Box from '@mui/system/Box';
|
||||
import AppConfig from '../../../../classes/app-config';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
||||
@ -38,6 +38,7 @@ const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElemen
|
||||
onSuccess: () => {
|
||||
setModel(model);
|
||||
handleOnMutationSuccess(onClose, queryClient);
|
||||
queryClient.invalidateQueries(`maps-${mapId}`);
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
|
@ -3,7 +3,7 @@ import { useIntl } from 'react-intl';
|
||||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client';
|
||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||
import { activeInstance, useFetchMapById } from '../../../../redux/clientSlice';
|
||||
import { SimpleDialogProps, handleOnMutationSuccess } from '..';
|
||||
import Input from '../../../form/input';
|
||||
import BaseDialog from '../base-dialog';
|
||||
@ -58,7 +58,7 @@ const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = useFetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { alpha, Theme } from '@mui/material/styles';
|
||||
import { alpha, useTheme } from '@mui/material/styles';
|
||||
|
||||
import createStyles from '@mui/styles/createStyles';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
export const useStyles = makeStyles((theme: Theme) =>
|
||||
export const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
root: {
|
||||
width: '100%',
|
||||
},
|
||||
paper: {
|
||||
width: '100%',
|
||||
marginBottom: theme.spacing(2),
|
||||
marginBottom: useTheme().spacing(2),
|
||||
},
|
||||
table: {
|
||||
minWidth: 750,
|
||||
@ -66,14 +66,14 @@ export const useStyles = makeStyles((theme: Theme) =>
|
||||
},
|
||||
search: {
|
||||
borderRadius: 9,
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.15),
|
||||
backgroundColor: alpha(useTheme().palette.common.white, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.25),
|
||||
backgroundColor: alpha(useTheme().palette.common.white, 0.25),
|
||||
},
|
||||
margin: '10px 0px',
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(1),
|
||||
[useTheme().breakpoints.up('sm')]: {
|
||||
marginLeft: useTheme().spacing(1),
|
||||
width: 'auto',
|
||||
},
|
||||
float: 'right',
|
||||
@ -97,10 +97,10 @@ export const useStyles = makeStyles((theme: Theme) =>
|
||||
// vertical padding + font size from searchIcon
|
||||
border: '1px solid #ffa800',
|
||||
borderRadius: 4,
|
||||
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
|
||||
transition: theme.transitions.create('width'),
|
||||
paddingLeft: `calc(1em + ${useTheme().spacing(4)})`,
|
||||
transition: useTheme().transitions.create('width'),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
[useTheme().breakpoints.up('sm')]: {
|
||||
width: '12ch',
|
||||
'&:focus': {
|
||||
width: '20ch',
|
||||
|
@ -1,30 +1,19 @@
|
||||
import { Theme } from '@mui/material/styles';
|
||||
|
||||
import createStyles from '@mui/styles/createStyles';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
const drawerWidth = 300;
|
||||
|
||||
export const useStyles = makeStyles((theme: Theme) =>
|
||||
export const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: 'flex',
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
background: '#ffffff',
|
||||
},
|
||||
appBarShift: {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
newMapButton: {
|
||||
marginRight: 10,
|
||||
@ -48,10 +37,6 @@ export const useStyles = makeStyles((theme: Theme) =>
|
||||
drawerOpen: {
|
||||
background: '#ffa800',
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
toolbar: {
|
||||
display: 'flex',
|
||||
|
@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './app';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
async function bootstrapApplication() {
|
||||
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
|
||||
const container = document.getElementById('root') as HTMLElement
|
||||
const root = createRoot(container!);
|
||||
root.render(<App />);
|
||||
}
|
||||
|
||||
bootstrapApplication();
|
||||
|
@ -58,7 +58,7 @@ type MapLoadResult = {
|
||||
map: MapInfo | null;
|
||||
};
|
||||
|
||||
export const fetchMapById = (id: number): MapLoadResult => {
|
||||
export const useFetchMapById = (id: number): MapLoadResult => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>(`maps-${id}`, () => {
|
||||
return client.fetchAllMaps();
|
||||
@ -85,7 +85,7 @@ export const fetchMapById = (id: number): MapLoadResult => {
|
||||
return { isLoading: isLoading, error: errorMsg, map: map };
|
||||
};
|
||||
|
||||
export const fetchAccount = (): AccountInfo | undefined => {
|
||||
export const useFetchAccount = (): AccountInfo | undefined => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
||||
return client.fetchAccountInfo();
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
|
||||
import rootReducer from './rootReducer';
|
||||
|
||||
// Create Service object...
|
||||
const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware: [
|
||||
...getDefaultMiddleware({
|
||||
serializableCheck: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default store;
|
||||
|
@ -1,15 +1,15 @@
|
||||
export const getCsrfToken = (): string | null => {
|
||||
const meta = document.head.querySelector('meta[name="_csrf"]');
|
||||
if (!meta) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
return meta.getAttribute('content');
|
||||
};
|
||||
|
||||
export const getCsrfTokenParameter = (): string | null => {
|
||||
export const getCsrfTokenParameter = (): string => {
|
||||
const meta = document.head.querySelector('meta[name="_csrf_parameter"]');
|
||||
if (!meta) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
return meta.getAttribute('content');
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user