@rbxts/react-clean-ui 1.3.0 → 1.4.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 (35) hide show
  1. package/out/Components/Chart/Pie.luau +13 -20
  2. package/out/Components/Input/Button.d.ts +2 -1
  3. package/out/Components/Input/Button.luau +1 -0
  4. package/out/Components/Interaction/Tooltip.d.ts +2 -0
  5. package/out/Components/Interaction/Tooltip.luau +62 -34
  6. package/out/Components/Layout/Accordion.d.ts +14 -3
  7. package/out/Components/Layout/Accordion.luau +76 -10
  8. package/out/Components/Layout/Draggable.d.ts +7 -4
  9. package/out/Components/Layout/Draggable.luau +191 -98
  10. package/out/Components/Layout/Droppable.d.ts +5 -2
  11. package/out/Components/Layout/Droppable.luau +50 -10
  12. package/out/Components/Layout/FadeGroup.d.ts +6 -0
  13. package/out/Components/Layout/FadeGroup.luau +58 -0
  14. package/out/Components/Layout/Group.d.ts +2 -2
  15. package/out/Components/Layout/Tabs.d.ts +1 -0
  16. package/out/Components/Layout/Tabs.luau +1 -0
  17. package/out/Components/Layout/index.d.ts +1 -0
  18. package/out/Components/Layout/init.luau +3 -0
  19. package/out/Components/Surface/Card.d.ts +1 -3
  20. package/out/Components/Surface/Icon.luau +1 -0
  21. package/out/Components/Typography/Text.luau +2 -2
  22. package/out/Contexts/draggable.context.d.ts +11 -0
  23. package/out/Contexts/draggable.context.luau +5 -0
  24. package/out/Helpers/create-ui-story.d.ts +1 -1
  25. package/out/Helpers/element.helper.d.ts +8 -0
  26. package/out/Helpers/element.helper.luau +77 -0
  27. package/out/Helpers/gui.helper.d.ts +6 -0
  28. package/out/Helpers/gui.helper.luau +141 -7
  29. package/out/Helpers/index.d.ts +1 -0
  30. package/out/Helpers/init.luau +3 -0
  31. package/out/Helpers/size.helper.luau +2 -10
  32. package/out/Providers/registry.provider.d.ts +7 -3
  33. package/out/Providers/registry.provider.luau +15 -15
  34. package/out/Theme/themes/wooden.theme.luau +9 -0
  35. package/package.json +1 -1
@@ -132,14 +132,7 @@ local function Pie(props)
132
132
  end
133
133
  sectionRemaining = 1 + sectionRemaining
134
134
  end
135
- local _condition = not props["Label-hidden"]
136
- if _condition then
137
- _condition = v.label
138
- if _condition ~= "" and _condition then
139
- _condition = (not props["label-hover"] or state.hover == index)
140
- end
141
- end
142
- if _condition ~= "" and _condition then
135
+ if not props["Label-hidden"] and v.label ~= nil and v.label ~= "" and (not props["label-hover"] or state.hover == index) then
143
136
  local label = v.label
144
137
  local isTextLabel = type(label) == "string" or label.text ~= nil
145
138
  local position = if #props.values == 1 then UDim2.fromScale(0.5, 0.5) else getCirclePosition(totalPercent, props["label-distance"])
@@ -157,31 +150,31 @@ local function Pie(props)
157
150
  BackgroundColor3 = background,
158
151
  BorderSizePixel = 0,
159
152
  }
160
- local _condition_1 = theme.components.charts.pie.labels.backgroundTransparency
161
- if _condition_1 == nil then
162
- _condition_1 = theme.components.box.backgroundTransparency
153
+ local _condition = theme.components.charts.pie.labels.backgroundTransparency
154
+ if _condition == nil then
155
+ _condition = theme.components.box.backgroundTransparency
163
156
  end
164
- _attributes.BackgroundTransparency = _condition_1
157
+ _attributes.BackgroundTransparency = _condition
165
158
  local _exp_2 = React.createElement(Text, {
166
159
  name = "PieLabelText",
167
160
  text = text,
168
161
  typography = TypographyHelper:getTypography(theme, "md", theme.components.charts.pie.labels.typography),
169
162
  })
170
163
  local _attributes_1 = {}
171
- local _condition_2 = theme.components.charts.pie.labels.cornerRadius
172
- if _condition_2 == nil then
173
- _condition_2 = theme.components.box.cornerRadius
164
+ local _condition_1 = theme.components.charts.pie.labels.cornerRadius
165
+ if _condition_1 == nil then
166
+ _condition_1 = theme.components.box.cornerRadius
174
167
  end
175
- _attributes_1.radius = _condition_2
168
+ _attributes_1.radius = _condition_1
176
169
  local _exp_3 = React.createElement(Corners, _attributes_1)
177
170
  local _attributes_2 = {
178
171
  key = "Stroke",
179
172
  }
180
- local _condition_3 = theme.components.charts.pie.labels.borderThickness
181
- if _condition_3 == nil then
182
- _condition_3 = theme.components.box.borderThickness
173
+ local _condition_2 = theme.components.charts.pie.labels.borderThickness
174
+ if _condition_2 == nil then
175
+ _condition_2 = theme.components.box.borderThickness
183
176
  end
184
- _attributes_2.Thickness = _condition_3
177
+ _attributes_2.Thickness = _condition_2
185
178
  _attributes_2.BorderStrokePosition = Enum.BorderStrokePosition.Inner
186
179
  _attributes_2.Color = border
187
180
  local _arg0_1 = React.createElement("frame", _attributes, _exp_2, _exp_3, React.createElement("uistroke", _attributes_2), React.createElement(Padding, {
@@ -1,5 +1,5 @@
1
1
  import React from "@rbxts/react";
2
- import { BackgroundElementProps, IconElementProps, IntentElementProps, ScalableElementProps, ShadowElementProps, SpacedElementProps, ZIndexElementProps } from "../../Interfaces/";
2
+ import { BackgroundElementProps, CssPadding, IconElementProps, IntentElementProps, ScalableElementProps, ShadowElementProps, SpacedElementProps, ZIndexElementProps } from "../../Interfaces/";
3
3
  import { IconProps } from "../Surface";
4
4
  import { ThemeTemplate } from "../../Theme/theme.template";
5
5
  export type ButtonStyleOverride = Partial<ThemeTemplate["components"]["button"]>;
@@ -14,6 +14,7 @@ export interface ButtonProps extends SpacedElementProps, ShadowElementProps, ZIn
14
14
  LayoutOrder?: number;
15
15
  styleOverride?: ButtonStyleOverride;
16
16
  name?: string;
17
+ padding?: CssPadding;
17
18
  }
18
19
  export interface ButtonTextProps extends ScalableElementProps, IntentElementProps {
19
20
  children?: string;
@@ -94,6 +94,7 @@ local function ButtonIcon(props)
94
94
  _attributes.icon = props.icon
95
95
  _attributes.spinning = props.spinning
96
96
  _attributes.speed = props.speed
97
+ _attributes.LayoutOrder = props.LayoutOrder
97
98
  local _exp = props.intent
98
99
  local _exp_1 = if props.disabled then "disabled" else "default"
99
100
  local _exp_2 = theme.components.button.intents
@@ -6,6 +6,8 @@ interface TooltipProps extends BoxProps, IntentElementProps {
6
6
  content: React.ReactNode | string;
7
7
  children: React.ReactElement<React.InstanceProps<GuiObject>>;
8
8
  placement?: TooltipPlacement;
9
+ disabled?: boolean;
10
+ open?: boolean;
9
11
  }
10
12
  export declare function Tooltip(props: TooltipProps): React.JSX.Element;
11
13
  export {};
@@ -3,6 +3,7 @@ local TS = _G[script]
3
3
  local React = TS.import(script, TS.getModule(script, "@rbxts", "react"))
4
4
  local _Contexts = TS.import(script, script.Parent.Parent.Parent, "Contexts")
5
5
  local CleanThemeContext = _Contexts.CleanThemeContext
6
+ local DragStateContext = _Contexts.DragStateContext
6
7
  local OverlayConsumer = _Contexts.OverlayConsumer
7
8
  local Text = TS.import(script, script.Parent.Parent, "Typography").Text
8
9
  local ReactRoblox = TS.import(script, TS.getModule(script, "@rbxts", "react-roblox"))
@@ -13,6 +14,7 @@ local Padding = _Decorator.Padding
13
14
  local _Helpers = TS.import(script, script.Parent.Parent.Parent, "Helpers")
14
15
  local ColorHelper = _Helpers.ColorHelper
15
16
  local CssHelper = _Helpers.CssHelper
17
+ local ElementHelper = _Helpers.ElementHelper
16
18
  local SpacingHelper = _Helpers.SpacingHelper
17
19
  local useTween = TS.import(script, TS.getModule(script, "@rbxts", "react-ripple").src).useTween
18
20
  local function TooltipPopup(props)
@@ -30,8 +32,7 @@ local function TooltipPopup(props)
30
32
  left = 0,
31
33
  right = 0,
32
34
  }
33
- local _value = theme.components.tooltip.boxShadow
34
- local shadow = if _value ~= 0 and _value == _value and _value ~= "" and _value then CssHelper:parseCssShadow(theme.components.tooltip.boxShadow) else nil
35
+ local shadow = if theme.components.tooltip.boxShadow ~= nil and theme.components.tooltip.boxShadow ~= 0 then CssHelper:parseCssShadow(theme.components.tooltip.boxShadow) else nil
35
36
  if shadow ~= nil then
36
37
  resolvedPadding.top += shadow.blurRadius.Offset + shadow.spread.Y.Offset - shadow.offset.Y.Offset
37
38
  resolvedPadding.bottom += shadow.blurRadius.Offset + shadow.spread.Y.Offset + shadow.offset.Y.Offset
@@ -178,12 +179,20 @@ end
178
179
  local function Tooltip(props)
179
180
  local targetRef = React.useRef()
180
181
  local hideRequest = React.useRef(0)
182
+ local hoveredRef = React.useRef(false)
183
+ local shownRef = React.useRef(false)
181
184
  local target, setTarget = React.useState()
182
185
  local visible, setVisible = React.useState(false)
183
186
  local theme = React.useContext(CleanThemeContext)
187
+ local dragState = React.useContext(DragStateContext)
184
188
  local fadeDuration = theme.components.tooltip.fadeDuration
185
- local show = function()
186
- hideRequest.current += 1
189
+ local suppressed = props.disabled == true or dragState.preview or dragState.dragging
190
+ local suppressedRef = React.useRef(suppressed)
191
+ suppressedRef.current = suppressed
192
+ local controlled = props.open ~= nil
193
+ local controlledRef = React.useRef(controlled)
194
+ controlledRef.current = controlled
195
+ local captureTarget = function()
187
196
  local _result = targetRef.current
188
197
  if _result ~= nil then
189
198
  _result = _result.AbsolutePosition
@@ -204,9 +213,15 @@ local function Tooltip(props)
204
213
  _object[_left_1] = _result_2
205
214
  setTarget(_object)
206
215
  end
216
+ end
217
+ local show = function()
218
+ shownRef.current = true
219
+ hideRequest.current += 1
220
+ captureTarget()
207
221
  setVisible(true)
208
222
  end
209
223
  local hide = function()
224
+ shownRef.current = false
210
225
  setVisible(false)
211
226
  local request = hideRequest.current + 1
212
227
  hideRequest.current = request
@@ -220,40 +235,53 @@ local function Tooltip(props)
220
235
  end
221
236
  end)
222
237
  end
223
- local _exp = props.children
224
- local _object = {
225
- ref = targetRef,
226
- }
227
- local _left = "Event"
228
- local _object_1 = {}
229
- local _spread = props.children.props.Event
230
- if _spread then
231
- for _k, _v in _spread do
232
- _object_1[_k] = _v
238
+ React.useEffect(function()
239
+ if suppressed then
240
+ hoveredRef.current = false
233
241
  end
234
- end
235
- _object_1.MouseEnter = function(guiObject, x, y)
236
- local _result = props.children.props.Event
237
- if _result ~= nil then
238
- _result = _result.MouseEnter
239
- if _result ~= nil then
240
- _result(guiObject, x, y)
242
+ local _condition = not suppressed
243
+ if _condition then
244
+ local _condition_1 = props.open
245
+ if _condition_1 == nil then
246
+ _condition_1 = hoveredRef.current
241
247
  end
248
+ _condition = _condition_1
242
249
  end
243
- show()
244
- end
245
- _object_1.MouseLeave = function(guiObject, x, y)
246
- local _result = props.children.props.Event
247
- if _result ~= nil then
248
- _result = _result.MouseLeave
249
- if _result ~= nil then
250
- _result(guiObject, x, y)
251
- end
250
+ local wanted = _condition
251
+ if wanted and not shownRef.current then
252
+ show()
253
+ elseif not wanted and shownRef.current then
254
+ hide()
252
255
  end
253
- hide()
254
- end
255
- _object[_left] = _object_1
256
- local child = React.cloneElement(_exp, _object)
256
+ end, { suppressed, props.open })
257
+ React.useEffect(function()
258
+ local instance = targetRef.current
259
+ if not visible or instance == nil then
260
+ return nil
261
+ end
262
+ local positionChanged = instance:GetPropertyChangedSignal("AbsolutePosition"):Connect(captureTarget)
263
+ local sizeChanged = instance:GetPropertyChangedSignal("AbsoluteSize"):Connect(captureTarget)
264
+ return function()
265
+ positionChanged:Disconnect()
266
+ sizeChanged:Disconnect()
267
+ end
268
+ end, { visible })
269
+ local child = ElementHelper:cloneWithEvents(props.children, {
270
+ ref = targetRef,
271
+ }, {
272
+ MouseEnter = function()
273
+ hoveredRef.current = true
274
+ if not controlledRef.current and not suppressedRef.current then
275
+ show()
276
+ end
277
+ end,
278
+ MouseLeave = function()
279
+ hoveredRef.current = false
280
+ if not controlledRef.current then
281
+ hide()
282
+ end
283
+ end,
284
+ })
257
285
  return React.createElement(React.Fragment, nil, child, target ~= nil and (React.createElement(OverlayConsumer, {
258
286
  render = function(overlay)
259
287
  if overlay == nil then
@@ -17,16 +17,27 @@ interface AccordionContentProps extends PaddingProps {
17
17
  text?: string;
18
18
  }
19
19
  declare function AccordionContent(_props: AccordionContentProps): undefined;
20
- export interface AccordionProps extends ScalableElementProps {
20
+ interface AccordionBaseProps extends ScalableElementProps {
21
21
  children?: React.ReactNode;
22
22
  collapsible?: boolean;
23
23
  animationDuration?: number;
24
+ autoSize?: boolean;
25
+ keepMounted?: boolean;
26
+ name?: string;
27
+ }
28
+ export interface AccordionProps extends AccordionBaseProps {
29
+ multiple?: false;
24
30
  defaultValue?: string;
25
31
  value?: string;
26
32
  onValueChange?: (value: string | undefined) => void;
27
- name?: string;
28
33
  }
29
- type AccordionComponent = React.ForwardRefExoticComponent<AccordionProps & React.RefAttributes<ImageLabel>> & {
34
+ export interface AccordionMultipleProps extends AccordionBaseProps {
35
+ multiple: true;
36
+ defaultValue?: string[];
37
+ value?: string[];
38
+ onValueChange?: (value: string[]) => void;
39
+ }
40
+ type AccordionComponent = React.ForwardRefExoticComponent<(AccordionProps | AccordionMultipleProps) & React.RefAttributes<ImageLabel>> & {
30
41
  Item: typeof AccordionItem;
31
42
  Header: typeof AccordionHeader;
32
43
  Content: typeof AccordionContent;
@@ -119,15 +119,33 @@ local function AccordionHeaderVisual(props)
119
119
  Rotation = rotation,
120
120
  }))))
121
121
  end
122
+ local function toValues(value)
123
+ if value == nil then
124
+ return {}
125
+ end
126
+ local _value = value
127
+ return if type(_value) == "string" then { value } else value
128
+ end
122
129
  local function RenderedAccordionItem(props)
123
130
  local theme = React.useContext(CleanThemeContext)
124
131
  local mounted, setMounted = React.useState(props.open)
125
132
  local height, heightTween = useTween(0, {
126
133
  duration = props.duration,
127
134
  })
135
+ local contentRef = React.useRef()
128
136
  React.useEffect(function()
129
137
  if props.open then
130
138
  setMounted(true)
139
+ local content = contentRef.current
140
+ if content ~= nil then
141
+ heightTween:setGoal(content.AbsoluteSize.Y, {
142
+ duration = props.duration,
143
+ })
144
+ if props.duration == 0 then
145
+ heightTween:setPosition(content.AbsoluteSize.Y)
146
+ end
147
+ heightTween:start()
148
+ end
131
149
  else
132
150
  heightTween:setGoal(0, {
133
151
  duration = props.duration,
@@ -252,11 +270,12 @@ local function RenderedAccordionItem(props)
252
270
  disabled = props.disabled,
253
271
  duration = props.duration,
254
272
  open = props.open,
255
- })), mounted and (React.createElement("frame", {
273
+ })), (mounted or props.keepMounted) and (React.createElement("frame", {
256
274
  key = "ContentClip",
257
275
  BackgroundTransparency = 1,
258
276
  BorderSizePixel = 0,
259
277
  ClipsDescendants = true,
278
+ Visible = mounted,
260
279
  Size = height:map(function(value)
261
280
  local _exp = UDim2.fromScale(1, 0)
262
281
  local _arg0 = UDim2.fromOffset(0, value)
@@ -264,6 +283,7 @@ local function RenderedAccordionItem(props)
264
283
  end),
265
284
  }, React.createElement("frame", {
266
285
  key = "Content",
286
+ ref = contentRef,
267
287
  BackgroundColor3 = theme.components.accordion.content.backgroundColor,
268
288
  BackgroundTransparency = theme.components.accordion.content.backgroundTransparency,
269
289
  BorderSizePixel = 0,
@@ -341,9 +361,10 @@ local Accordion = React.forwardRef(function(props, ref)
341
361
  end)
342
362
  return parsed
343
363
  end, { props.children })
344
- local initial = if props.defaultValue ~= nil then { props.defaultValue } else {}
345
- local uncontrolled, setUncontrolled = React.useState(initial)
346
- local supplied = if props.value ~= nil then { props.value } else uncontrolled
364
+ local uncontrolled, setUncontrolled = React.useState(function()
365
+ return toValues(props.defaultValue)
366
+ end)
367
+ local supplied = if props.value ~= nil then toValues(props.value) else uncontrolled
347
368
  local _set = {}
348
369
  -- ▼ ReadonlyArray.map ▼
349
370
  local _newValue = table.create(#items)
@@ -372,7 +393,8 @@ local Accordion = React.forwardRef(function(props, ref)
372
393
  end
373
394
  end
374
395
  -- ▲ ReadonlyArray.filter ▲
375
- local openValues = _newValue_1
396
+ local filtered = _newValue_1
397
+ local openValues = if props.multiple or #filtered <= 1 then filtered else { filtered[1] }
376
398
  React.useEffect(function()
377
399
  local _condition = props.value == nil
378
400
  if _condition then
@@ -406,7 +428,38 @@ local Accordion = React.forwardRef(function(props, ref)
406
428
  local _value = item.value
407
429
  local isOpen = table.find(openValues, _value) ~= nil
408
430
  local nextValues
409
- if isOpen then
431
+ if props.multiple then
432
+ -- ▼ ReadonlyArray.map ▼
433
+ local _newValue_2 = table.create(#items)
434
+ local _callback_2 = function(other)
435
+ return other.value
436
+ end
437
+ for _k, _v in items do
438
+ _newValue_2[_k] = _callback_2(_v, _k - 1, items)
439
+ end
440
+ -- ▲ ReadonlyArray.map ▲
441
+ -- ▼ ReadonlyArray.filter ▼
442
+ local _newValue_3 = {}
443
+ local _callback_3 = function(value)
444
+ local _result
445
+ if value == item.value then
446
+ _result = not isOpen
447
+ else
448
+ local _value_1 = value
449
+ _result = table.find(openValues, _value_1) ~= nil
450
+ end
451
+ return _result
452
+ end
453
+ local _length_1 = 0
454
+ for _k, _v in _newValue_2 do
455
+ if _callback_3(_v, _k - 1, _newValue_2) == true then
456
+ _length_1 += 1
457
+ _newValue_3[_length_1] = _v
458
+ end
459
+ end
460
+ -- ▲ ReadonlyArray.filter ▲
461
+ nextValues = _newValue_3
462
+ elseif isOpen then
410
463
  if not props.collapsible then
411
464
  return nil
412
465
  end
@@ -417,9 +470,16 @@ local Accordion = React.forwardRef(function(props, ref)
417
470
  if props.value == nil then
418
471
  setUncontrolled(nextValues)
419
472
  end
420
- local _result = props.onValueChange
421
- if _result ~= nil then
422
- _result(nextValues[1])
473
+ if props.multiple then
474
+ local _result = props.onValueChange
475
+ if _result ~= nil then
476
+ _result(nextValues)
477
+ end
478
+ else
479
+ local _result = props.onValueChange
480
+ if _result ~= nil then
481
+ _result(nextValues[1])
482
+ end
423
483
  end
424
484
  end
425
485
  local _attributes = {
@@ -431,7 +491,8 @@ local Accordion = React.forwardRef(function(props, ref)
431
491
  end
432
492
  _attributes.name = _condition
433
493
  _attributes.width = "100%"
434
- _attributes.height = "100%"
494
+ _attributes.height = if props.autoSize then nil else "100%"
495
+ _attributes.AutomaticSize = if props.autoSize then Enum.AutomaticSize.Y else nil
435
496
  _attributes.ClipsDescendants = true
436
497
  local _exp = React.createElement(Corners, {
437
498
  radius = theme.components.accordion.cornerRadius,
@@ -459,6 +520,11 @@ local Accordion = React.forwardRef(function(props, ref)
459
520
  _condition_1 = theme.components.accordion.animation.duration
460
521
  end
461
522
  _attributes_1.duration = _condition_1
523
+ local _condition_2 = props.keepMounted
524
+ if _condition_2 == nil then
525
+ _condition_2 = false
526
+ end
527
+ _attributes_1.keepMounted = _condition_2
462
528
  _attributes_1.onActivate = function()
463
529
  return activate(item)
464
530
  end
@@ -1,15 +1,18 @@
1
1
  import React from "@rbxts/react";
2
2
  import { DroppableRegistration } from "../../Providers";
3
+ import { DragEventInfo } from "../../Contexts";
3
4
  type GuiObjectProps = React.InstanceProps<GuiObject>;
4
5
  type GuiElement = React.ReactElement<GuiObjectProps>;
5
6
  interface DragHandleProps {
6
7
  children: GuiElement;
7
8
  }
8
9
  declare function DragHandle({ children }: DragHandleProps): React.ReactElement<GuiObjectProps, string | React.JSXElementConstructor<any>>;
9
- interface DraggableProps {
10
- onDropped?: (droppable?: DroppableRegistration) => void;
11
- onDragged?: (droppable?: DroppableRegistration) => void;
12
- onStartDrag?: () => void;
10
+ export interface DraggableProps {
11
+ onDropped?: (droppable: DroppableRegistration | undefined, info: DragEventInfo) => void;
12
+ onDragged?: (droppable: DroppableRegistration | undefined, info: DragEventInfo) => void;
13
+ onStartDrag?: (info: DragEventInfo) => void;
14
+ inputTypes?: Enum.UserInputType[];
15
+ renderOverlay?: () => React.ReactNode;
13
16
  placeholder?: boolean;
14
17
  retainPosition?: boolean;
15
18
  children: GuiElement;