@budibase/frontend-core 2.9.38 → 2.9.39-alpha.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 +4 -4
- package/src/api/groups.js +22 -0
- package/src/api/index.js +2 -0
- package/src/api/permissions.js +23 -0
- package/src/api/rows.js +22 -3
- package/src/api/user.js +27 -4
- package/src/api/viewsV2.js +72 -0
- package/src/components/grid/cells/DataCell.svelte +8 -3
- package/src/components/grid/cells/GutterCell.svelte +4 -4
- package/src/components/grid/cells/HeaderCell.svelte +3 -3
- package/src/components/grid/controls/HideColumnsButton.svelte +10 -7
- package/src/components/grid/controls/SizeButton.svelte +10 -4
- package/src/components/grid/controls/SortButton.svelte +12 -34
- package/src/components/grid/layout/Grid.svelte +22 -17
- package/src/components/grid/layout/GridBody.svelte +2 -2
- package/src/components/grid/layout/HeaderRow.svelte +3 -4
- package/src/components/grid/layout/NewColumnButton.svelte +3 -2
- package/src/components/grid/layout/NewRow.svelte +6 -6
- package/src/components/grid/layout/StickyColumn.svelte +2 -2
- package/src/components/grid/lib/websocket.js +18 -12
- package/src/components/grid/overlays/KeyboardManager.svelte +12 -11
- package/src/components/grid/overlays/MenuOverlay.svelte +3 -6
- package/src/components/grid/stores/clipboard.js +1 -1
- package/src/components/grid/stores/columns.js +39 -99
- package/src/components/grid/stores/config.js +33 -6
- package/src/components/grid/stores/datasource.js +131 -0
- package/src/components/grid/stores/filter.js +2 -2
- package/src/components/grid/stores/index.js +14 -3
- package/src/components/grid/stores/menu.js +1 -1
- package/src/components/grid/stores/pagination.js +3 -4
- package/src/components/grid/stores/reorder.js +1 -1
- package/src/components/grid/stores/resize.js +1 -1
- package/src/components/grid/stores/rows.js +61 -101
- package/src/components/grid/stores/sort.js +29 -6
- package/src/components/grid/stores/table.js +129 -0
- package/src/components/grid/stores/ui.js +24 -36
- package/src/components/grid/stores/users.js +8 -1
- package/src/components/grid/stores/viewV2.js +212 -0
- package/src/components/grid/stores/viewport.js +4 -4
- package/src/constants.js +21 -13
- package/src/fetch/DataFetch.js +35 -24
- package/src/fetch/QueryFetch.js +4 -0
- package/src/fetch/ViewV2Fetch.js +65 -0
- package/src/fetch/fetchData.js +2 -0
- package/src/utils/roles.js +5 -3
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
let anchor
|
|
9
9
|
let open = false
|
|
10
|
+
|
|
10
11
|
$: columnsWidth = $renderedColumns.reduce(
|
|
11
12
|
(total, col) => (total += col.width),
|
|
12
13
|
0
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
const close = () => {
|
|
18
19
|
open = false
|
|
19
20
|
}
|
|
21
|
+
|
|
20
22
|
onMount(() => subscribe("close-edit-column", close))
|
|
21
23
|
</script>
|
|
22
24
|
|
|
@@ -32,10 +34,9 @@
|
|
|
32
34
|
<Popover
|
|
33
35
|
bind:open
|
|
34
36
|
{anchor}
|
|
35
|
-
align="right"
|
|
37
|
+
align={$renderedColumns.length ? "right" : "left"}
|
|
36
38
|
offset={0}
|
|
37
39
|
popoverTarget={document.getElementById(`add-column-button`)}
|
|
38
|
-
animate={false}
|
|
39
40
|
customZindex={100}
|
|
40
41
|
>
|
|
41
42
|
<div
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
dispatch,
|
|
18
18
|
rows,
|
|
19
19
|
focusedCellAPI,
|
|
20
|
-
|
|
20
|
+
datasource,
|
|
21
21
|
subscribe,
|
|
22
22
|
renderedRows,
|
|
23
23
|
renderedColumns,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
columnHorizontalInversionIndex,
|
|
29
29
|
selectedRows,
|
|
30
30
|
loading,
|
|
31
|
-
|
|
31
|
+
config,
|
|
32
32
|
} = getContext("grid")
|
|
33
33
|
|
|
34
34
|
let visible = false
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
$: firstColumn = $stickyColumn || $renderedColumns[0]
|
|
40
40
|
$: width = GutterWidth + ($stickyColumn?.width || 0)
|
|
41
|
-
$: $
|
|
41
|
+
$: $datasource, (visible = false)
|
|
42
42
|
$: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
|
|
43
43
|
$: selectedRowCount = Object.values($selectedRows).length
|
|
44
44
|
$: hasNoRows = !$rows.length
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
document.addEventListener("keydown", handleKeyPress)
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const updateValue = (
|
|
124
|
-
newRow[
|
|
123
|
+
const updateValue = ({ column, value }) => {
|
|
124
|
+
newRow[column] = value
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
const addViaModal = () => {
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
condition={hasNoRows && !$loading}
|
|
155
155
|
type={TooltipType.Info}
|
|
156
156
|
>
|
|
157
|
-
{#if !visible && !selectedRowCount && $canAddRows}
|
|
157
|
+
{#if !visible && !selectedRowCount && $config.canAddRows}
|
|
158
158
|
<div
|
|
159
159
|
class="new-row-fab"
|
|
160
160
|
on:click={() => dispatch("add-row-inline")}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
renderedRows,
|
|
17
17
|
focusedCellId,
|
|
18
18
|
hoveredRowId,
|
|
19
|
-
|
|
19
|
+
config,
|
|
20
20
|
selectedCellMap,
|
|
21
21
|
focusedRow,
|
|
22
22
|
scrollLeft,
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
{/if}
|
|
95
95
|
</div>
|
|
96
96
|
{/each}
|
|
97
|
-
{#if $canAddRows}
|
|
97
|
+
{#if $config.canAddRows}
|
|
98
98
|
<div
|
|
99
99
|
class="row new"
|
|
100
100
|
on:mouseenter={$isDragging
|
|
@@ -3,18 +3,21 @@ import { createWebsocket } from "../../../utils"
|
|
|
3
3
|
import { SocketEvent, GridSocketEvent } from "@budibase/shared-core"
|
|
4
4
|
|
|
5
5
|
export const createGridWebsocket = context => {
|
|
6
|
-
const { rows,
|
|
6
|
+
const { rows, datasource, users, focusedCellId, definition, API } = context
|
|
7
7
|
const socket = createWebsocket("/socket/grid")
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const connectToDatasource = datasource => {
|
|
10
10
|
if (!socket.connected) {
|
|
11
11
|
return
|
|
12
12
|
}
|
|
13
13
|
// Identify which table we are editing
|
|
14
14
|
const appId = API.getAppID()
|
|
15
15
|
socket.emit(
|
|
16
|
-
GridSocketEvent.
|
|
17
|
-
{
|
|
16
|
+
GridSocketEvent.SelectDatasource,
|
|
17
|
+
{
|
|
18
|
+
datasource,
|
|
19
|
+
appId,
|
|
20
|
+
},
|
|
18
21
|
({ users: gridUsers }) => {
|
|
19
22
|
users.set(gridUsers)
|
|
20
23
|
}
|
|
@@ -23,7 +26,7 @@ export const createGridWebsocket = context => {
|
|
|
23
26
|
|
|
24
27
|
// Built-in events
|
|
25
28
|
socket.on("connect", () => {
|
|
26
|
-
|
|
29
|
+
connectToDatasource(get(datasource))
|
|
27
30
|
})
|
|
28
31
|
socket.on("connect_error", err => {
|
|
29
32
|
console.log("Failed to connect to grid websocket:", err.message)
|
|
@@ -48,16 +51,19 @@ export const createGridWebsocket = context => {
|
|
|
48
51
|
})
|
|
49
52
|
|
|
50
53
|
// Table events
|
|
51
|
-
socket.onOther(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
socket.onOther(
|
|
55
|
+
GridSocketEvent.DatasourceChange,
|
|
56
|
+
({ datasource: newDatasource }) => {
|
|
57
|
+
// Only update definition if one exists. If the datasource was deleted
|
|
58
|
+
// then we don't want to know - let the builder navigate away
|
|
59
|
+
if (newDatasource) {
|
|
60
|
+
definition.set(newDatasource)
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
|
-
|
|
63
|
+
)
|
|
58
64
|
|
|
59
65
|
// Change websocket connection when table changes
|
|
60
|
-
|
|
66
|
+
datasource.subscribe(connectToDatasource)
|
|
61
67
|
|
|
62
68
|
// Notify selected cell changes
|
|
63
69
|
focusedCellId.subscribe($focusedCellId => {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { NewRowID } from "../lib/constants"
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
|
-
|
|
7
|
+
rows,
|
|
8
8
|
focusedCellId,
|
|
9
9
|
visibleColumns,
|
|
10
10
|
focusedRow,
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
config,
|
|
17
17
|
menu,
|
|
18
18
|
gridFocused,
|
|
19
|
-
canAddRows,
|
|
20
19
|
} = getContext("grid")
|
|
21
20
|
|
|
22
21
|
const ignoredOriginSelectors = [
|
|
@@ -46,12 +45,12 @@
|
|
|
46
45
|
e.preventDefault()
|
|
47
46
|
focusFirstCell()
|
|
48
47
|
} else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
|
49
|
-
if ($canAddRows) {
|
|
48
|
+
if ($config.canAddRows) {
|
|
50
49
|
e.preventDefault()
|
|
51
50
|
dispatch("add-row-inline")
|
|
52
51
|
}
|
|
53
52
|
} else if (e.key === "Delete" || e.key === "Backspace") {
|
|
54
|
-
if (Object.keys($selectedRows).length && $config.
|
|
53
|
+
if (Object.keys($selectedRows).length && $config.canDeleteRows) {
|
|
55
54
|
dispatch("request-bulk-delete")
|
|
56
55
|
}
|
|
57
56
|
}
|
|
@@ -100,7 +99,7 @@
|
|
|
100
99
|
}
|
|
101
100
|
break
|
|
102
101
|
case "Enter":
|
|
103
|
-
if ($canAddRows) {
|
|
102
|
+
if ($config.canAddRows) {
|
|
104
103
|
dispatch("add-row-inline")
|
|
105
104
|
}
|
|
106
105
|
}
|
|
@@ -120,7 +119,7 @@
|
|
|
120
119
|
break
|
|
121
120
|
case "Delete":
|
|
122
121
|
case "Backspace":
|
|
123
|
-
if (Object.keys($selectedRows).length && $config.
|
|
122
|
+
if (Object.keys($selectedRows).length && $config.canDeleteRows) {
|
|
124
123
|
dispatch("request-bulk-delete")
|
|
125
124
|
} else {
|
|
126
125
|
deleteSelectedCell()
|
|
@@ -131,7 +130,7 @@
|
|
|
131
130
|
break
|
|
132
131
|
case " ":
|
|
133
132
|
case "Space":
|
|
134
|
-
if ($config.
|
|
133
|
+
if ($config.canDeleteRows) {
|
|
135
134
|
toggleSelectRow()
|
|
136
135
|
}
|
|
137
136
|
break
|
|
@@ -143,7 +142,7 @@
|
|
|
143
142
|
|
|
144
143
|
// Focuses the first cell in the grid
|
|
145
144
|
const focusFirstCell = () => {
|
|
146
|
-
const firstRow = $
|
|
145
|
+
const firstRow = $rows[0]
|
|
147
146
|
if (!firstRow) {
|
|
148
147
|
return
|
|
149
148
|
}
|
|
@@ -184,7 +183,7 @@
|
|
|
184
183
|
if (!$focusedRow) {
|
|
185
184
|
return
|
|
186
185
|
}
|
|
187
|
-
const newRow = $
|
|
186
|
+
const newRow = $rows[$focusedRow.__idx + delta]
|
|
188
187
|
if (newRow) {
|
|
189
188
|
const split = $focusedCellId.split("-")
|
|
190
189
|
$focusedCellId = `${newRow._id}-${split[1]}`
|
|
@@ -216,13 +215,15 @@
|
|
|
216
215
|
if ($focusedCellAPI && !$focusedCellAPI.isReadonly()) {
|
|
217
216
|
const type = $focusedCellAPI.getType()
|
|
218
217
|
if (type === "number" && keyCodeIsNumber(keyCode)) {
|
|
219
|
-
|
|
218
|
+
// Update the value locally but don't save it yet
|
|
219
|
+
$focusedCellAPI.setValue(parseInt(key), { save: false })
|
|
220
220
|
$focusedCellAPI.focus()
|
|
221
221
|
} else if (
|
|
222
222
|
["string", "barcodeqr", "longform"].includes(type) &&
|
|
223
223
|
(keyCodeIsLetter(keyCode) || keyCodeIsNumber(keyCode))
|
|
224
224
|
) {
|
|
225
|
-
|
|
225
|
+
// Update the value locally but don't save it yet
|
|
226
|
+
$focusedCellAPI.setValue(key, { save: false })
|
|
226
227
|
$focusedCellAPI.focus()
|
|
227
228
|
}
|
|
228
229
|
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
focusedCellAPI,
|
|
18
18
|
focusedRowId,
|
|
19
19
|
notifications,
|
|
20
|
-
canAddRows,
|
|
21
20
|
} = getContext("grid")
|
|
22
21
|
|
|
23
22
|
$: style = makeStyle($menu)
|
|
@@ -68,9 +67,7 @@
|
|
|
68
67
|
</MenuItem>
|
|
69
68
|
<MenuItem
|
|
70
69
|
icon="Maximize"
|
|
71
|
-
disabled={isNewRow ||
|
|
72
|
-
!$config.allowEditRows ||
|
|
73
|
-
!$config.allowExpandRows}
|
|
70
|
+
disabled={isNewRow || !$config.canEditRows || !$config.canExpandRows}
|
|
74
71
|
on:click={() => dispatch("edit-row", $focusedRow)}
|
|
75
72
|
on:click={menu.actions.close}
|
|
76
73
|
>
|
|
@@ -94,14 +91,14 @@
|
|
|
94
91
|
</MenuItem>
|
|
95
92
|
<MenuItem
|
|
96
93
|
icon="Duplicate"
|
|
97
|
-
disabled={isNewRow || !$canAddRows}
|
|
94
|
+
disabled={isNewRow || !$config.canAddRows}
|
|
98
95
|
on:click={duplicate}
|
|
99
96
|
>
|
|
100
97
|
Duplicate row
|
|
101
98
|
</MenuItem>
|
|
102
99
|
<MenuItem
|
|
103
100
|
icon="Delete"
|
|
104
|
-
disabled={isNewRow || !$config.
|
|
101
|
+
disabled={isNewRow || !$config.canDeleteRows}
|
|
105
102
|
on:click={deleteRow}
|
|
106
103
|
>
|
|
107
104
|
Delete row
|
|
@@ -35,20 +35,10 @@ export const createStores = () => {
|
|
|
35
35
|
[]
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
// Checks if we have a certain column by name
|
|
39
|
-
const hasColumn = column => {
|
|
40
|
-
const $columns = get(columns)
|
|
41
|
-
const $sticky = get(stickyColumn)
|
|
42
|
-
return $columns.some(col => col.name === column) || $sticky?.name === column
|
|
43
|
-
}
|
|
44
|
-
|
|
45
38
|
return {
|
|
46
39
|
columns: {
|
|
47
40
|
...columns,
|
|
48
41
|
subscribe: enrichedColumns.subscribe,
|
|
49
|
-
actions: {
|
|
50
|
-
hasColumn,
|
|
51
|
-
},
|
|
52
42
|
},
|
|
53
43
|
stickyColumn,
|
|
54
44
|
visibleColumns,
|
|
@@ -56,12 +46,35 @@ export const createStores = () => {
|
|
|
56
46
|
}
|
|
57
47
|
|
|
58
48
|
export const deriveStores = context => {
|
|
59
|
-
const {
|
|
49
|
+
const { columns, stickyColumn } = context
|
|
50
|
+
|
|
51
|
+
// Derive if we have any normal columns
|
|
52
|
+
const hasNonAutoColumn = derived(
|
|
53
|
+
[columns, stickyColumn],
|
|
54
|
+
([$columns, $stickyColumn]) => {
|
|
55
|
+
let allCols = $columns || []
|
|
56
|
+
if ($stickyColumn) {
|
|
57
|
+
allCols = [...allCols, $stickyColumn]
|
|
58
|
+
}
|
|
59
|
+
const normalCols = allCols.filter(column => {
|
|
60
|
+
return !column.schema?.autocolumn
|
|
61
|
+
})
|
|
62
|
+
return normalCols.length > 0
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
hasNonAutoColumn,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const createActions = context => {
|
|
72
|
+
const { columns, stickyColumn, datasource, definition } = context
|
|
60
73
|
|
|
61
|
-
// Updates the
|
|
74
|
+
// Updates the datasources primary display column
|
|
62
75
|
const changePrimaryDisplay = async column => {
|
|
63
|
-
return await
|
|
64
|
-
...get(
|
|
76
|
+
return await datasource.actions.saveDefinition({
|
|
77
|
+
...get(definition),
|
|
65
78
|
primaryDisplay: column,
|
|
66
79
|
})
|
|
67
80
|
}
|
|
@@ -83,29 +96,14 @@ export const deriveStores = context => {
|
|
|
83
96
|
await saveChanges()
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
//
|
|
87
|
-
const hasNonAutoColumn = derived(
|
|
88
|
-
[columns, stickyColumn],
|
|
89
|
-
([$columns, $stickyColumn]) => {
|
|
90
|
-
let allCols = $columns || []
|
|
91
|
-
if ($stickyColumn) {
|
|
92
|
-
allCols = [...allCols, $stickyColumn]
|
|
93
|
-
}
|
|
94
|
-
const normalCols = allCols.filter(column => {
|
|
95
|
-
return !column.schema?.autocolumn
|
|
96
|
-
})
|
|
97
|
-
return normalCols.length > 0
|
|
98
|
-
}
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
// Persists column changes by saving metadata against table schema
|
|
99
|
+
// Persists column changes by saving metadata against datasource schema
|
|
102
100
|
const saveChanges = async () => {
|
|
103
101
|
const $columns = get(columns)
|
|
104
|
-
const $
|
|
102
|
+
const $definition = get(definition)
|
|
105
103
|
const $stickyColumn = get(stickyColumn)
|
|
106
|
-
const newSchema = cloneDeep($
|
|
104
|
+
const newSchema = cloneDeep($definition.schema)
|
|
107
105
|
|
|
108
|
-
// Build new updated
|
|
106
|
+
// Build new updated datasource schema
|
|
109
107
|
Object.keys(newSchema).forEach(column => {
|
|
110
108
|
// Respect order specified by columns
|
|
111
109
|
const index = $columns.findIndex(x => x.name === column)
|
|
@@ -125,31 +123,17 @@ export const deriveStores = context => {
|
|
|
125
123
|
}
|
|
126
124
|
})
|
|
127
125
|
|
|
128
|
-
await
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Update local state
|
|
133
|
-
table.set(newTable)
|
|
134
|
-
|
|
135
|
-
// Update server
|
|
136
|
-
if (get(config).allowSchemaChanges) {
|
|
137
|
-
await API.saveTable(newTable)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Broadcast change to external state can be updated, as this change
|
|
141
|
-
// will not be received by the builder websocket because we caused it ourselves
|
|
142
|
-
dispatch("updatetable", newTable)
|
|
126
|
+
await datasource.actions.saveDefinition({
|
|
127
|
+
...$definition,
|
|
128
|
+
schema: newSchema,
|
|
129
|
+
})
|
|
143
130
|
}
|
|
144
131
|
|
|
145
132
|
return {
|
|
146
|
-
hasNonAutoColumn,
|
|
147
133
|
columns: {
|
|
148
134
|
...columns,
|
|
149
135
|
actions: {
|
|
150
|
-
...columns.actions,
|
|
151
136
|
saveChanges,
|
|
152
|
-
saveTable,
|
|
153
137
|
changePrimaryDisplay,
|
|
154
138
|
changeAllColumnWidths,
|
|
155
139
|
},
|
|
@@ -158,51 +142,7 @@ export const deriveStores = context => {
|
|
|
158
142
|
}
|
|
159
143
|
|
|
160
144
|
export const initialise = context => {
|
|
161
|
-
const {
|
|
162
|
-
context
|
|
163
|
-
|
|
164
|
-
const schema = derived(
|
|
165
|
-
[table, schemaOverrides, columnWhitelist],
|
|
166
|
-
([$table, $schemaOverrides, $columnWhitelist]) => {
|
|
167
|
-
if (!$table?.schema) {
|
|
168
|
-
return null
|
|
169
|
-
}
|
|
170
|
-
let newSchema = { ...$table?.schema }
|
|
171
|
-
|
|
172
|
-
// Edge case to temporarily allow deletion of duplicated user
|
|
173
|
-
// fields that were saved with the "disabled" flag set.
|
|
174
|
-
// By overriding the saved schema we ensure only overrides can
|
|
175
|
-
// set the disabled flag.
|
|
176
|
-
// TODO: remove in future
|
|
177
|
-
Object.keys(newSchema).forEach(field => {
|
|
178
|
-
newSchema[field] = {
|
|
179
|
-
...newSchema[field],
|
|
180
|
-
disabled: false,
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
// Apply schema overrides
|
|
185
|
-
Object.keys($schemaOverrides || {}).forEach(field => {
|
|
186
|
-
if (newSchema[field]) {
|
|
187
|
-
newSchema[field] = {
|
|
188
|
-
...newSchema[field],
|
|
189
|
-
...$schemaOverrides[field],
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
// Apply whitelist if specified
|
|
195
|
-
if ($columnWhitelist?.length) {
|
|
196
|
-
Object.keys(newSchema).forEach(key => {
|
|
197
|
-
if (!$columnWhitelist.includes(key)) {
|
|
198
|
-
delete newSchema[key]
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return newSchema
|
|
204
|
-
}
|
|
205
|
-
)
|
|
145
|
+
const { definition, columns, stickyColumn, schema } = context
|
|
206
146
|
|
|
207
147
|
// Merge new schema fields with existing schema in order to preserve widths
|
|
208
148
|
schema.subscribe($schema => {
|
|
@@ -211,12 +151,12 @@ export const initialise = context => {
|
|
|
211
151
|
stickyColumn.set(null)
|
|
212
152
|
return
|
|
213
153
|
}
|
|
214
|
-
const $
|
|
154
|
+
const $definition = get(definition)
|
|
215
155
|
|
|
216
156
|
// Find primary display
|
|
217
157
|
let primaryDisplay
|
|
218
|
-
if ($
|
|
219
|
-
primaryDisplay = $
|
|
158
|
+
if ($definition.primaryDisplay && $schema[$definition.primaryDisplay]) {
|
|
159
|
+
primaryDisplay = $definition.primaryDisplay
|
|
220
160
|
}
|
|
221
161
|
|
|
222
162
|
// Get field list
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { writable } from "svelte/store"
|
|
2
1
|
import { derivedMemo } from "../../../utils"
|
|
2
|
+
import { derived } from "svelte/store"
|
|
3
3
|
|
|
4
4
|
export const createStores = context => {
|
|
5
|
-
const
|
|
6
|
-
const getProp = prop => derivedMemo(
|
|
5
|
+
const { props } = context
|
|
6
|
+
const getProp = prop => derivedMemo(props, $props => $props[prop])
|
|
7
7
|
|
|
8
8
|
// Derive and memoize some props so that we can react to them in isolation
|
|
9
|
-
const
|
|
9
|
+
const datasource = getProp("datasource")
|
|
10
10
|
const initialSortColumn = getProp("initialSortColumn")
|
|
11
11
|
const initialSortOrder = getProp("initialSortOrder")
|
|
12
12
|
const initialFilter = getProp("initialFilter")
|
|
@@ -17,8 +17,7 @@ export const createStores = context => {
|
|
|
17
17
|
const notifyError = getProp("notifyError")
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
|
-
|
|
21
|
-
tableId,
|
|
20
|
+
datasource,
|
|
22
21
|
initialSortColumn,
|
|
23
22
|
initialSortOrder,
|
|
24
23
|
initialFilter,
|
|
@@ -29,3 +28,31 @@ export const createStores = context => {
|
|
|
29
28
|
notifyError,
|
|
30
29
|
}
|
|
31
30
|
}
|
|
31
|
+
|
|
32
|
+
export const deriveStores = context => {
|
|
33
|
+
const { props, hasNonAutoColumn } = context
|
|
34
|
+
|
|
35
|
+
// Derive features
|
|
36
|
+
const config = derived(
|
|
37
|
+
[props, hasNonAutoColumn],
|
|
38
|
+
([$props, $hasNonAutoColumn]) => {
|
|
39
|
+
let config = { ...$props }
|
|
40
|
+
|
|
41
|
+
// Disable some features if we're editing a view
|
|
42
|
+
if ($props.datasource?.type === "viewV2") {
|
|
43
|
+
config.canEditColumns = false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Disable adding rows if we don't have any valid columns
|
|
47
|
+
if (!$hasNonAutoColumn) {
|
|
48
|
+
config.canAddRows = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return config
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
config,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { derived, get, writable } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const createStores = () => {
|
|
4
|
+
const definition = writable(null)
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
definition,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const deriveStores = context => {
|
|
12
|
+
const { definition, schemaOverrides, columnWhitelist } = context
|
|
13
|
+
|
|
14
|
+
const schema = derived(
|
|
15
|
+
[definition, schemaOverrides, columnWhitelist],
|
|
16
|
+
([$definition, $schemaOverrides, $columnWhitelist]) => {
|
|
17
|
+
if (!$definition?.schema) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
let newSchema = { ...$definition?.schema }
|
|
21
|
+
|
|
22
|
+
// Apply schema overrides
|
|
23
|
+
Object.keys($schemaOverrides || {}).forEach(field => {
|
|
24
|
+
if (newSchema[field]) {
|
|
25
|
+
newSchema[field] = {
|
|
26
|
+
...newSchema[field],
|
|
27
|
+
...$schemaOverrides[field],
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Apply whitelist if specified
|
|
33
|
+
if ($columnWhitelist?.length) {
|
|
34
|
+
Object.keys(newSchema).forEach(key => {
|
|
35
|
+
if (!$columnWhitelist.includes(key)) {
|
|
36
|
+
delete newSchema[key]
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return newSchema
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
schema,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const createActions = context => {
|
|
51
|
+
const { datasource, definition, config, dispatch, table, viewV2 } = context
|
|
52
|
+
|
|
53
|
+
// Gets the appropriate API for the configured datasource type
|
|
54
|
+
const getAPI = () => {
|
|
55
|
+
const $datasource = get(datasource)
|
|
56
|
+
switch ($datasource?.type) {
|
|
57
|
+
case "table":
|
|
58
|
+
return table
|
|
59
|
+
case "viewV2":
|
|
60
|
+
return viewV2
|
|
61
|
+
default:
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Refreshes the datasource definition
|
|
67
|
+
const refreshDefinition = async () => {
|
|
68
|
+
return await getAPI()?.actions.refreshDefinition()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Saves the datasource definition
|
|
72
|
+
const saveDefinition = async newDefinition => {
|
|
73
|
+
// Update local state
|
|
74
|
+
definition.set(newDefinition)
|
|
75
|
+
|
|
76
|
+
// Update server
|
|
77
|
+
if (get(config).canSaveSchema) {
|
|
78
|
+
await getAPI()?.actions.saveDefinition(newDefinition)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Broadcast change to external state can be updated, as this change
|
|
82
|
+
// will not be received by the builder websocket because we caused it ourselves
|
|
83
|
+
dispatch("updatedatasource", newDefinition)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Adds a row to the datasource
|
|
87
|
+
const addRow = async row => {
|
|
88
|
+
return await getAPI()?.actions.addRow(row)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Updates an existing row in the datasource
|
|
92
|
+
const updateRow = async row => {
|
|
93
|
+
return await getAPI()?.actions.updateRow(row)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Deletes rows from the datasource
|
|
97
|
+
const deleteRows = async rows => {
|
|
98
|
+
return await getAPI()?.actions.deleteRows(rows)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Gets a single row from a datasource
|
|
102
|
+
const getRow = async id => {
|
|
103
|
+
return await getAPI()?.actions.getRow(id)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Checks if a certain datasource config is valid
|
|
107
|
+
const isDatasourceValid = datasource => {
|
|
108
|
+
return getAPI()?.actions.isDatasourceValid(datasource)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Checks if this datasource can use a specific column by name
|
|
112
|
+
const canUseColumn = name => {
|
|
113
|
+
return getAPI()?.actions.canUseColumn(name)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
datasource: {
|
|
118
|
+
...datasource,
|
|
119
|
+
actions: {
|
|
120
|
+
refreshDefinition,
|
|
121
|
+
saveDefinition,
|
|
122
|
+
addRow,
|
|
123
|
+
updateRow,
|
|
124
|
+
deleteRows,
|
|
125
|
+
getRow,
|
|
126
|
+
isDatasourceValid,
|
|
127
|
+
canUseColumn,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { writable } from "svelte/store"
|
|
1
|
+
import { writable, get } from "svelte/store"
|
|
2
2
|
|
|
3
3
|
export const createStores = context => {
|
|
4
4
|
const { props } = context
|
|
5
5
|
|
|
6
6
|
// Initialise to default props
|
|
7
|
-
const filter = writable(props.initialFilter)
|
|
7
|
+
const filter = writable(get(props).initialFilter)
|
|
8
8
|
|
|
9
9
|
return {
|
|
10
10
|
filter,
|