@budibase/bbui 3.23.38 → 3.23.48
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.23.
|
|
4
|
+
"version": "3.23.48",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"svelte": "src/index.ts",
|
|
7
7
|
"module": "dist/bbui.mjs",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "0a78faa2adbb8fb3b81de2b9b00352e60178f12f"
|
|
111
111
|
}
|
|
@@ -27,9 +27,14 @@
|
|
|
27
27
|
class:open={show || !collapsible}
|
|
28
28
|
>
|
|
29
29
|
<div class="name">{name}</div>
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
30
|
+
<div class="header-right">
|
|
31
|
+
<div class="actions" on:click|stopPropagation>
|
|
32
|
+
<slot name="actions" />
|
|
33
|
+
</div>
|
|
34
|
+
{#if collapsible}
|
|
35
|
+
<Icon size="S" name={show ? "minus" : "plus"} />
|
|
36
|
+
{/if}
|
|
37
|
+
</div>
|
|
33
38
|
</div>
|
|
34
39
|
{/if}
|
|
35
40
|
<div
|
|
@@ -91,6 +96,19 @@
|
|
|
91
96
|
user-select: none;
|
|
92
97
|
}
|
|
93
98
|
|
|
99
|
+
.header-right {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: flex-end;
|
|
103
|
+
gap: var(--spacing-s);
|
|
104
|
+
flex: 0 0 auto;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.actions {
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
.property-panel {
|
|
95
113
|
display: none;
|
|
96
114
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import "@spectrum-css/menu/dist/index-vars.css"
|
|
8
8
|
import "@spectrum-css/picker/dist/index-vars.css"
|
|
9
9
|
import "@spectrum-css/popover/dist/index-vars.css"
|
|
10
|
-
import { createEventDispatcher, onDestroy } from "svelte"
|
|
10
|
+
import { createEventDispatcher, onDestroy, tick } from "svelte"
|
|
11
11
|
import clickOutside from "../../Actions/clickOutside"
|
|
12
12
|
import Icon from "../../Icon/Icon.svelte"
|
|
13
13
|
import Popover from "../../Popover/Popover.svelte"
|
|
@@ -74,11 +74,19 @@
|
|
|
74
74
|
export let toggleSelectAll: () => void = () => {}
|
|
75
75
|
export let hideChevron: boolean = false
|
|
76
76
|
|
|
77
|
+
const maxHeight = 360
|
|
78
|
+
const VIRTUALIZATION_THRESHOLD = 200
|
|
79
|
+
const VIRTUALIZATION_OVERSCAN = 6
|
|
80
|
+
const OPTION_HEIGHT = 36
|
|
81
|
+
|
|
77
82
|
const dispatch = createEventDispatcher()
|
|
78
83
|
|
|
79
84
|
let button: HTMLButtonElement | null = null
|
|
80
85
|
let component: HTMLUListElement | null = null
|
|
81
86
|
let optionIconDescriptor: ResolvedIcon | null = null
|
|
87
|
+
let virtualizedOptions: Array<{ option: O; idx: number }> = []
|
|
88
|
+
let virtualPaddingTop = 0
|
|
89
|
+
let virtualPaddingBottom = 0
|
|
82
90
|
|
|
83
91
|
const resolveIcon = (icon: PickerIconInput): ResolvedIcon | null => {
|
|
84
92
|
if (!icon) {
|
|
@@ -105,6 +113,22 @@
|
|
|
105
113
|
searchTerm,
|
|
106
114
|
getOptionLabel
|
|
107
115
|
)
|
|
116
|
+
$: virtualizationEnabled = filteredOptions.length > VIRTUALIZATION_THRESHOLD
|
|
117
|
+
$: {
|
|
118
|
+
if (!virtualizationEnabled) {
|
|
119
|
+
virtualizedOptions = filteredOptions.map((option, idx) => ({
|
|
120
|
+
option,
|
|
121
|
+
idx,
|
|
122
|
+
}))
|
|
123
|
+
virtualPaddingTop = 0
|
|
124
|
+
virtualPaddingBottom = 0
|
|
125
|
+
} else {
|
|
126
|
+
tick().then(updateVirtualSlice)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
$: if (virtualizationEnabled && component) {
|
|
130
|
+
tick().then(updateVirtualSlice)
|
|
131
|
+
}
|
|
108
132
|
|
|
109
133
|
const onClick = (e: MouseEvent) => {
|
|
110
134
|
e.preventDefault()
|
|
@@ -158,9 +182,43 @@
|
|
|
158
182
|
}
|
|
159
183
|
}
|
|
160
184
|
|
|
161
|
-
|
|
185
|
+
const updateVirtualSlice = () => {
|
|
186
|
+
if (!virtualizationEnabled || !component) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
const total = filteredOptions.length
|
|
190
|
+
if (!total) {
|
|
191
|
+
virtualizedOptions = []
|
|
192
|
+
virtualPaddingTop = 0
|
|
193
|
+
virtualPaddingBottom = 0
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
const scrollTop = component.scrollTop
|
|
197
|
+
const baseStart = Math.floor(scrollTop / OPTION_HEIGHT)
|
|
198
|
+
const startIndex = Math.max(baseStart - VIRTUALIZATION_OVERSCAN, 0)
|
|
199
|
+
const visibleCount =
|
|
200
|
+
Math.ceil(maxHeight / OPTION_HEIGHT) + VIRTUALIZATION_OVERSCAN * 2
|
|
201
|
+
const endIndex = Math.min(startIndex + visibleCount, total)
|
|
202
|
+
virtualPaddingTop = startIndex * OPTION_HEIGHT
|
|
203
|
+
virtualPaddingBottom = Math.max(total - endIndex, 0) * OPTION_HEIGHT
|
|
204
|
+
virtualizedOptions = filteredOptions
|
|
205
|
+
.slice(startIndex, endIndex)
|
|
206
|
+
.map((option, offset) => ({
|
|
207
|
+
option,
|
|
208
|
+
idx: startIndex + offset,
|
|
209
|
+
}))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const handleScroll = (event: Event) => {
|
|
213
|
+
const target = event.currentTarget as HTMLElement
|
|
214
|
+
onScroll(event)
|
|
215
|
+
if (virtualizationEnabled && target === component) {
|
|
216
|
+
updateVirtualSlice()
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
162
220
|
onDestroy(() => {
|
|
163
|
-
component
|
|
221
|
+
component = null
|
|
164
222
|
})
|
|
165
223
|
</script>
|
|
166
224
|
|
|
@@ -209,7 +267,7 @@
|
|
|
209
267
|
useAnchorWidth={!autoWidth}
|
|
210
268
|
maxWidth={autoWidth ? 400 : undefined}
|
|
211
269
|
customHeight={customPopoverHeight}
|
|
212
|
-
maxHeight
|
|
270
|
+
{maxHeight}
|
|
213
271
|
>
|
|
214
272
|
<div
|
|
215
273
|
class="popover-content"
|
|
@@ -226,7 +284,12 @@
|
|
|
226
284
|
placeholder={searchPlaceholder}
|
|
227
285
|
/>
|
|
228
286
|
{/if}
|
|
229
|
-
<ul
|
|
287
|
+
<ul
|
|
288
|
+
class="spectrum-Menu"
|
|
289
|
+
role="listbox"
|
|
290
|
+
bind:this={component}
|
|
291
|
+
on:scroll={handleScroll}
|
|
292
|
+
>
|
|
230
293
|
{#if showSelectAll && filteredOptions.length > 0}
|
|
231
294
|
<li
|
|
232
295
|
class="spectrum-Menu-item select-all-item"
|
|
@@ -269,7 +332,14 @@
|
|
|
269
332
|
</li>
|
|
270
333
|
{/if}
|
|
271
334
|
{#if filteredOptions.length}
|
|
272
|
-
{#
|
|
335
|
+
{#if virtualizationEnabled && virtualPaddingTop > 0}
|
|
336
|
+
<li
|
|
337
|
+
class="virtual-spacer"
|
|
338
|
+
aria-hidden="true"
|
|
339
|
+
style={`height:${virtualPaddingTop}px`}
|
|
340
|
+
/>
|
|
341
|
+
{/if}
|
|
342
|
+
{#each virtualizedOptions as { option, idx } (getOptionValue(option, idx) ?? idx)}
|
|
273
343
|
<li
|
|
274
344
|
class="spectrum-Menu-item"
|
|
275
345
|
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
|
@@ -321,6 +391,13 @@
|
|
|
321
391
|
</div>
|
|
322
392
|
</li>
|
|
323
393
|
{/each}
|
|
394
|
+
{#if virtualizationEnabled && virtualPaddingBottom > 0}
|
|
395
|
+
<li
|
|
396
|
+
class="virtual-spacer"
|
|
397
|
+
aria-hidden="true"
|
|
398
|
+
style={`height:${virtualPaddingBottom}px`}
|
|
399
|
+
/>
|
|
400
|
+
{/if}
|
|
324
401
|
{/if}
|
|
325
402
|
</ul>
|
|
326
403
|
|
|
@@ -462,4 +539,10 @@
|
|
|
462
539
|
.select-all-item .select-all-check {
|
|
463
540
|
display: block;
|
|
464
541
|
}
|
|
542
|
+
.virtual-spacer {
|
|
543
|
+
list-style: none;
|
|
544
|
+
margin: 0;
|
|
545
|
+
padding: 0;
|
|
546
|
+
pointer-events: none;
|
|
547
|
+
}
|
|
465
548
|
</style>
|