@budibase/frontend-core 3.0.3 → 3.1.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,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
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": "3.0
|
|
10
|
-
"@budibase/shared-core": "3.0
|
|
11
|
-
"@budibase/types": "3.0
|
|
9
|
+
"@budibase/bbui": "3.1.0",
|
|
10
|
+
"@budibase/shared-core": "3.1.0",
|
|
11
|
+
"@budibase/types": "3.1.0",
|
|
12
12
|
"dayjs": "^1.10.8",
|
|
13
13
|
"lodash": "4.17.21",
|
|
14
14
|
"shortid": "2.2.15",
|
|
15
15
|
"socket.io-client": "^4.7.5"
|
|
16
16
|
},
|
|
17
|
-
"gitHead": "
|
|
17
|
+
"gitHead": "9499b52f113017483f4e8a7852c51e0f86d0b3a5"
|
|
18
18
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Input, Icon, Drawer, Button } from "@budibase/bbui"
|
|
3
|
+
import { isJSBinding } from "@budibase/string-templates"
|
|
4
|
+
import { createEventDispatcher } from "svelte"
|
|
5
|
+
|
|
6
|
+
export let filter
|
|
7
|
+
export let disabled = false
|
|
8
|
+
export let bindings = []
|
|
9
|
+
export let panel
|
|
10
|
+
export let drawerTitle
|
|
11
|
+
export let toReadable
|
|
12
|
+
export let toRuntime
|
|
13
|
+
|
|
14
|
+
const dispatch = createEventDispatcher()
|
|
15
|
+
|
|
16
|
+
let bindingDrawer
|
|
17
|
+
let fieldValue
|
|
18
|
+
|
|
19
|
+
$: fieldValue = filter?.field
|
|
20
|
+
$: readableValue = toReadable ? toReadable(bindings, fieldValue) : fieldValue
|
|
21
|
+
$: drawerValue = toDrawerValue(fieldValue)
|
|
22
|
+
$: isJS = isJSBinding(fieldValue)
|
|
23
|
+
|
|
24
|
+
const drawerOnChange = e => {
|
|
25
|
+
drawerValue = e.detail
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const onChange = e => {
|
|
29
|
+
fieldValue = e.detail
|
|
30
|
+
dispatch("change", {
|
|
31
|
+
field: toRuntime ? toRuntime(bindings, fieldValue) : fieldValue,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const onConfirmBinding = () => {
|
|
36
|
+
dispatch("change", {
|
|
37
|
+
field: toRuntime ? toRuntime(bindings, drawerValue) : drawerValue,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Converts arrays into strings. The CodeEditor expects a string or encoded JS
|
|
43
|
+
*
|
|
44
|
+
* @param{string} fieldValue
|
|
45
|
+
*/
|
|
46
|
+
const toDrawerValue = fieldValue => {
|
|
47
|
+
return Array.isArray(fieldValue) ? fieldValue.join(",") : readableValue
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<div>
|
|
52
|
+
<Drawer
|
|
53
|
+
on:drawerHide
|
|
54
|
+
on:drawerShow
|
|
55
|
+
bind:this={bindingDrawer}
|
|
56
|
+
title={drawerTitle || ""}
|
|
57
|
+
forceModal
|
|
58
|
+
>
|
|
59
|
+
<Button
|
|
60
|
+
cta
|
|
61
|
+
slot="buttons"
|
|
62
|
+
on:click={() => {
|
|
63
|
+
onConfirmBinding()
|
|
64
|
+
bindingDrawer.hide()
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
Confirm
|
|
68
|
+
</Button>
|
|
69
|
+
|
|
70
|
+
<svelte:component
|
|
71
|
+
this={panel}
|
|
72
|
+
slot="body"
|
|
73
|
+
value={drawerValue}
|
|
74
|
+
allowJS
|
|
75
|
+
allowHelpers
|
|
76
|
+
allowHBS
|
|
77
|
+
on:change={drawerOnChange}
|
|
78
|
+
{bindings}
|
|
79
|
+
/>
|
|
80
|
+
</Drawer>
|
|
81
|
+
|
|
82
|
+
<div class="field-wrap" class:bindings={true}>
|
|
83
|
+
<div class="field">
|
|
84
|
+
<Input
|
|
85
|
+
disabled={filter.noValue}
|
|
86
|
+
readonly={isJS}
|
|
87
|
+
value={isJS ? "(JavaScript function)" : readableValue}
|
|
88
|
+
on:change={onChange}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="binding-control">
|
|
93
|
+
{#if !disabled}
|
|
94
|
+
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
95
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
96
|
+
<div
|
|
97
|
+
class="icon binding"
|
|
98
|
+
on:click={() => {
|
|
99
|
+
bindingDrawer.show()
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
<Icon size="S" name="FlashOn" />
|
|
103
|
+
</div>
|
|
104
|
+
{/if}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<style>
|
|
110
|
+
.field-wrap {
|
|
111
|
+
display: flex;
|
|
112
|
+
}
|
|
113
|
+
.field {
|
|
114
|
+
flex: 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.field-wrap.bindings .field :global(.spectrum-Form-itemField),
|
|
118
|
+
.field-wrap.bindings .field :global(input),
|
|
119
|
+
.field-wrap.bindings .field :global(.spectrum-Picker) {
|
|
120
|
+
border-top-right-radius: 0px;
|
|
121
|
+
border-bottom-right-radius: 0px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.field-wrap.bindings
|
|
125
|
+
.field
|
|
126
|
+
:global(.spectrum-InputGroup.spectrum-Datepicker) {
|
|
127
|
+
min-width: unset;
|
|
128
|
+
border-radius: 0px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.field-wrap.bindings
|
|
132
|
+
.field
|
|
133
|
+
:global(
|
|
134
|
+
.spectrum-InputGroup.spectrum-Datepicker
|
|
135
|
+
.spectrum-Textfield-input.spectrum-InputGroup-input
|
|
136
|
+
) {
|
|
137
|
+
width: 100%;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.binding-control .icon {
|
|
141
|
+
border: 1px solid
|
|
142
|
+
var(
|
|
143
|
+
--spectrum-textfield-m-border-color,
|
|
144
|
+
var(--spectrum-alias-border-color)
|
|
145
|
+
);
|
|
146
|
+
border-left: 0px;
|
|
147
|
+
border-top-right-radius: 4px;
|
|
148
|
+
border-bottom-right-radius: 4px;
|
|
149
|
+
justify-content: center;
|
|
150
|
+
align-items: center;
|
|
151
|
+
display: flex;
|
|
152
|
+
flex-direction: row;
|
|
153
|
+
box-sizing: border-box;
|
|
154
|
+
width: 31px;
|
|
155
|
+
color: var(--spectrum-alias-text-color);
|
|
156
|
+
background-color: var(--spectrum-global-color-gray-75);
|
|
157
|
+
transition: background-color
|
|
158
|
+
var(--spectrum-global-animation-duration-100, 130ms),
|
|
159
|
+
box-shadow var(--spectrum-global-animation-duration-100, 130ms),
|
|
160
|
+
border-color var(--spectrum-global-animation-duration-100, 130ms);
|
|
161
|
+
height: calc(var(--spectrum-alias-item-height-m));
|
|
162
|
+
}
|
|
163
|
+
.binding-control .icon.binding {
|
|
164
|
+
color: var(--yellow);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.binding-control .icon:hover {
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
background-color: var(--spectrum-global-color-gray-50);
|
|
170
|
+
border-color: var(--spectrum-alias-border-color-hover);
|
|
171
|
+
color: var(--spectrum-alias-text-color-hover);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.binding-control .icon.binding:hover {
|
|
175
|
+
color: var(--yellow);
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { QueryUtils, Constants } from "@budibase/frontend-core"
|
|
17
17
|
import { getContext, createEventDispatcher } from "svelte"
|
|
18
18
|
import FilterField from "./FilterField.svelte"
|
|
19
|
+
import ConditionField from "./ConditionField.svelte"
|
|
19
20
|
import { utils } from "@budibase/shared-core"
|
|
20
21
|
|
|
21
22
|
const dispatch = createEventDispatcher()
|
|
@@ -33,8 +34,10 @@
|
|
|
33
34
|
export let datasource
|
|
34
35
|
export let behaviourFilters = false
|
|
35
36
|
export let allowBindings = false
|
|
37
|
+
export let allowOnEmpty = true
|
|
38
|
+
export let builderType = "filter"
|
|
39
|
+
export let docsURL = "https://docs.budibase.com/docs/searchfilter-data"
|
|
36
40
|
|
|
37
|
-
// Review
|
|
38
41
|
export let bindings
|
|
39
42
|
export let panel
|
|
40
43
|
export let toReadable
|
|
@@ -53,6 +56,10 @@
|
|
|
53
56
|
schemaFields = [...schemaFields, { name: "_id", type: "string" }]
|
|
54
57
|
}
|
|
55
58
|
}
|
|
59
|
+
$: prefix =
|
|
60
|
+
builderType === "filter"
|
|
61
|
+
? "Show data which matches"
|
|
62
|
+
: "Run branch when matching"
|
|
56
63
|
|
|
57
64
|
// We still may need to migrate this even though the backend does it automatically now
|
|
58
65
|
// for query definitions. This is because we might be editing saved filter definitions
|
|
@@ -103,6 +110,10 @@
|
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
const getValidOperatorsForType = filter => {
|
|
113
|
+
if (builderType === "condition") {
|
|
114
|
+
return [OperatorOptions.Equals, OperatorOptions.NotEquals]
|
|
115
|
+
}
|
|
116
|
+
|
|
106
117
|
if (!filter?.field && !filter?.name) {
|
|
107
118
|
return []
|
|
108
119
|
}
|
|
@@ -222,6 +233,9 @@
|
|
|
222
233
|
} else if (addFilter) {
|
|
223
234
|
targetGroup.filters.push({
|
|
224
235
|
valueType: FilterValueType.VALUE,
|
|
236
|
+
...(builderType === "condition"
|
|
237
|
+
? { operator: OperatorOptions.Equals.value, type: FieldType.STRING }
|
|
238
|
+
: {}),
|
|
225
239
|
})
|
|
226
240
|
} else if (group) {
|
|
227
241
|
editable.groups[groupIdx] = {
|
|
@@ -242,6 +256,11 @@
|
|
|
242
256
|
filters: [
|
|
243
257
|
{
|
|
244
258
|
valueType: FilterValueType.VALUE,
|
|
259
|
+
...(builderType === "condition"
|
|
260
|
+
? {
|
|
261
|
+
operator: OperatorOptions.Equals.value,
|
|
262
|
+
}
|
|
263
|
+
: {}),
|
|
245
264
|
},
|
|
246
265
|
],
|
|
247
266
|
})
|
|
@@ -271,7 +290,7 @@
|
|
|
271
290
|
|
|
272
291
|
{#if editableFilters?.groups?.length}
|
|
273
292
|
<div class="global-filter-header">
|
|
274
|
-
<span>
|
|
293
|
+
<span>{prefix}</span>
|
|
275
294
|
<span class="operator-picker">
|
|
276
295
|
<Select
|
|
277
296
|
value={editableFilters?.logicalOperator}
|
|
@@ -286,7 +305,7 @@
|
|
|
286
305
|
placeholder={false}
|
|
287
306
|
/>
|
|
288
307
|
</span>
|
|
289
|
-
<span>of the following
|
|
308
|
+
<span>of the following {builderType} groups:</span>
|
|
290
309
|
</div>
|
|
291
310
|
{/if}
|
|
292
311
|
{#if editableFilters?.groups?.length}
|
|
@@ -315,7 +334,7 @@
|
|
|
315
334
|
placeholder={false}
|
|
316
335
|
/>
|
|
317
336
|
</span>
|
|
318
|
-
<span>of the following
|
|
337
|
+
<span>of the following {builderType}s are matched:</span>
|
|
319
338
|
</div>
|
|
320
339
|
<div class="group-actions">
|
|
321
340
|
<Icon
|
|
@@ -346,20 +365,38 @@
|
|
|
346
365
|
<div class="filters">
|
|
347
366
|
{#each group.filters as filter, filterIdx}
|
|
348
367
|
<div class="filter">
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
{#if builderType === "filter"}
|
|
369
|
+
<Select
|
|
370
|
+
value={filter.field}
|
|
371
|
+
options={fieldOptions}
|
|
372
|
+
on:change={e => {
|
|
373
|
+
const updated = { ...filter, field: e.detail }
|
|
374
|
+
onFieldChange(updated)
|
|
375
|
+
onFilterFieldUpdate(updated, groupIdx, filterIdx)
|
|
376
|
+
}}
|
|
377
|
+
placeholder="Column"
|
|
378
|
+
/>
|
|
379
|
+
{:else}
|
|
380
|
+
<ConditionField
|
|
381
|
+
placeholder="Value"
|
|
382
|
+
{filter}
|
|
383
|
+
drawerTitle={"Edit Binding"}
|
|
384
|
+
{bindings}
|
|
385
|
+
{panel}
|
|
386
|
+
{toReadable}
|
|
387
|
+
{toRuntime}
|
|
388
|
+
on:change={e => {
|
|
389
|
+
const updated = {
|
|
390
|
+
...filter,
|
|
391
|
+
field: e.detail.field,
|
|
392
|
+
}
|
|
393
|
+
onFilterFieldUpdate(updated, groupIdx, filterIdx)
|
|
394
|
+
}}
|
|
395
|
+
/>
|
|
396
|
+
{/if}
|
|
360
397
|
<Select
|
|
361
398
|
value={filter.operator}
|
|
362
|
-
disabled={!filter.field}
|
|
399
|
+
disabled={!filter.field && builderType === "filter"}
|
|
363
400
|
options={getValidOperatorsForType(filter)}
|
|
364
401
|
on:change={e => {
|
|
365
402
|
const updated = { ...filter, operator: e.detail }
|
|
@@ -368,11 +405,18 @@
|
|
|
368
405
|
}}
|
|
369
406
|
placeholder={false}
|
|
370
407
|
/>
|
|
371
|
-
|
|
372
408
|
<FilterField
|
|
373
409
|
placeholder="Value"
|
|
410
|
+
drawerTitle={builderType === "condition"
|
|
411
|
+
? "Edit binding"
|
|
412
|
+
: null}
|
|
374
413
|
{allowBindings}
|
|
375
|
-
{
|
|
414
|
+
filter={{
|
|
415
|
+
...filter,
|
|
416
|
+
...(builderType === "condition"
|
|
417
|
+
? { type: FieldType.STRING }
|
|
418
|
+
: {}),
|
|
419
|
+
}}
|
|
376
420
|
{schemaFields}
|
|
377
421
|
{bindings}
|
|
378
422
|
{panel}
|
|
@@ -408,7 +452,7 @@
|
|
|
408
452
|
|
|
409
453
|
<div class="filters-footer">
|
|
410
454
|
<Layout noPadding>
|
|
411
|
-
{#if behaviourFilters && editableFilters?.groups?.length}
|
|
455
|
+
{#if behaviourFilters && allowOnEmpty && editableFilters?.groups?.length}
|
|
412
456
|
<div class="empty-filter">
|
|
413
457
|
<span>Return</span>
|
|
414
458
|
<span class="empty-filter-picker">
|
|
@@ -425,7 +469,7 @@
|
|
|
425
469
|
placeholder={false}
|
|
426
470
|
/>
|
|
427
471
|
</span>
|
|
428
|
-
<span>when all
|
|
472
|
+
<span>when all {builderType}s are empty</span>
|
|
429
473
|
</div>
|
|
430
474
|
{/if}
|
|
431
475
|
<div class="add-group">
|
|
@@ -439,17 +483,16 @@
|
|
|
439
483
|
})
|
|
440
484
|
}}
|
|
441
485
|
>
|
|
442
|
-
Add
|
|
486
|
+
Add {builderType} group
|
|
443
487
|
</Button>
|
|
444
|
-
|
|
445
|
-
href="
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
</a>
|
|
488
|
+
{#if docsURL}
|
|
489
|
+
<a href={docsURL} target="_blank">
|
|
490
|
+
<Icon
|
|
491
|
+
name="HelpOutline"
|
|
492
|
+
color="var(--spectrum-global-color-gray-600)"
|
|
493
|
+
/>
|
|
494
|
+
</a>
|
|
495
|
+
{/if}
|
|
453
496
|
</div>
|
|
454
497
|
</Layout>
|
|
455
498
|
</div>
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
export let allowBindings = false
|
|
22
22
|
export let schemaFields
|
|
23
23
|
export let panel
|
|
24
|
+
export let drawerTitle
|
|
24
25
|
export let toReadable
|
|
25
26
|
export let toRuntime
|
|
26
27
|
|
|
@@ -28,7 +29,6 @@
|
|
|
28
29
|
const { OperatorOptions, FilterValueType } = Constants
|
|
29
30
|
|
|
30
31
|
let bindingDrawer
|
|
31
|
-
let fieldValue
|
|
32
32
|
|
|
33
33
|
$: fieldValue = filter?.value
|
|
34
34
|
$: readableValue = toReadable ? toReadable(bindings, fieldValue) : fieldValue
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
on:drawerHide
|
|
134
134
|
on:drawerShow
|
|
135
135
|
bind:this={bindingDrawer}
|
|
136
|
-
title={filter.field}
|
|
136
|
+
title={drawerTitle || filter.field}
|
|
137
137
|
forceModal
|
|
138
138
|
>
|
|
139
139
|
<Button
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
{#if filter.valueType === FilterValueType.BINDING}
|
|
169
169
|
<Input
|
|
170
170
|
disabled={filter.noValue}
|
|
171
|
-
readonly={
|
|
171
|
+
readonly={isJS}
|
|
172
172
|
value={isJS ? "(JavaScript function)" : readableValue}
|
|
173
173
|
on:change={onChange}
|
|
174
174
|
/>
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
|
|
232
232
|
<div class="binding-control">
|
|
233
233
|
<!-- needs field, operator -->
|
|
234
|
-
{#if !disabled && allowBindings &&
|
|
234
|
+
{#if !disabled && allowBindings && !filter.noValue}
|
|
235
235
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
236
236
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
237
237
|
<div
|