|
|
|
@ -1,4 +1,5 @@ |
|
|
|
<script> |
|
|
|
import { onMount, getContext } from "svelte" |
|
|
|
import Block from "components/Block.svelte" |
|
|
|
import BlockComponent from "components/BlockComponent.svelte" |
|
|
|
import { Heading } from "@budibase/bbui" |
|
|
|
@ -16,85 +17,156 @@ |
|
|
|
export let quiet |
|
|
|
export let size |
|
|
|
|
|
|
|
const { API, styleable } = getContext("sdk") |
|
|
|
const context = getContext("context") |
|
|
|
const component = getContext("component") |
|
|
|
const schemaComponentMap = { |
|
|
|
string: "stringfield", |
|
|
|
options: "optionsfield", |
|
|
|
number: "numberfield", |
|
|
|
datetime: "datetimefield", |
|
|
|
boolean: "booleanfield", |
|
|
|
} |
|
|
|
|
|
|
|
let formId |
|
|
|
let dataProviderId |
|
|
|
let schema |
|
|
|
|
|
|
|
$: enrichedFilter = enrichFilter(filter, searchColumns, formId) |
|
|
|
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema) |
|
|
|
$: enrichedFilter = enrichFilter(filter, enrichedSearchColumns, formId) |
|
|
|
|
|
|
|
// Enrich the default filter with the specified search fields |
|
|
|
const enrichFilter = (filter, searchColumns, formId) => { |
|
|
|
const enrichFilter = (filter, columns, formId) => { |
|
|
|
let enrichedFilter = [...(filter || [])] |
|
|
|
searchColumns?.forEach(column => { |
|
|
|
columns?.forEach(column => { |
|
|
|
enrichedFilter.push({ |
|
|
|
field: column, |
|
|
|
field: column.name, |
|
|
|
operator: "equal", |
|
|
|
type: "string", |
|
|
|
valueType: "Binding", |
|
|
|
value: `{{ [${formId}].[${column}] }}`, |
|
|
|
value: `{{ [${formId}].[${column.name}] }}`, |
|
|
|
}) |
|
|
|
}) |
|
|
|
return enrichedFilter |
|
|
|
} |
|
|
|
|
|
|
|
// Determine data types for search fields and only use those that are valid |
|
|
|
const enrichSearchColumns = (searchColumns, schema) => { |
|
|
|
let enrichedColumns = [] |
|
|
|
searchColumns?.forEach(column => { |
|
|
|
const schemaType = schema?.[column]?.type |
|
|
|
const componentType = schemaComponentMap[schemaType] |
|
|
|
if (componentType) { |
|
|
|
enrichedColumns.push({ |
|
|
|
name: column, |
|
|
|
componentType, |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
return enrichedColumns |
|
|
|
} |
|
|
|
|
|
|
|
// Load the datasource schema on mount so we can determine column types |
|
|
|
onMount(async () => { |
|
|
|
if (dataSource) { |
|
|
|
schema = await API.fetchDatasourceSchema(dataSource) |
|
|
|
} |
|
|
|
}) |
|
|
|
</script> |
|
|
|
|
|
|
|
<Block> |
|
|
|
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> |
|
|
|
{#if searchColumns?.length} |
|
|
|
<div class="search"> |
|
|
|
{#if title} |
|
|
|
<div class={size} use:styleable={$component.styles}> |
|
|
|
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> |
|
|
|
{#if title || enrichedSearchColumns?.length} |
|
|
|
<div class="header"> |
|
|
|
<div class="title"> |
|
|
|
<Heading>{title}</Heading> |
|
|
|
<Heading>{title || ""}</Heading> |
|
|
|
</div> |
|
|
|
{/if} |
|
|
|
{#each searchColumns as column} |
|
|
|
<BlockComponent |
|
|
|
type="stringfield" |
|
|
|
props={{ |
|
|
|
field: column, |
|
|
|
placeholder: column, |
|
|
|
label: column, |
|
|
|
}} |
|
|
|
/> |
|
|
|
{/each} |
|
|
|
</div> |
|
|
|
{/if} |
|
|
|
<BlockComponent |
|
|
|
type="dataprovider" |
|
|
|
bind:id={dataProviderId} |
|
|
|
props={{ |
|
|
|
dataSource, |
|
|
|
filter: enrichedFilter, |
|
|
|
sortColumn, |
|
|
|
sortOrder, |
|
|
|
paginate, |
|
|
|
limit: rowCount, |
|
|
|
}} |
|
|
|
> |
|
|
|
{#if enrichedSearchColumns?.length} |
|
|
|
<div class="search" class:mobile={$context.device.mobile}> |
|
|
|
{#each enrichedSearchColumns as column} |
|
|
|
<BlockComponent |
|
|
|
type={column.componentType} |
|
|
|
props={{ |
|
|
|
field: column.name, |
|
|
|
placeholder: column.name, |
|
|
|
text: column.name, |
|
|
|
autoWidth: true, |
|
|
|
}} |
|
|
|
/> |
|
|
|
{/each} |
|
|
|
</div> |
|
|
|
{/if} |
|
|
|
</div> |
|
|
|
{/if} |
|
|
|
<BlockComponent |
|
|
|
type="table" |
|
|
|
type="dataprovider" |
|
|
|
bind:id={dataProviderId} |
|
|
|
props={{ |
|
|
|
dataProvider: `{{ literal [${dataProviderId}] }}`, |
|
|
|
columns: tableColumns, |
|
|
|
showAutoColumns, |
|
|
|
rowCount, |
|
|
|
quiet, |
|
|
|
size, |
|
|
|
dataSource, |
|
|
|
filter: enrichedFilter, |
|
|
|
sortColumn, |
|
|
|
sortOrder, |
|
|
|
paginate, |
|
|
|
limit: rowCount, |
|
|
|
}} |
|
|
|
/> |
|
|
|
> |
|
|
|
<BlockComponent |
|
|
|
type="table" |
|
|
|
props={{ |
|
|
|
dataProvider: `{{ literal [${dataProviderId}] }}`, |
|
|
|
columns: tableColumns, |
|
|
|
showAutoColumns, |
|
|
|
rowCount, |
|
|
|
quiet, |
|
|
|
size, |
|
|
|
}} |
|
|
|
/> |
|
|
|
</BlockComponent> |
|
|
|
</BlockComponent> |
|
|
|
</BlockComponent> |
|
|
|
</div> |
|
|
|
</Block> |
|
|
|
|
|
|
|
<style> |
|
|
|
.search { |
|
|
|
.header { |
|
|
|
display: flex; |
|
|
|
flex-direction: row; |
|
|
|
justify-content: flex-end; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
gap: 20px; |
|
|
|
flex-wrap: wrap; |
|
|
|
margin-bottom: 20px; |
|
|
|
gap: 10px; |
|
|
|
} |
|
|
|
.title { |
|
|
|
flex: 1 1 auto; |
|
|
|
} |
|
|
|
.search { |
|
|
|
flex: 0 0 auto; |
|
|
|
display: flex; |
|
|
|
flex-direction: row; |
|
|
|
justify-content: flex-start; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
max-width: 100%; |
|
|
|
flex-wrap: wrap; |
|
|
|
} |
|
|
|
.search :global(.component > *) { |
|
|
|
width: 200px; |
|
|
|
} |
|
|
|
.search :global(.spectrum-InputGroup .spectrum-InputGroup-input) { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
.search :global(.spectrum-InputGroup) { |
|
|
|
min-width: 0; |
|
|
|
} |
|
|
|
.search.mobile { |
|
|
|
flex-direction: column; |
|
|
|
justify-content: flex-start; |
|
|
|
align-items: stretch; |
|
|
|
position: relative; |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
.search.mobile :global(.component > *) { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|