@lattice-ui/combobox 0.5.0-next.1 → 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.
|
@@ -1,17 +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
|
|
9
10
|
local _motion = TS.import(script, TS.getModule(script, "@lattice-ui", "motion").out)
|
|
10
|
-
local
|
|
11
|
-
local
|
|
11
|
+
local createCanvasGroupPopperEntranceRecipe = _motion.createCanvasGroupPopperEntranceRecipe
|
|
12
|
+
local usePresenceMotionController = _motion.usePresenceMotionController
|
|
12
13
|
local usePopper = TS.import(script, TS.getModule(script, "@lattice-ui", "popper").out).usePopper
|
|
13
14
|
local useComboboxContext = TS.import(script, script.Parent, "context").useComboboxContext
|
|
14
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
|
|
15
21
|
local function toGuiObject(instance)
|
|
16
22
|
if not instance or not instance:IsA("GuiObject") then
|
|
17
23
|
return nil
|
|
@@ -21,43 +27,64 @@ end
|
|
|
21
27
|
local function ComboboxContentImpl(props)
|
|
22
28
|
local comboboxContext = useComboboxContext()
|
|
23
29
|
local open = comboboxContext.open
|
|
30
|
+
local shouldMeasure = open or props.motionPresent or props.onExitComplete ~= nil
|
|
24
31
|
local popper = usePopper({
|
|
25
32
|
anchorRef = comboboxContext.anchorRef,
|
|
26
33
|
contentRef = comboboxContext.contentRef,
|
|
27
34
|
placement = props.placement,
|
|
28
35
|
offset = props.offset,
|
|
29
36
|
padding = props.padding,
|
|
30
|
-
enabled =
|
|
37
|
+
enabled = shouldMeasure,
|
|
31
38
|
})
|
|
32
39
|
local defaultTransition = React.useMemo(function()
|
|
33
|
-
return
|
|
40
|
+
return createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET)
|
|
34
41
|
end, { popper.placement })
|
|
35
|
-
local
|
|
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,
|
|
49
|
+
})
|
|
36
50
|
local setContentRef = React.useCallback(function(instance)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
local guiObject = toGuiObject(instance)
|
|
52
|
+
comboboxContext.contentRef.current = guiObject
|
|
53
|
+
motion.ref.current = guiObject
|
|
54
|
+
end, { comboboxContext.contentRef, motion.ref })
|
|
40
55
|
local handleDismiss = React.useCallback(function()
|
|
41
56
|
comboboxContext.setOpen(false)
|
|
42
57
|
end, { comboboxContext })
|
|
43
|
-
local
|
|
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
|
|
44
61
|
local contentNode = if props.asChild then ((function()
|
|
45
62
|
local child = props.children
|
|
46
63
|
if not React.isValidElement(child) then
|
|
47
64
|
error("[ComboboxContent] `asChild` requires a child element.")
|
|
48
65
|
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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,
|
|
52
80
|
ref = setContentRef,
|
|
53
|
-
},
|
|
54
|
-
end)()) else (React.createElement("
|
|
55
|
-
AnchorPoint = popper.anchorPoint,
|
|
81
|
+
}, React.cloneElement(_exp, _object))
|
|
82
|
+
end)()) else (React.createElement("canvasgroup", {
|
|
56
83
|
AutomaticSize = Enum.AutomaticSize.XY,
|
|
57
84
|
BackgroundTransparency = 1,
|
|
58
85
|
BorderSizePixel = 0,
|
|
59
86
|
Size = UDim2.fromOffset(0, 0),
|
|
60
|
-
Visible =
|
|
87
|
+
Visible = contentVisible,
|
|
61
88
|
ref = setContentRef,
|
|
62
89
|
}, props.children))
|
|
63
90
|
return React.createElement(DismissableLayer, {
|
|
@@ -68,10 +95,12 @@ local function ComboboxContentImpl(props)
|
|
|
68
95
|
onInteractOutside = props.onInteractOutside,
|
|
69
96
|
onPointerDownOutside = props.onPointerDownOutside,
|
|
70
97
|
}, React.createElement("frame", {
|
|
98
|
+
AnchorPoint = popper.anchorPoint,
|
|
71
99
|
BackgroundTransparency = 1,
|
|
72
100
|
BorderSizePixel = 0,
|
|
73
|
-
Position =
|
|
101
|
+
Position = popperPosition,
|
|
74
102
|
Size = UDim2.fromOffset(0, 0),
|
|
103
|
+
Visible = shouldRender,
|
|
75
104
|
}, contentNode))
|
|
76
105
|
end
|
|
77
106
|
local function ComboboxContent(props)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lattice-ui/combobox",
|
|
3
|
-
"version": "0.5.0-next.
|
|
3
|
+
"version": "0.5.0-next.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "out/init.luau",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"url": "https://github.com/astra-void/lattice-ui.git"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@lattice-ui/core": "0.5.0-next.
|
|
20
|
-
"@lattice-ui/
|
|
21
|
-
"@lattice-ui/
|
|
22
|
-
"@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"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@rbxts/react": "17.3.7-ts.1",
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { composeRefs, getElementRef, React } from "@lattice-ui/core";
|
|
2
2
|
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
3
3
|
import { DismissableLayer, Presence } from "@lattice-ui/layer";
|
|
4
|
-
import {
|
|
4
|
+
import { createCanvasGroupPopperEntranceRecipe, usePresenceMotionController } from "@lattice-ui/motion";
|
|
5
5
|
import type { PopperPlacement } from "@lattice-ui/popper";
|
|
6
6
|
import { usePopper } from "@lattice-ui/popper";
|
|
7
7
|
import { useComboboxContext } from "./context";
|
|
8
8
|
import type { ComboboxContentProps } from "./types";
|
|
9
9
|
|
|
10
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
|
+
}
|
|
11
18
|
|
|
12
19
|
function toGuiObject(instance: Instance | undefined) {
|
|
13
20
|
if (!instance || !instance.IsA("GuiObject")) {
|
|
@@ -31,6 +38,7 @@ function ComboboxContentImpl(props: {
|
|
|
31
38
|
}) {
|
|
32
39
|
const comboboxContext = useComboboxContext();
|
|
33
40
|
const open = comboboxContext.open;
|
|
41
|
+
const shouldMeasure = open || props.motionPresent || props.onExitComplete !== undefined;
|
|
34
42
|
|
|
35
43
|
const popper = usePopper({
|
|
36
44
|
anchorRef: comboboxContext.anchorRef,
|
|
@@ -38,31 +46,39 @@ function ComboboxContentImpl(props: {
|
|
|
38
46
|
placement: props.placement,
|
|
39
47
|
offset: props.offset,
|
|
40
48
|
padding: props.padding,
|
|
41
|
-
enabled:
|
|
49
|
+
enabled: shouldMeasure,
|
|
42
50
|
});
|
|
43
51
|
|
|
44
52
|
const defaultTransition = React.useMemo(
|
|
45
|
-
() =>
|
|
53
|
+
() => createCanvasGroupPopperEntranceRecipe(popper.placement, CONTENT_OFFSET),
|
|
46
54
|
[popper.placement],
|
|
47
55
|
);
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
props.
|
|
52
|
-
|
|
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,
|
|
64
|
+
});
|
|
53
65
|
|
|
54
66
|
const setContentRef = React.useCallback(
|
|
55
67
|
(instance: Instance | undefined) => {
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
const guiObject = toGuiObject(instance);
|
|
69
|
+
comboboxContext.contentRef.current = guiObject;
|
|
70
|
+
motion.ref.current = guiObject;
|
|
58
71
|
},
|
|
59
|
-
[comboboxContext.contentRef,
|
|
72
|
+
[comboboxContext.contentRef, motion.ref],
|
|
60
73
|
);
|
|
61
74
|
|
|
62
75
|
const handleDismiss = React.useCallback(() => {
|
|
63
76
|
comboboxContext.setOpen(false);
|
|
64
77
|
}, [comboboxContext]);
|
|
65
|
-
|
|
78
|
+
|
|
79
|
+
const shouldRender = motion.mounted;
|
|
80
|
+
const contentVisible = shouldRender && (motion.present || motion.phase !== "exited");
|
|
81
|
+
const popperPosition = popper.isPositioned ? popper.position : HIDDEN_POSITION;
|
|
66
82
|
|
|
67
83
|
const contentNode = props.asChild ? (
|
|
68
84
|
(() => {
|
|
@@ -71,24 +87,38 @@ function ComboboxContentImpl(props: {
|
|
|
71
87
|
error("[ComboboxContent] `asChild` requires a child element.");
|
|
72
88
|
}
|
|
73
89
|
|
|
90
|
+
const childProps = toGuiPropBag((child as { props?: unknown }).props);
|
|
91
|
+
const childRef = getElementRef<Instance>(child);
|
|
92
|
+
|
|
74
93
|
return (
|
|
75
|
-
<
|
|
76
|
-
{
|
|
77
|
-
|
|
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>
|
|
78
109
|
);
|
|
79
110
|
})()
|
|
80
111
|
) : (
|
|
81
|
-
<
|
|
82
|
-
AnchorPoint={popper.anchorPoint}
|
|
112
|
+
<canvasgroup
|
|
83
113
|
AutomaticSize={Enum.AutomaticSize.XY}
|
|
84
114
|
BackgroundTransparency={1}
|
|
85
115
|
BorderSizePixel={0}
|
|
86
116
|
Size={UDim2.fromOffset(0, 0)}
|
|
87
|
-
Visible={
|
|
117
|
+
Visible={contentVisible}
|
|
88
118
|
ref={setContentRef}
|
|
89
119
|
>
|
|
90
120
|
{props.children}
|
|
91
|
-
</
|
|
121
|
+
</canvasgroup>
|
|
92
122
|
);
|
|
93
123
|
|
|
94
124
|
return (
|
|
@@ -101,10 +131,12 @@ function ComboboxContentImpl(props: {
|
|
|
101
131
|
onPointerDownOutside={props.onPointerDownOutside}
|
|
102
132
|
>
|
|
103
133
|
<frame
|
|
134
|
+
AnchorPoint={popper.anchorPoint}
|
|
104
135
|
BackgroundTransparency={1}
|
|
105
136
|
BorderSizePixel={0}
|
|
106
|
-
Position={
|
|
137
|
+
Position={popperPosition}
|
|
107
138
|
Size={UDim2.fromOffset(0, 0)}
|
|
139
|
+
Visible={shouldRender}
|
|
108
140
|
>
|
|
109
141
|
{contentNode}
|
|
110
142
|
</frame>
|