@lattice-ui/dialog 0.1.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/out/Dialog/DialogClose.d.ts +3 -0
- package/out/Dialog/DialogClose.luau +38 -0
- package/out/Dialog/DialogContent.d.ts +3 -0
- package/out/Dialog/DialogContent.luau +73 -0
- package/out/Dialog/DialogOverlay.d.ts +3 -0
- package/out/Dialog/DialogOverlay.luau +50 -0
- package/out/Dialog/DialogPortal.d.ts +3 -0
- package/out/Dialog/DialogPortal.luau +33 -0
- package/out/Dialog/DialogRoot.d.ts +3 -0
- package/out/Dialog/DialogRoot.luau +44 -0
- package/out/Dialog/DialogTrigger.d.ts +3 -0
- package/out/Dialog/DialogTrigger.luau +53 -0
- package/out/Dialog/context.d.ts +3 -0
- package/out/Dialog/context.luau +10 -0
- package/out/Dialog/types.d.ts +44 -0
- package/out/Dialog/types.luau +2 -0
- package/out/index.d.ts +7 -0
- package/out/init.luau +10 -0
- package/package.json +25 -0
- package/src/Dialog/DialogClose.tsx +35 -0
- package/src/Dialog/DialogContent.tsx +84 -0
- package/src/Dialog/DialogOverlay.tsx +48 -0
- package/src/Dialog/DialogPortal.tsx +28 -0
- package/src/Dialog/DialogRoot.tsx +32 -0
- package/src/Dialog/DialogTrigger.tsx +60 -0
- package/src/Dialog/context.ts +6 -0
- package/src/Dialog/types.ts +52 -0
- package/src/index.ts +14 -0
- package/tsconfig.json +16 -0
- package/tsconfig.typecheck.json +25 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local useDialogContext = TS.import(script, script.Parent, "context").useDialogContext
|
|
7
|
+
local function DialogClose(props)
|
|
8
|
+
local dialogContext = useDialogContext()
|
|
9
|
+
local handleActivated = React.useCallback(function()
|
|
10
|
+
dialogContext.setOpen(false)
|
|
11
|
+
end, { dialogContext.setOpen })
|
|
12
|
+
if props.asChild then
|
|
13
|
+
local child = props.children
|
|
14
|
+
if not child then
|
|
15
|
+
error("[DialogClose] `asChild` requires a child element.")
|
|
16
|
+
end
|
|
17
|
+
return React.createElement(Slot, {
|
|
18
|
+
Event = {
|
|
19
|
+
Activated = handleActivated,
|
|
20
|
+
},
|
|
21
|
+
}, child)
|
|
22
|
+
end
|
|
23
|
+
return React.createElement("textbutton", {
|
|
24
|
+
AutoButtonColor = false,
|
|
25
|
+
BackgroundTransparency = 1,
|
|
26
|
+
BorderSizePixel = 0,
|
|
27
|
+
Event = {
|
|
28
|
+
Activated = handleActivated,
|
|
29
|
+
},
|
|
30
|
+
Size = UDim2.fromOffset(110, 34),
|
|
31
|
+
Text = "Close",
|
|
32
|
+
TextColor3 = Color3.fromRGB(240, 244, 250),
|
|
33
|
+
TextSize = 16,
|
|
34
|
+
}, props.children)
|
|
35
|
+
end
|
|
36
|
+
return {
|
|
37
|
+
DialogClose = DialogClose,
|
|
38
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).React
|
|
4
|
+
local FocusScope = TS.import(script, TS.getModule(script, "@lattice-ui", "focus").out).FocusScope
|
|
5
|
+
local _layer = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out)
|
|
6
|
+
local DismissableLayer = _layer.DismissableLayer
|
|
7
|
+
local Presence = _layer.Presence
|
|
8
|
+
local useDialogContext = TS.import(script, script.Parent, "context").useDialogContext
|
|
9
|
+
local function DialogContentImpl(props)
|
|
10
|
+
return React.createElement(DismissableLayer, {
|
|
11
|
+
enabled = props.enabled,
|
|
12
|
+
modal = props.modal,
|
|
13
|
+
onDismiss = props.onDismiss,
|
|
14
|
+
onEscapeKeyDown = props.onEscapeKeyDown,
|
|
15
|
+
onInteractOutside = props.onInteractOutside,
|
|
16
|
+
onPointerDownOutside = props.onPointerDownOutside,
|
|
17
|
+
}, React.createElement(FocusScope, {
|
|
18
|
+
restoreFocus = props.restoreFocus,
|
|
19
|
+
trapped = props.trapFocus,
|
|
20
|
+
}, props.children))
|
|
21
|
+
end
|
|
22
|
+
local function DialogContent(props)
|
|
23
|
+
local dialogContext = useDialogContext()
|
|
24
|
+
local open = dialogContext.open
|
|
25
|
+
local forceMount = props.forceMount == true
|
|
26
|
+
local _condition = props.trapFocus
|
|
27
|
+
if _condition == nil then
|
|
28
|
+
_condition = true
|
|
29
|
+
end
|
|
30
|
+
local trapFocus = _condition
|
|
31
|
+
local _condition_1 = props.restoreFocus
|
|
32
|
+
if _condition_1 == nil then
|
|
33
|
+
_condition_1 = true
|
|
34
|
+
end
|
|
35
|
+
local restoreFocus = _condition_1
|
|
36
|
+
local handleDismiss = React.useCallback(function()
|
|
37
|
+
dialogContext.setOpen(false)
|
|
38
|
+
end, { dialogContext.setOpen })
|
|
39
|
+
if not open and not forceMount then
|
|
40
|
+
return nil
|
|
41
|
+
end
|
|
42
|
+
if forceMount then
|
|
43
|
+
return React.createElement(DialogContentImpl, {
|
|
44
|
+
enabled = open,
|
|
45
|
+
modal = dialogContext.modal,
|
|
46
|
+
onDismiss = handleDismiss,
|
|
47
|
+
onEscapeKeyDown = props.onEscapeKeyDown,
|
|
48
|
+
onInteractOutside = props.onInteractOutside,
|
|
49
|
+
onPointerDownOutside = props.onPointerDownOutside,
|
|
50
|
+
restoreFocus = restoreFocus,
|
|
51
|
+
trapFocus = trapFocus,
|
|
52
|
+
}, props.children)
|
|
53
|
+
end
|
|
54
|
+
return React.createElement(Presence, {
|
|
55
|
+
exitFallbackMs = 0,
|
|
56
|
+
present = open,
|
|
57
|
+
render = function(state)
|
|
58
|
+
return React.createElement(DialogContentImpl, {
|
|
59
|
+
enabled = state.isPresent,
|
|
60
|
+
modal = dialogContext.modal,
|
|
61
|
+
onDismiss = handleDismiss,
|
|
62
|
+
onEscapeKeyDown = props.onEscapeKeyDown,
|
|
63
|
+
onInteractOutside = props.onInteractOutside,
|
|
64
|
+
onPointerDownOutside = props.onPointerDownOutside,
|
|
65
|
+
restoreFocus = restoreFocus,
|
|
66
|
+
trapFocus = trapFocus,
|
|
67
|
+
}, props.children)
|
|
68
|
+
end,
|
|
69
|
+
})
|
|
70
|
+
end
|
|
71
|
+
return {
|
|
72
|
+
DialogContent = DialogContent,
|
|
73
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local useDialogContext = TS.import(script, script.Parent, "context").useDialogContext
|
|
7
|
+
local function DialogOverlay(props)
|
|
8
|
+
local dialogContext = useDialogContext()
|
|
9
|
+
local open = dialogContext.open
|
|
10
|
+
local shouldRender = open or props.forceMount == true
|
|
11
|
+
local handleActivated = React.useCallback(function()
|
|
12
|
+
dialogContext.setOpen(false)
|
|
13
|
+
end, { dialogContext.setOpen })
|
|
14
|
+
if not shouldRender then
|
|
15
|
+
return nil
|
|
16
|
+
end
|
|
17
|
+
if props.asChild then
|
|
18
|
+
local child = props.children
|
|
19
|
+
if not child then
|
|
20
|
+
error("[DialogOverlay] `asChild` requires a child element.")
|
|
21
|
+
end
|
|
22
|
+
return React.createElement(Slot, {
|
|
23
|
+
Active = open,
|
|
24
|
+
Event = {
|
|
25
|
+
Activated = handleActivated,
|
|
26
|
+
},
|
|
27
|
+
Visible = open,
|
|
28
|
+
}, child)
|
|
29
|
+
end
|
|
30
|
+
return React.createElement("textbutton", {
|
|
31
|
+
Active = open,
|
|
32
|
+
AutoButtonColor = false,
|
|
33
|
+
BackgroundColor3 = Color3.fromRGB(8, 10, 14),
|
|
34
|
+
BackgroundTransparency = if open then 0.35 else 1,
|
|
35
|
+
BorderSizePixel = 0,
|
|
36
|
+
Event = {
|
|
37
|
+
Activated = handleActivated,
|
|
38
|
+
},
|
|
39
|
+
Position = UDim2.fromScale(0, 0),
|
|
40
|
+
Selectable = false,
|
|
41
|
+
Size = UDim2.fromScale(1, 1),
|
|
42
|
+
Text = "",
|
|
43
|
+
TextTransparency = 1,
|
|
44
|
+
Visible = open,
|
|
45
|
+
ZIndex = 5,
|
|
46
|
+
})
|
|
47
|
+
end
|
|
48
|
+
return {
|
|
49
|
+
DialogOverlay = DialogOverlay,
|
|
50
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).React
|
|
4
|
+
local _layer = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out)
|
|
5
|
+
local Portal = _layer.Portal
|
|
6
|
+
local PortalProvider = _layer.PortalProvider
|
|
7
|
+
local usePortalContext = _layer.usePortalContext
|
|
8
|
+
local function DialogPortalWithOverrides(props)
|
|
9
|
+
local portalContext = usePortalContext()
|
|
10
|
+
local container = props.container or portalContext.container
|
|
11
|
+
local _condition = props.displayOrderBase
|
|
12
|
+
if _condition == nil then
|
|
13
|
+
_condition = portalContext.displayOrderBase
|
|
14
|
+
end
|
|
15
|
+
local displayOrderBase = _condition
|
|
16
|
+
return React.createElement(PortalProvider, {
|
|
17
|
+
container = container,
|
|
18
|
+
displayOrderBase = displayOrderBase,
|
|
19
|
+
}, React.createElement(Portal, nil, props.children))
|
|
20
|
+
end
|
|
21
|
+
local function DialogPortal(props)
|
|
22
|
+
local hasOverrides = props.container ~= nil or props.displayOrderBase ~= nil
|
|
23
|
+
if hasOverrides then
|
|
24
|
+
return React.createElement(DialogPortalWithOverrides, {
|
|
25
|
+
container = props.container,
|
|
26
|
+
displayOrderBase = props.displayOrderBase,
|
|
27
|
+
}, props.children)
|
|
28
|
+
end
|
|
29
|
+
return React.createElement(Portal, nil, props.children)
|
|
30
|
+
end
|
|
31
|
+
return {
|
|
32
|
+
DialogPortal = DialogPortal,
|
|
33
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local useControllableState = _core.useControllableState
|
|
6
|
+
local DialogContextProvider = TS.import(script, script.Parent, "context").DialogContextProvider
|
|
7
|
+
local function Dialog(props)
|
|
8
|
+
local _object = {
|
|
9
|
+
value = props.open,
|
|
10
|
+
}
|
|
11
|
+
local _left = "defaultValue"
|
|
12
|
+
local _condition = props.defaultOpen
|
|
13
|
+
if _condition == nil then
|
|
14
|
+
_condition = false
|
|
15
|
+
end
|
|
16
|
+
_object[_left] = _condition
|
|
17
|
+
_object.onChange = props.onOpenChange
|
|
18
|
+
local _binding = useControllableState(_object)
|
|
19
|
+
local open = _binding[1]
|
|
20
|
+
local setOpenState = _binding[2]
|
|
21
|
+
local _condition_1 = props.modal
|
|
22
|
+
if _condition_1 == nil then
|
|
23
|
+
_condition_1 = true
|
|
24
|
+
end
|
|
25
|
+
local modal = _condition_1
|
|
26
|
+
local triggerRef = React.useRef()
|
|
27
|
+
local setOpen = React.useCallback(function(nextOpen)
|
|
28
|
+
setOpenState(nextOpen)
|
|
29
|
+
end, { setOpenState })
|
|
30
|
+
local contextValue = React.useMemo(function()
|
|
31
|
+
return {
|
|
32
|
+
open = open,
|
|
33
|
+
setOpen = setOpen,
|
|
34
|
+
modal = modal,
|
|
35
|
+
triggerRef = triggerRef,
|
|
36
|
+
}
|
|
37
|
+
end, { modal, open, setOpen })
|
|
38
|
+
return React.createElement(DialogContextProvider, {
|
|
39
|
+
value = contextValue,
|
|
40
|
+
}, props.children)
|
|
41
|
+
end
|
|
42
|
+
return {
|
|
43
|
+
Dialog = Dialog,
|
|
44
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
|
|
4
|
+
local React = _core.React
|
|
5
|
+
local Slot = _core.Slot
|
|
6
|
+
local useDialogContext = TS.import(script, script.Parent, "context").useDialogContext
|
|
7
|
+
local function toGuiObject(instance)
|
|
8
|
+
if not instance or not instance:IsA("GuiObject") then
|
|
9
|
+
return nil
|
|
10
|
+
end
|
|
11
|
+
return instance
|
|
12
|
+
end
|
|
13
|
+
local function DialogTrigger(props)
|
|
14
|
+
local dialogContext = useDialogContext()
|
|
15
|
+
local setTriggerRef = React.useCallback(function(instance)
|
|
16
|
+
dialogContext.triggerRef.current = toGuiObject(instance)
|
|
17
|
+
end, { dialogContext.triggerRef })
|
|
18
|
+
local handleActivated = React.useCallback(function()
|
|
19
|
+
if props.disabled then
|
|
20
|
+
return nil
|
|
21
|
+
end
|
|
22
|
+
dialogContext.setOpen(true)
|
|
23
|
+
end, { dialogContext.setOpen, props.disabled })
|
|
24
|
+
if props.asChild then
|
|
25
|
+
local child = props.children
|
|
26
|
+
if not child then
|
|
27
|
+
error("[DialogTrigger] `asChild` requires a child element.")
|
|
28
|
+
end
|
|
29
|
+
return React.createElement(Slot, {
|
|
30
|
+
Event = {
|
|
31
|
+
Activated = handleActivated,
|
|
32
|
+
},
|
|
33
|
+
ref = setTriggerRef,
|
|
34
|
+
}, child)
|
|
35
|
+
end
|
|
36
|
+
return React.createElement("textbutton", {
|
|
37
|
+
Active = props.disabled ~= true,
|
|
38
|
+
AutoButtonColor = false,
|
|
39
|
+
BackgroundTransparency = 1,
|
|
40
|
+
BorderSizePixel = 0,
|
|
41
|
+
Event = {
|
|
42
|
+
Activated = handleActivated,
|
|
43
|
+
},
|
|
44
|
+
Size = UDim2.fromOffset(140, 38),
|
|
45
|
+
Text = "Open Dialog",
|
|
46
|
+
TextColor3 = Color3.fromRGB(240, 244, 250),
|
|
47
|
+
TextSize = 16,
|
|
48
|
+
ref = setTriggerRef,
|
|
49
|
+
}, props.children)
|
|
50
|
+
end
|
|
51
|
+
return {
|
|
52
|
+
DialogTrigger = DialogTrigger,
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createStrictContext = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).createStrictContext
|
|
4
|
+
local _binding = createStrictContext("Dialog")
|
|
5
|
+
local DialogContextProvider = _binding[1]
|
|
6
|
+
local useDialogContext = _binding[2]
|
|
7
|
+
return {
|
|
8
|
+
DialogContextProvider = DialogContextProvider,
|
|
9
|
+
useDialogContext = useDialogContext,
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
|
+
import type React from "@rbxts/react";
|
|
3
|
+
export type DialogSetOpen = (open: boolean) => void;
|
|
4
|
+
export type DialogContextValue = {
|
|
5
|
+
open: boolean;
|
|
6
|
+
setOpen: DialogSetOpen;
|
|
7
|
+
modal: boolean;
|
|
8
|
+
triggerRef: React.MutableRefObject<GuiObject | undefined>;
|
|
9
|
+
};
|
|
10
|
+
export type DialogProps = {
|
|
11
|
+
open?: boolean;
|
|
12
|
+
defaultOpen?: boolean;
|
|
13
|
+
onOpenChange?: (open: boolean) => void;
|
|
14
|
+
modal?: boolean;
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
};
|
|
17
|
+
export type DialogTriggerProps = {
|
|
18
|
+
asChild?: boolean;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
children?: React.ReactElement;
|
|
21
|
+
};
|
|
22
|
+
export type DialogPortalProps = {
|
|
23
|
+
container?: BasePlayerGui;
|
|
24
|
+
displayOrderBase?: number;
|
|
25
|
+
children?: React.ReactNode;
|
|
26
|
+
};
|
|
27
|
+
export type DialogOverlayProps = {
|
|
28
|
+
asChild?: boolean;
|
|
29
|
+
forceMount?: boolean;
|
|
30
|
+
children?: React.ReactElement;
|
|
31
|
+
};
|
|
32
|
+
export type DialogContentProps = {
|
|
33
|
+
forceMount?: boolean;
|
|
34
|
+
trapFocus?: boolean;
|
|
35
|
+
restoreFocus?: boolean;
|
|
36
|
+
onEscapeKeyDown?: (event: LayerInteractEvent) => void;
|
|
37
|
+
onPointerDownOutside?: (event: LayerInteractEvent) => void;
|
|
38
|
+
onInteractOutside?: (event: LayerInteractEvent) => void;
|
|
39
|
+
children?: React.ReactNode;
|
|
40
|
+
};
|
|
41
|
+
export type DialogCloseProps = {
|
|
42
|
+
asChild?: boolean;
|
|
43
|
+
children?: React.ReactElement;
|
|
44
|
+
};
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { DialogClose } from "./Dialog/DialogClose";
|
|
2
|
+
export { DialogContent } from "./Dialog/DialogContent";
|
|
3
|
+
export { DialogOverlay } from "./Dialog/DialogOverlay";
|
|
4
|
+
export { DialogPortal } from "./Dialog/DialogPortal";
|
|
5
|
+
export { Dialog } from "./Dialog/DialogRoot";
|
|
6
|
+
export { DialogTrigger } from "./Dialog/DialogTrigger";
|
|
7
|
+
export type { DialogCloseProps, DialogContentProps, DialogOverlayProps, DialogPortalProps, DialogProps, DialogTriggerProps, } from "./Dialog/types";
|
package/out/init.luau
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
exports.DialogClose = TS.import(script, script, "Dialog", "DialogClose").DialogClose
|
|
5
|
+
exports.DialogContent = TS.import(script, script, "Dialog", "DialogContent").DialogContent
|
|
6
|
+
exports.DialogOverlay = TS.import(script, script, "Dialog", "DialogOverlay").DialogOverlay
|
|
7
|
+
exports.DialogPortal = TS.import(script, script, "Dialog", "DialogPortal").DialogPortal
|
|
8
|
+
exports.Dialog = TS.import(script, script, "Dialog", "DialogRoot").Dialog
|
|
9
|
+
exports.DialogTrigger = TS.import(script, script, "Dialog", "DialogTrigger").DialogTrigger
|
|
10
|
+
return exports
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lattice-ui/dialog",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "out/init.luau",
|
|
6
|
+
"types": "out/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@lattice-ui/core": "0.1.1",
|
|
9
|
+
"@lattice-ui/focus": "0.1.1",
|
|
10
|
+
"@lattice-ui/layer": "0.1.1"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@rbxts/react": "17.3.7-ts.1",
|
|
14
|
+
"@rbxts/react-roblox": "17.3.7-ts.1"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@rbxts/react": "^17",
|
|
18
|
+
"@rbxts/react-roblox": "^17"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rbxtsc -p tsconfig.json",
|
|
22
|
+
"watch": "rbxtsc -p tsconfig.json -w",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.typecheck.json"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { useDialogContext } from "./context";
|
|
3
|
+
import type { DialogCloseProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function DialogClose(props: DialogCloseProps) {
|
|
6
|
+
const dialogContext = useDialogContext();
|
|
7
|
+
|
|
8
|
+
const handleActivated = React.useCallback(() => {
|
|
9
|
+
dialogContext.setOpen(false);
|
|
10
|
+
}, [dialogContext.setOpen]);
|
|
11
|
+
|
|
12
|
+
if (props.asChild) {
|
|
13
|
+
const child = props.children;
|
|
14
|
+
if (!child) {
|
|
15
|
+
error("[DialogClose] `asChild` requires a child element.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <Slot Event={{ Activated: handleActivated }}>{child}</Slot>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<textbutton
|
|
23
|
+
AutoButtonColor={false}
|
|
24
|
+
BackgroundTransparency={1}
|
|
25
|
+
BorderSizePixel={0}
|
|
26
|
+
Event={{ Activated: handleActivated }}
|
|
27
|
+
Size={UDim2.fromOffset(110, 34)}
|
|
28
|
+
Text="Close"
|
|
29
|
+
TextColor3={Color3.fromRGB(240, 244, 250)}
|
|
30
|
+
TextSize={16}
|
|
31
|
+
>
|
|
32
|
+
{props.children}
|
|
33
|
+
</textbutton>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { React } from "@lattice-ui/core";
|
|
2
|
+
import { FocusScope } from "@lattice-ui/focus";
|
|
3
|
+
import { DismissableLayer, Presence } from "@lattice-ui/layer";
|
|
4
|
+
import { useDialogContext } from "./context";
|
|
5
|
+
import type { DialogContentProps } from "./types";
|
|
6
|
+
|
|
7
|
+
type DialogContentImplProps = {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
modal: boolean;
|
|
10
|
+
trapFocus: boolean;
|
|
11
|
+
restoreFocus: boolean;
|
|
12
|
+
onDismiss: () => void;
|
|
13
|
+
} & Pick<DialogContentProps, "children" | "onEscapeKeyDown" | "onInteractOutside" | "onPointerDownOutside">;
|
|
14
|
+
|
|
15
|
+
function DialogContentImpl(props: DialogContentImplProps) {
|
|
16
|
+
return (
|
|
17
|
+
<DismissableLayer
|
|
18
|
+
enabled={props.enabled}
|
|
19
|
+
modal={props.modal}
|
|
20
|
+
onDismiss={props.onDismiss}
|
|
21
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
22
|
+
onInteractOutside={props.onInteractOutside}
|
|
23
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
24
|
+
>
|
|
25
|
+
<FocusScope restoreFocus={props.restoreFocus} trapped={props.trapFocus}>
|
|
26
|
+
{props.children}
|
|
27
|
+
</FocusScope>
|
|
28
|
+
</DismissableLayer>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function DialogContent(props: DialogContentProps) {
|
|
33
|
+
const dialogContext = useDialogContext();
|
|
34
|
+
const open = dialogContext.open;
|
|
35
|
+
const forceMount = props.forceMount === true;
|
|
36
|
+
const trapFocus = props.trapFocus ?? true;
|
|
37
|
+
const restoreFocus = props.restoreFocus ?? true;
|
|
38
|
+
|
|
39
|
+
const handleDismiss = React.useCallback(() => {
|
|
40
|
+
dialogContext.setOpen(false);
|
|
41
|
+
}, [dialogContext.setOpen]);
|
|
42
|
+
|
|
43
|
+
if (!open && !forceMount) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (forceMount) {
|
|
48
|
+
return (
|
|
49
|
+
<DialogContentImpl
|
|
50
|
+
enabled={open}
|
|
51
|
+
modal={dialogContext.modal}
|
|
52
|
+
onDismiss={handleDismiss}
|
|
53
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
54
|
+
onInteractOutside={props.onInteractOutside}
|
|
55
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
56
|
+
restoreFocus={restoreFocus}
|
|
57
|
+
trapFocus={trapFocus}
|
|
58
|
+
>
|
|
59
|
+
{props.children}
|
|
60
|
+
</DialogContentImpl>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Presence
|
|
66
|
+
exitFallbackMs={0}
|
|
67
|
+
present={open}
|
|
68
|
+
render={(state) => (
|
|
69
|
+
<DialogContentImpl
|
|
70
|
+
enabled={state.isPresent}
|
|
71
|
+
modal={dialogContext.modal}
|
|
72
|
+
onDismiss={handleDismiss}
|
|
73
|
+
onEscapeKeyDown={props.onEscapeKeyDown}
|
|
74
|
+
onInteractOutside={props.onInteractOutside}
|
|
75
|
+
onPointerDownOutside={props.onPointerDownOutside}
|
|
76
|
+
restoreFocus={restoreFocus}
|
|
77
|
+
trapFocus={trapFocus}
|
|
78
|
+
>
|
|
79
|
+
{props.children}
|
|
80
|
+
</DialogContentImpl>
|
|
81
|
+
)}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { useDialogContext } from "./context";
|
|
3
|
+
import type { DialogOverlayProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function DialogOverlay(props: DialogOverlayProps) {
|
|
6
|
+
const dialogContext = useDialogContext();
|
|
7
|
+
const open = dialogContext.open;
|
|
8
|
+
const shouldRender = open || props.forceMount === true;
|
|
9
|
+
|
|
10
|
+
const handleActivated = React.useCallback(() => {
|
|
11
|
+
dialogContext.setOpen(false);
|
|
12
|
+
}, [dialogContext.setOpen]);
|
|
13
|
+
|
|
14
|
+
if (!shouldRender) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (props.asChild) {
|
|
19
|
+
const child = props.children;
|
|
20
|
+
if (!child) {
|
|
21
|
+
error("[DialogOverlay] `asChild` requires a child element.");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Slot Active={open} Event={{ Activated: handleActivated }} Visible={open}>
|
|
26
|
+
{child}
|
|
27
|
+
</Slot>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<textbutton
|
|
33
|
+
Active={open}
|
|
34
|
+
AutoButtonColor={false}
|
|
35
|
+
BackgroundColor3={Color3.fromRGB(8, 10, 14)}
|
|
36
|
+
BackgroundTransparency={open ? 0.35 : 1}
|
|
37
|
+
BorderSizePixel={0}
|
|
38
|
+
Event={{ Activated: handleActivated }}
|
|
39
|
+
Position={UDim2.fromScale(0, 0)}
|
|
40
|
+
Selectable={false}
|
|
41
|
+
Size={UDim2.fromScale(1, 1)}
|
|
42
|
+
Text=""
|
|
43
|
+
TextTransparency={1}
|
|
44
|
+
Visible={open}
|
|
45
|
+
ZIndex={5}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { React } from "@lattice-ui/core";
|
|
2
|
+
import { Portal, PortalProvider, usePortalContext } from "@lattice-ui/layer";
|
|
3
|
+
import type { DialogPortalProps } from "./types";
|
|
4
|
+
|
|
5
|
+
function DialogPortalWithOverrides(props: DialogPortalProps) {
|
|
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 DialogPortal(props: DialogPortalProps) {
|
|
18
|
+
const hasOverrides = props.container !== undefined || props.displayOrderBase !== undefined;
|
|
19
|
+
if (hasOverrides) {
|
|
20
|
+
return (
|
|
21
|
+
<DialogPortalWithOverrides container={props.container} displayOrderBase={props.displayOrderBase}>
|
|
22
|
+
{props.children}
|
|
23
|
+
</DialogPortalWithOverrides>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return <Portal>{props.children}</Portal>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { React, useControllableState } from "@lattice-ui/core";
|
|
2
|
+
import { DialogContextProvider } from "./context";
|
|
3
|
+
import type { DialogProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function Dialog(props: DialogProps) {
|
|
6
|
+
const [open, setOpenState] = useControllableState<boolean>({
|
|
7
|
+
value: props.open,
|
|
8
|
+
defaultValue: props.defaultOpen ?? false,
|
|
9
|
+
onChange: props.onOpenChange,
|
|
10
|
+
});
|
|
11
|
+
const modal = props.modal ?? true;
|
|
12
|
+
const triggerRef = React.useRef<GuiObject>();
|
|
13
|
+
|
|
14
|
+
const setOpen = React.useCallback(
|
|
15
|
+
(nextOpen: boolean) => {
|
|
16
|
+
setOpenState(nextOpen);
|
|
17
|
+
},
|
|
18
|
+
[setOpenState],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const contextValue = React.useMemo(
|
|
22
|
+
() => ({
|
|
23
|
+
open,
|
|
24
|
+
setOpen,
|
|
25
|
+
modal,
|
|
26
|
+
triggerRef,
|
|
27
|
+
}),
|
|
28
|
+
[modal, open, setOpen],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return <DialogContextProvider value={contextValue}>{props.children}</DialogContextProvider>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { React, Slot } from "@lattice-ui/core";
|
|
2
|
+
import { useDialogContext } from "./context";
|
|
3
|
+
import type { DialogTriggerProps } from "./types";
|
|
4
|
+
|
|
5
|
+
function toGuiObject(instance: Instance | undefined) {
|
|
6
|
+
if (!instance || !instance.IsA("GuiObject")) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return instance;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function DialogTrigger(props: DialogTriggerProps) {
|
|
14
|
+
const dialogContext = useDialogContext();
|
|
15
|
+
|
|
16
|
+
const setTriggerRef = React.useCallback(
|
|
17
|
+
(instance: Instance | undefined) => {
|
|
18
|
+
dialogContext.triggerRef.current = toGuiObject(instance);
|
|
19
|
+
},
|
|
20
|
+
[dialogContext.triggerRef],
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const handleActivated = React.useCallback(() => {
|
|
24
|
+
if (props.disabled) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dialogContext.setOpen(true);
|
|
29
|
+
}, [dialogContext.setOpen, props.disabled]);
|
|
30
|
+
|
|
31
|
+
if (props.asChild) {
|
|
32
|
+
const child = props.children;
|
|
33
|
+
if (!child) {
|
|
34
|
+
error("[DialogTrigger] `asChild` requires a child element.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Slot Event={{ Activated: handleActivated }} ref={setTriggerRef}>
|
|
39
|
+
{child}
|
|
40
|
+
</Slot>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<textbutton
|
|
46
|
+
Active={props.disabled !== true}
|
|
47
|
+
AutoButtonColor={false}
|
|
48
|
+
BackgroundTransparency={1}
|
|
49
|
+
BorderSizePixel={0}
|
|
50
|
+
Event={{ Activated: handleActivated }}
|
|
51
|
+
Size={UDim2.fromOffset(140, 38)}
|
|
52
|
+
Text="Open Dialog"
|
|
53
|
+
TextColor3={Color3.fromRGB(240, 244, 250)}
|
|
54
|
+
TextSize={16}
|
|
55
|
+
ref={setTriggerRef}
|
|
56
|
+
>
|
|
57
|
+
{props.children}
|
|
58
|
+
</textbutton>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LayerInteractEvent } from "@lattice-ui/layer";
|
|
2
|
+
import type React from "@rbxts/react";
|
|
3
|
+
|
|
4
|
+
export type DialogSetOpen = (open: boolean) => void;
|
|
5
|
+
|
|
6
|
+
export type DialogContextValue = {
|
|
7
|
+
open: boolean;
|
|
8
|
+
setOpen: DialogSetOpen;
|
|
9
|
+
modal: boolean;
|
|
10
|
+
triggerRef: React.MutableRefObject<GuiObject | undefined>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type DialogProps = {
|
|
14
|
+
open?: boolean;
|
|
15
|
+
defaultOpen?: boolean;
|
|
16
|
+
onOpenChange?: (open: boolean) => void;
|
|
17
|
+
modal?: boolean;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type DialogTriggerProps = {
|
|
22
|
+
asChild?: boolean;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
children?: React.ReactElement;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type DialogPortalProps = {
|
|
28
|
+
container?: BasePlayerGui;
|
|
29
|
+
displayOrderBase?: number;
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type DialogOverlayProps = {
|
|
34
|
+
asChild?: boolean;
|
|
35
|
+
forceMount?: boolean;
|
|
36
|
+
children?: React.ReactElement;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type DialogContentProps = {
|
|
40
|
+
forceMount?: boolean;
|
|
41
|
+
trapFocus?: boolean;
|
|
42
|
+
restoreFocus?: boolean;
|
|
43
|
+
onEscapeKeyDown?: (event: LayerInteractEvent) => void;
|
|
44
|
+
onPointerDownOutside?: (event: LayerInteractEvent) => void;
|
|
45
|
+
onInteractOutside?: (event: LayerInteractEvent) => void;
|
|
46
|
+
children?: React.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type DialogCloseProps = {
|
|
50
|
+
asChild?: boolean;
|
|
51
|
+
children?: React.ReactElement;
|
|
52
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { DialogClose } from "./Dialog/DialogClose";
|
|
2
|
+
export { DialogContent } from "./Dialog/DialogContent";
|
|
3
|
+
export { DialogOverlay } from "./Dialog/DialogOverlay";
|
|
4
|
+
export { DialogPortal } from "./Dialog/DialogPortal";
|
|
5
|
+
export { Dialog } from "./Dialog/DialogRoot";
|
|
6
|
+
export { DialogTrigger } from "./Dialog/DialogTrigger";
|
|
7
|
+
export type {
|
|
8
|
+
DialogCloseProps,
|
|
9
|
+
DialogContentProps,
|
|
10
|
+
DialogOverlayProps,
|
|
11
|
+
DialogPortalProps,
|
|
12
|
+
DialogProps,
|
|
13
|
+
DialogTriggerProps,
|
|
14
|
+
} from "./Dialog/types";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"baseUrl": "..",
|
|
6
|
+
"rootDir": "..",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@lattice-ui/checkbox": ["checkbox/src/index.ts"],
|
|
9
|
+
"@lattice-ui/core": ["core/src/index.ts"],
|
|
10
|
+
"@lattice-ui/dialog": ["dialog/src/index.ts"],
|
|
11
|
+
"@lattice-ui/focus": ["focus/src/index.ts"],
|
|
12
|
+
"@lattice-ui/layer": ["layer/src/index.ts"],
|
|
13
|
+
"@lattice-ui/menu": ["menu/src/index.ts"],
|
|
14
|
+
"@lattice-ui/popover": ["popover/src/index.ts"],
|
|
15
|
+
"@lattice-ui/popper": ["popper/src/index.ts"],
|
|
16
|
+
"@lattice-ui/radio-group": ["radio-group/src/index.ts"],
|
|
17
|
+
"@lattice-ui/style": ["style/src/index.ts"],
|
|
18
|
+
"@lattice-ui/switch": ["switch/src/index.ts"],
|
|
19
|
+
"@lattice-ui/system": ["system/src/index.ts"],
|
|
20
|
+
"@lattice-ui/tabs": ["tabs/src/index.ts"],
|
|
21
|
+
"@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
|
|
22
|
+
"@lattice-ui/tooltip": ["tooltip/src/index.ts"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|