@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.
Files changed (33) hide show
  1. package/package.json +5 -5
  2. package/src/api/backups.js +0 -3
  3. package/src/components/grid/cells/AttachmentCell.svelte +27 -28
  4. package/src/components/grid/cells/BBReferenceCell.svelte +6 -3
  5. package/src/components/grid/cells/DateCell.svelte +90 -60
  6. package/src/components/grid/cells/GridCell.svelte +3 -0
  7. package/src/components/grid/cells/HeaderCell.svelte +99 -102
  8. package/src/components/grid/cells/LongFormCell.svelte +30 -34
  9. package/src/components/grid/cells/OptionsCell.svelte +20 -35
  10. package/src/components/grid/cells/RelationshipCell.svelte +17 -64
  11. package/src/components/grid/controls/HideColumnsButton.svelte +1 -0
  12. package/src/components/grid/controls/MigrationModal.svelte +7 -3
  13. package/src/components/grid/layout/Grid.svelte +13 -7
  14. package/src/components/grid/layout/GridScrollWrapper.svelte +4 -0
  15. package/src/components/grid/layout/NewColumnButton.svelte +23 -21
  16. package/src/components/grid/layout/NewRow.svelte +6 -1
  17. package/src/components/grid/lib/constants.js +9 -4
  18. package/src/components/grid/lib/utils.js +7 -0
  19. package/src/components/grid/overlays/GridPopover.svelte +71 -0
  20. package/src/components/grid/overlays/KeyboardManager.svelte +1 -0
  21. package/src/components/grid/overlays/MenuOverlay.svelte +68 -66
  22. package/src/components/grid/overlays/PopoverOverlay.svelte +9 -0
  23. package/src/components/grid/overlays/ResizeOverlay.svelte +2 -0
  24. package/src/components/grid/overlays/ScrollOverlay.svelte +10 -14
  25. package/src/components/grid/stores/columns.js +44 -20
  26. package/src/components/grid/stores/menu.js +2 -2
  27. package/src/components/grid/stores/reorder.js +26 -16
  28. package/src/components/grid/stores/resize.js +13 -2
  29. package/src/components/grid/stores/rows.js +41 -8
  30. package/src/components/grid/stores/ui.js +1 -1
  31. package/src/components/grid/stores/viewport.js +4 -5
  32. package/src/constants.js +3 -3
  33. 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>
@@ -20,6 +20,7 @@
20
20
 
21
21
  const ignoredOriginSelectors = [
22
22
  ".spectrum-Modal",
23
+ ".date-time-popover",
23
24
  "#builder-side-panel-container",
24
25
  "[data-grid-ignore]",
25
26
  ]
@@ -1,7 +1,8 @@
1
1
  <script>
2
- import { clickOutside, Menu, MenuItem, Helpers } from "@budibase/bbui"
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
- <div class="menu" {style} use:clickOutside={() => menu.actions.close()}>
53
- <Menu>
54
- <MenuItem
55
- icon="Copy"
56
- on:click={clipboard.actions.copy}
57
- on:click={menu.actions.close}
58
- >
59
- Copy
60
- </MenuItem>
61
- <MenuItem
62
- icon="Paste"
63
- disabled={$copiedCell == null || $focusedCellAPI?.isReadonly()}
64
- on:click={clipboard.actions.paste}
65
- on:click={menu.actions.close}
66
- >
67
- Paste
68
- </MenuItem>
69
- <MenuItem
70
- icon="Maximize"
71
- disabled={isNewRow || !$config.canEditRows || !$config.canExpandRows}
72
- on:click={() => dispatch("edit-row", $focusedRow)}
73
- on:click={menu.actions.close}
74
- >
75
- Edit row in modal
76
- </MenuItem>
77
- <MenuItem
78
- icon="Copy"
79
- disabled={isNewRow || !$focusedRow?._id || !$isDatasourcePlus}
80
- on:click={() => copyToClipboard($focusedRow?._id)}
81
- on:click={menu.actions.close}
82
- >
83
- Copy row _id
84
- </MenuItem>
85
- <MenuItem
86
- icon="Copy"
87
- disabled={isNewRow || !$focusedRow?._rev}
88
- on:click={() => copyToClipboard($focusedRow?._rev)}
89
- on:click={menu.actions.close}
90
- >
91
- Copy row _rev
92
- </MenuItem>
93
- <MenuItem
94
- icon="Duplicate"
95
- disabled={isNewRow || !$config.canAddRows}
96
- on:click={duplicate}
97
- >
98
- Duplicate row
99
- </MenuItem>
100
- <MenuItem
101
- icon="Delete"
102
- disabled={isNewRow || !$config.canDeleteRows}
103
- on:click={deleteRow}
104
- >
105
- Delete row
106
- </MenuItem>
107
- </Menu>
108
- </div>
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>
@@ -0,0 +1,9 @@
1
+ <div class="grid-popover-container" />
2
+
3
+ <style>
4
+ .grid-popover-container {
5
+ position: fixed;
6
+ top: 0;
7
+ left: 0;
8
+ }
9
+ </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 closeMenu = () => {
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 = getLocation(e).y
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
- closeMenu()
69
+ closePopovers()
74
70
  }
75
71
  const moveVDragging = domDebounce(e => {
76
- const delta = getLocation(e).y - initialMouse
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 = getLocation(e).x
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
- closeMenu()
98
+ closePopovers()
103
99
  }
104
100
  const moveHDragging = domDebounce(e => {
105
- const delta = getLocation(e).x - initialMouse
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
- // Derive if we have any normal columns
52
- const hasNonAutoColumn = derived(
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
- const normalCols = allCols.filter(column => {
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 { definition, columns, stickyColumn, enrichedSchema } = context
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
- enrichedSchema.subscribe($enrichedSchema => {
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 $columns = get(columns)
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, rand } = context
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(`grid-${rand}`)
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.clientX
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 = 8
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
- // Save column changes
173
- await columns.actions.saveChanges()
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.splice(targetIdx, 0, removed[0])
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: e.clientX,
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 dx = e.clientX - initialMouseX
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) {