Add common collapsed button group and use it for row actions and client button groups. Add collapsed settings to grids and form blocks
This commit is contained in:
parent
d8b6d10dce
commit
51cbced119
|
@ -0,0 +1,39 @@
|
||||||
|
<script>
|
||||||
|
import Button from "../Button/Button.svelte"
|
||||||
|
import Popover from "../Popover/Popover.svelte"
|
||||||
|
import Menu from "../Menu/Menu.svelte"
|
||||||
|
import MenuItem from "../Menu/Item.svelte"
|
||||||
|
|
||||||
|
export let buttons
|
||||||
|
export let text = "Action"
|
||||||
|
export let size = "M"
|
||||||
|
export let align = "left"
|
||||||
|
export let offset
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let popover
|
||||||
|
|
||||||
|
const handleClick = async button => {
|
||||||
|
popover.hide()
|
||||||
|
await button.onClick?.()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
bind:ref={anchor}
|
||||||
|
{size}
|
||||||
|
icon="ChevronDown"
|
||||||
|
cta
|
||||||
|
on:click={() => popover?.show()}
|
||||||
|
>
|
||||||
|
{text || "Action"}
|
||||||
|
</Button>
|
||||||
|
<Popover bind:this={popover} {align} {anchor} {offset}>
|
||||||
|
<Menu>
|
||||||
|
{#each buttons as button}
|
||||||
|
<MenuItem on:click={() => handleClick(button)} disabled={button.disabled}>
|
||||||
|
{button.text || "Button"}
|
||||||
|
</MenuItem>
|
||||||
|
{/each}
|
||||||
|
</Menu>
|
||||||
|
</Popover>
|
|
@ -39,6 +39,7 @@ export { default as ActionGroup } from "./ActionGroup/ActionGroup.svelte"
|
||||||
export { default as ActionMenu } from "./ActionMenu/ActionMenu.svelte"
|
export { default as ActionMenu } from "./ActionMenu/ActionMenu.svelte"
|
||||||
export { default as Button } from "./Button/Button.svelte"
|
export { default as Button } from "./Button/Button.svelte"
|
||||||
export { default as ButtonGroup } from "./ButtonGroup/ButtonGroup.svelte"
|
export { default as ButtonGroup } from "./ButtonGroup/ButtonGroup.svelte"
|
||||||
|
export { default as CollapsedButtonGroup } from "./ButtonGroup/CollapsedButtonGroup.svelte"
|
||||||
export { default as ClearButton } from "./ClearButton/ClearButton.svelte"
|
export { default as ClearButton } from "./ClearButton/ClearButton.svelte"
|
||||||
export { default as Icon } from "./Icon/Icon.svelte"
|
export { default as Icon } from "./Icon/Icon.svelte"
|
||||||
export { default as IconAvatar } from "./Icon/IconAvatar.svelte"
|
export { default as IconAvatar } from "./Icon/IconAvatar.svelte"
|
||||||
|
|
|
@ -30,10 +30,6 @@
|
||||||
|
|
||||||
let rowActions = []
|
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) => {
|
||||||
|
@ -58,21 +54,22 @@
|
||||||
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
||||||
$: currentTheme = $themeStore?.theme
|
$: currentTheme = $themeStore?.theme
|
||||||
$: darkMode = !currentTheme.includes("light")
|
$: darkMode = !currentTheme.includes("light")
|
||||||
$: buttons = [
|
$: buttons = makeRowActionButtons(rowActions)
|
||||||
{
|
|
||||||
text: "Action",
|
|
||||||
type: "cta",
|
|
||||||
icon: "ChevronDown",
|
|
||||||
onClick: async (e, row, refresh) => {
|
|
||||||
rowActionRow = row
|
|
||||||
rowActionAnchor = e.currentTarget
|
|
||||||
rowActionPopover.show()
|
|
||||||
refreshRow = refresh
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
$: fetchRowActions(id)
|
$: fetchRowActions(id)
|
||||||
|
|
||||||
|
const makeRowActionButtons = rowActions => {
|
||||||
|
return rowActions.map(action => ({
|
||||||
|
text: action.name,
|
||||||
|
onClick: async row => {
|
||||||
|
await API.rowActions.trigger({
|
||||||
|
rowActionId: action.id,
|
||||||
|
tableId: id,
|
||||||
|
rowId: row._id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
const relationshipSupport = datasource => {
|
const relationshipSupport = datasource => {
|
||||||
const integration = $integrations[datasource?.source]
|
const integration = $integrations[datasource?.source]
|
||||||
return !isInternal && integration?.relationships !== false
|
return !isInternal && integration?.relationships !== false
|
||||||
|
@ -110,16 +107,6 @@
|
||||||
const res = await API.rowActions.fetch(tableId)
|
const res = await API.rowActions.fetch(tableId)
|
||||||
rowActions = Object.values(res || {})
|
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}
|
||||||
|
@ -143,7 +130,8 @@
|
||||||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
isCloud={$admin.cloud}
|
isCloud={$admin.cloud}
|
||||||
buttons={rowActions.length ? buttons : null}
|
{buttons}
|
||||||
|
buttonsCollapsed
|
||||||
on:updatedatasource={handleGridTableUpdate}
|
on:updatedatasource={handleGridTableUpdate}
|
||||||
>
|
>
|
||||||
<!-- Controls -->
|
<!-- Controls -->
|
||||||
|
@ -192,19 +180,6 @@
|
||||||
<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);
|
||||||
|
|
|
@ -7591,6 +7591,18 @@
|
||||||
"key": "row"
|
"key": "row"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Collapse",
|
||||||
|
"key": "buttonsCollapsed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Collapsed text",
|
||||||
|
"key": "buttonsCollapsedText",
|
||||||
|
"dependsOn": "buttonsCollapsed",
|
||||||
|
"placeholder": "Action"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import BlockComponent from "../BlockComponent.svelte"
|
import BlockComponent from "../BlockComponent.svelte"
|
||||||
import Block from "../Block.svelte"
|
import Block from "../Block.svelte"
|
||||||
import { Button, Popover, Menu, MenuItem } from "@budibase/bbui"
|
import {
|
||||||
|
Button,
|
||||||
|
Popover,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
CollapsedButtonGroup,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
export let buttons = []
|
export let buttons = []
|
||||||
|
@ -16,39 +22,26 @@
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
|
|
||||||
let popover
|
$: collapsedButtons = collapsed ? makeCollapsed(buttons) : null
|
||||||
let anchor
|
|
||||||
|
|
||||||
const handleCollapsedClick = async button => {
|
const makeCollapsed = buttons => {
|
||||||
const fn = enrichButtonActions(button.onClick, $context)
|
return buttons.map(button => ({
|
||||||
await fn?.()
|
...button,
|
||||||
popover.hide()
|
onClick: async () => {
|
||||||
|
const fn = enrichButtonActions(button.onClick, $context)
|
||||||
|
await fn?.()
|
||||||
|
},
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if collapsed}
|
{#if collapsed}
|
||||||
<div use:styleable={$component.styles}>
|
<div use:styleable={$component.styles}>
|
||||||
<Button
|
<CollapsedButtonGroup
|
||||||
bind:ref={anchor}
|
text={collapsedText || "Action"}
|
||||||
on:click={() => popover?.show()}
|
buttons={collapsedButtons}
|
||||||
icon="ChevronDown"
|
/>
|
||||||
cta
|
|
||||||
>
|
|
||||||
{collapsedText || "Action"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<Popover bind:this={popover} align="left" {anchor}>
|
|
||||||
<Menu>
|
|
||||||
{#each buttons as button}
|
|
||||||
<MenuItem
|
|
||||||
on:click={() => handleCollapsedClick(button)}
|
|
||||||
disabled={button.disabled}
|
|
||||||
>
|
|
||||||
{button.text || "Button"}
|
|
||||||
</MenuItem>
|
|
||||||
{/each}
|
|
||||||
</Menu>
|
|
||||||
</Popover>
|
|
||||||
{:else}
|
{:else}
|
||||||
<Block>
|
<Block>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
export let columns = null
|
export let columns = null
|
||||||
export let onRowClick = null
|
export let onRowClick = null
|
||||||
export let buttons = null
|
export let buttons = null
|
||||||
|
export let buttonsCollapsed = false
|
||||||
|
export let buttonsCollapsedText = null
|
||||||
|
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
@ -115,15 +117,13 @@
|
||||||
text: settings.text,
|
text: settings.text,
|
||||||
type: settings.type,
|
type: settings.type,
|
||||||
icon: settings.icon,
|
icon: settings.icon,
|
||||||
onClick: async (_, row, refresh) => {
|
onClick: async row => {
|
||||||
// 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))
|
||||||
const res = await fn?.({ row })
|
return await fn?.({ row })
|
||||||
await refresh()
|
|
||||||
return res
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -183,6 +183,8 @@
|
||||||
notifySuccess={notificationStore.actions.success}
|
notifySuccess={notificationStore.actions.success}
|
||||||
notifyError={notificationStore.actions.error}
|
notifyError={notificationStore.actions.error}
|
||||||
buttons={enrichedButtons}
|
buttons={enrichedButtons}
|
||||||
|
{buttonsCollapsed}
|
||||||
|
{buttonsCollapsedText}
|
||||||
isCloud={$environmentStore.cloud}
|
isCloud={$environmentStore.cloud}
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import GridCell from "../cells/GridCell.svelte"
|
import GridCell from "../cells/GridCell.svelte"
|
||||||
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||||
import { BlankRowID } from "../lib/constants"
|
import { BlankRowID } from "../lib/constants"
|
||||||
|
import CollapsedButtonGroup from "../../../../../bbui/src/ButtonGroup/CollapsedButtonGroup.svelte"
|
||||||
|
|
||||||
const {
|
const {
|
||||||
renderedRows,
|
renderedRows,
|
||||||
|
@ -34,12 +35,16 @@
|
||||||
$: gridEnd = $width - $buttonColumnWidth - 1
|
$: gridEnd = $width - $buttonColumnWidth - 1
|
||||||
$: left = Math.min(columnEnd, gridEnd)
|
$: left = Math.min(columnEnd, gridEnd)
|
||||||
|
|
||||||
const handleClick = async (e, button, row) => {
|
const handleClick = async (button, row) => {
|
||||||
await button.onClick?.(
|
await button.onClick?.(rows.actions.cleanRow(row))
|
||||||
e,
|
await rows.actions.refreshRow(row._id)
|
||||||
rows.actions.cleanRow(row),
|
}
|
||||||
async () => await rows.actions.refreshRow(row._id)
|
|
||||||
)
|
const makeCollapsedButtons = (buttons, row) => {
|
||||||
|
return buttons.map(button => ({
|
||||||
|
...button,
|
||||||
|
onClick: () => handleClick(button, row),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -80,26 +85,33 @@
|
||||||
class="buttons"
|
class="buttons"
|
||||||
class:offset={$showVScrollbar && $showHScrollbar}
|
class:offset={$showVScrollbar && $showHScrollbar}
|
||||||
>
|
>
|
||||||
{#each buttons as button}
|
{#if $props.buttonsCollapsed}
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<CollapsedButtonGroup
|
||||||
<span on:click={e => handleClick(e, button, row)}>
|
buttons={makeCollapsedButtons(buttons, row)}
|
||||||
|
text={$props.buttonsCollapsedText || "Action"}
|
||||||
|
align="right"
|
||||||
|
offset={5}
|
||||||
|
size="S"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
{#each buttons as button}
|
||||||
<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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</GridCell>
|
</GridCell>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -43,7 +43,9 @@
|
||||||
export let notifySuccess = null
|
export let notifySuccess = null
|
||||||
export let notifyError = null
|
export let notifyError = null
|
||||||
export let buttons = null
|
export let buttons = null
|
||||||
export let darkMode
|
export let buttonsCollapsed = false
|
||||||
|
export let buttonsCollapsedText = null
|
||||||
|
export let darkMode = false
|
||||||
export let isCloud = null
|
export let isCloud = null
|
||||||
export let rowConditions = null
|
export let rowConditions = null
|
||||||
|
|
||||||
|
@ -98,6 +100,8 @@
|
||||||
notifySuccess,
|
notifySuccess,
|
||||||
notifyError,
|
notifyError,
|
||||||
buttons,
|
buttons,
|
||||||
|
buttonsCollapsed,
|
||||||
|
buttonsCollapsedText,
|
||||||
darkMode,
|
darkMode,
|
||||||
isCloud,
|
isCloud,
|
||||||
rowConditions,
|
rowConditions,
|
||||||
|
|
Loading…
Reference in New Issue