@budibase/bbui 3.36.4 → 3.37.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.
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.36.4",
4
+ "version": "3.37.0",
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": "5e383bc443a2e84c6788f6b73f457f4a48e5cb2f"
106
+ "gitHead": "2b6fff45694d41568ff0cd26f1130cf98f946684"
107
107
  }
@@ -2,7 +2,7 @@
2
2
  import { setContext, getContext } from "svelte"
3
3
  import Popover from "../Popover/Popover.svelte"
4
4
  import Menu from "../Menu/Menu.svelte"
5
- import type { PopoverAlignment } from "../constants"
5
+ import type { PopoverAlignment, PopoverWidthMode } from "../constants"
6
6
 
7
7
  export let disabled: boolean = false
8
8
  export let align: `${PopoverAlignment}` = "left"
@@ -10,7 +10,7 @@
10
10
  export let openOnHover: boolean = false
11
11
  export let animate: boolean | undefined = true
12
12
  export let offset: number | undefined = undefined
13
- export let useAnchorWidth = false
13
+ export let widthMode: PopoverWidthMode = "no-anchor"
14
14
  export let roundedPopover: boolean = false
15
15
 
16
16
  const actionMenuContext = getContext("actionMenu")
@@ -76,7 +76,7 @@
76
76
  {portalTarget}
77
77
  {animate}
78
78
  {offset}
79
- {useAnchorWidth}
79
+ {widthMode}
80
80
  resizable={false}
81
81
  borderRadius={roundedPopover ? "12px" : undefined}
82
82
  on:open
@@ -1,6 +1,6 @@
1
1
  // Strategies are defined as [Popover]To[Anchor].
2
2
  // They can apply for both horizontal and vertical alignment.
3
- import { PopoverAlignment } from "../constants"
3
+ import { PopoverAlignment, type PopoverWidthMode } from "../constants"
4
4
 
5
5
  type Strategy =
6
6
  | "StartToStart"
@@ -14,6 +14,7 @@ export interface Styles {
14
14
  maxHeight?: number
15
15
  minWidth?: number
16
16
  maxWidth?: number
17
+ widthMode: PopoverWidthMode
17
18
  offset?: number
18
19
  left: number
19
20
  top: number
@@ -31,11 +32,12 @@ interface Opts {
31
32
  maxHeight?: number
32
33
  maxWidth?: number
33
34
  minWidth?: number
34
- useAnchorWidth: boolean
35
+ widthMode: PopoverWidthMode
35
36
  offset: number
36
37
  customUpdate?: UpdateHandler
37
38
  resizable: boolean
38
39
  wrap: boolean
40
+ onScroll?: () => void
39
41
  }
40
42
 
41
43
  export default function positionDropdown(element: HTMLElement, opts: Opts) {
@@ -44,7 +46,16 @@ export default function positionDropdown(element: HTMLElement, opts: Opts) {
44
46
 
45
47
  // We need a static reference to this function so that we can properly
46
48
  // clean up the scroll listener.
47
- const scrollUpdate = () => updatePosition(latestOpts)
49
+ const scrollUpdate = (event: Event) => {
50
+ if (event.target instanceof Node && element.contains(event.target)) {
51
+ return
52
+ }
53
+ if (latestOpts.onScroll) {
54
+ latestOpts.onScroll()
55
+ return
56
+ }
57
+ updatePosition(latestOpts)
58
+ }
48
59
 
49
60
  // Updates the position of the dropdown
50
61
  const updatePosition = (opts: Opts) => {
@@ -54,7 +65,7 @@ export default function positionDropdown(element: HTMLElement, opts: Opts) {
54
65
  maxHeight,
55
66
  maxWidth,
56
67
  minWidth,
57
- useAnchorWidth,
68
+ widthMode,
58
69
  offset = 5,
59
70
  customUpdate,
60
71
  resizable,
@@ -70,10 +81,13 @@ export default function positionDropdown(element: HTMLElement, opts: Opts) {
70
81
  const winWidth = window.innerWidth
71
82
  const winHeight = window.innerHeight
72
83
  const screenOffset = 8
84
+ const fixedToAnchor = widthMode === "fixed-to-anchor"
85
+ const minAnchor = widthMode === "min-to-anchor"
73
86
  let styles: Styles = {
74
87
  maxHeight,
75
- minWidth: useAnchorWidth ? anchorBounds.width : minWidth,
76
- maxWidth: useAnchorWidth ? anchorBounds.width : maxWidth,
88
+ minWidth: fixedToAnchor || minAnchor ? anchorBounds.width : minWidth,
89
+ maxWidth: fixedToAnchor ? anchorBounds.width : maxWidth,
90
+ widthMode,
77
91
  left: 0,
78
92
  top: 0,
79
93
  }
@@ -266,6 +280,9 @@ export default function positionDropdown(element: HTMLElement, opts: Opts) {
266
280
 
267
281
  for (const [key, value] of Object.entries(styles)) {
268
282
  const name = key as keyof Styles
283
+ if (name === "widthMode") {
284
+ continue
285
+ }
269
286
  if (value != null) {
270
287
  element.style[name] = `${value}px`
271
288
  } else {
@@ -15,6 +15,8 @@
15
15
  export let options: O[] = []
16
16
  export let helpText: string | undefined = undefined
17
17
  export let required: boolean | undefined = false
18
+ export let popoverAutoWidth = false
19
+ export let wrapText = false
18
20
 
19
21
  const extractProperty = (item: O, property: string): string => {
20
22
  if (item && typeof item === "object" && property in item) {
@@ -63,6 +65,8 @@
63
65
  {readonly}
64
66
  {getOptionLabel}
65
67
  {getOptionValue}
68
+ {popoverAutoWidth}
69
+ {wrapText}
66
70
  on:change={onChange}
67
71
  on:pick
68
72
  on:type
@@ -14,6 +14,7 @@
14
14
  export let options: O[] = []
15
15
  export let disabled = false
16
16
  export let readonly = false
17
+ export let columns = 1
17
18
  export let getOptionLabel = (option: O) => `${option}`
18
19
  export let getOptionValue = (option: O) => option as unknown as V
19
20
  export let showSelectAll = false
@@ -48,9 +49,19 @@
48
49
  dispatch("change", allValues)
49
50
  }
50
51
  }
52
+
53
+ $: horizontalColumns =
54
+ direction === "horizontal" && columns > 1
55
+ ? `repeat(${columns}, minmax(0, 1fr))`
56
+ : undefined
57
+ $: useGridLayout = Boolean(horizontalColumns)
51
58
  </script>
52
59
 
53
- <div class={`spectrum-FieldGroup spectrum-FieldGroup--${direction}`}>
60
+ <div
61
+ class={`spectrum-FieldGroup spectrum-FieldGroup--${direction}`}
62
+ class:horizontal={useGridLayout}
63
+ style:grid-template-columns={horizontalColumns}
64
+ >
54
65
  {#if showSelectAll && options?.length > 0}
55
66
  <div
56
67
  title={selectAllText}
@@ -122,6 +133,13 @@
122
133
  .readonly {
123
134
  pointer-events: none;
124
135
  }
136
+ .horizontal {
137
+ display: grid;
138
+ align-items: start;
139
+ column-gap: var(--spectrum-global-dimension-size-200);
140
+ row-gap: var(--spectrum-global-dimension-size-100);
141
+ width: 100%;
142
+ }
125
143
  .icon {
126
144
  display: none;
127
145
  left: 50%;
@@ -139,6 +157,10 @@
139
157
  margin-bottom: 8px;
140
158
  padding: 0;
141
159
  }
160
+ .horizontal .select-all-checkbox {
161
+ grid-column: 1 / -1;
162
+ margin-bottom: 0;
163
+ }
142
164
  .select-all-checkbox .spectrum-Checkbox {
143
165
  padding: 0;
144
166
  }
@@ -21,6 +21,8 @@
21
21
  export let options: O[] = []
22
22
  export let getOptionLabel = (option: O) => `${option}`
23
23
  export let getOptionValue = (option: O) => `${option}`
24
+ export let popoverAutoWidth = false
25
+ export let wrapText = false
24
26
 
25
27
  const dispatch = createEventDispatcher<{
26
28
  change: string
@@ -112,10 +114,14 @@
112
114
  {open}
113
115
  align={PopoverAlignment.Left}
114
116
  on:close={() => (open = false)}
115
- useAnchorWidth
117
+ widthMode={popoverAutoWidth ? "min-to-anchor" : "fixed-to-anchor"}
118
+ maxWidth={popoverAutoWidth ? 400 : undefined}
119
+ closeOnScroll
116
120
  >
117
121
  <div
118
122
  class="popover-content"
123
+ class:auto-width={popoverAutoWidth}
124
+ class:wrap-text={wrapText}
119
125
  use:clickOutside={() => {
120
126
  open = false
121
127
  }}
@@ -175,5 +181,24 @@
175
181
  .popover-content:not(.auto-width) .spectrum-Menu-itemLabel {
176
182
  width: 0;
177
183
  flex: 1 1 auto;
184
+ overflow: hidden;
185
+ text-overflow: ellipsis;
186
+ white-space: nowrap;
187
+ }
188
+ .popover-content.auto-width .spectrum-Menu-item {
189
+ padding-right: var(--spacing-xl);
190
+ }
191
+ .popover-content.wrap-text .spectrum-Menu-item {
192
+ height: auto;
193
+ min-height: var(--spectrum-menu-item-height);
194
+ align-items: flex-start;
195
+ }
196
+ .popover-content.wrap-text .spectrum-Menu-itemLabel {
197
+ white-space: normal;
198
+ overflow: visible;
199
+ text-overflow: unset;
200
+ overflow-wrap: anywhere;
201
+ width: auto;
202
+ flex: 1 1 auto;
178
203
  }
179
204
  </style>
@@ -77,7 +77,7 @@
77
77
  portalTarget={appendTo}
78
78
  {anchor}
79
79
  {align}
80
- useAnchorWidth={timeOnly}
80
+ widthMode={timeOnly ? "fixed-to-anchor" : "no-anchor"}
81
81
  resizable={false}
82
82
  >
83
83
  {#if isOpen}
@@ -18,6 +18,7 @@
18
18
  export let autocomplete: boolean = false
19
19
  export let sort: boolean = false
20
20
  export let autoWidth: boolean = false
21
+ export let popoverAutoWidth: boolean = false
21
22
  export let searchTerm: string | null = null
22
23
  export let customPopoverHeight: string | undefined = undefined
23
24
  export let open: boolean = false
@@ -140,6 +141,7 @@
140
141
  onSelectOption={toggleOption}
141
142
  {sort}
142
143
  {autoWidth}
144
+ {popoverAutoWidth}
143
145
  {customPopoverHeight}
144
146
  {loading}
145
147
  {onOptionMouseenter}
@@ -55,6 +55,7 @@
55
55
  export let readonly: boolean = false
56
56
  export let quiet: boolean = false
57
57
  export let autoWidth: boolean | undefined = false
58
+ export let popoverAutoWidth: boolean | undefined = false
58
59
  export let autocomplete: boolean = false
59
60
  export let sort: boolean = false
60
61
  export let searchTerm: string | null = null
@@ -93,6 +94,7 @@
93
94
  let virtualPaddingTop = 0
94
95
  let virtualPaddingBottom = 0
95
96
 
97
+ $: effectivePopoverAutoWidth = autoWidth || popoverAutoWidth
96
98
  const resolveIcon = (icon: PickerIconInput): ResolvedIcon | null => {
97
99
  if (!icon) {
98
100
  return null
@@ -278,14 +280,15 @@
278
280
  align={align || PopoverAlignment.Left}
279
281
  {open}
280
282
  on:close={() => (open = false)}
281
- useAnchorWidth={!autoWidth}
282
- maxWidth={autoWidth ? 400 : undefined}
283
+ widthMode={effectivePopoverAutoWidth ? "min-to-anchor" : "fixed-to-anchor"}
284
+ maxWidth={effectivePopoverAutoWidth ? 400 : undefined}
283
285
  customHeight={customPopoverHeight}
284
286
  {maxHeight}
287
+ closeOnScroll
285
288
  >
286
289
  <div
287
290
  class="popover-content"
288
- class:auto-width={autoWidth}
291
+ class:auto-width={effectivePopoverAutoWidth}
289
292
  class:wrap-text={wrapText}
290
293
  class:size-s={size === "S"}
291
294
  class:size-m={size === "M"}
@@ -37,6 +37,7 @@
37
37
  export let quiet: boolean = false
38
38
  export let bordered: boolean = true
39
39
  export let autoWidth: boolean = false
40
+ export let popoverAutoWidth: boolean = false
40
41
  export let autocomplete: boolean = false
41
42
  export let sort: boolean = false
42
43
  export let align: PopoverAlignment | undefined = PopoverAlignment.Left
@@ -122,6 +123,7 @@
122
123
  {fieldColour}
123
124
  {options}
124
125
  {autoWidth}
126
+ {popoverAutoWidth}
125
127
  {align}
126
128
  {footer}
127
129
  {getOptionLabel}
@@ -20,6 +20,7 @@
20
20
  ) => option
21
21
  export let sort = false
22
22
  export let autoWidth = false
23
+ export let popoverAutoWidth = false
23
24
  export let autocomplete = false
24
25
  export let searchTerm: string | undefined = undefined
25
26
  export let customPopoverHeight: string | undefined = undefined
@@ -48,6 +49,7 @@
48
49
  {getOptionLabel}
49
50
  {getOptionValue}
50
51
  {autoWidth}
52
+ {popoverAutoWidth}
51
53
  {autocomplete}
52
54
  {customPopoverHeight}
53
55
  {onOptionMouseenter}
@@ -37,6 +37,7 @@
37
37
  export let size: "S" | "M" | "L" = "M"
38
38
  export let bordered: boolean = true
39
39
  export let autoWidth: boolean = false
40
+ export let popoverAutoWidth: boolean = false
40
41
  export let sort: boolean = false
41
42
  export let tooltip: string | undefined = undefined
42
43
  export let tooltipMessage:
@@ -92,6 +93,7 @@
92
93
  {options}
93
94
  {placeholder}
94
95
  {autoWidth}
96
+ {popoverAutoWidth}
95
97
  {sort}
96
98
  {align}
97
99
  {footer}
@@ -15,7 +15,7 @@
15
15
  import positionDropdown, {
16
16
  type UpdateHandler,
17
17
  } from "../Actions/positionDropdown"
18
- import { PopoverAlignment } from "../constants"
18
+ import { PopoverAlignment, type PopoverWidthMode } from "../constants"
19
19
  import Context from "../context"
20
20
 
21
21
  export let anchor: HTMLElement | undefined
@@ -27,7 +27,7 @@
27
27
  export let maxHeight: number | undefined = undefined
28
28
  export let borderRadius: string | undefined = undefined
29
29
  export let open = false
30
- export let useAnchorWidth = false
30
+ export let widthMode: PopoverWidthMode = "no-anchor"
31
31
  export let dismissible = true
32
32
  export let offset = 4
33
33
  export let customHeight: string | undefined = undefined
@@ -38,6 +38,7 @@
38
38
  export let clickOutsideOverride = false
39
39
  export let resizable = true
40
40
  export let wrap = false
41
+ export let closeOnScroll = false
41
42
 
42
43
  const dispatch = createEventDispatcher<{ open: void; close: void }>()
43
44
  const animationDuration = 260
@@ -127,11 +128,12 @@
127
128
  maxHeight,
128
129
  maxWidth,
129
130
  minWidth,
130
- useAnchorWidth,
131
+ widthMode,
131
132
  offset,
132
133
  customUpdate: handlePositionUpdate,
133
134
  resizable,
134
135
  wrap,
136
+ onScroll: closeOnScroll ? hide : undefined,
135
137
  }}
136
138
  use:clickOutside={{
137
139
  callback: dismissible ? handleOutsideClick : () => {},
package/src/constants.ts CHANGED
@@ -8,6 +8,8 @@ export enum PopoverAlignment {
8
8
  LeftContextMenu = "left-context-menu",
9
9
  }
10
10
 
11
+ export type PopoverWidthMode = "no-anchor" | "min-to-anchor" | "fixed-to-anchor"
12
+
11
13
  export enum TooltipPosition {
12
14
  Top = "top",
13
15
  Right = "right",