* Add Flipper

* Remove old UI

* Add boilerplate UI

* Change plugin version

* Merge upstream

* Bunch of new UI changes

Too lazy to list them all in individual commits

* Touch ripple for buttons and a few other things

* Make the close button on the PluginGui work

* Set button state to guiEnabled

* Implement Connecting, NotConnected; add Header; don't update plugin button on render

* Replace mapLerpColor with mapLerp

* Update blendAlpha to be 0 without any values

* Add ActionFillTransparency to Theme.Button

* Suffix all Theme entries

* Update Flipper

* Add disconnect button

* Remove cancel button

* Add settings page

* Add scrollbar and dark theme support to settings

* Include settings in startSession

* Set context default value to nil

I always thought this was the name, lol...

* Add Error page

* Fix preloadAssets

* Fix preloadAssets import

* Update checkbox colors a little

* Add setting descriptions

* Fix scrolling frame in settings panel

* Remove .vscode

* Rename Throbber to Spinner

* Update merge

* Move Spinner images to assets

* Change casing of directories

* Remove old directories

* Add comments to getDerivedStateFromProps

* Account for offset in host TextBox size

* Turn width variables into constants

* Attempt to fix the comments

* Add a missing comma in Settings

* Remove a double space

* Remove Dummy object

* Move most of the Studio logic out of render

* Don't truncate port input

* Replace merge with Dictionary.merge

* Replace "Got it!" with "Okay"

* Add projectName to setStatus call

* Add Flipper to build.rs
This commit is contained in:
Reselim
2020-12-14 14:07:39 -05:00
committed by GitHub
parent a937fc38db
commit cae4c46669
49 changed files with 2249 additions and 1320 deletions

View File

@@ -0,0 +1,41 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local SlicedImage = require(script.Parent.SlicedImage)
local e = Roact.createElement
local function BorderedContainer(props)
return Theme.with(function(theme)
return e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.BorderedContainer.BackgroundColor,
transparency = props.transparency,
size = props.size,
position = props.position,
anchorPoint = props.anchorPoint,
layoutOrder = props.layoutOrder,
}, {
Content = e("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, props[Roact.Children]),
Border = e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = theme.BorderedContainer.BorderColor,
transparency = props.transparency,
size = UDim2.new(1, 0, 1, 0),
}),
})
end)
end
return BorderedContainer

View File

@@ -0,0 +1,96 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Flipper = require(Rojo.Flipper)
local Assets = require(Plugin.Assets)
local Theme = require(Plugin.App.Theme)
local bindingUtil = require(Plugin.App.bindingUtil)
local SlicedImage = require(script.Parent.SlicedImage)
local e = Roact.createElement
local Checkbox = Roact.Component:extend("Checkbox")
function Checkbox:init()
self.motor = Flipper.SingleMotor.new(self.props.active and 1 or 0)
self.binding = bindingUtil.fromMotor(self.motor)
end
function Checkbox:didUpdate(lastProps)
if lastProps.active ~= self.props.active then
self.motor:setGoal(
Flipper.Spring.new(self.props.active and 1 or 0, {
frequency = 6,
dampingRatio = 1.1,
})
)
end
end
function Checkbox:render()
return Theme.with(function(theme)
theme = theme.Checkbox
local activeTransparency = Roact.joinBindings({
self.binding:map(function(value)
return 1 - value
end),
self.props.transparency,
}):map(bindingUtil.blendAlpha)
return e("ImageButton", {
Size = UDim2.new(0, 28, 0, 28),
Position = self.props.position,
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
ZIndex = self.props.zIndex,
BackgroundTransparency = 1,
[Roact.Event.Activated] = self.props.onClick,
}, {
Active = e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.Active.BackgroundColor,
transparency = activeTransparency,
size = UDim2.new(1, 0, 1, 0),
zIndex = 2,
}, {
Icon = e("ImageLabel", {
Image = Assets.Images.Checkbox.Active,
ImageColor3 = theme.Active.IconColor,
ImageTransparency = activeTransparency,
Size = UDim2.new(0, 16, 0, 16),
Position = UDim2.new(0.5, 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
}),
}),
Inactive = e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = theme.Inactive.BorderColor,
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
}, {
Icon = e("ImageLabel", {
Image = Assets.Images.Checkbox.Inactive,
ImageColor3 = theme.Inactive.IconColor,
ImageTransparency = self.props.transparency,
Size = UDim2.new(0, 16, 0, 16),
Position = UDim2.new(0.5, 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
}),
}),
})
end)
end
return Checkbox

View File

@@ -0,0 +1,55 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local Config = require(Plugin.Config)
local Version = require(Plugin.Version)
local e = Roact.createElement
local function Header(props)
return Theme.with(function(theme)
return e("Frame", {
Size = UDim2.new(1, 0, 0, 32),
LayoutOrder = props.layoutOrder,
BackgroundTransparency = 1,
}, {
Logo = e("ImageLabel", {
Image = Assets.Images.Logo,
ImageColor3 = theme.Header.LogoColor,
ImageTransparency = props.transparency,
Size = UDim2.new(0, 60, 0, 27),
LayoutOrder = 1,
BackgroundTransparency = 1,
}),
Version = e("TextLabel", {
Text = Version.display(Config.version),
Font = Enum.Font.Gotham,
TextSize = 14,
TextColor3 = theme.Header.VersionColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = props.transparency,
Size = UDim2.new(1, 0, 0, 14),
LayoutOrder = 2,
BackgroundTransparency = 1,
}),
Layout = e("UIListLayout", {
VerticalAlignment = Enum.VerticalAlignment.Center,
FillDirection = Enum.FillDirection.Horizontal,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 15),
}),
})
end)
end
return Header

View File

@@ -0,0 +1,79 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Flipper = require(Rojo.Flipper)
local Assets = require(Plugin.Assets)
local bindingUtil = require(Plugin.App.bindingUtil)
local HOVER_SPRING_PROPS = {
frequency = 5,
dampingRatio = 1.1,
}
local e = Roact.createElement
local IconButton = Roact.Component:extend("IconButton")
function IconButton:init()
self.motor = Flipper.SingleMotor.new(0)
self.binding = bindingUtil.fromMotor(self.motor)
end
function IconButton:render()
local iconSize = self.props.iconSize
return e("ImageButton", {
Size = UDim2.new(0, iconSize * 1.5, 0, iconSize * 1.5),
Position = self.props.position,
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
ZIndex = self.props.zIndex,
BackgroundTransparency = 1,
[Roact.Event.Activated] = self.props.onClick,
[Roact.Event.MouseEnter] = function()
self.motor:setGoal(
Flipper.Spring.new(1, HOVER_SPRING_PROPS)
)
end,
[Roact.Event.MouseLeave] = function()
self.motor:setGoal(
Flipper.Spring.new(0, HOVER_SPRING_PROPS)
)
end,
}, {
Icon = e("ImageLabel", {
Image = self.props.icon,
ImageColor3 = self.props.color,
ImageTransparency = self.props.transparency,
Size = UDim2.new(0, iconSize, 0, iconSize),
Position = UDim2.new(0.5, 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
}),
HoverCircle = e("ImageLabel", {
Image = Assets.Images.Circles[128],
ImageColor3 = self.props.color,
ImageTransparency = Roact.joinBindings({
hover = self.binding,
transparency = self.props.transparency,
}):map(function(values)
return bindingUtil.blendAlpha({ 0.85, 1 - values.hover, values.transparency })
end),
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}),
})
end
return IconButton

View File

@@ -0,0 +1,42 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Assets = require(Plugin.Assets)
local Theme = require(Plugin.App.Theme)
local bindingUtil = require(Plugin.App.bindingUtil)
local e = Roact.createElement
local function ScrollingFrame(props)
return Theme.with(function(theme)
return e("ScrollingFrame", {
ScrollBarThickness = 9,
ScrollBarImageColor3 = theme.ScrollBarColor,
ScrollBarImageTransparency = props.transparency:map(function(value)
return bindingUtil.blendAlpha({ 0.65, value })
end),
TopImage = Assets.Images.ScrollBar.Top,
MidImage = Assets.Images.ScrollBar.Middle,
BottomImage = Assets.Images.ScrollBar.Bottom,
ElasticBehavior = Enum.ElasticBehavior.Always,
ScrollingDirection = Enum.ScrollingDirection.Y,
Size = props.size,
Position = props.position,
AnchorPoint = props.anchorPoint,
CanvasSize = props.contentSize:map(function(value)
return UDim2.new(0, 0, 0, value.Y)
end),
BorderSizePixel = 0,
BackgroundTransparency = 1,
[Roact.Change.AbsoluteSize] = props[Roact.Change.AbsoluteSize]
}, props[Roact.Children])
end)
end
return ScrollingFrame

View File

@@ -0,0 +1,29 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Roact = require(Rojo.Roact)
local e = Roact.createElement
local function SlicedImage(props)
local slice = props.slice
return e("ImageLabel", {
Image = slice.Image,
ImageColor3 = props.color,
ImageTransparency = props.transparency,
ScaleType = Enum.ScaleType.Slice,
SliceCenter = slice.Center,
SliceScale = slice.Scale,
Size = props.size,
Position = props.position,
AnchorPoint = props.anchorPoint,
ZIndex = props.zIndex,
LayoutOrder = props.layoutOrder,
BackgroundTransparency = 1,
}, props[Roact.Children])
end
return SlicedImage

View File

@@ -0,0 +1,66 @@
local RunService = game:GetService("RunService")
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local ROTATIONS_PER_SECOND = 1.75
local e = Roact.createElement
local Spinner = Roact.PureComponent:extend("Spinner")
function Spinner:init()
self.rotation, self.setRotation = Roact.createBinding(0)
end
function Spinner:render()
return Theme.with(function(theme)
return e("ImageLabel", {
Image = Assets.Images.Spinner.Background,
ImageColor3 = theme.Spinner.BackgroundColor,
ImageTransparency = self.props.transparency,
Size = UDim2.new(0, 24, 0, 24),
Position = self.props.position,
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
BackgroundTransparency = 1,
}, {
Foreground = e("ImageLabel", {
Image = Assets.Images.Spinner.Foreground,
ImageColor3 = theme.Spinner.ForegroundColor,
ImageTransparency = self.props.transparency,
Size = UDim2.new(1, 0, 1, 0),
Rotation = self.rotation:map(function(value)
return value * 360
end),
BackgroundTransparency = 1,
}),
})
end)
end
function Spinner:didMount()
self.stepper = RunService.RenderStepped:Connect(function(deltaTime)
local rotation = self.rotation:getValue()
rotation = rotation + deltaTime * ROTATIONS_PER_SECOND
rotation = rotation % 1
self.setRotation(rotation)
end)
end
function Spinner:willUnmount()
self.stepper:Disconnect()
end
return Spinner

View File

@@ -0,0 +1,7 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Roact = require(Rojo.Roact)
local StudioPluginContext = Roact.createContext(nil)
return StudioPluginContext

View File

@@ -0,0 +1,84 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Dictionary = require(Plugin.Dictionary)
local StudioPluginContext = require(script.Parent.StudioPluginContext)
local e = Roact.createElement
local StudioPluginGui = Roact.PureComponent:extend("StudioPluginGui")
StudioPluginGui.defaultProps = {
initDockState = Enum.InitialDockState.Right,
active = false,
overridePreviousState = false,
floatingSize = Vector2.new(0, 0),
minimumSize = Vector2.new(0, 0),
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
}
function StudioPluginGui:init()
local floatingSize = self.props.floatingSize
local minimumSize = self.props.minimumSize
local dockWidgetPluginGuiInfo = DockWidgetPluginGuiInfo.new(
self.props.initDockState,
self.props.active,
self.props.overridePreviousState,
floatingSize.X, floatingSize.Y,
minimumSize.X, minimumSize.Y
)
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(self.props.id, dockWidgetPluginGuiInfo)
pluginGui.Name = self.props.id
pluginGui.Title = self.props.title
pluginGui.ZIndexBehavior = self.props.zIndexBehavior
if self.props.onInitialState then
self.props.onInitialState(pluginGui.Enabled)
end
pluginGui:BindToClose(function()
if self.props.onClose then
self.props.onClose()
else
pluginGui.Enabled = false
end
end)
self.pluginGui = pluginGui
end
function StudioPluginGui:render()
return e(Roact.Portal, {
target = self.pluginGui,
}, self.props[Roact.Children])
end
function StudioPluginGui:didUpdate(lastProps)
if self.props.active ~= lastProps.active then
-- This is intentionally in didUpdate to make sure the initial active state
-- (if the PluginGui is open initially) is preserved.
self.pluginGui.Enabled = self.props.active
end
end
function StudioPluginGui:willUnmount()
self.pluginGui:Destroy()
end
local function StudioPluginGuiWrapper(props)
return e(StudioPluginContext.Consumer, {
render = function(plugin)
return e(StudioPluginGui, Dictionary.merge(props, {
plugin = plugin,
}))
end,
})
end
return StudioPluginGuiWrapper

View File

@@ -0,0 +1,66 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Dictionary = require(Plugin.Dictionary)
local StudioToolbarContext = require(script.Parent.StudioToolbarContext)
local e = Roact.createElement
local StudioToggleButton = Roact.Component:extend("StudioToggleButton")
StudioToggleButton.defaultProps = {
enabled = true,
active = false,
}
function StudioToggleButton:init()
local button = self.props.toolbar:CreateButton(
self.props.name,
self.props.tooltip,
self.props.icon,
self.props.text
)
button.Click:Connect(function()
if self.props.onClick then
self.props.onClick()
end
end)
button.ClickableWhenViewportHidden = true
self.button = button
end
function StudioToggleButton:render()
return nil
end
function StudioToggleButton:didUpdate(lastProps)
if self.props.enabled ~= lastProps.enabled then
self.button.Enabled = self.props.enabled
end
if self.props.active ~= lastProps.active then
self.button:SetActive(self.props.active)
end
end
function StudioToggleButton:willUnmount()
self.button:Destroy()
end
local function StudioToggleButtonWrapper(props)
return e(StudioToolbarContext.Consumer, {
render = function(toolbar)
return e(StudioToggleButton, Dictionary.merge(props, {
toolbar = toolbar,
}))
end,
})
end
return StudioToggleButtonWrapper

View File

@@ -0,0 +1,45 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Dictionary = require(Plugin.Dictionary)
local StudioToolbarContext = require(script.Parent.StudioToolbarContext)
local StudioPluginContext = require(script.Parent.StudioPluginContext)
local e = Roact.createElement
local StudioToolbar = Roact.Component:extend("StudioToolbar")
function StudioToolbar:init()
self.toolbar = self.props.plugin:CreateToolbar(self.props.name)
end
function StudioToolbar:render()
return e(StudioToolbarContext.Provider, {
value = self.toolbar,
}, self.props[Roact.Children])
end
function StudioToolbar:didUpdate(lastProps)
if self.props.name ~= lastProps.name then
self.toolbar.Name = self.props.name
end
end
function StudioToolbar:willUnmount()
self.toolbar:Destroy()
end
local function StudioToolbarWrapper(props)
return e(StudioPluginContext.Consumer, {
render = function(plugin)
return e(StudioToolbar, Dictionary.merge(props, {
plugin = plugin,
}))
end,
})
end
return StudioToolbarWrapper

View File

@@ -0,0 +1,7 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Roact = require(Rojo.Roact)
local StudioToolbarContext = Roact.createContext(nil)
return StudioToolbarContext

View File

@@ -0,0 +1,137 @@
local TextService = game:GetService("TextService")
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Flipper = require(Rojo.Flipper)
local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local bindingUtil = require(Plugin.App.bindingUtil)
local SlicedImage = require(script.Parent.SlicedImage)
local TouchRipple = require(script.Parent.TouchRipple)
local SPRING_PROPS = {
frequency = 5,
dampingRatio = 1,
}
local e = Roact.createElement
local TextButton = Roact.Component:extend("TextButton")
function TextButton:init()
self.motor = Flipper.GroupMotor.new({
hover = 0,
enabled = self.props.enabled and 1 or 0,
})
self.binding = bindingUtil.fromMotor(self.motor)
end
function TextButton:didUpdate(lastProps)
if lastProps.enabled ~= self.props.enabled then
self.motor:setGoal({
enabled = Flipper.Spring.new(self.props.enabled and 1 or 0),
})
end
end
function TextButton:render()
return Theme.with(function(theme)
local textSize = TextService:GetTextSize(
self.props.text, 18, Enum.Font.GothamSemibold,
Vector2.new(math.huge, math.huge)
)
local style = self.props.style
theme = theme.Button[style]
local bindingHover = bindingUtil.deriveProperty(self.binding, "hover")
local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled")
return e("ImageButton", {
Size = UDim2.new(0, 15 + textSize.X + 15, 0, 34),
Position = self.props.position,
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
BackgroundTransparency = 1,
[Roact.Event.Activated] = self.props.onClick,
[Roact.Event.MouseEnter] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(1, SPRING_PROPS),
})
end,
[Roact.Event.MouseLeave] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(0, SPRING_PROPS),
})
end,
}, {
TouchRipple = e(TouchRipple, {
color = theme.ActionFillColor,
transparency = self.props.transparency:map(function(value)
return bindingUtil.blendAlpha({ theme.ActionFillTransparency, value })
end),
zIndex = 2,
}),
Text = e("TextLabel", {
Text = self.props.text,
Font = Enum.Font.GothamSemibold,
TextSize = 18,
TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.TextColor, theme.Disabled.TextColor),
TextTransparency = self.props.transparency,
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}),
Border = style == "Bordered" and e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
zIndex = 0,
}),
HoverOverlay = e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.ActionFillColor,
transparency = Roact.joinBindings({
hover = bindingHover:map(function(value)
return 1 - value
end),
transparency = self.props.transparency,
}):map(function(values)
return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency })
end),
size = UDim2.new(1, 0, 1, 0),
zIndex = -1,
}),
Background = style == "Solid" and e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BackgroundColor, theme.Disabled.BackgroundColor),
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
zIndex = -2,
}),
})
end)
end
return TextButton

View File

@@ -0,0 +1,145 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Roact = require(Rojo.Roact)
local Flipper = require(Rojo.Flipper)
local Assets = require(Plugin.Assets)
local bindingUtil = require(Plugin.App.bindingUtil)
local EXPAND_SPRING = {
frequency = 7,
dampingRatio = 2,
}
local TouchRipple = Roact.Component:extend("TouchRipple")
function TouchRipple:init()
self.ref = Roact.createRef()
self.motor = Flipper.GroupMotor.new({
scale = 0,
opacity = 0,
})
self.binding = bindingUtil.fromMotor(self.motor)
self.position, self.setPosition = Roact.createBinding(
Vector2.new(0, 0)
)
end
function TouchRipple:reset()
self.motor:setGoal({
scale = Flipper.Instant.new(0),
opacity = Flipper.Instant.new(0),
})
-- Forces motor to update
self.motor:step(0)
end
function TouchRipple:calculateRadius(position)
local container = self.ref.current
if container then
local corner = Vector2.new(
math.floor((1 - position.X) + 0.5),
math.floor((1 - position.Y) + 0.5)
)
local size = container.AbsoluteSize
local ratio = size / math.min(size.X, size.Y)
return ((corner * ratio) - (position * ratio)).Magnitude
else
return 0
end
end
function TouchRipple:render()
local scale = bindingUtil.deriveProperty(self.binding, "scale")
local transparency = bindingUtil.deriveProperty(self.binding, "opacity"):map(function(value)
return 1 - value
end)
transparency = Roact.joinBindings({
transparency,
self.props.transparency,
}):map(bindingUtil.blendAlpha)
return Roact.createElement("Frame", {
ClipsDescendants = true,
Size = UDim2.new(1, 0, 1, 0),
ZIndex = self.props.zIndex,
BackgroundTransparency = 1,
[Roact.Ref] = self.ref,
[Roact.Event.InputBegan] = function(object, input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
self:reset()
local position = Vector2.new(input.Position.X, input.Position.Y)
local relativePosition = (position - object.AbsolutePosition) / object.AbsoluteSize
self.setPosition(relativePosition)
self.motor:setGoal({
scale = Flipper.Spring.new(1, EXPAND_SPRING),
opacity = Flipper.Spring.new(1, EXPAND_SPRING),
})
input:GetPropertyChangedSignal("UserInputState"):Connect(function()
local userInputState = input.UserInputState
if
userInputState == Enum.UserInputState.Cancel
or userInputState == Enum.UserInputState.End
then
self.motor:setGoal({
opacity = Flipper.Spring.new(0, {
frequency = 5,
dampingRatio = 1,
}),
})
end
end)
end
end,
}, {
Circle = Roact.createElement("ImageLabel", {
Image = Assets.Images.Circles[500],
ImageColor3 = self.props.color,
ImageTransparency = transparency,
Size = Roact.joinBindings({
scale = scale,
position = self.position,
}):map(function(values)
local targetSize = self:calculateRadius(values.position) * 2
local currentSize = targetSize * values.scale
local container = self.ref.current
if container then
local containerSize = container.AbsoluteSize
local containerAspect = containerSize.X / containerSize.Y
return UDim2.new(
currentSize / math.max(containerAspect, 1), 0,
currentSize * math.min(containerAspect, 1), 0
)
end
end),
Position = self.position:map(function(value)
return UDim2.new(value.X, 0, value.Y, 0)
end),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
}),
})
end
return TouchRipple