@budibase/frontend-core 2.29.21 → 2.29.23

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 (51) hide show
  1. package/package.json +5 -5
  2. package/src/components/FilterBuilder.svelte +1 -5
  3. package/src/components/grid/cells/DataCell.svelte +63 -12
  4. package/src/components/grid/cells/GridCell.svelte +36 -16
  5. package/src/components/grid/cells/GutterCell.svelte +15 -8
  6. package/src/components/grid/cells/HeaderCell.svelte +3 -3
  7. package/src/components/grid/cells/RelationshipCell.svelte +16 -8
  8. package/src/components/grid/controls/BulkDeleteHandler.svelte +98 -13
  9. package/src/components/grid/controls/BulkDuplicationHandler.svelte +79 -0
  10. package/src/components/grid/controls/ClipboardHandler.svelte +67 -0
  11. package/src/components/grid/controls/ColumnsSettingButton.svelte +5 -10
  12. package/src/components/grid/controls/SizeButton.svelte +6 -13
  13. package/src/components/grid/controls/SortButton.svelte +8 -22
  14. package/src/components/grid/layout/ButtonColumn.svelte +12 -6
  15. package/src/components/grid/layout/Grid.svelte +11 -7
  16. package/src/components/grid/layout/GridBody.svelte +2 -2
  17. package/src/components/grid/layout/GridRow.svelte +12 -6
  18. package/src/components/grid/layout/GridScrollWrapper.svelte +9 -7
  19. package/src/components/grid/layout/HeaderRow.svelte +2 -2
  20. package/src/components/grid/layout/NewColumnButton.svelte +11 -5
  21. package/src/components/grid/layout/NewRow.svelte +16 -12
  22. package/src/components/grid/layout/StickyColumn.svelte +24 -14
  23. package/src/components/grid/lib/utils.js +4 -4
  24. package/src/components/grid/overlays/KeyboardManager.svelte +144 -95
  25. package/src/components/grid/overlays/MenuOverlay.svelte +114 -63
  26. package/src/components/grid/overlays/ReorderOverlay.svelte +14 -18
  27. package/src/components/grid/overlays/ResizeOverlay.svelte +8 -21
  28. package/src/components/grid/stores/clipboard.js +215 -18
  29. package/src/components/grid/stores/columns.js +78 -97
  30. package/src/components/grid/stores/conditions.js +157 -0
  31. package/src/components/grid/stores/config.js +2 -2
  32. package/src/components/grid/stores/datasource.js +4 -14
  33. package/src/components/grid/stores/datasources/nonPlus.js +2 -4
  34. package/src/components/grid/stores/datasources/table.js +6 -5
  35. package/src/components/grid/stores/datasources/viewV2.js +7 -9
  36. package/src/components/grid/stores/index.js +5 -3
  37. package/src/components/grid/stores/menu.js +40 -6
  38. package/src/components/grid/stores/pagination.js +9 -3
  39. package/src/components/grid/stores/reorder.js +67 -42
  40. package/src/components/grid/stores/resize.js +1 -1
  41. package/src/components/grid/stores/rows.js +220 -85
  42. package/src/components/grid/stores/scroll.js +31 -28
  43. package/src/components/grid/stores/ui.js +295 -70
  44. package/src/components/grid/stores/users.js +2 -2
  45. package/src/components/grid/stores/validation.js +43 -16
  46. package/src/components/grid/stores/viewport.js +30 -24
  47. package/src/components/index.js +1 -0
  48. package/src/constants.js +3 -0
  49. package/src/themes/midnight.css +18 -17
  50. package/src/themes/nord.css +2 -1
  51. package/src/utils/utils.js +2 -0
@@ -13,22 +13,25 @@
13
13
  const {
14
14
  rows,
15
15
  selectedRows,
16
- stickyColumn,
16
+ displayColumn,
17
17
  renderedRows,
18
18
  focusedCellId,
19
19
  hoveredRowId,
20
20
  config,
21
21
  selectedCellMap,
22
+ userCellMap,
22
23
  focusedRow,
23
24
  scrollLeft,
24
25
  dispatch,
25
26
  contentLines,
26
27
  isDragging,
28
+ isSelectingCells,
29
+ selectedCellCount,
27
30
  } = getContext("grid")
28
31
 
29
32
  $: rowCount = $rows.length
30
33
  $: selectedRowCount = Object.values($selectedRows).length
31
- $: width = GutterWidth + ($stickyColumn?.width || 0)
34
+ $: width = GutterWidth + ($displayColumn?.width || 0)
32
35
 
33
36
  const selectAll = () => {
34
37
  const allSelected = selectedRowCount === rowCount
@@ -57,8 +60,8 @@
57
60
  rowSelected={selectedRowCount && selectedRowCount === rowCount}
58
61
  disabled={!$renderedRows.length}
59
62
  />
60
- {#if $stickyColumn}
61
- <HeaderCell column={$stickyColumn} orderable={false} idx="sticky">
63
+ {#if $displayColumn}
64
+ <HeaderCell column={$displayColumn} orderable={false} idx="sticky">
62
65
  <slot name="edit-column" />
63
66
  </HeaderCell>
64
67
  {/if}
@@ -69,9 +72,11 @@
69
72
  <GridScrollWrapper scrollVertically attachHandlers>
70
73
  {#each $renderedRows as row, idx}
71
74
  {@const rowSelected = !!$selectedRows[row._id]}
72
- {@const rowHovered = $hoveredRowId === row._id}
75
+ {@const rowHovered =
76
+ $hoveredRowId === row._id &&
77
+ (!$selectedCellCount || !$isSelectingCells)}
73
78
  {@const rowFocused = $focusedRow?._id === row._id}
74
- {@const cellId = getCellID(row._id, $stickyColumn?.name)}
79
+ {@const cellId = getCellID(row._id, $displayColumn?.name)}
75
80
  <div
76
81
  class="row"
77
82
  on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
@@ -79,20 +84,22 @@
79
84
  on:click={() => dispatch("rowclick", rows.actions.cleanRow(row))}
80
85
  >
81
86
  <GutterCell {row} {rowFocused} {rowHovered} {rowSelected} />
82
- {#if $stickyColumn}
87
+ {#if $displayColumn}
83
88
  <DataCell
84
89
  {row}
85
90
  {cellId}
86
91
  {rowFocused}
87
- selected={rowSelected}
92
+ {rowSelected}
93
+ cellSelected={$selectedCellMap[cellId]}
88
94
  highlighted={rowHovered || rowFocused}
89
95
  rowIdx={row.__idx}
90
96
  topRow={idx === 0}
91
97
  focused={$focusedCellId === cellId}
92
- selectedUser={$selectedCellMap[cellId]}
93
- width={$stickyColumn.width}
94
- column={$stickyColumn}
98
+ selectedUser={$userCellMap[cellId]}
99
+ width={$displayColumn.width}
100
+ column={$displayColumn}
95
101
  contentLines={$contentLines}
102
+ isSelectingCells={$isSelectingCells}
96
103
  />
97
104
  {/if}
98
105
  </div>
@@ -107,9 +114,9 @@
107
114
  <GutterCell rowHovered={$hoveredRowId === BlankRowID}>
108
115
  <Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
109
116
  </GutterCell>
110
- {#if $stickyColumn}
117
+ {#if $displayColumn}
111
118
  <GridCell
112
- width={$stickyColumn.width}
119
+ width={$displayColumn.width}
113
120
  highlighted={$hoveredRowId === BlankRowID}
114
121
  >
115
122
  <KeyboardShortcut padded keybind="Ctrl+Enter" />
@@ -155,7 +162,7 @@
155
162
 
156
163
  /* Don't show borders between cells in the sticky column */
157
164
  .sticky-column :global(.cell:not(:last-child)) {
158
- border-right: none;
165
+ border-right-color: transparent;
159
166
  }
160
167
 
161
168
  .header {
@@ -164,6 +171,9 @@
164
171
  .header :global(.cell) {
165
172
  background: var(--grid-background-alt);
166
173
  }
174
+ .header :global(.cell::before) {
175
+ display: none;
176
+ }
167
177
  .row {
168
178
  display: flex;
169
179
  flex-direction: row;
@@ -1,17 +1,17 @@
1
1
  import { helpers } from "@budibase/shared-core"
2
2
  import { TypeIconMap } from "../../../constants"
3
3
 
4
- // we can't use "-" for joining the ID/field, as this can be present in the ID or column name
5
- // using something very unusual to avoid this problem
4
+ // We can't use "-" as a separator as this can be present in the ID
5
+ // or column name, so we use something very unusual to avoid this problem
6
6
  const JOINING_CHARACTER = "‽‽"
7
7
 
8
8
  export const parseCellID = cellId => {
9
9
  if (!cellId) {
10
- return { id: undefined, field: undefined }
10
+ return { rowId: undefined, field: undefined }
11
11
  }
12
12
  const parts = cellId.split(JOINING_CHARACTER)
13
13
  const field = parts.pop()
14
- return { id: parts.join(JOINING_CHARACTER), field }
14
+ return { rowId: parts.join(JOINING_CHARACTER), field }
15
15
  }
16
16
 
17
17
  export const getCellID = (rowId, fieldName) => {
@@ -1,23 +1,26 @@
1
1
  <script>
2
2
  import { getContext, onMount } from "svelte"
3
3
  import { debounce } from "../../../utils/utils"
4
- import { NewRowID } from "../lib/constants"
5
4
  import { getCellID, parseCellID } from "../lib/utils"
5
+ import { NewRowID } from "../lib/constants"
6
6
 
7
7
  const {
8
8
  rows,
9
9
  focusedCellId,
10
10
  visibleColumns,
11
- focusedRow,
12
- stickyColumn,
11
+ rowLookupMap,
13
12
  focusedCellAPI,
14
- clipboard,
15
13
  dispatch,
16
- selectedRows,
14
+ selectedRowCount,
17
15
  config,
18
16
  menu,
19
17
  gridFocused,
20
18
  keyboardBlocked,
19
+ selectedCellCount,
20
+ selectedCells,
21
+ cellSelection,
22
+ columnLookupMap,
23
+ focusedRowId,
21
24
  } = getContext("grid")
22
25
 
23
26
  const ignoredOriginSelectors = [
@@ -43,23 +46,51 @@
43
46
  }
44
47
  }
45
48
 
46
- // Handle certain key presses regardless of selection state
47
- if (e.key === "Enter" && (e.ctrlKey || e.metaKey) && $config.canAddRows) {
49
+ // Sugar for preventing default
50
+ const handle = fn => {
48
51
  e.preventDefault()
49
- dispatch("add-row-inline")
50
- return
52
+ fn()
51
53
  }
52
54
 
53
- // If nothing selected avoid processing further key presses
55
+ // Handle certain key presses regardless of selection state
56
+ if (e.metaKey || e.ctrlKey) {
57
+ switch (e.key) {
58
+ case "c":
59
+ return handle(() => dispatch("copy"))
60
+ case "v":
61
+ return handle(() => dispatch("paste"))
62
+ case "Enter":
63
+ return handle(() => {
64
+ if ($config.canAddRows) {
65
+ dispatch("add-row-inline")
66
+ }
67
+ })
68
+ }
69
+ }
70
+
71
+ // Handle certain key presses if we have cells selected
72
+ if ($selectedCellCount) {
73
+ switch (e.key) {
74
+ case "Escape":
75
+ return handle(selectedCells.actions.clear)
76
+ case "Delete":
77
+ case "Backspace":
78
+ return handle(() => dispatch("request-bulk-delete"))
79
+ }
80
+ }
81
+
82
+ // Handle certain key presses only if no cell focused
54
83
  if (!$focusedCellId) {
55
84
  if (e.key === "Tab" || e.key?.startsWith("Arrow")) {
56
- e.preventDefault()
57
- focusFirstCell()
85
+ handle(focusFirstCell)
58
86
  } else if (e.key === "Delete" || e.key === "Backspace") {
59
- if (Object.keys($selectedRows).length && $config.canDeleteRows) {
60
- dispatch("request-bulk-delete")
61
- }
87
+ handle(() => {
88
+ if ($selectedRowCount && $config.canDeleteRows) {
89
+ dispatch("request-bulk-delete")
90
+ }
91
+ })
62
92
  }
93
+ // Avoid processing anything else
63
94
  return
64
95
  }
65
96
 
@@ -69,18 +100,19 @@
69
100
  // By setting a tiny timeout here we can ensure that other listeners
70
101
  // which depend on being able to read cell state on an escape keypress
71
102
  // get a chance to observe the true state before we blur
72
- if (api?.isActive()) {
73
- setTimeout(api?.blur, 10)
74
- } else {
75
- $focusedCellId = null
76
- }
77
- menu.actions.close()
78
- return
103
+ return handle(() => {
104
+ if (api?.isActive()) {
105
+ setTimeout(api?.blur, 10)
106
+ } else {
107
+ $focusedCellId = null
108
+ }
109
+ menu.actions.close()
110
+ })
79
111
  } else if (e.key === "Tab") {
80
- e.preventDefault()
81
- api?.blur?.()
82
- changeFocusedColumn(1)
83
- return
112
+ return handle(() => {
113
+ api?.blur?.()
114
+ changeFocusedColumn(1)
115
+ })
84
116
  }
85
117
 
86
118
  // Pass the key event to the selected cell and let it decide whether to
@@ -91,57 +123,33 @@
91
123
  return
92
124
  }
93
125
  }
94
- e.preventDefault()
95
126
 
96
127
  // Handle the key ourselves
97
128
  if (e.metaKey || e.ctrlKey) {
98
- switch (e.key) {
99
- case "c":
100
- clipboard.actions.copy()
101
- break
102
- case "v":
103
- if (!api?.isReadonly()) {
104
- clipboard.actions.paste()
105
- }
106
- break
107
- case "Enter":
108
- if ($config.canAddRows) {
109
- dispatch("add-row-inline")
110
- }
111
- }
129
+ //
112
130
  } else {
113
131
  switch (e.key) {
114
132
  case "ArrowLeft":
115
- changeFocusedColumn(-1)
116
- break
133
+ return handle(() => changeFocusedColumn(-1, e.shiftKey))
117
134
  case "ArrowRight":
118
- changeFocusedColumn(1)
119
- break
135
+ return handle(() => changeFocusedColumn(1, e.shiftKey))
120
136
  case "ArrowUp":
121
- changeFocusedRow(-1)
122
- break
137
+ return handle(() => changeFocusedRow(-1, e.shiftKey))
123
138
  case "ArrowDown":
124
- changeFocusedRow(1)
125
- break
139
+ return handle(() => changeFocusedRow(1, e.shiftKey))
126
140
  case "Delete":
127
141
  case "Backspace":
128
- if (Object.keys($selectedRows).length && $config.canDeleteRows) {
129
- dispatch("request-bulk-delete")
130
- } else {
131
- deleteSelectedCell()
132
- }
133
- break
142
+ return handle(() => {
143
+ if ($selectedRowCount && $config.canDeleteRows) {
144
+ dispatch("request-bulk-delete")
145
+ } else {
146
+ deleteSelectedCell()
147
+ }
148
+ })
134
149
  case "Enter":
135
- focusCell()
136
- break
137
- case " ":
138
- case "Space":
139
- if ($config.canDeleteRows) {
140
- toggleSelectRow()
141
- }
142
- break
150
+ return handle(focusCell)
143
151
  default:
144
- startEnteringValue(e.key, e.which)
152
+ return handle(() => startEnteringValue(e.key, e.which))
145
153
  }
146
154
  }
147
155
  }
@@ -152,7 +160,7 @@
152
160
  if (!firstRow) {
153
161
  return
154
162
  }
155
- const firstColumn = $stickyColumn || $visibleColumns[0]
163
+ const firstColumn = $visibleColumns[0]
156
164
  if (!firstColumn) {
157
165
  return
158
166
  }
@@ -160,38 +168,87 @@
160
168
  }
161
169
 
162
170
  // Changes the focused cell by moving it left or right to a different column
163
- const changeFocusedColumn = delta => {
164
- if (!$focusedCellId) {
171
+ const changeFocusedColumn = (delta, shiftKey) => {
172
+ // Determine which cell we are working with
173
+ let sourceCellId = $focusedCellId
174
+ if (shiftKey && $selectedCellCount) {
175
+ sourceCellId = $cellSelection.targetCellId
176
+ }
177
+ if (!sourceCellId) {
165
178
  return
166
179
  }
167
- const cols = $visibleColumns
168
- const { id, field: columnName } = parseCellID($focusedCellId)
169
- let newColumnName
170
- if (columnName === $stickyColumn?.name) {
171
- const index = delta - 1
172
- newColumnName = cols[index]?.name
173
- } else {
174
- const index = cols.findIndex(col => col.name === columnName) + delta
175
- if (index === -1) {
176
- newColumnName = $stickyColumn?.name
180
+
181
+ // Determine the new position for this cell
182
+ const { rowId, field } = parseCellID(sourceCellId)
183
+ const colIdx = $columnLookupMap[field].__idx
184
+ const nextColumn = $visibleColumns[colIdx + delta]
185
+ if (!nextColumn) {
186
+ return
187
+ }
188
+ const targetCellId = getCellID(rowId, nextColumn.name)
189
+
190
+ // Apply change
191
+ if (shiftKey) {
192
+ if ($selectedCellCount) {
193
+ // We have selected cells and still are holding shift - update selection
194
+ selectedCells.actions.updateTarget(targetCellId)
195
+
196
+ // Restore focused cell if this removes the selection
197
+ if (!$selectedCellCount) {
198
+ focusedCellId.set(targetCellId)
199
+ }
177
200
  } else {
178
- newColumnName = cols[index]?.name
201
+ // We have no selection but are holding shift - select these cells
202
+ selectedCells.actions.selectRange(sourceCellId, targetCellId)
179
203
  }
180
- }
181
- if (newColumnName) {
182
- $focusedCellId = getCellID(id, newColumnName)
204
+ } else {
205
+ // We aren't holding shift - just focus this cell
206
+ focusedCellId.set(targetCellId)
183
207
  }
184
208
  }
185
209
 
186
210
  // Changes the focused cell by moving it up or down to a new row
187
- const changeFocusedRow = delta => {
188
- if (!$focusedRow) {
211
+ const changeFocusedRow = (delta, shiftKey) => {
212
+ // Ignore for new row component
213
+ if ($focusedRowId === NewRowID) {
189
214
  return
190
215
  }
191
- const newRow = $rows[$focusedRow.__idx + delta]
192
- if (newRow) {
193
- const { field } = parseCellID($focusedCellId)
194
- $focusedCellId = getCellID(newRow._id, field)
216
+
217
+ // Determine which cell we are working with
218
+ let sourceCellId = $focusedCellId
219
+ if (shiftKey && $selectedCellCount) {
220
+ sourceCellId = $cellSelection.targetCellId
221
+ }
222
+ if (!sourceCellId) {
223
+ return
224
+ }
225
+
226
+ // Determine the new position for this cell
227
+ const { rowId, field } = parseCellID(sourceCellId)
228
+ const rowIdx = $rowLookupMap[rowId].__idx
229
+ const newRow = $rows[rowIdx + delta]
230
+ if (!newRow) {
231
+ return
232
+ }
233
+ const targetCellId = getCellID(newRow._id, field)
234
+
235
+ // Apply change
236
+ if (shiftKey) {
237
+ if ($selectedCellCount) {
238
+ // We have selected cells and still are holding shift - update selection
239
+ selectedCells.actions.updateTarget(targetCellId)
240
+
241
+ // Restore focused cell if this removes the selection
242
+ if (!$selectedCellCount) {
243
+ focusedCellId.set(targetCellId)
244
+ }
245
+ } else {
246
+ // We have no selection but are holding shift - select these cells
247
+ selectedCells.actions.selectRange(sourceCellId, targetCellId)
248
+ }
249
+ } else {
250
+ // We aren't holding shift - just focus this cell
251
+ focusedCellId.set(targetCellId)
195
252
  }
196
253
  }
197
254
 
@@ -234,14 +291,6 @@
234
291
  }
235
292
  }
236
293
 
237
- const toggleSelectRow = () => {
238
- const id = $focusedRow?._id
239
- if (!id || id === NewRowID) {
240
- return
241
- }
242
- selectedRows.actions.toggleRow(id)
243
- }
244
-
245
294
  onMount(() => {
246
295
  document.addEventListener("keydown", handleKeyDown)
247
296
  return () => {
@@ -9,17 +9,17 @@
9
9
  focusedRow,
10
10
  menu,
11
11
  rows,
12
- columns,
13
- focusedCellId,
14
- stickyColumn,
15
12
  config,
16
- copiedCell,
17
- clipboard,
18
13
  dispatch,
19
- focusedCellAPI,
20
14
  focusedRowId,
21
15
  notifications,
22
16
  hasBudibaseIdentifiers,
17
+ selectedRowCount,
18
+ copyAllowed,
19
+ pasteAllowed,
20
+ selectedCellCount,
21
+ visibleColumns,
22
+ selectedCells,
23
23
  } = getContext("grid")
24
24
 
25
25
  let anchor
@@ -32,17 +32,20 @@
32
32
  }
33
33
 
34
34
  const deleteRow = () => {
35
- rows.actions.deleteRows([$focusedRow])
36
35
  menu.actions.close()
36
+ rows.actions.deleteRows([$focusedRow])
37
37
  $notifications.success("Deleted 1 row")
38
38
  }
39
39
 
40
- const duplicate = async () => {
40
+ const duplicateRow = async () => {
41
41
  menu.actions.close()
42
42
  const newRow = await rows.actions.duplicateRow($focusedRow)
43
43
  if (newRow) {
44
- const column = $stickyColumn?.name || $columns[0].name
45
- $focusedCellId = getCellID(newRow._id, column)
44
+ const firstCol = $visibleColumns[0]
45
+ const lastCol = $visibleColumns[$visibleColumns.length - 1]
46
+ const startCellId = getCellID(newRow._id, firstCol.name)
47
+ const endCellId = getCellID(newRow._id, lastCol.name)
48
+ selectedCells.actions.selectRange(startCellId, endCellId)
46
49
  }
47
50
  }
48
51
 
@@ -58,59 +61,107 @@
58
61
  {#key style}
59
62
  <GridPopover {anchor} on:close={menu.actions.close} maxHeight={null}>
60
63
  <Menu>
61
- <MenuItem
62
- icon="Copy"
63
- on:click={clipboard.actions.copy}
64
- on:click={menu.actions.close}
65
- >
66
- Copy
67
- </MenuItem>
68
- <MenuItem
69
- icon="Paste"
70
- disabled={$copiedCell == null || $focusedCellAPI?.isReadonly()}
71
- on:click={clipboard.actions.paste}
72
- on:click={menu.actions.close}
73
- >
74
- Paste
75
- </MenuItem>
76
- <MenuItem
77
- icon="Maximize"
78
- disabled={isNewRow || !$config.canEditRows || !$config.canExpandRows}
79
- on:click={() => dispatch("edit-row", $focusedRow)}
80
- on:click={menu.actions.close}
81
- >
82
- Edit row in modal
83
- </MenuItem>
84
- <MenuItem
85
- icon="Copy"
86
- disabled={isNewRow || !$focusedRow?._id || !$hasBudibaseIdentifiers}
87
- on:click={() => copyToClipboard($focusedRow?._id)}
88
- on:click={menu.actions.close}
89
- >
90
- Copy row _id
91
- </MenuItem>
92
- <MenuItem
93
- icon="Copy"
94
- disabled={isNewRow || !$focusedRow?._rev || !$hasBudibaseIdentifiers}
95
- on:click={() => copyToClipboard($focusedRow?._rev)}
96
- on:click={menu.actions.close}
97
- >
98
- Copy row _rev
99
- </MenuItem>
100
- <MenuItem
101
- icon="Duplicate"
102
- disabled={isNewRow || !$config.canAddRows}
103
- on:click={duplicate}
104
- >
105
- Duplicate row
106
- </MenuItem>
107
- <MenuItem
108
- icon="Delete"
109
- disabled={isNewRow || !$config.canDeleteRows}
110
- on:click={deleteRow}
111
- >
112
- Delete row
113
- </MenuItem>
64
+ {#if $menu.multiRowMode}
65
+ <MenuItem
66
+ icon="Duplicate"
67
+ disabled={!$config.canAddRows || $selectedRowCount > 50}
68
+ on:click={() => dispatch("request-bulk-duplicate")}
69
+ on:click={menu.actions.close}
70
+ >
71
+ Duplicate {$selectedRowCount} rows
72
+ </MenuItem>
73
+ <MenuItem
74
+ icon="Delete"
75
+ disabled={!$config.canDeleteRows}
76
+ on:click={() => dispatch("request-bulk-delete")}
77
+ on:click={menu.actions.close}
78
+ >
79
+ Delete {$selectedRowCount} rows
80
+ </MenuItem>
81
+ {:else if $menu.multiCellMode}
82
+ <MenuItem
83
+ icon="Copy"
84
+ disabled={!$copyAllowed}
85
+ on:click={() => dispatch("copy")}
86
+ on:click={menu.actions.close}
87
+ >
88
+ Copy
89
+ </MenuItem>
90
+ <MenuItem
91
+ icon="Paste"
92
+ disabled={!$pasteAllowed}
93
+ on:click={() => dispatch("paste")}
94
+ on:click={menu.actions.close}
95
+ >
96
+ Paste
97
+ </MenuItem>
98
+ <MenuItem
99
+ icon="Delete"
100
+ disabled={!$config.canEditRows}
101
+ on:click={() => dispatch("request-bulk-delete")}
102
+ >
103
+ Delete {$selectedCellCount} cells
104
+ </MenuItem>
105
+ {:else}
106
+ <MenuItem
107
+ icon="Copy"
108
+ disabled={!$copyAllowed}
109
+ on:click={() => dispatch("copy")}
110
+ on:click={menu.actions.close}
111
+ >
112
+ Copy
113
+ </MenuItem>
114
+ <MenuItem
115
+ icon="Paste"
116
+ disabled={!$pasteAllowed}
117
+ on:click={() => dispatch("paste")}
118
+ on:click={menu.actions.close}
119
+ >
120
+ Paste
121
+ </MenuItem>
122
+ <MenuItem
123
+ icon="Maximize"
124
+ disabled={isNewRow ||
125
+ !$config.canEditRows ||
126
+ !$config.canExpandRows}
127
+ on:click={() => dispatch("edit-row", $focusedRow)}
128
+ on:click={menu.actions.close}
129
+ >
130
+ Edit row in modal
131
+ </MenuItem>
132
+ <MenuItem
133
+ icon="Copy"
134
+ disabled={isNewRow || !$focusedRow?._id || !$hasBudibaseIdentifiers}
135
+ on:click={() => copyToClipboard($focusedRow?._id)}
136
+ on:click={menu.actions.close}
137
+ >
138
+ Copy row _id
139
+ </MenuItem>
140
+ <MenuItem
141
+ icon="Copy"
142
+ disabled={isNewRow ||
143
+ !$focusedRow?._rev ||
144
+ !$hasBudibaseIdentifiers}
145
+ on:click={() => copyToClipboard($focusedRow?._rev)}
146
+ on:click={menu.actions.close}
147
+ >
148
+ Copy row _rev
149
+ </MenuItem>
150
+ <MenuItem
151
+ icon="Duplicate"
152
+ disabled={isNewRow || !$config.canAddRows}
153
+ on:click={duplicateRow}
154
+ >
155
+ Duplicate row
156
+ </MenuItem>
157
+ <MenuItem
158
+ icon="Delete"
159
+ disabled={isNewRow || !$config.canDeleteRows}
160
+ on:click={deleteRow}
161
+ >
162
+ Delete row
163
+ </MenuItem>
164
+ {/if}
114
165
  </Menu>
115
166
  </GridPopover>
116
167
  {/key}