@jqhtml/core 2.3.2 → 2.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1848,6 +1848,14 @@ class Jqhtml_Component {
1848
1848
  * @private
1849
1849
  */
1850
1850
  async _wait_for_children_ready() {
1851
+ // Server-rendered components (created via jqhtml.boot()) may have children
1852
+ // that were hydrated asynchronously during the 'render' event callback.
1853
+ // Those children couldn't register via _find_dom_parent() because they were
1854
+ // created after the parent's lifecycle started. Force DOM traversal fallback
1855
+ // to reliably discover all children, including boot-hydrated ones.
1856
+ if (this.args._inner_html !== undefined) {
1857
+ this._use_dom_fallback = true;
1858
+ }
1851
1859
  const children = this._get_dom_children();
1852
1860
  if (children.length === 0) {
1853
1861
  return; // No children, nothing to wait for
@@ -2953,6 +2961,136 @@ function escape_html(str) {
2953
2961
  return div.innerHTML;
2954
2962
  }
2955
2963
 
2964
+ /**
2965
+ * JQHTML Boot - Component Hydration System
2966
+ *
2967
+ * Bridges server-rendered HTML and client-side jqhtml components.
2968
+ *
2969
+ * Server output: <div class="_Component_Init" data-component-init-name="User_Card" data-component-args='{"id":1}'></div>
2970
+ * After boot(): <div class="User_Card Component">...rendered template...</div>
2971
+ *
2972
+ * Usage:
2973
+ * await jqhtml.boot(); // Hydrate all placeholders, wait for ready
2974
+ * jqhtml.boot($('#container')); // Hydrate within scope
2975
+ */
2976
+ /**
2977
+ * Hydrate all _Component_Init placeholders within a scope.
2978
+ *
2979
+ * @param scope - jQuery object, HTMLElement, or undefined (defaults to body)
2980
+ * @returns Promise that resolves when all components (including nested) are ready
2981
+ */
2982
+ function boot(scope) {
2983
+ const jQ = typeof jQuery !== 'undefined' ? jQuery : $;
2984
+ if (!scope) {
2985
+ scope = jQ('body');
2986
+ }
2987
+ else if (!(scope instanceof jQ)) {
2988
+ scope = jQ(scope);
2989
+ }
2990
+ const readyPromises = [];
2991
+ // Find top-level placeholders only (skip those nested inside other placeholders)
2992
+ scope.find('._Component_Init').each(function () {
2993
+ const $element = jQ(this);
2994
+ // Skip if removed from DOM
2995
+ if (!document.contains($element[0])) {
2996
+ return;
2997
+ }
2998
+ // Skip nested placeholders - they'll be hydrated by parent's render callback
2999
+ let parent = $element[0].parentElement;
3000
+ while (parent) {
3001
+ if (parent.classList.contains('_Component_Init')) {
3002
+ return;
3003
+ }
3004
+ parent = parent.parentElement;
3005
+ }
3006
+ // Hydrate this placeholder
3007
+ const component = hydrateElement($element);
3008
+ if (!component)
3009
+ return;
3010
+ // On render, recursively hydrate any nested placeholders
3011
+ component.on('render', function () {
3012
+ hydrateNested(component.$, jQ);
3013
+ });
3014
+ // Collect ready promise - parent.ready() waits for all children via _wait_for_children_ready()
3015
+ readyPromises.push(component.ready());
3016
+ });
3017
+ // Fire jqhtml:ready event when all top-level components are ready
3018
+ const result = Promise.all(readyPromises);
3019
+ result.then(() => {
3020
+ document.dispatchEvent(new CustomEvent('jqhtml:ready'));
3021
+ });
3022
+ return result;
3023
+ }
3024
+ /**
3025
+ * Hydrate nested _Component_Init placeholders within a component's DOM.
3026
+ * Called from parent's 'render' event.
3027
+ */
3028
+ function hydrateNested($scope, jQ) {
3029
+ $scope.find('._Component_Init').each(function () {
3030
+ const $element = jQ(this);
3031
+ if (!document.contains($element[0])) {
3032
+ return;
3033
+ }
3034
+ // Skip if nested inside another placeholder
3035
+ let parent = $element[0].parentElement;
3036
+ while (parent && parent !== $scope[0]) {
3037
+ if (parent.classList.contains('_Component_Init')) {
3038
+ return;
3039
+ }
3040
+ parent = parent.parentElement;
3041
+ }
3042
+ const component = hydrateElement($element);
3043
+ if (!component)
3044
+ return;
3045
+ // Recursively handle deeper nesting
3046
+ component.on('render', function () {
3047
+ hydrateNested(component.$, jQ);
3048
+ });
3049
+ });
3050
+ }
3051
+ /**
3052
+ * Hydrate a single _Component_Init element into a live component.
3053
+ */
3054
+ function hydrateElement($element, jQ) {
3055
+ const componentName = $element.attr('data-component-init-name');
3056
+ if (!componentName)
3057
+ return null;
3058
+ // Parse args
3059
+ const argsString = $element.attr('data-component-args');
3060
+ let args = {};
3061
+ if (argsString) {
3062
+ try {
3063
+ args = JSON.parse(argsString);
3064
+ }
3065
+ catch (e) {
3066
+ console.error(`[jqhtml.boot] Failed to parse args for ${componentName}:`, e);
3067
+ }
3068
+ }
3069
+ // Strip data- prefix from args if present
3070
+ const filteredArgs = {};
3071
+ for (const [key, value] of Object.entries(args)) {
3072
+ filteredArgs[key.startsWith('data-') ? key.substring(5) : key] = value;
3073
+ }
3074
+ // Capture innerHTML for content() and mark as boot-created
3075
+ filteredArgs._inner_html = $element.html();
3076
+ filteredArgs._component_name = componentName;
3077
+ // Cleanup placeholder
3078
+ $element.removeAttr('data-component-init-name');
3079
+ $element.removeAttr('data-component-args');
3080
+ $element.removeData('component-init-name');
3081
+ $element.removeData('component-args');
3082
+ $element.removeClass('_Component_Init');
3083
+ $element.empty();
3084
+ // Create component
3085
+ try {
3086
+ return $element.component(componentName, filteredArgs).component();
3087
+ }
3088
+ catch (error) {
3089
+ console.error(`[jqhtml.boot] Failed to create ${componentName}:`, error);
3090
+ return null;
3091
+ }
3092
+ }
3093
+
2956
3094
  /**
2957
3095
  * JQHTML Debug Overlay
2958
3096
  *
@@ -4233,7 +4371,7 @@ function init(jQuery) {
4233
4371
  }
4234
4372
  }
4235
4373
  // Version - will be replaced during build with actual version from package.json
4236
- const version = '2.2.222';
4374
+ const version = '2.3.4';
4237
4375
  // Default export with all functionality
4238
4376
  const jqhtml = {
4239
4377
  // Core
@@ -4325,7 +4463,9 @@ const jqhtml = {
4325
4463
  // Cache key setter - enables component caching via local storage
4326
4464
  set_cache_key(cache_key) {
4327
4465
  Jqhtml_Local_Storage.set_cache_key(cache_key);
4328
- }
4466
+ },
4467
+ // Boot - hydrate server-rendered component placeholders
4468
+ boot
4329
4469
  };
4330
4470
  // Auto-register on window for browser environments
4331
4471
  // This is REQUIRED for compiled templates which use window.jqhtml.register_template()
@@ -4343,5 +4483,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
4343
4483
  }
4344
4484
  }
4345
4485
 
4346
- export { DebugOverlay, Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, create_component, jqhtml as default, devWarn, escape_html, extract_slots, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, hideDebugOverlay, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register_component, register_template, render_template, showDebugOverlay, version };
4486
+ export { DebugOverlay, Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, extract_slots, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, hideDebugOverlay, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register_component, register_template, render_template, showDebugOverlay, version };
4347
4487
  //# sourceMappingURL=index.js.map