budibase/packages/client/src/components/app/forms/RelationshipField.svelte

153 lines
3.4 KiB
Svelte
Raw Normal View History

<script>
2023-09-21 11:39:02 +02:00
import { CoreSelect, CoreMultiselect } from "@budibase/bbui"
import { fetchData } from "@budibase/frontend-core"
import { getContext } from "svelte"
import Field from "./Field.svelte"
import { FieldTypes } from "../../../constants"
const { API } = getContext("sdk")
export let field
export let label
export let placeholder
export let disabled = false
export let validation
export let autocomplete = true
export let defaultValue
export let onChange
2023-07-18 10:36:20 +02:00
export let filter
let fieldState
let fieldApi
let fieldSchema
let tableDefinition
2023-09-22 13:04:44 +02:00
let searchTerm
2021-02-22 12:40:24 +01:00
$: multiselect = fieldSchema?.relationshipType !== "one-to-many"
$: linkedTableId = fieldSchema?.tableId
2023-07-18 10:36:20 +02:00
$: fetch = fetchData({
API,
datasource: {
type: "table",
tableId: linkedTableId,
},
options: {
filter,
limit: 100,
},
})
2023-09-21 12:29:39 +02:00
2023-07-18 10:36:20 +02:00
$: tableDefinition = $fetch.definition
2023-09-22 10:14:17 +02:00
$: selectedValue = multiselect
? flatten(fieldState?.value) ?? []
: flatten(fieldState?.value)?.[0]
2021-08-17 15:13:57 +02:00
$: component = multiselect ? CoreMultiselect : CoreSelect
$: expandedDefaultValue = expand(defaultValue)
2023-09-22 12:16:54 +02:00
$: primaryDisplay = tableDefinition?.primaryDisplay
$: initialValues =
initialValues ||
(primaryDisplay &&
fieldState?.value?.map(x => ({
_id: x._id,
[primaryDisplay]: x.primaryDisplay,
})))
2023-09-22 12:39:49 +02:00
$: allOptions = {
...(allOptions || {}),
...[...($fetch.rows || []), ...(initialValues || [])]?.reduce((p, c) => {
p[c._id] = c
return p
}, {}),
}
2023-09-22 13:04:44 +02:00
$: options = Object.values(allOptions)
2023-09-21 12:29:39 +02:00
2023-09-22 13:04:44 +02:00
$: fetchRows(searchTerm, primaryDisplay)
2023-09-22 12:16:54 +02:00
2023-09-22 13:04:44 +02:00
const fetchRows = (searchTerm, primaryDisplay) => {
const allRowsFetched =
$fetch.loaded &&
!Object.keys($fetch.query.string).length &&
!$fetch.hasNextPage
if (!allRowsFetched) {
// Don't request until we have the primary display
if (primaryDisplay) {
fetch.update({
2023-09-22 13:04:44 +02:00
query: { string: { [primaryDisplay]: searchTerm } },
})
}
2023-09-21 12:29:39 +02:00
}
}
2021-05-04 12:32:22 +02:00
const flatten = values => {
if (!values) {
return []
}
2022-08-05 15:53:41 +02:00
if (!Array.isArray(values)) {
values = [values]
}
2021-05-04 12:32:22 +02:00
return values.map(value => (typeof value === "object" ? value._id : value))
}
2021-05-04 12:32:22 +02:00
const getDisplayName = row => {
2023-09-21 12:29:39 +02:00
return row?.[primaryDisplay] || "-"
}
2021-05-04 12:32:22 +02:00
const singleHandler = e => {
handleChange(e.detail == null ? [] : [e.detail])
}
2021-05-04 12:32:22 +02:00
const multiHandler = e => {
handleChange(e.detail)
}
const expand = values => {
if (!values) {
return []
}
if (Array.isArray(values)) {
return values
}
return values.split(",").map(value => value.trim())
}
const handleChange = value => {
const changed = fieldApi.setValue(value)
if (onChange && changed) {
onChange({ value })
}
}
</script>
<Field
{label}
{field}
{disabled}
{validation}
defaultValue={expandedDefaultValue}
type={FieldTypes.LINK}
bind:fieldState
bind:fieldApi
bind:fieldSchema
>
{#if fieldState}
2023-09-21 11:39:02 +02:00
<svelte:component
this={component}
{options}
{autocomplete}
2023-09-22 11:00:56 +02:00
value={selectedValue}
2023-09-21 11:39:02 +02:00
on:change={multiselect ? multiHandler : singleHandler}
id={fieldState.fieldId}
disabled={fieldState.disabled}
error={fieldState.error}
getOptionLabel={getDisplayName}
getOptionValue={option => option._id}
{placeholder}
2023-09-21 12:29:39 +02:00
sort
2023-09-22 13:04:44 +02:00
useFetch={false}
bind:searchTerm
2023-09-21 11:39:02 +02:00
/>
{/if}
</Field>