Add context menu to sheets with deletion and duplication features
This commit is contained in:
parent
ef54813764
commit
3c71acd68e
|
@ -1,5 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { Modal, ModalContent, ActionButton } from "@budibase/bbui"
|
import {
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ActionButton,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { selectedRows, rows } = getContext("sheet")
|
const { selectedRows, rows } = getContext("sheet")
|
||||||
|
@ -19,7 +24,9 @@
|
||||||
|
|
||||||
// Deletion callback when confirmed
|
// Deletion callback when confirmed
|
||||||
const performDeletion = async () => {
|
const performDeletion = async () => {
|
||||||
|
const count = rowsToDelete.length
|
||||||
await rows.actions.deleteRows(rowsToDelete)
|
await rows.actions.deleteRows(rowsToDelete)
|
||||||
|
notifications.success(`Deleted ${count} row${count === 1 ? "" : "s"}`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -33,7 +40,7 @@
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Add screens"
|
title="Delete rows"
|
||||||
confirmText="Continue"
|
confirmText="Continue"
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
onConfirm={performDeletion}
|
onConfirm={performDeletion}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
clickOutside,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
const { selectedCellRow, menu, rows, columns, selectedCellId } =
|
||||||
|
getContext("sheet")
|
||||||
|
|
||||||
|
let modal
|
||||||
|
|
||||||
|
$: style = makeStyle($menu)
|
||||||
|
|
||||||
|
const makeStyle = menu => {
|
||||||
|
return `left:${menu.left}px; top:${menu.top}px;`
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRow = () => {
|
||||||
|
rows.actions.deleteRows([$selectedCellRow])
|
||||||
|
menu.actions.close()
|
||||||
|
notifications.success("Deleted 1 row")
|
||||||
|
}
|
||||||
|
|
||||||
|
const duplicate = async () => {
|
||||||
|
let clone = { ...$selectedCellRow }
|
||||||
|
delete clone._id
|
||||||
|
delete clone._rev
|
||||||
|
delete clone.__idx
|
||||||
|
const newRow = await rows.actions.addRow(clone, $selectedCellRow.__idx + 1)
|
||||||
|
if (newRow) {
|
||||||
|
$selectedCellId = `${newRow._id}-${$columns[0].name}`
|
||||||
|
menu.actions.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $menu.visible}
|
||||||
|
<div class="menu" {style} use:clickOutside={() => menu.actions.close()}>
|
||||||
|
<Menu>
|
||||||
|
<MenuItem icon="Delete" on:click={modal.show}>Delete row</MenuItem>
|
||||||
|
<MenuItem icon="Duplicate" on:click={duplicate}>Duplicate row</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<ModalContent
|
||||||
|
title="Delete row"
|
||||||
|
confirmText="Continue"
|
||||||
|
cancelText="Cancel"
|
||||||
|
onConfirm={deleteRow}
|
||||||
|
size="M"
|
||||||
|
>
|
||||||
|
Are you sure you want to delete this row?
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.menu {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--cell-background);
|
||||||
|
border: var(--cell-border);
|
||||||
|
width: 160px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -14,11 +14,13 @@
|
||||||
import { createWebsocket } from "./websocket"
|
import { createWebsocket } from "./websocket"
|
||||||
import { createUserStores } from "./stores/users"
|
import { createUserStores } from "./stores/users"
|
||||||
import { createResizeStores } from "./stores/resize"
|
import { createResizeStores } from "./stores/resize"
|
||||||
|
import { createMenuStores } from "./stores/menu"
|
||||||
import DeleteButton from "./DeleteButton.svelte"
|
import DeleteButton from "./DeleteButton.svelte"
|
||||||
import SheetBody from "./SheetBody.svelte"
|
import SheetBody from "./SheetBody.svelte"
|
||||||
import ResizeOverlay from "./ResizeOverlay.svelte"
|
import ResizeOverlay from "./ResizeOverlay.svelte"
|
||||||
import HeaderRow from "./HeaderRow.svelte"
|
import HeaderRow from "./HeaderRow.svelte"
|
||||||
import ScrollOverlay from "./ScrollOverlay.svelte"
|
import ScrollOverlay from "./ScrollOverlay.svelte"
|
||||||
|
import MenuOverlay from "./MenuOverlay.svelte"
|
||||||
import StickyColumn from "./StickyColumn.svelte"
|
import StickyColumn from "./StickyColumn.svelte"
|
||||||
import UserAvatars from "./UserAvatars.svelte"
|
import UserAvatars from "./UserAvatars.svelte"
|
||||||
|
|
||||||
|
@ -60,6 +62,7 @@
|
||||||
context = { ...context, ...createReorderStores(context) }
|
context = { ...context, ...createReorderStores(context) }
|
||||||
context = { ...context, ...createInterfaceStores(context) }
|
context = { ...context, ...createInterfaceStores(context) }
|
||||||
context = { ...context, ...createUserStores(context) }
|
context = { ...context, ...createUserStores(context) }
|
||||||
|
context = { ...context, ...createMenuStores(context) }
|
||||||
|
|
||||||
// Reference some stores for local use
|
// Reference some stores for local use
|
||||||
const { isResizing, isReordering } = context
|
const { isResizing, isReordering } = context
|
||||||
|
@ -107,6 +110,7 @@
|
||||||
</div>
|
</div>
|
||||||
<ResizeOverlay />
|
<ResizeOverlay />
|
||||||
<ScrollOverlay />
|
<ScrollOverlay />
|
||||||
|
<MenuOverlay />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -122,7 +126,7 @@
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
--cell-background: var(--spectrum-global-color-gray-50);
|
--cell-background: var(--spectrum-global-color-gray-50);
|
||||||
--cell-background-hover: var(--spectrum-global-color-gray-75);
|
--cell-background-hover: var(--spectrum-global-color-gray-100);
|
||||||
--cell-padding: 10px;
|
--cell-padding: 10px;
|
||||||
--cell-spacing: 4px;
|
--cell-spacing: 4px;
|
||||||
--cell-font-size: 14px;
|
--cell-font-size: 14px;
|
||||||
|
|
|
@ -4,27 +4,23 @@
|
||||||
import NewRow from "./NewRow.svelte"
|
import NewRow from "./NewRow.svelte"
|
||||||
import SheetRow from "./SheetRow.svelte"
|
import SheetRow from "./SheetRow.svelte"
|
||||||
|
|
||||||
const { selectedCellId, bounds, visibleRows, config } = getContext("sheet")
|
const { bounds, visibleRows, config } = getContext("sheet")
|
||||||
|
|
||||||
let ref
|
let body
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Observe and record the height of the body
|
// Observe and record the height of the body
|
||||||
const observer = new ResizeObserver(() => {
|
const observer = new ResizeObserver(() => {
|
||||||
bounds.set(ref.getBoundingClientRect())
|
bounds.set(body.getBoundingClientRect())
|
||||||
})
|
})
|
||||||
observer.observe(ref)
|
observer.observe(body)
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div bind:this={body} class="sheet-body">
|
||||||
bind:this={ref}
|
|
||||||
class="sheet-body"
|
|
||||||
on:click|self={() => ($selectedCellId = null)}
|
|
||||||
>
|
|
||||||
<SheetScrollWrapper>
|
<SheetScrollWrapper>
|
||||||
{#each $visibleRows as row, idx}
|
{#each $visibleRows as row, idx}
|
||||||
<SheetRow {row} {idx} />
|
<SheetRow {row} {idx} />
|
||||||
|
|
|
@ -14,10 +14,13 @@
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
hoveredRowId,
|
hoveredRowId,
|
||||||
selectedCellMap,
|
selectedCellMap,
|
||||||
|
selectedCellRow,
|
||||||
|
menu,
|
||||||
} = getContext("sheet")
|
} = getContext("sheet")
|
||||||
|
|
||||||
$: rowSelected = !!$selectedRows[row._id]
|
$: rowSelected = !!$selectedRows[row._id]
|
||||||
$: rowHovered = $hoveredRowId === row._id
|
$: rowHovered = $hoveredRowId === row._id
|
||||||
|
$: containsSelectedCell = $selectedCellRow?._id === row._id
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -27,23 +30,24 @@
|
||||||
on:mouseleave={() => ($hoveredRowId = null)}
|
on:mouseleave={() => ($hoveredRowId = null)}
|
||||||
>
|
>
|
||||||
{#each $visibleColumns as column (column.name)}
|
{#each $visibleColumns as column (column.name)}
|
||||||
{@const cellIdx = `${row._id}-${column.name}`}
|
{@const cellId = `${row._id}-${column.name}`}
|
||||||
<SheetCell
|
<SheetCell
|
||||||
{rowSelected}
|
rowSelected={rowSelected || containsSelectedCell}
|
||||||
{rowHovered}
|
{rowHovered}
|
||||||
rowIdx={idx}
|
rowIdx={idx}
|
||||||
selected={$selectedCellId === cellIdx}
|
selected={$selectedCellId === cellId}
|
||||||
selectedUser={$selectedCellMap[cellIdx]}
|
selectedUser={$selectedCellMap[cellId]}
|
||||||
reorderSource={$reorder.sourceColumn === column.name}
|
reorderSource={$reorder.sourceColumn === column.name}
|
||||||
reorderTarget={$reorder.targetColumn === column.name}
|
reorderTarget={$reorder.targetColumn === column.name}
|
||||||
on:click={() => ($selectedCellId = cellIdx)}
|
on:click={() => ($selectedCellId = cellId)}
|
||||||
|
on:contextmenu={e => menu.actions.open(cellId, e)}
|
||||||
width={column.width}
|
width={column.width}
|
||||||
>
|
>
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={getCellRenderer(column)}
|
this={getCellRenderer(column)}
|
||||||
value={row[column.name]}
|
value={row[column.name]}
|
||||||
schema={column.schema}
|
schema={column.schema}
|
||||||
selected={$selectedCellId === cellIdx}
|
selected={$selectedCellId === cellId}
|
||||||
onChange={val => rows.actions.updateRow(row._id, column, val)}
|
onChange={val => rows.actions.updateRow(row._id, column, val)}
|
||||||
readonly={column.schema.autocolumn}
|
readonly={column.schema.autocolumn}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
hoveredRowId,
|
hoveredRowId,
|
||||||
maxScrollTop,
|
maxScrollTop,
|
||||||
maxScrollLeft,
|
maxScrollLeft,
|
||||||
|
selectedCellId,
|
||||||
} = getContext("sheet")
|
} = getContext("sheet")
|
||||||
|
|
||||||
export let scrollVertically = true
|
export let scrollVertically = true
|
||||||
|
@ -71,7 +72,11 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="outer" on:wheel={wheelInteractive ? handleWheel : null}>
|
<div
|
||||||
|
class="outer"
|
||||||
|
on:wheel={wheelInteractive ? handleWheel : null}
|
||||||
|
on:click|self={() => ($selectedCellId = null)}
|
||||||
|
>
|
||||||
<div {style}>
|
<div {style}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { Checkbox, Icon } from "@budibase/bbui"
|
import { Checkbox, Icon } from "@budibase/bbui"
|
||||||
import { getIconForField } from "./utils"
|
|
||||||
import SheetCell from "./cells/SheetCell.svelte"
|
import SheetCell from "./cells/SheetCell.svelte"
|
||||||
import { getCellRenderer } from "./renderers"
|
import { getCellRenderer } from "./renderers"
|
||||||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||||
|
@ -18,6 +17,7 @@
|
||||||
reorder,
|
reorder,
|
||||||
config,
|
config,
|
||||||
selectedCellMap,
|
selectedCellMap,
|
||||||
|
selectedCellRow,
|
||||||
} = getContext("sheet")
|
} = getContext("sheet")
|
||||||
|
|
||||||
$: scrollLeft = $scroll.left
|
$: scrollLeft = $scroll.left
|
||||||
|
@ -88,8 +88,14 @@
|
||||||
{#each $visibleRows as row, idx}
|
{#each $visibleRows as row, idx}
|
||||||
{@const rowSelected = !!$selectedRows[row._id]}
|
{@const rowSelected = !!$selectedRows[row._id]}
|
||||||
{@const rowHovered = $hoveredRowId === row._id}
|
{@const rowHovered = $hoveredRowId === row._id}
|
||||||
|
{@const containsSelectedRow = $selectedCellRow?._id === row._id}
|
||||||
<div class="row" on:mouseenter={() => ($hoveredRowId = row._id)}>
|
<div class="row" on:mouseenter={() => ($hoveredRowId = row._id)}>
|
||||||
<SheetCell label {rowSelected} {rowHovered} width="40">
|
<SheetCell
|
||||||
|
label
|
||||||
|
rowSelected={rowSelected || containsSelectedRow}
|
||||||
|
{rowHovered}
|
||||||
|
width="40"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
on:click={() => selectRow(row._id)}
|
on:click={() => selectRow(row._id)}
|
||||||
class="checkbox"
|
class="checkbox"
|
||||||
|
@ -108,9 +114,9 @@
|
||||||
</SheetCell>
|
</SheetCell>
|
||||||
|
|
||||||
{#if $stickyColumn}
|
{#if $stickyColumn}
|
||||||
{@const cellIdx = `${row._id}-${$stickyColumn.name}`}
|
{@const cellIdx = `${row._id}-${stickyColumn.name}`}
|
||||||
<SheetCell
|
<SheetCell
|
||||||
{rowSelected}
|
rowSelected={rowSelected || containsSelectedRow}
|
||||||
{rowHovered}
|
{rowHovered}
|
||||||
rowIdx={idx}
|
rowIdx={idx}
|
||||||
sticky
|
sticky
|
||||||
|
|
|
@ -83,10 +83,10 @@
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
.cell.row-selected {
|
.cell.row-selected {
|
||||||
background-color: var(--spectrum-global-color-gray-100);
|
--cell-background: var(--spectrum-global-color-gray-75);
|
||||||
}
|
}
|
||||||
.cell.row-hovered {
|
.cell.row-hovered {
|
||||||
background: var(--cell-background-hover);
|
--cell-background: var(--cell-background-hover);
|
||||||
}
|
}
|
||||||
.cell.center {
|
.cell.center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
|
|
||||||
/* Reorder styles */
|
/* Reorder styles */
|
||||||
.cell.reorder-source {
|
.cell.reorder-source {
|
||||||
background: var(--spectrum-global-color-gray-100);
|
--cell-background: var(--spectrum-global-color-gray-100);
|
||||||
}
|
}
|
||||||
.cell.reorder-target:after {
|
.cell.reorder-target:after {
|
||||||
content: " ";
|
content: " ";
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get, derived } from "svelte/store"
|
||||||
|
|
||||||
export const createInterfaceStores = context => {
|
export const createInterfaceStores = context => {
|
||||||
const { rows } = context
|
const { rows, rowLookupMap } = context
|
||||||
const selectedCellId = writable(null)
|
const selectedCellId = writable(null)
|
||||||
const selectedRows = writable({})
|
const selectedRows = writable({})
|
||||||
const hoveredRowId = writable(null)
|
const hoveredRowId = writable(null)
|
||||||
|
|
||||||
|
// Derive the row that contains the selected cell.
|
||||||
|
const selectedCellRow = derived(
|
||||||
|
[selectedCellId, rowLookupMap, rows],
|
||||||
|
([$selectedCellId, $rowLookupMap, $rows]) => {
|
||||||
|
const rowId = $selectedCellId?.split("-")[0]
|
||||||
|
const index = $rowLookupMap[rowId]
|
||||||
|
return $rows[index]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Ensure we clear invalid rows from state if they disappear
|
// Ensure we clear invalid rows from state if they disappear
|
||||||
rows.subscribe($rows => {
|
rows.subscribe($rows => {
|
||||||
const $selectedCellId = get(selectedCellId)
|
const $selectedCellId = get(selectedCellId)
|
||||||
|
@ -38,5 +48,5 @@ export const createInterfaceStores = context => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return { selectedCellId, selectedRows, hoveredRowId }
|
return { selectedCellId, selectedRows, hoveredRowId, selectedCellRow }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { writable, get } from "svelte/store"
|
||||||
|
|
||||||
|
export const createMenuStores = context => {
|
||||||
|
const { bounds, selectedCellId, stickyColumn, cellHeight } = context
|
||||||
|
const menu = writable({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
visible: false,
|
||||||
|
selectedRow: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = (cellId, e) => {
|
||||||
|
const $bounds = get(bounds)
|
||||||
|
const $stickyColumn = get(stickyColumn)
|
||||||
|
e.preventDefault()
|
||||||
|
selectedCellId.set(cellId)
|
||||||
|
menu.set({
|
||||||
|
left: e.clientX - $bounds.left + 44 + ($stickyColumn?.width || 0),
|
||||||
|
top: e.clientY - $bounds.top + cellHeight + 4,
|
||||||
|
visible: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
menu.update(state => ({
|
||||||
|
...state,
|
||||||
|
visible: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
menu: {
|
||||||
|
...menu,
|
||||||
|
actions: {
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,19 @@ export const createRowsStore = context => {
|
||||||
column: null,
|
column: null,
|
||||||
order: null,
|
order: null,
|
||||||
})
|
})
|
||||||
|
const enrichedRows = derived(rows, $rows => {
|
||||||
|
return $rows.map((row, idx) => ({
|
||||||
|
...row,
|
||||||
|
__idx: idx,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
const rowLookupMap = derived(enrichedRows, $rows => {
|
||||||
|
let map = {}
|
||||||
|
for (let i = 0; i < $rows.length; i++) {
|
||||||
|
map[$rows[i]._id] = i
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
|
||||||
// Local cache of row IDs to speed up checking if a row exists
|
// Local cache of row IDs to speed up checking if a row exists
|
||||||
let rowCacheMap = {}
|
let rowCacheMap = {}
|
||||||
|
@ -84,29 +97,21 @@ export const createRowsStore = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Adds a new empty row
|
// Adds a new empty row
|
||||||
const addRow = async () => {
|
const addRow = async (row, idx) => {
|
||||||
try {
|
try {
|
||||||
// Create row
|
// Create row
|
||||||
let newRow = await API.saveRow({ tableId: get(tableId) })
|
const newRow = await API.saveRow({ ...row, tableId: get(tableId) })
|
||||||
|
|
||||||
// Use search endpoint to fetch the row again, ensuring relationships are
|
|
||||||
// properly enriched
|
|
||||||
const res = await API.searchTable({
|
|
||||||
tableId: get(tableId),
|
|
||||||
limit: 1,
|
|
||||||
query: {
|
|
||||||
equal: {
|
|
||||||
_id: newRow._id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
paginate: false,
|
|
||||||
})
|
|
||||||
if (res?.rows?.[0]) {
|
|
||||||
newRow = res.rows[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
handleNewRows([newRow])
|
if (idx) {
|
||||||
|
rowCacheMap[newRow._id] = true
|
||||||
|
rows.update(state => {
|
||||||
|
state.splice(idx, 0, newRow)
|
||||||
|
return state.slice()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
handleNewRows([newRow])
|
||||||
|
}
|
||||||
return newRow
|
return newRow
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Error adding row: ${error?.message}`)
|
notifications.error(`Error adding row: ${error?.message}`)
|
||||||
|
@ -115,10 +120,6 @@ export const createRowsStore = context => {
|
||||||
|
|
||||||
// Refreshes a specific row, handling updates, addition or deletion
|
// Refreshes a specific row, handling updates, addition or deletion
|
||||||
const refreshRow = async id => {
|
const refreshRow = async id => {
|
||||||
// Get index of row to check if it exists
|
|
||||||
const $rows = get(rows)
|
|
||||||
const index = $rows.findIndex(row => row._id === id)
|
|
||||||
|
|
||||||
// Fetch row from the server again
|
// Fetch row from the server again
|
||||||
const res = await API.searchTable({
|
const res = await API.searchTable({
|
||||||
tableId: get(tableId),
|
tableId: get(tableId),
|
||||||
|
@ -132,12 +133,16 @@ export const createRowsStore = context => {
|
||||||
})
|
})
|
||||||
let newRow = res?.rows?.[0]
|
let newRow = res?.rows?.[0]
|
||||||
|
|
||||||
|
// Get index of row to check if it exists
|
||||||
|
const $rows = get(rows)
|
||||||
|
const index = $rows.findIndex(row => row._id === id)
|
||||||
|
|
||||||
// Process as either an update, addition or deletion
|
// Process as either an update, addition or deletion
|
||||||
if (newRow) {
|
if (newRow) {
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
// An existing row was updated
|
// An existing row was updated
|
||||||
rows.update(state => {
|
rows.update(state => {
|
||||||
state[index] = { ...newRow, __idx: index }
|
state[index] = { ...newRow }
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,8 +187,6 @@ export const createRowsStore = context => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Error saving row: ${error?.message}`)
|
notifications.error(`Error saving row: ${error?.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await refreshRow(row._id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes an array of rows
|
// Deletes an array of rows
|
||||||
|
@ -214,15 +217,7 @@ export const createRowsStore = context => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rowsToAppend.length) {
|
if (rowsToAppend.length) {
|
||||||
rows.update($rows => {
|
rows.update(state => [...state, ...rowsToAppend])
|
||||||
return [
|
|
||||||
...$rows,
|
|
||||||
...rowsToAppend.map((row, idx) => ({
|
|
||||||
...row,
|
|
||||||
__idx: $rows.length + idx,
|
|
||||||
})),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,9 +228,7 @@ export const createRowsStore = context => {
|
||||||
// We deliberately do not remove IDs from the cache map as the data may
|
// We deliberately do not remove IDs from the cache map as the data may
|
||||||
// still exist inside the fetch, but we don't want to add it again
|
// still exist inside the fetch, but we don't want to add it again
|
||||||
rows.update(state => {
|
rows.update(state => {
|
||||||
return state
|
return state.filter(row => !deletedIds.includes(row._id))
|
||||||
.filter(row => !deletedIds.includes(row._id))
|
|
||||||
.map((row, idx) => ({ ...row, __idx: idx }))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// If we ended up with no rows, try getting the next page
|
// If we ended up with no rows, try getting the next page
|
||||||
|
@ -257,6 +250,7 @@ export const createRowsStore = context => {
|
||||||
return {
|
return {
|
||||||
rows: {
|
rows: {
|
||||||
...rows,
|
...rows,
|
||||||
|
subscribe: enrichedRows.subscribe,
|
||||||
actions: {
|
actions: {
|
||||||
addRow,
|
addRow,
|
||||||
updateRow,
|
updateRow,
|
||||||
|
@ -267,6 +261,7 @@ export const createRowsStore = context => {
|
||||||
refreshSchema,
|
refreshSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
rowLookupMap,
|
||||||
table,
|
table,
|
||||||
schema,
|
schema,
|
||||||
sort,
|
sort,
|
||||||
|
|
Loading…
Reference in New Issue