@budibase/frontend-core 2.5.6-alpha.34 → 2.5.6-alpha.36

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.5.6-alpha.34",
3
+ "version": "2.5.6-alpha.36",
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.5.6-alpha.34",
10
- "@budibase/shared-core": "2.5.6-alpha.34",
9
+ "@budibase/bbui": "2.5.6-alpha.36",
10
+ "@budibase/shared-core": "2.5.6-alpha.36",
11
11
  "dayjs": "^1.11.7",
12
12
  "lodash": "^4.17.21",
13
13
  "socket.io-client": "^4.6.1",
14
14
  "svelte": "^3.46.2"
15
15
  },
16
- "gitHead": "7968ddb9253cdc7176f68a50923262f47711811d"
16
+ "gitHead": "6169dc58d2c2e62f2af975cd17f6c97e8366d422"
17
17
  }
@@ -19,6 +19,7 @@
19
19
  export let updateValue = rows.actions.updateValue
20
20
  export let invertX = false
21
21
  export let invertY = false
22
+ export let contentLines = 1
22
23
 
23
24
  const emptyError = writable(null)
24
25
 
@@ -84,5 +85,7 @@
84
85
  {readonly}
85
86
  {invertY}
86
87
  {invertX}
88
+ {contentLines}
87
89
  />
90
+ <slot />
88
91
  </GridCell>
@@ -117,6 +117,9 @@
117
117
  .cell.error {
118
118
  --cell-color: var(--spectrum-global-color-red-500);
119
119
  }
120
+ .cell.readonly {
121
+ --cell-color: var(--spectrum-global-color-gray-600);
122
+ }
120
123
  .cell:not(.focused) {
121
124
  user-select: none;
122
125
  }
@@ -37,6 +37,8 @@
37
37
  $: sortedBy = column.name === $sort.column
38
38
  $: canMoveLeft = orderable && idx > 0
39
39
  $: canMoveRight = orderable && idx < $renderedColumns.length - 1
40
+ $: ascendingLabel = column.schema?.type === "number" ? "low-high" : "A-Z"
41
+ $: descendingLabel = column.schema?.type === "number" ? "high-low" : "Z-A"
40
42
 
41
43
  const editColumn = () => {
42
44
  dispatch("edit-column", column.schema)
@@ -179,14 +181,14 @@
179
181
  on:click={sortAscending}
180
182
  disabled={column.name === $sort.column && $sort.order === "ascending"}
181
183
  >
182
- Sort A-Z
184
+ Sort {ascendingLabel}
183
185
  </MenuItem>
184
186
  <MenuItem
185
187
  icon="SortOrderDown"
186
188
  on:click={sortDescending}
187
189
  disabled={column.name === $sort.column && $sort.order === "descending"}
188
190
  >
189
- Sort Z-A
191
+ Sort {descendingLabel}
190
192
  </MenuItem>
191
193
  <MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
192
194
  Move left
@@ -12,6 +12,7 @@
12
12
  export let api
13
13
  export let invertX = false
14
14
  export let invertY = false
15
+ export let contentLines = 1
15
16
 
16
17
  let isOpen = false
17
18
  let focusedOptionIdx = null
@@ -86,7 +87,11 @@
86
87
  class:open
87
88
  on:click|self={editable ? open : null}
88
89
  >
89
- <div class="values" on:click={editable ? open : null}>
90
+ <div
91
+ class="values"
92
+ class:wrap={contentLines > 1}
93
+ on:click={editable ? open : null}
94
+ >
90
95
  {#each values as val}
91
96
  {@const color = getOptionColor(val)}
92
97
  {#if color}
@@ -160,6 +165,9 @@
160
165
  grid-row-gap: var(--cell-padding);
161
166
  overflow: hidden;
162
167
  padding: var(--cell-padding);
168
+ flex-wrap: nowrap;
169
+ }
170
+ .values.wrap {
163
171
  flex-wrap: wrap;
164
172
  }
165
173
  .text {
@@ -29,6 +29,7 @@
29
29
  export let onChange
30
30
  export let invertX = false
31
31
  export let invertY = false
32
+ export let contentLines = 1
32
33
 
33
34
  const { API, dispatch } = getContext("grid")
34
35
  const color = getColor(0)
@@ -243,7 +244,11 @@
243
244
 
244
245
  <div class="wrapper" class:editable class:focused style="--color:{color};">
245
246
  <div class="container">
246
- <div class="values" on:wheel={e => (focused ? e.stopPropagation() : null)}>
247
+ <div
248
+ class="values"
249
+ class:wrap={editable || contentLines > 1}
250
+ on:wheel={e => (focused ? e.stopPropagation() : null)}
251
+ >
247
252
  {#each value || [] as relationship, idx}
248
253
  {#if relationship.primaryDisplay}
249
254
  <div class="badge">
@@ -376,6 +381,9 @@
376
381
  grid-row-gap: var(--cell-padding);
377
382
  overflow: hidden;
378
383
  padding: var(--cell-padding);
384
+ flex-wrap: nowrap;
385
+ }
386
+ .values.wrap {
379
387
  flex-wrap: wrap;
380
388
  }
381
389
  .count {
@@ -3,16 +3,13 @@
3
3
  import { ActionButton, Popover, Select } from "@budibase/bbui"
4
4
 
5
5
  const { sort, columns, stickyColumn } = getContext("grid")
6
- const orderOptions = [
7
- { label: "A-Z", value: "ascending" },
8
- { label: "Z-A", value: "descending" },
9
- ]
10
6
 
11
7
  let open = false
12
8
  let anchor
13
9
 
14
10
  $: columnOptions = getColumnOptions($stickyColumn, $columns)
15
11
  $: checkValidSortColumn($sort.column, $stickyColumn, $columns)
12
+ $: orderOptions = getOrderOptions($sort.column, columnOptions)
16
13
 
17
14
  const getColumnOptions = (stickyColumn, columns) => {
18
15
  let options = []
@@ -20,6 +17,7 @@
20
17
  options.push({
21
18
  label: stickyColumn.label || stickyColumn.name,
22
19
  value: stickyColumn.name,
20
+ type: stickyColumn.schema?.type,
23
21
  })
24
22
  }
25
23
  return [
@@ -27,10 +25,25 @@
27
25
  ...columns.map(col => ({
28
26
  label: col.label || col.name,
29
27
  value: col.name,
28
+ type: col.schema?.type,
30
29
  })),
31
30
  ]
32
31
  }
33
32
 
33
+ const getOrderOptions = (column, columnOptions) => {
34
+ const type = columnOptions.find(col => col.value === column)?.type
35
+ return [
36
+ {
37
+ label: type === "number" ? "Low-high" : "A-Z",
38
+ value: "ascending",
39
+ },
40
+ {
41
+ label: type === "number" ? "High-low" : "Z-A",
42
+ value: "descending",
43
+ },
44
+ ]
45
+ }
46
+
34
47
  const updateSortColumn = e => {
35
48
  sort.update(state => ({
36
49
  ...state,
@@ -15,6 +15,7 @@
15
15
  selectedCellMap,
16
16
  focusedRow,
17
17
  columnHorizontalInversionIndex,
18
+ contentLines,
18
19
  } = getContext("grid")
19
20
 
20
21
  $: rowSelected = !!$selectedRows[row._id]
@@ -44,6 +45,7 @@
44
45
  focused={$focusedCellId === cellId}
45
46
  selectedUser={$selectedCellMap[cellId]}
46
47
  width={column.width}
48
+ contentLines={$contentLines}
47
49
  />
48
50
  {/each}
49
51
  </div>
@@ -0,0 +1,53 @@
1
+ <script>
2
+ export let keybind
3
+ export let padded = false
4
+ export let overlay = false
5
+
6
+ $: parsedKeys = parseKeys(keybind)
7
+
8
+ const parseKeys = keybind => {
9
+ return keybind?.split("+").map(key => {
10
+ if (key.toLowerCase() === "ctrl") {
11
+ return navigator.platform.startsWith("Mac") ? "⌘" : key
12
+ } else if (key.toLowerCase() === "enter") {
13
+ return "↵"
14
+ }
15
+ return key
16
+ })
17
+ }
18
+ </script>
19
+
20
+ <div class="keys" class:padded class:overlay>
21
+ {#each parsedKeys as key}
22
+ <div class="key">
23
+ {key}
24
+ </div>
25
+ {/each}
26
+ </div>
27
+
28
+ <style>
29
+ .keys {
30
+ display: flex;
31
+ flex-direction: row;
32
+ gap: 3px;
33
+ }
34
+ .keys.padded {
35
+ padding: var(--cell-padding);
36
+ }
37
+ .key {
38
+ padding: 2px 6px;
39
+ font-size: 12px;
40
+ font-weight: 600;
41
+ background-color: var(--spectrum-global-color-gray-200);
42
+ color: var(--spectrum-global-color-gray-700);
43
+ border-radius: 4px;
44
+ text-align: center;
45
+ display: grid;
46
+ place-items: center;
47
+ text-transform: uppercase;
48
+ }
49
+ .overlay .key {
50
+ background: rgba(255, 255, 255, 0.2);
51
+ color: #eee;
52
+ }
53
+ </style>
@@ -7,6 +7,7 @@
7
7
  import { GutterWidth } from "../lib/constants"
8
8
  import { NewRowID } from "../lib/constants"
9
9
  import GutterCell from "../cells/GutterCell.svelte"
10
+ import KeyboardShortcut from "./KeyboardShortcut.svelte"
10
11
 
11
12
  const {
12
13
  hoveredRowId,
@@ -27,13 +28,14 @@
27
28
  columnHorizontalInversionIndex,
28
29
  } = getContext("grid")
29
30
 
31
+ let visible = false
30
32
  let isAdding = false
31
33
  let newRow = {}
32
34
  let offset = 0
33
35
 
34
36
  $: firstColumn = $stickyColumn || $renderedColumns[0]
35
37
  $: width = GutterWidth + ($stickyColumn?.width || 0)
36
- $: $tableId, (isAdding = false)
38
+ $: $tableId, (visible = false)
37
39
  $: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
38
40
 
39
41
  const shouldInvertY = (offset, inversionIndex, rows) => {
@@ -45,7 +47,8 @@
45
47
 
46
48
  const addRow = async () => {
47
49
  // Blur the active cell and tick to let final value updates propagate
48
- $focusedCellAPI?.blur()
50
+ isAdding = true
51
+ $focusedCellId = null
49
52
  await tick()
50
53
 
51
54
  // Create row
@@ -60,17 +63,19 @@
60
63
  $focusedCellId = `${savedRow._id}-${firstColumn.name}`
61
64
  }
62
65
  }
66
+ isAdding = false
63
67
  }
64
68
 
65
69
  const clear = () => {
66
70
  isAdding = false
71
+ visible = false
67
72
  $focusedCellId = null
68
73
  $hoveredRowId = null
69
74
  document.removeEventListener("keydown", handleKeyPress)
70
75
  }
71
76
 
72
77
  const startAdding = async () => {
73
- if (isAdding) {
78
+ if (visible) {
74
79
  return
75
80
  }
76
81
 
@@ -95,7 +100,7 @@
95
100
 
96
101
  // Update state and select initial cell
97
102
  newRow = {}
98
- isAdding = true
103
+ visible = true
99
104
  $hoveredRowId = NewRowID
100
105
  if (firstColumn) {
101
106
  $focusedCellId = `${NewRowID}-${firstColumn.name}`
@@ -115,7 +120,7 @@
115
120
  }
116
121
 
117
122
  const handleKeyPress = e => {
118
- if (!isAdding) {
123
+ if (!visible) {
119
124
  return
120
125
  }
121
126
  if (e.key === "Escape") {
@@ -137,7 +142,7 @@
137
142
  </script>
138
143
 
139
144
  <!-- Only show new row functionality if we have any columns -->
140
- {#if isAdding}
145
+ {#if visible}
141
146
  <div
142
147
  class="container"
143
148
  class:floating={offset > 0}
@@ -148,6 +153,9 @@
148
153
  <div class="sticky-column" transition:fade={{ duration: 130 }}>
149
154
  <GutterCell on:expand={addViaModal} rowHovered>
150
155
  <Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
156
+ {#if isAdding}
157
+ <div in:fade={{ duration: 130 }} class="loading-overlay" />
158
+ {/if}
151
159
  </GutterCell>
152
160
  {#if $stickyColumn}
153
161
  {@const cellId = `${NewRowID}-${$stickyColumn.name}`}
@@ -161,7 +169,14 @@
161
169
  {updateValue}
162
170
  rowIdx={0}
163
171
  {invertY}
164
- />
172
+ >
173
+ {#if $stickyColumn?.schema?.autocolumn}
174
+ <div class="readonly-overlay">Can't edit auto column</div>
175
+ {/if}
176
+ {#if isAdding}
177
+ <div in:fade={{ duration: 130 }} class="loading-overlay" />
178
+ {/if}
179
+ </DataCell>
165
180
  {/if}
166
181
  </div>
167
182
  <div class="normal-columns" transition:fade={{ duration: 130 }}>
@@ -181,15 +196,32 @@
181
196
  rowIdx={0}
182
197
  invertX={columnIdx >= $columnHorizontalInversionIndex}
183
198
  {invertY}
184
- />
199
+ >
200
+ {#if column?.schema?.autocolumn}
201
+ <div class="readonly-overlay">Can't edit auto column</div>
202
+ {/if}
203
+ {#if isAdding}
204
+ <div in:fade={{ duration: 130 }} class="loading-overlay" />
205
+ {/if}
206
+ </DataCell>
185
207
  {/key}
186
208
  {/each}
187
209
  </div>
188
210
  </GridScrollWrapper>
189
211
  </div>
190
212
  <div class="buttons" transition:fade={{ duration: 130 }}>
191
- <Button size="M" cta on:click={addRow}>Save</Button>
192
- <Button size="M" secondary newStyles on:click={clear}>Cancel</Button>
213
+ <Button size="M" cta on:click={addRow} disabled={isAdding}>
214
+ <div class="button-with-keys">
215
+ Save
216
+ <KeyboardShortcut overlay keybind="Ctrl+Enter" />
217
+ </div>
218
+ </Button>
219
+ <Button size="M" secondary newStyles on:click={clear}>
220
+ <div class="button-with-keys">
221
+ Cancel
222
+ <KeyboardShortcut overlay keybind="Esc" />
223
+ </div>
224
+ </Button>
193
225
  </div>
194
226
  </div>
195
227
  {/if}
@@ -240,6 +272,14 @@
240
272
  top: calc(var(--row-height) + var(--offset) + 24px);
241
273
  left: var(--gutter-width);
242
274
  }
275
+ .button-with-keys {
276
+ display: flex;
277
+ gap: 6px;
278
+ align-items: center;
279
+ }
280
+ .button-with-keys :global(> div) {
281
+ padding-top: 2px;
282
+ }
243
283
 
244
284
  /* Sticky column styles */
245
285
  .sticky-column {
@@ -262,4 +302,33 @@
262
302
  width: 0;
263
303
  display: flex;
264
304
  }
305
+
306
+ /* Readonly cell overlay */
307
+ .readonly-overlay {
308
+ position: absolute;
309
+ top: 0;
310
+ left: 0;
311
+ height: var(--row-height);
312
+ width: 100%;
313
+ padding: var(--cell-padding);
314
+ font-style: italic;
315
+ color: var(--spectrum-global-color-gray-600);
316
+ z-index: 1;
317
+ user-select: none;
318
+ overflow: hidden;
319
+ white-space: nowrap;
320
+ text-overflow: ellipsis;
321
+ }
322
+
323
+ /* Overlay while row is being added */
324
+ .loading-overlay {
325
+ position: absolute;
326
+ top: 0;
327
+ left: 0;
328
+ height: var(--row-height);
329
+ width: 100%;
330
+ z-index: 1;
331
+ background: var(--spectrum-global-color-gray-400);
332
+ opacity: 0.25;
333
+ }
265
334
  </style>
@@ -7,6 +7,7 @@
7
7
  import HeaderCell from "../cells/HeaderCell.svelte"
8
8
  import { GutterWidth, BlankRowID } from "../lib/constants"
9
9
  import GutterCell from "../cells/GutterCell.svelte"
10
+ import KeyboardShortcut from "./KeyboardShortcut.svelte"
10
11
 
11
12
  const {
12
13
  rows,
@@ -21,6 +22,7 @@
21
22
  focusedRow,
22
23
  scrollLeft,
23
24
  dispatch,
25
+ contentLines,
24
26
  } = getContext("grid")
25
27
 
26
28
  $: rowCount = $rows.length
@@ -85,6 +87,7 @@
85
87
  selectedUser={$selectedCellMap[cellId]}
86
88
  width={$stickyColumn.width}
87
89
  column={$stickyColumn}
90
+ contentLines={$contentLines}
88
91
  />
89
92
  {/if}
90
93
  </div>
@@ -103,7 +106,9 @@
103
106
  <GridCell
104
107
  width={$stickyColumn.width}
105
108
  highlighted={$hoveredRowId === BlankRowID}
106
- />
109
+ >
110
+ <KeyboardShortcut padded keybind="Ctrl+Enter" />
111
+ </GridCell>
107
112
  {/if}
108
113
  </div>
109
114
  {/if}
@@ -15,8 +15,22 @@
15
15
  selectedRows,
16
16
  } = getContext("grid")
17
17
 
18
+ const ignoredOriginSelectors = [
19
+ ".spectrum-Modal",
20
+ "#builder-side-panel-container",
21
+ ]
22
+
18
23
  // Global key listener which intercepts all key events
19
24
  const handleKeyDown = e => {
25
+ // Avoid processing events sourced from certain origins
26
+ if (e.target?.closest) {
27
+ for (let selector of ignoredOriginSelectors) {
28
+ if (e.target.closest(selector)) {
29
+ return
30
+ }
31
+ }
32
+ }
33
+
20
34
  // If nothing selected avoid processing further key presses
21
35
  if (!$focusedCellId) {
22
36
  if (e.key === "Tab" || e.key?.startsWith("Arrow")) {
@@ -60,11 +74,6 @@
60
74
  return
61
75
  }
62
76
  }
63
-
64
- // Avoid processing events sourced from modals
65
- if (e.target?.closest?.(".spectrum-Modal")) {
66
- return
67
- }
68
77
  e.preventDefault()
69
78
 
70
79
  // Handle the key ourselves