@budibase/bbui 3.37.3 → 3.37.5

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/bbui",
3
3
  "description": "A UI solution used in the different Budibase projects.",
4
- "version": "3.37.3",
4
+ "version": "3.37.5",
5
5
  "license": "MPL-2.0",
6
6
  "module": "dist/bbui.mjs",
7
7
  "exports": {
@@ -103,5 +103,5 @@
103
103
  }
104
104
  }
105
105
  },
106
- "gitHead": "ca7841a3a42b1a1328df0cbb96a51650c0c1e8a7"
106
+ "gitHead": "1d2debd2fe45dcf8d8c76d9865e3457be3aaebbe"
107
107
  }
@@ -12,6 +12,7 @@
12
12
  export let offset: number | undefined = undefined
13
13
  export let widthMode: PopoverWidthMode = "no-anchor"
14
14
  export let roundedPopover: boolean = false
15
+ export let customZIndex: number | undefined = undefined
15
16
 
16
17
  const actionMenuContext = getContext("actionMenu")
17
18
 
@@ -76,6 +77,7 @@
76
77
  {portalTarget}
77
78
  {animate}
78
79
  {offset}
80
+ {customZIndex}
79
81
  {widthMode}
80
82
  resizable={false}
81
83
  borderRadius={roundedPopover ? "12px" : undefined}
@@ -63,6 +63,12 @@
63
63
  import ActionButton from "../ActionButton/ActionButton.svelte"
64
64
  import Button from "../Button/Button.svelte"
65
65
  import Icon from "../Icon/Icon.svelte"
66
+ import {
67
+ addOverlay,
68
+ removeOverlay,
69
+ overlayStack,
70
+ BASE_Z_INDEX,
71
+ } from "../Modal/overlayStack"
66
72
 
67
73
  export let title = ""
68
74
  export let forceModal = false
@@ -75,7 +81,10 @@
75
81
  let drawerId = generate()
76
82
 
77
83
  $: depth = $openDrawers.length - $openDrawers.indexOf(drawerId) - 1
78
- $: style = getStyle(depth, $drawerLeft, $drawerWidth, $modal, zIndex)
84
+ $: stackIndex = $overlayStack.indexOf(drawerId)
85
+ $: computedZIndex =
86
+ zIndex ?? (stackIndex === -1 ? BASE_Z_INDEX : BASE_Z_INDEX + stackIndex)
87
+ $: style = getStyle(depth, $drawerLeft, $drawerWidth, $modal, computedZIndex)
79
88
 
80
89
  const getStyle = (depth, left, width, modal, zIndex) => {
81
90
  let style = `
@@ -108,6 +117,7 @@
108
117
  visible = true
109
118
  dispatch("drawerShow", drawerId)
110
119
  openDrawers.update(state => [...state, drawerId])
120
+ addOverlay(drawerId)
111
121
  }
112
122
 
113
123
  export function hide() {
@@ -117,6 +127,7 @@
117
127
  visible = false
118
128
  dispatch("drawerHide", drawerId)
119
129
  openDrawers.update(state => state.filter(id => id !== drawerId))
130
+ removeOverlay(drawerId)
120
131
  unobserve()
121
132
  }
122
133
 
@@ -180,7 +191,7 @@
180
191
  class="underlay"
181
192
  class:hidden={!$modal}
182
193
  transition:drawerFade|local
183
- style={zIndex != null ? `z-index: ${zIndex - 1}` : undefined}
194
+ style="z-index: {computedZIndex - 1}"
184
195
  ></div>
185
196
  <div
186
197
  class="drawer"
@@ -10,23 +10,42 @@
10
10
  <script lang="ts">
11
11
  import "@spectrum-css/modal/dist/index-vars.css"
12
12
  import "@spectrum-css/underlay/dist/index-vars.css"
13
- import { createEventDispatcher, setContext, tick, onMount } from "svelte"
13
+ import {
14
+ createEventDispatcher,
15
+ setContext,
16
+ tick,
17
+ onMount,
18
+ onDestroy,
19
+ } from "svelte"
14
20
  import { fade, fly } from "svelte/transition"
15
21
  import Portal from "svelte-portal"
16
22
  import Context from "../context"
17
23
  import { ModalCancelFrom } from "../constants"
18
24
  import { generate } from "shortid"
25
+ import {
26
+ BASE_Z_INDEX,
27
+ overlayStack,
28
+ addOverlay,
29
+ removeOverlay,
30
+ isActiveOverlay,
31
+ } from "./overlayStack"
19
32
 
20
33
  export let fixed: boolean = false
21
34
  export let inline: boolean = false
22
35
  export let disableCancel: boolean = false
23
36
  export let closeOnOutsideClick: boolean = true
24
37
  export let autoFocus: boolean = true
25
- export let zIndex: number = 1001
38
+ export let zIndex: number | undefined = undefined
39
+ export let beforeClose: (() => Promise<boolean>) | undefined = undefined
40
+
41
+ $: stackIndex = $overlayStack.indexOf(modalId)
42
+ $: computedZIndex =
43
+ zIndex ?? (stackIndex === -1 ? BASE_Z_INDEX : BASE_Z_INDEX + stackIndex)
26
44
 
27
45
  // Ensure any popovers inside this modal are rendered inside this modal
28
46
  // Unique ids are required to ensure nested modals are parented correctly
29
47
  const uniqueId = generate()
48
+ const modalId = uniqueId
30
49
  setContext(Context.PopoverRoot, `.spectrum-Modal-${uniqueId}`)
31
50
 
32
51
  const dispatch = createEventDispatcher<{
@@ -48,6 +67,7 @@
48
67
  return
49
68
  }
50
69
  visible = true
70
+ addOverlay(modalId)
51
71
  }
52
72
 
53
73
  export function hide(): void {
@@ -55,6 +75,7 @@
55
75
  return
56
76
  }
57
77
  visible = false
78
+ removeOverlay(modalId)
58
79
  }
59
80
 
60
81
  export function toggle(): void {
@@ -65,23 +86,30 @@
65
86
  }
66
87
  }
67
88
 
68
- export function cancel(from: ModalCancelFrom): void {
89
+ export async function cancel(from: ModalCancelFrom): Promise<void> {
69
90
  if (!visible || disableCancel) {
70
91
  return
71
92
  }
93
+ if (beforeClose) {
94
+ const ok = await beforeClose()
95
+ if (!ok) return
96
+ }
72
97
  dispatch("cancel", from)
73
98
  hide()
74
99
  }
75
100
 
76
- function handleKey(e: KeyboardEvent): void {
77
- if (visible && e.key === "Escape") {
78
- cancel(ModalCancelFrom.ESCAPE_KEY)
101
+ const isActiveModal = () => isActiveOverlay(modalId)
102
+
103
+ async function handleKey(e: KeyboardEvent): Promise<void> {
104
+ if (visible && e.key === "Escape" && isActiveModal()) {
105
+ e.stopImmediatePropagation()
106
+ await cancel(ModalCancelFrom.ESCAPE_KEY)
79
107
  }
80
108
  }
81
109
 
82
- function handleOutsideClick(): void {
83
- if (closeOnOutsideClick) {
84
- cancel(ModalCancelFrom.OUTSIDE_CLICK)
110
+ async function handleOutsideClick(): Promise<void> {
111
+ if (closeOnOutsideClick && isActiveModal()) {
112
+ await cancel(ModalCancelFrom.OUTSIDE_CLICK)
85
113
  }
86
114
  }
87
115
 
@@ -109,7 +137,7 @@
109
137
  show: () => void
110
138
  hide: () => void
111
139
  toggle: () => void
112
- cancel: () => void
140
+ cancel: (_: ModalCancelFrom) => Promise<void>
113
141
  })
114
142
 
115
143
  onMount(() => {
@@ -118,6 +146,12 @@
118
146
  document.removeEventListener("keydown", handleKey)
119
147
  }
120
148
  })
149
+
150
+ onDestroy(() => {
151
+ if (visible) {
152
+ removeOverlay(modalId)
153
+ }
154
+ })
121
155
  </script>
122
156
 
123
157
  {#if inline}
@@ -145,7 +179,7 @@
145
179
  <div
146
180
  class="spectrum-Underlay is-open"
147
181
  on:mousedown|self={handleOutsideClick}
148
- style="z-index:{zIndex || 999}"
182
+ style="z-index:{computedZIndex}"
149
183
  >
150
184
  <div
151
185
  class="background"
@@ -185,7 +219,7 @@
185
219
  overflow-x: hidden;
186
220
  background: transparent;
187
221
  }
188
- .background {
222
+ .spectrum-Underlay .background {
189
223
  background: var(--modal-background, rgba(0, 0, 0, 0.75));
190
224
  opacity: 0.65;
191
225
  position: fixed;
@@ -58,7 +58,7 @@
58
58
  async function close(): Promise<void> {
59
59
  loading = true
60
60
  if (!onCancel || (await onCancel()) !== keepOpen) {
61
- cancel(ModalCancelFrom.CANCEL_BUTTON)
61
+ await cancel(ModalCancelFrom.CANCEL_BUTTON)
62
62
  }
63
63
  loading = false
64
64
  }
@@ -153,7 +153,7 @@
153
153
  <Icon
154
154
  hoverable
155
155
  name="x"
156
- on:click={() => cancel(ModalCancelFrom.CLOSE_BUTTON)}
156
+ on:click={async () => cancel(ModalCancelFrom.CLOSE_BUTTON)}
157
157
  />
158
158
  </div>
159
159
  {/if}
@@ -0,0 +1,18 @@
1
+ import { writable } from "svelte/store"
2
+
3
+ export const BASE_Z_INDEX = 1001
4
+ export const overlayStack = writable<string[]>([])
5
+
6
+ export const addOverlay = (id: string) => {
7
+ overlayStack.update(stack => [...stack, id])
8
+ }
9
+
10
+ export const removeOverlay = (id: string) => {
11
+ overlayStack.update(stack => stack.filter(s => s !== id))
12
+ }
13
+
14
+ export const isActiveOverlay = (id: string): boolean => {
15
+ let stack: string[] = []
16
+ overlayStack.subscribe(s => (stack = s))()
17
+ return stack.length > 0 && stack[stack.length - 1] === id
18
+ }
@@ -17,6 +17,7 @@
17
17
  } from "../Actions/positionDropdown"
18
18
  import { PopoverAlignment, type PopoverWidthMode } from "../constants"
19
19
  import Context from "../context"
20
+ import { type Readable } from "svelte/store"
20
21
 
21
22
  export let anchor: HTMLElement | undefined
22
23
  export let align: PopoverAlignment | `${PopoverAlignment}` =
@@ -47,9 +48,14 @@
47
48
  let blockPointerEvents = false
48
49
 
49
50
  // Portal library lacks types, so we have to type this as any even though it's
50
- // actually a string
51
+ const popoverRoot = getContext<string>(Context.PopoverRoot)
52
+ const popoverPortalOverride = getContext(Context.PopoverPortalOverride) as
53
+ | Readable<HTMLElement | undefined>
54
+ | undefined
55
+
51
56
  $: target = (portalTarget ||
52
- getContext(Context.PopoverRoot) ||
57
+ $popoverPortalOverride ||
58
+ popoverRoot ||
53
59
  ".spectrum") as any
54
60
  $: {
55
61
  // Disable pointer events for the initial part of the animation, because we
@@ -2,12 +2,20 @@
2
2
  import Portal from "svelte-portal"
3
3
  import { getContext } from "svelte"
4
4
  import Context from "../context"
5
+ import { readable } from "svelte/store"
5
6
 
6
7
  export let anchor
7
8
  export let visible = false
8
9
  export let offset = 0
9
10
 
10
- $: target = getContext(Context.PopoverRoot) || "#app"
11
+ const popoverRootCtx = getContext(Context.PopoverRoot)
12
+ const popoverRootStore =
13
+ popoverRootCtx &&
14
+ typeof popoverRootCtx === "object" &&
15
+ "subscribe" in popoverRootCtx
16
+ ? popoverRootCtx
17
+ : readable(popoverRootCtx || "#app")
18
+ $: target = $popoverRootStore || "#app"
11
19
 
12
20
  let hovering = false
13
21
  let tooltip
package/src/context.ts CHANGED
@@ -21,9 +21,11 @@ declare module "svelte" {
21
21
  interface Module {
22
22
  Modal: "bbui-modal"
23
23
  PopoverRoot: "bbui-popover-root"
24
+ PopoverPortalOverride: "bbui-popover-portal-override"
24
25
  }
25
26
 
26
27
  export default {
27
28
  Modal: "bbui-modal",
28
29
  PopoverRoot: "bbui-popover-root",
30
+ PopoverPortalOverride: "bbui-popover-portal-override",
29
31
  } as Module
package/src/index.ts CHANGED
@@ -70,6 +70,13 @@ export { default as MenuSection } from "./Menu/Section.svelte"
70
70
  export { default as MenuSeparator } from "./Menu/Separator.svelte"
71
71
  export { default as Modal, type ModalAPI } from "./Modal/Modal.svelte"
72
72
  export { keepOpen, default as ModalContent } from "./Modal/ModalContent.svelte"
73
+ export {
74
+ addOverlay,
75
+ removeOverlay,
76
+ isActiveOverlay,
77
+ overlayStack,
78
+ BASE_Z_INDEX,
79
+ } from "./Modal/overlayStack"
73
80
  export { default as Notification } from "./Notification/Notification.svelte"
74
81
  export { default as NotificationDisplay } from "./Notification/NotificationDisplay.svelte"
75
82
  export { default as Pagination } from "./Pagination/Pagination.svelte"