1
0
Fork 0
mirror of synced 2024-07-29 18:15:49 +12:00

Add live eval of bindings

This commit is contained in:
Andrew Kingston 2024-02-02 10:27:29 +00:00
parent a880c5e62a
commit daec133f79
3 changed files with 79 additions and 51 deletions

View file

@ -15,6 +15,7 @@
decodeJSBinding, decodeJSBinding,
encodeJSBinding, encodeJSBinding,
convertToJS, convertToJS,
processStringSync,
} from "@budibase/string-templates" } from "@budibase/string-templates"
import { import {
readableToRuntimeBinding, readableToRuntimeBinding,
@ -59,15 +60,21 @@
let hbsValue = initialValueJS ? null : value let hbsValue = initialValueJS ? null : value
let sidebar = true let sidebar = true
let targetMode = null let targetMode = null
let expressionResult
$: usingJS = mode === "JavaScript" $: usingJS = mode === "JavaScript"
$: editorMode = mode == "JavaScript" ? EditorModes.JS : EditorModes.Handlebars $: editorMode =
mode === "JavaScript" ? EditorModes.JS : EditorModes.Handlebars
$: bindingCompletions = bindingsToCompletions(bindings, editorMode) $: bindingCompletions = bindingsToCompletions(bindings, editorMode)
$: runtimeExpression = readableToRuntimeBinding(bindings, value)
$: expressionResult = processStringSync(runtimeExpression || "", context)
const updateValue = val => { const updateValue = val => {
valid = isValid(readableToRuntimeBinding(bindings, val)) const runtimeExpression = readableToRuntimeBinding(bindings, val)
valid = isValid(runtimeExpression)
if (valid) { if (valid) {
dispatch("change", val) dispatch("change", val)
expressionResult = processStringSync(runtimeExpression || "", context)
} }
} }
@ -114,7 +121,7 @@
} }
const switchMode = () => { const switchMode = () => {
if (targetMode == "Text") { if (targetMode === "Text") {
jsValue = null jsValue = null
updateValue(jsValue) updateValue(jsValue)
} else { } else {
@ -204,6 +211,11 @@
autofocus={autofocusEditor} autofocus={autofocusEditor}
/> />
</div> </div>
{#if expressionResult}
<div class="result">
{expressionResult}
</div>
{/if}
<div class="binding-footer"> <div class="binding-footer">
<div class="messaging"> <div class="messaging">
{#if !valid} {#if !valid}
@ -308,6 +320,11 @@
autofocus={autofocusEditor} autofocus={autofocusEditor}
/> />
</div> </div>
{#if expressionResult}
<div class="result">
{expressionResult}
</div>
{/if}
<div class="binding-footer"> <div class="binding-footer">
<div class="messaging"> <div class="messaging">
<Icon name="FlashOn" /> <Icon name="FlashOn" />
@ -515,4 +532,18 @@
.binding-drawer :global(.code-editor > div) { .binding-drawer :global(.code-editor > div) {
height: 100%; height: 100%;
} }
.result {
margin: 0;
background: var(--spectrum-global-color-gray-200);
font-size: 14px;
padding: var(--spacing-l);
border-radius: var(--border-radius-s);
font-family: monospace;
border: 1px solid var(--spectrum-global-color-gray-300);
max-height: 200px;
overflow: auto;
white-space: pre;
word-wrap: anywhere;
}
</style> </style>

View file

@ -16,12 +16,8 @@
let popoverAnchor let popoverAnchor
let hoverTarget let hoverTarget
let helpers = handlebarsCompletions() let helpers = handlebarsCompletions()
let selectedCategory let selectedCategory
$: searchRgx = new RegExp(search, "ig")
// Icons
$: bindingIcons = bindings?.reduce((acc, ele) => { $: bindingIcons = bindings?.reduce((acc, ele) => {
if (ele.icon) { if (ele.icon) {
acc[ele.category] = acc[ele.category] || ele.icon acc[ele.category] = acc[ele.category] || ele.icon
@ -29,10 +25,9 @@
return acc return acc
}, {}) }, {})
$: categoryIcons = { ...bindingIcons, Helpers: "MagicWand" } $: categoryIcons = { ...bindingIcons, Helpers: "MagicWand" }
$: categories = Object.entries(groupBy("category", bindings)) $: categories = Object.entries(groupBy("category", bindings))
$: categoryNames = getCategoryNames(categories) $: categoryNames = getCategoryNames(categories)
$: searchRgx = new RegExp(search, "ig")
$: filteredCategories = categories $: filteredCategories = categories
.map(([name, categoryBindings]) => ({ .map(([name, categoryBindings]) => ({
name, name,
@ -73,38 +68,35 @@
} }
return names return names
} }
const getBindingValue = binding => {
const hbs = `{{ ${binding.runtimeBinding} }}`
return processStringSync(hbs, context)
}
</script> </script>
<span class="detailPopover"> <Popover
<Popover align="left-outside"
align="left-outside" bind:this={popover}
bind:this={popover} anchor={popoverAnchor}
anchor={popoverAnchor} maxWidth={400}
maxWidth={300} maxHeight={300}
maxHeight={300} dismissible={false}
dismissible={false} >
> <div class="helper">
<Layout gap="S"> <Layout gap="S">
<div class="helper"> {#if hoverTarget.description}
{#if hoverTarget.title} <div>
<div class="helper__name">{hoverTarget.title}</div> <!-- eslint-disable-next-line svelte/no-at-html-tags-->
{/if} {@html hoverTarget.description}
{#if hoverTarget.description} </div>
<div class="helper__description"> {/if}
<!-- eslint-disable-next-line svelte/no-at-html-tags--> {#if hoverTarget.code}
{@html hoverTarget.description} <pre>{hoverTarget.code}</pre>
</div> {/if}
{/if}
{#if hoverTarget.example}
<pre class="helper__example">{hoverTarget.example}</pre>
{/if}
{#if hoverTarget.val}
<pre>{hoverTarget.val}</pre>
{/if}
</div>
</Layout> </Layout>
</Popover> </div>
</span> </Popover>
<Layout noPadding gap="S"> <Layout noPadding gap="S">
{#if selectedCategory} {#if selectedCategory}
@ -171,14 +163,13 @@
<li <li
class="binding" class="binding"
on:mouseenter={e => { on:mouseenter={e => {
const hbs = `{{ ${binding.runtimeBinding} }}` let val = getBindingValue(binding)
const val = processStringSync(hbs, context) if (val === "") {
console.log(binding.runtimeBinding, val) val = " "
}
popoverAnchor = e.target popoverAnchor = e.target
hoverTarget = { hoverTarget = {
title: binding.display?.name || binding.fieldSchema?.name, code: val,
description: binding.description,
val,
} }
popover.show() popover.show()
e.stopPropagation() e.stopPropagation()
@ -224,19 +215,15 @@
{#each filteredHelpers as helper} {#each filteredHelpers as helper}
<li <li
class="binding" class="binding"
on:click={() => addHelper(helper, mode.name == "javascript")} on:click={() => addHelper(helper, mode.name === "javascript")}
on:mouseenter={e => { on:mouseenter={e => {
popoverAnchor = e.target popoverAnchor = e.target
if (!helper.displayText && helper.description) { if (!helper.displayText && helper.description) {
return return
} }
hoverTarget = { hoverTarget = {
title: helper.displayText,
description: helper.description, description: helper.description,
example: getHelperExample( code: getHelperExample(helper, mode.name === "javascript"),
helper,
mode.name == "javascript"
),
} }
popover.show() popover.show()
e.stopPropagation() e.stopPropagation()
@ -397,4 +384,16 @@
margin-left: 2px; margin-left: 2px;
font-weight: 600; font-weight: 600;
} }
.helper pre {
padding: 0;
margin: 0;
font-size: 12px;
white-space: pre-wrap;
word-break: break-all;
}
.helper :global(p) {
padding: 0;
margin: 0;
}
</style> </style>

View file

@ -150,8 +150,6 @@
onMount(() => { onMount(() => {
store.actions.preview.sendEvent("request-context") store.actions.preview.sendEvent("request-context")
}) })
$: console.log(context)
</script> </script>
{#each sections as section, idx (section.name)} {#each sections as section, idx (section.name)}