@budibase/frontend-core 2.5.6-alpha.3 → 2.5.6-alpha.31
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/api/other.js +0 -9
- package/src/components/grid/cells/AttachmentCell.svelte +154 -0
- package/src/components/grid/cells/BooleanCell.svelte +44 -0
- package/src/components/grid/cells/DataCell.svelte +88 -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 +162 -0
- package/src/components/grid/cells/GutterCell.svelte +127 -0
- package/src/components/grid/cells/HeaderCell.svelte +246 -0
- package/src/components/grid/cells/JSONCell.svelte +36 -0
- package/src/components/grid/cells/LongFormCell.svelte +120 -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 +240 -0
- package/src/components/grid/cells/RelationshipCell.svelte +504 -0
- package/src/components/grid/cells/TextCell.svelte +111 -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/ColumnWidthButton.svelte +91 -0
- package/src/components/grid/controls/DeleteButton.svelte +66 -0
- package/src/components/grid/controls/HideColumnsButton.svelte +95 -0
- package/src/components/grid/controls/RowHeightButton.svelte +70 -0
- package/src/components/grid/controls/SortButton.svelte +116 -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 +254 -0
- package/src/components/grid/layout/GridBody.svelte +73 -0
- package/src/components/grid/layout/GridRow.svelte +57 -0
- package/src/components/grid/layout/GridScrollWrapper.svelte +81 -0
- package/src/components/grid/layout/HeaderRow.svelte +62 -0
- package/src/components/grid/layout/NewRow.svelte +265 -0
- package/src/components/grid/layout/RowCount.svelte +9 -0
- package/src/components/grid/layout/StickyColumn.svelte +171 -0
- package/src/components/grid/layout/UserAvatars.svelte +20 -0
- package/src/components/grid/lib/constants.js +14 -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 +223 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +96 -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 +467 -0
- package/src/components/grid/stores/scroll.js +201 -0
- package/src/components/grid/stores/ui.js +159 -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 +145 -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,206 @@
|
|
|
1
|
+
import { derived, get, writable } from "svelte/store"
|
|
2
|
+
import { cloneDeep } from "lodash/fp"
|
|
3
|
+
import { GutterWidth, DefaultColumnWidth } from "../lib/constants"
|
|
4
|
+
|
|
5
|
+
export const createStores = () => {
|
|
6
|
+
const columns = writable([])
|
|
7
|
+
const stickyColumn = writable(null)
|
|
8
|
+
|
|
9
|
+
// Derive an enriched version of columns with left offsets and indexes
|
|
10
|
+
// automatically calculated
|
|
11
|
+
const enrichedColumns = derived(
|
|
12
|
+
columns,
|
|
13
|
+
$columns => {
|
|
14
|
+
let offset = 0
|
|
15
|
+
return $columns.map(column => {
|
|
16
|
+
const enriched = {
|
|
17
|
+
...column,
|
|
18
|
+
left: offset,
|
|
19
|
+
}
|
|
20
|
+
if (column.visible) {
|
|
21
|
+
offset += column.width
|
|
22
|
+
}
|
|
23
|
+
return enriched
|
|
24
|
+
})
|
|
25
|
+
},
|
|
26
|
+
[]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
// Derived list of columns which have not been explicitly hidden
|
|
30
|
+
const visibleColumns = derived(
|
|
31
|
+
enrichedColumns,
|
|
32
|
+
$columns => {
|
|
33
|
+
return $columns.filter(col => col.visible)
|
|
34
|
+
},
|
|
35
|
+
[]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
columns: {
|
|
40
|
+
...columns,
|
|
41
|
+
subscribe: enrichedColumns.subscribe,
|
|
42
|
+
},
|
|
43
|
+
stickyColumn,
|
|
44
|
+
visibleColumns,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const deriveStores = context => {
|
|
49
|
+
const { table, columns, stickyColumn, API, dispatch } = context
|
|
50
|
+
|
|
51
|
+
// Updates the tables primary display column
|
|
52
|
+
const changePrimaryDisplay = async column => {
|
|
53
|
+
return await saveTable({
|
|
54
|
+
...get(table),
|
|
55
|
+
primaryDisplay: column,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Persists column changes by saving metadata against table schema
|
|
60
|
+
const saveChanges = async () => {
|
|
61
|
+
const $columns = get(columns)
|
|
62
|
+
const $table = get(table)
|
|
63
|
+
const $stickyColumn = get(stickyColumn)
|
|
64
|
+
const newSchema = cloneDeep($table.schema)
|
|
65
|
+
|
|
66
|
+
// Build new updated table schema
|
|
67
|
+
Object.keys(newSchema).forEach(column => {
|
|
68
|
+
// Respect order specified by columns
|
|
69
|
+
const index = $columns.findIndex(x => x.name === column)
|
|
70
|
+
if (index !== -1) {
|
|
71
|
+
newSchema[column].order = index
|
|
72
|
+
} else {
|
|
73
|
+
delete newSchema[column].order
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Copy over metadata
|
|
77
|
+
if (column === $stickyColumn?.name) {
|
|
78
|
+
newSchema[column].visible = true
|
|
79
|
+
newSchema[column].width = $stickyColumn.width || DefaultColumnWidth
|
|
80
|
+
} else {
|
|
81
|
+
newSchema[column].visible = $columns[index]?.visible ?? true
|
|
82
|
+
newSchema[column].width = $columns[index]?.width || DefaultColumnWidth
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
await saveTable({ ...$table, schema: newSchema })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const saveTable = async newTable => {
|
|
90
|
+
// Update local state
|
|
91
|
+
table.set(newTable)
|
|
92
|
+
|
|
93
|
+
// Broadcast event so that we can keep sync with external state
|
|
94
|
+
// (e.g. data section which maintains a list of table definitions)
|
|
95
|
+
dispatch("updatetable", newTable)
|
|
96
|
+
|
|
97
|
+
// Update server
|
|
98
|
+
await API.saveTable(newTable)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
columns: {
|
|
103
|
+
...columns,
|
|
104
|
+
actions: {
|
|
105
|
+
saveChanges,
|
|
106
|
+
saveTable,
|
|
107
|
+
changePrimaryDisplay,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const initialise = context => {
|
|
114
|
+
const { table, columns, stickyColumn, schemaOverrides } = context
|
|
115
|
+
|
|
116
|
+
const schema = derived(
|
|
117
|
+
[table, schemaOverrides],
|
|
118
|
+
([$table, $schemaOverrides]) => {
|
|
119
|
+
let newSchema = $table?.schema
|
|
120
|
+
if (!newSchema) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
Object.keys($schemaOverrides || {}).forEach(field => {
|
|
124
|
+
if (newSchema[field]) {
|
|
125
|
+
newSchema[field] = {
|
|
126
|
+
...newSchema[field],
|
|
127
|
+
...$schemaOverrides[field],
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
return newSchema
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
// Merge new schema fields with existing schema in order to preserve widths
|
|
136
|
+
schema.subscribe($schema => {
|
|
137
|
+
if (!$schema) {
|
|
138
|
+
columns.set([])
|
|
139
|
+
stickyColumn.set(null)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
const $table = get(table)
|
|
143
|
+
|
|
144
|
+
// Find primary display
|
|
145
|
+
let primaryDisplay
|
|
146
|
+
if ($table.primaryDisplay && $schema[$table.primaryDisplay]) {
|
|
147
|
+
primaryDisplay = $table.primaryDisplay
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get field list
|
|
151
|
+
let fields = []
|
|
152
|
+
Object.keys($schema).forEach(field => {
|
|
153
|
+
if (field !== primaryDisplay) {
|
|
154
|
+
fields.push(field)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Update columns, removing extraneous columns and adding missing ones
|
|
159
|
+
columns.set(
|
|
160
|
+
fields
|
|
161
|
+
.map(field => ({
|
|
162
|
+
name: field,
|
|
163
|
+
label: $schema[field].name || field,
|
|
164
|
+
schema: $schema[field],
|
|
165
|
+
width: $schema[field].width || DefaultColumnWidth,
|
|
166
|
+
visible: $schema[field].visible ?? true,
|
|
167
|
+
order: $schema[field].order,
|
|
168
|
+
}))
|
|
169
|
+
.sort((a, b) => {
|
|
170
|
+
// Sort by order first
|
|
171
|
+
const orderA = a.order
|
|
172
|
+
const orderB = b.order
|
|
173
|
+
if (orderA != null && orderB != null) {
|
|
174
|
+
return orderA < orderB ? -1 : 1
|
|
175
|
+
} else if (orderA != null) {
|
|
176
|
+
return -1
|
|
177
|
+
} else if (orderB != null) {
|
|
178
|
+
return 1
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Then sort by auto columns
|
|
182
|
+
const autoColA = a.schema?.autocolumn
|
|
183
|
+
const autoColB = b.schema?.autocolumn
|
|
184
|
+
if (autoColA === autoColB) {
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
return autoColA ? 1 : -1
|
|
188
|
+
})
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
// Update sticky column
|
|
192
|
+
if (!primaryDisplay) {
|
|
193
|
+
stickyColumn.set(null)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
stickyColumn.set({
|
|
197
|
+
name: primaryDisplay,
|
|
198
|
+
label: $schema[primaryDisplay].name || primaryDisplay,
|
|
199
|
+
schema: $schema[primaryDisplay],
|
|
200
|
+
width: $schema[primaryDisplay].width || DefaultColumnWidth,
|
|
201
|
+
visible: true,
|
|
202
|
+
order: 0,
|
|
203
|
+
left: GutterWidth,
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as Bounds from "./bounds"
|
|
2
|
+
import * as Columns from "./columns"
|
|
3
|
+
import * as Menu from "./menu"
|
|
4
|
+
import * as Pagination from "./pagination"
|
|
5
|
+
import * as Reorder from "./reorder"
|
|
6
|
+
import * as Resize from "./resize"
|
|
7
|
+
import * as Rows from "./rows"
|
|
8
|
+
import * as Scroll from "./scroll"
|
|
9
|
+
import * as UI from "./ui"
|
|
10
|
+
import * as Users from "./users"
|
|
11
|
+
import * as Validation from "./validation"
|
|
12
|
+
import * as Viewport from "./viewport"
|
|
13
|
+
import * as Clipboard from "./clipboard"
|
|
14
|
+
|
|
15
|
+
const DependencyOrderedStores = [
|
|
16
|
+
Bounds,
|
|
17
|
+
Scroll,
|
|
18
|
+
Rows,
|
|
19
|
+
Columns,
|
|
20
|
+
UI,
|
|
21
|
+
Validation,
|
|
22
|
+
Resize,
|
|
23
|
+
Viewport,
|
|
24
|
+
Reorder,
|
|
25
|
+
Users,
|
|
26
|
+
Menu,
|
|
27
|
+
Pagination,
|
|
28
|
+
Clipboard,
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
export const attachStores = context => {
|
|
32
|
+
// Atomic store creation
|
|
33
|
+
for (let store of DependencyOrderedStores) {
|
|
34
|
+
context = { ...context, ...store.createStores?.(context) }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Derived store creation
|
|
38
|
+
for (let store of DependencyOrderedStores) {
|
|
39
|
+
context = { ...context, ...store.deriveStores?.(context) }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Initialise any store logic
|
|
43
|
+
for (let store of DependencyOrderedStores) {
|
|
44
|
+
store.initialise?.(context)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return context
|
|
48
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { writable, get } from "svelte/store"
|
|
2
|
+
import { GutterWidth } from "../lib/constants"
|
|
3
|
+
|
|
4
|
+
export const createStores = () => {
|
|
5
|
+
const menu = writable({
|
|
6
|
+
x: 0,
|
|
7
|
+
y: 0,
|
|
8
|
+
visible: false,
|
|
9
|
+
selectedRow: null,
|
|
10
|
+
})
|
|
11
|
+
return {
|
|
12
|
+
menu,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const deriveStores = context => {
|
|
17
|
+
const { menu, bounds, focusedCellId, stickyColumn, rowHeight } = context
|
|
18
|
+
|
|
19
|
+
const open = (cellId, e) => {
|
|
20
|
+
const $bounds = get(bounds)
|
|
21
|
+
const $stickyColumn = get(stickyColumn)
|
|
22
|
+
const $rowHeight = get(rowHeight)
|
|
23
|
+
e.preventDefault()
|
|
24
|
+
focusedCellId.set(cellId)
|
|
25
|
+
menu.set({
|
|
26
|
+
left:
|
|
27
|
+
e.clientX - $bounds.left + GutterWidth + ($stickyColumn?.width || 0),
|
|
28
|
+
top: e.clientY - $bounds.top + $rowHeight,
|
|
29
|
+
visible: true,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const close = () => {
|
|
34
|
+
menu.update(state => ({
|
|
35
|
+
...state,
|
|
36
|
+
visible: false,
|
|
37
|
+
}))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
menu: {
|
|
42
|
+
...menu,
|
|
43
|
+
actions: {
|
|
44
|
+
open,
|
|
45
|
+
close,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { derived } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
export const initialise = context => {
|
|
4
|
+
const { scrolledRowCount, rows, visualRowCapacity } = context
|
|
5
|
+
|
|
6
|
+
// Derive how many rows we have in total
|
|
7
|
+
const rowCount = derived(rows, $rows => $rows.length, 0)
|
|
8
|
+
|
|
9
|
+
// Derive how many rows we have available to scroll
|
|
10
|
+
const remainingRows = derived(
|
|
11
|
+
[scrolledRowCount, rowCount, visualRowCapacity],
|
|
12
|
+
([$scrolledRowCount, $rowCount, $visualRowCapacity]) => {
|
|
13
|
+
return Math.max(0, $rowCount - $scrolledRowCount - $visualRowCapacity)
|
|
14
|
+
},
|
|
15
|
+
100
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// Fetch next page when fewer than 25 remaining rows to scroll
|
|
19
|
+
remainingRows.subscribe(remaining => {
|
|
20
|
+
if (remaining < 25) {
|
|
21
|
+
rows.actions.loadNextPage()
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { get, writable, derived } from "svelte/store"
|
|
2
|
+
|
|
3
|
+
const reorderInitialState = {
|
|
4
|
+
sourceColumn: null,
|
|
5
|
+
targetColumn: null,
|
|
6
|
+
breakpoints: [],
|
|
7
|
+
initialMouseX: null,
|
|
8
|
+
scrollLeft: 0,
|
|
9
|
+
gridLeft: 0,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const createStores = () => {
|
|
13
|
+
const reorder = writable(reorderInitialState)
|
|
14
|
+
const isReordering = derived(
|
|
15
|
+
reorder,
|
|
16
|
+
$reorder => !!$reorder.sourceColumn,
|
|
17
|
+
false
|
|
18
|
+
)
|
|
19
|
+
return {
|
|
20
|
+
reorder,
|
|
21
|
+
isReordering,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const deriveStores = context => {
|
|
26
|
+
const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } =
|
|
27
|
+
context
|
|
28
|
+
|
|
29
|
+
// Callback when dragging on a colum header and starting reordering
|
|
30
|
+
const startReordering = (column, e) => {
|
|
31
|
+
const $visibleColumns = get(visibleColumns)
|
|
32
|
+
const $bounds = get(bounds)
|
|
33
|
+
const $scroll = get(scroll)
|
|
34
|
+
const $stickyColumn = get(stickyColumn)
|
|
35
|
+
ui.actions.blur()
|
|
36
|
+
|
|
37
|
+
// Generate new breakpoints for the current columns
|
|
38
|
+
let breakpoints = $visibleColumns.map(col => ({
|
|
39
|
+
x: col.left + col.width,
|
|
40
|
+
column: col.name,
|
|
41
|
+
}))
|
|
42
|
+
if ($stickyColumn) {
|
|
43
|
+
breakpoints.unshift({
|
|
44
|
+
x: 0,
|
|
45
|
+
column: $stickyColumn.name,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Update state
|
|
50
|
+
reorder.set({
|
|
51
|
+
sourceColumn: column,
|
|
52
|
+
targetColumn: null,
|
|
53
|
+
breakpoints,
|
|
54
|
+
initialMouseX: e.clientX,
|
|
55
|
+
scrollLeft: $scroll.left,
|
|
56
|
+
gridLeft: $bounds.left,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Add listeners to handle mouse movement
|
|
60
|
+
document.addEventListener("mousemove", onReorderMouseMove)
|
|
61
|
+
document.addEventListener("mouseup", stopReordering)
|
|
62
|
+
|
|
63
|
+
// Trigger a move event immediately so ensure a candidate column is chosen
|
|
64
|
+
onReorderMouseMove(e)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Callback when moving the mouse when reordering columns
|
|
68
|
+
const onReorderMouseMove = e => {
|
|
69
|
+
const $reorder = get(reorder)
|
|
70
|
+
|
|
71
|
+
// Compute the closest breakpoint to the current position
|
|
72
|
+
let targetColumn
|
|
73
|
+
let minDistance = Number.MAX_SAFE_INTEGER
|
|
74
|
+
const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft
|
|
75
|
+
$reorder.breakpoints.forEach(point => {
|
|
76
|
+
const distance = Math.abs(point.x - mouseX)
|
|
77
|
+
if (distance < minDistance) {
|
|
78
|
+
minDistance = distance
|
|
79
|
+
targetColumn = point.column
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (targetColumn !== $reorder.targetColumn) {
|
|
84
|
+
reorder.update(state => ({
|
|
85
|
+
...state,
|
|
86
|
+
targetColumn,
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Callback when stopping reordering columns
|
|
92
|
+
const stopReordering = async () => {
|
|
93
|
+
// Swap position of columns
|
|
94
|
+
let { sourceColumn, targetColumn } = get(reorder)
|
|
95
|
+
moveColumn(sourceColumn, targetColumn)
|
|
96
|
+
|
|
97
|
+
// Reset state
|
|
98
|
+
reorder.set(reorderInitialState)
|
|
99
|
+
|
|
100
|
+
// Remove event handlers
|
|
101
|
+
document.removeEventListener("mousemove", onReorderMouseMove)
|
|
102
|
+
document.removeEventListener("mouseup", stopReordering)
|
|
103
|
+
|
|
104
|
+
// Save column changes
|
|
105
|
+
await columns.actions.saveChanges()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Moves a column after another columns.
|
|
109
|
+
// An undefined target column will move the source to index 0.
|
|
110
|
+
const moveColumn = (sourceColumn, targetColumn) => {
|
|
111
|
+
let $columns = get(columns)
|
|
112
|
+
let sourceIdx = $columns.findIndex(x => x.name === sourceColumn)
|
|
113
|
+
let targetIdx = $columns.findIndex(x => x.name === targetColumn)
|
|
114
|
+
targetIdx++
|
|
115
|
+
columns.update(state => {
|
|
116
|
+
const removed = state.splice(sourceIdx, 1)
|
|
117
|
+
if (--targetIdx < sourceIdx) {
|
|
118
|
+
targetIdx++
|
|
119
|
+
}
|
|
120
|
+
state.splice(targetIdx, 0, removed[0])
|
|
121
|
+
return state.slice()
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Moves a column one place left (as appears visually)
|
|
126
|
+
const moveColumnLeft = async column => {
|
|
127
|
+
const $visibleColumns = get(visibleColumns)
|
|
128
|
+
const sourceIdx = $visibleColumns.findIndex(x => x.name === column)
|
|
129
|
+
moveColumn(column, $visibleColumns[sourceIdx - 2]?.name)
|
|
130
|
+
await columns.actions.saveChanges()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Moves a column one place right (as appears visually)
|
|
134
|
+
const moveColumnRight = async column => {
|
|
135
|
+
const $visibleColumns = get(visibleColumns)
|
|
136
|
+
const sourceIdx = $visibleColumns.findIndex(x => x.name === column)
|
|
137
|
+
if (sourceIdx === $visibleColumns.length - 1) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
moveColumn(column, $visibleColumns[sourceIdx + 1]?.name)
|
|
141
|
+
await columns.actions.saveChanges()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
reorder: {
|
|
146
|
+
...reorder,
|
|
147
|
+
actions: {
|
|
148
|
+
startReordering,
|
|
149
|
+
stopReordering,
|
|
150
|
+
moveColumnLeft,
|
|
151
|
+
moveColumnRight,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { writable, get, derived } from "svelte/store"
|
|
2
|
+
import { MinColumnWidth, DefaultColumnWidth } from "../lib/constants"
|
|
3
|
+
|
|
4
|
+
const initialState = {
|
|
5
|
+
initialMouseX: null,
|
|
6
|
+
initialWidth: null,
|
|
7
|
+
column: null,
|
|
8
|
+
columnIdx: null,
|
|
9
|
+
width: 0,
|
|
10
|
+
left: 0,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const createStores = () => {
|
|
14
|
+
const resize = writable(initialState)
|
|
15
|
+
const isResizing = derived(resize, $resize => $resize.column != null, false)
|
|
16
|
+
return {
|
|
17
|
+
resize,
|
|
18
|
+
isResizing,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const deriveStores = context => {
|
|
23
|
+
const { resize, columns, stickyColumn, ui } = context
|
|
24
|
+
|
|
25
|
+
// Starts resizing a certain column
|
|
26
|
+
const startResizing = (column, e) => {
|
|
27
|
+
// Prevent propagation to stop reordering triggering
|
|
28
|
+
e.stopPropagation()
|
|
29
|
+
ui.actions.blur()
|
|
30
|
+
|
|
31
|
+
// Find and cache index
|
|
32
|
+
let columnIdx = get(columns).findIndex(col => col.name === column.name)
|
|
33
|
+
if (columnIdx === -1) {
|
|
34
|
+
columnIdx = "sticky"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Set initial store state
|
|
38
|
+
resize.set({
|
|
39
|
+
width: column.width,
|
|
40
|
+
left: column.left,
|
|
41
|
+
initialWidth: column.width,
|
|
42
|
+
initialMouseX: e.clientX,
|
|
43
|
+
column: column.name,
|
|
44
|
+
columnIdx,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Add mouse event listeners to handle resizing
|
|
48
|
+
document.addEventListener("mousemove", onResizeMouseMove)
|
|
49
|
+
document.addEventListener("mouseup", stopResizing)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Handler for moving the mouse to resize columns
|
|
53
|
+
const onResizeMouseMove = e => {
|
|
54
|
+
const { initialMouseX, initialWidth, width, columnIdx } = get(resize)
|
|
55
|
+
const dx = e.clientX - initialMouseX
|
|
56
|
+
const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx))
|
|
57
|
+
|
|
58
|
+
// Ignore small changes
|
|
59
|
+
if (Math.abs(width - newWidth) < 5) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Update column state
|
|
64
|
+
if (columnIdx === "sticky") {
|
|
65
|
+
stickyColumn.update(state => ({
|
|
66
|
+
...state,
|
|
67
|
+
width: newWidth,
|
|
68
|
+
}))
|
|
69
|
+
} else {
|
|
70
|
+
columns.update(state => {
|
|
71
|
+
state[columnIdx].width = newWidth
|
|
72
|
+
return [...state]
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Update state
|
|
77
|
+
resize.update(state => ({
|
|
78
|
+
...state,
|
|
79
|
+
width: newWidth,
|
|
80
|
+
}))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Stop resizing any columns
|
|
84
|
+
const stopResizing = async () => {
|
|
85
|
+
// Reset state
|
|
86
|
+
const $resize = get(resize)
|
|
87
|
+
resize.set(initialState)
|
|
88
|
+
document.removeEventListener("mousemove", onResizeMouseMove)
|
|
89
|
+
document.removeEventListener("mouseup", stopResizing)
|
|
90
|
+
|
|
91
|
+
// Persist width if it changed
|
|
92
|
+
if ($resize.width !== $resize.initialWidth) {
|
|
93
|
+
await columns.actions.saveChanges()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Resets a column size back to default
|
|
98
|
+
const resetSize = async column => {
|
|
99
|
+
const $stickyColumn = get(stickyColumn)
|
|
100
|
+
if (column.name === $stickyColumn?.name) {
|
|
101
|
+
stickyColumn.update(state => ({
|
|
102
|
+
...state,
|
|
103
|
+
width: DefaultColumnWidth,
|
|
104
|
+
}))
|
|
105
|
+
} else {
|
|
106
|
+
columns.update(state => {
|
|
107
|
+
const columnIdx = state.findIndex(x => x.name === column.name)
|
|
108
|
+
state[columnIdx].width = DefaultColumnWidth
|
|
109
|
+
return [...state]
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
await columns.actions.saveChanges()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
resize: {
|
|
117
|
+
...resize,
|
|
118
|
+
actions: {
|
|
119
|
+
startResizing,
|
|
120
|
+
resetSize,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
}
|