jecs/modules/Jabby/ui/components/display/resizeable_bar.luau

207 lines
6.4 KiB
Text
Raw Normal View History

2026-02-18 00:29:34 +00:00
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local vide = require(script.Parent.Parent.Parent.Parent.vide)
local theme = require(script.Parent.Parent.Parent.util.theme)
local padding = require(script.Parent.Parent.util.padding)
local rounded_frame = require(script.Parent.Parent.util.rounded_frame)
local typography = require(script.Parent.typography)
local create = vide.create
local source = vide.source
local derive = vide.derive
local effect = vide.effect
local cleanup = vide.cleanup
local indexes = vide.indexes
local changed = vide.changed
local untrack = vide.untrack
local MAX_PIXELS_OFFSET = 32
local BEFORE = source(0)
local AFTER = source(1)
type ResizeableBar = {
meaning: () -> { string },
min_sizes: (() -> { vide.source<number>? })?,
sizes: vide.source<{ vide.source<number> }>,
suggested_sizes: { number }?,
splits: (vide.source<{ vide.source<number> }>)?,
base_splits: { number }?
}
return function(props: ResizeableBar)
local meaning = props.meaning
local sizes = props.sizes
local min_sizes = props.min_sizes or source({}) :: never
local suggested_sizes = props.suggested_sizes or {}
local absolute_size = source(Vector2.one)
local absolute_position = source(Vector2.one)
local total = derive(function()
return #meaning()
end)
local splits = props.splits or source {}
local total_columns = derive(function()
return #props.meaning()
end)
effect(function(previous)
local new = {}
for i = 1, total_columns() - 1 do
local old_split = vide.read(previous and previous[i] or nil)
new[i] = source(math.min(if old_split and old_split ~= 1 then old_split else suggested_sizes[i] or 1, i / total_columns()))
end
splits(new)
return new
end)
for i, split in (props.base_splits :: never) or {} do
splits()[i](split)
end
local function get_size(index: number)
local split_before = splits()[index - 1] or BEFORE :: never
local split_after = splits()[index] or AFTER :: never
local size = split_after() - split_before()
return size
end
local function get_min_size(i: number)
local min_size = min_sizes()[i]
return min_size and min_size() or 0.025
end
effect(function()
local new = setmetatable({}, {
__index = function()
return function() return 0 end
end,
})
for i = 1, total() do
min_sizes()[i] = min_sizes()[i] or source(0.025)
untrack(function()
new[i] = derive(function()
return get_size(i)
end)
end)
end
sizes(new :: any)
end)
local down = false
local updating = 0
return rounded_frame {
size = function()
return UDim2.new(1, 0, 0, 32)
end,
topleft = UDim.new(0, 8),
topright = UDim.new(0, 8),
color = theme.bg[1],
create "TextButton" {
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
AutoLocalize = false,
Text = "",
create "UIListLayout" {
FillDirection = Enum.FillDirection.Horizontal,
Padding = UDim.new(0, 0)
},
indexes(meaning, function(column, i)
return typography {
size = function()
return UDim2.fromScale(get_size(i), 1)
end,
automaticsize = Enum.AutomaticSize.None,
text = function()
return column() or ""
end,
xalignment = Enum.TextXAlignment.Left,
truncate = Enum.TextTruncate.AtEnd,
header = true,
textsize = 18,
padding {x = UDim.new(0, 8)}
}
end),
changed("AbsoluteSize", absolute_size),
changed("AbsolutePosition", absolute_position),
MouseButton1Down = function(x: number)
-- find the nearest split
x -= absolute_position().X
local absolute_size = absolute_size()
local nearest = -1
for i, location in splits() do
local absolute_x = absolute_size.X * location()
if math.abs(x - absolute_x) > MAX_PIXELS_OFFSET then continue end
nearest = i
end
down = nearest ~= -1
updating = nearest
end,
MouseButton1Up = function()
down = false
end,
cleanup(RunService.Heartbeat:Connect(function()
local x = UserInputService:GetMouseLocation().X
x -= absolute_position().X
if down == false then return end
down = UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) == true
local relative = x / absolute_size().X
local current = splits()[updating]()
local left_to_move = relative - current
if left_to_move > 0 then
for i = updating, total() - 1, 1 do
local min_size = get_min_size(i + 1)
local size = get_size(i + 1)
local new_size = math.max(size - left_to_move, min_size)
local difference = size - new_size
splits()[i](splits()[i]() + difference)
left_to_move -= difference
if left_to_move == 0 then break end
end
else
for i = updating, 1, -1 do
local min_size = get_min_size(i)
local size = math.max(get_size(i), min_size) -- this is changing, which it isnt supposed to do
local new_size = math.max(size + left_to_move, min_size)
local difference = new_size - size
splits()[i](splits()[i]() + difference)
--assert((new_size + difference) == get_size(i - 1))
left_to_move -= difference
if left_to_move == 0 then break end
end
end
end)),
}
}
end