@budibase/frontend-core 2.6.19-alpha.12 → 2.6.19-alpha.14

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.6.19-alpha.12",
3
+ "version": "2.6.19-alpha.14",
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.6.19-alpha.12",
10
- "@budibase/shared-core": "2.6.19-alpha.12",
9
+ "@budibase/bbui": "2.6.19-alpha.14",
10
+ "@budibase/shared-core": "2.6.19-alpha.14",
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": "29935057b058f97e80a208b0cfdcf65b0ce150c2"
16
+ "gitHead": "9e6f7b9f573647eb08850781396dc0d4b874a4b5"
17
17
  }
@@ -58,4 +58,15 @@ export const buildDatasourceEndpoints = API => ({
58
58
  url: `/api/datasources/${datasourceId}/${datasourceRev}`,
59
59
  })
60
60
  },
61
+
62
+ /**
63
+ * Validate a datasource configuration
64
+ * @param datasource the datasource configuration to validate
65
+ */
66
+ validateDatasource: async datasource => {
67
+ return await API.post({
68
+ url: `/api/datasources/verify`,
69
+ body: { datasource },
70
+ })
71
+ },
61
72
  })
@@ -37,6 +37,9 @@
37
37
  .boolean-cell {
38
38
  padding: 2px var(--cell-padding);
39
39
  pointer-events: none;
40
+ flex: 1 1 auto;
41
+ display: flex;
42
+ justify-content: center;
40
43
  }
41
44
  .boolean-cell.editable {
42
45
  pointer-events: all;
@@ -11,6 +11,7 @@
11
11
  export let selected
12
12
  export let rowFocused
13
13
  export let rowIdx
14
+ export let topRow = false
14
15
  export let focused
15
16
  export let selectedUser
16
17
  export let column
@@ -68,6 +69,7 @@
68
69
  {highlighted}
69
70
  {selected}
70
71
  {rowIdx}
72
+ {topRow}
71
73
  {focused}
72
74
  {selectedUser}
73
75
  {readonly}
@@ -6,6 +6,7 @@
6
6
  export let selectedUser = null
7
7
  export let error = null
8
8
  export let rowIdx
9
+ export let topRow = false
9
10
  export let defaultHeight = false
10
11
  export let center = false
11
12
  export let readonly = false
@@ -31,13 +32,14 @@
31
32
  class:readonly
32
33
  class:default-height={defaultHeight}
33
34
  class:selected-other={selectedUser != null}
35
+ class:alt={rowIdx % 2 === 1}
36
+ class:top={topRow}
34
37
  on:focus
35
38
  on:mousedown
36
39
  on:mouseup
37
40
  on:click
38
41
  on:contextmenu
39
42
  {style}
40
- data-row={rowIdx}
41
43
  >
42
44
  {#if error}
43
45
  <div class="label">
@@ -70,6 +72,9 @@
70
72
  width: 0;
71
73
  --cell-color: transparent;
72
74
  }
75
+ .cell.alt {
76
+ --cell-background: var(--cell-background-alt);
77
+ }
73
78
  .cell.default-height {
74
79
  height: var(--default-row-height);
75
80
  }
@@ -98,8 +103,8 @@
98
103
  .cell.selected-other:not(.focused):after {
99
104
  border-radius: 0 2px 2px 2px;
100
105
  }
101
- .cell[data-row="0"].error:after,
102
- .cell[data-row="0"].selected-other:not(.focused):after {
106
+ .cell.top.error:after,
107
+ .cell.top.selected-other:not(.focused):after {
103
108
  border-radius: 2px 2px 2px 0;
104
109
  }
105
110
 
@@ -152,7 +157,7 @@
152
157
  overflow: hidden;
153
158
  user-select: none;
154
159
  }
155
- .cell[data-row="0"] .label {
160
+ .cell.top .label {
156
161
  bottom: auto;
157
162
  top: 100%;
158
163
  border-radius: 0 2px 2px 2px;
@@ -21,16 +21,7 @@
21
21
  svelteDispatch("select")
22
22
  const id = row?._id
23
23
  if (id) {
24
- selectedRows.update(state => {
25
- let newState = {
26
- ...state,
27
- [id]: !state[id],
28
- }
29
- if (!newState[id]) {
30
- delete newState[id]
31
- }
32
- return newState
33
- })
24
+ selectedRows.actions.toggleRow(id)
34
25
  }
35
26
  }
36
27
 
@@ -47,6 +38,7 @@
47
38
  highlighted={rowFocused || rowHovered}
48
39
  selected={rowSelected}
49
40
  {defaultHeight}
41
+ rowIdx={row?.__idx}
50
42
  >
51
43
  <div class="gutter">
52
44
  {#if $$slots.default}
@@ -196,7 +196,11 @@
196
196
  <MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}>
197
197
  Move right
198
198
  </MenuItem>
199
- <MenuItem icon="VisibilityOff" on:click={hideColumn}>Hide column</MenuItem>
199
+ <MenuItem
200
+ disabled={idx === "sticky"}
201
+ icon="VisibilityOff"
202
+ on:click={hideColumn}>Hide column</MenuItem
203
+ >
200
204
  </Menu>
201
205
  </Popover>
202
206
 
@@ -41,6 +41,7 @@
41
41
  export let allowExpandRows = true
42
42
  export let allowEditRows = true
43
43
  export let allowDeleteRows = true
44
+ export let stripeRows = false
44
45
 
45
46
  // Unique identifier for DOM nodes inside this instance
46
47
  const rand = Math.random()
@@ -55,6 +56,7 @@
55
56
  allowExpandRows,
56
57
  allowEditRows,
57
58
  allowDeleteRows,
59
+ stripeRows,
58
60
  })
59
61
 
60
62
  // Build up context
@@ -90,6 +92,7 @@
90
92
  allowExpandRows,
91
93
  allowEditRows,
92
94
  allowDeleteRows,
95
+ stripeRows,
93
96
  })
94
97
 
95
98
  // Set context for children to consume
@@ -107,6 +110,7 @@
107
110
  id="grid-{rand}"
108
111
  class:is-resizing={$isResizing}
109
112
  class:is-reordering={$isReordering}
113
+ class:stripe={$config.stripeRows}
110
114
  style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
111
115
  >
112
116
  <div class="controls">
@@ -169,6 +173,7 @@
169
173
  /* Variables */
170
174
  --cell-background: var(--spectrum-global-color-gray-50);
171
175
  --cell-background-hover: var(--spectrum-global-color-gray-100);
176
+ --cell-background-alt: var(--cell-background);
172
177
  --cell-padding: 8px;
173
178
  --cell-spacing: 4px;
174
179
  --cell-border: 1px solid var(--spectrum-global-color-gray-200);
@@ -185,6 +190,9 @@
185
190
  .grid.is-reordering :global(*) {
186
191
  cursor: grabbing !important;
187
192
  }
193
+ .grid.stripe {
194
+ --cell-background-alt: var(--spectrum-global-color-gray-75);
195
+ }
188
196
 
189
197
  .grid-data-outer,
190
198
  .grid-data-inner {
@@ -36,7 +36,11 @@
36
36
  <div bind:this={body} class="grid-body">
37
37
  <GridScrollWrapper scrollHorizontally scrollVertically wheelInteractive>
38
38
  {#each $renderedRows as row, idx}
39
- <GridRow {row} {idx} invertY={idx >= $rowVerticalInversionIndex} />
39
+ <GridRow
40
+ {row}
41
+ top={idx === 0}
42
+ invertY={idx >= $rowVerticalInversionIndex}
43
+ />
40
44
  {/each}
41
45
  {#if $config.allowAddRows && $renderedColumns.length}
42
46
  <div
@@ -3,7 +3,7 @@
3
3
  import DataCell from "../cells/DataCell.svelte"
4
4
 
5
5
  export let row
6
- export let idx
6
+ export let top = false
7
7
  export let invertY = false
8
8
 
9
9
  const {
@@ -41,7 +41,8 @@
41
41
  invertX={columnIdx >= $columnHorizontalInversionIndex}
42
42
  highlighted={rowHovered || rowFocused || reorderSource === column.name}
43
43
  selected={rowSelected}
44
- rowIdx={idx}
44
+ rowIdx={row.__idx}
45
+ topRow={top}
45
46
  focused={$focusedCellId === cellId}
46
47
  selectedUser={$selectedCellMap[cellId]}
47
48
  width={column.width}
@@ -61,7 +61,7 @@
61
61
  border-right: var(--cell-border);
62
62
  border-bottom: var(--cell-border);
63
63
  background: var(--spectrum-global-color-gray-100);
64
- z-index: 20;
64
+ z-index: 1;
65
65
  }
66
66
  .add:hover {
67
67
  background: var(--spectrum-global-color-gray-200);
@@ -38,7 +38,7 @@
38
38
  padding: 2px 6px;
39
39
  font-size: 12px;
40
40
  font-weight: 600;
41
- background-color: var(--spectrum-global-color-gray-200);
41
+ background-color: var(--spectrum-global-color-gray-300);
42
42
  color: var(--spectrum-global-color-gray-700);
43
43
  border-radius: 4px;
44
44
  text-align: center;
@@ -167,7 +167,7 @@
167
167
  focused={$focusedCellId === cellId}
168
168
  width={$stickyColumn.width}
169
169
  {updateValue}
170
- rowIdx={0}
170
+ topRow={offset === 0}
171
171
  {invertY}
172
172
  >
173
173
  {#if $stickyColumn?.schema?.autocolumn}
@@ -193,7 +193,7 @@
193
193
  row={newRow}
194
194
  focused={$focusedCellId === cellId}
195
195
  width={column.width}
196
- rowIdx={0}
196
+ topRow={offset === 0}
197
197
  invertX={columnIdx >= $columnHorizontalInversionIndex}
198
198
  {invertY}
199
199
  >
@@ -219,7 +219,7 @@
219
219
  <Button size="M" secondary newStyles on:click={clear}>
220
220
  <div class="button-with-keys">
221
221
  Cancel
222
- <KeyboardShortcut overlay keybind="Esc" />
222
+ <KeyboardShortcut keybind="Esc" />
223
223
  </div>
224
224
  </Button>
225
225
  </div>
@@ -82,7 +82,8 @@
82
82
  {rowFocused}
83
83
  selected={rowSelected}
84
84
  highlighted={rowHovered || rowFocused}
85
- rowIdx={idx}
85
+ rowIdx={row.__idx}
86
+ topRow={idx === 0}
86
87
  focused={$focusedCellId === cellId}
87
88
  selectedUser={$selectedCellMap[cellId]}
88
89
  width={$stickyColumn.width}
@@ -224,10 +224,7 @@
224
224
  if (!id || id === NewRowID) {
225
225
  return
226
226
  }
227
- selectedRows.update(state => {
228
- state[id] = !state[id]
229
- return state
230
- })
227
+ selectedRows.actions.toggleRow(id)
231
228
  }
232
229
 
233
230
  onMount(() => {
@@ -4,9 +4,10 @@ const reorderInitialState = {
4
4
  sourceColumn: null,
5
5
  targetColumn: null,
6
6
  breakpoints: [],
7
- initialMouseX: null,
8
- scrollLeft: 0,
9
7
  gridLeft: 0,
8
+ width: 0,
9
+ latestX: 0,
10
+ increment: 0,
10
11
  }
11
12
 
12
13
  export const createStores = () => {
@@ -23,14 +24,24 @@ export const createStores = () => {
23
24
  }
24
25
 
25
26
  export const deriveStores = context => {
26
- const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } =
27
- context
27
+ const {
28
+ reorder,
29
+ columns,
30
+ visibleColumns,
31
+ scroll,
32
+ bounds,
33
+ stickyColumn,
34
+ ui,
35
+ maxScrollLeft,
36
+ } = context
37
+
38
+ let autoScrollInterval
39
+ let isAutoScrolling
28
40
 
29
41
  // Callback when dragging on a colum header and starting reordering
30
42
  const startReordering = (column, e) => {
31
43
  const $visibleColumns = get(visibleColumns)
32
44
  const $bounds = get(bounds)
33
- const $scroll = get(scroll)
34
45
  const $stickyColumn = get(stickyColumn)
35
46
  ui.actions.blur()
36
47
 
@@ -51,9 +62,8 @@ export const deriveStores = context => {
51
62
  sourceColumn: column,
52
63
  targetColumn: null,
53
64
  breakpoints,
54
- initialMouseX: e.clientX,
55
- scrollLeft: $scroll.left,
56
65
  gridLeft: $bounds.left,
66
+ width: $bounds.width,
57
67
  })
58
68
 
59
69
  // Add listeners to handle mouse movement
@@ -66,12 +76,44 @@ export const deriveStores = context => {
66
76
 
67
77
  // Callback when moving the mouse when reordering columns
68
78
  const onReorderMouseMove = e => {
79
+ // Immediately handle the current position
80
+ const x = e.clientX
81
+ reorder.update(state => ({
82
+ ...state,
83
+ latestX: x,
84
+ }))
85
+ considerReorderPosition()
86
+
87
+ // Check if we need to start auto-scrolling
88
+ const $reorder = get(reorder)
89
+ const proximityCutoff = 140
90
+ const speedFactor = 8
91
+ const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
92
+ const leftProximity = Math.max(0, x - $reorder.gridLeft)
93
+ if (rightProximity < proximityCutoff) {
94
+ const weight = proximityCutoff - rightProximity
95
+ const increment = (weight / proximityCutoff) * speedFactor
96
+ reorder.update(state => ({ ...state, increment }))
97
+ startAutoScroll()
98
+ } else if (leftProximity < proximityCutoff) {
99
+ const weight = -1 * (proximityCutoff - leftProximity)
100
+ const increment = (weight / proximityCutoff) * speedFactor
101
+ reorder.update(state => ({ ...state, increment }))
102
+ startAutoScroll()
103
+ } else {
104
+ stopAutoScroll()
105
+ }
106
+ }
107
+
108
+ // Actual logic to consider the current position and determine the new order
109
+ const considerReorderPosition = () => {
69
110
  const $reorder = get(reorder)
111
+ const $scroll = get(scroll)
70
112
 
71
113
  // Compute the closest breakpoint to the current position
72
114
  let targetColumn
73
115
  let minDistance = Number.MAX_SAFE_INTEGER
74
- const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft
116
+ const mouseX = $reorder.latestX - $reorder.gridLeft + $scroll.left
75
117
  $reorder.breakpoints.forEach(point => {
76
118
  const distance = Math.abs(point.x - mouseX)
77
119
  if (distance < minDistance) {
@@ -79,7 +121,6 @@ export const deriveStores = context => {
79
121
  targetColumn = point.column
80
122
  }
81
123
  })
82
-
83
124
  if (targetColumn !== $reorder.targetColumn) {
84
125
  reorder.update(state => ({
85
126
  ...state,
@@ -88,8 +129,35 @@ export const deriveStores = context => {
88
129
  }
89
130
  }
90
131
 
132
+ // Commences auto-scrolling in a certain direction, triggered when the mouse
133
+ // approaches the edges of the grid
134
+ const startAutoScroll = () => {
135
+ if (isAutoScrolling) {
136
+ return
137
+ }
138
+ isAutoScrolling = true
139
+ autoScrollInterval = setInterval(() => {
140
+ const $maxLeft = get(maxScrollLeft)
141
+ const { increment } = get(reorder)
142
+ scroll.update(state => ({
143
+ ...state,
144
+ left: Math.max(0, Math.min($maxLeft, state.left + increment)),
145
+ }))
146
+ considerReorderPosition()
147
+ }, 10)
148
+ }
149
+
150
+ // Stops auto scrolling
151
+ const stopAutoScroll = () => {
152
+ isAutoScrolling = false
153
+ clearInterval(autoScrollInterval)
154
+ }
155
+
91
156
  // Callback when stopping reordering columns
92
157
  const stopReordering = async () => {
158
+ // Ensure auto-scrolling is stopped
159
+ stopAutoScroll()
160
+
93
161
  // Swap position of columns
94
162
  let { sourceColumn, targetColumn } = get(reorder)
95
163
  moveColumn(sourceColumn, targetColumn)
@@ -25,14 +25,33 @@ export const createStores = () => {
25
25
  null
26
26
  )
27
27
 
28
+ // Toggles whether a certain row ID is selected or not
29
+ const toggleSelectedRow = id => {
30
+ selectedRows.update(state => {
31
+ let newState = {
32
+ ...state,
33
+ [id]: !state[id],
34
+ }
35
+ if (!newState[id]) {
36
+ delete newState[id]
37
+ }
38
+ return newState
39
+ })
40
+ }
41
+
28
42
  return {
29
43
  focusedCellId,
30
44
  focusedCellAPI,
31
45
  focusedRowId,
32
46
  previousFocusedRowId,
33
- selectedRows,
34
47
  hoveredRowId,
35
48
  rowHeight,
49
+ selectedRows: {
50
+ ...selectedRows,
51
+ actions: {
52
+ toggleRow: toggleSelectedRow,
53
+ },
54
+ },
36
55
  }
37
56
  }
38
57