forked from tsai/budibase
committed by
GitHub
28 changed files with 1771 additions and 162 deletions
@ -0,0 +1,75 @@ |
|||||
|
import { FIELDS } from "constants/backend" |
||||
|
import { tables } from "stores/backend" |
||||
|
import { get as svelteGet } from "svelte/store" |
||||
|
|
||||
|
// currently supported level of relationship depth (server side)
|
||||
|
const MAX_DEPTH = 1 |
||||
|
const TYPES_TO_SKIP = [ |
||||
|
FIELDS.FORMULA.type, |
||||
|
FIELDS.LONGFORM.type, |
||||
|
FIELDS.ATTACHMENT.type, |
||||
|
] |
||||
|
|
||||
|
export function getBindings({ |
||||
|
table, |
||||
|
path = null, |
||||
|
category = null, |
||||
|
depth = 0, |
||||
|
}) { |
||||
|
let bindings = [] |
||||
|
if (!table) { |
||||
|
return bindings |
||||
|
} |
||||
|
for (let [column, schema] of Object.entries(table.schema)) { |
||||
|
const isRelationship = schema.type === FIELDS.LINK.type |
||||
|
// skip relationships after a certain depth and types which
|
||||
|
// can't bind to
|
||||
|
if ( |
||||
|
TYPES_TO_SKIP.includes(schema.type) || |
||||
|
(isRelationship && depth >= MAX_DEPTH) |
||||
|
) { |
||||
|
continue |
||||
|
} |
||||
|
category = category == null ? `${table.name} Fields` : category |
||||
|
if (isRelationship && depth < MAX_DEPTH) { |
||||
|
const relatedTable = svelteGet(tables).list.find( |
||||
|
table => table._id === schema.tableId |
||||
|
) |
||||
|
const relatedBindings = bindings.concat( |
||||
|
getBindings({ |
||||
|
table: relatedTable, |
||||
|
path: column, |
||||
|
category: `${column} Relationships`, |
||||
|
depth: depth + 1, |
||||
|
}) |
||||
|
) |
||||
|
// remove the ones that have already been found
|
||||
|
bindings = bindings.concat( |
||||
|
relatedBindings.filter( |
||||
|
related => !bindings.find(binding => binding.path === related.path) |
||||
|
) |
||||
|
) |
||||
|
} |
||||
|
const field = Object.values(FIELDS).find( |
||||
|
field => field.type === schema.type |
||||
|
) |
||||
|
const label = path == null ? column : `${path}.0.${column}` |
||||
|
// only supply a description for relationship paths
|
||||
|
const description = |
||||
|
path == null |
||||
|
? undefined |
||||
|
: `Update the "0" with the index of relationships or use array helpers` |
||||
|
bindings.push({ |
||||
|
label: label, |
||||
|
type: field.name === FIELDS.LINK.name ? "Array" : field.name, |
||||
|
category, |
||||
|
path: label, |
||||
|
description, |
||||
|
// don't include path, it messes things up, relationship path
|
||||
|
// will be replaced by the main array binding
|
||||
|
readableBinding: column, |
||||
|
runtimeBinding: `[${column}]`, |
||||
|
}) |
||||
|
} |
||||
|
return bindings |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
<script> |
||||
|
import { Icon, Input, Modal, Body, ModalContent } from "@budibase/bbui" |
||||
|
import { |
||||
|
readableToRuntimeBinding, |
||||
|
runtimeToReadableBinding, |
||||
|
} from "builderStore/dataBinding" |
||||
|
import ServerBindingPanel from "components/common/bindings/ServerBindingPanel.svelte" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
export let panel = ServerBindingPanel |
||||
|
export let value = "" |
||||
|
export let bindings = [] |
||||
|
export let thin = true |
||||
|
export let title = "Bindings" |
||||
|
export let placeholder |
||||
|
export let label |
||||
|
|
||||
|
let bindingModal |
||||
|
let validity = true |
||||
|
|
||||
|
$: readableValue = runtimeToReadableBinding(bindings, value) |
||||
|
$: tempValue = readableValue |
||||
|
$: invalid = !validity |
||||
|
|
||||
|
const saveBinding = () => { |
||||
|
onChange(tempValue) |
||||
|
bindingModal.hide() |
||||
|
} |
||||
|
|
||||
|
const onChange = input => { |
||||
|
dispatch("change", readableToRuntimeBinding(bindings, input)) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="control"> |
||||
|
<Input |
||||
|
{label} |
||||
|
{thin} |
||||
|
value={readableValue} |
||||
|
on:change={event => onChange(event.target.value)} |
||||
|
{placeholder} |
||||
|
/> |
||||
|
<div class="icon" on:click={bindingModal.show}> |
||||
|
<Icon size="S" name="FlashOn" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<Modal bind:this={bindingModal}> |
||||
|
<ModalContent |
||||
|
{title} |
||||
|
onConfirm={saveBinding} |
||||
|
bind:disabled={invalid} |
||||
|
size="XL" |
||||
|
> |
||||
|
<Body extraSmall grey> |
||||
|
Add the objects on the left to enrich your text. |
||||
|
</Body> |
||||
|
<svelte:component |
||||
|
this={panel} |
||||
|
serverSide |
||||
|
value={readableValue} |
||||
|
bind:validity |
||||
|
on:update={event => (tempValue = event.detail)} |
||||
|
bindableProperties={bindings} |
||||
|
/> |
||||
|
</ModalContent> |
||||
|
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.control { |
||||
|
flex: 1; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
right: 1px; |
||||
|
bottom: 1px; |
||||
|
position: absolute; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
box-sizing: border-box; |
||||
|
border-left: 1px solid var(--spectrum-alias-border-color); |
||||
|
border-top-right-radius: var(--spectrum-alias-border-radius-regular); |
||||
|
border-bottom-right-radius: var(--spectrum-alias-border-radius-regular); |
||||
|
width: 31px; |
||||
|
color: var(--spectrum-alias-text-color); |
||||
|
background-color: var(--spectrum-global-color-gray-75); |
||||
|
transition: background-color |
||||
|
var(--spectrum-global-animation-duration-100, 130ms), |
||||
|
box-shadow var(--spectrum-global-animation-duration-100, 130ms), |
||||
|
border-color var(--spectrum-global-animation-duration-100, 130ms); |
||||
|
height: calc(var(--spectrum-alias-item-height-m) - 2px); |
||||
|
} |
||||
|
|
||||
|
.icon:hover { |
||||
|
cursor: pointer; |
||||
|
color: var(--spectrum-alias-text-color-hover); |
||||
|
background-color: var(--spectrum-global-color-gray-50); |
||||
|
border-color: var(--spectrum-alias-border-color-hover); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,16 @@ |
|||||
|
export function addToText(value, caretPos, binding) { |
||||
|
binding = typeof binding === "string" ? binding : binding.path |
||||
|
value = value == null ? "" : value |
||||
|
if (!value.includes("{{") && !value.includes("}}")) { |
||||
|
binding = `{{ ${binding} }}` |
||||
|
} |
||||
|
if (caretPos.start) { |
||||
|
value = |
||||
|
value.substring(0, caretPos.start) + |
||||
|
binding + |
||||
|
value.substring(caretPos.end, value.length) |
||||
|
} else { |
||||
|
value += binding |
||||
|
} |
||||
|
return value |
||||
|
} |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue