Merge pull request #13610 from Budibase/table-width-setting
Table column initial width setting
This commit is contained in:
commit
39f59c076a
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import { runtimeToReadableBinding } from "dataBinding"
|
import { runtimeToReadableBinding } from "dataBinding"
|
||||||
import { isJSBinding } from "@budibase/string-templates"
|
import { isJSBinding } from "@budibase/string-templates"
|
||||||
|
|
|
@ -100,9 +100,6 @@
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
get(store).actions.select(draggableItem.id)
|
get(store).actions.select(draggableItem.id)
|
||||||
}}
|
}}
|
||||||
on:mousedown={() => {
|
|
||||||
get(store).actions.select()
|
|
||||||
}}
|
|
||||||
bind:this={anchors[draggableItem.id]}
|
bind:this={anchors[draggableItem.id]}
|
||||||
class:highlighted={draggableItem.id === $store.selected}
|
class:highlighted={draggableItem.id === $store.selected}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { componentStore } from "stores/builder"
|
import { componentStore } from "stores/builder"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher, getContext } from "svelte"
|
||||||
import { customPositionHandler } from "."
|
|
||||||
import ComponentSettingsSection from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte"
|
import ComponentSettingsSection from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte"
|
||||||
|
|
||||||
export let anchor
|
export let anchor
|
||||||
|
@ -18,76 +17,74 @@
|
||||||
|
|
||||||
let popover
|
let popover
|
||||||
let drawers = []
|
let drawers = []
|
||||||
let open = false
|
let isOpen = false
|
||||||
|
|
||||||
// Auto hide the component when another item is selected
|
// Auto hide the component when another item is selected
|
||||||
$: if (open && $draggable.selected !== componentInstance._id) {
|
$: if (open && $draggable.selected !== componentInstance._id) {
|
||||||
popover.hide()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open automatically if the component is marked as selected
|
// Open automatically if the component is marked as selected
|
||||||
$: if (!open && $draggable.selected === componentInstance._id && popover) {
|
$: if (!open && $draggable.selected === componentInstance._id && popover) {
|
||||||
popover.show()
|
open()
|
||||||
open = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: componentDef = componentStore.getDefinition(componentInstance._component)
|
$: componentDef = componentStore.getDefinition(componentInstance._component)
|
||||||
$: parsedComponentDef = processComponentDefinitionSettings(componentDef)
|
$: parsedComponentDef = processComponentDefinitionSettings(componentDef)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
isOpen = true
|
||||||
|
drawers = []
|
||||||
|
$draggable.actions.select(componentInstance._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
// Slight delay allows us to be able to properly toggle open/close state by
|
||||||
|
// clicking again on the settings icon
|
||||||
|
setTimeout(() => {
|
||||||
|
isOpen = false
|
||||||
|
if ($draggable.selected === componentInstance._id) {
|
||||||
|
$draggable.actions.select()
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleOpen = () => {
|
||||||
|
if (isOpen) {
|
||||||
|
close()
|
||||||
|
} else {
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const processComponentDefinitionSettings = componentDef => {
|
const processComponentDefinitionSettings = componentDef => {
|
||||||
if (!componentDef) {
|
if (!componentDef) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
const clone = cloneDeep(componentDef)
|
const clone = cloneDeep(componentDef)
|
||||||
|
|
||||||
if (typeof parseSettings === "function") {
|
if (typeof parseSettings === "function") {
|
||||||
clone.settings = parseSettings(clone.settings)
|
clone.settings = parseSettings(clone.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSetting = async (setting, value) => {
|
const updateSetting = async (setting, value) => {
|
||||||
const nestedComponentInstance = cloneDeep(componentInstance)
|
const nestedComponentInstance = cloneDeep(componentInstance)
|
||||||
|
|
||||||
const patchFn = componentStore.updateComponentSetting(setting.key, value)
|
const patchFn = componentStore.updateComponentSetting(setting.key, value)
|
||||||
patchFn(nestedComponentInstance)
|
patchFn(nestedComponentInstance)
|
||||||
|
|
||||||
dispatch("change", nestedComponentInstance)
|
dispatch("change", nestedComponentInstance)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Icon
|
<Icon name="Settings" hoverable size="S" on:click={toggleOpen} />
|
||||||
name="Settings"
|
|
||||||
hoverable
|
|
||||||
size="S"
|
|
||||||
on:click={() => {
|
|
||||||
if (!open) {
|
|
||||||
popover.show()
|
|
||||||
open = true
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
bind:this={popover}
|
open={isOpen}
|
||||||
on:open={() => {
|
on:close={close}
|
||||||
drawers = []
|
|
||||||
$draggable.actions.select(componentInstance._id)
|
|
||||||
}}
|
|
||||||
on:close={() => {
|
|
||||||
open = false
|
|
||||||
if ($draggable.selected === componentInstance._id) {
|
|
||||||
$draggable.actions.select()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{anchor}
|
{anchor}
|
||||||
align="left-outside"
|
align="left-outside"
|
||||||
showPopover={drawers.length === 0}
|
showPopover={drawers.length === 0}
|
||||||
clickOutsideOverride={drawers.length > 0}
|
clickOutsideOverride={drawers.length > 0}
|
||||||
maxHeight={600}
|
maxHeight={600}
|
||||||
offset={18}
|
offset={18}
|
||||||
handlePostionUpdate={customPositionHandler}
|
|
||||||
>
|
>
|
||||||
<span class="popover-wrap">
|
<span class="popover-wrap">
|
||||||
<Layout noPadding noGap>
|
<Layout noPadding noGap>
|
|
@ -1,18 +0,0 @@
|
||||||
export const customPositionHandler = (anchorBounds, eleBounds, cfg) => {
|
|
||||||
let { left, top, offset } = cfg
|
|
||||||
let percentageOffset = 30
|
|
||||||
// left-outside
|
|
||||||
left = anchorBounds.left - eleBounds.width - (offset || 5)
|
|
||||||
|
|
||||||
// shift up from the anchor, if space allows
|
|
||||||
let offsetPos = Math.floor(eleBounds.height / 100) * percentageOffset
|
|
||||||
let defaultTop = anchorBounds.top - offsetPos
|
|
||||||
|
|
||||||
if (window.innerHeight - defaultTop < eleBounds.height) {
|
|
||||||
top = window.innerHeight - eleBounds.height - 5
|
|
||||||
} else {
|
|
||||||
top = anchorBounds.top - offsetPos
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...cfg, left, top }
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Toggle, Icon } from "@budibase/bbui"
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Toggle, Icon } from "@budibase/bbui"
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import { setContext } from "svelte"
|
import { setContext } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
|
@ -67,6 +67,7 @@ const toGridFormat = draggableListColumns => {
|
||||||
label: entry.label,
|
label: entry.label,
|
||||||
field: entry.field,
|
field: entry.field,
|
||||||
active: entry.active,
|
active: entry.active,
|
||||||
|
width: entry.width,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +82,7 @@ const toDraggableListFormat = (gridFormatColumns, createComponent, schema) => {
|
||||||
field: column.field,
|
field: column.field,
|
||||||
label: column.label,
|
label: column.label,
|
||||||
columnType: schema[column.field].type,
|
columnType: schema[column.field].type,
|
||||||
|
width: column.width,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
NewFormSteps,
|
NewFormSteps,
|
||||||
} from "./steps"
|
} from "./steps"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { customPositionHandler } from "components/design/settings/controls/EditComponentPopover"
|
|
||||||
|
|
||||||
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
||||||
|
|
||||||
|
@ -187,7 +186,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_CREATE_STEPS)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_CREATE_STEPS)
|
||||||
builderStore.highlightSetting("steps", "info")
|
builderStore.highlightSetting("steps", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -203,7 +201,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_ROW_ID)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_ROW_ID)
|
||||||
builderStore.highlightSetting("rowId", "info")
|
builderStore.highlightSetting("rowId", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -219,7 +216,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_VIEW_UPDATE_STEPS)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_VIEW_UPDATE_STEPS)
|
||||||
builderStore.highlightSetting("steps", "info")
|
builderStore.highlightSetting("steps", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
scrollIntoView: true,
|
scrollIntoView: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2746,6 +2746,14 @@
|
||||||
"type": "plainText",
|
"type": "plainText",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"key": "label"
|
"key": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Initial width",
|
||||||
|
"key": "width",
|
||||||
|
"placeholder": "Auto",
|
||||||
|
"min": 80,
|
||||||
|
"max": 9999
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
||||||
$: enrichedButtons = enrichButtons(buttons)
|
$: enrichedButtons = enrichButtons(buttons)
|
||||||
$: selectedRows = deriveSelectedRows(gridContext)
|
$: selectedRows = deriveSelectedRows(gridContext)
|
||||||
|
$: styles = patchStyles($component.styles, minHeight)
|
||||||
$: data = { selectedRows: $selectedRows }
|
$: data = { selectedRows: $selectedRows }
|
||||||
$: actions = [
|
$: actions = [
|
||||||
{
|
{
|
||||||
|
@ -84,9 +85,11 @@
|
||||||
|
|
||||||
const getSchemaOverrides = columns => {
|
const getSchemaOverrides = columns => {
|
||||||
let overrides = {}
|
let overrides = {}
|
||||||
columns.forEach(column => {
|
columns.forEach((column, idx) => {
|
||||||
overrides[column.field] = {
|
overrides[column.field] = {
|
||||||
displayName: column.label,
|
displayName: column.label,
|
||||||
|
width: column.width,
|
||||||
|
order: idx,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return overrides
|
return overrides
|
||||||
|
@ -128,17 +131,23 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const patchStyles = (styles, minHeight) => {
|
||||||
|
return {
|
||||||
|
...styles,
|
||||||
|
normal: {
|
||||||
|
...styles?.normal,
|
||||||
|
"min-height": `${minHeight}px`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
gridContext = grid.getContext()
|
gridContext = grid.getContext()
|
||||||
gridContext.minHeight.subscribe($height => (minHeight = $height))
|
gridContext.minHeight.subscribe($height => (minHeight = $height))
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span style="--min-height:{minHeight}px">
|
<div use:styleable={styles} class:in-builder={$builderStore.inBuilder}>
|
||||||
<div
|
|
||||||
use:styleable={$component.styles}
|
|
||||||
class:in-builder={$builderStore.inBuilder}
|
|
||||||
>
|
|
||||||
<Grid
|
<Grid
|
||||||
bind:this={grid}
|
bind:this={grid}
|
||||||
datasource={table}
|
datasource={table}
|
||||||
|
@ -166,14 +175,10 @@
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
|
||||||
|
|
||||||
<Provider {data} {actions} />
|
<Provider {data} {actions} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
span {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
div {
|
div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -182,7 +187,6 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 410px;
|
height: 410px;
|
||||||
min-height: var(--min-height);
|
|
||||||
}
|
}
|
||||||
div.in-builder :global(*) {
|
div.in-builder :global(*) {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
subscribe,
|
subscribe,
|
||||||
config,
|
config,
|
||||||
ui,
|
ui,
|
||||||
columns,
|
|
||||||
definition,
|
definition,
|
||||||
datasource,
|
datasource,
|
||||||
schema,
|
schema,
|
||||||
|
@ -158,17 +157,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeDisplayColumn = () => {
|
const makeDisplayColumn = () => {
|
||||||
columns.actions.changePrimaryDisplay(column.name)
|
datasource.actions.changePrimaryDisplay(column.name)
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideColumn = () => {
|
const hideColumn = () => {
|
||||||
columns.update(state => {
|
datasource.actions.addSchemaMutation(column.name, { visible: false })
|
||||||
const index = state.findIndex(col => col.name === column.name)
|
datasource.actions.saveSchemaMutations()
|
||||||
state[index].visible = false
|
|
||||||
return state.slice()
|
|
||||||
})
|
|
||||||
columns.actions.saveChanges()
|
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { ActionButton, Popover, Toggle, Icon } from "@budibase/bbui"
|
import { ActionButton, Popover, Toggle, Icon } from "@budibase/bbui"
|
||||||
import { getColumnIcon } from "../lib/utils"
|
import { getColumnIcon } from "../lib/utils"
|
||||||
|
|
||||||
const { columns, stickyColumn, dispatch } = getContext("grid")
|
const { columns, datasource, stickyColumn, dispatch } = getContext("grid")
|
||||||
|
|
||||||
let open = false
|
let open = false
|
||||||
let anchor
|
let anchor
|
||||||
|
@ -11,36 +11,20 @@
|
||||||
$: anyHidden = $columns.some(col => !col.visible)
|
$: anyHidden = $columns.some(col => !col.visible)
|
||||||
$: text = getText($columns)
|
$: text = getText($columns)
|
||||||
|
|
||||||
const toggleVisibility = async (column, visible) => {
|
const toggleColumn = async (column, visible) => {
|
||||||
columns.update(state => {
|
datasource.actions.addSchemaMutation(column.name, { visible })
|
||||||
const index = state.findIndex(col => col.name === column.name)
|
await datasource.actions.saveSchemaMutations()
|
||||||
state[index].visible = visible
|
|
||||||
return state.slice()
|
|
||||||
})
|
|
||||||
await columns.actions.saveChanges()
|
|
||||||
dispatch(visible ? "show-column" : "hide-column")
|
dispatch(visible ? "show-column" : "hide-column")
|
||||||
}
|
}
|
||||||
|
|
||||||
const showAll = async () => {
|
const toggleAll = async visible => {
|
||||||
columns.update(state => {
|
let mutations = {}
|
||||||
return state.map(col => ({
|
$columns.forEach(column => {
|
||||||
...col,
|
mutations[column.name] = { visible }
|
||||||
visible: true,
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
await columns.actions.saveChanges()
|
datasource.actions.addSchemaMutations(mutations)
|
||||||
dispatch("show-column")
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
dispatch(visible ? "show-column" : "hide-column")
|
||||||
|
|
||||||
const hideAll = async () => {
|
|
||||||
columns.update(state => {
|
|
||||||
return state.map(col => ({
|
|
||||||
...col,
|
|
||||||
visible: false,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
await columns.actions.saveChanges()
|
|
||||||
dispatch("hide-column")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getText = columns => {
|
const getText = columns => {
|
||||||
|
@ -80,14 +64,14 @@
|
||||||
<Toggle
|
<Toggle
|
||||||
size="S"
|
size="S"
|
||||||
value={column.visible}
|
value={column.visible}
|
||||||
on:change={e => toggleVisibility(column, e.detail)}
|
on:change={e => toggleColumn(column, e.detail)}
|
||||||
disabled={column.primaryDisplay}
|
disabled={column.primaryDisplay}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<ActionButton on:click={showAll}>Show all</ActionButton>
|
<ActionButton on:click={() => toggleAll(true)}>Show all</ActionButton>
|
||||||
<ActionButton on:click={hideAll}>Hide all</ActionButton>
|
<ActionButton on:click={() => toggleAll(false)}>Hide all</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { derived, get, writable } from "svelte/store"
|
import { derived, get, writable } from "svelte/store"
|
||||||
import { cloneDeep } from "lodash/fp"
|
|
||||||
import { GutterWidth, DefaultColumnWidth } from "../lib/constants"
|
import { GutterWidth, DefaultColumnWidth } from "../lib/constants"
|
||||||
|
|
||||||
export const createStores = () => {
|
export const createStores = () => {
|
||||||
|
@ -75,72 +74,23 @@ export const deriveStores = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createActions = context => {
|
export const createActions = context => {
|
||||||
const { columns, stickyColumn, datasource, definition, schema } = context
|
const { columns, datasource, schema } = context
|
||||||
|
|
||||||
// Updates the datasources primary display column
|
|
||||||
const changePrimaryDisplay = async column => {
|
|
||||||
return await datasource.actions.saveDefinition({
|
|
||||||
...get(definition),
|
|
||||||
primaryDisplay: column,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the width of all columns
|
// Updates the width of all columns
|
||||||
const changeAllColumnWidths = async width => {
|
const changeAllColumnWidths = async width => {
|
||||||
columns.update(state => {
|
const $schema = get(schema)
|
||||||
return state.map(col => ({
|
let mutations = {}
|
||||||
...col,
|
Object.keys($schema).forEach(field => {
|
||||||
width,
|
mutations[field] = { width }
|
||||||
}))
|
|
||||||
})
|
|
||||||
if (get(stickyColumn)) {
|
|
||||||
stickyColumn.update(state => ({
|
|
||||||
...state,
|
|
||||||
width,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
await saveChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Persists column changes by saving metadata against datasource schema
|
|
||||||
const saveChanges = async () => {
|
|
||||||
const $columns = get(columns)
|
|
||||||
const $definition = get(definition)
|
|
||||||
const $stickyColumn = get(stickyColumn)
|
|
||||||
let newSchema = cloneDeep(get(schema)) || {}
|
|
||||||
|
|
||||||
// Build new updated datasource schema
|
|
||||||
Object.keys(newSchema).forEach(column => {
|
|
||||||
// Respect order specified by columns
|
|
||||||
const index = $columns.findIndex(x => x.name === column)
|
|
||||||
if (index !== -1) {
|
|
||||||
newSchema[column].order = index
|
|
||||||
} else {
|
|
||||||
delete newSchema[column].order
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy over metadata
|
|
||||||
if (column === $stickyColumn?.name) {
|
|
||||||
newSchema[column].visible = true
|
|
||||||
newSchema[column].width = $stickyColumn.width || DefaultColumnWidth
|
|
||||||
} else {
|
|
||||||
newSchema[column].visible = $columns[index]?.visible ?? true
|
|
||||||
newSchema[column].width = $columns[index]?.width || DefaultColumnWidth
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await datasource.actions.saveDefinition({
|
|
||||||
...$definition,
|
|
||||||
schema: newSchema,
|
|
||||||
})
|
})
|
||||||
|
datasource.actions.addSchemaMutations(mutations)
|
||||||
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
columns: {
|
columns: {
|
||||||
...columns,
|
...columns,
|
||||||
actions: {
|
actions: {
|
||||||
saveChanges,
|
|
||||||
changePrimaryDisplay,
|
|
||||||
changeAllColumnWidths,
|
changeAllColumnWidths,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,15 +4,23 @@ import { memo } from "../../../utils"
|
||||||
|
|
||||||
export const createStores = () => {
|
export const createStores = () => {
|
||||||
const definition = memo(null)
|
const definition = memo(null)
|
||||||
|
const schemaMutations = memo({})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
definition,
|
definition,
|
||||||
|
schemaMutations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = context => {
|
||||||
const { API, definition, schemaOverrides, columnWhitelist, datasource } =
|
const {
|
||||||
context
|
API,
|
||||||
|
definition,
|
||||||
|
schemaOverrides,
|
||||||
|
columnWhitelist,
|
||||||
|
datasource,
|
||||||
|
schemaMutations,
|
||||||
|
} = context
|
||||||
|
|
||||||
const schema = derived(definition, $definition => {
|
const schema = derived(definition, $definition => {
|
||||||
let schema = getDatasourceSchema({
|
let schema = getDatasourceSchema({
|
||||||
|
@ -35,42 +43,26 @@ export const deriveStores = context => {
|
||||||
return schema
|
return schema
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Derives the total enriched schema, made up of the saved schema and any
|
||||||
|
// prop and user overrides
|
||||||
const enrichedSchema = derived(
|
const enrichedSchema = derived(
|
||||||
[schema, schemaOverrides, columnWhitelist],
|
[schema, schemaOverrides, schemaMutations, columnWhitelist],
|
||||||
([$schema, $schemaOverrides, $columnWhitelist]) => {
|
([$schema, $schemaOverrides, $schemaMutations, $columnWhitelist]) => {
|
||||||
if (!$schema) {
|
if (!$schema) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
let enrichedSchema = { ...$schema }
|
let enrichedSchema = {}
|
||||||
|
Object.keys($schema).forEach(field => {
|
||||||
// Apply schema overrides
|
// Apply whitelist if provided
|
||||||
Object.keys($schemaOverrides || {}).forEach(field => {
|
if ($columnWhitelist?.length && !$columnWhitelist.includes(field)) {
|
||||||
if (enrichedSchema[field]) {
|
return
|
||||||
|
}
|
||||||
enrichedSchema[field] = {
|
enrichedSchema[field] = {
|
||||||
...enrichedSchema[field],
|
...$schema[field],
|
||||||
...$schemaOverrides[field],
|
...$schemaOverrides?.[field],
|
||||||
}
|
...$schemaMutations[field],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Apply whitelist if specified
|
|
||||||
if ($columnWhitelist?.length) {
|
|
||||||
const sortedColumns = {}
|
|
||||||
|
|
||||||
$columnWhitelist.forEach((columnKey, idx) => {
|
|
||||||
const enrichedColumn = enrichedSchema[columnKey]
|
|
||||||
if (enrichedColumn) {
|
|
||||||
sortedColumns[columnKey] = {
|
|
||||||
...enrichedColumn,
|
|
||||||
order: idx,
|
|
||||||
visible: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return sortedColumns
|
|
||||||
}
|
|
||||||
|
|
||||||
return enrichedSchema
|
return enrichedSchema
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -100,6 +92,8 @@ export const createActions = context => {
|
||||||
table,
|
table,
|
||||||
viewV2,
|
viewV2,
|
||||||
nonPlus,
|
nonPlus,
|
||||||
|
schemaMutations,
|
||||||
|
schema,
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
// Gets the appropriate API for the configured datasource type
|
// Gets the appropriate API for the configured datasource type
|
||||||
|
@ -136,11 +130,81 @@ export const createActions = context => {
|
||||||
// Update server
|
// Update server
|
||||||
if (get(config).canSaveSchema) {
|
if (get(config).canSaveSchema) {
|
||||||
await getAPI()?.actions.saveDefinition(newDefinition)
|
await getAPI()?.actions.saveDefinition(newDefinition)
|
||||||
|
|
||||||
|
// Broadcast change so external state can be updated, as this change
|
||||||
|
// will not be received by the builder websocket because we caused it
|
||||||
|
// ourselves
|
||||||
|
dispatch("updatedatasource", newDefinition)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast change to external state can be updated, as this change
|
// Updates the datasources primary display column
|
||||||
// will not be received by the builder websocket because we caused it ourselves
|
const changePrimaryDisplay = async column => {
|
||||||
dispatch("updatedatasource", newDefinition)
|
return await saveDefinition({
|
||||||
|
...get(definition),
|
||||||
|
primaryDisplay: column,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a schema mutation for a single field
|
||||||
|
const addSchemaMutation = (field, mutation) => {
|
||||||
|
if (!field || !mutation) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
schemaMutations.update($schemaMutations => {
|
||||||
|
return {
|
||||||
|
...$schemaMutations,
|
||||||
|
[field]: {
|
||||||
|
...$schemaMutations[field],
|
||||||
|
...mutation,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds schema mutations for multiple fields at once
|
||||||
|
const addSchemaMutations = mutations => {
|
||||||
|
const fields = Object.keys(mutations || {})
|
||||||
|
if (!fields.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
schemaMutations.update($schemaMutations => {
|
||||||
|
let newSchemaMutations = { ...$schemaMutations }
|
||||||
|
fields.forEach(field => {
|
||||||
|
newSchemaMutations[field] = {
|
||||||
|
...newSchemaMutations[field],
|
||||||
|
...mutations[field],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return newSchemaMutations
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saves schema changes to the server, if possible
|
||||||
|
const saveSchemaMutations = async () => {
|
||||||
|
// If we can't save schema changes then we just want to keep this in memory
|
||||||
|
if (!get(config).canSaveSchema) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const $definition = get(definition)
|
||||||
|
const $schemaMutations = get(schemaMutations)
|
||||||
|
const $schema = get(schema)
|
||||||
|
let newSchema = {}
|
||||||
|
|
||||||
|
// Build new updated datasource schema
|
||||||
|
Object.keys($schema).forEach(column => {
|
||||||
|
newSchema[column] = {
|
||||||
|
...$schema[column],
|
||||||
|
...$schemaMutations[column],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Save the changes, then reset our local mutations
|
||||||
|
await saveDefinition({
|
||||||
|
...$definition,
|
||||||
|
schema: newSchema,
|
||||||
|
})
|
||||||
|
schemaMutations.set({})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a row to the datasource
|
// Adds a row to the datasource
|
||||||
|
@ -185,6 +249,10 @@ export const createActions = context => {
|
||||||
getRow,
|
getRow,
|
||||||
isDatasourceValid,
|
isDatasourceValid,
|
||||||
canUseColumn,
|
canUseColumn,
|
||||||
|
changePrimaryDisplay,
|
||||||
|
addSchemaMutation,
|
||||||
|
addSchemaMutations,
|
||||||
|
saveSchemaMutations,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ export const createActions = context => {
|
||||||
stickyColumn,
|
stickyColumn,
|
||||||
maxScrollLeft,
|
maxScrollLeft,
|
||||||
width,
|
width,
|
||||||
|
datasource,
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
let autoScrollInterval
|
let autoScrollInterval
|
||||||
|
@ -173,20 +174,17 @@ export const createActions = context => {
|
||||||
document.removeEventListener("touchend", stopReordering)
|
document.removeEventListener("touchend", stopReordering)
|
||||||
document.removeEventListener("touchcancel", stopReordering)
|
document.removeEventListener("touchcancel", stopReordering)
|
||||||
|
|
||||||
// Ensure there's actually a change
|
// Ensure there's actually a change before saving
|
||||||
let { sourceColumn, targetColumn } = get(reorder)
|
const { sourceColumn, targetColumn } = get(reorder)
|
||||||
if (sourceColumn !== targetColumn) {
|
|
||||||
moveColumn(sourceColumn, targetColumn)
|
|
||||||
await columns.actions.saveChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset state
|
|
||||||
reorder.set(reorderInitialState)
|
reorder.set(reorderInitialState)
|
||||||
|
if (sourceColumn !== targetColumn) {
|
||||||
|
await moveColumn(sourceColumn, targetColumn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves a column after another columns.
|
// Moves a column after another columns.
|
||||||
// An undefined target column will move the source to index 0.
|
// An undefined target column will move the source to index 0.
|
||||||
const moveColumn = (sourceColumn, targetColumn) => {
|
const moveColumn = async (sourceColumn, targetColumn) => {
|
||||||
let $columns = get(columns)
|
let $columns = get(columns)
|
||||||
let sourceIdx = $columns.findIndex(x => x.name === sourceColumn)
|
let sourceIdx = $columns.findIndex(x => x.name === sourceColumn)
|
||||||
let targetIdx = $columns.findIndex(x => x.name === targetColumn)
|
let targetIdx = $columns.findIndex(x => x.name === targetColumn)
|
||||||
|
@ -198,14 +196,21 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
return state.toSpliced(targetIdx, 0, removed[0])
|
return state.toSpliced(targetIdx, 0, removed[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Extract new orders as schema mutations
|
||||||
|
let mutations = {}
|
||||||
|
get(columns).forEach((column, idx) => {
|
||||||
|
mutations[column.name] = { order: idx }
|
||||||
|
})
|
||||||
|
datasource.actions.addSchemaMutations(mutations)
|
||||||
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves a column one place left (as appears visually)
|
// Moves a column one place left (as appears visually)
|
||||||
const moveColumnLeft = async column => {
|
const moveColumnLeft = async column => {
|
||||||
const $visibleColumns = get(visibleColumns)
|
const $visibleColumns = get(visibleColumns)
|
||||||
const sourceIdx = $visibleColumns.findIndex(x => x.name === column)
|
const sourceIdx = $visibleColumns.findIndex(x => x.name === column)
|
||||||
moveColumn(column, $visibleColumns[sourceIdx - 2]?.name)
|
await moveColumn(column, $visibleColumns[sourceIdx - 2]?.name)
|
||||||
await columns.actions.saveChanges()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves a column one place right (as appears visually)
|
// Moves a column one place right (as appears visually)
|
||||||
|
@ -215,8 +220,7 @@ export const createActions = context => {
|
||||||
if (sourceIdx === $visibleColumns.length - 1) {
|
if (sourceIdx === $visibleColumns.length - 1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
moveColumn(column, $visibleColumns[sourceIdx + 1]?.name)
|
await moveColumn(column, $visibleColumns[sourceIdx + 1]?.name)
|
||||||
await columns.actions.saveChanges()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -6,7 +6,6 @@ const initialState = {
|
||||||
initialMouseX: null,
|
initialMouseX: null,
|
||||||
initialWidth: null,
|
initialWidth: null,
|
||||||
column: null,
|
column: null,
|
||||||
columnIdx: null,
|
|
||||||
width: 0,
|
width: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
}
|
}
|
||||||
|
@ -21,7 +20,7 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createActions = context => {
|
export const createActions = context => {
|
||||||
const { resize, columns, stickyColumn, ui } = context
|
const { resize, ui, datasource } = context
|
||||||
|
|
||||||
// Starts resizing a certain column
|
// Starts resizing a certain column
|
||||||
const startResizing = (column, e) => {
|
const startResizing = (column, e) => {
|
||||||
|
@ -32,12 +31,6 @@ export const createActions = context => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
ui.actions.blur()
|
ui.actions.blur()
|
||||||
|
|
||||||
// Find and cache index
|
|
||||||
let columnIdx = get(columns).findIndex(col => col.name === column.name)
|
|
||||||
if (columnIdx === -1) {
|
|
||||||
columnIdx = "sticky"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial store state
|
// Set initial store state
|
||||||
resize.set({
|
resize.set({
|
||||||
width: column.width,
|
width: column.width,
|
||||||
|
@ -45,7 +38,6 @@ export const createActions = context => {
|
||||||
initialWidth: column.width,
|
initialWidth: column.width,
|
||||||
initialMouseX: x,
|
initialMouseX: x,
|
||||||
column: column.name,
|
column: column.name,
|
||||||
columnIdx,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add mouse event listeners to handle resizing
|
// Add mouse event listeners to handle resizing
|
||||||
|
@ -58,7 +50,7 @@ export const createActions = context => {
|
||||||
|
|
||||||
// Handler for moving the mouse to resize columns
|
// Handler for moving the mouse to resize columns
|
||||||
const onResizeMouseMove = e => {
|
const onResizeMouseMove = e => {
|
||||||
const { initialMouseX, initialWidth, width, columnIdx } = get(resize)
|
const { initialMouseX, initialWidth, width, column } = get(resize)
|
||||||
const { x } = parseEventLocation(e)
|
const { x } = parseEventLocation(e)
|
||||||
const dx = x - initialMouseX
|
const dx = x - initialMouseX
|
||||||
const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx))
|
const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx))
|
||||||
|
@ -69,17 +61,7 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update column state
|
// Update column state
|
||||||
if (columnIdx === "sticky") {
|
datasource.actions.addSchemaMutation(column, { width })
|
||||||
stickyColumn.update(state => ({
|
|
||||||
...state,
|
|
||||||
width: newWidth,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
columns.update(state => {
|
|
||||||
state[columnIdx].width = newWidth
|
|
||||||
return [...state]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
resize.update(state => ({
|
resize.update(state => ({
|
||||||
|
@ -101,26 +83,16 @@ export const createActions = context => {
|
||||||
|
|
||||||
// Persist width if it changed
|
// Persist width if it changed
|
||||||
if ($resize.width !== $resize.initialWidth) {
|
if ($resize.width !== $resize.initialWidth) {
|
||||||
await columns.actions.saveChanges()
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets a column size back to default
|
// Resets a column size back to default
|
||||||
const resetSize = async column => {
|
const resetSize = async column => {
|
||||||
const $stickyColumn = get(stickyColumn)
|
datasource.actions.addSchemaMutation(column.name, {
|
||||||
if (column.name === $stickyColumn?.name) {
|
|
||||||
stickyColumn.update(state => ({
|
|
||||||
...state,
|
|
||||||
width: DefaultColumnWidth,
|
width: DefaultColumnWidth,
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
columns.update(state => {
|
|
||||||
const columnIdx = state.findIndex(x => x.name === column.name)
|
|
||||||
state[columnIdx].width = DefaultColumnWidth
|
|
||||||
return [...state]
|
|
||||||
})
|
})
|
||||||
}
|
await datasource.actions.saveSchemaMutations()
|
||||||
await columns.actions.saveChanges()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue