|
local Selection = game:GetService("Selection") |
|
|
|
local React = require(script.Parent.Parent.Parent.Packages.React) |
|
local ReactRoblox = require(script.Parent.Parent.Parent.Packages.ReactRoblox) |
|
|
|
local useStudioTheme = require(script.Parent.Parent.Parent.Hooks.useStudioTheme) |
|
|
|
local e = React.createElement |
|
|
|
type Props = { |
|
useLayout: boolean?, |
|
direction: Enum.FillDirection?, |
|
spacing: number?, |
|
children: React.ReactNode, |
|
} |
|
|
|
local DEFAULT_PROPS = { |
|
useLayout = true, |
|
direction = Enum.FillDirection.Vertical, |
|
spacing = 8, |
|
} |
|
|
|
local CanvasComponent = React.forwardRef(function(props: Props, ref) |
|
local theme = useStudioTheme() |
|
local isLight = theme.Name == "Light" |
|
|
|
local canvasSize, setCanvasSize = React.useState(Vector2.one) |
|
local contentSize, setContentSize = React.useState(Vector2.one) |
|
|
|
local doesOverflow = contentSize.Y > canvasSize.Y |
|
local useLayout = if props.useLayout == nil |
|
then DEFAULT_PROPS.useLayout |
|
else props.useLayout |
|
|
|
local innerCanvasRef = React.useRef(nil :: ScrollingFrame?) |
|
|
|
React.useImperativeHandle(ref, function() |
|
return innerCanvasRef.current |
|
end) |
|
|
|
return e("ImageLabel", { |
|
key = "StoryCanvas", |
|
|
|
BackgroundColor3 = isLight and Color3.new(1, 1, 1) or Color3.new(), |
|
BorderSizePixel = 0, |
|
Size = UDim2.fromScale(1, 1), |
|
Image = "rbxasset://textures/meshPartFallback.png", |
|
ImageTransparency = 0.8, |
|
ScaleType = Enum.ScaleType.Tile, |
|
TileSize = UDim2.fromOffset(16, 16), |
|
}, { |
|
Padding = doesOverflow and e("UIPadding", { |
|
PaddingRight = UDim.new(0, 8), |
|
PaddingTop = UDim.new(0, 8), |
|
-- PaddingBottom = UDim.new(0, 8), |
|
PaddingLeft = UDim.new(0, 8), |
|
}), |
|
|
|
Canvas = e("ScrollingFrame", { |
|
ref = innerCanvasRef, |
|
|
|
BackgroundTransparency = 1, |
|
BorderSizePixel = 0, |
|
Size = UDim2.new(1, 0, 1, 0), |
|
AutomaticCanvasSize = Enum.AutomaticSize.Y, |
|
CanvasSize = UDim2.new(), |
|
VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar, |
|
ScrollingDirection = Enum.ScrollingDirection.Y, |
|
ScrollBarThickness = 8, |
|
ScrollBarImageColor3 = theme:GetColor( |
|
Enum.StudioStyleGuideColor.DimmedText |
|
), |
|
BottomImage = "rbxasset://textures/AnimationEditor/image_scrollbar_vertical_bottom.png", |
|
MidImage = "rbxasset://textures/AnimationEditor/image_scrollbar_vertical_mid.png", |
|
TopImage = "rbxasset://textures/AnimationEditor/image_scrollbar_vertical_top.png", |
|
|
|
[React.Change.AbsoluteSize] = function(rbx: ScrollingFrame) |
|
setCanvasSize(rbx.AbsoluteSize) |
|
end, |
|
}, { |
|
layout = useLayout and e("UIListLayout", { |
|
Padding = UDim.new(0, props.spacing or DEFAULT_PROPS.spacing), |
|
FillDirection = props.direction or DEFAULT_PROPS.direction, |
|
HorizontalAlignment = Enum.HorizontalAlignment.Center, |
|
SortOrder = Enum.SortOrder.LayoutOrder, |
|
VerticalAlignment = doesOverflow and Enum.VerticalAlignment.Top |
|
or Enum.VerticalAlignment.Center, |
|
|
|
[React.Change.AbsoluteContentSize] = function(rbx: UIListLayout) |
|
setContentSize(rbx.AbsoluteContentSize) |
|
end, |
|
}), |
|
padding = e("UIPadding", { |
|
PaddingBottom = UDim.new(0, 8), |
|
PaddingLeft = UDim.new(0, 8), |
|
PaddingRight = UDim.new(0, 8), |
|
PaddingTop = UDim.new(0, 8), |
|
}), |
|
}, props.children), |
|
}) |
|
end) |
|
|
|
local function renderCanvas(children: React.ReactNode, props: Props?) |
|
local withProps = props or DEFAULT_PROPS |
|
|
|
return function(target: Instance) |
|
local appRef = React.createRef() |
|
withProps.ref = appRef |
|
|
|
local root = ReactRoblox.createRoot(target) |
|
local app = React.createElement(CanvasComponent, withProps, children) |
|
|
|
root:render(app) |
|
|
|
local hoarcekatToggle = true |
|
local signal = Selection.SelectionChanged:Connect(function() |
|
local hoarcekatSelected = table.find(Selection:Get(), target) |
|
|
|
if appRef.current and hoarcekatToggle and hoarcekatSelected then |
|
Selection:Add({ appRef.current }) |
|
Selection:Remove({ target }) |
|
end |
|
|
|
if hoarcekatSelected then |
|
hoarcekatToggle = not hoarcekatToggle |
|
end |
|
end) |
|
|
|
return function() |
|
signal:Disconnect() |
|
root:unmount() |
|
end |
|
end |
|
end |
|
|
|
return { |
|
render = renderCanvas, |
|
} |