@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.
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogCloseProps } from "./types";
3
+ export declare function DialogClose(props: DialogCloseProps): React.JSX.Element;
@@ -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,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogContentProps } from "./types";
3
+ export declare function DialogContent(props: DialogContentProps): React.JSX.Element | undefined;
@@ -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,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogOverlayProps } from "./types";
3
+ export declare function DialogOverlay(props: DialogOverlayProps): React.JSX.Element | undefined;
@@ -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,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogPortalProps } from "./types";
3
+ export declare function DialogPortal(props: DialogPortalProps): React.JSX.Element;
@@ -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,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogProps } from "./types";
3
+ export declare function Dialog(props: DialogProps): React.JSX.Element;
@@ -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,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { DialogTriggerProps } from "./types";
3
+ export declare function DialogTrigger(props: DialogTriggerProps): React.JSX.Element;
@@ -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,3 @@
1
+ import type { DialogContextValue } from "./types";
2
+ declare const DialogContextProvider: import("@rbxts/react").Provider<DialogContextValue | undefined>, useDialogContext: () => DialogContextValue;
3
+ export { DialogContextProvider, useDialogContext };
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
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,6 @@
1
+ import { createStrictContext } from "@lattice-ui/core";
2
+ import type { DialogContextValue } from "./types";
3
+
4
+ const [DialogContextProvider, useDialogContext] = createStrictContext<DialogContextValue>("Dialog");
5
+
6
+ export { DialogContextProvider, useDialogContext };
@@ -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
+ }