@budibase/frontend-core 3.0.4 → 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.4",
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.4",
10
- "@budibase/shared-core": "3.0.4",
11
- "@budibase/types": "3.0.4",
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": "2d42ac82463b0c0aa59d07f2aeac554543764d3d"
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>Show data which matches</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 filter groups:</span>
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 filters are matched:</span>
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
- <Select
350
- value={filter.field}
351
- options={fieldOptions}
352
- on:change={e => {
353
- const updated = { ...filter, field: e.detail }
354
- onFieldChange(updated)
355
- onFilterFieldUpdate(updated, groupIdx, filterIdx)
356
- }}
357
- placeholder="Column"
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
- {filter}
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 filters are empty</span>
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 filter group
486
+ Add {builderType} group
443
487
  </Button>
444
- <a
445
- href="https://docs.budibase.com/docs/searchfilter-data"
446
- target="_blank"
447
- >
448
- <Icon
449
- name="HelpOutline"
450
- color="var(--spectrum-global-color-gray-600)"
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={true}
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 && filter.field && !filter.noValue}
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