@budibase/frontend-core 2.7.34-alpha.4 → 2.7.34-alpha.7
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 +4 -4
- package/src/api/analytics.js +2 -2
- package/src/api/index.js +16 -2
- package/src/api/rows.js +3 -1
- package/src/components/grid/cells/AttachmentCell.svelte +3 -1
- package/src/components/grid/cells/GridCell.svelte +1 -1
- package/src/components/grid/cells/GutterCell.svelte +9 -14
- package/src/components/grid/cells/HeaderCell.svelte +7 -5
- package/src/components/grid/cells/LongFormCell.svelte +1 -1
- package/src/components/grid/cells/OptionsCell.svelte +4 -5
- package/src/components/grid/cells/RelationshipCell.svelte +22 -11
- package/src/components/grid/controls/AddColumnButton.svelte +1 -1
- package/src/components/grid/controls/BulkDeleteHandler.svelte +2 -8
- package/src/components/grid/controls/HideColumnsButton.svelte +9 -4
- package/src/components/grid/controls/SizeButton.svelte +135 -0
- package/src/components/grid/controls/SortButton.svelte +3 -4
- package/src/components/grid/layout/Grid.svelte +66 -59
- package/src/components/grid/layout/GridBody.svelte +3 -2
- package/src/components/grid/layout/GridRow.svelte +3 -2
- package/src/components/grid/layout/GridScrollWrapper.svelte +6 -0
- package/src/components/grid/layout/HeaderRow.svelte +3 -3
- package/src/components/grid/layout/NewRow.svelte +37 -5
- package/src/components/grid/layout/StickyColumn.svelte +10 -8
- package/src/components/grid/lib/constants.js +4 -3
- package/src/components/grid/lib/websocket.js +3 -2
- package/src/components/grid/overlays/KeyboardManager.svelte +6 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +3 -1
- package/src/components/grid/overlays/ReorderOverlay.svelte +1 -1
- package/src/components/grid/overlays/ResizeOverlay.svelte +1 -1
- package/src/components/grid/overlays/ScrollOverlay.svelte +25 -2
- package/src/components/grid/stores/columns.js +37 -6
- package/src/components/grid/stores/config.js +27 -0
- package/src/components/grid/stores/filter.js +19 -0
- package/src/components/grid/stores/index.js +6 -0
- package/src/components/grid/stores/menu.js +15 -9
- package/src/components/grid/stores/rows.js +18 -16
- package/src/components/grid/stores/scroll.js +5 -7
- package/src/components/grid/stores/sort.js +27 -0
- package/src/components/grid/stores/ui.js +19 -4
- package/src/components/grid/stores/viewport.js +17 -6
- package/src/fetch/DataFetch.js +4 -2
- package/src/utils/index.js +1 -0
- package/src/utils/memo.js +43 -0
- package/src/components/grid/controls/ColumnWidthButton.svelte +0 -92
- package/src/components/grid/controls/RowHeightButton.svelte +0 -71
|
@@ -46,7 +46,7 @@ export const createStores = () => {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export const deriveStores = context => {
|
|
49
|
-
const { table, columns, stickyColumn, API, dispatch } = context
|
|
49
|
+
const { table, columns, stickyColumn, API, dispatch, config } = context
|
|
50
50
|
|
|
51
51
|
// Updates the tables primary display column
|
|
52
52
|
const changePrimaryDisplay = async column => {
|
|
@@ -56,6 +56,23 @@ export const deriveStores = context => {
|
|
|
56
56
|
})
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// Updates the width of all columns
|
|
60
|
+
const changeAllColumnWidths = async width => {
|
|
61
|
+
columns.update(state => {
|
|
62
|
+
return state.map(col => ({
|
|
63
|
+
...col,
|
|
64
|
+
width,
|
|
65
|
+
}))
|
|
66
|
+
})
|
|
67
|
+
if (get(stickyColumn)) {
|
|
68
|
+
stickyColumn.update(state => ({
|
|
69
|
+
...state,
|
|
70
|
+
width,
|
|
71
|
+
}))
|
|
72
|
+
}
|
|
73
|
+
await saveChanges()
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
// Persists column changes by saving metadata against table schema
|
|
60
77
|
const saveChanges = async () => {
|
|
61
78
|
const $columns = get(columns)
|
|
@@ -91,7 +108,9 @@ export const deriveStores = context => {
|
|
|
91
108
|
table.set(newTable)
|
|
92
109
|
|
|
93
110
|
// Update server
|
|
94
|
-
|
|
111
|
+
if (get(config).allowSchemaChanges) {
|
|
112
|
+
await API.saveTable(newTable)
|
|
113
|
+
}
|
|
95
114
|
|
|
96
115
|
// Broadcast change to external state can be updated, as this change
|
|
97
116
|
// will not be received by the builder websocket because we caused it ourselves
|
|
@@ -105,17 +124,19 @@ export const deriveStores = context => {
|
|
|
105
124
|
saveChanges,
|
|
106
125
|
saveTable,
|
|
107
126
|
changePrimaryDisplay,
|
|
127
|
+
changeAllColumnWidths,
|
|
108
128
|
},
|
|
109
129
|
},
|
|
110
130
|
}
|
|
111
131
|
}
|
|
112
132
|
|
|
113
133
|
export const initialise = context => {
|
|
114
|
-
const { table, columns, stickyColumn, schemaOverrides } =
|
|
134
|
+
const { table, columns, stickyColumn, schemaOverrides, columnWhitelist } =
|
|
135
|
+
context
|
|
115
136
|
|
|
116
137
|
const schema = derived(
|
|
117
|
-
[table, schemaOverrides],
|
|
118
|
-
([$table, $schemaOverrides]) => {
|
|
138
|
+
[table, schemaOverrides, columnWhitelist],
|
|
139
|
+
([$table, $schemaOverrides, $columnWhitelist]) => {
|
|
119
140
|
if (!$table?.schema) {
|
|
120
141
|
return null
|
|
121
142
|
}
|
|
@@ -142,6 +163,16 @@ export const initialise = context => {
|
|
|
142
163
|
}
|
|
143
164
|
}
|
|
144
165
|
})
|
|
166
|
+
|
|
167
|
+
// Apply whitelist if specified
|
|
168
|
+
if ($columnWhitelist?.length) {
|
|
169
|
+
Object.keys(newSchema).forEach(key => {
|
|
170
|
+
if (!$columnWhitelist.includes(key)) {
|
|
171
|
+
delete newSchema[key]
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
145
176
|
return newSchema
|
|
146
177
|
}
|
|
147
178
|
)
|
|
@@ -209,7 +240,7 @@ export const initialise = context => {
|
|
|
209
240
|
}
|
|
210
241
|
stickyColumn.set({
|
|
211
242
|
name: primaryDisplay,
|
|
212
|
-
label: $schema[primaryDisplay].
|
|
243
|
+
label: $schema[primaryDisplay].displayName || primaryDisplay,
|
|
213
244
|
schema: $schema[primaryDisplay],
|
|
214
245
|
width: $schema[primaryDisplay].width || DefaultColumnWidth,
|
|
215
246
|
visible: true,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writable } from "svelte/store"
|
|
2
|
+
import { derivedMemo } from "../../../utils"
|
|
3
|
+
|
|
4
|
+
export const createStores = context => {
|
|
5
|
+
const config = writable(context.props)
|
|
6
|
+
const getProp = prop => derivedMemo(config, $config => $config[prop])
|
|
7
|
+
|
|
8
|
+
// Derive and memoize some props so that we can react to them in isolation
|
|
9
|
+
const tableId = getProp("tableId")
|
|
10
|
+
const initialSortColumn = getProp("initialSortColumn")
|
|
11
|
+
const initialSortOrder = getProp("initialSortOrder")
|
|
12
|
+
const initialFilter = getProp("initialFilter")
|
|
13
|
+
const initialRowHeight = getProp("initialRowHeight")
|
|
14
|
+
const schemaOverrides = getProp("schemaOverrides")
|
|
15
|
+
const columnWhitelist = getProp("columnWhitelist")
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
config,
|
|
19
|
+
tableId,
|
|
20
|
+
initialSortColumn,
|
|
21
|
+
initialSortOrder,
|
|
22
|
+
initialFilter,
|
|
23
|
+
initialRowHeight,
|
|
24
|
+
schemaOverrides,
|
|
25
|
+
columnWhitelist,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { writable } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const createStores = context => {
|
|
4
|
+
const { props } = context
|
|
5
|
+
|
|
6
|
+
// Initialise to default props
|
|
7
|
+
const filter = writable(props.initialFilter)
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
filter,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const initialise = context => {
|
|
15
|
+
const { filter, initialFilter } = context
|
|
16
|
+
|
|
17
|
+
// Reset filter when initial filter prop changes
|
|
18
|
+
initialFilter.subscribe(filter.set)
|
|
19
|
+
}
|
|
@@ -11,8 +11,14 @@ import * as Users from "./users"
|
|
|
11
11
|
import * as Validation from "./validation"
|
|
12
12
|
import * as Viewport from "./viewport"
|
|
13
13
|
import * as Clipboard from "./clipboard"
|
|
14
|
+
import * as Config from "./config"
|
|
15
|
+
import * as Sort from "./sort"
|
|
16
|
+
import * as Filter from "./filter"
|
|
14
17
|
|
|
15
18
|
const DependencyOrderedStores = [
|
|
19
|
+
Config,
|
|
20
|
+
Sort,
|
|
21
|
+
Filter,
|
|
16
22
|
Bounds,
|
|
17
23
|
Scroll,
|
|
18
24
|
Rows,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { writable
|
|
2
|
-
import { GutterWidth } from "../lib/constants"
|
|
1
|
+
import { writable } from "svelte/store"
|
|
3
2
|
|
|
4
3
|
export const createStores = () => {
|
|
5
4
|
const menu = writable({
|
|
@@ -14,18 +13,25 @@ export const createStores = () => {
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export const deriveStores = context => {
|
|
17
|
-
const { menu,
|
|
16
|
+
const { menu, focusedCellId, rand } = context
|
|
18
17
|
|
|
19
18
|
const open = (cellId, e) => {
|
|
20
|
-
const $bounds = get(bounds)
|
|
21
|
-
const $stickyColumn = get(stickyColumn)
|
|
22
|
-
const $rowHeight = get(rowHeight)
|
|
23
19
|
e.preventDefault()
|
|
20
|
+
|
|
21
|
+
// Get DOM node for grid data wrapper to compute relative position to
|
|
22
|
+
const gridNode = document.getElementById(`grid-${rand}`)
|
|
23
|
+
const dataNode = gridNode?.getElementsByClassName("grid-data-outer")?.[0]
|
|
24
|
+
if (!dataNode) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Compute bounds of cell relative to outer data node
|
|
29
|
+
const targetBounds = e.target.getBoundingClientRect()
|
|
30
|
+
const dataBounds = dataNode.getBoundingClientRect()
|
|
24
31
|
focusedCellId.set(cellId)
|
|
25
32
|
menu.set({
|
|
26
|
-
left:
|
|
27
|
-
|
|
28
|
-
top: e.clientY - $bounds.top + $rowHeight,
|
|
33
|
+
left: targetBounds.left - dataBounds.left + e.offsetX,
|
|
34
|
+
top: targetBounds.top - dataBounds.top + e.offsetY,
|
|
29
35
|
visible: true,
|
|
30
36
|
})
|
|
31
37
|
}
|
|
@@ -4,18 +4,13 @@ import { notifications } from "@budibase/bbui"
|
|
|
4
4
|
import { NewRowID, RowPageSize } from "../lib/constants"
|
|
5
5
|
import { tick } from "svelte"
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
column: null,
|
|
9
|
-
order: "ascending",
|
|
10
|
-
}
|
|
7
|
+
const SuppressErrors = true
|
|
11
8
|
|
|
12
9
|
export const createStores = () => {
|
|
13
10
|
const rows = writable([])
|
|
14
11
|
const table = writable(null)
|
|
15
|
-
const filter = writable([])
|
|
16
12
|
const loading = writable(false)
|
|
17
13
|
const loaded = writable(false)
|
|
18
|
-
const sort = writable(initialSortState)
|
|
19
14
|
const rowChangeCache = writable({})
|
|
20
15
|
const inProgressChanges = writable({})
|
|
21
16
|
const hasNextPage = writable(false)
|
|
@@ -47,10 +42,8 @@ export const createStores = () => {
|
|
|
47
42
|
rows,
|
|
48
43
|
rowLookupMap,
|
|
49
44
|
table,
|
|
50
|
-
filter,
|
|
51
45
|
loaded,
|
|
52
46
|
loading,
|
|
53
|
-
sort,
|
|
54
47
|
rowChangeCache,
|
|
55
48
|
inProgressChanges,
|
|
56
49
|
hasNextPage,
|
|
@@ -98,15 +91,18 @@ export const deriveStores = context => {
|
|
|
98
91
|
// Reset everything when table ID changes
|
|
99
92
|
let unsubscribe = null
|
|
100
93
|
let lastResetKey = null
|
|
101
|
-
tableId.subscribe($tableId => {
|
|
94
|
+
tableId.subscribe(async $tableId => {
|
|
102
95
|
// Unsub from previous fetch if one exists
|
|
103
96
|
unsubscribe?.()
|
|
104
97
|
fetch.set(null)
|
|
105
98
|
instanceLoaded.set(false)
|
|
106
99
|
loading.set(true)
|
|
107
100
|
|
|
108
|
-
//
|
|
109
|
-
|
|
101
|
+
// Tick to allow other reactive logic to update stores when table ID changes
|
|
102
|
+
// before proceeding. This allows us to wipe filters etc if needed.
|
|
103
|
+
await tick()
|
|
104
|
+
const $filter = get(filter)
|
|
105
|
+
const $sort = get(sort)
|
|
110
106
|
|
|
111
107
|
// Create new fetch model
|
|
112
108
|
const newFetch = fetchData({
|
|
@@ -116,9 +112,9 @@ export const deriveStores = context => {
|
|
|
116
112
|
tableId: $tableId,
|
|
117
113
|
},
|
|
118
114
|
options: {
|
|
119
|
-
filter:
|
|
120
|
-
sortColumn:
|
|
121
|
-
sortOrder:
|
|
115
|
+
filter: $filter,
|
|
116
|
+
sortColumn: $sort.column,
|
|
117
|
+
sortOrder: $sort.order,
|
|
122
118
|
limit: RowPageSize,
|
|
123
119
|
paginate: true,
|
|
124
120
|
},
|
|
@@ -224,7 +220,10 @@ export const deriveStores = context => {
|
|
|
224
220
|
const addRow = async (row, idx, bubble = false) => {
|
|
225
221
|
try {
|
|
226
222
|
// Create row
|
|
227
|
-
const newRow = await API.saveRow(
|
|
223
|
+
const newRow = await API.saveRow(
|
|
224
|
+
{ ...row, tableId: get(tableId) },
|
|
225
|
+
SuppressErrors
|
|
226
|
+
)
|
|
228
227
|
|
|
229
228
|
// Update state
|
|
230
229
|
if (idx != null) {
|
|
@@ -351,7 +350,10 @@ export const deriveStores = context => {
|
|
|
351
350
|
...state,
|
|
352
351
|
[rowId]: true,
|
|
353
352
|
}))
|
|
354
|
-
const saved = await API.saveRow(
|
|
353
|
+
const saved = await API.saveRow(
|
|
354
|
+
{ ...row, ...get(rowChangeCache)[rowId] },
|
|
355
|
+
SuppressErrors
|
|
356
|
+
)
|
|
355
357
|
|
|
356
358
|
// Update state after a successful change
|
|
357
359
|
if (saved?._id) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writable, derived, get } from "svelte/store"
|
|
2
2
|
import { tick } from "svelte"
|
|
3
|
-
import { Padding, GutterWidth } from "../lib/constants"
|
|
3
|
+
import { Padding, GutterWidth, FocusedCellMinOffset } from "../lib/constants"
|
|
4
4
|
|
|
5
5
|
export const createStores = () => {
|
|
6
6
|
const scroll = writable({
|
|
@@ -138,14 +138,13 @@ export const initialise = context => {
|
|
|
138
138
|
const $scroll = get(scroll)
|
|
139
139
|
const $bounds = get(bounds)
|
|
140
140
|
const $rowHeight = get(rowHeight)
|
|
141
|
-
const verticalOffset = 60
|
|
142
141
|
|
|
143
142
|
// Ensure vertical position is viewable
|
|
144
143
|
if ($focusedRow) {
|
|
145
144
|
// Ensure row is not below bottom of screen
|
|
146
145
|
const rowYPos = $focusedRow.__idx * $rowHeight
|
|
147
146
|
const bottomCutoff =
|
|
148
|
-
$scroll.top + $bounds.height - $rowHeight -
|
|
147
|
+
$scroll.top + $bounds.height - $rowHeight - FocusedCellMinOffset
|
|
149
148
|
let delta = rowYPos - bottomCutoff
|
|
150
149
|
if (delta > 0) {
|
|
151
150
|
scroll.update(state => ({
|
|
@@ -156,7 +155,7 @@ export const initialise = context => {
|
|
|
156
155
|
|
|
157
156
|
// Ensure row is not above top of screen
|
|
158
157
|
else {
|
|
159
|
-
const delta = $scroll.top - rowYPos +
|
|
158
|
+
const delta = $scroll.top - rowYPos + FocusedCellMinOffset
|
|
160
159
|
if (delta > 0) {
|
|
161
160
|
scroll.update(state => ({
|
|
162
161
|
...state,
|
|
@@ -171,13 +170,12 @@ export const initialise = context => {
|
|
|
171
170
|
const $visibleColumns = get(visibleColumns)
|
|
172
171
|
const columnName = $focusedCellId?.split("-")[1]
|
|
173
172
|
const column = $visibleColumns.find(col => col.name === columnName)
|
|
174
|
-
const horizontalOffset = 50
|
|
175
173
|
if (!column) {
|
|
176
174
|
return
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
// Ensure column is not cutoff on left edge
|
|
180
|
-
let delta = $scroll.left - column.left +
|
|
178
|
+
let delta = $scroll.left - column.left + FocusedCellMinOffset
|
|
181
179
|
if (delta > 0) {
|
|
182
180
|
scroll.update(state => ({
|
|
183
181
|
...state,
|
|
@@ -188,7 +186,7 @@ export const initialise = context => {
|
|
|
188
186
|
// Ensure column is not cutoff on right edge
|
|
189
187
|
else {
|
|
190
188
|
const rightEdge = column.left + column.width
|
|
191
|
-
const rightBound = $bounds.width + $scroll.left -
|
|
189
|
+
const rightBound = $bounds.width + $scroll.left - FocusedCellMinOffset
|
|
192
190
|
delta = rightEdge - rightBound
|
|
193
191
|
if (delta > 0) {
|
|
194
192
|
scroll.update(state => ({
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writable } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const createStores = context => {
|
|
4
|
+
const { props } = context
|
|
5
|
+
|
|
6
|
+
// Initialise to default props
|
|
7
|
+
const sort = writable({
|
|
8
|
+
column: props.initialSortColumn,
|
|
9
|
+
order: props.initialSortOrder || "ascending",
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
sort,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const initialise = context => {
|
|
18
|
+
const { sort, initialSortColumn, initialSortOrder } = context
|
|
19
|
+
|
|
20
|
+
// Reset sort when initial sort props change
|
|
21
|
+
initialSortColumn.subscribe(newSortColumn => {
|
|
22
|
+
sort.update(state => ({ ...state, column: newSortColumn }))
|
|
23
|
+
})
|
|
24
|
+
initialSortOrder.subscribe(newSortOrder => {
|
|
25
|
+
sort.update(state => ({ ...state, order: newSortOrder }))
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -8,13 +8,16 @@ import {
|
|
|
8
8
|
NewRowID,
|
|
9
9
|
} from "../lib/constants"
|
|
10
10
|
|
|
11
|
-
export const createStores =
|
|
11
|
+
export const createStores = context => {
|
|
12
|
+
const { props } = context
|
|
12
13
|
const focusedCellId = writable(null)
|
|
13
14
|
const focusedCellAPI = writable(null)
|
|
14
15
|
const selectedRows = writable({})
|
|
15
16
|
const hoveredRowId = writable(null)
|
|
16
|
-
const rowHeight = writable(DefaultRowHeight)
|
|
17
|
+
const rowHeight = writable(props.initialRowHeight || DefaultRowHeight)
|
|
17
18
|
const previousFocusedRowId = writable(null)
|
|
19
|
+
const gridFocused = writable(false)
|
|
20
|
+
const isDragging = writable(false)
|
|
18
21
|
|
|
19
22
|
// Derive the current focused row ID
|
|
20
23
|
const focusedRowId = derived(
|
|
@@ -46,6 +49,8 @@ export const createStores = () => {
|
|
|
46
49
|
previousFocusedRowId,
|
|
47
50
|
hoveredRowId,
|
|
48
51
|
rowHeight,
|
|
52
|
+
gridFocused,
|
|
53
|
+
isDragging,
|
|
49
54
|
selectedRows: {
|
|
50
55
|
...selectedRows,
|
|
51
56
|
actions: {
|
|
@@ -94,9 +99,9 @@ export const deriveStores = context => {
|
|
|
94
99
|
|
|
95
100
|
// Derive the amount of content lines to show in cells depending on row height
|
|
96
101
|
const contentLines = derived(rowHeight, $rowHeight => {
|
|
97
|
-
if ($rowHeight
|
|
102
|
+
if ($rowHeight >= LargeRowHeight) {
|
|
98
103
|
return 3
|
|
99
|
-
} else if ($rowHeight
|
|
104
|
+
} else if ($rowHeight >= MediumRowHeight) {
|
|
100
105
|
return 2
|
|
101
106
|
}
|
|
102
107
|
return 1
|
|
@@ -129,6 +134,7 @@ export const initialise = context => {
|
|
|
129
134
|
hoveredRowId,
|
|
130
135
|
table,
|
|
131
136
|
rowHeight,
|
|
137
|
+
initialRowHeight,
|
|
132
138
|
} = context
|
|
133
139
|
|
|
134
140
|
// Ensure we clear invalid rows from state if they disappear
|
|
@@ -185,4 +191,13 @@ export const initialise = context => {
|
|
|
185
191
|
table.subscribe($table => {
|
|
186
192
|
rowHeight.set($table?.rowHeight || DefaultRowHeight)
|
|
187
193
|
})
|
|
194
|
+
|
|
195
|
+
// Reset row height when initial row height prop changes
|
|
196
|
+
initialRowHeight.subscribe(height => {
|
|
197
|
+
if (height) {
|
|
198
|
+
rowHeight.set(height)
|
|
199
|
+
} else {
|
|
200
|
+
rowHeight.set(get(table)?.rowHeight || DefaultRowHeight)
|
|
201
|
+
}
|
|
202
|
+
})
|
|
188
203
|
}
|
|
@@ -108,11 +108,22 @@ export const deriveStores = context => {
|
|
|
108
108
|
// Determine the row index at which we should start vertically inverting cell
|
|
109
109
|
// dropdowns
|
|
110
110
|
const rowVerticalInversionIndex = derived(
|
|
111
|
-
[
|
|
112
|
-
([$
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
[height, rowHeight, scrollTop],
|
|
112
|
+
([$height, $rowHeight, $scrollTop]) => {
|
|
113
|
+
const offset = $scrollTop % $rowHeight
|
|
114
|
+
|
|
115
|
+
// Compute the last row index with space to render popovers below it
|
|
116
|
+
const minBottom =
|
|
117
|
+
$height - ScrollBarSize * 3 - MaxCellRenderHeight + offset
|
|
118
|
+
const lastIdx = Math.floor(minBottom / $rowHeight)
|
|
119
|
+
|
|
120
|
+
// Compute the first row index with space to render popovers above it
|
|
121
|
+
const minTop = MaxCellRenderHeight + offset
|
|
122
|
+
const firstIdx = Math.ceil(minTop / $rowHeight)
|
|
123
|
+
|
|
124
|
+
// Use the greater of the two indices so that we prefer content below,
|
|
125
|
+
// unless there is room to render the entire popover above
|
|
126
|
+
return Math.max(lastIdx, firstIdx)
|
|
116
127
|
}
|
|
117
128
|
)
|
|
118
129
|
|
|
@@ -125,7 +136,7 @@ export const deriveStores = context => {
|
|
|
125
136
|
let inversionIdx = $renderedColumns.length
|
|
126
137
|
for (let i = $renderedColumns.length - 1; i >= 0; i--, inversionIdx--) {
|
|
127
138
|
const rightEdge = $renderedColumns[i].left + $renderedColumns[i].width
|
|
128
|
-
if (rightEdge + MaxCellRenderWidthOverflow
|
|
139
|
+
if (rightEdge + MaxCellRenderWidthOverflow <= cutoff) {
|
|
129
140
|
break
|
|
130
141
|
}
|
|
131
142
|
}
|
package/src/fetch/DataFetch.js
CHANGED
|
@@ -136,8 +136,10 @@ export default class DataFetch {
|
|
|
136
136
|
this.options.sortOrder = "ascending"
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
// If no sort column,
|
|
140
|
-
|
|
139
|
+
// If no sort column, or an invalid sort column is provided, use the primary
|
|
140
|
+
// display and fallback to first column
|
|
141
|
+
const sortValid = this.options.sortColumn && schema[this.options.sortColumn]
|
|
142
|
+
if (!sortValid) {
|
|
141
143
|
let newSortColumn
|
|
142
144
|
if (definition?.primaryDisplay && schema[definition.primaryDisplay]) {
|
|
143
145
|
newSortColumn = definition.primaryDisplay
|
package/src/utils/index.js
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { writable, get, derived } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
// A simple svelte store which deeply compares all changes and ensures that
|
|
4
|
+
// subscribed children will only fire when a new value is actually set
|
|
5
|
+
export const memo = initialValue => {
|
|
6
|
+
const store = writable(initialValue)
|
|
7
|
+
|
|
8
|
+
const tryUpdateValue = (newValue, currentValue) => {
|
|
9
|
+
// Sanity check for primitive equality
|
|
10
|
+
if (currentValue === newValue) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Otherwise deep compare via JSON stringify
|
|
15
|
+
const currentString = JSON.stringify(currentValue)
|
|
16
|
+
const newString = JSON.stringify(newValue)
|
|
17
|
+
if (currentString !== newString) {
|
|
18
|
+
store.set(newValue)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
subscribe: store.subscribe,
|
|
24
|
+
set: newValue => {
|
|
25
|
+
const currentValue = get(store)
|
|
26
|
+
tryUpdateValue(newValue, currentValue)
|
|
27
|
+
},
|
|
28
|
+
update: updateFn => {
|
|
29
|
+
const currentValue = get(store)
|
|
30
|
+
let mutableCurrentValue = JSON.parse(JSON.stringify(currentValue))
|
|
31
|
+
const newValue = updateFn(mutableCurrentValue)
|
|
32
|
+
tryUpdateValue(newValue, currentValue)
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Enriched version of svelte's derived store which returns a memo
|
|
38
|
+
export const derivedMemo = (store, derivation) => {
|
|
39
|
+
const derivedStore = derived(store, derivation)
|
|
40
|
+
const memoStore = memo(get(derivedStore))
|
|
41
|
+
derivedStore.subscribe(memoStore.set)
|
|
42
|
+
return memoStore
|
|
43
|
+
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { getContext } from "svelte"
|
|
3
|
-
import { ActionButton, Popover } from "@budibase/bbui"
|
|
4
|
-
import { DefaultColumnWidth } from "../lib/constants"
|
|
5
|
-
|
|
6
|
-
const { stickyColumn, columns, compact } = getContext("grid")
|
|
7
|
-
const smallSize = 120
|
|
8
|
-
const mediumSize = DefaultColumnWidth
|
|
9
|
-
const largeSize = DefaultColumnWidth * 1.5
|
|
10
|
-
|
|
11
|
-
let open = false
|
|
12
|
-
let anchor
|
|
13
|
-
|
|
14
|
-
$: allCols = $columns.concat($stickyColumn ? [$stickyColumn] : [])
|
|
15
|
-
$: allSmall = allCols.every(col => col.width === smallSize)
|
|
16
|
-
$: allMedium = allCols.every(col => col.width === mediumSize)
|
|
17
|
-
$: allLarge = allCols.every(col => col.width === largeSize)
|
|
18
|
-
$: custom = !allSmall && !allMedium && !allLarge
|
|
19
|
-
$: sizeOptions = [
|
|
20
|
-
{
|
|
21
|
-
label: "Small",
|
|
22
|
-
size: smallSize,
|
|
23
|
-
selected: allSmall,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
label: "Medium",
|
|
27
|
-
size: mediumSize,
|
|
28
|
-
selected: allMedium,
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
label: "Large",
|
|
32
|
-
size: largeSize,
|
|
33
|
-
selected: allLarge,
|
|
34
|
-
},
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
const changeColumnWidth = async width => {
|
|
38
|
-
columns.update(state => {
|
|
39
|
-
state.forEach(column => {
|
|
40
|
-
column.width = width
|
|
41
|
-
})
|
|
42
|
-
return state
|
|
43
|
-
})
|
|
44
|
-
if ($stickyColumn) {
|
|
45
|
-
stickyColumn.update(state => ({
|
|
46
|
-
...state,
|
|
47
|
-
width,
|
|
48
|
-
}))
|
|
49
|
-
}
|
|
50
|
-
await columns.actions.saveChanges()
|
|
51
|
-
}
|
|
52
|
-
</script>
|
|
53
|
-
|
|
54
|
-
<div bind:this={anchor}>
|
|
55
|
-
<ActionButton
|
|
56
|
-
icon="MoveLeftRight"
|
|
57
|
-
quiet
|
|
58
|
-
size="M"
|
|
59
|
-
on:click={() => (open = !open)}
|
|
60
|
-
selected={open}
|
|
61
|
-
disabled={!allCols.length}
|
|
62
|
-
tooltip={$compact ? "Width" : null}
|
|
63
|
-
>
|
|
64
|
-
{$compact ? "" : "Width"}
|
|
65
|
-
</ActionButton>
|
|
66
|
-
</div>
|
|
67
|
-
|
|
68
|
-
<Popover bind:open {anchor} align={$compact ? "right" : "left"}>
|
|
69
|
-
<div class="content">
|
|
70
|
-
{#each sizeOptions as option}
|
|
71
|
-
<ActionButton
|
|
72
|
-
quiet
|
|
73
|
-
on:click={() => changeColumnWidth(option.size)}
|
|
74
|
-
selected={option.selected}
|
|
75
|
-
>
|
|
76
|
-
{option.label}
|
|
77
|
-
</ActionButton>
|
|
78
|
-
{/each}
|
|
79
|
-
{#if custom}
|
|
80
|
-
<ActionButton selected={custom} quiet>Custom</ActionButton>
|
|
81
|
-
{/if}
|
|
82
|
-
</div>
|
|
83
|
-
</Popover>
|
|
84
|
-
|
|
85
|
-
<style>
|
|
86
|
-
.content {
|
|
87
|
-
padding: 12px;
|
|
88
|
-
display: flex;
|
|
89
|
-
align-items: center;
|
|
90
|
-
gap: 8px;
|
|
91
|
-
}
|
|
92
|
-
</style>
|