Tidy up logic for showing and hiding popovers for bindings and helpers

This commit is contained in:
Andrew Kingston 2024-02-20 10:11:27 +00:00
parent 7a278234b5
commit ca3f464523
4 changed files with 91 additions and 74 deletions

View File

@ -117,7 +117,7 @@
const indentWithTabCustom = { const indentWithTabCustom = {
key: "Tab", key: "Tab",
run: view => { run: view => {
if (completionStatus(view.state) == "active") { if (completionStatus(view.state) === "active") {
acceptCompletion(view) acceptCompletion(view)
return true return true
} }
@ -131,7 +131,7 @@
} }
const buildKeymap = () => { const buildKeymap = () => {
const baseMap = [ return [
...closeBracketsKeymap, ...closeBracketsKeymap,
...defaultKeymap, ...defaultKeymap,
...historyKeymap, ...historyKeymap,
@ -139,7 +139,6 @@
...completionKeymap, ...completionKeymap,
indentWithTabCustom, indentWithTabCustom,
] ]
return baseMap
} }
const buildBaseExtensions = () => { const buildBaseExtensions = () => {
@ -215,7 +214,7 @@
) )
} }
if (mode.name == "javascript") { if (mode.name === "javascript") {
complete.push(javascript()) complete.push(javascript())
complete.push(highlightWhitespace()) complete.push(highlightWhitespace())
complete.push(lineNumbers()) complete.push(lineNumbers())
@ -321,4 +320,19 @@
border-radius: var(--border-radius-s); border-radius: var(--border-radius-s);
padding: 4px 6px; padding: 4px 6px;
} }
.code-editor :global(.binding__example) {
padding: 0;
margin: 0;
font-size: 12px;
white-space: pre;
text-overflow: ellipsis;
overflow: hidden;
max-height: 480px;
}
.code-editor :global(.binding__example span) {
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
</style> </style>

View File

@ -255,6 +255,11 @@ export const buildBindingInfoNode = (completion, binding) => {
const ele = document.createElement("div") const ele = document.createElement("div")
ele.classList.add("info-bubble") ele.classList.add("info-bubble")
if (binding.valueHTML) {
ele.innerHTML = `<div class="binding__example">${binding.valueHTML}</div>`
return ele
}
const exampleNodeHtml = binding.readableBinding const exampleNodeHtml = binding.readableBinding
? `<div class="binding__example">{{ ${binding.readableBinding} }}</div>` ? `<div class="binding__example">{{ ${binding.readableBinding} }}</div>`
: "" : ""

View File

@ -62,16 +62,45 @@
let targetMode = null let targetMode = null
let expressionResult let expressionResult
$: enrichedBindings = enrichBindings(bindings, context)
$: usingJS = mode === "JavaScript" $: usingJS = mode === "JavaScript"
$: editorMode = $: editorMode =
mode === "JavaScript" ? EditorModes.JS : EditorModes.Handlebars mode === "JavaScript" ? EditorModes.JS : EditorModes.Handlebars
$: bindingCompletions = bindingsToCompletions(bindings, editorMode) $: bindingCompletions = bindingsToCompletions(enrichedBindings, editorMode)
$: runtimeExpression = readableToRuntimeBinding(bindings, value) $: runtimeExpression = readableToRuntimeBinding(enrichedBindings, value)
$: expressionResult = processStringSync(runtimeExpression || "", context) $: expressionResult = processStringSync(runtimeExpression || "", context)
$: bindingHelpers = new BindingHelpers(getCaretPosition, insertAtPos) $: bindingHelpers = new BindingHelpers(getCaretPosition, insertAtPos)
const getBindingValue = (binding, context) => {
const hbs = `{{ literal ${binding.runtimeBinding} }}`
const res = processStringSync(hbs, context)
return JSON.stringify(res, null, 2)
}
const highlightJSON = json => {
return formatHighlight(json, {
keyColor: "#e06c75",
numberColor: "#e5c07b",
stringColor: "#98c379",
trueColor: "#d19a66",
falseColor: "#d19a66",
nullColor: "#c678dd",
})
}
const enrichBindings = (bindings, context) => {
return bindings.map(binding => {
const value = getBindingValue(binding, context)
return {
...binding,
value,
valueHTML: highlightJSON(value),
}
})
}
const updateValue = val => { const updateValue = val => {
const runtimeExpression = readableToRuntimeBinding(bindings, val) const runtimeExpression = readableToRuntimeBinding(enrichedBindings, val)
valid = isValid(runtimeExpression) valid = isValid(runtimeExpression)
if (valid) { if (valid) {
dispatch("change", val) dispatch("change", val)
@ -116,9 +145,9 @@
} }
const convert = () => { const convert = () => {
const runtime = readableToRuntimeBinding(bindings, hbsValue) const runtime = readableToRuntimeBinding(enrichedBindings, hbsValue)
const runtimeJs = encodeJSBinding(convertToJS(runtime)) const runtimeJs = encodeJSBinding(convertToJS(runtime))
jsValue = runtimeToReadableBinding(bindings, runtimeJs) jsValue = runtimeToReadableBinding(enrichedBindings, runtimeJs)
hbsValue = null hbsValue = null
mode = "JavaScript" mode = "JavaScript"
onSelectBinding("", { forceJS: true }) onSelectBinding("", { forceJS: true })
@ -143,7 +172,7 @@
} }
onMount(() => { onMount(() => {
valid = isValid(readableToRuntimeBinding(bindings, value)) valid = isValid(readableToRuntimeBinding(enrichedBindings, value))
}) })
</script> </script>
@ -261,7 +290,7 @@
{#if sidebar} {#if sidebar}
<div class="binding-picker"> <div class="binding-picker">
<BindingPicker <BindingPicker
{bindings} bindings={enrichedBindings}
{allowHelpers} {allowHelpers}
{context} {context}
addHelper={onSelectHelper} addHelper={onSelectHelper}
@ -348,7 +377,7 @@
{#if sidebar} {#if sidebar}
<div class="binding-picker"> <div class="binding-picker">
<BindingPicker <BindingPicker
{bindings} bindings={enrichedBindings}
{allowHelpers} {allowHelpers}
{context} {context}
addHelper={onSelectHelper} addHelper={onSelectHelper}

View File

@ -71,36 +71,26 @@
return names return names
} }
const getBindingValue = binding => { const showBindingPopover = (binding, target) => {
const hbs = `{{ literal ${binding.runtimeBinding} }}` stopHidingPopover()
const res = processStringSync(hbs, context)
return JSON.stringify(res, null, 2)
}
const highlight = json => {
return formatHighlight(json, {
keyColor: "#e06c75",
numberColor: "#e5c07b",
stringColor: "#98c379",
trueColor: "#d19a66",
falseColor: "#d19a66",
nullColor: "#c678dd",
})
}
const showPopover = (target, binding) => {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
let val = getBindingValue(binding)
if (val !== "") {
popoverAnchor = target popoverAnchor = target
hoverTarget = { hoverTarget = {
code: val, code: binding.valueHTML,
} }
popover.show() popover.show()
} }
const showHelperPopover = (helper, target) => {
stopHidingPopover()
if (!helper.displayText && helper.description) {
return
}
popoverAnchor = target
hoverTarget = {
description: helper.description,
code: getHelperExample(helper, mode.name === "javascript"),
}
popover.show()
} }
const hidePopover = () => { const hidePopover = () => {
@ -112,7 +102,7 @@
}, 100) }, 100)
} }
const stopHiding = () => { const stopHidingPopover = () => {
if (hideTimeout) { if (hideTimeout) {
clearTimeout(hideTimeout) clearTimeout(hideTimeout)
hideTimeout = null hideTimeout = null
@ -126,9 +116,10 @@
anchor={popoverAnchor} anchor={popoverAnchor}
minWidth={0} minWidth={0}
maxWidth={480} maxWidth={480}
maxHeight={300} maxHeight={480}
dismissible={false} dismissible={false}
on:mouseenter={stopHiding} on:mouseenter={stopHidingPopover}
on:mouseleave={hidePopover}
> >
<div class="helper"> <div class="helper">
<Layout gap="S"> <Layout gap="S">
@ -139,10 +130,8 @@
</div> </div>
{/if} {/if}
{#if hoverTarget.code} {#if hoverTarget.code}
<pre>
<!-- eslint-disable-next-line svelte/no-at-html-tags--> <!-- eslint-disable-next-line svelte/no-at-html-tags-->
{@html highlight(hoverTarget.code)} <pre>{@html hoverTarget.code}</pre>
</pre>
{/if} {/if}
</Layout> </Layout>
</div> </div>
@ -212,10 +201,8 @@
{#each category.bindings as binding} {#each category.bindings as binding}
<li <li
class="binding" class="binding"
on:mouseenter={e => showPopover(e.target, binding)} on:mouseenter={e => showBindingPopover(binding, e.target)}
on:mouseleave={hidePopover} on:mouseleave={hidePopover}
on:focus={() => {}}
on:blur={() => {}}
on:click={() => addBinding(binding)} on:click={() => addBinding(binding)}
> >
<span class="binding__label"> <span class="binding__label">
@ -227,7 +214,6 @@
{binding.readableBinding} {binding.readableBinding}
{/if} {/if}
</span> </span>
{#if binding.display?.type || binding.fieldSchema?.type} {#if binding.display?.type || binding.fieldSchema?.type}
<span class="binding__typeWrap"> <span class="binding__typeWrap">
<span class="binding__type"> <span class="binding__type">
@ -250,26 +236,9 @@
{#each filteredHelpers as helper} {#each filteredHelpers as helper}
<li <li
class="binding" class="binding"
on:mouseenter={e => showHelperPopover(helper, e.target)}
on:mouseleave={hidePopover}
on:click={() => addHelper(helper, mode.name === "javascript")} on:click={() => addHelper(helper, mode.name === "javascript")}
on:mouseenter={e => {
popoverAnchor = e.target
if (!helper.displayText && helper.description) {
return
}
hoverTarget = {
description: helper.description,
code: getHelperExample(helper, mode.name === "javascript"),
}
popover.show()
e.stopPropagation()
}}
on:mouseleave={() => {
popover.hide()
popoverAnchor = null
hoverTarget = null
}}
on:focus={() => {}}
on:blur={() => {}}
> >
<span class="binding__label">{helper.displayText}</span> <span class="binding__label">{helper.displayText}</span>
<span class="binding__typeWrap"> <span class="binding__typeWrap">
@ -287,16 +256,16 @@
<style> <style>
.search :global(input) { .search :global(input) {
border: none; border: none;
border-radius: 0px; border-radius: 0;
background: none; background: none;
padding: 0px; padding: 0;
} }
.search { .search {
padding: var(--spacing-m) var(--spacing-l); padding: var(--spacing-m) var(--spacing-l);
display: flex; display: flex;
align-items: center; align-items: center;
border-top: 0px; border-top: 0;
border-bottom: var(--border-light); border-bottom: var(--border-light);
border-left: 2px solid transparent; border-left: 2px solid transparent;
border-right: 2px solid transparent; border-right: 2px solid transparent;
@ -316,17 +285,17 @@
} }
ul.category-list { ul.category-list {
padding: 0px var(--spacing-l); padding: 0 var(--spacing-l);
padding-bottom: var(--spacing-l); padding-bottom: var(--spacing-l);
} }
.sub-section { .sub-section {
padding: var(--spacing-l); padding: var(--spacing-l);
padding-top: 0px; padding-top: 0;
} }
.sub-section-back { .sub-section-back {
padding: var(--spacing-l); padding: var(--spacing-l);
padding-top: var(--spacing-xl); padding-top: var(--spacing-xl);
padding-bottom: 0px; padding-bottom: 0;
} }
.cat-heading { .cat-heading {
margin-bottom: var(--spacing-l); margin-bottom: var(--spacing-l);