Update snippets to be an array

This commit is contained in:
Andrew Kingston 2024-03-05 19:11:34 +00:00
parent 5b3280832c
commit 706f9b5d4a
11 changed files with 110 additions and 63 deletions

View File

@ -41,6 +41,7 @@
export let allowHelpers = true
export let allowSnippets = true
export let context = null
export let snippets = null
export let autofocusEditor = false
const drawerContext = getContext("drawer")
@ -69,14 +70,14 @@
$: drawerContext?.modal.subscribe(val => (drawerIsModal = val))
$: editorModeOptions = allowJS ? [Modes.Text, Modes.JavaScript] : [Modes.Text]
$: sidePanelOptions = getSidePanelOptions(context, allowSnippets, mode)
$: enrichedBindings = enrichBindings(bindings, context)
$: enrichedBindings = enrichBindings(bindings, context, snippets)
$: usingJS = mode === Modes.JavaScript
$: editorMode =
mode === Modes.JavaScript ? EditorModes.JS : EditorModes.Handlebars
$: editorValue = editorMode === EditorModes.JS ? jsValue : hbsValue
$: bindingCompletions = bindingsToCompletions(enrichedBindings, editorMode)
$: runtimeExpression = readableToRuntimeBinding(enrichedBindings, value)
$: requestUpdateEvaluation(runtimeExpression, context)
$: requestEval(runtimeExpression, context, snippets)
$: bindingHelpers = new BindingHelpers(getCaretPosition, insertAtPos)
$: {
if (!sidePanelOptions.includes(sidePanel)) {
@ -95,20 +96,23 @@
return options
}
const debouncedUpdateEvaluation = Utils.debounce((expression, context) => {
expressionResult = processStringSync(expression || "", context)
const debouncedEval = Utils.debounce((expression, context, snippets) => {
expressionResult = processStringSync(expression || "", {
...context,
snippets,
})
evaluating = false
}, 260)
const requestUpdateEvaluation = (expression, context) => {
const requestEval = (expression, context, snippets) => {
evaluating = true
debouncedUpdateEvaluation(expression, context)
debouncedEval(expression, context, snippets)
}
const getBindingValue = (binding, context) => {
const getBindingValue = (binding, context, snippets) => {
const js = `return $("${binding.runtimeBinding}")`
const hbs = encodeJSBinding(js)
const res = processStringSync(hbs, context)
const res = processStringSync(hbs, { ...context, snippets })
return JSON.stringify(res, null, 2)
}
@ -123,9 +127,9 @@
})
}
const enrichBindings = (bindings, context) => {
const enrichBindings = (bindings, context, snippets) => {
return bindings.map(binding => {
const value = getBindingValue(binding, context)
const value = getBindingValue(binding, context, snippets)
return {
...binding,
value,
@ -139,7 +143,7 @@
valid = isValid(runtimeExpression)
if (valid) {
dispatch("change", val)
requestUpdateEvaluation(runtimeExpression, context)
requestEval(runtimeExpression, context, snippets)
}
}
@ -152,10 +156,6 @@
bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, { js })
}
const onSelectSnippet = snippet => {
bindingHelpers.onSelectSnippet(jsValue, snippet)
}
const changeMode = newMode => {
if (targetMode || newMode === mode) {
return
@ -316,7 +316,10 @@
expression={editorValue}
/>
{:else if sidePanel === SidePanels.Snippets}
<SnippetSidePanel addSnippet={onSelectSnippet} />
<SnippetSidePanel
addSnippet={snippet => bindingHelpers.onSelectSnippet(snippet)}
{snippets}
/>
{/if}
</div>
</div>

View File

@ -1,6 +1,6 @@
<script>
import BindingPanel from "./BindingPanel.svelte"
import { previewStore } from "stores/builder"
import { previewStore, snippetStore } from "stores/builder"
import { onMount } from "svelte"
export let bindings = []
@ -30,6 +30,7 @@
bind:valid
bindings={enrichedBindings}
context={$previewStore.selectedComponentContext}
snippets={$snippetStore}
{value}
{allowJS}
{allowHelpers}

View File

@ -1,5 +1,6 @@
<script>
import BindingPanel from "./BindingPanel.svelte"
import { snippetStore } from "stores/builder"
export let bindings = []
export let valid
@ -22,6 +23,7 @@
<BindingPanel
bind:valid
bindings={enrichedBindings}
snippets={$snippetStore}
{value}
{allowJS}
{context}

View File

@ -13,43 +13,6 @@
let hoveredSnippet
let hideTimeout
snippets = [
{
name: "Square",
code: `
return function(num) {
return num * num
}
`,
},
{
name: "HelloWorld",
code: `
return "Hello, world!"
`,
},
{
name: "Colorful",
code: `
let a = null
let b = "asdasd"
let c = 123123
let d = undefined
let e = [1, 2, 3]
let f = { foo: "bar" }
let g = Math.round(1.234)
if (a === b) {
return c ?? e
}
return d || f
// comment
let h = 1 + 2 + 3 * 3
let i = true
let j = false
`,
},
]
$: filteredSnippets = getFilteredSnippets(snippets, search)
const getFilteredSnippets = (snippets, search) => {

View File

@ -40,11 +40,9 @@ export class BindingHelpers {
}
// Adds a JS/HBS helper to the expression
onSelectSnippet(value, snippet) {
onSelectSnippet(snippet) {
const pos = this.getCaretPosition()
const { start, end } = pos
const jsVal = decodeJSBinding(value)
const insertVal = jsInsert(jsVal, start, end, `snippets.${snippet.name}`)
this.insertAtPos({ start, end, value: insertVal })
this.insertAtPos({ start, end, value: `snippets.${snippet.name}` })
}
}

View File

@ -18,6 +18,7 @@ import {
} from "./automations.js"
import { userStore, userSelectedResourceMap, isOnlyUser } from "./users.js"
import { deploymentStore } from "./deployments.js"
import { snippetStore } from "./snippets"
// Backend
import { tables } from "./tables"
@ -62,6 +63,7 @@ export {
queries,
flags,
hoverStore,
snippetStore,
}
export const reset = () => {
@ -101,6 +103,7 @@ export const initialise = async pkg => {
builderStore.init(application)
navigationStore.syncAppNavigation(application?.navigation)
themeStore.syncAppTheme(application)
snippetStore.syncMetadata(application)
screenStore.syncAppScreens(pkg)
layoutStore.syncAppLayouts(pkg)
resetBuilderHistory()

View File

@ -0,0 +1,71 @@
import { writable } from "svelte/store"
const EXAMPLE_SNIPPETS = [
{
name: "Square",
code: `
return function(num) {
return num * num
}
`,
},
{
name: "HelloWorld",
code: `
return "Hello, world!"
`,
},
{
name: "Colorful",
code: `
let a = null
let b = "asdasd"
let c = 123123
let d = undefined
let e = [1, 2, 3]
let f = { foo: "bar" }
let g = Math.round(1.234)
if (a === b) {
return c ?? e
}
return d || f
// comment
let h = 1 + 2 + 3 * 3
let i = true
let j = false
`,
},
]
const createSnippetStore = () => {
const store = writable(EXAMPLE_SNIPPETS)
const syncMetadata = metadata => {
store.set(metadata?.snippets || EXAMPLE_SNIPPETS)
}
const createSnippet = snippet => {
store.update(state => [...state, snippet])
}
const updateSnippet = updatedSnippet => {
store.update(state => [
...state.filter(snippet => snippet.name !== updatedSnippet.name),
updatedSnippet,
])
}
const deleteSnippet = snippetName => {
store.update(state => state.filter(snippet => snippet.name !== snippetName))
}
return {
...store,
syncMetadata,
createSnippet,
updateSnippet,
deleteSnippet,
}
}
export const snippetStore = createSnippetStore()

View File

@ -6,6 +6,7 @@ import {
themeStore,
navigationStore,
deploymentStore,
snippetStore,
datasources,
tables,
} from "stores/builder"
@ -64,6 +65,7 @@ export const createBuilderWebsocket = appId => {
appStore.syncMetadata(metadata)
themeStore.syncMetadata(metadata)
navigationStore.syncMetadata(metadata)
snippetStore.syncMetadata(metadata)
})
socket.onOther(
BuilderSocketEvent.AppPublishChange,

View File

@ -1,3 +1,3 @@
"use strict";var snippets=(()=>{var s=Object.create;var p=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,m=Object.prototype.hasOwnProperty;var W=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports),d=(e,r)=>{for(var n in r)p(e,n,{get:r[n],enumerable:!0})},o=(e,r,n,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let t of c(r))!m.call(e,t)&&t!==n&&p(e,t,{get:()=>r[t],enumerable:!(i=x(r,t))||i.enumerable});return e};var g=(e,r,n)=>(n=e!=null?s(l(e)):{},o(r||!e||!e.__esModule?p(n,"default",{value:e,enumerable:!0}):n,e)),v=e=>o(p({},"__esModule",{value:!0}),e);var u=W((_,f)=>{f.exports.iifeWrapper=e=>`(function(){
"use strict";var snippets=(()=>{var u=Object.create;var i=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var m=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var l=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),W=(e,n)=>{for(var r in n)i(e,r,{get:n[r],enumerable:!0})},o=(e,n,r,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of d(n))!x.call(e,t)&&t!==r&&i(e,t,{get:()=>n[t],enumerable:!(p=c(n,t))||p.enumerable});return e};var g=(e,n,r)=>(r=e!=null?u(m(e)):{},o(n||!e||!e.__esModule?i(r,"default",{value:e,enumerable:!0}):r,e)),v=e=>o(i({},"__esModule",{value:!0}),e);var a=l((_,f)=>{f.exports.iifeWrapper=e=>`(function(){
${e}
})();`});var y={};d(y,{default:()=>w});var a=g(u()),w=new Proxy({},{get:function(e,r){return[eval][0]((0,a.iifeWrapper)($("snippets")[r]))}});return v(y);})();
})();`});var y={};W(y,{default:()=>w});var s=g(a()),w=new Proxy({},{get:function(e,n){let r=($("snippets")||[]).find(p=>p.name===n);return[eval][0]((0,s.iifeWrapper)(r.code))}});return v(y);})();

View File

@ -7,12 +7,14 @@ export default new Proxy(
{
get: function (_, name) {
// Get snippet definitions from global context, get the correct snippet
// then eval the JS.
// then eval the JS. This will error if the snippet doesn't exist, but
// that's intended.
// https://esbuild.github.io/content-types/#direct-eval for info on why
// eval is being called this way.
// @ts-ignore
// eslint-disable-next-line no-undef
return [eval][0](iifeWrapper($("snippets")[name]))
const snippet = ($("snippets") || []).find(x => x.name === name)
return [eval][0](iifeWrapper(snippet.code))
},
}
)

View File

@ -63,7 +63,9 @@ module.exports.processJS = (handlebars, context) => {
{},
{
get: function (_, name) {
return eval(iifeWrapper(context.snippets[name]))
// This will error if the snippet doesn't exist, but that's intended
const snippet = (context.snippets || []).find(x => x.name === name)
return eval(iifeWrapper(snippet.code))
},
}
),