From d10ef56925051beb44805e4f993a39b993162019 Mon Sep 17 00:00:00 2001 From: James <119134081+2jammers@users.noreply.github.com> Date: Sat, 14 Dec 2024 00:10:52 -0600 Subject: [PATCH] 0.3 untested --- src/Apply.luau | 2 +- src/{Changed.luau => Change.luau} | 0 src/Clean.luau | 3 +- src/Computed.luau | 107 +++++++++++++++++++++ src/Effect.luau | 119 ------------------------ src/Is.luau | 17 ---- src/New.luau | 19 +++- src/Spring.luau | 33 ++++--- src/State.luau | 30 ++---- src/Types.luau | 38 ++++---- src/Update.luau | 2 +- src/init.luau | 9 +- tests/client/UI.client.luau | 40 ++++---- tests/client/components/Background.luau | 13 ++- 14 files changed, 202 insertions(+), 230 deletions(-) rename src/{Changed.luau => Change.luau} (100%) create mode 100644 src/Computed.luau delete mode 100644 src/Effect.luau delete mode 100644 src/Is.luau diff --git a/src/Apply.luau b/src/Apply.luau index 340275c..de45586 100644 --- a/src/Apply.luau +++ b/src/Apply.luau @@ -14,7 +14,7 @@ return function(instance: Instance, property: any, value: any) end elseif type(property) == "string" then if type(value) == "table" then - (value :: Types.Constructor):_Bind(property, instance) + (value :: Types.Constructor):_Bind(property, instance) else (instance :: any)[property] = value end diff --git a/src/Changed.luau b/src/Change.luau similarity index 100% rename from src/Changed.luau rename to src/Change.luau diff --git a/src/Clean.luau b/src/Clean.luau index be21004..314a442 100644 --- a/src/Clean.luau +++ b/src/Clean.luau @@ -2,7 +2,6 @@ local Root = script.Parent local Action = require(Root.Action) local Types = require(Root.Types) -local Is = require(Root.Is) local Debugger = require(Root.Parent.roblox_packages.debugger) -- Module @@ -17,7 +16,7 @@ return function(values: { any }): Types.Action Debugger.Assert(type(values) == "table", "InvalidType", "table", type(values)) instance.Destroying:Once(function() for _, value in values do - if Is.Constructor(value) or typeof(value) == "Instance" then + if (type(value) == "table" and value._Bind) or typeof(value) == "Instance" then value:Destroy() elseif typeof(value) == "RBXScriptConnection" then value:Disconnect() diff --git a/src/Computed.luau b/src/Computed.luau new file mode 100644 index 0000000..4654fb8 --- /dev/null +++ b/src/Computed.luau @@ -0,0 +1,107 @@ +-- Variables +local Root = script.Parent +local Types = require(Root.Types) +local Debugger = require(Root.Parent.roblox_packages.debugger) + +local Class = {} + +-- Functions + +--[=[ + Gets the current value of a value object for use within the compute. + + [Learn More](https://luminlabsdev.github.io/ui-framework/api/computed/#use) +]=] +local function Use(value: Types.State | any) + if type(value) == "table" then + return value:Get() + else + return value + end +end + +local function Listener(self: Types.Computed) + return function() + self._Value = self._Processor(Use) + + if self._Instances ~= nil then + for prop, instance in self._Instances do + (instance :: any)[prop] = self._Value + end + end + end +end + +--[=[ + Gets the current value of the compute object. + + [Learn More](https://luminlabsdev.github.io/ui-framework/api/computed/#get) +]=] +function Class.Get(self: Types.Computed): any + return self._Value +end + +function Class._Bind(self: Types.Computed, prop: string, instance: Instance) + if self._Instances == nil then + self._Instances = {} + end + + (self._Instances :: {})[prop] = instance; + (instance :: any)[prop] = self._Value +end + +--[=[ + Destroys the compute object. + + [Learn More](https://luminlabsdev.github.io/ui-framework/api/computed/#destroy) +]=] +function Class.Destroy(self: Types.Computed) + if self._Dependencies then + for dependency: any, disconnect: any in self._Dependencies do + disconnect() + self._Dependencies[dependency] = nil + end + end + + table.clear(self :: any) + setmetatable(self :: any, nil) +end + +-- Module + +--[=[ + Creates a new computed value, which changes the final value when a dependency is changed. + + [Learn More](https://luminlabsdev.github.io/ui-framework/api/#computed) +]=] +return function( + processor: (use: (value: Types.State | any) -> ()) -> (), + dependencies: { Types.State | Types.Spring }? +): Types.ComputedExport + local self = setmetatable({}, { __index = Class }) + + self._Type = "Computed" + self._Processor = processor + self._Value = processor(Use) + self._Instances = {} + self._Dependencies = nil + + if dependencies then + local Type = type(dependencies) + Debugger.Assert(Type == "table", "InvalidType", "table", Type) + self._Dependencies = {} :: any + for _, dependency in dependencies do + if type(dependency) == "table" then + if dependency._Type == "Spring" then + (self._Dependencies :: any)[dependency] = (dependency :: any)._Goal:Listen(Listener(self :: any)) + elseif dependency._Type == "State" then + (self._Dependencies :: any)[dependency] = (dependency :: any):Listen(Listener(self :: any)) + else + Debugger.Fatal("InvalidType", "state or spring", type(dependency)) + end + end + end + end + + return self :: any +end diff --git a/src/Effect.luau b/src/Effect.luau deleted file mode 100644 index a204c51..0000000 --- a/src/Effect.luau +++ /dev/null @@ -1,119 +0,0 @@ --- Variables -local Root = script.Parent.Parent -local Debugger = require(Root.Parent.roblox_packages.debugger) -local Utility = require(Root.Utility) -local Is = require(Root.Is) -local Types = require(Root.Types) - -local Class = {} - --- Functions - ---[=[ - Gets the current value of a value object for use within the compute. - - [Learn More](https://luminlabsdev.github.io/ui-framework/api/compute/#use) -]=] -local function use(value: Types.Constructor | any) - if Is.Constructor(value) then - return (value :: Types.Constructor):Get() - else - return value - end -end - -local function ListenerFunction(self: Types.Compute) - return function() - self._Result = self._Processor(use) - - if self._Instances ~= nil then - for prop, instance in self._Instances do - (instance :: any)[prop] = self._Result - end - end - end -end - ---[=[ - Gets the current value of the compute object. - - [Learn More](https://luminlabsdev.github.io/ui-framework/api/compute/#get) -]=] -function Class.Get(self: Types.Compute): any - return self._Result -end - -function Class._Bind(self: Types.Compute, prop: string, instance: Instance) - if self._Instances == nil then - self._Instances = {} - end - - (self._Instances :: {})[prop] = instance; - (instance :: any)[prop] = self._Result -end - ---[=[ - Destroys the compute object. - - [Learn More](https://luminlabsdev.github.io/ui-framework/api/compute/#destroy) -]=] -function Class.Destroy(self: Types.Compute): nil - if self._Dependencies then - for dependency, disconnect in self._Dependencies do - disconnect() - self._Dependencies[dependency] = nil - end - end - - table.clear(self :: any) - setmetatable(self :: any, nil) - - return nil -end - --- Module - ---[=[ - Creates a new compute which is similar to a value but will compute the value on change. - - [Learn More](https://luminlabsdev.github.io/ui-framework/api/#compute) -]=] -return function( - processor: (get: typeof(use)) -> (), - dependencies: { Types.State | Types.Spring | Types.Constructor }? -): Types.ComputeExport - local Dependencies = nil :: { [Types.State | Types.Spring | Types.Constructor]: () -> () }? - local self = setmetatable({}, { __index = Class }) - - self._Type = "compute" - self._Processor = processor - self._Dependencies = Dependencies - self._Result = self._Processor(use) - self._Instances = {} - - if dependencies then - local TypeOfDependencies = type(dependencies) - - if TypeOfDependencies == "table" then - Dependencies = { - -- [State]: DisconnectListenerFunction, - } :: {} - - for _, v in dependencies do - if Is.Constructor(v :: any) then - if (v :: Types.Constructor)._Type == "state" then - (Dependencies :: {})[v] = (v :: any):Listen(ListenerFunction(self :: any)) - elseif (v :: Types.Constructor)._Type == "spring" then - (Dependencies :: {})[v] = (v :: any)._State:Listen(ListenerFunction(self :: any)) - else - Debugger.Warn("ConstructorNotSupported", v._Type) - end - end - end - else - Debugger.Warn("TypeMismatch", "table", TypeOfDependencies) - end - end - - return self :: any -end diff --git a/src/Is.luau b/src/Is.luau deleted file mode 100644 index 37eb06b..0000000 --- a/src/Is.luau +++ /dev/null @@ -1,17 +0,0 @@ --- Variables -local Types = require(script.Parent.Types) - --- Functions -local function Constructor(item: Types.Constructor) - return if type(item) == "table" and item._Type and item._Bind and item.Get then true else false -end - -local function Action(item: Types.Action) - return if type(item) == "table" and item.Name and item.Apply then true else false -end - --- Module -return table.freeze({ - Constructor = Constructor, - Action = Action, -}) diff --git a/src/New.luau b/src/New.luau index f8b15f8..b8e958a 100644 --- a/src/New.luau +++ b/src/New.luau @@ -7,15 +7,24 @@ local Defaults = require(Root.Defaults) -- Module --[=[ - Creates a new instance or component with the provided children and properties. + Clones or creates a new isntance with the provided properties. Default properties are not + set on clones. [Learn More](https://luminlabsdev.github.io/ui-framework/api/#new) ]=] -return function(class: string, props: { any }?): Instance - local Success, New = pcall(Instance.new, class) -- Create the instance wrapped inside pcall +return function(class: string | Instance, props: { any }?): Instance + Debugger.Assert(type(class) == "string" or typeof(class) == "Instance", "FailedCreation", class, "") + + local Success, New + + if type(class) == "string" then + Success, New = pcall(Instance.new, class) + else + Success, New = true, class:Clone() + end if Success then - if Defaults[class] then + if type(class) == "string" and Defaults[class] then for prop, value in Defaults[class] do (New :: any)[prop] = value end @@ -33,7 +42,7 @@ return function(class: string, props: { any }?): Instance New.Parent = (props :: any).Parent end end - else -- If it wasn't successful, error it because instance creation has to be complete. + else Debugger.Fatal("FailedCreation", class, New) end return New diff --git a/src/Spring.luau b/src/Spring.luau index 84aed5d..fe06808 100644 --- a/src/Spring.luau +++ b/src/Spring.luau @@ -1,10 +1,6 @@ -- Variables -local Root = script.Parent.Parent -local Packages = script.Parent.Parent.Parent.roblox_packages - -local Debugger = require(Packages.debugger) -local Utils = require(Root.Utility) -local Is = require(Root.Is) +local Root = script.Parent +local Debugger = require(Root.Parent.roblox_packages.debugger) local Types = require(Root.Types) local Spr = require(script.Parent.Spr) @@ -28,7 +24,7 @@ local Animatable = { function Class._Bind(self: Types.Spring, prop: string, instance: Instance) (instance :: any)[prop] = self._Goal:Get() - self._Animated = instance + table.insert(self._Instances, instance) self._Goal:Listen(function(new) Spr.target(instance, self._Damping, self._Frequency, { [prop] = new, @@ -37,12 +33,25 @@ function Class._Bind(self: Types.Spring, prop: string, instance: Instance) end --[[ - Gets the current value that the spring is moving. + Stops the current spring from moving, leaving it at its current position. + + [Learn More](https://luminlabsdev.github.io/ui-framework/api/spring/#stop) +]] +function Class.Stop(self: Types.Spring) + if self._Instances then + for _, instance in self._Instances do + Spr.stop(instance) + end + end +end + +--[[ + Gets the end goal of the spring. [Learn More](https://luminlabsdev.github.io/ui-framework/api/spring/#get) ]] function Class.Get(self: Types.Spring): Types.Animatable - return 1 + return self._Goal:Get() end --[[ @@ -63,14 +72,14 @@ end ]=] return function(goal: Types.State, damping: number?, frequency: number?): Types.SpringExport Debugger.Assert(table.find(Animatable, typeof((goal :: any):Get())), "NotAnimatable", typeof((goal :: any):Get())) - Debugger.Assert(Is.Constructor(goal :: any), "InvalidType", "State", type(goal)) + Debugger.Assert(type(goal) == "table" and goal._Type == "State", "InvalidType", "State", type(goal)) local self = setmetatable({}, { __index = Class }) - self._Type = "spring" + self._Type = "Spring" self._Damping = damping or 1 self._Frequency = frequency or 1 - self._Animated = nil + self._Instances = {} self._Goal = goal return self :: any diff --git a/src/State.luau b/src/State.luau index 0baaaa1..30da72b 100644 --- a/src/State.luau +++ b/src/State.luau @@ -13,7 +13,7 @@ local Class = {} [Learn More](https://luminlabsdev.github.io/ui-framework/api/state/#get) ]=] function Class.Get(self: Types.State): any - return self._State + return self._Value end --[=[ @@ -22,22 +22,17 @@ end [Learn More](https://luminlabsdev.github.io/ui-framework/api/state/#set) ]=] function Class.Set(self: Types.State, newValue: T): T - local Type = type(newValue) - - if Type == "table" then - Debugger.Warn("RestrictedType", Type) - return newValue - end - - local OldValue = self._State + Debugger.Assert(type(newValue) ~= "table", "InvalidType", "any", "table") + + local OldValue = self._Value -- Normal checks - if self._State == newValue then + if self._Value == newValue then return newValue end -- Setting the actual value and triggering the listeners - self._State = newValue + self._Value = newValue -- Loop through the listeners table and call every function each of them have. for _, fn in self._Listeners do @@ -69,7 +64,7 @@ function Class.Listen(self: Types.State, listener: (new: any, old: any) -> ()): end function Class._Bind(self: Types.State, prop: string, instance: Instance) - (instance :: any)[prop] = self._State + (instance :: any)[prop] = self._Value ;(self :: any):Listen(function(newValue) (instance :: any)[prop] = newValue @@ -94,17 +89,12 @@ end [Learn More](https://luminlabsdev.github.io/ui-framework/api/#state) ]=] return function(initial: any): Types.StateExport - local Type = type(initial) - - if Type == "table" then - Debugger.Warn("RestrictedType", Type) - initial = initial or nil - end + Debugger.Assert(type(initial) ~= "table", "InvalidType", "any", "table") local self = setmetatable({}, { __index = Class }) - self._Type = "state" - self._State = initial + self._Type = "State" + self._Value = initial self._Listeners = {} return self :: any diff --git a/src/Types.luau b/src/Types.luau index 64aaa15..015728a 100644 --- a/src/Types.luau +++ b/src/Types.luau @@ -1,69 +1,65 @@ export type Animatable = boolean | number - | BrickColor | CFrame | Vector3 | Vector2 | UDim2 | UDim | Color3 - | Vector3int16 - | Vector2int16 | ColorSequence | NumberSequence | NumberRange - | Rect export type Action = (instance: Instance, ...any) -> () -export type Constructor = { +export type Constructor = { + _Type: string, _Bind: (self: T, prop: string, instance: Instance) -> (), - _Type: string, - Get: (self: T) -> any, + Get: (self: T) -> U, Destroy: (self: T) -> (), } export type StateExport = { Set: (self: StateExport, value: any) -> State, Listen: (self: StateExport, listener: (new: any, old: any) -> ()) -> () -> (), -} & Constructor +} & Constructor export type State = typeof(setmetatable( {} :: { - _Type: "state", - _State: any, + _Type: "State", + _Value: any, _Listeners: { (newValue: any, oldValue: any) -> () }, }, {} :: { __index: StateExport } )) export type SpringExport = { - _Update: (self: SpringExport, linearTargetPosition: { number }, delta: number) -> (), -} & Constructor + Stop: (self: SpringExport) -> (), +} & Constructor export type Spring = typeof(setmetatable( {} :: { - _Type: "spring", + _Type: "Spring", _Goal: StateExport, _Damping: number, _Frequency: number, - _Animated: Instance, + _Instances: {Instance}, }, {} :: { __index: SpringExport } )) -export type ComputeExport = Constructor +export type ComputedExport = Constructor -export type Compute = typeof(setmetatable( +export type Computed = typeof(setmetatable( {} :: { - _Type: "compute", - _Processor: (get: (value: Constructor | any) -> ()) -> (), - _Dependencies: { [State | Spring | Constructor]: () -> () }?, - _Result: any, + _Type: "Computed", + _Processor: (use: (value: State | any) -> ()) -> (), + _Value: any, + _Dependencies: { State | Spring }, _Instances: { [string]: Instance }?, }, - {} :: { __index: ComputeExport } + {} :: { __index: ComputedExport } )) return {} diff --git a/src/Update.luau b/src/Update.luau index 13cc308..13e9b0b 100644 --- a/src/Update.luau +++ b/src/Update.luau @@ -5,7 +5,7 @@ local Apply = require(Root.Apply) -- Module --[=[ - Updates an instance, writing to its properties or adding children. + Updates an instance, writing to its properties. [Learn More](https://luminlabsdev.github.io/ui-framework/api/#update) ]=] diff --git a/src/init.luau b/src/init.luau index cf7da0c..87b0e1e 100644 --- a/src/init.luau +++ b/src/init.luau @@ -7,7 +7,7 @@ local Debugger = require(Packages.debugger) export type Action = Types.Action export type State = Types.State export type Spring = Types.Spring -export type Compute = Types.Compute +export type Computed = Types.Computed export type Animatable = Types.Animatable -- Module @@ -19,15 +19,12 @@ Debugger.SetMetadata({ return table.freeze({ New = require(script.New), Update = require(script.Update), - State = require(script.State), - Compute = require(script.Compute), - + Computed = require(script.Computed), Spring = require(script.Spring), - Action = require(script.Action), Event = require(script.Event), - Changed = require(script.Changed), + Changed = require(script.Change), Tags = require(script.Tags), Clean = require(script.Clean), }) diff --git a/tests/client/UI.client.luau b/tests/client/UI.client.luau index 2888327..c0945e0 100644 --- a/tests/client/UI.client.luau +++ b/tests/client/UI.client.luau @@ -12,53 +12,47 @@ local MyComponent = require(script.Parent.components.Background) -- VARIABLES > local Player = Players.LocalPlayer local New = Framework.New +local Event = Framework.Event +local Changed = Framework.Changed local State = Framework.State("Hello") local Goal = Framework.State(UDim2.fromScale(0.5, 1)) local Spring = Framework.Spring(Goal :: any, 0.5, 5) local c = 1 -function e(rizz) - -end - -- UI New("ScreenGui", { Name = "MenuUI", IgnoreGuiInset = true, Parent = Player:WaitForChild("PlayerGui"), - e(1), -}, { - Background = New("Frame", { + + New("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromScale(1, 1), ZIndex = 1, }), - ComponentBackgroundChildren = New(MyComponent, { + + MyComponent({ Color = Color3.fromRGB(151, 71, 71), - Position = Spring :: any, - }, { - New("TextLabel", { - Text = State, - BackgroundColor3 = Color3.fromRGB(128, 95, 95), - Position = UDim2.fromScale(0.5, 0.5), - ZIndex = 3, - }), + Position = Spring, + Text = State, }), - ComponentBackground = New(MyComponent, { + + MyComponent({ Color = Color3.fromRGB(103, 196, 246), Position = UDim2.fromScale(0.5, 0.1), }), + New("TextButton", { - [Framework.Event "Activated"] = function() + Event("Activated", function() c+=1 State:Set(`Hello {c}`) - Goal:Set(UDim2.fromScale(0.5, if c%2 == 0 then 1 else 1.5)) - end, + Goal:Set(UDim2.fromScale(0.5, if c%2 == 0 then 0.9 else 0.6)) + end), - [Framework.Changed "Name"] = function(new_name: any) - print(new_name) - end, + Changed("Name", function(new: any) + print(new) + end), Text = "Test", Position = UDim2.fromScale(0.5, 0.2), diff --git a/tests/client/components/Background.luau b/tests/client/components/Background.luau index 295c15f..4d075d0 100644 --- a/tests/client/components/Background.luau +++ b/tests/client/components/Background.luau @@ -12,9 +12,9 @@ local New = Framework.New type Background = { Color: Color3, - Position: UDim2?, + Position: any?, Size: UDim2?, - [any]: any, + Text: any?, } return function(props: Background) @@ -24,5 +24,12 @@ return function(props: Background) AnchorPoint = Vector2.new(0.5, 0.5), Size = props.Size or UDim2.fromScale(0.5, 0.5), ZIndex = 2, - }, props.Children) + + New("TextLabel", { + Text = props.Text, + BackgroundColor3 = Color3.fromRGB(128, 95, 95), + Position = UDim2.fromScale(0.5, 0.5), + ZIndex = 3, + }), + }) end