@budibase/frontend-core 2.5.6-alpha.5 → 2.5.6-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 +6 -4
- package/src/components/grid/cells/AttachmentCell.svelte +153 -0
- package/src/components/grid/cells/BooleanCell.svelte +44 -0
- package/src/components/grid/cells/DataCell.svelte +86 -0
- package/src/components/grid/cells/DateCell.svelte +77 -0
- package/src/components/grid/cells/FormulaCell.svelte +5 -0
- package/src/components/grid/cells/GridCell.svelte +159 -0
- package/src/components/grid/cells/HeaderCell.svelte +237 -0
- package/src/components/grid/cells/JSONCell.svelte +36 -0
- package/src/components/grid/cells/LongFormCell.svelte +117 -0
- package/src/components/grid/cells/MultiSelectCell.svelte +7 -0
- package/src/components/grid/cells/NumberCell.svelte +7 -0
- package/src/components/grid/cells/OptionsCell.svelte +239 -0
- package/src/components/grid/cells/RelationshipCell.svelte +503 -0
- package/src/components/grid/cells/TextCell.svelte +110 -0
- package/src/components/grid/controls/AddColumnButton.svelte +16 -0
- package/src/components/grid/controls/AddRowButton.svelte +18 -0
- package/src/components/grid/controls/BetaButton.svelte +46 -0
- package/src/components/grid/controls/DeleteButton.svelte +72 -0
- package/src/components/grid/controls/HideColumnsButton.svelte +91 -0
- package/src/components/grid/controls/RowHeightButton.svelte +70 -0
- package/src/components/grid/controls/SortButton.svelte +107 -0
- package/src/components/grid/index.js +1 -0
- package/src/components/grid/layout/Avatar.svelte +24 -0
- package/src/components/grid/layout/Grid.svelte +250 -0
- package/src/components/grid/layout/GridBody.svelte +38 -0
- package/src/components/grid/layout/GridRow.svelte +57 -0
- package/src/components/grid/layout/GridScrollWrapper.svelte +84 -0
- package/src/components/grid/layout/HeaderRow.svelte +30 -0
- package/src/components/grid/layout/NewRowTop.svelte +247 -0
- package/src/components/grid/layout/RowCount.svelte +9 -0
- package/src/components/grid/layout/StickyColumn.svelte +245 -0
- package/src/components/grid/layout/UserAvatars.svelte +20 -0
- package/src/components/grid/lib/constants.js +11 -0
- package/src/components/grid/lib/events.js +29 -0
- package/src/components/grid/lib/renderers.js +29 -0
- package/src/components/grid/lib/utils.js +26 -0
- package/src/components/grid/lib/websocket.js +63 -0
- package/src/components/grid/overlays/KeyboardManager.svelte +180 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +95 -0
- package/src/components/grid/overlays/ReorderOverlay.svelte +63 -0
- package/src/components/grid/overlays/ResizeOverlay.svelte +70 -0
- package/src/components/grid/overlays/ScrollOverlay.svelte +121 -0
- package/src/components/grid/stores/bounds.js +16 -0
- package/src/components/grid/stores/clipboard.js +34 -0
- package/src/components/grid/stores/columns.js +206 -0
- package/src/components/grid/stores/index.js +48 -0
- package/src/components/grid/stores/menu.js +49 -0
- package/src/components/grid/stores/pagination.js +24 -0
- package/src/components/grid/stores/reorder.js +155 -0
- package/src/components/grid/stores/resize.js +124 -0
- package/src/components/grid/stores/rows.js +462 -0
- package/src/components/grid/stores/scroll.js +201 -0
- package/src/components/grid/stores/ui.js +168 -0
- package/src/components/grid/stores/users.js +115 -0
- package/src/components/grid/stores/validation.js +45 -0
- package/src/components/grid/stores/viewport.js +141 -0
- package/src/components/index.js +1 -0
- package/src/fetch/DataFetch.js +27 -21
- package/src/fetch/QueryFetch.js +1 -1
- package/src/themes/midnight.css +3 -0
- package/src/themes/nord.css +3 -0
- package/src/utils/utils.js +20 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { writable, get, derived } from "svelte/store"
|
|
2
|
+
import {
|
|
3
|
+
DefaultRowHeight,
|
|
4
|
+
LargeRowHeight,
|
|
5
|
+
MediumRowHeight,
|
|
6
|
+
} from "../lib/constants"
|
|
7
|
+
|
|
8
|
+
export const createStores = () => {
|
|
9
|
+
const focusedCellId = writable(null)
|
|
10
|
+
const focusedCellAPI = writable(null)
|
|
11
|
+
const selectedRows = writable({})
|
|
12
|
+
const hoveredRowId = writable(null)
|
|
13
|
+
const rowHeight = writable(DefaultRowHeight)
|
|
14
|
+
const previousFocusedRowId = writable(null)
|
|
15
|
+
|
|
16
|
+
// Derive the current focused row ID
|
|
17
|
+
const focusedRowId = derived(
|
|
18
|
+
focusedCellId,
|
|
19
|
+
$focusedCellId => {
|
|
20
|
+
return $focusedCellId?.split("-")[0]
|
|
21
|
+
},
|
|
22
|
+
null
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
focusedCellId,
|
|
27
|
+
focusedCellAPI,
|
|
28
|
+
focusedRowId,
|
|
29
|
+
previousFocusedRowId,
|
|
30
|
+
selectedRows,
|
|
31
|
+
hoveredRowId,
|
|
32
|
+
rowHeight,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const deriveStores = context => {
|
|
37
|
+
const {
|
|
38
|
+
focusedCellId,
|
|
39
|
+
selectedRows,
|
|
40
|
+
hoveredRowId,
|
|
41
|
+
enrichedRows,
|
|
42
|
+
rowLookupMap,
|
|
43
|
+
rowHeight,
|
|
44
|
+
} = context
|
|
45
|
+
|
|
46
|
+
// Derive the row that contains the selected cell
|
|
47
|
+
const focusedRow = derived(
|
|
48
|
+
[focusedCellId, rowLookupMap, enrichedRows],
|
|
49
|
+
([$focusedCellId, $rowLookupMap, $enrichedRows]) => {
|
|
50
|
+
const rowId = $focusedCellId?.split("-")[0]
|
|
51
|
+
|
|
52
|
+
if (rowId === "new") {
|
|
53
|
+
// Edge case for new row
|
|
54
|
+
return { _id: rowId }
|
|
55
|
+
} else {
|
|
56
|
+
// All normal rows
|
|
57
|
+
const index = $rowLookupMap[rowId]
|
|
58
|
+
return $enrichedRows[index]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
null
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
// Callback when leaving the grid, deselecting all focussed or selected items
|
|
65
|
+
const blur = () => {
|
|
66
|
+
focusedCellId.set(null)
|
|
67
|
+
selectedRows.set({})
|
|
68
|
+
hoveredRowId.set(null)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const contentLines = derived(rowHeight, $rowHeight => {
|
|
72
|
+
if ($rowHeight === LargeRowHeight) {
|
|
73
|
+
return 3
|
|
74
|
+
} else if ($rowHeight === MediumRowHeight) {
|
|
75
|
+
return 2
|
|
76
|
+
}
|
|
77
|
+
return 1
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
focusedRow,
|
|
82
|
+
contentLines,
|
|
83
|
+
ui: {
|
|
84
|
+
actions: {
|
|
85
|
+
blur,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const initialise = context => {
|
|
92
|
+
const {
|
|
93
|
+
focusedRowId,
|
|
94
|
+
previousFocusedRowId,
|
|
95
|
+
rows,
|
|
96
|
+
focusedCellId,
|
|
97
|
+
selectedRows,
|
|
98
|
+
hoveredRowId,
|
|
99
|
+
table,
|
|
100
|
+
rowHeight,
|
|
101
|
+
} = context
|
|
102
|
+
|
|
103
|
+
// Ensure we clear invalid rows from state if they disappear
|
|
104
|
+
rows.subscribe(() => {
|
|
105
|
+
const $focusedCellId = get(focusedCellId)
|
|
106
|
+
const $selectedRows = get(selectedRows)
|
|
107
|
+
const $hoveredRowId = get(hoveredRowId)
|
|
108
|
+
const hasRow = rows.actions.hasRow
|
|
109
|
+
|
|
110
|
+
// Check selected cell
|
|
111
|
+
const selectedRowId = $focusedCellId?.split("-")[0]
|
|
112
|
+
if (selectedRowId && !hasRow(selectedRowId)) {
|
|
113
|
+
focusedCellId.set(null)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check hovered row
|
|
117
|
+
if ($hoveredRowId && !hasRow($hoveredRowId)) {
|
|
118
|
+
hoveredRowId.set(null)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check selected rows
|
|
122
|
+
let newSelectedRows = { ...$selectedRows }
|
|
123
|
+
let selectedRowsNeedsUpdate = false
|
|
124
|
+
const selectedIds = Object.keys($selectedRows)
|
|
125
|
+
for (let i = 0; i < selectedIds.length; i++) {
|
|
126
|
+
if (!hasRow(selectedIds[i])) {
|
|
127
|
+
delete newSelectedRows[selectedIds[i]]
|
|
128
|
+
selectedRowsNeedsUpdate = true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (selectedRowsNeedsUpdate) {
|
|
132
|
+
selectedRows.set(newSelectedRows)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Remember the last focused row ID so that we can store the previous one
|
|
137
|
+
let lastFocusedRowId = null
|
|
138
|
+
focusedRowId.subscribe(id => {
|
|
139
|
+
previousFocusedRowId.set(lastFocusedRowId)
|
|
140
|
+
lastFocusedRowId = id
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Reset selected rows when selected cell changes
|
|
144
|
+
focusedCellId.subscribe(id => {
|
|
145
|
+
if (id) {
|
|
146
|
+
selectedRows.set({})
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// Unset selected cell when rows are selected
|
|
151
|
+
selectedRows.subscribe(rows => {
|
|
152
|
+
if (Object.keys(rows || {}).length) {
|
|
153
|
+
focusedCellId.set(null)
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Remove hovered row when a cell is selected
|
|
158
|
+
focusedCellId.subscribe(cell => {
|
|
159
|
+
if (cell && get(hoveredRowId)) {
|
|
160
|
+
hoveredRowId.set(null)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Pull row height from table
|
|
165
|
+
table.subscribe($table => {
|
|
166
|
+
rowHeight.set($table?.rowHeight || DefaultRowHeight)
|
|
167
|
+
})
|
|
168
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { writable, get, derived } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const createStores = () => {
|
|
4
|
+
const users = writable([])
|
|
5
|
+
const userId = writable(null)
|
|
6
|
+
|
|
7
|
+
// Enrich users with unique colours
|
|
8
|
+
const enrichedUsers = derived(
|
|
9
|
+
[users, userId],
|
|
10
|
+
([$users, $userId]) => {
|
|
11
|
+
return (
|
|
12
|
+
$users
|
|
13
|
+
.slice()
|
|
14
|
+
// Place current user first
|
|
15
|
+
.sort((a, b) => {
|
|
16
|
+
if (a.id === $userId) {
|
|
17
|
+
return -1
|
|
18
|
+
} else if (b.id === $userId) {
|
|
19
|
+
return 1
|
|
20
|
+
} else {
|
|
21
|
+
return 0
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
// Enrich users with colors
|
|
25
|
+
.map((user, idx) => {
|
|
26
|
+
// Generate random colour hue
|
|
27
|
+
let hue = 1
|
|
28
|
+
for (let i = 0; i < user.email.length && i < 5; i++) {
|
|
29
|
+
hue *= user.email.charCodeAt(i + 1)
|
|
30
|
+
hue /= 17
|
|
31
|
+
}
|
|
32
|
+
hue = hue % 360
|
|
33
|
+
const color =
|
|
34
|
+
idx === 0
|
|
35
|
+
? "var(--spectrum-global-color-blue-400)"
|
|
36
|
+
: `hsl(${hue}, 50%, 40%)`
|
|
37
|
+
|
|
38
|
+
// Generate friendly label
|
|
39
|
+
let label = user.email
|
|
40
|
+
if (user.firstName) {
|
|
41
|
+
label = user.firstName
|
|
42
|
+
if (user.lastName) {
|
|
43
|
+
label += ` ${user.lastName}`
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
...user,
|
|
49
|
+
color,
|
|
50
|
+
label,
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
},
|
|
55
|
+
[]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
users: {
|
|
60
|
+
...users,
|
|
61
|
+
subscribe: enrichedUsers.subscribe,
|
|
62
|
+
},
|
|
63
|
+
userId,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const deriveStores = context => {
|
|
68
|
+
const { users, userId } = context
|
|
69
|
+
|
|
70
|
+
// Generate a lookup map of cell ID to the user that has it selected, to make
|
|
71
|
+
// lookups inside cells extremely fast
|
|
72
|
+
const selectedCellMap = derived(
|
|
73
|
+
[users, userId],
|
|
74
|
+
([$enrichedUsers, $userId]) => {
|
|
75
|
+
let map = {}
|
|
76
|
+
$enrichedUsers.forEach(user => {
|
|
77
|
+
if (user.focusedCellId && user.id !== $userId) {
|
|
78
|
+
map[user.focusedCellId] = user
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
return map
|
|
82
|
+
},
|
|
83
|
+
{}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const updateUser = user => {
|
|
87
|
+
const $users = get(users)
|
|
88
|
+
const index = $users.findIndex(x => x.id === user.id)
|
|
89
|
+
if (index === -1) {
|
|
90
|
+
users.set([...$users, user])
|
|
91
|
+
} else {
|
|
92
|
+
users.update(state => {
|
|
93
|
+
state[index] = user
|
|
94
|
+
return state.slice()
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const removeUser = user => {
|
|
100
|
+
users.update(state => {
|
|
101
|
+
return state.filter(x => x.id !== user.id)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
users: {
|
|
107
|
+
...users,
|
|
108
|
+
actions: {
|
|
109
|
+
updateUser,
|
|
110
|
+
removeUser,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
selectedCellMap,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { writable, get } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const createStores = () => {
|
|
4
|
+
const validation = writable({})
|
|
5
|
+
|
|
6
|
+
const setError = (cellId, error) => {
|
|
7
|
+
if (!cellId) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
validation.update(state => ({
|
|
11
|
+
...state,
|
|
12
|
+
[cellId]: error,
|
|
13
|
+
}))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
validation: {
|
|
18
|
+
...validation,
|
|
19
|
+
actions: {
|
|
20
|
+
setError,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const initialise = context => {
|
|
27
|
+
const { validation, previousFocusedRowId, columns, stickyColumn } = context
|
|
28
|
+
|
|
29
|
+
// Remove validation errors from previous focused row
|
|
30
|
+
previousFocusedRowId.subscribe(id => {
|
|
31
|
+
if (id) {
|
|
32
|
+
const $columns = get(columns)
|
|
33
|
+
const $stickyColumn = get(stickyColumn)
|
|
34
|
+
validation.update(state => {
|
|
35
|
+
$columns.forEach(column => {
|
|
36
|
+
state[`${id}-${column.name}`] = null
|
|
37
|
+
})
|
|
38
|
+
if ($stickyColumn) {
|
|
39
|
+
state[`${id}-${$stickyColumn.name}`] = null
|
|
40
|
+
}
|
|
41
|
+
return state
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { derived, get } from "svelte/store"
|
|
2
|
+
import {
|
|
3
|
+
MaxCellRenderHeight,
|
|
4
|
+
MaxCellRenderWidthOverflow,
|
|
5
|
+
ScrollBarSize,
|
|
6
|
+
} from "../lib/constants"
|
|
7
|
+
|
|
8
|
+
export const deriveStores = context => {
|
|
9
|
+
const {
|
|
10
|
+
rowHeight,
|
|
11
|
+
visibleColumns,
|
|
12
|
+
enrichedRows,
|
|
13
|
+
scrollTop,
|
|
14
|
+
scrollLeft,
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
17
|
+
} = context
|
|
18
|
+
|
|
19
|
+
// Derive visible rows
|
|
20
|
+
// Split into multiple stores containing primitives to optimise invalidation
|
|
21
|
+
// as much as possible
|
|
22
|
+
const scrolledRowCount = derived(
|
|
23
|
+
[scrollTop, rowHeight],
|
|
24
|
+
([$scrollTop, $rowHeight]) => {
|
|
25
|
+
return Math.floor($scrollTop / $rowHeight)
|
|
26
|
+
},
|
|
27
|
+
0
|
|
28
|
+
)
|
|
29
|
+
const visualRowCapacity = derived(
|
|
30
|
+
[height, rowHeight],
|
|
31
|
+
([$height, $rowHeight]) => {
|
|
32
|
+
return Math.ceil($height / $rowHeight) + 1
|
|
33
|
+
},
|
|
34
|
+
0
|
|
35
|
+
)
|
|
36
|
+
const renderedRows = derived(
|
|
37
|
+
[enrichedRows, scrolledRowCount, visualRowCapacity],
|
|
38
|
+
([$enrichedRows, $scrolledRowCount, $visualRowCapacity]) => {
|
|
39
|
+
return $enrichedRows.slice(
|
|
40
|
+
$scrolledRowCount,
|
|
41
|
+
$scrolledRowCount + $visualRowCapacity
|
|
42
|
+
)
|
|
43
|
+
},
|
|
44
|
+
[]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
// Derive visible columns
|
|
48
|
+
const renderedColumns = derived(
|
|
49
|
+
[visibleColumns, scrollLeft, width],
|
|
50
|
+
([$visibleColumns, $scrollLeft, $width]) => {
|
|
51
|
+
if (!$visibleColumns.length) {
|
|
52
|
+
return []
|
|
53
|
+
}
|
|
54
|
+
let startColIdx = 0
|
|
55
|
+
let rightEdge = $visibleColumns[0].width
|
|
56
|
+
while (
|
|
57
|
+
rightEdge < $scrollLeft &&
|
|
58
|
+
startColIdx < $visibleColumns.length - 1
|
|
59
|
+
) {
|
|
60
|
+
startColIdx++
|
|
61
|
+
rightEdge += $visibleColumns[startColIdx].width
|
|
62
|
+
}
|
|
63
|
+
let endColIdx = startColIdx + 1
|
|
64
|
+
let leftEdge = rightEdge
|
|
65
|
+
while (
|
|
66
|
+
leftEdge < $width + $scrollLeft &&
|
|
67
|
+
endColIdx < $visibleColumns.length
|
|
68
|
+
) {
|
|
69
|
+
leftEdge += $visibleColumns[endColIdx].width
|
|
70
|
+
endColIdx++
|
|
71
|
+
}
|
|
72
|
+
const nextRenderedColumns = $visibleColumns.slice(startColIdx, endColIdx)
|
|
73
|
+
|
|
74
|
+
// Cautiously shrink the number of rendered columns.
|
|
75
|
+
// This is to avoid rapidly shrinking and growing the visible column count
|
|
76
|
+
// which results in remounting cells
|
|
77
|
+
const currentCount = get(renderedColumns).length
|
|
78
|
+
if (currentCount === nextRenderedColumns.length + 1) {
|
|
79
|
+
return $visibleColumns.slice(startColIdx, endColIdx + 1)
|
|
80
|
+
} else {
|
|
81
|
+
return nextRenderedColumns
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
[]
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const hiddenColumnsWidth = derived(
|
|
88
|
+
[renderedColumns, visibleColumns],
|
|
89
|
+
([$renderedColumns, $visibleColumns]) => {
|
|
90
|
+
const idx = $visibleColumns.findIndex(
|
|
91
|
+
col => col.name === $renderedColumns[0]?.name
|
|
92
|
+
)
|
|
93
|
+
let width = 0
|
|
94
|
+
if (idx > 0) {
|
|
95
|
+
for (let i = 0; i < idx; i++) {
|
|
96
|
+
width += $visibleColumns[i].width
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return width
|
|
100
|
+
},
|
|
101
|
+
0
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
// Determine the row index at which we should start vertically inverting cell
|
|
105
|
+
// dropdowns
|
|
106
|
+
const rowVerticalInversionIndex = derived(
|
|
107
|
+
[visualRowCapacity, rowHeight],
|
|
108
|
+
([$visualRowCapacity, $rowHeight]) => {
|
|
109
|
+
return (
|
|
110
|
+
$visualRowCapacity - Math.ceil(MaxCellRenderHeight / $rowHeight) - 2
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
// Determine the column index at which we should start horizontally inverting
|
|
116
|
+
// cell dropdowns
|
|
117
|
+
const columnHorizontalInversionIndex = derived(
|
|
118
|
+
[renderedColumns, scrollLeft, width],
|
|
119
|
+
([$renderedColumns, $scrollLeft, $width]) => {
|
|
120
|
+
const cutoff = $width + $scrollLeft - ScrollBarSize * 3
|
|
121
|
+
let inversionIdx = $renderedColumns.length
|
|
122
|
+
for (let i = $renderedColumns.length - 1; i >= 0; i--, inversionIdx--) {
|
|
123
|
+
const rightEdge = $renderedColumns[i].left + $renderedColumns[i].width
|
|
124
|
+
if (rightEdge + MaxCellRenderWidthOverflow < cutoff) {
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return inversionIdx
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
scrolledRowCount,
|
|
134
|
+
visualRowCapacity,
|
|
135
|
+
renderedRows,
|
|
136
|
+
renderedColumns,
|
|
137
|
+
hiddenColumnsWidth,
|
|
138
|
+
rowVerticalInversionIndex,
|
|
139
|
+
columnHorizontalInversionIndex,
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/components/index.js
CHANGED
package/src/fetch/DataFetch.js
CHANGED
|
@@ -21,11 +21,11 @@ export default class DataFetch {
|
|
|
21
21
|
this.API = null
|
|
22
22
|
|
|
23
23
|
// Feature flags
|
|
24
|
-
this.
|
|
24
|
+
this.features = {
|
|
25
25
|
supportsSearch: false,
|
|
26
26
|
supportsSort: false,
|
|
27
27
|
supportsPagination: false,
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
29
|
|
|
30
30
|
// Config
|
|
31
31
|
this.options = {
|
|
@@ -56,6 +56,7 @@ export default class DataFetch {
|
|
|
56
56
|
pageNumber: 0,
|
|
57
57
|
cursor: null,
|
|
58
58
|
cursors: [],
|
|
59
|
+
resetKey: Math.random(),
|
|
59
60
|
})
|
|
60
61
|
|
|
61
62
|
// Merge options with their default values
|
|
@@ -81,17 +82,14 @@ export default class DataFetch {
|
|
|
81
82
|
this.prevPage = this.prevPage.bind(this)
|
|
82
83
|
|
|
83
84
|
// Derive certain properties to return
|
|
84
|
-
this.derivedStore = derived(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
hasNextPage: this.hasNextPage($store),
|
|
91
|
-
hasPrevPage: this.hasPrevPage($store),
|
|
92
|
-
}
|
|
85
|
+
this.derivedStore = derived(this.store, $store => {
|
|
86
|
+
return {
|
|
87
|
+
...$store,
|
|
88
|
+
...this.features,
|
|
89
|
+
hasNextPage: this.hasNextPage($store),
|
|
90
|
+
hasPrevPage: this.hasPrevPage($store),
|
|
93
91
|
}
|
|
94
|
-
)
|
|
92
|
+
})
|
|
95
93
|
|
|
96
94
|
// Mark as loaded if we have no datasource
|
|
97
95
|
if (!this.options.datasource) {
|
|
@@ -120,11 +118,11 @@ export default class DataFetch {
|
|
|
120
118
|
// Fetch datasource definition and determine feature flags
|
|
121
119
|
const definition = await this.getDefinition(datasource)
|
|
122
120
|
const features = this.determineFeatureFlags(definition)
|
|
123
|
-
this.
|
|
121
|
+
this.features = {
|
|
124
122
|
supportsSearch: !!features?.supportsSearch,
|
|
125
123
|
supportsSort: !!features?.supportsSort,
|
|
126
124
|
supportsPagination: paginate && !!features?.supportsPagination,
|
|
127
|
-
}
|
|
125
|
+
}
|
|
128
126
|
|
|
129
127
|
// Fetch and enrich schema
|
|
130
128
|
let schema = this.getSchema(datasource, definition)
|
|
@@ -138,11 +136,17 @@ export default class DataFetch {
|
|
|
138
136
|
this.options.sortOrder = "ascending"
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
// If no sort column, use the
|
|
139
|
+
// If no sort column, use the primary display and fallback to first column
|
|
142
140
|
if (!this.options.sortColumn) {
|
|
143
|
-
|
|
141
|
+
let newSortColumn
|
|
142
|
+
if (definition?.primaryDisplay && schema[definition.primaryDisplay]) {
|
|
143
|
+
newSortColumn = definition.primaryDisplay
|
|
144
|
+
} else {
|
|
145
|
+
newSortColumn = Object.keys(schema)[0]
|
|
146
|
+
}
|
|
147
|
+
this.options.sortColumn = newSortColumn
|
|
144
148
|
}
|
|
145
|
-
const { sortColumn } = this.options
|
|
149
|
+
const { sortOrder, sortColumn } = this.options
|
|
146
150
|
|
|
147
151
|
// Determine what sort type to use
|
|
148
152
|
let sortType = "string"
|
|
@@ -167,6 +171,8 @@ export default class DataFetch {
|
|
|
167
171
|
loading: true,
|
|
168
172
|
cursors: [],
|
|
169
173
|
cursor: null,
|
|
174
|
+
sortOrder,
|
|
175
|
+
sortColumn,
|
|
170
176
|
}))
|
|
171
177
|
|
|
172
178
|
// Actually fetch data
|
|
@@ -180,6 +186,7 @@ export default class DataFetch {
|
|
|
180
186
|
info: page.info,
|
|
181
187
|
cursors: paginate && page.hasNextPage ? [null, page.cursor] : [null],
|
|
182
188
|
error: page.error,
|
|
189
|
+
resetKey: Math.random(),
|
|
183
190
|
}))
|
|
184
191
|
}
|
|
185
192
|
|
|
@@ -189,23 +196,22 @@ export default class DataFetch {
|
|
|
189
196
|
async getPage() {
|
|
190
197
|
const { sortColumn, sortOrder, sortType, limit } = this.options
|
|
191
198
|
const { query } = get(this.store)
|
|
192
|
-
const features = get(this.featureStore)
|
|
193
199
|
|
|
194
200
|
// Get the actual data
|
|
195
201
|
let { rows, info, hasNextPage, cursor, error } = await this.getData()
|
|
196
202
|
|
|
197
203
|
// If we don't support searching, do a client search
|
|
198
|
-
if (!features.supportsSearch) {
|
|
204
|
+
if (!this.features.supportsSearch) {
|
|
199
205
|
rows = runLuceneQuery(rows, query)
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
// If we don't support sorting, do a client-side sort
|
|
203
|
-
if (!features.supportsSort) {
|
|
209
|
+
if (!this.features.supportsSort) {
|
|
204
210
|
rows = luceneSort(rows, sortColumn, sortOrder, sortType)
|
|
205
211
|
}
|
|
206
212
|
|
|
207
213
|
// If we don't support pagination, do a client-side limit
|
|
208
|
-
if (!features.supportsPagination) {
|
|
214
|
+
if (!this.features.supportsPagination) {
|
|
209
215
|
rows = luceneLimit(rows, limit)
|
|
210
216
|
}
|
|
211
217
|
|
package/src/fetch/QueryFetch.js
CHANGED
|
@@ -31,7 +31,7 @@ export default class QueryFetch extends DataFetch {
|
|
|
31
31
|
|
|
32
32
|
async getData() {
|
|
33
33
|
const { datasource, limit, paginate } = this.options
|
|
34
|
-
const { supportsPagination } =
|
|
34
|
+
const { supportsPagination } = this.features
|
|
35
35
|
const { cursor, definition } = get(this.store)
|
|
36
36
|
const type = definition?.fields?.pagination?.type
|
|
37
37
|
|
package/src/themes/midnight.css
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
--spectrum-global-color-gray-800: hsl(var(--hue), var(--sat), 85%);
|
|
14
14
|
--spectrum-global-color-gray-900: hsl(var(--hue), var(--sat), 95%);
|
|
15
15
|
|
|
16
|
+
/* Custom additions */
|
|
16
17
|
--modal-background: var(--spectrum-global-color-gray-50);
|
|
18
|
+
--drop-shadow: rgba(0, 0, 0, 0.25) !important;
|
|
19
|
+
--spectrum-global-color-blue-100: rgba(35, 40, 50) !important;
|
|
17
20
|
}
|
|
18
21
|
|
package/src/themes/nord.css
CHANGED
|
@@ -46,5 +46,8 @@
|
|
|
46
46
|
--spectrum-alias-highlight-active: rgba(169, 177, 193, 0.1);
|
|
47
47
|
--spectrum-alias-background-color-hover-overlay: rgba(169, 177, 193, 0.1);
|
|
48
48
|
|
|
49
|
+
/* Custom additions */
|
|
49
50
|
--modal-background: var(--spectrum-global-color-gray-50);
|
|
51
|
+
--drop-shadow: rgba(0, 0, 0, 0.15) !important;
|
|
52
|
+
--spectrum-global-color-blue-100: rgb(56, 65, 84) !important;
|
|
50
53
|
}
|
package/src/utils/utils.js
CHANGED
|
@@ -86,3 +86,23 @@ export const throttle = (callback, minDelay = 1000) => {
|
|
|
86
86
|
}
|
|
87
87
|
return invoke
|
|
88
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Utility to debounce DOM activities using requestAnimationFrame
|
|
92
|
+
* @param callback the function to run
|
|
93
|
+
* @returns {Function}
|
|
94
|
+
*/
|
|
95
|
+
export const domDebounce = callback => {
|
|
96
|
+
let active = false
|
|
97
|
+
let lastParams
|
|
98
|
+
return (...params) => {
|
|
99
|
+
lastParams = params
|
|
100
|
+
if (!active) {
|
|
101
|
+
active = true
|
|
102
|
+
requestAnimationFrame(() => {
|
|
103
|
+
callback(...lastParams)
|
|
104
|
+
active = false
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|