Merge pull request #12939 from Budibase/labday/small-ux-improvement-mike-d
A small UX improvement to the automation scripting system
This commit is contained in:
commit
10159e1852
|
@ -15,7 +15,6 @@
|
||||||
Icon,
|
Icon,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
Detail,
|
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
||||||
import { automationStore, selectedAutomation } from "builderStore"
|
import { automationStore, selectedAutomation } from "builderStore"
|
||||||
|
@ -33,6 +32,8 @@
|
||||||
import Editor from "components/integration/QueryEditor.svelte"
|
import Editor from "components/integration/QueryEditor.svelte"
|
||||||
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
||||||
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
||||||
|
import BindingPicker from "components/common/bindings/BindingPicker.svelte"
|
||||||
|
import { BindingHelpers } from "components/common/bindings/utils"
|
||||||
import {
|
import {
|
||||||
bindingsToCompletions,
|
bindingsToCompletions,
|
||||||
hbAutocomplete,
|
hbAutocomplete,
|
||||||
|
@ -56,7 +57,7 @@
|
||||||
let drawer
|
let drawer
|
||||||
let fillWidth = true
|
let fillWidth = true
|
||||||
let inputData
|
let inputData
|
||||||
let codeBindingOpen = false
|
let insertAtPos, getCaretPosition
|
||||||
$: filters = lookForFilters(schemaProperties) || []
|
$: filters = lookForFilters(schemaProperties) || []
|
||||||
$: tempFilters = filters
|
$: tempFilters = filters
|
||||||
$: stepId = block.stepId
|
$: stepId = block.stepId
|
||||||
|
@ -75,6 +76,10 @@
|
||||||
$: isUpdateRow = stepId === ActionStepID.UPDATE_ROW
|
$: isUpdateRow = stepId === ActionStepID.UPDATE_ROW
|
||||||
$: codeMode =
|
$: codeMode =
|
||||||
stepId === "EXECUTE_BASH" ? EditorModes.Handlebars : EditorModes.JS
|
stepId === "EXECUTE_BASH" ? EditorModes.Handlebars : EditorModes.JS
|
||||||
|
$: bindingsHelpers = new BindingHelpers(getCaretPosition, insertAtPos, {
|
||||||
|
disableWrapping: true,
|
||||||
|
})
|
||||||
|
$: editingJs = codeMode === EditorModes.JS
|
||||||
|
|
||||||
$: stepCompletions =
|
$: stepCompletions =
|
||||||
codeMode === EditorModes.Handlebars
|
codeMode === EditorModes.Handlebars
|
||||||
|
@ -539,18 +544,8 @@
|
||||||
/>
|
/>
|
||||||
{:else if value.customType === "code"}
|
{:else if value.customType === "code"}
|
||||||
<CodeEditorModal>
|
<CodeEditorModal>
|
||||||
{#if codeMode == EditorModes.JS}
|
<div class:js-editor={editingJs}>
|
||||||
<ActionButton
|
<div class:js-code={editingJs} style="width: 100%">
|
||||||
on:click={() => (codeBindingOpen = !codeBindingOpen)}
|
|
||||||
quiet
|
|
||||||
icon={codeBindingOpen ? "ChevronDown" : "ChevronRight"}
|
|
||||||
>
|
|
||||||
<Detail size="S">Bindings</Detail>
|
|
||||||
</ActionButton>
|
|
||||||
{#if codeBindingOpen}
|
|
||||||
<pre>{JSON.stringify(bindings, null, 2)}</pre>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
value={inputData[key]}
|
value={inputData[key]}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
|
@ -560,11 +555,13 @@
|
||||||
}}
|
}}
|
||||||
completions={stepCompletions}
|
completions={stepCompletions}
|
||||||
mode={codeMode}
|
mode={codeMode}
|
||||||
autocompleteEnabled={codeMode != EditorModes.JS}
|
autocompleteEnabled={codeMode !== EditorModes.JS}
|
||||||
|
bind:getCaretPosition
|
||||||
|
bind:insertAtPos
|
||||||
height={500}
|
height={500}
|
||||||
/>
|
/>
|
||||||
<div class="messaging">
|
<div class="messaging">
|
||||||
{#if codeMode == EditorModes.Handlebars}
|
{#if codeMode === EditorModes.Handlebars}
|
||||||
<Icon name="FlashOn" />
|
<Icon name="FlashOn" />
|
||||||
<div class="messaging-wrap">
|
<div class="messaging-wrap">
|
||||||
<div>
|
<div>
|
||||||
|
@ -575,6 +572,26 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{#if editingJs}
|
||||||
|
<div class="js-binding-picker">
|
||||||
|
<BindingPicker
|
||||||
|
{bindings}
|
||||||
|
allowHelpers={false}
|
||||||
|
addBinding={binding =>
|
||||||
|
bindingsHelpers.onSelectBinding(
|
||||||
|
inputData[key],
|
||||||
|
binding,
|
||||||
|
{
|
||||||
|
js: true,
|
||||||
|
dontDecode: true,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
mode="javascript"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</CodeEditorModal>
|
</CodeEditorModal>
|
||||||
{:else if value.customType === "loopOption"}
|
{:else if value.customType === "loopOption"}
|
||||||
<Select
|
<Select
|
||||||
|
@ -658,4 +675,20 @@
|
||||||
.test :global(.drawer) {
|
.test :global(.drawer) {
|
||||||
width: 10000px !important;
|
width: 10000px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.js-editor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-code {
|
||||||
|
flex: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-binding-picker {
|
||||||
|
flex: 3;
|
||||||
|
margin-top: calc((var(--spacing-xl) * -1) + 1px);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
export let autocompleteEnabled = true
|
export let autocompleteEnabled = true
|
||||||
export let autofocus = false
|
export let autofocus = false
|
||||||
|
export let jsBindingWrapping = true
|
||||||
|
|
||||||
// Export a function to expose caret position
|
// Export a function to expose caret position
|
||||||
export const getCaretPosition = () => {
|
export const getCaretPosition = () => {
|
||||||
|
@ -187,7 +188,7 @@
|
||||||
)
|
)
|
||||||
complete.push(
|
complete.push(
|
||||||
EditorView.inputHandler.of((view, from, to, insert) => {
|
EditorView.inputHandler.of((view, from, to, insert) => {
|
||||||
if (insert === "$") {
|
if (jsBindingWrapping && insert === "$") {
|
||||||
let { text } = view.state.doc.lineAt(from)
|
let { text } = view.state.doc.lineAt(from)
|
||||||
|
|
||||||
const left = from ? text.substring(0, from) : ""
|
const left = from ? text.substring(0, from) : ""
|
||||||
|
|
|
@ -286,13 +286,14 @@ export const hbInsert = (value, from, to, text) => {
|
||||||
return parsedInsert
|
return parsedInsert
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jsInsert(value, from, to, text, { helper } = {}) {
|
export function jsInsert(value, from, to, text, { helper, disableWrapping }) {
|
||||||
let parsedInsert = ""
|
let parsedInsert = ""
|
||||||
|
|
||||||
const left = from ? value.substring(0, from) : ""
|
const left = from ? value.substring(0, from) : ""
|
||||||
const right = to ? value.substring(to) : ""
|
const right = to ? value.substring(to) : ""
|
||||||
|
if (disableWrapping) {
|
||||||
if (helper) {
|
parsedInsert = text
|
||||||
|
} else if (helper) {
|
||||||
parsedInsert = `helpers.${text}()`
|
parsedInsert = `helpers.${text}()`
|
||||||
} else if (!left.includes('$("') || !right.includes('")')) {
|
} else if (!left.includes('$("') || !right.includes('")')) {
|
||||||
parsedInsert = `$("${text}")`
|
parsedInsert = `$("${text}")`
|
||||||
|
|
|
@ -29,10 +29,9 @@
|
||||||
hbAutocomplete,
|
hbAutocomplete,
|
||||||
EditorModes,
|
EditorModes,
|
||||||
bindingsToCompletions,
|
bindingsToCompletions,
|
||||||
hbInsert,
|
|
||||||
jsInsert,
|
|
||||||
} from "../CodeEditor"
|
} from "../CodeEditor"
|
||||||
import BindingPicker from "./BindingPicker.svelte"
|
import BindingPicker from "./BindingPicker.svelte"
|
||||||
|
import { BindingHelpers } from "./utils"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -60,8 +59,10 @@
|
||||||
let targetMode = null
|
let targetMode = null
|
||||||
|
|
||||||
$: 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)
|
||||||
|
$: bindingHelpers = new BindingHelpers(getCaretPosition, insertAtPos)
|
||||||
|
|
||||||
const updateValue = val => {
|
const updateValue = val => {
|
||||||
valid = isValid(readableToRuntimeBinding(bindings, val))
|
valid = isValid(readableToRuntimeBinding(bindings, val))
|
||||||
|
@ -70,31 +71,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a JS/HBS helper to the expression
|
|
||||||
const onSelectHelper = (helper, js) => {
|
const onSelectHelper = (helper, js) => {
|
||||||
const pos = getCaretPosition()
|
bindingHelpers.onSelectHelper(js ? jsValue : hbsValue, helper, { js })
|
||||||
const { start, end } = pos
|
|
||||||
if (js) {
|
|
||||||
let js = decodeJSBinding(jsValue)
|
|
||||||
const insertVal = jsInsert(js, start, end, helper.text, { helper: true })
|
|
||||||
insertAtPos({ start, end, value: insertVal })
|
|
||||||
} else {
|
|
||||||
const insertVal = hbInsert(hbsValue, start, end, helper.text)
|
|
||||||
insertAtPos({ start, end, value: insertVal })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a data binding to the expression
|
|
||||||
const onSelectBinding = (binding, { forceJS } = {}) => {
|
const onSelectBinding = (binding, { forceJS } = {}) => {
|
||||||
const { start, end } = getCaretPosition()
|
const js = usingJS || forceJS
|
||||||
if (usingJS || forceJS) {
|
bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, { js })
|
||||||
let js = decodeJSBinding(jsValue)
|
|
||||||
const insertVal = jsInsert(js, start, end, binding.readableBinding)
|
|
||||||
insertAtPos({ start, end, value: insertVal })
|
|
||||||
} else {
|
|
||||||
const insertVal = hbInsert(hbsValue, start, end, binding.readableBinding)
|
|
||||||
insertAtPos({ start, end, value: insertVal })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeMode = e => {
|
const onChangeMode = e => {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
export let bindings
|
export let bindings
|
||||||
export let mode
|
export let mode
|
||||||
export let allowHelpers
|
export let allowHelpers
|
||||||
|
export let noPaddingTop = false
|
||||||
|
|
||||||
let search = ""
|
let search = ""
|
||||||
let popover
|
let popover
|
||||||
|
|
|
@ -1,38 +1,41 @@
|
||||||
export function addHBSBinding(value, caretPos, binding) {
|
import { decodeJSBinding } from "@budibase/string-templates"
|
||||||
binding = typeof binding === "string" ? binding : binding.path
|
import { hbInsert, jsInsert } from "components/common/CodeEditor"
|
||||||
value = value == null ? "" : value
|
|
||||||
|
|
||||||
const left = caretPos?.start ? value.substring(0, caretPos.start) : ""
|
export class BindingHelpers {
|
||||||
const right = caretPos?.end ? value.substring(caretPos.end) : ""
|
constructor(getCaretPosition, insertAtPos, { disableWrapping } = {}) {
|
||||||
if (!left.includes("{{") || !right.includes("}}")) {
|
this.getCaretPosition = getCaretPosition
|
||||||
binding = `{{ ${binding} }}`
|
this.insertAtPos = insertAtPos
|
||||||
}
|
this.disableWrapping = disableWrapping
|
||||||
if (caretPos.start) {
|
|
||||||
value =
|
|
||||||
value.substring(0, caretPos.start) +
|
|
||||||
binding +
|
|
||||||
value.substring(caretPos.end, value.length)
|
|
||||||
} else {
|
|
||||||
value += binding
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addJSBinding(value, caretPos, binding, { helper } = {}) {
|
// Adds a JS/HBS helper to the expression
|
||||||
binding = typeof binding === "string" ? binding : binding.path
|
onSelectHelper(value, helper, { js, dontDecode }) {
|
||||||
value = value == null ? "" : value
|
const pos = this.getCaretPosition()
|
||||||
if (!helper) {
|
const { start, end } = pos
|
||||||
binding = `$("${binding}")`
|
if (js) {
|
||||||
|
const jsVal = dontDecode ? value : decodeJSBinding(value)
|
||||||
|
const insertVal = jsInsert(jsVal, start, end, helper.text, {
|
||||||
|
helper: true,
|
||||||
|
})
|
||||||
|
this.insertAtPos({ start, end, value: insertVal })
|
||||||
} else {
|
} else {
|
||||||
binding = `helpers.${binding}()`
|
const insertVal = hbInsert(value, start, end, helper.text)
|
||||||
|
this.insertAtPos({ start, end, value: insertVal })
|
||||||
}
|
}
|
||||||
if (caretPos.start) {
|
}
|
||||||
value =
|
|
||||||
value.substring(0, caretPos.start) +
|
// Adds a data binding to the expression
|
||||||
binding +
|
onSelectBinding(value, binding, { js, dontDecode }) {
|
||||||
value.substring(caretPos.end, value.length)
|
const { start, end } = this.getCaretPosition()
|
||||||
|
if (js) {
|
||||||
|
const jsVal = dontDecode ? value : decodeJSBinding(value)
|
||||||
|
const insertVal = jsInsert(jsVal, start, end, binding.readableBinding, {
|
||||||
|
disableWrapping: this.disableWrapping,
|
||||||
|
})
|
||||||
|
this.insertAtPos({ start, end, value: insertVal })
|
||||||
} else {
|
} else {
|
||||||
value += binding
|
const insertVal = hbInsert(value, start, end, binding.readableBinding)
|
||||||
|
this.insertAtPos({ start, end, value: insertVal })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue