@budibase/frontend-core 2.23.12 → 2.24.0
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 +5 -5
- package/src/api/backups.js +0 -3
- package/src/components/grid/cells/AttachmentCell.svelte +27 -28
- package/src/components/grid/cells/BBReferenceCell.svelte +6 -3
- package/src/components/grid/cells/DateCell.svelte +90 -60
- package/src/components/grid/cells/GridCell.svelte +3 -0
- package/src/components/grid/cells/HeaderCell.svelte +99 -102
- package/src/components/grid/cells/LongFormCell.svelte +30 -34
- package/src/components/grid/cells/OptionsCell.svelte +20 -35
- package/src/components/grid/cells/RelationshipCell.svelte +17 -64
- package/src/components/grid/controls/HideColumnsButton.svelte +1 -0
- package/src/components/grid/controls/MigrationModal.svelte +7 -3
- package/src/components/grid/layout/Grid.svelte +13 -7
- package/src/components/grid/layout/GridScrollWrapper.svelte +4 -0
- package/src/components/grid/layout/NewColumnButton.svelte +23 -21
- package/src/components/grid/layout/NewRow.svelte +6 -1
- package/src/components/grid/lib/constants.js +9 -4
- package/src/components/grid/lib/utils.js +7 -0
- package/src/components/grid/overlays/GridPopover.svelte +71 -0
- package/src/components/grid/overlays/KeyboardManager.svelte +1 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +68 -66
- package/src/components/grid/overlays/PopoverOverlay.svelte +9 -0
- package/src/components/grid/overlays/ResizeOverlay.svelte +2 -0
- package/src/components/grid/overlays/ScrollOverlay.svelte +10 -14
- package/src/components/grid/stores/columns.js +44 -20
- package/src/components/grid/stores/menu.js +2 -2
- package/src/components/grid/stores/reorder.js +26 -16
- package/src/components/grid/stores/resize.js +13 -2
- package/src/components/grid/stores/rows.js +41 -8
- package/src/components/grid/stores/ui.js +1 -1
- package/src/components/grid/stores/viewport.js +4 -5
- package/src/constants.js +3 -3
- package/src/utils/rows.js +4 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Popover, clickOutside } from "@budibase/bbui"
|
|
3
|
+
import { createEventDispatcher, getContext } from "svelte"
|
|
4
|
+
import {
|
|
5
|
+
PopoverMinWidth,
|
|
6
|
+
PopoverMaxWidth,
|
|
7
|
+
PopoverMaxHeight,
|
|
8
|
+
} from "../lib/constants"
|
|
9
|
+
|
|
10
|
+
export let anchor
|
|
11
|
+
export let minWidth = PopoverMinWidth
|
|
12
|
+
export let maxWidth = PopoverMaxWidth
|
|
13
|
+
export let maxHeight = PopoverMaxHeight
|
|
14
|
+
export let align = "left"
|
|
15
|
+
export let open = true
|
|
16
|
+
export let resizable = false
|
|
17
|
+
export let wrap = true
|
|
18
|
+
|
|
19
|
+
const { gridID } = getContext("grid")
|
|
20
|
+
const dispatch = createEventDispatcher()
|
|
21
|
+
|
|
22
|
+
$: style = buildStyles(minWidth, maxWidth, maxHeight)
|
|
23
|
+
|
|
24
|
+
const buildStyles = (minWidth, maxWidth, maxHeight) => {
|
|
25
|
+
let style = ""
|
|
26
|
+
if (minWidth != null) {
|
|
27
|
+
style += `min-width: ${minWidth}px;`
|
|
28
|
+
}
|
|
29
|
+
if (maxWidth != null) {
|
|
30
|
+
style += `max-width: ${maxWidth}px;`
|
|
31
|
+
}
|
|
32
|
+
if (maxHeight != null) {
|
|
33
|
+
style += `max-height: ${maxHeight}px;`
|
|
34
|
+
}
|
|
35
|
+
return style
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<Popover
|
|
40
|
+
{open}
|
|
41
|
+
{anchor}
|
|
42
|
+
{align}
|
|
43
|
+
{resizable}
|
|
44
|
+
{wrap}
|
|
45
|
+
portalTarget="#{gridID} .grid-popover-container"
|
|
46
|
+
offset={0}
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
class="grid-popover-contents"
|
|
50
|
+
{style}
|
|
51
|
+
use:clickOutside={() => dispatch("close")}
|
|
52
|
+
on:wheel={e => e.stopPropagation()}
|
|
53
|
+
>
|
|
54
|
+
<slot />
|
|
55
|
+
</div>
|
|
56
|
+
</Popover>
|
|
57
|
+
|
|
58
|
+
<style>
|
|
59
|
+
:global(.grid-popover-container .spectrum-Popover) {
|
|
60
|
+
background: var(--grid-background);
|
|
61
|
+
min-width: none;
|
|
62
|
+
max-width: none;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
}
|
|
65
|
+
.grid-popover-contents {
|
|
66
|
+
overflow-y: auto;
|
|
67
|
+
overflow-x: hidden;
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
}
|
|
71
|
+
</style>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import {
|
|
2
|
+
import { Menu, MenuItem, Helpers } from "@budibase/bbui"
|
|
3
3
|
import { getContext } from "svelte"
|
|
4
4
|
import { NewRowID } from "../lib/constants"
|
|
5
|
+
import GridPopover from "./GridPopover.svelte"
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
focusedRow,
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
isDatasourcePlus,
|
|
21
22
|
} = getContext("grid")
|
|
22
23
|
|
|
24
|
+
let anchor
|
|
25
|
+
|
|
23
26
|
$: style = makeStyle($menu)
|
|
24
27
|
$: isNewRow = $focusedRowId === NewRowID
|
|
25
28
|
|
|
@@ -48,75 +51,74 @@
|
|
|
48
51
|
}
|
|
49
52
|
</script>
|
|
50
53
|
|
|
54
|
+
<div bind:this={anchor} {style} class="menu-anchor" />
|
|
55
|
+
|
|
51
56
|
{#if $menu.visible}
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
57
|
+
{#key style}
|
|
58
|
+
<GridPopover {anchor} on:close={menu.actions.close} maxHeight={null}>
|
|
59
|
+
<Menu>
|
|
60
|
+
<MenuItem
|
|
61
|
+
icon="Copy"
|
|
62
|
+
on:click={clipboard.actions.copy}
|
|
63
|
+
on:click={menu.actions.close}
|
|
64
|
+
>
|
|
65
|
+
Copy
|
|
66
|
+
</MenuItem>
|
|
67
|
+
<MenuItem
|
|
68
|
+
icon="Paste"
|
|
69
|
+
disabled={$copiedCell == null || $focusedCellAPI?.isReadonly()}
|
|
70
|
+
on:click={clipboard.actions.paste}
|
|
71
|
+
on:click={menu.actions.close}
|
|
72
|
+
>
|
|
73
|
+
Paste
|
|
74
|
+
</MenuItem>
|
|
75
|
+
<MenuItem
|
|
76
|
+
icon="Maximize"
|
|
77
|
+
disabled={isNewRow || !$config.canEditRows || !$config.canExpandRows}
|
|
78
|
+
on:click={() => dispatch("edit-row", $focusedRow)}
|
|
79
|
+
on:click={menu.actions.close}
|
|
80
|
+
>
|
|
81
|
+
Edit row in modal
|
|
82
|
+
</MenuItem>
|
|
83
|
+
<MenuItem
|
|
84
|
+
icon="Copy"
|
|
85
|
+
disabled={isNewRow || !$focusedRow?._id || !$isDatasourcePlus}
|
|
86
|
+
on:click={() => copyToClipboard($focusedRow?._id)}
|
|
87
|
+
on:click={menu.actions.close}
|
|
88
|
+
>
|
|
89
|
+
Copy row _id
|
|
90
|
+
</MenuItem>
|
|
91
|
+
<MenuItem
|
|
92
|
+
icon="Copy"
|
|
93
|
+
disabled={isNewRow || !$focusedRow?._rev}
|
|
94
|
+
on:click={() => copyToClipboard($focusedRow?._rev)}
|
|
95
|
+
on:click={menu.actions.close}
|
|
96
|
+
>
|
|
97
|
+
Copy row _rev
|
|
98
|
+
</MenuItem>
|
|
99
|
+
<MenuItem
|
|
100
|
+
icon="Duplicate"
|
|
101
|
+
disabled={isNewRow || !$config.canAddRows}
|
|
102
|
+
on:click={duplicate}
|
|
103
|
+
>
|
|
104
|
+
Duplicate row
|
|
105
|
+
</MenuItem>
|
|
106
|
+
<MenuItem
|
|
107
|
+
icon="Delete"
|
|
108
|
+
disabled={isNewRow || !$config.canDeleteRows}
|
|
109
|
+
on:click={deleteRow}
|
|
110
|
+
>
|
|
111
|
+
Delete row
|
|
112
|
+
</MenuItem>
|
|
113
|
+
</Menu>
|
|
114
|
+
</GridPopover>
|
|
115
|
+
{/key}
|
|
109
116
|
{/if}
|
|
110
117
|
|
|
111
118
|
<style>
|
|
112
|
-
.menu {
|
|
119
|
+
.menu-anchor {
|
|
120
|
+
opacity: 0;
|
|
121
|
+
pointer-events: none;
|
|
113
122
|
position: absolute;
|
|
114
|
-
background: var(--cell-background);
|
|
115
|
-
border: 1px solid var(--spectrum-global-color-gray-300);
|
|
116
|
-
width: 180px;
|
|
117
|
-
border-radius: 4px;
|
|
118
|
-
display: flex;
|
|
119
|
-
flex-direction: column;
|
|
120
|
-
box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
|
|
121
123
|
}
|
|
122
124
|
</style>
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
class="resize-slider"
|
|
22
22
|
class:visible={activeColumn === $stickyColumn.name}
|
|
23
23
|
on:mousedown={e => resize.actions.startResizing($stickyColumn, e)}
|
|
24
|
+
on:touchstart={e => resize.actions.startResizing($stickyColumn, e)}
|
|
24
25
|
on:dblclick={() => resize.actions.resetSize($stickyColumn)}
|
|
25
26
|
style="left:{GutterWidth + $stickyColumn.width}px;"
|
|
26
27
|
>
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
class="resize-slider"
|
|
33
34
|
class:visible={activeColumn === column.name}
|
|
34
35
|
on:mousedown={e => resize.actions.startResizing(column, e)}
|
|
36
|
+
on:touchstart={e => resize.actions.startResizing(column, e)}
|
|
35
37
|
on:dblclick={() => resize.actions.resetSize(column)}
|
|
36
38
|
style={getStyle(column, offset, $scrollLeft)}
|
|
37
39
|
>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { getContext } from "svelte"
|
|
3
3
|
import { domDebounce } from "../../../utils/utils"
|
|
4
4
|
import { DefaultRowHeight, ScrollBarSize } from "../lib/constants"
|
|
5
|
+
import { parseEventLocation } from "../lib/utils"
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
scroll,
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
height,
|
|
18
19
|
isDragging,
|
|
19
20
|
menu,
|
|
21
|
+
focusedCellAPI,
|
|
20
22
|
} = getContext("grid")
|
|
21
23
|
|
|
22
24
|
// State for dragging bars
|
|
@@ -47,33 +49,27 @@
|
|
|
47
49
|
$: barLeft = ScrollBarSize + availWidth * ($scrollLeft / $maxScrollLeft)
|
|
48
50
|
|
|
49
51
|
// Helper to close the context menu if it's open
|
|
50
|
-
const
|
|
52
|
+
const closePopovers = () => {
|
|
51
53
|
if ($menu.visible) {
|
|
52
54
|
menu.actions.close()
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const getLocation = e => {
|
|
57
|
-
return {
|
|
58
|
-
y: e.touches?.[0]?.clientY ?? e.clientY,
|
|
59
|
-
x: e.touches?.[0]?.clientX ?? e.clientX,
|
|
60
|
-
}
|
|
56
|
+
$focusedCellAPI?.blur()
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
// V scrollbar drag handlers
|
|
64
60
|
const startVDragging = e => {
|
|
65
61
|
e.preventDefault()
|
|
66
|
-
initialMouse =
|
|
62
|
+
initialMouse = parseEventLocation(e).y
|
|
67
63
|
initialScroll = $scrollTop
|
|
68
64
|
document.addEventListener("mousemove", moveVDragging)
|
|
69
65
|
document.addEventListener("touchmove", moveVDragging)
|
|
70
66
|
document.addEventListener("mouseup", stopVDragging)
|
|
71
67
|
document.addEventListener("touchend", stopVDragging)
|
|
72
68
|
isDraggingV = true
|
|
73
|
-
|
|
69
|
+
closePopovers()
|
|
74
70
|
}
|
|
75
71
|
const moveVDragging = domDebounce(e => {
|
|
76
|
-
const delta =
|
|
72
|
+
const delta = parseEventLocation(e).y - initialMouse
|
|
77
73
|
const weight = delta / availHeight
|
|
78
74
|
const newScrollTop = initialScroll + weight * $maxScrollTop
|
|
79
75
|
scroll.update(state => ({
|
|
@@ -92,17 +88,17 @@
|
|
|
92
88
|
// H scrollbar drag handlers
|
|
93
89
|
const startHDragging = e => {
|
|
94
90
|
e.preventDefault()
|
|
95
|
-
initialMouse =
|
|
91
|
+
initialMouse = parseEventLocation(e).x
|
|
96
92
|
initialScroll = $scrollLeft
|
|
97
93
|
document.addEventListener("mousemove", moveHDragging)
|
|
98
94
|
document.addEventListener("touchmove", moveHDragging)
|
|
99
95
|
document.addEventListener("mouseup", stopHDragging)
|
|
100
96
|
document.addEventListener("touchend", stopHDragging)
|
|
101
97
|
isDraggingH = true
|
|
102
|
-
|
|
98
|
+
closePopovers()
|
|
103
99
|
}
|
|
104
100
|
const moveHDragging = domDebounce(e => {
|
|
105
|
-
const delta =
|
|
101
|
+
const delta = parseEventLocation(e).x - initialMouse
|
|
106
102
|
const weight = delta / availWidth
|
|
107
103
|
const newScrollLeft = initialScroll + weight * $maxScrollLeft
|
|
108
104
|
scroll.update(state => ({
|
|
@@ -48,22 +48,28 @@ export const createStores = () => {
|
|
|
48
48
|
export const deriveStores = context => {
|
|
49
49
|
const { columns, stickyColumn } = context
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
51
|
+
// Quick access to all columns
|
|
52
|
+
const allColumns = derived(
|
|
53
53
|
[columns, stickyColumn],
|
|
54
54
|
([$columns, $stickyColumn]) => {
|
|
55
55
|
let allCols = $columns || []
|
|
56
56
|
if ($stickyColumn) {
|
|
57
57
|
allCols = [...allCols, $stickyColumn]
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
return !column.schema?.autocolumn
|
|
61
|
-
})
|
|
62
|
-
return normalCols.length > 0
|
|
59
|
+
return allCols
|
|
63
60
|
}
|
|
64
61
|
)
|
|
65
62
|
|
|
63
|
+
// Derive if we have any normal columns
|
|
64
|
+
const hasNonAutoColumn = derived(allColumns, $allColumns => {
|
|
65
|
+
const normalCols = $allColumns.filter(column => {
|
|
66
|
+
return !column.schema?.autocolumn
|
|
67
|
+
})
|
|
68
|
+
return normalCols.length > 0
|
|
69
|
+
})
|
|
70
|
+
|
|
66
71
|
return {
|
|
72
|
+
allColumns,
|
|
67
73
|
hasNonAutoColumn,
|
|
68
74
|
}
|
|
69
75
|
}
|
|
@@ -142,24 +148,26 @@ export const createActions = context => {
|
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
export const initialise = context => {
|
|
145
|
-
const {
|
|
151
|
+
const {
|
|
152
|
+
definition,
|
|
153
|
+
columns,
|
|
154
|
+
stickyColumn,
|
|
155
|
+
allColumns,
|
|
156
|
+
enrichedSchema,
|
|
157
|
+
compact,
|
|
158
|
+
} = context
|
|
146
159
|
|
|
147
160
|
// Merge new schema fields with existing schema in order to preserve widths
|
|
148
|
-
|
|
161
|
+
const processColumns = $enrichedSchema => {
|
|
149
162
|
if (!$enrichedSchema) {
|
|
150
163
|
columns.set([])
|
|
151
164
|
stickyColumn.set(null)
|
|
152
165
|
return
|
|
153
166
|
}
|
|
154
167
|
const $definition = get(definition)
|
|
155
|
-
const $
|
|
168
|
+
const $allColumns = get(allColumns)
|
|
156
169
|
const $stickyColumn = get(stickyColumn)
|
|
157
|
-
|
|
158
|
-
// Generate array of all columns to easily find pre-existing columns
|
|
159
|
-
let allColumns = $columns || []
|
|
160
|
-
if ($stickyColumn) {
|
|
161
|
-
allColumns.push($stickyColumn)
|
|
162
|
-
}
|
|
170
|
+
const $compact = get(compact)
|
|
163
171
|
|
|
164
172
|
// Find primary display
|
|
165
173
|
let primaryDisplay
|
|
@@ -171,7 +179,7 @@ export const initialise = context => {
|
|
|
171
179
|
// Get field list
|
|
172
180
|
let fields = []
|
|
173
181
|
Object.keys($enrichedSchema).forEach(field => {
|
|
174
|
-
if (field !== primaryDisplay) {
|
|
182
|
+
if ($compact || field !== primaryDisplay) {
|
|
175
183
|
fields.push(field)
|
|
176
184
|
}
|
|
177
185
|
})
|
|
@@ -181,7 +189,7 @@ export const initialise = context => {
|
|
|
181
189
|
fields
|
|
182
190
|
.map(field => {
|
|
183
191
|
const fieldSchema = $enrichedSchema[field]
|
|
184
|
-
const oldColumn = allColumns?.find(x => x.name === field)
|
|
192
|
+
const oldColumn = $allColumns?.find(x => x.name === field)
|
|
185
193
|
return {
|
|
186
194
|
name: field,
|
|
187
195
|
label: fieldSchema.displayName || field,
|
|
@@ -189,9 +197,18 @@ export const initialise = context => {
|
|
|
189
197
|
width: fieldSchema.width || oldColumn?.width || DefaultColumnWidth,
|
|
190
198
|
visible: fieldSchema.visible ?? true,
|
|
191
199
|
order: fieldSchema.order ?? oldColumn?.order,
|
|
200
|
+
primaryDisplay: field === primaryDisplay,
|
|
192
201
|
}
|
|
193
202
|
})
|
|
194
203
|
.sort((a, b) => {
|
|
204
|
+
// If we don't have a pinned column then primary display will be in
|
|
205
|
+
// the normal columns list, and should be first
|
|
206
|
+
if (a.name === primaryDisplay) {
|
|
207
|
+
return -1
|
|
208
|
+
} else if (b.name === primaryDisplay) {
|
|
209
|
+
return 1
|
|
210
|
+
}
|
|
211
|
+
|
|
195
212
|
// Sort by order first
|
|
196
213
|
const orderA = a.order
|
|
197
214
|
const orderB = b.order
|
|
@@ -214,12 +231,12 @@ export const initialise = context => {
|
|
|
214
231
|
)
|
|
215
232
|
|
|
216
233
|
// Update sticky column
|
|
217
|
-
if (!primaryDisplay) {
|
|
234
|
+
if ($compact || !primaryDisplay) {
|
|
218
235
|
stickyColumn.set(null)
|
|
219
236
|
return
|
|
220
237
|
}
|
|
221
238
|
const stickySchema = $enrichedSchema[primaryDisplay]
|
|
222
|
-
const oldStickyColumn = allColumns?.find(x => x.name === primaryDisplay)
|
|
239
|
+
const oldStickyColumn = $allColumns?.find(x => x.name === primaryDisplay)
|
|
223
240
|
stickyColumn.set({
|
|
224
241
|
name: primaryDisplay,
|
|
225
242
|
label: stickySchema.displayName || primaryDisplay,
|
|
@@ -228,6 +245,13 @@ export const initialise = context => {
|
|
|
228
245
|
visible: true,
|
|
229
246
|
order: 0,
|
|
230
247
|
left: GutterWidth,
|
|
248
|
+
primaryDisplay: true,
|
|
231
249
|
})
|
|
232
|
-
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Process columns when schema changes
|
|
253
|
+
enrichedSchema.subscribe(processColumns)
|
|
254
|
+
|
|
255
|
+
// Process columns when compact flag changes
|
|
256
|
+
compact.subscribe(() => processColumns(get(enrichedSchema)))
|
|
233
257
|
}
|
|
@@ -13,13 +13,13 @@ export const createStores = () => {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const createActions = context => {
|
|
16
|
-
const { menu, focusedCellId,
|
|
16
|
+
const { menu, focusedCellId, gridID } = context
|
|
17
17
|
|
|
18
18
|
const open = (cellId, e) => {
|
|
19
19
|
e.preventDefault()
|
|
20
20
|
|
|
21
21
|
// Get DOM node for grid data wrapper to compute relative position to
|
|
22
|
-
const gridNode = document.getElementById(
|
|
22
|
+
const gridNode = document.getElementById(gridID)
|
|
23
23
|
const dataNode = gridNode?.getElementsByClassName("grid-data-outer")?.[0]
|
|
24
24
|
if (!dataNode) {
|
|
25
25
|
return
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { get, writable, derived } from "svelte/store"
|
|
2
|
+
import { parseEventLocation } from "../lib/utils"
|
|
2
3
|
|
|
3
4
|
const reorderInitialState = {
|
|
4
5
|
sourceColumn: null,
|
|
@@ -31,8 +32,8 @@ export const createActions = context => {
|
|
|
31
32
|
scroll,
|
|
32
33
|
bounds,
|
|
33
34
|
stickyColumn,
|
|
34
|
-
ui,
|
|
35
35
|
maxScrollLeft,
|
|
36
|
+
width,
|
|
36
37
|
} = context
|
|
37
38
|
|
|
38
39
|
let autoScrollInterval
|
|
@@ -43,7 +44,6 @@ export const createActions = context => {
|
|
|
43
44
|
const $visibleColumns = get(visibleColumns)
|
|
44
45
|
const $bounds = get(bounds)
|
|
45
46
|
const $stickyColumn = get(stickyColumn)
|
|
46
|
-
ui.actions.blur()
|
|
47
47
|
|
|
48
48
|
// Generate new breakpoints for the current columns
|
|
49
49
|
let breakpoints = $visibleColumns.map(col => ({
|
|
@@ -55,6 +55,11 @@ export const createActions = context => {
|
|
|
55
55
|
x: 0,
|
|
56
56
|
column: $stickyColumn.name,
|
|
57
57
|
})
|
|
58
|
+
} else if (!$visibleColumns[0].primaryDisplay) {
|
|
59
|
+
breakpoints.unshift({
|
|
60
|
+
x: 0,
|
|
61
|
+
column: null,
|
|
62
|
+
})
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Update state
|
|
@@ -69,6 +74,9 @@ export const createActions = context => {
|
|
|
69
74
|
// Add listeners to handle mouse movement
|
|
70
75
|
document.addEventListener("mousemove", onReorderMouseMove)
|
|
71
76
|
document.addEventListener("mouseup", stopReordering)
|
|
77
|
+
document.addEventListener("touchmove", onReorderMouseMove)
|
|
78
|
+
document.addEventListener("touchend", stopReordering)
|
|
79
|
+
document.addEventListener("touchcancel", stopReordering)
|
|
72
80
|
|
|
73
81
|
// Trigger a move event immediately so ensure a candidate column is chosen
|
|
74
82
|
onReorderMouseMove(e)
|
|
@@ -77,7 +85,7 @@ export const createActions = context => {
|
|
|
77
85
|
// Callback when moving the mouse when reordering columns
|
|
78
86
|
const onReorderMouseMove = e => {
|
|
79
87
|
// Immediately handle the current position
|
|
80
|
-
const x = e
|
|
88
|
+
const { x } = parseEventLocation(e)
|
|
81
89
|
reorder.update(state => ({
|
|
82
90
|
...state,
|
|
83
91
|
latestX: x,
|
|
@@ -86,8 +94,8 @@ export const createActions = context => {
|
|
|
86
94
|
|
|
87
95
|
// Check if we need to start auto-scrolling
|
|
88
96
|
const $reorder = get(reorder)
|
|
89
|
-
const proximityCutoff = 140
|
|
90
|
-
const speedFactor =
|
|
97
|
+
const proximityCutoff = Math.min(140, get(width) / 6)
|
|
98
|
+
const speedFactor = 16
|
|
91
99
|
const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
|
|
92
100
|
const leftProximity = Math.max(0, x - $reorder.gridLeft)
|
|
93
101
|
if (rightProximity < proximityCutoff) {
|
|
@@ -158,19 +166,22 @@ export const createActions = context => {
|
|
|
158
166
|
// Ensure auto-scrolling is stopped
|
|
159
167
|
stopAutoScroll()
|
|
160
168
|
|
|
161
|
-
// Swap position of columns
|
|
162
|
-
let { sourceColumn, targetColumn } = get(reorder)
|
|
163
|
-
moveColumn(sourceColumn, targetColumn)
|
|
164
|
-
|
|
165
|
-
// Reset state
|
|
166
|
-
reorder.set(reorderInitialState)
|
|
167
|
-
|
|
168
169
|
// Remove event handlers
|
|
169
170
|
document.removeEventListener("mousemove", onReorderMouseMove)
|
|
170
171
|
document.removeEventListener("mouseup", stopReordering)
|
|
172
|
+
document.removeEventListener("touchmove", onReorderMouseMove)
|
|
173
|
+
document.removeEventListener("touchend", stopReordering)
|
|
174
|
+
document.removeEventListener("touchcancel", stopReordering)
|
|
171
175
|
|
|
172
|
-
//
|
|
173
|
-
|
|
176
|
+
// Ensure there's actually a change
|
|
177
|
+
let { sourceColumn, targetColumn } = get(reorder)
|
|
178
|
+
if (sourceColumn !== targetColumn) {
|
|
179
|
+
moveColumn(sourceColumn, targetColumn)
|
|
180
|
+
await columns.actions.saveChanges()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Reset state
|
|
184
|
+
reorder.set(reorderInitialState)
|
|
174
185
|
}
|
|
175
186
|
|
|
176
187
|
// Moves a column after another columns.
|
|
@@ -185,8 +196,7 @@ export const createActions = context => {
|
|
|
185
196
|
if (--targetIdx < sourceIdx) {
|
|
186
197
|
targetIdx++
|
|
187
198
|
}
|
|
188
|
-
state.
|
|
189
|
-
return state.slice()
|
|
199
|
+
return state.toSpliced(targetIdx, 0, removed[0])
|
|
190
200
|
})
|
|
191
201
|
}
|
|
192
202
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { writable, get, derived } from "svelte/store"
|
|
2
2
|
import { MinColumnWidth, DefaultColumnWidth } from "../lib/constants"
|
|
3
|
+
import { parseEventLocation } from "../lib/utils"
|
|
3
4
|
|
|
4
5
|
const initialState = {
|
|
5
6
|
initialMouseX: null,
|
|
@@ -24,8 +25,11 @@ export const createActions = context => {
|
|
|
24
25
|
|
|
25
26
|
// Starts resizing a certain column
|
|
26
27
|
const startResizing = (column, e) => {
|
|
28
|
+
const { x } = parseEventLocation(e)
|
|
29
|
+
|
|
27
30
|
// Prevent propagation to stop reordering triggering
|
|
28
31
|
e.stopPropagation()
|
|
32
|
+
e.preventDefault()
|
|
29
33
|
ui.actions.blur()
|
|
30
34
|
|
|
31
35
|
// Find and cache index
|
|
@@ -39,7 +43,7 @@ export const createActions = context => {
|
|
|
39
43
|
width: column.width,
|
|
40
44
|
left: column.left,
|
|
41
45
|
initialWidth: column.width,
|
|
42
|
-
initialMouseX:
|
|
46
|
+
initialMouseX: x,
|
|
43
47
|
column: column.name,
|
|
44
48
|
columnIdx,
|
|
45
49
|
})
|
|
@@ -47,12 +51,16 @@ export const createActions = context => {
|
|
|
47
51
|
// Add mouse event listeners to handle resizing
|
|
48
52
|
document.addEventListener("mousemove", onResizeMouseMove)
|
|
49
53
|
document.addEventListener("mouseup", stopResizing)
|
|
54
|
+
document.addEventListener("touchmove", onResizeMouseMove)
|
|
55
|
+
document.addEventListener("touchend", stopResizing)
|
|
56
|
+
document.addEventListener("touchcancel", stopResizing)
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
// Handler for moving the mouse to resize columns
|
|
53
60
|
const onResizeMouseMove = e => {
|
|
54
61
|
const { initialMouseX, initialWidth, width, columnIdx } = get(resize)
|
|
55
|
-
const
|
|
62
|
+
const { x } = parseEventLocation(e)
|
|
63
|
+
const dx = x - initialMouseX
|
|
56
64
|
const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx))
|
|
57
65
|
|
|
58
66
|
// Ignore small changes
|
|
@@ -87,6 +95,9 @@ export const createActions = context => {
|
|
|
87
95
|
resize.set(initialState)
|
|
88
96
|
document.removeEventListener("mousemove", onResizeMouseMove)
|
|
89
97
|
document.removeEventListener("mouseup", stopResizing)
|
|
98
|
+
document.removeEventListener("touchmove", onResizeMouseMove)
|
|
99
|
+
document.removeEventListener("touchend", stopResizing)
|
|
100
|
+
document.removeEventListener("touchcancel", stopResizing)
|
|
90
101
|
|
|
91
102
|
// Persist width if it changed
|
|
92
103
|
if ($resize.width !== $resize.initialWidth) {
|