@lightningtv/solid 3.0.13 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,404 @@
1
+ import { type ElementNode } from './elementNode.js';
2
+ import { isTextNode, isElementText } from './utils.js';
3
+
4
+ function getArrayValue(
5
+ val: number | [number, number, number, number] | undefined,
6
+ index: number,
7
+ defaultValue: number = 0,
8
+ ): number {
9
+ if (val === undefined) return defaultValue;
10
+ if (typeof val === 'number') return val;
11
+ return val[index] || defaultValue;
12
+ }
13
+
14
+ export default function (node: ElementNode): boolean {
15
+ const direction = node.flexDirection || 'row';
16
+ const isRow = direction === 'row' || direction === 'row-reverse';
17
+ const isReverse =
18
+ direction === 'row-reverse' || direction === 'column-reverse';
19
+ const dimension = isRow ? 'width' : 'height';
20
+ const crossDimension = isRow ? 'height' : 'width';
21
+
22
+ // padding order: Top, Right, Bottom, Left
23
+ const nodePadding = node.padding;
24
+ const paddingTop = (node.paddingTop ??
25
+ getArrayValue(nodePadding, 0)) as number;
26
+ const paddingRight = (node.paddingRight ??
27
+ getArrayValue(nodePadding, 1)) as number;
28
+ const paddingBottom = (node.paddingBottom ??
29
+ getArrayValue(nodePadding, 2)) as number;
30
+ const paddingLeft = (node.paddingLeft ??
31
+ getArrayValue(nodePadding, 3)) as number;
32
+
33
+ const paddingStart = isRow ? paddingLeft : paddingTop;
34
+ const paddingEnd = isRow ? paddingRight : paddingBottom;
35
+ const paddingCrossStart = isRow ? paddingTop : paddingLeft;
36
+ const paddingCrossEnd = isRow ? paddingBottom : paddingRight;
37
+ const nodePaddingTotal = paddingStart + paddingEnd;
38
+
39
+ const minDimension = isRow ? 'minWidth' : 'minHeight';
40
+ const crossMinDimension = isRow ? 'minHeight' : 'minWidth';
41
+
42
+ const children = node.children;
43
+ const numChildren = children.length;
44
+
45
+ if (numChildren === 0) {
46
+ return false;
47
+ }
48
+
49
+ // Optimize arrays caching
50
+ let processableChildrenIndices: number[] = [];
51
+ let hasOrder = false;
52
+ let totalFlexGrow = 0;
53
+ let totalFlexShrink = 0;
54
+
55
+ for (let i = 0; i < numChildren; i++) {
56
+ const c = children[i]!;
57
+
58
+ if (isElementText(c) && c.text && !(c.width || c.height)) {
59
+ return false; // specific text layout constraint
60
+ }
61
+
62
+ if (isTextNode(c) || c.flexItem === false) {
63
+ continue;
64
+ }
65
+
66
+ if (c.flexOrder !== undefined) {
67
+ hasOrder = true;
68
+ }
69
+
70
+ const flexGrow = c.flexGrow;
71
+ if (flexGrow !== undefined && flexGrow > 0) {
72
+ totalFlexGrow += flexGrow;
73
+ }
74
+
75
+ const flexShrink = c.flexShrink;
76
+ if (flexShrink !== undefined && flexShrink > 0) {
77
+ totalFlexShrink += flexShrink;
78
+ }
79
+
80
+ if (c[minDimension] && (c[dimension] || 0) < c[minDimension]!) {
81
+ c[dimension] = c[minDimension]!;
82
+ }
83
+
84
+ if (
85
+ c[crossMinDimension] &&
86
+ (c[crossDimension] || 0) < c[crossMinDimension]!
87
+ ) {
88
+ c[crossDimension] = c[crossMinDimension]!;
89
+ }
90
+
91
+ processableChildrenIndices.push(i);
92
+ }
93
+
94
+ if (hasOrder) {
95
+ processableChildrenIndices.sort((aIdx, bIdx) => {
96
+ const a = children[aIdx] as ElementNode;
97
+ const b = children[bIdx] as ElementNode;
98
+ return (a.flexOrder || 0) - (b.flexOrder || 0);
99
+ });
100
+ }
101
+
102
+ // Apply reverse layout ordering
103
+ if (isReverse || node.direction === 'rtl') {
104
+ processableChildrenIndices.reverse();
105
+ }
106
+
107
+ const numProcessedChildren = processableChildrenIndices.length;
108
+ if (numProcessedChildren === 0) {
109
+ return false;
110
+ }
111
+
112
+ const prop = isRow ? 'x' : 'y';
113
+ const crossProp = isRow ? 'y' : 'x';
114
+ const containerSize = Math.max(
115
+ node[dimension] || 0,
116
+ node[minDimension] || 0,
117
+ 0,
118
+ );
119
+ let containerCrossSize = Math.max(
120
+ node[crossDimension] || 0,
121
+ node[crossMinDimension] || 0,
122
+ 0,
123
+ );
124
+ const isWrapReverse = node.flexWrap === 'wrap-reverse';
125
+ const gap = node.gap || 0;
126
+ const justify = node.justifyContent || 'flexStart';
127
+ const align = node.alignItems || (node.flexWrap ? 'flexStart' : undefined);
128
+ let containerUpdated = false;
129
+
130
+ // Resolve sizes matching old processed calculation
131
+ const childMainSizes = new Float32Array(numProcessedChildren);
132
+ const childMarginStarts = new Float32Array(numProcessedChildren);
133
+ const childMarginEnds = new Float32Array(numProcessedChildren);
134
+ const childTotalMainSizes = new Float32Array(numProcessedChildren);
135
+ const childCrossSizes = new Float32Array(numProcessedChildren);
136
+ const childMarginCrossStarts = new Float32Array(numProcessedChildren);
137
+ const childMarginCrossEnds = new Float32Array(numProcessedChildren);
138
+
139
+ let sumOfFlexBaseSizesWithMargins = 0;
140
+
141
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
142
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
143
+ const marginArray = c.margin;
144
+ // index mappings for margins: Top: 0, Right: 1, Bottom: 2, Left: 3
145
+ // if row: main = left/right (3/1),
146
+ const flexBasis = c.flexBasis;
147
+ const isBasisAuto = flexBasis === undefined || flexBasis === 'auto';
148
+ const computedBasis = isBasisAuto
149
+ ? c[dimension] || 0
150
+ : (flexBasis as number);
151
+ const baseMainSize = isBasisAuto
152
+ ? computedBasis
153
+ : Math.max(computedBasis, c[minDimension] || 0);
154
+
155
+ const marginStart = isRow
156
+ ? c.marginLeft || getArrayValue(marginArray, 3)
157
+ : c.marginTop || getArrayValue(marginArray, 0);
158
+ const marginEnd = isRow
159
+ ? c.marginRight || getArrayValue(marginArray, 1)
160
+ : c.marginBottom || getArrayValue(marginArray, 2);
161
+ const marginCrossStart = isRow
162
+ ? c.marginTop || getArrayValue(marginArray, 0)
163
+ : c.marginLeft || getArrayValue(marginArray, 3);
164
+ const marginCrossEnd = isRow
165
+ ? c.marginBottom || getArrayValue(marginArray, 2)
166
+ : c.marginRight || getArrayValue(marginArray, 1);
167
+
168
+ childMainSizes[idx] = baseMainSize;
169
+ childMarginStarts[idx] = marginStart;
170
+ childMarginEnds[idx] = marginEnd;
171
+ childTotalMainSizes[idx] = baseMainSize + marginStart + marginEnd;
172
+ childCrossSizes[idx] = c[crossDimension] || 0;
173
+ childMarginCrossStarts[idx] = marginCrossStart;
174
+ childMarginCrossEnds[idx] = marginCrossEnd;
175
+
176
+ sumOfFlexBaseSizesWithMargins += childTotalMainSizes[idx]!;
177
+ }
178
+
179
+ if ((totalFlexGrow > 0 || totalFlexShrink > 0) && numProcessedChildren > 1) {
180
+ node.flexBoundary = node.flexBoundary || 'fixed';
181
+
182
+ const totalGapSpace =
183
+ numProcessedChildren > 0 ? gap * (numProcessedChildren - 1) : 0;
184
+ const availableSpace =
185
+ containerSize - sumOfFlexBaseSizesWithMargins - totalGapSpace;
186
+
187
+ if (availableSpace > 0 && totalFlexGrow > 0) {
188
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
189
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
190
+ const flexGrowValue = c.flexGrow || 0;
191
+ if (flexGrowValue > 0) {
192
+ const shareOfSpace = (flexGrowValue / totalFlexGrow) * availableSpace;
193
+ const newMainSize = childMainSizes[idx]! + shareOfSpace;
194
+ c[dimension] = newMainSize;
195
+ childMainSizes[idx] = newMainSize;
196
+ childTotalMainSizes[idx] =
197
+ newMainSize + childMarginStarts[idx]! + childMarginEnds[idx]!;
198
+ }
199
+ }
200
+ node._containsFlexGrow = node._containsFlexGrow ? null : true;
201
+ } else if (availableSpace < 0 && totalFlexShrink > 0) {
202
+ // Flex Shrink Phase
203
+ let totalScaledShrinkFactor = 0;
204
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
205
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
206
+ const flexShrinkValue = c.flexShrink || 0;
207
+ totalScaledShrinkFactor += flexShrinkValue * childMainSizes[idx]!;
208
+ }
209
+
210
+ if (totalScaledShrinkFactor > 0) {
211
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
212
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
213
+ const flexShrinkValue = c.flexShrink || 0;
214
+ if (flexShrinkValue > 0) {
215
+ const shrinkRatio =
216
+ (flexShrinkValue * childMainSizes[idx]!) /
217
+ totalScaledShrinkFactor;
218
+ const sizeReduction = shrinkRatio * Math.abs(availableSpace);
219
+ let newMainSize = childMainSizes[idx]! - sizeReduction;
220
+
221
+ // Constrain by min width/height
222
+ const minBound = c[minDimension] || 0;
223
+ if (newMainSize < minBound) {
224
+ newMainSize = minBound;
225
+ }
226
+
227
+ c[dimension] = newMainSize;
228
+ childMainSizes[idx] = newMainSize;
229
+ childTotalMainSizes[idx] =
230
+ newMainSize + childMarginStarts[idx]! + childMarginEnds[idx]!;
231
+ }
232
+ }
233
+ }
234
+ node._containsFlexGrow = node._containsFlexGrow ? null : true;
235
+ } else if (node._containsFlexGrow) {
236
+ node._containsFlexGrow = null;
237
+ }
238
+ }
239
+
240
+ let totalItemSize = 0;
241
+ if (
242
+ justify === 'center' ||
243
+ justify === 'spaceBetween' ||
244
+ justify === 'spaceEvenly' ||
245
+ justify === 'spaceAround'
246
+ ) {
247
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
248
+ totalItemSize += childTotalMainSizes[idx]!;
249
+ }
250
+ }
251
+
252
+ const doCrossAlign = containerCrossSize
253
+ ? (c: ElementNode, idx: number, crossCurrentPos: number = 0) => {
254
+ const alignSelf = c.alignSelf || align;
255
+ if (!alignSelf) {
256
+ return;
257
+ }
258
+ if (alignSelf === 'flexStart') {
259
+ c[crossProp] = crossCurrentPos + childMarginCrossStarts[idx]!;
260
+ } else if (alignSelf === 'center') {
261
+ c[crossProp] =
262
+ crossCurrentPos +
263
+ (containerCrossSize - childCrossSizes[idx]!) / 2 +
264
+ childMarginCrossStarts[idx]!;
265
+ } else if (alignSelf === 'flexEnd') {
266
+ c[crossProp] =
267
+ crossCurrentPos +
268
+ containerCrossSize -
269
+ childCrossSizes[idx]! -
270
+ childMarginCrossEnds[idx]!;
271
+ }
272
+ }
273
+ : (_c: ElementNode, _idx: number, _crossCurrentPos: number = 0) => {
274
+ /* no-op */
275
+ };
276
+
277
+ if (isRow && node._calcHeight && !node.flexCrossBoundary) {
278
+ let maxHeight = 0;
279
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
280
+ if (childCrossSizes[idx]! > maxHeight) maxHeight = childCrossSizes[idx]!;
281
+ }
282
+ const newHeight = maxHeight || node.height;
283
+ if (newHeight !== node.height) {
284
+ containerUpdated = true;
285
+ node.height = containerCrossSize = newHeight;
286
+ }
287
+ }
288
+
289
+ let currentPos = paddingStart;
290
+ if (justify === 'flexStart') {
291
+ if (node.flexWrap === 'wrap') {
292
+ const childCrossSizeVar =
293
+ numProcessedChildren > 0 ? childCrossSizes[0]! : containerCrossSize;
294
+ let crossCurrentPos = isWrapReverse
295
+ ? containerCrossSize - paddingCrossEnd - childCrossSizeVar
296
+ : paddingCrossStart;
297
+ const crossGap = isRow ? (node.columnGap ?? gap) : (node.rowGap ?? gap);
298
+
299
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
300
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
301
+ if (
302
+ currentPos + childTotalMainSizes[idx]! > containerSize &&
303
+ currentPos > paddingStart
304
+ ) {
305
+ currentPos = paddingStart;
306
+ crossCurrentPos += isWrapReverse
307
+ ? -(childCrossSizeVar + crossGap)
308
+ : childCrossSizeVar + crossGap;
309
+ }
310
+ c[prop] = currentPos + childMarginStarts[idx]!;
311
+ currentPos += childTotalMainSizes[idx]! + gap;
312
+ doCrossAlign(c, idx, crossCurrentPos);
313
+ }
314
+
315
+ const finalCrossSize = isWrapReverse
316
+ ? containerCrossSize - crossCurrentPos + paddingCrossStart
317
+ : crossCurrentPos + childCrossSizeVar + paddingCrossEnd;
318
+
319
+ if (node[crossDimension] !== finalCrossSize) {
320
+ node[`preFlex${crossDimension}`] = node[crossDimension];
321
+ node[crossDimension] = finalCrossSize;
322
+ containerUpdated = true;
323
+ }
324
+ } else {
325
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
326
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
327
+ c[prop] = currentPos + childMarginStarts[idx]!;
328
+ currentPos += childTotalMainSizes[idx]! + gap;
329
+ doCrossAlign(c, idx, paddingCrossStart);
330
+ }
331
+ }
332
+
333
+ // Update container size
334
+ if (node.flexBoundary !== 'fixed' && node.flexWrap !== 'wrap') {
335
+ let calculatedSize = currentPos - gap + paddingEnd;
336
+ const minSize = node[minDimension] || 0;
337
+ if (calculatedSize < minSize) {
338
+ calculatedSize = minSize;
339
+ }
340
+ if (calculatedSize !== (node[dimension] || 0)) {
341
+ node[`preFlex${dimension}`] = containerSize;
342
+ node[dimension] = calculatedSize;
343
+ return true;
344
+ }
345
+ }
346
+ } else if (justify === 'flexEnd') {
347
+ currentPos = containerSize - paddingEnd;
348
+ for (let idx = numProcessedChildren - 1; idx >= 0; idx--) {
349
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
350
+ c[prop] = currentPos - childMainSizes[idx]! - childMarginEnds[idx]!;
351
+ currentPos -= childTotalMainSizes[idx]! + gap;
352
+ doCrossAlign(c, idx, paddingCrossStart);
353
+ }
354
+ } else if (justify === 'center') {
355
+ currentPos =
356
+ (containerSize - (totalItemSize + gap * (numProcessedChildren - 1))) / 2 +
357
+ paddingStart;
358
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
359
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
360
+ c[prop] = currentPos + childMarginStarts[idx]!;
361
+ currentPos += childTotalMainSizes[idx]! + gap;
362
+ doCrossAlign(c, idx, paddingCrossStart);
363
+ }
364
+ } else if (justify === 'spaceBetween') {
365
+ const spaceBetween =
366
+ numProcessedChildren > 1
367
+ ? (containerSize - totalItemSize - nodePaddingTotal) /
368
+ (numProcessedChildren - 1)
369
+ : 0;
370
+ currentPos = paddingStart;
371
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
372
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
373
+ c[prop] = currentPos + childMarginStarts[idx]!;
374
+ currentPos += childTotalMainSizes[idx]! + spaceBetween;
375
+ doCrossAlign(c, idx, paddingCrossStart);
376
+ }
377
+ } else if (justify === 'spaceAround') {
378
+ const spaceAround =
379
+ numProcessedChildren > 0
380
+ ? (containerSize - totalItemSize - nodePaddingTotal) /
381
+ numProcessedChildren
382
+ : 0;
383
+ currentPos = paddingStart + spaceAround / 2;
384
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
385
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
386
+ c[prop] = currentPos + childMarginStarts[idx]!;
387
+ currentPos += childTotalMainSizes[idx]! + spaceAround;
388
+ doCrossAlign(c, idx, paddingCrossStart);
389
+ }
390
+ } else if (justify === 'spaceEvenly') {
391
+ const spaceEvenly =
392
+ (containerSize - totalItemSize - nodePaddingTotal) /
393
+ (numProcessedChildren + 1);
394
+ currentPos = spaceEvenly + paddingStart;
395
+ for (let idx = 0; idx < numProcessedChildren; idx++) {
396
+ const c = children[processableChildrenIndices[idx]!] as ElementNode;
397
+ c[prop] = currentPos + childMarginStarts[idx]!;
398
+ currentPos += childTotalMainSizes[idx]! + spaceEvenly;
399
+ doCrossAlign(c, idx, paddingCrossStart);
400
+ }
401
+ }
402
+
403
+ return containerUpdated;
404
+ }