@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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * JQHTML Core v2.2.222
2
+ * JQHTML Core v2.3.4
3
3
  * (c) 2025 JQHTML Team
4
4
  * Released under the MIT License
5
5
  */
@@ -1853,6 +1853,14 @@ class Jqhtml_Component {
1853
1853
  * @private
1854
1854
  */
1855
1855
  async _wait_for_children_ready() {
1856
+ // Server-rendered components (created via jqhtml.boot()) may have children
1857
+ // that were hydrated asynchronously during the 'render' event callback.
1858
+ // Those children couldn't register via _find_dom_parent() because they were
1859
+ // created after the parent's lifecycle started. Force DOM traversal fallback
1860
+ // to reliably discover all children, including boot-hydrated ones.
1861
+ if (this.args._inner_html !== undefined) {
1862
+ this._use_dom_fallback = true;
1863
+ }
1856
1864
  const children = this._get_dom_children();
1857
1865
  if (children.length === 0) {
1858
1866
  return; // No children, nothing to wait for
@@ -2958,6 +2966,136 @@ function escape_html(str) {
2958
2966
  return div.innerHTML;
2959
2967
  }
2960
2968
 
2969
+ /**
2970
+ * JQHTML Boot - Component Hydration System
2971
+ *
2972
+ * Bridges server-rendered HTML and client-side jqhtml components.
2973
+ *
2974
+ * Server output: <div class="_Component_Init" data-component-init-name="User_Card" data-component-args='{"id":1}'></div>
2975
+ * After boot(): <div class="User_Card Component">...rendered template...</div>
2976
+ *
2977
+ * Usage:
2978
+ * await jqhtml.boot(); // Hydrate all placeholders, wait for ready
2979
+ * jqhtml.boot($('#container')); // Hydrate within scope
2980
+ */
2981
+ /**
2982
+ * Hydrate all _Component_Init placeholders within a scope.
2983
+ *
2984
+ * @param scope - jQuery object, HTMLElement, or undefined (defaults to body)
2985
+ * @returns Promise that resolves when all components (including nested) are ready
2986
+ */
2987
+ function boot(scope) {
2988
+ const jQ = typeof jQuery !== 'undefined' ? jQuery : $;
2989
+ if (!scope) {
2990
+ scope = jQ('body');
2991
+ }
2992
+ else if (!(scope instanceof jQ)) {
2993
+ scope = jQ(scope);
2994
+ }
2995
+ const readyPromises = [];
2996
+ // Find top-level placeholders only (skip those nested inside other placeholders)
2997
+ scope.find('._Component_Init').each(function () {
2998
+ const $element = jQ(this);
2999
+ // Skip if removed from DOM
3000
+ if (!document.contains($element[0])) {
3001
+ return;
3002
+ }
3003
+ // Skip nested placeholders - they'll be hydrated by parent's render callback
3004
+ let parent = $element[0].parentElement;
3005
+ while (parent) {
3006
+ if (parent.classList.contains('_Component_Init')) {
3007
+ return;
3008
+ }
3009
+ parent = parent.parentElement;
3010
+ }
3011
+ // Hydrate this placeholder
3012
+ const component = hydrateElement($element);
3013
+ if (!component)
3014
+ return;
3015
+ // On render, recursively hydrate any nested placeholders
3016
+ component.on('render', function () {
3017
+ hydrateNested(component.$, jQ);
3018
+ });
3019
+ // Collect ready promise - parent.ready() waits for all children via _wait_for_children_ready()
3020
+ readyPromises.push(component.ready());
3021
+ });
3022
+ // Fire jqhtml:ready event when all top-level components are ready
3023
+ const result = Promise.all(readyPromises);
3024
+ result.then(() => {
3025
+ document.dispatchEvent(new CustomEvent('jqhtml:ready'));
3026
+ });
3027
+ return result;
3028
+ }
3029
+ /**
3030
+ * Hydrate nested _Component_Init placeholders within a component's DOM.
3031
+ * Called from parent's 'render' event.
3032
+ */
3033
+ function hydrateNested($scope, jQ) {
3034
+ $scope.find('._Component_Init').each(function () {
3035
+ const $element = jQ(this);
3036
+ if (!document.contains($element[0])) {
3037
+ return;
3038
+ }
3039
+ // Skip if nested inside another placeholder
3040
+ let parent = $element[0].parentElement;
3041
+ while (parent && parent !== $scope[0]) {
3042
+ if (parent.classList.contains('_Component_Init')) {
3043
+ return;
3044
+ }
3045
+ parent = parent.parentElement;
3046
+ }
3047
+ const component = hydrateElement($element);
3048
+ if (!component)
3049
+ return;
3050
+ // Recursively handle deeper nesting
3051
+ component.on('render', function () {
3052
+ hydrateNested(component.$, jQ);
3053
+ });
3054
+ });
3055
+ }
3056
+ /**
3057
+ * Hydrate a single _Component_Init element into a live component.
3058
+ */
3059
+ function hydrateElement($element, jQ) {
3060
+ const componentName = $element.attr('data-component-init-name');
3061
+ if (!componentName)
3062
+ return null;
3063
+ // Parse args
3064
+ const argsString = $element.attr('data-component-args');
3065
+ let args = {};
3066
+ if (argsString) {
3067
+ try {
3068
+ args = JSON.parse(argsString);
3069
+ }
3070
+ catch (e) {
3071
+ console.error(`[jqhtml.boot] Failed to parse args for ${componentName}:`, e);
3072
+ }
3073
+ }
3074
+ // Strip data- prefix from args if present
3075
+ const filteredArgs = {};
3076
+ for (const [key, value] of Object.entries(args)) {
3077
+ filteredArgs[key.startsWith('data-') ? key.substring(5) : key] = value;
3078
+ }
3079
+ // Capture innerHTML for content() and mark as boot-created
3080
+ filteredArgs._inner_html = $element.html();
3081
+ filteredArgs._component_name = componentName;
3082
+ // Cleanup placeholder
3083
+ $element.removeAttr('data-component-init-name');
3084
+ $element.removeAttr('data-component-args');
3085
+ $element.removeData('component-init-name');
3086
+ $element.removeData('component-args');
3087
+ $element.removeClass('_Component_Init');
3088
+ $element.empty();
3089
+ // Create component
3090
+ try {
3091
+ return $element.component(componentName, filteredArgs).component();
3092
+ }
3093
+ catch (error) {
3094
+ console.error(`[jqhtml.boot] Failed to create ${componentName}:`, error);
3095
+ return null;
3096
+ }
3097
+ }
3098
+
2961
3099
  /**
2962
3100
  * JQHTML Debug Overlay
2963
3101
  *
@@ -4238,7 +4376,7 @@ function init(jQuery) {
4238
4376
  }
4239
4377
  }
4240
4378
  // Version - will be replaced during build with actual version from package.json
4241
- const version = '2.2.222';
4379
+ const version = '2.3.4';
4242
4380
  // Default export with all functionality
4243
4381
  const jqhtml = {
4244
4382
  // Core
@@ -4330,7 +4468,9 @@ const jqhtml = {
4330
4468
  // Cache key setter - enables component caching via local storage
4331
4469
  set_cache_key(cache_key) {
4332
4470
  Jqhtml_Local_Storage.set_cache_key(cache_key);
4333
- }
4471
+ },
4472
+ // Boot - hydrate server-rendered component placeholders
4473
+ boot
4334
4474
  };
4335
4475
  // Auto-register on window for browser environments
4336
4476
  // This is REQUIRED for compiled templates which use window.jqhtml.register_template()
@@ -4348,5 +4488,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
4348
4488
  }
4349
4489
  }
4350
4490
 
4351
- 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 };
4491
+ 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 };
4352
4492
  //# sourceMappingURL=jqhtml-core.esm.js.map