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

View File

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

View File

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

View File

@ -13,43 +13,6 @@
let hoveredSnippet let hoveredSnippet
let hideTimeout 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) $: filteredSnippets = getFilteredSnippets(snippets, search)
const getFilteredSnippets = (snippets, search) => { const getFilteredSnippets = (snippets, search) => {

View File

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

View File

@ -18,6 +18,7 @@ import {
} from "./automations.js" } from "./automations.js"
import { userStore, userSelectedResourceMap, isOnlyUser } from "./users.js" import { userStore, userSelectedResourceMap, isOnlyUser } from "./users.js"
import { deploymentStore } from "./deployments.js" import { deploymentStore } from "./deployments.js"
import { snippetStore } from "./snippets"
// Backend // Backend
import { tables } from "./tables" import { tables } from "./tables"
@ -62,6 +63,7 @@ export {
queries, queries,
flags, flags,
hoverStore, hoverStore,
snippetStore,
} }
export const reset = () => { export const reset = () => {
@ -101,6 +103,7 @@ export const initialise = async pkg => {
builderStore.init(application) builderStore.init(application)
navigationStore.syncAppNavigation(application?.navigation) navigationStore.syncAppNavigation(application?.navigation)
themeStore.syncAppTheme(application) themeStore.syncAppTheme(application)
snippetStore.syncMetadata(application)
screenStore.syncAppScreens(pkg) screenStore.syncAppScreens(pkg)
layoutStore.syncAppLayouts(pkg) layoutStore.syncAppLayouts(pkg)
resetBuilderHistory() 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, themeStore,
navigationStore, navigationStore,
deploymentStore, deploymentStore,
snippetStore,
datasources, datasources,
tables, tables,
} from "stores/builder" } from "stores/builder"
@ -64,6 +65,7 @@ export const createBuilderWebsocket = appId => {
appStore.syncMetadata(metadata) appStore.syncMetadata(metadata)
themeStore.syncMetadata(metadata) themeStore.syncMetadata(metadata)
navigationStore.syncMetadata(metadata) navigationStore.syncMetadata(metadata)
snippetStore.syncMetadata(metadata)
}) })
socket.onOther( socket.onOther(
BuilderSocketEvent.AppPublishChange, 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} ${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: function (_, name) {
// Get snippet definitions from global context, get the correct snippet // 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 // https://esbuild.github.io/content-types/#direct-eval for info on why
// eval is being called this way. // eval is being called this way.
// @ts-ignore // @ts-ignore
// eslint-disable-next-line no-undef // 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) { 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))
}, },
} }
), ),