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