diff --git a/packages/frontend-core/src/components/sheet/Sheet.svelte b/packages/frontend-core/src/components/sheet/Sheet.svelte index e3e857252e..ceb158b529 100644 --- a/packages/frontend-core/src/components/sheet/Sheet.svelte +++ b/packages/frontend-core/src/components/sheet/Sheet.svelte @@ -86,10 +86,6 @@ // Expose ability to retrieve context externally to allow sheet control export const getContext = () => context - // Local flag for if the sheet has ever had data - let initialised = false - loaded.subscribe(state => (initialised = initialised || state)) - // Initialise websocket for multi-user onMount(() => createWebsocket(context)) @@ -112,7 +108,7 @@ - {#if initialised} + {#if $loaded}
diff --git a/packages/frontend-core/src/components/sheet/SheetControls.svelte b/packages/frontend-core/src/components/sheet/SheetControls.svelte index ac6b747510..28f1ec6ddb 100644 --- a/packages/frontend-core/src/components/sheet/SheetControls.svelte +++ b/packages/frontend-core/src/components/sheet/SheetControls.svelte @@ -1,9 +1,10 @@ -Sort + Hide fields diff --git a/packages/frontend-core/src/components/sheet/controls/SortButton.svelte b/packages/frontend-core/src/components/sheet/controls/SortButton.svelte new file mode 100644 index 0000000000..1e28af66cb --- /dev/null +++ b/packages/frontend-core/src/components/sheet/controls/SortButton.svelte @@ -0,0 +1,52 @@ + + +
+ (open = !open)} + selected={!!$sort.order} + > + Sort + +
+ + +
+ +
+
+ + diff --git a/packages/frontend-core/src/components/sheet/stores/rows.js b/packages/frontend-core/src/components/sheet/stores/rows.js index b7ce3b7ad2..c9219ffdc1 100644 --- a/packages/frontend-core/src/components/sheet/stores/rows.js +++ b/packages/frontend-core/src/components/sheet/stores/rows.js @@ -1,5 +1,4 @@ import { writable, derived, get } from "svelte/store" -import { LuceneUtils } from "../../../index" import { fetchData } from "../../../fetch/fetchData" import { notifications } from "@budibase/bbui" @@ -11,20 +10,25 @@ export const createRowsStore = context => { const table = writable(null) const filter = writable([]) const loaded = writable(false) + const fetch = writable(null) const sort = writable({ column: null, order: null, }) + + // Enrich rows with an index property const enrichedRows = derived(rows, $rows => { return $rows.map((row, idx) => ({ ...row, __idx: idx, })) }) + + // Generate a lookup map to quick find a row by ID const rowLookupMap = derived(enrichedRows, $rows => { let map = {} - for (let i = 0; i < $rows.length; i++) { - map[$rows[i]._id] = i + for (let row of $rows) { + map[row._id] = row.__idx } return map }) @@ -33,69 +37,84 @@ export const createRowsStore = context => { let rowCacheMap = {} // Reset everything when table ID changes - tableId.subscribe(() => { - filter.set([]) + let unsubscribe = null + tableId.subscribe($tableId => { + // Unsub from previous fetch if one exists + unsubscribe?.() + fetch.set(null) + + // Reset state sort.set({ column: null, order: null, }) - }) + filter.set([]) - // Local stores for managing fetching data - const query = derived(filter, $filter => - LuceneUtils.buildLuceneQuery($filter) - ) - const fetch = derived([tableId, query, sort], ([$tableId, $query, $sort]) => { - if (!$tableId) { - return null - } - // Wipe state and fully hydrate next time our fetch returns data - loaded.set(false) - - // Create fetch and load initial data - return fetchData({ + // Create new fetch model + const newFetch = fetchData({ API, datasource: { type: "table", tableId: $tableId, }, options: { - sortColumn: $sort.column, - sortOrder: $sort.order, - query: $query, + filter: [], + sortColumn: null, + sortOrder: null, limit: 100, paginate: true, }, }) - }) - // Observe each data fetch and extract some data - fetch.subscribe($fetch => { - if (!$fetch) { - return - } - $fetch.subscribe($$fetch => { - if ($$fetch.loaded) { - if (!get(loaded)) { + // Subscribe to changes of this fetch model + unsubscribe = newFetch.subscribe($fetch => { + if ($fetch.loaded && !$fetch.loading) { + if ($fetch.pageNumber === 0) { // Hydrate initial data - loaded.set(true) rowCacheMap = {} rows.set([]) + + // Update sorting from fetch if required + const $sort = get(sort) + if (!$sort.column) { + sort.set({ + column: $fetch.sortColumn, + order: $fetch.sortOrder, + }) + } } // Update schema and enrich primary display into schema - let newSchema = $$fetch.schema - const primaryDisplay = $$fetch.definition?.primaryDisplay + let newSchema = $fetch.schema + const primaryDisplay = $fetch.definition?.primaryDisplay if (primaryDisplay && newSchema[primaryDisplay]) { newSchema[primaryDisplay].primaryDisplay = true } schema.set(newSchema) - table.set($$fetch.definition) + table.set($fetch.definition) // Process new rows - handleNewRows($$fetch.rows) + handleNewRows($fetch.rows) + + // Notify that we're loaded + loaded.set(true) } }) + + fetch.set(newFetch) + }) + + // Update fetch when filter or sort config changes + filter.subscribe($filter => { + get(fetch)?.update({ + filter: $filter, + }) + }) + sort.subscribe($sort => { + get(fetch)?.update({ + sortOrder: $sort.order, + sortColumn: $sort.column, + }) }) // Adds a new empty row diff --git a/packages/frontend-core/src/fetch/DataFetch.js b/packages/frontend-core/src/fetch/DataFetch.js index 4c47f13172..049a840128 100644 --- a/packages/frontend-core/src/fetch/DataFetch.js +++ b/packages/frontend-core/src/fetch/DataFetch.js @@ -21,11 +21,11 @@ export default class DataFetch { this.API = null // Feature flags - this.featureStore = writable({ + this.features = { supportsSearch: false, supportsSort: false, supportsPagination: false, - }) + } // Config this.options = { @@ -81,17 +81,14 @@ export default class DataFetch { this.prevPage = this.prevPage.bind(this) // Derive certain properties to return - this.derivedStore = derived( - [this.store, this.featureStore], - ([$store, $featureStore]) => { - return { - ...$store, - ...$featureStore, - hasNextPage: this.hasNextPage($store), - hasPrevPage: this.hasPrevPage($store), - } + this.derivedStore = derived(this.store, $store => { + return { + ...$store, + ...this.features, + hasNextPage: this.hasNextPage($store), + hasPrevPage: this.hasPrevPage($store), } - ) + }) // Mark as loaded if we have no datasource if (!this.options.datasource) { @@ -120,11 +117,11 @@ export default class DataFetch { // Fetch datasource definition and determine feature flags const definition = await this.getDefinition(datasource) const features = this.determineFeatureFlags(definition) - this.featureStore.set({ + this.features = { supportsSearch: !!features?.supportsSearch, supportsSort: !!features?.supportsSort, supportsPagination: paginate && !!features?.supportsPagination, - }) + } // Fetch and enrich schema let schema = this.getSchema(datasource, definition) @@ -138,11 +135,17 @@ export default class DataFetch { this.options.sortOrder = "ascending" } - // If no sort column, use the first field in the schema + // If no sort column, use the primary display and fallback to first column if (!this.options.sortColumn) { - this.options.sortColumn = Object.keys(schema)[0] + let newSortColumn + if (definition?.primaryDisplay && schema[definition.primaryDisplay]) { + newSortColumn = definition.primaryDisplay + } else { + newSortColumn = Object.keys(schema)[0] + } + this.options.sortColumn = newSortColumn } - const { sortColumn } = this.options + const { sortOrder, sortColumn } = this.options // Determine what sort type to use let sortType = "string" @@ -167,6 +170,8 @@ export default class DataFetch { loading: true, cursors: [], cursor: null, + sortOrder, + sortColumn, })) // Actually fetch data @@ -189,23 +194,23 @@ export default class DataFetch { async getPage() { const { sortColumn, sortOrder, sortType, limit } = this.options const { query } = get(this.store) - const features = get(this.featureStore) // Get the actual data + console.log("===== FETCH =====") let { rows, info, hasNextPage, cursor, error } = await this.getData() // If we don't support searching, do a client search - if (!features.supportsSearch) { + if (!this.features.supportsSearch) { rows = runLuceneQuery(rows, query) } // If we don't support sorting, do a client-side sort - if (!features.supportsSort) { + if (!this.features.supportsSort) { rows = luceneSort(rows, sortColumn, sortOrder, sortType) } // If we don't support pagination, do a client-side limit - if (!features.supportsPagination) { + if (!this.features.supportsPagination) { rows = luceneLimit(rows, limit) } diff --git a/packages/frontend-core/src/fetch/QueryFetch.js b/packages/frontend-core/src/fetch/QueryFetch.js index 65f0a4f130..456abaec79 100644 --- a/packages/frontend-core/src/fetch/QueryFetch.js +++ b/packages/frontend-core/src/fetch/QueryFetch.js @@ -31,7 +31,7 @@ export default class QueryFetch extends DataFetch { async getData() { const { datasource, limit, paginate } = this.options - const { supportsPagination } = get(this.featureStore) + const { supportsPagination } = this.features const { cursor, definition } = get(this.store) const type = definition?.fields?.pagination?.type