-
Notifications
You must be signed in to change notification settings - Fork 24
/
layout.js
142 lines (118 loc) · 3.87 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
// This plugin uses the computeLayout module
// to assign sizes to visible components.
//
// Draws from previous work found at
// https://github.com/curran/model-contrib/blob/gh-pages/modules/boxes.js
//
// By Curran Kelleher June 2015
var computeLayout = require("./computeLayout");
var Model = require("model-js");
var _ = require("lodash");
// The layout Chiasm plugin constructor function.
function Layout(chiasm){
// The public API object returned by the constructor function.
var model = Model({
publicProperties: ["layout"],
layout: {}
});
// Sets the `box` model property based on actual container size .
function setBox(){
model.box = {
x: 0,
y: 0,
width: chiasm.container.clientWidth,
height: chiasm.container.clientHeight
};
}
// Initialize `model.box`.
setBox();
// Update `model.box` on resize
window.addEventListener("resize", setBox);
model.destroy = function(){
window.removeEventListener("resize", setBox);
};
// Respond to changes is box and layout.
model.when(["layout", "sizes", "box"], function(layout, sizes, box){
// Compute the layout.
var boxes = computeLayout(layout, sizes, box);
// Apply the layout via the `box` property of components.
Object.keys(boxes).forEach(function(alias){
chiasm.getComponent(alias).then(function(component){
component.box = boxes[alias];
});
});
});
// Compute `sizes` from chiasm components.
model.when(["layout"], function(layout){
// Extract the list of aliases referenced in the layout.
var aliases = aliasesInLayout(layout);
// Set sizes once initially.
extractSizes(aliases);
// Set sizes when the "size" property changes on any component.
aliases.forEach(function(alias){
chiasm.getComponent(alias).then(function(component){
// TODO clean up listeners, test for leaks.
// TODO bubble errors to UI
component.when("size", function(size){
extractSizes(aliases);
});
});
});
});
// Sets `model.sizes` by extracting the "size" and "hidden"
// properties component corresponding to each alias in `aliases`.
function extractSizes(aliases){
// Compute which component aliases are referenced.
var sizes = {};
// For each alias referenced in the layout,
Promise.all(aliases.map(function(alias){
return new Promise(function(resolve, reject){
chiasm.getComponent(alias).then(function(component){
// store its "size" and "hidden" properties.
if(component.size || component.hidden){
sizes[alias] = {};
if(component.size){
sizes[alias].size = component.size;
}
if(component.hidden){
// TODO test this line
sizes[alias].hidden = component.hidden;
}
}
resolve();
}, reject);
});
})).then(function(){
// Set the stored "size" and "hidden" properties
// on the model to trigger the layout computation.
if(!_.isEqual(model.sizes, sizes)){
model.sizes = sizes;
}
}, function(err){
// Throw the error so it can be seen in a Node environment.
throw err;
});
}
// Computes which aliases are referenced in the given layout.
function aliasesInLayout(layout){
var aliases = [];
if(isLeafNode(layout)){
aliases.push(layout);
} else {
layout.children.forEach(function(child){
aliases.push.apply(aliases, aliasesInLayout(child));
});
}
return aliases;
}
// Determines whether the given node in the layout tree
// is a leaf node or a non-leaf node.
function isLeafNode(layout){
// If it is a leaf node, then it is a string
// that is interpreted as a component alias.
return typeof layout === "string";
}
// Return the public API.
return model;
}
module.exports = Layout;