Type BindingSidePanel

This commit is contained in:
Adria Navarro 2025-02-20 10:58:03 +01:00
parent 5fb2a6ea2c
commit 894cfba0e0
6 changed files with 79 additions and 50 deletions

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

@ -421,7 +421,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,40 @@
<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 { BindingMode, EnrichedBinding, Helper } 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 +56,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 +71,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 +95,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 +104,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 +130,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 +186,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 +243,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)}
> >
@ -266,7 +280,8 @@
class="binding" class="binding"
on:mouseenter={e => showHelperPopover(helper, e.target)} on:mouseenter={e => showHelperPopover(helper, e.target)}
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,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,4 +1,5 @@
export interface Helper { export interface Helper {
displayText: string
example: string example: string
description: string description: string
args: any[] args: any[]