Migrate TopicFatory to typescript.
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
@ -856,10 +856,8 @@ class Designer extends Events {
|
||||
|
||||
addIconType(type: 'image' | 'emoji', iconType: string): void {
|
||||
const topicsIds = this.getModel().filterTopicsIds();
|
||||
const featureType: FeatureType = type === 'emoji' ? 'eicon' : 'icon';
|
||||
|
||||
const featureType: FeatureType = (
|
||||
type === 'emoji' ? TopicFeatureFactory.EmojiIcon.id : TopicFeatureFactory.SvgIcon.id
|
||||
) as FeatureType;
|
||||
if (topicsIds.length > 0) {
|
||||
this._actionDispatcher.addFeatureToTopic(topicsIds[0], featureType, {
|
||||
id: iconType,
|
||||
|
@ -26,8 +26,6 @@ import Topic from './Topic';
|
||||
import ActionDispatcher from './ActionDispatcher';
|
||||
|
||||
class EmojiCharIcon implements Icon {
|
||||
private char: string;
|
||||
|
||||
private element: ElementClass;
|
||||
|
||||
private group: IconGroup;
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Point, ElementClass } from '@wisemapping/web2d';
|
||||
import { Point, ElementClass, Group } from '@wisemapping/web2d';
|
||||
import IconGroup from './IconGroup';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import SizeType from './SizeType';
|
||||
|
||||
interface Icon {
|
||||
getElement(): ElementClass;
|
||||
|
||||
setGroup(group: IconGroup);
|
||||
setGroup(group: IconGroup): Group;
|
||||
|
||||
getGroup(): IconGroup;
|
||||
|
||||
@ -13,11 +14,11 @@ interface Icon {
|
||||
|
||||
getPosition(): Point;
|
||||
|
||||
addEvent(type: string, fnc): void;
|
||||
addEvent(type: string, fnc: () => void): void;
|
||||
|
||||
remove(): void;
|
||||
|
||||
getModel();
|
||||
getModel(): FeatureModel;
|
||||
}
|
||||
|
||||
export default Icon;
|
||||
|
@ -22,6 +22,7 @@ import IconGroupRemoveTip from './IconGroupRemoveTip';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import Icon from './Icon';
|
||||
|
||||
const ORDER_BY_TYPE = new Map<string, number>();
|
||||
ORDER_BY_TYPE.set('icon', 0);
|
||||
@ -29,7 +30,7 @@ ORDER_BY_TYPE.set('note', 1);
|
||||
ORDER_BY_TYPE.set('link', 2);
|
||||
|
||||
class IconGroup {
|
||||
private _icons: ImageIcon[];
|
||||
private _icons: Icon[];
|
||||
|
||||
private _group: Group;
|
||||
|
||||
@ -84,7 +85,7 @@ class IconGroup {
|
||||
this._resize(this._icons.length);
|
||||
}
|
||||
|
||||
addIcon(icon: ImageIcon, remove: boolean) {
|
||||
addIcon(icon: Icon, remove: boolean): void {
|
||||
$defined(icon, 'icon is not defined');
|
||||
|
||||
// Order could have change, need to re-add all.
|
||||
@ -114,8 +115,8 @@ class IconGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private _findIconFromModel(iconModel: FeatureModel) {
|
||||
let result: ImageIcon | null = null;
|
||||
private _findIconFromModel(iconModel: FeatureModel): Icon {
|
||||
let result: Icon | null = null;
|
||||
|
||||
this._icons.forEach((icon) => {
|
||||
const elModel = icon.getModel();
|
||||
@ -140,7 +141,7 @@ class IconGroup {
|
||||
this._removeIcon(icon);
|
||||
}
|
||||
|
||||
private _removeIcon(icon: ImageIcon) {
|
||||
private _removeIcon(icon: Icon) {
|
||||
$assert(icon, 'icon can not be null');
|
||||
|
||||
this._removeTip.close(0);
|
||||
@ -179,7 +180,7 @@ class IconGroup {
|
||||
this._group.setCoordSize(iconsLength * iconSize, iconSize);
|
||||
}
|
||||
|
||||
private _positionIcon(icon: ImageIcon, order: number) {
|
||||
private _positionIcon(icon: Icon, order: number) {
|
||||
const iconSize = ImageIcon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||
icon
|
||||
.getElement()
|
||||
|
@ -23,9 +23,9 @@ import FeatureModel from './model/FeatureModel';
|
||||
import Icon from './Icon';
|
||||
|
||||
abstract class ImageIcon implements Icon {
|
||||
protected _image: Image;
|
||||
private _image: Image;
|
||||
|
||||
protected _group: IconGroup;
|
||||
private _group: IconGroup;
|
||||
|
||||
constructor(url: string) {
|
||||
$assert(url, 'image url can not be null');
|
||||
|
@ -43,7 +43,7 @@ class LinkIcon extends ImageIcon {
|
||||
}
|
||||
|
||||
private _registerEvents() {
|
||||
this._image.setCursor('pointer');
|
||||
this.getElement().setCursor('pointer');
|
||||
|
||||
const manager = WidgetManager.getInstance();
|
||||
manager.createTooltipForLink(this._topic, this._linksModel as LinkModel, this);
|
||||
|
@ -42,7 +42,7 @@ class NoteIcon extends ImageIcon {
|
||||
}
|
||||
|
||||
private _registerEvents(): void {
|
||||
this._image.setCursor('pointer');
|
||||
this.getElement().setCursor('pointer');
|
||||
|
||||
const manager = WidgetManager.getInstance();
|
||||
manager.createTooltipForNote(this._topic, this._linksModel as NoteModel, this);
|
||||
|
@ -19,6 +19,8 @@ import { $assert } from '@wisemapping/core-js';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import ActionDispatcher from './ActionDispatcher';
|
||||
import iconFamily from './model/SvgIconFamily.json';
|
||||
import Topic from './Topic';
|
||||
import SvgIconModel from './model/SvgIconModel';
|
||||
|
||||
function importAll(r) {
|
||||
const images = {};
|
||||
@ -28,10 +30,16 @@ function importAll(r) {
|
||||
return images;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const images = importAll(require.context('../../assets/icons', false, /\.(png|svg)$/));
|
||||
|
||||
class SvgImageIcon extends ImageIcon {
|
||||
constructor(topic, iconModel, readOnly) {
|
||||
private _topicId: number;
|
||||
|
||||
private _featureModel: SvgIconModel;
|
||||
|
||||
constructor(topic: Topic, iconModel: SvgIconModel, readOnly: boolean) {
|
||||
$assert(iconModel, 'iconModel can not be null');
|
||||
$assert(topic, 'topic can not be null');
|
||||
|
||||
@ -52,13 +60,13 @@ class SvgImageIcon extends ImageIcon {
|
||||
const newIconType = SvgImageIcon._getNextFamilyIconId(iconTypeClick);
|
||||
iconModel.setIconType(newIconType);
|
||||
|
||||
me._image.setHref(SvgImageIcon.getImageUrl(newIconType));
|
||||
me.getElement().setHref(SvgImageIcon.getImageUrl(newIconType));
|
||||
});
|
||||
this._image.setCursor('pointer');
|
||||
this.getElement().setCursor('pointer');
|
||||
}
|
||||
}
|
||||
|
||||
static getImageUrl(iconId) {
|
||||
static getImageUrl(iconId: string) {
|
||||
let result = images[`${iconId}.svg`];
|
||||
if (!result) {
|
||||
result = images[`${iconId}.png`];
|
||||
@ -70,11 +78,11 @@ class SvgImageIcon extends ImageIcon {
|
||||
return this._featureModel;
|
||||
}
|
||||
|
||||
static _getNextFamilyIconId(iconId) {
|
||||
private static _getNextFamilyIconId(iconId: string): string {
|
||||
const familyIcons = SvgImageIcon._getFamilyIcons(iconId);
|
||||
$assert(familyIcons !== null, `Family Icon not found: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
let result: string | null = null;
|
||||
for (let i = 0; i < familyIcons.length && result == null; i++) {
|
||||
if (familyIcons[i] === iconId) {
|
||||
// Is last one?
|
||||
@ -87,28 +95,18 @@ class SvgImageIcon extends ImageIcon {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (!result) {
|
||||
throw new Error(`Could not find iconId ${iconId}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static _getFamilyIcons(iconId) {
|
||||
private static _getFamilyIcons(iconId: string): string[] {
|
||||
$assert(iconId != null, 'id must not be null');
|
||||
$assert(iconId.indexOf('_') !== -1, `Invalid icon id (it must contain '_'). Id: ${iconId}`);
|
||||
|
||||
let result = null;
|
||||
let result: string[] | null = null;
|
||||
for (let i = 0; i < iconFamily.length; i++) {
|
||||
const family = iconFamily[i];
|
||||
const iconFamilyId = iconId.substr(0, iconId.indexOf('_'));
|
||||
@ -118,6 +116,11 @@ class SvgImageIcon extends ImageIcon {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
throw new Error(`Could not find icon id ${iconId}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ import NoteModel from './model/NoteModel';
|
||||
import LinkModel from './model/LinkModel';
|
||||
import SizeType from './SizeType';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import ImageIcon from './ImageIcon';
|
||||
import PositionType from './PositionType';
|
||||
import LineTopicShape from './widget/LineTopicShape';
|
||||
import ImageTopicShape from './widget/ImageTopicShape';
|
||||
import ColorUtil from './render/ColorUtil';
|
||||
import Icon from './Icon';
|
||||
|
||||
const ICON_SCALING_FACTOR = 1.3;
|
||||
|
||||
@ -305,20 +305,19 @@ abstract class Topic extends NodeGraph {
|
||||
// Load topic features ...
|
||||
const model = this.getModel();
|
||||
const featuresModel = model.getFeatures();
|
||||
|
||||
featuresModel.forEach((f) => {
|
||||
const icon = TopicFeatureFactory.createIcon(this, f, this.isReadOnly());
|
||||
|
||||
const type = f.getType();
|
||||
const addRemoveAction =
|
||||
type === TopicFeatureFactory.SvgIcon.id || type === TopicFeatureFactory.EmojiIcon.id;
|
||||
|
||||
const addRemoveAction = type === 'eicon' || type === 'icon';
|
||||
result.addIcon(icon, addRemoveAction && !this.isReadOnly());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
addFeature(featureModel: FeatureModel): ImageIcon {
|
||||
addFeature(featureModel: FeatureModel): Icon {
|
||||
const iconGroup = this.getOrBuildIconGroup();
|
||||
this.closeEditors();
|
||||
|
||||
@ -326,10 +325,8 @@ abstract class Topic extends NodeGraph {
|
||||
const model = this.getModel();
|
||||
model.addFeature(featureModel);
|
||||
|
||||
const result: ImageIcon = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly());
|
||||
const isIcon =
|
||||
featureModel.getType() === TopicFeatureFactory.SvgIcon.id ||
|
||||
featureModel.getType() === TopicFeatureFactory.EmojiIcon.id;
|
||||
const result: Icon = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly());
|
||||
const isIcon = featureModel.getType() === 'icon' || featureModel.getType() === 'eicon';
|
||||
iconGroup.addIcon(result, isIcon && !this.isReadOnly());
|
||||
|
||||
this.redraw();
|
||||
@ -697,7 +694,7 @@ abstract class Topic extends NodeGraph {
|
||||
|
||||
getNoteValue(): string {
|
||||
const model = this.getModel();
|
||||
const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
|
||||
const notes = model.findFeatureByType('note');
|
||||
let result;
|
||||
if (notes.length > 0) {
|
||||
result = (notes[0] as NoteModel).getText();
|
||||
@ -710,7 +707,7 @@ abstract class Topic extends NodeGraph {
|
||||
const topicId = this.getId();
|
||||
const model = this.getModel();
|
||||
const dispatcher = ActionDispatcher.getInstance();
|
||||
const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
|
||||
const notes = model.findFeatureByType('note');
|
||||
|
||||
if (!$defined(value) && notes.length > 0) {
|
||||
const featureId = notes[0].getId();
|
||||
@ -720,7 +717,7 @@ abstract class Topic extends NodeGraph {
|
||||
text: value,
|
||||
});
|
||||
} else {
|
||||
dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Note.id, {
|
||||
dispatcher.addFeatureToTopic(topicId, 'note', {
|
||||
text: value,
|
||||
});
|
||||
}
|
||||
@ -729,7 +726,7 @@ abstract class Topic extends NodeGraph {
|
||||
getLinkValue(): string {
|
||||
const model = this.getModel();
|
||||
// @param {mindplot.model.LinkModel[]} links
|
||||
const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
|
||||
const links = model.findFeatureByType('link');
|
||||
let result;
|
||||
if (links.length > 0) {
|
||||
result = (links[0] as LinkModel).getUrl();
|
||||
@ -742,7 +739,7 @@ abstract class Topic extends NodeGraph {
|
||||
const topicId = this.getId();
|
||||
const model = this.getModel();
|
||||
const dispatcher = ActionDispatcher.getInstance();
|
||||
const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
|
||||
const links = model.findFeatureByType('link');
|
||||
|
||||
if (!$defined(value)) {
|
||||
const featureId = links[0].getId();
|
||||
@ -752,7 +749,7 @@ abstract class Topic extends NodeGraph {
|
||||
url: value,
|
||||
});
|
||||
} else {
|
||||
dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Link.id, {
|
||||
dispatcher.addFeatureToTopic(topicId, 'link', {
|
||||
url: value,
|
||||
});
|
||||
}
|
||||
|
@ -1,75 +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 EmojiCharIcon from './EmojiCharIcon';
|
||||
import SvgImageIcon from './SvgImageIcon';
|
||||
import LinkIcon from './LinkIcon';
|
||||
import NoteIcon from './NoteIcon';
|
||||
|
||||
const TopicFeatureFactory = {
|
||||
/** the icon object */
|
||||
SvgIcon: {
|
||||
id: 'icon',
|
||||
icon: SvgImageIcon,
|
||||
},
|
||||
|
||||
EmojiIcon: {
|
||||
id: 'eicon',
|
||||
icon: EmojiCharIcon,
|
||||
},
|
||||
|
||||
/** the link object */
|
||||
Link: {
|
||||
id: 'link',
|
||||
icon: LinkIcon,
|
||||
},
|
||||
|
||||
/** the note object */
|
||||
Note: {
|
||||
id: 'note',
|
||||
icon: NoteIcon,
|
||||
},
|
||||
|
||||
createIcon(topic, model, readOnly) {
|
||||
$assert(topic, 'topic can not be null');
|
||||
$assert(model, 'model can not be null');
|
||||
|
||||
const { icon: Icon } = TopicFeatureFactory._featuresMetadataById.filter(
|
||||
(elem) => elem.id === model.getType(),
|
||||
)[0];
|
||||
|
||||
// Temporal catch to idenfify bug. Please, remove.
|
||||
let result;
|
||||
try {
|
||||
result = new Icon(topic, model, readOnly);
|
||||
} catch (e) {
|
||||
throw new Error(`${e} - ${JSON.stringify(model)}`);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
TopicFeatureFactory._featuresMetadataById = [
|
||||
TopicFeatureFactory.SvgIcon,
|
||||
TopicFeatureFactory.EmojiIcon,
|
||||
TopicFeatureFactory.Link,
|
||||
TopicFeatureFactory.Note,
|
||||
];
|
||||
|
||||
export default TopicFeatureFactory;
|
57
packages/mindplot/src/components/TopicFeature.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 EmojiCharIcon from './EmojiCharIcon';
|
||||
import SvgImageIcon from './SvgImageIcon';
|
||||
import LinkIcon from './LinkIcon';
|
||||
import NoteIcon from './NoteIcon';
|
||||
import FeatureModel from './model/FeatureModel';
|
||||
import Topic from './Topic';
|
||||
import SvgIconModel from './model/SvgIconModel';
|
||||
import LinkModel from './model/LinkModel';
|
||||
import NoteModel from './model/NoteModel';
|
||||
import Icon from './Icon';
|
||||
import EmojiIconModel from './model/EmojiIconModel';
|
||||
|
||||
class TopicFeatureFactory {
|
||||
static createIcon(topic: Topic, model: FeatureModel, readOnly: boolean): Icon {
|
||||
let result: Icon;
|
||||
const featureType = model.getType();
|
||||
switch (featureType) {
|
||||
case 'icon':
|
||||
result = new SvgImageIcon(topic, model as SvgIconModel, readOnly);
|
||||
break;
|
||||
case 'eicon':
|
||||
result = new EmojiCharIcon(topic, model as EmojiIconModel, readOnly);
|
||||
break;
|
||||
case 'link':
|
||||
result = new LinkIcon(topic, model as LinkModel, readOnly);
|
||||
break;
|
||||
case 'note':
|
||||
result = new NoteIcon(topic, model as NoteModel, readOnly);
|
||||
break;
|
||||
default: {
|
||||
const exhaustiveCheck: never = featureType;
|
||||
throw new Error(`Unhandled feature type case: ${exhaustiveCheck}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default TopicFeatureFactory;
|