mirror of
https://github.com/centau/vide.git
synced 2026-08-20 23:01:37 +00:00
Compare commits
7 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3bfc65607 | ||
|
|
452060a533 | ||
|
|
5ed4c01940 | ||
|
|
19eaeb424f | ||
|
|
7deae9c9f6 | ||
|
|
3faa1ed701 | ||
|
|
820bda8eb1 |
12 changed files with 94 additions and 105 deletions
1
.github/workflows/wallypesde.yml
vendored
1
.github/workflows/wallypesde.yml
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
name: publish to wally and pesde
|
name: publish to wally and pesde
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
|
|
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## [0.4.1] - 2026-07-11
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `branch()` is now allowed to be used within a reactive scope.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- `create()` overloads. Supported is now only `create(class)(props)`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `create()` types in the new solver should now work without `::`.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
## [0.4.0] - 2026-01-17
|
## [0.4.0] - 2026-01-17
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,7 @@ export type source<T> = vide.source<T>
|
||||||
export type Source<T> = vide.Source<T>
|
export type Source<T> = vide.Source<T>
|
||||||
export type context<T> = vide.context<T>
|
export type context<T> = vide.context<T>
|
||||||
export type Context<T> = vide.Context<T>
|
export type Context<T> = vide.Context<T>
|
||||||
|
export type Instances = vide.Instances
|
||||||
|
export type Properties<T> = vide.Properties<T>
|
||||||
|
|
||||||
return vide
|
return vide
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name = "centau/vide"
|
name = "centau/vide"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
description = "A reactive Luau library for creating UI."
|
description = "A reactive Luau library for creating UI."
|
||||||
authors = ["centau"]
|
authors = ["centau"]
|
||||||
repository = "https://github.com/centau/vide"
|
repository = "https://github.com/centau/vide"
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ local function branch<T>(fn: () -> T): (() -> (), T)
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent = current.owner
|
local parent = current.owner
|
||||||
if not parent or parent.effect then
|
if not parent then
|
||||||
error(`current scope is not owned by a stable scope`, 0)
|
error(`current scope is not owned by a scope`, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local node = create_node(parent, false, false)
|
local node = create_node(parent, false, false)
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ local defaults = require "./defaults"
|
||||||
local apply = require "./apply"
|
local apply = require "./apply"
|
||||||
local flags = require "./flags"
|
local flags = require "./flags"
|
||||||
|
|
||||||
local ctor_cache = {} :: { [string]: (AnyProps) -> Instance }
|
local function create_constructor_for_class(class: string): ({ [unknown]: unknown }) -> Instance
|
||||||
local function lazy_init(_, class: string)
|
local function constructor(properties: { [unknown]: unknown }): Instance
|
||||||
local function ctor(properties: AnyProps): Instance
|
|
||||||
local ok, instance: Instance = pcall(Instance.new, class :: any)
|
local ok, instance: Instance = pcall(Instance.new, class :: any)
|
||||||
if not ok then error(`invalid class name { class }`, 0) end
|
if not ok then error(`invalid class name {class}`, 0) end
|
||||||
|
|
||||||
if flags.defaults then
|
if flags.defaults then
|
||||||
local default: { [string]: unknown }? = defaults[class]
|
local default: { [string]: unknown }? = defaults[class]
|
||||||
|
|
@ -23,31 +22,37 @@ local function lazy_init(_, class: string)
|
||||||
return apply(instance, properties)
|
return apply(instance, properties)
|
||||||
end
|
end
|
||||||
|
|
||||||
ctor_cache[class] = ctor
|
return constructor
|
||||||
return ctor
|
|
||||||
end
|
end
|
||||||
setmetatable(ctor_cache, { __index = lazy_init })
|
|
||||||
|
local constructor_cache = {} :: { [string]: ({ [unknown]: unknown }) -> Instance }
|
||||||
|
|
||||||
-- todo: remove support for different overloads
|
-- todo: remove support for different overloads
|
||||||
local function create(class_or_instance: string|Instance, properties: AnyProps?): ((AnyProps) -> Instance) | Instance
|
local function create(class_or_instance: string|Instance, properties: { [unknown]: unknown }?): unknown
|
||||||
if type(class_or_instance) ~= "string" and typeof(class_or_instance) ~= "Instance" then
|
if type(class_or_instance) ~= "string" and typeof(class_or_instance) ~= "Instance" then
|
||||||
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0)
|
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local ctor = if type(class_or_instance) == "string"
|
local constructor: ({ [unknown]: unknown }) -> Instance
|
||||||
then ctor_cache[class_or_instance]
|
if type(class_or_instance) == "string" then
|
||||||
else function(properties)
|
constructor = constructor_cache[class_or_instance]
|
||||||
local clone = class_or_instance:Clone()
|
if not constructor then
|
||||||
if not clone then error "attempt to clone a non-archivable instance" end
|
constructor = create_constructor_for_class(class_or_instance)
|
||||||
return apply(clone, properties)
|
constructor_cache[class_or_instance] = constructor
|
||||||
|
end
|
||||||
|
else
|
||||||
|
constructor = function(props)
|
||||||
|
local clone = assert(class_or_instance:Clone(), "attempt to clone a non-archivable instance")
|
||||||
|
return apply(clone, props)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return if properties
|
return if properties
|
||||||
then ctor(properties)
|
then constructor(properties)
|
||||||
else ctor
|
else constructor
|
||||||
end
|
end
|
||||||
|
|
||||||
type Instances = {
|
export type Instances = {
|
||||||
Folder: Folder,
|
Folder: Folder,
|
||||||
BillboardGui: BillboardGui,
|
BillboardGui: BillboardGui,
|
||||||
CanvasGroup: CanvasGroup,
|
CanvasGroup: CanvasGroup,
|
||||||
|
|
@ -65,18 +70,29 @@ type Instances = {
|
||||||
UIGradient: UIGradient,
|
UIGradient: UIGradient,
|
||||||
UIGridLayout: UIGridLayout,
|
UIGridLayout: UIGridLayout,
|
||||||
UIListLayout: UIListLayout,
|
UIListLayout: UIListLayout,
|
||||||
|
UISizeConstraint: UISizeConstraint,
|
||||||
|
UITextSizeConstraint: UITextSizeConstraint,
|
||||||
|
UIScale: UIScale,
|
||||||
|
UIPadding: UIPadding,
|
||||||
|
UIStroke: UIStroke,
|
||||||
|
UIFlexItem: UIFlexItem,
|
||||||
|
UIPageLayout: UIPageLayout,
|
||||||
|
UITableLayout: UITableLayout,
|
||||||
|
VideoFrame: VideoFrame,
|
||||||
|
ViewportFrame: ViewportFrame,
|
||||||
|
ProximityPrompt: ProximityPrompt,
|
||||||
|
UIDragDetector: UIDragDetector,
|
||||||
Camera: Camera,
|
Camera: Camera,
|
||||||
WorldModel: WorldModel,
|
WorldModel: WorldModel,
|
||||||
}
|
}
|
||||||
|
|
||||||
type function Properties(instance: type?)
|
export type function Properties(instance: type?)
|
||||||
local properties = types.newtable()
|
local properties = types.newtable()
|
||||||
|
|
||||||
while instance do
|
while instance do
|
||||||
for i, v in instance:properties() do
|
for i, v in instance:properties() do
|
||||||
local connector = v.read and v.read.tag == "table" and v.read:readproperty(types.singleton("Connect"))
|
local connector = v.read and v.read.tag == "table" and v.read:readproperty(types.singleton("Connect"))
|
||||||
if connector then
|
if connector then
|
||||||
if not connector then continue end
|
|
||||||
local params = connector:parameters().head
|
local params = connector:parameters().head
|
||||||
if not params then continue end
|
if not params then continue end
|
||||||
local listener = params[2]
|
local listener = params[2]
|
||||||
|
|
@ -98,13 +114,6 @@ type function Properties(instance: type?)
|
||||||
return properties
|
return properties
|
||||||
end
|
end
|
||||||
|
|
||||||
type AnyProps = { [any]: any }
|
type Create = <Name>(Name|keyof<Instances>|"") -> (Properties<index<Instances, Name>>) -> index<Instances, Name>
|
||||||
type Create = (
|
|
||||||
(Instance) -> (AnyProps) -> Instance
|
|
||||||
) & (
|
|
||||||
(string, AnyProps) -> Instance
|
|
||||||
) & (
|
|
||||||
<Name>(Name|keyof<Instances>) -> (Properties<index<Instances, Name>>) -> index<Instances, Name>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (create :: any) :: Create
|
return create :: Create
|
||||||
|
|
|
||||||
|
|
@ -110,4 +110,4 @@ return {
|
||||||
BorderColor3 = Color3.new(0, 0, 0),
|
BorderColor3 = Color3.new(0, 0, 0),
|
||||||
BorderSizePixel = 0
|
BorderSizePixel = 0
|
||||||
}
|
}
|
||||||
}
|
} :: { [string]: { [string]: unknown} }
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
assert(game, "when using vide outside of Roblox, require lib.luau instead")
|
assert(game, "when using vide outside of Roblox, require lib.luau instead")
|
||||||
|
|
||||||
local vide = require(script.lib)
|
local vide = require("@self/lib")
|
||||||
|
|
||||||
export type source<T> = vide.source<T>
|
export type source<T> = vide.source<T>
|
||||||
export type Source<T> = vide.Source<T>
|
export type Source<T> = vide.Source<T>
|
||||||
export type context<T> = vide.context<T>
|
export type context<T> = vide.context<T>
|
||||||
export type Context<T> = vide.Context<T>
|
export type Context<T> = vide.Context<T>
|
||||||
|
export type Instances = vide.Instances
|
||||||
|
export type Properties<T> = vide.Properties<T>
|
||||||
|
|
||||||
return vide
|
return vide
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local version = { major = 0, minor = 4, patch = 0 }
|
local version = { major = 0, minor = 4, patch = 1 }
|
||||||
|
|
||||||
local root = require "./root"
|
local root = require "./root"
|
||||||
local branch = require "./branch"
|
local branch = require "./branch"
|
||||||
|
|
@ -27,6 +27,8 @@ export type Source<T> = source.Source<T>
|
||||||
export type source<T> = Source<T>
|
export type source<T> = Source<T>
|
||||||
export type Context<T> = context.Context<T>
|
export type Context<T> = context.Context<T>
|
||||||
export type context<T> = Context<T>
|
export type context<T> = Context<T>
|
||||||
|
export type Instances = create.Instances
|
||||||
|
export type Properties<T> = create.Properties<T>
|
||||||
|
|
||||||
local function step(dt: number)
|
local function step(dt: number)
|
||||||
if game then debug.profilebegin("VIDE STEP") end
|
if game then debug.profilebegin("VIDE STEP") end
|
||||||
|
|
|
||||||
27
test/create-type-test.luau
Normal file
27
test/create-type-test.luau
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
local vide = require "../src/"
|
||||||
|
|
||||||
|
local count = vide.source(0)
|
||||||
|
|
||||||
|
vide.create("TextButton") {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
AnchorPoint = "bad value", -- should error
|
||||||
|
InvalidProperty = true, -- should error
|
||||||
|
|
||||||
|
Text = function()
|
||||||
|
return "count: " .. count()
|
||||||
|
end,
|
||||||
|
|
||||||
|
Size = function() -- should error
|
||||||
|
return "bad value"
|
||||||
|
end,
|
||||||
|
|
||||||
|
MouseEnter = function(x, y)
|
||||||
|
|
||||||
|
end,
|
||||||
|
|
||||||
|
Activated = "bad value", -- should error
|
||||||
|
|
||||||
|
vide.create "TextLabel" {},
|
||||||
|
|
||||||
|
function() end,
|
||||||
|
}
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
type Pseudo = {
|
|
||||||
Test: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type Instances = {
|
|
||||||
TextLabel: TextLabel,
|
|
||||||
TextButton: TextButton,
|
|
||||||
ImageLabel: ImageLabel,
|
|
||||||
Pseudo: Pseudo
|
|
||||||
}
|
|
||||||
|
|
||||||
type function Properties(instance: type?)
|
|
||||||
local properties = types.newtable()
|
|
||||||
|
|
||||||
while instance do
|
|
||||||
for i, v in instance:properties() do
|
|
||||||
local connector = v.read and v.read.tag == "table" and v.read:readproperty(types.singleton("Connect"))
|
|
||||||
if connector then
|
|
||||||
if not connector then continue end
|
|
||||||
local params = connector:parameters().head
|
|
||||||
if not params then continue end
|
|
||||||
local listener = params[2]
|
|
||||||
if not listener then continue end
|
|
||||||
properties:setproperty(i, types.optional(listener))
|
|
||||||
elseif v.write then
|
|
||||||
properties:setproperty(i, types.optional(types.unionof(
|
|
||||||
v.write,
|
|
||||||
types.newfunction({}, { head = { v.write } })
|
|
||||||
)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
instance = instance:readparent()
|
|
||||||
end
|
|
||||||
|
|
||||||
properties:setindexer(types.number, types.any)
|
|
||||||
|
|
||||||
return properties
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create<Name>(name: Name | keyof<Instances>): (Properties<index<Instances, Name>>) -> index<Instances, Name>
|
|
||||||
return function()
|
|
||||||
return "" :: any
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local vide = require "../src/"
|
|
||||||
local count = vide.source(0)
|
|
||||||
create("TextButton") { -- todo: why is `:: "TextButton"` not necessary?
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
AnchorPoint = "bad value", -- should error
|
|
||||||
InvalidProperty = true, -- should error
|
|
||||||
|
|
||||||
Text = function()
|
|
||||||
return "count: " .. count()
|
|
||||||
end,
|
|
||||||
|
|
||||||
Size = function() -- should error
|
|
||||||
return "bad value"
|
|
||||||
end,
|
|
||||||
|
|
||||||
MouseEnter = function(x, y)
|
|
||||||
|
|
||||||
end,
|
|
||||||
|
|
||||||
Activated = "bad value", -- should error
|
|
||||||
|
|
||||||
create "TextLabel" {},
|
|
||||||
function() end,
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name = "centau/vide"
|
name = "centau/vide"
|
||||||
description = "A reactive Luau library for creating UI. "
|
description = "A reactive Luau library for creating UI. "
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
include = ["default.project.json", "LICENSE", "src"]
|
include = ["default.project.json", "LICENSE", "src"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue