@budibase/frontend-core 2.11.43 → 2.11.45
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 +4 -4
- package/src/components/grid/cells/HeaderCell.svelte +183 -37
- package/src/components/grid/layout/NewRow.svelte +4 -2
- package/src/components/grid/overlays/KeyboardManager.svelte +1 -0
- package/src/components/grid/stores/datasource.js +11 -5
- package/src/components/grid/stores/datasources/nonPlus.js +5 -2
- package/src/components/grid/stores/datasources/table.js +5 -2
- package/src/components/grid/stores/datasources/viewV2.js +29 -23
- package/src/components/grid/stores/filter.js +67 -1
- package/src/components/grid/stores/rows.js +9 -3
- package/src/fetch/ViewV2Fetch.js +22 -3
- package/src/fetch/index.js +16 -4
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.45",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
7
7
|
"svelte": "src/index.js",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@budibase/bbui": "2.11.
|
|
10
|
-
"@budibase/shared-core": "2.11.
|
|
9
|
+
"@budibase/bbui": "2.11.45",
|
|
10
|
+
"@budibase/shared-core": "2.11.45",
|
|
11
11
|
"dayjs": "^1.10.8",
|
|
12
12
|
"lodash": "^4.17.21",
|
|
13
13
|
"socket.io-client": "^4.6.1",
|
|
14
14
|
"svelte": "^3.46.2"
|
|
15
15
|
},
|
|
16
|
-
"gitHead": "
|
|
16
|
+
"gitHead": "c3b2735a78e2c16d36d6cee6e534c7f7dc39fa98"
|
|
17
17
|
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { Icon, Popover, Menu, MenuItem, clickOutside } from "@budibase/bbui"
|
|
5
5
|
import GridCell from "./GridCell.svelte"
|
|
6
6
|
import { getColumnIcon } from "../lib/utils"
|
|
7
|
+
import { debounce } from "../../../utils/utils"
|
|
8
|
+
import { FieldType, FormulaTypes } from "@budibase/types"
|
|
7
9
|
|
|
8
10
|
export let column
|
|
9
11
|
export let idx
|
|
@@ -24,23 +26,69 @@
|
|
|
24
26
|
definition,
|
|
25
27
|
datasource,
|
|
26
28
|
schema,
|
|
29
|
+
focusedCellId,
|
|
30
|
+
filter,
|
|
31
|
+
inlineFilters,
|
|
27
32
|
} = getContext("grid")
|
|
28
33
|
|
|
34
|
+
const searchableTypes = [
|
|
35
|
+
FieldType.STRING,
|
|
36
|
+
FieldType.OPTIONS,
|
|
37
|
+
FieldType.NUMBER,
|
|
38
|
+
FieldType.BIGINT,
|
|
39
|
+
FieldType.ARRAY,
|
|
40
|
+
FieldType.LONGFORM,
|
|
41
|
+
]
|
|
42
|
+
|
|
29
43
|
let anchor
|
|
30
44
|
let open = false
|
|
31
45
|
let editIsOpen = false
|
|
32
46
|
let timeout
|
|
33
47
|
let popover
|
|
48
|
+
let searchValue
|
|
49
|
+
let input
|
|
34
50
|
|
|
35
51
|
$: sortedBy = column.name === $sort.column
|
|
36
52
|
$: canMoveLeft = orderable && idx > 0
|
|
37
53
|
$: canMoveRight = orderable && idx < $renderedColumns.length - 1
|
|
38
|
-
$:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
$:
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
$: sortingLabels = getSortingLabels(column.schema?.type)
|
|
55
|
+
$: searchable = isColumnSearchable(column)
|
|
56
|
+
$: resetSearchValue(column.name)
|
|
57
|
+
$: searching = searchValue != null
|
|
58
|
+
$: debouncedUpdateFilter(searchValue)
|
|
59
|
+
|
|
60
|
+
const getSortingLabels = type => {
|
|
61
|
+
switch (type) {
|
|
62
|
+
case FieldType.NUMBER:
|
|
63
|
+
case FieldType.BIGINT:
|
|
64
|
+
return {
|
|
65
|
+
ascending: "low-high",
|
|
66
|
+
descending: "high-low",
|
|
67
|
+
}
|
|
68
|
+
case FieldType.DATETIME:
|
|
69
|
+
return {
|
|
70
|
+
ascending: "old-new",
|
|
71
|
+
descending: "new-old",
|
|
72
|
+
}
|
|
73
|
+
default:
|
|
74
|
+
return {
|
|
75
|
+
ascending: "A-Z",
|
|
76
|
+
descending: "Z-A",
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const resetSearchValue = name => {
|
|
82
|
+
searchValue = $inlineFilters?.find(x => x.id === `inline-${name}`)?.value
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const isColumnSearchable = col => {
|
|
86
|
+
const { type, formulaType } = col.schema
|
|
87
|
+
return (
|
|
88
|
+
searchableTypes.includes(type) ||
|
|
89
|
+
(type === FieldType.FORMULA && formulaType === FormulaTypes.STATIC)
|
|
90
|
+
)
|
|
91
|
+
}
|
|
44
92
|
|
|
45
93
|
const editColumn = async () => {
|
|
46
94
|
editIsOpen = true
|
|
@@ -141,12 +189,46 @@
|
|
|
141
189
|
})
|
|
142
190
|
}
|
|
143
191
|
|
|
192
|
+
const startSearching = async () => {
|
|
193
|
+
$focusedCellId = null
|
|
194
|
+
searchValue = ""
|
|
195
|
+
await tick()
|
|
196
|
+
input?.focus()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const onInputKeyDown = e => {
|
|
200
|
+
if (e.key === "Enter") {
|
|
201
|
+
updateFilter()
|
|
202
|
+
} else if (e.key === "Escape") {
|
|
203
|
+
input?.blur()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const stopSearching = () => {
|
|
208
|
+
searchValue = null
|
|
209
|
+
updateFilter()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const onBlurInput = () => {
|
|
213
|
+
if (searchValue === "") {
|
|
214
|
+
searchValue = null
|
|
215
|
+
}
|
|
216
|
+
updateFilter()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const updateFilter = () => {
|
|
220
|
+
filter.actions.addInlineFilter(column, searchValue)
|
|
221
|
+
}
|
|
222
|
+
const debouncedUpdateFilter = debounce(updateFilter, 250)
|
|
223
|
+
|
|
144
224
|
onMount(() => subscribe("close-edit-column", cancelEdit))
|
|
145
225
|
</script>
|
|
146
226
|
|
|
147
227
|
<div
|
|
148
228
|
class="header-cell"
|
|
149
229
|
class:open
|
|
230
|
+
class:searchable
|
|
231
|
+
class:searching
|
|
150
232
|
style="flex: 0 0 {column.width}px;"
|
|
151
233
|
bind:this={anchor}
|
|
152
234
|
class:disabled={$isReordering || $isResizing}
|
|
@@ -161,30 +243,49 @@
|
|
|
161
243
|
defaultHeight
|
|
162
244
|
center
|
|
163
245
|
>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
246
|
+
{#if searching}
|
|
247
|
+
<input
|
|
248
|
+
bind:this={input}
|
|
249
|
+
type="text"
|
|
250
|
+
bind:value={searchValue}
|
|
251
|
+
on:blur={onBlurInput}
|
|
252
|
+
on:click={() => focusedCellId.set(null)}
|
|
253
|
+
on:keydown={onInputKeyDown}
|
|
254
|
+
data-grid-ignore
|
|
255
|
+
/>
|
|
256
|
+
{/if}
|
|
257
|
+
|
|
258
|
+
<div class="column-icon">
|
|
259
|
+
<Icon size="S" name={getColumnIcon(column)} />
|
|
260
|
+
</div>
|
|
261
|
+
<div class="search-icon" on:click={startSearching}>
|
|
262
|
+
<Icon hoverable size="S" name="Search" />
|
|
263
|
+
</div>
|
|
264
|
+
|
|
169
265
|
<div class="name">
|
|
170
266
|
{column.label}
|
|
171
267
|
</div>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
268
|
+
|
|
269
|
+
{#if searching}
|
|
270
|
+
<div class="clear-icon" on:click={stopSearching}>
|
|
271
|
+
<Icon hoverable size="S" name="Close" />
|
|
272
|
+
</div>
|
|
273
|
+
{:else}
|
|
274
|
+
{#if sortedBy}
|
|
275
|
+
<div class="sort-indicator">
|
|
276
|
+
<Icon
|
|
277
|
+
hoverable
|
|
278
|
+
size="S"
|
|
279
|
+
name={$sort.order === "descending"
|
|
280
|
+
? "SortOrderDown"
|
|
281
|
+
: "SortOrderUp"}
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
{/if}
|
|
285
|
+
<div class="more-icon" on:click={() => (open = true)}>
|
|
286
|
+
<Icon hoverable size="S" name="MoreVertical" />
|
|
179
287
|
</div>
|
|
180
288
|
{/if}
|
|
181
|
-
<div class="more" on:click={() => (open = true)}>
|
|
182
|
-
<Icon
|
|
183
|
-
size="S"
|
|
184
|
-
name="MoreVertical"
|
|
185
|
-
color="var(--spectrum-global-color-gray-600)"
|
|
186
|
-
/>
|
|
187
|
-
</div>
|
|
188
289
|
</GridCell>
|
|
189
290
|
</div>
|
|
190
291
|
|
|
@@ -235,7 +336,7 @@
|
|
|
235
336
|
disabled={!canBeSortColumn(column.schema.type) ||
|
|
236
337
|
(column.name === $sort.column && $sort.order === "ascending")}
|
|
237
338
|
>
|
|
238
|
-
Sort {
|
|
339
|
+
Sort {sortingLabels.ascending}
|
|
239
340
|
</MenuItem>
|
|
240
341
|
<MenuItem
|
|
241
342
|
icon="SortOrderDown"
|
|
@@ -243,7 +344,7 @@
|
|
|
243
344
|
disabled={!canBeSortColumn(column.schema.type) ||
|
|
244
345
|
(column.name === $sort.column && $sort.order === "descending")}
|
|
245
346
|
>
|
|
246
|
-
Sort {
|
|
347
|
+
Sort {sortingLabels.descending}
|
|
247
348
|
</MenuItem>
|
|
248
349
|
<MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
|
|
249
350
|
Move left
|
|
@@ -283,6 +384,29 @@
|
|
|
283
384
|
background: var(--grid-background-alt);
|
|
284
385
|
}
|
|
285
386
|
|
|
387
|
+
/* Icon colors */
|
|
388
|
+
.header-cell :global(.spectrum-Icon) {
|
|
389
|
+
color: var(--spectrum-global-color-gray-600);
|
|
390
|
+
}
|
|
391
|
+
.header-cell :global(.spectrum-Icon.hoverable:hover) {
|
|
392
|
+
color: var(--spectrum-global-color-gray-800) !important;
|
|
393
|
+
cursor: pointer;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* Search icon */
|
|
397
|
+
.search-icon {
|
|
398
|
+
display: none;
|
|
399
|
+
}
|
|
400
|
+
.header-cell.searchable:not(.open):hover .search-icon,
|
|
401
|
+
.header-cell.searchable.searching .search-icon {
|
|
402
|
+
display: block;
|
|
403
|
+
}
|
|
404
|
+
.header-cell.searchable:not(.open):hover .column-icon,
|
|
405
|
+
.header-cell.searchable.searching .column-icon {
|
|
406
|
+
display: none;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Main center content */
|
|
286
410
|
.name {
|
|
287
411
|
flex: 1 1 auto;
|
|
288
412
|
width: 0;
|
|
@@ -290,23 +414,45 @@
|
|
|
290
414
|
text-overflow: ellipsis;
|
|
291
415
|
overflow: hidden;
|
|
292
416
|
}
|
|
417
|
+
.header-cell.searching .name {
|
|
418
|
+
opacity: 0;
|
|
419
|
+
pointer-events: none;
|
|
420
|
+
}
|
|
421
|
+
input {
|
|
422
|
+
display: none;
|
|
423
|
+
font-family: var(--font-sans);
|
|
424
|
+
outline: none;
|
|
425
|
+
border: 1px solid transparent;
|
|
426
|
+
background: transparent;
|
|
427
|
+
color: var(--spectrum-global-color-gray-800);
|
|
428
|
+
position: absolute;
|
|
429
|
+
top: 0;
|
|
430
|
+
left: 0;
|
|
431
|
+
width: 100%;
|
|
432
|
+
height: 100%;
|
|
433
|
+
padding: 0 30px;
|
|
434
|
+
border-radius: 2px;
|
|
435
|
+
}
|
|
436
|
+
input:focus {
|
|
437
|
+
border: 1px solid var(--accent-color);
|
|
438
|
+
}
|
|
439
|
+
input:not(:focus) {
|
|
440
|
+
background: var(--spectrum-global-color-gray-200);
|
|
441
|
+
}
|
|
442
|
+
.header-cell.searching input {
|
|
443
|
+
display: block;
|
|
444
|
+
}
|
|
293
445
|
|
|
294
|
-
|
|
446
|
+
/* Right icons */
|
|
447
|
+
.more-icon {
|
|
295
448
|
display: none;
|
|
296
449
|
padding: 4px;
|
|
297
450
|
margin: 0 -4px;
|
|
298
451
|
}
|
|
299
|
-
.header-cell.open .more,
|
|
300
|
-
.header-cell:hover .more {
|
|
452
|
+
.header-cell.open .more-icon,
|
|
453
|
+
.header-cell:hover .more-icon {
|
|
301
454
|
display: block;
|
|
302
455
|
}
|
|
303
|
-
.more:hover {
|
|
304
|
-
cursor: pointer;
|
|
305
|
-
}
|
|
306
|
-
.more:hover :global(.spectrum-Icon) {
|
|
307
|
-
color: var(--spectrum-global-color-gray-800) !important;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
456
|
.header-cell.open .sort-indicator,
|
|
311
457
|
.header-cell:hover .sort-indicator {
|
|
312
458
|
display: none;
|
|
@@ -27,8 +27,10 @@
|
|
|
27
27
|
rowVerticalInversionIndex,
|
|
28
28
|
columnHorizontalInversionIndex,
|
|
29
29
|
selectedRows,
|
|
30
|
-
|
|
30
|
+
loaded,
|
|
31
|
+
refreshing,
|
|
31
32
|
config,
|
|
33
|
+
filter,
|
|
32
34
|
} = getContext("grid")
|
|
33
35
|
|
|
34
36
|
let visible = false
|
|
@@ -153,7 +155,7 @@
|
|
|
153
155
|
<!-- New row FAB -->
|
|
154
156
|
<TempTooltip
|
|
155
157
|
text="Click here to create your first row"
|
|
156
|
-
condition={hasNoRows && !$
|
|
158
|
+
condition={hasNoRows && $loaded && !$filter?.length && !$refreshing}
|
|
157
159
|
type={TooltipType.Info}
|
|
158
160
|
>
|
|
159
161
|
{#if !visible && !selectedRowCount && $config.canAddRows}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { derived, get
|
|
2
|
-
import { getDatasourceDefinition } from "../../../fetch"
|
|
1
|
+
import { derived, get } from "svelte/store"
|
|
2
|
+
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch"
|
|
3
|
+
import { memo } from "../../../utils"
|
|
3
4
|
|
|
4
5
|
export const createStores = () => {
|
|
5
|
-
const definition =
|
|
6
|
+
const definition = memo(null)
|
|
6
7
|
|
|
7
8
|
return {
|
|
8
9
|
definition,
|
|
@@ -10,10 +11,15 @@ export const createStores = () => {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export const deriveStores = context => {
|
|
13
|
-
const { definition, schemaOverrides, columnWhitelist, datasource } =
|
|
14
|
+
const { API, definition, schemaOverrides, columnWhitelist, datasource } =
|
|
15
|
+
context
|
|
14
16
|
|
|
15
17
|
const schema = derived(definition, $definition => {
|
|
16
|
-
let schema =
|
|
18
|
+
let schema = getDatasourceSchema({
|
|
19
|
+
API,
|
|
20
|
+
datasource: get(datasource),
|
|
21
|
+
definition: $definition,
|
|
22
|
+
})
|
|
17
23
|
if (!schema) {
|
|
18
24
|
return null
|
|
19
25
|
}
|
|
@@ -66,6 +66,8 @@ export const initialise = context => {
|
|
|
66
66
|
datasource,
|
|
67
67
|
sort,
|
|
68
68
|
filter,
|
|
69
|
+
inlineFilters,
|
|
70
|
+
allFilters,
|
|
69
71
|
nonPlus,
|
|
70
72
|
initialFilter,
|
|
71
73
|
initialSortColumn,
|
|
@@ -87,6 +89,7 @@ export const initialise = context => {
|
|
|
87
89
|
|
|
88
90
|
// Wipe state
|
|
89
91
|
filter.set(get(initialFilter))
|
|
92
|
+
inlineFilters.set([])
|
|
90
93
|
sort.set({
|
|
91
94
|
column: get(initialSortColumn),
|
|
92
95
|
order: get(initialSortOrder) || "ascending",
|
|
@@ -94,14 +97,14 @@ export const initialise = context => {
|
|
|
94
97
|
|
|
95
98
|
// Update fetch when filter changes
|
|
96
99
|
unsubscribers.push(
|
|
97
|
-
|
|
100
|
+
allFilters.subscribe($allFilters => {
|
|
98
101
|
// Ensure we're updating the correct fetch
|
|
99
102
|
const $fetch = get(fetch)
|
|
100
103
|
if (!isSameDatasource($fetch?.options?.datasource, $datasource)) {
|
|
101
104
|
return
|
|
102
105
|
}
|
|
103
106
|
$fetch.update({
|
|
104
|
-
filter: $
|
|
107
|
+
filter: $allFilters,
|
|
105
108
|
})
|
|
106
109
|
})
|
|
107
110
|
)
|
|
@@ -71,6 +71,8 @@ export const initialise = context => {
|
|
|
71
71
|
datasource,
|
|
72
72
|
fetch,
|
|
73
73
|
filter,
|
|
74
|
+
inlineFilters,
|
|
75
|
+
allFilters,
|
|
74
76
|
sort,
|
|
75
77
|
table,
|
|
76
78
|
initialFilter,
|
|
@@ -93,6 +95,7 @@ export const initialise = context => {
|
|
|
93
95
|
|
|
94
96
|
// Wipe state
|
|
95
97
|
filter.set(get(initialFilter))
|
|
98
|
+
inlineFilters.set([])
|
|
96
99
|
sort.set({
|
|
97
100
|
column: get(initialSortColumn),
|
|
98
101
|
order: get(initialSortOrder) || "ascending",
|
|
@@ -100,14 +103,14 @@ export const initialise = context => {
|
|
|
100
103
|
|
|
101
104
|
// Update fetch when filter changes
|
|
102
105
|
unsubscribers.push(
|
|
103
|
-
|
|
106
|
+
allFilters.subscribe($allFilters => {
|
|
104
107
|
// Ensure we're updating the correct fetch
|
|
105
108
|
const $fetch = get(fetch)
|
|
106
109
|
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
|
|
107
110
|
return
|
|
108
111
|
}
|
|
109
112
|
$fetch.update({
|
|
110
|
-
filter: $
|
|
113
|
+
filter: $allFilters,
|
|
111
114
|
})
|
|
112
115
|
})
|
|
113
116
|
)
|
|
@@ -73,6 +73,8 @@ export const initialise = context => {
|
|
|
73
73
|
sort,
|
|
74
74
|
rows,
|
|
75
75
|
filter,
|
|
76
|
+
inlineFilters,
|
|
77
|
+
allFilters,
|
|
76
78
|
subscribe,
|
|
77
79
|
viewV2,
|
|
78
80
|
initialFilter,
|
|
@@ -97,6 +99,7 @@ export const initialise = context => {
|
|
|
97
99
|
|
|
98
100
|
// Reset state for new view
|
|
99
101
|
filter.set(get(initialFilter))
|
|
102
|
+
inlineFilters.set([])
|
|
100
103
|
sort.set({
|
|
101
104
|
column: get(initialSortColumn),
|
|
102
105
|
order: get(initialSortOrder) || "ascending",
|
|
@@ -143,21 +146,19 @@ export const initialise = context => {
|
|
|
143
146
|
order: $sort.order || "ascending",
|
|
144
147
|
},
|
|
145
148
|
})
|
|
146
|
-
await rows.actions.refreshData()
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
$fetch.update({
|
|
157
|
-
sortOrder: $sort.order || "ascending",
|
|
158
|
-
sortColumn: $sort.column,
|
|
159
|
-
})
|
|
151
|
+
|
|
152
|
+
// Also update the fetch to ensure the new sort is respected.
|
|
153
|
+
// Ensure we're updating the correct fetch.
|
|
154
|
+
const $fetch = get(fetch)
|
|
155
|
+
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
|
|
156
|
+
return
|
|
160
157
|
}
|
|
158
|
+
$fetch.update({
|
|
159
|
+
sortOrder: $sort.order,
|
|
160
|
+
sortColumn: $sort.column,
|
|
161
|
+
})
|
|
161
162
|
})
|
|
162
163
|
)
|
|
163
164
|
|
|
@@ -176,20 +177,25 @@ export const initialise = context => {
|
|
|
176
177
|
...$view,
|
|
177
178
|
query: $filter,
|
|
178
179
|
})
|
|
179
|
-
await rows.actions.refreshData()
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
})
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// Keep fetch up to date with filters.
|
|
186
|
+
// If we're able to save filters against the view then we only need to apply
|
|
187
|
+
// inline filters to the fetch, as saved filters are applied server side.
|
|
188
|
+
// If we can't save filters, then all filters must be applied to the fetch.
|
|
189
|
+
unsubscribers.push(
|
|
190
|
+
allFilters.subscribe($allFilters => {
|
|
191
|
+
// Ensure we're updating the correct fetch
|
|
192
|
+
const $fetch = get(fetch)
|
|
193
|
+
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
|
|
194
|
+
return
|
|
192
195
|
}
|
|
196
|
+
$fetch.update({
|
|
197
|
+
filter: $allFilters,
|
|
198
|
+
})
|
|
193
199
|
})
|
|
194
200
|
)
|
|
195
201
|
|
|
@@ -1,13 +1,79 @@
|
|
|
1
|
-
import { writable, get } from "svelte/store"
|
|
1
|
+
import { writable, get, derived } from "svelte/store"
|
|
2
|
+
import { FieldType } from "@budibase/types"
|
|
2
3
|
|
|
3
4
|
export const createStores = context => {
|
|
4
5
|
const { props } = context
|
|
5
6
|
|
|
6
7
|
// Initialise to default props
|
|
7
8
|
const filter = writable(get(props).initialFilter)
|
|
9
|
+
const inlineFilters = writable([])
|
|
8
10
|
|
|
9
11
|
return {
|
|
10
12
|
filter,
|
|
13
|
+
inlineFilters,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const deriveStores = context => {
|
|
18
|
+
const { filter, inlineFilters } = context
|
|
19
|
+
|
|
20
|
+
const allFilters = derived(
|
|
21
|
+
[filter, inlineFilters],
|
|
22
|
+
([$filter, $inlineFilters]) => {
|
|
23
|
+
return [...($filter || []), ...$inlineFilters]
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
allFilters,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const createActions = context => {
|
|
33
|
+
const { filter, inlineFilters } = context
|
|
34
|
+
|
|
35
|
+
const addInlineFilter = (column, value) => {
|
|
36
|
+
const filterId = `inline-${column.name}`
|
|
37
|
+
const type = column.schema.type
|
|
38
|
+
let inlineFilter = {
|
|
39
|
+
field: column.name,
|
|
40
|
+
id: filterId,
|
|
41
|
+
operator: "string",
|
|
42
|
+
valueType: "value",
|
|
43
|
+
type,
|
|
44
|
+
value,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Add overrides specific so the certain column type
|
|
48
|
+
if (type === FieldType.NUMBER) {
|
|
49
|
+
inlineFilter.value = parseFloat(value)
|
|
50
|
+
inlineFilter.operator = "equal"
|
|
51
|
+
} else if (type === FieldType.BIGINT) {
|
|
52
|
+
inlineFilter.operator = "equal"
|
|
53
|
+
} else if (type === FieldType.ARRAY) {
|
|
54
|
+
inlineFilter.operator = "contains"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add this filter
|
|
58
|
+
inlineFilters.update($inlineFilters => {
|
|
59
|
+
// Remove any existing inline filter for this column
|
|
60
|
+
$inlineFilters = $inlineFilters?.filter(x => x.id !== filterId)
|
|
61
|
+
|
|
62
|
+
// Add new one if a value exists
|
|
63
|
+
if (value) {
|
|
64
|
+
$inlineFilters.push(inlineFilter)
|
|
65
|
+
}
|
|
66
|
+
return $inlineFilters
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
filter: {
|
|
72
|
+
...filter,
|
|
73
|
+
actions: {
|
|
74
|
+
addInlineFilter,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
11
77
|
}
|
|
12
78
|
}
|
|
13
79
|
|
|
@@ -8,6 +8,7 @@ export const createStores = () => {
|
|
|
8
8
|
const rows = writable([])
|
|
9
9
|
const loading = writable(false)
|
|
10
10
|
const loaded = writable(false)
|
|
11
|
+
const refreshing = writable(false)
|
|
11
12
|
const rowChangeCache = writable({})
|
|
12
13
|
const inProgressChanges = writable({})
|
|
13
14
|
const hasNextPage = writable(false)
|
|
@@ -53,6 +54,7 @@ export const createStores = () => {
|
|
|
53
54
|
fetch,
|
|
54
55
|
rowLookupMap,
|
|
55
56
|
loaded,
|
|
57
|
+
refreshing,
|
|
56
58
|
loading,
|
|
57
59
|
rowChangeCache,
|
|
58
60
|
inProgressChanges,
|
|
@@ -66,7 +68,7 @@ export const createActions = context => {
|
|
|
66
68
|
rows,
|
|
67
69
|
rowLookupMap,
|
|
68
70
|
definition,
|
|
69
|
-
|
|
71
|
+
allFilters,
|
|
70
72
|
loading,
|
|
71
73
|
sort,
|
|
72
74
|
datasource,
|
|
@@ -82,6 +84,7 @@ export const createActions = context => {
|
|
|
82
84
|
notifications,
|
|
83
85
|
fetch,
|
|
84
86
|
isDatasourcePlus,
|
|
87
|
+
refreshing,
|
|
85
88
|
} = context
|
|
86
89
|
const instanceLoaded = writable(false)
|
|
87
90
|
|
|
@@ -108,7 +111,7 @@ export const createActions = context => {
|
|
|
108
111
|
// Tick to allow other reactive logic to update stores when datasource changes
|
|
109
112
|
// before proceeding. This allows us to wipe filters etc if needed.
|
|
110
113
|
await tick()
|
|
111
|
-
const $
|
|
114
|
+
const $allFilters = get(allFilters)
|
|
112
115
|
const $sort = get(sort)
|
|
113
116
|
|
|
114
117
|
// Determine how many rows to fetch per page
|
|
@@ -120,7 +123,7 @@ export const createActions = context => {
|
|
|
120
123
|
API,
|
|
121
124
|
datasource: $datasource,
|
|
122
125
|
options: {
|
|
123
|
-
filter: $
|
|
126
|
+
filter: $allFilters,
|
|
124
127
|
sortColumn: $sort.column,
|
|
125
128
|
sortOrder: $sort.order,
|
|
126
129
|
limit,
|
|
@@ -176,6 +179,9 @@ export const createActions = context => {
|
|
|
176
179
|
// Notify that we're loaded
|
|
177
180
|
loading.set(false)
|
|
178
181
|
}
|
|
182
|
+
|
|
183
|
+
// Update refreshing state
|
|
184
|
+
refreshing.set($fetch.loading)
|
|
179
185
|
})
|
|
180
186
|
|
|
181
187
|
fetch.set(newFetch)
|
package/src/fetch/ViewV2Fetch.js
CHANGED
|
@@ -35,9 +35,28 @@ export default class ViewV2Fetch extends DataFetch {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async getData() {
|
|
38
|
-
const {
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const {
|
|
39
|
+
datasource,
|
|
40
|
+
limit,
|
|
41
|
+
sortColumn,
|
|
42
|
+
sortOrder,
|
|
43
|
+
sortType,
|
|
44
|
+
paginate,
|
|
45
|
+
filter,
|
|
46
|
+
} = this.options
|
|
47
|
+
const { cursor, query, definition } = get(this.store)
|
|
48
|
+
|
|
49
|
+
// If sort/filter params are not defined, update options to store the
|
|
50
|
+
// params built in to this view. This ensures that we can accurately
|
|
51
|
+
// compare old and new params and skip a redundant API call.
|
|
52
|
+
if (!sortColumn && definition.sort?.field) {
|
|
53
|
+
this.options.sortColumn = definition.sort.field
|
|
54
|
+
this.options.sortOrder = definition.sort.order
|
|
55
|
+
}
|
|
56
|
+
if (!filter?.length && definition.query?.length) {
|
|
57
|
+
this.options.filter = definition.query
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
try {
|
|
42
61
|
const res = await this.API.viewV2.fetch({
|
|
43
62
|
viewId: datasource.id,
|
package/src/fetch/index.js
CHANGED
|
@@ -32,12 +32,24 @@ export const fetchData = ({ API, datasource, options }) => {
|
|
|
32
32
|
return new Fetch({ API, datasource, ...options })
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
|
|
35
|
+
// Creates an empty fetch instance with no datasource configured, so no data
|
|
36
|
+
// will initially be loaded
|
|
37
|
+
const createEmptyFetchInstance = ({ API, datasource }) => {
|
|
37
38
|
const handler = DataFetchMap[datasource?.type]
|
|
38
39
|
if (!handler) {
|
|
39
40
|
return null
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
return new handler({ API })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fetches the definition of any type of datasource
|
|
46
|
+
export const getDatasourceDefinition = async ({ API, datasource }) => {
|
|
47
|
+
const instance = createEmptyFetchInstance({ API, datasource })
|
|
48
|
+
return await instance?.getDefinition(datasource)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Fetches the schema of any type of datasource
|
|
52
|
+
export const getDatasourceSchema = ({ API, datasource, definition }) => {
|
|
53
|
+
const instance = createEmptyFetchInstance({ API, datasource })
|
|
54
|
+
return instance?.getSchema(datasource, definition)
|
|
43
55
|
}
|