@lattice-ui/combobox 0.4.4 → 0.5.0-next.2
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 +71 -31
- 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 +96 -43
- 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;
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local composeRefs = _core.composeRefs
|
|
5
|
+
local getElementRef = _core.getElementRef
|
|
4
6
|
local React = _core.React
|
|
5
|
-
local Slot = _core.Slot
|
|
6
7
|
local _layer = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out)
|
|
7
8
|
local DismissableLayer = _layer.DismissableLayer
|
|
8
9
|
local Presence = _layer.Presence
|
|
10
|
+
local _motion = TS.import(script, TS.getModule(script, "@lattice-ui", "motion").out)
|
|
11
|
+
local createCanvasGroupPopperEntranceRecipe = _motion.createCanvasGroupPopperEntranceRecipe
|
|
12
|
+
local usePresenceMotionController = _motion.usePresenceMotionController
|
|
9
13
|
local usePopper = TS.import(script, TS.getModule(script, "@lattice-ui", "popper").out).usePopper
|
|
10
14
|
local useComboboxContext = TS.import(script, script.Parent, "context").useComboboxContext
|
|
15
|
+
local CONTENT_OFFSET = 6
|
|
16
|
+
local HIDDEN_POSITION = UDim2.fromOffset(-9999, -9999)
|
|
17
|
+
local function toGuiPropBag(value)
|
|
18
|
+
local _value = value
|
|
19
|
+
return if type(_value) == "table" then value else {}
|
|
20
|
+
end
|
|
11
21
|
local function toGuiObject(instance)
|
|
12
22
|
if not instance or not instance:IsA("GuiObject") then
|
|
13
23
|
return nil
|
|
@@ -16,83 +26,113 @@ local function toGuiObject(instance)
|
|
|
16
26
|
end
|
|
17
27
|
local function ComboboxContentImpl(props)
|
|
18
28
|
local comboboxContext = useComboboxContext()
|
|
29
|
+
local open = comboboxContext.open
|
|
30
|
+
local shouldMeasure = open or props.motionPresent or props.onExitComplete ~= nil
|
|
19
31
|
local popper = usePopper({
|
|
20
32
|
anchorRef = comboboxContext.anchorRef,
|
|
21
33
|
contentRef = comboboxContext.contentRef,
|
|
22
34
|
placement = props.placement,
|
|
23
35
|
offset = props.offset,
|
|
24
36
|
padding = props.padding,
|
|
25
|
-
enabled =
|
|
37
|
+
enabled = shouldMeasure,
|
|
38
|
+
})
|
|
39
|
+
local defaultTransition = React.useMemo(function()
|
|
40
|
+
return createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET)
|
|
41
|
+
end, { popper.placement })
|
|
42
|
+
local recipe = props.transition or defaultTransition
|
|
43
|
+
local motion = usePresenceMotionController({
|
|
44
|
+
present = props.motionPresent,
|
|
45
|
+
ready = popper.isPositioned,
|
|
46
|
+
forceMount = props.forceMount,
|
|
47
|
+
config = recipe,
|
|
48
|
+
onExitComplete = props.onExitComplete,
|
|
26
49
|
})
|
|
27
50
|
local setContentRef = React.useCallback(function(instance)
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
local guiObject = toGuiObject(instance)
|
|
52
|
+
comboboxContext.contentRef.current = guiObject
|
|
53
|
+
motion.ref.current = guiObject
|
|
54
|
+
end, { comboboxContext.contentRef, motion.ref })
|
|
55
|
+
local handleDismiss = React.useCallback(function()
|
|
56
|
+
comboboxContext.setOpen(false)
|
|
57
|
+
end, { comboboxContext })
|
|
58
|
+
local shouldRender = motion.mounted
|
|
59
|
+
local contentVisible = shouldRender and (motion.present or motion.phase ~= "exited")
|
|
60
|
+
local popperPosition = if popper.isPositioned then popper.position else HIDDEN_POSITION
|
|
30
61
|
local contentNode = if props.asChild then ((function()
|
|
31
62
|
local child = props.children
|
|
32
63
|
if not React.isValidElement(child) then
|
|
33
64
|
error("[ComboboxContent] `asChild` requires a child element.")
|
|
34
65
|
end
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
local childProps = toGuiPropBag(child.props)
|
|
67
|
+
local childRef = getElementRef(child)
|
|
68
|
+
local _exp = child
|
|
69
|
+
local _object = table.clone(childProps)
|
|
70
|
+
setmetatable(_object, nil)
|
|
71
|
+
_object.Position = UDim2.fromOffset(0, 0)
|
|
72
|
+
_object.Visible = contentVisible
|
|
73
|
+
_object.ref = composeRefs(childRef)
|
|
74
|
+
return React.createElement("canvasgroup", {
|
|
75
|
+
AutomaticSize = Enum.AutomaticSize.XY,
|
|
76
|
+
BackgroundTransparency = 1,
|
|
77
|
+
BorderSizePixel = 0,
|
|
78
|
+
Size = UDim2.fromOffset(0, 0),
|
|
79
|
+
Visible = contentVisible,
|
|
39
80
|
ref = setContentRef,
|
|
40
|
-
},
|
|
41
|
-
end)()) else (React.createElement("
|
|
42
|
-
|
|
81
|
+
}, React.cloneElement(_exp, _object))
|
|
82
|
+
end)()) else (React.createElement("canvasgroup", {
|
|
83
|
+
AutomaticSize = Enum.AutomaticSize.XY,
|
|
43
84
|
BackgroundTransparency = 1,
|
|
44
85
|
BorderSizePixel = 0,
|
|
45
|
-
Position = popper.position,
|
|
46
86
|
Size = UDim2.fromOffset(0, 0),
|
|
47
|
-
Visible =
|
|
87
|
+
Visible = contentVisible,
|
|
48
88
|
ref = setContentRef,
|
|
49
89
|
}, props.children))
|
|
50
90
|
return React.createElement(DismissableLayer, {
|
|
51
|
-
enabled =
|
|
91
|
+
enabled = open,
|
|
52
92
|
insideRefs = { comboboxContext.triggerRef, comboboxContext.inputRef },
|
|
53
93
|
modal = false,
|
|
54
|
-
onDismiss =
|
|
94
|
+
onDismiss = handleDismiss,
|
|
55
95
|
onInteractOutside = props.onInteractOutside,
|
|
56
96
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
57
|
-
},
|
|
97
|
+
}, React.createElement("frame", {
|
|
98
|
+
AnchorPoint = popper.anchorPoint,
|
|
99
|
+
BackgroundTransparency = 1,
|
|
100
|
+
BorderSizePixel = 0,
|
|
101
|
+
Position = popperPosition,
|
|
102
|
+
Size = UDim2.fromOffset(0, 0),
|
|
103
|
+
Visible = shouldRender,
|
|
104
|
+
}, contentNode))
|
|
58
105
|
end
|
|
59
106
|
local function ComboboxContent(props)
|
|
60
107
|
local comboboxContext = useComboboxContext()
|
|
61
108
|
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
|
|
109
|
+
if props.forceMount then
|
|
70
110
|
return React.createElement(ComboboxContentImpl, {
|
|
71
111
|
asChild = props.asChild,
|
|
72
|
-
|
|
112
|
+
forceMount = props.forceMount,
|
|
113
|
+
motionPresent = open,
|
|
73
114
|
offset = props.offset,
|
|
74
|
-
onDismiss = handleDismiss,
|
|
75
115
|
onInteractOutside = props.onInteractOutside,
|
|
76
116
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
77
117
|
padding = props.padding,
|
|
78
118
|
placement = props.placement,
|
|
79
|
-
|
|
119
|
+
transition = props.transition,
|
|
80
120
|
}, props.children)
|
|
81
121
|
end
|
|
82
122
|
return React.createElement(Presence, {
|
|
83
|
-
exitFallbackMs = 0,
|
|
84
123
|
present = open,
|
|
85
124
|
render = function(state)
|
|
86
125
|
return React.createElement(ComboboxContentImpl, {
|
|
87
126
|
asChild = props.asChild,
|
|
88
|
-
|
|
127
|
+
forceMount = props.forceMount,
|
|
128
|
+
motionPresent = state.isPresent,
|
|
89
129
|
offset = props.offset,
|
|
90
|
-
|
|
130
|
+
onExitComplete = state.onExitComplete,
|
|
91
131
|
onInteractOutside = props.onInteractOutside,
|
|
92
132
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
93
133
|
padding = props.padding,
|
|
94
134
|
placement = props.placement,
|
|
95
|
-
|
|
135
|
+
transition = props.transition,
|
|
96
136
|
}, props.children)
|
|
97
137
|
end,
|
|
98
138
|
})
|
|
@@ -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.2",
|
|
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.2",
|
|
20
|
+
"@lattice-ui/motion": "0.5.0-next.2",
|
|
21
|
+
"@lattice-ui/popper": "0.5.0-next.2",
|
|
22
|
+
"@lattice-ui/layer": "0.5.0-next.2"
|
|
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,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { composeRefs, getElementRef, React } from "@lattice-ui/core";
|
|
2
|
+
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
3
|
import { DismissableLayer, Presence } from "@lattice-ui/layer";
|
|
4
|
+
import { createCanvasGroupPopperEntranceRecipe, usePresenceMotionController } 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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} & Pick<ComboboxContentProps, "children" | "onInteractOutside" | "onPointerDownOutside">;
|
|
10
|
+
const CONTENT_OFFSET = 6;
|
|
11
|
+
const HIDDEN_POSITION = UDim2.fromOffset(-9999, -9999);
|
|
12
|
+
|
|
13
|
+
type GuiPropBag = React.Attributes & Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
function toGuiPropBag(value: unknown): GuiPropBag {
|
|
16
|
+
return typeIs(value, "table") ? (value as GuiPropBag) : {};
|
|
17
|
+
}
|
|
16
18
|
|
|
17
19
|
function toGuiObject(instance: Instance | undefined) {
|
|
18
20
|
if (!instance || !instance.IsA("GuiObject")) {
|
|
19
21
|
return undefined;
|
|
20
22
|
}
|
|
21
|
-
|
|
22
23
|
return instance;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function ComboboxContentImpl(props:
|
|
26
|
+
function ComboboxContentImpl(props: {
|
|
27
|
+
motionPresent: boolean;
|
|
28
|
+
onExitComplete?: () => void;
|
|
29
|
+
placement?: PopperPlacement;
|
|
30
|
+
offset?: Vector2;
|
|
31
|
+
padding?: number;
|
|
32
|
+
forceMount?: boolean;
|
|
33
|
+
onPointerDownOutside?: (event: LayerInteractEvent) => void;
|
|
34
|
+
onInteractOutside?: (event: LayerInteractEvent) => void;
|
|
35
|
+
asChild?: boolean;
|
|
36
|
+
transition?: ComboboxContentProps["transition"];
|
|
37
|
+
children?: React.ReactNode;
|
|
38
|
+
}) {
|
|
26
39
|
const comboboxContext = useComboboxContext();
|
|
40
|
+
const open = comboboxContext.open;
|
|
41
|
+
const shouldMeasure = open || props.motionPresent || props.onExitComplete !== undefined;
|
|
27
42
|
|
|
28
43
|
const popper = usePopper({
|
|
29
44
|
anchorRef: comboboxContext.anchorRef,
|
|
@@ -31,16 +46,40 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
31
46
|
placement: props.placement,
|
|
32
47
|
offset: props.offset,
|
|
33
48
|
padding: props.padding,
|
|
34
|
-
enabled:
|
|
49
|
+
enabled: shouldMeasure,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const defaultTransition = React.useMemo(
|
|
53
|
+
() => createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET),
|
|
54
|
+
[popper.placement],
|
|
55
|
+
);
|
|
56
|
+
const recipe = props.transition ?? defaultTransition;
|
|
57
|
+
|
|
58
|
+
const motion = usePresenceMotionController<GuiObject>({
|
|
59
|
+
present: props.motionPresent,
|
|
60
|
+
ready: popper.isPositioned,
|
|
61
|
+
forceMount: props.forceMount,
|
|
62
|
+
config: recipe,
|
|
63
|
+
onExitComplete: props.onExitComplete,
|
|
35
64
|
});
|
|
36
65
|
|
|
37
66
|
const setContentRef = React.useCallback(
|
|
38
67
|
(instance: Instance | undefined) => {
|
|
39
|
-
|
|
68
|
+
const guiObject = toGuiObject(instance);
|
|
69
|
+
comboboxContext.contentRef.current = guiObject;
|
|
70
|
+
motion.ref.current = guiObject;
|
|
40
71
|
},
|
|
41
|
-
[comboboxContext.contentRef],
|
|
72
|
+
[comboboxContext.contentRef, motion.ref],
|
|
42
73
|
);
|
|
43
74
|
|
|
75
|
+
const handleDismiss = React.useCallback(() => {
|
|
76
|
+
comboboxContext.setOpen(false);
|
|
77
|
+
}, [comboboxContext]);
|
|
78
|
+
|
|
79
|
+
const shouldRender = motion.mounted;
|
|
80
|
+
const contentVisible = shouldRender && (motion.present || motion.phase !== "exited");
|
|
81
|
+
const popperPosition = popper.isPositioned ? popper.position : HIDDEN_POSITION;
|
|
82
|
+
|
|
44
83
|
const contentNode = props.asChild ? (
|
|
45
84
|
(() => {
|
|
46
85
|
const child = props.children;
|
|
@@ -48,36 +87,59 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
48
87
|
error("[ComboboxContent] `asChild` requires a child element.");
|
|
49
88
|
}
|
|
50
89
|
|
|
90
|
+
const childProps = toGuiPropBag((child as { props?: unknown }).props);
|
|
91
|
+
const childRef = getElementRef<Instance>(child);
|
|
92
|
+
|
|
51
93
|
return (
|
|
52
|
-
<
|
|
53
|
-
{
|
|
54
|
-
|
|
94
|
+
<canvasgroup
|
|
95
|
+
AutomaticSize={Enum.AutomaticSize.XY}
|
|
96
|
+
BackgroundTransparency={1}
|
|
97
|
+
BorderSizePixel={0}
|
|
98
|
+
Size={UDim2.fromOffset(0, 0)}
|
|
99
|
+
Visible={contentVisible}
|
|
100
|
+
ref={setContentRef as React.Ref<CanvasGroup>}
|
|
101
|
+
>
|
|
102
|
+
{React.cloneElement(child as React.ReactElement<GuiPropBag>, {
|
|
103
|
+
...childProps,
|
|
104
|
+
Position: UDim2.fromOffset(0, 0),
|
|
105
|
+
Visible: contentVisible,
|
|
106
|
+
ref: composeRefs(childRef),
|
|
107
|
+
})}
|
|
108
|
+
</canvasgroup>
|
|
55
109
|
);
|
|
56
110
|
})()
|
|
57
111
|
) : (
|
|
58
|
-
<
|
|
59
|
-
|
|
112
|
+
<canvasgroup
|
|
113
|
+
AutomaticSize={Enum.AutomaticSize.XY}
|
|
60
114
|
BackgroundTransparency={1}
|
|
61
115
|
BorderSizePixel={0}
|
|
62
|
-
Position={popper.position}
|
|
63
116
|
Size={UDim2.fromOffset(0, 0)}
|
|
64
|
-
Visible={
|
|
117
|
+
Visible={contentVisible}
|
|
65
118
|
ref={setContentRef}
|
|
66
119
|
>
|
|
67
120
|
{props.children}
|
|
68
|
-
</
|
|
121
|
+
</canvasgroup>
|
|
69
122
|
);
|
|
70
123
|
|
|
71
124
|
return (
|
|
72
125
|
<DismissableLayer
|
|
73
|
-
enabled={
|
|
126
|
+
enabled={open}
|
|
74
127
|
insideRefs={[comboboxContext.triggerRef, comboboxContext.inputRef]}
|
|
75
128
|
modal={false}
|
|
76
|
-
onDismiss={
|
|
129
|
+
onDismiss={handleDismiss}
|
|
77
130
|
onInteractOutside={props.onInteractOutside}
|
|
78
131
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
79
132
|
>
|
|
80
|
-
|
|
133
|
+
<frame
|
|
134
|
+
AnchorPoint={popper.anchorPoint}
|
|
135
|
+
BackgroundTransparency={1}
|
|
136
|
+
BorderSizePixel={0}
|
|
137
|
+
Position={popperPosition}
|
|
138
|
+
Size={UDim2.fromOffset(0, 0)}
|
|
139
|
+
Visible={shouldRender}
|
|
140
|
+
>
|
|
141
|
+
{contentNode}
|
|
142
|
+
</frame>
|
|
81
143
|
</DismissableLayer>
|
|
82
144
|
);
|
|
83
145
|
}
|
|
@@ -85,28 +147,19 @@ function ComboboxContentImpl(props: ComboboxContentImplProps) {
|
|
|
85
147
|
export function ComboboxContent(props: ComboboxContentProps) {
|
|
86
148
|
const comboboxContext = useComboboxContext();
|
|
87
149
|
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
150
|
|
|
98
|
-
if (forceMount) {
|
|
151
|
+
if (props.forceMount) {
|
|
99
152
|
return (
|
|
100
153
|
<ComboboxContentImpl
|
|
101
154
|
asChild={props.asChild}
|
|
102
|
-
|
|
155
|
+
forceMount={props.forceMount}
|
|
156
|
+
motionPresent={open}
|
|
103
157
|
offset={props.offset}
|
|
104
|
-
onDismiss={handleDismiss}
|
|
105
158
|
onInteractOutside={props.onInteractOutside}
|
|
106
159
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
107
160
|
padding={props.padding}
|
|
108
161
|
placement={props.placement}
|
|
109
|
-
|
|
162
|
+
transition={props.transition}
|
|
110
163
|
>
|
|
111
164
|
{props.children}
|
|
112
165
|
</ComboboxContentImpl>
|
|
@@ -115,19 +168,19 @@ export function ComboboxContent(props: ComboboxContentProps) {
|
|
|
115
168
|
|
|
116
169
|
return (
|
|
117
170
|
<Presence
|
|
118
|
-
exitFallbackMs={0}
|
|
119
171
|
present={open}
|
|
120
172
|
render={(state) => (
|
|
121
173
|
<ComboboxContentImpl
|
|
122
174
|
asChild={props.asChild}
|
|
123
|
-
|
|
175
|
+
forceMount={props.forceMount}
|
|
176
|
+
motionPresent={state.isPresent}
|
|
124
177
|
offset={props.offset}
|
|
125
|
-
|
|
178
|
+
onExitComplete={state.onExitComplete}
|
|
126
179
|
onInteractOutside={props.onInteractOutside}
|
|
127
180
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
128
181
|
padding={props.padding}
|
|
129
182
|
placement={props.placement}
|
|
130
|
-
|
|
183
|
+
transition={props.transition}
|
|
131
184
|
>
|
|
132
185
|
{props.children}
|
|
133
186
|
</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
|
-
}
|