UpdateInPlace.luau
Luau, 79 lines from UI overview
--!strict
-- UpdateInPlace: refresh a list of rows without destroying them.
-- A panel that destroyed and re-cloned every row each second dropped any tap whose press and
-- release straddled the refresh (Activated needs both on the same instance), and reset hover/press
-- animations. Rows are now keyed: reused when the key set is unchanged, only labels update.
local UpdateInPlace = {}
export type Item = { key: string, locked: boolean?, [string]: any }
export type List = {
container: GuiObject,
template: GuiObject,
fill: (row: GuiObject, item: Item) -> (), -- set texts/colours/bars ONLY (no Instance.new)
wire: (row: GuiObject, item: Item) -> (), -- connect buttons ONCE per row; read item via row attribute
rows: { [string]: GuiObject },
signature: string,
}
function UpdateInPlace.new(container: GuiObject, template: GuiObject, fill, wire): List
return { container = container, template = template, fill = fill, wire = wire, rows = {}, signature = "" }
end
local function sig(items: { Item }): string
local parts = table.create(#items)
for i, it in items do
parts[i] = it.key .. (if it.locked then "#L" else "")
end
return table.concat(parts, "|")
end
function UpdateInPlace.render(list: List, items: { Item })
local s = sig(items)
if s ~= list.signature then
-- the set changed: keep rows whose key survives, add new ones, drop the rest
local keep = {}
for i, it in items do
local row = list.rows[it.key]
if not row then
row = list.template:Clone()
row.Name = "Row_" .. it.key
row:SetAttribute("Key", it.key)
list.wire(row, it)
row.Parent = list.container
end
row.LayoutOrder = i
keep[it.key] = row
end
for key, row in list.rows do
if not keep[key] then row:Destroy() end
end
list.rows = keep
list.signature = s
end
-- every refresh: only cheap property writes
for _, it in items do
list.fill(list.rows[it.key], it)
end
end
--[[ Usage
local sell = UpdateInPlace.new(panel.List, templates.SellRow,
function(row, it)
(row :: any).Name_.Text = it.name
(row :: any).Price.Text = NumberFormat.cash(it.price)
end,
function(row, it)
local key = row:GetAttribute("Key")
(row :: any).SellButton.Activated:Connect(function() Remotes.Sell:FireServer(key) end)
end)
-- refresh only while visible, and only when state changes (plus a 1 s tick for timers)
State.changed:Connect(function()
if panel.Visible then UpdateInPlace.render(sell, buildItems(State.get())) end
end)
]]
return UpdateInPlace