devexpress-diagram
Version:
DevExpress Diagram Control
164 lines (154 loc) • 8.73 kB
text/typescript
import { ToolboxDraggingObject, IShapeToolboxOptions, Toolbox } from "./Toolbox";
import { IShapeDescriptionManager } from "../../Model/Shapes/Descriptions/ShapeDescriptionManager";
import { svgNS } from "../RenderHelper";
import { Shape } from "../../Model/Shapes/Shape";
import { ShapeDescription } from "../../Model/Shapes/Descriptions/ShapeDescription";
import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { TextShapeDescription } from "../../Model/Shapes/Descriptions/General/TextShapeDescription";
import { ITextMeasurer } from "../Measurer/ITextMeasurer";
import { DEFAULT_STROKE_WIDTH } from "../../Model/Style";
import { CustomShapeDescription } from "../../Model/Shapes/Descriptions/CustomShapeDescription";
import { RectanglePrimitive } from "../Primitives/RectaglePrimitive";
const DEFAULT_SHAPE_ICON_SIZE = 32;
const SHRINK_TEXT_SHAPE_ICON_SIZE = 26;
const SHRINKED_TEXT = "T";
export class IconToolbox extends Toolbox {
options: IShapeIconToolboxOptions;
constructor(parent: HTMLElement, readonly: boolean, allowDragging: boolean,
shapeDescriptionManager: IShapeDescriptionManager,
shapeTypes: string[], getAllowedShapeTypes: (shapes: string[]) => string[],
options: IShapeIconToolboxOptions, measurer: ITextMeasurer) {
super(parent, readonly, allowDragging, shapeDescriptionManager, shapeTypes, getAllowedShapeTypes);
this.options = options;
this.measurer = measurer;
}
createElements(element: HTMLElement, shapeTypes: string[]) {
const svgElement = document.createElementNS(svgNS, "svg");
svgElement.className.baseVal = "dxdi-canvas";
element.appendChild(svgElement);
this.drawShapeIcons(svgElement, shapeTypes, this.options.toolboxWidth || svgElement.getBoundingClientRect().width);
}
drawShapeIcons(parent: SVGElement, shapeTypes: string[], svgWidth: number) {
const lineWidth = DEFAULT_STROKE_WIDTH;
const targetWidth = svgWidth - 2 * lineWidth;
let shapeIconSize = this.options.shapeIconSize;
if(!shapeIconSize && this.options.shapeIconSpacing && this.options.shapeIconCountInRow)
shapeIconSize = Math.floor((targetWidth - (this.options.shapeIconCountInRow - 1) * this.options.shapeIconSpacing) / this.options.shapeIconCountInRow);
if(!shapeIconSize)
shapeIconSize = DEFAULT_SHAPE_ICON_SIZE;
shapeIconSize = Math.max(shapeIconSize, this.options.shapeIconSpacing / 2);
let width = shapeIconSize;
let iconCountInRow = this.options.shapeIconCountInRow;
if(!iconCountInRow) {
iconCountInRow = 1;
while(width < targetWidth) {
width += this.options.shapeIconSpacing + shapeIconSize;
if(width < targetWidth)
iconCountInRow++;
}
}
const shapeIconSpacing = (iconCountInRow > 1) ? (targetWidth - shapeIconSize * iconCountInRow) / (iconCountInRow - 1) : 0;
let xPos = lineWidth; let yPos = lineWidth;
const size = UnitConverter.pixelsToTwips(shapeIconSize);
shapeTypes.forEach((shapeType, index) => {
if(index > 0 && index % iconCountInRow === 0) {
xPos = lineWidth;
yPos += shapeIconSize + shapeIconSpacing;
}
const shapeDescription = this.shapeDescriptionManager.get(shapeType);
const shape = this.createShape(shapeDescription, xPos, yPos, shapeIconSize < SHRINK_TEXT_SHAPE_ICON_SIZE);
this.updateShapeIconBounds(shape, shapeIconSize);
const shapeEl = this.drawShape(parent, shape);
this.drawSelector(shapeEl, UnitConverter.pixelsToTwips(xPos), UnitConverter.pixelsToTwips(yPos), size);
xPos += shapeIconSize + shapeIconSpacing;
});
parent.style.height = yPos + shapeIconSize + lineWidth + "px";
parent.style.width = svgWidth + "px";
}
drawShape(parent: SVGElement, shape: Shape): SVGGElement {
const primitives = shape.description.createPrimitives(shape, true);
const gEl: SVGGElement = document.createElementNS(svgNS, "g");
gEl.setAttribute("data-tb-type", shape.description.key.toString());
gEl.setAttribute("class", "toolbox-item");
gEl.setAttribute("title", shape.description.getTitle());
if(this.options.shapeIconAttributes)
for(const key in this.options.shapeIconAttributes)
if(Object.prototype.hasOwnProperty.call(this.options.shapeIconAttributes, key))
gEl.setAttribute(key, this.options.shapeIconAttributes[key]);
parent.appendChild(gEl);
primitives.forEach(pr => {
const el = pr.createElement(e => gEl.appendChild(e));
pr.applyElementProperties(el, this.measurer);
});
return gEl;
}
drawSelector(parent: SVGGElement, x: number, y: number, size: number) {
const selectorRect = new RectanglePrimitive(x, y, size, size, undefined, "selector");
selectorRect.createElement(el => {
selectorRect.applyElementProperties(el, this.measurer);
parent.appendChild(el);
});
}
createShape(shapeDescription: ShapeDescription, xPos: number, yPos: number, shrinkText?: boolean) {
const xPosT = UnitConverter.pixelsToTwips(xPos);
const yPosT = UnitConverter.pixelsToTwips(yPos);
const shape = new Shape(shapeDescription, new Point(xPosT, yPosT), true);
if(this.needResetShapeText(shapeDescription))
shape.text = "";
else if(shrinkText)
shape.text = SHRINKED_TEXT;
return shape;
}
needResetShapeText(shapeDescription: ShapeDescription) {
if(shapeDescription instanceof TextShapeDescription)
return false;
if(shapeDescription instanceof CustomShapeDescription && shapeDescription.baseDescription instanceof TextShapeDescription)
return false;
return true;
}
updateShapeIconBounds(shape: Shape, shapeIconSize: number) {
const shapeSizeT = UnitConverter.pixelsToTwips(shapeIconSize);
shape.size.height = shape.size.width * shape.getToolboxHeightToWidthRatio();
if(shape.size.width > shape.size.height) {
const ratio = shape.size.height / shape.size.width;
shape.size.width = shapeSizeT;
shape.size.height = shapeSizeT * ratio;
shape.position.y = shape.position.y + (shapeSizeT - shape.size.height) / 2;
shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.width; });
}
else if(shape.size.width < shape.size.height) {
const ratio = shape.size.width / shape.size.height;
shape.size.height = shapeSizeT;
shape.size.width = shapeSizeT * ratio;
shape.position.x = shape.position.x + (shapeSizeT - shape.size.width) / 2;
shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.height; });
}
else {
shape.size.width = shapeSizeT;
shape.size.height = shapeSizeT;
shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.width; });
}
}
protected createDraggingElement(draggingObject: ToolboxDraggingObject): HTMLElement {
const element = document.createElement("DIV");
element.setAttribute("class", "dxdi-toolbox-drag-item");
document.body.appendChild(element);
const svgElement = document.createElementNS(svgNS, "svg");
svgElement.className.baseVal = "dxdi-canvas";
element.appendChild(svgElement);
const shapeDescription = this.shapeDescriptionManager.get(draggingObject.evt.data);
const shape = this.createShape(shapeDescription, DEFAULT_STROKE_WIDTH, DEFAULT_STROKE_WIDTH);
this.drawShape(svgElement, shape);
element.style.width = UnitConverter.twipsToPixels(shape.size.width) + 2 * DEFAULT_STROKE_WIDTH + "px";
element.style.height = UnitConverter.twipsToPixels(shape.size.height) + 2 * DEFAULT_STROKE_WIDTH + "px";
return element;
}
}
export interface IShapeIconToolboxOptions extends IShapeToolboxOptions {
shapeIconSpacing: number;
shapeIconSize?: number;
shapeIconCountInRow?: number;
shapeIconAttributes?: {[key: string]: any};
toolboxWidth?: number;
}