@rbxts/centurion-ui 1.0.7 → 2.0.0-rc.1

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.
Files changed (141) hide show
  1. package/README.md +60 -15
  2. package/centurion.luau +10 -0
  3. package/package.json +13 -23
  4. package/src/analyse/cursor.luau +25 -0
  5. package/src/analyse/highlight.luau +91 -0
  6. package/src/analyse/init.luau +318 -0
  7. package/src/analyse/leventine.luau +298 -0
  8. package/src/analyse/match.luau +39 -0
  9. package/src/analyse/suggest.luau +41 -0
  10. package/src/app/header.luau +128 -0
  11. package/src/app/history.luau +258 -0
  12. package/src/app/init.luau +699 -0
  13. package/src/app/suggestions.luau +419 -0
  14. package/src/button.luau +13 -0
  15. package/src/components/background.luau +37 -0
  16. package/src/components/container.luau +33 -0
  17. package/src/components/corner.luau +11 -0
  18. package/src/components/divider.luau +28 -0
  19. package/src/components/flex.luau +108 -0
  20. package/src/components/gap.luau +25 -0
  21. package/src/components/image.luau +33 -0
  22. package/src/components/padding.luau +59 -0
  23. package/src/components/stroke.luau +20 -0
  24. package/src/components/text.luau +57 -0
  25. package/src/components/textbox.luau +186 -0
  26. package/src/fade.luau +23 -0
  27. package/src/init.luau +142 -0
  28. package/src/input.luau +16 -0
  29. package/src/px.luau +68 -0
  30. package/src/rich.luau +14 -0
  31. package/src/state.luau +33 -0
  32. package/src/theme.luau +70 -0
  33. package/src/ts/index.d.ts +60 -0
  34. package/src/ts/init.luau +5 -0
  35. package/src/types.luau +85 -0
  36. package/vide/init.luau +8 -0
  37. package/vide/src/action.luau +25 -0
  38. package/vide/src/apply.luau +162 -0
  39. package/vide/src/batch.luau +23 -0
  40. package/vide/src/branch.luau +40 -0
  41. package/vide/src/changed.luau +18 -0
  42. package/vide/src/cleanup.luau +43 -0
  43. package/vide/src/context.luau +74 -0
  44. package/vide/src/create.luau +107 -0
  45. package/vide/src/defaults.luau +113 -0
  46. package/vide/src/derive.luau +18 -0
  47. package/vide/src/effect.luau +12 -0
  48. package/vide/src/flags.luau +12 -0
  49. package/vide/src/graph.luau +321 -0
  50. package/vide/src/implicit_effect.luau +125 -0
  51. package/vide/src/indexes.luau +121 -0
  52. package/vide/src/init.luau +10 -0
  53. package/vide/src/lib.luau +120 -0
  54. package/vide/src/mount.luau +11 -0
  55. package/vide/src/read.luau +5 -0
  56. package/vide/src/root.luau +35 -0
  57. package/vide/src/show.luau +36 -0
  58. package/vide/src/source.luau +31 -0
  59. package/vide/src/spring.luau +328 -0
  60. package/vide/src/switch.luau +114 -0
  61. package/vide/src/timeout.luau +27 -0
  62. package/vide/src/untrack.luau +25 -0
  63. package/vide/src/values.luau +131 -0
  64. package/out/app/centurion-app.d.ts +0 -3
  65. package/out/app/centurion-app.luau +0 -86
  66. package/out/app/index.d.ts +0 -32
  67. package/out/app/init.luau +0 -105
  68. package/out/components/history/history-line.d.ts +0 -9
  69. package/out/components/history/history-line.luau +0 -88
  70. package/out/components/history/history-list.d.ts +0 -11
  71. package/out/components/history/history-list.luau +0 -67
  72. package/out/components/history/index.d.ts +0 -1
  73. package/out/components/history/init.luau +0 -7
  74. package/out/components/index.d.ts +0 -2
  75. package/out/components/init.luau +0 -10
  76. package/out/components/suggestions/badge.d.ts +0 -16
  77. package/out/components/suggestions/badge.luau +0 -40
  78. package/out/components/suggestions/index.d.ts +0 -1
  79. package/out/components/suggestions/init.luau +0 -7
  80. package/out/components/suggestions/main-suggestion.d.ts +0 -9
  81. package/out/components/suggestions/main-suggestion.luau +0 -232
  82. package/out/components/suggestions/suggestion-list.d.ts +0 -8
  83. package/out/components/suggestions/suggestion-list.luau +0 -96
  84. package/out/components/suggestions/suggestions.d.ts +0 -2
  85. package/out/components/suggestions/suggestions.luau +0 -64
  86. package/out/components/suggestions/types.d.ts +0 -6
  87. package/out/components/suggestions/types.luau +0 -2
  88. package/out/components/suggestions/util.d.ts +0 -1
  89. package/out/components/suggestions/util.luau +0 -22
  90. package/out/components/terminal/command.d.ts +0 -6
  91. package/out/components/terminal/command.luau +0 -74
  92. package/out/components/terminal/index.d.ts +0 -1
  93. package/out/components/terminal/init.luau +0 -7
  94. package/out/components/terminal/suggestion.d.ts +0 -26
  95. package/out/components/terminal/suggestion.luau +0 -205
  96. package/out/components/terminal/terminal-text-field/index.d.ts +0 -1
  97. package/out/components/terminal/terminal-text-field/init.luau +0 -7
  98. package/out/components/terminal/terminal-text-field/suggestion.d.ts +0 -5
  99. package/out/components/terminal/terminal-text-field/suggestion.luau +0 -124
  100. package/out/components/terminal/terminal-text-field/terminal-text-field.d.ts +0 -13
  101. package/out/components/terminal/terminal-text-field/terminal-text-field.luau +0 -273
  102. package/out/components/terminal/terminal.d.ts +0 -2
  103. package/out/components/terminal/terminal.luau +0 -304
  104. package/out/components/ui/frame.d.ts +0 -17
  105. package/out/components/ui/frame.luau +0 -32
  106. package/out/components/ui/group.d.ts +0 -15
  107. package/out/components/ui/group.luau +0 -29
  108. package/out/components/ui/layer.d.ts +0 -8
  109. package/out/components/ui/layer.luau +0 -26
  110. package/out/components/ui/outline.d.ts +0 -15
  111. package/out/components/ui/outline.luau +0 -116
  112. package/out/components/ui/padding.d.ts +0 -10
  113. package/out/components/ui/padding.luau +0 -19
  114. package/out/components/ui/scrolling-frame.d.ts +0 -14
  115. package/out/components/ui/scrolling-frame.luau +0 -44
  116. package/out/components/ui/text-field.d.ts +0 -12
  117. package/out/components/ui/text-field.luau +0 -60
  118. package/out/components/ui/text.d.ts +0 -17
  119. package/out/components/ui/text.luau +0 -61
  120. package/out/constants/options.d.ts +0 -2
  121. package/out/constants/options.luau +0 -27
  122. package/out/constants/text.d.ts +0 -3
  123. package/out/constants/text.luau +0 -9
  124. package/out/constants/util.d.ts +0 -1
  125. package/out/constants/util.luau +0 -7
  126. package/out/hooks/use-client.d.ts +0 -2
  127. package/out/hooks/use-client.luau +0 -17
  128. package/out/hooks/use-event.d.ts +0 -14
  129. package/out/hooks/use-event.luau +0 -58
  130. package/out/hooks/use-px.d.ts +0 -19
  131. package/out/hooks/use-px.luau +0 -78
  132. package/out/index.d.ts +0 -2
  133. package/out/init.luau +0 -10
  134. package/out/palette.d.ts +0 -47
  135. package/out/palette.luau +0 -42
  136. package/out/store.d.ts +0 -12
  137. package/out/store.luau +0 -26
  138. package/out/types.d.ts +0 -44
  139. package/out/types.luau +0 -2
  140. package/out/utils/string.d.ts +0 -5
  141. package/out/utils/string.luau +0 -25
@@ -0,0 +1,419 @@
1
+ local background = require("../components/background")
2
+ local button_name = require("../button")
3
+ local centurion = require("../../centurion")
4
+ local container = require("../components/container")
5
+ local corner = require("../components/corner")
6
+ local divider = require("../components/divider")
7
+ local fade = require("../fade")
8
+ local flex = require("../components/flex")
9
+ local padding = require("../components/padding")
10
+ local preferred_input = require("../input")
11
+ local px = require("../px")
12
+ local rich = require("../rich")
13
+ local state = require("../state")
14
+ local stroke = require("../components/stroke")
15
+ local text = require("../components/text")
16
+ local theme = require("../theme")
17
+ local types = require("../types")
18
+ local vide = require("../../vide")
19
+
20
+ local create = vide.create
21
+ local derive = vide.derive
22
+ local indexes = vide.indexes
23
+ local show = vide.show
24
+ local source = vide.source
25
+ local spring = vide.spring
26
+
27
+ export type Suggestions = {
28
+ read analysis: () -> types.AnalysisResult,
29
+ read selected_suggestion: () -> number,
30
+ read on_select: (suggestion: types.AnalysisSuggestion) -> (),
31
+ read order: number?,
32
+
33
+ [number]: unknown,
34
+ }
35
+
36
+ -- e.g. <name: type>, [name: type] if optional, <name: type...> if it takes the rest of the input
37
+ local function format_argument(arg: centurion.Argument): string
38
+ local label = `{arg.name}: {arg.type}`
39
+
40
+ local num_args = arg.num_args
41
+ if num_args == "rest" then
42
+ label ..= "..."
43
+ elseif typeof(num_args) == "number" and num_args > 1 then
44
+ label ..= ` x{num_args}`
45
+ end
46
+
47
+ return if arg.optional then `[{label}]` else `<{label}>`
48
+ end
49
+
50
+ -- small filled label, e.g. the type of an argument
51
+ local function pill(props: { text: () -> string, color: () -> Color3, order: number? })
52
+ return background {
53
+ auto = "XY",
54
+ order = props.order,
55
+ color = props.color,
56
+
57
+ corner(px(4)),
58
+ padding { x = px(6), y = px(2) },
59
+
60
+ text {
61
+ auto = "XY",
62
+ text = props.text,
63
+ text_size = px(state.subtext_size),
64
+ text_style = "bg",
65
+ weight = Enum.FontWeight.Bold,
66
+ },
67
+ }
68
+ end
69
+
70
+ local function suggestion_row(value: () -> types.AnalysisSuggestion, i: number, props: Suggestions)
71
+ local function selected()
72
+ return props.selected_suggestion() == i
73
+ end
74
+ local hovered = source(false)
75
+
76
+ return create "TextButton" {
77
+ AutomaticSize = Enum.AutomaticSize.XY,
78
+ AutoButtonColor = false,
79
+ BackgroundColor3 = function()
80
+ return state.theme().surface
81
+ end,
82
+ BackgroundTransparency = fade.fade(spring(function()
83
+ if selected() then
84
+ return state.transparency()
85
+ elseif hovered() then
86
+ return 1 - (1 - state.transparency()) * 0.5
87
+ end
88
+ return 1
89
+ end, 0.1)),
90
+ LayoutOrder = i,
91
+ Text = "",
92
+
93
+ MouseEnter = function()
94
+ hovered(true)
95
+ end,
96
+ MouseLeave = function()
97
+ hovered(false)
98
+ end,
99
+ Activated = function()
100
+ props.on_select(value())
101
+ end,
102
+
103
+ corner(px(4)),
104
+ padding { x = px(8), y = px(5) },
105
+ flex():column():vertical("center"):between("horizontal"),
106
+
107
+ container {
108
+ auto = "XY",
109
+ order = 1,
110
+
111
+ flex():column():vertical("center"),
112
+
113
+ -- marks the suggestion Tab will complete
114
+ text {
115
+ size = px(UDim2.fromOffset(18, 0)),
116
+ auto = "Y",
117
+ order = 1,
118
+ text = ">",
119
+ text_style = "highlight",
120
+ weight = Enum.FontWeight.Bold,
121
+ xalignment = Enum.TextXAlignment.Left,
122
+ transparency = spring(function()
123
+ return if selected() then 0 else 1
124
+ end, 0.1),
125
+ },
126
+
127
+ text {
128
+ auto = "XY",
129
+ order = 2,
130
+ -- characters matching the input are emphasised
131
+ text = function()
132
+ local suggestion = value()
133
+ local name = suggestion.name
134
+ local highlight = state.theme().highlight
135
+
136
+ local out = {}
137
+ local pos = 1
138
+ for _, range in suggestion.matches do
139
+ table.insert(out, rich.escape(name:sub(pos, range.x - 1)))
140
+ table.insert(out, `<b>{rich.color(rich.escape(name:sub(range.x, range.y)), highlight)}</b>`)
141
+ pos = range.y + 1
142
+ end
143
+ table.insert(out, rich.escape(name:sub(pos)))
144
+
145
+ return table.concat(out)
146
+ end,
147
+ rich_text = true,
148
+ text_style = function()
149
+ return if selected() then "normal" else "subtext"
150
+ end,
151
+ xalignment = Enum.TextXAlignment.Left,
152
+ },
153
+ },
154
+
155
+ show(function()
156
+ return value().description ~= nil
157
+ end, function()
158
+ return text {
159
+ auto = "XY",
160
+ order = 2,
161
+ text = function()
162
+ return value().description or ""
163
+ end,
164
+ text_size = px(state.subtext_size),
165
+ text_style = "subtext",
166
+ xalignment = Enum.TextXAlignment.Right,
167
+
168
+ -- keeps the description away from the name
169
+ padding { left = px(32) },
170
+ }
171
+ end),
172
+ }
173
+ end
174
+
175
+ local function suggestions(props: Suggestions)
176
+ local usage = derive(function()
177
+ local analysis = props.analysis()
178
+ if analysis.argument == nil or analysis.command == nil then
179
+ return nil
180
+ end
181
+
182
+ local cmd = centurion.registry.commands[analysis.command]
183
+ if cmd == nil or cmd.arguments == nil then
184
+ return nil
185
+ end
186
+
187
+ local args: { centurion.Argument } = { cmd.arguments() }
188
+ return {
189
+ command = analysis.command,
190
+ args = args,
191
+ index = analysis.argument.index,
192
+ }
193
+ end)
194
+
195
+ local arg = derive(function()
196
+ local current = usage()
197
+ return current and current.args[current.index]
198
+ end)
199
+
200
+ local failed = derive(function()
201
+ return not props.analysis().result.success
202
+ end)
203
+
204
+ local has_suggestions = derive(function()
205
+ return #props.analysis().suggestions > 0
206
+ end)
207
+
208
+ local input = preferred_input()
209
+
210
+ -- the sections above the list, which are divided from it when both are shown
211
+ local has_details = derive(function()
212
+ return failed() or arg() ~= nil
213
+ end)
214
+
215
+ return background {
216
+ auto = "XY",
217
+ order = props.order,
218
+ color = function()
219
+ return state.theme().background
220
+ end,
221
+ transparency = state.transparency,
222
+
223
+ corner(px(8)),
224
+ stroke {
225
+ color = function()
226
+ return theme.border(state.theme())
227
+ end,
228
+ },
229
+ create "UISizeConstraint" {
230
+ MinSize = function()
231
+ return Vector2.new(px.convert(320), 0)
232
+ end,
233
+ },
234
+ flex():row():fill("horizontal"),
235
+
236
+ -- why the input can't be run as it is
237
+ show(failed, function()
238
+ return container {
239
+ auto = "XY",
240
+ order = 1,
241
+
242
+ padding { x = px(12), y = px(8) },
243
+ flex():column():vertical("center"):gap(8),
244
+
245
+ pill {
246
+ order = 1,
247
+ text = function()
248
+ return "error"
249
+ end,
250
+ color = function()
251
+ return state.theme().error
252
+ end,
253
+ },
254
+
255
+ text {
256
+ auto = "XY",
257
+ order = 2,
258
+ text = function()
259
+ return props.analysis().result.error or ""
260
+ end,
261
+ text_size = px(state.subtext_size),
262
+ text_style = "error",
263
+ xalignment = Enum.TextXAlignment.Left,
264
+ },
265
+ }
266
+ end),
267
+
268
+ -- the full signature, and details of the argument being typed
269
+ show(arg, function()
270
+ return container {
271
+ auto = "XY",
272
+ order = 2,
273
+
274
+ padding { x = px(12), y = px(8) },
275
+ flex():row():gap(6),
276
+
277
+ text {
278
+ auto = "XY",
279
+ order = 1,
280
+ text = function()
281
+ local current = usage()
282
+ if not current then
283
+ return ""
284
+ end
285
+
286
+ local colors = state.theme()
287
+ local parts = { `<b>{rich.escape(current.command)}</b>` }
288
+ for i, value in current.args do
289
+ local label = rich.escape(format_argument(value))
290
+ table.insert(
291
+ parts,
292
+ if i == current.index
293
+ then `<b>{rich.color(label, colors.highlight)}</b>`
294
+ else rich.color(label, colors.subtext)
295
+ )
296
+ end
297
+
298
+ return table.concat(parts, " ")
299
+ end,
300
+ rich_text = true,
301
+ text_size = px(state.subtext_size),
302
+ xalignment = Enum.TextXAlignment.Left,
303
+ },
304
+
305
+ container {
306
+ auto = "XY",
307
+ order = 2,
308
+
309
+ flex():column():vertical("center"):gap(8),
310
+
311
+ pill {
312
+ order = 1,
313
+ text = function()
314
+ return arg() and arg().type or ""
315
+ end,
316
+ color = function()
317
+ return state.theme().highlight
318
+ end,
319
+ },
320
+
321
+ text {
322
+ auto = "XY",
323
+ order = 2,
324
+ text = function()
325
+ return arg() and arg().name or ""
326
+ end,
327
+ weight = Enum.FontWeight.Bold,
328
+ xalignment = Enum.TextXAlignment.Left,
329
+ },
330
+
331
+ show(function()
332
+ return arg() and arg().description ~= nil
333
+ end, function()
334
+ return text {
335
+ auto = "XY",
336
+ order = 3,
337
+ text = function()
338
+ return arg() and arg().description or ""
339
+ end,
340
+ text_size = px(state.subtext_size),
341
+ text_style = "subtext",
342
+ xalignment = Enum.TextXAlignment.Left,
343
+ }
344
+ end),
345
+ },
346
+ }
347
+ end),
348
+
349
+ divider {
350
+ order = 3,
351
+ visible = function()
352
+ return has_details() and has_suggestions()
353
+ end,
354
+ },
355
+
356
+ show(has_suggestions, function()
357
+ return container {
358
+ auto = "XY",
359
+ order = 4,
360
+
361
+ padding(px(4)),
362
+ flex():row():fill("horizontal"),
363
+
364
+ indexes(function()
365
+ return props.analysis().suggestions
366
+ end, function(value: () -> types.AnalysisSuggestion, i)
367
+ return suggestion_row(value, i, props)
368
+ end),
369
+ }
370
+ end),
371
+
372
+ divider {
373
+ order = 5,
374
+ visible = has_suggestions,
375
+ },
376
+
377
+ show(has_suggestions, function()
378
+ return container {
379
+ auto = "XY",
380
+ order = 6,
381
+
382
+ padding { x = px(12), y = px(6) },
383
+
384
+ text {
385
+ auto = "XY",
386
+ text = function()
387
+ local key = rich.color("%s", state.theme().text)
388
+ if input() == Enum.PreferredInput.Touch then
389
+ return `<b>{key:format("Tap")}</b> a suggestion to complete it`
390
+ elseif input() == Enum.PreferredInput.Gamepad then
391
+ return table.concat({
392
+ `<b>{key:format(button_name(Enum.KeyCode.ButtonA))}</b> complete`,
393
+ `<b>{key:format("D-pad")}</b> select`,
394
+ `<b>{key:format(button_name(Enum.KeyCode.ButtonX))}</b> edit`,
395
+ `<b>{key:format(button_name(Enum.KeyCode.ButtonY))}</b> run`,
396
+ `<b>{key:format(button_name(Enum.KeyCode.ButtonB))}</b> close`,
397
+ }, " ")
398
+ end
399
+
400
+ local hints = {
401
+ `<b>{key:format("Tab")}</b> complete`,
402
+ `<b>{key:format("↑↓")}</b> select`,
403
+ `<b>{key:format("Enter")}</b> run`,
404
+ }
405
+ return table.concat(hints, " ")
406
+ end,
407
+ rich_text = true,
408
+ text_size = px(state.subtext_size),
409
+ text_style = "subtext",
410
+ xalignment = Enum.TextXAlignment.Left,
411
+ },
412
+ }
413
+ end),
414
+
415
+ unpack(props),
416
+ }
417
+ end
418
+
419
+ return suggestions
@@ -0,0 +1,13 @@
1
+ local UserInputService = game:GetService("UserInputService")
2
+
3
+ -- name of a gamepad button for the connected controller, e.g. "A" on Xbox
4
+ -- and "Cross" on PlayStation
5
+ local function button_name(key: Enum.KeyCode): string
6
+ local name = UserInputService:GetStringForKeyCode(key)
7
+ if name == "" then
8
+ name = key.Name
9
+ end
10
+ return (name:gsub("^Button", ""))
11
+ end
12
+
13
+ return button_name
@@ -0,0 +1,37 @@
1
+ local fade = require("../fade")
2
+ local vide = require("../../vide")
3
+
4
+ local create = vide.create
5
+
6
+ type Can<T> = T | () -> T
7
+ type Background = {
8
+ read size: Can<UDim2>?,
9
+ read position: Can<UDim2>?,
10
+ read anchor: Can<Vector2>?,
11
+ read color: Can<Color3>?,
12
+ read zindex: Can<number>?,
13
+ read auto: Can<Enum.AutomaticSize | "X" | "Y" | "XY">?,
14
+ read order: Can<number>?,
15
+ read transparency: Can<number>?,
16
+ read visible: Can<boolean>?,
17
+
18
+ [number]: unknown,
19
+ }
20
+
21
+ return function(props: Background)
22
+ return create "Frame" {
23
+ Size = props.size,
24
+ BackgroundColor3 = props.color,
25
+ BackgroundTransparency = fade.fade(props.transparency),
26
+ Position = props.position,
27
+ AnchorPoint = props.anchor,
28
+ AutomaticSize = props.auto,
29
+ LayoutOrder = props.order,
30
+ ZIndex = props.zindex,
31
+ Visible = props.visible,
32
+
33
+ AutoLocalize = false,
34
+
35
+ unpack(props),
36
+ }
37
+ end
@@ -0,0 +1,33 @@
1
+ local vide = require("../../vide")
2
+
3
+ local create = vide.create
4
+
5
+ type Can<T> = T | () -> T
6
+ type Container = {
7
+ read size: Can<UDim2>?,
8
+ read position: Can<UDim2>?,
9
+ read anchor: Can<Vector2>?,
10
+ read zindex: Can<number>?,
11
+ read auto: Can<Enum.AutomaticSize | "X" | "Y" | "XY">?,
12
+ read order: Can<number>?,
13
+ read clip: Can<boolean>?,
14
+
15
+ [number]: unknown,
16
+ }
17
+
18
+ return function(props: Container)
19
+ return create "Frame" {
20
+ Size = props.size,
21
+ Position = props.position,
22
+ AnchorPoint = props.anchor,
23
+ AutomaticSize = props.auto,
24
+ LayoutOrder = props.order,
25
+ ZIndex = props.zindex,
26
+ ClipsDescendants = props.clip,
27
+
28
+ AutoLocalize = false,
29
+ BackgroundTransparency = 1,
30
+
31
+ unpack(props),
32
+ }
33
+ end
@@ -0,0 +1,11 @@
1
+ local vide = require("../../vide")
2
+
3
+ local create = vide.create
4
+
5
+ return function(px: number)
6
+ return create "UICorner" {
7
+ CornerRadius = function()
8
+ return UDim.new(0, vide.read(px))
9
+ end,
10
+ }
11
+ end
@@ -0,0 +1,28 @@
1
+ local fade = require("../fade")
2
+ local state = require("../state")
3
+ local theme = require("../theme")
4
+ local vide = require("../../vide")
5
+
6
+ local create = vide.create
7
+
8
+ type Can<T> = T | () -> T
9
+ type Divider = {
10
+ read order: Can<number>?,
11
+ read visible: Can<boolean>?,
12
+ }
13
+
14
+ -- a 1px horizontal rule. It has no width of its own and relies on the parent's
15
+ -- list layout filling it horizontally: a scale width would make an
16
+ -- automatically sized parent grow to fill its own parent
17
+ return function(props: Divider?)
18
+ return create "Frame" {
19
+ Size = UDim2.fromOffset(0, 1),
20
+ BackgroundColor3 = function()
21
+ return theme.border(state.theme())
22
+ end,
23
+ BackgroundTransparency = fade.fade(),
24
+ BorderSizePixel = 0,
25
+ LayoutOrder = props and props.order,
26
+ Visible = props and props.visible,
27
+ }
28
+ end
@@ -0,0 +1,108 @@
1
+ local px = require("../px")
2
+ local vide = require("../../vide")
3
+
4
+ local create = vide.create
5
+ local effect = vide.effect
6
+ local source = vide.source
7
+ local read = vide.read
8
+
9
+ return function()
10
+ local gap = source(UDim.new())
11
+
12
+ local list: UIListLayout = create "UIListLayout" {
13
+ SortOrder = Enum.SortOrder.LayoutOrder,
14
+ Padding = px(gap),
15
+ }
16
+ local t = { list }
17
+ local mt = {}
18
+
19
+ function mt:row()
20
+ list.FillDirection = Enum.FillDirection.Vertical
21
+ return self
22
+ end
23
+
24
+ function mt:column()
25
+ list.FillDirection = Enum.FillDirection.Horizontal
26
+ return self
27
+ end
28
+
29
+ function mt:name()
30
+ list.SortOrder = Enum.SortOrder.Name
31
+ return self
32
+ end
33
+
34
+ function mt:none(direction: "horizontal" | "vertical"?)
35
+ if direction ~= "vertical" then
36
+ list.HorizontalFlex = Enum.UIFlexAlignment.None
37
+ end
38
+ if direction ~= "horizontal" then
39
+ list.VerticalFlex = Enum.UIFlexAlignment.None
40
+ end
41
+ return self
42
+ end
43
+
44
+ function mt:even(direction: "horizontal" | "vertical"?)
45
+ if direction ~= "vertical" then
46
+ list.HorizontalFlex = Enum.UIFlexAlignment.SpaceEvenly
47
+ end
48
+ if direction ~= "horizontal" then
49
+ list.VerticalFlex = Enum.UIFlexAlignment.SpaceEvenly
50
+ end
51
+ return self
52
+ end
53
+
54
+ function mt:around(direction: "horizontal" | "vertical"?)
55
+ if direction ~= "vertical" then
56
+ list.HorizontalFlex = Enum.UIFlexAlignment.SpaceAround
57
+ end
58
+ if direction ~= "horizontal" then
59
+ list.VerticalFlex = Enum.UIFlexAlignment.SpaceAround
60
+ end
61
+ return self
62
+ end
63
+
64
+ function mt:between(direction: "horizontal" | "vertical"?)
65
+ if direction ~= "vertical" then
66
+ list.HorizontalFlex = Enum.UIFlexAlignment.SpaceBetween
67
+ end
68
+ if direction ~= "horizontal" then
69
+ list.VerticalFlex = Enum.UIFlexAlignment.SpaceBetween
70
+ end
71
+ return self
72
+ end
73
+
74
+ function mt:fill(direction: "horizontal" | "vertical")
75
+ if direction ~= "vertical" then
76
+ list.HorizontalFlex = Enum.UIFlexAlignment.Fill
77
+ end
78
+ if direction ~= "horizontal" then
79
+ list.VerticalFlex = Enum.UIFlexAlignment.Fill
80
+ end
81
+ return self
82
+ end
83
+
84
+ function mt:horizontal(align: "left" | "right" | "center")
85
+ list.HorizontalAlignment = if align == "left"
86
+ then Enum.HorizontalAlignment.Left
87
+ elseif align == "right" then Enum.HorizontalAlignment.Right
88
+ else Enum.HorizontalAlignment.Center
89
+ return self
90
+ end
91
+
92
+ function mt:vertical(align: "top" | "bottom" | "center")
93
+ list.VerticalAlignment = if align == "top"
94
+ then Enum.VerticalAlignment.Top
95
+ elseif align == "bottom" then Enum.VerticalAlignment.Bottom
96
+ else Enum.VerticalAlignment.Center
97
+ return self
98
+ end
99
+
100
+ function mt:gap(value: number | () -> number)
101
+ effect(function()
102
+ gap(UDim.new(0, read(value)))
103
+ end)
104
+ return self
105
+ end
106
+
107
+ return setmetatable(t, { __index = mt })
108
+ end
@@ -0,0 +1,25 @@
1
+ local vide = require("../../vide")
2
+
3
+ local create = vide.create
4
+ local read = vide.read
5
+
6
+ type Can<T> = T | () -> T
7
+ type Gap = {
8
+ read width: Can<number>?,
9
+ read height: Can<number>?,
10
+
11
+ [number]: unknown,
12
+ }
13
+
14
+ return function(props: Gap)
15
+ return create "Frame" {
16
+ Name = "gap",
17
+ Size = function()
18
+ return UDim2.fromOffset(read(props.width) or 0, read(props.height) or 0)
19
+ end,
20
+
21
+ BackgroundTransparency = 1,
22
+
23
+ unpack(props) :: any,
24
+ }
25
+ end
@@ -0,0 +1,33 @@
1
+ local fade = require("../fade")
2
+ local vide = require("../../vide")
3
+
4
+ local create = vide.create
5
+
6
+ type Can<T> = T | () -> T
7
+ type Image = {
8
+ read image: Can<string>,
9
+ read size: Can<UDim2>?,
10
+ read color: Can<Color3>?,
11
+ read transparency: Can<number>?,
12
+ read order: Can<number>?,
13
+ read visible: Can<boolean>?,
14
+
15
+ [number]: unknown,
16
+ }
17
+
18
+ return function(props: Image)
19
+ return create "ImageLabel" {
20
+ Image = props.image,
21
+ Size = props.size,
22
+ ImageColor3 = props.color,
23
+ ImageTransparency = fade.fade(props.transparency),
24
+ LayoutOrder = props.order,
25
+ Visible = props.visible,
26
+ ScaleType = Enum.ScaleType.Fit,
27
+
28
+ AutoLocalize = false,
29
+ BackgroundTransparency = 1,
30
+
31
+ unpack(props),
32
+ }
33
+ end