@lattice-ui/select 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.
Files changed (40) hide show
  1. package/README.md +23 -0
  2. package/out/Select/SelectContent.d.ts +3 -0
  3. package/out/Select/SelectContent.luau +268 -0
  4. package/out/Select/SelectGroup.d.ts +3 -0
  5. package/out/Select/SelectGroup.luau +22 -0
  6. package/out/Select/SelectItem.d.ts +3 -0
  7. package/out/Select/SelectItem.luau +124 -0
  8. package/out/Select/SelectLabel.d.ts +3 -0
  9. package/out/Select/SelectLabel.luau +26 -0
  10. package/out/Select/SelectPortal.d.ts +3 -0
  11. package/out/Select/SelectPortal.luau +33 -0
  12. package/out/Select/SelectRoot.d.ts +3 -0
  13. package/out/Select/SelectRoot.luau +201 -0
  14. package/out/Select/SelectSeparator.d.ts +3 -0
  15. package/out/Select/SelectSeparator.luau +22 -0
  16. package/out/Select/SelectTrigger.d.ts +3 -0
  17. package/out/Select/SelectTrigger.luau +72 -0
  18. package/out/Select/SelectValue.d.ts +3 -0
  19. package/out/Select/SelectValue.luau +47 -0
  20. package/out/Select/context.d.ts +3 -0
  21. package/out/Select/context.luau +10 -0
  22. package/out/Select/types.d.ts +84 -0
  23. package/out/Select/types.luau +2 -0
  24. package/out/index.d.ts +22 -0
  25. package/out/init.luau +34 -0
  26. package/package.json +26 -0
  27. package/src/Select/SelectContent.tsx +297 -0
  28. package/src/Select/SelectGroup.tsx +19 -0
  29. package/src/Select/SelectItem.tsx +132 -0
  30. package/src/Select/SelectLabel.tsx +27 -0
  31. package/src/Select/SelectPortal.tsx +28 -0
  32. package/src/Select/SelectRoot.tsx +124 -0
  33. package/src/Select/SelectSeparator.tsx +19 -0
  34. package/src/Select/SelectTrigger.tsx +89 -0
  35. package/src/Select/SelectValue.tsx +42 -0
  36. package/src/Select/context.ts +6 -0
  37. package/src/Select/types.ts +96 -0
  38. package/src/index.ts +59 -0
  39. package/tsconfig.json +16 -0
  40. package/tsconfig.typecheck.json +35 -0
@@ -0,0 +1,297 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { RovingFocusGroup } from "@lattice-ui/focus";
3
+ import { DismissableLayer, Presence } from "@lattice-ui/layer";
4
+ import { usePopper } from "@lattice-ui/popper";
5
+ import { useSelectContext } from "./context";
6
+ import type { SelectContentProps, SelectItemRegistration } from "./types";
7
+
8
+ const GuiService = game.GetService("GuiService");
9
+ const UserInputService = game.GetService("UserInputService");
10
+
11
+ const digitKeyMap: Record<string, string> = {
12
+ Zero: "0",
13
+ One: "1",
14
+ Two: "2",
15
+ Three: "3",
16
+ Four: "4",
17
+ Five: "5",
18
+ Six: "6",
19
+ Seven: "7",
20
+ Eight: "8",
21
+ Nine: "9",
22
+ };
23
+
24
+ type SelectContentImplProps = {
25
+ enabled: boolean;
26
+ visible: boolean;
27
+ onDismiss: () => void;
28
+ asChild?: boolean;
29
+ placement?: SelectContentProps["placement"];
30
+ offset?: SelectContentProps["offset"];
31
+ padding?: SelectContentProps["padding"];
32
+ } & Pick<SelectContentProps, "children" | "onEscapeKeyDown" | "onInteractOutside" | "onPointerDownOutside">;
33
+
34
+ function toGuiObject(instance: Instance | undefined) {
35
+ if (!instance || !instance.IsA("GuiObject")) {
36
+ return undefined;
37
+ }
38
+
39
+ return instance;
40
+ }
41
+
42
+ function toSearchCharacter(keyCode: Enum.KeyCode) {
43
+ if (keyCode === Enum.KeyCode.Space) {
44
+ return " ";
45
+ }
46
+
47
+ const digitCharacter = digitKeyMap[keyCode.Name];
48
+ if (digitCharacter !== undefined) {
49
+ return digitCharacter;
50
+ }
51
+
52
+ if (keyCode.Name.size() === 1) {
53
+ return string.lower(keyCode.Name);
54
+ }
55
+
56
+ return undefined;
57
+ }
58
+
59
+ function startsWithIgnoreCase(value: string, query: string) {
60
+ if (query.size() === 0) {
61
+ return false;
62
+ }
63
+
64
+ const normalizedValue = string.lower(value);
65
+ return string.sub(normalizedValue, 1, query.size()) === query;
66
+ }
67
+
68
+ function findCurrentIndex(items: Array<SelectItemRegistration>, selectedObject: GuiObject | undefined) {
69
+ if (!selectedObject) {
70
+ return -1;
71
+ }
72
+
73
+ return items.findIndex((item) => {
74
+ const node = item.getNode();
75
+ if (!node) {
76
+ return false;
77
+ }
78
+
79
+ return selectedObject === node || selectedObject.IsDescendantOf(node);
80
+ });
81
+ }
82
+
83
+ function findTypeaheadMatch(items: Array<SelectItemRegistration>, query: string, startIndex: number) {
84
+ const itemCount = items.size();
85
+ if (itemCount === 0) {
86
+ return -1;
87
+ }
88
+
89
+ for (let attempts = 0; attempts < itemCount; attempts++) {
90
+ const candidateIndex = (startIndex + attempts) % itemCount;
91
+ const candidate = items[candidateIndex];
92
+ if (!candidate || candidate.getDisabled()) {
93
+ continue;
94
+ }
95
+
96
+ if (startsWithIgnoreCase(candidate.getTextValue(), query)) {
97
+ return candidateIndex;
98
+ }
99
+ }
100
+
101
+ return -1;
102
+ }
103
+
104
+ function focusItem(item: SelectItemRegistration | undefined) {
105
+ if (!item) {
106
+ return;
107
+ }
108
+
109
+ const node = item.getNode();
110
+ if (!node || !node.Selectable) {
111
+ return;
112
+ }
113
+
114
+ GuiService.SelectedObject = node;
115
+ }
116
+
117
+ function SelectContentImpl(props: SelectContentImplProps) {
118
+ const selectContext = useSelectContext();
119
+
120
+ const popper = usePopper({
121
+ anchorRef: selectContext.triggerRef,
122
+ contentRef: selectContext.contentRef,
123
+ placement: props.placement,
124
+ offset: props.offset,
125
+ padding: props.padding,
126
+ enabled: props.enabled,
127
+ });
128
+
129
+ const setContentRef = React.useCallback(
130
+ (instance: Instance | undefined) => {
131
+ selectContext.contentRef.current = toGuiObject(instance);
132
+ },
133
+ [selectContext.contentRef],
134
+ );
135
+
136
+ const searchRef = React.useRef("");
137
+ const searchTimestampRef = React.useRef(0);
138
+
139
+ React.useEffect(() => {
140
+ if (!props.enabled) {
141
+ return;
142
+ }
143
+
144
+ const orderedItems = selectContext.getOrderedItems();
145
+ const selectedItem = orderedItems.find((item) => item.value === selectContext.value && !item.getDisabled());
146
+ focusItem(selectedItem);
147
+ }, [props.enabled, selectContext, selectContext.value]);
148
+
149
+ React.useEffect(() => {
150
+ if (!props.enabled) {
151
+ return;
152
+ }
153
+
154
+ searchRef.current = "";
155
+ searchTimestampRef.current = 0;
156
+
157
+ const connection = UserInputService.InputBegan.Connect((inputObject, gameProcessedEvent) => {
158
+ if (gameProcessedEvent) {
159
+ return;
160
+ }
161
+
162
+ if (inputObject.UserInputType !== Enum.UserInputType.Keyboard) {
163
+ return;
164
+ }
165
+
166
+ const contentNode = selectContext.contentRef.current;
167
+ const selectedObject = GuiService.SelectedObject;
168
+ if (!contentNode || !selectedObject || !selectedObject.IsDescendantOf(contentNode)) {
169
+ return;
170
+ }
171
+
172
+ const searchCharacter = toSearchCharacter(inputObject.KeyCode);
173
+ if (searchCharacter === undefined) {
174
+ return;
175
+ }
176
+
177
+ const now = os.clock();
178
+ const shouldResetQuery = now - searchTimestampRef.current > 0.8;
179
+ const nextQuery = shouldResetQuery ? searchCharacter : `${searchRef.current}${searchCharacter}`;
180
+ searchRef.current = nextQuery;
181
+ searchTimestampRef.current = now;
182
+
183
+ const orderedItems = selectContext.getOrderedItems();
184
+ const currentIndex = findCurrentIndex(orderedItems, selectedObject);
185
+ const startIndex = currentIndex >= 0 ? currentIndex + 1 : 0;
186
+ const matchIndex = findTypeaheadMatch(orderedItems, string.lower(nextQuery), startIndex);
187
+ if (matchIndex < 0) {
188
+ return;
189
+ }
190
+
191
+ const matchedItem = orderedItems[matchIndex];
192
+ focusItem(matchedItem);
193
+ });
194
+
195
+ return () => {
196
+ connection.Disconnect();
197
+ };
198
+ }, [props.enabled, selectContext]);
199
+
200
+ const contentNode = props.asChild ? (
201
+ (() => {
202
+ const child = props.children;
203
+ if (!React.isValidElement(child)) {
204
+ error("[SelectContent] `asChild` requires a child element.");
205
+ }
206
+
207
+ return (
208
+ <Slot AnchorPoint={popper.anchorPoint} Position={popper.position} Visible={props.visible} ref={setContentRef}>
209
+ {child}
210
+ </Slot>
211
+ );
212
+ })()
213
+ ) : (
214
+ <frame
215
+ AnchorPoint={popper.anchorPoint}
216
+ BackgroundTransparency={1}
217
+ BorderSizePixel={0}
218
+ Position={popper.position}
219
+ Size={UDim2.fromOffset(0, 0)}
220
+ Visible={props.visible}
221
+ ref={setContentRef}
222
+ >
223
+ {props.children}
224
+ </frame>
225
+ );
226
+
227
+ return (
228
+ <DismissableLayer
229
+ enabled={props.enabled}
230
+ modal={false}
231
+ onDismiss={props.onDismiss}
232
+ onEscapeKeyDown={props.onEscapeKeyDown}
233
+ onInteractOutside={props.onInteractOutside}
234
+ onPointerDownOutside={props.onPointerDownOutside}
235
+ >
236
+ <RovingFocusGroup active={props.enabled} autoFocus="first" loop={selectContext.loop} orientation="vertical">
237
+ {contentNode}
238
+ </RovingFocusGroup>
239
+ </DismissableLayer>
240
+ );
241
+ }
242
+
243
+ export function SelectContent(props: SelectContentProps) {
244
+ const selectContext = useSelectContext();
245
+ const open = selectContext.open;
246
+ const forceMount = props.forceMount === true;
247
+
248
+ const handleDismiss = React.useCallback(() => {
249
+ selectContext.setOpen(false);
250
+ }, [selectContext]);
251
+
252
+ if (!open && !forceMount) {
253
+ return undefined;
254
+ }
255
+
256
+ if (forceMount) {
257
+ return (
258
+ <SelectContentImpl
259
+ asChild={props.asChild}
260
+ enabled={open}
261
+ offset={props.offset}
262
+ onDismiss={handleDismiss}
263
+ onEscapeKeyDown={props.onEscapeKeyDown}
264
+ onInteractOutside={props.onInteractOutside}
265
+ onPointerDownOutside={props.onPointerDownOutside}
266
+ padding={props.padding}
267
+ placement={props.placement}
268
+ visible={open}
269
+ >
270
+ {props.children}
271
+ </SelectContentImpl>
272
+ );
273
+ }
274
+
275
+ return (
276
+ <Presence
277
+ exitFallbackMs={0}
278
+ present={open}
279
+ render={(state) => (
280
+ <SelectContentImpl
281
+ asChild={props.asChild}
282
+ enabled={state.isPresent}
283
+ offset={props.offset}
284
+ onDismiss={handleDismiss}
285
+ onEscapeKeyDown={props.onEscapeKeyDown}
286
+ onInteractOutside={props.onInteractOutside}
287
+ onPointerDownOutside={props.onPointerDownOutside}
288
+ padding={props.padding}
289
+ placement={props.placement}
290
+ visible={state.isPresent}
291
+ >
292
+ {props.children}
293
+ </SelectContentImpl>
294
+ )}
295
+ />
296
+ );
297
+ }
@@ -0,0 +1,19 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import type { SelectGroupProps } from "./types";
3
+
4
+ export function SelectGroup(props: SelectGroupProps) {
5
+ if (props.asChild) {
6
+ const child = props.children;
7
+ if (!child) {
8
+ error("[SelectGroup] `asChild` requires a child element.");
9
+ }
10
+
11
+ return <Slot>{child}</Slot>;
12
+ }
13
+
14
+ return (
15
+ <frame BackgroundTransparency={1} BorderSizePixel={0} Size={UDim2.fromOffset(220, 108)}>
16
+ {props.children}
17
+ </frame>
18
+ );
19
+ }
@@ -0,0 +1,132 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { RovingFocusItem } from "@lattice-ui/focus";
3
+ import { useSelectContext } from "./context";
4
+ import type { SelectItemProps } from "./types";
5
+
6
+ let nextItemId = 0;
7
+ let nextItemOrder = 0;
8
+
9
+ function toGuiObject(instance: Instance | undefined) {
10
+ if (!instance || !instance.IsA("GuiObject")) {
11
+ return undefined;
12
+ }
13
+
14
+ return instance;
15
+ }
16
+
17
+ export function SelectItem(props: SelectItemProps) {
18
+ const selectContext = useSelectContext();
19
+ const itemRef = React.useRef<GuiObject>();
20
+
21
+ const disabled = selectContext.disabled || props.disabled === true;
22
+ const textValue = props.textValue ?? props.value;
23
+
24
+ const disabledRef = React.useRef(disabled);
25
+ const textValueRef = React.useRef(textValue);
26
+
27
+ React.useEffect(() => {
28
+ disabledRef.current = disabled;
29
+ }, [disabled]);
30
+
31
+ React.useEffect(() => {
32
+ textValueRef.current = textValue;
33
+ }, [textValue]);
34
+
35
+ const itemIdRef = React.useRef(0);
36
+ if (itemIdRef.current === 0) {
37
+ nextItemId += 1;
38
+ itemIdRef.current = nextItemId;
39
+ }
40
+
41
+ const itemOrderRef = React.useRef(0);
42
+ if (itemOrderRef.current === 0) {
43
+ nextItemOrder += 1;
44
+ itemOrderRef.current = nextItemOrder;
45
+ }
46
+
47
+ React.useEffect(() => {
48
+ return selectContext.registerItem({
49
+ id: itemIdRef.current,
50
+ value: props.value,
51
+ order: itemOrderRef.current,
52
+ getNode: () => itemRef.current,
53
+ getDisabled: () => disabledRef.current,
54
+ getTextValue: () => textValueRef.current,
55
+ });
56
+ }, [props.value, selectContext]);
57
+
58
+ const setItemRef = React.useCallback((instance: Instance | undefined) => {
59
+ itemRef.current = toGuiObject(instance);
60
+ }, []);
61
+
62
+ const handleSelect = React.useCallback(() => {
63
+ if (disabled) {
64
+ return;
65
+ }
66
+
67
+ selectContext.setValue(props.value);
68
+ selectContext.setOpen(false);
69
+ }, [disabled, props.value, selectContext]);
70
+
71
+ const handleInputBegan = React.useCallback(
72
+ (_rbx: GuiObject, inputObject: InputObject) => {
73
+ if (disabled) {
74
+ return;
75
+ }
76
+
77
+ const keyCode = inputObject.KeyCode;
78
+ if (keyCode !== Enum.KeyCode.Return && keyCode !== Enum.KeyCode.Space) {
79
+ return;
80
+ }
81
+
82
+ selectContext.setValue(props.value);
83
+ selectContext.setOpen(false);
84
+ },
85
+ [disabled, props.value, selectContext],
86
+ );
87
+
88
+ const eventHandlers = React.useMemo(
89
+ () => ({
90
+ Activated: handleSelect,
91
+ InputBegan: handleInputBegan,
92
+ }),
93
+ [handleInputBegan, handleSelect],
94
+ );
95
+
96
+ if (props.asChild) {
97
+ const child = props.children;
98
+ if (!child) {
99
+ error("[SelectItem] `asChild` requires a child element.");
100
+ }
101
+
102
+ return (
103
+ <RovingFocusItem asChild disabled={disabled}>
104
+ <Slot Active={!disabled} Event={eventHandlers} Selectable={!disabled} ref={setItemRef}>
105
+ {child}
106
+ </Slot>
107
+ </RovingFocusItem>
108
+ );
109
+ }
110
+
111
+ return (
112
+ <RovingFocusItem asChild disabled={disabled}>
113
+ <textbutton
114
+ Active={!disabled}
115
+ AutoButtonColor={false}
116
+ BackgroundColor3={Color3.fromRGB(47, 53, 68)}
117
+ BorderSizePixel={0}
118
+ Event={eventHandlers}
119
+ Selectable={!disabled}
120
+ Size={UDim2.fromOffset(220, 32)}
121
+ Text={textValue}
122
+ TextColor3={disabled ? Color3.fromRGB(134, 141, 156) : Color3.fromRGB(234, 239, 247)}
123
+ TextSize={15}
124
+ TextXAlignment={Enum.TextXAlignment.Left}
125
+ ref={setItemRef}
126
+ >
127
+ <uipadding PaddingLeft={new UDim(0, 10)} PaddingRight={new UDim(0, 10)} />
128
+ {props.children}
129
+ </textbutton>
130
+ </RovingFocusItem>
131
+ );
132
+ }
@@ -0,0 +1,27 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import type { SelectLabelProps } from "./types";
3
+
4
+ export function SelectLabel(props: SelectLabelProps) {
5
+ if (props.asChild) {
6
+ const child = props.children;
7
+ if (!child) {
8
+ error("[SelectLabel] `asChild` requires a child element.");
9
+ }
10
+
11
+ return <Slot>{child}</Slot>;
12
+ }
13
+
14
+ return (
15
+ <textlabel
16
+ BackgroundTransparency={1}
17
+ BorderSizePixel={0}
18
+ Size={UDim2.fromOffset(220, 20)}
19
+ Text="Label"
20
+ TextColor3={Color3.fromRGB(168, 176, 191)}
21
+ TextSize={13}
22
+ TextXAlignment={Enum.TextXAlignment.Left}
23
+ >
24
+ {props.children}
25
+ </textlabel>
26
+ );
27
+ }
@@ -0,0 +1,28 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import { Portal, PortalProvider, usePortalContext } from "@lattice-ui/layer";
3
+ import type { SelectPortalProps } from "./types";
4
+
5
+ function SelectPortalWithOverrides(props: SelectPortalProps) {
6
+ const portalContext = usePortalContext();
7
+ const container = props.container ?? portalContext.container;
8
+ const displayOrderBase = props.displayOrderBase ?? portalContext.displayOrderBase;
9
+
10
+ return (
11
+ <PortalProvider container={container} displayOrderBase={displayOrderBase}>
12
+ <Portal>{props.children}</Portal>
13
+ </PortalProvider>
14
+ );
15
+ }
16
+
17
+ export function SelectPortal(props: SelectPortalProps) {
18
+ const hasOverrides = props.container !== undefined || props.displayOrderBase !== undefined;
19
+ if (hasOverrides) {
20
+ return (
21
+ <SelectPortalWithOverrides container={props.container} displayOrderBase={props.displayOrderBase}>
22
+ {props.children}
23
+ </SelectPortalWithOverrides>
24
+ );
25
+ }
26
+
27
+ return <Portal>{props.children}</Portal>;
28
+ }
@@ -0,0 +1,124 @@
1
+ import { React, useControllableState } from "@lattice-ui/core";
2
+ import { SelectContextProvider } from "./context";
3
+ import type { SelectItemRegistration, SelectProps } from "./types";
4
+
5
+ function getOrderedItems(items: Array<SelectItemRegistration>) {
6
+ const ordered = [...items];
7
+ ordered.sort((left, right) => left.order < right.order);
8
+ return ordered;
9
+ }
10
+
11
+ export function SelectRoot(props: SelectProps) {
12
+ const [open, setOpenState] = useControllableState<boolean>({
13
+ value: props.open,
14
+ defaultValue: props.defaultOpen ?? false,
15
+ onChange: props.onOpenChange,
16
+ });
17
+
18
+ const [value, setValueState] = useControllableState<string | undefined>({
19
+ value: props.value,
20
+ defaultValue: props.defaultValue,
21
+ onChange: (nextValue) => {
22
+ if (nextValue !== undefined) {
23
+ props.onValueChange?.(nextValue);
24
+ }
25
+ },
26
+ });
27
+
28
+ const disabled = props.disabled === true;
29
+ const required = props.required === true;
30
+ const loop = props.loop ?? true;
31
+
32
+ const triggerRef = React.useRef<GuiObject>();
33
+ const contentRef = React.useRef<GuiObject>();
34
+
35
+ const itemEntriesRef = React.useRef<Array<SelectItemRegistration>>([]);
36
+ const [registryRevision, setRegistryRevision] = React.useState(0);
37
+
38
+ const registerItem = React.useCallback((item: SelectItemRegistration) => {
39
+ itemEntriesRef.current.push(item);
40
+ setRegistryRevision((revision) => revision + 1);
41
+
42
+ return () => {
43
+ const index = itemEntriesRef.current.findIndex((entry) => entry.id === item.id);
44
+ if (index >= 0) {
45
+ itemEntriesRef.current.remove(index);
46
+ setRegistryRevision((revision) => revision + 1);
47
+ }
48
+ };
49
+ }, []);
50
+
51
+ const resolveOrderedItems = React.useCallback(() => {
52
+ return getOrderedItems(itemEntriesRef.current);
53
+ }, [registryRevision]);
54
+
55
+ const getItemText = React.useCallback(
56
+ (candidateValue: string) => {
57
+ const selected = resolveOrderedItems().find((item) => item.value === candidateValue);
58
+ return selected?.getTextValue();
59
+ },
60
+ [resolveOrderedItems],
61
+ );
62
+
63
+ const setOpen = React.useCallback(
64
+ (nextOpen: boolean) => {
65
+ if (disabled && nextOpen) {
66
+ return;
67
+ }
68
+
69
+ setOpenState(nextOpen);
70
+ },
71
+ [disabled, setOpenState],
72
+ );
73
+
74
+ const setValue = React.useCallback(
75
+ (nextValue: string) => {
76
+ if (disabled) {
77
+ return;
78
+ }
79
+
80
+ const selected = resolveOrderedItems().find((item) => item.value === nextValue);
81
+ if (selected && selected.getDisabled()) {
82
+ return;
83
+ }
84
+
85
+ setValueState(nextValue);
86
+ },
87
+ [disabled, resolveOrderedItems, setValueState],
88
+ );
89
+
90
+ React.useEffect(() => {
91
+ if (value === undefined) {
92
+ return;
93
+ }
94
+
95
+ const orderedItems = resolveOrderedItems();
96
+ const selected = orderedItems.find((item) => item.value === value);
97
+ if (selected && !selected.getDisabled()) {
98
+ return;
99
+ }
100
+
101
+ const fallback = orderedItems.find((item) => !item.getDisabled());
102
+ setValueState(fallback?.value);
103
+ }, [registryRevision, resolveOrderedItems, setValueState, value]);
104
+
105
+ const contextValue = React.useMemo(
106
+ () => ({
107
+ open,
108
+ setOpen,
109
+ value,
110
+ setValue,
111
+ disabled,
112
+ required,
113
+ loop,
114
+ triggerRef,
115
+ contentRef,
116
+ registerItem,
117
+ getOrderedItems: resolveOrderedItems,
118
+ getItemText,
119
+ }),
120
+ [disabled, getItemText, loop, open, registerItem, required, resolveOrderedItems, setOpen, setValue, value],
121
+ );
122
+
123
+ return <SelectContextProvider value={contextValue}>{props.children}</SelectContextProvider>;
124
+ }
@@ -0,0 +1,19 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import type { SelectSeparatorProps } from "./types";
3
+
4
+ export function SelectSeparator(props: SelectSeparatorProps) {
5
+ if (props.asChild) {
6
+ const child = props.children;
7
+ if (!child) {
8
+ error("[SelectSeparator] `asChild` requires a child element.");
9
+ }
10
+
11
+ return <Slot>{child}</Slot>;
12
+ }
13
+
14
+ return (
15
+ <frame BackgroundColor3={Color3.fromRGB(78, 86, 104)} BorderSizePixel={0} Size={UDim2.fromOffset(220, 1)}>
16
+ {props.children}
17
+ </frame>
18
+ );
19
+ }