albert-svg
Version:
Dynamic SVG generation using Cassowary constraints
240 lines (212 loc) • 6.07 kB
JavaScript
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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 {
Equation,
Expression,
GEQ,
LEQ,
Strength,
Inequality
} from "cassowary";
import {
alignAll,
distribute,
eqAll,
geqAll,
fixAll,
forEach,
leqAll,
spaceHorizontally,
spaceVertically,
variable
} from "./helpers";
import {
appendTo,
createElement,
insertAfter,
insertAt,
insertBefore,
prependTo
} from "./utils";
export default class Group {
constructor(childrenOrAttributes = {}, attributes = {}) {
if (Array.isArray(childrenOrAttributes)) {
this.children_ = childrenOrAttributes;
this.attributes_ = attributes;
} else {
this.children_ = [];
this.attributes_ = childrenOrAttributes;
}
this.constraints_ = [];
const idPrefix = attributes.id ? attributes.id + ":" : "";
this.leftEdge = variable(idPrefix + "group.leftEdge", 0);
this.topEdge = variable(idPrefix + "group.topEdge", 0);
this.rightEdge = variable(idPrefix + "group.rightEdge", 0);
this.bottomEdge = variable(idPrefix + "group.bottomEdge", 0);
this.x = new Expression(this.leftEdge);
this.y = new Expression(this.topEdge);
this.width = new Expression(this.rightEdge).minus(this.leftEdge);
this.height = new Expression(this.bottomEdge).minus(this.topEdge);
this.centerX = new Expression(this.leftEdge).plus(this.rightEdge).divide(2);
this.centerY = new Expression(this.topEdge).plus(this.bottomEdge).divide(2);
this.topMostChild_ = null;
this.bottomMostChild_ = null;
this.leftMostChild_ = null;
this.rightMostChild_ = null;
}
append(...children) {
appendTo(this.children_, children);
return this;
}
prepend(...children) {
prependTo(this.children_, children);
return this;
}
insertAt(index, ...children) {
insertAt(this.children_, index, children);
return this;
}
insertBefore(child, ...children) {
insertBefore(this.children_, child, children);
return this;
}
insertAfter(child, ...children) {
insertAfter(this.children_, child, children);
return this;
}
render() {
const el = createElement("g", this.attributes_);
for (const child of this.children_) {
el.appendChild(child.render());
}
// For debugging
el.setAttributeNS(null, "data-top-edge", this.topEdge.value);
el.setAttributeNS(null, "data-right-edge", this.rightEdge.value);
el.setAttributeNS(null, "data-bottom-edge", this.bottomEdge.value);
el.setAttributeNS(null, "data-left-edge", this.leftEdge.value);
return el;
}
constraints() {
const result = this.constraints_.slice();
if (this.topMostChild_) {
result.push(
new Equation(
this.topEdge,
this.topMostChild_.topEdge,
Strength.medium,
1
)
);
}
if (this.bottomMostChild_) {
result.push(
new Equation(
this.bottomEdge,
this.bottomMostChild_.bottomEdge,
Strength.medium,
1
)
);
}
if (this.leftMostChild_) {
result.push(
new Equation(
this.leftEdge,
this.leftMostChild_.leftEdge,
Strength.medium,
1
)
);
}
if (this.rightMostChild_) {
result.push(
new Equation(
this.rightEdge,
this.rightMostChild_.rightEdge,
Strength.medium,
1
)
);
}
for (const child of this.children_) {
if (child !== this.topMostChild_) {
result.push(
new Inequality(this.topEdge, LEQ, child.topEdge, Strength.weak, 1)
);
}
if (child !== this.bottomMostChild_) {
result.push(
new Inequality(
this.bottomEdge,
GEQ,
child.bottomEdge,
Strength.weak,
1
)
);
}
if (child !== this.leftMostChild_) {
result.push(
new Inequality(this.leftEdge, LEQ, child.leftEdge, Strength.weak, 1)
);
}
if (child !== this.rightMostChild_) {
result.push(
new Inequality(this.rightEdge, GEQ, child.rightEdge, Strength.weak, 1)
);
}
}
return result;
}
forEach(constraint) {
appendTo(this.constraints_, forEach(this.children_, constraint));
return this;
}
fixAll(getter) {
appendTo(this.constraints_, fixAll(this.children_, getter));
return this;
}
alignAll(getter, distance = undefined) {
appendTo(this.constraints_, alignAll(this.children_, getter, distance));
return this;
}
eqAll(getter) {
appendTo(this.constraints_, eqAll(this.children_, getter));
return this;
}
geqAll(getter) {
appendTo(this.constraints_, geqAll(this.children_, getter));
return this;
}
leqAll(getter) {
appendTo(this.constraints_, leqAll(this.children_, getter));
return this;
}
distribute(getter) {
appendTo(this.constraints_, distribute(this.children_, getter));
return this;
}
spaceHorizontally(distance = 0) {
this.leftMostChild_ = this.children_[0];
this.rightMostChild_ = this.children_[this.children_.length - 1];
appendTo(this.constraints_, spaceHorizontally(this.children_, distance));
return this;
}
spaceVertically(distance = 0) {
this.topMostChild_ = this.children_[0];
this.bottomMostChild_ = this.children_[this.children_.length - 1];
appendTo(this.constraints_, spaceVertically(this.children_, distance));
return this;
}
}