-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlayout.js
168 lines (127 loc) · 4.62 KB
/
layout.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
var ChiasmComponent = require("chiasm-component");
var None = require("model-js").None;
var computeLayout = require("./computeLayout");
var _ = require("lodash");
var d3 = require("d3");
function Layout(chiasm){
var my = ChiasmComponent({
layout: {},
sizes: {},
containerSelector: None
});
my.when("containerSelector", function (containerSelector){
if(containerSelector !== None){
// If the containerSelector changes, remove all component
// DOM elements from the old container.
if(my.container){
removeAllChildren(my.container);
}
my.container = document.querySelector(containerSelector);
// Initialize `my.box` based on the container size.
setBox();
}
});
// Sets the `box` my property based on actual container size .
function setBox(){
if(my.container){
my.box = {
x: 0,
y: 0,
width: my.container.clientWidth,
height: my.container.clientHeight
};
}
}
my.when("container", setBox);
// Add this guard here so unit tests can run in Node.
if (typeof window !== "undefined"){
var listener = _.throttle(setBox, 100);
// Update `my.box` on resize
window.addEventListener("resize", listener);
my.destroy = function(){
window.removeEventListener("resize", listener);
};
}
// Respond to changes is box and layout.
my.when(["layout", "sizes", "box"], function(layout, sizes, box){
var boxes = computeLayout(layout, sizes, box);
Object.keys(boxes).forEach(function (alias){
// Annotate the DOM so it is clear what each element corresponds to
// for developers inspecting the DOM.
var domClass = "chiasm-component-" + alias;
chiasm.getComponent(alias).then(function (component){
var box = boxes[alias];
// Pass the box into the component so it can resize its content
// using box.width and box.height.
component.box = box;
// Set the (x, y) box offset on the component's DOM element.
if(component.el){
if(component.el instanceof SVGGraphicsElement){
// Use SVG transform property to position SVG component elements.
d3.select(component.el)
.attr("transform", "translate(" + box.x + "," + box.y + ")")
.classed(domClass, true);
} else {
// Use CSS to position non-SVG component elements (e.g. <div/>).
d3.select(component.el)
// Use CSS `position: absolute;` so setting `left` and `top` CSS
// properties later will position the SVG relative to the parent container.
.style("position", "absolute")
// Set the CSS `left` and `top` properties to move the
// SVG to `(box.x, box.y)` relative to the parent container.
.style("left", box.x + "px")
.style("top", box.y + "px")
.classed(domClass, true);
}
}
});
});
});
// Computes which aliases are referenced in the given layout.
function aliasesInLayout(layout, sizes){
return Object.keys(computeLayout(layout, sizes, {
width: 100,
height: 100
}));
}
function removeAllChildren(parent){
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
}
// Handle DOM management for components.
my.when(["container", "layout", "sizes"], function(container, layout, sizes){
removeAllChildren(container);
var containerDIV = d3.select(container)
.append("div")
.attr("class", "chiasm-container-for-non-svg-elements");
var containerSVG = d3.select(container)
.append("svg")
.attr("class", "chiasm-container-for-svg-elements")
my.containerSVG = containerSVG;
// Add the DOM elements for each component to the container.
var aliases = aliasesInLayout(layout, sizes);
aliases.forEach(function (alias){
chiasm.getComponent(alias).then(function (component){
if(!component.el){
throw new Error("Every component referenced in layout must have a DOM " +
"element 'el' defined. Component with alias " + alias +
" has no 'el' property defined.");
}
if(component.el instanceof SVGGraphicsElement){
containerSVG.node().appendChild(component.el);
} else {
containerDIV.node().appendChild(component.el);
}
});
});
});
my.when(["containerSVG", "box"], function(containerSVG, box){
containerSVG
.attr("width", box.width)
.attr("height", box.height);
});
// Return the public API.
return my;
}
module.exports = Layout;