|
1 | 1 | import { type Component } from 'obsidian';
|
2 | 2 | import type MetaBindPlugin from 'packages/obsidian/src/main';
|
3 | 3 | import { V_API_createTable } from 'packages/obsidian/src/APIValidators';
|
4 |
| -import { API } from 'packages/core/src/api/API.js'; |
| 4 | +import { API, type LifecycleHook } from 'packages/core/src/api/API.js'; |
5 | 5 | import { MetaBindTable } from 'packages/core/src/fields/metaBindTable/MetaBindTable';
|
6 | 6 | import { type BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration';
|
7 | 7 | import { type UnvalidatedInputFieldDeclaration } from 'packages/core/src/parsers/inputFieldParser/InputFieldDeclaration';
|
8 | 8 | import { type UnvalidatedViewFieldDeclaration } from 'packages/core/src/parsers/viewFieldParser/ViewFieldDeclaration';
|
9 | 9 | import { getUUID } from 'packages/core/src/utils/Utils';
|
10 |
| -import { validateArgs } from 'packages/core/src/utils/ZodUtils'; |
| 10 | +import { validate, validateArgs } from 'packages/core/src/utils/ZodUtils'; |
11 | 11 | import { ErrorLevel, MetaBindInternalError } from 'packages/core/src/utils/errors/MetaBindErrors';
|
12 | 12 | import { MarkdownRenderChildWidget } from 'packages/obsidian/src/cm6/Cm6_Widgets';
|
13 | 13 | import { FieldMDRC } from 'packages/obsidian/src/FieldMDRC';
|
14 | 14 | import { type FieldBase } from 'packages/core/src/fields/FieldBase';
|
15 | 15 | import { type FieldType, isFieldTypeAllowedInline } from 'packages/core/src/config/FieldConfigs';
|
| 16 | +import { z } from 'zod'; |
| 17 | +import { V_BindTargetDeclaration } from 'packages/core/src/api/Validators'; |
| 18 | +import { Signal } from 'packages/core/src/utils/Signal'; |
| 19 | +import { getJsEnginePluginAPI } from 'packages/obsidian/src/ObsUtils'; |
| 20 | +import { type ReactiveComponent } from 'jsEngine/api/reactive/ReactiveComponent'; |
16 | 21 |
|
17 | 22 | export interface ComponentLike {
|
18 | 23 | addChild(child: Component): void;
|
@@ -85,4 +90,57 @@ export class ObsidianAPI extends API<MetaBindPlugin> {
|
85 | 90 | cause: `Invalid inline mdrc type "${mdrcType}"`,
|
86 | 91 | });
|
87 | 92 | }
|
| 93 | + |
| 94 | + /** |
| 95 | + * Creates a JS Engine reactive component that will re-render when the given bind targets change. |
| 96 | + * |
| 97 | + * This requires JS Engine to be installed and enabled! |
| 98 | + * |
| 99 | + * @param bindTargets |
| 100 | + * @param lifecycleHook |
| 101 | + * @param callback |
| 102 | + */ |
| 103 | + public reactiveMetadata( |
| 104 | + bindTargets: BindTargetDeclaration[], |
| 105 | + lifecycleHook: LifecycleHook, |
| 106 | + callback: (...values: unknown[]) => Promise<unknown>, |
| 107 | + ): ReactiveComponent { |
| 108 | + validate( |
| 109 | + z.object({ |
| 110 | + bindTargets: V_BindTargetDeclaration.array(), |
| 111 | + lifecycleHook: this.plugin.internal.getLifecycleHookValidator(), |
| 112 | + callback: z.function(), |
| 113 | + }), |
| 114 | + { |
| 115 | + bindTargets: bindTargets, |
| 116 | + lifecycleHook: lifecycleHook, |
| 117 | + callback: callback, |
| 118 | + }, |
| 119 | + ); |
| 120 | + |
| 121 | + const jsEngine = getJsEnginePluginAPI(this.plugin); |
| 122 | + |
| 123 | + const uuid = getUUID(); |
| 124 | + const signal = new Signal<unknown>(undefined); |
| 125 | + |
| 126 | + const dependencies = bindTargets.map(bindTarget => ({ |
| 127 | + bindTarget: bindTarget, |
| 128 | + callbackSignal: new Signal<unknown>(undefined), |
| 129 | + })); |
| 130 | + |
| 131 | + const subscription = this.plugin.metadataManager.subscribeComputed( |
| 132 | + uuid, |
| 133 | + signal, |
| 134 | + undefined, |
| 135 | + dependencies, |
| 136 | + (values: unknown[]) => reactive.refresh(...values), |
| 137 | + () => {}, |
| 138 | + ); |
| 139 | + |
| 140 | + lifecycleHook.register(() => subscription.unsubscribe()); |
| 141 | + |
| 142 | + const reactive = jsEngine.reactive(callback, ...dependencies.map(x => x.callbackSignal.get())); |
| 143 | + |
| 144 | + return reactive; |
| 145 | + } |
88 | 146 | }
|
0 commit comments