mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Update property nesting
Deeper nested properties guaranteed to be processed after shallower nested properties. Also added strict mode checks for duplicate nested properties.
This commit is contained in:
parent
ddb88cfdf8
commit
a769139136
5 changed files with 125 additions and 29 deletions
|
|
@ -43,7 +43,7 @@ export default defineConfig({
|
|||
{ text: "Source", link: "/tut/crash-course/4-source" },
|
||||
{ text: "Derived Source", link: "/tut/crash-course/5-derived-source" },
|
||||
{ text: "Table Source", link: "/tut/crash-course/6-table-source" },
|
||||
{ text: "Property Groups", link: "/tut/crash-course/7-property-groups" },
|
||||
{ text: "Nested Properties", link: "/tut/crash-course/7-nested-properties" },
|
||||
{ text: "Actions", link: "/tut/crash-course/8-actions" },
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ Currently, strict mode will:
|
|||
2. Run watchers twice when a source updates.
|
||||
3. Throw an error if yields occur where they are not allowed.
|
||||
4. Checks for `indexes()` and `values()` returning primitive values.
|
||||
5. Better error reporting and stack traces.
|
||||
5. Checks for duplicate nested properties at same depth.
|
||||
6. Better error reporting and stack traces.
|
||||
|
||||
By rerunning sources and watchers, any side-effects are made more apparent.
|
||||
This also helps ensure that cleanups are being handled correctly.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Property Groups
|
||||
# Nested Properties
|
||||
|
||||
Often when creating components from existing components, you can find yourself
|
||||
repetitively passing through properties such as size or position.
|
||||
|
|
@ -99,3 +99,22 @@ List {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
Deeper nested properties are guaranteed to be set after shallower nested
|
||||
properties, this can be used to create overridable default properties.
|
||||
|
||||
```lua
|
||||
local function CenteredList(props: Children & Layout)
|
||||
return List {
|
||||
Layout = {
|
||||
props.Layout,
|
||||
|
||||
-- can be overriden by `props.Layout`
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Position = UDim2.fromScale(0.5, 0)
|
||||
},
|
||||
|
||||
Children = props.Children
|
||||
}
|
||||
end
|
||||
```
|
||||
|
|
@ -5,11 +5,12 @@ if not game then
|
|||
typeof = require "test/mock".typeof
|
||||
end
|
||||
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
|
||||
local flags = require(script.Parent.flags)
|
||||
local throw = require(script.Parent.throw)
|
||||
local bind = require(script.Parent.bind)
|
||||
local _, is_action = require(script.Parent.action)()
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
|
||||
local event_buffer: { [string]: () -> () } = {}
|
||||
local action_buffers = {} :: { { () -> () } }
|
||||
|
|
@ -21,15 +22,42 @@ setmetatable(action_buffers :: any, {
|
|||
end
|
||||
})
|
||||
|
||||
local function recurse(instance: Instance, properties: { [unknown]: unknown })
|
||||
local nested_debug_cache: { [number]: { [string]: true } } = {}
|
||||
|
||||
setmetatable(nested_debug_cache :: any, {
|
||||
__index = function(_, i: number)
|
||||
nested_debug_cache[i] = {}
|
||||
return nested_debug_cache[i]
|
||||
end
|
||||
})
|
||||
|
||||
local nested_stack = {} :: { {} | number }
|
||||
|
||||
local function process(instance: Instance, properties: { [unknown]: unknown })
|
||||
local strict = flags.strict
|
||||
|
||||
table.clear(nested_stack)
|
||||
if strict then table.clear(nested_debug_cache) end
|
||||
|
||||
local depth = 1
|
||||
|
||||
repeat
|
||||
for property, value in properties do
|
||||
if type(value) == "table" then
|
||||
if is_action(value) then
|
||||
table.insert(action_buffers[(value :: any).priority], (value :: any).callback :: () -> ())
|
||||
else
|
||||
recurse(instance, value :: {})
|
||||
table.insert(nested_stack, depth + 1)
|
||||
table.insert(nested_stack, value :: {})
|
||||
end
|
||||
elseif type(property) == "string" then
|
||||
if strict then
|
||||
if nested_debug_cache[depth][property] then
|
||||
throw(`duplicate property {property} at depth {depth}`)
|
||||
end
|
||||
nested_debug_cache[depth][property] = true
|
||||
end
|
||||
|
||||
if type(value) == "function" then
|
||||
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
||||
event_buffer[property] = value :: () -> ()
|
||||
|
|
@ -47,6 +75,11 @@ local function recurse(instance: Instance, properties: { [unknown]: unknown })
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
properties = table.remove(nested_stack) :: {}
|
||||
depth = table.remove(nested_stack) :: number
|
||||
|
||||
until not properties
|
||||
end
|
||||
|
||||
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
||||
|
|
@ -58,7 +91,7 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
|||
table.clear(buffer)
|
||||
end
|
||||
|
||||
recurse(instance, properties)
|
||||
process(instance, properties)
|
||||
|
||||
for event, fn in next, event_buffer do
|
||||
(instance :: any)[event]:Connect(fn)
|
||||
|
|
|
|||
|
|
@ -596,6 +596,20 @@ TEST("create()", function()
|
|||
CHECK(text.Text == "test")
|
||||
end
|
||||
|
||||
do CASE "nested precedence"
|
||||
local text = create "TextLabel" {
|
||||
Group = {
|
||||
Text = "1",
|
||||
|
||||
{
|
||||
Text = "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(text.Text == "2")
|
||||
end
|
||||
|
||||
do CASE "independent"
|
||||
local frame = create "Frame"
|
||||
CHECK(frame {} ~= frame {})
|
||||
|
|
@ -1153,6 +1167,7 @@ end)
|
|||
TEST("strict", function()
|
||||
vide.strict = true
|
||||
|
||||
local create = vide.create
|
||||
local source = vide.source
|
||||
local derive = vide.derive
|
||||
local watch = vide.watch
|
||||
|
|
@ -1231,6 +1246,34 @@ TEST("strict", function()
|
|||
|
||||
CHECK(not ok)
|
||||
end
|
||||
|
||||
do CASE "duplicate properties"
|
||||
local ok = pcall(function()
|
||||
create "TextLabel" {
|
||||
{
|
||||
Name = "foo"
|
||||
},
|
||||
{
|
||||
Name = "bar"
|
||||
}
|
||||
}
|
||||
end)
|
||||
|
||||
CHECK(not ok)
|
||||
|
||||
ok = pcall(function()
|
||||
create "TextLabel" {
|
||||
{
|
||||
Name = "foo",
|
||||
{
|
||||
Name = "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
end)
|
||||
|
||||
CHECK(ok)
|
||||
end
|
||||
end)
|
||||
|
||||
local ok = FINISH()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue