@lattice-ui/combobox 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 +23 -0
- package/out/Combobox/ComboboxContent.d.ts +3 -0
- package/out/Combobox/ComboboxContent.luau +110 -0
- package/out/Combobox/ComboboxGroup.d.ts +3 -0
- package/out/Combobox/ComboboxGroup.luau +22 -0
- package/out/Combobox/ComboboxInput.d.ts +3 -0
- package/out/Combobox/ComboboxInput.luau +88 -0
- package/out/Combobox/ComboboxItem.d.ts +3 -0
- package/out/Combobox/ComboboxItem.luau +131 -0
- package/out/Combobox/ComboboxLabel.d.ts +3 -0
- package/out/Combobox/ComboboxLabel.luau +26 -0
- package/out/Combobox/ComboboxPortal.d.ts +3 -0
- package/out/Combobox/ComboboxPortal.luau +33 -0
- package/out/Combobox/ComboboxRoot.d.ts +4 -0
- package/out/Combobox/ComboboxRoot.luau +256 -0
- package/out/Combobox/ComboboxSeparator.d.ts +3 -0
- package/out/Combobox/ComboboxSeparator.luau +22 -0
- package/out/Combobox/ComboboxTrigger.d.ts +3 -0
- package/out/Combobox/ComboboxTrigger.luau +72 -0
- package/out/Combobox/ComboboxValue.d.ts +3 -0
- package/out/Combobox/ComboboxValue.luau +47 -0
- package/out/Combobox/context.d.ts +3 -0
- package/out/Combobox/context.luau +10 -0
- package/out/Combobox/logic.d.ts +10 -0
- package/out/Combobox/logic.luau +103 -0
- package/out/Combobox/types.d.ts +105 -0
- package/out/Combobox/types.luau +2 -0
- package/out/index.d.ts +24 -0
- package/out/init.luau +32 -0
- package/package.json +26 -0
- package/src/Combobox/ComboboxContent.tsx +142 -0
- package/src/Combobox/ComboboxGroup.tsx +19 -0
- package/src/Combobox/ComboboxInput.tsx +98 -0
- package/src/Combobox/ComboboxItem.tsx +134 -0
- package/src/Combobox/ComboboxLabel.tsx +27 -0
- package/src/Combobox/ComboboxPortal.tsx +28 -0
- package/src/Combobox/ComboboxRoot.tsx +202 -0
- package/src/Combobox/ComboboxSeparator.tsx +19 -0
- package/src/Combobox/ComboboxTrigger.tsx +89 -0
- package/src/Combobox/ComboboxValue.tsx +42 -0
- package/src/Combobox/context.ts +6 -0
- package/src/Combobox/logic.ts +56 -0
- package/src/Combobox/types.ts +119 -0
- package/src/index.ts +48 -0
- package/tsconfig.json +16 -0
- package/tsconfig.typecheck.json +35 -0
package/out/init.luau
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local ComboboxContent = TS.import(script, script, "Combobox", "ComboboxContent").ComboboxContent
|
|
5
|
+
local ComboboxGroup = TS.import(script, script, "Combobox", "ComboboxGroup").ComboboxGroup
|
|
6
|
+
local ComboboxInput = TS.import(script, script, "Combobox", "ComboboxInput").ComboboxInput
|
|
7
|
+
local ComboboxItem = TS.import(script, script, "Combobox", "ComboboxItem").ComboboxItem
|
|
8
|
+
local ComboboxLabel = TS.import(script, script, "Combobox", "ComboboxLabel").ComboboxLabel
|
|
9
|
+
local ComboboxPortal = TS.import(script, script, "Combobox", "ComboboxPortal").ComboboxPortal
|
|
10
|
+
local ComboboxRoot = TS.import(script, script, "Combobox", "ComboboxRoot").ComboboxRoot
|
|
11
|
+
local ComboboxSeparator = TS.import(script, script, "Combobox", "ComboboxSeparator").ComboboxSeparator
|
|
12
|
+
local ComboboxTrigger = TS.import(script, script, "Combobox", "ComboboxTrigger").ComboboxTrigger
|
|
13
|
+
local ComboboxValue = TS.import(script, script, "Combobox", "ComboboxValue").ComboboxValue
|
|
14
|
+
local Combobox = {
|
|
15
|
+
Root = ComboboxRoot,
|
|
16
|
+
Trigger = ComboboxTrigger,
|
|
17
|
+
Input = ComboboxInput,
|
|
18
|
+
Value = ComboboxValue,
|
|
19
|
+
Portal = ComboboxPortal,
|
|
20
|
+
Content = ComboboxContent,
|
|
21
|
+
Item = ComboboxItem,
|
|
22
|
+
Group = ComboboxGroup,
|
|
23
|
+
Label = ComboboxLabel,
|
|
24
|
+
Separator = ComboboxSeparator,
|
|
25
|
+
}
|
|
26
|
+
local _logic = TS.import(script, script, "Combobox", "logic")
|
|
27
|
+
exports.defaultComboboxFilter = _logic.defaultComboboxFilter
|
|
28
|
+
exports.filterComboboxOptions = _logic.filterComboboxOptions
|
|
29
|
+
exports.resolveComboboxInputValue = _logic.resolveComboboxInputValue
|
|
30
|
+
exports.resolveForcedComboboxValue = _logic.resolveForcedComboboxValue
|
|
31
|
+
exports.Combobox = Combobox
|
|
32
|
+
return exports
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lattice-ui/combobox",
|
|
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
|
+
"@lattice-ui/layer": "0.3.0",
|
|
11
|
+
"@lattice-ui/popper": "0.3.0"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@rbxts/react": "17.3.7-ts.1",
|
|
15
|
+
"@rbxts/react-roblox": "17.3.7-ts.1"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@rbxts/react": "^17",
|
|
19
|
+
"@rbxts/react-roblox": "^17"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "rbxtsc -p tsconfig.json",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
24
|
+
"watch": "rbxtsc -p tsconfig.json -w"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
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 { useComboboxContext } from "./context";
|
|
6
|
+
import type { ComboboxContentProps } from "./types";
|
|
7
|
+
|
|
8
|
+
type ComboboxContentImplProps = {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
visible: boolean;
|
|
11
|
+
onDismiss: () => void;
|
|
12
|
+
asChild?: boolean;
|
|
13
|
+
placement?: ComboboxContentProps["placement"];
|
|
14
|
+
offset?: ComboboxContentProps["offset"];
|
|
15
|
+
padding?: ComboboxContentProps["padding"];
|
|
16
|
+
} & Pick<ComboboxContentProps, "children" | "onEscapeKeyDown" | "onInteractOutside" | "onPointerDownOutside">;
|
|
17
|
+
|
|
18
|
+
function toGuiObject(instance: Instance | undefined) {
|
|
19
|
+
if (!instance || !instance.IsA("GuiObject")) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
27
|
+
const comboboxContext = useComboboxContext();
|
|
28
|
+
|
|
29
|
+
const popper = usePopper({
|
|
30
|
+
anchorRef: comboboxContext.triggerRef,
|
|
31
|
+
contentRef: comboboxContext.contentRef,
|
|
32
|
+
placement: props.placement,
|
|
33
|
+
offset: props.offset,
|
|
34
|
+
padding: props.padding,
|
|
35
|
+
enabled: props.enabled,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const setContentRef = React.useCallback(
|
|
39
|
+
(instance: Instance | undefined) => {
|
|
40
|
+
comboboxContext.contentRef.current = toGuiObject(instance);
|
|
41
|
+
},
|
|
42
|
+
[comboboxContext.contentRef],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const contentNode = props.asChild ? (
|
|
46
|
+
(() => {
|
|
47
|
+
const child = props.children;
|
|
48
|
+
if (!React.isValidElement(child)) {
|
|
49
|
+
error("[ComboboxContent] `asChild` requires a child element.");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Slot AnchorPoint={popper.anchorPoint} Position={popper.position} Visible={props.visible} ref={setContentRef}>
|
|
54
|
+
{child}
|
|
55
|
+
</Slot>
|
|
56
|
+
);
|
|
57
|
+
})()
|
|
58
|
+
) : (
|
|
59
|
+
<frame
|
|
60
|
+
AnchorPoint={popper.anchorPoint}
|
|
61
|
+
BackgroundTransparency={1}
|
|
62
|
+
BorderSizePixel={0}
|
|
63
|
+
Position={popper.position}
|
|
64
|
+
Size={UDim2.fromOffset(0, 0)}
|
|
65
|
+
Visible={props.visible}
|
|
66
|
+
ref={setContentRef}
|
|
67
|
+
>
|
|
68
|
+
{props.children}
|
|
69
|
+
</frame>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<DismissableLayer
|
|
74
|
+
enabled={props.enabled}
|
|
75
|
+
modal={false}
|
|
76
|
+
onDismiss={props.onDismiss}
|
|
77
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
78
|
+
onInteractOutside={props.onInteractOutside}
|
|
79
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
80
|
+
>
|
|
81
|
+
<RovingFocusGroup active={props.enabled} autoFocus="first" loop={comboboxContext.loop} orientation="vertical">
|
|
82
|
+
{contentNode}
|
|
83
|
+
</RovingFocusGroup>
|
|
84
|
+
</DismissableLayer>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function ComboboxContent(props: ComboboxContentProps) {
|
|
89
|
+
const comboboxContext = useComboboxContext();
|
|
90
|
+
const open = comboboxContext.open;
|
|
91
|
+
const forceMount = props.forceMount === true;
|
|
92
|
+
|
|
93
|
+
const handleDismiss = React.useCallback(() => {
|
|
94
|
+
comboboxContext.setOpen(false);
|
|
95
|
+
}, [comboboxContext]);
|
|
96
|
+
|
|
97
|
+
if (!open && !forceMount) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (forceMount) {
|
|
102
|
+
return (
|
|
103
|
+
<ComboboxContentImpl
|
|
104
|
+
asChild={props.asChild}
|
|
105
|
+
enabled={open}
|
|
106
|
+
offset={props.offset}
|
|
107
|
+
onDismiss={handleDismiss}
|
|
108
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
109
|
+
onInteractOutside={props.onInteractOutside}
|
|
110
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
111
|
+
padding={props.padding}
|
|
112
|
+
placement={props.placement}
|
|
113
|
+
visible={open}
|
|
114
|
+
>
|
|
115
|
+
{props.children}
|
|
116
|
+
</ComboboxContentImpl>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Presence
|
|
122
|
+
exitFallbackMs={0}
|
|
123
|
+
present={open}
|
|
124
|
+
render={(state) => (
|
|
125
|
+
<ComboboxContentImpl
|
|
126
|
+
asChild={props.asChild}
|
|
127
|
+
enabled={state.isPresent}
|
|
128
|
+
offset={props.offset}
|
|
129
|
+
onDismiss={handleDismiss}
|
|
130
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
131
|
+
onInteractOutside={props.onInteractOutside}
|
|
132
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
133
|
+
padding={props.padding}
|
|
134
|
+
placement={props.placement}
|
|
135
|
+
visible={state.isPresent}
|
|
136
|
+
>
|
|
137
|
+
{props.children}
|
|
138
|
+
</ComboboxContentImpl>
|
|
139
|
+
)}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import type { ComboboxGroupProps } from "./types";
|
|
3
|
+
|
|
4
|
+
export function ComboboxGroup(props: ComboboxGroupProps) {
|
|
5
|
+
if (props.asChild) {
|
|
6
|
+
const child = props.children;
|
|
7
|
+
if (!child) {
|
|
8
|
+
error("[ComboboxGroup] `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,98 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { useComboboxContext } from "./context";
|
|
3
|
+
import type { ComboboxInputProps } from "./types";
|
|
4
|
+
|
|
5
|
+
function toTextBox(instance: Instance | undefined) {
|
|
6
|
+
if (!instance || !instance.IsA("TextBox")) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return instance;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ComboboxInput(props: ComboboxInputProps) {
|
|
14
|
+
const comboboxContext = useComboboxContext();
|
|
15
|
+
const disabled = comboboxContext.disabled || props.disabled === true;
|
|
16
|
+
const readOnly = comboboxContext.readOnly || props.readOnly === true;
|
|
17
|
+
|
|
18
|
+
const setInputRef = React.useCallback(
|
|
19
|
+
(instance: Instance | undefined) => {
|
|
20
|
+
comboboxContext.inputRef.current = toTextBox(instance);
|
|
21
|
+
},
|
|
22
|
+
[comboboxContext.inputRef],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const handleTextChanged = React.useCallback(
|
|
26
|
+
(textBox: TextBox) => {
|
|
27
|
+
if (disabled || readOnly) {
|
|
28
|
+
if (textBox.Text !== comboboxContext.inputValue) {
|
|
29
|
+
textBox.Text = comboboxContext.inputValue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
comboboxContext.setInputValue(textBox.Text);
|
|
36
|
+
},
|
|
37
|
+
[comboboxContext, disabled, readOnly],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const handleFocusLost = React.useCallback(() => {
|
|
41
|
+
comboboxContext.setOpen(false);
|
|
42
|
+
comboboxContext.syncInputFromValue();
|
|
43
|
+
}, [comboboxContext]);
|
|
44
|
+
|
|
45
|
+
const handleInputBegan = React.useCallback(
|
|
46
|
+
(_rbx: GuiObject, inputObject: InputObject) => {
|
|
47
|
+
if (disabled) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const keyCode = inputObject.KeyCode;
|
|
52
|
+
if (keyCode === Enum.KeyCode.Down || keyCode === Enum.KeyCode.Up) {
|
|
53
|
+
comboboxContext.setOpen(true);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
[comboboxContext, disabled],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const sharedProps = {
|
|
60
|
+
Active: !disabled,
|
|
61
|
+
ClearTextOnFocus: false,
|
|
62
|
+
PlaceholderText: props.placeholder ?? "Type to filter",
|
|
63
|
+
Selectable: !disabled,
|
|
64
|
+
Text: comboboxContext.inputValue,
|
|
65
|
+
TextEditable: !disabled && !readOnly,
|
|
66
|
+
Change: {
|
|
67
|
+
Text: handleTextChanged,
|
|
68
|
+
},
|
|
69
|
+
Event: {
|
|
70
|
+
FocusLost: handleFocusLost,
|
|
71
|
+
InputBegan: handleInputBegan,
|
|
72
|
+
},
|
|
73
|
+
ref: setInputRef,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (props.asChild) {
|
|
77
|
+
const child = props.children;
|
|
78
|
+
if (!child) {
|
|
79
|
+
error("[ComboboxInput] `asChild` requires a child element.");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return <Slot {...sharedProps}>{child}</Slot>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<textbox
|
|
87
|
+
{...sharedProps}
|
|
88
|
+
BackgroundColor3={Color3.fromRGB(39, 46, 61)}
|
|
89
|
+
BorderSizePixel={0}
|
|
90
|
+
Size={UDim2.fromOffset(240, 36)}
|
|
91
|
+
TextColor3={disabled ? Color3.fromRGB(137, 145, 162) : Color3.fromRGB(235, 240, 248)}
|
|
92
|
+
TextSize={15}
|
|
93
|
+
TextXAlignment={Enum.TextXAlignment.Left}
|
|
94
|
+
>
|
|
95
|
+
<uipadding PaddingLeft={new UDim(0, 10)} PaddingRight={new UDim(0, 10)} />
|
|
96
|
+
</textbox>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { RovingFocusItem } from "@lattice-ui/focus";
|
|
3
|
+
import { useComboboxContext } from "./context";
|
|
4
|
+
import type { ComboboxItemProps } 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 ComboboxItem(props: ComboboxItemProps) {
|
|
18
|
+
const comboboxContext = useComboboxContext();
|
|
19
|
+
const itemRef = React.useRef<GuiObject>();
|
|
20
|
+
|
|
21
|
+
const itemQueryMatch = comboboxContext.filterFn(props.textValue ?? props.value, comboboxContext.inputValue);
|
|
22
|
+
const disabled = comboboxContext.disabled || props.disabled === true || !itemQueryMatch;
|
|
23
|
+
const textValue = props.textValue ?? props.value;
|
|
24
|
+
|
|
25
|
+
const disabledRef = React.useRef(disabled);
|
|
26
|
+
const textValueRef = React.useRef(textValue);
|
|
27
|
+
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
disabledRef.current = disabled;
|
|
30
|
+
}, [disabled]);
|
|
31
|
+
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
textValueRef.current = textValue;
|
|
34
|
+
}, [textValue]);
|
|
35
|
+
|
|
36
|
+
const itemIdRef = React.useRef(0);
|
|
37
|
+
if (itemIdRef.current === 0) {
|
|
38
|
+
nextItemId += 1;
|
|
39
|
+
itemIdRef.current = nextItemId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const itemOrderRef = React.useRef(0);
|
|
43
|
+
if (itemOrderRef.current === 0) {
|
|
44
|
+
nextItemOrder += 1;
|
|
45
|
+
itemOrderRef.current = nextItemOrder;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
return comboboxContext.registerItem({
|
|
50
|
+
id: itemIdRef.current,
|
|
51
|
+
value: props.value,
|
|
52
|
+
order: itemOrderRef.current,
|
|
53
|
+
getNode: () => itemRef.current,
|
|
54
|
+
getDisabled: () => disabledRef.current,
|
|
55
|
+
getTextValue: () => textValueRef.current,
|
|
56
|
+
});
|
|
57
|
+
}, [comboboxContext, props.value]);
|
|
58
|
+
|
|
59
|
+
const setItemRef = React.useCallback((instance: Instance | undefined) => {
|
|
60
|
+
itemRef.current = toGuiObject(instance);
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
const handleSelect = React.useCallback(() => {
|
|
64
|
+
if (disabled) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
comboboxContext.setValue(props.value);
|
|
69
|
+
comboboxContext.setOpen(false);
|
|
70
|
+
}, [comboboxContext, disabled, props.value]);
|
|
71
|
+
|
|
72
|
+
const handleInputBegan = React.useCallback(
|
|
73
|
+
(_rbx: GuiObject, inputObject: InputObject) => {
|
|
74
|
+
if (disabled) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const keyCode = inputObject.KeyCode;
|
|
79
|
+
if (keyCode !== Enum.KeyCode.Return && keyCode !== Enum.KeyCode.Space) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
comboboxContext.setValue(props.value);
|
|
84
|
+
comboboxContext.setOpen(false);
|
|
85
|
+
},
|
|
86
|
+
[comboboxContext, disabled, props.value],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const eventHandlers = React.useMemo(
|
|
90
|
+
() => ({
|
|
91
|
+
Activated: handleSelect,
|
|
92
|
+
InputBegan: handleInputBegan,
|
|
93
|
+
}),
|
|
94
|
+
[handleInputBegan, handleSelect],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (props.asChild) {
|
|
98
|
+
const child = props.children;
|
|
99
|
+
if (!child) {
|
|
100
|
+
error("[ComboboxItem] `asChild` requires a child element.");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<RovingFocusItem asChild disabled={disabled}>
|
|
105
|
+
<Slot Active={!disabled} Event={eventHandlers} Selectable={!disabled} Visible={itemQueryMatch} ref={setItemRef}>
|
|
106
|
+
{child}
|
|
107
|
+
</Slot>
|
|
108
|
+
</RovingFocusItem>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<RovingFocusItem asChild disabled={disabled}>
|
|
114
|
+
<textbutton
|
|
115
|
+
Active={!disabled}
|
|
116
|
+
AutoButtonColor={false}
|
|
117
|
+
BackgroundColor3={Color3.fromRGB(47, 53, 68)}
|
|
118
|
+
BorderSizePixel={0}
|
|
119
|
+
Event={eventHandlers}
|
|
120
|
+
Selectable={!disabled}
|
|
121
|
+
Size={UDim2.fromOffset(220, 32)}
|
|
122
|
+
Text={textValue}
|
|
123
|
+
TextColor3={disabled ? Color3.fromRGB(134, 141, 156) : Color3.fromRGB(234, 239, 247)}
|
|
124
|
+
TextSize={15}
|
|
125
|
+
TextXAlignment={Enum.TextXAlignment.Left}
|
|
126
|
+
Visible={itemQueryMatch}
|
|
127
|
+
ref={setItemRef}
|
|
128
|
+
>
|
|
129
|
+
<uipadding PaddingLeft={new UDim(0, 10)} PaddingRight={new UDim(0, 10)} />
|
|
130
|
+
{props.children}
|
|
131
|
+
</textbutton>
|
|
132
|
+
</RovingFocusItem>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import type { ComboboxLabelProps } from "./types";
|
|
3
|
+
|
|
4
|
+
export function ComboboxLabel(props: ComboboxLabelProps) {
|
|
5
|
+
if (props.asChild) {
|
|
6
|
+
const child = props.children;
|
|
7
|
+
if (!child) {
|
|
8
|
+
error("[ComboboxLabel] `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 { ComboboxPortalProps } from "./types";
|
|
4
|
+
|
|
5
|
+
function ComboboxPortalWithOverrides(props: ComboboxPortalProps) {
|
|
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 ComboboxPortal(props: ComboboxPortalProps) {
|
|
18
|
+
const hasOverrides = props.container !== undefined || props.displayOrderBase !== undefined;
|
|
19
|
+
if (hasOverrides) {
|
|
20
|
+
return (
|
|
21
|
+
<ComboboxPortalWithOverrides container={props.container} displayOrderBase={props.displayOrderBase}>
|
|
22
|
+
{props.children}
|
|
23
|
+
</ComboboxPortalWithOverrides>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return <Portal>{props.children}</Portal>;
|
|
28
|
+
}
|