diff --git a/packages/bbui/src/Actions/position_dropdown.js b/packages/bbui/src/Actions/position_dropdown.js index a25cc1bbd5..7570a39c8c 100644 --- a/packages/bbui/src/Actions/position_dropdown.js +++ b/packages/bbui/src/Actions/position_dropdown.js @@ -1,4 +1,4 @@ -export default function positionDropdown(element, { anchor, align }) { +export default function positionDropdown(element, { anchor, align, maxWidth }) { let positionSide = "top" let maxHeight = 0 let dimensions = getDimensions(anchor) @@ -34,13 +34,24 @@ export default function positionDropdown(element, { anchor, align }) { } function calcLeftPosition() { - return align === "right" - ? dimensions.left + dimensions.width - dimensions.containerWidth - : dimensions.left + let left + + if (align == "right") { + left = dimensions.left + dimensions.width - dimensions.containerWidth + } else if (align == "right-side") { + left = dimensions.left + dimensions.width + } else { + left = dimensions.left + } + + return left } element.style.position = "absolute" element.style.zIndex = "9999" + if (maxWidth) { + element.style.maxWidth = `${maxWidth}px` + } element.style.minWidth = `${dimensions.width}px` element.style.maxHeight = `${maxHeight.toFixed(0)}px` element.style.transformOrigin = `center ${positionSide}` @@ -54,10 +65,8 @@ export default function positionDropdown(element, { anchor, align }) { element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px` }) }) - resizeObserver.observe(anchor) resizeObserver.observe(element) - return { destroy() { resizeObserver.disconnect() diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte index 756e5e6a09..91581724d5 100644 --- a/packages/bbui/src/Popover/Popover.svelte +++ b/packages/bbui/src/Popover/Popover.svelte @@ -11,6 +11,7 @@ export let align = "right" export let portalTarget export let dataCy + export let maxWidth export let direction = "bottom" export let showTip = false @@ -45,7 +46,7 @@
{ providerId, // Table ID is used by JSON fields to know what table the field is in tableId: table?._id, + category: "Data", }) }) }) @@ -385,6 +386,7 @@ const getUserBindings = () => { // datasource options, based on bindable properties fieldSchema, providerId: "user", + category: "Current User", }) }) return bindings @@ -401,11 +403,13 @@ const getDeviceBindings = () => { type: "context", runtimeBinding: `${safeDevice}.${makePropSafe("mobile")}`, readableBinding: `Device.Mobile`, + category: "Device", }) bindings.push({ type: "context", runtimeBinding: `${safeDevice}.${makePropSafe("tablet")}`, readableBinding: `Device.Tablet`, + category: "Device", }) } return bindings @@ -460,6 +464,7 @@ const getStateBindings = () => { type: "context", runtimeBinding: `${safeState}.${makePropSafe(key)}`, readableBinding: `State.${key}`, + category: "State", })) } return bindings @@ -482,11 +487,13 @@ const getUrlBindings = asset => { type: "context", runtimeBinding: `${safeURL}.${makePropSafe(param)}`, readableBinding: `URL.${param}`, + category: "URL", })) const queryParamsBinding = { type: "context", runtimeBinding: makePropSafe("query"), readableBinding: "Query params", + category: "URL", } return urlParamBindings.concat([queryParamsBinding]) } @@ -497,6 +504,7 @@ const getRoleBindings = () => { type: "context", runtimeBinding: `trim "${role._id}"`, readableBinding: `Role.${role.name}`, + category: "Role", } }) } diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 49cbd434cf..40a4ed8061 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -9,6 +9,9 @@ Body, Layout, Button, + ActionButton, + Icon, + Popover, } from "@budibase/bbui" import { createEventDispatcher, onMount } from "svelte" import { @@ -45,6 +48,21 @@ let jsValue = initialValueJS ? value : null let hbsValue = initialValueJS ? null : value + let selectedCategory = null + let categoryIcons = { + Device: "DevicePhone", + "Current User": "User", + Helpers: "MagicWand", + Data: "Data", + State: "AutomatedSegment", + URL: "RailTop", + Role: "UsersLock", + } + + let popover + let popoverAnchor + let hoverHelper + $: usingJS = mode === "JavaScript" $: searchRgx = new RegExp(search, "ig") $: categories = Object.entries(groupBy("category", bindings)) @@ -55,10 +73,25 @@ return binding.readableBinding.match(searchRgx) }), })) - .filter(category => category.bindings?.length > 0) + .filter(category => { + return ( + category.bindings?.length > 0 && + (!selectedCategory ? true : selectedCategory === category.name) + ) + }) + $: filteredHelpers = helpers?.filter(helper => { return helper.label.match(searchRgx) || helper.description.match(searchRgx) }) + + $: categoryNames = [ + ...categories.reduce((acc, cat) => { + acc.push(cat[0]) + return acc + }, []), + "Helpers", + ] + $: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`) const updateValue = val => { @@ -140,23 +173,91 @@ }) + + + + {#if selectedCategory === "Helpers" || search} +
+
{hoverHelper.displayText}
+
+ {@html hoverHelper.description} +
+
{getHelperExample(
+              hoverHelper,
+              usingJS
+            )}
+
+ {/if} +
+
+
+ -
-
+ + {#if selectedCategory} +
+ { + selectedCategory = null + }} + > + Back + +
+ {/if} + + {#if !selectedCategory}
Search
-
- {#each filteredCategories as category} - {#if category.bindings?.length} -
-
{category.name}
+ {/if} + + {#if !selectedCategory && !search} +
    + {#each categoryNames as categoryName} +
  • { + selectedCategory = categoryName + }} + > + + {categoryName} + +
  • + {/each} +
+ {/if} + + {#if selectedCategory || search} + {#each filteredCategories as category} + {#if category.bindings?.length} +
+ {category.name} +
    {#each category.bindings as binding} -
  • addBinding(binding)}> + +
  • addBinding(binding)}> {binding.readableBinding} - {#if binding.type} - {binding.type} + + {#if binding.fieldSchema?.type} + + + {binding.fieldSchema.type} + + {/if} {#if binding.description}
    @@ -167,31 +268,44 @@
  • {/each}
-
+ {/if} + {/each} + + {#if selectedCategory === "Helpers" || search} + {#if filteredHelpers?.length} +
Helpers
+
    + {#each filteredHelpers as helper} +
  • addHelper(helper, usingJS)} + on:mouseenter={e => { + // if (e.target !== this) return + popoverAnchor = e.target + hoverHelper = helper + popover.show() + e.stopPropagation() + }} + on:mouseleave={() => { + popover.hide() + popoverAnchor = null + hoverHelper = null + }} + on:focus={() => {}} + on:blur={() => {}} + > + {helper.displayText} + {#if helper.type} + + {helper.type} + + {/if} +
  • + {/each} +
+ {/if} {/if} - {/each} - {#if filteredHelpers?.length} -
-
Helpers
-
    - {#each filteredHelpers as helper} -
  • addHelper(helper, usingJS)}> -
    -
    {helper.displayText}
    -
    - {@html helper.description} -
    -
    {getHelperExample(
    -                      helper,
    -                      usingJS
    -                    )}
    -
    -
  • - {/each} -
-
{/if} -
+
@@ -241,6 +355,35 @@