@lattice-ui/layer 0.3.2 → 0.4.0

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,6 +1,8 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
- local React = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).React
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local FocusLayerProvider = _core.FocusLayerProvider
5
+ local React = _core.React
4
6
  local DEFAULT_LAYER_IGNORE_GUI_INSET = TS.import(script, script.Parent.Parent, "internals", "constants").DEFAULT_LAYER_IGNORE_GUI_INSET
5
7
  local Portal = TS.import(script, script.Parent.Parent, "portal", "Portal").Portal
6
8
  local usePortalContext = TS.import(script, script.Parent.Parent, "portal", "PortalProvider").usePortalContext
@@ -24,13 +26,13 @@ local function DismissableLayer(props)
24
26
  local shouldBlockOutsidePointer = props.modal == true or props.disableOutsidePointerEvents == true
25
27
  local layerIgnoresGuiInset = DEFAULT_LAYER_IGNORE_GUI_INSET
26
28
  local portalContext = usePortalContext()
27
- local contentRootRef = React.useRef()
29
+ local contentWrapperRef = React.useRef()
28
30
  local stackOrder, setStackOrder = React.useState(0)
29
31
  local enabledRef = useLatest(enabled)
32
+ local insideRefsRef = useLatest(props.insideRefs or {})
30
33
  local onDismissRef = useLatest(props.onDismiss)
31
34
  local onPointerDownOutsideRef = useLatest(props.onPointerDownOutside)
32
35
  local onInteractOutsideRef = useLatest(props.onInteractOutside)
33
- local onEscapeKeyDownRef = useLatest(props.onEscapeKeyDown)
34
36
  local callPointerDownOutside = React.useCallback(function(event)
35
37
  local _result = onPointerDownOutsideRef.current
36
38
  if _result ~= nil then
@@ -43,12 +45,6 @@ local function DismissableLayer(props)
43
45
  _result(event)
44
46
  end
45
47
  end, {})
46
- local callEscape = React.useCallback(function(event)
47
- local _result = onEscapeKeyDownRef.current
48
- if _result ~= nil then
49
- _result(event)
50
- end
51
- end, {})
52
48
  local callDismiss = React.useCallback(function()
53
49
  local _result = onDismissRef.current
54
50
  if _result ~= nil then
@@ -61,24 +57,35 @@ local function DismissableLayer(props)
61
57
  return enabledRef.current
62
58
  end,
63
59
  isPointerOutside = function(inputObject)
64
- local contentRoot = contentRootRef.current
65
- if not contentRoot then
60
+ local contentWrapper = contentWrapperRef.current
61
+ if not contentWrapper then
66
62
  return false
67
63
  end
68
- return isOutsidePointerEvent(inputObject, portalContext.container, contentRoot, {
64
+ local _exp = insideRefsRef.current
65
+ -- ▼ ReadonlyArray.map ▼
66
+ local _newValue = table.create(#_exp)
67
+ local _callback = function(ref)
68
+ return ref.current
69
+ end
70
+ for _k, _v in _exp do
71
+ _newValue[_k] = _callback(_v, _k - 1, _exp)
72
+ end
73
+ -- ▲ ReadonlyArray.map ▲
74
+ local insideRoots = _newValue
75
+ return isOutsidePointerEvent(inputObject, portalContext.container, contentWrapper, {
76
+ insideRoots = insideRoots,
69
77
  layerIgnoresGuiInset = layerIgnoresGuiInset,
70
78
  })
71
79
  end,
72
80
  onPointerDownOutside = callPointerDownOutside,
73
81
  onInteractOutside = callInteractOutside,
74
- onEscapeKeyDown = callEscape,
75
82
  onDismiss = callDismiss,
76
83
  })
77
84
  setStackOrder(registration.mountOrder)
78
85
  return function()
79
86
  unregisterLayer(registration.id)
80
87
  end
81
- end, { callDismiss, callEscape, callInteractOutside, callPointerDownOutside, enabledRef, layerIgnoresGuiInset, portalContext.container })
88
+ end, { callDismiss, callInteractOutside, callPointerDownOutside, enabledRef, insideRefsRef, layerIgnoresGuiInset, portalContext.container })
82
89
  return React.createElement(Portal, nil, React.createElement("screengui", {
83
90
  key = `Layer_{stackOrder}`,
84
91
  DisplayOrder = portalContext.displayOrderBase + stackOrder,
@@ -102,9 +109,11 @@ local function DismissableLayer(props)
102
109
  BorderSizePixel = 0,
103
110
  Position = UDim2.fromScale(0, 0),
104
111
  Size = UDim2.fromScale(1, 1),
105
- ref = contentRootRef,
112
+ ref = contentWrapperRef,
106
113
  ZIndex = 1,
107
- }, props.children)))
114
+ }, React.createElement(FocusLayerProvider, {
115
+ layerOrder = stackOrder,
116
+ }, props.children))))
108
117
  end
109
118
  return {
110
119
  DismissableLayer = DismissableLayer,
@@ -1,8 +1,9 @@
1
1
  import type { LayerInteractEvent } from "./types";
2
2
  type OutsidePointerOptions = {
3
3
  layerIgnoresGuiInset: boolean;
4
+ insideRoots?: Array<GuiObject | undefined>;
4
5
  };
5
6
  export declare function isPointerInput(inputObject: InputObject): boolean;
6
7
  export declare function toLayerInteractEvent(originalEvent: InputObject): LayerInteractEvent;
7
- export declare function isOutsidePointerEvent(inputObject: InputObject, container: BasePlayerGui, contentRoot: GuiObject, options: OutsidePointerOptions): boolean;
8
+ export declare function isOutsidePointerEvent(inputObject: InputObject, container: BasePlayerGui, contentWrapper: GuiObject, options: OutsidePointerOptions): boolean;
8
9
  export {};
@@ -15,6 +15,20 @@ local function toLayerInteractEvent(originalEvent)
15
15
  }
16
16
  return event
17
17
  end
18
+ local function isWithinInsideRoots(hitObject, insideRoots)
19
+ for _, insideRoot in insideRoots do
20
+ if not insideRoot then
21
+ continue
22
+ end
23
+ if hitObject == insideRoot or hitObject:IsDescendantOf(insideRoot) then
24
+ return true
25
+ end
26
+ end
27
+ return false
28
+ end
29
+ local function isWithinContentBoundary(hitObject, contentWrapper)
30
+ return hitObject:IsDescendantOf(contentWrapper)
31
+ end
18
32
  local function addUniqueSample(samples, sampleKeys, x, y)
19
33
  local roundedX = math.round(x)
20
34
  local roundedY = math.round(y)
@@ -42,14 +56,15 @@ local function getPointerSamples(pointerPosition, options)
42
56
  end
43
57
  return samples
44
58
  end
45
- local function isOutsidePointerEvent(inputObject, container, contentRoot, options)
59
+ local function isOutsidePointerEvent(inputObject, container, contentWrapper, options)
46
60
  local rawPointerPosition = inputObject.Position
47
61
  local pointerPosition = Vector2.new(rawPointerPosition.X, rawPointerPosition.Y)
48
62
  local pointerSamples = getPointerSamples(pointerPosition, options)
63
+ local insideRoots = options.insideRoots or {}
49
64
  for _, sample in pointerSamples do
50
65
  local hitGuiObjects = container:GetGuiObjectsAtPosition(sample.X, sample.Y)
51
66
  for _1, hitObject in hitGuiObjects do
52
- if hitObject:IsDescendantOf(contentRoot) then
67
+ if isWithinContentBoundary(hitObject, contentWrapper) or isWithinInsideRoots(hitObject, insideRoots) then
53
68
  return false
54
69
  end
55
70
  end
@@ -6,7 +6,6 @@ type LayerEntry = {
6
6
  isPointerOutside: (inputObject: InputObject) => boolean;
7
7
  onPointerDownOutside?: (event: LayerInteractEvent) => void;
8
8
  onInteractOutside?: (event: LayerInteractEvent) => void;
9
- onEscapeKeyDown?: (event: LayerInteractEvent) => void;
10
9
  onDismiss?: () => void;
11
10
  };
12
11
  type RegisterLayerParams = Omit<LayerEntry, "id" | "mountOrder">;
@@ -1,8 +1,6 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
- local _env = TS.import(script, script.Parent.Parent, "internals", "env")
4
- local GuiService = _env.GuiService
5
- local UserInputService = _env.UserInputService
3
+ local UserInputService = TS.import(script, script.Parent.Parent, "internals", "env").UserInputService
6
4
  local _events = TS.import(script, script.Parent, "events")
7
5
  local isPointerInput = _events.isPointerInput
8
6
  local toLayerInteractEvent = _events.toLayerInteractEvent
@@ -27,38 +25,15 @@ local function handleDismissEvent(entry, event)
27
25
  end
28
26
  end
29
27
  end
30
- local function shouldIgnoreEscapeDismiss()
31
- local focusedTextBox = UserInputService:GetFocusedTextBox()
32
- if focusedTextBox then
33
- return true
34
- end
35
- local selectedObject = GuiService.SelectedObject
36
- if selectedObject and selectedObject:IsA("TextBox") then
37
- return true
38
- end
39
- return false
40
- end
41
28
  local function handleInputBegan(inputObject, gameProcessedEvent)
42
29
  if gameProcessedEvent then
43
30
  return nil
44
31
  end
45
- local topLayer = getTopMostEnabledLayer()
46
- if not topLayer then
47
- return nil
48
- end
49
- if inputObject.KeyCode == Enum.KeyCode.Escape then
50
- if shouldIgnoreEscapeDismiss() then
51
- return nil
52
- end
53
- local escapeEvent = toLayerInteractEvent(inputObject)
54
- local _result = topLayer.onEscapeKeyDown
55
- if _result ~= nil then
56
- _result(escapeEvent)
57
- end
58
- handleDismissEvent(topLayer, escapeEvent)
32
+ if not isPointerInput(inputObject) then
59
33
  return nil
60
34
  end
61
- if not isPointerInput(inputObject) then
35
+ local topLayer = getTopMostEnabledLayer()
36
+ if not topLayer then
62
37
  return nil
63
38
  end
64
39
  if not topLayer.isPointerOutside(inputObject) then
@@ -84,11 +59,10 @@ local function startInputListener()
84
59
  end)
85
60
  end
86
61
  local function stopInputListener()
87
- if not inputConnection then
88
- return nil
62
+ if inputConnection then
63
+ inputConnection:Disconnect()
64
+ inputConnection = nil
89
65
  end
90
- inputConnection:Disconnect()
91
- inputConnection = nil
92
66
  end
93
67
  local function syncInputListener()
94
68
  if #layerEntries > 0 then
@@ -7,10 +7,10 @@ export type LayerInteractEvent = {
7
7
  export type DismissableLayerProps = {
8
8
  children?: React.ReactNode;
9
9
  enabled?: boolean;
10
+ insideRefs?: Array<React.MutableRefObject<GuiObject | undefined> | React.MutableRefObject<TextBox | undefined>>;
10
11
  modal?: boolean;
11
12
  disableOutsidePointerEvents?: boolean;
12
13
  onPointerDownOutside?: (event: LayerInteractEvent) => void;
13
14
  onInteractOutside?: (event: LayerInteractEvent) => void;
14
- onEscapeKeyDown?: (event: LayerInteractEvent) => void;
15
15
  onDismiss?: () => void;
16
16
  };
@@ -1,3 +1,4 @@
1
1
  export declare const UserInputService: UserInputService;
2
2
  export declare const GuiService: GuiService;
3
+ export declare const ContextActionService: ContextActionService;
3
4
  export declare function getGuiInsetTopLeft(): Vector2;
@@ -1,6 +1,7 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local UserInputService = game:GetService("UserInputService")
3
3
  local GuiService = game:GetService("GuiService")
4
+ local ContextActionService = game:GetService("ContextActionService")
4
5
  local function getGuiInsetTopLeft()
5
6
  local topLeftInset = GuiService:GetGuiInset()
6
7
  return topLeftInset
@@ -9,4 +10,5 @@ return {
9
10
  getGuiInsetTopLeft = getGuiInsetTopLeft,
10
11
  UserInputService = UserInputService,
11
12
  GuiService = GuiService,
13
+ ContextActionService = ContextActionService,
12
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattice-ui/layer",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  "README.md"
10
10
  ],
11
11
  "dependencies": {
12
- "@lattice-ui/core": "0.3.2"
12
+ "@lattice-ui/core": "0.4.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@rbxts/react": "17.3.7-ts.1",