-- Notification: Ported from roblox studio using codify plugin due to complexity, work on a better component soon

local Components = script.Parent
local Packages = Components.Parent.Packages

local Roact = require(Packages.Roact)
local new = Roact.createElement

return function (props)

	return new("Frame", {
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BackgroundTransparency = 0.4,
		BorderSizePixel = 0,
		Size = UDim2.fromScale(1, 0),
		AutoLocalize = false,
	}, {
		content = new("Frame", {
			AutomaticSize = Enum.AutomaticSize.Y,
			BackgroundColor3 = Color3.fromRGB(255, 255, 255),
			BackgroundTransparency = 1,
			LayoutOrder = 1,
			Size = UDim2.fromScale(1, 0),
		}, props[Roact.Children]),

		uIListLayout = new("UIListLayout", {
			SortOrder = Enum.SortOrder.LayoutOrder,
			VerticalAlignment = Enum.VerticalAlignment.Bottom,
		}),

		header = new("Frame", {
			BackgroundColor3 = Color3.fromRGB(0, 0, 0),
			BackgroundTransparency = 1,
			BorderColor3 = Color3.fromRGB(27, 42, 53),
			BorderSizePixel = 0,
			Size = UDim2.new(1, 0, 0, 25),
		}, {
			fill = new("CanvasGroup", {
				GroupColor3 = Color3.fromRGB(0, 0, 0),
				GroupTransparency = 0.5,
				BackgroundColor3 = Color3.fromRGB(255, 255, 255),
				BackgroundTransparency = 1,
				BorderSizePixel = 0,
				Size = UDim2.fromScale(1, 1),
				ZIndex = 0,
			}, {
				corner = new("Frame", {
					BackgroundColor3 = Color3.fromRGB(255, 255, 255),
					Size = UDim2.new(1, 0, 0, 35),
				}, {
					uICorner = new("UICorner", {
						CornerRadius = UDim.new(0, 4),
					}),

					cornerFilling = new("Frame", {
						AnchorPoint = Vector2.new(0, 1),
						BackgroundColor3 = Color3.fromRGB(255, 255, 255),
						BorderSizePixel = 0,
						Position = UDim2.fromScale(0, 1),
						Size = UDim2.new(1, 0, 0, 5),
					}),
				}),
			}),

			wrapper = new("Frame", {
				BackgroundColor3 = Color3.fromRGB(255, 255, 255),
				BackgroundTransparency = 1,
				Size = UDim2.fromScale(1, 1),
			}, {
				uIPadding = new("UIPadding", {
					PaddingBottom = UDim.new(0, 5),
					PaddingLeft = UDim.new(0, 5),
					PaddingRight = UDim.new(0, 5),
					PaddingTop = UDim.new(0, 5),
				}),

				left = new("Frame", {
					BackgroundColor3 = Color3.fromRGB(255, 255, 255),
					BackgroundTransparency = 1,
					Size = UDim2.fromScale(1, 1),
				}, {
					icon = props.Image and new("ImageLabel", {
						Image = props.Image,
						BackgroundColor3 = Color3.fromRGB(255, 255, 255),
						BackgroundTransparency = 1,
						BorderSizePixel = 0,
						Position = UDim2.fromOffset(5, 5),
						Size = UDim2.fromOffset(16, 16),
						ZIndex = 10,
						AutoLocalize = false,
					}),

					title = new("TextLabel", {
						FontFace = Font.fromEnum(Enum.Font.SourceSansBold),
						Text = props.TitleText,
						TextColor3 = Color3.fromRGB(255, 255, 255),
						TextSize = 14,
						TextStrokeColor3 = Color3.fromRGB(90, 90, 90),
						TextWrapped = true,
						TextXAlignment = Enum.TextXAlignment.Left,
						BackgroundTransparency = 1,
						BorderSizePixel = 0,
						Position = UDim2.fromOffset(30, 8),
						Size = UDim2.new(1, -60, 0, 15),
						ZIndex = 10,
						AutoLocalize = false,
					}),

					uIListLayout1 = new("UIListLayout", {
						Padding = UDim.new(0, 5),
						FillDirection = Enum.FillDirection.Horizontal,
						SortOrder = Enum.SortOrder.LayoutOrder,
						VerticalAlignment = Enum.VerticalAlignment.Center,
					}),
				}),

				right = new("Frame", {
					BackgroundColor3 = Color3.fromRGB(255, 255, 255),
					BackgroundTransparency = 1,
					Size = UDim2.fromScale(1, 1),
				}, {
					close = new("TextButton", {
						FontFace = Font.fromEnum(Enum.Font.FredokaOne),
						Text = "x",
						TextColor3 = Color3.fromRGB(255, 255, 255),
						TextSize = 19,
						TextWrapped = true,
						BackgroundColor3 = Color3.fromRGB(195, 33, 35),
						BackgroundTransparency = 1,
						BorderSizePixel = 0,
						Position = UDim2.new(1, -25, 0, 5),
						Size = UDim2.fromOffset(12, 12),
						ZIndex = 10,
						AutoLocalize = false,
						[Roact.Event.Activated] = props.OnClose,
					}),

					uIListLayout2 = new("UIListLayout", {
						Padding = UDim.new(0, 5),
						FillDirection = Enum.FillDirection.Horizontal,
						HorizontalAlignment = Enum.HorizontalAlignment.Right,
						SortOrder = Enum.SortOrder.LayoutOrder,
					}),
				}),
			}),
		}),

		uICorner1 = new("UICorner", {
			CornerRadius = UDim.new(0, 4),
		}),
	})
end


local Components = script.Parent
local Packages = Components.Parent.Packages

local Roact = require(Packages.Roact)
local new = Roact.createElement

local Notification = require(script.Parent.Notification)
local NotificationStory = Roact.PureComponent:extend(script.Name)

function NotificationStory:render()

	return new(Notification, {
		TitleText = "Notification",
		OnClose = warn,
	}, {
		Test = new('TextLabel', {
			Size = UDim2.fromOffset(200, 100),
			BackgroundTransparency = 0.9,
			Text = '[Roact.Children] goes here.\n200 x 100 test TextLabel',
			TextColor3 = Color3.fromRGB(255, 255, 255),
		})
	})
end

return NotificationStory


-- DropShadow: A component that is the base structure for a UI DropShadow

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

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

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

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

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

DropShadow.defaultProps = {
	CornerRadius = UDim.new(0, 8), --- @defaultProp
}

DropShadow.validateProps = t.strictInterface({

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

	--- @prop @optional @style BackgroundColor string|table The DropShadow's background color
	BackgroundColor = t.optional(StyleValidator),

	--- @prop @optional CornerRadius UDim Corner radius
	CornerRadius = t.optional(t.UDim),

	--- @prop @optional AutomaticSize Enum.AutomaticSize Auto sizing
	AutomaticSize = t.optional(t.EnumItem),

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

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

	--- @prop @optional [Roact.Children] table The contents to display in the DropShadow area
	[Roact.Children] = t.optional(t.table),

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

function DropShadow:render()
	local props = self.props or {}

	return WithTheme(function(Theme)

		-- ////////// DropShadow
		return new('ImageLabel', Sift.Dictionary.join({
			BackgroundTransparency = 1,
			AnchorPoint = props.AnchorPoint,
			Position = props.Position,
			Image = 'rbxassetid://186491278',
			ImageTransparency = 0.25,
			ScaleType = Enum.ScaleType.Slice,
			SliceCenter = Rect.new(20, 15, 76, 71),
			SliceScale = props.CornerRadius.Offset / 4, -- This works kinda?

			Size = props.Size,
		}, props.Native), props[Roact.Children])
	end)
end

return DropShadow


-- FocusFrame: This frame will show above all other ui

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

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

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

FocusFrame.defaultProps = {
	Priority = 1, --- @defaultProp
	RelativeToParent = false, --- @defaultProp
	ConsumeInput = true, --- @defaultProp
	BackgroundTransparency = 1, --- @defaultProp
	Size = UDim2.fromScale(1, 1), --- @defaultProp
}

FocusFrame.validateProps = t.interface({

	--- @prop @optional Priority number THe priority the frame will be shown at
	Priority = t.optional(t.number),

	--- @prop Reason string The reason that this frame is being used for orver another frame, this is for futureproofing, a few examples are: DROPDOWN, TOOLTIP, MOUSECURSORADORNMENT
	Reason = t.string,

	--- @prop @optional RelativeToParent boolean Determines if the position is relative to the Frame's parent
	RelativeToParent = t.optional(t.boolean),

	--- @prop @optional ConsumeInput boolean If true, the FocusFrame will block all input to other components, any attempt to interact with external components will cause the focus to be lost
	ConsumeInput = t.optional(t.boolean),

	--- @prop @optional [Roact.Event.FocusLost] function The functin to call when the focus is lost
	[Roact.Event.FocusLost] = t.optional(t.callback),
})

function FocusFrame:init()
	local props = self.props

	-- Connection container
	self.Connections = {}

	-- ////////// Safe Props

	local SafeProps =  Sift.Dictionary.copy(props)

	-- Remove unsafe props
	SafeProps.Priority = nil
	SafeProps.RelativeToParent = nil
	SafeProps.Reason = nil
	SafeProps.ConsumeInput = nil
	SafeProps[Roact.Event.FocusLost] = nil

	-- Register safe props
	self.SafeProps = SafeProps

	-- ////////// Wrapper Frame

	-- Frame reference for mirroring size and position
	self.rbx = props[Roact.Ref] or Roact.createRef() -- Roact does not support multiple refs, use provided
end

function FocusFrame:didMount()
	local SafeProps = self.SafeProps

	-- ////////// Wrapper Frame

	-- Create a wrapper frame
	local WrapperFrame = Instance.new('Frame')

	for Property, Value in pairs(SafeProps) do

		-- Avoid Roact PropMarkers
		if typeof(Property) ~= 'string' then
			continue
		end

		WrapperFrame[Property] = Value
	end

	-- Register
	self.WrapperFrame = WrapperFrame

	-- ////////// Mirror

	-- Mirror
	self:Update()

	local rbx = self.rbx.current
	local Gui = rbx:FindFirstAncestorWhichIsA('GuiMain') or rbx:FindFirstAncestorWhichIsA('PluginGui')

	self.Connections.Changed = rbx.Changed:Connect(function()
		self:Update()
	end)
end

function FocusFrame:didUpdate()
	self:Update()
end

function FocusFrame:Update()
	local props = self.props
	local rbx = self.rbx.current
	local WrapperFrame = self.WrapperFrame

	local Gui = rbx:FindFirstAncestorWhichIsA('GuiMain') or rbx:FindFirstAncestorWhichIsA('PluginGui')

	if not Gui then
		return
	end

	-- ////////// Input Consumer

	if props.ConsumeInput and not self.InputConsumer then

		local InputConsumer = Instance.new('ImageButton')
		InputConsumer.BackgroundTransparency = 1
		InputConsumer.Size = UDim2.fromScale(1, 1)

		-- Sink input
		self.Connections.InputConsumerHover = InputConsumer.MouseEnter:Connect(function()end)
		self.Connections.InputConsumerFocusLost = InputConsumer.MouseButton1Down:Connect(function()
			self:ReleaseFocus()
		end)

		self.InputConsumer = InputConsumer
		InputConsumer.Parent = Gui

		local ScrollConsumer = Instance.new('ScrollingFrame')
		ScrollConsumer.Size = UDim2.fromScale(1, 1)
		ScrollConsumer.ScrollingEnabled = true
		ScrollConsumer.CanvasSize = UDim2.fromScale(1, 1)
		ScrollConsumer.BorderSizePixel = 1
		ScrollConsumer.BackgroundTransparency = 1
		ScrollConsumer.ScrollBarImageTransparency = 1
		ScrollConsumer.ScrollBarThickness = 0

		ScrollConsumer.Parent = InputConsumer

	end

	-- ////////// Wrapper Frame

	local AbsoluteSize = rbx.AbsoluteSize
	local AbsolutePosition = rbx.AbsolutePosition
	local AbsoluteRotation = rbx.AbsoluteRotation

	WrapperFrame.Size = UDim2.fromOffset(AbsoluteSize.X, AbsoluteSize.Y)
	WrapperFrame.Position = UDim2.fromOffset(AbsolutePosition.X, AbsolutePosition.Y)
	WrapperFrame.Rotation = AbsoluteRotation

	-- Move children
	for _, Child in pairs(rbx:GetChildren()) do
		Child.Parent = WrapperFrame
	end

	-- Priority level
	WrapperFrame.ZIndex = props.Priority

	-- Show above all Gui with the MainGui
	WrapperFrame.Parent = Gui
end

function FocusFrame:ReleaseFocus()

	if self.props[Roact.Event.FocusLost] then
		task.spawn(self.props[Roact.Event.FocusLost], self.rbx.current)
	end
end

function FocusFrame:Cleanup()
	self.WrapperFrame:Destroy()

	if self.InputConsumer then
		self.InputConsumer:Destroy()
	end

	for _, Connection in pairs(self.Connections) do
		pcall(Connection.Disconnect, Connection)
	end
end

function FocusFrame:willUnmount()
	self:Cleanup()
end

function FocusFrame:render()
	local SafeProps = self.SafeProps

	-- ////////// FocusFrame
	return new('Frame', Sift.Dictionary.join({
		[Roact.Ref] = self.rbx,
	}, SafeProps))

end

return FocusFrame


-- UIStroke: A custom UIStroke wrapper with support for stroke inset

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

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

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

UIStroke.defaultProps = {
	Thickness = 1, --- @defaultProp
	BorderMode = Enum.BorderMode.Inset, --- @defaultProp
}

function UIStroke:ClearConnections()

	if self.SizeChangedConnection then
		self.SizeChangedConnection = self.SizeChangedConnection:Disconnect()
	end

	if self.CornerChangedConnection then
		self.CornerChangedConnection = self.CornerChangedConnection:Disconnect()
	end

	if self.ChildAddedConnection then
		self.ChildAddedConnection = self.ChildAddedConnection:Disconnect()
	end
end

function UIStroke:willUnmount()
	self:ClearConnections()
end

function UIStroke:ApplyChanges(rbx)
	local props = self.props
	local Parent = rbx and rbx.Parent

	if rbx and Parent and Parent:IsA('GuiObject') then

		-- Apply the size
		local AbsoluteSize = Parent.AbsoluteSize
		rbx.Size = UDim2.fromOffset(AbsoluteSize.X, AbsoluteSize.Y)
		- (props.BorderMode == Enum.BorderMode.Inset and UDim2.fromOffset(props.Thickness * 2, props.Thickness * 2) or UDim2.new()) -- Account for stroke width and inset

		-- Apply corner radius
		local Corner = Parent:FindFirstChildOfClass('UICorner')
		if Corner then

			if not self.CornerChangedConnection then
				self.CornerChangedConnection = Corner:GetPropertyChangedSignal('CornerRadius'):Connect(function()
					self:ApplyChanges(rbx)
				end)
			end

			rbx.Corner.CornerRadius = Corner.CornerRadius - (props.BorderMode == Enum.BorderMode.Inset and UDim.new(0, props.Thickness) or UDim.new()) -- Account for stroke width
		end
	end
end

function UIStroke:render()
	local props = self.props

	local SafeProps = Sift.Dictionary.copy(props)
	SafeProps.BorderMode = nil

	return new('Frame', {
		AnchorPoint = Vector2.new(0.5, 0.5),
		BackgroundTransparency = 1,
		Position = UDim2.fromScale(0.5, 0.5),

		-- Used for size and corner detection
		[Roact.Ref] = function (rbx: GuiObject)

			if rbx and rbx.Parent and rbx.Parent:IsA('GuiObject') then

				-- Prepare
				self:ClearConnections()
				self:ApplyChanges(rbx)

				-- Detect whenever an applicable UI modifier is added
				self.ChildAddedConnection = rbx.Parent.ChildAdded:Connect(function()
					self:ApplyChanges(rbx)
				end)

				-- Detect whenever the parent frame's size is changed
				self.SizeChangedConnection = rbx.Parent:GetPropertyChangedSignal('AbsoluteSize'):Connect(function()
					self:ApplyChanges(rbx)
				end)
			end
		end
	}, {
		Stroke = new('UIStroke', SafeProps),

		Corner = new('UICorner', {
			CornerRadius = UDim.new(0, 0),
		}),
	})
end

return UIStroke


-- Flyout: Flyout component

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

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

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

local Acrylic = require(Winro.App.Surface.Acrylic)

local FLYOUT_BASE_FILL = "colors/Background/Fill_Color/Acrylic_Background/Base"
local FLYOUT_BASE_STROKE = "colors/Stroke_Color/Surface_Stroke/Flyout"
local FLYOUT_TEXT_STYLE = "colors/Fill_Color/Text/Primary"
local FLYOUT_FONT_STYLE = "fonts/Body"

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

Flyout.defaultProps = {
	Size = UDim2.fromOffset(320, 72), --- @defaultProp
	FitContents = true, --- @defaultProp
}

Flyout.validateProps = t.strictInterface({

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

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

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

	--- @prop @optional RichText boolean The Flyout's rich text property
	RichText = t.optional(t.boolean),

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

	--- @prop @optional FitContents boolean Determines if the Flyout will resize to fit it's contents
	FitContents = t.optional(t.boolean),

	--- @prop @optional Native table {property:value} The native props for the Flyout's main element
	Native = t.optional(t.table),

	--- @prop @optional [Roact.Children] table The flyout's contents
	[Roact.Children] = t.optional(t.table),
})

function Flyout:init()

	-- Content fitting
	self.ContentSize, self.SetContentSize = Roact.createBinding(self.props.Size)
end

function Flyout:render()
	local props = self.props

	return WithTheme(function(Theme)

		-- ////////// Flyout Surface

		local SurfaceStyle = Theme[FLYOUT_BASE_FILL]
		local StrokeStyle = Theme[FLYOUT_BASE_STROKE]

		local Base = new(Acrylic, {
			BackgroundColor3 = SurfaceStyle.Color,
			BackgroundTransparency = SurfaceStyle.Transparency,
			BorderSizePixel = 0,
			Size = self.ContentSize,
		}, {

			Corners = new('UICorner', {
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Outer'] - 1), -- -1px to account for stroke
			}),
			Stroke = new('UIStroke', {
				Color = StrokeStyle.Color,
				Transparency = StrokeStyle.Transparency,
				Thickness = 1,
			}),
		})

		-- ////////// Flyout Content

		local TextStyle = Theme[FLYOUT_TEXT_STYLE]
		local TextFont = Theme[FLYOUT_FONT_STYLE]

		local Container = new('Frame', {
			AutomaticSize = props.FitContents and 'Y' or 'None',
			BackgroundTransparency = 1,
			Size = self.ContentSize,
		}, {

			Layout = new('UIListLayout', {
				SortOrder = Enum.SortOrder.LayoutOrder
			}),
			Content = new('Frame', {
				AutomaticSize = props.FitContents and 'Y' or 'None',
				BackgroundTransparency = 1,
				Size = self.ContentSize,

				[Roact.Change.AbsoluteSize] = function(rbx)
					if props.FitContents then
						self.SetContentSize(UDim2.fromOffset(rbx.AbsoluteSize.X, rbx.AbsoluteSize.Y))
					end
				end,
			}, {
				Contents = Roact.createFragment(props[Roact.Children] or {}),

				_Padding = new('UIPadding', {
					PaddingBottom = UDim.new(0, 16),
					PaddingLeft = UDim.new(0, 16),
					PaddingRight = UDim.new(0, 16),
					PaddingTop = UDim.new(0, 16),
				}),
				_Layout = new('UIListLayout', {
					FillDirection = Enum.FillDirection.Vertical,
					Padding = UDim.new(0, 16),
					SortOrder = Enum.SortOrder.LayoutOrder,
				}),
				_Text = props.Text and new('TextLabel', {
					LayoutOrder = -1,
					BackgroundTransparency = 1,
					AutomaticSize = props.FitContents and 'Y' or 'None',
					FontFace = TextFont.Font,
					Size = UDim2.fromScale(1, props.FitContents and 0 or 1),
					LineHeight = TextFont.LineHeight,
					RichText = props.RichText,
					Text = props.Text,
					TextColor3 = TextStyle.Color,
					TextSize = TextFont.Size,
					TextTransparency = TextStyle.Transparency,
					TextWrapped = true,
					TextXAlignment = 'Left',
					TextYAlignment = 'Top',
				}),
			})
		})

		-- ////////// Flyout

		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			AutomaticSize = props.FitContents and 'Y' or 'None',
			BackgroundTransparency = 1,
			Position = props.Position,
			Size = self.ContentSize,

			[Roact.Change.AbsoluteSize] = function(rbx)
				self.SetContentSize(UDim2.fromOffset(rbx.AbsoluteSize.X, rbx.AbsoluteSize.Y))
			end,
		}, props.Native), {
			Base = Base,
			Container = Container,
		})
	end)
end

return Flyout


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

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

local Flyout = require(script.Parent.Flyout)
local Button = require(Winro.App.Input.Button.Button)

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

function FlyoutStory:render()
	return new(Flyout, Sift.Dictionary.join({
		Size = UDim2.new(1, 0, 0, 0),
		FitContents = true,
		Text = 'This is the flyout component\nThe flyout component should not be used as a dialog but instead to show quick actions or notification alerts.',
		[Roact.Children] = self.props.IncludeContents and new(Button, {
			Size = UDim2.new(0, 150, 0, 30),
			Text = 'Contents'
		})
	}, self.props.controls))
end

return {
	controls = {
		IncludeContents = true,
	},
	stories = {
		Flyout = function (props)
			return new(FlyoutStory, props.controls)
		end
	}
}


-- Tooltip: A flyout style label

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

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

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

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

local GetTextSize = require(Winro.Utility.GetTextSize)
local DropShadow = require(Winro.App.Effect.DropShadow)
local Acrylic = require(Winro.App.Surface.Acrylic)

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

Tooltip.defaultProps = {
	AutomaticSize = Enum.AutomaticSize.XY, --- @defaultProp
	Position = UDim2.new(), --- @defaultProp
	Size = UDim2.fromOffset(0, 28), --- @defaultProp
	DropShadowEnabled = true, --- @defaultProp
}

Tooltip.validateProps = t.strictInterface({

	--- @prop @optional AutomaticSize Enum.AutomaticSize The Tooltip's automatic scaling
	AutomaticSize = t.optional(t.enum(Enum.AutomaticSize)),

	--- @prop @optional AnchorPoint Vector2 Anchor point
	AnchorPoint = t.optional(t.Vector2),

	--- @prop @optional DropShadowEnabled boolan Drop shadow's enabled status
	DropShadowEnabled = t.optional(t.boolean),

	--- @prop @optional Position UDim2 Position
	Position = t.optional(t.UDim2),

	--- @prop @optional Size UDim2 Size
	Size = t.optional(t.UDim2),

	--- @prop Text string The Tooltip's text
	Text = t.string,

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

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

function Tooltip:init()

	-- Size binding, for `AutomaticSize`
	self.Size, self.SetSize = Roact.createBinding(UDim2.new())
end

function Tooltip:render()
	local props = self.props

	return WithTheme(function(Theme)

		-- ////////// Text label

		local LabelFontStyle = Theme['fonts/Caption']
		local LabelTextStyle = Theme['colors/Fill_Color/Text/Primary']

		local GotTextSize, TextSize = GetTextSize(props.Text, LabelFontStyle.Font, LabelFontStyle.Size)

		-- Fallback TextSize
		if not GotTextSize then
			warn('GetTextSize() Failed:', TextSize)
			TextSize = Vector2.new(0, 100)
		end

		local Label = new('TextLabel', {
			AutomaticSize = if GotTextSize then props.AutomaticSize else Enum.AutomaticSize.X,
			BackgroundTransparency = 1,
			FontFace = LabelFontStyle.Font,
			LayoutOrder = 2,
			Size = UDim2.fromOffset(TextSize.X, TextSize.Y),
			Text = props.Text,
			TextColor3 = LabelTextStyle.Color,
			TextSize = LabelFontStyle.Size,
			TextTransparency = LabelTextStyle.Transparency,
			TextXAlignment = Enum.TextXAlignment.Left,
		})

		-- ////////// 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 Icon = IconImage and new('ImageLabel', {
			AnchorPoint = Vector2.new(0.5, 0.5),
			BackgroundTransparency = 1,
			Image = IconImage.Image,
			ImageColor3 = LabelTextStyle.Color,
			ImageRectOffset = IconImage.ImageRectOffset,
			ImageRectSize = IconImage.ImageRectSize,
			ImageTransparency = LabelTextStyle.Transparency,
			Position = UDim2.fromScale(0.5, 0.5),
			ResampleMode = IconImage.ResampleMode,
			Size = IconImageSize,
		})

		-- ////////// Text and Icon Wrapper

		local Wrapper = new('Frame', {
			AutomaticSize = props.AutomaticSize,
			BackgroundTransparency = 1,
		}, {
			Text = Label,
			Icon = Icon,

			Layout = new('UIListLayout', {
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = Enum.VerticalAlignment.Center,
				FillDirection = Enum.FillDirection.Horizontal,
				Padding = UDim.new(0, 8),
			}),
		})

		-- ////////// Stroke

		local StrokeStyle = Theme['colors/Stroke_Color/Surface_Stroke/Flyout']

		local Stroke = new('UIStroke', {
			Color = StrokeStyle.Color,
			Transparency = StrokeStyle.Transparency,
		})

		-- ////////// Tooltip

		local TooltipStyle = Theme['colors/Background/Fill_Color/Acrylic_Background/Default']

		local Tooltip = new(Acrylic, {
			AutomaticSize = props.AutomaticSize,
			AnchorPoint = props.DropShadowEnabled and Vector2.new(0.5, 0.5) or props.AnchorPoint,
			BackgroundColor3 = TooltipStyle.Color,
			BackgroundTransparency = TooltipStyle.Transparency,
			Position = props.DropShadowEnabled and UDim2.fromScale(0.5, 0.5) or props.Position,
			Size = props.Size,

			[Roact.Change.AbsoluteSize] = function (rbx)
				local AbsoluteSize = rbx.AbsoluteSize

				self.SetSize(UDim2.fromOffset(AbsoluteSize.X, AbsoluteSize.Y) - UDim2.fromOffset(8 + 8, 7 + 5)) -- Account for padding
			end
		}, {
			TextAndIconWrapper = Wrapper,
			Stroke = Stroke,

			Corners = new('UICorner', {
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Inner']),
			}),
			Layout = new('UIListLayout', {
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = Enum.VerticalAlignment.Center,
				Padding = UDim.new(0, 10),
			}),
			Padding = new('UIPadding', {
				PaddingBottom = UDim.new(0, 7),
				PaddingLeft = UDim.new(0, 8),
				PaddingRight = UDim.new(0, 8),
				PaddingTop = UDim.new(0, 5),
			}),
		})

		if props.DropShadowEnabled then
			return new(DropShadow, {
				AnchorPoint = props.AnchorPoint,
				Position = props.Position - UDim2.fromOffset(20 / 2, 20 / 2),
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Inner']),
				Size = self.Size:map(function(Size)
					return Size + UDim2.fromOffset(20, 20) + UDim2.fromOffset(8 + 8, 7 + 5) -- Accont for padding
				end),
			}, Tooltip)
		else
			return Tooltip
		end
	end)
end

return Tooltip


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

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

local Tooltip = require(script.Parent.Tooltip)
local TooltipStory = Roact.PureComponent:extend(script.Name)

function TooltipStory:render()
	local props = self.props

	return new(Tooltip, Sift.Dictionary.join({
		Text = props.Text .. (props.Wrapped and '\nThat is wrapped' or ''),
		Icon = props.Icon and 'images/roblox/icons/logo/studiologo_small/1x' or nil,
	}, props.Props))
end

return {
	controls = {
		Text = 'Tooltip Text',
		Icon = false,
		Wrapped = false,
	},
	stories = {
		Tooltip = function(props)
			return new(TooltipStory, props.controls)
		end,
	}
}


-- Button: The basic button component
-- https://www.figma.com/file/uNmIxgdbUT44MZjQCTIMe3/Windows-UI-3-(Community)?node-id=24412%3A121169

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 Sift = require(Packages.Sift)
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local new = Roact.createElement

local ButtonRoot = script.Parent
local ButtonStyleValidator = require(ButtonRoot.Validators.ButtonStyleValidator)
local Descriptions = require(ButtonRoot.Descriptions.ButtonStateDescriptions)

local BUTTON_COLOR_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Button/Color"
local BUTTON_TRANSPARENCY_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Button/Transparency"

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

Button.StyleBindings = {
	'Fill',
	'Stroke',
	'Text',
}

Button.defaultProps = {
	AnchorPoint = Vector2.new(0.5, 0.5), --- @defaultProp
	BorderSizePixel = 1, --- @defaultProp
	Font = "fonts/Body", --- @defaultProp
	Position = UDim2.fromScale(0.5, 0.5), --- @defaultProp
	Size = UDim2.fromOffset(240, 30), --- @defaultProp
	Style = 'Standard', --- @defaultProp
	Text = 'Text', --- @defaultProp
}

Button.validateProps = t.strictInterface({

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

	--- @prop @optional @style BackgroundColor string|table The style to use for the Button's background color
	BackgroundColor = t.optional(StyleValidator),

	--- @prop @optional @style BorderColor string|table The button's border style
	BorderColor = t.optional(StyleValidator),

	--- @prop @optional BorderSizePixel number The size, in pixels, of the border
	BorderSizePixel = t.optional(t.numberPositive),

	--- @prop @optional CornerRadius UDim The radius of the Button's corners
	CornerRadius = t.optional(t.UDim),

	--- @prop @optional Disabled boolean If `true`, the button will appear as disabled
	Disabled = t.optional(t.boolean),

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

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

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

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

	--- @prop @optional Style string The Button's style
	Style = t.optional(ButtonStyleValidator),

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

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

	--- @prop @optional [Roact.Children] table Child contents
	[Roact.Children] = 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 Button:init()

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

	-- Prop Overrides
	local FillOverride = props.BackgroundColor
	local StrokeOverride = props.BorderColor
	local TextOverride = props.TextColor

	if FillOverride then
		Description.Fill = FillOverride
	end

	if StrokeOverride then
		Description.Stroke = StrokeOverride
	end

	if TextOverride then
		Description.Text = TextOverride
	end

	ApplyDescription(self, Description, Theme, Theme[BUTTON_COLOR_ANIMATION_SPRING_SETTINGS], Theme[BUTTON_TRANSPARENCY_ANIMATION_SPRING_SETTINGS])
end

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

	return WithTheme(function(Theme)

		-- Apply Description
		if props.Style == 'Accent' then
			if props.Disabled then
				self:ApplyDescription(Descriptions.Accent.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.Accent.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.Accent.Hover, Theme)
			elseif state.State == 'Pressed' then
				self:ApplyDescription(Descriptions.Accent.Pressed, Theme)
			end
		elseif props.Style == 'Standard' or props.Style == 'Default' then
			if props.Disabled then
				self:ApplyDescription(Descriptions.Standard.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.Standard.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.Standard.Hover, Theme)
			elseif state.State == 'Pressed' then
				self:ApplyDescription(Descriptions.Standard.Pressed, Theme)
			end
		elseif props.Style == 'Primary' then
			if props.Disabled then
				self:ApplyDescription(Descriptions.Primary.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.Primary.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.Primary.Hover, Theme)
			elseif state.State == 'Pressed' then
				self:ApplyDescription(Descriptions.Primary.Pressed, Theme)
			end
		elseif props.Style == 'Secondary' then
			if props.Disabled then
				self:ApplyDescription(Descriptions.Secondary.Disabled, Theme)
			elseif state.State == 'Rest' then
				self:ApplyDescription(Descriptions.Secondary.Rest, Theme)
			elseif state.State == 'Hover' then
				self:ApplyDescription(Descriptions.Secondary.Hover, Theme)
			elseif state.State == 'Pressed' then
				self:ApplyDescription(Descriptions.Secondary.Pressed, Theme)
			end
		end

		-- ////////// Button Stroke

		local Stroke = new('UIStroke', {
			Enabled = not props.Disabled,
			ApplyStrokeMode = 'Border',
			Thickness = props.BorderSizePixel,

			Color = self.Stroke:map(function(Style)
				return Style.Color
			end),
		}, {

			Gradient = new('UIGradient', {
				Rotation = 90,

				Color = self.Stroke:map(function(Style)
					return (Style.ColorSequence or ColorSequence.new(Style.Color))
				end),
				Transparency = self.Stroke:map(function(Style)
					return (Style.TransparencySequence or NumberSequence.new(Style.Transparency))
				end),
			})
		})

		-- ////////// Button

		local ButtonFont = Theme[props.Font]

		local Button = new('TextButton', {
			AutoButtonColor = false,
			AnchorPoint = Vector2.new(0.5, 0.5),
			FontFace = ButtonFont.Font,
			Position = UDim2.fromScale(0.5, 0.5),
			Size = UDim2.fromScale(1, 1) - UDim2.fromOffset(2, 2), -- Account for stroke thickness
			Text = props.Text,
			TextSize = ButtonFont.Size,

			BackgroundColor3 = self.Fill:map(function(Style)
				return Style.Color
			end),
			BackgroundTransparency = self.Fill:map(function(Style)
				return Style.Transparency
			end),
			TextColor3 = self.Text:map(function(Style)
				return Style.Color
			end),
			TextTransparency = self.Text: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],
		}, {
			Stroke = Stroke,

			Corners = new('UICorner', {
				CornerRadius = props.CornerRadius or UDim.new(0, Theme['styles/CornerRadius/Default/Inner']),
			}),
		})

		-- ////////// Content Wrapper

		local ContentWrapper = props[Roact.Children] and new('Frame', {
			BackgroundTransparency = 1,
			Size = UDim2.fromScale(1, 1),
		}, props[Roact.Children]) or nil

		-- ////////// Button Wrapper
		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			LayoutOrder = props.LayoutOrder,
			Position = props.Position,
			Size = props.Size,
		}, props.Native), {
			Button = Button,
			ContentWrapper = ContentWrapper,
		})
	end)
end

return Button


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

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

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

local ButtonRoot = script.Parent
local Button = require(ButtonRoot.Button)

local BORDER_PADDING = UDim.new(0, 8)

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

function ButtonStory:render()
	local props = self.props

	return WithTheme(function(Theme)
		return new('Frame', {
			BackgroundTransparency = props.BackgroundTransparency,
			BackgroundColor3 = Theme['colors/Background/Fill_Color/Solid_Background/Base'].Color,
			AutomaticSize = 'XY'
		}, {
			Padding = new('UIPadding', {
				PaddingBottom = BORDER_PADDING,
				PaddingLeft = BORDER_PADDING,
				PaddingRight = BORDER_PADDING,
				PaddingTop = BORDER_PADDING,
			}),
			Button = new(Button, {
				AnchorPoint = Vector2.new(0, 0),
				Disabled = props.Disabled,
				Position = UDim2.fromScale(0, 0),
				Style = props.Style,
				[Roact.Event.Activated] = print
			})
		})
	end)
end

return {
	controls = {
		Disabled = false,
		Style = {
			'Accent',
			'Primary',
			'Secondary',
			'Standard',
			'Default',
		}
	},
	stories = {
		Button = function (props)
			return Roact.createFragment({
				OnBackground = new(ButtonStory, Sift.Dictionary.join({
					BackgroundTransparency = 0,
				}, props.controls)),
				NoBackground = new(ButtonStory, Sift.Dictionary.join({
					BackgroundTransparency = 1,
				}, props.controls))
			})
		end
	}
}


-- ButtonStack: A button list

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

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

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

local ButtonRoot = script.Parent
local Button = require(ButtonRoot.Button)

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

ButtonStack.defaultProps = {
	ButtonHeight = UDim.new(0, 30), --- @defaultProp,
	Padding = UDim.new(0, 8), --- @defaultProp
	ButtonAlignment = Enum.HorizontalAlignment.Left, --- @defaultProp
	HorizontalAlignment = Enum.HorizontalAlignment.Right, --- @defaultProp
	VerticalAlignment = Enum.VerticalAlignment.Center, --- @defaultProp
	MinSlack = 50, --- @defaultProp
	StackWidth = UDim.new(1, 0), --- @defaultProp
	PaddingLeft = UDim.new(), --- @defaultProp
	PaddingRight = UDim.new(), --- @defaultProp
	PaddingBottom = UDim.new(), --- @defaultProp
	PaddingTop = UDim.new(), --- @defaultProp
}

ButtonStack.validateProps = t.strictInterface({

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

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

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

	--- @prop @optional FillWidth boolean Determines if buttons fill the entire Stack
	FillWidth = t.optional(t.boolean),

	--- @prop @optional Padding UDim The padding between each button
	Padding = t.optional(t.UDim),

	--- @prop @optional PaddingLeft UDim The left padding
	PaddingLeft = t.optional(t.UDim),

	--- @prop @optional PaddingRight UDim The right padding
	PaddingRight = t.optional(t.UDim),

	--- @prop @optional PaddingTop UDim The top padding
	PaddingTop = t.optional(t.UDim),

	--- @prop @optional PaddingBottom UDim The bottom padding
	PaddingBottom = t.optional(t.UDim),

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

	--- @prop @optional StackWidth UDim The space to fill
	StackWidth = t.optional(t.UDim),

	--- @prop Buttons table The props for the buttons to display
	Buttons = t.table,

	--- @prop @optional ButtonHeight UDim Each Button's height
	ButtonHeight = t.optional(t.UDim),

	--- @prop @optional ButtonAlignment Enum.HorizontalAlignment The aligmnent of buttons
	ButtonAlignment = t.optional(t.enum(Enum.HorizontalAlignment)),

	--- @prop @optional HorizontalAlignment Enum.HorizontalAlignment The Horizontal alignment of the buttons
	HorizontalAlignment = t.optional(t.enum(Enum.HorizontalAlignment)),

	--- @prop @optional VerticalAlignment Enum.VerticalAlignment The Vertical alignment of the buttons
	VerticalAlignment = t.optional(t.enum(Enum.VerticalAlignment)),

	--- @prop @optional MinSlack number The MinSlack width between the end of the buttons to the end of the stack before buttons fill the entire Stack
	MinSlack = t.optional(t.number),

	--- @prop @optional [Roact.Children] table Roact.Children
	[Roact.Children] = t.optional(t.table),

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

function ButtonStack:init()

	-- Initial state
	self:setState({
		Width = 0
	})
end

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

	return WithTheme(function(Theme)

		-- ////////// Buttons
		local Buttons = {}

		-- Get the divided size for each button
		local FullWidthSize = UDim2.new(1 / #props.Buttons)

		local IsFirstButton = true
		for ButtonIndex, ButtonProps in pairs(props.Buttons) do

			Buttons['Button' .. ButtonIndex] = new(Button, Sift.Dictionary.join(ButtonProps, {
				LayoutOrder = props.ButtonAlignment == Enum.HorizontalAlignment.Right and -ButtonIndex or ButtonIndex,
				Size = UDim2.new(FullWidthSize.X.Scale, FullWidthSize.X.Offset - (IsFirstButton and 0 or props.Padding.Offset), props.ButtonHeight.Scale, props.ButtonHeight.Offset),
			}))

			-- Register first button
			IsFirstButton = false
		end

		-- ////////// Button Container

		local ButtonContainer = new('Frame', {
			BackgroundTransparency = 1,
			Size = UDim2.new(props.StackWidth.Scale, props.StackWidth.Offset, 1, 0),
		}, {
			Buttons = Roact.createFragment(Buttons),

			Layout = new('UIListLayout', {
				FillDirection = Enum.FillDirection.Horizontal,
				HorizontalAlignment = props.HorizontalAlignment,
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = props.VerticalAlignment,
				Padding = props.Padding,
			}),
		})

		-- ////////// Button Stack
		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			LayoutOrder = props.LayoutOrder,
			Position = props.Position,
			Size = props.Size,

			[Roact.Change.AbsoluteSize] = function (rbx)
				self:setState({
					Width = rbx.AbsoluteSize.X
				})
			end,
		}, props.Native), {
			ButtonContainer = ButtonContainer,
			PropChildren = Roact.createFragment(props[Roact.Children] or {}),

			Layout = new('UIListLayout', {
				FillDirection = Enum.FillDirection.Horizontal,
				HorizontalAlignment = props.HorizontalAlignment,
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = props.VerticalAlignment,
			}),
			Padding = new('UIPadding', {
				PaddingTop = props.PaddingTop,
				PaddingBottom = props.PaddingBottom,
				PaddingLeft = props.PaddingLeft,
				PaddingRight = props.PaddingRight,
			}),
		})
	end)
end

return ButtonStack


-- ButtonStack: A button list

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

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

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

local ButtonRoot = script.Parent
local Button = require(ButtonRoot.Button)

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

ButtonStack.defaultProps = {
	ButtonHeight = UDim.new(0, 30), --- @defaultProp,
	Padding = UDim.new(0, 8), --- @defaultProp
	ButtonAlignment = Enum.HorizontalAlignment.Left, --- @defaultProp
	HorizontalAlignment = Enum.HorizontalAlignment.Right, --- @defaultProp
	VerticalAlignment = Enum.VerticalAlignment.Center, --- @defaultProp
	MinSlack = 50, --- @defaultProp
	StackWidth = UDim.new(1, 0), --- @defaultProp
	PaddingLeft = UDim.new(), --- @defaultProp
	PaddingRight = UDim.new(), --- @defaultProp
	PaddingTop = UDim.new(), --- @defaultProp
}

ButtonStack.validateProps = t.strictInterface({

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

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

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

	--- @prop @optional FillWidth boolean Determines if buttons fill the entire Stack
	FillWidth = t.optional(t.boolean),

	--- @prop @optional Padding UDim The padding between each button
	Padding = t.optional(t.UDim),

	--- @prop @optional PaddingLeft UDim The left padding
	PaddingLeft = t.optional(t.UDim),

	--- @prop @optional PaddingRight UDim The right padding
	PaddingRight = t.optional(t.UDim),

	--- @prop @optional PaddingTop UDim The top padding
	PaddingTop = t.optional(t.UDim),

	--- @prop @optional PaddingBottom UDim The bottom padding
	PaddingBottom = t.optional(t.UDim),

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

	--- @prop @optional StackWidth UDim The space to fill
	StackWidth = t.optional(t.UDim),

	--- @prop Buttons table The props for the buttons to display
	Buttons = t.table,

	--- @prop @optional ButtonHeight UDim Each Button's height
	ButtonHeight = t.optional(t.UDim),

	--- @prop @optional ButtonAlignment Enum.HorizontalAlignment The aligmnent of buttons
	ButtonAlignment = t.optional(t.enum(Enum.HorizontalAlignment)),

	--- @prop @optional HorizontalAlignment Enum.HorizontalAlignment The Horizontal alignment of the buttons
	HorizontalAlignment = t.optional(t.enum(Enum.HorizontalAlignment)),

	--- @prop @optional VerticalAlignment Enum.VerticalAlignment The Vertical alignment of the buttons
	VerticalAlignment = t.optional(t.enum(Enum.VerticalAlignment)),

	--- @prop @optional MinSlack number The MinSlack width between the end of the buttons to the end of the stack before buttons fill the entire Stack
	MinSlack = t.optional(t.number),

	--- @prop @optional [Roact.Children] table Roact.Children
	[Roact.Children] = t.optional(t.table),

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

function ButtonStack:init()

	-- Initial state
	self:setState({
		Width = 0
	})
end

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

	return WithTheme(function(Theme)

		-- ////////// Buttons
		local Buttons = {}

		-- Get the divided size for each button
		local FullWidthSize = UDim2.new(1 / #props.Buttons)

		local IsFirstButton = true
		for ButtonIndex, ButtonProps in pairs(props.Buttons) do

			Buttons['Button' .. ButtonIndex] = new(Button, Sift.Dictionary.join(ButtonProps, {
				LayoutOrder = props.ButtonAlignment == Enum.HorizontalAlignment.Right and -ButtonIndex or ButtonIndex,
				Size = UDim2.new(FullWidthSize.X.Scale, FullWidthSize.X.Offset - (IsFirstButton and 0 or props.Padding.Offset), props.ButtonHeight.Scale, props.ButtonHeight.Offset),
			}))

			-- Register first button
			IsFirstButton = false
		end

		-- ////////// Button Container

		local ButtonContainer = new('Frame', {
			BackgroundTransparency = 1,
			Size = UDim2.new(props.StackWidth.Scale, props.StackWidth.Offset, 1, 0),
		}, {
			Buttons = Roact.createFragment(Buttons),

			Layout = new('UIListLayout', {
				FillDirection = Enum.FillDirection.Horizontal,
				HorizontalAlignment = props.HorizontalAlignment,
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = props.VerticalAlignment,
				Padding = props.Padding,
			}),
		})

		-- ////////// Button Stack
		return new('Frame', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			LayoutOrder = props.LayoutOrder,
			Position = props.Position,
			Size = props.Size,

			[Roact.Change.AbsoluteSize] = function (rbx)
				self:setState({
					Width = rbx.AbsoluteSize.X
				})
			end,
		}, props.Native), {
			ButtonContainer = ButtonContainer,
			PropChildren = Roact.createFragment(props[Roact.Children] or {}),

			Layout = new('UIListLayout', {
				FillDirection = Enum.FillDirection.Horizontal,
				HorizontalAlignment = props.HorizontalAlignment,
				SortOrder = Enum.SortOrder.LayoutOrder,
				VerticalAlignment = props.VerticalAlignment,
			}),
			Padding = new('UIPadding', {
				PaddingLeft = props.PaddingLeft,
				PaddingRight = props.PaddingRight,
			}),
		})
	end)
end

return ButtonStack


-- TitleBar: The top bar of a window component

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

local FrameVisualizer = require(Winro.Utility.FrameVisualizer)

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

local ButtonStack = require(script.Parent.ButtonStack)

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

function ButtonStackStory:render()

	local ButtonStack = new(ButtonStack, Sift.Dictionary.join({
		Size = UDim2.new(1, 0, 0, 30),
		ButtonHeight = UDim.new(0, 30),
		Buttons = {
			[1] = {
				Text = 'Primary',
				Style = 'Accent',
				[Roact.Event.Activated] = print,
			},
			[2] = {
				Text = 'Secondary',
				Style = 'Standard',
				[Roact.Event.Activated] = print,
			},
			[3] = {
				Text = 'Tertiary',
				Style = 'Standard',
				[Roact.Event.Activated] = print,
			},
		}
	}, self.props.Props))

	if self.props.Wireframe then
		return new(FrameVisualizer, {
			Size = UDim2.new(1, 0, 1, 0),
		}, ButtonStack)
	else
		return ButtonStack
	end
end

return {
	controls = {
		Wireframe = false,
	},
	stories = {
		ButtonStack = function(props)
			return new(ButtonStackStory, props.controls)
		end
	}
}


return {
	Accent = {
		Disabled = {
			Name = 'Accent/Disabled',
			Fill = 'colors/Fill_Color/Accent/Disabled',
			Stroke = 'colors/Fill_Color/Accent/Disabled',
			Text = 'colors/Fill_Color/Text_On_Accent/Disabled',
		},
		Rest = {
			Name = 'Accent/Rest',
			Fill = 'colors/Fill_Color/Accent/Default',
			Stroke = 'colors/Elevation/Accent_Control/Border',
			Text = 'colors/Fill_Color/Text_On_Accent/Primary',
		},
		Hover = {
			Name = 'Accent/Hover',
			Fill = 'colors/Fill_Color/Accent/Secondary',
			Stroke = 'colors/Elevation/Accent_Control/Border',
			Text = 'colors/Fill_Color/Text_On_Accent/Primary',
		},
		Pressed = {
			Name = 'Accent/Pressed',
			Fill = 'colors/Fill_Color/Accent/Tertiary',
			Stroke = 'colors/Stroke_Color/Control_Stroke/On_Accent_Default',
			Text = 'colors/Fill_Color/Text_On_Accent/Secondary',
		},
	},
	Standard = {
		Disabled = {
			Name = 'Standard/Disabled',
			Fill = 'colors/Fill_Color/Control/Disabled',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Disabled',
		},
		Rest = {
			Name = 'Standard/Rest',
			Fill = 'colors/Fill_Color/Control/Default',
			Stroke = 'colors/Elevation/Control/Border',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Hover = {
			Name = 'Standard/Hover',
			Fill = 'colors/Fill_Color/Control/Secondary',
			Stroke = 'colors/Elevation/Control/Border',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Pressed = {
			Name = 'Standard/Pressed',
			Fill = 'colors/Fill_Color/Control/Tertiary',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Secondary',
		},
	},
	Primary = {
		Disabled = {
			Name = 'Primary/Disabled',
			Fill = 'colors/Fill_Color/Accent/Disabled',
			Stroke = 'colors/Fill_Color/Accent/Disabled',
			Text = 'colors/Fill_Color/Text_On_Accent/Disabled',
		},
		Rest = {
			Name = 'Primary/Rest',
			Fill = 'colors/Fill_Color/Subtle/Transparent',
			Stroke = 'colors/Fill_Color/Accent/Secondary',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Hover = {
			Name = 'Primary/Hover',
			Fill = 'colors/Fill_Color/Accent/Tertiary',
			Stroke = 'colors/Fill_Color/Accent/Secondary',
			Text = 'colors/Fill_Color/Text_On_Accent/Primary',
		},
		Pressed = {
			Name = 'Primary/Pressed',
			Fill = 'colors/Fill_Color/Accent/Tertiary',
			Stroke = 'colors/Stroke_Color/Control_Stroke/On_Accent_Default',
			Text = 'colors/Fill_Color/Text_On_Accent/Secondary',
		},
	},
	Secondary = {
		Disabled = {
			Name = 'Secondary/Disabled',
			Fill = 'colors/Fill_Color/Control/Disabled',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Disabled',
		},
		Rest = {
			Name = 'Secondary/Rest',
			Fill = 'colors/Fill_Color/Control/Default',
			Stroke = 'colors/Elevation/Control/Border',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Hover = {
			Name = 'Secondary/Hover',
			Fill = 'colors/Fill_Color/Control/Secondary',
			Stroke = 'colors/Elevation/Control/Border',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Pressed = {
			Name = 'Standard/Pressed',
			Fill = 'colors/Fill_Color/Control/Tertiary',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Secondary',
		},
	}
}


return function (Style)
	return (
		Style == 'Default'
		or Style == 'Accent'
		or Style == 'Standard'
		or Style == 'Primary'
		or Style == 'Secondary'
	), 'Expected `Accent`, `Standard`, `Primary`, `Secondary` or `Default`'
end


-- 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


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

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

local Roact = require(Packages.Roact)
local new = Roact.createElement

local Checkbox = require(script.Parent.Checkbox)

local BORDER_PADDING = UDim.new(0, 8)

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

function CheckboxStory:init()
	self:setState({
		Selected = nil
	})
end

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

	return WithTheme(function(Theme)

		local Checkbox = new(Checkbox, {
			Label = state.Selected == nil and 'Indeterminate' or state.Selected == true and 'Selected' or 'Deselected',
			Selected = state.Selected,
			Disabled = self.props.Disabled,
			[Roact.Event.Activated] = function(...)
				print(...)
				self:setState({
					Selected = not state.Selected
				})
			end
		})

		if props.OnBackground then
			return new('Frame', {
				BackgroundTransparency = props.BackgroundTransparency,
				BackgroundColor3 = Theme['colors/Background/Fill_Color/Solid_Background/Base'].Color,
				AutomaticSize = 'XY',
				Size = UDim2.new(1, 0, 0, 45)
			}, {
				Padding = new('UIPadding', {
				PaddingBottom = BORDER_PADDING,
				PaddingLeft = BORDER_PADDING,
				PaddingRight = BORDER_PADDING,
				PaddingTop = BORDER_PADDING,
			}),
				Checkbox = Checkbox
			})
		else
			return Checkbox
		end
	end)
end

local Controls = {
	OnBackground = true,
	Disabled = false
}

return {
	storyElement = CheckboxStory,
	roact = Roact,
	controls = Controls,
	stories = {
		Checkbox = function(props)
			local controls = props.controls

			return Roact.createElement(CheckboxStory, controls)
		end
	}
}


-- CheckboxGroup: A grouped collection of CheckBox elements
-- 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 Sift = require(Packages.Sift)
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local new = Roact.createElement

local CheckboxRoot = script.Parent
local Checkbox = require(CheckboxRoot.Checkbox)

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

CheckboxGroup.defaultProps = {
	HeaderFont = 'fonts/Body', --- @defaultProp
	HeaderTextColor = 'colors/Fill_Color/Text/Primary', --- @defaultProp
	HeaderHeight = 21, --- @defaultProp
	FitContents = false, --- @defaultProp
	Size = UDim2.new(1, 0, 1, 0), --- @defaultProp
}

CheckboxGroup.validateProps = t.strictInterface({

	--- @prop @optional HeaderText string The CheckboxGroup's header text
	HeaderText = t.string,

	--- @prop Checkboxes table {CheckboxProps} The checkboxes to display
	Checkboxes = t.table,

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

	--- @prop @optional FitContents boolean Determines if the CheckboxGroup will resize to fit its contents
	FitContents = t.optional(t.boolean),

	--- @prop @optional @style HeaderTextColor string The color of the header's text
	HeaderTextColor = t.optional(t.string),

	--- @prop @optional @style HeaderFont string The font of the header
	HeaderFont = t.optional(t.string),

	--- @prop @optional HeaderHeight number The height, in pixels of the header
	HeaderHeight = t.optional(t.numberPositive),

	--- @prop @optional Native table Native props
	Native = t.optional(t.table)
})

function CheckboxGroup:init()

	-- CheckboxGroup Checkbox Container height
	self.ContainerHeight, self.SetContainerHeight = Roact.createBinding(0)
end

function CheckboxGroup:GetCheckboxes()
	local props = self.props

	local Checkboxes = {}

	for Index, props in pairs(props.Checkboxes) do
		Checkboxes[Index] = new(Checkbox, props)
	end

	return Checkboxes
end

function CheckboxGroup:render()
	local props = self.props

	return WithTheme(function(Theme)

		-- ////////// CheckboxGroup Header Label

		local HeaderTextColor = Theme[props.HeaderTextColor]
		local HeaderFont = Theme[props.HeaderFont]

		local Label = new('TextLabel', {
			BackgroundTransparency = 1,
			FontFace = HeaderFont.Font,
			Position = UDim2.fromOffset(3, 1),
			Size = UDim2.new(1, -3, 1, -1),
			Text = props.HeaderText,
			TextColor3 = HeaderTextColor.Color,
			TextSize = HeaderFont.Size,
			TextTransparency = HeaderTextColor.Transparency,
			TextXAlignment = 'Left',
		})

		-- ////////// CheckboxGroup Header

		local Header = new('Frame', {
			BackgroundTransparency = 1,
			Size = UDim2.new(1, 0, 0, props.HeaderHeight),
		}, {
			Label = Label,
		})

		-- ////////// CheckboxGroup Container

		local CheckboxContainer = new('Frame', {
			AutomaticSize = props.FitContents and 'Y' or 'None',
			BackgroundTransparency = 1,
			Position = UDim2.fromOffset(0, props.HeaderHeight),

			[Roact.Change.AbsoluteSize] = function(rbx)
				self.SetContainerHeight(rbx.AbsoluteSize.Y)
			end,

			Size = self.ContainerHeight:map(function(Height)

				-- Do not auto-size if FitContents is not set
				if not props.FitContents then
					return props.Size
				end

				return UDim2.new(1, 0, 0, Height)
			end),
		}, {
			Checkboxes = Roact.createFragment(self:GetCheckboxes()),

			InitialPadding = new('Frame', {
				BackgroundTransparency = 1,
				LayoutOrder = -1,
				Size = UDim2.new(1, 0, 0, 8)
			}),
			Layout = new('UIListLayout', {
				FillDirection = Enum.FillDirection.Vertical,
				Padding = UDim.new(0, 8),
				SortOrder = Enum.SortOrder.LayoutOrder
			}),
		})

		-- ////////// CheckboxGroup
		return new('Frame', Sift.Dictionary.join({
			BackgroundTransparency = 1,
			Size = self.ContainerHeight:map(function(Height)

				-- Do not auto-size if FitContents is not set
				if not props.FitContents then
					return props.Size
				end

				return UDim2.new(props.Size.X.Scale, props.Size.X.Offset, 0, Height + props.HeaderHeight)
			end)
		}, props.Native), {
			Header = Header,
			CheckboxContainer = CheckboxContainer
		})
	end)
end

return CheckboxGroup


local Winro = script.Parent.Parent.Parent.Parent
local Packages = Winro.Parent
local Roact = require(Packages.Roact)
local new = Roact.createElement

local CheckboxRoot = script.Parent
local CheckboxGroup = require(CheckboxRoot.CheckboxGroup)
local CheckboxStoryElement = require(CheckboxRoot["Checkbox.story"]).storyElement

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

function CheckboxGroupStory:GetCheckboxes()

	local Checkboxes = {}

	for Index = 1, self.props.Amount do
		Checkboxes[Index] = {
			Label = tostring(Index),
		}
	end

	return Checkboxes
end

function CheckboxGroupStory:render()
	return new(CheckboxGroup, {
		HeaderText = self.props.HeaderText,
		Checkboxes = self:GetCheckboxes()
	})
end

return {
	controls = {
		HeaderText = 'Header text',
		Amount = 10,
		Disabled = false,
	},
	stories = {
		CheckboxGroup = function (props)
			return new(CheckboxGroupStory, props.controls)
		end
	}
}


local ClearColor = {
	Color = Color3.new(),
	Transparency = 1
}
return {
	NoFill = {
		Disabled = {
			Name = 'NoFill/Disabled',
			Check = ClearColor,
			CheckObscureWidth = 1,
			Fill = "colors/Fill_Color/Control_Alt/Disabled",
			Stroke = "colors/Stroke_Color/Control_Strong_Stroke/Disabled",
			Text = "colors/Fill_Color/Text/Disabled",
		},
		Rest = {
			Name = 'NoFill/Rest',
			Check = ClearColor,
			CheckObscureWidth = 1,
			Fill = "colors/Fill_Color/Control_Alt/Secondary",
			Stroke = "colors/Stroke_Color/Control_Strong_Stroke/Default",
			Text = "colors/Fill_Color/Text/Primary"
		},
		Hover = {
			Name = 'NoFill/Hover',
			Check = ClearColor,
			CheckObscureWidth = 1,
			Fill = "colors/Fill_Color/Control_Alt/Tertiary",
			Stroke = "colors/Stroke_Color/Control_Strong_Stroke/Default",
			Text = "colors/Fill_Color/Text/Primary"
		},
		Pressed = {
			Name = 'NoFill/Pressed',
			Check = ClearColor,
			CheckObscureWidth = 1,
			Fill = "colors/Fill_Color/Control_Alt/Quarternary",
			Stroke = "colors/Stroke_Color/Control_Strong_Stroke/Disabled",
			Text = "colors/Fill_Color/Text/Primary"
		}
	},
	Fill = {
		Disabled = {
			Name = 'Fill/Disabled',
			Check = "colors/Fill_Color/Text_On_Accent/Disabled",
			CheckObscureWidth = 0,
			Fill = "colors/Fill_Color/Accent/Disabled",
			Stroke = "colors/Fill_Color/Accent/Disabled",
			Text = "colors/Fill_Color/Text/Disabled"
		},
		Rest = {
			Name = 'Fill/Rest',
			Check = "colors/Fill_Color/Text_On_Accent/Primary",
			CheckObscureWidth = 0,
			Fill = "colors/Fill_Color/Accent/Default",
			Stroke = "colors/Fill_Color/Accent/Default",
			Text = "colors/Fill_Color/Text/Primary"
		},
		Hover = {
			Name = 'Fill/Hover',
			Check = "colors/Fill_Color/Text_On_Accent/Primary",
			CheckObscureWidth = 0,
			Fill = "colors/Fill_Color/Accent/Secondary",
			Stroke = "colors/Fill_Color/Accent/Secondary",
			Text = "colors/Fill_Color/Text/Primary"
		},
		Pressed = {
			Name = 'Fill/Pressed',
			Check = "colors/Fill_Color/Text_On_Accent/Secondary",
			CheckObscureWidth = 0,
			Fill = "colors/Fill_Color/Accent/Tertiary",
			Stroke = "colors/Fill_Color/Accent/Tertiary",
			Text = "colors/Fill_Color/Text/Primary"
		}
	}
}


return function (Value)
	if Value == nil then
		return 'Indeterminate'
	elseif Value == true then
		return 'Selected'
	elseif Value == false then
		return 'None'
	end
end


local Winro = script.Parent.Parent
local Packages = Winro.Parent
local t = require(Packages.t)

return t.table(t.strictInterface({
	Selected = t.string,
	Label = t.optional(t.string),
	OnClicked = t.callback
}))


-- ComboBox: A drop down option selector

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

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

local ContextMenu = require(Winro.App.Menu.ContextMenu)
local FocusFrame = require(Winro.App.Effect.FocusFrame)

local DropdownRoot = script.Parent
local DropdownButton = require(DropdownRoot.DropdownButton)

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

ComboBox.defaultProps = {
	Size = UDim2.fromOffset(120, 32), --- @defaultProp
}

ComboBox.validateProps = t.strictInterface({

	--- @prop @optional AnchorPoint Vector2 Anchorpoint
	AnchorPoint = t.optional(t.Vector2),

	--- @prop @optional LayoutOrder number Layout order
	LayoutOrder = t.optional(t.number),

	--- @prop @optional Size UDim2 Size
	Size = t.optional(t.UDim2),

	--- @prop @optional Position UDim2 Position
	Position = t.optional(t.UDim2),

	--- @prop @optional DropdownButtonProps table The props for the dropdown button
	DropdownButtonProps = t.optional(t.table),

	--- @prop Options table The options to display
	Options = t.table,

	--- @prop @optional SelectedOption string The currently selected option
	SelectedOption = t.optional(t.string),

	--- @prop @optional OnOptionSelected function The callback to call when an option is selected
	OnOptionSelected = t.optional(t.callback),

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

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

	-- ////////// Context Menu

	local Options = {}

	for Index, Option in pairs(props.Options) do
		Options[Index] = {
			Text = Option,
			Selected = (Option == props.SelectedOption),
			OnClicked = function()

				self:setState({
					ContextMenuVisible = not state.ContextMenuVisible,
				})

				props.OnOptionSelected(Option)
			end
		}
	end

	local ContextMenu = state.ContextMenuVisible and new(FocusFrame, {
		RelativeToParent = true,
		Reason = 'COMBOBOX_DROPDOWN_CONTEXT_MENU',
		[Roact.Event.FocusLost] = function()
			self:setState({
				ContextMenuVisible = not state.ContextMenuVisible,
			})
		end,
	}, {

		ContextMenu = new(ContextMenu, {
			BackgroundTransparency = 0,
			Options = Options,
			Size = UDim2.new(1, 0, 0, props.Size),
		}),
	})

	-- ////////// Dropdown Button

	local Button = new(DropdownButton, Sift.Dictionary.join({
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.fromScale(0.5, 0.5),
		Size = UDim2.fromScale(1, 1),
		Text = props.SelectedOption or '*',

		[Roact.Event.Activated] = function ()

			if props.DropdownButtonProps and props.DropdownButtonProps.Disabled then
				return
			end

			self:setState({
				ContextMenuVisible = not state.ContextMenuVisible
			})
		end,
	}, props.DropdownButtonProps))

	-- ////////// ComboBox wrapper
	return new('Frame', Sift.Dictionary.join({
		AnchorPoint = props.AnchorPoint,
		BackgroundTransparency = 1,
		LayoutOrder = props.LayoutOrder,
		Position = props.Position,
		Size = props.Size,
	}, props.Native), {
		Button = Button,
		ContextMenu = ContextMenu,
	})
end

return ComboBox


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

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

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

local DropdownRoot = script.Parent
local ComboBox = require(DropdownRoot.ComboBox)

local BORDER_PADDING = UDim.new(0, 8)

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

function ComboBoxStory:render()
	local props = self.props

	return WithTheme(function(Theme)
		return new('Frame', {
			Size = UDim2.fromOffset(120, 32) + UDim2.fromOffset(BORDER_PADDING.Offset * 2, BORDER_PADDING.Offset * 2),
			BackgroundTransparency = props.BackgroundTransparency,
			BackgroundColor3 = Theme['colors/Background/Fill_Color/Solid_Background/Base'].Color,
			AutomaticSize = 'XY'
		}, {
			Padding = new('UIPadding', {
				PaddingBottom = BORDER_PADDING,
				PaddingLeft = BORDER_PADDING,
				PaddingRight = BORDER_PADDING,
				PaddingTop = BORDER_PADDING,
			}),
			ComboBox = new(ComboBox, {
				DropdownButtonProps = {
					Disabled = props.Disabled,
				},
				SelectedOption = self.state.SelectedOption,
				OnOptionSelected = function(Option, ...)
					print(Option, ...)
					self:setState({
						SelectedOption = Option
					})
				end,
				Options = {
					'Option A',
					'Option B',
					'Option C',
					'Option D',
					'Really really long option text',
				}
			})
		})
	end)
end

return {
	controls = {
		Disabled = false,
	},
	stories = {
		DropdownButton = function (props)
			return new(ComboBoxStory, Sift.Dictionary.join({
				BackgroundTransparency = 0,
			}, props.controls))
		end,
		NoBackground = function(props)
			return new(ComboBoxStory, Sift.Dictionary.join({
				BackgroundTransparency = 1,
			}, props.controls))
		end
	}
}


return {
	Disabled = {
		Name = 'Disabled',
		Fill = 'colors/Fill_Color/Control/Disabled',
		Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
		Text = 'colors/Fill_Color/Text/Disabled',
	},
	Rest = {
		Name = 'Rest',
		Fill = 'colors/Fill_Color/Control/Default',
		Stroke = 'colors/Elevation/Control/Border',
		Text = 'colors/Fill_Color/Text/Primary',
	},
	Hover = {
		Name = 'Hover',
		Fill = 'colors/Fill_Color/Control/Secondary',
		Stroke = 'colors/Elevation/Control/Border',
		Text = 'colors/Fill_Color/Text/Primary',
	},
	Pressed = {
		Name = 'Pressed',
		Fill = 'colors/Fill_Color/Control/Tertiary',
		Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
		Text = 'colors/Fill_Color/Text/Secondary',
	}
}


-- DropdownButton: A button that is meant to be used as the button for dropdown components

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 Sift = require(Packages.Sift)
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local new = Roact.createElement

local UIStroke = require(Winro.App.Effect.UIStroke)

local DROPDOWNBUTTON_CHEVRONDOWN_IMAGE = 'images/Fluent/ChevronDownMed/12px'
local DROPDOWNBUTTON_COLOR_ANIMATION_SPRING_SETTINGS = 'styles/AnimationSpringParams/Control/Input/Button/Color'
local DROPDOWNBUTTON_TRANSPARENCY_ANIMATION_SPRING_SETTINGS = 'styles/AnimationSpringParams/Control/Input/Button/Transparency'
local DROPDOWNBUTTON_LAYOUT_PADDING = 11
local DROPDOWNBUTTON_PADDING_HORIZONTAL = 11
local DROPDOWNBUTTON_PADDING_VERTICAL = 4

local DropdownRoot = script.Parent
local Descriptions = require(DropdownRoot.Descriptions.DropdownButtonDescriptions)

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

DropdownButton.Descriptions = Descriptions

DropdownButton.StyleBindings = {
	'Stroke',
	'Fill',
	'Text',
}

DropdownButton.defaultProps = {
	Size = UDim2.fromOffset(120, 32), --- @defaultProp
}

DropdownButton.validateProps = t.strictInterface({

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

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

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

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

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

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

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

	--- @prop @optional [Roact.Children] table Roact.Children
	[Roact.Children] = 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 DropdownButton:init()

	-- 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 DropdownButton:ApplyDescription(Description, Theme)
	ApplyDescription(self, Description, Theme, Theme[DROPDOWNBUTTON_COLOR_ANIMATION_SPRING_SETTINGS], Theme[DROPDOWNBUTTON_TRANSPARENCY_ANIMATION_SPRING_SETTINGS])
end

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

	return WithTheme(function(Theme)

		-- Apply description
		if props.Disabled then
			self:ApplyDescription(self.Descriptions.Disabled, Theme)
		elseif state.State == 'Rest' then
			self:ApplyDescription(self.Descriptions.Rest, Theme)
		elseif state.State == 'Hover' then
			self:ApplyDescription(self.Descriptions.Hover, Theme)
		elseif state.State == 'Pressed' then
			self:ApplyDescription(self.Descriptions.Pressed, Theme)
		end

		-- ////////// Chevron Icon

		local ChevronImageIcon = Theme[DROPDOWNBUTTON_CHEVRONDOWN_IMAGE]

		local Chevron = new('ImageButton', {
			AnchorPoint = Vector2.new(1, 0.5),
			BackgroundTransparency = 1,
			Image = ChevronImageIcon.Image,
			ImageRectOffset = ChevronImageIcon.ImageRectOffset,
			ImageRectSize = ChevronImageIcon.ImageRectSize,
			Position = UDim2.new(1, -DROPDOWNBUTTON_PADDING_HORIZONTAL, 0.5, 0),
			Size = UDim2.fromOffset(ChevronImageIcon.ImageRectSize.X, ChevronImageIcon.ImageRectSize.Y),

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

		-- ////////// Button Label

		local LabelFontStyle = Theme['fonts/Body']

		local Label = new('TextLabel', {
			AnchorPoint = Vector2.new(0, 0.5),
			BackgroundTransparency = 1,
			FontFace = LabelFontStyle.Font,
			LineHeight = LabelFontStyle.LineHeight,
			Position = UDim2.new(0, DROPDOWNBUTTON_PADDING_HORIZONTAL, 0.5, 0),
			Size = UDim2.new(0, 0),
			Text = props.Text,
			TextSize = LabelFontStyle.Size,
			TextXAlignment = Enum.TextXAlignment.Left,

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

		-- ////////// Button Fill

		local Fill = new('Frame', {
			AnchorPoint = Vector2.new(0.5, 0.5),
			Position = UDim2.fromScale(0.5, 0.5),
			Size = UDim2.new(1, -2, 1, -2), -- Account for stroke thickness

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

			Corners = new('UICorner', {
				CornerRadius = UDim.new(0, Theme['styles/CornerRadius/Default/Inner']),
			}),
			Stroke = new(UIStroke, {
				BorderMode = Enum.BorderMode.Outline,
			}, {

				Gradient = new('UIGradient', {
					Rotation = 90,

					Color = self.Stroke:map(function(Style)
						return Style.ColorSequence or ColorSequence.new(Style.Color)
					end),
					Transparency = self.Stroke:map(function(Style)
						return Style.TransparencySequence or NumberSequence.new(Style.Transparency)
					end),
				}),
			}),
		})

		-- ////////// Dropdown Button
		return new('ImageButton', Sift.Dictionary.join({
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			Size = props.Size,
			Position = props.Position,
			LayoutOrder = props.LayoutOrder,

			[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],
		}, props.Native), {
			Label = Label,
			Chevron = Chevron,
			Fill = Fill,

			ChildWrapper = props[Roact.Children] and new('Frame', {
				BackgroundTransparency = 1,
				Size = UDim2.fromScale(1, 1),
				ZIndex = 2,
			}, props[Roact.Children])
		})
	end)
end

return DropdownButton


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

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

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

local DropdownRoot = script.Parent
local DropdownButton = require(DropdownRoot.DropdownButton)

local BORDER_PADDING = UDim.new(0, 8)

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

function DropdownButtonStory:render()
	local props = self.props

	return WithTheme(function(Theme)
		return new('Frame', {
			Size = UDim2.fromOffset(120, 32) + UDim2.fromOffset(BORDER_PADDING.Offset * 2, BORDER_PADDING.Offset * 2),
			BackgroundTransparency = props.BackgroundTransparency,
			BackgroundColor3 = Theme['colors/Background/Fill_Color/Solid_Background/Base'].Color,
			AutomaticSize = 'XY'
		}, {
			Padding = new('UIPadding', {
				PaddingBottom = BORDER_PADDING,
				PaddingLeft = BORDER_PADDING,
				PaddingRight = BORDER_PADDING,
				PaddingTop = BORDER_PADDING,
			}),
			Button = new(DropdownButton, {
				AnchorPoint = Vector2.new(0, 0),
				Disabled = props.Disabled,
				Position = UDim2.fromScale(0, 0),
				[Roact.Event.Activated] = print
			})
		})
	end)
end

return {
	controls = {
		Disabled = false,
	},
	stories = {
		DropdownButton = function (props)
			return new(DropdownButtonStory, Sift.Dictionary.join({
				BackgroundTransparency = 0,
			}, props.controls))
		end,
		NoBackground = function(props)
			return new(DropdownButtonStory, Sift.Dictionary.join({
				BackgroundTransparency = 1,
			}, props.controls))
		end
	}
}


return {
	Rest = {
		Name = 'Rest',
		Text = 'colors/Fill_Color/Accent_Text/Primary',
	},
	Hover = {
		Name = 'Hover',
		Text = 'colors/Fill_Color/Accent_Text/Secondary',
	},
	Pressed = {
		Name = 'Pressed',
		Text = 'colors/Fill_Color/Accent_Text/Tertiary',
	},
	Disabled = {
		Name = 'Disabled',
		Text = 'colors/Fill_Color/Accent_Text/Disabled',
	},
}


-- Hyperlink: Hyperlink Compoent

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

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

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

local HyperlinkRoot = script.Parent
local Descriptions = require(HyperlinkRoot.Descriptions)

local HYPERLINK_TRANSPARENCY_ANIMATION_OPTIONS = 'styles/AnimationSpringParams/Control/Input/Button/Color'
local HYPERLINK_COLOR_ANIMATION_OPTIONS = 'styles/AnimationSpringParams/Control/Input/Button/Transparency'

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

HyperlinkButton.StyleBindings = {
	'Text',
}

HyperlinkButton.defaultProps = {
	Size = UDim2.fromScale(1, 1), --- @defaultProp
	Text = 'Hyperlink', --- @defaultProp
}

HyperlinkButton.validateProps = t.strictInterface({

	--- @prop @optional AutomaticSize Enum.AutmaticSize Autmatic size
	AutomaticSize = t.optional(t.enum(Enum.AutomaticSize)),

	--- @prop @optional Disabled boolean Determines if the HyperlinkButton shows as disabled
	Disabled = t.optional(t.boolean),

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

	--- @prop @optional TextXAlignment Enum.TextXAlignment Horizontal text alignment
	TextXAlignment = t.optional(t.enum(Enum.TextXAlignment)),

	--- @prop @optional TextYAlignment Enum.TextYAlignment Vertical text alignment
	TextYAlignment = t.optional(t.enum(Enum.TextYAlignment)),

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

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

	--- @prop @optional Position UDim2 The Hyperlink's positon
	Position = t.optional(t.UDim2),

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

	--- @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 HyperlinkButton:init()

	-- 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 HyperlinkButton: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 HyperlinkButton:ApplyDescription(Description, Theme)
	ApplyDescription(self, Description, Theme, Theme[HYPERLINK_COLOR_ANIMATION_OPTIONS], Theme[HYPERLINK_TRANSPARENCY_ANIMATION_OPTIONS])
end

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

	return WithTheme(function(Theme)

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

		-- ////////// Hyperlink Button

		local HyperlinkButtonFontStyle = Theme['fonts/Body']

		return new('TextButton', Sift.Dictionary.join({
			AutomaticSize = props.AutomaticSize,
			AnchorPoint = props.AnchorPoint,
			BackgroundTransparency = 1,
			FontFace = HyperlinkButtonFontStyle.Font,
			LayoutOrder = props.LayoutOrder,
			LineHeight = HyperlinkButtonFontStyle.LineHeight,
			Position = props.Position,
			Size = props.Size,
			Text = props.Text,
			TextSize = HyperlinkButtonFontStyle.Size,
			TextXAlignment = props.TextXAlignment,
			TextYalignment = props.TextYAlignment,

			[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],

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

return HyperlinkButton


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

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

local HyperlinkButton = require(script.Parent.HyperlinkButton)
local HyperlinkButtonStory = Roact.PureComponent:extend(script.Name)

function HyperlinkButtonStory:render()
	local props = self.props

	return new(HyperlinkButton, Sift.Dictionary.join({
		Disabled = props.Disabled,
		Text = 'Hyperlink Story'
	}, props.Props))
end

return {
	controls = {
		Disabled = false,
	},
	stories = {
		HyperlinkButton = function(props)
			return new(HyperlinkButtonStory, props.controls)
		end
	}
}


local TRANSPARENT = {
	Color = Color3.new(),
	Transparency = 1,
}

return {
	Rest = {
		Focus = {
			Button = 'colors/Fill_Color/Text/Secondary',
			Highlight = 'colors/Fill_Color/Accent/Default',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Primary',
		},
		Rest = {
			Button = TRANSPARENT,
			Highlight = 'colors/Elevation/Text_Control/Border',
			Stroke = 'colors/Stroke_Color/Control_Stroke/Default',
			Text = 'colors/Fill_Color/Text/Primary',
		},
	},
}


-- TextInput: A string input component, a textbox

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 Sift = require(Packages.Sift)
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local new = Roact.createElement

local TextRoot = script.Parent
local Descriptions = require(TextRoot.Descriptions.TextInputDescriptions)

local TEXTINPUT_COLOR_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Button/Color"
local TEXTINPUT_TRANSPARENCY_ANIMATION_SPRING_SETTINGS = "styles/AnimationSpringParams/Control/Input/Button/Transparency"

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

TextInput.StyleBindings = {
	'Button',
	'Highlight',
	'Stroke',
	'Text',
}

TextInput.defaultProps = {

	--- @prop @optional Text string Current text
	Text = t.optional(t.string),

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

	--- @prop @optional [Roact.Children] table Child contents
	[Roact.Children] = 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),
}

TextInput.validateProps = t.strictInterface({

})

function TextInput:init()
	local props = self.props

	-- Text value binding
	self.Text, self.SetText = Roact.createBinding(props.Text)

	-- 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 TextInput: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 TextInput:ApplyDescription(Description, Theme)
	return ApplyDescription(self, Description, Theme,  Theme[TEXTINPUT_COLOR_ANIMATION_SPRING_SETTINGS], Theme[TEXTINPUT_TRANSPARENCY_ANIMATION_SPRING_SETTINGS])
end

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

	return WithTheme(function(Theme)

		-- Apply description
		if state.State == 'Focused' then

		end
	end)
end

return TextInput


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

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

local TextInput = require(script.Parent.TextInput)
local TextInputStory = Roact.PureComponent:extend(script.Name)

function TextInputStory:render()
	local props = self.props

	return new(TextInput, Sift.Dictionary.join({

	}, props.Props))
end

return {
	controls = {},
	stories = {
		TextInput = function(props)
			return new(TextInputStory, props.controls)
		end
	}
}