azul-sync 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. package/README.md +142 -0
  5. package/dist/build.d.ts +19 -0
  6. package/dist/build.d.ts.map +1 -0
  7. package/dist/build.js +92 -0
  8. package/dist/build.js.map +1 -0
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +397 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/config.d.ts +26 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +105 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/fs/fileWriter.d.ts +100 -0
  18. package/dist/fs/fileWriter.d.ts.map +1 -0
  19. package/dist/fs/fileWriter.js +342 -0
  20. package/dist/fs/fileWriter.js.map +1 -0
  21. package/dist/fs/treeManager.d.ts +84 -0
  22. package/dist/fs/treeManager.d.ts.map +1 -0
  23. package/dist/fs/treeManager.js +365 -0
  24. package/dist/fs/treeManager.js.map +1 -0
  25. package/dist/fs/watcher.d.ts +39 -0
  26. package/dist/fs/watcher.d.ts.map +1 -0
  27. package/dist/fs/watcher.js +120 -0
  28. package/dist/fs/watcher.js.map +1 -0
  29. package/dist/index.d.ts +61 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +349 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/ipc/httpPolling.d.ts +56 -0
  34. package/dist/ipc/httpPolling.d.ts.map +1 -0
  35. package/dist/ipc/httpPolling.js +171 -0
  36. package/dist/ipc/httpPolling.js.map +1 -0
  37. package/dist/ipc/messages.d.ts +112 -0
  38. package/dist/ipc/messages.d.ts.map +1 -0
  39. package/dist/ipc/messages.js +5 -0
  40. package/dist/ipc/messages.js.map +1 -0
  41. package/dist/ipc/server.d.ts +50 -0
  42. package/dist/ipc/server.d.ts.map +1 -0
  43. package/dist/ipc/server.js +168 -0
  44. package/dist/ipc/server.js.map +1 -0
  45. package/dist/pack.d.ts +19 -0
  46. package/dist/pack.d.ts.map +1 -0
  47. package/dist/pack.js +225 -0
  48. package/dist/pack.js.map +1 -0
  49. package/dist/push.d.ts +43 -0
  50. package/dist/push.d.ts.map +1 -0
  51. package/dist/push.js +532 -0
  52. package/dist/push.js.map +1 -0
  53. package/dist/rojo.d.ts +9 -0
  54. package/dist/rojo.d.ts.map +1 -0
  55. package/dist/rojo.js +114 -0
  56. package/dist/rojo.js.map +1 -0
  57. package/dist/snapshot/rojo.d.ts +39 -0
  58. package/dist/snapshot/rojo.d.ts.map +1 -0
  59. package/dist/snapshot/rojo.js +364 -0
  60. package/dist/snapshot/rojo.js.map +1 -0
  61. package/dist/snapshot.d.ts +23 -0
  62. package/dist/snapshot.d.ts.map +1 -0
  63. package/dist/snapshot.js +132 -0
  64. package/dist/snapshot.js.map +1 -0
  65. package/dist/sourcemap/generator.d.ts +78 -0
  66. package/dist/sourcemap/generator.d.ts.map +1 -0
  67. package/dist/sourcemap/generator.js +351 -0
  68. package/dist/sourcemap/generator.js.map +1 -0
  69. package/dist/sourcemap/propertyLoader.d.ts +19 -0
  70. package/dist/sourcemap/propertyLoader.d.ts.map +1 -0
  71. package/dist/sourcemap/propertyLoader.js +131 -0
  72. package/dist/sourcemap/propertyLoader.js.map +1 -0
  73. package/dist/util/id.d.ts +9 -0
  74. package/dist/util/id.d.ts.map +1 -0
  75. package/dist/util/id.js +14 -0
  76. package/dist/util/id.js.map +1 -0
  77. package/dist/util/log.d.ts +13 -0
  78. package/dist/util/log.d.ts.map +1 -0
  79. package/dist/util/log.js +51 -0
  80. package/dist/util/log.js.map +1 -0
  81. package/docs/assets/azul-logo.pdn +0 -0
  82. package/docs/assets/logo-200px.png +0 -0
  83. package/docs/assets/logo.png +0 -0
  84. package/docs/assets/plugin/toolbox.png +0 -0
  85. package/docs/assets/synced.png +0 -0
  86. package/package.json +41 -0
  87. package/plugin/README.md +54 -0
  88. package/plugin/sourcemap.json +264 -0
  89. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Actor/AzulSync.server.luau +905 -0
  90. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/AzulService.luau +1010 -0
  91. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Config.luau +29 -0
  92. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Enums.luau +11 -0
  93. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CollapsibleTitledSection.luau +214 -0
  94. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ColorPicker.luau +360 -0
  95. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CustomTextButton.luau +170 -0
  96. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/DropdownMenu.luau +363 -0
  97. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/HorizontalLine.luau +43 -0
  98. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ImageButtonWithText.luau +181 -0
  99. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledCheckbox.luau +295 -0
  100. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledColorInputPicker.luau +294 -0
  101. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledMultiChoice.luau +163 -0
  102. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledNumberInput.luau +312 -0
  103. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledRadioButton.luau +55 -0
  104. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledSlider.luau +151 -0
  105. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledTextInput.luau +222 -0
  106. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledToggleButton.luau +73 -0
  107. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/StatefulImageButton.luau +125 -0
  108. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalScrollingFrame.luau +100 -0
  109. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalSpacer.luau +35 -0
  110. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticallyScalingListFrame.luau +107 -0
  111. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/GuiUtilities.luau +429 -0
  112. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/RbxGui.luau +4363 -0
  113. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/UI.luau +425 -0
  114. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/WebSocketClient.luau +161 -0
  115. package/src/build.ts +120 -0
  116. package/src/cli.ts +496 -0
  117. package/src/config.ts +170 -0
  118. package/src/fs/fileWriter.ts +414 -0
  119. package/src/fs/treeManager.ts +458 -0
  120. package/src/fs/watcher.ts +142 -0
  121. package/src/index.ts +450 -0
  122. package/src/ipc/httpPolling.ts +214 -0
  123. package/src/ipc/messages.ts +159 -0
  124. package/src/ipc/server.ts +196 -0
  125. package/src/pack.ts +309 -0
  126. package/src/push.ts +726 -0
  127. package/src/snapshot/rojo.ts +467 -0
  128. package/src/snapshot.ts +161 -0
  129. package/src/sourcemap/generator.ts +504 -0
  130. package/src/sourcemap/propertyLoader.ts +195 -0
  131. package/src/util/id.ts +15 -0
  132. package/src/util/log.ts +94 -0
  133. package/tsconfig.json +24 -0
@@ -0,0 +1,170 @@
1
+ ----------------------------------------
2
+ --
3
+ -- CustomTextButton.lua
4
+ --
5
+ -- Creates text button with custom look & feel, hover/click effects.
6
+ --
7
+ ----------------------------------------
8
+ GuiUtilities = require("../GuiUtilities")
9
+
10
+ CustomTextButtonClass = {}
11
+ CustomTextButtonClass.__index = CustomTextButtonClass
12
+
13
+ --- CustomTextButtonClass constructor.
14
+ --- @param nameSuffix string -- Suffix to append to the button's name.
15
+ --- @param labelText string -- Text to display on the button.
16
+ --- @param square boolean? -- Optional flag to render the button with square corners.
17
+ --- @return CustomTextButtonClass -- A new instance of the custom text button class.
18
+ function CustomTextButtonClass.new(nameSuffix: string, labelText: string, square: boolean?)
19
+ local self = {}
20
+ setmetatable(self, CustomTextButtonClass)
21
+
22
+ self._clickedFunction = nil
23
+
24
+ local frame = Instance.new("Frame")
25
+ frame.Name = "ButtonFrame " .. nameSuffix
26
+ frame.BackgroundTransparency = 1
27
+ frame.BorderSizePixel = 0
28
+ frame.Size = UDim2.new(0, 70, 0, 25 - GuiUtilities.kButtonVerticalFudge)
29
+
30
+ local button = Instance.new('TextButton')
31
+ button.Name = nameSuffix
32
+ button.AnchorPoint = Vector2.new(0, 0.5)
33
+ button.BackgroundTransparency = 0
34
+ button.AutoButtonColor = false
35
+ button.Text = labelText
36
+ button.Font = Enum.Font.SourceSans
37
+ button.TextSize = 15
38
+ button.Position = UDim2.new(0, 0, 0.5, 0)
39
+ button.Size = UDim2.new(1, 0, 1, GuiUtilities.kButtonVerticalFudge)
40
+ button.Parent = frame
41
+
42
+ local uICorner = Instance.new("UICorner")
43
+ uICorner.CornerRadius = UDim.new(0, 5)
44
+ uICorner.Parent = button
45
+
46
+ local uiStroke = Instance.new("UIStroke")
47
+ uiStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
48
+ uiStroke.Parent = button
49
+
50
+ if square then
51
+ button.BorderSizePixel = 1
52
+ uiStroke.Enabled = false
53
+ uICorner.CornerRadius = UDim.new(0, 0)
54
+ uICorner.Parent = frame
55
+ end
56
+
57
+ GuiUtilities.syncGuiElementButtonColor(button)
58
+ GuiUtilities.syncGuiElementFontColor(button)
59
+ GuiUtilities.syncGuiElementBorderColor(button)
60
+ GuiUtilities.syncGuiElementUIStrokeColor(uiStroke)
61
+
62
+ self._frame = frame
63
+ self._button = button
64
+
65
+ self._clicked = false
66
+ self._hovered = false
67
+ self._disabled = false
68
+
69
+ button.InputBegan:Connect(function(input)
70
+ if (input.UserInputType == Enum.UserInputType.MouseMovement) then
71
+ self._hovered = true
72
+ self:_UpdateButtonVisual()
73
+ end
74
+ end)
75
+
76
+ button.InputEnded:Connect(function(input)
77
+ if (input.UserInputType == Enum.UserInputType.MouseMovement) then
78
+ self._hovered = false
79
+ self._clicked = false
80
+ self:_UpdateButtonVisual()
81
+ end
82
+ end)
83
+
84
+ button.MouseButton1Down:Connect(function()
85
+ self._clicked = true
86
+ self:_UpdateButtonVisual()
87
+ end)
88
+
89
+ button.MouseButton1Up:Connect(function()
90
+ self._clicked = false
91
+ self:_UpdateButtonVisual()
92
+ end)
93
+
94
+ GuiUtilities.BindThemeChanged(function ()
95
+ if self._disabled then
96
+ task.delay(0, function ()
97
+ self:_UpdateButtonVisual()
98
+ end)
99
+ end
100
+ end)
101
+
102
+ button.Activated:Connect(function (inputObject, timesPressed)
103
+ if self._clickedFunction then
104
+ self._clickedFunction(inputObject, timesPressed)
105
+ end
106
+ end)
107
+
108
+ self:_UpdateButtonVisual()
109
+
110
+ return self
111
+ end
112
+
113
+ function CustomTextButtonClass:_UpdateButtonVisual()
114
+ if self._disabled then -- background color
115
+ self._button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Disabled)
116
+ elseif (self._clicked) then
117
+ self._button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Pressed)
118
+ elseif (self._hovered) then
119
+ self._button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
120
+ else
121
+ self._button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Default)
122
+ end
123
+ if self._disabled then -- button text color
124
+ self._button.TextColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.ButtonText, Enum.StudioStyleGuideModifier.Disabled)
125
+ else
126
+ self._button.TextColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.ButtonText, Enum.StudioStyleGuideModifier.Default)
127
+ end
128
+ end
129
+
130
+ -- Backwards compatibility (should be removed in the future)
131
+ CustomTextButtonClass.getButton = CustomTextButtonClass.GetButton
132
+
133
+ --- Returns the internal button instance.
134
+ --- @return TextButton -- The TextButton instance used in the custom button.
135
+ function CustomTextButtonClass:GetButton()
136
+ return self._button
137
+ end
138
+
139
+ --- Returns the outer frame containing the button.
140
+ --- @return Frame -- The Frame instance wrapping the button.
141
+ function CustomTextButtonClass:GetFrame()
142
+ return self._frame
143
+ end
144
+
145
+ --- Returns whether the button is currently disabled.
146
+ --- @return boolean -- True if the button is disabled, false otherwise.
147
+ function CustomTextButtonClass:GetDisabled()
148
+ return self._disabled
149
+ end
150
+
151
+ --- Sets the disabled state of the button and updates its visual appearance.
152
+ --- @param state boolean -- True to disable the button, false to enable it.
153
+ function CustomTextButtonClass:SetDisabled(state: boolean)
154
+ self._disabled = state
155
+ self:_UpdateButtonVisual()
156
+ end
157
+
158
+ --- Sets the size of the button's outer frame.
159
+ --- @param size UDim2 -- The desired size for the frame.
160
+ function CustomTextButtonClass:SetSize(size: UDim2)
161
+ self._frame.Size = size + UDim2.fromOffset(0, -GuiUtilities.kButtonVerticalFudge)
162
+ end
163
+
164
+ --- Sets the function to be called when the button is clicked.
165
+ --- @param cf (inputObject: InputObject, timesPressed: number) -> () -- A callback function or nil to remove the function.
166
+ function CustomTextButtonClass:SetClickedFunction(cf: (inputObject: InputObject, timesPressed: number) -> () | nil)
167
+ self._clickedFunction = cf
168
+ end
169
+
170
+ return CustomTextButtonClass
@@ -0,0 +1,363 @@
1
+ ----------------------------------------
2
+ --
3
+ -- DropdownMenuClass.lua
4
+ --
5
+ -- Creates dropdown-style selection menu.
6
+ --
7
+ ----------------------------------------
8
+ GuiUtilities = require("../GuiUtilities")
9
+
10
+ local kArrowDown = "rbxasset://textures/WindControl/ArrowDown.png"
11
+ local kDropdownButtonHeight = 23
12
+
13
+ DropdownMenuClass = {}
14
+ DropdownMenuClass.__index = DropdownMenuClass
15
+
16
+ DropdownMenuClass._defaultLength = 189
17
+
18
+ --- DropdownMenuClass constructor.
19
+ --- @param nameSuffix string -- Suffix to append to the dropdown's name.
20
+ --- @param labelText string -- Label text to display alongside the dropdown.
21
+ --- @param selectionTable {{Id: string | number, Text: string}} -- Table of selection options to populate the dropdown.
22
+ --- @param placeholderText string? -- Optional text to display when no option is selected.
23
+ --- @return DropdownMenuClass -- A new instance of the dropdown menu.
24
+ function DropdownMenuClass.new(nameSuffix: string, labelText: string, selectionTable: {{Id: number | string, Text: string}}, placeholderText: string?)
25
+ local self = {}
26
+ setmetatable(self, DropdownMenuClass)
27
+ self._dropdownButtonHeight = kDropdownButtonHeight
28
+ self._expandedSize = 0
29
+ self._placeholderText = placeholderText or "Select option"
30
+
31
+ local section = GuiUtilities.MakeStandardFixedHeightFrame('DMSection' .. nameSuffix)
32
+ self._sectionFrame = section
33
+
34
+ local label = GuiUtilities.MakeStandardPropertyLabel(labelText)
35
+ label.Parent = section
36
+
37
+ local button = Instance.new("TextButton")
38
+ button.AutoButtonColor = false
39
+ button.Text = ""
40
+ button.Size = UDim2.new(0, 100, 0.6, 0)
41
+ button.AnchorPoint = Vector2.new(0,0.5)
42
+ button.Position = UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin, 0.5, 0)
43
+ button.Parent = section
44
+ GuiUtilities.syncGuiElementBackgroundColor(button)
45
+ GuiUtilities.syncGuiElementBorderColor(button)
46
+ GuiUtilities.syncGuiElementInputFieldColor(button)
47
+
48
+ local buttonLabel = Instance.new("TextLabel")
49
+ buttonLabel.Name = "ButtonLabel"
50
+ buttonLabel.BackgroundTransparency = 1
51
+ buttonLabel.Text = self._placeholderText
52
+ buttonLabel.Size = UDim2.new(0, 85, 1, 0)
53
+ buttonLabel.AnchorPoint = Vector2.new(0,0.5)
54
+ buttonLabel.Position = UDim2.new(0, 0, 0.5, 0)
55
+ buttonLabel.Parent = button
56
+ GuiUtilities.syncGuiElementFontColor(buttonLabel)
57
+
58
+ local invisFrame = Instance.new("Frame")
59
+ invisFrame.Size = UDim2.new(1,0,0,0)
60
+ invisFrame.AnchorPoint = Vector2.new(0.5,0)
61
+ invisFrame.Position = UDim2.new(0.5,0,1,0)
62
+ invisFrame.BackgroundTransparency = 1
63
+ invisFrame.ClipsDescendants = true
64
+ invisFrame.Parent = button
65
+
66
+ local contentsFrame = Instance.new("ScrollingFrame")
67
+ contentsFrame.CanvasSize = UDim2.new(1,0,0,0)
68
+ contentsFrame.ScrollBarThickness = 0
69
+ contentsFrame.Size = UDim2.new(1,-2,0,self._expandedSize)
70
+ contentsFrame.AnchorPoint = Vector2.new(0.5,0)
71
+ contentsFrame.Position = UDim2.new(0.5,0,0,0)
72
+ contentsFrame.BorderSizePixel = 1
73
+ contentsFrame.ZIndex = 9
74
+ contentsFrame.Parent = invisFrame
75
+ GuiUtilities.syncGuiElementInputFieldColor(contentsFrame)
76
+ GuiUtilities.syncGuiElementBorderColor(contentsFrame)
77
+
78
+ local listLayout = Instance.new("UIListLayout")
79
+ listLayout.FillDirection = Enum.FillDirection.Vertical
80
+ listLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
81
+ listLayout.SortOrder = Enum.SortOrder.LayoutOrder
82
+ listLayout.Parent = contentsFrame
83
+
84
+ local arrow = Instance.new("ImageLabel")
85
+ arrow.Name = "Arrow"
86
+ arrow.AnchorPoint = Vector2.new(1, 0)
87
+ arrow.Position = UDim2.new(1, 0)
88
+ arrow.Size = UDim2.new(0, 15, 1, 0)
89
+ arrow.BackgroundTransparency = 1
90
+ arrow.Image = kArrowDown
91
+ arrow.Parent = button
92
+
93
+ self._titleLabel = label
94
+ self._dropButton = button
95
+ self._buttonLabel = buttonLabel
96
+ self._contentsFrame = contentsFrame
97
+ self._invisFrame = invisFrame
98
+ self._listLayout = listLayout
99
+ self._arrow = arrow
100
+ self._buttonArray = {}
101
+ self._selected = ""
102
+ self._expanded = false
103
+ self._rightClickResetEnabled = true
104
+
105
+ self:_SetButtonHoverEvents(button, Enum.StudioStyleGuideColor.InputFieldBackground)
106
+
107
+ self._dropButton.MouseButton1Click:Connect(function()
108
+ self:_ToggleExpand()
109
+ end)
110
+
111
+ self._dropButton.MouseButton2Click:Connect(function()
112
+ if not self._rightClickResetEnabled then return end
113
+ self:_Retract()
114
+ self:ResetValue()
115
+ end)
116
+
117
+ if selectionTable then -- if the user provided a table of selections
118
+ self:AddSelectionsFromTable(selectionTable)
119
+ end
120
+
121
+ GuiUtilities.BindThemeChanged(function () self:_UpdateColors() end)
122
+ self:_UpdateColors()
123
+
124
+ return self
125
+ end
126
+
127
+ function DropdownMenuClass:_ToggleExpand()
128
+ -- get the widget
129
+ local widget = self._invisFrame:FindFirstAncestorOfClass("DockWidgetPluginGui")
130
+ if widget then
131
+ -- now test if the bottom of the frame is below the absolute size of the widget
132
+ local absSizeY = self._contentsFrame.AbsoluteSize.Y
133
+ local absPosY = self._contentsFrame.AbsolutePosition.Y
134
+ local absAdded
135
+ if self._expanded and self._contentsFrame.AnchorPoint == Vector2.new(0.5,1) then
136
+ absAdded = absSizeY+absPosY+absSizeY
137
+ elseif self._contentsFrame.AnchorPoint == Vector2.new(0.5,1) then
138
+ absAdded = absSizeY+absPosY+absSizeY
139
+ else
140
+ absAdded = absSizeY+absPosY
141
+ end
142
+
143
+ if not self._expanded then
144
+ if absAdded >= widget.AbsoluteSize.Y then
145
+ self._contentsFrame.AnchorPoint = Vector2.new(0.5,1)
146
+ self._contentsFrame.Position = UDim2.new(0.5,0,1,0)
147
+ self._invisFrame.AnchorPoint = Vector2.new(0.5,1)
148
+ self._invisFrame.Position = UDim2.new(0.5,0,0,0)
149
+ else
150
+ self._contentsFrame.AnchorPoint = Vector2.new(0.5,0)
151
+ self._contentsFrame.Position = UDim2.new(0.5,0,0,0)
152
+ self._invisFrame.AnchorPoint = Vector2.new(0.5,0)
153
+ self._invisFrame.Position = UDim2.new(0.5,0,1,0)
154
+ end
155
+ end
156
+ end
157
+
158
+ if self._expanded then
159
+ self:_Retract()
160
+ self._expanded = false
161
+ else
162
+ self:_Expand()
163
+ self._expanded = true
164
+ end
165
+ end
166
+
167
+ function DropdownMenuClass:_Retract()
168
+ self._invisFrame:TweenSize(UDim2.new(1,2,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.15, true)
169
+ self._expanded = false
170
+ end
171
+
172
+ function DropdownMenuClass:_Expand()
173
+ self._invisFrame:TweenSize(UDim2.new(1,2,0,self._expandedSize+2), Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.15, true)
174
+ self._expanded = true
175
+ end
176
+
177
+ function DropdownMenuClass:_UpdateSize()
178
+ local count = 0
179
+ for _,v in pairs(self._buttonArray) do
180
+ count += 1
181
+ end
182
+ if count >= 7 then
183
+ self._canvasSize = count*self._dropdownButtonHeight
184
+ self._expandedSize = DropdownMenuClass._defaultLength
185
+ self._contentsFrame.CanvasSize = UDim2.new(1,-2,0,self._canvasSize)
186
+ self._contentsFrame.Size = UDim2.new(1,-2,0,self._expandedSize)
187
+ if self._expanded then
188
+ self._invisFrame:TweenSize(UDim2.new(1,2,0,self._expandedSize), Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.15, true)
189
+ end
190
+ else
191
+ self._canvasSize = count*self._dropdownButtonHeight
192
+ self._expandedSize = count*self._dropdownButtonHeight
193
+
194
+ self._contentsFrame.CanvasSize = UDim2.new(1,-2,0,self._canvasSize)
195
+ self._contentsFrame.Size = UDim2.new(1,-2,0,self._expandedSize)
196
+ if self._expanded then
197
+ self._invisFrame:TweenSize(UDim2.new(1,2,0,self._expandedSize), Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.15, true)
198
+ end
199
+ end
200
+
201
+ end
202
+
203
+ function DropdownMenuClass:_UpdateColors()
204
+ local themeName = GuiUtilities.GetThemeName()
205
+ self._arrow.ImageColor3 = if themeName == "Light" then GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Border) else GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.TitlebarText)
206
+ end
207
+
208
+ function DropdownMenuClass:_SetButtonHoverEvents(button: GuiButton, defaultStyle: Enum.StudioStyleGuideColor)
209
+ button.InputBegan:Connect(function(inputObject: InputObject)
210
+ if inputObject.UserInputType ~= Enum.UserInputType.MouseMovement then return end
211
+ button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
212
+ end)
213
+
214
+ button.InputEnded:Connect(function (inputObject: InputObject)
215
+ if inputObject.UserInputType ~= Enum.UserInputType.MouseMovement then return end
216
+ button.BackgroundColor3 = GuiUtilities.GetThemeColor(defaultStyle)
217
+ end)
218
+
219
+ button.MouseButton1Down:Connect(function()
220
+ button.BackgroundColor3 = GuiUtilities.GetThemeColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Pressed)
221
+ end)
222
+
223
+ button.MouseButton1Up:Connect(function()
224
+ button.BackgroundColor3 = GuiUtilities.GetThemeColor(defaultStyle)
225
+ end)
226
+ end
227
+
228
+ --- Adds a single selection item to the dropdown menu.
229
+ --- @param selectionTable {Id: string | number, Text: string} -- Table containing selection data with `Text` (string) and `Id` (string or number).
230
+ function DropdownMenuClass:AddSelectionItem(selectionTable: {Id: number | string, Text: string})
231
+ assert(type(selectionTable.Text) == "string", "Expected String for Text, got "..type(selectionTable.Text))
232
+ assert(type(selectionTable.Id) == "string" or type(selectionTable.Id) == "number", "Expected String or Number for Id, got " .. type(selectionTable.Id))
233
+
234
+ if self._buttonArray[selectionTable.Id] then -- if the selection already exists
235
+ return warn("There is already a selection with identifier: "..selectionTable.Id)
236
+ end
237
+
238
+ local button = Instance.new("TextButton")
239
+ button.Name = selectionTable.Text
240
+ button.Text = selectionTable.Text
241
+ button.BorderMode = Enum.BorderMode.Inset
242
+ button.Size = UDim2.new(1,0,0,self._dropdownButtonHeight)
243
+ button.BackgroundTransparency = 0
244
+ button.AutoButtonColor = false
245
+ button.ZIndex = 10
246
+ button.Parent = self._contentsFrame
247
+ GuiUtilities.syncGuiElementFontColor(button)
248
+ GuiUtilities.syncGuiElementBorderColor(button)
249
+ GuiUtilities.syncGuiElementBackgroundColor(button)
250
+
251
+ local connection = button.MouseButton1Click:Connect(function()
252
+ self:SetValue(selectionTable.Id)
253
+ self:_Retract()
254
+ end)
255
+
256
+ self._buttonArray[selectionTable.Id] = {
257
+ Id = selectionTable.Id,
258
+ Text = selectionTable.Text,
259
+ button = button,
260
+ connection = connection
261
+ }
262
+
263
+ self:_SetButtonHoverEvents(button, Enum.StudioStyleGuideColor.MainBackground)
264
+
265
+ -- change contentsFrame size. Remember to do this at the end next time.
266
+ self:_UpdateSize()
267
+ return
268
+ end
269
+
270
+ --- Removes a selection item from the dropdown menu by its identifier.
271
+ --- @param identifier string | number -- Identifier of the selection to remove.
272
+ function DropdownMenuClass:RemoveSelection(identifier)
273
+ if self._buttonArray[identifier] then
274
+ self._buttonArray[identifier].connection:Disconnect()
275
+ self._buttonArray[identifier].button:Destroy()
276
+ self._buttonArray[identifier] = nil
277
+ self:_UpdateSize() -- resize frame
278
+ end
279
+ end
280
+
281
+ --- Adds multiple selection items to the dropdown from a table of selection tables.
282
+ --- @param selectionTable table -- Table of selection tables to add.
283
+ function DropdownMenuClass:AddSelectionsFromTable(selectionTable)
284
+ assert(type(selectionTable) == "table", "Expected table. Got "..type(selectionTable))
285
+
286
+ for i,v in pairs(selectionTable) do
287
+ if type(v) == "table" then
288
+ self:AddSelectionItem(v)
289
+ end
290
+ end
291
+ end
292
+
293
+ --- Resets the dropdown selection to none and restores placeholder text.
294
+ function DropdownMenuClass:ResetValue()
295
+ self._selected = ""
296
+ self._buttonLabel.Text = self._placeholderText
297
+ end
298
+
299
+ --- Sets the current value of the dropdown to the option with the given identifier.
300
+ --- Triggers the value changed callback if defined.
301
+ --- @param newValue number | string -- The identifier of the option to select.
302
+ function DropdownMenuClass:SetValue(newValue: number | string)
303
+ assert(self._buttonArray[newValue] ~= nil, "Could not find an option with the id: " .. newValue)
304
+ self._selected = newValue
305
+ self._buttonLabel.Text = self._buttonArray[newValue].Text
306
+ if (self._valueChangedFunction) then
307
+ self._valueChangedFunction(newValue, self._buttonArray[newValue].Text)
308
+ end
309
+ end
310
+
311
+ --- Returns the value (Id) of the selected option, or nil if nothing is selected.
312
+ --- @return number | string | nil -- Id of selected option, or nil.
313
+ function DropdownMenuClass:GetValue(): number | string | nil
314
+ if self._selected == "" then return nil end
315
+ return self._buttonArray[self._selected].Id
316
+ end
317
+
318
+ --- Returns the text (Text) of the selected option, or nil if nothing is selected.
319
+ --- @return string | nil -- Text of selected option, or nil.
320
+ function DropdownMenuClass:GetText(): string | nil
321
+ if self._selected == "" then return nil end
322
+ return self._buttonArray[self._selected].Text
323
+ end
324
+
325
+ --- Returns the frame containing the full dropdown section (title + contents).
326
+ --- @return Frame -- The section frame.
327
+ function DropdownMenuClass:GetSectionFrame()
328
+ return self._sectionFrame
329
+ end
330
+
331
+ --- Returns the frame containing the dropdown's content (selection options).
332
+ --- @return Frame -- The contents frame.
333
+ function DropdownMenuClass:GetContentsFrame()
334
+ return self._contentsFrame
335
+ end
336
+
337
+ --- Changes the label text displayed above the dropdown menu.
338
+ --- @param labelText string -- New label text to display.
339
+ function DropdownMenuClass:SetLabelText(labelText: string)
340
+ assert(type(labelText) == "string", "Expected string. Got "..type(labelText))
341
+ self._titleLabel.Text = labelText
342
+ end
343
+
344
+ --- Sets the function to call when the selected value changes.
345
+ --- @param vcFunction (newValue: number | string) -> () -- A function that takes the new value (number or string) as an argument.
346
+ function DropdownMenuClass:SetValueChangedFunction(vcFunction: (newValue: number | string, newText: string) -> ())
347
+ self._valueChangedFunction = vcFunction
348
+ end
349
+
350
+ --- Sets the sort order of the dropdown menu items within a UI layout.
351
+ --- Defaults to `LayoutOrder` if not explicitly set.
352
+ --- @param layoutOrder Enum.SortOrder -- The sort order to apply (e.g., Name, LayoutOrder).
353
+ function DropdownMenuClass:SetSortOrder(layoutOrder: Enum.SortOrder)
354
+ self._listLayout.SortOrder = layoutOrder
355
+ end
356
+
357
+ --- Enables or disables the ability to reset the dropdown selection via right-click.
358
+ --- @param state boolean -- Whether right-click reset is enabled.
359
+ function DropdownMenuClass:SetRightClickResetEnabled(state: boolean)
360
+ self._rightClickResetEnabled = state
361
+ end
362
+
363
+ return DropdownMenuClass
@@ -0,0 +1,43 @@
1
+ ----------------------------------------
2
+ --
3
+ -- HorizontalLineClass.lua
4
+ --
5
+ -- Creates a horizontal line of a fixed height and color
6
+ --
7
+ ----------------------------------------
8
+ GuiUtilities = require("../GuiUtilities")
9
+
10
+ local kDefaultColor = Color3.new(0.5, 0.5, 0.5)
11
+
12
+ local HorizontalLineClass = {}
13
+ HorizontalLineClass.__index = HorizontalLineClass
14
+
15
+ --- HorizontalLineClass constructor.
16
+ --- @param nameSuffix string -- Suffix to append to the line's name for uniqueness.
17
+ --- @param height number? -- Optional height of the horizontal line. Defaults to a predefined value if not specified.
18
+ --- @param color Color3? -- Optional color of the horizontal line. Defaults to a standard UI color if not specified.
19
+ --- @return HorizontalLineClass -- A new instance of the horizontal line class.
20
+ function HorizontalLineClass.new(nameSuffix: string, height: number?, color: Color3?)
21
+ local self = setmetatable({}, HorizontalLineClass)
22
+
23
+ local frame = Instance.new("Frame")
24
+ frame.Name = "HLF " .. nameSuffix
25
+ frame.BackgroundTransparency = 0
26
+ frame.BorderSizePixel = 0
27
+ frame.Size = UDim2.new(1, 0, 0, height or GuiUtilities.kStandardPropertyHeight)
28
+
29
+ frame.BackgroundColor3 = if color then color else kDefaultColor
30
+ if not color then GuiUtilities.syncGuiElementColorCustom(frame, "BackgroundColor3", Enum.StudioStyleGuideColor.Border) end
31
+
32
+ self._frame = frame
33
+
34
+ return self
35
+ end
36
+
37
+ --- Returns the main frame representing the horizontal line UI element.
38
+ --- @return Frame -- The UI frame containing the horizontal line.
39
+ function HorizontalLineClass:GetFrame()
40
+ return self._frame
41
+ end
42
+
43
+ return HorizontalLineClass