@rbxts/react-clean-ui 1.1.0 → 1.2.4

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 (110) hide show
  1. package/out/Components/Chart/BarChart.d.ts +1 -0
  2. package/out/Components/Chart/BarChart.luau +149 -112
  3. package/out/Components/Chart/Pie.d.ts +1 -0
  4. package/out/Components/Chart/Pie.luau +21 -6
  5. package/out/Components/Decorator/BoxShadow.d.ts +1 -0
  6. package/out/Components/Decorator/BoxShadow.luau +12 -5
  7. package/out/Components/Decorator/Corners.d.ts +1 -0
  8. package/out/Components/Decorator/Corners.luau +8 -3
  9. package/out/Components/Decorator/Gradient.d.ts +8 -0
  10. package/out/Components/Decorator/Gradient.luau +23 -0
  11. package/out/Components/Decorator/Padding.d.ts +3 -1
  12. package/out/Components/Decorator/Padding.luau +11 -6
  13. package/out/Components/Decorator/index.d.ts +1 -0
  14. package/out/Components/Decorator/init.luau +3 -0
  15. package/out/Components/Input/Button.d.ts +1 -0
  16. package/out/Components/Input/Button.luau +49 -34
  17. package/out/Components/Input/Checkbox.d.ts +1 -0
  18. package/out/Components/Input/Checkbox.luau +28 -18
  19. package/out/Components/Input/HoverButton.d.ts +1 -0
  20. package/out/Components/Input/HoverButton.luau +44 -42
  21. package/out/Components/Input/Increment.d.ts +1 -0
  22. package/out/Components/Input/Increment.luau +10 -3
  23. package/out/Components/Input/Input.d.ts +3 -1
  24. package/out/Components/Input/Input.luau +89 -68
  25. package/out/Components/Input/Select.d.ts +11 -1
  26. package/out/Components/Input/Select.luau +315 -39
  27. package/out/Components/Input/Slider.luau +44 -53
  28. package/out/Components/Input/Switch.d.ts +9 -0
  29. package/out/Components/Input/Switch.luau +145 -0
  30. package/out/Components/Input/index.d.ts +1 -0
  31. package/out/Components/Input/init.luau +3 -0
  32. package/out/Components/Interaction/Modal.d.ts +14 -0
  33. package/out/Components/Interaction/Modal.luau +262 -0
  34. package/out/Components/Interaction/Toast.d.ts +2 -1
  35. package/out/Components/Interaction/Toast.luau +53 -28
  36. package/out/Components/Interaction/Tooltip.luau +36 -25
  37. package/out/Components/Interaction/index.d.ts +1 -0
  38. package/out/Components/Interaction/init.luau +3 -0
  39. package/out/Components/Layout/Accordion.d.ts +1 -0
  40. package/out/Components/Layout/Accordion.luau +51 -55
  41. package/out/Components/Layout/Column.d.ts +1 -0
  42. package/out/Components/Layout/Column.luau +11 -6
  43. package/out/Components/Layout/Container.d.ts +3 -0
  44. package/out/Components/Layout/Container.luau +26 -17
  45. package/out/Components/Layout/Draggable.luau +25 -3
  46. package/out/Components/Layout/Fieldset.d.ts +1 -0
  47. package/out/Components/Layout/Fieldset.luau +14 -42
  48. package/out/Components/Layout/FlexItem.luau +7 -0
  49. package/out/Components/Layout/Grid.d.ts +10 -0
  50. package/out/Components/Layout/Grid.luau +172 -0
  51. package/out/Components/Layout/Group.d.ts +1 -0
  52. package/out/Components/Layout/Group.luau +48 -60
  53. package/out/Components/Layout/HStack.d.ts +4 -0
  54. package/out/Components/Layout/HStack.luau +17 -10
  55. package/out/Components/Layout/Pagination.d.ts +32 -0
  56. package/out/Components/Layout/Pagination.luau +400 -0
  57. package/out/Components/Layout/Row.d.ts +1 -0
  58. package/out/Components/Layout/Row.luau +20 -48
  59. package/out/Components/Layout/Scroller.d.ts +1 -0
  60. package/out/Components/Layout/Scroller.luau +26 -34
  61. package/out/Components/Layout/Table.d.ts +31 -0
  62. package/out/Components/Layout/Table.luau +342 -0
  63. package/out/Components/Layout/Tabs.d.ts +20 -10
  64. package/out/Components/Layout/Tabs.luau +113 -119
  65. package/out/Components/Layout/VStack.d.ts +3 -0
  66. package/out/Components/Layout/VStack.luau +17 -10
  67. package/out/Components/Layout/index.d.ts +3 -0
  68. package/out/Components/Layout/init.luau +9 -0
  69. package/out/Components/Navigation/Menu.d.ts +1 -0
  70. package/out/Components/Navigation/Menu.luau +12 -3
  71. package/out/Components/Surface/Box.d.ts +3 -1
  72. package/out/Components/Surface/Box.luau +15 -4
  73. package/out/Components/Surface/Card.d.ts +1 -0
  74. package/out/Components/Surface/Card.luau +56 -154
  75. package/out/Components/Surface/Icon.d.ts +1 -0
  76. package/out/Components/Surface/Icon.luau +25 -16
  77. package/out/Components/Surface/ProgressBar.d.ts +14 -0
  78. package/out/Components/Surface/ProgressBar.luau +230 -0
  79. package/out/Components/Surface/index.d.ts +1 -0
  80. package/out/Components/Surface/init.luau +3 -0
  81. package/out/Components/Typography/Text.d.ts +1 -0
  82. package/out/Components/Typography/Text.luau +57 -53
  83. package/out/Contexts/index.d.ts +1 -0
  84. package/out/Contexts/init.luau +3 -0
  85. package/out/Contexts/modal.context.d.ts +15 -0
  86. package/out/Contexts/modal.context.luau +23 -0
  87. package/out/Contexts/toast.context.d.ts +1 -0
  88. package/out/Helpers/color.helper.luau +28 -0
  89. package/out/Helpers/css.helper.d.ts +4 -1
  90. package/out/Helpers/css.helper.luau +76 -6
  91. package/out/Helpers/gui.helper.luau +0 -3
  92. package/out/Helpers/size.helper.luau +0 -2
  93. package/out/Helpers/spacing.helper.luau +0 -3
  94. package/out/Helpers/typography.helper.luau +0 -2
  95. package/out/Interfaces/css.types.d.ts +7 -0
  96. package/out/Interfaces/css.types.luau +0 -20
  97. package/out/Providers/app.provider.luau +2 -1
  98. package/out/Providers/index.d.ts +1 -0
  99. package/out/Providers/init.luau +3 -0
  100. package/out/Providers/modal.provider.d.ts +6 -0
  101. package/out/Providers/modal.provider.luau +86 -0
  102. package/out/Providers/overlay.provider.luau +1 -0
  103. package/out/Providers/registry.provider.luau +0 -2
  104. package/out/Theme/theme.style.d.ts +2 -1
  105. package/out/Theme/theme.template.d.ts +126 -1
  106. package/out/Theme/themes/dark.theme.luau +225 -0
  107. package/out/Theme/themes/default.theme.luau +210 -0
  108. package/out/Theme/themes/sandstone.theme.luau +173 -0
  109. package/out/Theme/themes/wooden.theme.luau +195 -0
  110. package/package.json +1 -1
@@ -5,16 +5,23 @@ local CleanThemeContext = TS.import(script, script.Parent.Parent.Parent, "Contex
5
5
  local SpacingHelper = TS.import(script, script.Parent.Parent.Parent, "Helpers").SpacingHelper
6
6
  local function VStack(props)
7
7
  local theme = React.useContext(CleanThemeContext)
8
- return React.createElement(React.Fragment, nil, React.createElement("uilistlayout", {
9
- FillDirection = Enum.FillDirection.Vertical,
10
- HorizontalFlex = props.HorizontalFlex or Enum.UIFlexAlignment.Fill,
11
- VerticalFlex = props.VerticalFlex,
12
- VerticalAlignment = props.VerticalAlignment or props.valign,
13
- Padding = props.Padding or UDim.new(0, SpacingHelper:GetPadding(theme, props.spacing)),
14
- SortOrder = Enum.SortOrder.LayoutOrder,
15
- Change = props.Change,
16
- Event = props.Event,
17
- }), props.children)
8
+ local _attributes = {}
9
+ local _condition = props.name
10
+ if _condition == nil then
11
+ _condition = "VStack"
12
+ end
13
+ _attributes.key = _condition
14
+ _attributes.FillDirection = Enum.FillDirection.Vertical
15
+ _attributes.HorizontalFlex = props.HorizontalFlex or Enum.UIFlexAlignment.Fill
16
+ _attributes.VerticalFlex = props.VerticalFlex
17
+ _attributes.VerticalAlignment = props.VerticalAlignment or props.valign
18
+ _attributes.HorizontalAlignment = props.HorizontalAlignment
19
+ _attributes.Padding = props.Padding or UDim.new(0, SpacingHelper:GetPadding(theme, props.spacing))
20
+ _attributes.Wraps = props.Wraps
21
+ _attributes.SortOrder = Enum.SortOrder.LayoutOrder
22
+ _attributes.Change = props.Change
23
+ _attributes.Event = props.Event
24
+ return React.createElement(React.Fragment, nil, React.createElement("uilistlayout", _attributes), props.children)
18
25
  end
19
26
  return {
20
27
  VStack = VStack,
@@ -5,9 +5,12 @@ export * from './Draggable';
5
5
  export * from './Droppable';
6
6
  export * from './Fieldset';
7
7
  export * from './FlexItem';
8
+ export * from './Grid';
8
9
  export * from './Group';
9
10
  export * from './HStack';
11
+ export * from './Pagination';
10
12
  export * from './Row';
11
13
  export * from './Scroller';
12
14
  export * from './Tabs';
15
+ export * from './Table';
13
16
  export * from './VStack';
@@ -22,12 +22,18 @@ end
22
22
  for _k, _v in TS.import(script, script, "FlexItem") or {} do
23
23
  exports[_k] = _v
24
24
  end
25
+ for _k, _v in TS.import(script, script, "Grid") or {} do
26
+ exports[_k] = _v
27
+ end
25
28
  for _k, _v in TS.import(script, script, "Group") or {} do
26
29
  exports[_k] = _v
27
30
  end
28
31
  for _k, _v in TS.import(script, script, "HStack") or {} do
29
32
  exports[_k] = _v
30
33
  end
34
+ for _k, _v in TS.import(script, script, "Pagination") or {} do
35
+ exports[_k] = _v
36
+ end
31
37
  for _k, _v in TS.import(script, script, "Row") or {} do
32
38
  exports[_k] = _v
33
39
  end
@@ -37,6 +43,9 @@ end
37
43
  for _k, _v in TS.import(script, script, "Tabs") or {} do
38
44
  exports[_k] = _v
39
45
  end
46
+ for _k, _v in TS.import(script, script, "Table") or {} do
47
+ exports[_k] = _v
48
+ end
40
49
  for _k, _v in TS.import(script, script, "VStack") or {} do
41
50
  exports[_k] = _v
42
51
  end
@@ -7,6 +7,7 @@ export declare function MenuItem(props: MenuItemProps): React.JSX.Element;
7
7
  interface MenuProps {
8
8
  title: string;
9
9
  collapsed?: boolean;
10
+ name?: string;
10
11
  }
11
12
  type MenuComponent = React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<ImageLabel>> & {
12
13
  Item: typeof MenuItem;
@@ -14,6 +14,7 @@ local NavigationContext = TS.import(script, script.Parent.Parent.Parent, "Contex
14
14
  local function MenuItem(props)
15
15
  local context = React.useContext(NavigationContext)
16
16
  return React.createElement(Button, {
17
+ name = "MenuItem",
17
18
  text = if not context.collapsed then props.title else nil,
18
19
  icon = props.icon,
19
20
  Event = props.Event,
@@ -26,19 +27,26 @@ local Menu = React.forwardRef(function(props, ref)
26
27
  _condition = false
27
28
  end
28
29
  local collapsed, setCollapsed = React.useState(_condition)
30
+ local _attributes = {}
31
+ local _condition_1 = props.name
32
+ if _condition_1 == nil then
33
+ _condition_1 = "Menu"
34
+ end
35
+ _attributes.name = _condition_1
36
+ _attributes.ref = ref
29
37
  return React.createElement(NavigationContext.Provider, {
30
38
  value = {
31
39
  collapsed = collapsed,
32
40
  },
33
- }, React.createElement(Container, {
34
- ref = ref,
35
- }, React.createElement(Group, nil, React.createElement(VStack, {
41
+ }, React.createElement(Container, _attributes, React.createElement(Group, nil, React.createElement(VStack, {
36
42
  spacing = "sm",
37
43
  }, React.createElement(Container, {
44
+ name = "MenuHeader",
38
45
  group = true,
39
46
  }, React.createElement(HStack, {
40
47
  valign = "Center",
41
48
  }, React.createElement(Button, {
49
+ name = "MenuToggleButton",
42
50
  icon = "bars",
43
51
  LayoutOrder = 1,
44
52
  Event = {
@@ -47,6 +55,7 @@ local Menu = React.forwardRef(function(props, ref)
47
55
  end,
48
56
  },
49
57
  }), not collapsed and React.createElement(Text, {
58
+ name = "MenuTitle",
50
59
  text = props.title,
51
60
  variant = "heading",
52
61
  LayoutOrder = 2,
@@ -1,9 +1,11 @@
1
1
  import React from "@rbxts/react";
2
- import { BackgroundElementProps, PositionElementProps, ShadowElementProps, SizeElementProps, SpacedElementProps, ZIndexElementProps } from "../../Interfaces/";
2
+ import { BackgroundElementProps, CssBackgroundGradient, PositionElementProps, ShadowElementProps, SizeElementProps, SpacedElementProps, ZIndexElementProps } from "../../Interfaces/";
3
3
  import { CssBackgroundImage } from "../../Theme";
4
4
  export interface BoxProps extends SpacedElementProps, ShadowElementProps, BackgroundElementProps, ZIndexElementProps, SizeElementProps, PositionElementProps, React.InstanceProps<ImageLabel> {
5
5
  'border-thickness'?: number;
6
6
  'border-color'?: Color3;
7
7
  'background-image'?: CssBackgroundImage;
8
+ 'background-gradient'?: CssBackgroundGradient;
9
+ name?: string;
8
10
  }
9
11
  export declare const Box: React.ForwardRefExoticComponent<Omit<BoxProps, "ref"> & React.RefAttributes<ImageLabel>>;
@@ -6,6 +6,7 @@ local _Decorator = TS.import(script, script.Parent.Parent, "Decorator")
6
6
  local Padding = _Decorator.Padding
7
7
  local Corners = _Decorator.Corners
8
8
  local BoxShadow = _Decorator.BoxShadow
9
+ local Gradient = _Decorator.Gradient
9
10
  local Container = TS.import(script, script.Parent.Parent, "Layout").Container
10
11
  local _Helpers = TS.import(script, script.Parent.Parent.Parent, "Helpers")
11
12
  local SizeHelper = _Helpers.SizeHelper
@@ -17,7 +18,13 @@ local Box = React.forwardRef(function(props, ref)
17
18
  _condition = theme.components.box.borderThickness
18
19
  end
19
20
  local borderThickness = _condition
21
+ local _condition_1 = props.name
22
+ if _condition_1 == nil then
23
+ _condition_1 = "Box"
24
+ end
25
+ local name = _condition_1
20
26
  local _attributes = {
27
+ name = name,
21
28
  ref = ref,
22
29
  }
23
30
  for _k, _v in props do
@@ -25,16 +32,17 @@ local Box = React.forwardRef(function(props, ref)
25
32
  end
26
33
  _attributes.Size = SizeHelper:GetSize(props, UDim2.fromScale(1, 1))
27
34
  _attributes.AutomaticSize = SizeHelper:GetAutoSize(props)
28
- local _condition_1 = props.BackgroundTransparency
29
- if _condition_1 == nil then
30
- _condition_1 = theme.components.box.backgroundTransparency
35
+ local _condition_2 = props.BackgroundTransparency
36
+ if _condition_2 == nil then
37
+ _condition_2 = theme.components.box.backgroundTransparency
31
38
  end
32
- _attributes.BackgroundTransparency = _condition_1
39
+ _attributes.BackgroundTransparency = _condition_2
33
40
  _attributes.BackgroundColor3 = props.BackgroundColor3 or theme.components.box.backgroundColor
34
41
  _attributes.ZIndex = props.ZIndex
35
42
  _attributes.Event = props.Event
36
43
  _attributes.backgroundImage = props["background-image"] or theme.components.box.backgroundImage
37
44
  local _exp = borderThickness > 0 and (React.createElement("uistroke", {
45
+ key = "Stroke",
38
46
  Thickness = borderThickness,
39
47
  BorderStrokePosition = Enum.BorderStrokePosition.Inner,
40
48
  Color = props["border-color"] or theme.components.box.borderColor,
@@ -42,10 +50,13 @@ local Box = React.forwardRef(function(props, ref)
42
50
  local _attributes_1 = table.clone(props)
43
51
  setmetatable(_attributes_1, nil)
44
52
  _attributes_1.value = theme.components.box.boxShadow
53
+ _attributes_1.name = `{name}BoxShadow`
45
54
  return React.createElement(Container, _attributes, _exp, React.createElement(BoxShadow, _attributes_1), React.createElement(Padding, {
46
55
  resolvedPadding = SpacingHelper:GetResolvedPadding(theme, props, theme.components.box.spacing, theme.components.box.padding),
47
56
  }), React.createElement(Corners, {
48
57
  radius = theme.components.box.cornerRadius,
58
+ }), React.createElement(Gradient, {
59
+ value = props["background-gradient"] or theme.components.box.backgroundGradient,
49
60
  }), props.children)
50
61
  end)
51
62
  return {
@@ -23,6 +23,7 @@ type CardComponent = React.ForwardRefExoticComponent<CardProps & React.RefAttrib
23
23
  Header: typeof CardHeader;
24
24
  Footer: typeof CardFooter;
25
25
  Body: typeof CardBody;
26
+ name?: string;
26
27
  };
27
28
  declare const Card: CardComponent;
28
29
  export { Card };
@@ -14,13 +14,6 @@ local SpacingHelper = _Helpers.SpacingHelper
14
14
  local CleanThemeContext = TS.import(script, script.Parent.Parent.Parent, "Contexts").CleanThemeContext
15
15
  local Padding = TS.import(script, script.Parent.Parent, "Decorator").Padding
16
16
  local CardContext = React.createContext({})
17
- -- Resolves a theme-driven CssPosition into raw Position/AnchorPoint offsets.
18
- -- This is a one-off resolver rather than a SizeHelper addition because
19
- -- CssPosition's `center` is axis-aware (`"x"` / `"y"`) so it can express
20
- -- "center horizontally, anchor to the top with an offset" — something
21
- -- SizeHelper.GetPosition/GetAnchor can't do since they apply `center`
22
- -- identically to both axes, and that symmetric behavior is relied on
23
- -- elsewhere in the codebase so isn't something to widen here.
24
17
  local function resolveCssPositionOffset(position)
25
18
  local centerX = position.center == true or position.center == "x"
26
19
  local centerY = position.center == true or position.center == "y"
@@ -28,19 +21,6 @@ local function resolveCssPositionOffset(position)
28
21
  local anchorY = if centerY then 0.5 else (if position.top == nil and position.bottom ~= nil then 1 else 0)
29
22
  local posX = if centerX then UDim.new(0.5, 0) elseif position.left ~= nil then SizeHelper:toUDim(position.left) elseif position.right ~= nil then UDim.new(1 - SizeHelper:toUDim(position.right).Scale, -SizeHelper:toUDim(position.right).Offset) else UDim.new(0, 0)
30
23
  local posY = if centerY then UDim.new(0.5, 0) elseif position.top ~= nil then SizeHelper:toUDim(position.top) elseif position.bottom ~= nil then UDim.new(1 - SizeHelper:toUDim(position.bottom).Scale, -SizeHelper:toUDim(position.bottom).Offset) else UDim.new(0, 0)
31
- -- A theme-driven fixed/relative width (only meaningful in overlay mode,
32
- -- see CssPosition.width) forces a fixed-width/auto-height Size, taking
33
- -- over the caller's default Size={fromScale(0,0)}/AutomaticSize={XY} —
34
- -- otherwise Size/AutomaticSize are left undefined so the caller's own
35
- -- default applies unchanged. Parsed via CssHelper.parseCssSize (not
36
- -- SizeHelper.toUDim, used for top/left/right/bottom above) since it's
37
- -- the one that understands the "<percent>% - <px>px" calc() shape a
38
- -- width value may use (e.g. "100% - 50px").
39
- --
40
- -- When `width` isn't explicit but both `left` and `right` are set, imply
41
- -- a width from them the same way CSS does for an absolutely-positioned
42
- -- element: width = 100% - left - right. Explicit `width` always wins if
43
- -- somehow both are set.
44
24
  local impliedWidth = if position.width == nil and position.left ~= nil and position.right ~= nil then (function()
45
25
  local leftUDim = SizeHelper:toUDim(position.left)
46
26
  local rightUDim = SizeHelper:toUDim(position.right)
@@ -63,18 +43,6 @@ local function resolveCssPositionOffset(position)
63
43
  end
64
44
  return _object
65
45
  end
66
- -- Header/footer overlay mode escapes the card's VStack (a UIListLayout
67
- -- forcibly repositions every sibling it lays out, so there's no way to keep
68
- -- a custom Position/AnchorPoint on a child that stays inside it) — Card
69
- -- pulls an overlay-requesting Header/Footer out of the flow and renders it
70
- -- as an absolutely-positioned sibling of the Box instead. Overlay mode is
71
- -- requested via the instance `overlay` prop or `theme.components.card.header
72
- -- /footer.position.position === "absolute"`; when the instance sets any of
73
- -- its own Position/AnchorPoint/top/left/right/bottom/center fields, those
74
- -- win wholesale over the theme's CssPosition (mixing partial fields across
75
- -- the two differently-shaped position vocabularies isn't attempted), exactly
76
- -- mirroring the precedence the previous PositionElementProps-based
77
- -- implementation gave instance props.
78
46
  local function resolveOverlayPosition(props, themePosition)
79
47
  local _condition = props.overlay
80
48
  if _condition == nil then
@@ -165,6 +133,7 @@ local CardHeader = React.forwardRef(function(props, ref)
165
133
  local overlay = _binding.overlay
166
134
  local positionProps = _binding.positionProps
167
135
  local _attributes = {
136
+ name = "CardHeader",
168
137
  ref = ref,
169
138
  }
170
139
  for _k, _v in props do
@@ -179,6 +148,7 @@ local CardHeader = React.forwardRef(function(props, ref)
179
148
  _attributes.BackgroundColor3 = intent.backgroundColor
180
149
  _attributes.BorderSizePixel = 0
181
150
  _attributes.backgroundImage = intent.backgroundImage
151
+ _attributes.backgroundGradient = intent.backgroundGradient
182
152
  _attributes.Change = if overlay and card.reportOverlayHeaderRect ~= nil then {
183
153
  AbsolutePosition = function(instance)
184
154
  return card.reportOverlayHeaderRect(instance.AbsolutePosition, instance.AbsoluteSize)
@@ -190,7 +160,9 @@ local CardHeader = React.forwardRef(function(props, ref)
190
160
  local _exp = React.createElement(Padding, {
191
161
  resolvedPadding = padding,
192
162
  })
193
- local _attributes_1 = {}
163
+ local _attributes_1 = {
164
+ key = "Stroke",
165
+ }
194
166
  local _condition = theme.components.card.header.borderThickness
195
167
  if _condition == nil then
196
168
  _condition = theme.components.card.borderThickness
@@ -199,6 +171,7 @@ local CardHeader = React.forwardRef(function(props, ref)
199
171
  _attributes_1.Color = intent.borderColor
200
172
  _attributes_1.BorderStrokePosition = Enum.BorderStrokePosition.Inner
201
173
  return React.createElement(Container, _attributes, _exp, React.createElement("uistroke", _attributes_1), React.createElement("uicorner", {
174
+ key = "Corners",
202
175
  TopLeftRadius = corners,
203
176
  TopRightRadius = corners,
204
177
  BottomLeftRadius = UDim.new(0, 0),
@@ -207,16 +180,9 @@ local CardHeader = React.forwardRef(function(props, ref)
207
180
  end)
208
181
  local CardBody = React.forwardRef(function(props, ref)
209
182
  local theme = React.useContext(CleanThemeContext)
210
- -- Unified 4-tier padding resolution (see SpacingHelper.GetResolvedPadding):
211
- -- `card.body.spacing` (tier 2) overrides the global spacing map per
212
- -- scale key, falling back to it for any key it doesn't define, and
213
- -- `card.body.padding` (tier 3) overrides the scale-based tiers
214
- -- entirely at the active key. CardBody wraps its children in a
215
- -- Container, not a Box, so it doesn't get Box's automatic
216
- -- `box.padding` application for free — this resolves independently
217
- -- against `theme.components.card.body`'s own tiers.
218
183
  local paddingSourceProps = props
219
184
  local _attributes = {
185
+ name = "CardBody",
220
186
  ref = ref,
221
187
  Size = SizeHelper:GetSize(props, UDim2.fromScale(0, 0)),
222
188
  }
@@ -246,6 +212,7 @@ local CardFooter = React.forwardRef(function(props, ref)
246
212
  local overlay = _binding.overlay
247
213
  local positionProps = _binding.positionProps
248
214
  local _attributes = {
215
+ name = "CardFooter",
249
216
  ref = ref,
250
217
  }
251
218
  for _k, _v in props do
@@ -260,6 +227,7 @@ local CardFooter = React.forwardRef(function(props, ref)
260
227
  _attributes.BackgroundColor3 = intent.backgroundColor
261
228
  _attributes.BorderSizePixel = 0
262
229
  _attributes.backgroundImage = intent.backgroundImage
230
+ _attributes.backgroundGradient = intent.backgroundGradient
263
231
  _attributes.Change = if overlay and card.reportOverlayFooterRect ~= nil then {
264
232
  AbsolutePosition = function(instance)
265
233
  return card.reportOverlayFooterRect(instance.AbsolutePosition, instance.AbsoluteSize)
@@ -271,7 +239,9 @@ local CardFooter = React.forwardRef(function(props, ref)
271
239
  local _exp = React.createElement(Padding, {
272
240
  resolvedPadding = padding,
273
241
  })
274
- local _attributes_1 = {}
242
+ local _attributes_1 = {
243
+ key = "Stroke",
244
+ }
275
245
  local _condition = theme.components.card.footer.borderThickness
276
246
  if _condition == nil then
277
247
  _condition = theme.components.card.borderThickness
@@ -280,6 +250,7 @@ local CardFooter = React.forwardRef(function(props, ref)
280
250
  _attributes_1.Color = intent.borderColor
281
251
  _attributes_1.BorderStrokePosition = Enum.BorderStrokePosition.Inner
282
252
  return React.createElement(Container, _attributes, _exp, React.createElement("uistroke", _attributes_1), React.createElement("uicorner", {
253
+ key = "Corners",
283
254
  TopLeftRadius = UDim.new(0, 0),
284
255
  TopRightRadius = UDim.new(0, 0),
285
256
  BottomLeftRadius = corners,
@@ -288,18 +259,6 @@ local CardFooter = React.forwardRef(function(props, ref)
288
259
  end)
289
260
  local Card = React.forwardRef(function(props, ref)
290
261
  local theme = React.useContext(CleanThemeContext)
291
- -- Overlay clearance: an overlay header (see resolveOverlayPosition)
292
- -- is pulled out of the VStack entirely, so nothing reserves space
293
- -- for it — its rendered box typically extends downward past its own
294
- -- anchor point and overlaps into the top of the Box. These two
295
- -- pieces of state capture the header's reported rect (via
296
- -- CardContext.reportOverlayHeaderRect, set on CardHeader below) and
297
- -- the overlay wrapper frame's own AbsolutePosition, so the amount of
298
- -- that overlap can be measured and reserved as extra top padding on
299
- -- the Box (see headerClearance/boxProps below). Both are undefined
300
- -- until their first Change event fires after mount, which is the
301
- -- same measure-then-settle characteristic Accordion.tsx accepts for
302
- -- its own AbsoluteSize-driven measurements.
303
262
  local headerRect, setHeaderRect = React.useState()
304
263
  local footerRect, setFooterRect = React.useState()
305
264
  local wrapperAbsoluteY, setWrapperAbsoluteY = React.useState()
@@ -325,10 +284,6 @@ local Card = React.forwardRef(function(props, ref)
325
284
  }
326
285
  end, { props.intent, reportOverlayHeaderRect, reportOverlayFooterRect })
327
286
  local intent = ColorHelper:getIntentColors(theme, props.intent or "primary", "default", theme.components.card.header.intents)
328
- -- Pull out a Header/Footer that wants overlay placement so it can be
329
- -- rendered outside the VStack below (see resolveOverlayPosition above
330
- -- for why it can't stay inside the VStack and still be positioned).
331
- -- Everything else renders in place, in its original order.
332
287
  local _binding = React.useMemo(function()
333
288
  local flow = {}
334
289
  local header
@@ -337,8 +292,11 @@ local Card = React.forwardRef(function(props, ref)
337
292
  if child == nil then
338
293
  return nil
339
294
  end
340
- if React.isValidElement(child) and child.type == CardHeader and header == nil then
341
- local headerElement = child
295
+ local directHeader = if React.isValidElement(child) and child.type == CardHeader then child else nil
296
+ local wrappedChild = if React.isValidElement(child) then (child.props).children else nil
297
+ local wrappedHeader = if React.isValidElement(wrappedChild) and wrappedChild.type == CardHeader then wrappedChild else nil
298
+ local headerElement = directHeader or wrappedHeader
299
+ if headerElement ~= nil and header == nil then
342
300
  local _condition = headerElement.props.overlay
343
301
  if _condition == nil then
344
302
  local _result = theme.components.card.header.position
@@ -348,7 +306,7 @@ local Card = React.forwardRef(function(props, ref)
348
306
  _condition = (_result == "absolute")
349
307
  end
350
308
  if _condition then
351
- header = headerElement
309
+ header = child
352
310
  return nil
353
311
  end
354
312
  end
@@ -380,56 +338,9 @@ local Card = React.forwardRef(function(props, ref)
380
338
  local overlayHeader = _binding.overlayHeader
381
339
  local overlayFooter = _binding.overlayFooter
382
340
  local hasOverlay = overlayHeader ~= nil or overlayFooter ~= nil
383
- React.useEffect(function()
384
- if not hasOverlay then
385
- return nil
386
- end
387
- local frame = overlayWrapperRef.current
388
- if not frame then
389
- return nil
390
- end
391
- -- Some renderers (e.g. the browser-based Loom scene preview) don't
392
- -- back this ref with an instance that supports property-changed
393
- -- signals, so guard the subscription instead of throwing and
394
- -- losing overlay clearance tracking.
395
- local positionConn
396
- local sizeConn
397
- pcall(function()
398
- positionConn = frame:GetPropertyChangedSignal("AbsolutePosition"):Connect(function()
399
- return setWrapperAbsoluteY(frame.AbsolutePosition.Y)
400
- end)
401
- end)
402
- pcall(function()
403
- sizeConn = frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
404
- return setWrapperAbsoluteSize(frame.AbsoluteSize)
405
- end)
406
- end)
407
- return function()
408
- local _result = positionConn
409
- if _result ~= nil then
410
- _result:Disconnect()
411
- end
412
- local _result_1 = sizeConn
413
- if _result_1 ~= nil then
414
- _result_1:Disconnect()
415
- end
416
- end
417
- end, { hasOverlay })
418
- -- How far the overlay header's bottom edge extends past the
419
- -- wrapper's (i.e. the Box's) top edge — 0 when there's no overlap
420
- -- (e.g. a small/no theme offset) or nothing has been measured yet.
421
341
  local headerClearance = if overlayHeader ~= nil and headerRect ~= nil and wrapperAbsoluteY ~= nil then math.max(0, (headerRect.position.Y + headerRect.size.Y) - wrapperAbsoluteY) else 0
422
- -- Same idea, mirrored for the footer: the wrapper's bottom edge
423
- -- (its top plus its own height) minus the footer's reported top
424
- -- edge (AbsolutePosition is always the top-left corner regardless
425
- -- of AnchorPoint) is how far the footer's top edge sits above the
426
- -- Box's bottom edge, i.e. how much it overlaps upward into it.
427
342
  local wrapperBottomY = if wrapperAbsoluteY ~= nil and wrapperAbsoluteSize ~= nil then wrapperAbsoluteY + wrapperAbsoluteSize.Y else nil
428
343
  local footerClearance = if overlayFooter ~= nil and footerRect ~= nil and wrapperBottomY ~= nil then math.max(0, wrapperBottomY - footerRect.position.Y) else 0
429
- -- The Box below now sits inside an extra positioning frame (see
430
- -- boxWithOverlay), so its own Position/AnchorPoint/ZIndex/LayoutOrder
431
- -- are cleared here to avoid double-applying them — the wrapper frame
432
- -- owns those instead.
433
344
  local _result
434
345
  if hasOverlay then
435
346
  local _object = table.clone(props)
@@ -447,18 +358,6 @@ local Card = React.forwardRef(function(props, ref)
447
358
  _result = props
448
359
  end
449
360
  local boxProps = _result
450
- -- When there's no footer at all, footerClearance is always 0 (there's
451
- -- nothing to measure an overlap against), so fall back to the Box's
452
- -- own explicit theme.components.box.padding bottom inset — if the
453
- -- theme sets one — as a floor instead. This keeps content clear of a
454
- -- themed Box's decorative border image (e.g. wooden's wood-frame art,
455
- -- which needs a real bottom inset) even when there's no footer to
456
- -- reserve that space via measured overlap. Themes that don't set an
457
- -- explicit box padding (GetExplicitPadding returns undefined) resolve
458
- -- this floor to 0, so behavior there is unchanged. When a footer IS
459
- -- present, footerClearance (its real measured overlap) is used as-is
460
- -- exactly as before — this floor never applies then, since the
461
- -- footer's own overlap already reserves whatever space it needs.
462
361
  local _result_1
463
362
  if overlayFooter == nil then
464
363
  local _result_2 = SpacingHelper:GetExplicitPadding(theme.components.box.padding, theme.default.spacing)
@@ -474,12 +373,6 @@ local Card = React.forwardRef(function(props, ref)
474
373
  _result_1 = 0
475
374
  end
476
375
  local explicitBoxBottomFloor = _result_1
477
- -- When an overlay header and/or footer measurably overlaps into the
478
- -- Box (headerClearance/footerClearance > 0), or the footer-absent
479
- -- floor above is nonzero, reserve that much extra top/bottom padding
480
- -- via resolvedPadding — this takes over Box's own outer padding,
481
- -- which is otherwise 0/0/0/0 since Card always forces spacing="None"
482
- -- on Box below.
483
376
  local _result_2
484
377
  if headerClearance > 0 or footerClearance > 0 or explicitBoxBottomFloor > 0 then
485
378
  local _object = table.clone(boxProps)
@@ -497,6 +390,11 @@ local Card = React.forwardRef(function(props, ref)
497
390
  local boxPropsWithOverlayClearance = _result_2
498
391
  local _attributes = table.clone(boxPropsWithOverlayClearance)
499
392
  setmetatable(_attributes, nil)
393
+ local _condition = props.name
394
+ if _condition == nil then
395
+ _condition = "Card"
396
+ end
397
+ _attributes.name = _condition
500
398
  _attributes.ref = ref
501
399
  _attributes.center = nil
502
400
  _attributes.spacing = "None"
@@ -504,46 +402,50 @@ local Card = React.forwardRef(function(props, ref)
504
402
  local cardBox = (React.createElement(Box, _attributes, React.createElement(VStack, {
505
403
  spacing = "None",
506
404
  }, flowChildren)))
507
- -- ClipsDescendants is off so an overlay header/footer can protrude
508
- -- past the Box's own edges (e.g. a plaque mounted above the top
509
- -- border). This frame takes over the Position/AnchorPoint the Box
510
- -- would otherwise have used, and auto-sizes to hug the Box (plus
511
- -- whatever the overlay siblings protrude by).
512
- --
513
- -- `center` is stripped before deriving Position/AnchorPoint here,
514
- -- the same way `cardBox`'s Box always gets `center={undefined}`:
515
- -- when `center` is set, centering is handled by the outer
516
- -- `isCentered` wrapper below (a UIListLayout that centers this
517
- -- whole frame), not by this frame's own Position/AnchorPoint. If
518
- -- both applied `center` at once, the UIListLayout would place the
519
- -- frame using an (0,0)-anchor assumption while the frame itself
520
- -- reported a (0.5,0.5) AnchorPoint, offsetting it by half its own
521
- -- size and letting its (ClipsDescendants=false) contents spill out.
522
- -- Explicit top/left/right/bottom/Position/AnchorPoint overrides are
523
- -- unaffected and still apply directly to this frame.
524
405
  local _object = table.clone(props)
525
406
  setmetatable(_object, nil)
526
407
  _object.center = nil
527
408
  local wrapperPositionProps = _object
528
- local boxWithOverlay = if hasOverlay then (React.createElement("frame", {
529
- ref = overlayWrapperRef,
530
- BackgroundTransparency = 1,
531
- ClipsDescendants = false,
532
- Size = SizeHelper:GetSize(props, UDim2.fromScale(1, 1)),
533
- AutomaticSize = SizeHelper:GetAutoSize(props),
534
- Position = SizeHelper:GetPosition(wrapperPositionProps),
535
- AnchorPoint = SizeHelper:GetAnchor(wrapperPositionProps),
536
- ZIndex = props.ZIndex,
537
- LayoutOrder = props.LayoutOrder,
538
- }, cardBox, overlayHeader, overlayFooter)) else cardBox
409
+ local _result_3
410
+ if hasOverlay then
411
+ local _attributes_1 = {}
412
+ local _condition_1 = props.name
413
+ if _condition_1 == nil then
414
+ _condition_1 = "CardOverlayWrapper"
415
+ end
416
+ _attributes_1.key = _condition_1
417
+ _attributes_1.ref = overlayWrapperRef
418
+ _attributes_1.BackgroundTransparency = 1
419
+ _attributes_1.ClipsDescendants = false
420
+ _attributes_1.Size = SizeHelper:GetSize(props, UDim2.fromScale(1, 1))
421
+ _attributes_1.AutomaticSize = SizeHelper:GetAutoSize(props)
422
+ _attributes_1.Position = SizeHelper:GetPosition(wrapperPositionProps)
423
+ _attributes_1.AnchorPoint = SizeHelper:GetAnchor(wrapperPositionProps)
424
+ _attributes_1.ZIndex = props.ZIndex
425
+ _attributes_1.LayoutOrder = props.LayoutOrder
426
+ _attributes_1.Change = {
427
+ AbsolutePosition = function(instance)
428
+ return setWrapperAbsoluteY(instance.AbsolutePosition.Y)
429
+ end,
430
+ AbsoluteSize = function(instance)
431
+ return setWrapperAbsoluteSize(instance.AbsoluteSize)
432
+ end,
433
+ }
434
+ _result_3 = (React.createElement("frame", _attributes_1, cardBox, overlayHeader, overlayFooter))
435
+ else
436
+ _result_3 = cardBox
437
+ end
438
+ local boxWithOverlay = _result_3
539
439
  local isCentered = props.center == true and props.Position == nil and props.AnchorPoint == nil
540
440
  return React.createElement(CardContext.Provider, {
541
441
  value = contextValue,
542
442
  }, if isCentered then (React.createElement(Container, {
443
+ name = "CardCenterWrapper",
543
444
  Size = UDim2.fromScale(1, 1),
544
445
  ZIndex = props.ZIndex,
545
446
  LayoutOrder = props.LayoutOrder,
546
447
  }, React.createElement("uilistlayout", {
448
+ key = "CenterLayout",
547
449
  FillDirection = Enum.FillDirection.Horizontal,
548
450
  HorizontalAlignment = Enum.HorizontalAlignment.Center,
549
451
  VerticalAlignment = Enum.VerticalAlignment.Center,
@@ -6,5 +6,6 @@ export interface IconProps extends IconElementProps, ScalableElementProps, React
6
6
  spinning?: boolean;
7
7
  speed?: number;
8
8
  Rotation?: number | Binding<number>;
9
+ name?: string;
9
10
  }
10
11
  export declare function Icon(props: IconProps): React.JSX.Element;
@@ -20,11 +20,16 @@ local function SpinningIcon(props)
20
20
  tween:setGoal(360)
21
21
  tween:start()
22
22
  end)
23
- return React.createElement("frame", {
24
- BackgroundTransparency = 1,
25
- Size = UDim2.fromOffset(0, 0),
26
- AutomaticSize = Enum.AutomaticSize.XY,
27
- }, React.createElement(Icon, {
23
+ local _attributes = {}
24
+ local _condition_1 = props.name
25
+ if _condition_1 == nil then
26
+ _condition_1 = "Icon"
27
+ end
28
+ _attributes.key = _condition_1
29
+ _attributes.BackgroundTransparency = 1
30
+ _attributes.Size = UDim2.fromOffset(0, 0)
31
+ _attributes.AutomaticSize = Enum.AutomaticSize.XY
32
+ return React.createElement("frame", _attributes, React.createElement(Icon, {
28
33
  Size = props.Size,
29
34
  color = props.color,
30
35
  icon = props.icon,
@@ -51,19 +56,23 @@ function Icon(props)
51
56
  setmetatable(_attributes, nil)
52
57
  return React.createElement(SpinningIcon, _attributes)
53
58
  end
54
- local _attributes = {
55
- AnchorPoint = props.AnchorPoint,
56
- Rotation = props.Rotation,
57
- Size = props.Size or UDim2.fromOffset(size, size),
58
- Position = props.Position,
59
- LayoutOrder = props.LayoutOrder,
60
- Image = if iconId == nil then nil else `rbxassetid://{iconId}`,
61
- }
62
- local _condition = props.BackgroundTransparency
59
+ local _attributes = {}
60
+ local _condition = props.name
63
61
  if _condition == nil then
64
- _condition = 1
62
+ _condition = "Icon"
63
+ end
64
+ _attributes.key = _condition
65
+ _attributes.AnchorPoint = props.AnchorPoint
66
+ _attributes.Rotation = props.Rotation
67
+ _attributes.Size = props.Size or UDim2.fromOffset(size, size)
68
+ _attributes.Position = props.Position
69
+ _attributes.LayoutOrder = props.LayoutOrder
70
+ _attributes.Image = if iconId == nil then nil else `rbxassetid://{iconId}`
71
+ local _condition_1 = props.BackgroundTransparency
72
+ if _condition_1 == nil then
73
+ _condition_1 = 1
65
74
  end
66
- _attributes.BackgroundTransparency = _condition
75
+ _attributes.BackgroundTransparency = _condition_1
67
76
  _attributes.ImageColor3 = props.color or theme.colors.intents.primary.default.textColor
68
77
  _attributes.ZIndex = props.ZIndex
69
78
  _attributes.Visible = props.Visible
@@ -0,0 +1,14 @@
1
+ import React from "@rbxts/react";
2
+ import { IntentElementProps, PositionElementProps, ScalableElementProps, SizeElementProps, ZIndexElementProps } from "../../Interfaces";
3
+ export interface ProgressBarProps extends IntentElementProps, ScalableElementProps, SizeElementProps, PositionElementProps, ZIndexElementProps, React.InstanceProps<ImageLabel> {
4
+ value: number;
5
+ max?: number;
6
+ label?: string;
7
+ showValue?: boolean;
8
+ valueFormatter?: (value: number, max: number) => string;
9
+ name?: string;
10
+ striped?: boolean;
11
+ stripeDuration?: number;
12
+ stripeDirection?: number;
13
+ }
14
+ export declare const ProgressBar: React.ForwardRefExoticComponent<Omit<ProgressBarProps, "ref"> & React.RefAttributes<ImageLabel>>;