@lattice-ui/combobox 0.4.3 → 0.5.0-next.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/default.project.json +9 -0
- package/out/Combobox/ComboboxContent.d.ts +1 -1
- package/out/Combobox/ComboboxContent.luau +35 -24
- package/out/Combobox/ComboboxInput.luau +5 -3
- package/out/Combobox/ComboboxItem.luau +3 -2
- package/out/Combobox/ComboboxRoot.luau +20 -4
- package/out/Combobox/logic.luau +1 -5
- package/out/Combobox/types.d.ts +2 -0
- package/package.json +18 -4
- package/src/Combobox/ComboboxContent.tsx +57 -36
- package/src/Combobox/ComboboxInput.tsx +6 -3
- package/src/Combobox/ComboboxItem.tsx +4 -2
- package/src/Combobox/ComboboxRoot.tsx +2 -0
- package/src/Combobox/logic.ts +1 -1
- package/src/Combobox/types.ts +2 -0
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-typecheck.log +0 -4
- package/tsconfig.json +0 -16
- package/tsconfig.typecheck.json +0 -35
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { React } from "@lattice-ui/core";
|
|
2
2
|
import type { ComboboxContentProps } from "./types";
|
|
3
|
-
export declare function ComboboxContent(props: ComboboxContentProps): React.JSX.Element
|
|
3
|
+
export declare function ComboboxContent(props: ComboboxContentProps): React.JSX.Element;
|
|
@@ -6,8 +6,12 @@ local Slot = _core.Slot
|
|
|
6
6
|
local _layer = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out)
|
|
7
7
|
local DismissableLayer = _layer.DismissableLayer
|
|
8
8
|
local Presence = _layer.Presence
|
|
9
|
+
local _motion = TS.import(script, TS.getModule(script, "@lattice-ui", "motion").out)
|
|
10
|
+
local createPopperEntranceRecipe = _motion.createPopperEntranceRecipe
|
|
11
|
+
local usePresenceMotion = _motion.usePresenceMotion
|
|
9
12
|
local usePopper = TS.import(script, TS.getModule(script, "@lattice-ui", "popper").out).usePopper
|
|
10
13
|
local useComboboxContext = TS.import(script, script.Parent, "context").useComboboxContext
|
|
14
|
+
local CONTENT_OFFSET = 6
|
|
11
15
|
local function toGuiObject(instance)
|
|
12
16
|
if not instance or not instance:IsA("GuiObject") then
|
|
13
17
|
return nil
|
|
@@ -16,17 +20,27 @@ local function toGuiObject(instance)
|
|
|
16
20
|
end
|
|
17
21
|
local function ComboboxContentImpl(props)
|
|
18
22
|
local comboboxContext = useComboboxContext()
|
|
23
|
+
local open = comboboxContext.open
|
|
19
24
|
local popper = usePopper({
|
|
20
25
|
anchorRef = comboboxContext.anchorRef,
|
|
21
26
|
contentRef = comboboxContext.contentRef,
|
|
22
27
|
placement = props.placement,
|
|
23
28
|
offset = props.offset,
|
|
24
29
|
padding = props.padding,
|
|
25
|
-
enabled =
|
|
30
|
+
enabled = open,
|
|
26
31
|
})
|
|
32
|
+
local defaultTransition = React.useMemo(function()
|
|
33
|
+
return createPopperEntranceRecipe(popper.placement, CONTENT_OFFSET)
|
|
34
|
+
end, { popper.placement })
|
|
35
|
+
local motionRef = usePresenceMotion(props.motionPresent and popper.isPositioned, props.transition or defaultTransition, props.onExitComplete)
|
|
27
36
|
local setContentRef = React.useCallback(function(instance)
|
|
28
37
|
comboboxContext.contentRef.current = toGuiObject(instance)
|
|
29
|
-
|
|
38
|
+
motionRef.current = toGuiObject(instance)
|
|
39
|
+
end, { comboboxContext.contentRef, motionRef })
|
|
40
|
+
local handleDismiss = React.useCallback(function()
|
|
41
|
+
comboboxContext.setOpen(false)
|
|
42
|
+
end, { comboboxContext })
|
|
43
|
+
local isActuallyVisible = open or (props.motionPresent and popper.isPositioned)
|
|
30
44
|
local contentNode = if props.asChild then ((function()
|
|
31
45
|
local child = props.children
|
|
32
46
|
if not React.isValidElement(child) then
|
|
@@ -34,65 +48,62 @@ local function ComboboxContentImpl(props)
|
|
|
34
48
|
end
|
|
35
49
|
return React.createElement(Slot, {
|
|
36
50
|
AnchorPoint = popper.anchorPoint,
|
|
37
|
-
|
|
38
|
-
Visible = props.visible,
|
|
51
|
+
Visible = isActuallyVisible,
|
|
39
52
|
ref = setContentRef,
|
|
40
53
|
}, child)
|
|
41
54
|
end)()) else (React.createElement("frame", {
|
|
42
55
|
AnchorPoint = popper.anchorPoint,
|
|
56
|
+
AutomaticSize = Enum.AutomaticSize.XY,
|
|
43
57
|
BackgroundTransparency = 1,
|
|
44
58
|
BorderSizePixel = 0,
|
|
45
|
-
Position = popper.position,
|
|
46
59
|
Size = UDim2.fromOffset(0, 0),
|
|
47
|
-
Visible =
|
|
60
|
+
Visible = isActuallyVisible,
|
|
48
61
|
ref = setContentRef,
|
|
49
62
|
}, props.children))
|
|
50
63
|
return React.createElement(DismissableLayer, {
|
|
51
|
-
enabled =
|
|
64
|
+
enabled = open,
|
|
52
65
|
insideRefs = { comboboxContext.triggerRef, comboboxContext.inputRef },
|
|
53
66
|
modal = false,
|
|
54
|
-
onDismiss =
|
|
67
|
+
onDismiss = handleDismiss,
|
|
55
68
|
onInteractOutside = props.onInteractOutside,
|
|
56
69
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
57
|
-
},
|
|
70
|
+
}, React.createElement("frame", {
|
|
71
|
+
BackgroundTransparency = 1,
|
|
72
|
+
BorderSizePixel = 0,
|
|
73
|
+
Position = if popper.isPositioned then popper.position else UDim2.fromOffset(-9999, -9999),
|
|
74
|
+
Size = UDim2.fromOffset(0, 0),
|
|
75
|
+
}, contentNode))
|
|
58
76
|
end
|
|
59
77
|
local function ComboboxContent(props)
|
|
60
78
|
local comboboxContext = useComboboxContext()
|
|
61
79
|
local open = comboboxContext.open
|
|
62
|
-
|
|
63
|
-
local handleDismiss = React.useCallback(function()
|
|
64
|
-
comboboxContext.setOpen(false)
|
|
65
|
-
end, { comboboxContext })
|
|
66
|
-
if not open and not forceMount then
|
|
67
|
-
return nil
|
|
68
|
-
end
|
|
69
|
-
if forceMount then
|
|
80
|
+
if props.forceMount then
|
|
70
81
|
return React.createElement(ComboboxContentImpl, {
|
|
71
82
|
asChild = props.asChild,
|
|
72
|
-
|
|
83
|
+
forceMount = props.forceMount,
|
|
84
|
+
motionPresent = open,
|
|
73
85
|
offset = props.offset,
|
|
74
|
-
onDismiss = handleDismiss,
|
|
75
86
|
onInteractOutside = props.onInteractOutside,
|
|
76
87
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
77
88
|
padding = props.padding,
|
|
78
89
|
placement = props.placement,
|
|
79
|
-
|
|
90
|
+
transition = props.transition,
|
|
80
91
|
}, props.children)
|
|
81
92
|
end
|
|
82
93
|
return React.createElement(Presence, {
|
|
83
|
-
exitFallbackMs = 0,
|
|
84
94
|
present = open,
|
|
85
95
|
render = function(state)
|
|
86
96
|
return React.createElement(ComboboxContentImpl, {
|
|
87
97
|
asChild = props.asChild,
|
|
88
|
-
|
|
98
|
+
forceMount = props.forceMount,
|
|
99
|
+
motionPresent = state.isPresent,
|
|
89
100
|
offset = props.offset,
|
|
90
|
-
|
|
101
|
+
onExitComplete = state.onExitComplete,
|
|
91
102
|
onInteractOutside = props.onInteractOutside,
|
|
92
103
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
93
104
|
padding = props.padding,
|
|
94
105
|
placement = props.placement,
|
|
95
|
-
|
|
106
|
+
transition = props.transition,
|
|
96
107
|
}, props.children)
|
|
97
108
|
end,
|
|
98
109
|
})
|
|
@@ -26,13 +26,15 @@ local function ComboboxInput(props)
|
|
|
26
26
|
comboboxContext.anchorRef.current = comboboxContext.triggerRef.current
|
|
27
27
|
end
|
|
28
28
|
end, { comboboxContext.anchorRef, comboboxContext.inputRef, comboboxContext.triggerRef })
|
|
29
|
+
local lastInputValueRef = React.useRef(comboboxContext.inputValue)
|
|
30
|
+
lastInputValueRef.current = comboboxContext.inputValue
|
|
29
31
|
local handleTextChanged = React.useCallback(function(textBox)
|
|
30
|
-
if textBox.Text ==
|
|
32
|
+
if textBox.Text == lastInputValueRef.current then
|
|
31
33
|
return nil
|
|
32
34
|
end
|
|
33
35
|
if disabled or readOnly then
|
|
34
|
-
if textBox.Text ~=
|
|
35
|
-
textBox.Text =
|
|
36
|
+
if textBox.Text ~= lastInputValueRef.current then
|
|
37
|
+
textBox.Text = lastInputValueRef.current
|
|
36
38
|
end
|
|
37
39
|
return nil
|
|
38
40
|
end
|
|
@@ -38,8 +38,9 @@ local function ComboboxItem(props)
|
|
|
38
38
|
nextItemOrder += 1
|
|
39
39
|
itemOrderRef.current = nextItemOrder
|
|
40
40
|
end
|
|
41
|
+
local registerItem = comboboxContext.registerItem
|
|
41
42
|
React.useEffect(function()
|
|
42
|
-
return
|
|
43
|
+
return registerItem({
|
|
43
44
|
id = itemIdRef.current,
|
|
44
45
|
value = props.value,
|
|
45
46
|
order = itemOrderRef.current,
|
|
@@ -50,7 +51,7 @@ local function ComboboxItem(props)
|
|
|
50
51
|
return textValueRef.current
|
|
51
52
|
end,
|
|
52
53
|
})
|
|
53
|
-
end, {
|
|
54
|
+
end, { registerItem, props.value })
|
|
54
55
|
local handleSelect = React.useCallback(function()
|
|
55
56
|
if interactionDisabled then
|
|
56
57
|
return nil
|
|
@@ -86,6 +86,21 @@ local function ComboboxRoot(props)
|
|
|
86
86
|
local itemTextCacheRef = React.useRef({})
|
|
87
87
|
local registryRevision, setRegistryRevision = React.useState(0)
|
|
88
88
|
local registerItem = React.useCallback(function(item)
|
|
89
|
+
local _exp = itemEntriesRef.current
|
|
90
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
91
|
+
local _newValue = {}
|
|
92
|
+
local _callback = function(entry)
|
|
93
|
+
return entry.id ~= item.id
|
|
94
|
+
end
|
|
95
|
+
local _length = 0
|
|
96
|
+
for _k, _v in _exp do
|
|
97
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
98
|
+
_length += 1
|
|
99
|
+
_newValue[_length] = _v
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
103
|
+
itemEntriesRef.current = _newValue
|
|
89
104
|
local _current = itemEntriesRef.current
|
|
90
105
|
local _item = item
|
|
91
106
|
table.insert(_current, _item)
|
|
@@ -94,14 +109,14 @@ local function ComboboxRoot(props)
|
|
|
94
109
|
return revision + 1
|
|
95
110
|
end)
|
|
96
111
|
return function()
|
|
97
|
-
local
|
|
112
|
+
local _exp_1 = itemEntriesRef.current
|
|
98
113
|
-- ▼ ReadonlyArray.findIndex ▼
|
|
99
|
-
local
|
|
114
|
+
local _callback_1 = function(entry)
|
|
100
115
|
return entry.id == item.id
|
|
101
116
|
end
|
|
102
117
|
local _result = -1
|
|
103
|
-
for _i, _v in
|
|
104
|
-
if
|
|
118
|
+
for _i, _v in _exp_1 do
|
|
119
|
+
if _callback_1(_v, _i - 1, _exp_1) == true then
|
|
105
120
|
_result = _i - 1
|
|
106
121
|
break
|
|
107
122
|
end
|
|
@@ -110,6 +125,7 @@ local function ComboboxRoot(props)
|
|
|
110
125
|
local index = _result
|
|
111
126
|
if index >= 0 then
|
|
112
127
|
table.remove(itemEntriesRef.current, index + 1)
|
|
128
|
+
itemTextCacheRef.current[item.value] = nil
|
|
113
129
|
setRegistryRevision(function(revision)
|
|
114
130
|
return revision + 1
|
|
115
131
|
end)
|
package/out/Combobox/logic.luau
CHANGED
|
@@ -23,11 +23,7 @@ local function resolveForcedComboboxValue(currentValue, options)
|
|
|
23
23
|
return nil
|
|
24
24
|
end
|
|
25
25
|
if currentValue == nil then
|
|
26
|
-
|
|
27
|
-
if _result ~= nil then
|
|
28
|
-
_result = _result.value
|
|
29
|
-
end
|
|
30
|
-
return _result
|
|
26
|
+
return nil
|
|
31
27
|
end
|
|
32
28
|
-- ▼ ReadonlyArray.find ▼
|
|
33
29
|
local _callback_1 = function(option)
|
package/out/Combobox/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
|
+
import type { PresenceMotionConfig as MotionConfig } from "@lattice-ui/motion";
|
|
2
3
|
import type { PopperPlacement } from "@lattice-ui/popper";
|
|
3
4
|
import type React from "@rbxts/react";
|
|
4
5
|
export type ComboboxFilterFn = (itemText: string, query: string) => boolean;
|
|
@@ -70,6 +71,7 @@ export type ComboboxPortalProps = {
|
|
|
70
71
|
children?: React.ReactNode;
|
|
71
72
|
};
|
|
72
73
|
export type ComboboxContentProps = {
|
|
74
|
+
transition?: MotionConfig;
|
|
73
75
|
asChild?: boolean;
|
|
74
76
|
forceMount?: boolean;
|
|
75
77
|
placement?: PopperPlacement;
|
package/package.json
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lattice-ui/combobox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-next.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "out/init.luau",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
7
|
+
"source": "src/index.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"default.project.json",
|
|
10
|
+
"out",
|
|
11
|
+
"src",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/astra-void/lattice-ui.git"
|
|
17
|
+
},
|
|
7
18
|
"dependencies": {
|
|
8
|
-
"@lattice-ui/core": "0.
|
|
9
|
-
"@lattice-ui/
|
|
10
|
-
"@lattice-ui/
|
|
19
|
+
"@lattice-ui/core": "0.5.0-next.1",
|
|
20
|
+
"@lattice-ui/layer": "0.5.0-next.1",
|
|
21
|
+
"@lattice-ui/motion": "0.5.0-next.1",
|
|
22
|
+
"@lattice-ui/popper": "0.5.0-next.1"
|
|
11
23
|
},
|
|
12
24
|
"devDependencies": {
|
|
13
25
|
"@rbxts/react": "17.3.7-ts.1",
|
|
@@ -19,6 +31,8 @@
|
|
|
19
31
|
},
|
|
20
32
|
"scripts": {
|
|
21
33
|
"build": "rbxtsc -p tsconfig.json",
|
|
34
|
+
"lint": "eslint .",
|
|
35
|
+
"lint:fix": "eslint . --fix",
|
|
22
36
|
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
23
37
|
"watch": "rbxtsc -p tsconfig.json -w"
|
|
24
38
|
}
|
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
3
|
import { DismissableLayer, Presence } from "@lattice-ui/layer";
|
|
4
|
+
import { createPopperEntranceRecipe, usePresenceMotion } from "@lattice-ui/motion";
|
|
5
|
+
import type { PopperPlacement } from "@lattice-ui/popper";
|
|
3
6
|
import { usePopper } from "@lattice-ui/popper";
|
|
4
7
|
import { useComboboxContext } from "./context";
|
|
5
8
|
import type { ComboboxContentProps } from "./types";
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
enabled: boolean;
|
|
9
|
-
visible: boolean;
|
|
10
|
-
onDismiss: () => void;
|
|
11
|
-
asChild?: boolean;
|
|
12
|
-
placement?: ComboboxContentProps["placement"];
|
|
13
|
-
offset?: ComboboxContentProps["offset"];
|
|
14
|
-
padding?: ComboboxContentProps["padding"];
|
|
15
|
-
} & Pick<ComboboxContentProps, "children" | "onInteractOutside" | "onPointerDownOutside">;
|
|
10
|
+
const CONTENT_OFFSET = 6;
|
|
16
11
|
|
|
17
12
|
function toGuiObject(instance: Instance | undefined) {
|
|
18
13
|
if (!instance || !instance.IsA("GuiObject")) {
|
|
19
14
|
return undefined;
|
|
20
15
|
}
|
|
21
|
-
|
|
22
16
|
return instance;
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
function ComboboxContentImpl(props:
|
|
19
|
+
function ComboboxContentImpl(props: {
|
|
20
|
+
motionPresent: boolean;
|
|
21
|
+
onExitComplete?: () => void;
|
|
22
|
+
placement?: PopperPlacement;
|
|
23
|
+
offset?: Vector2;
|
|
24
|
+
padding?: number;
|
|
25
|
+
forceMount?: boolean;
|
|
26
|
+
onPointerDownOutside?: (event: LayerInteractEvent) => void;
|
|
27
|
+
onInteractOutside?: (event: LayerInteractEvent) => void;
|
|
28
|
+
asChild?: boolean;
|
|
29
|
+
transition?: ComboboxContentProps["transition"];
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
}) {
|
|
26
32
|
const comboboxContext = useComboboxContext();
|
|
33
|
+
const open = comboboxContext.open;
|
|
27
34
|
|
|
28
35
|
const popper = usePopper({
|
|
29
36
|
anchorRef: comboboxContext.anchorRef,
|
|
@@ -31,16 +38,32 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
31
38
|
placement: props.placement,
|
|
32
39
|
offset: props.offset,
|
|
33
40
|
padding: props.padding,
|
|
34
|
-
enabled:
|
|
41
|
+
enabled: open,
|
|
35
42
|
});
|
|
36
43
|
|
|
44
|
+
const defaultTransition = React.useMemo(
|
|
45
|
+
() => createPopperEntranceRecipe(popper.placement, CONTENT_OFFSET),
|
|
46
|
+
[popper.placement],
|
|
47
|
+
);
|
|
48
|
+
const motionRef = usePresenceMotion<GuiObject>(
|
|
49
|
+
props.motionPresent && popper.isPositioned,
|
|
50
|
+
props.transition ?? defaultTransition,
|
|
51
|
+
props.onExitComplete,
|
|
52
|
+
);
|
|
53
|
+
|
|
37
54
|
const setContentRef = React.useCallback(
|
|
38
55
|
(instance: Instance | undefined) => {
|
|
39
56
|
comboboxContext.contentRef.current = toGuiObject(instance);
|
|
57
|
+
motionRef.current = toGuiObject(instance);
|
|
40
58
|
},
|
|
41
|
-
[comboboxContext.contentRef],
|
|
59
|
+
[comboboxContext.contentRef, motionRef],
|
|
42
60
|
);
|
|
43
61
|
|
|
62
|
+
const handleDismiss = React.useCallback(() => {
|
|
63
|
+
comboboxContext.setOpen(false);
|
|
64
|
+
}, [comboboxContext]);
|
|
65
|
+
const isActuallyVisible = open || (props.motionPresent && popper.isPositioned);
|
|
66
|
+
|
|
44
67
|
const contentNode = props.asChild ? (
|
|
45
68
|
(() => {
|
|
46
69
|
const child = props.children;
|
|
@@ -49,7 +72,7 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
49
72
|
}
|
|
50
73
|
|
|
51
74
|
return (
|
|
52
|
-
<Slot AnchorPoint={popper.anchorPoint}
|
|
75
|
+
<Slot AnchorPoint={popper.anchorPoint} Visible={isActuallyVisible} ref={setContentRef}>
|
|
53
76
|
{child}
|
|
54
77
|
</Slot>
|
|
55
78
|
);
|
|
@@ -57,11 +80,11 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
57
80
|
) : (
|
|
58
81
|
<frame
|
|
59
82
|
AnchorPoint={popper.anchorPoint}
|
|
83
|
+
AutomaticSize={Enum.AutomaticSize.XY}
|
|
60
84
|
BackgroundTransparency={1}
|
|
61
85
|
BorderSizePixel={0}
|
|
62
|
-
Position={popper.position}
|
|
63
86
|
Size={UDim2.fromOffset(0, 0)}
|
|
64
|
-
Visible={
|
|
87
|
+
Visible={isActuallyVisible}
|
|
65
88
|
ref={setContentRef}
|
|
66
89
|
>
|
|
67
90
|
{props.children}
|
|
@@ -70,14 +93,21 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
70
93
|
|
|
71
94
|
return (
|
|
72
95
|
<DismissableLayer
|
|
73
|
-
enabled={
|
|
96
|
+
enabled={open}
|
|
74
97
|
insideRefs={[comboboxContext.triggerRef, comboboxContext.inputRef]}
|
|
75
98
|
modal={false}
|
|
76
|
-
onDismiss={
|
|
99
|
+
onDismiss={handleDismiss}
|
|
77
100
|
onInteractOutside={props.onInteractOutside}
|
|
78
101
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
79
102
|
>
|
|
80
|
-
|
|
103
|
+
<frame
|
|
104
|
+
BackgroundTransparency={1}
|
|
105
|
+
BorderSizePixel={0}
|
|
106
|
+
Position={popper.isPositioned ? popper.position : UDim2.fromOffset(-9999, -9999)}
|
|
107
|
+
Size={UDim2.fromOffset(0, 0)}
|
|
108
|
+
>
|
|
109
|
+
{contentNode}
|
|
110
|
+
</frame>
|
|
81
111
|
</DismissableLayer>
|
|
82
112
|
);
|
|
83
113
|
}
|
|
@@ -85,28 +115,19 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
85
115
|
export function ComboboxContent(props: ComboboxContentProps) {
|
|
86
116
|
const comboboxContext = useComboboxContext();
|
|
87
117
|
const open = comboboxContext.open;
|
|
88
|
-
const forceMount = props.forceMount === true;
|
|
89
|
-
|
|
90
|
-
const handleDismiss = React.useCallback(() => {
|
|
91
|
-
comboboxContext.setOpen(false);
|
|
92
|
-
}, [comboboxContext]);
|
|
93
|
-
|
|
94
|
-
if (!open && !forceMount) {
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
118
|
|
|
98
|
-
if (forceMount) {
|
|
119
|
+
if (props.forceMount) {
|
|
99
120
|
return (
|
|
100
121
|
<ComboboxContentImpl
|
|
101
122
|
asChild={props.asChild}
|
|
102
|
-
|
|
123
|
+
forceMount={props.forceMount}
|
|
124
|
+
motionPresent={open}
|
|
103
125
|
offset={props.offset}
|
|
104
|
-
onDismiss={handleDismiss}
|
|
105
126
|
onInteractOutside={props.onInteractOutside}
|
|
106
127
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
107
128
|
padding={props.padding}
|
|
108
129
|
placement={props.placement}
|
|
109
|
-
|
|
130
|
+
transition={props.transition}
|
|
110
131
|
>
|
|
111
132
|
{props.children}
|
|
112
133
|
</ComboboxContentImpl>
|
|
@@ -115,19 +136,19 @@ export function ComboboxContent(props: ComboboxContentProps) {
|
|
|
115
136
|
|
|
116
137
|
return (
|
|
117
138
|
<Presence
|
|
118
|
-
exitFallbackMs={0}
|
|
119
139
|
present={open}
|
|
120
140
|
render={(state) => (
|
|
121
141
|
<ComboboxContentImpl
|
|
122
142
|
asChild={props.asChild}
|
|
123
|
-
|
|
143
|
+
forceMount={props.forceMount}
|
|
144
|
+
motionPresent={state.isPresent}
|
|
124
145
|
offset={props.offset}
|
|
125
|
-
|
|
146
|
+
onExitComplete={state.onExitComplete}
|
|
126
147
|
onInteractOutside={props.onInteractOutside}
|
|
127
148
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
128
149
|
padding={props.padding}
|
|
129
150
|
placement={props.placement}
|
|
130
|
-
|
|
151
|
+
transition={props.transition}
|
|
131
152
|
>
|
|
132
153
|
{props.children}
|
|
133
154
|
</ComboboxContentImpl>
|
|
@@ -34,15 +34,18 @@ export function ComboboxInput(props: ComboboxInputProps) {
|
|
|
34
34
|
[comboboxContext.anchorRef, comboboxContext.inputRef, comboboxContext.triggerRef],
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
+
const lastInputValueRef = React.useRef(comboboxContext.inputValue);
|
|
38
|
+
lastInputValueRef.current = comboboxContext.inputValue;
|
|
39
|
+
|
|
37
40
|
const handleTextChanged = React.useCallback(
|
|
38
41
|
(textBox: TextBox) => {
|
|
39
|
-
if (textBox.Text ===
|
|
42
|
+
if (textBox.Text === lastInputValueRef.current) {
|
|
40
43
|
return;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
if (disabled || readOnly) {
|
|
44
|
-
if (textBox.Text !==
|
|
45
|
-
textBox.Text =
|
|
47
|
+
if (textBox.Text !== lastInputValueRef.current) {
|
|
48
|
+
textBox.Text = lastInputValueRef.current;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
return;
|
|
@@ -35,15 +35,17 @@ export function ComboboxItem(props: ComboboxItemProps) {
|
|
|
35
35
|
itemOrderRef.current = nextItemOrder;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const registerItem = comboboxContext.registerItem;
|
|
39
|
+
|
|
38
40
|
React.useEffect(() => {
|
|
39
|
-
return
|
|
41
|
+
return registerItem({
|
|
40
42
|
id: itemIdRef.current,
|
|
41
43
|
value: props.value,
|
|
42
44
|
order: itemOrderRef.current,
|
|
43
45
|
getDisabled: () => disabledRef.current,
|
|
44
46
|
getTextValue: () => textValueRef.current,
|
|
45
47
|
});
|
|
46
|
-
}, [
|
|
48
|
+
}, [registerItem, props.value]);
|
|
47
49
|
|
|
48
50
|
const handleSelect = React.useCallback(() => {
|
|
49
51
|
if (interactionDisabled) {
|
|
@@ -55,6 +55,7 @@ export function ComboboxRoot(props: ComboboxProps) {
|
|
|
55
55
|
const [registryRevision, setRegistryRevision] = React.useState(0);
|
|
56
56
|
|
|
57
57
|
const registerItem = React.useCallback((item: ComboboxItemRegistration) => {
|
|
58
|
+
itemEntriesRef.current = itemEntriesRef.current.filter((entry) => entry.id !== item.id);
|
|
58
59
|
itemEntriesRef.current.push(item);
|
|
59
60
|
itemTextCacheRef.current[item.value] = item.getTextValue();
|
|
60
61
|
setRegistryRevision((revision) => revision + 1);
|
|
@@ -63,6 +64,7 @@ export function ComboboxRoot(props: ComboboxProps) {
|
|
|
63
64
|
const index = itemEntriesRef.current.findIndex((entry) => entry.id === item.id);
|
|
64
65
|
if (index >= 0) {
|
|
65
66
|
itemEntriesRef.current.remove(index);
|
|
67
|
+
delete itemTextCacheRef.current[item.value];
|
|
66
68
|
setRegistryRevision((revision) => revision + 1);
|
|
67
69
|
}
|
|
68
70
|
};
|
package/src/Combobox/logic.ts
CHANGED
|
@@ -19,7 +19,7 @@ export function resolveForcedComboboxValue(currentValue: string | undefined, opt
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
if (currentValue === undefined) {
|
|
22
|
-
return
|
|
22
|
+
return undefined;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const selected = enabled.find((option) => option.value === currentValue);
|
package/src/Combobox/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
|
+
import type { PresenceMotionConfig as MotionConfig } from "@lattice-ui/motion";
|
|
2
3
|
import type { PopperPlacement } from "@lattice-ui/popper";
|
|
3
4
|
import type React from "@rbxts/react";
|
|
4
5
|
|
|
@@ -80,6 +81,7 @@ export type ComboboxPortalProps = {
|
|
|
80
81
|
};
|
|
81
82
|
|
|
82
83
|
export type ComboboxContentProps = {
|
|
84
|
+
transition?: MotionConfig;
|
|
83
85
|
asChild?: boolean;
|
|
84
86
|
forceMount?: boolean;
|
|
85
87
|
placement?: PopperPlacement;
|
package/.turbo/turbo-build.log
DELETED
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
}
|
package/tsconfig.typecheck.json
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
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
|
-
}
|