@budibase/frontend-core 3.18.0 → 3.18.2
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 +2 -2
- package/src/components/grid/layout/ButtonColumn.svelte +52 -11
- package/src/components/grid/layout/GridRow.svelte +13 -0
- package/src/components/grid/stores/conditions.ts +109 -22
- package/src/components/grid/stores/datasource.ts +1 -1
- package/src/components/grid/stores/index.ts +14 -1
- package/src/fetch/DataFetch.ts +1 -1
- package/src/utils/settings.js +3 -0
- package/src/utils/utils.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.18.
|
|
3
|
+
"version": "3.18.2",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
"shortid": "2.2.15",
|
|
18
18
|
"socket.io-client": "^4.7.5"
|
|
19
19
|
},
|
|
20
|
-
"gitHead": "
|
|
20
|
+
"gitHead": "43d59f5579dd910575d8aebefb40fae25570c05d"
|
|
21
21
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
showHScrollbar,
|
|
22
22
|
dispatch,
|
|
23
23
|
config,
|
|
24
|
+
metadata,
|
|
24
25
|
} = getContext("grid")
|
|
25
26
|
|
|
26
27
|
let container
|
|
@@ -42,6 +43,29 @@
|
|
|
42
43
|
return gridButtons
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
// Apply button conditions and return filtered/modified buttons for a specific row
|
|
47
|
+
const getButtonsForRow = (buttons, row) => {
|
|
48
|
+
if (!buttons || !row) return buttons
|
|
49
|
+
|
|
50
|
+
const rowMetadata = $metadata?.[row._id]?.button || {}
|
|
51
|
+
|
|
52
|
+
return buttons
|
|
53
|
+
.map((button, index) => {
|
|
54
|
+
const buttonMetadata = rowMetadata[index]
|
|
55
|
+
if (!buttonMetadata) return button
|
|
56
|
+
|
|
57
|
+
// Skip hidden buttons
|
|
58
|
+
if (buttonMetadata.hidden) return null
|
|
59
|
+
|
|
60
|
+
// Apply any setting updates
|
|
61
|
+
return {
|
|
62
|
+
...button,
|
|
63
|
+
...buttonMetadata,
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.filter(button => button !== null)
|
|
67
|
+
}
|
|
68
|
+
|
|
45
69
|
const handleClick = async (button, row) => {
|
|
46
70
|
await button.onClick?.(rows.actions.cleanRow(row))
|
|
47
71
|
await rows.actions.refreshRow(row._id)
|
|
@@ -75,13 +99,14 @@
|
|
|
75
99
|
{@const rowSelected = !!$selectedRows[row._id]}
|
|
76
100
|
{@const rowHovered = $hoveredRowId === row._id}
|
|
77
101
|
{@const rowFocused = $focusedRow?._id === row._id}
|
|
102
|
+
{@const rowButtons = getButtonsForRow(buttons, row)}
|
|
78
103
|
<div
|
|
79
104
|
class="row"
|
|
80
105
|
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
|
|
81
106
|
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
|
|
82
107
|
>
|
|
83
108
|
<GridCell
|
|
84
|
-
width="
|
|
109
|
+
width="100%"
|
|
85
110
|
rowIdx={row.__idx}
|
|
86
111
|
selected={rowSelected}
|
|
87
112
|
highlighted={rowHovered || rowFocused}
|
|
@@ -92,17 +117,21 @@
|
|
|
92
117
|
class:offset={$showVScrollbar && $showHScrollbar}
|
|
93
118
|
>
|
|
94
119
|
{#if $props.buttonsCollapsed}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
120
|
+
{#if rowButtons.length > 0}
|
|
121
|
+
<CollapsedButtonGroup
|
|
122
|
+
buttons={makeCollapsedButtons(rowButtons, row)}
|
|
123
|
+
text={$props.buttonsCollapsedText || "Action"}
|
|
124
|
+
align="right"
|
|
125
|
+
offset={5}
|
|
126
|
+
size="S"
|
|
127
|
+
animate={false}
|
|
128
|
+
on:mouseenter={() => ($hoveredRowId = row._id)}
|
|
129
|
+
/>
|
|
130
|
+
{:else}
|
|
131
|
+
<div class="button-placeholder-collapsed" />
|
|
132
|
+
{/if}
|
|
104
133
|
{:else}
|
|
105
|
-
{#each
|
|
134
|
+
{#each rowButtons as button}
|
|
106
135
|
<Button
|
|
107
136
|
newStyles
|
|
108
137
|
size="S"
|
|
@@ -119,6 +148,9 @@
|
|
|
119
148
|
{button.text || "Button"}
|
|
120
149
|
</Button>
|
|
121
150
|
{/each}
|
|
151
|
+
{#if rowButtons.length === 0}
|
|
152
|
+
<div class="button-placeholder" />
|
|
153
|
+
{/if}
|
|
122
154
|
{/if}
|
|
123
155
|
</div>
|
|
124
156
|
</GridCell>
|
|
@@ -179,6 +211,15 @@
|
|
|
179
211
|
align-items: center;
|
|
180
212
|
gap: 4px;
|
|
181
213
|
}
|
|
214
|
+
.button-placeholder {
|
|
215
|
+
min-width: 60px;
|
|
216
|
+
height: 32px;
|
|
217
|
+
}
|
|
218
|
+
.button-placeholder-collapsed {
|
|
219
|
+
min-width: 70px;
|
|
220
|
+
height: 32px;
|
|
221
|
+
visibility: hidden;
|
|
222
|
+
}
|
|
182
223
|
.blank :global(.cell:hover) {
|
|
183
224
|
cursor: pointer;
|
|
184
225
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { getContext } from "svelte"
|
|
3
3
|
import DataCell from "../cells/DataCell.svelte"
|
|
4
|
+
import GridCell from "../cells/GridCell.svelte"
|
|
4
5
|
import { getCellID } from "../lib/utils"
|
|
5
6
|
|
|
6
7
|
export let row
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
isSelectingCells,
|
|
23
24
|
selectedCellMap,
|
|
24
25
|
selectedCellCount,
|
|
26
|
+
props,
|
|
27
|
+
buttonColumnWidth,
|
|
25
28
|
} = getContext("grid")
|
|
26
29
|
|
|
27
30
|
$: rowSelected = !!$selectedRows[row._id]
|
|
@@ -29,6 +32,8 @@
|
|
|
29
32
|
$hoveredRowId === row._id && (!$selectedCellCount || !$isSelectingCells)
|
|
30
33
|
$: rowFocused = $focusedRow?._id === row._id
|
|
31
34
|
$: reorderSource = $reorder.sourceColumn
|
|
35
|
+
$: hasButtons = $props?.buttons?.length > 0
|
|
36
|
+
$: needsButtonSpacer = hasButtons && $buttonColumnWidth > 0
|
|
32
37
|
</script>
|
|
33
38
|
|
|
34
39
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
@@ -60,6 +65,14 @@
|
|
|
60
65
|
isSelectingCells={$isSelectingCells}
|
|
61
66
|
/>
|
|
62
67
|
{/each}
|
|
68
|
+
{#if needsButtonSpacer}
|
|
69
|
+
<GridCell
|
|
70
|
+
width={$buttonColumnWidth}
|
|
71
|
+
selected={rowSelected}
|
|
72
|
+
highlighted={rowHovered || rowFocused}
|
|
73
|
+
rowIdx={row.__idx}
|
|
74
|
+
/>
|
|
75
|
+
{/if}
|
|
63
76
|
</div>
|
|
64
77
|
|
|
65
78
|
<style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writable, get, Writable, Readable } from "svelte/store"
|
|
1
|
+
import { writable, get, derived, Writable, Readable } from "svelte/store"
|
|
2
2
|
import { derivedMemo, QueryUtils } from "../../../utils"
|
|
3
3
|
import {
|
|
4
4
|
FieldType,
|
|
@@ -27,23 +27,42 @@ export const createStores = (): ConditionStore => {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export const deriveStores = (context: StoreContext): ConditionDerivedStore => {
|
|
30
|
-
const { columns } = context
|
|
30
|
+
const { columns, props } = context
|
|
31
31
|
|
|
32
32
|
// Derive and memoize the cell conditions present in our columns so that we
|
|
33
33
|
// only recompute condition metadata when absolutely necessary
|
|
34
|
-
const conditions = derivedMemo(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
const conditions = derivedMemo(
|
|
35
|
+
derived([columns, props], ([$columns, $props]) => {
|
|
36
|
+
let newConditions: UICondition[] = []
|
|
37
|
+
|
|
38
|
+
// Add column conditions
|
|
39
|
+
for (let column of $columns) {
|
|
40
|
+
for (let condition of column.conditions || []) {
|
|
41
|
+
newConditions.push({
|
|
42
|
+
...condition,
|
|
43
|
+
column: column.name,
|
|
44
|
+
type: column.schema.type,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
|
|
49
|
+
// Add button conditions
|
|
50
|
+
if ($props.buttons) {
|
|
51
|
+
for (let button of $props.buttons) {
|
|
52
|
+
for (let condition of button.conditions || []) {
|
|
53
|
+
newConditions.push({
|
|
54
|
+
...condition,
|
|
55
|
+
target: "button",
|
|
56
|
+
buttonIndex: $props.buttons.indexOf(button),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return newConditions
|
|
63
|
+
}),
|
|
64
|
+
conditions => conditions
|
|
65
|
+
)
|
|
47
66
|
|
|
48
67
|
return {
|
|
49
68
|
conditions,
|
|
@@ -58,7 +77,7 @@ export const initialise = (context: StoreContext) => {
|
|
|
58
77
|
let newMetadata: Record<string, any> = {}
|
|
59
78
|
if ($conditions?.length) {
|
|
60
79
|
for (let row of get(rows)) {
|
|
61
|
-
newMetadata[row._id] = evaluateConditions(row, $conditions)
|
|
80
|
+
newMetadata[row._id] = evaluateConditions(row, $conditions, context)
|
|
62
81
|
}
|
|
63
82
|
}
|
|
64
83
|
metadata.set(newMetadata)
|
|
@@ -86,7 +105,7 @@ export const initialise = (context: StoreContext) => {
|
|
|
86
105
|
continue
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
$metadata[row._id] = evaluateConditions(row, $conditions)
|
|
108
|
+
$metadata[row._id] = evaluateConditions(row, $conditions, context)
|
|
90
109
|
}
|
|
91
110
|
if (Object.keys(metadataUpdates).length) {
|
|
92
111
|
metadata.update(state => ({
|
|
@@ -118,17 +137,62 @@ const TypeCoercionMap: Partial<Record<FieldType, (val: string) => any>> = {
|
|
|
118
137
|
|
|
119
138
|
// Evaluates an array of cell conditions against a certain row and returns the
|
|
120
139
|
// resultant metadata
|
|
121
|
-
const evaluateConditions = (
|
|
140
|
+
const evaluateConditions = (
|
|
141
|
+
row: UIRow,
|
|
142
|
+
conditions: UICondition[],
|
|
143
|
+
context: StoreContext
|
|
144
|
+
) => {
|
|
122
145
|
const metadata: {
|
|
123
146
|
version?: string
|
|
124
147
|
row: Record<string, string>
|
|
125
148
|
cell: Record<string, any>
|
|
149
|
+
button: Record<string, any>
|
|
126
150
|
} = {
|
|
127
151
|
version: row._rev,
|
|
128
152
|
row: {},
|
|
129
153
|
cell: {},
|
|
154
|
+
button: {},
|
|
130
155
|
}
|
|
131
|
-
|
|
156
|
+
|
|
157
|
+
// Get dynamic button conditions
|
|
158
|
+
const { props } = context
|
|
159
|
+
const $props = get(props)
|
|
160
|
+
let allConditions = [...conditions]
|
|
161
|
+
|
|
162
|
+
// Add dynamic button conditions from getRowConditions
|
|
163
|
+
if ($props.buttons) {
|
|
164
|
+
for (let button of $props.buttons) {
|
|
165
|
+
if (button.getRowConditions) {
|
|
166
|
+
const dynamicConditions = button.getRowConditions(row) || []
|
|
167
|
+
for (let condition of dynamicConditions) {
|
|
168
|
+
allConditions.push({
|
|
169
|
+
...condition,
|
|
170
|
+
target: "button",
|
|
171
|
+
buttonIndex: $props.buttons.indexOf(button),
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Pre-process button conditions to set default visibility for show conditions
|
|
179
|
+
const buttonShowConditions = new Set()
|
|
180
|
+
for (let condition of allConditions) {
|
|
181
|
+
if (
|
|
182
|
+
condition.target === "button" &&
|
|
183
|
+
condition.action === "show" &&
|
|
184
|
+
typeof condition.buttonIndex === "number"
|
|
185
|
+
) {
|
|
186
|
+
buttonShowConditions.add(condition.buttonIndex)
|
|
187
|
+
// Initialize button metadata and set as hidden by default for show conditions
|
|
188
|
+
if (!metadata.button[condition.buttonIndex]) {
|
|
189
|
+
metadata.button[condition.buttonIndex] = {}
|
|
190
|
+
}
|
|
191
|
+
metadata.button[condition.buttonIndex].hidden = true
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (let condition of allConditions) {
|
|
132
196
|
try {
|
|
133
197
|
let {
|
|
134
198
|
column,
|
|
@@ -138,8 +202,19 @@ const evaluateConditions = (row: UIRow, conditions: UICondition[]) => {
|
|
|
138
202
|
metadataKey,
|
|
139
203
|
metadataValue,
|
|
140
204
|
target,
|
|
205
|
+
buttonIndex,
|
|
206
|
+
newValue,
|
|
207
|
+
action,
|
|
208
|
+
setting,
|
|
209
|
+
settingValue,
|
|
141
210
|
} = condition
|
|
142
|
-
|
|
211
|
+
|
|
212
|
+
let value
|
|
213
|
+
if (target === "button") {
|
|
214
|
+
value = newValue
|
|
215
|
+
} else {
|
|
216
|
+
value = row[column]
|
|
217
|
+
}
|
|
143
218
|
|
|
144
219
|
// Coerce values into correct types for primitives
|
|
145
220
|
let coercedType = type
|
|
@@ -154,8 +229,8 @@ const evaluateConditions = (row: UIRow, conditions: UICondition[]) => {
|
|
|
154
229
|
}
|
|
155
230
|
const coerce = TypeCoercionMap[coercedType]
|
|
156
231
|
if (coerce) {
|
|
157
|
-
value = coerce(value)
|
|
158
|
-
referenceValue = coerce(referenceValue)
|
|
232
|
+
value = coerce(value as string)
|
|
233
|
+
referenceValue = coerce(referenceValue as string)
|
|
159
234
|
}
|
|
160
235
|
|
|
161
236
|
// Build lucene compatible condition expression
|
|
@@ -169,7 +244,19 @@ const evaluateConditions = (row: UIRow, conditions: UICondition[]) => {
|
|
|
169
244
|
query.onEmptyFilter = EmptyFilterOption.RETURN_NONE
|
|
170
245
|
const result = QueryUtils.runQuery([{ value }], query)
|
|
171
246
|
if (result.length > 0) {
|
|
172
|
-
if (target === "
|
|
247
|
+
if (target === "button" && typeof buttonIndex === "number") {
|
|
248
|
+
if (!metadata.button[buttonIndex]) {
|
|
249
|
+
metadata.button[buttonIndex] = {}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (action === "hide") {
|
|
253
|
+
metadata.button[buttonIndex].hidden = true
|
|
254
|
+
} else if (action === "show") {
|
|
255
|
+
metadata.button[buttonIndex].hidden = false
|
|
256
|
+
} else if (action === "update" && setting) {
|
|
257
|
+
metadata.button[buttonIndex][setting] = settingValue
|
|
258
|
+
}
|
|
259
|
+
} else if (target === "row") {
|
|
173
260
|
metadata.row = {
|
|
174
261
|
...metadata.row,
|
|
175
262
|
[metadataKey]: metadataValue,
|
|
@@ -24,7 +24,12 @@ import * as ViewV2 from "./datasources/viewV2"
|
|
|
24
24
|
import * as NonPlus from "./datasources/nonPlus"
|
|
25
25
|
import * as Cache from "./cache"
|
|
26
26
|
import * as Conditions from "./conditions"
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
SortOrder,
|
|
29
|
+
UICondition,
|
|
30
|
+
UIDatasource,
|
|
31
|
+
UISearchFilter,
|
|
32
|
+
} from "@budibase/types"
|
|
28
33
|
import * as Constants from "../lib/constants"
|
|
29
34
|
import * as GridClipboard from "../../../stores/gridClipboard"
|
|
30
35
|
import { ExternalClipboardData } from "../../../stores/gridClipboard"
|
|
@@ -79,6 +84,14 @@ export interface BaseStoreProps {
|
|
|
79
84
|
minHeight?: number
|
|
80
85
|
canHideColumns?: boolean
|
|
81
86
|
externalClipboard?: ExternalClipboardData
|
|
87
|
+
buttons?: {
|
|
88
|
+
text: string
|
|
89
|
+
onClick: unknown
|
|
90
|
+
conditions?: UICondition[]
|
|
91
|
+
getRowConditions?: (row: any) => UICondition[]
|
|
92
|
+
}[]
|
|
93
|
+
buttonsCollapsed?: boolean
|
|
94
|
+
buttonsCollapsedText?: string
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
export interface BaseStore {
|
package/src/fetch/DataFetch.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writable, derived, get, Writable, Readable } from "svelte/store"
|
|
2
|
-
import
|
|
2
|
+
import cloneDeep from "lodash/fp/cloneDeep"
|
|
3
3
|
import { QueryUtils } from "../utils"
|
|
4
4
|
import { convertJSONSchemaToTableSchema } from "../utils/json"
|
|
5
5
|
import {
|
package/src/utils/settings.js
CHANGED
|
@@ -3,6 +3,9 @@ import { helpers } from "@budibase/shared-core"
|
|
|
3
3
|
// Util to check if a setting can be rendered for a certain instance, based on
|
|
4
4
|
// the "dependsOn" metadata in the manifest
|
|
5
5
|
export const shouldDisplaySetting = (instance, setting) => {
|
|
6
|
+
if (setting.nestedOnly && !setting.nested) {
|
|
7
|
+
return false
|
|
8
|
+
}
|
|
6
9
|
let dependsOn = setting.dependsOn
|
|
7
10
|
if (dependsOn && !Array.isArray(dependsOn)) {
|
|
8
11
|
dependsOn = [dependsOn]
|
package/src/utils/utils.ts
CHANGED