diff --git a/packages/mindplot/src/components/MultilineTextEditor.ts b/packages/mindplot/src/components/MultilineTextEditor.ts index 8d70d24b..1c69233f 100644 --- a/packages/mindplot/src/components/MultilineTextEditor.ts +++ b/packages/mindplot/src/components/MultilineTextEditor.ts @@ -59,6 +59,7 @@ class EditorComponent extends Events { resize: 'none', overflow: 'hidden', padding: '0px 0px 0px 0px', + lineHeight: '100%', }); result.append(textareaElem); @@ -121,7 +122,7 @@ class EditorComponent extends Events { }); } - private resize(text?: string) { + private resize(text?: string): void { // Force relayout ... EventBus.instance.fireEvent('forceLayout'); @@ -132,15 +133,12 @@ class EditorComponent extends Events { const textValue = text || this.getTextAreaText(); const textElem = this.getTextareaElem(); - const lines = textValue.split('\n'); - let maxLineLength = 1; - lines.forEach((line: string) => { - maxLineLength = Math.max(line.length, maxLineLength); - }); + const rows = [...textValue].filter((x) => x === '\n').length + 1; + const maxLineLength = Math.max(...textValue.split('\n').map((l) => l.length)); textElem.attr('cols', maxLineLength); - textElem.attr('rows', lines.length); + textElem.attr('rows', rows); this._containerElem.css({ width: `${maxLineLength + 2}em`, diff --git a/packages/mindplot/src/components/Topic.ts b/packages/mindplot/src/components/Topic.ts index 5eef5d9c..6f0c378d 100644 --- a/packages/mindplot/src/components/Topic.ts +++ b/packages/mindplot/src/components/Topic.ts @@ -1233,7 +1233,7 @@ abstract class Topic extends NodeGraph { textShape.setFontName(fontFamily); const text = this.getText(); - textShape.setText(text.trim()); + textShape.setText(text); // Update outer shape style ... const outerShape = this.getOuterShape(); diff --git a/packages/mindplot/src/components/layout/AbstractBasicSorter.ts b/packages/mindplot/src/components/layout/AbstractBasicSorter.ts index 52a2116b..025ca728 100644 --- a/packages/mindplot/src/components/layout/AbstractBasicSorter.ts +++ b/packages/mindplot/src/components/layout/AbstractBasicSorter.ts @@ -47,12 +47,9 @@ abstract class AbstractBasicSorter extends ChildrenSorterStrategy { if (children.length === 0 || node.areChildrenShrunken()) { result = height; } else { - let childrenHeight = 0; - - children.forEach((child) => { - childrenHeight += this._computeChildrenHeight(treeSet, child, heightCache); - }); - + const childrenHeight = children + .map((child) => this._computeChildrenHeight(treeSet, child, heightCache)) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0); result = Math.max(height, childrenHeight); } @@ -63,7 +60,7 @@ abstract class AbstractBasicSorter extends ChildrenSorterStrategy { return result; } - protected _getSortedChildren(treeSet: RootedTreeSet, node: Node) { + protected _getSortedChildren(treeSet: RootedTreeSet, node: Node): Node[] { const result = treeSet.getChildren(node); result.sort((a, b) => a.getOrder() - b.getOrder()); return result; diff --git a/packages/mindplot/src/components/layout/BalancedSorter.ts b/packages/mindplot/src/components/layout/BalancedSorter.ts index cf649ba0..1751c54e 100644 --- a/packages/mindplot/src/components/layout/BalancedSorter.ts +++ b/packages/mindplot/src/components/layout/BalancedSorter.ts @@ -148,7 +148,7 @@ class BalancedSorter extends AbstractBasicSorter { } } - computeOffsets(treeSet: RootedTreeSet, node: Node) { + computeOffsets(treeSet: RootedTreeSet, node: Node): Map { $assert(treeSet, 'treeSet can no be null.'); $assert(node, 'node can no be null.'); @@ -180,7 +180,7 @@ class BalancedSorter extends AbstractBasicSorter { let ysum = 0; // Calculate the offsets ... - const result = {}; + const result = new Map(); for (let i = 0; i < heights.length; i++) { const direction = heights[i].order % 2 ? -1 : 1; @@ -202,7 +202,7 @@ class BalancedSorter extends AbstractBasicSorter { $assert(!Number.isNaN(xOffset), 'xOffset can not be null'); $assert(!Number.isNaN(yOffset), 'yOffset can not be null'); - result[heights[i].id] = { x: xOffset, y: yOffset }; + result.set(heights[i].id, { x: xOffset, y: yOffset }); } return result; } diff --git a/packages/mindplot/src/components/layout/ChildrenSorterStrategy.ts b/packages/mindplot/src/components/layout/ChildrenSorterStrategy.ts index 0806d30e..8d266e3a 100644 --- a/packages/mindplot/src/components/layout/ChildrenSorterStrategy.ts +++ b/packages/mindplot/src/components/layout/ChildrenSorterStrategy.ts @@ -22,7 +22,7 @@ import PositionType from '../PositionType'; abstract class ChildrenSorterStrategy { abstract computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node): Map; - abstract computeOffsets(treeSet: RootedTreeSet, node: Node): void; + abstract computeOffsets(treeSet: RootedTreeSet, node: Node): Map; abstract insert(treeSet: RootedTreeSet, parent: Node, child: Node, order: number): void; diff --git a/packages/mindplot/src/components/layout/Node.ts b/packages/mindplot/src/components/layout/Node.ts index af05655d..a5680521 100644 --- a/packages/mindplot/src/components/layout/Node.ts +++ b/packages/mindplot/src/components/layout/Node.ts @@ -145,7 +145,6 @@ class Node { /** */ setSize(size: SizeType) { - $assert($defined(size), 'Size can not be null'); this._setProperty('size', { ...size }); } @@ -177,14 +176,7 @@ class Node { setPosition(position: PositionType) { // This is a performance improvement to avoid movements that really could be avoided. - const currentPos = this.getPosition(); - if ( - currentPos == null || - Math.abs(currentPos.x - position.x) > 2 || - Math.abs(currentPos.y - position.y) > 2 - ) { - this._setProperty('position', position); - } + this._setProperty('position', position); } _setProperty(key: string, value) { diff --git a/packages/mindplot/src/components/layout/OriginalLayout.ts b/packages/mindplot/src/components/layout/OriginalLayout.ts index 4c492cc7..0044df7f 100644 --- a/packages/mindplot/src/components/layout/OriginalLayout.ts +++ b/packages/mindplot/src/components/layout/OriginalLayout.ts @@ -81,15 +81,17 @@ class OriginalLayout { // Calculate all node heights ... const sorter = node.getSorter(); const heightById = sorter.computeChildrenIdByHeights(this._treeSet, node); - this._layoutChildren(node, heightById); - this._fixOverlapping(node, heightById); + + this.layoutChildren(node, heightById); + // this.fixOverlapping(node, heightById); }); } - private _layoutChildren(node: Node, heightById: Map): void { + private layoutChildren(node: Node, heightById: Map): void { const nodeId = node.getId(); const children = this._treeSet.getChildren(node); const parent = this._treeSet.getParent(node); + const childrenOrderMoved = children.some((child) => child.hasOrderChanged()); const childrenSizeChanged = children.some((child) => child.hasSizeChanged()); @@ -99,43 +101,24 @@ class OriginalLayout { const parentHeightChanged = parent ? parent._heightChanged : false; const heightChanged = node._branchHeight !== newBranchHeight; - // eslint-disable-next-line no-param-reassign node._heightChanged = heightChanged || parentHeightChanged; if (childrenOrderMoved || childrenSizeChanged || heightChanged || parentHeightChanged) { const sorter = node.getSorter(); const offsetById = sorter.computeOffsets(this._treeSet, node); const parentPosition = node.getPosition(); - const me = this; children.forEach((child) => { - const offset = offsetById[child.getId()]; - - const childFreeDisplacement = child.getFreeDisplacement(); - const direction = node.getSorter().getChildDirection(me._treeSet, child); - - if ( - (direction > 0 && childFreeDisplacement.x < 0) || - (direction < 0 && childFreeDisplacement.x > 0) - ) { - child.resetFreeDisplacement(); - child.setFreeDisplacement({ - x: -childFreeDisplacement.x, - y: childFreeDisplacement.y, - }); - } - - offset.x += child.getFreeDisplacement().x; - offset.y += child.getFreeDisplacement().y; + const offset = offsetById.get(child.getId())!; const parentX = parentPosition.x; const parentY = parentPosition.y; const newPos = { x: parentX + offset.x, - y: parentY + offset.y + me._calculateAlignOffset(node, child, heightById), + y: parentY + offset.y + this.calculateAlignOffset(node, child, heightById), }; - me._treeSet.updateBranchPosition(child, newPos); + this._treeSet.updateBranchPosition(child, newPos); }); node._branchHeight = newBranchHeight; @@ -143,15 +126,11 @@ class OriginalLayout { // Continue reordering the children nodes ... children.forEach((child) => { - this._layoutChildren(child, heightById); + this.layoutChildren(child, heightById); }); } - private _calculateAlignOffset(node: Node, child: Node, heightById: Map): number { - if (child.isFree()) { - return 0; - } - + private calculateAlignOffset(node: Node, child: Node, heightById: Map): number { let offset = 0; const nodeHeight = node.getSize().height; @@ -192,14 +171,11 @@ class OriginalLayout { ); } - private _fixOverlapping(node: Node, heightById: Map): void { + private fixOverlapping(node: Node, heightById: Map): void { const children = this._treeSet.getChildren(node); - if (node.isFree()) { - this._shiftBranches(node, heightById); - } children.forEach((child) => { - this._fixOverlapping(child, heightById); + this.fixOverlapping(child, heightById); }); } diff --git a/packages/mindplot/src/components/layout/RootedTreeSet.ts b/packages/mindplot/src/components/layout/RootedTreeSet.ts index 3b6b4d82..a18562a3 100644 --- a/packages/mindplot/src/components/layout/RootedTreeSet.ts +++ b/packages/mindplot/src/components/layout/RootedTreeSet.ts @@ -334,9 +334,8 @@ class RootedTreeSet { const yOffset = oldPos.y - position.y; const children = this.getChildren(node); - const me = this; children.forEach((child) => { - me.shiftBranchPosition(child, xOffset, yOffset); + this.shiftBranchPosition(child, xOffset, yOffset); }); } diff --git a/packages/mindplot/src/components/layout/SymmetricSorter.ts b/packages/mindplot/src/components/layout/SymmetricSorter.ts index 8473bcc7..4c752b19 100644 --- a/packages/mindplot/src/components/layout/SymmetricSorter.ts +++ b/packages/mindplot/src/components/layout/SymmetricSorter.ts @@ -198,25 +198,11 @@ class SymmetricSorter extends AbstractBasicSorter { node.setOrder(0); } - /** - * @param treeSet - * @param node - * @throws will throw an error if treeSet is null or undefined - * @throws will throw an error if node is null or undefined - * @throws will throw an error if the calculated x offset cannot be converted to a numeric - * value, is null or undefined - * @throws will throw an error if the calculated y offset cannot be converted to a numeric - * value, is null or undefined - * @return offsets - */ - computeOffsets(treeSet: RootedTreeSet, node: Node) { - $assert(treeSet, 'treeSet can no be null.'); - $assert(node, 'node can no be null.'); - + computeOffsets(treeSet: RootedTreeSet, node: Node): Map { const children = this._getSortedChildren(treeSet, node); // Compute heights ... - const heights = children + const sizeById = children .map((child) => ({ id: child.getId(), order: child.getOrder(), @@ -227,30 +213,26 @@ class SymmetricSorter extends AbstractBasicSorter { .reverse(); // Compute the center of the branch ... - let totalHeight = 0; - heights.forEach((elem) => { - totalHeight += elem.height; - }); + const totalHeight = sizeById + .map((e) => e.height) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0); let ysum = totalHeight / 2; // Calculate the offsets ... - const result = {}; - for (let i = 0; i < heights.length; i++) { - ysum -= heights[i].height; - const childNode = treeSet.find(heights[i].id); + const result = new Map(); + for (let i = 0; i < sizeById.length; i++) { + ysum -= sizeById[i].height; + const childNode = treeSet.find(sizeById[i].id); const direction = this.getChildDirection(treeSet, childNode); - const yOffset = ysum + heights[i].height / 2; + const yOffset = ysum + sizeById[i].height / 2; const xOffset = direction * - (heights[i].width / 2 + + (sizeById[i].width / 2 + node.getSize().width / 2 + SymmetricSorter.INTERNODE_HORIZONTAL_PADDING); - $assert(!Number.isNaN(xOffset), 'xOffset can not be null'); - $assert(!Number.isNaN(yOffset), 'yOffset can not be null'); - - result[heights[i].id] = { x: xOffset, y: yOffset }; + result.set(sizeById[i].id, { x: xOffset, y: yOffset }); } return result; } diff --git a/packages/web2d/src/components/Text.ts b/packages/web2d/src/components/Text.ts index d41dcfe7..80692448 100644 --- a/packages/web2d/src/components/Text.ts +++ b/packages/web2d/src/components/Text.ts @@ -96,8 +96,7 @@ class Text extends WorkspaceElement { } getFontHeight(): number { - const lines = this.peer.getText().split('\n').length; - return this.getShapeHeight() / lines; + return this.getShapeHeight() / this.peer.getTextLines().length; } getPosition(): PositionType { diff --git a/packages/web2d/src/components/peer/svg/TextPeer.ts b/packages/web2d/src/components/peer/svg/TextPeer.ts index 5e928237..44286b03 100644 --- a/packages/web2d/src/components/peer/svg/TextPeer.ts +++ b/packages/web2d/src/components/peer/svg/TextPeer.ts @@ -20,53 +20,77 @@ import FontPeer, { FontStyle } from './FontPeer'; import ElementPeer from './ElementPeer'; import { getPosition } from '../utils/DomUtils'; import SizeType from '../../SizeType'; +import PositionType from '../../PositionType'; class TextPeer extends ElementPeer { private _position: { x: number; y: number }; private _font: FontPeer; - private _textAlign: any; - private _text: string | undefined; + private _textAlign: string; + private _text: string; constructor(fontPeer: FontPeer) { const svgElement = window.document.createElementNS('http://www.w3.org/2000/svg', 'text'); super(svgElement); this._position = { x: 0, y: 0 }; this._font = fontPeer; + this._text = ''; + this._textAlign = 'left'; } - append(element: ElementPeer) { + append(element: ElementPeer): void { this._native.appendChild(element._native); } - setTextAlignment(align: string) { + setTextAlignment(align: string): void { this._textAlign = align; } - getTextAlignment() { - return $defined(this._textAlign) ? this._textAlign : 'left'; + getTextAlignment(): 'left' | 'right' | 'center' { + return this._textAlign ? (this._textAlign as 'left' | 'right' | 'center') : 'left'; } setText(text: string) { + this._text = text; + // Remove all previous nodes ... while (this._native.firstChild) { this._native.removeChild(this._native.firstChild); } - this._text = text; - if (text) { - const lines = text.split('\n'); - lines.forEach((line) => { - const tspan = window.document.createElementNS(ElementPeer.svgNamespace, 'tspan'); - tspan.setAttribute('dy', '1em'); - tspan.setAttribute('x', String(this.getPosition().x)); + // Add nodes ... + this.getTextLines().forEach((l) => { + // Append a new line ... + const tspan = window.document.createElementNS(ElementPeer.svgNamespace, 'tspan'); + tspan.setAttribute('dy', '1em'); + tspan.setAttribute('x', this.getPosition().x.toFixed(1)); - tspan.textContent = line.length === 0 ? ' ' : line; - this._native.appendChild(tspan); - }); - } + // Add new line ... + tspan.textContent = l || ' '; + this._native.appendChild(tspan); + }); } - getText(): any { + getTextLines(): string[] { + const result: string[] = []; + if (this._text) { + const text = this._text; + let line = ''; + let i = 0; + do { + const c = text[i]; + if (c === '\n' || i === text.length) { + result.push(line); + line = ''; + } else { + line = line + c; + } + i = i + 1; + } while (i < text.length + 1); + } + return result; + } + + getText(): string { return this._text; } @@ -81,20 +105,20 @@ class TextPeer extends ElementPeer { }); } - getPosition() { + getPosition(): PositionType { return this._position; } - getNativePosition() { + getNativePosition(): { left: number; top: number } { return getPosition(this._native); } setFont(fontName: string, size: number, style: string, weight: string): void { - if ($defined(fontName)) { + if (fontName) { this._font = new FontPeer(fontName); } - if ($defined(style)) { + if (style) { this._font.setStyle(style); } if ($defined(weight)) { @@ -103,10 +127,10 @@ class TextPeer extends ElementPeer { if ($defined(size)) { this._font.setSize(size); } - this._updateFontStyle(); + this.updateFontStyle(); } - _updateFontStyle() { + private updateFontStyle() { this._native.setAttribute('font-family', this._font.getFontName()); this._native.setAttribute('font-size', this._font.getGraphSize()); this._native.setAttribute('font-style', this._font.getStyle()); @@ -123,17 +147,17 @@ class TextPeer extends ElementPeer { setTextSize(size: number) { this._font.setSize(size); - this._updateFontStyle(); + this.updateFontStyle(); } setStyle(style: string) { this._font.setStyle(style); - this._updateFontStyle(); + this.updateFontStyle(); } setWeight(weight: string) { this._font.setWeight(weight); - this._updateFontStyle(); + this.updateFontStyle(); } setFontName(fontName: string): void { @@ -142,7 +166,7 @@ class TextPeer extends ElementPeer { this._font.setSize(oldFont.getSize()); this._font.setStyle(oldFont.getStyle()); this._font.setWeight(oldFont.getWeight()); - this._updateFontStyle(); + this.updateFontStyle(); } getFontStyle(): FontStyle { @@ -159,7 +183,7 @@ class TextPeer extends ElementPeer { setFontSize(size: number): void { this._font.setSize(size); - this._updateFontStyle(); + this.updateFontStyle(); } getShapeWidth(): number {