Functional Electrum subscriptions

This commit is contained in:
2025-04-12 05:05:08 +00:00
committed by Jim Hamill
parent 761ebd9fbe
commit 426b1cc68e
4 changed files with 176 additions and 32 deletions
@@ -17,7 +17,7 @@
v-if="state.selectedFromFunction?.extendedInput"
v-model="state.selectedFromFunction.models[i]"
class="extended-input"
@input="autoGrow($event.target)"
@input="(e) => autoGrow(e.target as HTMLTextAreaElement)"
></textarea>
<input
v-else
@@ -37,11 +37,11 @@
<div v-if="state.toFunctions.length || state.primitive" class="section output-section">
<h2>{{ state.toFunctions.length ? 'Output' : 'Instance' }}</h2>
<template v-for="(toFunction, i) of toFunctions" :key="i">
<template v-for="(toFunction, i) of state.toFunctions" :key="i">
<div>
<div class="output-box-container">
<LabelledSection :label="toFunction.label" :content="getPrimitive(() => toFunction.callback(state.primitive))" />
<CopyButton :content="getPrimitive(() => toFunction.callback(state.primitive))" />
<LabelledSection :label="toFunction.label" :content="getPrimitive(() => toFunction.callback(state.primitive!))" />
<CopyButton :content="getPrimitive(() => toFunction.callback(state.primitive!))" />
</div>
</div>
</template>
@@ -51,37 +51,51 @@
<div class="form-group select-group">
<label for="functionSelect">Method</label>
<select v-model="state.selectedMethod">
<option v-for="method of props.methods" :key="method.label" :value="method">{{ method.label }}</option>
<option v-for="method of state.methods" :key="method.label" :value="method">{{ method.label }}</option>
</select>
</div>
<div class="form-group input-group" :class="{ 'extended': state.selectedMethod?.extendedInput }">
<div v-if="state.selectedMethod" class="form-group input-group" :class="{ 'extended': state.selectedMethod.extendedInput }">
<template v-for="(_input, i) of state.selectedMethod.inputs" :key="i">
<label for="field">{{ state.selectedMethod.inputs[i] }}</label>
<div>
<textarea
v-if="state.selectedMethod?.extendedInput"
v-model="state.selectedMethod.models[i]"
class="extended-input"
@input="autoGrow($event.target)"
></textarea>
<input
v-else
type="text"
v-model="state.selectedMethod.models[i]"
/>
<div v-if="state.selectedMethod.error" class="text-error">{{ state.selectedMethod.error }}</div>
<div class="input-with-remove">
<textarea
v-if="state.selectedMethod.extendedInput"
v-model="state.selectedMethod.models[i]"
class="extended-input"
@input="(e) => autoGrow(e.target as HTMLTextAreaElement)"
></textarea>
<input
v-else
type="text"
v-model="state.selectedMethod.models[i]"
/>
<button
v-if="state.selectedMethod.dynamicInputs && !state.selectedMethod.inputs[i].length"
style="background: transparent"
class="remove-input-button"
@click="() => removeMethodInput(i)"
title="Remove input"
>
×
</button>
</div>
</div>
</template>
<div v-if="state.selectedMethod.error" class="text-error">{{ state.selectedMethod.error }}</div>
<div v-if="state.selectedMethod?.dynamicInputs" class="add-input-container">
<button class="flat-rounded-button" @click="addMethodInput">Add Parameter</button>
</div>
</div>
</div>
<div>
<button type="submit" class="gradient-button" @click="() => executeMethod(state.selectedMethod?.callback(state.primitive, state.selectedMethod.models))">Execute Method</button>
<button type="submit" class="gradient-button" @click="() => executeMethod(() => state.selectedMethod?.callback(state.primitive!, state.selectedMethod.models))">Execute Method</button>
</div>
<!-- Result -->
<div>
<div v-if="state.selectedMethod">
<div class="output-box-container">
<LabelledSection label="Result" :content="state.selectedMethod.result" />
<CopyButton :content="state.selectedMethod.result" />
<LabelledSection label="Result" :content="state.selectedMethod.result || ''" />
<CopyButton :content="state.selectedMethod.result || ''" />
</div>
</div>
</template>
@@ -134,6 +148,9 @@ export interface Method<Primitive> {
// Error (if one occurred).
error?: string;
// Whether the number of inputs is dynamic.
dynamicInputs?: boolean;
// Function that takes the models and returns the primitive
callback: (primitive: Primitive, value: Array<any>) => string | Promise<string>;
@@ -191,7 +208,7 @@ async function setPrimitive(functionToExecute: () => Primitive | Promise<Primiti
state.value.error = undefined;
state.value.toFunctions.forEach(async (wrappedMethod, i) => {
state.value.toFunctions[i].result = await wrappedMethod.callback(state.value.primitive)
state.value.toFunctions[i].result = await wrappedMethod.callback(state.value.primitive!)
});
state.value.methods.forEach((method, i) => {
@@ -218,14 +235,20 @@ function getPrimitive(functionToExecute: () => string): string {
}
}
async function executeMethod(wrappedMethod: () => string | Promise<string>): Promise<void | string> {
async function executeMethod(wrappedMethod: () => string | Promise<string> | undefined): Promise<void | string> {
if(!wrappedMethod || !state.value.selectedMethod) {
return;
}
try {
if(!state.value.primitive) {
return '';
}
state.value.selectedMethod.result = await wrappedMethod;
const result = await wrappedMethod();
// Update the result directly on the selected method
state.value.selectedMethod.result = result;
state.value.selectedMethod.error = undefined;
} catch(error: any) {
state.value.selectedMethod.error = error.message;
@@ -234,10 +257,29 @@ async function executeMethod(wrappedMethod: () => string | Promise<string>): Pro
}
};
async function addMethodInput() {
if(!state.value.selectedMethod) {
return;
}
state.value.selectedMethod.models.push('');
state.value.selectedMethod.inputs.push('');
}
async function removeMethodInput(index: number) {
if(!state.value.selectedMethod) {
return;
}
state.value.selectedMethod.models.splice(index, 1);
state.value.selectedMethod.inputs.splice(index, 1);
}
// Select the first "from" function.
state.value.selectedFromFunction = state.value.fromFunctions[0];
const autoGrow = (element: HTMLTextAreaElement) => {
const autoGrow = (element: HTMLTextAreaElement | null) => {
if (!element) return;
element.style.height = 'auto'
element.style.height = `${Math.min(element.scrollHeight, 300)}px`
}