@budibase/frontend-core 2.12.6 → 2.12.8

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 CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "2.12.6",
3
+ "version": "2.12.8",
4
4
  "description": "Budibase frontend core libraries used in builder and client",
5
5
  "author": "Budibase",
6
6
  "license": "MPL-2.0",
7
7
  "svelte": "src/index.js",
8
8
  "dependencies": {
9
- "@budibase/bbui": "2.12.6",
10
- "@budibase/shared-core": "2.12.6",
9
+ "@budibase/bbui": "2.12.8",
10
+ "@budibase/shared-core": "2.12.8",
11
11
  "dayjs": "^1.10.8",
12
12
  "lodash": "^4.17.21",
13
13
  "socket.io-client": "^4.6.1",
14
14
  "svelte": "^3.46.2"
15
15
  },
16
- "gitHead": "1e5fcb443bf5906edc55237be7e70f4e20b66e42"
16
+ "gitHead": "cd06f286adc60ca241fa9e36bfc873671f9e10a1"
17
17
  }
@@ -1,27 +1,10 @@
1
- <script context="module">
2
- // We can create a module level cache for all relationship cells to avoid
3
- // having to fetch the table definition one time for each cell
4
- let primaryDisplayCache = {}
5
-
6
- const getPrimaryDisplayForTableId = async (API, tableId) => {
7
- if (primaryDisplayCache[tableId]) {
8
- return primaryDisplayCache[tableId]
9
- }
10
- const definition = await API.fetchTableDefinition(tableId)
11
- const primaryDisplay =
12
- definition?.primaryDisplay || definition?.schema?.[0]?.name
13
- primaryDisplayCache[tableId] = primaryDisplay
14
- return primaryDisplay
15
- }
16
- </script>
17
-
18
1
  <script>
19
2
  import { getColor } from "../lib/utils"
20
3
  import { onMount, getContext } from "svelte"
21
4
  import { Icon, Input, ProgressCircle, clickOutside } from "@budibase/bbui"
22
5
  import { debounce } from "../../../utils/utils"
23
6
 
24
- const { API, dispatch } = getContext("grid")
7
+ const { API, dispatch, cache } = getContext("grid")
25
8
 
26
9
  export let value
27
10
  export let api
@@ -147,7 +130,9 @@
147
130
  // Find the primary display for the related table
148
131
  if (!primaryDisplay) {
149
132
  searching = true
150
- primaryDisplay = await getPrimaryDisplayForTableId(API, schema.tableId)
133
+ primaryDisplay = await cache.actions.getPrimaryDisplayForTableId(
134
+ schema.tableId
135
+ )
151
136
  }
152
137
 
153
138
  // Show initial list of results
@@ -195,7 +180,7 @@
195
180
  const toggleRow = async row => {
196
181
  if (value?.some(x => x._id === row._id)) {
197
182
  // If the row is already included, remove it and update the candidate
198
- // row to be the the same position if possible
183
+ // row to be the same position if possible
199
184
  if (oneRowOnly) {
200
185
  await onChange([])
201
186
  } else {
@@ -260,31 +245,29 @@
260
245
  class:wrap={editable || contentLines > 1}
261
246
  on:wheel={e => (focused ? e.stopPropagation() : null)}
262
247
  >
263
- {#if Array.isArray(value) && value.length}
264
- {#each value as relationship}
265
- {#if relationship[primaryDisplay] || relationship.primaryDisplay}
266
- <div class="badge">
267
- <span
268
- on:click={editable
269
- ? () => showRelationship(relationship._id)
270
- : null}
271
- >
272
- {readable(
273
- relationship[primaryDisplay] || relationship.primaryDisplay
274
- )}
275
- </span>
276
- {#if editable}
277
- <Icon
278
- name="Close"
279
- size="XS"
280
- hoverable
281
- on:click={() => toggleRow(relationship)}
282
- />
283
- {/if}
284
- </div>
285
- {/if}
286
- {/each}
287
- {/if}
248
+ {#each value || [] as relationship}
249
+ {#if relationship[primaryDisplay] || relationship.primaryDisplay}
250
+ <div class="badge">
251
+ <span
252
+ on:click={editable
253
+ ? () => showRelationship(relationship._id)
254
+ : null}
255
+ >
256
+ {readable(
257
+ relationship[primaryDisplay] || relationship.primaryDisplay
258
+ )}
259
+ </span>
260
+ {#if editable}
261
+ <Icon
262
+ name="Close"
263
+ size="XS"
264
+ hoverable
265
+ on:click={() => toggleRow(relationship)}
266
+ />
267
+ {/if}
268
+ </div>
269
+ {/if}
270
+ {/each}
288
271
  {#if editable}
289
272
  <div class="add" on:click={open}>
290
273
  <Icon name="Add" size="S" />
@@ -320,7 +303,7 @@
320
303
  <div class="searching">
321
304
  <ProgressCircle size="S" />
322
305
  </div>
323
- {:else if Array.isArray(searchResults) && searchResults.length}
306
+ {:else if searchResults?.length}
324
307
  <div class="results">
325
308
  {#each searchResults as row, idx}
326
309
  <div
@@ -0,0 +1,47 @@
1
+ export const createActions = context => {
2
+ const { API } = context
3
+
4
+ // Cache for the primary display columns of different tables.
5
+ // If we ever need to cache table definitions for other purposes then we can
6
+ // expand this to be a more generic cache.
7
+ let primaryDisplayCache = {}
8
+
9
+ const resetPrimaryDisplayCache = () => {
10
+ primaryDisplayCache = {}
11
+ }
12
+
13
+ const getPrimaryDisplayForTableId = async tableId => {
14
+ // If we've never encountered this tableId before then store a promise that
15
+ // resolves to the primary display so that subsequent invocations before the
16
+ // promise completes can reuse this promise
17
+ if (!primaryDisplayCache[tableId]) {
18
+ primaryDisplayCache[tableId] = new Promise(resolve => {
19
+ API.fetchTableDefinition(tableId).then(def => {
20
+ const display = def?.primaryDisplay || def?.schema?.[0]?.name
21
+ primaryDisplayCache[tableId] = display
22
+ resolve(display)
23
+ })
24
+ })
25
+ }
26
+
27
+ // We await the result so that we account for both promises and primitives
28
+ return await primaryDisplayCache[tableId]
29
+ }
30
+
31
+ return {
32
+ cache: {
33
+ actions: {
34
+ getPrimaryDisplayForTableId,
35
+ resetPrimaryDisplayCache,
36
+ },
37
+ },
38
+ }
39
+ }
40
+
41
+ export const initialise = context => {
42
+ const { datasource, cache } = context
43
+
44
+ // Wipe the caches whenever the datasource changes to ensure we aren't
45
+ // storing any stale information
46
+ datasource.subscribe(cache.actions.resetPrimaryDisplayCache)
47
+ }
@@ -160,11 +160,6 @@ export const createActions = context => {
160
160
  return getAPI()?.actions.canUseColumn(name)
161
161
  }
162
162
 
163
- // Gets the default number of rows for a single page
164
- const getFeatures = () => {
165
- return getAPI()?.actions.getFeatures()
166
- }
167
-
168
163
  return {
169
164
  datasource: {
170
165
  ...datasource,
@@ -177,7 +172,6 @@ export const createActions = context => {
177
172
  getRow,
178
173
  isDatasourceValid,
179
174
  canUseColumn,
180
- getFeatures,
181
175
  },
182
176
  },
183
177
  }
@@ -35,11 +35,6 @@ export const createActions = context => {
35
35
  return $columns.some(col => col.name === name) || $sticky?.name === name
36
36
  }
37
37
 
38
- const getFeatures = () => {
39
- // We don't support any features
40
- return {}
41
- }
42
-
43
38
  return {
44
39
  nonPlus: {
45
40
  actions: {
@@ -50,7 +45,6 @@ export const createActions = context => {
50
45
  getRow,
51
46
  isDatasourceValid,
52
47
  canUseColumn,
53
- getFeatures,
54
48
  },
55
49
  },
56
50
  }
@@ -1,5 +1,4 @@
1
1
  import { get } from "svelte/store"
2
- import TableFetch from "../../../../fetch/TableFetch"
3
2
 
4
3
  const SuppressErrors = true
5
4
 
@@ -46,10 +45,6 @@ export const createActions = context => {
46
45
  return $columns.some(col => col.name === name) || $sticky?.name === name
47
46
  }
48
47
 
49
- const getFeatures = () => {
50
- return new TableFetch({ API }).determineFeatureFlags()
51
- }
52
-
53
48
  return {
54
49
  table: {
55
50
  actions: {
@@ -60,7 +55,6 @@ export const createActions = context => {
60
55
  getRow,
61
56
  isDatasourceValid,
62
57
  canUseColumn,
63
- getFeatures,
64
58
  },
65
59
  },
66
60
  }
@@ -1,5 +1,4 @@
1
1
  import { get } from "svelte/store"
2
- import ViewV2Fetch from "../../../../fetch/ViewV2Fetch"
3
2
 
4
3
  const SuppressErrors = true
5
4
 
@@ -46,10 +45,6 @@ export const createActions = context => {
46
45
  )
47
46
  }
48
47
 
49
- const getFeatures = () => {
50
- return new ViewV2Fetch({ API }).determineFeatureFlags()
51
- }
52
-
53
48
  return {
54
49
  viewV2: {
55
50
  actions: {
@@ -60,7 +55,6 @@ export const createActions = context => {
60
55
  getRow,
61
56
  isDatasourceValid,
62
57
  canUseColumn,
63
- getFeatures,
64
58
  },
65
59
  },
66
60
  }
@@ -19,6 +19,7 @@ import * as Datasource from "./datasource"
19
19
  import * as Table from "./datasources/table"
20
20
  import * as ViewV2 from "./datasources/viewV2"
21
21
  import * as NonPlus from "./datasources/nonPlus"
22
+ import * as Cache from "./cache"
22
23
 
23
24
  const DependencyOrderedStores = [
24
25
  Sort,
@@ -42,6 +43,7 @@ const DependencyOrderedStores = [
42
43
  Clipboard,
43
44
  Config,
44
45
  Notifications,
46
+ Cache,
45
47
  ]
46
48
 
47
49
  export const attachStores = context => {
@@ -114,10 +114,6 @@ export const createActions = context => {
114
114
  const $allFilters = get(allFilters)
115
115
  const $sort = get(sort)
116
116
 
117
- // Determine how many rows to fetch per page
118
- const features = datasource.actions.getFeatures()
119
- const limit = features?.supportsPagination ? RowPageSize : null
120
-
121
117
  // Create new fetch model
122
118
  const newFetch = fetchData({
123
119
  API,
@@ -126,8 +122,12 @@ export const createActions = context => {
126
122
  filter: $allFilters,
127
123
  sortColumn: $sort.column,
128
124
  sortOrder: $sort.order,
129
- limit,
125
+ limit: RowPageSize,
130
126
  paginate: true,
127
+
128
+ // Disable client side limiting, so that for queries and custom data
129
+ // sources we don't impose fake row limits. We want all the data.
130
+ clientSideLimiting: false,
131
131
  },
132
132
  })
133
133
 
@@ -43,6 +43,11 @@ export default class DataFetch {
43
43
 
44
44
  // Pagination config
45
45
  paginate: true,
46
+
47
+ // Client side feature customisation
48
+ clientSideSearching: true,
49
+ clientSideSorting: true,
50
+ clientSideLimiting: true,
46
51
  }
47
52
 
48
53
  // State of the fetch
@@ -208,24 +213,32 @@ export default class DataFetch {
208
213
  * Fetches some filtered, sorted and paginated data
209
214
  */
210
215
  async getPage() {
211
- const { sortColumn, sortOrder, sortType, limit } = this.options
216
+ const {
217
+ sortColumn,
218
+ sortOrder,
219
+ sortType,
220
+ limit,
221
+ clientSideSearching,
222
+ clientSideSorting,
223
+ clientSideLimiting,
224
+ } = this.options
212
225
  const { query } = get(this.store)
213
226
 
214
227
  // Get the actual data
215
228
  let { rows, info, hasNextPage, cursor, error } = await this.getData()
216
229
 
217
230
  // If we don't support searching, do a client search
218
- if (!this.features.supportsSearch) {
231
+ if (!this.features.supportsSearch && clientSideSearching) {
219
232
  rows = runLuceneQuery(rows, query)
220
233
  }
221
234
 
222
235
  // If we don't support sorting, do a client-side sort
223
- if (!this.features.supportsSort) {
236
+ if (!this.features.supportsSort && clientSideSorting) {
224
237
  rows = luceneSort(rows, sortColumn, sortOrder, sortType)
225
238
  }
226
239
 
227
240
  // If we don't support pagination, do a client-side limit
228
- if (!this.features.supportsPagination) {
241
+ if (!this.features.supportsPagination && clientSideLimiting) {
229
242
  rows = luceneLimit(rows, limit)
230
243
  }
231
244