Skip to content

Commit 56c0f4d

Browse files
authored
Add files via upload
1 parent 24a364f commit 56c0f4d

File tree

2 files changed

+437
-0
lines changed

2 files changed

+437
-0
lines changed

layout.js

+380
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
registerLayout('test', class {
2+
static get inputProperties() {
3+
return [ '--padding', '--columns' ];
4+
}
5+
static get childInputProperties() {
6+
return [ ];
7+
}
8+
9+
*intrinsicSizes() {}
10+
*layout(children, edges, constraints, styleMap) {
11+
const inlineSize = constraints.fixedInlineSize;
12+
13+
const padding = parseInt(styleMap.get('--padding').toString());
14+
const columnValue = styleMap.get('--columns').toString();
15+
16+
let columns = parseInt(columnValue);
17+
if (columnValue == 'auto' || !columns) {
18+
columns = Math.ceil(inlineSize / 350); // MAGIC.
19+
}
20+
21+
const childInlineSize = (inlineSize - ((columns + 1) * padding)) / columns;
22+
const childFragments = yield children.map((child) => {
23+
return child.layoutNextFragment({fixedInlineSize: childInlineSize});
24+
});
25+
26+
let autoBlockSize = 0;
27+
const columnOffsets = Array(columns).fill(0);
28+
for (let childFragment of childFragments) {
29+
const min = columnOffsets.reduce((acc, val, idx) => {
30+
if (!acc || val < acc.val) {
31+
return {idx, val};
32+
}
33+
34+
return acc;
35+
}, {val: +Infinity, idx: -1});
36+
37+
childFragment.inlineOffset = padding + (childInlineSize + padding) * min.idx;
38+
childFragment.blockOffset = padding + min.val;
39+
40+
columnOffsets[min.idx] = childFragment.blockOffset + childFragment.blockSize;
41+
autoBlockSize = Math.max(autoBlockSize, columnOffsets[min.idx] + padding);
42+
}
43+
44+
return {autoBlockSize, childFragments};
45+
}
46+
});
47+
48+
registerLayout('simple-block', class {
49+
*intrinsicSizes() {}
50+
*layout(children, _, constraints) {
51+
const childFragments = yield children.map((child) => {
52+
return child.layoutNextFragment({fixedInlineSize: constraints.fixedInlineSize});
53+
});
54+
55+
let autoBlockSize = 0;
56+
for (let childFragment of childFragments) {
57+
childFragment.blockOffset = autoBlockSize;
58+
autoBlockSize += childFragment.blockSize;
59+
}
60+
61+
return {autoBlockSize, childFragments};
62+
}
63+
});
64+
65+
function normalizeOperator(operator) {
66+
switch (operator) {
67+
case 'above':
68+
return 'above';
69+
case 'below':
70+
return 'below';
71+
case 'left-of':
72+
case 'leftOf':
73+
return 'leftOf';
74+
case 'right-of':
75+
case 'rightOf':
76+
return 'rightOf';
77+
case 'align-top':
78+
case 'alignTop':
79+
return 'alignTop';
80+
case 'align-bottom':
81+
case 'alignBottom':
82+
return 'alignBottom';
83+
case 'align-left':
84+
case 'alignLeft':
85+
return 'alignLeft';
86+
case 'align-right':
87+
case 'alignRight':
88+
return 'alignRight';
89+
case 'align-parent-top':
90+
case 'alignParentTop':
91+
return 'alignParentTop';
92+
case 'align-parent-bottom':
93+
case 'alignParentBottom':
94+
return 'alignParentBottom';
95+
case 'align-parent-left':
96+
case 'alignParentLeft':
97+
return 'alignParentLeft';
98+
case 'align-parent-right':
99+
case 'alignParentRight':
100+
return 'alignParentRight';
101+
case 'center-horizontal':
102+
case 'centerHorizontal':
103+
return 'centerHorizontal';
104+
case 'center-vertical':
105+
case 'centerVertical':
106+
return 'centerVertical';
107+
default:
108+
return null;
109+
}
110+
}
111+
112+
function parseRelativeConstraints(str) {
113+
const constraintList = str.split(',').map(str => str.trim().split(' ').map(str2 => str2.trim()));
114+
const relativeConstraints = {};
115+
116+
for (let constraint of constraintList) {
117+
if (constraint.length === 3 || constraint.length === 2) {
118+
const [target, op, dest] = constraint;
119+
120+
if (!relativeConstraints[target])
121+
relativeConstraints[target] = {};
122+
123+
const operator = normalizeOperator(op);
124+
if (!operator)
125+
continue;
126+
127+
relativeConstraints[target][normalizeOperator(op)] = dest || true;
128+
}
129+
}
130+
131+
return relativeConstraints;
132+
}
133+
134+
function sortChildren(relativeConstraints, childNames, mode) {
135+
const predecessorsByChild = {};
136+
for (let childName of childNames) {
137+
predecessorsByChild[childName] = mode === 'vertical' ?
138+
getPredecessorsVertical(relativeConstraints, childName) :
139+
getPredecessorsHorizontal(relativeConstraints, childName);
140+
}
141+
142+
let sortedSoFar = 0;
143+
let changed = true;
144+
145+
while (changed) {
146+
changed = false;
147+
148+
for (let i = sortedSoFar; i < childNames.length; i++) {
149+
const childName = childNames[i];
150+
const predecessors = predecessorsByChild[childName];
151+
if (predecessors.length === 0) {
152+
// Move element into sorted part of the list.
153+
if (i !== sortedSoFar) {
154+
const tmp = childNames[i];
155+
childNames[i] = childNames[sortedSoFar];
156+
childNames[sortedSoFar] = tmp;
157+
}
158+
159+
sortedSoFar++;
160+
changed = true;
161+
162+
// Remove this as a predecessor.
163+
for (let j = sortedSoFar; j < childNames.length; j++) {
164+
const l = predecessorsByChild[childNames[j]];
165+
const idx = l.indexOf(childName);
166+
if (idx >= 0)
167+
l.splice(idx, 1);
168+
}
169+
}
170+
}
171+
}
172+
173+
if (sortedSoFar < childNames.length) {
174+
throw Error("Cycle in dependency graph.");
175+
}
176+
}
177+
178+
function getPredecessorsVertical(relativeConstraints, childName) {
179+
let predecessors = [];
180+
181+
const c = relativeConstraints[childName] || {};
182+
if (c.above)
183+
predecessors.push(c.above);
184+
185+
if (c.below)
186+
predecessors.push(c.below);
187+
188+
if (c.alignTop)
189+
predecessors.push(c.alignTop);
190+
191+
if (c.alignBottom)
192+
predecessors.push(c.alignBottom);
193+
194+
return predecessors;
195+
}
196+
197+
function getPredecessorsHorizontal(relativeConstraints, childName) {
198+
let predecessors = [];
199+
200+
const c = relativeConstraints[childName] || {};
201+
if (c.leftOf)
202+
predecessors.push(c.leftOf);
203+
204+
if (c.rightOf)
205+
predecessors.push(c.rightOf);
206+
207+
if (c.alignLeft)
208+
predecessors.push(c.alignLeft);
209+
210+
if (c.alignRight)
211+
predecessors.push(c.alignRight);
212+
213+
return predecessors;
214+
}
215+
216+
function applyHorizontalRules(relativeConstraints, childPositions, childName, inlineSize) {
217+
const position = childPositions[childName];
218+
position.left = -1;
219+
position.right = -1;
220+
221+
const c = relativeConstraints[childName] || {};
222+
if (c.leftOf && childPositions[c.leftOf])
223+
position.right = childPositions[c.leftOf].left;
224+
225+
if (c.rightOf && childPositions[c.rightOf])
226+
position.left = childPositions[c.rightOf].right;
227+
228+
if (c.alignLeft && childPositions[c.alignLeft])
229+
position.left = childPositions[c.alignLeft].left
230+
231+
if (c.alignRight && childPositions[c.alignRight])
232+
position.right = childPositions[c.alignRight].right;
233+
234+
if (c.alignParentLeft)
235+
position.left = 0;
236+
237+
if (c.alignParentRight)
238+
position.right = inlineSize;
239+
}
240+
241+
function applyVerticalRules(relativeConstraints, childPositions, childName, blockSize) {
242+
const position = childPositions[childName];
243+
position.top = -1;
244+
position.bottom = -1;
245+
246+
const c = relativeConstraints[childName] || {};
247+
if (c.above && childPositions[c.above])
248+
position.bottom = childPositions[c.above].top;
249+
250+
if (c.below && childPositions[c.below])
251+
position.top = childPositions[c.below].bottom;
252+
253+
if (c.alignTop && childPositions[c.alignTop])
254+
position.top = childPositions[c.alignTop].top
255+
256+
if (c.alignBottom && childPositions[c.alignBottom])
257+
position.bottom = childPositions[c.alignBottom].bottom;
258+
259+
if (c.alignParentTop)
260+
position.top = 0;
261+
262+
if (c.alignParentBottom && blockSize !== null)
263+
position.bottom = blockSize;
264+
}
265+
266+
function* measureChildHorizontal(child, position) {
267+
let childInlineSize = 0;
268+
if (position.left >= 0 && position.right >= 0) {
269+
childInlineSize = Math.max(0, position.right - position.left);
270+
} else {
271+
childInlineSize = (yield child.layoutNextFragment({})).inlineSize;
272+
}
273+
274+
return childInlineSize;
275+
}
276+
277+
function* measureChild(child, position) {
278+
const childConstraints = {};
279+
280+
if (position.top >= 0 && position.bottom >= 0)
281+
childConstraints.fixedBlockSize = Math.max(0, position.bottom - position.top);
282+
283+
if (position.left >= 0 && position.right >= 0)
284+
childConstraints.fixedInlineSize = Math.max(0, position.right - position.left);
285+
286+
return yield child.layoutNextFragment(childConstraints);
287+
}
288+
289+
function positionChildHorizontal(position, constraint, inlineSize, childInlineSize) {
290+
if (position.left < 0 && position.right >= 0) {
291+
// Right fixed, left unspecified.
292+
position.left = position.right - childInlineSize;
293+
} else if (position.left >= 0 && position.right < 0) {
294+
position.right = position.left + childInlineSize;
295+
} else if (position.left < 0 && position.right < 0) {
296+
// Both unspecified.
297+
const c = constraint || {};
298+
if (c.centerHorizontal) {
299+
position.left = (inlineSize - childInlineSize) / 2;
300+
position.right = position.left + childInlineSize;
301+
} else {
302+
position.left = 0;
303+
position.right = childInlineSize;
304+
}
305+
}
306+
}
307+
308+
function positionChildVertical(position, cosntraint, blockSize, childBlockSize) {
309+
if (position.top < 0 && position.bottom >= 0) {
310+
position.top = position.bottom - childBlockSize;
311+
} else if (position.top >= 0 && position.bottom < 0) {
312+
position.bottom = position.top + childBlockSize;
313+
} else {
314+
// TODO implement once blockSize is not null.
315+
position.top = 0;
316+
position.bottom = childBlockSize;
317+
}
318+
}
319+
320+
registerLayout('relative', class {
321+
static get inputProperties() { return [ '--relative-constraints', ]; }
322+
static get childInputProperties() { return [ '--relative-name', ]; }
323+
324+
*intrinsicSizes() {}
325+
326+
*layout(children, edges, constraints, styleMap) {
327+
const relativeConstraints = parseRelativeConstraints(styleMap.get('--relative-constraints').toString());
328+
329+
const childrenMap = children.reduce((map, child) => {
330+
const val = child.styleMap.get('--relative-name');
331+
if (val && val.toString() !== '') {
332+
child.name = val.toString().trim();
333+
map[child.name] = child;
334+
}
335+
return map;
336+
}, {});
337+
338+
const childPositions = Object.keys(childrenMap).reduce((map, key) => {
339+
map[key] = {};
340+
return map;
341+
}, {});
342+
343+
const sortedChildNamesHorizontal = Object.keys(childrenMap);
344+
const sortedChildNamesVertical = Object.keys(childrenMap);
345+
sortChildren(relativeConstraints, sortedChildNamesHorizontal, 'horizontal');
346+
sortChildren(relativeConstraints, sortedChildNamesVertical, 'vertical');
347+
348+
for (let childName of sortedChildNamesHorizontal) {
349+
applyHorizontalRules(relativeConstraints, childPositions, childName, constraints.fixedInlineSize);
350+
const childInlineSize = yield* measureChildHorizontal(childrenMap[childName], childPositions[childName]);
351+
positionChildHorizontal(childPositions[childName], relativeConstraints[childName], constraints.fixedInlineSize, childInlineSize);
352+
}
353+
354+
const childFragmentMap = {};
355+
for (let childName of sortedChildNamesVertical) {
356+
applyVerticalRules(relativeConstraints, childPositions, childName, null);
357+
const fragment = yield* measureChild(childrenMap[childName], childPositions[childName]);
358+
childFragmentMap[childName] = fragment;
359+
positionChildVertical(childPositions[childName], relativeConstraints[childName], null, fragment.blockSize);
360+
}
361+
362+
let autoBlockSize = 0;
363+
const childFragments = [];
364+
for (let i = 0; i < children.length; i++) {
365+
const childName = children[i].name;
366+
const fragment = childName ? childFragmentMap[childName] : (yield children[i].layoutNextFragment({}));
367+
childFragments.push(fragment);
368+
369+
if (childName) {
370+
const position = childPositions[childName];
371+
fragment.inlineOffset = position.left;
372+
fragment.blockOffset = position.top;
373+
}
374+
375+
autoBlockSize = Math.max(autoBlockSize, fragment.blockOffset + fragment.blockSize);
376+
}
377+
378+
return {autoBlockSize, childFragments};
379+
}
380+
});

0 commit comments

Comments
 (0)