Merge branch 'master' into execute-script-v2

This commit is contained in:
deanhannigan 2025-03-03 12:39:52 +00:00 committed by GitHub
commit f37ffb0930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 104 additions and 62 deletions

View File

@ -94,6 +94,15 @@ export default [
allowImportExportEverywhere: true, allowImportExportEverywhere: true,
}, },
}, },
plugins: {
...config.plugins,
"@typescript-eslint": tseslint.plugin,
},
rules: {
...config.rules,
"@typescript-eslint/consistent-type-imports": "error",
},
})), })),
...tseslint.configs.strict.map(config => ({ ...tseslint.configs.strict.map(config => ({
...config, ...config,

View File

@ -27,7 +27,7 @@ export type UpdateHandler = (
interface Opts { interface Opts {
anchor?: HTMLElement anchor?: HTMLElement
align: PopoverAlignment align: PopoverAlignment | `${PopoverAlignment}`
maxHeight?: number maxHeight?: number
maxWidth?: number maxWidth?: number
minWidth?: number minWidth?: number

View File

@ -19,7 +19,8 @@
import { PopoverAlignment } from "../constants" import { PopoverAlignment } from "../constants"
export let anchor: HTMLElement export let anchor: HTMLElement
export let align: PopoverAlignment = PopoverAlignment.Right export let align: PopoverAlignment | `${PopoverAlignment}` =
PopoverAlignment.Right
export let portalTarget: string | undefined = undefined export let portalTarget: string | undefined = undefined
export let minWidth: number | undefined = undefined export let minWidth: number | undefined = undefined
export let maxWidth: number | undefined = undefined export let maxWidth: number | undefined = undefined

View File

@ -422,7 +422,7 @@
{context} {context}
addHelper={onSelectHelper} addHelper={onSelectHelper}
addBinding={onSelectBinding} addBinding={onSelectBinding}
mode={editorMode} {mode}
/> />
{:else if sidePanel === SidePanel.Evaluation} {:else if sidePanel === SidePanel.Evaluation}
<EvaluationSidePanel <EvaluationSidePanel

View File

@ -1,32 +1,41 @@
<script> <script lang="ts">
import groupBy from "lodash/fp/groupBy" import groupBy from "lodash/fp/groupBy"
import { convertToJS } from "@budibase/string-templates" import { convertToJS } from "@budibase/string-templates"
import { Input, Layout, Icon, Popover } from "@budibase/bbui" import { Input, Layout, Icon, Popover } from "@budibase/bbui"
import { handlebarsCompletions } from "@/constants/completions" import { handlebarsCompletions } from "@/constants/completions"
import type { EnrichedBinding, Helper } from "@budibase/types"
import { BindingMode } from "@budibase/types"
export let addHelper export let addHelper: (_helper: Helper, _js?: boolean) => void
export let addBinding export let addBinding: (_binding: EnrichedBinding) => void
export let bindings export let bindings: EnrichedBinding[]
export let mode export let mode: BindingMode
export let allowHelpers export let allowHelpers: boolean
export let context = null export let context = null
let search = "" let search = ""
let searching = false let searching = false
let popover let popover: Popover
let popoverAnchor let popoverAnchor: HTMLElement | null
let hoverTarget let hoverTarget: {
helper: boolean
code: string
description?: string
} | null
let helpers = handlebarsCompletions() let helpers = handlebarsCompletions()
let selectedCategory let selectedCategory: string | null
let hideTimeout let hideTimeout: ReturnType<typeof setTimeout> | null
$: bindingIcons = bindings?.reduce((acc, ele) => { $: bindingIcons = bindings?.reduce<Record<string, string>>((acc, ele) => {
if (ele.icon) { if (ele.icon) {
acc[ele.category] = acc[ele.category] || ele.icon acc[ele.category] = acc[ele.category] || ele.icon
} }
return acc return acc
}, {}) }, {})
$: categoryIcons = { ...bindingIcons, Helpers: "MagicWand" } $: categoryIcons = {
...bindingIcons,
Helpers: "MagicWand",
} as Record<string, string>
$: categories = Object.entries(groupBy("category", bindings)) $: categories = Object.entries(groupBy("category", bindings))
$: categoryNames = getCategoryNames(categories) $: categoryNames = getCategoryNames(categories)
$: searchRgx = new RegExp(search, "ig") $: searchRgx = new RegExp(search, "ig")
@ -48,11 +57,11 @@
(!search || (!search ||
helper.label.match(searchRgx) || helper.label.match(searchRgx) ||
helper.description.match(searchRgx)) && helper.description.match(searchRgx)) &&
(mode.name !== "javascript" || helper.allowsJs) (mode !== BindingMode.JavaScript || helper.allowsJs)
) )
}) })
const getHelperExample = (helper, js) => { const getHelperExample = (helper: Helper, js: boolean) => {
let example = helper.example || "" let example = helper.example || ""
if (js) { if (js) {
example = convertToJS(example).split("\n")[0].split("= ")[1] example = convertToJS(example).split("\n")[0].split("= ")[1]
@ -63,15 +72,18 @@
return example || "" return example || ""
} }
const getCategoryNames = categories => { const getCategoryNames = (categories: [string, EnrichedBinding[]][]) => {
let names = [...categories.map(cat => cat[0])] const names = [...categories.map(cat => cat[0])]
if (allowHelpers) { if (allowHelpers) {
names.push("Helpers") names.push("Helpers")
} }
return names return names
} }
const showBindingPopover = (binding, target) => { const showBindingPopover = (
binding: EnrichedBinding,
target: HTMLElement
) => {
if (!context || !binding.value || binding.value === "") { if (!context || !binding.value || binding.value === "") {
return return
} }
@ -84,7 +96,7 @@
popover.show() popover.show()
} }
const showHelperPopover = (helper, target) => { const showHelperPopover = (helper: any, target: HTMLElement) => {
stopHidingPopover() stopHidingPopover()
if (!helper.displayText && helper.description) { if (!helper.displayText && helper.description) {
return return
@ -93,7 +105,7 @@
hoverTarget = { hoverTarget = {
helper: true, helper: true,
description: helper.description, description: helper.description,
code: getHelperExample(helper, mode.name === "javascript"), code: getHelperExample(helper, mode === BindingMode.JavaScript),
} }
popover.show() popover.show()
} }
@ -119,37 +131,39 @@
search = "" search = ""
} }
const stopSearching = e => { const stopSearching = (e: Event) => {
e.stopPropagation() e.stopPropagation()
searching = false searching = false
search = "" search = ""
} }
</script> </script>
<Popover {#if popoverAnchor && hoverTarget}
align="left-outside" <Popover
bind:this={popover} align="left-outside"
anchor={popoverAnchor} bind:this={popover}
minWidth={0} anchor={popoverAnchor}
maxWidth={480} minWidth={0}
maxHeight={480} maxWidth={480}
dismissible={false} maxHeight={480}
on:mouseenter={stopHidingPopover} dismissible={false}
on:mouseleave={hidePopover} on:mouseenter={stopHidingPopover}
> on:mouseleave={hidePopover}
<div class="binding-popover" class:helper={hoverTarget.helper}> >
{#if hoverTarget.description} <div class="binding-popover" class:helper={hoverTarget.helper}>
<div> {#if hoverTarget.description}
<div>
<!-- eslint-disable-next-line svelte/no-at-html-tags-->
{@html hoverTarget.description}
</div>
{/if}
{#if hoverTarget.code}
<!-- eslint-disable-next-line svelte/no-at-html-tags--> <!-- eslint-disable-next-line svelte/no-at-html-tags-->
{@html hoverTarget.description} <pre>{@html hoverTarget.code}</pre>
</div> {/if}
{/if} </div>
{#if hoverTarget.code} </Popover>
<!-- eslint-disable-next-line svelte/no-at-html-tags--> {/if}
<pre>{@html hoverTarget.code}</pre>
{/if}
</div>
</Popover>
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions --> <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
@ -173,7 +187,7 @@
<div class="search-input"> <div class="search-input">
<Input <Input
placeholder="Search for bindings" placeholder="Search for bindings"
autocomplete="off" autocomplete={false}
bind:value={search} bind:value={search}
autofocus autofocus
/> />
@ -230,7 +244,8 @@
{#each category.bindings as binding} {#each category.bindings as binding}
<li <li
class="binding" class="binding"
on:mouseenter={e => showBindingPopover(binding, e.target)} on:mouseenter={e =>
showBindingPopover(binding, e.currentTarget)}
on:mouseleave={hidePopover} on:mouseleave={hidePopover}
on:click={() => addBinding(binding)} on:click={() => addBinding(binding)}
> >
@ -264,9 +279,11 @@
{#each filteredHelpers as helper} {#each filteredHelpers as helper}
<li <li
class="binding" class="binding"
on:mouseenter={e => showHelperPopover(helper, e.target)} on:mouseenter={e =>
showHelperPopover(helper, e.currentTarget)}
on:mouseleave={hidePopover} on:mouseleave={hidePopover}
on:click={() => addHelper(helper, mode.name === "javascript")} on:click={() =>
addHelper(helper, mode === BindingMode.JavaScript)}
> >
<span class="binding__label">{helper.displayText}</span> <span class="binding__label">{helper.displayText}</span>
<span class="binding__typeWrap"> <span class="binding__typeWrap">

View File

@ -1,10 +1,10 @@
import { getManifest, helpersToRemoveForJs } from "@budibase/string-templates" import { getManifest, helpersToRemoveForJs } from "@budibase/string-templates"
import { Helper } from "@budibase/types"
export function handlebarsCompletions() { export function handlebarsCompletions(): Helper[] {
const manifest = getManifest() const manifest = getManifest()
return Object.values(manifest).flatMap(helpersObj =>
return Object.keys(manifest).flatMap(key => Object.entries(helpersObj).map<Helper>(([helperName, helperConfig]) => ({
Object.entries(manifest[key]).map(([helperName, helperConfig]) => ({
text: helperName, text: helperName,
path: helperName, path: helperName,
example: helperConfig.example, example: helperConfig.example,
@ -14,6 +14,7 @@ export function handlebarsCompletions() {
allowsJs: allowsJs:
!helperConfig.requiresBlock && !helperConfig.requiresBlock &&
!helpersToRemoveForJs.includes(helperName), !helpersToRemoveForJs.includes(helperName),
args: helperConfig.args,
})) }))
) )
} }

View File

@ -2,9 +2,7 @@
import { getContext } from "svelte" import { getContext } from "svelte"
import { Pagination, ProgressCircle } from "@budibase/bbui" import { Pagination, ProgressCircle } from "@budibase/bbui"
import { fetchData, QueryUtils } from "@budibase/frontend-core" import { fetchData, QueryUtils } from "@budibase/frontend-core"
import { import type {
LogicalOperator,
EmptyFilterOption,
TableSchema, TableSchema,
SortOrder, SortOrder,
SearchFilters, SearchFilters,
@ -14,6 +12,7 @@
GroupUserDatasource, GroupUserDatasource,
DataFetchOptions, DataFetchOptions,
} from "@budibase/types" } from "@budibase/types"
import { LogicalOperator, EmptyFilterOption } from "@budibase/types"
type ProviderDatasource = Exclude< type ProviderDatasource = Exclude<
DataFetchDatasource, DataFetchDatasource,

View File

@ -4,7 +4,7 @@
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
import FormBlockWrapper from "./FormBlockWrapper.svelte" import FormBlockWrapper from "./FormBlockWrapper.svelte"
import { get } from "svelte/store" import { get } from "svelte/store"
import { TableSchema, UIDatasource } from "@budibase/types" import type { TableSchema, UIDatasource } from "@budibase/types"
type Field = { name: string; active: boolean } type Field = { name: string; active: boolean }

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { getContext } from "svelte" import { getContext } from "svelte"
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { UIComponentError } from "@budibase/types" import type { UIComponentError } from "@budibase/types"
import ComponentErrorStateCta from "./ComponentErrorStateCTA.svelte" import ComponentErrorStateCta from "./ComponentErrorStateCTA.svelte"
export let componentErrors: UIComponentError[] | undefined export let componentErrors: UIComponentError[] | undefined

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { getContext } from "svelte" import { getContext } from "svelte"
import { UIComponentError } from "@budibase/types" import type { UIComponentError } from "@budibase/types"
export let error: UIComponentError | undefined export let error: UIComponentError | undefined

View File

@ -6,7 +6,7 @@
import { findComponentById } from "@/utils/components.js" import { findComponentById } from "@/utils/components.js"
import { isGridEvent } from "@/utils/grid" import { isGridEvent } from "@/utils/grid"
import { DNDPlaceholderID } from "@/constants" import { DNDPlaceholderID } from "@/constants"
import { Component } from "@budibase/types" import type { Component } from "@budibase/types"
type ChildCoords = { type ChildCoords = {
placeholder: boolean placeholder: boolean

View File

@ -1,7 +1,19 @@
export interface EnrichedBinding { export interface EnrichedBinding {
value: string
valueHTML: string
runtimeBinding: string runtimeBinding: string
readableBinding: string readableBinding: string
type?: null | string type?: null | string
icon?: string
category: string
display?: { name: string; type: string }
fieldSchema?: {
name: string
tableId: string
type: string
subtype?: string
prefixKeys?: string
}
} }
export enum BindingMode { export enum BindingMode {

View File

@ -1,6 +1,9 @@
export interface Helper { export interface Helper {
label: string
displayText: string
example: string example: string
description: string description: string
args: any[] args: any[]
requiresBlock?: boolean requiresBlock?: boolean
allowsJs: boolean
} }