local U=Roact.Component:extend("\073\116\101\109\076\105\115\116")U.defaultProps={MaxHeight=-1}function U.init(Z,U)Z:setState({Min=0;Max=U.MaxHeight,CanvasPosition=Vector2.new()})Z.UpdateBoundaries=function(U)if Z.Mounted then Z:setState({CanvasPosition=U.CanvasPosition;Min=U.CanvasPosition.Y-U.AbsoluteSize.Y;Max=U.CanvasPosition.Y+U.AbsoluteSize.Y})end end end function U.didMount(U)U.Mounted=true end function U.willUnmount(U)U.Mounted=false end function U.didUpdate(f,U,Z)local Q=f.props.ScrollTo and U.ScrollTo~=f.props.ScrollTo if U.Scope~=f.props.Scope and not Q then f:setState({CanvasPosition=Vector2.new(0,0);Min=0,Max=f.props.MaxHeight})end end function U.render(U)local Z=U.props local f=U.state local Q=0 local K=0 local G local w={}local r=0 local F=Z.RowHeight local c=Support.Values(Z.Items)table.sort(c,function(U,Z)return U.Order<Z.Order end)for F=1,#c,1 do local N=c[F]local D=N.Parent and Z.IdMap[N.Parent]local p=D and Z.Items[D]local E=true local I=0 if D then local U=p while U do if not U.Expanded then E=nil break else I=I+1 end local f=Z.IdMap[U.Parent]U=Z.Items[f]end end if N.Id==Z.ScrollTo and U.ScrolledTo~=Z.ScrollTo then local Q=r*Z.RowHeight if Q<f.CanvasPosition.Y or Q>f.CanvasPosition.Y+Z.MaxHeight then G=Vector2.new(0,Q)U.ScrolledTo=N.Id end end if E then r=r+1 local U=(r-1)*Z.RowHeight if U<f.Min then Q=Q+1 E=nil elseif U>f.Max then K=K+1 E=nil end end w[N.Id]=E and new(ItemRow,Support.Merge({},N,{Depth=I,Core=Z.Core,ToggleExpand=Z.ToggleExpand;Height=Z.RowHeight}))end return new(ScrollingFrame,{Layout="\076\105\115\116";LayoutDirection="\086\101\114\116\105\099\097\108";CanvasSize=UDim2.new(1,-2,0,r*Z.RowHeight),Size=UDim2.new(1,0,0,r*Z.RowHeight),CanvasPosition=G or f.CanvasPosition;ScrollBarThickness=4,ScrollBarImageTransparency=.6;VerticalScrollBarInset="\083\099\114\111\108\108\066\097\114";[Roact.Change.CanvasPosition]=U.UpdateBoundaries,[Roact.Children]=Support.Merge(w,{TopSpacer=new(Frame,{Size=UDim2.new(0,0,0,Q*Z.RowHeight),LayoutOrder=0}),BottomSpacer=new(Frame,{Size=UDim2.new(0,0,0,K*Z.RowHeight);LayoutOrder=#c+1});SizeConstraint=new("\085\073\083\105\122\101\067\111\110\115\116\114\097\105\110\116",{MinSize=Vector2.new(0,20),MaxSize=Vector2.new(math.huge,Z.MaxHeight)})})})end return U
-- Create component
local ItemList = Roact.Component:extend("ItemList")
ItemList.defaultProps = {
	MaxHeight = -1,
}

function ItemList:init(props)
	self:setState({
		Min = 0,
		Max = props.MaxHeight,
		CanvasPosition = Vector2.new(),
	})

	-- Create callback for updating canvas boundaries
	self.UpdateBoundaries = function(rbx)
		if self.Mounted then
			self:setState({
				CanvasPosition = rbx.CanvasPosition,
				Min = rbx.CanvasPosition.Y - rbx.AbsoluteSize.Y,
				Max = rbx.CanvasPosition.Y + rbx.AbsoluteSize.Y,
			})
		end
	end
end

function ItemList:didMount()
	self.Mounted = true
end

function ItemList:willUnmount()
	self.Mounted = false
end

function ItemList:didUpdate(previousProps, previousState)
	local IsScrollTargetSet = self.props.ScrollTo and (previousProps.ScrollTo ~= self.props.ScrollTo)

	-- Reset canvas position whenever scope updates (unless a scrolling target is set)
	if (previousProps.Scope ~= self.props.Scope) and not IsScrollTargetSet then
		self:setState({
			CanvasPosition = Vector2.new(0, 0),
			Min = 0,
			Max = self.props.MaxHeight,
		})
	end
end

function ItemList:render()
	local props = self.props
	local state = self.state

	-- Keep track of how many items are out of view
	local SkippedAbove = 0
	local SkippedBelow = 0
	local TargetCanvasPosition

	-- Declare a button for each item
	local ItemList = {}
	local VisibleItemCount = 0
	local ItemHeight = props.RowHeight

	-- Go through each item in order
	local OrderedItems = Support.Values(props.Items)
	table.sort(OrderedItems, function(A, B)
		return A.Order < B.Order
	end)
	for i = 1, #OrderedItems do
		local Item = OrderedItems[i]

		-- Get item parent state
		local ParentId = Item.Parent and props.IdMap[Item.Parent]
		local ParentState = ParentId and props.Items[ParentId]

		-- Determine visibility and depth from ancestors
		local Visible = true
		local Depth = 0
		if ParentId then
			local ParentState = ParentState
			while ParentState do
				-- Stop if ancestor not visible
				if not ParentState.Expanded then
					Visible = nil
					break

				-- Count visible ancestors
				else
					Depth = Depth + 1
				end

				-- Check next ancestor
				local ParentId = props.IdMap[ParentState.Parent]
				ParentState = props.Items[ParentId]
			end
		end

		-- Set canvas position to begin at item if requested and out-of-view
		if (Item.Id == props.ScrollTo) and (self.ScrolledTo ~= props.ScrollTo) then
			local ItemPosition = VisibleItemCount * props.RowHeight
			if ItemPosition < state.CanvasPosition.Y or ItemPosition > (state.CanvasPosition.Y + props.MaxHeight) then
				TargetCanvasPosition = Vector2.new(0, ItemPosition)
				self.ScrolledTo = Item.Id
			end
		end

		-- Calculate whether item is in view
		if Visible then
			VisibleItemCount = VisibleItemCount + 1
			local ItemTop = (VisibleItemCount - 1) * props.RowHeight
			if ItemTop < state.Min then
				SkippedAbove = SkippedAbove + 1
				Visible = nil
			elseif ItemTop > state.Max then
				SkippedBelow = SkippedBelow + 1
				Visible = nil
			end
		end

		-- Declare component for item
		ItemList[Item.Id] = Visible
			and new(
				ItemRow,
				Support.Merge({}, Item, {
					Depth = Depth,
					Core = props.Core,
					ToggleExpand = props.ToggleExpand,
					Height = props.RowHeight,
				})
			)
	end

	return new(ScrollingFrame, {
		Layout = "List",
		LayoutDirection = "Vertical",
		CanvasSize = UDim2.new(1, -2, 0, VisibleItemCount * props.RowHeight),
		Size = UDim2.new(1, 0, 0, VisibleItemCount * props.RowHeight),
		CanvasPosition = TargetCanvasPosition or state.CanvasPosition,
		ScrollBarThickness = 4,
		ScrollBarImageTransparency = 0.6,
		VerticalScrollBarInset = "ScrollBar",
		[Roact.Change.CanvasPosition] = self.UpdateBoundaries,
		[Roact.Children] = Support.Merge(ItemList, {
			TopSpacer = new(Frame, {
				Size = UDim2.new(0, 0, 0, SkippedAbove * props.RowHeight),
				LayoutOrder = 0,
			}),
			BottomSpacer = new(Frame, {
				Size = UDim2.new(0, 0, 0, SkippedBelow * props.RowHeight),
				LayoutOrder = #OrderedItems + 1,
			}),
			SizeConstraint = new("UISizeConstraint", {
				MinSize = Vector2.new(0, 20),
				MaxSize = Vector2.new(math.huge, props.MaxHeight),
			}),
		}),
	})
end

return ItemList
