@zolomedia/bifrost-client 1.7.74
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/L1_Foundation/L1_Foundation.js +13 -0
- package/L1_Foundation/bootstrap/bootstrap.js +11 -0
- package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
- package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
- package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
- package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
- package/L1_Foundation/bootstrap/module_registry.js +102 -0
- package/L1_Foundation/bootstrap/prism_loader.js +164 -0
- package/L1_Foundation/config/client_config.js +110 -0
- package/L1_Foundation/config/config.js +7 -0
- package/L1_Foundation/connection/connection.js +8 -0
- package/L1_Foundation/connection/websocket_connection.js +122 -0
- package/L1_Foundation/constants/bifrost_constants.js +284 -0
- package/L1_Foundation/constants/constants.js +7 -0
- package/L1_Foundation/logger/logger.js +10 -0
- package/L2_Handling/L2_Handling.js +15 -0
- package/L2_Handling/cache/cache.js +22 -0
- package/L2_Handling/cache/cache_constants.js +69 -0
- package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
- package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
- package/L2_Handling/cache/orchestration/orchestration.js +12 -0
- package/L2_Handling/cache/storage/session_manager.js +289 -0
- package/L2_Handling/cache/storage/storage.js +10 -0
- package/L2_Handling/cache/storage/storage_manager.js +590 -0
- package/L2_Handling/display/composite/composite.js +13 -0
- package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
- package/L2_Handling/display/composite/swiper_renderer.js +564 -0
- package/L2_Handling/display/composite/terminal_renderer.js +922 -0
- package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
- package/L2_Handling/display/display.js +30 -0
- package/L2_Handling/display/feedback/feedback.js +11 -0
- package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
- package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
- package/L2_Handling/display/inputs/button_renderer.js +634 -0
- package/L2_Handling/display/inputs/form_renderer.js +583 -0
- package/L2_Handling/display/inputs/input_renderer.js +658 -0
- package/L2_Handling/display/inputs/inputs.js +12 -0
- package/L2_Handling/display/navigation/menu_renderer.js +206 -0
- package/L2_Handling/display/navigation/navigation.js +11 -0
- package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
- package/L2_Handling/display/orchestration/orchestration.js +11 -0
- package/L2_Handling/display/orchestration/renderer.js +430 -0
- package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
- package/L2_Handling/display/outputs/alert_renderer.js +161 -0
- package/L2_Handling/display/outputs/audio_renderer.js +94 -0
- package/L2_Handling/display/outputs/card_renderer.js +229 -0
- package/L2_Handling/display/outputs/code_renderer.js +66 -0
- package/L2_Handling/display/outputs/dl_renderer.js +131 -0
- package/L2_Handling/display/outputs/header_renderer.js +162 -0
- package/L2_Handling/display/outputs/icon_renderer.js +107 -0
- package/L2_Handling/display/outputs/image_renderer.js +145 -0
- package/L2_Handling/display/outputs/list_renderer.js +190 -0
- package/L2_Handling/display/outputs/outputs.js +19 -0
- package/L2_Handling/display/outputs/table_renderer.js +765 -0
- package/L2_Handling/display/outputs/text_renderer.js +818 -0
- package/L2_Handling/display/outputs/typography_renderer.js +293 -0
- package/L2_Handling/display/outputs/video_renderer.js +116 -0
- package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
- package/L2_Handling/display/primitives/form_primitives.js +526 -0
- package/L2_Handling/display/primitives/generic_containers.js +109 -0
- package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
- package/L2_Handling/display/primitives/link_primitives.js +552 -0
- package/L2_Handling/display/primitives/lists_primitives.js +262 -0
- package/L2_Handling/display/primitives/media_primitives.js +383 -0
- package/L2_Handling/display/primitives/primitives.js +19 -0
- package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
- package/L2_Handling/display/primitives/table_primitives.js +528 -0
- package/L2_Handling/display/primitives/typography_primitives.js +175 -0
- package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
- package/L2_Handling/display/specialized/specialized.js +10 -0
- package/L2_Handling/hooks/hooks.js +9 -0
- package/L2_Handling/hooks/menu_integration.js +57 -0
- package/L2_Handling/hooks/widget_hook_manager.js +292 -0
- package/L2_Handling/message/message.js +8 -0
- package/L2_Handling/message/message_handler.js +701 -0
- package/L2_Handling/navigation/navigation.js +8 -0
- package/L2_Handling/navigation/navigation_manager.js +403 -0
- package/L2_Handling/zhooks/features/cache_live.js +287 -0
- package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
- package/L2_Handling/zhooks/zhooks_manager.js +65 -0
- package/L2_Handling/zvaf/zvaf.js +8 -0
- package/L2_Handling/zvaf/zvaf_manager.js +334 -0
- package/L3_Abstraction/L3_Abstraction.js +12 -0
- package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
- package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
- package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
- package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
- package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
- package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
- package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
- package/L3_Abstraction/renderer/renderer.js +1 -0
- package/L3_Abstraction/session/session.js +1 -0
- package/L4_Orchestration/L4_Orchestration.js +11 -0
- package/L4_Orchestration/client/client.js +1 -0
- package/L4_Orchestration/facade/facade.js +9 -0
- package/L4_Orchestration/facade/manager_registry.js +118 -0
- package/L4_Orchestration/facade/renderer_registry.js +274 -0
- package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
- package/L4_Orchestration/lifecycle/initializer.js +135 -0
- package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
- package/L4_Orchestration/rendering/facade.js +94 -0
- package/L4_Orchestration/rendering/rendering.js +7 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bifrost_client.js +204 -0
- package/bifrost_core.js +1686 -0
- package/docs/ARCHITECTURE.md +111 -0
- package/docs/PROTOCOL.md +106 -0
- package/docs/RENDERERS.md +101 -0
- package/docs/SECURITY.md +92 -0
- package/package.json +24 -0
- package/syntax/prism-zconfig.js +41 -0
- package/syntax/prism-zenv.js +69 -0
- package/syntax/prism-zolo-theme.css +288 -0
- package/syntax/prism-zolo.js +380 -0
- package/syntax/prism-zschema.js +38 -0
- package/syntax/prism-zspark.js +25 -0
- package/syntax/prism-zui.js +68 -0
- package/zSys/accessibility/accessibility.js +10 -0
- package/zSys/accessibility/emoji_accessibility.js +173 -0
- package/zSys/dom/block_utils.js +122 -0
- package/zSys/dom/container_utils.js +370 -0
- package/zSys/dom/dom.js +13 -0
- package/zSys/dom/dom_utils.js +328 -0
- package/zSys/dom/encoding_utils.js +117 -0
- package/zSys/dom/style_utils.js +71 -0
- package/zSys/errors/error_display.js +299 -0
- package/zSys/errors/errors.js +10 -0
- package/zSys/theme/color_utils.js +274 -0
- package/zSys/theme/dark_mode_utils.js +272 -0
- package/zSys/theme/size_utils.js +256 -0
- package/zSys/theme/spacing_utils.js +405 -0
- package/zSys/theme/theme.js +14 -0
- package/zSys/theme/zbase.css +1735 -0
- package/zSys/theme/zbase_inject.js +161 -0
- package/zSys/theme/ztheme_utils.js +305 -0
- package/zSys/validation/error_boundary.js +201 -0
- package/zSys/validation/validation.js +11 -0
- package/zSys/validation/validation_utils.js +238 -0
- package/zSys/zSys.js +14 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3_Abstraction/orchestrator/metadata_processor.js
|
|
3
|
+
*
|
|
4
|
+
* Metadata Processing for Declarative Rendering
|
|
5
|
+
*
|
|
6
|
+
* Handles extraction and application of metadata from YAML data:
|
|
7
|
+
* - _zClass: CSS classes
|
|
8
|
+
* - _zStyle: Inline styles
|
|
9
|
+
* - _zHTML: Element type (div, section, article, etc.)
|
|
10
|
+
* - _zId / zId: Element ID
|
|
11
|
+
* - _zGroup: Grouped rendering context
|
|
12
|
+
* - _zGroupLabel: Label for input groups
|
|
13
|
+
* - _zScripts: Client-side scripts
|
|
14
|
+
*
|
|
15
|
+
* Extracted from zdisplay_orchestrator.js (Phase 4.4a)
|
|
16
|
+
*
|
|
17
|
+
* ════════════════════════════════════════════════════════════════════════════
|
|
18
|
+
* _zClass / _zStyle CONTRACT — SINGLE SOURCE OF TRUTH (do not re-implement)
|
|
19
|
+
* ════════════════════════════════════════════════════════════════════════════
|
|
20
|
+
* applyMetadata() is the ONE place _zClass/_zStyle are read and applied across
|
|
21
|
+
* all three tiers. Renderers must NOT hand-roll className/style parsing.
|
|
22
|
+
*
|
|
23
|
+
* THREE TIERS, ONE FUNCTION:
|
|
24
|
+
* • zBlock — renderBlock / renderChunkProgressive call applyMetadata (overwrite).
|
|
25
|
+
* • zKey — createContainer calls applyMetadata (overwrite).
|
|
26
|
+
* • zEvent — renderZDisplayEvent runs a single APPEND-mode applyMetadata pass on
|
|
27
|
+
* the returned element AFTER the switch. Leaf renderers own only their
|
|
28
|
+
* CONTEXTUAL class (color → zText- / zBtn- / zSignal- variants); they
|
|
29
|
+
* do NOT touch _zClass or _zStyle.
|
|
30
|
+
*
|
|
31
|
+
* CLASS MODES:
|
|
32
|
+
* • overwrite (default): _zClass OWNS className — for freshly-created wrappers.
|
|
33
|
+
* • append (options.append): _zClass LAYERS via classList.add — for event nodes
|
|
34
|
+
* that already carry intrinsic classes (zText, bi-*, zTable…). Dedupes.
|
|
35
|
+
*
|
|
36
|
+
* _zStyle: MERGES onto any existing inline style (indent margin, media max-width),
|
|
37
|
+
* never clobbers it. It is an additive escape hatch.
|
|
38
|
+
*
|
|
39
|
+
* COMPOSITE OPT-OUT (`element.__zMetaScoped = true`):
|
|
40
|
+
* A renderer that returns a WRAPPER but places _zClass on an INNER node sets
|
|
41
|
+
* __zMetaScoped on the wrapper so the central event pass skips it (no mis-target,
|
|
42
|
+
* no double-apply). Current composites: media-with-caption (<figure> → inner
|
|
43
|
+
* <img>/<video>/<audio>) and zTable (.zTable-container → inner <table>).
|
|
44
|
+
* Cases that return null (zDash, in-place table replace) skip the pass entirely
|
|
45
|
+
* and therefore keep their own _zClass handling.
|
|
46
|
+
* ════════════════════════════════════════════════════════════════════════════
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
// Layer 0: Primitives
|
|
50
|
+
import { createSemanticElement } from '../../L2_Handling/display/primitives/semantic_element_primitive.js';
|
|
51
|
+
import { convertStyleToString } from '../../zSys/dom/style_utils.js';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* MetadataProcessor - Extracts and applies metadata to DOM elements
|
|
55
|
+
*/
|
|
56
|
+
export class MetadataProcessor {
|
|
57
|
+
constructor(logger) {
|
|
58
|
+
this.logger = logger;
|
|
59
|
+
|
|
60
|
+
// Define metadata keys that should be skipped during iteration
|
|
61
|
+
this.METADATA_KEYS = [
|
|
62
|
+
'_zClass',
|
|
63
|
+
'_zStyle',
|
|
64
|
+
'_zHTML',
|
|
65
|
+
'_zId',
|
|
66
|
+
'zScripts',
|
|
67
|
+
'_zScripts',
|
|
68
|
+
'_zGroup',
|
|
69
|
+
'_zGroupLabel',
|
|
70
|
+
'zId'
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract metadata from data object (underscore-prefixed keys)
|
|
76
|
+
* @param {Object} data - YAML data object
|
|
77
|
+
* @returns {Object} Metadata object with extracted keys
|
|
78
|
+
*/
|
|
79
|
+
extractMetadata(data) {
|
|
80
|
+
const metadata = {};
|
|
81
|
+
|
|
82
|
+
if (!data || typeof data !== 'object') {
|
|
83
|
+
return metadata;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const [key, value] of Object.entries(data)) {
|
|
87
|
+
if (key.startsWith('_') || key === 'zId') {
|
|
88
|
+
metadata[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return metadata;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if a key is a metadata key (should be skipped during iteration)
|
|
97
|
+
* @param {string} key - Key to check
|
|
98
|
+
* @returns {boolean} True if key is metadata
|
|
99
|
+
*/
|
|
100
|
+
isMetadataKey(key) {
|
|
101
|
+
return this.METADATA_KEYS.includes(key) || key.startsWith('~');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a container element with metadata applied
|
|
106
|
+
* @param {string} zKey - Key for data-zkey attribute
|
|
107
|
+
* @param {Object} metadata - Metadata object
|
|
108
|
+
* @param {Object} logger - Logger instance
|
|
109
|
+
* @returns {HTMLElement} Container element with metadata applied
|
|
110
|
+
*/
|
|
111
|
+
createContainer(zKey, metadata, logger = this.logger) {
|
|
112
|
+
// Use centralized semantic element primitive (SSOT for _zHTML)
|
|
113
|
+
const elementType = metadata._zHTML || 'div';
|
|
114
|
+
const container = createSemanticElement(elementType, {}, logger);
|
|
115
|
+
|
|
116
|
+
// Apply metadata
|
|
117
|
+
this.applyMetadata(container, metadata, zKey, logger);
|
|
118
|
+
|
|
119
|
+
// Add form submit logging for debugging
|
|
120
|
+
if (container.tagName.toLowerCase() === 'form') {
|
|
121
|
+
container.addEventListener('submit', (event) => {
|
|
122
|
+
logger.log(`[MetadataProcessor] Form submitted: ${zKey}`);
|
|
123
|
+
logger.log('[MetadataProcessor] Form data:', new FormData(container));
|
|
124
|
+
logger.log('[MetadataProcessor] Form validity:', container.checkValidity());
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return container;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Normalize a _zClass value (string with comma/space separators, or array)
|
|
133
|
+
* into a clean array of class tokens. SSOT for the "how do I read _zClass"
|
|
134
|
+
* question that every tier (block / key / event) used to answer differently.
|
|
135
|
+
* @param {string|Array} zClass
|
|
136
|
+
* @returns {string[]}
|
|
137
|
+
*/
|
|
138
|
+
normalizeClasses(zClass) {
|
|
139
|
+
if (!zClass) return [];
|
|
140
|
+
const parts = Array.isArray(zClass)
|
|
141
|
+
? zClass
|
|
142
|
+
: (zClass.includes(',') ? zClass.split(',') : zClass.split(' '));
|
|
143
|
+
return parts.map(c => String(c).trim()).filter(Boolean);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Apply metadata to an existing element.
|
|
148
|
+
*
|
|
149
|
+
* Two class modes:
|
|
150
|
+
* - overwrite (default): _zClass OWNS className — used by zBlock/zKey wrappers
|
|
151
|
+
* that are freshly created and have no intrinsic classes.
|
|
152
|
+
* - append (options.append): _zClass LAYERS on top via classList.add — used by
|
|
153
|
+
* zEvent elements that already carry renderer-intrinsic classes (zText,
|
|
154
|
+
* bi-*, zTable …) which must survive. Empty/absent _zClass is a no-op.
|
|
155
|
+
*
|
|
156
|
+
* @param {HTMLElement} element - Element to apply metadata to
|
|
157
|
+
* @param {Object} metadata - Metadata object (_zClass, _zStyle, zId)
|
|
158
|
+
* @param {string} [zKey] - Optional key for data-zkey attribute
|
|
159
|
+
* @param {Object} [logger] - Optional logger instance
|
|
160
|
+
* @param {Object} [options] - { append?: boolean }
|
|
161
|
+
*/
|
|
162
|
+
applyMetadata(element, metadata, zKey = null, logger = this.logger, options = {}) {
|
|
163
|
+
const append = options.append === true;
|
|
164
|
+
|
|
165
|
+
// Apply custom classes
|
|
166
|
+
if (append) {
|
|
167
|
+
// Event tier: layer on top of intrinsic classes, never clear.
|
|
168
|
+
const extra = this.normalizeClasses(metadata._zClass);
|
|
169
|
+
if (extra.length) element.classList.add(...extra);
|
|
170
|
+
} else if (metadata._zClass !== undefined) {
|
|
171
|
+
if (metadata._zClass === '' || metadata._zClass === null) {
|
|
172
|
+
// Empty string or null = no container classes
|
|
173
|
+
element.className = '';
|
|
174
|
+
} else {
|
|
175
|
+
element.className = this.normalizeClasses(metadata._zClass).join(' ');
|
|
176
|
+
}
|
|
177
|
+
} else if (!element.className) {
|
|
178
|
+
// Default: NO classes (bare element, following HTML/CSS convention)
|
|
179
|
+
element.className = '';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Apply inline styles if provided (supports string or nested object).
|
|
183
|
+
// MERGE onto any existing inline style (e.g. a renderer's indent margin or a
|
|
184
|
+
// media element's max-width) instead of clobbering it — _zStyle is an additive
|
|
185
|
+
// escape hatch, not a replacement for intrinsic layout styles.
|
|
186
|
+
if (metadata._zStyle !== undefined && metadata._zStyle !== '' && metadata._zStyle !== null) {
|
|
187
|
+
const cssString = convertStyleToString(metadata._zStyle, logger);
|
|
188
|
+
if (cssString) {
|
|
189
|
+
const existing = (element.getAttribute('style') || '').trim();
|
|
190
|
+
if (existing) {
|
|
191
|
+
const sep = existing.endsWith(';') ? ' ' : '; ';
|
|
192
|
+
element.setAttribute('style', existing + sep + cssString);
|
|
193
|
+
} else {
|
|
194
|
+
element.setAttribute('style', cssString);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Apply custom ID if provided (no underscore = passed to both Bifrost & Terminal)
|
|
200
|
+
if (metadata.zId !== undefined && metadata.zId !== '' && metadata.zId !== null) {
|
|
201
|
+
element.setAttribute('id', metadata.zId);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Add data attribute for debugging/testing
|
|
205
|
+
if (zKey) {
|
|
206
|
+
element.setAttribute('data-zkey', zKey);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Apply classes to an element (helper for adding classes without replacing)
|
|
212
|
+
* @param {HTMLElement} element - Element to add classes to
|
|
213
|
+
* @param {string|Array} classes - Classes to add (string or array)
|
|
214
|
+
*/
|
|
215
|
+
applyClasses(element, classes) {
|
|
216
|
+
if (!classes) return;
|
|
217
|
+
|
|
218
|
+
if (Array.isArray(classes)) {
|
|
219
|
+
element.classList.add(...classes.filter(c => c.trim()));
|
|
220
|
+
} else if (typeof classes === 'string') {
|
|
221
|
+
const classList = classes.includes(',')
|
|
222
|
+
? classes.split(',').map(c => c.trim())
|
|
223
|
+
: classes.split(' ').filter(c => c.trim());
|
|
224
|
+
element.classList.add(...classList);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Check if data has block-level metadata
|
|
230
|
+
* @param {Object} data - Data object to check
|
|
231
|
+
* @returns {boolean} True if data has metadata keys
|
|
232
|
+
*/
|
|
233
|
+
hasBlockMetadata(data) {
|
|
234
|
+
if (!data || typeof data !== 'object') {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
return Object.keys(data).some(k => k.startsWith('_'));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Detect if metadata indicates input-group context
|
|
242
|
+
* @param {Object} metadata - Metadata object
|
|
243
|
+
* @returns {boolean} True if input-group context
|
|
244
|
+
*/
|
|
245
|
+
isInputGroupContext(metadata) {
|
|
246
|
+
return metadata._zGroup === 'input-group' ||
|
|
247
|
+
(metadata._zClass && metadata._zClass.includes('zInputGroup'));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3_Abstraction/orchestrator/navbar_builder.js
|
|
3
|
+
*
|
|
4
|
+
* NavBar Building and Rendering
|
|
5
|
+
*
|
|
6
|
+
* Handles navigation bar construction for both:
|
|
7
|
+
* - Meta NavBar (from zSession config, rendered in <zNavBar> element)
|
|
8
|
+
* - Content NavBar (from ~zNavBar* metadata in YAML content)
|
|
9
|
+
*
|
|
10
|
+
* Delegates actual rendering to NavigationRenderer (L2).
|
|
11
|
+
*
|
|
12
|
+
* Extracted from zdisplay_orchestrator.js (Phase 4.3)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* NavBarBuilder - Constructs and renders navigation bars
|
|
17
|
+
*/
|
|
18
|
+
export class NavBarBuilder {
|
|
19
|
+
constructor(client, logger) {
|
|
20
|
+
this.client = client;
|
|
21
|
+
this.logger = logger;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 3A: Wire event delegation onto a server-built navbar element.
|
|
26
|
+
*
|
|
27
|
+
* Python emits data-nav-action="navigate|dropdown-toggle|hamburger" so the
|
|
28
|
+
* client needs zero construction logic — only generic interaction handling.
|
|
29
|
+
*
|
|
30
|
+
* @param {HTMLElement} navEl - The <nav> element injected from nav_html
|
|
31
|
+
* @param {Object} client - BifrostClient (for navigationManager access)
|
|
32
|
+
* @param {Object} logger - Logger instance
|
|
33
|
+
*/
|
|
34
|
+
static wireNavBarEvents(navEl, client, logger) {
|
|
35
|
+
if (!navEl) return;
|
|
36
|
+
|
|
37
|
+
// ── Hamburger (mobile toggle) ────────────────────────────────────
|
|
38
|
+
const toggler = navEl.querySelector('[data-nav-action="hamburger"]');
|
|
39
|
+
if (toggler) {
|
|
40
|
+
const targetId = toggler.dataset.navTarget;
|
|
41
|
+
const collapse = navEl.querySelector(`#${targetId}`);
|
|
42
|
+
toggler.addEventListener('click', (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
const expanded = toggler.getAttribute('aria-expanded') === 'true';
|
|
45
|
+
toggler.setAttribute('aria-expanded', String(!expanded));
|
|
46
|
+
collapse?.classList.toggle('show', !expanded);
|
|
47
|
+
collapse?.classList.toggle('zShow', !expanded);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Dropdown toggles ─────────────────────────────────────────────
|
|
52
|
+
navEl.querySelectorAll('[data-nav-action="dropdown-toggle"]').forEach(toggle => {
|
|
53
|
+
const menu = toggle.nextElementSibling;
|
|
54
|
+
toggle.addEventListener('click', (e) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
const isOpen = menu?.classList.contains('zShow');
|
|
57
|
+
|
|
58
|
+
// Second click on an already-open dropdown → navigate to the parent page
|
|
59
|
+
if (isOpen) {
|
|
60
|
+
const href = toggle.getAttribute('href') || toggle.dataset.navHref;
|
|
61
|
+
if (href && href !== '#') {
|
|
62
|
+
menu.classList.remove('zShow');
|
|
63
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
64
|
+
if (client?.navigationManager) {
|
|
65
|
+
// navbar:true → server RESETs the crumb trail (SSOT with zCLI:
|
|
66
|
+
// a navbar pick becomes the new root). Browser Back/popstate does
|
|
67
|
+
// NOT pass this, so it still pops the stack normally.
|
|
68
|
+
client.navigationManager.navigateToRoute(href, { navbar: true });
|
|
69
|
+
} else {
|
|
70
|
+
window.location.href = href;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Close all sibling dropdowns
|
|
77
|
+
navEl.querySelectorAll('.zDropdown-menu.zShow').forEach(m => {
|
|
78
|
+
if (m !== menu) {
|
|
79
|
+
m.classList.remove('zShow');
|
|
80
|
+
m.previousElementSibling?.setAttribute('aria-expanded', 'false');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
menu?.classList.toggle('zShow', !isOpen);
|
|
84
|
+
toggle.setAttribute('aria-expanded', String(!isOpen));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ── Navigation links ─────────────────────────────────────────────
|
|
89
|
+
navEl.querySelectorAll('[data-nav-action="navigate"]').forEach(link => {
|
|
90
|
+
link.addEventListener('click', (e) => {
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
const href = link.dataset.navHref || link.getAttribute('href');
|
|
93
|
+
// Close enclosing dropdown (if any)
|
|
94
|
+
const menu = link.closest('.zDropdown-menu');
|
|
95
|
+
if (menu) {
|
|
96
|
+
menu.classList.remove('zShow');
|
|
97
|
+
menu.previousElementSibling?.setAttribute('aria-expanded', 'false');
|
|
98
|
+
}
|
|
99
|
+
if (client?.navigationManager) {
|
|
100
|
+
// navbar:true → server RESETs the crumb trail (SSOT with zCLI navbar).
|
|
101
|
+
client.navigationManager.navigateToRoute(href, { navbar: true });
|
|
102
|
+
} else {
|
|
103
|
+
window.location.href = href;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ── Close dropdowns on outside click ─────────────────────────────
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
document.addEventListener('click', (e) => {
|
|
111
|
+
if (!navEl.contains(e.target)) {
|
|
112
|
+
navEl.querySelectorAll('.zDropdown-menu.zShow').forEach(menu => {
|
|
113
|
+
menu.classList.remove('zShow');
|
|
114
|
+
menu.previousElementSibling?.setAttribute('aria-expanded', 'false');
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}, 100);
|
|
119
|
+
|
|
120
|
+
if (logger) logger.debug('[NavBarBuilder] wireNavBarEvents attached');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Render navbar DOM element (v1.6.1: Returns DOM element to preserve event listeners)
|
|
125
|
+
* Used for meta navbar (from zSession config) rendered in <zNavBar> element
|
|
126
|
+
* @param {Array} items - Navbar items (e.g., ['zVaF', 'zAbout', '^zLogin'])
|
|
127
|
+
* @param {Object} options - Client options (for title/brand)
|
|
128
|
+
* @returns {Promise<HTMLElement|null>} Navbar DOM element
|
|
129
|
+
*/
|
|
130
|
+
async renderMetaNavBarHTML(items, options) {
|
|
131
|
+
this.logger.debug('[NavBarBuilder] renderMetaNavBarHTML called:', items.length);
|
|
132
|
+
|
|
133
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
134
|
+
this.logger.warn('[NavBarBuilder] [WARN] No navbar items provided');
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Load navigation renderer
|
|
140
|
+
const navRenderer = await this.client._ensureNavigationRenderer();
|
|
141
|
+
|
|
142
|
+
// Render navbar element
|
|
143
|
+
const navElement = navRenderer.renderNavBar(items, {
|
|
144
|
+
className: 'zcli-navbar-meta',
|
|
145
|
+
theme: 'light',
|
|
146
|
+
href: (item) => {
|
|
147
|
+
// Strip modifiers (^ for bounce-back, ~ for anchor) from URL
|
|
148
|
+
const cleanItem = item.replace(/^[\^~]+/, '');
|
|
149
|
+
return `/${cleanItem}`;
|
|
150
|
+
},
|
|
151
|
+
brand: options.brand || options.title // Use dedicated brand field (always zSpark)
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// FIX v1.6.1: Return DOM element directly (NOT outerHTML!)
|
|
155
|
+
// This preserves event listeners attached by link_primitives.js
|
|
156
|
+
// The caller (zvaf_manager.js) will append the element instead of setting innerHTML
|
|
157
|
+
this.logger.log('[NavBarBuilder] Returning navbar DOM element (preserves event listeners)');
|
|
158
|
+
return navElement;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
this.logger.error('[NavBarBuilder] Failed to render navbar element:', error);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Render navigation bar from metadata (~zNavBar* in content)
|
|
167
|
+
* Used for content-embedded navbars in YAML
|
|
168
|
+
* @param {Array} items - Navbar items
|
|
169
|
+
* @param {HTMLElement} parentElement - Parent element to append to
|
|
170
|
+
*/
|
|
171
|
+
async renderNavBar(items, parentElement) {
|
|
172
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
173
|
+
this.logger.warn('[NavBarBuilder] ~zNavBar* has no items or is not an array');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
// Load navigation renderer
|
|
179
|
+
const navRenderer = await this.client._ensureNavigationRenderer();
|
|
180
|
+
|
|
181
|
+
// Render navbar with zTheme zNavbar component
|
|
182
|
+
const navElement = navRenderer.renderNavBar(items, {
|
|
183
|
+
theme: 'light'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (navElement) {
|
|
187
|
+
parentElement.appendChild(navElement);
|
|
188
|
+
|
|
189
|
+
// Re-initialize zTheme collapse now that navbar is in DOM
|
|
190
|
+
if (window.zTheme && typeof window.zTheme.initCollapse === 'function') {
|
|
191
|
+
window.zTheme.initCollapse();
|
|
192
|
+
this.logger.log('[NavBarBuilder] Re-initialized zTheme collapse for navbar');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.logger.log('[NavBarBuilder] Rendered navigation bar with items:', items);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
this.logger.error('[NavBarBuilder] Failed to render navigation bar:', error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3_Abstraction/orchestrator - Display Orchestration Components
|
|
3
|
+
*
|
|
4
|
+
* High-level orchestration for declarative rendering pipeline.
|
|
5
|
+
* Coordinates L2 display handlers and manages rendering flow.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { WizardGateHandler } from './wizard_gate_handler.js';
|
|
9
|
+
export { NavBarBuilder } from './navbar_builder.js';
|
|
10
|
+
export { MetadataProcessor } from './metadata_processor.js';
|
|
11
|
+
export { GroupRenderer } from './group_renderer.js';
|
|
12
|
+
export { ContainerUnwrapper } from './container_unwrapper.js';
|
|
13
|
+
export { InputEventHandler } from './input_event_handler.js';
|