@lattice-ui/radio-group 0.3.2 → 0.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.
@@ -3,25 +3,77 @@ local TS = _G[script]
3
3
  local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
4
  local React = _core.React
5
5
  local Slot = _core.Slot
6
- local RovingFocusItem = TS.import(script, TS.getModule(script, "@lattice-ui", "focus").out).RovingFocusItem
6
+ local useFocusNode = _core.useFocusNode
7
7
  local _context = TS.import(script, script.Parent, "context")
8
8
  local RadioGroupItemContextProvider = _context.RadioGroupItemContextProvider
9
9
  local useRadioGroupContext = _context.useRadioGroupContext
10
+ local nextItemId = 0
11
+ local nextItemOrder = 0
10
12
  local function RadioGroupItem(props)
11
13
  local radioGroupContext = useRadioGroupContext()
12
14
  local disabled = radioGroupContext.disabled or props.disabled == true
13
15
  local checked = radioGroupContext.value == props.value
16
+ local itemRef = React.useRef()
17
+ local disabledRef = React.useRef(disabled)
18
+ React.useEffect(function()
19
+ disabledRef.current = disabled
20
+ end, { disabled })
21
+ local itemIdRef = React.useRef(0)
22
+ if itemIdRef.current == 0 then
23
+ nextItemId += 1
24
+ itemIdRef.current = nextItemId
25
+ end
26
+ local itemOrderRef = React.useRef(0)
27
+ if itemOrderRef.current == 0 then
28
+ nextItemOrder += 1
29
+ itemOrderRef.current = nextItemOrder
30
+ end
31
+ React.useEffect(function()
32
+ return radioGroupContext.registerItem({
33
+ id = itemIdRef.current,
34
+ value = props.value,
35
+ order = itemOrderRef.current,
36
+ ref = itemRef,
37
+ getDisabled = function()
38
+ return disabledRef.current
39
+ end,
40
+ })
41
+ end, { props.value, radioGroupContext })
42
+ useFocusNode({
43
+ ref = itemRef,
44
+ getDisabled = function()
45
+ return disabledRef.current
46
+ end,
47
+ })
48
+ local setItemRef = React.useCallback(function(instance)
49
+ if not instance or not instance:IsA("GuiObject") then
50
+ itemRef.current = nil
51
+ return nil
52
+ end
53
+ itemRef.current = instance
54
+ end, {})
14
55
  local handleSelect = React.useCallback(function()
15
56
  if disabled then
16
57
  return nil
17
58
  end
18
59
  radioGroupContext.setValue(props.value)
19
60
  end, { disabled, props.value, radioGroupContext })
61
+ local handleSelectionGained = React.useCallback(function()
62
+ if disabled then
63
+ return nil
64
+ end
65
+ radioGroupContext.setValue(props.value)
66
+ end, { disabled, props.value, radioGroupContext })
20
67
  local handleInputBegan = React.useCallback(function(_rbx, inputObject)
21
68
  if disabled then
22
69
  return nil
23
70
  end
24
71
  local keyCode = inputObject.KeyCode
72
+ local direction = if radioGroupContext.orientation == "horizontal" then if keyCode == Enum.KeyCode.Left then -1 elseif keyCode == Enum.KeyCode.Right then 1 else nil elseif keyCode == Enum.KeyCode.Up then -1 elseif keyCode == Enum.KeyCode.Down then 1 else nil
73
+ if direction ~= nil then
74
+ radioGroupContext.moveSelection(props.value, direction)
75
+ return nil
76
+ end
25
77
  if keyCode ~= Enum.KeyCode.Return and keyCode ~= Enum.KeyCode.Space then
26
78
  return nil
27
79
  end
@@ -30,10 +82,10 @@ local function RadioGroupItem(props)
30
82
  local eventHandlers = React.useMemo(function()
31
83
  return {
32
84
  Activated = handleSelect,
33
- SelectionGained = handleSelect,
34
85
  InputBegan = handleInputBegan,
86
+ SelectionGained = handleSelectionGained,
35
87
  }
36
- end, { handleInputBegan, handleSelect })
88
+ end, { handleInputBegan, handleSelect, handleSelectionGained })
37
89
  local itemContextValue = React.useMemo(function()
38
90
  return {
39
91
  checked = checked,
@@ -47,18 +99,13 @@ local function RadioGroupItem(props)
47
99
  if not child then
48
100
  error("[RadioGroupItem] `asChild` requires a child element.")
49
101
  end
50
- return React.createElement(RovingFocusItem, {
51
- asChild = true,
52
- disabled = disabled,
53
- }, React.createElement(Slot, {
102
+ return React.createElement(Slot, {
54
103
  Active = not disabled,
55
104
  Event = eventHandlers,
56
105
  Selectable = not disabled,
57
- }, child))
58
- end)()) else (React.createElement(RovingFocusItem, {
59
- asChild = true,
60
- disabled = disabled,
61
- }, React.createElement("textbutton", {
106
+ ref = setItemRef,
107
+ }, child)
108
+ end)()) else (React.createElement("textbutton", {
62
109
  Active = not disabled,
63
110
  AutoButtonColor = false,
64
111
  BackgroundColor3 = if checked then Color3.fromRGB(88, 142, 255) else Color3.fromRGB(47, 53, 68),
@@ -69,7 +116,8 @@ local function RadioGroupItem(props)
69
116
  Text = props.value,
70
117
  TextColor3 = if disabled then Color3.fromRGB(139, 146, 160) else Color3.fromRGB(236, 241, 249),
71
118
  TextSize = 15,
72
- }, props.children))))
119
+ ref = setItemRef,
120
+ }, props.children)))
73
121
  end
74
122
  return {
75
123
  RadioGroupItem = RadioGroupItem,
@@ -1,11 +1,14 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
3
  local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local findOrderedSelectionEntry = _core.findOrderedSelectionEntry
5
+ local focusOrderedSelectionEntry = _core.focusOrderedSelectionEntry
6
+ local getRelativeOrderedSelectionEntry = _core.getRelativeOrderedSelectionEntry
4
7
  local React = _core.React
5
8
  local useControllableState = _core.useControllableState
6
- local RovingFocusGroup = TS.import(script, TS.getModule(script, "@lattice-ui", "focus").out).RovingFocusGroup
7
9
  local RadioGroupContextProvider = TS.import(script, script.Parent, "context").RadioGroupContextProvider
8
10
  local function RadioGroupRoot(props)
11
+ local orientation = props.orientation or "vertical"
9
12
  local _binding = useControllableState({
10
13
  value = props.value,
11
14
  defaultValue = props.defaultValue,
@@ -22,34 +25,74 @@ local function RadioGroupRoot(props)
22
25
  local setValueState = _binding[2]
23
26
  local disabled = props.disabled == true
24
27
  local required = props.required == true
25
- local _condition = props.loop
26
- if _condition == nil then
27
- _condition = true
28
- end
29
- local loop = _condition
30
- local orientation = props.orientation or "vertical"
28
+ local itemEntriesRef = React.useRef({})
29
+ local _, setRegistryRevision = React.useState(0)
31
30
  local setValue = React.useCallback(function(nextValue)
32
31
  if disabled then
33
32
  return nil
34
33
  end
35
34
  setValueState(nextValue)
36
35
  end, { disabled, setValueState })
36
+ local registerItem = React.useCallback(function(item)
37
+ local _current = itemEntriesRef.current
38
+ local _item = item
39
+ table.insert(_current, _item)
40
+ setRegistryRevision(function(revision)
41
+ return revision + 1
42
+ end)
43
+ return function()
44
+ local _exp = itemEntriesRef.current
45
+ -- ▼ ReadonlyArray.findIndex ▼
46
+ local _callback = function(entry)
47
+ return entry.id == item.id
48
+ end
49
+ local _result = -1
50
+ for _i, _v in _exp do
51
+ if _callback(_v, _i - 1, _exp) == true then
52
+ _result = _i - 1
53
+ break
54
+ end
55
+ end
56
+ -- ▲ ReadonlyArray.findIndex ▲
57
+ local index = _result
58
+ if index >= 0 then
59
+ table.remove(itemEntriesRef.current, index + 1)
60
+ setRegistryRevision(function(revision)
61
+ return revision + 1
62
+ end)
63
+ end
64
+ end
65
+ end, {})
66
+ local moveSelection = React.useCallback(function(fromValue, direction)
67
+ local currentItem = findOrderedSelectionEntry(itemEntriesRef.current, function(item)
68
+ return item.value == fromValue
69
+ end) or nil
70
+ local _exp = itemEntriesRef.current
71
+ local _result = currentItem
72
+ if _result ~= nil then
73
+ _result = _result.id
74
+ end
75
+ local nextItem = getRelativeOrderedSelectionEntry(_exp, _result, direction)
76
+ if not nextItem then
77
+ return nil
78
+ end
79
+ focusOrderedSelectionEntry(nextItem)
80
+ setValue(nextItem.value)
81
+ end, { setValue })
37
82
  local contextValue = React.useMemo(function()
38
83
  return {
39
84
  value = value,
40
85
  setValue = setValue,
41
86
  disabled = disabled,
42
87
  required = required,
88
+ orientation = orientation,
89
+ registerItem = registerItem,
90
+ moveSelection = moveSelection,
43
91
  }
44
- end, { disabled, required, setValue, value })
92
+ end, { disabled, moveSelection, orientation, registerItem, required, setValue, value })
45
93
  return React.createElement(RadioGroupContextProvider, {
46
94
  value = contextValue,
47
- }, React.createElement(RovingFocusGroup, {
48
- active = not disabled,
49
- autoFocus = "none",
50
- loop = loop,
51
- orientation = orientation,
52
- }, props.children))
95
+ }, props.children)
53
96
  end
54
97
  return {
55
98
  RadioGroupRoot = RadioGroupRoot,
@@ -1,11 +1,21 @@
1
1
  import type React from "@rbxts/react";
2
- export type RadioGroupOrientation = "horizontal" | "vertical" | "both";
3
2
  export type RadioGroupSetValue = (value: string) => void;
3
+ export type RadioGroupOrientation = "horizontal" | "vertical";
4
+ export type RadioGroupItemRegistration = {
5
+ id: number;
6
+ value: string;
7
+ order: number;
8
+ ref: React.MutableRefObject<GuiObject | undefined>;
9
+ getDisabled: () => boolean;
10
+ };
4
11
  export type RadioGroupContextValue = {
5
12
  value?: string;
6
13
  setValue: RadioGroupSetValue;
7
14
  disabled: boolean;
8
15
  required: boolean;
16
+ orientation: RadioGroupOrientation;
17
+ registerItem: (item: RadioGroupItemRegistration) => () => void;
18
+ moveSelection: (fromValue: string, direction: -1 | 1) => void;
9
19
  };
10
20
  export type RadioGroupItemContextValue = {
11
21
  checked: boolean;
@@ -17,7 +27,6 @@ export type RadioGroupProps = {
17
27
  onValueChange?: (value: string) => void;
18
28
  disabled?: boolean;
19
29
  required?: boolean;
20
- loop?: boolean;
21
30
  orientation?: RadioGroupOrientation;
22
31
  children?: React.ReactNode;
23
32
  };
package/out/index.d.ts CHANGED
@@ -6,5 +6,5 @@ export declare const RadioGroup: {
6
6
  readonly Item: typeof RadioGroupItem;
7
7
  readonly Indicator: typeof RadioGroupIndicator;
8
8
  };
9
+ export type { RadioGroupContextValue, RadioGroupIndicatorProps, RadioGroupItemContextValue, RadioGroupItemProps, RadioGroupItemRegistration, RadioGroupOrientation, RadioGroupProps, RadioGroupSetValue, } from "./RadioGroup/types";
9
10
  export { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot };
10
- export type { RadioGroupContextValue, RadioGroupIndicatorProps, RadioGroupItemContextValue, RadioGroupItemProps, RadioGroupProps, RadioGroupSetValue, } from "./RadioGroup/types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattice-ui/radio-group",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
@@ -9,8 +9,7 @@
9
9
  "README.md"
10
10
  ],
11
11
  "dependencies": {
12
- "@lattice-ui/core": "0.3.2",
13
- "@lattice-ui/focus": "0.3.2"
12
+ "@lattice-ui/core": "0.4.0"
14
13
  },
15
14
  "devDependencies": {
16
15
  "@rbxts/react": "17.3.7-ts.1",