Merged develop into fix/import-freemind

This commit is contained in:
Paulo Veiga 2022-04-06 12:07:00 +00:00
commit 31dc83d30b
12 changed files with 135 additions and 317 deletions

View File

@ -35,7 +35,7 @@ const mapId = 'welcome';
const options: EditorOptions = {
zoom: 0.8,
locked: false,
mapTitle: "Develop Mindnap",
mapTitle: "Develop WiseMapping",
mode: 'edition-owner',
locale: 'en',
enableKeyboardEvents: true

View File

@ -36,7 +36,7 @@ const persistence = new LocalStorageManager('samples/{id}.wxml', false);
const options: EditorOptions = {
zoom: 0.8,
locked: false,
mapTitle: "Develop Mindnap",
mapTitle: "Develop WiseMapping",
mode: 'viewonly',
locale: 'en',
enableKeyboardEvents: true

View File

@ -21,12 +21,19 @@ import Keyboard from './Keyboard';
import { Designer } from '..';
import Topic from './Topic';
import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
// Provides dispatcher of keyevents by key...
initHotKeyPluggin($);
export type EventCallback = (event?: Event) => void;
class DesignerKeyboard extends Keyboard {
// eslint-disable-next-line no-use-before-define
static _instance: DesignerKeyboard;
private static _instance: DesignerKeyboard;
static _disabled: boolean;
private static _disabled: boolean;
private static excludeFromEditor = ['Enter', 'CapsLock', 'Escape', 'F1', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'];
constructor(designer: Designer) {
super();
@ -35,173 +42,61 @@ class DesignerKeyboard extends Keyboard {
}
addShortcut(shortcuts: string[] | string, callback: EventCallback): void {
super.addShortcut(shortcuts, (e: Event) => {
super.addShortcut(shortcuts, () => {
if (DesignerKeyboard.isDisabled()) {
return;
}
callback(e);
callback();
});
}
private _registerEvents(designer: Designer) {
// Try with the keyboard ..
const model = designer.getModel();
this.addShortcut(
['backspace'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.deleteSelectedEntities();
},
);
this.addShortcut(
['space'], (event: Event) => {
designer.shrinkSelectedBranch();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['f2'], (event: Event) => {
event.stopPropagation();
event.preventDefault();
const node = model.selectedTopic();
if (node) {
node.showTextEditor(node.getText());
}
},
);
this.addShortcut(
['del'], (event: Event) => {
designer.deleteSelectedEntities();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['enter'], () => {
designer.createSiblingForSelectedNode();
},
);
this.addShortcut(
['insert'], (event: Event) => {
designer.createChildForSelectedNode();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['tab'], (eventevent: Event) => {
designer.createChildForSelectedNode();
eventevent.preventDefault();
eventevent.stopPropagation();
},
);
this.addShortcut(
['meta+enter'], (eventevent: Event) => {
eventevent.preventDefault();
eventevent.stopPropagation();
designer.createChildForSelectedNode();
},
);
this.addShortcut(
['ctrl+z', 'meta+z'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.undo();
},
);
this.addShortcut(
['ctrl+c', 'meta+c'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.copyToClipboard();
},
);
this.addShortcut(
['ctrl+l', 'meta+l'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.addLink();
},
);
this.addShortcut(
['ctrl+k', 'meta+k'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.addNote();
},
);
this.addShortcut(
['ctrl+v', 'meta+v'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.pasteClipboard();
},
);
this.addShortcut(
['ctrl+shift+z', 'meta+shift+z', 'ctrl+y', 'meta+y'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.redo();
},
);
this.addShortcut(
['ctrl+a', 'meta+a'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
designer.selectAll();
},
);
this.addShortcut(
['ctrl+b', 'meta+b'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
this.addShortcut(['backspace', 'del'], () => { designer.deleteSelectedEntities(); });
designer.changeFontWeight();
},
);
this.addShortcut(
['ctrl+s', 'meta+s'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
$(document).find('#save').trigger('click');
},
);
this.addShortcut(
['ctrl+i', 'meta+i'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
this.addShortcut('space', () => { designer.shrinkSelectedBranch(); });
designer.changeFontStyle();
},
);
this.addShortcut(
['ctrl+shift+a', 'meta+shift+a'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
this.addShortcut('f2', () => {
const node = model.selectedTopic();
if (node) {
node.showTextEditor(node.getText());
}
});
designer.deselectAll();
},
);
this.addShortcut(
['meta+=', 'ctrl+='], (event: Event) => {
event.preventDefault();
event.stopPropagation();
this.addShortcut(['insert', 'tab', 'meta+enter'], () => { designer.createChildForSelectedNode(); });
designer.zoomIn();
},
);
this.addShortcut(
['meta+-', 'ctrl+-'], (event: Event) => {
event.preventDefault();
event.stopPropagation();
this.addShortcut('enter', () => { designer.createSiblingForSelectedNode(); });
this.addShortcut(['ctrl+z', 'meta+z'], () => { designer.undo(); });
this.addShortcut(['ctrl+shift+z', 'meta+shift+z'], () => { designer.redo(); });
this.addShortcut(['ctrl+c', 'meta+c'], () => { designer.copyToClipboard(); });
this.addShortcut(['ctrl+l', 'meta+l'], () => { designer.addLink(); });
this.addShortcut(['ctrl+k', 'meta+k'], () => { designer.addNote(); });
this.addShortcut(['ctrl+v', 'meta+v'], () => { designer.pasteClipboard(); });
this.addShortcut(['ctrl+a', 'meta+a'], () => { designer.selectAll(); });
this.addShortcut(['ctrl+b', 'meta+b'], () => { designer.changeFontWeight(); });
this.addShortcut(['ctrl+s', 'meta+s'], () => { $(document).find('#save').trigger('click'); });
this.addShortcut(['ctrl+i', 'meta+i'], () => { designer.changeFontStyle(); });
this.addShortcut(['ctrl+shift+a', 'meta+shift+a'], () => { designer.deselectAll(); });
this.addShortcut(['meta+=', 'ctrl+='], () => { designer.zoomIn(); });
this.addShortcut(['meta+-', 'ctrl+-'], () => { designer.zoomOut(); });
designer.zoomOut();
},
);
const me = this;
this.addShortcut(
'right', (event: Event) => {
'right', () => {
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
@ -215,12 +110,11 @@ class DesignerKeyboard extends Keyboard {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
'left', (event: Event) => {
'left', () => {
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
@ -234,12 +128,11 @@ class DesignerKeyboard extends Keyboard {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
'up', (event: Event) => {
'up', () => {
const node = model.selectedTopic();
if (node) {
if (!node.isCentralTopic()) {
@ -249,12 +142,10 @@ class DesignerKeyboard extends Keyboard {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
'down', (event: Event) => {
'down', () => {
const node = model.selectedTopic();
if (node) {
if (!node.isCentralTopic()) {
@ -264,42 +155,26 @@ class DesignerKeyboard extends Keyboard {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
const excludes = ['esc', 'escape', 'f1', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'];
$(document).on('keypress', (event) => {
let keyCode: number;
if (DesignerKeyboard.isDisabled()) {
// Needs to be ignored ?
if (DesignerKeyboard.isDisabled() || DesignerKeyboard.excludeFromEditor.includes(event.code)) {
return;
}
// Firefox doesn't skip special keys for keypress event...
if (event.key && excludes.includes(event.key.toLowerCase())) {
// Is a modifier ?
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
// Sometimes Firefox doesn't contain keyCode value
if (event.key && event.keyCode === 0) {
keyCode = event.charCode;
} else {
keyCode = event.keyCode;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const jq: any = $;
const specialKey = jq.hotkeys.specialKeys[keyCode];
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !jq.hotkeys.shiftNums[keyCode]) {
const nodes = designer.getModel().filterSelectedTopics();
if (nodes.length > 0) {
// If a modifier is press, the key selected must be ignored.
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
nodes[0].showTextEditor('');
event.stopPropagation();
}
// If a node is selected, open the editor ...
const topic = designer.getModel().selectedTopic();
if (topic) {
event.stopPropagation();
event.preventDefault();
topic.showTextEditor(event.key);
}
});
}
@ -405,75 +280,17 @@ class DesignerKeyboard extends Keyboard {
this._disabled = false;
};
static pause = function pause() {
static pause() {
this._disabled = true;
};
}
static resume = function resume() {
static resume() {
this._disabled = false;
};
}
static isDisabled = function isDisabled() {
static isDisabled() {
return this._disabled;
};
static specialKeys = {
8: 'backspace',
9: 'tab',
10: 'return',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
19: 'pause',
20: 'capslock',
27: 'esc',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'insert',
46: 'del',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111: '/',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
144: 'numlock',
145: 'scroll',
186: ';',
191: '/',
220: '\\',
222: "'",
224: 'meta',
};
}
static getInstance() {
return this._instance;

View File

@ -59,27 +59,15 @@ class DesignerModel extends Events {
}
filterSelectedTopics(): Topic[] {
const result: Topic[] = [];
for (let i = 0; i < this._topics.length; i++) {
if (this._topics[i].isOnFocus()) {
result.push(this._topics[i]);
}
}
return result;
return this._topics.filter((t) => t.isOnFocus());
}
filterSelectedRelationships(): Relationship[] {
const result:Relationship[] = [];
for (let i = 0; i < this._relationships.length; i++) {
if (this._relationships[i].isOnFocus()) {
result.push(this._relationships[i]);
}
}
return result;
return this._relationships.filter((r) => r.isOnFocus());
}
getEntities(): (Relationship | Topic)[] {
let result:(Relationship|Topic)[] = [];
let result: (Relationship | Topic)[] = [];
result = result.concat(this._topics);
result = result.concat(this._relationships);
return result;
@ -133,15 +121,7 @@ class DesignerModel extends Events {
}
findTopicById(id: number): Topic | undefined {
let result: Topic | undefined;
for (let i = 0; i < this._topics.length; i++) {
const topic = this._topics[i];
if (topic.getId() === id) {
result = topic;
break;
}
}
return result;
return this._topics.find((t) => t.getId() === id);
}
}

View File

@ -202,7 +202,7 @@ ImageIcon.prototype.ICON_FAMILIES = [{
},
{
id: 'meetapps',
icons: ['meetapps_slack', 'meetapps_google-meet', 'meetapps_whatapp', 'meetapps_ms-teams', 'meetapps_zoom', 'meeetapps_facebook-messenger'],
icons: ['meetapps_slack', 'meetapps_google-meet', 'meetapps_whatapp', 'meetapps_ms-teams', 'meetapps_zoom', 'meetapps_facebook-messenger'],
},
{
id: 'appsgoogle',

View File

@ -16,12 +16,21 @@
* limitations under the License.
*/
import $ from 'jquery';
import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
// Provides dispatcher of keyevents by key...
initHotKeyPluggin($);
class Keyboard {
addShortcut(shortcuts: string[] | string, callback) {
addShortcut(shortcuts: string[] | string, callback: () => void) {
const shortcutsArray = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
shortcutsArray.forEach((shortcut) => {
$(document).bind('keydown', shortcut, callback);
$(document).bind('keydown', shortcut,
(e) => {
e.stopPropagation();
e.preventDefault();
callback();
});
});
}
}

View File

@ -18,13 +18,10 @@
import { $defined } from '@wisemapping/core-js';
import $ from 'jquery';
import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
import Events from './Events';
import ActionDispatcher from './ActionDispatcher';
import Topic from './Topic';
initHotKeyPluggin($);
class MultilineTextEditor extends Events {
private _topic: Topic;
@ -61,12 +58,11 @@ class MultilineTextEditor extends Events {
private _registerEvents(containerElem: JQuery) {
const textareaElem = this._getTextareaElem();
textareaElem.on('keydown', (event) => {
const j: any = $;
switch (j.hotkeys.specialKeys[event.keyCode]) {
case 'esc':
switch (event.code) {
case 'Escape':
this.close(false);
break;
case 'enter': {
case 'Enter': {
if (event.metaKey || event.ctrlKey) {
// Add return ...
const text = this._getTextAreaText();

View File

@ -16,7 +16,7 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
const [error, setError] = React.useState<ErrorInfo>();
const mutation = useMutation((id: number) => client.deleteMap(id), {
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
onError: (error: ErrorInfo) => {
setError(error);
},
@ -31,7 +31,7 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
};
const { map } = fetchMapById(mapId)
const alertTitle=`${intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })} ${map?.title}`;
const alertTitle = `${intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })} ${map?.title}`;
return (
<div>
<BaseDialog

View File

@ -18,7 +18,7 @@ const DeleteMultiselectDialog = ({
const queryClient = useQueryClient();
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
onError: (error) => {
console.error(`Unexpected error ${error}`);
},

View File

@ -22,13 +22,13 @@ export type BasicMapInfo = {
type ActionDialogProps = {
action?: ActionType;
mapsId: number[];
onClose: () => void;
onClose: (success?: boolean) => void;
fromEditor: boolean;
};
const ActionDispatcher = ({ mapsId, action, onClose, fromEditor }: ActionDialogProps): React.ReactElement => {
const handleOnClose = (): void => {
onClose();
const handleOnClose = (success?: boolean): void => {
onClose(success);
};
switch (action) {
@ -78,12 +78,12 @@ export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryC
export type SimpleDialogProps = {
mapId: number;
onClose: () => void;
onClose: (success?: boolean) => void;
};
export type MultiDialogProps = {
mapsId: number[];
onClose: () => void;
onClose: (success?: boolean) => void;
};
export default ActionDispatcher;

View File

@ -56,11 +56,18 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
}
);
const splitEmail = (emails: string): string[] => {
return emails.split(/,|;/)
.map(e => e.trim().replace(/\s/g, ''))
.filter(e => e.trim().length > 0);
}
const addMutation = useMutation(
(model: ShareModel) => {
const emails = model.emails.split(',');
const emails = splitEmail(model.emails);
const permissions = emails.map((email: string) => {
return { email: email.replace(/\s/g, ''), role: model.role };
return { email: email, role: model.role };
});
return client.addMapPermissions(mapId, model.message, permissions);
},
@ -114,7 +121,9 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
};
// very basic email validation, just make sure the basic syntax is fine
const isValid = model.emails.split(',').every(str => /\S+@\S+\.\S+/.test((str || '').trim()));
const isValid = splitEmail(model.emails)
.every(str => /\S+@\S+\.\S+/.test((str || '')
.trim()));
return (
<div>

View File

@ -59,9 +59,9 @@ function getComparator<Key extends keyof any>(
order: Order,
orderBy: Key
): (
a: { [key in Key]: number | string | boolean | Label[] | undefined },
b: { [key in Key]: number | string | Label[] | boolean }
) => number {
a: { [key in Key]: number | string | boolean | Label[] | undefined },
b: { [key in Key]: number | string | Label[] | boolean }
) => number {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
@ -240,19 +240,19 @@ export type ChangeLabelMutationFunctionParam = { maps: MapInfo[]; label: Label;
export const getChangeLabelMutationFunction =
(client: Client) =>
async ({ maps, label, checked }: ChangeLabelMutationFunctionParam): Promise<void> => {
if (!label.id) {
label.id = await client.createLabel(label.title, label.color);
}
if (checked) {
const toAdd = maps.filter((m) => !m.labels.find((l) => l.id === label.id));
await Promise.all(toAdd.map((m) => client.addLabelToMap(label.id, m.id)));
} else {
const toRemove = maps.filter((m) => m.labels.find((l) => l.id === label.id));
await Promise.all(toRemove.map((m) => client.deleteLabelFromMap(label.id, m.id)));
}
return Promise.resolve();
};
async ({ maps, label, checked }: ChangeLabelMutationFunctionParam): Promise<void> => {
if (!label.id) {
label.id = await client.createLabel(label.title, label.color);
}
if (checked) {
const toAdd = maps.filter((m) => !m.labels.find((l) => l.id === label.id));
await Promise.all(toAdd.map((m) => client.addLabelToMap(label.id, m.id)));
} else {
const toRemove = maps.filter((m) => m.labels.find((l) => l.id === label.id));
await Promise.all(toRemove.map((m) => client.deleteLabelFromMap(label.id, m.id)));
}
return Promise.resolve();
};
export const MapsList = (props: MapsListProps): React.ReactElement => {
const classes = useStyles();
@ -698,7 +698,14 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
<ActionDispatcher
action={activeDialog?.actionType}
onClose={() => setActiveDialog(undefined)}
onClose={(success) => {
setActiveDialog(undefined);
// If it was a success action, reset the selection list ...
if (success) {
setSelected([]);
}
}}
mapsId={activeDialog ? activeDialog.mapsId : []}
/>
</div>