117 lines
3.5 KiB
JavaScript
117 lines
3.5 KiB
JavaScript
/**
|
|
* Public handle for one component rendered by a DiagramEngine.
|
|
*
|
|
* A handle exposes only the operations used by the CMap editor. The drawing
|
|
* object remains private to the engine so application code cannot depend on
|
|
* the representation inherited from the original renderer.
|
|
*/
|
|
export class DiagramComponent {
|
|
constructor(engine, component, attributeNames) {
|
|
this.engine = engine;
|
|
this.component = component;
|
|
this.attributeNames = attributeNames;
|
|
this.baseVisible = true;
|
|
}
|
|
|
|
/** Read or update the supported rendering attributes. */
|
|
attr(name, value) {
|
|
if (name === undefined) {
|
|
const attributes = {};
|
|
for (const attributeName of this.attributeNames) {
|
|
attributes[attributeName] = this.component[attributeName]();
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
if (isPlainObject(name)) {
|
|
for (const [attributeName, attributeValue] of Object.entries(name)) {
|
|
this.attr(attributeName, attributeValue);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
if (!this.attributeNames.includes(name)) return this;
|
|
if (value === undefined) return this.component[name]();
|
|
|
|
this.component[name](value);
|
|
return this;
|
|
}
|
|
|
|
/** Remove this component from its diagram. */
|
|
remove() {
|
|
this.engine.removeComponent(this);
|
|
}
|
|
|
|
/** Move this component to the front of its own rendering band. */
|
|
toFront() {
|
|
this.engine.drawingSurface.toFront(this.component);
|
|
return this;
|
|
}
|
|
|
|
/** Return the concrete element currently rendering this component. */
|
|
element() {
|
|
return this.component.element();
|
|
}
|
|
|
|
/** Immediately render the current component state. */
|
|
redraw() {
|
|
this.component.redraw();
|
|
return this;
|
|
}
|
|
|
|
/** Read or change whether the component participates in the presentation. */
|
|
visible(value) {
|
|
if (value === undefined) return this.component.visible !== false;
|
|
|
|
this.baseVisible = Boolean(value);
|
|
this.engine.applyFilter(this);
|
|
return this.component.visible;
|
|
}
|
|
|
|
/** Register a callback invoked after this component has been rendered. */
|
|
onRendered(handler) {
|
|
validateOptionalHandler(handler, "render");
|
|
this.component.renderedHandler = handler ?
|
|
(element) => handler(this, element) : null;
|
|
|
|
if (handler && this.component.element()) handler(this, this.component.element());
|
|
return this;
|
|
}
|
|
|
|
/** Read or change whether the component can be dragged. */
|
|
draggable(enabled) {
|
|
if (enabled === undefined) {
|
|
return this.engine.drawingSurface.dragEnabled(this.component);
|
|
}
|
|
|
|
if (enabled) this.engine.drawingSurface.enableDrag(this.component);
|
|
else this.engine.drawingSurface.disableDrag(this.component);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
/** Return a new object containing only supported input attributes. */
|
|
export function pickAttributes(attributes, names) {
|
|
if (attributes === undefined) return {};
|
|
if (!isPlainObject(attributes)) throw new TypeError("Invalid component attributes");
|
|
|
|
const selected = {};
|
|
for (const name of names) {
|
|
if (name in attributes) selected[name] = attributes[name];
|
|
}
|
|
return selected;
|
|
}
|
|
|
|
/** Validate an optional event handler at the public engine boundary. */
|
|
export function validateOptionalHandler(handler, meaning) {
|
|
if (handler !== null && handler !== undefined && typeof handler !== "function") {
|
|
throw new TypeError(`Invalid ${meaning} handler`);
|
|
}
|
|
}
|
|
|
|
/** Determine whether a value is a plain attributes object. */
|
|
function isPlainObject(value) {
|
|
return typeof value === "object" && value !== null &&
|
|
Object.prototype.toString.call(value) === "[object Object]";
|
|
}
|