local C=script.Parent.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.CheckboxStateDescriptions)local n=require(i.Util.GetSelectedStateFromValue)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\073\110\112\117\116\047\067\104\101\099\107\098\111\120\047\067\111\108\111\114"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\073\110\112\117\116\047\067\104\101\099\107\098\111\120\047\084\114\097\110\115\112\097\114\101\110\099\121"local G="\105\109\097\103\101\115\047\070\108\117\101\110\116\047\065\099\099\101\112\116\077\101\100\105\117\109\047\049\050\112\120"local A="\105\109\097\103\101\115\047\070\108\117\101\110\116\047\068\097\115\104\049\050\047\049\050\112\120"local k="\102\111\110\116\047\066\111\100\121"local E=4 local L=V.PureComponent:extend(script.Name)L.StyleBindings={"\067\104\101\099\107";"\070\105\108\108";"\083\116\114\111\107\101","\084\101\120\116"}L.defaultProps={Size=UDim2.fromOffset(20,20)}L.validateProps=W.strictInterface({AnchorPoint=W.optional(W.Vector2),Font=W.optional(O),Position=W.optional(W.UDim2);Size=W.optional(W.UDim2),Selected=W.optional(W.boolean),Label=W.optional(W.string),TextWrapped=W.optional(W.boolean);TextColor=W.optional(O);Disabled=W.optional(W.boolean);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 L.init(C)for U,Z in pairs(C.StyleBindings)do C[Z],C["\083\101\116"..Z]=V.createBinding({})end C.CheckObscureWidth,C.SetCheckObscureWidth=V.createBinding(0)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 L.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 L.ApplyDescription(Z,C,U)C=r.Dictionary.copy(C)local b=Z.CheckObscureWidth:getValue()if b then local U=Z.CheckObscureWidthMotor if U then pcall(U.destroy,U)end local p=M.SingleMotor.new(b)Z.CheckObscureWidthMotor=p p:onStep(Z.SetCheckObscureWidth)p:onComplete(function()p:destroy()Z.CheckObscureWidthMotor=nil end)p:setGoal(M.Spring.new(C.CheckObscureWidth,{frequency=2.5,dampingRatio=1}))else Z.SetCheckObscureWidth(C.CheckObscureWidth)end local w=Z.props.TextColor if w then C.Text=w end p(Z,C,U,U[o],U[x])end function L.render(C)local U=C.props local Z=C.state return b(function(b)local p=n(U.Selected)if p=="\078\111\110\101"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 w=b[U.Font or k]local m=U.Label and X("\084\101\120\116\076\097\098\101\108",{AnchorPoint=Vector2.new(0,.5);BackgroundTransparency=1,FontFace=w.Font;Position=UDim2.new(0,(U.Size.X.Offset+20)+E,.5,0),Size=UDim2.new(1,-30,1,0);Text=U.Label;TextSize=w.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 O=X("\085\073\071\114\097\100\105\101\110\116",{Transparency=NumberSequence.new({[1]=NumberSequenceKeypoint.new(.0,0);[2]=NumberSequenceKeypoint.new(.001,0),[3]=NumberSequenceKeypoint.new(.002,1);[4]=NumberSequenceKeypoint.new(1,1)});Offset=C.CheckObscureWidth:map(function(C)return Vector2.new(1-C,0)end)})local M=b[p=="\083\101\108\101\099\116\101\100"and G or A]local W=X("\073\109\097\103\101\076\097\098\101\108",{Visible=p~="\078\111\110\101";AnchorPoint=Vector2.new(.5,.5),BackgroundTransparency=1,ResampleMode=M.ResampleMode,Image=M.Image;ImageRectOffset=M.ImageRectOffset;ImageRectSize=M.ImageRectSize;Position=UDim2.fromScale(.5,.5);Size=UDim2.fromOffset(M.ImageRectSize.X,M.ImageRectSize.Y),ImageColor3=C.Check:map(function(C)return C.Color end);ImageTransparency=C.Check:map(function(C)return C.Transparency end)},{ObscureGradient=O})local i=X("\085\073\083\116\114\111\107\101",{ApplyStrokeMode=Enum.ApplyStrokeMode.Border,Thickness=1,Color=C.Stroke:map(function(C)return C.Color end),Transparency=C.Stroke:map(function(C)return C.Transparency end)})local o=X("\073\109\097\103\101\066\117\116\116\111\110",{AnchorPoint=Vector2.new(0,.5),AutoButtonColor=false;BorderSizePixel=0,Position=UDim2.new(0,1+(U.Label and E or 0),.5,0);Size=U.Size-UDim2.fromOffset(2,2);[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];BackgroundColor3=C.Fill:map(function(C)return C.Color end),BackgroundTransparency=C.Fill:map(function(C)return C.Transparency end)},{Check=W;Stroke=i,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)})})return X("\070\114\097\109\101",r.Dictionary.join({AnchorPoint=U.AnchorPoint,Position=U.Position,BackgroundTransparency=1;Size=U.Size},U.Native),{Checkbox=o,Label=m})end)end return L
-- Checkbox: The basic checkbox component
-- https://www.figma.com/file/uNmIxgdbUT44MZjQCTIMe3/Windows-UI-3-(Community)?node-id=25616%3A1593

local Winro = script.Parent.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 CheckboxRoot = script.Parent
local Descriptions = require(CheckboxRoot.Descriptions.CheckboxStateDescriptions)
local GetSelectedStateFromValue = require(CheckboxRoot.Util.GetSelectedStateFromValue)

local CHECKBOX_COLOR_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Checkbox/Color"
local CHECKBOX_TRANSPARENCY_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Checkbox/Transparency"
local CHECKBOX_CHECKED = "images/Fluent/AcceptMedium/12px"
local CHECKBOX_INDETERMINATE = "images/Fluent/Dash12/12px"
local LABEL_FONT = "font/Body"
local CHECKBOX_LEFT_MARGIN = 4

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

Checkbox.StyleBindings = {
	'Check',
	'Fill',
	'Stroke',
	'Text',
}

Checkbox.defaultProps = {
	Size = UDim2.fromOffset(20, 20) --- @defaultProp
}

Checkbox.validateProps = t.strictInterface({

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

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

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

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

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

	--- @prop @optional Label string The Checkbox's label
	Label = t.optional(t.string),

	--- @prop @optional TextWrapped boolean For the `Label` prop
	TextWrapped = t.optional(t.boolean),

	--- @prop @optional @style TextColor string|table The label's text color style
	TextColor = t.optional(StyleValidator),

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

	--- @prop @optional Native table 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 Checkbox:init()

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

	-- Custom Style binding
	self.CheckObscureWidth, self.SetCheckObscureWidth = Roact.createBinding(0)

	-- 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 Checkbox: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 Checkbox:ApplyDescription(Description, Theme)
	Description = Sift.Dictionary.copy(Description)

	-- Custom style binding
	local CheckObscureWidth = self.CheckObscureWidth:getValue()
	
	-- Animate
	if CheckObscureWidth then

		-- Remove exiting motor
		local Existing = self.CheckObscureWidthMotor
		if Existing then
			pcall(Existing.destroy, Existing)
		end

		-- Create a new motor
		local Motor = Flipper.SingleMotor.new(CheckObscureWidth)
		self.CheckObscureWidthMotor = Motor

		-- Bind
		Motor:onStep(self.SetCheckObscureWidth)
		Motor:onComplete(function()
			Motor:destroy()
			self.CheckObscureWidthMotor = nil
		end)

		-- Animate
		Motor:setGoal(Flipper.Spring.new(Description.CheckObscureWidth, {
			frequency = 2.5,
			dampingRatio = 1,
		}))
	else
		self.SetCheckObscureWidth(Description.CheckObscureWidth)
	end

	-- Prop overrides
	local TextStyleOverride = self.props.TextColor

	if TextStyleOverride then
		Description.Text = TextStyleOverride
	end

	ApplyDescription(self, Description, Theme, Theme[CHECKBOX_COLOR_ANIMATION_SPRING_SETTINGS], Theme[CHECKBOX_TRANSPARENCY_ANIMATION_SPRING_SETTINGS])
end

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

	return WithTheme(function(Theme)
		local SelectedState = GetSelectedStateFromValue(props.Selected)

		-- Apply Description
		if SelectedState == 'None' 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

		-- ////////// Checkbox Label

		local LabelFont = Theme[props.Font or LABEL_FONT]

		local Label = props.Label and new('TextLabel', {
			AnchorPoint = Vector2.new(0, 0.5),
			BackgroundTransparency = 1,
			FontFace = LabelFont.Font,
			Position = UDim2.new(0, props.Size.X.Offset + 20 + CHECKBOX_LEFT_MARGIN, 0.5, 0),
			Size = UDim2.new(1, -30, 1, 0),
			Text = props.Label,
			TextSize = LabelFont.Size,
			TextXAlignment = 'Left',

			TextColor3 = self.Text:map(function(Style)
				return Style.Color
			end),
			TextTransparency = self.Text:map(function(Style)
				return Style.Transparency
			end),
		})
		
		-- ////////// Check animation gradient

		local Gradient = new('UIGradient', {
			Transparency = NumberSequence.new({
				[1] = NumberSequenceKeypoint.new(0.0, 0),
				[2] = NumberSequenceKeypoint.new(0.001, 0),
				[3] = NumberSequenceKeypoint.new(0.002, 1),
				[4] = NumberSequenceKeypoint.new(1, 1)
			}),
			Offset = self.CheckObscureWidth:map(function(Width)
				return Vector2.new(1 - Width, 0)
			end)
		})

		-- ////////// Checkbox Icon
		
		local CheckStyle = Theme[SelectedState == 'Selected' and CHECKBOX_CHECKED or CHECKBOX_INDETERMINATE]

		local Check = new('ImageLabel', {
			Visible = SelectedState ~= 'None',
			AnchorPoint = Vector2.new(0.5, 0.5),
			BackgroundTransparency = 1,
			ResampleMode = CheckStyle.ResampleMode,
			Image = CheckStyle.Image,
			ImageRectOffset = CheckStyle.ImageRectOffset,
			ImageRectSize = CheckStyle.ImageRectSize,
			Position = UDim2.fromScale(0.5, 0.5),
			Size = UDim2.fromOffset(CheckStyle.ImageRectSize.X, CheckStyle.ImageRectSize.Y),

			ImageColor3 = self.Check:map(function(Style)
				return Style.Color
			end),
			ImageTransparency = self.Check:map(function(Style)
				return Style.Transparency
			end),
		}, {
			ObscureGradient = Gradient
		})

		-- ////////// Checkbox Stroke

		local Stroke = new('UIStroke', {
			ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
			Thickness = 1,

			Color = self.Stroke:map(function(Style)
				return Style.Color
			end),
			Transparency = self.Stroke:map(function(Style)
				return Style.Transparency
			end),
		})
		
		-- ////////// Checkbox Base

		local Checkbox = new('ImageButton', {
			AnchorPoint = Vector2.new(0, 0.5),
			AutoButtonColor = false,
			BorderSizePixel = 0,
			Position = UDim2.new(0, 1 + (props.Label and CHECKBOX_LEFT_MARGIN or 0), 0.5, 0),
			Size = props.Size - UDim2.fromOffset(2, 2), -- Target size is 20px * 20px, but border adds 2px each axis
			
			[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],

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

			Corners = new('UICorner', {
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Inner'] - 1) -- Account for the 1px added by UIStroke
			}),
		})

		-- ////////// Checkbox

		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			Position = props.Position,
			BackgroundTransparency = 1,
			Size = props.Size
		}, props.Native), {
			Checkbox = Checkbox,
			Label = Label
		})
	end)
end

return Checkbox
