local a=Roact.Component:extend("\073\116\101\109\076\105\115\116")a.defaultProps={MaxHeight=300}function a.init(R,a)R:setState({Min=0,Max=a.MaxHeight,CanvasPosition=Vector2.new()})R.UpdateBoundaries=function(a)if R.Mounted then R:setState({CanvasPosition=a.CanvasPosition;Min=a.CanvasPosition.Y-a.AbsoluteSize.Y;Max=a.CanvasPosition.Y+a.AbsoluteSize.Y})end end end function a.didMount(a)a.Mounted=true end function a.willUnmount(a)a.Mounted=false end function a.didUpdate(l,a,R)local g=l.props.ScrollTo and a.ScrollTo~=l.props.ScrollTo if a.Scope~=l.props.Scope and not g then l:setState({CanvasPosition=Vector2.new(0,0),Min=0,Max=l.props.MaxHeight})end end function a.render(a)local R=a.props local l=a.state local g=0 local Q=0 local I local A={}local F=0 local M=R.RowHeight local O=Support.Values(R.Items)table.sort(O,function(a,R)return a.Order<R.Order end)for M=1,#O,1 do local W=O[M]local T=W.Parent and R.IdMap[W.Parent]local C=T and R.Items[T]local p=true local h=0 if T then local a=C while a do if not a.Expanded then p=nil break else h=h+1 end local l=R.IdMap[a.Parent]a=R.Items[l]end end if W.Id==R.ScrollTo and a.ScrolledTo~=R.ScrollTo then local g=F*R.RowHeight if g<l.CanvasPosition.Y or g>l.CanvasPosition.Y+R.MaxHeight then I=Vector2.new(0,g)a.ScrolledTo=W.Id end end if p then F=F+1 local a=(F-1)*R.RowHeight if a<l.Min then g=g+1 p=nil elseif a>l.Max then Q=Q+1 p=nil end end A[W.Id]=p and new(ItemRow,Support.Merge({},W,{Depth=h,Core=R.Core,ToggleExpand=R.ToggleExpand;Height=R.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,F*R.RowHeight);Size=UDim2.new(1,0,0,F*R.RowHeight),CanvasPosition=I or l.CanvasPosition,ScrollBarThickness=4,ScrollBarImageTransparency=.6;VerticalScrollBarInset="\083\099\114\111\108\108\066\097\114";[Roact.Change.CanvasPosition]=a.UpdateBoundaries,[Roact.Children]=Support.Merge(A,{TopSpacer=new(Frame,{Size=UDim2.new(0,0,0,g*R.RowHeight),LayoutOrder=0});BottomSpacer=new(Frame,{Size=UDim2.new(0,0,0,Q*R.RowHeight),LayoutOrder=#O+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,R.MaxHeight)})})})end return a
-- Create component
local ItemList = Roact.Component:extend("ItemList")
ItemList.defaultProps = {
	MaxHeight = 300,
}

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
