@budibase/frontend-core 2.25.0 → 2.26.1

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,18 +1,18 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "2.25.0",
3
+ "version": "2.26.1",
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.25.0",
10
- "@budibase/shared-core": "2.25.0",
11
- "@budibase/types": "2.25.0",
9
+ "@budibase/bbui": "2.26.1",
10
+ "@budibase/shared-core": "2.26.1",
11
+ "@budibase/types": "2.26.1",
12
12
  "dayjs": "^1.10.8",
13
13
  "lodash": "4.17.21",
14
14
  "shortid": "2.2.15",
15
15
  "socket.io-client": "^4.6.1"
16
16
  },
17
- "gitHead": "75d949ef66bf20519446d3999c3412e106ef561d"
17
+ "gitHead": "0d8b8b6ef7b4abe1339858e09319faec032fd263"
18
18
  }
@@ -125,6 +125,7 @@
125
125
  filter.type = fieldSchema?.type
126
126
  filter.subtype = fieldSchema?.subtype
127
127
  filter.formulaType = fieldSchema?.formulaType
128
+ filter.constraints = fieldSchema?.constraints
128
129
 
129
130
  // Update external type based on field
130
131
  filter.externalType = getSchema(filter)?.externalType
@@ -281,7 +282,7 @@
281
282
  timeOnly={getSchema(filter)?.timeOnly}
282
283
  bind:value={filter.value}
283
284
  />
284
- {:else if filter.type === FieldType.BB_REFERENCE}
285
+ {:else if [FieldType.BB_REFERENCE, FieldType.BB_REFERENCE_SINGLE].includes(filter.type)}
285
286
  <FilterUsers
286
287
  bind:value={filter.value}
287
288
  multiselect={[
@@ -1,19 +1,27 @@
1
1
  <script>
2
2
  import { getContext } from "svelte"
3
+ import { helpers } from "@budibase/shared-core"
3
4
  import RelationshipCell from "./RelationshipCell.svelte"
4
- import { BBReferenceFieldSubType, RelationshipType } from "@budibase/types"
5
+ import {
6
+ BBReferenceFieldSubType,
7
+ FieldType,
8
+ RelationshipType,
9
+ } from "@budibase/types"
5
10
 
6
11
  export let api
12
+ export let hideCounter = false
13
+ export let schema
7
14
 
8
15
  const { API } = getContext("grid")
9
- const { subtype } = $$props.schema
16
+ const { type, subtype } = schema
10
17
 
11
- const schema = {
18
+ $: schema = {
12
19
  ...$$props.schema,
13
20
  // This is not really used, just adding some content to be able to render the relationship cell
14
21
  tableId: "external",
15
22
  relationshipType:
16
- subtype === BBReferenceFieldSubType.USER
23
+ type === FieldType.BB_REFERENCE_SINGLE ||
24
+ helpers.schema.isDeprecatedSingleUserColumn(schema)
17
25
  ? RelationshipType.ONE_TO_MANY
18
26
  : RelationshipType.MANY_TO_MANY,
19
27
  }
@@ -44,8 +52,9 @@
44
52
 
45
53
  <RelationshipCell
46
54
  bind:api
47
- {...$$props}
55
+ {...$$restProps}
48
56
  {schema}
49
57
  {searchFunction}
50
58
  primaryDisplay={"email"}
59
+ {hideCounter}
51
60
  />
@@ -0,0 +1,22 @@
1
+ <script>
2
+ import BbReferenceCell from "./BBReferenceCell.svelte"
3
+
4
+ export let value
5
+ export let onChange
6
+ export let api
7
+
8
+ $: arrayValue = (!Array.isArray(value) && value ? [value] : value) || []
9
+
10
+ $: onValueChange = value => {
11
+ value = value[0] || null
12
+ onChange(value)
13
+ }
14
+ </script>
15
+
16
+ <BbReferenceCell
17
+ bind:api
18
+ {...$$restProps}
19
+ value={arrayValue}
20
+ onChange={onValueChange}
21
+ hideCounter={true}
22
+ />
@@ -17,6 +17,7 @@
17
17
  export let contentLines = 1
18
18
  export let searchFunction = API.searchTable
19
19
  export let primaryDisplay
20
+ export let hideCounter = false
20
21
 
21
22
  const color = getColor(0)
22
23
 
@@ -263,7 +264,7 @@
263
264
  </div>
264
265
  {/if}
265
266
  </div>
266
- {#if value?.length}
267
+ {#if !hideCounter && value?.length}
267
268
  <div class="count">
268
269
  {value?.length || 0}
269
270
  </div>
@@ -7,11 +7,6 @@
7
7
  } from "@budibase/bbui"
8
8
  import { getContext } from "svelte"
9
9
  import { ValidColumnNameRegex } from "@budibase/shared-core"
10
- import {
11
- BBReferenceFieldSubType,
12
- FieldType,
13
- RelationshipType,
14
- } from "@budibase/types"
15
10
 
16
11
  const { API, definition, rows } = getContext("grid")
17
12
 
@@ -33,20 +28,11 @@
33
28
  }
34
29
 
35
30
  const migrateUserColumn = async () => {
36
- let subtype = BBReferenceFieldSubType.USERS
37
- if (column.schema.relationshipType === RelationshipType.ONE_TO_MANY) {
38
- subtype = BBReferenceFieldSubType.USER
39
- }
40
-
41
31
  try {
42
32
  await API.migrateColumn({
43
33
  tableId: $definition._id,
44
- oldColumn: column.schema,
45
- newColumn: {
46
- name: newColumnName,
47
- type: FieldType.BB_REFERENCE,
48
- subtype,
49
- },
34
+ oldColumn: column.schema.name,
35
+ newColumn: newColumnName,
50
36
  })
51
37
  notifications.success("Column migrated")
52
38
  } catch (e) {
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import { getContext } from "svelte"
3
3
  import DataCell from "../cells/DataCell.svelte"
4
+ import { getCellID } from "../lib/utils"
4
5
 
5
6
  export let row
6
7
  export let top = false
@@ -38,7 +39,7 @@
38
39
  on:click={() => dispatch("rowclick", rows.actions.cleanRow(row))}
39
40
  >
40
41
  {#each $visibleColumns as column, columnIdx}
41
- {@const cellId = `${row._id}-${column.name}`}
42
+ {@const cellId = getCellID(row._id, column.name)}
42
43
  <DataCell
43
44
  {cellId}
44
45
  {column}
@@ -7,6 +7,7 @@
7
7
  import { GutterWidth, NewRowID } from "../lib/constants"
8
8
  import GutterCell from "../cells/GutterCell.svelte"
9
9
  import KeyboardShortcut from "./KeyboardShortcut.svelte"
10
+ import { getCellID } from "../lib/utils"
10
11
 
11
12
  const {
12
13
  hoveredRowId,
@@ -70,7 +71,7 @@
70
71
 
71
72
  // Select the first cell if possible
72
73
  if (firstColumn) {
73
- $focusedCellId = `${savedRow._id}-${firstColumn.name}`
74
+ $focusedCellId = getCellID(savedRow._id, firstColumn.name)
74
75
  }
75
76
  }
76
77
  isAdding = false
@@ -118,7 +119,7 @@
118
119
  visible = true
119
120
  $hoveredRowId = NewRowID
120
121
  if (firstColumn) {
121
- $focusedCellId = `${NewRowID}-${firstColumn.name}`
122
+ $focusedCellId = getCellID(NewRowID, firstColumn.name)
122
123
  }
123
124
 
124
125
  // Attach key listener
@@ -194,7 +195,7 @@
194
195
  {/if}
195
196
  </GutterCell>
196
197
  {#if $stickyColumn}
197
- {@const cellId = `${NewRowID}-${$stickyColumn.name}`}
198
+ {@const cellId = getCellID(NewRowID, $stickyColumn.name)}
198
199
  <DataCell
199
200
  {cellId}
200
201
  rowFocused
@@ -8,6 +8,7 @@
8
8
  import { GutterWidth, BlankRowID } from "../lib/constants"
9
9
  import GutterCell from "../cells/GutterCell.svelte"
10
10
  import KeyboardShortcut from "./KeyboardShortcut.svelte"
11
+ import { getCellID } from "../lib/utils"
11
12
 
12
13
  const {
13
14
  rows,
@@ -71,7 +72,7 @@
71
72
  {@const rowSelected = !!$selectedRows[row._id]}
72
73
  {@const rowHovered = $hoveredRowId === row._id}
73
74
  {@const rowFocused = $focusedRow?._id === row._id}
74
- {@const cellId = `${row._id}-${$stickyColumn?.name}`}
75
+ {@const cellId = getCellID(row._id, $stickyColumn?.name)}
75
76
  <div
76
77
  class="row"
77
78
  on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
@@ -13,6 +13,7 @@ import JSONCell from "../cells/JSONCell.svelte"
13
13
  import AttachmentCell from "../cells/AttachmentCell.svelte"
14
14
  import AttachmentSingleCell from "../cells/AttachmentSingleCell.svelte"
15
15
  import BBReferenceCell from "../cells/BBReferenceCell.svelte"
16
+ import BBReferenceSingleCell from "../cells/BBReferenceSingleCell.svelte"
16
17
 
17
18
  const TypeComponentMap = {
18
19
  [FieldType.STRING]: TextCell,
@@ -29,6 +30,7 @@ const TypeComponentMap = {
29
30
  [FieldType.FORMULA]: FormulaCell,
30
31
  [FieldType.JSON]: JSONCell,
31
32
  [FieldType.BB_REFERENCE]: BBReferenceCell,
33
+ [FieldType.BB_REFERENCE_SINGLE]: BBReferenceSingleCell,
32
34
  }
33
35
  export const getCellRenderer = column => {
34
36
  return TypeComponentMap[column?.schema?.type] || TextCell
@@ -1,5 +1,23 @@
1
+ import { helpers } from "@budibase/shared-core"
1
2
  import { TypeIconMap } from "../../../constants"
2
3
 
4
+ // we can't use "-" for joining the ID/field, as this can be present in the ID or column name
5
+ // using something very unusual to avoid this problem
6
+ const JOINING_CHARACTER = "‽‽"
7
+
8
+ export const parseCellID = rowId => {
9
+ if (!rowId) {
10
+ return undefined
11
+ }
12
+ const parts = rowId.split(JOINING_CHARACTER)
13
+ const field = parts.pop()
14
+ return { id: parts.join(JOINING_CHARACTER), field }
15
+ }
16
+
17
+ export const getCellID = (rowId, fieldName) => {
18
+ return `${rowId}${JOINING_CHARACTER}${fieldName}`
19
+ }
20
+
3
21
  export const getColor = (idx, opacity = 0.3) => {
4
22
  if (idx == null || idx === -1) {
5
23
  idx = 0
@@ -11,8 +29,12 @@ export const getColumnIcon = column => {
11
29
  if (column.schema.autocolumn) {
12
30
  return "MagicWand"
13
31
  }
14
- const { type, subtype } = column.schema
15
32
 
33
+ if (helpers.schema.isDeprecatedSingleUserColumn(column.schema)) {
34
+ return "User"
35
+ }
36
+
37
+ const { type, subtype } = column.schema
16
38
  const result =
17
39
  typeof TypeIconMap[type] === "object" && subtype
18
40
  ? TypeIconMap[type][subtype]
@@ -2,6 +2,7 @@
2
2
  import { getContext, onMount } from "svelte"
3
3
  import { debounce } from "../../../utils/utils"
4
4
  import { NewRowID } from "../lib/constants"
5
+ import { getCellID, parseCellID } from "../lib/utils"
5
6
 
6
7
  const {
7
8
  rows,
@@ -154,7 +155,7 @@
154
155
  if (!firstColumn) {
155
156
  return
156
157
  }
157
- focusedCellId.set(`${firstRow._id}-${firstColumn.name}`)
158
+ focusedCellId.set(getCellID(firstRow._id, firstColumn.name))
158
159
  }
159
160
 
160
161
  // Changes the focused cell by moving it left or right to a different column
@@ -163,8 +164,7 @@
163
164
  return
164
165
  }
165
166
  const cols = $visibleColumns
166
- const split = $focusedCellId.split("-")
167
- const columnName = split[1]
167
+ const { id, field: columnName } = parseCellID($focusedCellId)
168
168
  let newColumnName
169
169
  if (columnName === $stickyColumn?.name) {
170
170
  const index = delta - 1
@@ -178,7 +178,7 @@
178
178
  }
179
179
  }
180
180
  if (newColumnName) {
181
- $focusedCellId = `${split[0]}-${newColumnName}`
181
+ $focusedCellId = getCellID(id, newColumnName)
182
182
  }
183
183
  }
184
184
 
@@ -189,8 +189,8 @@
189
189
  }
190
190
  const newRow = $rows[$focusedRow.__idx + delta]
191
191
  if (newRow) {
192
- const split = $focusedCellId.split("-")
193
- $focusedCellId = `${newRow._id}-${split[1]}`
192
+ const { field } = parseCellID($focusedCellId)
193
+ $focusedCellId = getCellID(newRow._id, field)
194
194
  }
195
195
  }
196
196
 
@@ -3,6 +3,7 @@
3
3
  import { getContext } from "svelte"
4
4
  import { NewRowID } from "../lib/constants"
5
5
  import GridPopover from "./GridPopover.svelte"
6
+ import { getCellID } from "../lib/utils"
6
7
 
7
8
  const {
8
9
  focusedRow,
@@ -41,7 +42,7 @@
41
42
  const newRow = await rows.actions.duplicateRow($focusedRow)
42
43
  if (newRow) {
43
44
  const column = $stickyColumn?.name || $columns[0].name
44
- $focusedCellId = `${newRow._id}-${column}`
45
+ $focusedCellId = getCellID(newRow._id, column)
45
46
  }
46
47
  }
47
48
 
@@ -1,6 +1,7 @@
1
1
  import { writable, derived, get } from "svelte/store"
2
2
  import { fetchData } from "../../../fetch"
3
3
  import { NewRowID, RowPageSize } from "../lib/constants"
4
+ import { getCellID, parseCellID } from "../lib/utils"
4
5
  import { tick } from "svelte"
5
6
  import { Helpers } from "@budibase/bbui"
6
7
 
@@ -206,7 +207,7 @@ export const createActions = context => {
206
207
  // If the server doesn't reply with a valid error, assume that the source
207
208
  // of the error is the focused cell's column
208
209
  if (!error?.json?.validationErrors && errorString) {
209
- const focusedColumn = get(focusedCellId)?.split("-")[1]
210
+ const { field: focusedColumn } = parseCellID(get(focusedCellId))
210
211
  if (focusedColumn) {
211
212
  error = {
212
213
  json: {
@@ -245,7 +246,7 @@ export const createActions = context => {
245
246
  }
246
247
  // Set error against the cell
247
248
  validation.actions.setError(
248
- `${rowId}-${column}`,
249
+ getCellID(rowId, column),
249
250
  Helpers.capitalise(err)
250
251
  )
251
252
  // Ensure the column is visible
@@ -265,7 +266,7 @@ export const createActions = context => {
265
266
 
266
267
  // Focus the first cell with an error
267
268
  if (erroredColumns.length) {
268
- focusedCellId.set(`${rowId}-${erroredColumns[0]}`)
269
+ focusedCellId.set(getCellID(rowId, erroredColumns[0]))
269
270
  }
270
271
  } else {
271
272
  get(notifications).error(errorString || "An unknown error occurred")
@@ -571,9 +572,10 @@ export const initialise = context => {
571
572
  return
572
573
  }
573
574
  // Stop if we changed row
574
- const oldRowId = id.split("-")[0]
575
- const oldColumn = id.split("-")[1]
576
- const newRowId = get(focusedCellId)?.split("-")[0]
575
+ const split = parseCellID(id)
576
+ const oldRowId = split.id
577
+ const oldColumn = split.field
578
+ const { id: newRowId } = parseCellID(get(focusedCellId))
577
579
  if (oldRowId !== newRowId) {
578
580
  return
579
581
  }
@@ -1,6 +1,7 @@
1
1
  import { writable, derived, get } from "svelte/store"
2
2
  import { tick } from "svelte"
3
3
  import { Padding, GutterWidth, FocusedCellMinOffset } from "../lib/constants"
4
+ import { parseCellID } from "../lib/utils"
4
5
 
5
6
  export const createStores = () => {
6
7
  const scroll = writable({
@@ -176,7 +177,7 @@ export const initialise = context => {
176
177
  // Ensure horizontal position is viewable
177
178
  // Check horizontal position of columns next
178
179
  const $visibleColumns = get(visibleColumns)
179
- const columnName = $focusedCellId?.split("-")[1]
180
+ const { field: columnName } = parseCellID($focusedCellId)
180
181
  const column = $visibleColumns.find(col => col.name === columnName)
181
182
  if (!column) {
182
183
  return
@@ -7,6 +7,7 @@ import {
7
7
  MediumRowHeight,
8
8
  NewRowID,
9
9
  } from "../lib/constants"
10
+ import { parseCellID } from "../lib/utils"
10
11
 
11
12
  export const createStores = context => {
12
13
  const { props } = context
@@ -25,7 +26,7 @@ export const createStores = context => {
25
26
  const focusedRowId = derived(
26
27
  focusedCellId,
27
28
  $focusedCellId => {
28
- return $focusedCellId?.split("-")[0]
29
+ return parseCellID($focusedCellId)?.id
29
30
  },
30
31
  null
31
32
  )
@@ -72,7 +73,7 @@ export const deriveStores = context => {
72
73
  const focusedRow = derived(
73
74
  [focusedCellId, rowLookupMap, rows],
74
75
  ([$focusedCellId, $rowLookupMap, $rows]) => {
75
- const rowId = $focusedCellId?.split("-")[0]
76
+ const rowId = parseCellID($focusedCellId)?.id
76
77
 
77
78
  // Edge case for new rows
78
79
  if (rowId === NewRowID) {
@@ -152,7 +153,7 @@ export const initialise = context => {
152
153
  const hasRow = rows.actions.hasRow
153
154
 
154
155
  // Check selected cell
155
- const selectedRowId = $focusedCellId?.split("-")[0]
156
+ const selectedRowId = parseCellID($focusedCellId)?.id
156
157
  if (selectedRowId && !hasRow(selectedRowId)) {
157
158
  focusedCellId.set(null)
158
159
  }
@@ -1,4 +1,5 @@
1
1
  import { writable, get, derived } from "svelte/store"
2
+ import { getCellID, parseCellID } from "../lib/utils"
2
3
 
3
4
  // Normally we would break out actions into the explicit "createActions"
4
5
  // function, but for validation all these actions are pure so can go into
@@ -12,7 +13,7 @@ export const createStores = () => {
12
13
  Object.entries($validation).forEach(([key, error]) => {
13
14
  // Extract row ID from all errored cell IDs
14
15
  if (error) {
15
- map[key.split("-")[0]] = true
16
+ map[parseCellID(key).id] = true
16
17
  }
17
18
  })
18
19
  return map
@@ -53,10 +54,10 @@ export const initialise = context => {
53
54
  const $stickyColumn = get(stickyColumn)
54
55
  validation.update(state => {
55
56
  $columns.forEach(column => {
56
- state[`${id}-${column.name}`] = null
57
+ state[getCellID(id, column.name)] = null
57
58
  })
58
59
  if ($stickyColumn) {
59
- state[`${id}-${$stickyColumn.name}`] = null
60
+ state[getCellID(id, stickyColumn.name)] = null
60
61
  }
61
62
  return state
62
63
  })
package/src/constants.js CHANGED
@@ -132,10 +132,11 @@ export const TypeIconMap = {
132
132
  [FieldType.JSON]: "Brackets",
133
133
  [FieldType.BIGINT]: "TagBold",
134
134
  [FieldType.AUTO]: "MagicWand",
135
- [FieldType.USER]: "User",
136
- [FieldType.USERS]: "UserGroup",
137
135
  [FieldType.BB_REFERENCE]: {
138
- [BBReferenceFieldSubType.USER]: "User",
136
+ [BBReferenceFieldSubType.USER]: "UserGroup",
139
137
  [BBReferenceFieldSubType.USERS]: "UserGroup",
140
138
  },
139
+ [FieldType.BB_REFERENCE_SINGLE]: {
140
+ [BBReferenceFieldSubType.USER]: "User",
141
+ },
141
142
  }