@lattice-ui/tabs 0.1.1
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.
- package/README.md +11 -0
- package/out/Tabs/TabsContent.d.ts +3 -0
- package/out/Tabs/TabsContent.luau +56 -0
- package/out/Tabs/TabsList.d.ts +3 -0
- package/out/Tabs/TabsList.luau +30 -0
- package/out/Tabs/TabsRoot.d.ts +3 -0
- package/out/Tabs/TabsRoot.luau +212 -0
- package/out/Tabs/TabsTrigger.d.ts +3 -0
- package/out/Tabs/TabsTrigger.luau +109 -0
- package/out/Tabs/context.d.ts +3 -0
- package/out/Tabs/context.luau +10 -0
- package/out/Tabs/internals/ids.d.ts +2 -0
- package/out/Tabs/internals/ids.luau +16 -0
- package/out/Tabs/types.d.ts +42 -0
- package/out/Tabs/types.luau +2 -0
- package/out/index.d.ts +5 -0
- package/out/init.luau +10 -0
- package/package.json +25 -0
- package/src/Tabs/TabsContent.tsx +65 -0
- package/src/Tabs/TabsList.tsx +29 -0
- package/src/Tabs/TabsRoot.tsx +111 -0
- package/src/Tabs/TabsTrigger.tsx +127 -0
- package/src/Tabs/context.ts +6 -0
- package/src/Tabs/internals/ids.ts +13 -0
- package/src/Tabs/types.ts +50 -0
- package/src/index.ts +13 -0
- package/tsconfig.json +16 -0
- package/tsconfig.typecheck.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local Presence = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out).Presence
|
|
7
|
+
local useTabsContext = TS.import(script, script.Parent, "context").useTabsContext
|
|
8
|
+
local createTabsContentName = TS.import(script, script.Parent, "internals", "ids").createTabsContentName
|
|
9
|
+
local function TabsContentImpl(props)
|
|
10
|
+
local contentName = createTabsContentName(props.value)
|
|
11
|
+
if props.asChild then
|
|
12
|
+
local child = props.children
|
|
13
|
+
if not React.isValidElement(child) then
|
|
14
|
+
error("[TabsContent] `asChild` requires a child element.")
|
|
15
|
+
end
|
|
16
|
+
return React.createElement(Slot, {
|
|
17
|
+
Name = contentName,
|
|
18
|
+
Visible = props.visible,
|
|
19
|
+
}, child)
|
|
20
|
+
end
|
|
21
|
+
return React.createElement("frame", {
|
|
22
|
+
BackgroundTransparency = 1,
|
|
23
|
+
BorderSizePixel = 0,
|
|
24
|
+
Size = UDim2.fromOffset(0, 0),
|
|
25
|
+
Visible = props.visible,
|
|
26
|
+
}, props.children)
|
|
27
|
+
end
|
|
28
|
+
local function TabsContent(props)
|
|
29
|
+
local tabsContext = useTabsContext()
|
|
30
|
+
local selected = tabsContext.value == props.value
|
|
31
|
+
local forceMount = props.forceMount == true
|
|
32
|
+
if not selected and not forceMount then
|
|
33
|
+
return nil
|
|
34
|
+
end
|
|
35
|
+
if forceMount then
|
|
36
|
+
return React.createElement(TabsContentImpl, {
|
|
37
|
+
asChild = props.asChild,
|
|
38
|
+
value = props.value,
|
|
39
|
+
visible = selected,
|
|
40
|
+
}, props.children)
|
|
41
|
+
end
|
|
42
|
+
return React.createElement(Presence, {
|
|
43
|
+
exitFallbackMs = 0,
|
|
44
|
+
present = selected,
|
|
45
|
+
render = function(state)
|
|
46
|
+
return React.createElement(TabsContentImpl, {
|
|
47
|
+
asChild = props.asChild,
|
|
48
|
+
value = props.value,
|
|
49
|
+
visible = state.isPresent,
|
|
50
|
+
}, props.children)
|
|
51
|
+
end,
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
return {
|
|
55
|
+
TabsContent = TabsContent,
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local RovingFocusGroup = TS.import(script, TS.getModule(script, "@lattice-ui", "focus").out).RovingFocusGroup
|
|
7
|
+
local useTabsContext = TS.import(script, script.Parent, "context").useTabsContext
|
|
8
|
+
local function TabsList(props)
|
|
9
|
+
local tabsContext = useTabsContext()
|
|
10
|
+
local listNode = if props.asChild then ((function()
|
|
11
|
+
local child = props.children
|
|
12
|
+
if not React.isValidElement(child) then
|
|
13
|
+
error("[TabsList] `asChild` requires a child element.")
|
|
14
|
+
end
|
|
15
|
+
return React.createElement(Slot, nil, child)
|
|
16
|
+
end)()) else (React.createElement("frame", {
|
|
17
|
+
BackgroundTransparency = 1,
|
|
18
|
+
BorderSizePixel = 0,
|
|
19
|
+
Size = UDim2.fromOffset(0, 0),
|
|
20
|
+
}, props.children))
|
|
21
|
+
return React.createElement(RovingFocusGroup, {
|
|
22
|
+
active = true,
|
|
23
|
+
autoFocus = "none",
|
|
24
|
+
loop = true,
|
|
25
|
+
orientation = tabsContext.orientation,
|
|
26
|
+
}, listNode)
|
|
27
|
+
end
|
|
28
|
+
return {
|
|
29
|
+
TabsList = TabsList,
|
|
30
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local useControllableState = _core.useControllableState
|
|
6
|
+
local TabsContextProvider = TS.import(script, script.Parent, "context").TabsContextProvider
|
|
7
|
+
local function getOrderedTriggers(triggers)
|
|
8
|
+
local _array = {}
|
|
9
|
+
local _length = #_array
|
|
10
|
+
table.move(triggers, 1, #triggers, _length + 1, _array)
|
|
11
|
+
local ordered = _array
|
|
12
|
+
table.sort(ordered, function(a, b)
|
|
13
|
+
return a.order < b.order
|
|
14
|
+
end)
|
|
15
|
+
return ordered
|
|
16
|
+
end
|
|
17
|
+
local function resolveNextValue(currentValue, orderedTriggers, fallbackOrder)
|
|
18
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
19
|
+
local _newValue = {}
|
|
20
|
+
local _callback = function(trigger)
|
|
21
|
+
return not trigger.disabled
|
|
22
|
+
end
|
|
23
|
+
local _length = 0
|
|
24
|
+
for _k, _v in orderedTriggers do
|
|
25
|
+
if _callback(_v, _k - 1, orderedTriggers) == true then
|
|
26
|
+
_length += 1
|
|
27
|
+
_newValue[_length] = _v
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
31
|
+
local enabled = _newValue
|
|
32
|
+
if #enabled == 0 then
|
|
33
|
+
return nil
|
|
34
|
+
end
|
|
35
|
+
if currentValue == nil then
|
|
36
|
+
local _result = enabled[1]
|
|
37
|
+
if _result ~= nil then
|
|
38
|
+
_result = _result.value
|
|
39
|
+
end
|
|
40
|
+
return _result
|
|
41
|
+
end
|
|
42
|
+
-- ▼ ReadonlyArray.find ▼
|
|
43
|
+
local _callback_1 = function(trigger)
|
|
44
|
+
return trigger.value == currentValue
|
|
45
|
+
end
|
|
46
|
+
local _result
|
|
47
|
+
for _i, _v in enabled do
|
|
48
|
+
if _callback_1(_v, _i - 1, enabled) == true then
|
|
49
|
+
_result = _v
|
|
50
|
+
break
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
-- ▲ ReadonlyArray.find ▲
|
|
54
|
+
local selectedEnabled = _result
|
|
55
|
+
if selectedEnabled then
|
|
56
|
+
return selectedEnabled.value
|
|
57
|
+
end
|
|
58
|
+
-- ▼ ReadonlyArray.find ▼
|
|
59
|
+
local _callback_2 = function(trigger)
|
|
60
|
+
return trigger.value == currentValue
|
|
61
|
+
end
|
|
62
|
+
local _result_1
|
|
63
|
+
for _i, _v in orderedTriggers do
|
|
64
|
+
if _callback_2(_v, _i - 1, orderedTriggers) == true then
|
|
65
|
+
_result_1 = _v
|
|
66
|
+
break
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
-- ▲ ReadonlyArray.find ▲
|
|
70
|
+
local selected = _result_1
|
|
71
|
+
local _result_2 = selected
|
|
72
|
+
if _result_2 ~= nil then
|
|
73
|
+
_result_2 = _result_2.order
|
|
74
|
+
end
|
|
75
|
+
local _condition = _result_2
|
|
76
|
+
if _condition == nil then
|
|
77
|
+
_condition = fallbackOrder
|
|
78
|
+
end
|
|
79
|
+
local anchorOrder = _condition
|
|
80
|
+
if anchorOrder ~= nil then
|
|
81
|
+
-- ▼ ReadonlyArray.find ▼
|
|
82
|
+
local _callback_3 = function(trigger)
|
|
83
|
+
return trigger.order > anchorOrder
|
|
84
|
+
end
|
|
85
|
+
local _result_3
|
|
86
|
+
for _i, _v in enabled do
|
|
87
|
+
if _callback_3(_v, _i - 1, enabled) == true then
|
|
88
|
+
_result_3 = _v
|
|
89
|
+
break
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
-- ▲ ReadonlyArray.find ▲
|
|
93
|
+
local after = _result_3
|
|
94
|
+
if after then
|
|
95
|
+
return after.value
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
local _result_3 = enabled[1]
|
|
99
|
+
if _result_3 ~= nil then
|
|
100
|
+
_result_3 = _result_3.value
|
|
101
|
+
end
|
|
102
|
+
return _result_3
|
|
103
|
+
end
|
|
104
|
+
local function TabsRoot(props)
|
|
105
|
+
local _binding = useControllableState({
|
|
106
|
+
value = props.value,
|
|
107
|
+
defaultValue = props.defaultValue,
|
|
108
|
+
onChange = function(nextValue)
|
|
109
|
+
if nextValue ~= nil then
|
|
110
|
+
local _result = props.onValueChange
|
|
111
|
+
if _result ~= nil then
|
|
112
|
+
_result(nextValue)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end,
|
|
116
|
+
})
|
|
117
|
+
local value = _binding[1]
|
|
118
|
+
local setValueState = _binding[2]
|
|
119
|
+
local orientation = props.orientation or "horizontal"
|
|
120
|
+
local activationMode = props.activationMode or "automatic"
|
|
121
|
+
local triggerRegistryRef = React.useRef({})
|
|
122
|
+
local lastSelectedOrderRef = React.useRef()
|
|
123
|
+
local registryRevision, setRegistryRevision = React.useState(0)
|
|
124
|
+
local registerTrigger = React.useCallback(function(trigger)
|
|
125
|
+
local _current = triggerRegistryRef.current
|
|
126
|
+
local _trigger = trigger
|
|
127
|
+
table.insert(_current, _trigger)
|
|
128
|
+
setRegistryRevision(function(revision)
|
|
129
|
+
return revision + 1
|
|
130
|
+
end)
|
|
131
|
+
return function()
|
|
132
|
+
local _exp = triggerRegistryRef.current
|
|
133
|
+
-- ▼ ReadonlyArray.findIndex ▼
|
|
134
|
+
local _callback = function(entry)
|
|
135
|
+
return entry.id == trigger.id
|
|
136
|
+
end
|
|
137
|
+
local _result = -1
|
|
138
|
+
for _i, _v in _exp do
|
|
139
|
+
if _callback(_v, _i - 1, _exp) == true then
|
|
140
|
+
_result = _i - 1
|
|
141
|
+
break
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
-- ▲ ReadonlyArray.findIndex ▲
|
|
145
|
+
local index = _result
|
|
146
|
+
if index >= 0 then
|
|
147
|
+
table.remove(triggerRegistryRef.current, index + 1)
|
|
148
|
+
setRegistryRevision(function(revision)
|
|
149
|
+
return revision + 1
|
|
150
|
+
end)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end, {})
|
|
154
|
+
local setValue = React.useCallback(function(nextValue)
|
|
155
|
+
local orderedTriggers = getOrderedTriggers(triggerRegistryRef.current)
|
|
156
|
+
-- ▼ ReadonlyArray.find ▼
|
|
157
|
+
local _callback = function(trigger)
|
|
158
|
+
return trigger.value == nextValue and not trigger.disabled
|
|
159
|
+
end
|
|
160
|
+
local _result
|
|
161
|
+
for _i, _v in orderedTriggers do
|
|
162
|
+
if _callback(_v, _i - 1, orderedTriggers) == true then
|
|
163
|
+
_result = _v
|
|
164
|
+
break
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
-- ▲ ReadonlyArray.find ▲
|
|
168
|
+
local selected = _result
|
|
169
|
+
if selected then
|
|
170
|
+
lastSelectedOrderRef.current = selected.order
|
|
171
|
+
end
|
|
172
|
+
setValueState(nextValue)
|
|
173
|
+
end, { setValueState })
|
|
174
|
+
React.useEffect(function()
|
|
175
|
+
local orderedTriggers = getOrderedTriggers(triggerRegistryRef.current)
|
|
176
|
+
-- ▼ ReadonlyArray.find ▼
|
|
177
|
+
local _callback = function(trigger)
|
|
178
|
+
return trigger.value == value and not trigger.disabled
|
|
179
|
+
end
|
|
180
|
+
local _result
|
|
181
|
+
for _i, _v in orderedTriggers do
|
|
182
|
+
if _callback(_v, _i - 1, orderedTriggers) == true then
|
|
183
|
+
_result = _v
|
|
184
|
+
break
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
-- ▲ ReadonlyArray.find ▲
|
|
188
|
+
local selected = _result
|
|
189
|
+
if selected then
|
|
190
|
+
lastSelectedOrderRef.current = selected.order
|
|
191
|
+
end
|
|
192
|
+
local nextValue = resolveNextValue(value, orderedTriggers, lastSelectedOrderRef.current)
|
|
193
|
+
if nextValue ~= value then
|
|
194
|
+
setValueState(nextValue)
|
|
195
|
+
end
|
|
196
|
+
end, { registryRevision, setValueState, value })
|
|
197
|
+
local contextValue = React.useMemo(function()
|
|
198
|
+
return {
|
|
199
|
+
value = value,
|
|
200
|
+
setValue = setValue,
|
|
201
|
+
orientation = orientation,
|
|
202
|
+
activationMode = activationMode,
|
|
203
|
+
registerTrigger = registerTrigger,
|
|
204
|
+
}
|
|
205
|
+
end, { activationMode, orientation, registerTrigger, setValue, value })
|
|
206
|
+
return React.createElement(TabsContextProvider, {
|
|
207
|
+
value = contextValue,
|
|
208
|
+
}, props.children)
|
|
209
|
+
end
|
|
210
|
+
return {
|
|
211
|
+
TabsRoot = TabsRoot,
|
|
212
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local RovingFocusItem = TS.import(script, TS.getModule(script, "@lattice-ui", "focus").out).RovingFocusItem
|
|
7
|
+
local useTabsContext = TS.import(script, script.Parent, "context").useTabsContext
|
|
8
|
+
local createTabsTriggerName = TS.import(script, script.Parent, "internals", "ids").createTabsTriggerName
|
|
9
|
+
local nextTriggerId = 0
|
|
10
|
+
local nextTriggerOrder = 0
|
|
11
|
+
local function toGuiObject(instance)
|
|
12
|
+
if not instance or not instance:IsA("GuiObject") then
|
|
13
|
+
return nil
|
|
14
|
+
end
|
|
15
|
+
return instance
|
|
16
|
+
end
|
|
17
|
+
local function TabsTrigger(props)
|
|
18
|
+
local tabsContext = useTabsContext()
|
|
19
|
+
local triggerRef = React.useRef()
|
|
20
|
+
local selected = tabsContext.value == props.value
|
|
21
|
+
local disabled = props.disabled == true
|
|
22
|
+
local triggerIdRef = React.useRef(0)
|
|
23
|
+
if triggerIdRef.current == 0 then
|
|
24
|
+
nextTriggerId += 1
|
|
25
|
+
triggerIdRef.current = nextTriggerId
|
|
26
|
+
end
|
|
27
|
+
local triggerOrderRef = React.useRef(0)
|
|
28
|
+
if triggerOrderRef.current == 0 then
|
|
29
|
+
nextTriggerOrder += 1
|
|
30
|
+
triggerOrderRef.current = nextTriggerOrder
|
|
31
|
+
end
|
|
32
|
+
React.useEffect(function()
|
|
33
|
+
return tabsContext.registerTrigger({
|
|
34
|
+
id = triggerIdRef.current,
|
|
35
|
+
value = props.value,
|
|
36
|
+
disabled = disabled,
|
|
37
|
+
ref = triggerRef,
|
|
38
|
+
order = triggerOrderRef.current,
|
|
39
|
+
})
|
|
40
|
+
end, { disabled, props.value, tabsContext })
|
|
41
|
+
local setTriggerRef = React.useCallback(function(instance)
|
|
42
|
+
triggerRef.current = toGuiObject(instance)
|
|
43
|
+
end, {})
|
|
44
|
+
local handleActivated = React.useCallback(function()
|
|
45
|
+
if disabled then
|
|
46
|
+
return nil
|
|
47
|
+
end
|
|
48
|
+
tabsContext.setValue(props.value)
|
|
49
|
+
end, { disabled, props.value, tabsContext })
|
|
50
|
+
local handleSelectionGained = React.useCallback(function()
|
|
51
|
+
if disabled or tabsContext.activationMode ~= "automatic" then
|
|
52
|
+
return nil
|
|
53
|
+
end
|
|
54
|
+
tabsContext.setValue(props.value)
|
|
55
|
+
end, { disabled, props.value, tabsContext })
|
|
56
|
+
local handleInputBegan = React.useCallback(function(_rbx, inputObject)
|
|
57
|
+
if disabled or tabsContext.activationMode ~= "manual" then
|
|
58
|
+
return nil
|
|
59
|
+
end
|
|
60
|
+
local keyCode = inputObject.KeyCode
|
|
61
|
+
if keyCode ~= Enum.KeyCode.Return and keyCode ~= Enum.KeyCode.Space then
|
|
62
|
+
return nil
|
|
63
|
+
end
|
|
64
|
+
tabsContext.setValue(props.value)
|
|
65
|
+
end, { disabled, props.value, tabsContext })
|
|
66
|
+
local eventHandlers = React.useMemo(function()
|
|
67
|
+
return {
|
|
68
|
+
Activated = handleActivated,
|
|
69
|
+
SelectionGained = handleSelectionGained,
|
|
70
|
+
InputBegan = handleInputBegan,
|
|
71
|
+
}
|
|
72
|
+
end, { handleActivated, handleInputBegan, handleSelectionGained })
|
|
73
|
+
local triggerName = React.useMemo(function()
|
|
74
|
+
return createTabsTriggerName(props.value)
|
|
75
|
+
end, { props.value })
|
|
76
|
+
if props.asChild then
|
|
77
|
+
local child = props.children
|
|
78
|
+
if not child then
|
|
79
|
+
error("[TabsTrigger] `asChild` requires a child element.")
|
|
80
|
+
end
|
|
81
|
+
return React.createElement(RovingFocusItem, {
|
|
82
|
+
asChild = true,
|
|
83
|
+
disabled = disabled,
|
|
84
|
+
}, React.createElement(Slot, {
|
|
85
|
+
Event = eventHandlers,
|
|
86
|
+
Name = triggerName,
|
|
87
|
+
ref = setTriggerRef,
|
|
88
|
+
}, child))
|
|
89
|
+
end
|
|
90
|
+
return React.createElement(RovingFocusItem, {
|
|
91
|
+
asChild = true,
|
|
92
|
+
disabled = disabled,
|
|
93
|
+
}, React.createElement("textbutton", {
|
|
94
|
+
Active = not disabled,
|
|
95
|
+
AutoButtonColor = false,
|
|
96
|
+
BackgroundColor3 = if selected then Color3.fromRGB(86, 137, 245) else Color3.fromRGB(47, 53, 68),
|
|
97
|
+
BorderSizePixel = 0,
|
|
98
|
+
Event = eventHandlers,
|
|
99
|
+
Selectable = not disabled,
|
|
100
|
+
Size = UDim2.fromOffset(132, 34),
|
|
101
|
+
Text = props.value,
|
|
102
|
+
TextColor3 = if disabled then Color3.fromRGB(136, 144, 159) else Color3.fromRGB(235, 240, 248),
|
|
103
|
+
TextSize = 15,
|
|
104
|
+
ref = setTriggerRef,
|
|
105
|
+
}, props.children))
|
|
106
|
+
end
|
|
107
|
+
return {
|
|
108
|
+
TabsTrigger = TabsTrigger,
|
|
109
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createStrictContext = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).createStrictContext
|
|
4
|
+
local _binding = createStrictContext("Tabs")
|
|
5
|
+
local TabsContextProvider = _binding[1]
|
|
6
|
+
local useTabsContext = _binding[2]
|
|
7
|
+
return {
|
|
8
|
+
TabsContextProvider = TabsContextProvider,
|
|
9
|
+
useTabsContext = useTabsContext,
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function sanitizeValue(value)
|
|
3
|
+
local lowered = string.lower(value)
|
|
4
|
+
local sanitized = (string.gsub(lowered, "[^%w_%-]", "-"))
|
|
5
|
+
return if #sanitized > 0 then sanitized else "tab"
|
|
6
|
+
end
|
|
7
|
+
local function createTabsTriggerName(value)
|
|
8
|
+
return `TabsTrigger:{sanitizeValue(value)}`
|
|
9
|
+
end
|
|
10
|
+
local function createTabsContentName(value)
|
|
11
|
+
return `TabsContent:{sanitizeValue(value)}`
|
|
12
|
+
end
|
|
13
|
+
return {
|
|
14
|
+
createTabsTriggerName = createTabsTriggerName,
|
|
15
|
+
createTabsContentName = createTabsContentName,
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type React from "@rbxts/react";
|
|
2
|
+
export type TabsOrientation = "horizontal" | "vertical";
|
|
3
|
+
export type TabsActivationMode = "automatic" | "manual";
|
|
4
|
+
export type TabsSetValue = (value: string) => void;
|
|
5
|
+
export type TabsTriggerRegistration = {
|
|
6
|
+
id: number;
|
|
7
|
+
value: string;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
ref: React.MutableRefObject<GuiObject | undefined>;
|
|
10
|
+
order: number;
|
|
11
|
+
};
|
|
12
|
+
export type TabsContextValue = {
|
|
13
|
+
value?: string;
|
|
14
|
+
setValue: TabsSetValue;
|
|
15
|
+
orientation: TabsOrientation;
|
|
16
|
+
activationMode: TabsActivationMode;
|
|
17
|
+
registerTrigger: (trigger: TabsTriggerRegistration) => () => void;
|
|
18
|
+
};
|
|
19
|
+
export type TabsProps = {
|
|
20
|
+
value?: string;
|
|
21
|
+
defaultValue?: string;
|
|
22
|
+
onValueChange?: (value: string) => void;
|
|
23
|
+
orientation?: TabsOrientation;
|
|
24
|
+
activationMode?: TabsActivationMode;
|
|
25
|
+
children?: React.ReactNode;
|
|
26
|
+
};
|
|
27
|
+
export type TabsListProps = {
|
|
28
|
+
asChild?: boolean;
|
|
29
|
+
children?: React.ReactNode;
|
|
30
|
+
};
|
|
31
|
+
export type TabsTriggerProps = {
|
|
32
|
+
value: string;
|
|
33
|
+
asChild?: boolean;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
children?: React.ReactElement;
|
|
36
|
+
};
|
|
37
|
+
export type TabsContentProps = {
|
|
38
|
+
value: string;
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
forceMount?: boolean;
|
|
41
|
+
children?: React.ReactNode;
|
|
42
|
+
};
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { TabsContent } from "./Tabs/TabsContent";
|
|
2
|
+
export { TabsList } from "./Tabs/TabsList";
|
|
3
|
+
export { TabsRoot, TabsRoot as Tabs } from "./Tabs/TabsRoot";
|
|
4
|
+
export { TabsTrigger } from "./Tabs/TabsTrigger";
|
|
5
|
+
export type { TabsActivationMode, TabsContentProps, TabsContextValue, TabsListProps, TabsOrientation, TabsProps, TabsTriggerProps, } from "./Tabs/types";
|
package/out/init.luau
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
exports.TabsContent = TS.import(script, script, "Tabs", "TabsContent").TabsContent
|
|
5
|
+
exports.TabsList = TS.import(script, script, "Tabs", "TabsList").TabsList
|
|
6
|
+
local _TabsRoot = TS.import(script, script, "Tabs", "TabsRoot")
|
|
7
|
+
exports.TabsRoot = _TabsRoot.TabsRoot
|
|
8
|
+
exports.Tabs = _TabsRoot.TabsRoot
|
|
9
|
+
exports.TabsTrigger = TS.import(script, script, "Tabs", "TabsTrigger").TabsTrigger
|
|
10
|
+
return exports
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lattice-ui/tabs",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "out/init.luau",
|
|
6
|
+
"types": "out/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@lattice-ui/core": "0.1.1",
|
|
9
|
+
"@lattice-ui/focus": "0.1.1",
|
|
10
|
+
"@lattice-ui/layer": "0.1.1"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@rbxts/react": "17.3.7-ts.1",
|
|
14
|
+
"@rbxts/react-roblox": "17.3.7-ts.1"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@rbxts/react": "^17",
|
|
18
|
+
"@rbxts/react-roblox": "^17"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rbxtsc -p tsconfig.json",
|
|
22
|
+
"watch": "rbxtsc -p tsconfig.json -w",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.typecheck.json"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { Presence } from "@lattice-ui/layer";
|
|
3
|
+
import { useTabsContext } from "./context";
|
|
4
|
+
import { createTabsContentName } from "./internals/ids";
|
|
5
|
+
import type { TabsContentProps } from "./types";
|
|
6
|
+
|
|
7
|
+
type TabsContentImplProps = {
|
|
8
|
+
visible: boolean;
|
|
9
|
+
value: string;
|
|
10
|
+
asChild?: boolean;
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function TabsContentImpl(props: TabsContentImplProps) {
|
|
15
|
+
const contentName = createTabsContentName(props.value);
|
|
16
|
+
|
|
17
|
+
if (props.asChild) {
|
|
18
|
+
const child = props.children;
|
|
19
|
+
if (!React.isValidElement(child)) {
|
|
20
|
+
error("[TabsContent] `asChild` requires a child element.");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Slot Name={contentName} Visible={props.visible}>
|
|
25
|
+
{child}
|
|
26
|
+
</Slot>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<frame BackgroundTransparency={1} BorderSizePixel={0} Size={UDim2.fromOffset(0, 0)} Visible={props.visible}>
|
|
32
|
+
{props.children}
|
|
33
|
+
</frame>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function TabsContent(props: TabsContentProps) {
|
|
38
|
+
const tabsContext = useTabsContext();
|
|
39
|
+
const selected = tabsContext.value === props.value;
|
|
40
|
+
const forceMount = props.forceMount === true;
|
|
41
|
+
|
|
42
|
+
if (!selected && !forceMount) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (forceMount) {
|
|
47
|
+
return (
|
|
48
|
+
<TabsContentImpl asChild={props.asChild} value={props.value} visible={selected}>
|
|
49
|
+
{props.children}
|
|
50
|
+
</TabsContentImpl>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Presence
|
|
56
|
+
exitFallbackMs={0}
|
|
57
|
+
present={selected}
|
|
58
|
+
render={(state) => (
|
|
59
|
+
<TabsContentImpl asChild={props.asChild} value={props.value} visible={state.isPresent}>
|
|
60
|
+
{props.children}
|
|
61
|
+
</TabsContentImpl>
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { RovingFocusGroup } from "@lattice-ui/focus";
|
|
3
|
+
import { useTabsContext } from "./context";
|
|
4
|
+
import type { TabsListProps } from "./types";
|
|
5
|
+
|
|
6
|
+
export function TabsList(props: TabsListProps) {
|
|
7
|
+
const tabsContext = useTabsContext();
|
|
8
|
+
|
|
9
|
+
const listNode = props.asChild ? (
|
|
10
|
+
(() => {
|
|
11
|
+
const child = props.children;
|
|
12
|
+
if (!React.isValidElement(child)) {
|
|
13
|
+
error("[TabsList] `asChild` requires a child element.");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <Slot>{child}</Slot>;
|
|
17
|
+
})()
|
|
18
|
+
) : (
|
|
19
|
+
<frame BackgroundTransparency={1} BorderSizePixel={0} Size={UDim2.fromOffset(0, 0)}>
|
|
20
|
+
{props.children}
|
|
21
|
+
</frame>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<RovingFocusGroup active autoFocus="none" loop orientation={tabsContext.orientation}>
|
|
26
|
+
{listNode}
|
|
27
|
+
</RovingFocusGroup>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { React, useControllableState } from "@lattice-ui/core";
|
|
2
|
+
import { TabsContextProvider } from "./context";
|
|
3
|
+
import type { TabsProps, TabsTriggerRegistration } from "./types";
|
|
4
|
+
|
|
5
|
+
function getOrderedTriggers(triggers: Array<TabsTriggerRegistration>) {
|
|
6
|
+
const ordered = [...triggers];
|
|
7
|
+
ordered.sort((a, b) => a.order < b.order);
|
|
8
|
+
return ordered;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function resolveNextValue(
|
|
12
|
+
currentValue: string | undefined,
|
|
13
|
+
orderedTriggers: Array<TabsTriggerRegistration>,
|
|
14
|
+
fallbackOrder: number | undefined,
|
|
15
|
+
) {
|
|
16
|
+
const enabled = orderedTriggers.filter((trigger) => !trigger.disabled);
|
|
17
|
+
if (enabled.size() === 0) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (currentValue === undefined) {
|
|
22
|
+
return enabled[0]?.value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const selectedEnabled = enabled.find((trigger) => trigger.value === currentValue);
|
|
26
|
+
if (selectedEnabled) {
|
|
27
|
+
return selectedEnabled.value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const selected = orderedTriggers.find((trigger) => trigger.value === currentValue);
|
|
31
|
+
const anchorOrder = selected?.order ?? fallbackOrder;
|
|
32
|
+
if (anchorOrder !== undefined) {
|
|
33
|
+
const after = enabled.find((trigger) => trigger.order > anchorOrder);
|
|
34
|
+
if (after) {
|
|
35
|
+
return after.value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return enabled[0]?.value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function TabsRoot(props: TabsProps) {
|
|
43
|
+
const [value, setValueState] = useControllableState<string | undefined>({
|
|
44
|
+
value: props.value,
|
|
45
|
+
defaultValue: props.defaultValue,
|
|
46
|
+
onChange: (nextValue) => {
|
|
47
|
+
if (nextValue !== undefined) {
|
|
48
|
+
props.onValueChange?.(nextValue);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const orientation = props.orientation ?? "horizontal";
|
|
54
|
+
const activationMode = props.activationMode ?? "automatic";
|
|
55
|
+
|
|
56
|
+
const triggerRegistryRef = React.useRef<Array<TabsTriggerRegistration>>([]);
|
|
57
|
+
const lastSelectedOrderRef = React.useRef<number>();
|
|
58
|
+
const [registryRevision, setRegistryRevision] = React.useState(0);
|
|
59
|
+
|
|
60
|
+
const registerTrigger = React.useCallback((trigger: TabsTriggerRegistration) => {
|
|
61
|
+
triggerRegistryRef.current.push(trigger);
|
|
62
|
+
setRegistryRevision((revision) => revision + 1);
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
const index = triggerRegistryRef.current.findIndex((entry) => entry.id === trigger.id);
|
|
66
|
+
if (index >= 0) {
|
|
67
|
+
triggerRegistryRef.current.remove(index);
|
|
68
|
+
setRegistryRevision((revision) => revision + 1);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const setValue = React.useCallback(
|
|
74
|
+
(nextValue: string) => {
|
|
75
|
+
const orderedTriggers = getOrderedTriggers(triggerRegistryRef.current);
|
|
76
|
+
const selected = orderedTriggers.find((trigger) => trigger.value === nextValue && !trigger.disabled);
|
|
77
|
+
if (selected) {
|
|
78
|
+
lastSelectedOrderRef.current = selected.order;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setValueState(nextValue);
|
|
82
|
+
},
|
|
83
|
+
[setValueState],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
React.useEffect(() => {
|
|
87
|
+
const orderedTriggers = getOrderedTriggers(triggerRegistryRef.current);
|
|
88
|
+
const selected = orderedTriggers.find((trigger) => trigger.value === value && !trigger.disabled);
|
|
89
|
+
if (selected) {
|
|
90
|
+
lastSelectedOrderRef.current = selected.order;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const nextValue = resolveNextValue(value, orderedTriggers, lastSelectedOrderRef.current);
|
|
94
|
+
if (nextValue !== value) {
|
|
95
|
+
setValueState(nextValue);
|
|
96
|
+
}
|
|
97
|
+
}, [registryRevision, setValueState, value]);
|
|
98
|
+
|
|
99
|
+
const contextValue = React.useMemo(
|
|
100
|
+
() => ({
|
|
101
|
+
value,
|
|
102
|
+
setValue,
|
|
103
|
+
orientation,
|
|
104
|
+
activationMode,
|
|
105
|
+
registerTrigger,
|
|
106
|
+
}),
|
|
107
|
+
[activationMode, orientation, registerTrigger, setValue, value],
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return <TabsContextProvider value={contextValue}>{props.children}</TabsContextProvider>;
|
|
111
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { RovingFocusItem } from "@lattice-ui/focus";
|
|
3
|
+
import { useTabsContext } from "./context";
|
|
4
|
+
import { createTabsTriggerName } from "./internals/ids";
|
|
5
|
+
import type { TabsTriggerProps } from "./types";
|
|
6
|
+
|
|
7
|
+
let nextTriggerId = 0;
|
|
8
|
+
let nextTriggerOrder = 0;
|
|
9
|
+
|
|
10
|
+
function toGuiObject(instance: Instance | undefined) {
|
|
11
|
+
if (!instance || !instance.IsA("GuiObject")) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return instance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function TabsTrigger(props: TabsTriggerProps) {
|
|
19
|
+
const tabsContext = useTabsContext();
|
|
20
|
+
const triggerRef = React.useRef<GuiObject>();
|
|
21
|
+
const selected = tabsContext.value === props.value;
|
|
22
|
+
const disabled = props.disabled === true;
|
|
23
|
+
|
|
24
|
+
const triggerIdRef = React.useRef(0);
|
|
25
|
+
if (triggerIdRef.current === 0) {
|
|
26
|
+
nextTriggerId += 1;
|
|
27
|
+
triggerIdRef.current = nextTriggerId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const triggerOrderRef = React.useRef(0);
|
|
31
|
+
if (triggerOrderRef.current === 0) {
|
|
32
|
+
nextTriggerOrder += 1;
|
|
33
|
+
triggerOrderRef.current = nextTriggerOrder;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
return tabsContext.registerTrigger({
|
|
38
|
+
id: triggerIdRef.current,
|
|
39
|
+
value: props.value,
|
|
40
|
+
disabled,
|
|
41
|
+
ref: triggerRef,
|
|
42
|
+
order: triggerOrderRef.current,
|
|
43
|
+
});
|
|
44
|
+
}, [disabled, props.value, tabsContext]);
|
|
45
|
+
|
|
46
|
+
const setTriggerRef = React.useCallback((instance: Instance | undefined) => {
|
|
47
|
+
triggerRef.current = toGuiObject(instance);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const handleActivated = React.useCallback(() => {
|
|
51
|
+
if (disabled) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
tabsContext.setValue(props.value);
|
|
56
|
+
}, [disabled, props.value, tabsContext]);
|
|
57
|
+
|
|
58
|
+
const handleSelectionGained = React.useCallback(() => {
|
|
59
|
+
if (disabled || tabsContext.activationMode !== "automatic") {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
tabsContext.setValue(props.value);
|
|
64
|
+
}, [disabled, props.value, tabsContext]);
|
|
65
|
+
|
|
66
|
+
const handleInputBegan = React.useCallback(
|
|
67
|
+
(_rbx: TextButton, inputObject: InputObject) => {
|
|
68
|
+
if (disabled || tabsContext.activationMode !== "manual") {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const keyCode = inputObject.KeyCode;
|
|
73
|
+
if (keyCode !== Enum.KeyCode.Return && keyCode !== Enum.KeyCode.Space) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
tabsContext.setValue(props.value);
|
|
78
|
+
},
|
|
79
|
+
[disabled, props.value, tabsContext],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const eventHandlers = React.useMemo(
|
|
83
|
+
() => ({
|
|
84
|
+
Activated: handleActivated,
|
|
85
|
+
SelectionGained: handleSelectionGained,
|
|
86
|
+
InputBegan: handleInputBegan,
|
|
87
|
+
}),
|
|
88
|
+
[handleActivated, handleInputBegan, handleSelectionGained],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const triggerName = React.useMemo(() => createTabsTriggerName(props.value), [props.value]);
|
|
92
|
+
|
|
93
|
+
if (props.asChild) {
|
|
94
|
+
const child = props.children;
|
|
95
|
+
if (!child) {
|
|
96
|
+
error("[TabsTrigger] `asChild` requires a child element.");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<RovingFocusItem asChild disabled={disabled}>
|
|
101
|
+
<Slot Event={eventHandlers} Name={triggerName} ref={setTriggerRef}>
|
|
102
|
+
{child}
|
|
103
|
+
</Slot>
|
|
104
|
+
</RovingFocusItem>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<RovingFocusItem asChild disabled={disabled}>
|
|
110
|
+
<textbutton
|
|
111
|
+
Active={!disabled}
|
|
112
|
+
AutoButtonColor={false}
|
|
113
|
+
BackgroundColor3={selected ? Color3.fromRGB(86, 137, 245) : Color3.fromRGB(47, 53, 68)}
|
|
114
|
+
BorderSizePixel={0}
|
|
115
|
+
Event={eventHandlers}
|
|
116
|
+
Selectable={!disabled}
|
|
117
|
+
Size={UDim2.fromOffset(132, 34)}
|
|
118
|
+
Text={props.value}
|
|
119
|
+
TextColor3={disabled ? Color3.fromRGB(136, 144, 159) : Color3.fromRGB(235, 240, 248)}
|
|
120
|
+
TextSize={15}
|
|
121
|
+
ref={setTriggerRef}
|
|
122
|
+
>
|
|
123
|
+
{props.children}
|
|
124
|
+
</textbutton>
|
|
125
|
+
</RovingFocusItem>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function sanitizeValue(value: string) {
|
|
2
|
+
const lowered = string.lower(value);
|
|
3
|
+
const sanitized = string.gsub(lowered, "[^%w_%-]", "-")[0];
|
|
4
|
+
return sanitized.size() > 0 ? sanitized : "tab";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createTabsTriggerName(value: string) {
|
|
8
|
+
return `TabsTrigger:${sanitizeValue(value)}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createTabsContentName(value: string) {
|
|
12
|
+
return `TabsContent:${sanitizeValue(value)}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type React from "@rbxts/react";
|
|
2
|
+
|
|
3
|
+
export type TabsOrientation = "horizontal" | "vertical";
|
|
4
|
+
export type TabsActivationMode = "automatic" | "manual";
|
|
5
|
+
|
|
6
|
+
export type TabsSetValue = (value: string) => void;
|
|
7
|
+
|
|
8
|
+
export type TabsTriggerRegistration = {
|
|
9
|
+
id: number;
|
|
10
|
+
value: string;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
ref: React.MutableRefObject<GuiObject | undefined>;
|
|
13
|
+
order: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type TabsContextValue = {
|
|
17
|
+
value?: string;
|
|
18
|
+
setValue: TabsSetValue;
|
|
19
|
+
orientation: TabsOrientation;
|
|
20
|
+
activationMode: TabsActivationMode;
|
|
21
|
+
registerTrigger: (trigger: TabsTriggerRegistration) => () => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type TabsProps = {
|
|
25
|
+
value?: string;
|
|
26
|
+
defaultValue?: string;
|
|
27
|
+
onValueChange?: (value: string) => void;
|
|
28
|
+
orientation?: TabsOrientation;
|
|
29
|
+
activationMode?: TabsActivationMode;
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type TabsListProps = {
|
|
34
|
+
asChild?: boolean;
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type TabsTriggerProps = {
|
|
39
|
+
value: string;
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
children?: React.ReactElement;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type TabsContentProps = {
|
|
46
|
+
value: string;
|
|
47
|
+
asChild?: boolean;
|
|
48
|
+
forceMount?: boolean;
|
|
49
|
+
children?: React.ReactNode;
|
|
50
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { TabsContent } from "./Tabs/TabsContent";
|
|
2
|
+
export { TabsList } from "./Tabs/TabsList";
|
|
3
|
+
export { TabsRoot, TabsRoot as Tabs } from "./Tabs/TabsRoot";
|
|
4
|
+
export { TabsTrigger } from "./Tabs/TabsTrigger";
|
|
5
|
+
export type {
|
|
6
|
+
TabsActivationMode,
|
|
7
|
+
TabsContentProps,
|
|
8
|
+
TabsContextValue,
|
|
9
|
+
TabsListProps,
|
|
10
|
+
TabsOrientation,
|
|
11
|
+
TabsProps,
|
|
12
|
+
TabsTriggerProps,
|
|
13
|
+
} from "./Tabs/types";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "out",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"typeRoots": [
|
|
8
|
+
"./node_modules/@rbxts",
|
|
9
|
+
"../../node_modules/@rbxts",
|
|
10
|
+
"./node_modules/@lattice-ui",
|
|
11
|
+
"../../node_modules/@lattice-ui"
|
|
12
|
+
],
|
|
13
|
+
"types": ["types", "compiler-types"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"baseUrl": "..",
|
|
6
|
+
"rootDir": "..",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@lattice-ui/checkbox": ["checkbox/src/index.ts"],
|
|
9
|
+
"@lattice-ui/core": ["core/src/index.ts"],
|
|
10
|
+
"@lattice-ui/dialog": ["dialog/src/index.ts"],
|
|
11
|
+
"@lattice-ui/focus": ["focus/src/index.ts"],
|
|
12
|
+
"@lattice-ui/layer": ["layer/src/index.ts"],
|
|
13
|
+
"@lattice-ui/menu": ["menu/src/index.ts"],
|
|
14
|
+
"@lattice-ui/popover": ["popover/src/index.ts"],
|
|
15
|
+
"@lattice-ui/popper": ["popper/src/index.ts"],
|
|
16
|
+
"@lattice-ui/radio-group": ["radio-group/src/index.ts"],
|
|
17
|
+
"@lattice-ui/style": ["style/src/index.ts"],
|
|
18
|
+
"@lattice-ui/switch": ["switch/src/index.ts"],
|
|
19
|
+
"@lattice-ui/system": ["system/src/index.ts"],
|
|
20
|
+
"@lattice-ui/tabs": ["tabs/src/index.ts"],
|
|
21
|
+
"@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
|
|
22
|
+
"@lattice-ui/tooltip": ["tooltip/src/index.ts"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|