@lattice-ui/slider 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @lattice-ui/slider
2
+
3
+ Headless single-thumb slider primitives for Roblox UI.
4
+
5
+ ## Exports
6
+
7
+ - `Slider`
8
+ - `Slider.Root`
9
+ - `Slider.Track`
10
+ - `Slider.Range`
11
+ - `Slider.Thumb`
12
+
13
+ ## Notes
14
+
15
+ - Single-thumb only in this release.
16
+ - Supports controlled/uncontrolled `value`.
17
+ - Pointer drag and keyboard adjustments are both supported.
18
+ - Values are clamped to `[min, max]` and snapped to `step`.
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { SliderRangeProps } from "./types";
3
+ export declare function SliderRange(props: SliderRangeProps): React.JSX.Element;
@@ -0,0 +1,33 @@
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 useSliderContext = TS.import(script, script.Parent, "context").useSliderContext
7
+ local valueToPercent = TS.import(script, script.Parent, "internals", "math").valueToPercent
8
+ local function SliderRange(props)
9
+ local sliderContext = useSliderContext()
10
+ local percent = valueToPercent(sliderContext.value, sliderContext.min, sliderContext.max)
11
+ local rangeSize = if sliderContext.orientation == "horizontal" then UDim2.fromScale(percent, 1) else UDim2.fromScale(1, percent)
12
+ local rangePosition = if sliderContext.orientation == "horizontal" then UDim2.fromScale(0, 0) else UDim2.fromScale(0, 1 - percent)
13
+ if props.asChild then
14
+ local child = props.children
15
+ if not child then
16
+ error("[SliderRange] `asChild` requires a child element.")
17
+ end
18
+ return React.createElement(Slot, {
19
+ Name = "SliderRange",
20
+ Position = rangePosition,
21
+ Size = rangeSize,
22
+ }, child)
23
+ end
24
+ return React.createElement("frame", {
25
+ BackgroundColor3 = Color3.fromRGB(86, 142, 255),
26
+ BorderSizePixel = 0,
27
+ Position = rangePosition,
28
+ Size = rangeSize,
29
+ }, props.children)
30
+ end
31
+ return {
32
+ SliderRange = SliderRange,
33
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { SliderProps } from "./types";
3
+ export declare function SliderRoot(props: SliderProps): React.JSX.Element;
@@ -0,0 +1,203 @@
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 SliderContextProvider = TS.import(script, script.Parent, "context").SliderContextProvider
7
+ local _math = TS.import(script, script.Parent, "internals", "math")
8
+ local normalizeBounds = _math.normalizeBounds
9
+ local normalizeStep = _math.normalizeStep
10
+ local pointerPositionToValue = _math.pointerPositionToValue
11
+ local snapValueToStep = _math.snapValueToStep
12
+ local UserInputService = game:GetService("UserInputService")
13
+ local function toGuiObject(instance)
14
+ if not instance or not instance:IsA("GuiObject") then
15
+ return nil
16
+ end
17
+ return instance
18
+ end
19
+ local function isPointerStartInput(inputObject)
20
+ return inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch
21
+ end
22
+ local function SliderRoot(props)
23
+ local _condition = props.min
24
+ if _condition == nil then
25
+ _condition = 0
26
+ end
27
+ local _condition_1 = props.max
28
+ if _condition_1 == nil then
29
+ _condition_1 = 100
30
+ end
31
+ local bounds = normalizeBounds(_condition, _condition_1)
32
+ local min = bounds.min
33
+ local max = bounds.max
34
+ local _condition_2 = props.step
35
+ if _condition_2 == nil then
36
+ _condition_2 = 1
37
+ end
38
+ local step = normalizeStep(_condition_2)
39
+ local orientation = props.orientation or "horizontal"
40
+ local disabled = props.disabled == true
41
+ local _condition_3 = props.defaultValue
42
+ if _condition_3 == nil then
43
+ _condition_3 = min
44
+ end
45
+ local defaultValue = snapValueToStep(_condition_3, min, max, step)
46
+ local _binding = useControllableState({
47
+ value = props.value,
48
+ defaultValue = defaultValue,
49
+ onChange = props.onValueChange,
50
+ })
51
+ local valueState = _binding[1]
52
+ local setValueState = _binding[2]
53
+ local value = snapValueToStep(valueState, min, max, step)
54
+ local trackRef = React.useRef()
55
+ local thumbRef = React.useRef()
56
+ local latestValueRef = React.useRef(value)
57
+ React.useEffect(function()
58
+ latestValueRef.current = value
59
+ end, { value })
60
+ local setValue = React.useCallback(function(nextValue)
61
+ if disabled then
62
+ return nil
63
+ end
64
+ local normalizedValue = snapValueToStep(nextValue, min, max, step)
65
+ latestValueRef.current = normalizedValue
66
+ setValueState(normalizedValue)
67
+ end, { disabled, max, min, setValueState, step })
68
+ local commitValue = React.useCallback(function(nextValue)
69
+ if disabled then
70
+ return nil
71
+ end
72
+ local normalizedValue = snapValueToStep(nextValue, min, max, step)
73
+ local _result = props.onValueCommit
74
+ if _result ~= nil then
75
+ _result(normalizedValue)
76
+ end
77
+ end, { disabled, max, min, props.onValueCommit, step })
78
+ local activeDragInputRef = React.useRef()
79
+ local moveConnectionRef = React.useRef()
80
+ local endConnectionRef = React.useRef()
81
+ local disconnectDragging = React.useCallback(function()
82
+ local _result = moveConnectionRef.current
83
+ if _result ~= nil then
84
+ _result:Disconnect()
85
+ end
86
+ moveConnectionRef.current = nil
87
+ local _result_1 = endConnectionRef.current
88
+ if _result_1 ~= nil then
89
+ _result_1:Disconnect()
90
+ end
91
+ endConnectionRef.current = nil
92
+ activeDragInputRef.current = nil
93
+ end, {})
94
+ local updateValueFromInput = React.useCallback(function(inputObject)
95
+ local trackNode = trackRef.current
96
+ if not trackNode then
97
+ return nil
98
+ end
99
+ local pointerPosition = Vector2.new(inputObject.Position.X, inputObject.Position.Y)
100
+ local nextValue = pointerPositionToValue(pointerPosition, trackNode.AbsolutePosition, trackNode.AbsoluteSize, min, max, step, orientation)
101
+ setValue(nextValue)
102
+ return nextValue
103
+ end, { max, min, orientation, setValue, step })
104
+ local finishDrag = React.useCallback(function(inputObject)
105
+ local activeDragInput = activeDragInputRef.current
106
+ if not activeDragInput then
107
+ return nil
108
+ end
109
+ if inputObject then
110
+ local nextValue = updateValueFromInput(inputObject)
111
+ if nextValue ~= nil then
112
+ latestValueRef.current = nextValue
113
+ end
114
+ end
115
+ commitValue(latestValueRef.current)
116
+ disconnectDragging()
117
+ end, { commitValue, disconnectDragging, updateValueFromInput })
118
+ local startDrag = React.useCallback(function(inputObject)
119
+ if disabled or not isPointerStartInput(inputObject) then
120
+ return nil
121
+ end
122
+ activeDragInputRef.current = inputObject
123
+ local initialValue = updateValueFromInput(inputObject)
124
+ if initialValue ~= nil then
125
+ latestValueRef.current = initialValue
126
+ end
127
+ local _result = moveConnectionRef.current
128
+ if _result ~= nil then
129
+ _result:Disconnect()
130
+ end
131
+ local _result_1 = endConnectionRef.current
132
+ if _result_1 ~= nil then
133
+ _result_1:Disconnect()
134
+ end
135
+ moveConnectionRef.current = UserInputService.InputChanged:Connect(function(changedInput)
136
+ local activeDragInput = activeDragInputRef.current
137
+ if not activeDragInput then
138
+ return nil
139
+ end
140
+ if activeDragInput.UserInputType == Enum.UserInputType.Touch then
141
+ if changedInput.UserInputType ~= Enum.UserInputType.Touch or changedInput ~= activeDragInput then
142
+ return nil
143
+ end
144
+ local touchValue = updateValueFromInput(changedInput)
145
+ if touchValue ~= nil then
146
+ latestValueRef.current = touchValue
147
+ end
148
+ return nil
149
+ end
150
+ if changedInput.UserInputType ~= Enum.UserInputType.MouseMovement then
151
+ return nil
152
+ end
153
+ local mouseValue = updateValueFromInput(changedInput)
154
+ if mouseValue ~= nil then
155
+ latestValueRef.current = mouseValue
156
+ end
157
+ end)
158
+ endConnectionRef.current = UserInputService.InputEnded:Connect(function(endedInput)
159
+ local activeDragInput = activeDragInputRef.current
160
+ if not activeDragInput then
161
+ return nil
162
+ end
163
+ local endedTouch = activeDragInput.UserInputType == Enum.UserInputType.Touch and endedInput == activeDragInput
164
+ local endedMouse = activeDragInput.UserInputType == Enum.UserInputType.MouseButton1 and endedInput.UserInputType == Enum.UserInputType.MouseButton1
165
+ if not endedTouch and not endedMouse then
166
+ return nil
167
+ end
168
+ finishDrag(if endedTouch then endedInput else nil)
169
+ end)
170
+ end, { disabled, finishDrag, updateValueFromInput })
171
+ React.useEffect(function()
172
+ return function()
173
+ disconnectDragging()
174
+ end
175
+ end, { disconnectDragging })
176
+ local setTrackNode = React.useCallback(function(instance)
177
+ trackRef.current = toGuiObject(instance)
178
+ end, {})
179
+ local setThumbNode = React.useCallback(function(instance)
180
+ thumbRef.current = toGuiObject(instance)
181
+ end, {})
182
+ local contextValue = React.useMemo(function()
183
+ return {
184
+ value = value,
185
+ setValue = setValue,
186
+ commitValue = commitValue,
187
+ min = min,
188
+ max = max,
189
+ step = step,
190
+ orientation = orientation,
191
+ disabled = disabled,
192
+ setTrackNode = setTrackNode,
193
+ setThumbNode = setThumbNode,
194
+ startDrag = startDrag,
195
+ }
196
+ end, { commitValue, disabled, max, min, orientation, setThumbNode, setTrackNode, setValue, startDrag, step, value })
197
+ return React.createElement(SliderContextProvider, {
198
+ value = contextValue,
199
+ }, props.children)
200
+ end
201
+ return {
202
+ SliderRoot = SliderRoot,
203
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { SliderThumbProps } from "./types";
3
+ export declare function SliderThumb(props: SliderThumbProps): React.JSX.Element;
@@ -0,0 +1,81 @@
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 useSliderContext = TS.import(script, script.Parent, "context").useSliderContext
7
+ local valueToPercent = TS.import(script, script.Parent, "internals", "math").valueToPercent
8
+ local function isPointerStartInput(inputObject)
9
+ return inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch
10
+ end
11
+ local function SliderThumb(props)
12
+ local sliderContext = useSliderContext()
13
+ local percent = valueToPercent(sliderContext.value, sliderContext.min, sliderContext.max)
14
+ local position = if sliderContext.orientation == "horizontal" then UDim2.fromScale(percent, 0.5) else UDim2.fromScale(0.5, 1 - percent)
15
+ local handleInputBegan = React.useCallback(function(_rbx, inputObject)
16
+ if isPointerStartInput(inputObject) then
17
+ sliderContext.startDrag(inputObject)
18
+ return nil
19
+ end
20
+ if sliderContext.disabled then
21
+ return nil
22
+ end
23
+ local keyCode = inputObject.KeyCode
24
+ local nextValue
25
+ local pageStep = sliderContext.step * 10
26
+ if keyCode == Enum.KeyCode.Home then
27
+ nextValue = sliderContext.min
28
+ elseif keyCode == Enum.KeyCode.End then
29
+ nextValue = sliderContext.max
30
+ elseif keyCode == Enum.KeyCode.PageUp then
31
+ nextValue = sliderContext.value + pageStep
32
+ elseif keyCode == Enum.KeyCode.PageDown then
33
+ nextValue = sliderContext.value - pageStep
34
+ elseif keyCode == Enum.KeyCode.Right or keyCode == Enum.KeyCode.Up then
35
+ nextValue = sliderContext.value + sliderContext.step
36
+ elseif keyCode == Enum.KeyCode.Left or keyCode == Enum.KeyCode.Down then
37
+ nextValue = sliderContext.value - sliderContext.step
38
+ elseif keyCode == Enum.KeyCode.Return or keyCode == Enum.KeyCode.Space then
39
+ sliderContext.commitValue(sliderContext.value)
40
+ return nil
41
+ else
42
+ return nil
43
+ end
44
+ if nextValue == nil then
45
+ return nil
46
+ end
47
+ sliderContext.setValue(nextValue)
48
+ sliderContext.commitValue(nextValue)
49
+ end, { sliderContext })
50
+ local sharedProps = {
51
+ Active = not sliderContext.disabled,
52
+ AnchorPoint = Vector2.new(0.5, 0.5),
53
+ Name = "SliderThumb",
54
+ Position = position,
55
+ Selectable = not sliderContext.disabled,
56
+ Event = {
57
+ InputBegan = handleInputBegan,
58
+ },
59
+ ref = sliderContext.setThumbNode,
60
+ }
61
+ if props.asChild then
62
+ local child = props.children
63
+ if not child then
64
+ error("[SliderThumb] `asChild` requires a child element.")
65
+ end
66
+ local _attributes = table.clone(sharedProps)
67
+ setmetatable(_attributes, nil)
68
+ return React.createElement(Slot, _attributes, child)
69
+ end
70
+ local _attributes = table.clone(sharedProps)
71
+ setmetatable(_attributes, nil)
72
+ _attributes.AutoButtonColor = false
73
+ _attributes.BackgroundColor3 = Color3.fromRGB(235, 241, 250)
74
+ _attributes.BorderSizePixel = 0
75
+ _attributes.Size = UDim2.fromOffset(16, 16)
76
+ _attributes.Text = ""
77
+ return React.createElement("textbutton", _attributes, props.children)
78
+ end
79
+ return {
80
+ SliderThumb = SliderThumb,
81
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { SliderTrackProps } from "./types";
3
+ export declare function SliderTrack(props: SliderTrackProps): React.JSX.Element;
@@ -0,0 +1,45 @@
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 useSliderContext = TS.import(script, script.Parent, "context").useSliderContext
7
+ local function isPointerStartInput(inputObject)
8
+ return inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch
9
+ end
10
+ local function SliderTrack(props)
11
+ local sliderContext = useSliderContext()
12
+ local handleInputBegan = React.useCallback(function(_rbx, inputObject)
13
+ if not isPointerStartInput(inputObject) then
14
+ return nil
15
+ end
16
+ sliderContext.startDrag(inputObject)
17
+ end, { sliderContext })
18
+ local sharedProps = {
19
+ Active = not sliderContext.disabled,
20
+ Name = "SliderTrack",
21
+ Selectable = not sliderContext.disabled,
22
+ Event = {
23
+ InputBegan = handleInputBegan,
24
+ },
25
+ ref = sliderContext.setTrackNode,
26
+ }
27
+ if props.asChild then
28
+ local child = props.children
29
+ if not child then
30
+ error("[SliderTrack] `asChild` requires a child element.")
31
+ end
32
+ local _attributes = table.clone(sharedProps)
33
+ setmetatable(_attributes, nil)
34
+ return React.createElement(Slot, _attributes, child)
35
+ end
36
+ local _attributes = table.clone(sharedProps)
37
+ setmetatable(_attributes, nil)
38
+ _attributes.BackgroundColor3 = Color3.fromRGB(47, 53, 68)
39
+ _attributes.BorderSizePixel = 0
40
+ _attributes.Size = if sliderContext.orientation == "horizontal" then UDim2.fromOffset(260, 10) else UDim2.fromOffset(10, 220)
41
+ return React.createElement("frame", _attributes, props.children)
42
+ end
43
+ return {
44
+ SliderTrack = SliderTrack,
45
+ }
@@ -0,0 +1,3 @@
1
+ import type { SliderContextValue } from "./types";
2
+ declare const SliderContextProvider: import("@rbxts/react").Provider<SliderContextValue | undefined>, useSliderContext: () => SliderContextValue;
3
+ export { SliderContextProvider, useSliderContext };
@@ -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("Slider")
5
+ local SliderContextProvider = _binding[1]
6
+ local useSliderContext = _binding[2]
7
+ return {
8
+ SliderContextProvider = SliderContextProvider,
9
+ useSliderContext = useSliderContext,
10
+ }
@@ -0,0 +1,12 @@
1
+ import type { SliderOrientation } from "../types";
2
+ export declare function clampNumber(value: number, min: number, max: number): number;
3
+ export declare function normalizeBounds(min: number, max: number): {
4
+ min: number;
5
+ max: number;
6
+ };
7
+ export declare function normalizeStep(step: number): number;
8
+ export declare function snapValueToStep(value: number, min: number, max: number, step: number): number;
9
+ export declare function valueToPercent(value: number, min: number, max: number): number;
10
+ export declare function percentToValue(percent: number, min: number, max: number, step: number): number;
11
+ export declare function pointerPositionToPercent(pointerPosition: Vector2, trackPosition: Vector2, trackSize: Vector2, orientation: SliderOrientation): number;
12
+ export declare function pointerPositionToValue(pointerPosition: Vector2, trackPosition: Vector2, trackSize: Vector2, min: number, max: number, step: number, orientation: SliderOrientation): number;
@@ -0,0 +1,64 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local function clampNumber(value, min, max)
3
+ return math.clamp(value, min, max)
4
+ end
5
+ local function normalizeBounds(min, max)
6
+ if min <= max then
7
+ return {
8
+ min = min,
9
+ max = max,
10
+ }
11
+ end
12
+ return {
13
+ min = max,
14
+ max = min,
15
+ }
16
+ end
17
+ local function normalizeStep(step)
18
+ return if step > 0 then step else 1
19
+ end
20
+ local function snapValueToStep(value, min, max, step)
21
+ local clamped = clampNumber(value, min, max)
22
+ local stepCount = math.round((clamped - min) / step)
23
+ local snapped = min + stepCount * step
24
+ return clampNumber(snapped, min, max)
25
+ end
26
+ local function valueToPercent(value, min, max)
27
+ if max <= min then
28
+ return 0
29
+ end
30
+ return clampNumber((value - min) / (max - min), 0, 1)
31
+ end
32
+ local function percentToValue(percent, min, max, step)
33
+ local clampedPercent = clampNumber(percent, 0, 1)
34
+ local rawValue = min + (max - min) * clampedPercent
35
+ return snapValueToStep(rawValue, min, max, step)
36
+ end
37
+ local function pointerPositionToPercent(pointerPosition, trackPosition, trackSize, orientation)
38
+ if orientation == "horizontal" then
39
+ if trackSize.X <= 0 then
40
+ return 0
41
+ end
42
+ local percent = (pointerPosition.X - trackPosition.X) / trackSize.X
43
+ return clampNumber(percent, 0, 1)
44
+ end
45
+ if trackSize.Y <= 0 then
46
+ return 0
47
+ end
48
+ local percent = 1 - (pointerPosition.Y - trackPosition.Y) / trackSize.Y
49
+ return clampNumber(percent, 0, 1)
50
+ end
51
+ local function pointerPositionToValue(pointerPosition, trackPosition, trackSize, min, max, step, orientation)
52
+ local percent = pointerPositionToPercent(pointerPosition, trackPosition, trackSize, orientation)
53
+ return percentToValue(percent, min, max, step)
54
+ end
55
+ return {
56
+ clampNumber = clampNumber,
57
+ normalizeBounds = normalizeBounds,
58
+ normalizeStep = normalizeStep,
59
+ snapValueToStep = snapValueToStep,
60
+ valueToPercent = valueToPercent,
61
+ percentToValue = percentToValue,
62
+ pointerPositionToPercent = pointerPositionToPercent,
63
+ pointerPositionToValue = pointerPositionToValue,
64
+ }
@@ -0,0 +1,41 @@
1
+ import type React from "@rbxts/react";
2
+ export type SliderOrientation = "horizontal" | "vertical";
3
+ export type SliderSetValue = (value: number) => void;
4
+ export type SliderCommitValue = (value: number) => void;
5
+ export type SliderContextValue = {
6
+ value: number;
7
+ setValue: SliderSetValue;
8
+ commitValue: SliderCommitValue;
9
+ min: number;
10
+ max: number;
11
+ step: number;
12
+ orientation: SliderOrientation;
13
+ disabled: boolean;
14
+ setTrackNode: (instance: Instance | undefined) => void;
15
+ setThumbNode: (instance: Instance | undefined) => void;
16
+ startDrag: (inputObject: InputObject) => void;
17
+ };
18
+ export type SliderProps = {
19
+ value?: number;
20
+ defaultValue?: number;
21
+ onValueChange?: (value: number) => void;
22
+ onValueCommit?: (value: number) => void;
23
+ min?: number;
24
+ max?: number;
25
+ step?: number;
26
+ orientation?: SliderOrientation;
27
+ disabled?: boolean;
28
+ children?: React.ReactNode;
29
+ };
30
+ export type SliderTrackProps = {
31
+ asChild?: boolean;
32
+ children?: React.ReactElement;
33
+ };
34
+ export type SliderRangeProps = {
35
+ asChild?: boolean;
36
+ children?: React.ReactElement;
37
+ };
38
+ export type SliderThumbProps = {
39
+ asChild?: boolean;
40
+ children?: React.ReactElement;
41
+ };
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
package/out/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { SliderRange } from "./Slider/SliderRange";
2
+ import { SliderRoot } from "./Slider/SliderRoot";
3
+ import { SliderThumb } from "./Slider/SliderThumb";
4
+ import { SliderTrack } from "./Slider/SliderTrack";
5
+ export declare const Slider: {
6
+ readonly Root: typeof SliderRoot;
7
+ readonly Track: typeof SliderTrack;
8
+ readonly Range: typeof SliderRange;
9
+ readonly Thumb: typeof SliderThumb;
10
+ };
11
+ export type { SliderCommitValue, SliderContextValue, SliderOrientation, SliderProps, SliderRangeProps, SliderSetValue, SliderThumbProps, SliderTrackProps, } from "./Slider/types";
package/out/init.luau ADDED
@@ -0,0 +1,15 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local SliderRange = TS.import(script, script, "Slider", "SliderRange").SliderRange
4
+ local SliderRoot = TS.import(script, script, "Slider", "SliderRoot").SliderRoot
5
+ local SliderThumb = TS.import(script, script, "Slider", "SliderThumb").SliderThumb
6
+ local SliderTrack = TS.import(script, script, "Slider", "SliderTrack").SliderTrack
7
+ local Slider = {
8
+ Root = SliderRoot,
9
+ Track = SliderTrack,
10
+ Range = SliderRange,
11
+ Thumb = SliderThumb,
12
+ }
13
+ return {
14
+ Slider = Slider,
15
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@lattice-ui/slider",
3
+ "version": "0.3.0",
4
+ "private": false,
5
+ "main": "out/init.luau",
6
+ "types": "out/index.d.ts",
7
+ "dependencies": {
8
+ "@lattice-ui/core": "0.3.0",
9
+ "@lattice-ui/focus": "0.3.0"
10
+ },
11
+ "devDependencies": {
12
+ "@rbxts/react": "17.3.7-ts.1",
13
+ "@rbxts/react-roblox": "17.3.7-ts.1"
14
+ },
15
+ "peerDependencies": {
16
+ "@rbxts/react": "^17",
17
+ "@rbxts/react-roblox": "^17"
18
+ },
19
+ "scripts": {
20
+ "build": "rbxtsc -p tsconfig.json",
21
+ "typecheck": "tsc -p tsconfig.typecheck.json",
22
+ "watch": "rbxtsc -p tsconfig.json -w"
23
+ }
24
+ }
@@ -0,0 +1,38 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useSliderContext } from "./context";
3
+ import { valueToPercent } from "./internals/math";
4
+ import type { SliderRangeProps } from "./types";
5
+
6
+ export function SliderRange(props: SliderRangeProps) {
7
+ const sliderContext = useSliderContext();
8
+ const percent = valueToPercent(sliderContext.value, sliderContext.min, sliderContext.max);
9
+
10
+ const rangeSize =
11
+ sliderContext.orientation === "horizontal" ? UDim2.fromScale(percent, 1) : UDim2.fromScale(1, percent);
12
+ const rangePosition =
13
+ sliderContext.orientation === "horizontal" ? UDim2.fromScale(0, 0) : UDim2.fromScale(0, 1 - percent);
14
+
15
+ if (props.asChild) {
16
+ const child = props.children;
17
+ if (!child) {
18
+ error("[SliderRange] `asChild` requires a child element.");
19
+ }
20
+
21
+ return (
22
+ <Slot Name="SliderRange" Position={rangePosition} Size={rangeSize}>
23
+ {child}
24
+ </Slot>
25
+ );
26
+ }
27
+
28
+ return (
29
+ <frame
30
+ BackgroundColor3={Color3.fromRGB(86, 142, 255)}
31
+ BorderSizePixel={0}
32
+ Position={rangePosition}
33
+ Size={rangeSize}
34
+ >
35
+ {props.children}
36
+ </frame>
37
+ );
38
+ }
@@ -0,0 +1,228 @@
1
+ import { React, useControllableState } from "@lattice-ui/core";
2
+ import { SliderContextProvider } from "./context";
3
+ import { normalizeBounds, normalizeStep, pointerPositionToValue, snapValueToStep } from "./internals/math";
4
+ import type { SliderProps } from "./types";
5
+
6
+ const UserInputService = game.GetService("UserInputService");
7
+
8
+ function toGuiObject(instance: Instance | undefined) {
9
+ if (!instance || !instance.IsA("GuiObject")) {
10
+ return undefined;
11
+ }
12
+
13
+ return instance;
14
+ }
15
+
16
+ function isPointerStartInput(inputObject: InputObject) {
17
+ return (
18
+ inputObject.UserInputType === Enum.UserInputType.MouseButton1 ||
19
+ inputObject.UserInputType === Enum.UserInputType.Touch
20
+ );
21
+ }
22
+
23
+ export function SliderRoot(props: SliderProps) {
24
+ const bounds = normalizeBounds(props.min ?? 0, props.max ?? 100);
25
+ const min = bounds.min;
26
+ const max = bounds.max;
27
+ const step = normalizeStep(props.step ?? 1);
28
+ const orientation = props.orientation ?? "horizontal";
29
+ const disabled = props.disabled === true;
30
+
31
+ const defaultValue = snapValueToStep(props.defaultValue ?? min, min, max, step);
32
+
33
+ const [valueState, setValueState] = useControllableState<number>({
34
+ value: props.value,
35
+ defaultValue,
36
+ onChange: props.onValueChange,
37
+ });
38
+
39
+ const value = snapValueToStep(valueState, min, max, step);
40
+
41
+ const trackRef = React.useRef<GuiObject>();
42
+ const thumbRef = React.useRef<GuiObject>();
43
+ const latestValueRef = React.useRef(value);
44
+
45
+ React.useEffect(() => {
46
+ latestValueRef.current = value;
47
+ }, [value]);
48
+
49
+ const setValue = React.useCallback(
50
+ (nextValue: number) => {
51
+ if (disabled) {
52
+ return;
53
+ }
54
+
55
+ const normalizedValue = snapValueToStep(nextValue, min, max, step);
56
+ latestValueRef.current = normalizedValue;
57
+ setValueState(normalizedValue);
58
+ },
59
+ [disabled, max, min, setValueState, step],
60
+ );
61
+
62
+ const commitValue = React.useCallback(
63
+ (nextValue: number) => {
64
+ if (disabled) {
65
+ return;
66
+ }
67
+
68
+ const normalizedValue = snapValueToStep(nextValue, min, max, step);
69
+ props.onValueCommit?.(normalizedValue);
70
+ },
71
+ [disabled, max, min, props.onValueCommit, step],
72
+ );
73
+
74
+ const activeDragInputRef = React.useRef<InputObject>();
75
+ const moveConnectionRef = React.useRef<RBXScriptConnection>();
76
+ const endConnectionRef = React.useRef<RBXScriptConnection>();
77
+
78
+ const disconnectDragging = React.useCallback(() => {
79
+ moveConnectionRef.current?.Disconnect();
80
+ moveConnectionRef.current = undefined;
81
+
82
+ endConnectionRef.current?.Disconnect();
83
+ endConnectionRef.current = undefined;
84
+
85
+ activeDragInputRef.current = undefined;
86
+ }, []);
87
+
88
+ const updateValueFromInput = React.useCallback(
89
+ (inputObject: InputObject) => {
90
+ const trackNode = trackRef.current;
91
+ if (!trackNode) {
92
+ return undefined;
93
+ }
94
+
95
+ const pointerPosition = new Vector2(inputObject.Position.X, inputObject.Position.Y);
96
+ const nextValue = pointerPositionToValue(
97
+ pointerPosition,
98
+ trackNode.AbsolutePosition,
99
+ trackNode.AbsoluteSize,
100
+ min,
101
+ max,
102
+ step,
103
+ orientation,
104
+ );
105
+
106
+ setValue(nextValue);
107
+ return nextValue;
108
+ },
109
+ [max, min, orientation, setValue, step],
110
+ );
111
+
112
+ const finishDrag = React.useCallback(
113
+ (inputObject?: InputObject) => {
114
+ const activeDragInput = activeDragInputRef.current;
115
+ if (!activeDragInput) {
116
+ return;
117
+ }
118
+
119
+ if (inputObject) {
120
+ const nextValue = updateValueFromInput(inputObject);
121
+ if (nextValue !== undefined) {
122
+ latestValueRef.current = nextValue;
123
+ }
124
+ }
125
+
126
+ commitValue(latestValueRef.current);
127
+ disconnectDragging();
128
+ },
129
+ [commitValue, disconnectDragging, updateValueFromInput],
130
+ );
131
+
132
+ const startDrag = React.useCallback(
133
+ (inputObject: InputObject) => {
134
+ if (disabled || !isPointerStartInput(inputObject)) {
135
+ return;
136
+ }
137
+
138
+ activeDragInputRef.current = inputObject;
139
+ const initialValue = updateValueFromInput(inputObject);
140
+ if (initialValue !== undefined) {
141
+ latestValueRef.current = initialValue;
142
+ }
143
+
144
+ moveConnectionRef.current?.Disconnect();
145
+ endConnectionRef.current?.Disconnect();
146
+
147
+ moveConnectionRef.current = UserInputService.InputChanged.Connect((changedInput) => {
148
+ const activeDragInput = activeDragInputRef.current;
149
+ if (!activeDragInput) {
150
+ return;
151
+ }
152
+
153
+ if (activeDragInput.UserInputType === Enum.UserInputType.Touch) {
154
+ if (changedInput.UserInputType !== Enum.UserInputType.Touch || changedInput !== activeDragInput) {
155
+ return;
156
+ }
157
+
158
+ const touchValue = updateValueFromInput(changedInput);
159
+ if (touchValue !== undefined) {
160
+ latestValueRef.current = touchValue;
161
+ }
162
+ return;
163
+ }
164
+
165
+ if (changedInput.UserInputType !== Enum.UserInputType.MouseMovement) {
166
+ return;
167
+ }
168
+
169
+ const mouseValue = updateValueFromInput(changedInput);
170
+ if (mouseValue !== undefined) {
171
+ latestValueRef.current = mouseValue;
172
+ }
173
+ });
174
+
175
+ endConnectionRef.current = UserInputService.InputEnded.Connect((endedInput) => {
176
+ const activeDragInput = activeDragInputRef.current;
177
+ if (!activeDragInput) {
178
+ return;
179
+ }
180
+
181
+ const endedTouch = activeDragInput.UserInputType === Enum.UserInputType.Touch && endedInput === activeDragInput;
182
+ const endedMouse =
183
+ activeDragInput.UserInputType === Enum.UserInputType.MouseButton1 &&
184
+ endedInput.UserInputType === Enum.UserInputType.MouseButton1;
185
+
186
+ if (!endedTouch && !endedMouse) {
187
+ return;
188
+ }
189
+
190
+ finishDrag(endedTouch ? endedInput : undefined);
191
+ });
192
+ },
193
+ [disabled, finishDrag, updateValueFromInput],
194
+ );
195
+
196
+ React.useEffect(() => {
197
+ return () => {
198
+ disconnectDragging();
199
+ };
200
+ }, [disconnectDragging]);
201
+
202
+ const setTrackNode = React.useCallback((instance: Instance | undefined) => {
203
+ trackRef.current = toGuiObject(instance);
204
+ }, []);
205
+
206
+ const setThumbNode = React.useCallback((instance: Instance | undefined) => {
207
+ thumbRef.current = toGuiObject(instance);
208
+ }, []);
209
+
210
+ const contextValue = React.useMemo(
211
+ () => ({
212
+ value,
213
+ setValue,
214
+ commitValue,
215
+ min,
216
+ max,
217
+ step,
218
+ orientation,
219
+ disabled,
220
+ setTrackNode,
221
+ setThumbNode,
222
+ startDrag,
223
+ }),
224
+ [commitValue, disabled, max, min, orientation, setThumbNode, setTrackNode, setValue, startDrag, step, value],
225
+ );
226
+
227
+ return <SliderContextProvider value={contextValue}>{props.children}</SliderContextProvider>;
228
+ }
@@ -0,0 +1,97 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useSliderContext } from "./context";
3
+ import { valueToPercent } from "./internals/math";
4
+ import type { SliderThumbProps } from "./types";
5
+
6
+ function isPointerStartInput(inputObject: InputObject) {
7
+ return (
8
+ inputObject.UserInputType === Enum.UserInputType.MouseButton1 ||
9
+ inputObject.UserInputType === Enum.UserInputType.Touch
10
+ );
11
+ }
12
+
13
+ export function SliderThumb(props: SliderThumbProps) {
14
+ const sliderContext = useSliderContext();
15
+ const percent = valueToPercent(sliderContext.value, sliderContext.min, sliderContext.max);
16
+
17
+ const position =
18
+ sliderContext.orientation === "horizontal" ? UDim2.fromScale(percent, 0.5) : UDim2.fromScale(0.5, 1 - percent);
19
+
20
+ const handleInputBegan = React.useCallback(
21
+ (_rbx: GuiObject, inputObject: InputObject) => {
22
+ if (isPointerStartInput(inputObject)) {
23
+ sliderContext.startDrag(inputObject);
24
+ return;
25
+ }
26
+
27
+ if (sliderContext.disabled) {
28
+ return;
29
+ }
30
+
31
+ const keyCode = inputObject.KeyCode;
32
+ let nextValue: number | undefined;
33
+ const pageStep = sliderContext.step * 10;
34
+
35
+ if (keyCode === Enum.KeyCode.Home) {
36
+ nextValue = sliderContext.min;
37
+ } else if (keyCode === Enum.KeyCode.End) {
38
+ nextValue = sliderContext.max;
39
+ } else if (keyCode === Enum.KeyCode.PageUp) {
40
+ nextValue = sliderContext.value + pageStep;
41
+ } else if (keyCode === Enum.KeyCode.PageDown) {
42
+ nextValue = sliderContext.value - pageStep;
43
+ } else if (keyCode === Enum.KeyCode.Right || keyCode === Enum.KeyCode.Up) {
44
+ nextValue = sliderContext.value + sliderContext.step;
45
+ } else if (keyCode === Enum.KeyCode.Left || keyCode === Enum.KeyCode.Down) {
46
+ nextValue = sliderContext.value - sliderContext.step;
47
+ } else if (keyCode === Enum.KeyCode.Return || keyCode === Enum.KeyCode.Space) {
48
+ sliderContext.commitValue(sliderContext.value);
49
+ return;
50
+ } else {
51
+ return;
52
+ }
53
+
54
+ if (nextValue === undefined) {
55
+ return;
56
+ }
57
+
58
+ sliderContext.setValue(nextValue);
59
+ sliderContext.commitValue(nextValue);
60
+ },
61
+ [sliderContext],
62
+ );
63
+
64
+ const sharedProps = {
65
+ Active: !sliderContext.disabled,
66
+ AnchorPoint: new Vector2(0.5, 0.5),
67
+ Name: "SliderThumb",
68
+ Position: position,
69
+ Selectable: !sliderContext.disabled,
70
+ Event: {
71
+ InputBegan: handleInputBegan,
72
+ },
73
+ ref: sliderContext.setThumbNode,
74
+ };
75
+
76
+ if (props.asChild) {
77
+ const child = props.children;
78
+ if (!child) {
79
+ error("[SliderThumb] `asChild` requires a child element.");
80
+ }
81
+
82
+ return <Slot {...sharedProps}>{child}</Slot>;
83
+ }
84
+
85
+ return (
86
+ <textbutton
87
+ {...sharedProps}
88
+ AutoButtonColor={false}
89
+ BackgroundColor3={Color3.fromRGB(235, 241, 250)}
90
+ BorderSizePixel={0}
91
+ Size={UDim2.fromOffset(16, 16)}
92
+ Text=""
93
+ >
94
+ {props.children}
95
+ </textbutton>
96
+ );
97
+ }
@@ -0,0 +1,55 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useSliderContext } from "./context";
3
+ import type { SliderTrackProps } from "./types";
4
+
5
+ function isPointerStartInput(inputObject: InputObject) {
6
+ return (
7
+ inputObject.UserInputType === Enum.UserInputType.MouseButton1 ||
8
+ inputObject.UserInputType === Enum.UserInputType.Touch
9
+ );
10
+ }
11
+
12
+ export function SliderTrack(props: SliderTrackProps) {
13
+ const sliderContext = useSliderContext();
14
+
15
+ const handleInputBegan = React.useCallback(
16
+ (_rbx: GuiObject, inputObject: InputObject) => {
17
+ if (!isPointerStartInput(inputObject)) {
18
+ return;
19
+ }
20
+
21
+ sliderContext.startDrag(inputObject);
22
+ },
23
+ [sliderContext],
24
+ );
25
+
26
+ const sharedProps = {
27
+ Active: !sliderContext.disabled,
28
+ Name: "SliderTrack",
29
+ Selectable: !sliderContext.disabled,
30
+ Event: {
31
+ InputBegan: handleInputBegan,
32
+ },
33
+ ref: sliderContext.setTrackNode,
34
+ };
35
+
36
+ if (props.asChild) {
37
+ const child = props.children;
38
+ if (!child) {
39
+ error("[SliderTrack] `asChild` requires a child element.");
40
+ }
41
+
42
+ return <Slot {...sharedProps}>{child}</Slot>;
43
+ }
44
+
45
+ return (
46
+ <frame
47
+ {...sharedProps}
48
+ BackgroundColor3={Color3.fromRGB(47, 53, 68)}
49
+ BorderSizePixel={0}
50
+ Size={sliderContext.orientation === "horizontal" ? UDim2.fromOffset(260, 10) : UDim2.fromOffset(10, 220)}
51
+ >
52
+ {props.children}
53
+ </frame>
54
+ );
55
+ }
@@ -0,0 +1,6 @@
1
+ import { createStrictContext } from "@lattice-ui/core";
2
+ import type { SliderContextValue } from "./types";
3
+
4
+ const [SliderContextProvider, useSliderContext] = createStrictContext<SliderContextValue>("Slider");
5
+
6
+ export { SliderContextProvider, useSliderContext };
@@ -0,0 +1,80 @@
1
+ import type { SliderOrientation } from "../types";
2
+
3
+ export function clampNumber(value: number, min: number, max: number) {
4
+ return math.clamp(value, min, max);
5
+ }
6
+
7
+ export function normalizeBounds(min: number, max: number) {
8
+ if (min <= max) {
9
+ return {
10
+ min,
11
+ max,
12
+ };
13
+ }
14
+
15
+ return {
16
+ min: max,
17
+ max: min,
18
+ };
19
+ }
20
+
21
+ export function normalizeStep(step: number) {
22
+ return step > 0 ? step : 1;
23
+ }
24
+
25
+ export function snapValueToStep(value: number, min: number, max: number, step: number) {
26
+ const clamped = clampNumber(value, min, max);
27
+ const stepCount = math.round((clamped - min) / step);
28
+ const snapped = min + stepCount * step;
29
+ return clampNumber(snapped, min, max);
30
+ }
31
+
32
+ export function valueToPercent(value: number, min: number, max: number) {
33
+ if (max <= min) {
34
+ return 0;
35
+ }
36
+
37
+ return clampNumber((value - min) / (max - min), 0, 1);
38
+ }
39
+
40
+ export function percentToValue(percent: number, min: number, max: number, step: number) {
41
+ const clampedPercent = clampNumber(percent, 0, 1);
42
+ const rawValue = min + (max - min) * clampedPercent;
43
+ return snapValueToStep(rawValue, min, max, step);
44
+ }
45
+
46
+ export function pointerPositionToPercent(
47
+ pointerPosition: Vector2,
48
+ trackPosition: Vector2,
49
+ trackSize: Vector2,
50
+ orientation: SliderOrientation,
51
+ ) {
52
+ if (orientation === "horizontal") {
53
+ if (trackSize.X <= 0) {
54
+ return 0;
55
+ }
56
+
57
+ const percent = (pointerPosition.X - trackPosition.X) / trackSize.X;
58
+ return clampNumber(percent, 0, 1);
59
+ }
60
+
61
+ if (trackSize.Y <= 0) {
62
+ return 0;
63
+ }
64
+
65
+ const percent = 1 - (pointerPosition.Y - trackPosition.Y) / trackSize.Y;
66
+ return clampNumber(percent, 0, 1);
67
+ }
68
+
69
+ export function pointerPositionToValue(
70
+ pointerPosition: Vector2,
71
+ trackPosition: Vector2,
72
+ trackSize: Vector2,
73
+ min: number,
74
+ max: number,
75
+ step: number,
76
+ orientation: SliderOrientation,
77
+ ) {
78
+ const percent = pointerPositionToPercent(pointerPosition, trackPosition, trackSize, orientation);
79
+ return percentToValue(percent, min, max, step);
80
+ }
@@ -0,0 +1,48 @@
1
+ import type React from "@rbxts/react";
2
+
3
+ export type SliderOrientation = "horizontal" | "vertical";
4
+
5
+ export type SliderSetValue = (value: number) => void;
6
+ export type SliderCommitValue = (value: number) => void;
7
+
8
+ export type SliderContextValue = {
9
+ value: number;
10
+ setValue: SliderSetValue;
11
+ commitValue: SliderCommitValue;
12
+ min: number;
13
+ max: number;
14
+ step: number;
15
+ orientation: SliderOrientation;
16
+ disabled: boolean;
17
+ setTrackNode: (instance: Instance | undefined) => void;
18
+ setThumbNode: (instance: Instance | undefined) => void;
19
+ startDrag: (inputObject: InputObject) => void;
20
+ };
21
+
22
+ export type SliderProps = {
23
+ value?: number;
24
+ defaultValue?: number;
25
+ onValueChange?: (value: number) => void;
26
+ onValueCommit?: (value: number) => void;
27
+ min?: number;
28
+ max?: number;
29
+ step?: number;
30
+ orientation?: SliderOrientation;
31
+ disabled?: boolean;
32
+ children?: React.ReactNode;
33
+ };
34
+
35
+ export type SliderTrackProps = {
36
+ asChild?: boolean;
37
+ children?: React.ReactElement;
38
+ };
39
+
40
+ export type SliderRangeProps = {
41
+ asChild?: boolean;
42
+ children?: React.ReactElement;
43
+ };
44
+
45
+ export type SliderThumbProps = {
46
+ asChild?: boolean;
47
+ children?: React.ReactElement;
48
+ };
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { SliderRange } from "./Slider/SliderRange";
2
+ import { SliderRoot } from "./Slider/SliderRoot";
3
+ import { SliderThumb } from "./Slider/SliderThumb";
4
+ import { SliderTrack } from "./Slider/SliderTrack";
5
+
6
+ export const Slider = {
7
+ Root: SliderRoot,
8
+ Track: SliderTrack,
9
+ Range: SliderRange,
10
+ Thumb: SliderThumb,
11
+ } as const;
12
+
13
+ export type {
14
+ SliderCommitValue,
15
+ SliderContextValue,
16
+ SliderOrientation,
17
+ SliderProps,
18
+ SliderRangeProps,
19
+ SliderSetValue,
20
+ SliderThumbProps,
21
+ SliderTrackProps,
22
+ } from "./Slider/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,35 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "baseUrl": "..",
6
+ "rootDir": "..",
7
+ "paths": {
8
+ "@lattice-ui/accordion": ["accordion/src/index.ts"],
9
+ "@lattice-ui/avatar": ["avatar/src/index.ts"],
10
+ "@lattice-ui/checkbox": ["checkbox/src/index.ts"],
11
+ "@lattice-ui/combobox": ["combobox/src/index.ts"],
12
+ "@lattice-ui/core": ["core/src/index.ts"],
13
+ "@lattice-ui/dialog": ["dialog/src/index.ts"],
14
+ "@lattice-ui/focus": ["focus/src/index.ts"],
15
+ "@lattice-ui/layer": ["layer/src/index.ts"],
16
+ "@lattice-ui/menu": ["menu/src/index.ts"],
17
+ "@lattice-ui/popover": ["popover/src/index.ts"],
18
+ "@lattice-ui/popper": ["popper/src/index.ts"],
19
+ "@lattice-ui/progress": ["progress/src/index.ts"],
20
+ "@lattice-ui/radio-group": ["radio-group/src/index.ts"],
21
+ "@lattice-ui/scroll-area": ["scroll-area/src/index.ts"],
22
+ "@lattice-ui/select": ["select/src/index.ts"],
23
+ "@lattice-ui/slider": ["slider/src/index.ts"],
24
+ "@lattice-ui/style": ["style/src/index.ts"],
25
+ "@lattice-ui/switch": ["switch/src/index.ts"],
26
+ "@lattice-ui/system": ["system/src/index.ts"],
27
+ "@lattice-ui/tabs": ["tabs/src/index.ts"],
28
+ "@lattice-ui/text-field": ["text-field/src/index.ts"],
29
+ "@lattice-ui/textarea": ["textarea/src/index.ts"],
30
+ "@lattice-ui/toast": ["toast/src/index.ts"],
31
+ "@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
32
+ "@lattice-ui/tooltip": ["tooltip/src/index.ts"]
33
+ }
34
+ }
35
+ }