@snailycfx/stylesheet 1.0.10
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.
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/out/core/engine/absolute.d.ts +10 -0
- package/out/core/engine/absolute.d.ts.map +1 -0
- package/out/core/engine/absolute.luau +86 -0
- package/out/core/engine/flex.d.ts +14 -0
- package/out/core/engine/flex.d.ts.map +1 -0
- package/out/core/engine/flex.luau +284 -0
- package/out/core/engine/grid.d.ts +9 -0
- package/out/core/engine/grid.d.ts.map +1 -0
- package/out/core/engine/grid.luau +306 -0
- package/out/core/engine/index.d.ts +14 -0
- package/out/core/engine/index.d.ts.map +1 -0
- package/out/core/engine/init.luau +179 -0
- package/out/core/engine/node.d.ts +25 -0
- package/out/core/engine/node.d.ts.map +1 -0
- package/out/core/engine/node.luau +84 -0
- package/out/core/index.d.ts +16 -0
- package/out/core/index.d.ts.map +1 -0
- package/out/core/init.luau +21 -0
- package/out/core/parse/color.d.ts +3 -0
- package/out/core/parse/color.d.ts.map +1 -0
- package/out/core/parse/color.luau +22 -0
- package/out/core/parse/grid.d.ts +18 -0
- package/out/core/parse/grid.d.ts.map +1 -0
- package/out/core/parse/grid.luau +158 -0
- package/out/core/parse/index.d.ts +5 -0
- package/out/core/parse/index.d.ts.map +1 -0
- package/out/core/parse/init.luau +13 -0
- package/out/core/parse/udim.d.ts +5 -0
- package/out/core/parse/udim.d.ts.map +1 -0
- package/out/core/parse/udim.luau +53 -0
- package/out/core/types/RbxStyle.d.ts +17 -0
- package/out/core/types/RbxStyle.d.ts.map +1 -0
- package/out/core/types/RbxStyle.luau +2 -0
- package/out/core/types/index.d.ts +11 -0
- package/out/core/types/index.d.ts.map +1 -0
- package/out/core/types/init.luau +2 -0
- package/out/core/types/primitives.d.ts +7 -0
- package/out/core/types/primitives.d.ts.map +1 -0
- package/out/core/types/primitives.luau +2 -0
- package/out/core/types/style/background.d.ts +10 -0
- package/out/core/types/style/background.d.ts.map +1 -0
- package/out/core/types/style/background.luau +2 -0
- package/out/core/types/style/border.d.ts +8 -0
- package/out/core/types/style/border.d.ts.map +1 -0
- package/out/core/types/style/border.luau +2 -0
- package/out/core/types/style/index.d.ts +9 -0
- package/out/core/types/style/index.d.ts.map +1 -0
- package/out/core/types/style/init.luau +2 -0
- package/out/core/types/style/layout.d.ts +23 -0
- package/out/core/types/style/layout.d.ts.map +1 -0
- package/out/core/types/style/layout.luau +2 -0
- package/out/core/types/style/misc.d.ts +7 -0
- package/out/core/types/style/misc.d.ts.map +1 -0
- package/out/core/types/style/misc.luau +2 -0
- package/out/core/types/style/position.d.ts +11 -0
- package/out/core/types/style/position.d.ts.map +1 -0
- package/out/core/types/style/position.luau +2 -0
- package/out/core/types/style/size.d.ts +11 -0
- package/out/core/types/style/size.d.ts.map +1 -0
- package/out/core/types/style/size.luau +2 -0
- package/out/core/types/style/spacing.d.ts +21 -0
- package/out/core/types/style/spacing.d.ts.map +1 -0
- package/out/core/types/style/spacing.luau +2 -0
- package/out/core/types/style/text.d.ts +14 -0
- package/out/core/types/style/text.d.ts.map +1 -0
- package/out/core/types/style/text.luau +2 -0
- package/out/core/writer/index.d.ts +8 -0
- package/out/core/writer/index.d.ts.map +1 -0
- package/out/core/writer/init.luau +505 -0
- package/out/elements/index.d.ts +44 -0
- package/out/elements/index.d.ts.map +1 -0
- package/out/elements/init.luau +316 -0
- package/out/index.d.ts +11 -0
- package/out/index.d.ts.map +1 -0
- package/out/init.luau +19 -0
- package/out/stylesheet/index.d.ts +25 -0
- package/out/stylesheet/index.d.ts.map +1 -0
- package/out/stylesheet/init.luau +71 -0
- package/package.json +37 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _parse = TS.import(script, script.Parent.Parent, "parse")
|
|
4
|
+
local parseGridTemplate = _parse.parseGridTemplate
|
|
5
|
+
local resolveTrackSizes = _parse.resolveTrackSizes
|
|
6
|
+
local parseGridPlacement = _parse.parseGridPlacement
|
|
7
|
+
local resolvePixels = TS.import(script, script.Parent.Parent, "parse").resolvePixels
|
|
8
|
+
local function computeGridLayout(node, containerWidth, containerHeight)
|
|
9
|
+
local style = node.style
|
|
10
|
+
local _condition = style.gridTemplateColumns
|
|
11
|
+
if _condition == nil then
|
|
12
|
+
_condition = "1fr"
|
|
13
|
+
end
|
|
14
|
+
local colTemplate = _condition
|
|
15
|
+
local rowTemplate = style.gridTemplateRows
|
|
16
|
+
local _condition_1 = style.columnGap
|
|
17
|
+
if _condition_1 == nil then
|
|
18
|
+
_condition_1 = style.gap
|
|
19
|
+
if _condition_1 == nil then
|
|
20
|
+
_condition_1 = 0
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
local colGapPx = resolvePixels(_condition_1, containerWidth)
|
|
24
|
+
local _condition_2 = style.rowGap
|
|
25
|
+
if _condition_2 == nil then
|
|
26
|
+
_condition_2 = style.gap
|
|
27
|
+
if _condition_2 == nil then
|
|
28
|
+
_condition_2 = 0
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
local rowGapPx = resolvePixels(_condition_2, containerHeight)
|
|
32
|
+
local colSizes = resolveTrackSizes(parseGridTemplate(colTemplate), containerWidth, colGapPx)
|
|
33
|
+
local numCols = #colSizes
|
|
34
|
+
-- Solo hijos en flujo
|
|
35
|
+
local inFlow = {}
|
|
36
|
+
for _, child in node.children do
|
|
37
|
+
if child.style.position ~= "absolute" then
|
|
38
|
+
table.insert(inFlow, child)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
-- ── Matriz de ocupación ───────────────────────────────────────────────────
|
|
42
|
+
-- Usamos tabla plana en lugar de tabla de tablas para menos GC
|
|
43
|
+
-- Key: row * 1000 + col (asume max 1000 columnas, suficiente para UI)
|
|
44
|
+
local occupied = {}
|
|
45
|
+
local function isOccupied(col, row)
|
|
46
|
+
local _arg0 = row * 1000 + col
|
|
47
|
+
return occupied[_arg0] == true
|
|
48
|
+
end
|
|
49
|
+
local function occupy(col, row, colSpan, rowSpan)
|
|
50
|
+
do
|
|
51
|
+
local r = row
|
|
52
|
+
local _shouldIncrement = false
|
|
53
|
+
while true do
|
|
54
|
+
if _shouldIncrement then
|
|
55
|
+
r += 1
|
|
56
|
+
else
|
|
57
|
+
_shouldIncrement = true
|
|
58
|
+
end
|
|
59
|
+
if not (r < row + rowSpan) then
|
|
60
|
+
break
|
|
61
|
+
end
|
|
62
|
+
do
|
|
63
|
+
local c = col
|
|
64
|
+
local _shouldIncrement_1 = false
|
|
65
|
+
while true do
|
|
66
|
+
if _shouldIncrement_1 then
|
|
67
|
+
c += 1
|
|
68
|
+
else
|
|
69
|
+
_shouldIncrement_1 = true
|
|
70
|
+
end
|
|
71
|
+
if not (c < col + colSpan) then
|
|
72
|
+
break
|
|
73
|
+
end
|
|
74
|
+
local _arg0 = r * 1000 + c
|
|
75
|
+
occupied[_arg0] = true
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
local function findNextCell(colSpan, rowSpan)
|
|
82
|
+
local row = 0
|
|
83
|
+
while true do
|
|
84
|
+
do
|
|
85
|
+
local col = 0
|
|
86
|
+
local _shouldIncrement = false
|
|
87
|
+
while true do
|
|
88
|
+
if _shouldIncrement then
|
|
89
|
+
col += 1
|
|
90
|
+
else
|
|
91
|
+
_shouldIncrement = true
|
|
92
|
+
end
|
|
93
|
+
if not (col <= numCols - colSpan) then
|
|
94
|
+
break
|
|
95
|
+
end
|
|
96
|
+
local fits = true
|
|
97
|
+
do
|
|
98
|
+
local r = row
|
|
99
|
+
local _shouldIncrement_1 = false
|
|
100
|
+
while true do
|
|
101
|
+
if _shouldIncrement_1 then
|
|
102
|
+
r += 1
|
|
103
|
+
else
|
|
104
|
+
_shouldIncrement_1 = true
|
|
105
|
+
end
|
|
106
|
+
if not (r < row + rowSpan and fits) then
|
|
107
|
+
break
|
|
108
|
+
end
|
|
109
|
+
do
|
|
110
|
+
local c = col
|
|
111
|
+
local _shouldIncrement_2 = false
|
|
112
|
+
while true do
|
|
113
|
+
if _shouldIncrement_2 then
|
|
114
|
+
c += 1
|
|
115
|
+
else
|
|
116
|
+
_shouldIncrement_2 = true
|
|
117
|
+
end
|
|
118
|
+
if not (c < col + colSpan and fits) then
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
if isOccupied(c, r) then
|
|
122
|
+
fits = false
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
if fits then
|
|
129
|
+
return {
|
|
130
|
+
col = col,
|
|
131
|
+
row = row,
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
row += 1
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
-- ── Placement pass ────────────────────────────────────────────────────────
|
|
140
|
+
local placements = {}
|
|
141
|
+
for _, child in inFlow do
|
|
142
|
+
local cs = child.style
|
|
143
|
+
local _value = cs.gridColumn
|
|
144
|
+
local colP = if _value ~= "" and _value then parseGridPlacement(cs.gridColumn) else {
|
|
145
|
+
start = nil,
|
|
146
|
+
span = 1,
|
|
147
|
+
}
|
|
148
|
+
local _value_1 = cs.gridRow
|
|
149
|
+
local rowP = if _value_1 ~= "" and _value_1 then parseGridPlacement(cs.gridRow) else {
|
|
150
|
+
start = nil,
|
|
151
|
+
span = 1,
|
|
152
|
+
}
|
|
153
|
+
local colSpan = colP.span
|
|
154
|
+
local rowSpan = rowP.span
|
|
155
|
+
local col
|
|
156
|
+
local row
|
|
157
|
+
if colP.start ~= nil and rowP.start ~= nil then
|
|
158
|
+
col = colP.start - 1
|
|
159
|
+
row = rowP.start - 1
|
|
160
|
+
else
|
|
161
|
+
local nextCell = findNextCell(colSpan, rowSpan)
|
|
162
|
+
col = nextCell.col
|
|
163
|
+
row = nextCell.row
|
|
164
|
+
end
|
|
165
|
+
occupy(col, row, colSpan, rowSpan)
|
|
166
|
+
local _arg0 = {
|
|
167
|
+
node = child,
|
|
168
|
+
col = col,
|
|
169
|
+
row = row,
|
|
170
|
+
colSpan = colSpan,
|
|
171
|
+
rowSpan = rowSpan,
|
|
172
|
+
}
|
|
173
|
+
table.insert(placements, _arg0)
|
|
174
|
+
end
|
|
175
|
+
-- ── Calcular alturas de filas ─────────────────────────────────────────────
|
|
176
|
+
local numRows = 0
|
|
177
|
+
for _, p in placements do
|
|
178
|
+
local endS = p.row + p.rowSpan
|
|
179
|
+
if endS > numRows then
|
|
180
|
+
numRows = endS
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
local rowSizes = {}
|
|
184
|
+
if rowTemplate ~= "" and rowTemplate then
|
|
185
|
+
local resolved = resolveTrackSizes(parseGridTemplate(rowTemplate), containerHeight, rowGapPx)
|
|
186
|
+
for _, s in resolved do
|
|
187
|
+
table.insert(rowSizes, s)
|
|
188
|
+
end
|
|
189
|
+
while #rowSizes < numRows do
|
|
190
|
+
table.insert(rowSizes, 0)
|
|
191
|
+
end
|
|
192
|
+
else
|
|
193
|
+
do
|
|
194
|
+
local i = 0
|
|
195
|
+
local _shouldIncrement = false
|
|
196
|
+
while true do
|
|
197
|
+
if _shouldIncrement then
|
|
198
|
+
i += 1
|
|
199
|
+
else
|
|
200
|
+
_shouldIncrement = true
|
|
201
|
+
end
|
|
202
|
+
if not (i < numRows) then
|
|
203
|
+
break
|
|
204
|
+
end
|
|
205
|
+
table.insert(rowSizes, 0)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
for _, p in placements do
|
|
209
|
+
if p.rowSpan == 1 then
|
|
210
|
+
local _condition_3 = p.node.style.height
|
|
211
|
+
if _condition_3 == nil then
|
|
212
|
+
_condition_3 = 0
|
|
213
|
+
end
|
|
214
|
+
local childH = resolvePixels(_condition_3, containerHeight)
|
|
215
|
+
if childH > rowSizes[p.row + 1] then
|
|
216
|
+
rowSizes[p.row + 1] = childH
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
-- ── Offsets acumulados ────────────────────────────────────────────────────
|
|
222
|
+
local colOffsets = {}
|
|
223
|
+
local colAcc = 0
|
|
224
|
+
for _, size in colSizes do
|
|
225
|
+
local _colAcc = colAcc
|
|
226
|
+
table.insert(colOffsets, _colAcc)
|
|
227
|
+
colAcc += size + colGapPx
|
|
228
|
+
end
|
|
229
|
+
local rowOffsets = {}
|
|
230
|
+
local rowAcc = 0
|
|
231
|
+
for _, size in rowSizes do
|
|
232
|
+
local _rowAcc = rowAcc
|
|
233
|
+
table.insert(rowOffsets, _rowAcc)
|
|
234
|
+
rowAcc += size + rowGapPx
|
|
235
|
+
end
|
|
236
|
+
-- ── Suma de tracks para spans ─────────────────────────────────────────────
|
|
237
|
+
local function sumTracks(sizes, start, span)
|
|
238
|
+
local total = 0
|
|
239
|
+
do
|
|
240
|
+
local i = start
|
|
241
|
+
local _shouldIncrement = false
|
|
242
|
+
while true do
|
|
243
|
+
if _shouldIncrement then
|
|
244
|
+
i += 1
|
|
245
|
+
else
|
|
246
|
+
_shouldIncrement = true
|
|
247
|
+
end
|
|
248
|
+
if not (i < start + span) then
|
|
249
|
+
break
|
|
250
|
+
end
|
|
251
|
+
local _condition_3 = sizes[i + 1]
|
|
252
|
+
if _condition_3 == nil then
|
|
253
|
+
_condition_3 = 0
|
|
254
|
+
end
|
|
255
|
+
total += _condition_3
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
return total
|
|
259
|
+
end
|
|
260
|
+
-- ── Mapear a node.children usando índice paralelo ─────────────────────────
|
|
261
|
+
-- Índice por posición en inFlow para O(1) lookup sin Map de objetos
|
|
262
|
+
local placementByNode = {}
|
|
263
|
+
for _, p in placements do
|
|
264
|
+
local _node = p.node
|
|
265
|
+
placementByNode[_node] = p
|
|
266
|
+
end
|
|
267
|
+
local _exp = node.children
|
|
268
|
+
-- ▼ ReadonlyArray.map ▼
|
|
269
|
+
local _newValue = table.create(#_exp)
|
|
270
|
+
local _callback = function(child)
|
|
271
|
+
local _child = child
|
|
272
|
+
local p = placementByNode[_child]
|
|
273
|
+
if not p then
|
|
274
|
+
return {
|
|
275
|
+
x = 0,
|
|
276
|
+
y = 0,
|
|
277
|
+
width = 0,
|
|
278
|
+
height = 0,
|
|
279
|
+
}
|
|
280
|
+
end
|
|
281
|
+
local _object = {}
|
|
282
|
+
local _left = "x"
|
|
283
|
+
local _condition_3 = colOffsets[p.col + 1]
|
|
284
|
+
if _condition_3 == nil then
|
|
285
|
+
_condition_3 = 0
|
|
286
|
+
end
|
|
287
|
+
_object[_left] = _condition_3
|
|
288
|
+
local _left_1 = "y"
|
|
289
|
+
local _condition_4 = rowOffsets[p.row + 1]
|
|
290
|
+
if _condition_4 == nil then
|
|
291
|
+
_condition_4 = 0
|
|
292
|
+
end
|
|
293
|
+
_object[_left_1] = _condition_4
|
|
294
|
+
_object.width = sumTracks(colSizes, p.col, p.colSpan) + colGapPx * (p.colSpan - 1)
|
|
295
|
+
_object.height = sumTracks(rowSizes, p.row, p.rowSpan) + rowGapPx * (p.rowSpan - 1)
|
|
296
|
+
return _object
|
|
297
|
+
end
|
|
298
|
+
for _k, _v in _exp do
|
|
299
|
+
_newValue[_k] = _callback(_v, _k - 1, _exp)
|
|
300
|
+
end
|
|
301
|
+
-- ▲ ReadonlyArray.map ▲
|
|
302
|
+
return _newValue
|
|
303
|
+
end
|
|
304
|
+
return {
|
|
305
|
+
computeGridLayout = computeGridLayout,
|
|
306
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LayoutNode } from "./node";
|
|
2
|
+
export { createNode, updateNodeStyle, removeNode, markLayoutDirty, getRoot, findContainingBlockForAbsolute } from "./node";
|
|
3
|
+
export type { LayoutNode } from "./node";
|
|
4
|
+
/**
|
|
5
|
+
* Marca el nodo como dirty y agenda un layout pass en el próximo frame.
|
|
6
|
+
* Llama a esto cuando cambia un estilo o el tamaño de un contenedor.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runLayout(node: LayoutNode): void;
|
|
9
|
+
/**
|
|
10
|
+
* Ejecuta el layout inmediatamente sin esperar al próximo frame.
|
|
11
|
+
* Usar solo cuando necesitas los valores computados de forma síncrona.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runLayoutImmediate(node: LayoutNode): void;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/engine/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAOxC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,8BAA8B,EAAE,MAAM,QAAQ,CAAA;AAC1H,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AA0BxC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAGhD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAIzD"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local _node = TS.import(script, script, "node")
|
|
5
|
+
local getRoot = _node.getRoot
|
|
6
|
+
local markLayoutDirty = _node.markLayoutDirty
|
|
7
|
+
local computeFlexLayout = TS.import(script, script, "flex").computeFlexLayout
|
|
8
|
+
local computeGridLayout = TS.import(script, script, "grid").computeGridLayout
|
|
9
|
+
local computeAbsoluteLayout = TS.import(script, script, "absolute").computeAbsoluteLayout
|
|
10
|
+
local resolvePixels = TS.import(script, script.Parent, "parse").resolvePixels
|
|
11
|
+
local _node_1 = TS.import(script, script, "node")
|
|
12
|
+
exports.createNode = _node_1.createNode
|
|
13
|
+
exports.updateNodeStyle = _node_1.updateNodeStyle
|
|
14
|
+
exports.removeNode = _node_1.removeNode
|
|
15
|
+
exports.markLayoutDirty = _node_1.markLayoutDirty
|
|
16
|
+
exports.getRoot = _node_1.getRoot
|
|
17
|
+
exports.findContainingBlockForAbsolute = _node_1.findContainingBlockForAbsolute
|
|
18
|
+
-- ── Batching ──────────────────────────────────────────────────────────────────
|
|
19
|
+
-- Acumulamos nodos sucios y ejecutamos un único layout pass por frame
|
|
20
|
+
local scheduled = false
|
|
21
|
+
local pendingRoots = {}
|
|
22
|
+
local runLayoutImmediate
|
|
23
|
+
local function scheduleLayout(root)
|
|
24
|
+
local _root = root
|
|
25
|
+
pendingRoots[_root] = true
|
|
26
|
+
if scheduled then
|
|
27
|
+
return nil
|
|
28
|
+
end
|
|
29
|
+
scheduled = true
|
|
30
|
+
-- Diferir al siguiente Heartbeat para batching automático
|
|
31
|
+
local RunService = game:GetService("RunService")
|
|
32
|
+
local conn = RunService.Heartbeat:Once(function()
|
|
33
|
+
scheduled = false
|
|
34
|
+
local _array = {}
|
|
35
|
+
local _length = #_array
|
|
36
|
+
for _v in pendingRoots do
|
|
37
|
+
_length += 1
|
|
38
|
+
_array[_length] = _v
|
|
39
|
+
end
|
|
40
|
+
local roots = _array
|
|
41
|
+
table.clear(pendingRoots)
|
|
42
|
+
for _, r in roots do
|
|
43
|
+
if r.layoutDirty then
|
|
44
|
+
runLayoutImmediate(r)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end)
|
|
48
|
+
local _ = conn
|
|
49
|
+
local _1 = nil
|
|
50
|
+
end
|
|
51
|
+
--[[
|
|
52
|
+
*
|
|
53
|
+
* Marca el nodo como dirty y agenda un layout pass en el próximo frame.
|
|
54
|
+
* Llama a esto cuando cambia un estilo o el tamaño de un contenedor.
|
|
55
|
+
|
|
56
|
+
]]
|
|
57
|
+
local function runLayout(node)
|
|
58
|
+
markLayoutDirty(node)
|
|
59
|
+
scheduleLayout(getRoot(node))
|
|
60
|
+
end
|
|
61
|
+
--[[
|
|
62
|
+
*
|
|
63
|
+
* Ejecuta el layout inmediatamente sin esperar al próximo frame.
|
|
64
|
+
* Usar solo cuando necesitas los valores computados de forma síncrona.
|
|
65
|
+
|
|
66
|
+
]]
|
|
67
|
+
local layoutNode
|
|
68
|
+
function runLayoutImmediate(node)
|
|
69
|
+
if not node.layoutDirty then
|
|
70
|
+
return nil
|
|
71
|
+
end
|
|
72
|
+
local absSize = node.instance.AbsoluteSize
|
|
73
|
+
layoutNode(node, absSize.X, absSize.Y)
|
|
74
|
+
end
|
|
75
|
+
-- ── Layout recursivo ──────────────────────────────────────────────────────────
|
|
76
|
+
local computeBlockLayout, writeLayout, reparentIfNeeded
|
|
77
|
+
function layoutNode(node, containerWidth, containerHeight)
|
|
78
|
+
if not node.layoutDirty then
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
local style = node.style
|
|
82
|
+
-- Tamaño propio
|
|
83
|
+
node.computedWidth = if style.width ~= nil then resolvePixels(style.width, containerWidth) else containerWidth
|
|
84
|
+
node.computedHeight = if style.height ~= nil then resolvePixels(style.height, containerHeight) else containerHeight
|
|
85
|
+
if #node.children == 0 then
|
|
86
|
+
node.layoutDirty = false
|
|
87
|
+
return nil
|
|
88
|
+
end
|
|
89
|
+
local display = style.display
|
|
90
|
+
-- ── Layout de hijos en flujo ───────────────────────────────────────────────
|
|
91
|
+
local childLayouts
|
|
92
|
+
if display == "flex" then
|
|
93
|
+
childLayouts = computeFlexLayout(node, node.computedWidth, node.computedHeight)
|
|
94
|
+
elseif display == "grid" then
|
|
95
|
+
childLayouts = computeGridLayout(node, node.computedWidth, node.computedHeight)
|
|
96
|
+
else
|
|
97
|
+
childLayouts = computeBlockLayout(node, node.computedWidth, node.computedHeight)
|
|
98
|
+
end
|
|
99
|
+
-- ── Escribir y recursión: in-flow ─────────────────────────────────────────
|
|
100
|
+
for i = 0, #node.children - 1 do
|
|
101
|
+
local child = node.children[i + 1]
|
|
102
|
+
if child.style.position == "absolute" then
|
|
103
|
+
continue
|
|
104
|
+
end
|
|
105
|
+
local layout = childLayouts[i + 1]
|
|
106
|
+
if not layout then
|
|
107
|
+
continue
|
|
108
|
+
end
|
|
109
|
+
local sizeChanged = child.computedWidth ~= layout.width or child.computedHeight ~= layout.height
|
|
110
|
+
child.computedX = layout.x
|
|
111
|
+
child.computedY = layout.y
|
|
112
|
+
child.computedWidth = layout.width
|
|
113
|
+
child.computedHeight = layout.height
|
|
114
|
+
writeLayout(child.instance, layout.x, layout.y, layout.width, layout.height)
|
|
115
|
+
-- Solo recalcular hijos si el tamaño cambió
|
|
116
|
+
if sizeChanged then
|
|
117
|
+
child.layoutDirty = true
|
|
118
|
+
end
|
|
119
|
+
layoutNode(child, layout.width, layout.height)
|
|
120
|
+
end
|
|
121
|
+
-- ── Escribir y recursión: absolute ────────────────────────────────────────
|
|
122
|
+
for _, child in node.children do
|
|
123
|
+
if child.style.position ~= "absolute" then
|
|
124
|
+
continue
|
|
125
|
+
end
|
|
126
|
+
local abs = computeAbsoluteLayout(child)
|
|
127
|
+
local sizeChanged = child.computedWidth ~= abs.width or child.computedHeight ~= abs.height
|
|
128
|
+
child.computedX = abs.x
|
|
129
|
+
child.computedY = abs.y
|
|
130
|
+
child.computedWidth = abs.width
|
|
131
|
+
child.computedHeight = abs.height
|
|
132
|
+
reparentIfNeeded(child, abs.containingBlock)
|
|
133
|
+
writeLayout(child.instance, abs.x, abs.y, abs.width, abs.height)
|
|
134
|
+
if sizeChanged then
|
|
135
|
+
child.layoutDirty = true
|
|
136
|
+
end
|
|
137
|
+
layoutNode(child, abs.width, abs.height)
|
|
138
|
+
end
|
|
139
|
+
node.layoutDirty = false
|
|
140
|
+
end
|
|
141
|
+
-- ── Block layout ──────────────────────────────────────────────────────────────
|
|
142
|
+
function computeBlockLayout(node, containerWidth, containerHeight)
|
|
143
|
+
local yOffset = 0
|
|
144
|
+
local results = {}
|
|
145
|
+
for _, child in node.children do
|
|
146
|
+
if child.style.position == "absolute" then
|
|
147
|
+
table.insert(results, {
|
|
148
|
+
x = 0,
|
|
149
|
+
y = 0,
|
|
150
|
+
width = 0,
|
|
151
|
+
height = 0,
|
|
152
|
+
})
|
|
153
|
+
continue
|
|
154
|
+
end
|
|
155
|
+
local h = if child.style.height ~= nil then resolvePixels(child.style.height, containerHeight) else 0
|
|
156
|
+
local _arg0 = {
|
|
157
|
+
x = 0,
|
|
158
|
+
y = yOffset,
|
|
159
|
+
width = containerWidth,
|
|
160
|
+
height = h,
|
|
161
|
+
}
|
|
162
|
+
table.insert(results, _arg0)
|
|
163
|
+
yOffset += h
|
|
164
|
+
end
|
|
165
|
+
return results
|
|
166
|
+
end
|
|
167
|
+
-- ── Helpers ───────────────────────────────────────────────────────────────────
|
|
168
|
+
function writeLayout(instance, x, y, w, h)
|
|
169
|
+
instance.Position = UDim2.new(0, x, 0, y)
|
|
170
|
+
instance.Size = UDim2.new(0, w, 0, h)
|
|
171
|
+
end
|
|
172
|
+
function reparentIfNeeded(node, containingBlock)
|
|
173
|
+
if node.instance.Parent ~= containingBlock.instance then
|
|
174
|
+
node.instance.Parent = containingBlock.instance
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
exports.runLayout = runLayout
|
|
178
|
+
exports.runLayoutImmediate = runLayoutImmediate
|
|
179
|
+
return exports
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RbxStyle } from "../types";
|
|
2
|
+
export interface LayoutNode {
|
|
3
|
+
instance: GuiObject;
|
|
4
|
+
style: RbxStyle;
|
|
5
|
+
children: LayoutNode[];
|
|
6
|
+
parent: LayoutNode | undefined;
|
|
7
|
+
computedWidth: number;
|
|
8
|
+
computedHeight: number;
|
|
9
|
+
computedX: number;
|
|
10
|
+
computedY: number;
|
|
11
|
+
layoutDirty: boolean;
|
|
12
|
+
visualDirty: boolean;
|
|
13
|
+
lastVisualStyle: RbxStyle | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function createNode(instance: GuiObject, style: RbxStyle, parent?: LayoutNode): LayoutNode;
|
|
16
|
+
export declare function updateNodeStyle(node: LayoutNode, style: RbxStyle): void;
|
|
17
|
+
export declare function removeNode(node: LayoutNode): void;
|
|
18
|
+
export declare function markLayoutDirty(node: LayoutNode): void;
|
|
19
|
+
/**
|
|
20
|
+
* Sube el árbol hasta encontrar el ancestor con position:relative o position:absolute.
|
|
21
|
+
* Si no encuentra ninguno, devuelve la raíz.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findContainingBlockForAbsolute(node: LayoutNode): LayoutNode;
|
|
24
|
+
export declare function getRoot(node: LayoutNode): LayoutNode;
|
|
25
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/core/engine/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,KAAK,EAAE,QAAQ,CAAA;IACf,QAAQ,EAAE,UAAU,EAAE,CAAA;IACtB,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;IAG9B,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IAGjB,WAAW,EAAE,OAAO,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;IAGpB,eAAe,EAAE,QAAQ,GAAG,SAAS,CAAA;CACrC;AAED,wBAAgB,UAAU,CACzB,QAAQ,EAAE,SAAS,EACnB,KAAK,EAAE,QAAQ,EACf,MAAM,CAAC,EAAE,UAAU,GACjB,UAAU,CAiBZ;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAIvE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAOjD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAOtD;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAU3E;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAIpD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function createNode(instance, style, parent)
|
|
3
|
+
local node = {
|
|
4
|
+
instance = instance,
|
|
5
|
+
style = style,
|
|
6
|
+
children = {},
|
|
7
|
+
parent = parent,
|
|
8
|
+
computedWidth = 0,
|
|
9
|
+
computedHeight = 0,
|
|
10
|
+
computedX = 0,
|
|
11
|
+
computedY = 0,
|
|
12
|
+
layoutDirty = true,
|
|
13
|
+
visualDirty = true,
|
|
14
|
+
lastVisualStyle = nil,
|
|
15
|
+
}
|
|
16
|
+
if parent then
|
|
17
|
+
local _exp = parent.children
|
|
18
|
+
table.insert(_exp, node)
|
|
19
|
+
end
|
|
20
|
+
return node
|
|
21
|
+
end
|
|
22
|
+
local function updateNodeStyle(node, style)
|
|
23
|
+
node.style = style
|
|
24
|
+
node.layoutDirty = true
|
|
25
|
+
node.visualDirty = true
|
|
26
|
+
end
|
|
27
|
+
local function removeNode(node)
|
|
28
|
+
if node.parent then
|
|
29
|
+
local _children = node.parent.children
|
|
30
|
+
local _node = node
|
|
31
|
+
local idx = (table.find(_children, _node) or 0) - 1
|
|
32
|
+
if idx ~= -1 then
|
|
33
|
+
table.remove(node.parent.children, idx + 1)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
-- Marcar padre como dirty para que recalcule sin este hijo
|
|
37
|
+
if node.parent then
|
|
38
|
+
node.parent.layoutDirty = true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
local function markLayoutDirty(node)
|
|
42
|
+
-- Propagar dirty hacia arriba hasta encontrar un nodo que ya sea dirty
|
|
43
|
+
local current = node
|
|
44
|
+
while current ~= nil and not current.layoutDirty do
|
|
45
|
+
current.layoutDirty = true
|
|
46
|
+
current = current.parent
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
--[[
|
|
50
|
+
*
|
|
51
|
+
* Sube el árbol hasta encontrar el ancestor con position:relative o position:absolute.
|
|
52
|
+
* Si no encuentra ninguno, devuelve la raíz.
|
|
53
|
+
|
|
54
|
+
]]
|
|
55
|
+
local function findContainingBlockForAbsolute(node)
|
|
56
|
+
local current = node.parent
|
|
57
|
+
while current ~= nil do
|
|
58
|
+
local pos = current.style.position
|
|
59
|
+
if pos == "relative" or pos == "absolute" then
|
|
60
|
+
return current
|
|
61
|
+
end
|
|
62
|
+
current = current.parent
|
|
63
|
+
end
|
|
64
|
+
local root = node
|
|
65
|
+
while root.parent ~= nil do
|
|
66
|
+
root = root.parent
|
|
67
|
+
end
|
|
68
|
+
return root
|
|
69
|
+
end
|
|
70
|
+
local function getRoot(node)
|
|
71
|
+
local current = node
|
|
72
|
+
while current.parent ~= nil do
|
|
73
|
+
current = current.parent
|
|
74
|
+
end
|
|
75
|
+
return current
|
|
76
|
+
end
|
|
77
|
+
return {
|
|
78
|
+
createNode = createNode,
|
|
79
|
+
updateNodeStyle = updateNodeStyle,
|
|
80
|
+
removeNode = removeNode,
|
|
81
|
+
markLayoutDirty = markLayoutDirty,
|
|
82
|
+
findContainingBlockForAbsolute = findContainingBlockForAbsolute,
|
|
83
|
+
getRoot = getRoot,
|
|
84
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { UDimValue, ColorValue, GradientStop, RbxStyle, RbxStyleBase } from "./types";
|
|
2
|
+
export type { SpacingStyle } from "./types/style/spacing";
|
|
3
|
+
export type { SizeStyle } from "./types/style/size";
|
|
4
|
+
export type { BackgroundStyle } from "./types/style/background";
|
|
5
|
+
export type { BorderStyle } from "./types/style/border";
|
|
6
|
+
export type { TextStyle } from "./types/style/text";
|
|
7
|
+
export type { LayoutStyle, Display, FlexDirection, JustifyContent, AlignItems, AlignContent, FlexWrap } from "./types/style/layout";
|
|
8
|
+
export type { PositionStyle, PositionType } from "./types/style/position";
|
|
9
|
+
export type { MiscStyle } from "./types/style/misc";
|
|
10
|
+
export { parseColor } from "./parse/color";
|
|
11
|
+
export { parseUDim, parseUDim2, resolvePixels } from "./parse/udim";
|
|
12
|
+
export { parseGridTemplate, resolveTrackSizes } from "./parse/grid";
|
|
13
|
+
export { createNode, updateNodeStyle, removeNode, runLayout, runLayoutImmediate, markLayoutDirty, getRoot } from "./engine";
|
|
14
|
+
export type { LayoutNode } from "./engine";
|
|
15
|
+
export { writeVisual } from "./writer";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC1F,YAAY,EAAE,YAAY,EAAE,MAAS,uBAAuB,CAAA;AAC5D,YAAY,EAAE,SAAS,EAAE,MAAY,oBAAoB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAU,sBAAsB,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAY,oBAAoB,CAAA;AACzD,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACnI,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACzE,YAAY,EAAE,SAAS,EAAE,MAAY,oBAAoB,CAAA;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAoC,eAAe,CAAA;AACxE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAU,cAAc,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAU,cAAc,CAAA;AAEvE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC3H,YAAY,EAAE,UAAU,EAAE,MAA+B,UAAU,CAAA;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAmC,UAAU,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
exports.parseColor = TS.import(script, script, "parse", "color").parseColor
|
|
5
|
+
local _udim = TS.import(script, script, "parse", "udim")
|
|
6
|
+
exports.parseUDim = _udim.parseUDim
|
|
7
|
+
exports.parseUDim2 = _udim.parseUDim2
|
|
8
|
+
exports.resolvePixels = _udim.resolvePixels
|
|
9
|
+
local _grid = TS.import(script, script, "parse", "grid")
|
|
10
|
+
exports.parseGridTemplate = _grid.parseGridTemplate
|
|
11
|
+
exports.resolveTrackSizes = _grid.resolveTrackSizes
|
|
12
|
+
local _engine = TS.import(script, script, "engine")
|
|
13
|
+
exports.createNode = _engine.createNode
|
|
14
|
+
exports.updateNodeStyle = _engine.updateNodeStyle
|
|
15
|
+
exports.removeNode = _engine.removeNode
|
|
16
|
+
exports.runLayout = _engine.runLayout
|
|
17
|
+
exports.runLayoutImmediate = _engine.runLayoutImmediate
|
|
18
|
+
exports.markLayoutDirty = _engine.markLayoutDirty
|
|
19
|
+
exports.getRoot = _engine.getRoot
|
|
20
|
+
exports.writeVisual = TS.import(script, script, "writer").writeVisual
|
|
21
|
+
return exports
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/core/parse/color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAgBpD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function parseColor(value)
|
|
3
|
+
local _value = value
|
|
4
|
+
if typeof(_value) == "userdata" then
|
|
5
|
+
return value
|
|
6
|
+
end
|
|
7
|
+
local _value_1 = value
|
|
8
|
+
if type(_value_1) == "table" then
|
|
9
|
+
local _binding = value
|
|
10
|
+
local r = _binding[1]
|
|
11
|
+
local g = _binding[2]
|
|
12
|
+
local b = _binding[3]
|
|
13
|
+
return Color3.fromRGB(r, g, b)
|
|
14
|
+
end
|
|
15
|
+
local str = value
|
|
16
|
+
local hex = (string.gsub(str, "#", ""))
|
|
17
|
+
local expanded = if #hex == 3 then string.rep(string.sub(hex, 1, 1), 2) .. string.rep(string.sub(hex, 2, 2), 2) .. string.rep(string.sub(hex, 3, 3), 2) else hex
|
|
18
|
+
return Color3.fromHex(expanded)
|
|
19
|
+
end
|
|
20
|
+
return {
|
|
21
|
+
parseColor = parseColor,
|
|
22
|
+
}
|