Files
rspade_system/app/RSpade/Integrations/Jqhtml/Jqhtml_Integration.js
2025-12-03 21:28:08 +00:00

123 lines
6.1 KiB
JavaScript
Executable File

/**
* JQHTML Integration - Component Registration and Hydration Bootstrap
*
* This module bridges RSpade's manifest system with jqhtml's component runtime.
*
* == TWO-PHASE INITIALIZATION ==
*
* Phase 1: _on_framework_modules_define() - Component Registration
* - Runs early in framework boot, before DOM is processed
* - Registers all ES6 classes extending Component with jqhtml runtime
* - Tags static methods with cache IDs for jqhtml's caching system
* - After this phase, jqhtml knows: "User_Card" → UserCardClass
*
* Phase 2: _on_framework_modules_init() - DOM Hydration
* - Calls jqhtml.boot() to hydrate all ._Component_Init placeholders
* - Triggers 'jqhtml_ready' when all components are initialized
*
* == KEY PARTICIPANTS ==
*
* JqhtmlBladeCompiler.php - Transforms <Component /> tags into ._Component_Init divs
* jqhtml runtime - Maintains registry of component names → classes
* jqhtml.boot() - Finds and hydrates all ._Component_Init placeholders
* This module - Orchestrates registration and triggers hydration
*/
class Jqhtml_Integration {
/**
* Phase 1: Register Component Classes
*
* Compiled .jqhtml templates self-register their render methods with jqhtml.
* But the framework must separately register ES6 component classes (the ones
* extending Component with lifecycle methods like on_create, on_load, etc).
*
* This runs during framework_modules_define, before any DOM processing.
*/
static _on_framework_modules_define() {
// ─────────────────────────────────────────────────────────────────────
// Register Component Classes with jqhtml Runtime
//
// The Manifest knows all classes extending Component. We register each
// with jqhtml so it can instantiate them by name during hydration.
// ─────────────────────────────────────────────────────────────────────
let jqhtml_components = Manifest.get_extending('Component');
console_debug('JQHTML_INIT', 'Registering ' + jqhtml_components.length + ' Components');
for (let component of jqhtml_components) {
jqhtml.register_component(component.class_name, component.class_object);
}
// ─────────────────────────────────────────────────────────────────────
// Tag Static Methods with Cache IDs
//
// jqhtml caches component renders based on a hash of their args.
// Problem: Functions can't be serialized, so passing one (e.g., a
// DataGrid's data_source callback) would defeat caching entirely.
//
// Solution: Tag every static method with a stable string identifier.
// When jqhtml hashes component args, it uses _jqhtml_cache_id instead
// of the function reference, making the cache key deterministic.
//
// Example:
// <My_DataGrid $data_source=Controller.fetch />
//
// Without tagging: args hash includes [Function] → uncacheable
// With tagging: args hash includes "Controller.fetch" → cacheable
//
// This enables Ajax endpoints and other callbacks to be passed to
// components without breaking the automatic caching system.
// ─────────────────────────────────────────────────────────────────────
const all_classes = Manifest.get_all_classes();
let methods_tagged = 0;
for (const class_info of all_classes) {
const class_object = class_info.class_object;
const class_name = class_info.class_name;
const property_names = Object.getOwnPropertyNames(class_object);
for (const property_name of property_names) {
if (property_name === 'length' || property_name === 'name' || property_name === 'prototype') {
continue;
}
const property_value = class_object[property_name];
if (typeof property_value === 'function') {
property_value._jqhtml_cache_id = `${class_name}.${property_name}`;
methods_tagged++;
}
}
}
console_debug('JQHTML_INIT', `Tagged ${methods_tagged} static methods with _jqhtml_cache_id`);
// ─────────────────────────────────────────────────────────────────────
// Configure jqhtml Caching
//
// scope_key() changes when: app code changes, user logs out, site changes.
// This automatically invalidates cached component renders.
// ─────────────────────────────────────────────────────────────────────
jqhtml.set_cache_key(Rsx.scope_key());
window.jqhtml.debug.verbose = false;
}
/**
* Phase 2: DOM Hydration
*
* Delegates to jqhtml.boot() which finds all ._Component_Init placeholders
* and converts them into live components.
*
* jqhtml.boot() handles:
* - Finding ._Component_Init elements
* - Parsing data-component-init-name / data-component-args
* - Calling $element.component(name, args)
* - Recursive nested component handling
* - Promise tracking for async components
*/
static _on_framework_modules_init() {
jqhtml.boot().then(() => {
Rsx.trigger('jqhtml_ready');
});
}
}
// Class is automatically made global by RSX manifest - no window assignment needed