Merge branch 'master' into v3-ui
This commit is contained in:
commit
d082c7b77d
|
@ -277,5 +277,5 @@ export const flags = new FlagSet({
|
||||||
AUTOMATION_BRANCHING: Flag.boolean(env.isDev()),
|
AUTOMATION_BRANCHING: Flag.boolean(env.isDev()),
|
||||||
SQS: Flag.boolean(env.isDev()),
|
SQS: Flag.boolean(env.isDev()),
|
||||||
[FeatureFlag.AI_CUSTOM_CONFIGS]: Flag.boolean(env.isDev()),
|
[FeatureFlag.AI_CUSTOM_CONFIGS]: Flag.boolean(env.isDev()),
|
||||||
[FeatureFlag.ENRICHED_RELATIONSHIPS]: Flag.boolean(false),
|
[FeatureFlag.ENRICHED_RELATIONSHIPS]: Flag.boolean(env.isDev()),
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
||||||
import RoleCell from "./cells/RoleCell.svelte"
|
import RoleCell from "./cells/RoleCell.svelte"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { canBeSortColumn } from "@budibase/shared-core"
|
import { canBeSortColumn } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export let schema = {}
|
export let schema = {}
|
||||||
export let data = []
|
export let data = []
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
acc[key] =
|
acc[key] =
|
||||||
typeof schema[key] === "string" ? { type: schema[key] } : schema[key]
|
typeof schema[key] === "string" ? { type: schema[key] } : schema[key]
|
||||||
|
|
||||||
if (!canBeSortColumn(acc[key].type)) {
|
if (!canBeSortColumn(acc[key])) {
|
||||||
acc[key].sortable = false
|
acc[key].sortable = false
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
|
|
|
@ -121,8 +121,10 @@
|
||||||
label: name,
|
label: name,
|
||||||
schema: {
|
schema: {
|
||||||
type: column.type,
|
type: column.type,
|
||||||
|
subtype: column.subtype,
|
||||||
visible: column.visible,
|
visible: column.visible,
|
||||||
readonly: column.readonly,
|
readonly: column.readonly,
|
||||||
|
constraints: column.constraints, // This is needed to properly display "users" column
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,13 +13,15 @@
|
||||||
import { isEnabled } from "helpers/featureFlags"
|
import { isEnabled } from "helpers/featureFlags"
|
||||||
import { FeatureFlag } from "@budibase/types"
|
import { FeatureFlag } from "@budibase/types"
|
||||||
|
|
||||||
const { columns, datasource } = getContext("grid")
|
const { tableColumns, datasource } = getContext("grid")
|
||||||
|
|
||||||
let open = false
|
let open = false
|
||||||
let anchor
|
let anchor
|
||||||
|
|
||||||
$: anyRestricted = $columns.filter(col => !col.visible || col.readonly).length
|
$: anyRestricted = $tableColumns.filter(
|
||||||
$: text = anyRestricted ? `Columns: ${anyRestricted} restricted` : "Columns"
|
col => !col.visible || col.readonly
|
||||||
|
).length
|
||||||
|
$: text = anyRestricted ? `Columns: (${anyRestricted} restricted)` : "Columns"
|
||||||
$: permissions =
|
$: permissions =
|
||||||
$datasource.type === "viewV2"
|
$datasource.type === "viewV2"
|
||||||
? [
|
? [
|
||||||
|
@ -37,8 +39,8 @@
|
||||||
size="M"
|
size="M"
|
||||||
on:click={() => (open = !open)}
|
on:click={() => (open = !open)}
|
||||||
selected={open || anyRestricted}
|
selected={open || anyRestricted}
|
||||||
disabled={!$columns.length}
|
|
||||||
accentColor="#674D00"
|
accentColor="#674D00"
|
||||||
|
disabled={!$tableColumns.length}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
@ -46,7 +48,7 @@
|
||||||
|
|
||||||
<Popover bind:open {anchor} align="left">
|
<Popover bind:open {anchor} align="left">
|
||||||
<ColumnsSettingContent
|
<ColumnsSettingContent
|
||||||
columns={$columns}
|
columns={$tableColumns}
|
||||||
canSetRelationshipSchemas={isEnabled(FeatureFlag.ENRICHED_RELATIONSHIPS)}
|
canSetRelationshipSchemas={isEnabled(FeatureFlag.ENRICHED_RELATIONSHIPS)}
|
||||||
{permissions}
|
{permissions}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { ActionButton, Popover, Select } from "@budibase/bbui"
|
import { ActionButton, Popover, Select } from "@budibase/bbui"
|
||||||
import { canBeSortColumn } from "@budibase/shared-core"
|
import { canBeSortColumn } from "@budibase/frontend-core"
|
||||||
|
|
||||||
const { sort, columns } = getContext("grid")
|
const { sort, columns } = getContext("grid")
|
||||||
|
|
||||||
|
@ -13,8 +13,9 @@
|
||||||
label: col.label || col.name,
|
label: col.label || col.name,
|
||||||
value: col.name,
|
value: col.name,
|
||||||
type: col.schema?.type,
|
type: col.schema?.type,
|
||||||
|
related: col.related,
|
||||||
}))
|
}))
|
||||||
.filter(col => canBeSortColumn(col.type))
|
.filter(col => canBeSortColumn(col))
|
||||||
$: orderOptions = getOrderOptions($sort.column, columnOptions)
|
$: orderOptions = getOrderOptions($sort.column, columnOptions)
|
||||||
|
|
||||||
const getOrderOptions = (column, columnOptions) => {
|
const getOrderOptions = (column, columnOptions) => {
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
helpers,
|
helpers,
|
||||||
PROTECTED_INTERNAL_COLUMNS,
|
PROTECTED_INTERNAL_COLUMNS,
|
||||||
PROTECTED_EXTERNAL_COLUMNS,
|
PROTECTED_EXTERNAL_COLUMNS,
|
||||||
canBeDisplayColumn,
|
|
||||||
canHaveDefaultColumn,
|
canHaveDefaultColumn,
|
||||||
} from "@budibase/shared-core"
|
} from "@budibase/shared-core"
|
||||||
import { createEventDispatcher, getContext, onMount } from "svelte"
|
import { createEventDispatcher, getContext, onMount } from "svelte"
|
||||||
|
@ -43,7 +42,7 @@
|
||||||
SourceName,
|
SourceName,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
||||||
import { RowUtils } from "@budibase/frontend-core"
|
import { RowUtils, canBeDisplayColumn } from "@budibase/frontend-core"
|
||||||
import ServerBindingPanel from "components/common/bindings/ServerBindingPanel.svelte"
|
import ServerBindingPanel from "components/common/bindings/ServerBindingPanel.svelte"
|
||||||
import OptionsEditor from "./OptionsEditor.svelte"
|
import OptionsEditor from "./OptionsEditor.svelte"
|
||||||
import { isEnabled } from "helpers/featureFlags"
|
import { isEnabled } from "helpers/featureFlags"
|
||||||
|
@ -166,7 +165,7 @@
|
||||||
: availableAutoColumns
|
: availableAutoColumns
|
||||||
// used to select what different options can be displayed for column type
|
// used to select what different options can be displayed for column type
|
||||||
$: canBeDisplay =
|
$: canBeDisplay =
|
||||||
canBeDisplayColumn(editableColumn.type) && !editableColumn.autocolumn
|
canBeDisplayColumn(editableColumn) && !editableColumn.autocolumn
|
||||||
$: canHaveDefault =
|
$: canHaveDefault =
|
||||||
isEnabled("DEFAULT_VALUES") && canHaveDefaultColumn(editableColumn.type)
|
isEnabled("DEFAULT_VALUES") && canHaveDefaultColumn(editableColumn.type)
|
||||||
$: canBeRequired =
|
$: canBeRequired =
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Icon } from "@budibase/bbui"
|
import { Select, Icon } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import { canBeDisplayColumn, utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
import { canBeDisplayColumn } from "@budibase/frontend-core"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { parseFile } from "./utils"
|
import { parseFile } from "./utils"
|
||||||
|
|
||||||
|
@ -100,10 +101,10 @@
|
||||||
let rawRows = []
|
let rawRows = []
|
||||||
|
|
||||||
$: displayColumnOptions = Object.keys(schema || {}).filter(column => {
|
$: displayColumnOptions = Object.keys(schema || {}).filter(column => {
|
||||||
return validation[column] && canBeDisplayColumn(schema[column].type)
|
return validation[column] && canBeDisplayColumn(schema[column])
|
||||||
})
|
})
|
||||||
|
|
||||||
$: if (displayColumn && !canBeDisplayColumn(schema[displayColumn].type)) {
|
$: if (displayColumn && !canBeDisplayColumn(schema[displayColumn])) {
|
||||||
displayColumn = null
|
displayColumn = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { enrichSchemaWithRelColumns } from "@budibase/frontend-core"
|
||||||
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
||||||
import { selectedScreen, componentStore } from "stores/builder"
|
import { selectedScreen, componentStore } from "stores/builder"
|
||||||
import DraggableList from "../DraggableList/DraggableList.svelte"
|
import DraggableList from "../DraggableList/DraggableList.svelte"
|
||||||
|
@ -28,7 +29,8 @@
|
||||||
delete schema._rev
|
delete schema._rev
|
||||||
}
|
}
|
||||||
|
|
||||||
return schema
|
const result = enrichSchemaWithRelColumns(schema)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
||||||
|
|
|
@ -82,7 +82,7 @@ const toDraggableListFormat = (gridFormatColumns, createComponent, schema) => {
|
||||||
active: column.active,
|
active: column.active,
|
||||||
field: column.field,
|
field: column.field,
|
||||||
label: column.label,
|
label: column.label,
|
||||||
columnType: schema[column.field].type,
|
columnType: column.columnType || schema[column.field].type,
|
||||||
width: column.width,
|
width: column.width,
|
||||||
conditions: column.conditions,
|
conditions: column.conditions,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
||||||
import { selectedScreen } from "stores/builder"
|
import { selectedScreen } from "stores/builder"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { canBeSortColumn } from "@budibase/shared-core"
|
import { canBeSortColumn } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let value = ""
|
export let value = ""
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
const getSortableFields = schema => {
|
const getSortableFields = schema => {
|
||||||
return Object.entries(schema || {})
|
return Object.entries(schema || {})
|
||||||
.filter(entry => canBeSortColumn(entry[1].type))
|
.filter(entry => canBeSortColumn(entry[1]))
|
||||||
.map(entry => entry[0])
|
.map(entry => entry[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { getContext, onDestroy } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
import { Table } from "@budibase/bbui"
|
import { Table } from "@budibase/bbui"
|
||||||
import SlotRenderer from "./SlotRenderer.svelte"
|
import SlotRenderer from "./SlotRenderer.svelte"
|
||||||
import { canBeSortColumn } from "@budibase/shared-core"
|
import { canBeSortColumn } from "@budibase/frontend-core"
|
||||||
import Provider from "components/context/Provider.svelte"
|
import Provider from "components/context/Provider.svelte"
|
||||||
|
|
||||||
export let dataProvider
|
export let dataProvider
|
||||||
|
@ -146,7 +146,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newSchema[columnName] = schema[columnName]
|
newSchema[columnName] = schema[columnName]
|
||||||
if (!canBeSortColumn(schema[columnName].type)) {
|
if (!canBeSortColumn(schema[columnName])) {
|
||||||
newSchema[columnName].sortable = false
|
newSchema[columnName].sortable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { onMount, getContext } from "svelte"
|
import { onMount, getContext } from "svelte"
|
||||||
import { Dropzone } from "@budibase/bbui"
|
import { Dropzone } from "@budibase/bbui"
|
||||||
import GridPopover from "../overlays/GridPopover.svelte"
|
import GridPopover from "../overlays/GridPopover.svelte"
|
||||||
|
import { FieldType } from "@budibase/types"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
export let focused = false
|
export let focused = false
|
||||||
|
@ -81,7 +82,12 @@
|
||||||
>
|
>
|
||||||
{#each value || [] as attachment}
|
{#each value || [] as attachment}
|
||||||
{#if isImage(attachment.extension)}
|
{#if isImage(attachment.extension)}
|
||||||
<img src={attachment.url} alt={attachment.extension} />
|
<img
|
||||||
|
class:light={!$props?.darkMode &&
|
||||||
|
schema.type === FieldType.SIGNATURE_SINGLE}
|
||||||
|
src={attachment.url}
|
||||||
|
alt={attachment.extension}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="file" title={attachment.name}>
|
<div class="file" title={attachment.name}>
|
||||||
{attachment.extension}
|
{attachment.extension}
|
||||||
|
@ -140,4 +146,9 @@
|
||||||
width: 320px;
|
width: 320px;
|
||||||
padding: var(--cell-padding);
|
padding: var(--cell-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.attachment-cell img.light {
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount, tick } from "svelte"
|
import { getContext, onMount, tick } from "svelte"
|
||||||
import { canBeDisplayColumn, canBeSortColumn } from "@budibase/shared-core"
|
import { canBeSortColumn, canBeDisplayColumn } from "@budibase/frontend-core"
|
||||||
import { Icon, Menu, MenuItem, Modal } from "@budibase/bbui"
|
import { Icon, Menu, MenuItem, Modal } from "@budibase/bbui"
|
||||||
import GridCell from "./GridCell.svelte"
|
import GridCell from "./GridCell.svelte"
|
||||||
import { getColumnIcon } from "../../../utils/schema"
|
import { getColumnIcon } from "../../../utils/schema"
|
||||||
|
@ -165,7 +165,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideColumn = () => {
|
const hideColumn = () => {
|
||||||
datasource.actions.addSchemaMutation(column.name, { visible: false })
|
const { related } = column
|
||||||
|
const mutation = { visible: false }
|
||||||
|
if (!related) {
|
||||||
|
datasource.actions.addSchemaMutation(column.name, mutation)
|
||||||
|
} else {
|
||||||
|
datasource.actions.addSubSchemaMutation(
|
||||||
|
related.subField,
|
||||||
|
related.field,
|
||||||
|
mutation
|
||||||
|
)
|
||||||
|
}
|
||||||
datasource.actions.saveSchemaMutations()
|
datasource.actions.saveSchemaMutations()
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
|
@ -347,15 +357,14 @@
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="Label"
|
icon="Label"
|
||||||
on:click={makeDisplayColumn}
|
on:click={makeDisplayColumn}
|
||||||
disabled={column.primaryDisplay ||
|
disabled={column.primaryDisplay || !canBeDisplayColumn(column.schema)}
|
||||||
!canBeDisplayColumn(column.schema.type)}
|
|
||||||
>
|
>
|
||||||
Use as display column
|
Use as display column
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="SortOrderUp"
|
icon="SortOrderUp"
|
||||||
on:click={sortAscending}
|
on:click={sortAscending}
|
||||||
disabled={!canBeSortColumn(column.schema.type) ||
|
disabled={!canBeSortColumn(column.schema) ||
|
||||||
(column.name === $sort.column && $sort.order === "ascending")}
|
(column.name === $sort.column && $sort.order === "ascending")}
|
||||||
>
|
>
|
||||||
Sort {sortingLabels.ascending}
|
Sort {sortingLabels.ascending}
|
||||||
|
@ -363,7 +372,7 @@
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="SortOrderDown"
|
icon="SortOrderDown"
|
||||||
on:click={sortDescending}
|
on:click={sortDescending}
|
||||||
disabled={!canBeSortColumn(column.schema.type) ||
|
disabled={!canBeSortColumn(column.schema) ||
|
||||||
(column.name === $sort.column && $sort.order === "descending")}
|
(column.name === $sort.column && $sort.order === "descending")}
|
||||||
>
|
>
|
||||||
Sort {sortingLabels.descending}
|
Sort {sortingLabels.descending}
|
||||||
|
|
|
@ -35,5 +35,9 @@ const TypeComponentMap = {
|
||||||
[FieldType.BB_REFERENCE_SINGLE]: BBReferenceSingleCell,
|
[FieldType.BB_REFERENCE_SINGLE]: BBReferenceSingleCell,
|
||||||
}
|
}
|
||||||
export const getCellRenderer = column => {
|
export const getCellRenderer = column => {
|
||||||
return TypeComponentMap[column?.schema?.type] || TextCell
|
return (
|
||||||
|
TypeComponentMap[column?.schema?.cellRenderType] ||
|
||||||
|
TypeComponentMap[column?.schema?.type] ||
|
||||||
|
TextCell
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,11 @@ export const deriveStores = context => {
|
||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Derived list of columns which are direct part of the table
|
||||||
|
const tableColumns = derived(columns, $columns => {
|
||||||
|
return $columns.filter(col => !col.related)
|
||||||
|
})
|
||||||
|
|
||||||
// Derived list of columns which have not been explicitly hidden
|
// Derived list of columns which have not been explicitly hidden
|
||||||
const visibleColumns = derived(columns, $columns => {
|
const visibleColumns = derived(columns, $columns => {
|
||||||
return $columns.filter(col => col.visible)
|
return $columns.filter(col => col.visible)
|
||||||
|
@ -64,6 +69,7 @@ export const deriveStores = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
tableColumns,
|
||||||
displayColumn,
|
displayColumn,
|
||||||
columnLookupMap,
|
columnLookupMap,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
|
@ -73,16 +79,24 @@ export const deriveStores = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createActions = context => {
|
export const createActions = context => {
|
||||||
const { columns, datasource, schema } = context
|
const { columns, datasource } = context
|
||||||
|
|
||||||
// Updates the width of all columns
|
// Updates the width of all columns
|
||||||
const changeAllColumnWidths = async width => {
|
const changeAllColumnWidths = async width => {
|
||||||
const $schema = get(schema)
|
const $columns = get(columns)
|
||||||
let mutations = {}
|
$columns.forEach(column => {
|
||||||
Object.keys($schema).forEach(field => {
|
const { related } = column
|
||||||
mutations[field] = { width }
|
const mutation = { width }
|
||||||
|
if (!related) {
|
||||||
|
datasource.actions.addSchemaMutation(column.name, mutation)
|
||||||
|
} else {
|
||||||
|
datasource.actions.addSubSchemaMutation(
|
||||||
|
related.subField,
|
||||||
|
related.field,
|
||||||
|
mutation
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
datasource.actions.addSchemaMutations(mutations)
|
|
||||||
await datasource.actions.saveSchemaMutations()
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +150,7 @@ export const initialise = context => {
|
||||||
.map(field => {
|
.map(field => {
|
||||||
const fieldSchema = $enrichedSchema[field]
|
const fieldSchema = $enrichedSchema[field]
|
||||||
const oldColumn = $columns?.find(col => col.name === field)
|
const oldColumn = $columns?.find(col => col.name === field)
|
||||||
let column = {
|
const column = {
|
||||||
name: field,
|
name: field,
|
||||||
label: fieldSchema.displayName || field,
|
label: fieldSchema.displayName || field,
|
||||||
schema: fieldSchema,
|
schema: fieldSchema,
|
||||||
|
@ -145,6 +159,7 @@ export const initialise = context => {
|
||||||
readonly: fieldSchema.readonly,
|
readonly: fieldSchema.readonly,
|
||||||
order: fieldSchema.order ?? oldColumn?.order,
|
order: fieldSchema.order ?? oldColumn?.order,
|
||||||
conditions: fieldSchema.conditions,
|
conditions: fieldSchema.conditions,
|
||||||
|
related: fieldSchema.related,
|
||||||
}
|
}
|
||||||
// Override a few properties for primary display
|
// Override a few properties for primary display
|
||||||
if (field === primaryDisplay) {
|
if (field === primaryDisplay) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { derived, get } from "svelte/store"
|
import { derived, get } from "svelte/store"
|
||||||
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch"
|
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch"
|
||||||
import { memo } from "../../../utils"
|
import { enrichSchemaWithRelColumns, memo } from "../../../utils"
|
||||||
|
|
||||||
export const createStores = () => {
|
export const createStores = () => {
|
||||||
const definition = memo(null)
|
const definition = memo(null)
|
||||||
|
@ -53,10 +53,13 @@ export const deriveStores = context => {
|
||||||
if (!$schema) {
|
if (!$schema) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
let enrichedSchema = {}
|
|
||||||
Object.keys($schema).forEach(field => {
|
const schemaWithRelatedColumns = enrichSchemaWithRelColumns($schema)
|
||||||
|
|
||||||
|
const enrichedSchema = {}
|
||||||
|
Object.keys(schemaWithRelatedColumns).forEach(field => {
|
||||||
enrichedSchema[field] = {
|
enrichedSchema[field] = {
|
||||||
...$schema[field],
|
...schemaWithRelatedColumns[field],
|
||||||
...$schemaOverrides?.[field],
|
...$schemaOverrides?.[field],
|
||||||
...$schemaMutations[field],
|
...$schemaMutations[field],
|
||||||
}
|
}
|
||||||
|
@ -202,24 +205,6 @@ export const createActions = context => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Saves schema changes to the server, if possible
|
||||||
const saveSchemaMutations = async () => {
|
const saveSchemaMutations = async () => {
|
||||||
// If we can't save schema changes then we just want to keep this in memory
|
// If we can't save schema changes then we just want to keep this in memory
|
||||||
|
@ -309,7 +294,6 @@ export const createActions = context => {
|
||||||
changePrimaryDisplay,
|
changePrimaryDisplay,
|
||||||
addSchemaMutation,
|
addSchemaMutation,
|
||||||
addSubSchemaMutation,
|
addSubSchemaMutation,
|
||||||
addSchemaMutations,
|
|
||||||
saveSchemaMutations,
|
saveSchemaMutations,
|
||||||
resetSchemaMutations,
|
resetSchemaMutations,
|
||||||
},
|
},
|
||||||
|
|
|
@ -133,24 +133,29 @@ export const initialise = context => {
|
||||||
// When sorting changes, ensure view definition is kept up to date
|
// When sorting changes, ensure view definition is kept up to date
|
||||||
unsubscribers.push(
|
unsubscribers.push(
|
||||||
sort.subscribe(async $sort => {
|
sort.subscribe(async $sort => {
|
||||||
|
// Ensure we're updating the correct view
|
||||||
|
const $view = get(definition)
|
||||||
|
if ($view?.id !== $datasource.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if nothing actually changed
|
||||||
|
if (
|
||||||
|
$sort?.column === $view.sort?.field &&
|
||||||
|
$sort?.order === $view.sort?.order
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If we can mutate schema then update the view definition
|
// If we can mutate schema then update the view definition
|
||||||
if (get(config).canSaveSchema) {
|
if (get(config).canSaveSchema) {
|
||||||
const $view = get(definition)
|
await datasource.actions.saveDefinition({
|
||||||
if ($view?.id !== $datasource.id) {
|
...$view,
|
||||||
return
|
sort: {
|
||||||
}
|
field: $sort.column,
|
||||||
if (
|
order: $sort.order || "ascending",
|
||||||
$sort?.column !== $view.sort?.field ||
|
},
|
||||||
$sort?.order !== $view.sort?.order
|
})
|
||||||
) {
|
|
||||||
await datasource.actions.saveDefinition({
|
|
||||||
...$view,
|
|
||||||
sort: {
|
|
||||||
field: $sort.column,
|
|
||||||
order: $sort.order || "ascending",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also update the fetch to ensure the new sort is respected.
|
// Also update the fetch to ensure the new sort is respected.
|
||||||
|
|
|
@ -214,11 +214,20 @@ export const createActions = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Extract new orders as schema mutations
|
// Extract new orders as schema mutations
|
||||||
let mutations = {}
|
|
||||||
get(columns).forEach((column, idx) => {
|
get(columns).forEach((column, idx) => {
|
||||||
mutations[column.name] = { order: idx }
|
const { related } = column
|
||||||
|
const mutation = { order: idx }
|
||||||
|
if (!related) {
|
||||||
|
datasource.actions.addSchemaMutation(column.name, mutation)
|
||||||
|
} else {
|
||||||
|
datasource.actions.addSubSchemaMutation(
|
||||||
|
related.subField,
|
||||||
|
related.field,
|
||||||
|
mutation
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
datasource.actions.addSchemaMutations(mutations)
|
|
||||||
await datasource.actions.saveSchemaMutations()
|
await datasource.actions.saveSchemaMutations()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ export const createActions = context => {
|
||||||
initialWidth: column.width,
|
initialWidth: column.width,
|
||||||
initialMouseX: x,
|
initialMouseX: x,
|
||||||
column: column.name,
|
column: column.name,
|
||||||
|
related: column.related,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add mouse event listeners to handle resizing
|
// Add mouse event listeners to handle resizing
|
||||||
|
@ -50,7 +51,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, column } = get(resize)
|
const { initialMouseX, initialWidth, width, column, related } = 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))
|
||||||
|
@ -61,7 +62,13 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update column state
|
// Update column state
|
||||||
datasource.actions.addSchemaMutation(column, { width })
|
if (!related) {
|
||||||
|
datasource.actions.addSchemaMutation(column, { width })
|
||||||
|
} else {
|
||||||
|
datasource.actions.addSubSchemaMutation(related.subField, related.field, {
|
||||||
|
width,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
resize.update(state => ({
|
resize.update(state => ({
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { tick } from "svelte"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { sleep } from "../../../utils/utils"
|
import { sleep } from "../../../utils/utils"
|
||||||
import { FieldType } from "@budibase/types"
|
import { FieldType } from "@budibase/types"
|
||||||
|
import { getRelatedTableValues } from "../../../utils"
|
||||||
|
|
||||||
export const createStores = () => {
|
export const createStores = () => {
|
||||||
const rows = writable([])
|
const rows = writable([])
|
||||||
|
@ -42,15 +43,26 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = context => {
|
||||||
const { rows } = context
|
const { rows, enrichedSchema } = context
|
||||||
|
|
||||||
// Enrich rows with an index property and any pending changes
|
// Enrich rows with an index property and any pending changes
|
||||||
const enrichedRows = derived(rows, $rows => {
|
const enrichedRows = derived(
|
||||||
return $rows.map((row, idx) => ({
|
[rows, enrichedSchema],
|
||||||
...row,
|
([$rows, $enrichedSchema]) => {
|
||||||
__idx: idx,
|
const customColumns = Object.values($enrichedSchema || {}).filter(
|
||||||
}))
|
f => f.related
|
||||||
})
|
)
|
||||||
|
return $rows.map((row, idx) => ({
|
||||||
|
...row,
|
||||||
|
__idx: idx,
|
||||||
|
...customColumns.reduce((map, column) => {
|
||||||
|
const fromField = $enrichedSchema[column.related.field]
|
||||||
|
map[column.name] = getRelatedTableValues(row, column, fromField)
|
||||||
|
return map
|
||||||
|
}, {}),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Generate a lookup map to quick find a row by ID
|
// Generate a lookup map to quick find a row by ID
|
||||||
const rowLookupMap = derived(enrichedRows, $enrichedRows => {
|
const rowLookupMap = derived(enrichedRows, $enrichedRows => {
|
||||||
|
|
|
@ -11,3 +11,5 @@ export { createWebsocket } from "./websocket"
|
||||||
export * from "./download"
|
export * from "./download"
|
||||||
export * from "./theme"
|
export * from "./theme"
|
||||||
export * from "./settings"
|
export * from "./settings"
|
||||||
|
export * from "./relatedColumns"
|
||||||
|
export * from "./table"
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { FieldType, RelationshipType } from "@budibase/types"
|
||||||
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
|
||||||
|
const columnTypeManyTypeOverrides = {
|
||||||
|
[FieldType.DATETIME]: FieldType.STRING,
|
||||||
|
[FieldType.BOOLEAN]: FieldType.STRING,
|
||||||
|
[FieldType.SIGNATURE_SINGLE]: FieldType.ATTACHMENTS,
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnTypeManyParser = {
|
||||||
|
[FieldType.DATETIME]: (value, field) => {
|
||||||
|
function parseDate(value) {
|
||||||
|
const { timeOnly, dateOnly, ignoreTimezones } = field || {}
|
||||||
|
const enableTime = !dateOnly
|
||||||
|
const parsedValue = Helpers.parseDate(value, {
|
||||||
|
timeOnly,
|
||||||
|
enableTime,
|
||||||
|
ignoreTimezones,
|
||||||
|
})
|
||||||
|
const parsed = Helpers.getDateDisplayValue(parsedValue, {
|
||||||
|
enableTime,
|
||||||
|
timeOnly,
|
||||||
|
})
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
return value?.map(v => parseDate(v))
|
||||||
|
},
|
||||||
|
[FieldType.BOOLEAN]: value => value?.map(v => !!v),
|
||||||
|
[FieldType.BB_REFERENCE_SINGLE]: value => [
|
||||||
|
...new Map(value.map(i => [i._id, i])).values(),
|
||||||
|
],
|
||||||
|
[FieldType.BB_REFERENCE]: value => [
|
||||||
|
...new Map(value.map(i => [i._id, i])).values(),
|
||||||
|
],
|
||||||
|
[FieldType.ARRAY]: value => Array.from(new Set(value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enrichSchemaWithRelColumns(schema) {
|
||||||
|
if (!schema) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const result = Object.keys(schema).reduce((result, fieldName) => {
|
||||||
|
const field = schema[fieldName]
|
||||||
|
result[fieldName] = field
|
||||||
|
|
||||||
|
if (field.visible !== false && field.columns) {
|
||||||
|
const fromSingle =
|
||||||
|
field?.relationshipType === RelationshipType.ONE_TO_MANY
|
||||||
|
|
||||||
|
for (const relColumn of Object.keys(field.columns)) {
|
||||||
|
const relField = field.columns[relColumn]
|
||||||
|
if (!relField.visible) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const name = `${field.name}.${relColumn}`
|
||||||
|
result[name] = {
|
||||||
|
...relField,
|
||||||
|
name,
|
||||||
|
related: { field: fieldName, subField: relColumn },
|
||||||
|
cellRenderType:
|
||||||
|
(!fromSingle && columnTypeManyTypeOverrides[relField.type]) ||
|
||||||
|
relField.type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelatedTableValues(row, field, fromField) {
|
||||||
|
const fromSingle =
|
||||||
|
fromField?.relationshipType === RelationshipType.ONE_TO_MANY
|
||||||
|
|
||||||
|
let result = ""
|
||||||
|
|
||||||
|
if (fromSingle) {
|
||||||
|
result = row[field.related.field]?.[0]?.[field.related.subField]
|
||||||
|
} else {
|
||||||
|
const parser = columnTypeManyParser[field.type] || (value => value)
|
||||||
|
|
||||||
|
result = parser(
|
||||||
|
row[field.related.field]
|
||||||
|
?.flatMap(r => r[field.related.subField])
|
||||||
|
?.filter(i => i !== undefined && i !== null),
|
||||||
|
field
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
FieldType.STRING,
|
||||||
|
FieldType.NUMBER,
|
||||||
|
FieldType.BIGINT,
|
||||||
|
FieldType.BOOLEAN,
|
||||||
|
FieldType.DATETIME,
|
||||||
|
FieldType.LONGFORM,
|
||||||
|
FieldType.BARCODEQR,
|
||||||
|
].includes(field.type)
|
||||||
|
) {
|
||||||
|
result = result?.join(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import * as sharedCore from "@budibase/shared-core"
|
||||||
|
|
||||||
|
export function canBeDisplayColumn(column) {
|
||||||
|
if (!sharedCore.canBeDisplayColumn(column.type)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.related) {
|
||||||
|
// If it's a related column (only available in the frontend), don't allow using it as display column
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canBeSortColumn(column) {
|
||||||
|
if (!sharedCore.canBeSortColumn(column.type)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.related) {
|
||||||
|
// If it's a related column (only available in the frontend), don't allow using it as display column
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -2684,7 +2684,7 @@ describe.each([
|
||||||
async (__, retrieveDelegate) => {
|
async (__, retrieveDelegate) => {
|
||||||
await withCoreEnv(
|
await withCoreEnv(
|
||||||
{
|
{
|
||||||
TENANT_FEATURE_FLAGS: ``,
|
TENANT_FEATURE_FLAGS: `*:!${FeatureFlag.ENRICHED_RELATIONSHIPS}`,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const otherRows = _.sampleSize(auxData, 5)
|
const otherRows = _.sampleSize(auxData, 5)
|
||||||
|
|
Loading…
Reference in New Issue