Merge branch 'master' into chore/run_oss_checks
This commit is contained in:
commit
18b48f2c6b
|
@ -21,6 +21,7 @@
|
||||||
export let allowHelpers = true
|
export let allowHelpers = true
|
||||||
export let updateOnChange = true
|
export let updateOnChange = true
|
||||||
export let drawerLeft
|
export let drawerLeft
|
||||||
|
export let disableBindings = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let bindingDrawer
|
let bindingDrawer
|
||||||
|
@ -62,7 +63,7 @@
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{updateOnChange}
|
{updateOnChange}
|
||||||
/>
|
/>
|
||||||
{#if !disabled}
|
{#if !disabled && !disableBindings}
|
||||||
<div
|
<div
|
||||||
class="icon"
|
class="icon"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
$: schemaComponents = getContextProviderComponents(
|
$: schemaComponents = getContextProviderComponents(
|
||||||
$currentAsset,
|
$currentAsset,
|
||||||
$store.selectedComponentId,
|
$store.selectedComponentId,
|
||||||
"schema"
|
"schema",
|
||||||
|
{ includeSelf: nested }
|
||||||
)
|
)
|
||||||
$: providerOptions = getProviderOptions(formComponents, schemaComponents)
|
$: providerOptions = getProviderOptions(formComponents, schemaComponents)
|
||||||
$: schemaFields = getSchemaFields(parameters?.tableId)
|
$: schemaFields = getSchemaFields(parameters?.tableId)
|
||||||
|
|
|
@ -4,10 +4,15 @@
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
import { getEventContextBindings } from "builderStore/dataBinding"
|
||||||
|
|
||||||
|
export let componentInstance
|
||||||
export let componentBindings
|
export let componentBindings
|
||||||
export let bindings
|
export let bindings
|
||||||
export let value
|
export let value
|
||||||
|
export let key
|
||||||
|
export let nested
|
||||||
|
export let max
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -15,12 +20,18 @@
|
||||||
|
|
||||||
$: buttonList = sanitizeValue(value) || []
|
$: buttonList = sanitizeValue(value) || []
|
||||||
$: buttonCount = buttonList.length
|
$: buttonCount = buttonList.length
|
||||||
|
$: eventContextBindings = getEventContextBindings({
|
||||||
|
componentInstance,
|
||||||
|
settingKey: key,
|
||||||
|
})
|
||||||
|
$: allBindings = [...bindings, ...eventContextBindings]
|
||||||
$: itemProps = {
|
$: itemProps = {
|
||||||
componentBindings: componentBindings || [],
|
componentBindings: componentBindings || [],
|
||||||
bindings,
|
bindings: allBindings,
|
||||||
removeButton,
|
removeButton,
|
||||||
canRemove: buttonCount > 1,
|
nested,
|
||||||
}
|
}
|
||||||
|
$: canAddButtons = max == null || buttonList.length < max
|
||||||
|
|
||||||
const sanitizeValue = val => {
|
const sanitizeValue = val => {
|
||||||
return val?.map(button => {
|
return val?.map(button => {
|
||||||
|
@ -86,11 +97,16 @@
|
||||||
focus={focusItem}
|
focus={focusItem}
|
||||||
draggable={buttonCount > 1}
|
draggable={buttonCount > 1}
|
||||||
/>
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="list-footer" on:click={addButton}>
|
<div
|
||||||
|
class="list-footer"
|
||||||
|
class:disabled={!canAddButtons}
|
||||||
|
on:click={addButton}
|
||||||
|
class:empty={!buttonCount}
|
||||||
|
>
|
||||||
<div class="add-button">Add button</div>
|
<div class="add-button">Add button</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -120,15 +136,21 @@
|
||||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.list-footer.empty {
|
||||||
.add-button {
|
border-radius: 4px;
|
||||||
margin: var(--spacing-s);
|
}
|
||||||
|
.list-footer.disabled {
|
||||||
|
color: var(--spectrum-global-color-gray-500);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-footer:hover {
|
.list-footer:hover {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--spectrum-table-row-background-color-hover,
|
--spectrum-table-row-background-color-hover,
|
||||||
var(--spectrum-alias-highlight-hover)
|
var(--spectrum-alias-highlight-hover)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
margin: var(--spacing-s);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,11 +9,33 @@
|
||||||
export let bindings
|
export let bindings
|
||||||
export let anchor
|
export let anchor
|
||||||
export let removeButton
|
export let removeButton
|
||||||
export let canRemove
|
export let nested
|
||||||
|
|
||||||
$: readableText = isJSBinding(item.text)
|
$: readableText = isJSBinding(item.text)
|
||||||
? "(JavaScript function)"
|
? "(JavaScript function)"
|
||||||
: runtimeToReadableBinding([...bindings, componentBindings], item.text)
|
: runtimeToReadableBinding([...bindings, componentBindings], item.text)
|
||||||
|
|
||||||
|
// If this is a nested setting (for example inside a grid or form block) then
|
||||||
|
// we need to mark all the settings of the actual buttons as nested too, to
|
||||||
|
// allow us to reference context provided by the block.
|
||||||
|
// We will need to update this in future if the normal button component
|
||||||
|
// gets broken into multiple settings sections, as we assume a flat array.
|
||||||
|
const updatedNestedFlags = settings => {
|
||||||
|
if (!nested || !settings?.length) {
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
let newSettings = settings.map(setting => ({
|
||||||
|
...setting,
|
||||||
|
nested: true,
|
||||||
|
}))
|
||||||
|
// We need to prevent bindings for the button names because of how grid
|
||||||
|
// blocks work. This is an edge case but unavoidable.
|
||||||
|
let name = newSettings.find(x => x.key === "text")
|
||||||
|
if (name) {
|
||||||
|
name.disableBindings = true
|
||||||
|
}
|
||||||
|
return newSettings
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="list-item-body">
|
<div class="list-item-body">
|
||||||
|
@ -24,12 +46,12 @@
|
||||||
{componentBindings}
|
{componentBindings}
|
||||||
{bindings}
|
{bindings}
|
||||||
on:change
|
on:change
|
||||||
|
parseSettings={updatedNestedFlags}
|
||||||
/>
|
/>
|
||||||
<div class="field-label">{readableText || "Button"}</div>
|
<div class="field-label">{readableText || "Button"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-item-right">
|
<div class="list-item-right">
|
||||||
<Icon
|
<Icon
|
||||||
disabled={!canRemove}
|
|
||||||
size="S"
|
size="S"
|
||||||
name="Close"
|
name="Close"
|
||||||
hoverable
|
hoverable
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let sanitisedFields
|
let sanitisedFields
|
||||||
let fieldList
|
let fieldList
|
||||||
let schema
|
let schema
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
export let highlighted = false
|
export let highlighted = false
|
||||||
export let propertyFocus = false
|
export let propertyFocus = false
|
||||||
export let info = null
|
export let info = null
|
||||||
|
export let disableBindings = false
|
||||||
|
|
||||||
$: nullishValue = value == null || value === ""
|
$: nullishValue = value == null || value === ""
|
||||||
$: allBindings = getAllBindings(bindings, componentBindings, nested)
|
$: allBindings = getAllBindings(bindings, componentBindings, nested)
|
||||||
|
@ -99,6 +100,7 @@
|
||||||
{nested}
|
{nested}
|
||||||
{key}
|
{key}
|
||||||
{type}
|
{type}
|
||||||
|
{disableBindings}
|
||||||
{...props}
|
{...props}
|
||||||
on:drawerHide
|
on:drawerHide
|
||||||
on:drawerShow
|
on:drawerShow
|
||||||
|
|
|
@ -179,6 +179,7 @@
|
||||||
highlighted={$store.highlightedSettingKey === setting.key}
|
highlighted={$store.highlightedSettingKey === setting.key}
|
||||||
propertyFocus={$store.propertyFocus === setting.key}
|
propertyFocus={$store.propertyFocus === setting.key}
|
||||||
info={setting.info}
|
info={setting.info}
|
||||||
|
disableBindings={setting.disableBindings}
|
||||||
props={{
|
props={{
|
||||||
// Generic settings
|
// Generic settings
|
||||||
placeholder: setting.placeholder || null,
|
placeholder: setting.placeholder || null,
|
||||||
|
|
|
@ -270,7 +270,6 @@
|
||||||
{
|
{
|
||||||
"type": "buttonConfiguration",
|
"type": "buttonConfiguration",
|
||||||
"key": "buttons",
|
"key": "buttons",
|
||||||
"nested": true,
|
|
||||||
"defaultValue": [
|
"defaultValue": [
|
||||||
{
|
{
|
||||||
"type": "cta",
|
"type": "cta",
|
||||||
|
@ -6339,8 +6338,29 @@
|
||||||
"label": "High contrast",
|
"label": "High contrast",
|
||||||
"key": "stripeRows",
|
"key": "stripeRows",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": true,
|
||||||
|
"name": "Buttons",
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "buttonConfiguration",
|
||||||
|
"key": "buttons",
|
||||||
|
"nested": true,
|
||||||
|
"max": 3,
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"label": "Clicked row",
|
||||||
|
"key": "row"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"type": "schema"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"bbreferencefield": {
|
"bbreferencefield": {
|
||||||
"devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels",
|
"devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels",
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// NOTE: this is not a block - it's just named as such to avoid confusing users,
|
// NOTE: this is not a block - it's just named as such to avoid confusing users,
|
||||||
// because it functions similarly to one
|
// because it functions similarly to one
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
import { get } from "svelte/store"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
|
|
||||||
// table is actually any datasource, but called table for legacy compatibility
|
// table is actually any datasource, but called table for legacy compatibility
|
||||||
|
@ -16,12 +17,21 @@
|
||||||
export let fixedRowHeight = null
|
export let fixedRowHeight = null
|
||||||
export let columns = null
|
export let columns = null
|
||||||
export let onRowClick = null
|
export let onRowClick = null
|
||||||
|
export let buttons = null
|
||||||
|
|
||||||
|
const context = getContext("context")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { styleable, API, builderStore, notificationStore } = getContext("sdk")
|
const {
|
||||||
|
styleable,
|
||||||
|
API,
|
||||||
|
builderStore,
|
||||||
|
notificationStore,
|
||||||
|
enrichButtonActions,
|
||||||
|
} = getContext("sdk")
|
||||||
|
|
||||||
$: columnWhitelist = columns?.map(col => col.name)
|
$: columnWhitelist = columns?.map(col => col.name)
|
||||||
$: schemaOverrides = getSchemaOverrides(columns)
|
$: schemaOverrides = getSchemaOverrides(columns)
|
||||||
|
$: enrichedButtons = enrichButtons(buttons)
|
||||||
|
|
||||||
const getSchemaOverrides = columns => {
|
const getSchemaOverrides = columns => {
|
||||||
let overrides = {}
|
let overrides = {}
|
||||||
|
@ -33,6 +43,25 @@
|
||||||
})
|
})
|
||||||
return overrides
|
return overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enrichButtons = buttons => {
|
||||||
|
if (!buttons?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return buttons.map(settings => ({
|
||||||
|
size: "M",
|
||||||
|
text: settings.text,
|
||||||
|
type: settings.type,
|
||||||
|
onClick: async row => {
|
||||||
|
// We add a fake context binding in here, which allows us to pretend
|
||||||
|
// that the grid provides a "schema" binding - that lets us use the
|
||||||
|
// clicked row in things like save row actions
|
||||||
|
const enrichedContext = { ...get(context), [get(component).id]: row }
|
||||||
|
const fn = enrichButtonActions(settings.onClick, enrichedContext)
|
||||||
|
return await fn?.({ row })
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -58,6 +87,7 @@
|
||||||
showControls={false}
|
showControls={false}
|
||||||
notifySuccess={notificationStore.actions.success}
|
notifySuccess={notificationStore.actions.success}
|
||||||
notifyError={notificationStore.actions.error}
|
notifyError={notificationStore.actions.error}
|
||||||
|
buttons={enrichedButtons}
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
dndIsDragging,
|
dndIsDragging,
|
||||||
confirmationStore,
|
confirmationStore,
|
||||||
roleStore,
|
roleStore,
|
||||||
|
stateStore,
|
||||||
} from "stores"
|
} from "stores"
|
||||||
import { styleable } from "utils/styleable"
|
import { styleable } from "utils/styleable"
|
||||||
import { linkable } from "utils/linkable"
|
import { linkable } from "utils/linkable"
|
||||||
|
@ -24,9 +25,13 @@ import BlockComponent from "components/BlockComponent.svelte"
|
||||||
import { ActionTypes } from "./constants"
|
import { ActionTypes } from "./constants"
|
||||||
import { fetchDatasourceSchema } from "./utils/schema.js"
|
import { fetchDatasourceSchema } from "./utils/schema.js"
|
||||||
import { getAPIKey } from "./utils/api.js"
|
import { getAPIKey } from "./utils/api.js"
|
||||||
|
import { enrichButtonActions } from "./utils/buttonActions.js"
|
||||||
|
import { processStringSync, makePropSafe } from "@budibase/string-templates"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
API,
|
API,
|
||||||
|
|
||||||
|
// Stores
|
||||||
authStore,
|
authStore,
|
||||||
notificationStore,
|
notificationStore,
|
||||||
routeStore,
|
routeStore,
|
||||||
|
@ -41,13 +46,23 @@ export default {
|
||||||
currentRole,
|
currentRole,
|
||||||
confirmationStore,
|
confirmationStore,
|
||||||
roleStore,
|
roleStore,
|
||||||
|
stateStore,
|
||||||
|
|
||||||
|
// Utils
|
||||||
styleable,
|
styleable,
|
||||||
linkable,
|
linkable,
|
||||||
getAction,
|
getAction,
|
||||||
fetchDatasourceSchema,
|
fetchDatasourceSchema,
|
||||||
Provider,
|
|
||||||
ActionTypes,
|
|
||||||
getAPIKey,
|
getAPIKey,
|
||||||
|
enrichButtonActions,
|
||||||
|
processStringSync,
|
||||||
|
makePropSafe,
|
||||||
|
|
||||||
|
// Components
|
||||||
|
Provider,
|
||||||
Block,
|
Block,
|
||||||
BlockComponent,
|
BlockComponent,
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
ActionTypes,
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
$: style = getStyle(width, selectedUser)
|
$: style = getStyle(width, selectedUser)
|
||||||
|
|
||||||
const getStyle = (width, selectedUser) => {
|
const getStyle = (width, selectedUser) => {
|
||||||
let style = `flex: 0 0 ${width}px;`
|
let style = width === "auto" ? "width: auto;" : `flex: 0 0 ${width}px;`
|
||||||
if (selectedUser) {
|
if (selectedUser) {
|
||||||
style += `--user-color:${selectedUser.color};`
|
style += `--user-color:${selectedUser.color};`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
<script>
|
||||||
|
import { getContext, onMount } from "svelte"
|
||||||
|
import { Button } from "@budibase/bbui"
|
||||||
|
import GridCell from "../cells/GridCell.svelte"
|
||||||
|
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||||
|
|
||||||
|
const {
|
||||||
|
renderedRows,
|
||||||
|
hoveredRowId,
|
||||||
|
props,
|
||||||
|
width,
|
||||||
|
rows,
|
||||||
|
focusedRow,
|
||||||
|
selectedRows,
|
||||||
|
visibleColumns,
|
||||||
|
scroll,
|
||||||
|
isDragging,
|
||||||
|
buttonColumnWidth,
|
||||||
|
} = getContext("grid")
|
||||||
|
|
||||||
|
let measureContainer
|
||||||
|
|
||||||
|
$: buttons = $props.buttons?.slice(0, 3) || []
|
||||||
|
$: columnsWidth = $visibleColumns.reduce(
|
||||||
|
(total, col) => (total += col.width),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
$: end = columnsWidth - 1 - $scroll.left
|
||||||
|
$: left = Math.min($width - $buttonColumnWidth, end)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const observer = new ResizeObserver(entries => {
|
||||||
|
const width = entries?.[0]?.contentRect?.width ?? 0
|
||||||
|
buttonColumnWidth.set(width)
|
||||||
|
})
|
||||||
|
observer.observe(measureContainer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hidden copy of buttons to measure -->
|
||||||
|
<div class="measure" bind:this={measureContainer}>
|
||||||
|
<GridCell width="auto">
|
||||||
|
<div class="buttons">
|
||||||
|
{#each buttons as button}
|
||||||
|
<Button size="S">
|
||||||
|
{button.text || "Button"}
|
||||||
|
</Button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</GridCell>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="button-column"
|
||||||
|
style="left:{left}px"
|
||||||
|
class:hidden={$buttonColumnWidth === 0}
|
||||||
|
>
|
||||||
|
<div class="content" on:mouseleave={() => ($hoveredRowId = null)}>
|
||||||
|
<GridScrollWrapper scrollVertically attachHandlers>
|
||||||
|
{#each $renderedRows as row}
|
||||||
|
{@const rowSelected = !!$selectedRows[row._id]}
|
||||||
|
{@const rowHovered = $hoveredRowId === row._id}
|
||||||
|
{@const rowFocused = $focusedRow?._id === row._id}
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
|
||||||
|
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
|
||||||
|
>
|
||||||
|
<GridCell
|
||||||
|
width="auto"
|
||||||
|
rowIdx={row.__idx}
|
||||||
|
selected={rowSelected}
|
||||||
|
highlighted={rowHovered || rowFocused}
|
||||||
|
>
|
||||||
|
<div class="buttons">
|
||||||
|
{#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)}
|
||||||
|
>
|
||||||
|
{button.text || "Button"}
|
||||||
|
</Button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</GridCell>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</GridScrollWrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.button-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--cell-background);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.button-column.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 var(--cell-padding);
|
||||||
|
gap: var(--cell-padding);
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add left cell border */
|
||||||
|
.button-column :global(.cell) {
|
||||||
|
border-left: var(--cell-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden copy of buttons to measure width against */
|
||||||
|
.measure {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -48,6 +48,7 @@
|
||||||
export let fixedRowHeight = null
|
export let fixedRowHeight = null
|
||||||
export let notifySuccess = null
|
export let notifySuccess = null
|
||||||
export let notifyError = null
|
export let notifyError = null
|
||||||
|
export let buttons = null
|
||||||
|
|
||||||
// Unique identifier for DOM nodes inside this instance
|
// Unique identifier for DOM nodes inside this instance
|
||||||
const rand = Math.random()
|
const rand = Math.random()
|
||||||
|
@ -99,6 +100,7 @@
|
||||||
fixedRowHeight,
|
fixedRowHeight,
|
||||||
notifySuccess,
|
notifySuccess,
|
||||||
notifyError,
|
notifyError,
|
||||||
|
buttons,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set context for children to consume
|
// Set context for children to consume
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||||
import GridRow from "./GridRow.svelte"
|
import GridRow from "./GridRow.svelte"
|
||||||
import { BlankRowID } from "../lib/constants"
|
import { BlankRowID } from "../lib/constants"
|
||||||
|
import ButtonColumn from "./ButtonColumn.svelte"
|
||||||
|
|
||||||
const {
|
const {
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
dispatch,
|
dispatch,
|
||||||
isDragging,
|
isDragging,
|
||||||
config,
|
config,
|
||||||
|
props,
|
||||||
} = getContext("grid")
|
} = getContext("grid")
|
||||||
|
|
||||||
let body
|
let body
|
||||||
|
@ -54,6 +56,9 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</GridScrollWrapper>
|
</GridScrollWrapper>
|
||||||
|
{#if $props.buttons?.length}
|
||||||
|
<ButtonColumn />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -314,8 +314,12 @@ export const createActions = context => {
|
||||||
|
|
||||||
// Refreshes a specific row
|
// Refreshes a specific row
|
||||||
const refreshRow = async id => {
|
const refreshRow = async id => {
|
||||||
|
try {
|
||||||
const row = await datasource.actions.getRow(id)
|
const row = await datasource.actions.getRow(id)
|
||||||
replaceRow(id, row)
|
replaceRow(id, row)
|
||||||
|
} catch {
|
||||||
|
// Do nothing - we probably just don't support refreshing individual rows
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refreshes all data
|
// Refreshes all data
|
||||||
|
|
|
@ -20,8 +20,15 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = context => {
|
||||||
const { rows, visibleColumns, stickyColumn, rowHeight, width, height } =
|
const {
|
||||||
context
|
rows,
|
||||||
|
visibleColumns,
|
||||||
|
stickyColumn,
|
||||||
|
rowHeight,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
buttonColumnWidth,
|
||||||
|
} = context
|
||||||
|
|
||||||
// Memoize store primitives
|
// Memoize store primitives
|
||||||
const stickyColumnWidth = derived(stickyColumn, $col => $col?.width || 0, 0)
|
const stickyColumnWidth = derived(stickyColumn, $col => $col?.width || 0, 0)
|
||||||
|
@ -40,9 +47,10 @@ export const deriveStores = context => {
|
||||||
|
|
||||||
// Derive horizontal limits
|
// Derive horizontal limits
|
||||||
const contentWidth = derived(
|
const contentWidth = derived(
|
||||||
[visibleColumns, stickyColumnWidth],
|
[visibleColumns, stickyColumnWidth, buttonColumnWidth],
|
||||||
([$visibleColumns, $stickyColumnWidth]) => {
|
([$visibleColumns, $stickyColumnWidth, $buttonColumnWidth]) => {
|
||||||
let width = GutterWidth + Padding + $stickyColumnWidth
|
const space = Math.max(Padding, $buttonColumnWidth - 1)
|
||||||
|
let width = GutterWidth + space + $stickyColumnWidth
|
||||||
$visibleColumns.forEach(col => {
|
$visibleColumns.forEach(col => {
|
||||||
width += col.width
|
width += col.width
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,6 +18,7 @@ export const createStores = context => {
|
||||||
const previousFocusedRowId = writable(null)
|
const previousFocusedRowId = writable(null)
|
||||||
const gridFocused = writable(false)
|
const gridFocused = writable(false)
|
||||||
const isDragging = writable(false)
|
const isDragging = writable(false)
|
||||||
|
const buttonColumnWidth = writable(0)
|
||||||
|
|
||||||
// Derive the current focused row ID
|
// Derive the current focused row ID
|
||||||
const focusedRowId = derived(
|
const focusedRowId = derived(
|
||||||
|
@ -51,6 +52,7 @@ export const createStores = context => {
|
||||||
rowHeight,
|
rowHeight,
|
||||||
gridFocused,
|
gridFocused,
|
||||||
isDragging,
|
isDragging,
|
||||||
|
buttonColumnWidth,
|
||||||
selectedRows: {
|
selectedRows: {
|
||||||
...selectedRows,
|
...selectedRows,
|
||||||
actions: {
|
actions: {
|
||||||
|
|
Loading…
Reference in New Issue