-
Notifications
You must be signed in to change notification settings - Fork 146
/
Copy pathBoxComponent.js
211 lines (132 loc) · 4.73 KB
/
BoxComponent.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/**
Job: Handle everything related to a BoxComponent element dimensioning and positioning
Knows: Parents and children dimensions and positions
It's worth noting that in three-mesh-ui, it's the parent Block that computes
its children position. A Block can only have either only box components (Block)
as children, or only inline components (Text, InlineBlock).
*/
import { COLUMN, COLUMN_REVERSE, contentDirection, ROW, ROW_REVERSE } from '../../utils/block-layout/ContentDirection';
import { alignItems } from '../../utils/block-layout/AlignItems';
import { justifyContent } from '../../utils/block-layout/JustifyContent';
export default function BoxComponent( Base ) {
return class BoxComponent extends Base {
constructor( options ) {
super( options );
this.isBoxComponent = true;
this.childrenPos = {};
}
/** Get width of this component minus its padding */
getInnerWidth() {
const DIRECTION = this.getContentDirection();
switch ( DIRECTION ) {
case 'row' :
case 'row-reverse' :
return this.width - ( this.padding * 2 || 0 ) || this.getChildrenSideSum( 'width' );
case 'column' :
case 'column-reverse' :
return this.getHighestChildSizeOn( 'width' );
default :
console.error( `Invalid contentDirection : ${DIRECTION}` );
break;
}
}
/** Get height of this component minus its padding */
getInnerHeight() {
const DIRECTION = this.getContentDirection();
switch ( DIRECTION ) {
case 'row' :
case 'row-reverse' :
return this.getHighestChildSizeOn( 'height' );
case 'column' :
case 'column-reverse' :
return this.height - ( this.padding * 2 || 0 ) || this.getChildrenSideSum( 'height' );
default :
console.error( `Invalid contentDirection : ${DIRECTION}` );
break;
}
}
/** Return the sum of all this component's children sides + their margin */
getChildrenSideSum( dimension ) {
return this.childrenBoxes.reduce( ( accu, child ) => {
const margin = ( child.margin * 2 ) || 0;
const CHILD_SIZE = ( dimension === 'width' ) ?
( child.getWidth() + margin ) :
( child.getHeight() + margin );
return accu + CHILD_SIZE;
}, 0 );
}
/** Look in parent record what is the instructed position for this component, then set its position */
setPosFromParentRecords() {
if ( this.parentUI && this.parentUI.childrenPos[ this.id ] ) {
this.position.x = ( this.parentUI.childrenPos[ this.id ].x );
this.position.y = ( this.parentUI.childrenPos[ this.id ].y );
}
}
/** Position inner elements according to dimensions and layout parameters. */
computeChildrenPosition() {
if ( this.children.length > 0 ) {
const DIRECTION = this.getContentDirection();
let directionalOffset;
switch ( DIRECTION ) {
case ROW :
directionalOffset = - this.getInnerWidth() / 2;
break;
case ROW_REVERSE :
directionalOffset = this.getInnerWidth() / 2;
break;
case COLUMN :
directionalOffset = this.getInnerHeight() / 2;
break;
case COLUMN_REVERSE :
directionalOffset = - this.getInnerHeight() / 2;
break;
}
const REVERSE = - Math.sign( directionalOffset );
contentDirection(this, DIRECTION, directionalOffset, REVERSE );
justifyContent(this, DIRECTION, directionalOffset, REVERSE );
alignItems( this, DIRECTION );
}
}
/**
* Returns the highest linear dimension among all the children of the passed component
* MARGIN INCLUDED
*/
getHighestChildSizeOn( direction ) {
return this.childrenBoxes.reduce( ( accu, child ) => {
const margin = child.margin || 0;
const maxSize = direction === 'width' ?
child.getWidth() + ( margin * 2 ) :
child.getHeight() + ( margin * 2 );
return Math.max( accu, maxSize );
}, 0 );
}
/**
* Get width of this element
* With padding, without margin
*/
getWidth() {
// This is for stretch alignment
// @TODO : Conceive a better performant way
if( this.parentUI && this.parentUI.getAlignItems() === 'stretch' ){
if( this.parentUI.getContentDirection().indexOf('column') !== -1 ){
return this.parentUI.getWidth() - ( this.parentUI.padding * 2 || 0 );
}
}
return this.width || this.getInnerWidth() + ( this.padding * 2 || 0 );
}
/**
* Get height of this element
* With padding, without margin
*/
getHeight() {
// This is for stretch alignment
// @TODO : Conceive a better performant way
if( this.parentUI && this.parentUI.getAlignItems() === 'stretch' ){
if( this.parentUI.getContentDirection().indexOf('row') !== -1 ){
return this.parentUI.getHeight() - ( this.parentUI.padding * 2 || 0 );
}
}
return this.height || this.getInnerHeight() + ( this.padding * 2 || 0 );
}
};
}