@budibase/frontend-core 2.7.34-alpha.4 → 2.7.34-alpha.6

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 (44) hide show
  1. package/package.json +4 -4
  2. package/src/api/index.js +16 -2
  3. package/src/api/rows.js +3 -1
  4. package/src/components/grid/cells/AttachmentCell.svelte +3 -1
  5. package/src/components/grid/cells/GridCell.svelte +1 -1
  6. package/src/components/grid/cells/GutterCell.svelte +9 -14
  7. package/src/components/grid/cells/HeaderCell.svelte +7 -5
  8. package/src/components/grid/cells/LongFormCell.svelte +1 -1
  9. package/src/components/grid/cells/OptionsCell.svelte +4 -5
  10. package/src/components/grid/cells/RelationshipCell.svelte +22 -11
  11. package/src/components/grid/controls/AddColumnButton.svelte +1 -1
  12. package/src/components/grid/controls/BulkDeleteHandler.svelte +2 -8
  13. package/src/components/grid/controls/HideColumnsButton.svelte +9 -4
  14. package/src/components/grid/controls/SizeButton.svelte +135 -0
  15. package/src/components/grid/controls/SortButton.svelte +3 -4
  16. package/src/components/grid/layout/Grid.svelte +66 -59
  17. package/src/components/grid/layout/GridBody.svelte +3 -2
  18. package/src/components/grid/layout/GridRow.svelte +3 -2
  19. package/src/components/grid/layout/GridScrollWrapper.svelte +6 -0
  20. package/src/components/grid/layout/HeaderRow.svelte +3 -3
  21. package/src/components/grid/layout/NewRow.svelte +37 -5
  22. package/src/components/grid/layout/StickyColumn.svelte +10 -8
  23. package/src/components/grid/lib/constants.js +4 -3
  24. package/src/components/grid/lib/websocket.js +3 -2
  25. package/src/components/grid/overlays/KeyboardManager.svelte +6 -0
  26. package/src/components/grid/overlays/MenuOverlay.svelte +3 -1
  27. package/src/components/grid/overlays/ReorderOverlay.svelte +1 -1
  28. package/src/components/grid/overlays/ResizeOverlay.svelte +1 -1
  29. package/src/components/grid/overlays/ScrollOverlay.svelte +25 -2
  30. package/src/components/grid/stores/columns.js +37 -6
  31. package/src/components/grid/stores/config.js +27 -0
  32. package/src/components/grid/stores/filter.js +19 -0
  33. package/src/components/grid/stores/index.js +6 -0
  34. package/src/components/grid/stores/menu.js +15 -9
  35. package/src/components/grid/stores/rows.js +18 -16
  36. package/src/components/grid/stores/scroll.js +5 -7
  37. package/src/components/grid/stores/sort.js +27 -0
  38. package/src/components/grid/stores/ui.js +19 -4
  39. package/src/components/grid/stores/viewport.js +17 -6
  40. package/src/fetch/DataFetch.js +4 -2
  41. package/src/utils/index.js +1 -0
  42. package/src/utils/memo.js +43 -0
  43. package/src/components/grid/controls/ColumnWidthButton.svelte +0 -92
  44. package/src/components/grid/controls/RowHeightButton.svelte +0 -71
@@ -1,6 +1,5 @@
1
1
  <script>
2
2
  import { setContext, onMount } from "svelte"
3
- import { writable } from "svelte/store"
4
3
  import { fade } from "svelte/transition"
5
4
  import { clickOutside, ProgressCircle } from "@budibase/bbui"
6
5
  import { createEventManagers } from "../lib/events"
@@ -17,11 +16,8 @@
17
16
  import UserAvatars from "./UserAvatars.svelte"
18
17
  import KeyboardManager from "../overlays/KeyboardManager.svelte"
19
18
  import SortButton from "../controls/SortButton.svelte"
20
- import AddColumnButton from "../controls/AddColumnButton.svelte"
21
19
  import HideColumnsButton from "../controls/HideColumnsButton.svelte"
22
- import AddRowButton from "../controls/AddRowButton.svelte"
23
- import RowHeightButton from "../controls/RowHeightButton.svelte"
24
- import ColumnWidthButton from "../controls/ColumnWidthButton.svelte"
20
+ import SizeButton from "../controls/SizeButton.svelte"
25
21
  import NewRow from "./NewRow.svelte"
26
22
  import { createGridWebsocket } from "../lib/websocket"
27
23
  import {
@@ -33,48 +29,37 @@
33
29
 
34
30
  export let API = null
35
31
  export let tableId = null
36
- export let tableType = null
37
32
  export let schemaOverrides = null
33
+ export let columnWhitelist = null
38
34
  export let allowAddRows = true
39
- export let allowAddColumns = true
40
- export let allowEditColumns = true
41
35
  export let allowExpandRows = true
42
36
  export let allowEditRows = true
43
37
  export let allowDeleteRows = true
38
+ export let allowSchemaChanges = true
44
39
  export let stripeRows = false
45
40
  export let collaboration = true
46
41
  export let showAvatars = true
42
+ export let showControls = true
43
+ export let initialFilter = null
44
+ export let initialSortColumn = null
45
+ export let initialSortOrder = null
46
+ export let initialRowHeight = null
47
47
 
48
48
  // Unique identifier for DOM nodes inside this instance
49
49
  const rand = Math.random()
50
50
 
51
- // State stores
52
- const tableIdStore = writable(tableId)
53
- const schemaOverridesStore = writable(schemaOverrides)
54
- const config = writable({
55
- allowAddRows,
56
- allowAddColumns,
57
- allowEditColumns,
58
- allowExpandRows,
59
- allowEditRows,
60
- allowDeleteRows,
61
- stripeRows,
62
- })
63
-
64
51
  // Build up context
65
52
  let context = {
66
53
  API: API || createAPIClient(),
67
54
  rand,
68
- config,
69
- tableId: tableIdStore,
70
- tableType,
71
- schemaOverrides: schemaOverridesStore,
55
+ props: $$props,
72
56
  }
73
57
  context = { ...context, ...createEventManagers() }
74
58
  context = attachStores(context)
75
59
 
76
60
  // Reference some stores for local use
77
61
  const {
62
+ config,
78
63
  isResizing,
79
64
  isReordering,
80
65
  ui,
@@ -82,19 +67,27 @@
82
67
  loading,
83
68
  rowHeight,
84
69
  contentLines,
70
+ gridFocused,
85
71
  } = context
86
72
 
87
- // Keep stores up to date
88
- $: tableIdStore.set(tableId)
89
- $: schemaOverridesStore.set(schemaOverrides)
73
+ // Keep config store up to date with props
90
74
  $: config.set({
75
+ tableId,
76
+ schemaOverrides,
77
+ columnWhitelist,
91
78
  allowAddRows,
92
- allowAddColumns,
93
- allowEditColumns,
94
79
  allowExpandRows,
95
80
  allowEditRows,
96
81
  allowDeleteRows,
82
+ allowSchemaChanges,
97
83
  stripeRows,
84
+ collaboration,
85
+ showAvatars,
86
+ showControls,
87
+ initialFilter,
88
+ initialSortColumn,
89
+ initialSortOrder,
90
+ initialRowHeight,
98
91
  })
99
92
 
100
93
  // Set context for children to consume
@@ -116,25 +109,27 @@
116
109
  id="grid-{rand}"
117
110
  class:is-resizing={$isResizing}
118
111
  class:is-reordering={$isReordering}
119
- class:stripe={$config.stripeRows}
112
+ class:stripe={stripeRows}
113
+ on:mouseenter={() => gridFocused.set(true)}
114
+ on:mouseleave={() => gridFocused.set(false)}
120
115
  style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
121
116
  >
122
- <div class="controls">
123
- <div class="controls-left">
124
- <AddRowButton />
125
- <AddColumnButton />
126
- <slot name="controls" />
127
- <SortButton />
128
- <HideColumnsButton />
129
- <ColumnWidthButton />
130
- <RowHeightButton />
131
- </div>
132
- <div class="controls-right">
133
- {#if showAvatars}
134
- <UserAvatars />
135
- {/if}
117
+ {#if showControls}
118
+ <div class="controls">
119
+ <div class="controls-left">
120
+ <slot name="filter" />
121
+ <SortButton />
122
+ <HideColumnsButton />
123
+ <SizeButton />
124
+ <slot name="controls" />
125
+ </div>
126
+ <div class="controls-right">
127
+ {#if showAvatars}
128
+ <UserAvatars />
129
+ {/if}
130
+ </div>
136
131
  </div>
137
- </div>
132
+ {/if}
138
133
  {#if $loaded}
139
134
  <div class="grid-data-outer" use:clickOutside={ui.actions.blur}>
140
135
  <div class="grid-data-inner">
@@ -167,25 +162,28 @@
167
162
  </div>
168
163
 
169
164
  <style>
165
+ /* Core grid */
170
166
  .grid {
171
- flex: 1 1 auto;
172
- display: flex;
173
- flex-direction: column;
174
- justify-content: flex-start;
175
- align-items: stretch;
176
- position: relative;
177
- overflow: hidden;
178
- background: var(--cell-background);
179
-
180
167
  /* Variables */
181
- --cell-background: var(--spectrum-global-color-gray-50);
182
- --cell-background-hover: var(--spectrum-global-color-gray-100);
168
+ --accent-color: var(--primaryColor, var(--spectrum-global-color-blue-400));
169
+ --grid-background: var(--spectrum-global-color-gray-50);
170
+ --grid-background-alt: var(--spectrum-global-color-gray-100);
171
+ --cell-background: var(--grid-background);
172
+ --cell-background-hover: var(--grid-background-alt);
183
173
  --cell-background-alt: var(--cell-background);
184
174
  --cell-padding: 8px;
185
175
  --cell-spacing: 4px;
186
176
  --cell-border: 1px solid var(--spectrum-global-color-gray-200);
187
177
  --cell-font-size: 14px;
188
178
  --controls-height: 50px;
179
+ flex: 1 1 auto;
180
+ display: flex;
181
+ flex-direction: column;
182
+ justify-content: flex-start;
183
+ align-items: stretch;
184
+ position: relative;
185
+ overflow: hidden;
186
+ background: var(--grid-background);
189
187
  }
190
188
  .grid,
191
189
  .grid :global(*) {
@@ -201,6 +199,7 @@
201
199
  --cell-background-alt: var(--spectrum-global-color-gray-75);
202
200
  }
203
201
 
202
+ /* Data layers */
204
203
  .grid-data-outer,
205
204
  .grid-data-inner {
206
205
  flex: 1 1 auto;
@@ -234,7 +233,7 @@
234
233
  border-bottom: 2px solid var(--spectrum-global-color-gray-200);
235
234
  padding: var(--cell-padding);
236
235
  gap: var(--cell-spacing);
237
- background: var(--background);
236
+ background: var(--grid-background-alt);
238
237
  z-index: 2;
239
238
  }
240
239
  .controls-left,
@@ -270,7 +269,15 @@
270
269
  left: 0;
271
270
  width: 100%;
272
271
  height: 100%;
273
- background: var(--background);
272
+ background: var(--grid-background-alt);
274
273
  opacity: 0.6;
275
274
  }
275
+
276
+ /* Disable checkbox animation anywhere in the grid data */
277
+ .grid-data-outer :global(.spectrum-Checkbox-box:before),
278
+ .grid-data-outer :global(.spectrum-Checkbox-box:after),
279
+ .grid-data-outer :global(.spectrum-Checkbox-checkmark),
280
+ .grid-data-outer :global(.spectrum-Checkbox-partialCheckmark) {
281
+ transition: none;
282
+ }
276
283
  </style>
@@ -12,6 +12,7 @@
12
12
  config,
13
13
  hoveredRowId,
14
14
  dispatch,
15
+ isDragging,
15
16
  } = getContext("grid")
16
17
 
17
18
  let body
@@ -47,8 +48,8 @@
47
48
  class="blank"
48
49
  class:highlighted={$hoveredRowId === BlankRowID}
49
50
  style="width:{renderColumnsWidth}px"
50
- on:mouseenter={() => ($hoveredRowId = BlankRowID)}
51
- on:mouseleave={() => ($hoveredRowId = null)}
51
+ on:mouseenter={$isDragging ? null : () => ($hoveredRowId = BlankRowID)}
52
+ on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
52
53
  on:click={() => dispatch("add-row-inline")}
53
54
  />
54
55
  {/if}
@@ -16,6 +16,7 @@
16
16
  focusedRow,
17
17
  columnHorizontalInversionIndex,
18
18
  contentLines,
19
+ isDragging,
19
20
  } = getContext("grid")
20
21
 
21
22
  $: rowSelected = !!$selectedRows[row._id]
@@ -27,8 +28,8 @@
27
28
  <div
28
29
  class="row"
29
30
  on:focus
30
- on:mouseenter={() => ($hoveredRowId = row._id)}
31
- on:mouseleave={() => ($hoveredRowId = null)}
31
+ on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
32
+ on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
32
33
  >
33
34
  {#each $renderedColumns as column, columnIdx (column.name)}
34
35
  {@const cellId = `${row._id}-${column.name}`}
@@ -12,6 +12,7 @@
12
12
  bounds,
13
13
  hoveredRowId,
14
14
  hiddenColumnsWidth,
15
+ menu,
15
16
  } = getContext("grid")
16
17
 
17
18
  export let scrollVertically = false
@@ -30,6 +31,11 @@
30
31
  const handleWheel = e => {
31
32
  e.preventDefault()
32
33
  debouncedHandleWheel(e.deltaX, e.deltaY, e.clientY)
34
+
35
+ // If a context menu was visible, hide it
36
+ if ($menu.visible) {
37
+ menu.actions.close()
38
+ }
33
39
  }
34
40
  const debouncedHandleWheel = domDebounce((deltaX, deltaY, clientY) => {
35
41
  const { top, left } = $scroll
@@ -29,7 +29,7 @@
29
29
  {/each}
30
30
  </div>
31
31
  </GridScrollWrapper>
32
- {#if $config.allowAddColumns}
32
+ {#if $config.allowSchemaChanges}
33
33
  <div
34
34
  class="add"
35
35
  style="left:{left}px"
@@ -42,7 +42,7 @@
42
42
 
43
43
  <style>
44
44
  .header {
45
- background: var(--background);
45
+ background: var(--grid-background-alt);
46
46
  border-bottom: var(--cell-border);
47
47
  position: relative;
48
48
  height: var(--default-row-height);
@@ -60,7 +60,7 @@
60
60
  border-left: var(--cell-border);
61
61
  border-right: var(--cell-border);
62
62
  border-bottom: var(--cell-border);
63
- background: var(--spectrum-global-color-gray-100);
63
+ background: var(--grid-background-alt);
64
64
  z-index: 1;
65
65
  }
66
66
  .add:hover {
@@ -26,6 +26,8 @@
26
26
  maxScrollTop,
27
27
  rowVerticalInversionIndex,
28
28
  columnHorizontalInversionIndex,
29
+ selectedRows,
30
+ config,
29
31
  } = getContext("grid")
30
32
 
31
33
  let visible = false
@@ -37,6 +39,7 @@
37
39
  $: width = GutterWidth + ($stickyColumn?.width || 0)
38
40
  $: $tableId, (visible = false)
39
41
  $: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
42
+ $: selectedRowCount = Object.values($selectedRows).length
40
43
 
41
44
  const shouldInvertY = (offset, inversionIndex, rows) => {
42
45
  if (offset === 0) {
@@ -75,7 +78,7 @@
75
78
  }
76
79
 
77
80
  const startAdding = async () => {
78
- if (visible) {
81
+ if (visible || !firstColumn) {
79
82
  return
80
83
  }
81
84
 
@@ -129,9 +132,6 @@
129
132
  e.preventDefault()
130
133
  clear()
131
134
  }
132
- } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
133
- e.preventDefault()
134
- addRow()
135
135
  }
136
136
  }
137
137
 
@@ -141,6 +141,18 @@
141
141
  })
142
142
  </script>
143
143
 
144
+ <!-- New row FAB -->
145
+ {#if !visible && !selectedRowCount && $config.allowAddRows && firstColumn}
146
+ <div
147
+ class="new-row-fab"
148
+ on:click={() => dispatch("add-row-inline")}
149
+ transition:fade|local={{ duration: 130 }}
150
+ class:offset={!$stickyColumn}
151
+ >
152
+ <Icon name="Add" size="S" />
153
+ </div>
154
+ {/if}
155
+
144
156
  <!-- Only show new row functionality if we have any columns -->
145
157
  {#if visible}
146
158
  <div
@@ -151,7 +163,7 @@
151
163
  <div class="underlay sticky" transition:fade|local={{ duration: 130 }} />
152
164
  <div class="underlay" transition:fade|local={{ duration: 130 }} />
153
165
  <div class="sticky-column" transition:fade|local={{ duration: 130 }}>
154
- <GutterCell on:expand={addViaModal} rowHovered>
166
+ <GutterCell expandable on:expand={addViaModal} rowHovered>
155
167
  <Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
156
168
  {#if isAdding}
157
169
  <div in:fade={{ duration: 130 }} class="loading-overlay" />
@@ -227,6 +239,26 @@
227
239
  {/if}
228
240
 
229
241
  <style>
242
+ /* New row FAB */
243
+ .new-row-fab {
244
+ position: absolute;
245
+ top: var(--default-row-height);
246
+ left: calc(var(--gutter-width) / 2);
247
+ transform: translateX(6px) translateY(-50%);
248
+ background: var(--cell-background);
249
+ padding: 4px;
250
+ border-radius: 50%;
251
+ border: var(--cell-border);
252
+ z-index: 10;
253
+ }
254
+ .new-row-fab:hover {
255
+ background: var(--cell-background-hover);
256
+ cursor: pointer;
257
+ }
258
+ .new-row-fab.offset {
259
+ margin-left: -6px;
260
+ }
261
+
230
262
  .container {
231
263
  position: absolute;
232
264
  top: var(--default-row-height);
@@ -23,10 +23,11 @@
23
23
  scrollLeft,
24
24
  dispatch,
25
25
  contentLines,
26
+ isDragging,
26
27
  } = getContext("grid")
27
28
 
28
29
  $: rowCount = $rows.length
29
- $: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
30
+ $: selectedRowCount = Object.values($selectedRows).length
30
31
  $: width = GutterWidth + ($stickyColumn?.width || 0)
31
32
 
32
33
  const selectAll = () => {
@@ -50,7 +51,6 @@
50
51
  >
51
52
  <div class="header row">
52
53
  <GutterCell
53
- disableExpand
54
54
  disableNumber
55
55
  on:select={selectAll}
56
56
  defaultHeight
@@ -71,8 +71,8 @@
71
71
  {@const cellId = `${row._id}-${$stickyColumn?.name}`}
72
72
  <div
73
73
  class="row"
74
- on:mouseenter={() => ($hoveredRowId = row._id)}
75
- on:mouseleave={() => ($hoveredRowId = null)}
74
+ on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
75
+ on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
76
76
  >
77
77
  <GutterCell {row} {rowFocused} {rowHovered} {rowSelected} />
78
78
  {#if $stickyColumn}
@@ -96,11 +96,13 @@
96
96
  {#if $config.allowAddRows && ($renderedColumns.length || $stickyColumn)}
97
97
  <div
98
98
  class="row new"
99
- on:mouseenter={() => ($hoveredRowId = BlankRowID)}
100
- on:mouseleave={() => ($hoveredRowId = null)}
99
+ on:mouseenter={$isDragging
100
+ ? null
101
+ : () => ($hoveredRowId = BlankRowID)}
102
+ on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
101
103
  on:click={() => dispatch("add-row-inline")}
102
104
  >
103
- <GutterCell disableExpand rowHovered={$hoveredRowId === BlankRowID}>
105
+ <GutterCell rowHovered={$hoveredRowId === BlankRowID}>
104
106
  <Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
105
107
  </GutterCell>
106
108
  {#if $stickyColumn}
@@ -159,7 +161,7 @@
159
161
  z-index: 1;
160
162
  }
161
163
  .header :global(.cell) {
162
- background: var(--spectrum-global-color-gray-100);
164
+ background: var(--grid-background-alt);
163
165
  }
164
166
  .row {
165
167
  display: flex;
@@ -1,6 +1,5 @@
1
- export const Padding = 256
2
- export const MaxCellRenderHeight = 252
3
- export const MaxCellRenderWidthOverflow = 200
1
+ export const Padding = 246
2
+ export const MaxCellRenderHeight = 222
4
3
  export const ScrollBarSize = 8
5
4
  export const GutterWidth = 72
6
5
  export const DefaultColumnWidth = 200
@@ -12,3 +11,5 @@ export const DefaultRowHeight = SmallRowHeight
12
11
  export const NewRowID = "new"
13
12
  export const BlankRowID = "blank"
14
13
  export const RowPageSize = 100
14
+ export const FocusedCellMinOffset = 48
15
+ export const MaxCellRenderWidthOverflow = Padding - 3 * ScrollBarSize
@@ -3,7 +3,7 @@ import { createWebsocket } from "../../../utils"
3
3
  import { SocketEvent, GridSocketEvent } from "@budibase/shared-core"
4
4
 
5
5
  export const createGridWebsocket = context => {
6
- const { rows, tableId, users, focusedCellId, table } = context
6
+ const { rows, tableId, users, focusedCellId, table, API } = context
7
7
  const socket = createWebsocket("/socket/grid")
8
8
 
9
9
  const connectToTable = tableId => {
@@ -11,9 +11,10 @@ export const createGridWebsocket = context => {
11
11
  return
12
12
  }
13
13
  // Identify which table we are editing
14
+ const appId = API.getAppID()
14
15
  socket.emit(
15
16
  GridSocketEvent.SelectTable,
16
- { tableId },
17
+ { tableId, appId },
17
18
  ({ users: gridUsers }) => {
18
19
  users.set(gridUsers)
19
20
  }
@@ -15,6 +15,7 @@
15
15
  selectedRows,
16
16
  config,
17
17
  menu,
18
+ gridFocused,
18
19
  } = getContext("grid")
19
20
 
20
21
  const ignoredOriginSelectors = [
@@ -24,6 +25,11 @@
24
25
 
25
26
  // Global key listener which intercepts all key events
26
27
  const handleKeyDown = e => {
28
+ // Ignore completely if the grid is not focused
29
+ if (!$gridFocused) {
30
+ return
31
+ }
32
+
27
33
  // Avoid processing events sourced from certain origins
28
34
  if (e.target?.closest) {
29
35
  for (let selector of ignoredOriginSelectors) {
@@ -72,7 +72,9 @@
72
72
  </MenuItem>
73
73
  <MenuItem
74
74
  icon="Maximize"
75
- disabled={isNewRow || !$config.allowEditRows}
75
+ disabled={isNewRow ||
76
+ !$config.allowEditRows ||
77
+ !$config.allowExpandRows}
76
78
  on:click={() => dispatch("edit-row", $focusedRow)}
77
79
  on:click={menu.actions.close}
78
80
  >
@@ -57,7 +57,7 @@
57
57
  position: absolute;
58
58
  top: 0;
59
59
  width: 2px;
60
- background: var(--spectrum-global-color-blue-400);
60
+ background: var(--accent-color);
61
61
  margin-left: -2px;
62
62
  }
63
63
  </style>
@@ -65,6 +65,6 @@
65
65
  margin-left: -1px;
66
66
  width: 2px;
67
67
  height: 100%;
68
- background: var(--spectrum-global-color-blue-400);
68
+ background: var(--accent-color);
69
69
  }
70
70
  </style>
@@ -15,11 +15,18 @@
15
15
  scrollLeft,
16
16
  scrollTop,
17
17
  height,
18
+ isDragging,
19
+ menu,
18
20
  } = getContext("grid")
19
21
 
20
22
  // State for dragging bars
21
23
  let initialMouse
22
24
  let initialScroll
25
+ let isDraggingV = false
26
+ let isDraggingH = false
27
+
28
+ // Update state to reflect if we are dragging
29
+ $: isDragging.set(isDraggingV || isDraggingH)
23
30
 
24
31
  // Calculate V scrollbar size and offset
25
32
  // Terminology is the same for both axes:
@@ -39,6 +46,13 @@
39
46
  $: availWidth = renderWidth - barWidth
40
47
  $: barLeft = ScrollBarSize + availWidth * ($scrollLeft / $maxScrollLeft)
41
48
 
49
+ // Helper to close the context menu if it's open
50
+ const closeMenu = () => {
51
+ if ($menu.visible) {
52
+ menu.actions.close()
53
+ }
54
+ }
55
+
42
56
  // V scrollbar drag handlers
43
57
  const startVDragging = e => {
44
58
  e.preventDefault()
@@ -46,6 +60,8 @@
46
60
  initialScroll = $scrollTop
47
61
  document.addEventListener("mousemove", moveVDragging)
48
62
  document.addEventListener("mouseup", stopVDragging)
63
+ isDraggingV = true
64
+ closeMenu()
49
65
  }
50
66
  const moveVDragging = domDebounce(e => {
51
67
  const delta = e.clientY - initialMouse
@@ -59,6 +75,7 @@
59
75
  const stopVDragging = () => {
60
76
  document.removeEventListener("mousemove", moveVDragging)
61
77
  document.removeEventListener("mouseup", stopVDragging)
78
+ isDraggingV = false
62
79
  }
63
80
 
64
81
  // H scrollbar drag handlers
@@ -68,6 +85,8 @@
68
85
  initialScroll = $scrollLeft
69
86
  document.addEventListener("mousemove", moveHDragging)
70
87
  document.addEventListener("mouseup", stopHDragging)
88
+ isDraggingH = true
89
+ closeMenu()
71
90
  }
72
91
  const moveHDragging = domDebounce(e => {
73
92
  const delta = e.clientX - initialMouse
@@ -81,6 +100,7 @@
81
100
  const stopHDragging = () => {
82
101
  document.removeEventListener("mousemove", moveHDragging)
83
102
  document.removeEventListener("mouseup", stopHDragging)
103
+ isDraggingH = false
84
104
  }
85
105
  </script>
86
106
 
@@ -89,6 +109,7 @@
89
109
  class="v-scrollbar"
90
110
  style="--size:{ScrollBarSize}px; top:{barTop}px; height:{barHeight}px;"
91
111
  on:mousedown={startVDragging}
112
+ class:dragging={isDraggingV}
92
113
  />
93
114
  {/if}
94
115
  {#if $showHScrollbar}
@@ -96,6 +117,7 @@
96
117
  class="h-scrollbar"
97
118
  style="--size:{ScrollBarSize}px; left:{barLeft}px; width:{barWidth}px;"
98
119
  on:mousedown={startHDragging}
120
+ class:dragging={isDraggingH}
99
121
  />
100
122
  {/if}
101
123
 
@@ -103,11 +125,12 @@
103
125
  div {
104
126
  position: absolute;
105
127
  background: var(--spectrum-global-color-gray-500);
106
- opacity: 0.7;
128
+ opacity: 0.5;
107
129
  border-radius: 4px;
108
130
  transition: opacity 130ms ease-out;
109
131
  }
110
- div:hover {
132
+ div:hover,
133
+ div.dragging {
111
134
  opacity: 1;
112
135
  }
113
136
  .v-scrollbar {