Add initial support for query datasources in grids
This commit is contained in:
parent
b9ae3d0e23
commit
d03fdb6df9
|
@ -5545,12 +5545,11 @@
|
|||
"width": 600,
|
||||
"height": 400
|
||||
},
|
||||
"info": "Grid Blocks are only compatible with internal or SQL tables",
|
||||
"settings": [
|
||||
{
|
||||
"type": "table",
|
||||
"type": "dataSource",
|
||||
"label": "Data",
|
||||
"key": "table",
|
||||
"key": "datasource",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { getContext } from "svelte"
|
||||
import { Grid } from "@budibase/frontend-core"
|
||||
|
||||
export let table
|
||||
export let datasource
|
||||
export let allowAddRows = true
|
||||
export let allowEditRows = true
|
||||
export let allowDeleteRows = true
|
||||
|
@ -15,6 +15,9 @@
|
|||
export let fixedRowHeight = null
|
||||
export let columns = null
|
||||
|
||||
// Legacy settings
|
||||
export let table
|
||||
|
||||
const component = getContext("component")
|
||||
const { styleable, API, builderStore, notificationStore } = getContext("sdk")
|
||||
|
||||
|
@ -38,7 +41,7 @@
|
|||
class:in-builder={$builderStore.inBuilder}
|
||||
>
|
||||
<Grid
|
||||
datasource={table}
|
||||
datasource={datasource || table}
|
||||
{API}
|
||||
{stripeRows}
|
||||
{initialFilter}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
column.schema.autocolumn ||
|
||||
column.schema.disabled ||
|
||||
column.schema.type === "formula" ||
|
||||
(!$config.canEditRows && row._id)
|
||||
(!$config.canEditRows && !row._isNewRow)
|
||||
|
||||
// Register this cell API if the row is focused
|
||||
$: {
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
let visible = false
|
||||
let isAdding = false
|
||||
let newRow = {}
|
||||
let newRow = { _isNewRow: true }
|
||||
let offset = 0
|
||||
|
||||
$: firstColumn = $stickyColumn || $renderedColumns[0]
|
||||
|
@ -58,7 +58,9 @@
|
|||
|
||||
// Create row
|
||||
const newRowIndex = offset ? undefined : 0
|
||||
const savedRow = await rows.actions.addRow(newRow, newRowIndex)
|
||||
let rowToCreate = { ...newRow }
|
||||
delete rowToCreate._isNewRow
|
||||
const savedRow = await rows.actions.addRow(rowToCreate, newRowIndex)
|
||||
if (savedRow) {
|
||||
// Reset state
|
||||
clear()
|
||||
|
|
|
@ -37,9 +37,10 @@ export const deriveStores = context => {
|
|||
[props, hasNonAutoColumn],
|
||||
([$props, $hasNonAutoColumn]) => {
|
||||
let config = { ...$props }
|
||||
const type = $props.datasource?.type
|
||||
|
||||
// Disable some features if we're editing a view
|
||||
if ($props.datasource?.type === "viewV2") {
|
||||
if (type === "viewV2") {
|
||||
config.canEditColumns = false
|
||||
}
|
||||
|
||||
|
@ -48,6 +49,14 @@ export const deriveStores = context => {
|
|||
config.canAddRows = false
|
||||
}
|
||||
|
||||
// Disable features for non DS+
|
||||
if (!["table", "viewV2"].includes(type)) {
|
||||
config.canAddRows = false
|
||||
config.canEditRows = false
|
||||
config.canDeleteRows = false
|
||||
config.canExpandRows = false
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { derived, get, writable } from "svelte/store"
|
||||
import { getDatasourceDefinition } from "../../../fetch"
|
||||
|
||||
export const createStores = () => {
|
||||
const definition = writable(null)
|
||||
|
@ -19,6 +20,14 @@ export const deriveStores = context => {
|
|||
}
|
||||
let newSchema = { ...$definition?.schema }
|
||||
|
||||
// Ensure schema is configured as objects.
|
||||
// Certain datasources like queries use primitives.
|
||||
Object.keys(newSchema).forEach(key => {
|
||||
if (typeof newSchema[key] !== "object") {
|
||||
newSchema[key] = { type: newSchema[key] }
|
||||
}
|
||||
})
|
||||
|
||||
// Apply schema overrides
|
||||
Object.keys($schemaOverrides || {}).forEach(field => {
|
||||
if (newSchema[field]) {
|
||||
|
@ -48,7 +57,16 @@ export const deriveStores = context => {
|
|||
}
|
||||
|
||||
export const createActions = context => {
|
||||
const { datasource, definition, config, dispatch, table, viewV2 } = context
|
||||
const {
|
||||
API,
|
||||
datasource,
|
||||
definition,
|
||||
config,
|
||||
dispatch,
|
||||
table,
|
||||
viewV2,
|
||||
query,
|
||||
} = context
|
||||
|
||||
// Gets the appropriate API for the configured datasource type
|
||||
const getAPI = () => {
|
||||
|
@ -58,6 +76,8 @@ export const createActions = context => {
|
|||
return table
|
||||
case "viewV2":
|
||||
return viewV2
|
||||
case "query":
|
||||
return query
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
@ -65,7 +85,8 @@ export const createActions = context => {
|
|||
|
||||
// Refreshes the datasource definition
|
||||
const refreshDefinition = async () => {
|
||||
return await getAPI()?.actions.refreshDefinition()
|
||||
const def = await getDatasourceDefinition({ API, datasource })
|
||||
definition.set(def)
|
||||
}
|
||||
|
||||
// Saves the datasource definition
|
||||
|
|
|
@ -17,6 +17,7 @@ import * as Filter from "./filter"
|
|||
import * as Notifications from "./notifications"
|
||||
import * as Table from "./table"
|
||||
import * as ViewV2 from "./viewV2"
|
||||
import * as Query from "./query"
|
||||
import * as Datasource from "./datasource"
|
||||
|
||||
const DependencyOrderedStores = [
|
||||
|
@ -26,6 +27,7 @@ const DependencyOrderedStores = [
|
|||
Scroll,
|
||||
Table,
|
||||
ViewV2,
|
||||
Query,
|
||||
Datasource,
|
||||
Columns,
|
||||
Rows,
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import { get } from "svelte/store"
|
||||
|
||||
export const createActions = context => {
|
||||
const { API, columns, stickyColumn } = context
|
||||
|
||||
const saveDefinition = async newDefinition => {
|
||||
await API.saveQuery(newDefinition)
|
||||
}
|
||||
|
||||
const saveRow = async () => {
|
||||
throw "Rows cannot be updated through queries"
|
||||
}
|
||||
|
||||
const deleteRows = async () => {
|
||||
throw "Rows cannot be deleted through queries"
|
||||
}
|
||||
|
||||
const getRow = () => {
|
||||
throw "Queries don't support fetching individual rows"
|
||||
}
|
||||
|
||||
const isDatasourceValid = datasource => {
|
||||
return datasource?.type === "query" && datasource?._id
|
||||
}
|
||||
|
||||
const canUseColumn = name => {
|
||||
const $columns = get(columns)
|
||||
const $sticky = get(stickyColumn)
|
||||
return $columns.some(col => col.name === name) || $sticky?.name === name
|
||||
}
|
||||
|
||||
return {
|
||||
query: {
|
||||
actions: {
|
||||
saveDefinition,
|
||||
addRow: saveRow,
|
||||
updateRow: saveRow,
|
||||
deleteRows,
|
||||
getRow,
|
||||
isDatasourceValid,
|
||||
canUseColumn,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const initialise = context => {
|
||||
const {
|
||||
datasource,
|
||||
sort,
|
||||
filter,
|
||||
query,
|
||||
initialFilter,
|
||||
initialSortColumn,
|
||||
initialSortOrder,
|
||||
fetch,
|
||||
} = context
|
||||
|
||||
// Keep a list of subscriptions so that we can clear them when the datasource
|
||||
// config changes
|
||||
let unsubscribers = []
|
||||
|
||||
// Observe datasource changes and apply logic for view V2 datasources
|
||||
datasource.subscribe($datasource => {
|
||||
// Clear previous subscriptions
|
||||
unsubscribers?.forEach(unsubscribe => unsubscribe())
|
||||
unsubscribers = []
|
||||
if (!query.actions.isDatasourceValid($datasource)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Wipe state
|
||||
filter.set(get(initialFilter))
|
||||
sort.set({
|
||||
column: get(initialSortColumn),
|
||||
order: get(initialSortOrder) || "ascending",
|
||||
})
|
||||
|
||||
// Update fetch when filter changes
|
||||
unsubscribers.push(
|
||||
filter.subscribe($filter => {
|
||||
// Ensure we're updating the correct fetch
|
||||
const $fetch = get(fetch)
|
||||
if ($fetch?.options?.datasource?._id !== $datasource._id) {
|
||||
return
|
||||
}
|
||||
$fetch.update({
|
||||
filter: $filter,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
// Update fetch when sorting changes
|
||||
unsubscribers.push(
|
||||
sort.subscribe($sort => {
|
||||
// Ensure we're updating the correct fetch
|
||||
const $fetch = get(fetch)
|
||||
if ($fetch?.options?.datasource?._id !== $datasource._id) {
|
||||
return
|
||||
}
|
||||
$fetch.update({
|
||||
sortOrder: $sort.order || "ascending",
|
||||
sortColumn: $sort.column,
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { writable, derived, get } from "svelte/store"
|
||||
import { fetchData } from "../../../fetch/fetchData"
|
||||
import { fetchData } from "../../../fetch"
|
||||
import { NewRowID, RowPageSize } from "../lib/constants"
|
||||
import { tick } from "svelte"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
|
||||
export const createStores = () => {
|
||||
const rows = writable([])
|
||||
|
@ -413,6 +414,13 @@ export const createActions = context => {
|
|||
let newRow
|
||||
for (let i = 0; i < newRows.length; i++) {
|
||||
newRow = newRows[i]
|
||||
|
||||
// Ensure we have a unique _id.
|
||||
// This means generating one for non DS+.
|
||||
if (!newRow._id) {
|
||||
newRow._id = Helpers.hashString(JSON.stringify(newRow))
|
||||
}
|
||||
|
||||
if (!rowCacheMap[newRow._id]) {
|
||||
rowCacheMap[newRow._id] = true
|
||||
rowsToAppend.push(newRow)
|
||||
|
|
|
@ -3,11 +3,7 @@ import { get } from "svelte/store"
|
|||
const SuppressErrors = true
|
||||
|
||||
export const createActions = context => {
|
||||
const { definition, API, datasource, columns, stickyColumn } = context
|
||||
|
||||
const refreshDefinition = async () => {
|
||||
definition.set(await API.fetchTableDefinition(get(datasource).tableId))
|
||||
}
|
||||
const { API, datasource, columns, stickyColumn } = context
|
||||
|
||||
const saveDefinition = async newDefinition => {
|
||||
await API.saveTable(newDefinition)
|
||||
|
@ -52,7 +48,6 @@ export const createActions = context => {
|
|||
return {
|
||||
table: {
|
||||
actions: {
|
||||
refreshDefinition,
|
||||
saveDefinition,
|
||||
addRow: saveRow,
|
||||
updateRow: saveRow,
|
||||
|
|
|
@ -3,20 +3,7 @@ import { get } from "svelte/store"
|
|||
const SuppressErrors = true
|
||||
|
||||
export const createActions = context => {
|
||||
const { definition, API, datasource, columns, stickyColumn } = context
|
||||
|
||||
const refreshDefinition = async () => {
|
||||
const $datasource = get(datasource)
|
||||
if (!$datasource) {
|
||||
definition.set(null)
|
||||
return
|
||||
}
|
||||
const table = await API.fetchTableDefinition($datasource.tableId)
|
||||
const view = Object.values(table?.views || {}).find(
|
||||
view => view.id === $datasource.id
|
||||
)
|
||||
definition.set(view)
|
||||
}
|
||||
const { API, datasource, columns, stickyColumn } = context
|
||||
|
||||
const saveDefinition = async newDefinition => {
|
||||
await API.viewV2.update(newDefinition)
|
||||
|
@ -61,7 +48,6 @@ export const createActions = context => {
|
|||
return {
|
||||
viewV2: {
|
||||
actions: {
|
||||
refreshDefinition,
|
||||
saveDefinition,
|
||||
addRow: saveRow,
|
||||
updateRow: saveRow,
|
||||
|
|
|
@ -24,7 +24,18 @@ const DataFetchMap = {
|
|||
jsonarray: JSONArrayFetch,
|
||||
}
|
||||
|
||||
// Constructs a new fetch model for a certain datasource
|
||||
export const fetchData = ({ API, datasource, options }) => {
|
||||
const Fetch = DataFetchMap[datasource?.type] || TableFetch
|
||||
return new Fetch({ API, datasource, ...options })
|
||||
}
|
||||
|
||||
// Fetches the definition of any type of datasource
|
||||
export const getDatasourceDefinition = async ({ API, datasource }) => {
|
||||
const handler = DataFetchMap[datasource?.type]
|
||||
if (!handler) {
|
||||
return null
|
||||
}
|
||||
const instance = new handler({ API })
|
||||
return await instance.getDefinition(datasource)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
export { createAPIClient } from "./api"
|
||||
export { fetchData } from "./fetch/fetchData"
|
||||
export { fetchData } from "./fetch"
|
||||
export { Utils } from "./utils"
|
||||
export * as Constants from "./constants"
|
||||
export * from "./stores"
|
||||
|
|
Loading…
Reference in New Issue