@rbxts/rl 0.1.11
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/COPYING +674 -0
- package/PESDE_README.md +39 -0
- package/README.md +56 -0
- package/base_pesde.toml +10 -0
- package/build.luau +30 -0
- package/default.project.json +6 -0
- package/docs/api_ref.md +202 -0
- package/docs/concepts.md +21 -0
- package/package.json +16 -0
- package/pesde.toml +14 -0
- package/publish.sh +3 -0
- package/rokit.toml +4 -0
- package/src/index.d.ts +74 -0
- package/src/init.luau +247 -0
- package/wally.toml +9 -0
package/src/init.luau
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
|
|
3
|
+
local rl = {}
|
|
4
|
+
|
|
5
|
+
export type state<T> = (() -> T) & ((new_value : T, force : boolean?) -> T) & {
|
|
6
|
+
value : T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type callback<T> = (old_value : T, state_i : number?) -> any
|
|
10
|
+
|
|
11
|
+
local batching = false
|
|
12
|
+
-- {[state] : {{[callback] = true}, old_value, state_i}}
|
|
13
|
+
local batched : {[state<unknown>] : {any}} = {}
|
|
14
|
+
|
|
15
|
+
local current_callback : callback<unknown>? = nil
|
|
16
|
+
local effects : {[callback<unknown>] : number} = {}
|
|
17
|
+
|
|
18
|
+
local function newindex_err()
|
|
19
|
+
error("Cannot mutate a State by editing it's .value!")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
local function check<T>(v : T) : T
|
|
23
|
+
local vt = type(v)
|
|
24
|
+
assert(vt == "function", `Expected type 'function', got '{vt}'`)
|
|
25
|
+
return v
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
local warn = warn or function(msg : string) : ()
|
|
29
|
+
print(`\x1b[33m{msg}\x1b[0m`)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
--[=[
|
|
33
|
+
Disconnects all Effects.
|
|
34
|
+
]=]
|
|
35
|
+
function rl.cleanup() : ()
|
|
36
|
+
table.clear(effects)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
--[=[
|
|
40
|
+
Creates a State.
|
|
41
|
+
|
|
42
|
+
@param value T -- Initial value.
|
|
43
|
+
@param force boolean? -- Whether Effects connected to this State will trigger, despite the fact that the value didn't change.
|
|
44
|
+
@return state<T> -- State. Call with no arguments for reading with tracking, call with arguments for writing (2nd argument is for forcing), read .value field for reading without tracking.
|
|
45
|
+
]=]
|
|
46
|
+
function rl.state<T>(value : T?, force : boolean?) : state<T>
|
|
47
|
+
local callbacks : {[callback<unknown>] : number} = {}
|
|
48
|
+
local self = newproxy(true)
|
|
49
|
+
local mt = getmetatable(self)
|
|
50
|
+
mt.__newindex = newindex_err
|
|
51
|
+
mt.__index = function(_, k)
|
|
52
|
+
assert(k == "value", `Expected key 'value', got '{k}'!`)
|
|
53
|
+
return value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
mt.__call = function(... : T) : T
|
|
57
|
+
local argv = {...}
|
|
58
|
+
local old_value = value
|
|
59
|
+
local new_value = argv[2]
|
|
60
|
+
|
|
61
|
+
if #argv == 1 then
|
|
62
|
+
if current_callback and not callbacks[current_callback] then
|
|
63
|
+
effects[current_callback] += 1
|
|
64
|
+
callbacks[current_callback] = effects[current_callback]
|
|
65
|
+
end
|
|
66
|
+
elseif value :: any ~= new_value or force == true or argv[3] == true then
|
|
67
|
+
value = new_value
|
|
68
|
+
|
|
69
|
+
for callback, state_i in callbacks do
|
|
70
|
+
if not effects[callback] then
|
|
71
|
+
callbacks[callback] = nil
|
|
72
|
+
continue
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if batching then
|
|
76
|
+
local state_batch = batched[self]
|
|
77
|
+
if state_batch then
|
|
78
|
+
local x = state_batch[1]
|
|
79
|
+
if not x[callback] then
|
|
80
|
+
x[callback] = true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
state_batch[2] = old_value
|
|
84
|
+
else
|
|
85
|
+
batched[self] = {{[callback] = true}, old_value, state_i}
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
callback(old_value, state_i)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
return value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
return self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
--[=[
|
|
100
|
+
Batches any State write operation up until the function is done running.
|
|
101
|
+
|
|
102
|
+
@param f () -> any -- The function.
|
|
103
|
+
]=]
|
|
104
|
+
function rl.batch(f : () -> any) : ()
|
|
105
|
+
batching = true
|
|
106
|
+
f()
|
|
107
|
+
|
|
108
|
+
for _, result in batched do
|
|
109
|
+
for callback in result[1] do
|
|
110
|
+
callback(result[2], result[3])
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
batching = false
|
|
115
|
+
table.clear(batched :: {any})
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
--[=[
|
|
119
|
+
Creates an Effect, a binding of the given function to the event of States read (by calling) within it being updated.
|
|
120
|
+
|
|
121
|
+
@param callback callback<T> -- The function to be bound.
|
|
122
|
+
@return disconnect () -> () -- Function which disconnects the Effect from the States.
|
|
123
|
+
]=]
|
|
124
|
+
function rl.effect<T>(callback : callback<T>) : () -> ()
|
|
125
|
+
if current_callback then
|
|
126
|
+
warn("Nested effect")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
effects[callback] = 0
|
|
130
|
+
current_callback = callback
|
|
131
|
+
callback()
|
|
132
|
+
current_callback = nil
|
|
133
|
+
|
|
134
|
+
return function()
|
|
135
|
+
if not effects[callback] then
|
|
136
|
+
warn("This effect is already disconnected!")
|
|
137
|
+
return
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
effects[callback] = nil
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
--[=[
|
|
145
|
+
Returns a function which calls the given functions.
|
|
146
|
+
Any non-function or function which precedes a "true" value by 2 indices is going to be skipped (the second happens because of derives).
|
|
147
|
+
This is intended to be used for disconnecting Effects in bulk.
|
|
148
|
+
|
|
149
|
+
@param ... () -> () -- The functions.
|
|
150
|
+
@return f () -> () -- The function caller.
|
|
151
|
+
]=]
|
|
152
|
+
function rl.scope<T>(... : T) : () -> ()
|
|
153
|
+
local t = {...}
|
|
154
|
+
return function()
|
|
155
|
+
for i = 1, #t do
|
|
156
|
+
local v = t[i]
|
|
157
|
+
if type(v) ~= "function" or t[i + 2] == true then continue end
|
|
158
|
+
v()
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
--[=[
|
|
164
|
+
Wraps an Effect around the given callback which sets a newly created State's value
|
|
165
|
+
to be the return value of the callback.
|
|
166
|
+
Returns the created State, the Effect's disconnect function and a flag which can be ignored.
|
|
167
|
+
|
|
168
|
+
This is useful for States which depend on the value of another State, since their value will
|
|
169
|
+
be updated only when the State they depend on is also updated, and not every time it is read,
|
|
170
|
+
as how would happen with a function which's return value depends on a State.
|
|
171
|
+
|
|
172
|
+
@param callback () -> U -- the callback
|
|
173
|
+
@return derived_state state<U> -- the State that was created
|
|
174
|
+
@return disconnect () -> -- the created Effect's disconnect function
|
|
175
|
+
@return flag true -- a flag used internally by rl.scope(); can be ignored.
|
|
176
|
+
]=]
|
|
177
|
+
function rl.derive<T, U>(callback : () -> U) : (state<U>, () -> (), true)
|
|
178
|
+
local cache = rl.state()
|
|
179
|
+
return cache, rl.effect(function()
|
|
180
|
+
cache(callback())
|
|
181
|
+
end), true
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
--[=[
|
|
185
|
+
Branching function to be used within Effects.
|
|
186
|
+
Pass a condition to be checked, and after it a function to be run if it's successful.
|
|
187
|
+
The last value, if a function, and if not after a condition, will be threated as the "else" branch.
|
|
188
|
+
]=]
|
|
189
|
+
function rl.branch(...) : ()
|
|
190
|
+
local args = table.pack(...)
|
|
191
|
+
local n = args.n
|
|
192
|
+
assert(n >= 2, "Expected 2 or more arguments!")
|
|
193
|
+
|
|
194
|
+
local i = 1
|
|
195
|
+
while i + 1 <= n do
|
|
196
|
+
if args[i] or current_callback then
|
|
197
|
+
check(args[i + 1])()
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
i += 2
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if i <= n then
|
|
204
|
+
check(args[i])()
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
--[=[
|
|
209
|
+
Creates an effect for every function or State within the given table, which updates the key to either the return value of the function, or the value of the State.
|
|
210
|
+
Each Effect's disconnect function gets put in the table as t["disconnect_"..key], when key is a string, or as t["disconnect"_..i], where i is the numerical index at which the key was iterated over.
|
|
211
|
+
|
|
212
|
+
@param t {[K] : V} -- The table.
|
|
213
|
+
@return t {[K] : V} -- The same table.
|
|
214
|
+
@return disconnect_all () -> () -- Function which disconnects and eliminates each disconnect function of the created Effects.
|
|
215
|
+
]=]
|
|
216
|
+
function rl.mirror<K, V>(t : {[K] : V}) : ({[K] : V}, () -> ())
|
|
217
|
+
local disconnects : {[number | string] : () -> ()} = {}
|
|
218
|
+
|
|
219
|
+
for k, v in t do
|
|
220
|
+
local v_type = type(v)
|
|
221
|
+
if v_type == "function" or (v_type == "table" and (v :: {[any] : any}).value) then
|
|
222
|
+
local disconnect = rl.effect(function()
|
|
223
|
+
t[k] = v()
|
|
224
|
+
end)
|
|
225
|
+
|
|
226
|
+
local k_type = type(k)
|
|
227
|
+
if k_type == "string" then
|
|
228
|
+
disconnects[k] = disconnect
|
|
229
|
+
else
|
|
230
|
+
table.insert(disconnects :: {any}, disconnect)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
for name, disconnect in disconnects do
|
|
236
|
+
t["disconnect_"..name] = disconnect
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
return t, function()
|
|
240
|
+
for name, disconnect in disconnects do
|
|
241
|
+
disconnect()
|
|
242
|
+
t["disconnect_"..name] = nil
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
return rl
|
package/wally.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "wiam77/rl"
|
|
3
|
+
description = "Simple reactive library for Luau. Docs: https://pesde.dev/packages/wiam/rl/latest/any/docs/concepts"
|
|
4
|
+
version = "0.1.11"
|
|
5
|
+
license = "GPL-3.0-or-later"
|
|
6
|
+
registry = "https://github.com/UpliftGames/wally-index"
|
|
7
|
+
realm = "shared"
|
|
8
|
+
include = ["src", "src/*", "wally.toml", "COPYING", "README.md", "default.project.json"]
|
|
9
|
+
exclude = ["**"]
|