forked from tsai/budibase
3 changed files with 301 additions and 88 deletions
@ -0,0 +1,192 @@ |
|||||
|
import { writable, derived, get } from "svelte/store" |
||||
|
import * as API from "builderStore/api" |
||||
|
import { buildLuceneQuery } from "../../../client/src/utils/lucene" |
||||
|
|
||||
|
const defaultOptions = { |
||||
|
tableId: null, |
||||
|
filter: null, |
||||
|
limit: 10, |
||||
|
sortColumn: null, |
||||
|
sortOrder: "ascending", |
||||
|
paginate: true, |
||||
|
schema: null, |
||||
|
} |
||||
|
|
||||
|
export const fetchTableData = opts => { |
||||
|
// Save option set so we can override it later rather than relying on params
|
||||
|
let options = { |
||||
|
...defaultOptions, |
||||
|
...opts, |
||||
|
} |
||||
|
|
||||
|
// Local non-observable state
|
||||
|
let query |
||||
|
let sortType |
||||
|
|
||||
|
// Local observable state
|
||||
|
const store = writable({ |
||||
|
rows: [], |
||||
|
schema: null, |
||||
|
loading: false, |
||||
|
loaded: false, |
||||
|
bookmarks: [], |
||||
|
pageNumber: 0, |
||||
|
}) |
||||
|
|
||||
|
// Derive certain properties to return
|
||||
|
const derivedStore = derived(store, $store => { |
||||
|
return { |
||||
|
...$store, |
||||
|
hasNextPage: $store.bookmarks[$store.pageNumber + 1] != null, |
||||
|
hasPrevPage: $store.pageNumber > 0, |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const fetchPage = async bookmark => { |
||||
|
const { tableId, limit, sortColumn, sortOrder, paginate } = options |
||||
|
store.update($store => ({ ...$store, loading: true })) |
||||
|
const res = await API.post(`/api/${options.tableId}/search`, { |
||||
|
tableId, |
||||
|
query, |
||||
|
limit, |
||||
|
sort: sortColumn, |
||||
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending", |
||||
|
sortType, |
||||
|
paginate, |
||||
|
bookmark, |
||||
|
}) |
||||
|
store.update($store => ({ ...$store, loading: false, loaded: true })) |
||||
|
return await res.json() |
||||
|
} |
||||
|
|
||||
|
// Fetches a fresh set of results from the server
|
||||
|
const fetchData = async () => { |
||||
|
const { tableId, schema, sortColumn, filter } = options |
||||
|
|
||||
|
// Ensure table ID exists
|
||||
|
if (!tableId) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Get and enrich schema.
|
||||
|
// Ensure there are "name" properties for all fields and that field schema
|
||||
|
// are objects
|
||||
|
let enrichedSchema = schema |
||||
|
if (!enrichedSchema) { |
||||
|
const definition = await API.get(`/api/tables/${tableId}`) |
||||
|
enrichedSchema = definition?.schema ?? null |
||||
|
} |
||||
|
if (enrichedSchema) { |
||||
|
Object.entries(schema).forEach(([fieldName, fieldSchema]) => { |
||||
|
if (typeof fieldSchema === "string") { |
||||
|
enrichedSchema[fieldName] = { |
||||
|
type: fieldSchema, |
||||
|
name: fieldName, |
||||
|
} |
||||
|
} else { |
||||
|
enrichedSchema[fieldName] = { |
||||
|
...fieldSchema, |
||||
|
name: fieldName, |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// Save fixed schema so we can provide it later
|
||||
|
options.schema = enrichedSchema |
||||
|
} |
||||
|
|
||||
|
// Ensure schema exists
|
||||
|
if (!schema) { |
||||
|
return |
||||
|
} |
||||
|
store.update($store => ({ ...$store, schema })) |
||||
|
|
||||
|
// Work out what sort type to use
|
||||
|
if (!sortColumn || !schema[sortColumn]) { |
||||
|
sortType = "string" |
||||
|
} |
||||
|
const type = schema?.[sortColumn]?.type |
||||
|
sortType = type === "number" ? "number" : "string" |
||||
|
|
||||
|
// Build the lucene query
|
||||
|
query = buildLuceneQuery(filter) |
||||
|
|
||||
|
// Actually fetch data
|
||||
|
const page = await fetchPage() |
||||
|
store.update($store => ({ |
||||
|
...$store, |
||||
|
loading: false, |
||||
|
loaded: true, |
||||
|
pageNumber: 0, |
||||
|
rows: page.rows, |
||||
|
bookmarks: page.hasNextPage ? [null, page.bookmark] : [null], |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
// Fetches the next page of data
|
||||
|
const nextPage = async () => { |
||||
|
const state = get(derivedStore) |
||||
|
if (!options.paginate || !state.hasNextPage) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Fetch next page
|
||||
|
const page = await fetchPage(state.bookmarks[state.pageNumber + 1]) |
||||
|
|
||||
|
// Update state
|
||||
|
store.update($store => { |
||||
|
let { bookmarks, pageNumber } = $store |
||||
|
if (page.hasNextPage) { |
||||
|
bookmarks[pageNumber + 2] = page.bookmark |
||||
|
} |
||||
|
return { |
||||
|
...$store, |
||||
|
pageNumber: pageNumber + 1, |
||||
|
rows: page.rows, |
||||
|
bookmarks, |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Fetches the previous page of data
|
||||
|
const prevPage = async () => { |
||||
|
const state = get(derivedStore) |
||||
|
if (!options.paginate || !state.hasPrevPage) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Fetch previous page
|
||||
|
const page = await fetchPage(state.bookmarks[state.pageNumber - 1]) |
||||
|
|
||||
|
// Update state
|
||||
|
store.update($store => { |
||||
|
return { |
||||
|
...$store, |
||||
|
pageNumber: $store.pageNumber - 1, |
||||
|
rows: page.rows, |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Resets the data set and updates options
|
||||
|
const update = async newOptions => { |
||||
|
if (newOptions) { |
||||
|
options = { |
||||
|
...options, |
||||
|
...newOptions, |
||||
|
} |
||||
|
} |
||||
|
await fetchData() |
||||
|
} |
||||
|
|
||||
|
// Initially fetch data but don't bother waiting for the result
|
||||
|
fetchData() |
||||
|
|
||||
|
// Return our derived store which will be updated over time
|
||||
|
return { |
||||
|
subscribe: derivedStore.subscribe, |
||||
|
nextPage, |
||||
|
prevPage, |
||||
|
update, |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue