Add semantic token highlighting for 'that' variable and comment file references in VS Code extension Add Phone_Text_Input and Currency_Input components with formatting utilities Implement client widgets, form standardization, and soft delete functionality Add modal scroll lock and update documentation Implement comprehensive modal system with form integration and validation Fix modal component instantiation using jQuery plugin API Implement modal system with responsive sizing, queuing, and validation support Implement form submission with validation, error handling, and loading states Implement country/state selectors with dynamic data loading and Bootstrap styling Revert Rsx::Route() highlighting in Blade/PHP files Target specific PHP scopes for Rsx::Route() highlighting in Blade Expand injection selector for Rsx::Route() highlighting Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls Update jqhtml packages to v2.2.165 Add bundle path validation for common mistakes (development mode only) Create Ajax_Select_Input widget and Rsx_Reference_Data controller Create Country_Select_Input widget with default country support Initialize Tom Select on Select_Input widgets Add Tom Select bundle for enhanced select dropdowns Implement ISO 3166 geographic data system for country/region selection Implement widget-based form system with disabled state support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
163 lines
6.8 KiB
JavaScript
Executable File
163 lines
6.8 KiB
JavaScript
Executable File
/**
|
|
* JQHTML Integration - Automatic component registration and binding
|
|
*
|
|
* This module automatically:
|
|
* 1. Registers component classes that extend Jqhtml_Component
|
|
* 2. Binds templates to component classes when names match
|
|
* 3. Enables $(selector).component("Component_Name") syntax
|
|
*/
|
|
class Jqhtml_Integration {
|
|
/**
|
|
* Compiled Jqhtml templates self-register. The developer (the framework in this case) is still
|
|
* responsible for registering es6 component classes with jqhtml. This does so at an early stage
|
|
* of framework init.
|
|
*/
|
|
static _on_framework_modules_define() {
|
|
let jqhtml_components = Manifest.get_extending('Jqhtml_Component');
|
|
|
|
console_debug('JQHTML_INIT', 'Registering ' + jqhtml_components.length + ' Jqhtml Components');
|
|
|
|
for (let component of jqhtml_components) {
|
|
jqhtml.register_component(component.class_name, component.class_object);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Framework modules init phase - Bind components and initialize DOM
|
|
* This runs after templates are registered to bind component classes
|
|
* @param {jQuery} [$scope] Optional scope to search within (defaults to body)
|
|
* @returns {Array<Promise>|undefined} Array of promises for recursive calls, undefined for top-level
|
|
*/
|
|
static _on_framework_modules_init($scope) {
|
|
const is_top_level = !$scope;
|
|
const promises = [];
|
|
const components_needing_init = ($scope || $('body')).find('.Jqhtml_Component_Init');
|
|
if (components_needing_init.length > 0) {
|
|
console_debug('JQHTML_INIT', `Initializing ${components_needing_init.length} DOM components`);
|
|
}
|
|
|
|
components_needing_init.each(function () {
|
|
const $element = $(this);
|
|
|
|
// Skip if element is no longer attached to the document
|
|
// (may have been removed by a parent component's .empty() call)
|
|
if (!document.contains($element[0])) {
|
|
return;
|
|
}
|
|
|
|
// Check if any parent has Jqhtml_Component_Init class - skip nested components
|
|
let parent = $element[0].parentElement;
|
|
while (parent) {
|
|
if (parent.classList.contains('Jqhtml_Component_Init')) {
|
|
return; // Skip this element, it's nested
|
|
}
|
|
parent = parent.parentElement;
|
|
}
|
|
|
|
const component_name = $element.attr('data-component-init-name');
|
|
|
|
// jQuery's .data() doesn't auto-parse JSON - we need to parse it manually
|
|
let component_args = {};
|
|
const args_string = $element.attr('data-component-args');
|
|
|
|
// Unset component- php side initialization args, it is no longer needed as a compionent attribute
|
|
// Unsetting also prevents undesired access to this code in other parts of the program, prevening an
|
|
// unwanted future dependency on this paradigm
|
|
$element.removeAttr('data-component-init-name');
|
|
$element.removeAttr('data-component-args');
|
|
$element.removeData('component-init-name');
|
|
$element.removeData('component-args');
|
|
|
|
if (args_string) {
|
|
try {
|
|
component_args = JSON.parse(args_string);
|
|
} catch (e) {
|
|
console.error(`[JQHTML Integration] Failed to parse component args for ${component_name}:`, e);
|
|
component_args = {};
|
|
}
|
|
}
|
|
|
|
if (component_name) {
|
|
// Transform $ prefixed keys to data- attributes
|
|
let component_args_filtered = {};
|
|
for (const [key, value] of Object.entries(component_args)) {
|
|
// if (key.startsWith('$')) {
|
|
// component_args_filtered[key.substring(1)] = value;
|
|
// } else
|
|
if (key.startsWith('data-')) {
|
|
component_args_filtered[key.substring(5)] = value;
|
|
} else {
|
|
component_args_filtered[key] = value;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Store inner HTML as string for nested component processing
|
|
component_args_filtered._inner_html = $element.html();
|
|
$element.empty();
|
|
|
|
// Remove the init class before instantiation to prevent re-initialization
|
|
$element.removeClass('Jqhtml_Component_Init');
|
|
|
|
// Create promise for this component's initialization
|
|
const component_promise = new Promise((resolve) => {
|
|
// Use jQuery component plugin to create the component
|
|
// Plugin handles element internally, just pass args
|
|
// Get the updated $element from
|
|
let component = $element.component(component_name, component_args_filtered);
|
|
|
|
component.on('render', function () {
|
|
// Recursively collect promises from nested components
|
|
|
|
// Getting the updated component here - if the tag name was not div, the element would have been recreated, so we need to get the element set on the component, not from our earlier selector
|
|
|
|
const nested_promises = Jqhtml_Integration._on_framework_modules_init(component.$);
|
|
promises.push(...nested_promises);
|
|
|
|
// Resolve this component's promise
|
|
resolve();
|
|
}).$;
|
|
});
|
|
|
|
promises.push(component_promise);
|
|
} catch (error) {
|
|
console.error(`[JQHTML Integration] Failed to initialize component ${component_name}:`, error);
|
|
console.error('Error details:', error.stack || error);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Top-level call: spawn async handler to wait for all promises, then trigger event
|
|
if (is_top_level) {
|
|
(async () => {
|
|
await Promise.all(promises);
|
|
await Rsx._rsx_call_all_classes('on_jqhtml_ready');
|
|
Rsx.trigger('jqhtml_ready');
|
|
})();
|
|
return;
|
|
}
|
|
|
|
// Recursive call: return promises for parent to collect
|
|
return promises;
|
|
}
|
|
|
|
/**
|
|
* Get all registered component names
|
|
* @returns {Array<string>} Array of component names
|
|
*/
|
|
static get_component_names() {
|
|
return jqhtml.get_component_names();
|
|
}
|
|
|
|
/**
|
|
* Check if a component is registered
|
|
* @param {string} name Component name
|
|
* @returns {boolean} True if component is registered
|
|
*/
|
|
static has_component(name) {
|
|
return jqhtml.has_component(name);
|
|
}
|
|
}
|
|
|
|
// RSX manifest automatically makes classes global - no manual assignment needed
|