local C=script.Parent.Parent.Parent local U=C.Parent local Z=require(C.Theme)local b=Z.WithTheme local p=Z.ApplyDescription local w=Z.RegisterStateAction local m=C.Validators local O=require(m.StyleValidator)local M=require(U.Flipper)local r=require(U.Sift)local W=require(U.t)local V=require(U.Roact)local X=V.createElement local i=script.Parent local l=require(i.Descriptions.ListItemDescriptions)local n="\115\116\121\108\101\115\047\065\110\105\109\097\116\105\111\110\083\112\114\105\110\103\080\097\114\097\109\115\047\067\111\110\116\114\111\108\047\076\105\115\116\047\076\105\115\116\073\116\101\109\047\067\111\108\111\114"local o="\115\116\121\108\101\115\047\065\110\105\109\097\116\105\111\110\083\112\114\105\110\103\080\097\114\097\109\115\047\067\111\110\116\114\111\108\047\076\105\115\116\047\076\105\115\116\073\116\101\109\047\084\114\097\110\115\112\097\114\101\110\099\121"local x="\115\116\121\108\101\115\047\065\110\105\109\097\116\105\111\110\083\112\114\105\110\103\080\097\114\097\109\115\047\067\111\110\116\114\111\108\047\076\105\115\116\047\076\105\115\116\073\116\101\109\047\073\110\100\105\099\097\116\111\114"local G=V.PureComponent:extend(script.Name)G.StyleBindings={"\070\105\108\108";"\073\110\100\105\099\097\116\111\114";"\083\116\114\111\107\101","\084\101\120\116"}G.defaultProps={Width=UDim.new(0,160);Height=UDim.new(0,40),ShowIndicator=true,Font="\102\111\110\116\115\047\066\111\100\121"}G.validateProps=W.strictInterface({Selected=W.optional(W.boolean),Size=W.optional(W.UDim2),ShowIndicator=W.optional(W.boolean),Font=W.optional(O);AnchorPoint=W.optional(W.Vector2);Disabled=W.optional(W.boolean);LayoutOrder=W.optional(W.number),Position=W.optional(W.UDim2),Width=W.optional(W.UDim),Height=W.optional(W.UDim);Text=W.optional(W.string);Icon=W.optional(O);Native=W.optional(W.table),[V.Event.Activated]=W.optional(W.callback),[V.Event.MouseButton1Down]=W.optional(W.callback);[V.Event.MouseButton1Up]=W.optional(W.callback);[V.Event.MouseEnter]=W.optional(W.callback),[V.Event.MouseLeave]=W.optional(W.callback)})function G.init(C)C.IndicatorHeight,C.SetIndicatorHeight=V.createBinding(0)for U,Z in pairs(C.StyleBindings)do C[Z],C["\083\101\116"..Z]=V.createBinding({})end w(C,V.Event.Activated,nil)w(C,V.Event.MouseButton1Down,"\080\114\101\115\115\101\100")w(C,V.Event.MouseButton1Up,"\072\111\118\101\114")w(C,V.Event.MouseEnter,"\072\111\118\101\114")w(C,V.Event.MouseLeave,"\082\101\115\116")C:setState({State="\082\101\115\116"})end function G.willUnmount(C)for U,Z in pairs(C.StyleBindings)do local b=C[Z.."\077\111\116\111\114"]if b then pcall(b.Destroy,b)C[Z.."\077\111\116\111\114"]=nil end end end function G.ApplyDescription(Z,C,U)local b=Z.props C=r.Dictionary.copy(C)local w=b.BackgroundColor local m=b.TextColor if w then C.Fill=w end if m then C.Text=m end local O=Z.IndicatorHeight:getValue()if O then local b=Z.IndicatorHeightMotor if b then pcall(b.destroy,b)end local p=M.SingleMotor.new(O)Z.IndicatorHeightMotor=p p:onStep(Z.SetIndicatorHeight)p:onComplete(function()p:destroy()Z.IndicatorHeightMotor=nil end)p:setGoal(M.Spring.new(C.IndicatorHeight,U[x]))else Z.SetIndicatorHeight(C.IndicatorHeight)end p(Z,C,U,n,o)end function G.render(C)local U=C.props local Z=C.state return b(function(b)if not U.Selected then if U.Disabled then C:ApplyDescription(l.NoFill.Disabled,b)elseif Z.State=="\082\101\115\116"then C:ApplyDescription(l.NoFill.Rest,b)elseif Z.State=="\072\111\118\101\114"then C:ApplyDescription(l.NoFill.Hover,b)else C:ApplyDescription(l.NoFill.Pressed,b)end else if U.Disabled then C:ApplyDescription(l.Fill.Disabled,b)elseif Z.State=="\082\101\115\116"then C:ApplyDescription(l.Fill.Rest,b)elseif Z.State=="\072\111\118\101\114"then C:ApplyDescription(l.Fill.Hover,b)else C:ApplyDescription(l.Fill.Pressed,b)end end local p=b[U.Font]local w=X("\084\101\120\116\076\097\098\101\108",{AnchorPoint=Vector2.new(0,.5);BackgroundTransparency=1;FontFace=p.Font;Position=UDim2.new(0,16+(U.Icon and 28 or 0),.5,0);Size=UDim2.new(0,10,1,0),Text=U.Text,TextSize=p.Size;TextXAlignment="\076\101\102\116";TextColor3=C.Text:map(function(C)return C.Color end),TextTransparency=C.Text:map(function(C)return C.Transparency end)})local m=X("\070\114\097\109\101",{Visible=U.ShowIndicator;AnchorPoint=Vector2.new(0,.5);BorderSizePixel=0;Position=UDim2.new(0,5,.5,0),BackgroundColor3=C.Indicator:map(function(C)return C.Color end),BackgroundTransparency=C.Indicator:map(function(C)return C.Transparency end),Size=C.IndicatorHeight:map(function(C)return UDim2.fromOffset(3,C)end)},{Corners=X("\085\073\067\111\114\110\101\114",{CornerRadius=UDim.new(1,0)})})local O=X("\073\109\097\103\101\066\117\116\116\111\110",{AnchorPoint=Vector2.new(0,.5);AutoButtonColor=false,Size=UDim2.new(1,-8,1,-4)-UDim2.fromOffset(2,2);Position=UDim2.new(0,4,.5,0)+UDim2.fromOffset(1,0);BackgroundColor3=C.Fill:map(function(C)return C.Color end);BackgroundTransparency=C.Fill:map(function(C)return C.Transparency end),[V.Event.Activated]=C[V.Event.Activated],[V.Event.MouseButton1Down]=C[V.Event.MouseButton1Down],[V.Event.MouseButton1Up]=C[V.Event.MouseButton1Up];[V.Event.MouseEnter]=C[V.Event.MouseEnter],[V.Event.MouseLeave]=C[V.Event.MouseLeave]},{Corners=X("\085\073\067\111\114\110\101\114",{CornerRadius=UDim.new(0,b["\115\116\121\108\101\115\047\067\111\114\110\101\114\082\097\100\105\117\115\047\068\101\102\097\117\108\116\047\073\110\110\101\114"]-1)});Stroke=X("\085\073\083\116\114\111\107\101",{Thickness=1;Color=C.Stroke:map(function(C)return C.Color end),Transparency=C.Stroke:map(function(C)return C.Transparency end)})})local M=U.Icon and b[U.Icon]local W=nil if M then local C=M.ImageRectSize==Vector2.new()and Vector2.new(16,16)or M.ImageRectSize W=UDim2.fromOffset(math.clamp(C.X,0,36),math.clamp(C.Y,0,36))else W=UDim2.fromOffset(16,16)end local i=M and X("\073\109\097\103\101\076\097\098\101\108",{AnchorPoint=Vector2.new(.5,.5),BackgroundTransparency=1;Image=M.Image,ImageRectOffset=M.ImageRectOffset,ImageRectSize=M.ImageRectSize;Position=UDim2.fromScale(.5,.5),ResampleMode=M.ResampleMode,Size=W;ImageColor3=C.Text:map(function(C)return C.Color end),ImageTransparency=C.Text:map(function(C)return C.Transparency end)})local n=X("\070\114\097\109\101",{AnchorPoint=Vector2.new(.5,.5);BackgroundTransparency=1;Position=UDim2.new(0,24,.5,0),Size=UDim2.new(0,0,1,0)},i)return X("\070\114\097\109\101",r.Dictionary.join({AnchorPoint=U.AnchorPoint;BackgroundTransparency=1;Position=U.Position;Size=U.Size or UDim2.new(U.Width.Scale,U.Width.Offset,U.Height.Scale,U.Height.Offset);LayoutOrder=U.LayoutOrder}),{Text=w;Indicator=m;Base=O;Icon=n})end)end return G
-- ListItem: A list item
-- Assets/Lists & Collections/Lists & Collections/List View/List Item

local Winro = script.Parent.Parent.Parent
local Packages = Winro.Parent

local Theme = require(Winro.Theme)
local WithTheme = Theme.WithTheme
local ApplyDescription = Theme.ApplyDescription
local RegisterStateAction = Theme.RegisterStateAction

local Validators = Winro.Validators
local StyleValidator = require(Validators.StyleValidator)

local Flipper = require(Packages.Flipper)
local Sift = require(Packages.Sift)
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local new = Roact.createElement

local ListRoot = script.Parent
local Descriptions = require(ListRoot.Descriptions.ListItemDescriptions)

local LISTITEM_COLOR_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/List/ListItem/Color"
local LISTITEM_TRANSPARENCY_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/List/ListItem/Transparency"
local LISTITEM_INDICATOR_SIZE_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/List/ListItem/Indicator"

local ListItem = Roact.PureComponent:extend(script.Name)

ListItem.StyleBindings = {
	'Fill',
	'Indicator',
	'Stroke',
	'Text',
}

ListItem.defaultProps = {
	Width = UDim.new(0, 160), --- @defaultProp
	Height = UDim.new(0, 40), --- @defaultProp
	ShowIndicator = true, --- @defaultProp
	Font = "fonts/Body", --- @defaultProp
}

ListItem.validateProps = t.strictInterface({

	--- @prop @optional Selected boolean The ListItem's selected state
	Selected = t.optional(t.boolean),

	--- @prop @optional Size UDim2 The ListItem's size
	Size = t.optional(t.UDim2),

	--- @prop @optional ShowIndicator boolean Determines indicator visability
	ShowIndicator = t.optional(t.boolean),

	--- @prop @optional @style Font string|table The label's font style
	Font = t.optional(StyleValidator),

	--- @prop @optional AnchorPoint Vector2 The ListItem's anchor point
	AnchorPoint = t.optional(t.Vector2),

	--- @prop @optional Disabled boolean Determines if the item is shown as disabled
	Disabled = t.optional(t.boolean),

	--- @prop @optional LayoutOrder number The ListItem's layout order
	LayoutOrder = t.optional(t.number),

	--- @prop @optional Position UDim2 The ListItem's position
	Position = t.optional(t.UDim2),

	--- @prop @optional Width UDim The ListItem's Width @depricated Use Size instead
	Width = t.optional(t.UDim),

	--- @prop @optional Height UDim The ListItem's Height @depricated Use Size instead
	Height = t.optional(t.UDim),

	--- @prop @optional Text string The ListItem's text
	Text = t.optional(t.string),

	--- @prop @optional @style Icon string|table The ListItem's icon
	Icon = t.optional(StyleValidator),

	--- @prop @optional Native table The native props
	Native = t.optional(t.table),

	--- @prop @optional [Roact.Event.Activated] function Roact.Event.Activated
	[Roact.Event.Activated] = t.optional(t.callback),

	--- @prop @optional [Roact.Event.MouseButton1Down] function Roact.Event.MouseButton1Down
	[Roact.Event.MouseButton1Down] = t.optional(t.callback),

	--- @prop @optional [Roact.Event.MouseButton1Up] function Roact.Event.MouseButton1Up
	[Roact.Event.MouseButton1Up] = t.optional(t.callback),

	--- @prop @optional [Roact.Event.MouseEnter] function Roact.Event.MouseEnter
	[Roact.Event.MouseEnter] = t.optional(t.callback),

	--- @prop @optional [Roact.Event.MouseLeave] function Roact.Event.MouseLeave
	[Roact.Event.MouseLeave] = t.optional(t.callback),
})

function ListItem:init()
	
	-- Indicator size binding
	self.IndicatorHeight, self.SetIndicatorHeight = Roact.createBinding(0)

	-- Create Style Bindings
	for _, StyleBinding in pairs(self.StyleBindings) do
		self[StyleBinding], self['Set' .. StyleBinding] = Roact.createBinding({})
	end

	-- Actions
	RegisterStateAction(self, Roact.Event.Activated, nil)
	RegisterStateAction(self, Roact.Event.MouseButton1Down, 'Pressed')
	RegisterStateAction(self, Roact.Event.MouseButton1Up, 'Hover')
	RegisterStateAction(self, Roact.Event.MouseEnter, 'Hover')
	RegisterStateAction(self, Roact.Event.MouseLeave, 'Rest')

	-- Initial State
	self:setState({
		State = 'Rest'
	})
end

function ListItem:willUnmount()
	
	-- Clear motors for each style binding
	for _, StyleBinding in pairs(self.StyleBindings) do

		-- Look for a motor
		local Motor = self[StyleBinding .. 'Motor']

		-- Remove motor if found
		if Motor then
			pcall(Motor.Destroy, Motor)
			self[StyleBinding .. 'Motor'] = nil
		end
	end
end

function ListItem:ApplyDescription(Description, Theme)
	local props = self.props
	Description = Sift.Dictionary.copy(Description)
	
	-- Prop Overrides
	local FillOverride = props.BackgroundColor
	local TextOverride = props.TextColor

	if FillOverride then
		Description.Fill = FillOverride
	end

	if TextOverride then
		Description.Text = TextOverride
	end

	-- Set Indicator Size
	local InitialIndicatorHeight = self.IndicatorHeight:getValue()
	
	-- Animate
	if InitialIndicatorHeight then

		-- Remove exiting motor
		local Existing = self.IndicatorHeightMotor

		if Existing then
			pcall(Existing.destroy, Existing)
		end

		-- Create a new motor
		local Motor = Flipper.SingleMotor.new(InitialIndicatorHeight)
		self.IndicatorHeightMotor = Motor

		-- Bind
		Motor:onStep(self.SetIndicatorHeight)
		Motor:onComplete(function()
			Motor:destroy()
			self.IndicatorHeightMotor = nil
		end)

		-- Animate
		Motor:setGoal(Flipper.Spring.new(Description.IndicatorHeight,
			Theme[LISTITEM_INDICATOR_SIZE_ANIMATION_SPRING_SETTINGS])
		)
	else
		self.SetIndicatorHeight(Description.IndicatorHeight)
	end

	ApplyDescription(self, Description, Theme, LISTITEM_COLOR_ANIMATION_SPRING_SETTINGS, LISTITEM_TRANSPARENCY_ANIMATION_SPRING_SETTINGS)
end

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

	return WithTheme(function(Theme)

		-- Apply Description
		if not props.Selected then
			if props.Disabled then
				self:ApplyDescription(Descriptions.NoFill.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.NoFill.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.NoFill.Hover, Theme)
			else
				self:ApplyDescription(Descriptions.NoFill.Pressed, Theme)
			end
		else
			if props.Disabled then
				self:ApplyDescription(Descriptions.Fill.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.Fill.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.Fill.Hover, Theme)
			else
				self:ApplyDescription(Descriptions.Fill.Pressed, Theme)
			end
		end

		-- ///////// ListItem Label

		local LabelFontStyle = Theme[props.Font]

		local Label = new('TextLabel', {
			AnchorPoint = Vector2.new(0, 0.5),
			BackgroundTransparency = 1,
			FontFace = LabelFontStyle.Font,
			Position = UDim2.new(0, 16 + (props.Icon and 28 or 0), 0.5, 0),
			Size = UDim2.new(0, 10, 1, 0),
			Text = props.Text,
			TextSize = LabelFontStyle.Size,
			TextXAlignment = 'Left',

			TextColor3 = self.Text:map(function(Style)
				return Style.Color
			end),
			TextTransparency = self.Text:map(function(Style)
				return Style.Transparency
			end),
		})

		-- ////////// ListItem Indicator

		local Indicator = new('Frame', {
			Visible = props.ShowIndicator,
			AnchorPoint = Vector2.new(0, 0.5),
			BorderSizePixel = 0,
			Position = UDim2.new(0, 5, 0.5, 0),

			BackgroundColor3 = self.Indicator:map(function(Style)
				return Style.Color
			end),
			BackgroundTransparency = self.Indicator:map(function(Style)
				return Style.Transparency
			end),
			Size = self.IndicatorHeight:map(function(Height)
				return UDim2.fromOffset(3, Height)
			end),
		}, {

			Corners = new('UICorner', {
				CornerRadius = UDim.new(1, 0)
			}),
		})

		-- ////////// ListItem Base

		local Base = new('ImageButton', {
			AnchorPoint = Vector2.new(0, 0.5),
			AutoButtonColor = false,
			Size = UDim2.new(1, -(4 * 2), 1, -(2 * 2)) - UDim2.fromOffset(2, 2), -- Account for stroke thickness
			Position = UDim2.new(0, 4, 0.5, 0) + UDim2.fromOffset(1, 0), -- Accont for stroke thickness

			BackgroundColor3 = self.Fill:map(function(Style)
				return Style.Color
			end),
			BackgroundTransparency = self.Fill:map(function(Style)
				return Style.Transparency
			end),

			[Roact.Event.Activated] = self[Roact.Event.Activated],
			[Roact.Event.MouseButton1Down] = self[Roact.Event.MouseButton1Down],
			[Roact.Event.MouseButton1Up] = self[Roact.Event.MouseButton1Up],
			[Roact.Event.MouseEnter] = self[Roact.Event.MouseEnter],
			[Roact.Event.MouseLeave] = self[Roact.Event.MouseLeave],
		}, {

			Corners = new('UICorner', {
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Inner'] - 1) -- Account for 1px added by stroke
			}),
			Stroke = new('UIStroke', {
				Thickness = 1,
				Color = self.Stroke:map(function(Style)
					return Style.Color
				end),
				Transparency = self.Stroke:map(function(Style)
					return Style.Transparency
				end)
			})
		})
		
		-- ////////// ListItem Icon

		local IconImage = props.Icon and Theme[props.Icon]
		local IconImageSize = nil

		if IconImage then
			local IconImageRectSize = IconImage.ImageRectSize == Vector2.new() and Vector2.new(16, 16) or IconImage.ImageRectSize
			IconImageSize = UDim2.fromOffset(
				math.clamp(IconImageRectSize.X, 0, 9*4),
				math.clamp(IconImageRectSize.Y, 0, 9*4)
			)
		else
			IconImageSize = UDim2.fromOffset(16, 16)
		end
		
		local IconImage = IconImage and new('ImageLabel', {
			AnchorPoint = Vector2.new(0.5, 0.5),
			BackgroundTransparency = 1,
			Image = IconImage.Image,
			ImageRectOffset = IconImage.ImageRectOffset,
			ImageRectSize = IconImage.ImageRectSize,
			Position = UDim2.fromScale(0.5, 0.5),
			ResampleMode = IconImage.ResampleMode,
			Size = IconImageSize,

			ImageColor3 = self.Text:map(function(Style)
				return Style.Color
			end),
			ImageTransparency = self.Text:map(function(Style)
				return Style.Transparency
			end),
		})

		local Icon = new('Frame', {
			AnchorPoint = Vector2.new(0.5, 0.5),
			BackgroundTransparency = 1,
			Position = UDim2.new(0, 16 + 8, 0.5, 0),
			Size = UDim2.new(0, 0, 1, 0),
		}, IconImage)

		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			Position = props.Position,
			Size = props.Size or UDim2.new(props.Width.Scale, props.Width.Offset, props.Height.Scale, props.Height.Offset),
			LayoutOrder = props.LayoutOrder
		}), {
			Text = Label,
			Indicator = Indicator,
			Base = Base,
			Icon = Icon
		})
	end)
end

return ListItem
