wisemapping-frontend/packages/web2d/src/components/peer/svg/ElementPeer.ts
2023-02-11 22:56:39 -08:00

278 lines
7.9 KiB
TypeScript

/* eslint-disable no-unused-vars */
/* eslint-disable class-methods-use-this */
/*
* 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, $defined } from '@wisemapping/core-js';
import SizeType from '../../SizeType';
import EventUtils from '../utils/EventUtils';
class ElementPeer {
_native: SVGElement;
private _parent: ElementPeer | null;
protected _size: SizeType;
private _changeListeners: {};
private __handlers: Map<string, any>;
private _children: ElementPeer[];
private _stokeStyle: string | null;
constructor(svgElement: SVGElement) {
this._native = svgElement;
this._size = { width: 1, height: 1 };
this._changeListeners = {};
// http://support.adobe.com/devsup/devsup.nsf/docs/50493.htm
// __handlers stores handlers references so they can be removed afterwards
this.__handlers = new Map();
this._children = [];
this._parent = null;
this._stokeStyle = null;
}
setChildren(children: ElementPeer[]): void {
this._children = children;
}
getChildren(): ElementPeer[] {
return this._children;
}
getParent(): ElementPeer | null {
return this._parent;
}
setParent(parent: ElementPeer | null): void {
this._parent = parent;
}
append(elementPeer: ElementPeer): void {
// Store parent and child relationship.
elementPeer.setParent(this);
const children = this.getChildren();
children.push(elementPeer);
// Append element as a child.
this._native.appendChild(elementPeer._native);
// Broadcast events ...
EventUtils.broadcastChangeEvent(this, 'strokeStyle');
}
removeChild(elementPeer: ElementPeer): void {
// Store parent and child relationship.
elementPeer.setParent(null);
let children = this.getChildren();
// Remove from children array ...
const oldLength = children.length;
children = children.filter((c) => c !== elementPeer);
this.setChildren(children);
$assert(children.length < oldLength, `element could not be removed:${elementPeer}`);
// Append element as a child.
this._native.removeChild(elementPeer._native);
}
/**
* http://www.w3.org/TR/DOM-Level-3-Events/events.html
* http://developer.mozilla.org/en/docs/addEvent
*/
addEvent(type: string, listener) {
// wrap it so it can be ~backward compatible with jQuery.trigger
const wrappedListener = (e) => listener(e, e.detail);
this.__handlers.set(listener, wrappedListener);
this._native.addEventListener(type, wrappedListener);
}
trigger(type: string, event) {
// TODO: check this for correctness and for real jQuery.trigger replacement
this._native.dispatchEvent(new CustomEvent(type, { detail: event }));
}
removeEvent(type: string, listener) {
this._native.removeEventListener(type, this.__handlers.get(listener));
this.__handlers.delete(listener);
}
setSize(width: number, height: number): void {
if ($defined(width) && this._size.width !== width) {
this._size.width = width;
this._native.setAttribute('width', width.toFixed(0));
}
if ($defined(height) && this._size.height !== height) {
this._size.height = height;
this._native.setAttribute('height', height.toFixed(0));
}
EventUtils.broadcastChangeEvent(this, 'strokeStyle');
}
getSize(): SizeType {
return { width: this._size.width, height: this._size.height };
}
setFill(color: string | null, opacity?: number | null) {
if (color) {
this._native.setAttribute('fill', color);
}
if ($defined(opacity)) {
this._native.setAttribute('fill-opacity', String(opacity));
}
}
getFill() {
const color = this._native.getAttribute('fill');
const opacity = this._native.getAttribute('fill-opacity');
return { color, opacity: Number(opacity) };
}
getStroke() {
const stoke = this._native;
const color = stoke.getAttribute('stroke');
const dashstyle = this._stokeStyle;
const opacity = stoke.getAttribute('stroke-opacity');
const width = stoke.getAttribute('stroke-width');
return {
color,
style: dashstyle,
opacity,
width,
};
}
setStroke(width: number | null, style?: string | null, color?: string | null, opacity?: number) {
if ($defined(width)) {
this._native.setAttribute('stroke-width', `${width}`);
}
if (color) {
this._native.setAttribute('stroke', color);
}
if (style) {
this._stokeStyle = style;
switch (style) {
case 'dash':
this._native.setAttribute('stroke-dasharray', '5 5');
this._native.setAttribute('stroke-linecap', '');
break;
case 'dot':
this._native.setAttribute('stroke-dasharray', '1 8');
this._native.setAttribute('stroke-linecap', 'round');
break;
case 'dashdot':
case 'longdash':
this._native.setAttribute('stroke-dasharray', '10 5 2');
this._native.setAttribute('stroke-linecap', 'round');
break;
case 'solid':
this._native.setAttribute('stroke-dasharray', '');
this._native.setAttribute('stroke-linecap', '');
break;
default:
throw new Error(`Unsupported style: ${style}`);
}
}
if ($defined(opacity)) {
this._native.setAttribute('stroke-opacity', String(opacity));
}
}
setVisibility(value: boolean, fade?: number) {
this._native.setAttribute('visibility', value ? 'visible' : 'hidden');
this._native.style.opacity = String(value ? 1 : 0);
if (fade) {
this._native.style.transition = `visibility ${fade}ms, opacity ${fade}ms`;
} else {
this._native.style.transition = '';
}
}
isVisible(): boolean {
const visibility = this._native.getAttribute('visibility');
return !(visibility === 'hidden');
}
updateStrokeStyle() {
const strokeStyle = this._stokeStyle;
if (this.getParent()) {
if (strokeStyle && strokeStyle !== 'solid') {
this.setStroke(null, strokeStyle);
}
}
}
attachChangeEventListener(type, listener) {
const listeners = this.getChangeEventListeners(type);
if (!$defined(listener)) {
throw new Error('Listener can not be null');
}
listeners.push(listener);
}
getChangeEventListeners(type) {
let listeners = this._changeListeners[type];
if (!$defined(listeners)) {
listeners = [];
this._changeListeners[type] = listeners;
}
return listeners;
}
/**
* Move element to the front
*/
moveToFront() {
if (!this._native.parentNode) {
throw new Error('node not connected to parent');
}
this._native.parentNode.appendChild(this._native);
}
/**
* Move element to the back
*/
moveToBack() {
if (!this._native.parentNode) {
throw new Error('node not connected to parent');
}
this._native.parentNode.insertBefore(this._native, this._native.parentNode.firstChild);
}
setCursor(type: string) {
this._native.style.cursor = type;
}
static stokeStyleToStrokDasharray() {
return {
solid: [],
dot: [1, 3],
dash: [4, 3],
longdash: [10, 2],
dashdot: [5, 3, 1, 3],
};
}
protected static svgNamespace = 'http://www.w3.org/2000/svg';
protected static linkNamespace = 'http://www.w3.org/1999/xlink';
}
export default ElementPeer;