1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 | -- [Fusion imports omitted for clarity]
-- This is a relatively complete example of a button component.
-- It handles many common interactions such as hovering and clicking.
-- This should be a generally useful template for assembling components of your
-- own. Unless you're prototyping, it's probably wise to stick to some good
-- guidelines; the Tutorials have some tips if you don't have any existing
-- guidelines of your own.
-- Defining the names of properties the button accepts, and their types. This is
-- useful for autocomplete and helps catch some typos, but is optional.
export type Props = {
-- some generic properties we'll allow other code to control directly
Name: CanBeState<string>?,
LayoutOrder: CanBeState<number>?,
Position: CanBeState<UDim2>?,
AnchorPoint: CanBeState<Vector2>?,
Size: CanBeState<UDim2>?,
AutomaticSize: CanBeState<Enum.AutomaticSize>?,
ZIndex: CanBeState<number>?,
-- button-specific properties
Text: CanBeState<string>?,
OnClick: (() -> ())?,
Disabled: CanBeState<boolean>?
}
-- Returns `Child` to match Fusion's `Component` type. This should work for most
-- use cases, and offers the greatest encapsulation as you're able to swap out
-- your return type for an array or state object if you want to.
local function Button(props: Props): Child
-- To simplify our code later (because we're going to operate on this value)
if props.Disabled == nil then
props.Disabled = Value(false)
elseif typeof(props.Disabled) == "boolean" then
props.Disabled = Value(props.Disabled)
end
-- We should generally be careful about storing state in widely reused
-- components, as the Tutorials explain, but for contained use cases such as
-- hover states, it should be perfectly fine.
local isHovering = Value(false)
local isHeldDown = Value(false)
return New "TextButton" {
Name = props.Name,
LayoutOrder = props.LayoutOrder,
Position = props.Position,
AnchorPoint = props.AnchorPoint,
Size = props.Size,
AutomaticSize = props.AutomaticSize,
ZIndex = props.ZIndex,
Text = props.Text,
TextColor3 = Color3.fromHex("FFFFFF"),
BackgroundColor3 = Spring(Computed(function()
if props.Disabled:get() then
return Color3.fromHex("CCCCCC")
else
local baseColour = Color3.fromHex("0085FF")
-- darken/lighten when hovered or held down
if isHeldDown:get() then
baseColour = baseColour:Lerp(Color3.new(0, 0, 0), 0.25)
elseif isHovering:get() then
baseColour = baseColour:Lerp(Color3.new(1, 1, 1), 0.25)
end
return baseColour
end
end), 20),
[OnEvent "Activated"] = function()
-- Because we're not in a Computed callback (or similar), it's a
-- good idea to :get(false) so we're not adding any dependencies
-- anywhere.
if props.OnClick ~= nil and not props.Disabled:get(false) then
-- We're explicitly calling this function with no arguments to
-- match the types we specified above. If we just passed it
-- straight into the event, the function would receive arguments
-- from the Activated event, which might not be desirable.
props.OnClick()
end
end,
[OnEvent "MouseButton1Down"] = function()
isHeldDown:set(true) -- it's good UX to give immediate feedback
end,
[OnEvent "MouseButton1Up"] = function()
isHeldDown:set(false)
end,
[OnEvent "MouseEnter"] = function()
-- Roblox calls this event even if the button is being covered by
-- other UI. For simplicity, we won't worry about that.
isHovering:set(true)
end,
[OnEvent "MouseLeave"] = function()
isHovering:set(false)
-- If the button is being held down, but the cursor moves off the
-- button, then we won't receive the mouse up event. To make sure
-- the button doesn't get stuck held down, we'll release it if the
-- cursor leaves the button.
isHeldDown:set(false)
end,
[Children] = {
New "UICorner" {
CornerRadius = UDim.new(0, 4)
},
New "UIPadding" {
PaddingTop = UDim.new(0, 6),
PaddingBottom = UDim.new(0, 6),
PaddingLeft = UDim.new(0, 6),
PaddingRight = UDim.new(0, 6)
}
}
}
end
return Button
|