Add row actions to tables in data section
This commit is contained in:
parent
9fc837b04a
commit
c5e27b860f
|
@ -15,7 +15,6 @@
|
|||
let anchor
|
||||
let dropdown
|
||||
let timeout
|
||||
let open
|
||||
|
||||
// This is needed because display: contents is considered "invisible".
|
||||
// It should only ever be an action button, so should be fine.
|
||||
|
@ -23,7 +22,7 @@
|
|||
anchor = node.firstChild
|
||||
}
|
||||
|
||||
export const show = () => {
|
||||
export const show = e => {
|
||||
cancelHide()
|
||||
dropdown.show()
|
||||
}
|
||||
|
@ -64,11 +63,10 @@
|
|||
on:mouseenter={openOnHover ? show : null}
|
||||
on:mouseleave={openOnHover ? queueHide : null}
|
||||
>
|
||||
<slot name="control" {open} />
|
||||
<slot name="control" />
|
||||
</div>
|
||||
<Popover
|
||||
bind:this={dropdown}
|
||||
bind:open
|
||||
{anchor}
|
||||
{align}
|
||||
{portalTarget}
|
||||
|
|
|
@ -169,7 +169,7 @@ export default function positionDropdown(element, opts) {
|
|||
align === "left-context-menu"
|
||||
) {
|
||||
applyYStrategy(Strategies.StartToStart)
|
||||
styles.top -= 4 // Manual adjustment for action menu padding
|
||||
styles.top -= 5 // Manual adjustment for action menu padding
|
||||
} else {
|
||||
applyYStrategy(Strategies.StartToEnd)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { Banner } from "@budibase/bbui"
|
||||
import { Banner, Menu, MenuItem, Popover } from "@budibase/bbui"
|
||||
import { datasources, tables, integrations, appStore } from "stores/builder"
|
||||
import { themeStore, admin } from "stores/portal"
|
||||
import { TableNames } from "constants"
|
||||
|
@ -28,7 +28,12 @@
|
|||
status: { displayName: "Status", disabled: true },
|
||||
}
|
||||
|
||||
let rowActions = []
|
||||
let generateButton
|
||||
let rowActionPopover
|
||||
let rowActionRow
|
||||
let rowActionAnchor
|
||||
let refreshRow
|
||||
|
||||
$: autoColumnStatus = verifyAutocolumns($tables?.selected)
|
||||
$: duplicates = Object.values(autoColumnStatus).reduce((acc, status) => {
|
||||
|
@ -53,6 +58,20 @@
|
|||
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
||||
$: currentTheme = $themeStore?.theme
|
||||
$: darkMode = !currentTheme.includes("light")
|
||||
$: buttons = [
|
||||
{
|
||||
text: "Actions",
|
||||
type: "cta",
|
||||
icon: "ChevronDown",
|
||||
onClick: async (e, row, refresh) => {
|
||||
rowActionRow = row
|
||||
rowActionAnchor = e.currentTarget
|
||||
rowActionPopover.show()
|
||||
refreshRow = refresh
|
||||
},
|
||||
},
|
||||
]
|
||||
$: fetchRowActions(id)
|
||||
|
||||
const relationshipSupport = datasource => {
|
||||
const integration = $integrations[datasource?.source]
|
||||
|
@ -82,6 +101,25 @@
|
|||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
const fetchRowActions = async tableId => {
|
||||
if (!tableId) {
|
||||
rowActions = []
|
||||
return
|
||||
}
|
||||
const res = await API.rowActions.fetch(tableId)
|
||||
rowActions = Object.values(res || {})
|
||||
}
|
||||
|
||||
const runRowAction = async action => {
|
||||
await API.rowActions.trigger({
|
||||
rowActionId: action.id,
|
||||
tableId: id,
|
||||
rowId: rowActionRow._id,
|
||||
})
|
||||
await refreshRow()
|
||||
rowActionPopover.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $tables?.selected?.name}
|
||||
|
@ -105,6 +143,7 @@
|
|||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
||||
showAvatars={false}
|
||||
isCloud={$admin.cloud}
|
||||
buttons={rowActions.length ? buttons : null}
|
||||
on:updatedatasource={handleGridTableUpdate}
|
||||
>
|
||||
<!-- Controls -->
|
||||
|
@ -153,6 +192,19 @@
|
|||
<i>Create your first table to start building</i>
|
||||
{/if}
|
||||
|
||||
<Popover
|
||||
bind:this={rowActionPopover}
|
||||
align="right"
|
||||
anchor={rowActionAnchor}
|
||||
offset={5}
|
||||
>
|
||||
<Menu>
|
||||
{#each rowActions as action}
|
||||
<MenuItem on:click={() => runRowAction(action)}>{action.name}</MenuItem>
|
||||
{/each}
|
||||
</Menu>
|
||||
</Popover>
|
||||
|
||||
<style>
|
||||
i {
|
||||
font-size: var(--font-size-m);
|
||||
|
|
|
@ -115,13 +115,15 @@
|
|||
text: settings.text,
|
||||
type: settings.type,
|
||||
icon: settings.icon,
|
||||
onClick: async row => {
|
||||
onClick: async (_, row, refresh) => {
|
||||
// Create a fake, ephemeral context to run the buttons actions with
|
||||
const id = get(component).id
|
||||
const gridContext = createContextStore(context)
|
||||
gridContext.actions.provideData(id, row)
|
||||
const fn = enrichButtonActions(settings.onClick, get(gridContext))
|
||||
return await fn?.({ row })
|
||||
const res = await fn?.({ row })
|
||||
await refresh()
|
||||
return res
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -55,9 +55,12 @@ export const buildRowActionEndpoints = API => ({
|
|||
* @param tableId the ID of the table
|
||||
* @param rowActionId the ID of the row action to trigger
|
||||
*/
|
||||
trigger: async ({ tableId, rowActionId }) => {
|
||||
trigger: async ({ tableId, rowActionId, rowId }) => {
|
||||
return await API.post({
|
||||
url: `/api/tables/${tableId}/actions/${rowActionId}/trigger`,
|
||||
body: {
|
||||
rowId,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
isDragging,
|
||||
buttonColumnWidth,
|
||||
showVScrollbar,
|
||||
showHScrollbar,
|
||||
dispatch,
|
||||
} = getContext("grid")
|
||||
|
||||
|
@ -32,10 +33,12 @@
|
|||
$: gridEnd = $width - $buttonColumnWidth - 1
|
||||
$: left = Math.min(columnEnd, gridEnd)
|
||||
|
||||
const handleClick = async (button, row) => {
|
||||
await button.onClick?.(rows.actions.cleanRow(row))
|
||||
// Refresh the row in case it changed
|
||||
await rows.actions.refreshRow(row._id)
|
||||
const handleClick = async (e, button, row) => {
|
||||
await button.onClick?.(
|
||||
e,
|
||||
rows.actions.cleanRow(row),
|
||||
async () => await rows.actions.refreshRow(row._id)
|
||||
)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
@ -72,23 +75,29 @@
|
|||
highlighted={rowHovered || rowFocused}
|
||||
metadata={row.__metadata?.row}
|
||||
>
|
||||
<div class="buttons" class:offset={$showVScrollbar}>
|
||||
<div
|
||||
class="buttons"
|
||||
class:offset={$showVScrollbar && $showHScrollbar}
|
||||
>
|
||||
{#each buttons as button}
|
||||
<Button
|
||||
newStyles
|
||||
size="S"
|
||||
cta={button.type === "cta"}
|
||||
primary={button.type === "primary"}
|
||||
secondary={button.type === "secondary"}
|
||||
warning={button.type === "warning"}
|
||||
overBackground={button.type === "overBackground"}
|
||||
on:click={() => handleClick(button, row)}
|
||||
>
|
||||
{#if button.icon}
|
||||
<i class="{button.icon} S" />
|
||||
{/if}
|
||||
{button.text || "Button"}
|
||||
</Button>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span on:click={e => handleClick(e, button, row)}>
|
||||
<Button
|
||||
newStyles
|
||||
size="S"
|
||||
icon={button.icon}
|
||||
cta={button.type === "cta"}
|
||||
primary={button.type === "primary"}
|
||||
secondary={button.type === "secondary"}
|
||||
warning={button.type === "warning"}
|
||||
overBackground={button.type === "overBackground"}
|
||||
>
|
||||
{#if button.icon}
|
||||
<i class="{button.icon} S" />
|
||||
{/if}
|
||||
{button.text || "Button"}
|
||||
</Button>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</GridCell>
|
||||
|
|
Loading…
Reference in New Issue