@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,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Form Primitives - Data Input Elements
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* HTML form elements for user data input and submission.
|
|
7
|
+
* Covers all 18 input types plus form structure elements.
|
|
8
|
+
*
|
|
9
|
+
* @module rendering/form_primitives
|
|
10
|
+
* @layer 0.0 (RAWEST - form elements)
|
|
11
|
+
* @pattern Pure Factory Functions
|
|
12
|
+
*
|
|
13
|
+
* Philosophy:
|
|
14
|
+
* - Semantic data input (type enforcement)
|
|
15
|
+
* - Accessibility (labels, fieldsets, ARIA)
|
|
16
|
+
* - Native validation (email, url, number, etc.)
|
|
17
|
+
* - NO styling, NO classes (dress up later)
|
|
18
|
+
*
|
|
19
|
+
* Input Types Supported (18 total):
|
|
20
|
+
* - Text-based: text, email, password, search, tel, url
|
|
21
|
+
* - Numeric: number, range
|
|
22
|
+
* - Date/Time: date, datetime-local, month, week, time
|
|
23
|
+
* - Selection: checkbox, radio
|
|
24
|
+
* - File: file
|
|
25
|
+
* - Special: color, hidden
|
|
26
|
+
*
|
|
27
|
+
* Form Structure:
|
|
28
|
+
* - <form>: Container with submit handling
|
|
29
|
+
* - <fieldset> + <legend>: Group related inputs
|
|
30
|
+
* - <label>: Associate text with input (accessibility)
|
|
31
|
+
* - <input>: 18 different types
|
|
32
|
+
* - <textarea>: Multi-line text
|
|
33
|
+
* - <select> + <option> + <optgroup>: Dropdown menus
|
|
34
|
+
*
|
|
35
|
+
* Dependencies:
|
|
36
|
+
* - utils/dom_utils.js (createElement, setAttributes)
|
|
37
|
+
*
|
|
38
|
+
* Exports:
|
|
39
|
+
* - createInput(type, attributes) → HTMLInputElement
|
|
40
|
+
* - createTextarea(attributes) → HTMLTextAreaElement
|
|
41
|
+
* - createSelect(attributes) → HTMLSelectElement
|
|
42
|
+
* - createOption(value, text, attributes) → HTMLOptionElement
|
|
43
|
+
* - createOptgroup(label, attributes) → HTMLOptGroupElement
|
|
44
|
+
* - createLabel(forId, attributes) → HTMLLabelElement
|
|
45
|
+
* - createForm(attributes) → HTMLFormElement
|
|
46
|
+
* - createFieldset(attributes) → HTMLFieldSetElement
|
|
47
|
+
* - createLegend(attributes) → HTMLLegendElement
|
|
48
|
+
*
|
|
49
|
+
* Example:
|
|
50
|
+
* ```javascript
|
|
51
|
+
* import { createForm, createInput, createLabel } from './form_primitives.js';
|
|
52
|
+
*
|
|
53
|
+
* const form = createForm({ id: 'login' });
|
|
54
|
+
* const label = createLabel('email');
|
|
55
|
+
* label.textContent = 'Email:';
|
|
56
|
+
* const input = createInput('email', { id: 'email', required: true });
|
|
57
|
+
* form.appendChild(label);
|
|
58
|
+
* form.appendChild(input);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
// ─────────────────────────────────────────────────────────────────
|
|
63
|
+
// Imports
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
// Layer 2: Utilities
|
|
67
|
+
import { createElement, setAttributes } from '../../../zSys/dom/dom_utils.js';
|
|
68
|
+
|
|
69
|
+
//
|
|
70
|
+
// Input Element (18 Types)
|
|
71
|
+
//
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Valid HTML5 input types (18 total)
|
|
75
|
+
* Button-like types (submit, reset, button, image) are EXCLUDED - use <button> instead
|
|
76
|
+
*/
|
|
77
|
+
const VALID_INPUT_TYPES = [
|
|
78
|
+
// Text-based inputs
|
|
79
|
+
'text', 'email', 'password', 'search', 'tel', 'url',
|
|
80
|
+
// Numeric inputs
|
|
81
|
+
'number', 'range',
|
|
82
|
+
// Date/Time inputs
|
|
83
|
+
'date', 'datetime-local', 'month', 'week', 'time',
|
|
84
|
+
// Selection inputs
|
|
85
|
+
'checkbox', 'radio',
|
|
86
|
+
// File input
|
|
87
|
+
'file',
|
|
88
|
+
// Special inputs
|
|
89
|
+
'color', 'hidden'
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create an <input> element
|
|
94
|
+
*
|
|
95
|
+
* Supports all 18 semantic HTML5 input types.
|
|
96
|
+
* Type is validated and defaults to 'text'.
|
|
97
|
+
*
|
|
98
|
+
* INPUT TYPE REFERENCE:
|
|
99
|
+
*
|
|
100
|
+
* TEXT-BASED (6 types):
|
|
101
|
+
* - text: Plain text input (default)
|
|
102
|
+
* - email: Email address (native validation, keyboard optimized on mobile)
|
|
103
|
+
* - password: Hidden text (shows dots/asterisks)
|
|
104
|
+
* - search: Search queries (may show clear button)
|
|
105
|
+
* - tel: Telephone number (numeric keyboard on mobile)
|
|
106
|
+
* - url: URL input (native validation, keyboard optimized on mobile)
|
|
107
|
+
*
|
|
108
|
+
* NUMERIC (2 types):
|
|
109
|
+
* - number: Numeric input with steppers (supports min, max, step)
|
|
110
|
+
* - range: Slider control (supports min, max, step, value)
|
|
111
|
+
*
|
|
112
|
+
* DATE/TIME (5 types):
|
|
113
|
+
* - date: Date picker (YYYY-MM-DD)
|
|
114
|
+
* - datetime-local: Date + time picker (no timezone)
|
|
115
|
+
* - month: Month picker (YYYY-MM)
|
|
116
|
+
* - week: Week picker (YYYY-W##)
|
|
117
|
+
* - time: Time picker (HH:MM or HH:MM:SS)
|
|
118
|
+
*
|
|
119
|
+
* SELECTION (2 types):
|
|
120
|
+
* - checkbox: Toggle checkbox (supports checked)
|
|
121
|
+
* - radio: Radio button (group via same name attribute)
|
|
122
|
+
*
|
|
123
|
+
* FILE (1 type):
|
|
124
|
+
* - file: File upload (supports accept, multiple)
|
|
125
|
+
*
|
|
126
|
+
* SPECIAL (2 types):
|
|
127
|
+
* - color: Color picker (returns hex #RRGGBB)
|
|
128
|
+
* - hidden: Hidden input (no UI, stores value)
|
|
129
|
+
*
|
|
130
|
+
* @param {string} [type='text'] - Input type (one of 18 valid types)
|
|
131
|
+
* @param {Object} [attributes={}] - HTML attributes (id, name, required, placeholder, etc.)
|
|
132
|
+
* @returns {HTMLInputElement} The created input element
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Text input
|
|
136
|
+
* const username = createInput('text', { id: 'username', name: 'username', required: true });
|
|
137
|
+
*
|
|
138
|
+
* // Email input (native validation)
|
|
139
|
+
* const email = createInput('email', { id: 'email', placeholder: 'you@example.com' });
|
|
140
|
+
*
|
|
141
|
+
* // Password input
|
|
142
|
+
* const password = createInput('password', { id: 'pwd', autocomplete: 'current-password' });
|
|
143
|
+
*
|
|
144
|
+
* // Number input with constraints
|
|
145
|
+
* const age = createInput('number', { min: 18, max: 120, step: 1 });
|
|
146
|
+
*
|
|
147
|
+
* // Date input
|
|
148
|
+
* const birthday = createInput('date', { id: 'dob', min: '1900-01-01', max: '2024-12-31' });
|
|
149
|
+
*
|
|
150
|
+
* // Checkbox
|
|
151
|
+
* const agree = createInput('checkbox', { id: 'agree', name: 'agree', value: 'yes' });
|
|
152
|
+
*
|
|
153
|
+
* // Radio button (group by same name)
|
|
154
|
+
* const radio1 = createInput('radio', { name: 'plan', value: 'basic', checked: true });
|
|
155
|
+
* const radio2 = createInput('radio', { name: 'plan', value: 'pro' });
|
|
156
|
+
*
|
|
157
|
+
* // File upload (multiple files, accept images only)
|
|
158
|
+
* const fileInput = createInput('file', { accept: 'image/*', multiple: true });
|
|
159
|
+
*
|
|
160
|
+
* // Color picker
|
|
161
|
+
* const colorPicker = createInput('color', { value: '#ff0000' });
|
|
162
|
+
*
|
|
163
|
+
* // Hidden input
|
|
164
|
+
* const hiddenToken = createInput('hidden', { name: 'csrf_token', value: 'abc123' });
|
|
165
|
+
*/
|
|
166
|
+
export function createInput(type = 'text', attributes = {}) {
|
|
167
|
+
const input = createElement('input');
|
|
168
|
+
|
|
169
|
+
// Validate input type (default to 'text')
|
|
170
|
+
const validType = VALID_INPUT_TYPES.includes(type) ? type : 'text';
|
|
171
|
+
|
|
172
|
+
if (type && !VALID_INPUT_TYPES.includes(type)) {
|
|
173
|
+
console.warn(`[form_primitives] createInput: Invalid type "${type}", defaulting to "text". Valid types: ${VALID_INPUT_TYPES.join(', ')}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Sensible defaults
|
|
177
|
+
const defaults = {
|
|
178
|
+
type: validType
|
|
179
|
+
// TODO: Add opt-in autocomplete control later (removed default autocomplete: 'off')
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Merge defaults with user attributes (user attributes take precedence)
|
|
183
|
+
setAttributes(input, { ...defaults, ...attributes });
|
|
184
|
+
|
|
185
|
+
return input;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//
|
|
189
|
+
// Textarea Element (Multi-line Text)
|
|
190
|
+
//
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Create a <textarea> element
|
|
194
|
+
*
|
|
195
|
+
* Used for multi-line text input (comments, messages, descriptions).
|
|
196
|
+
* Separate element from <input>, NOT an input type.
|
|
197
|
+
*
|
|
198
|
+
* Common Attributes:
|
|
199
|
+
* - rows: Number of visible text rows (default browser behavior ~2)
|
|
200
|
+
* - cols: Width in characters (default browser behavior ~20)
|
|
201
|
+
* - maxlength: Maximum character count
|
|
202
|
+
* - placeholder: Placeholder text
|
|
203
|
+
* - required: Make field required
|
|
204
|
+
* - wrap: Text wrapping (soft/hard)
|
|
205
|
+
*
|
|
206
|
+
* @param {Object} [attributes={}] - HTML attributes (id, name, rows, cols, placeholder, etc.)
|
|
207
|
+
* @returns {HTMLTextAreaElement} The created textarea element
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* // Basic textarea
|
|
211
|
+
* const comments = createTextarea({ id: 'comments', name: 'comments' });
|
|
212
|
+
*
|
|
213
|
+
* // Textarea with size constraints
|
|
214
|
+
* const message = createTextarea({
|
|
215
|
+
* rows: 5,
|
|
216
|
+
* cols: 50,
|
|
217
|
+
* maxlength: 500,
|
|
218
|
+
* placeholder: 'Enter your message...'
|
|
219
|
+
* });
|
|
220
|
+
*
|
|
221
|
+
* // Required textarea
|
|
222
|
+
* const feedback = createTextarea({ id: 'feedback', required: true });
|
|
223
|
+
*/
|
|
224
|
+
export function createTextarea(attributes = {}) {
|
|
225
|
+
const textarea = createElement('textarea');
|
|
226
|
+
|
|
227
|
+
if (Object.keys(attributes).length > 0) {
|
|
228
|
+
setAttributes(textarea, attributes);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return textarea;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
//
|
|
235
|
+
// Select Element (Dropdown Menu)
|
|
236
|
+
//
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Create a <select> element
|
|
240
|
+
*
|
|
241
|
+
* Dropdown menu for selecting one or multiple options.
|
|
242
|
+
* Contains <option> elements (and optionally <optgroup>).
|
|
243
|
+
*
|
|
244
|
+
* Common Attributes:
|
|
245
|
+
* - multiple: Allow multiple selections (changes to listbox)
|
|
246
|
+
* - size: Number of visible options (for listbox mode)
|
|
247
|
+
* - required: Make selection required
|
|
248
|
+
* - disabled: Disable entire select
|
|
249
|
+
*
|
|
250
|
+
* @param {Object} [attributes={}] - HTML attributes (id, name, multiple, required, etc.)
|
|
251
|
+
* @returns {HTMLSelectElement} The created select element
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* // Basic dropdown
|
|
255
|
+
* const country = createSelect({ id: 'country', name: 'country' });
|
|
256
|
+
*
|
|
257
|
+
* // Multiple selection listbox
|
|
258
|
+
* const skills = createSelect({ id: 'skills', name: 'skills', multiple: true, size: 5 });
|
|
259
|
+
*
|
|
260
|
+
* // Required select
|
|
261
|
+
* const plan = createSelect({ id: 'plan', name: 'plan', required: true });
|
|
262
|
+
*/
|
|
263
|
+
export function createSelect(attributes = {}) {
|
|
264
|
+
const select = createElement('select');
|
|
265
|
+
|
|
266
|
+
if (Object.keys(attributes).length > 0) {
|
|
267
|
+
setAttributes(select, attributes);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return select;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Create an <option> element
|
|
275
|
+
*
|
|
276
|
+
* Individual option within a <select> dropdown.
|
|
277
|
+
* Must be a child of <select> or <optgroup>.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} value - The option value (submitted with form)
|
|
280
|
+
* @param {string} text - The visible text for the option
|
|
281
|
+
* @param {Object} [attributes={}] - HTML attributes (selected, disabled, etc.)
|
|
282
|
+
* @returns {HTMLOptionElement} The created option element
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* // Basic option
|
|
286
|
+
* const opt1 = createOption('us', 'United States');
|
|
287
|
+
*
|
|
288
|
+
* // Selected option
|
|
289
|
+
* const opt2 = createOption('ca', 'Canada', { selected: true });
|
|
290
|
+
*
|
|
291
|
+
* // Disabled option
|
|
292
|
+
* const opt3 = createOption('', '-- Select Country --', { disabled: true, selected: true });
|
|
293
|
+
*/
|
|
294
|
+
export function createOption(value, text, attributes = {}) {
|
|
295
|
+
const option = createElement('option');
|
|
296
|
+
|
|
297
|
+
// Set value and text
|
|
298
|
+
option.value = value;
|
|
299
|
+
option.textContent = text;
|
|
300
|
+
|
|
301
|
+
if (Object.keys(attributes).length > 0) {
|
|
302
|
+
setAttributes(option, attributes);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return option;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Create an <optgroup> element
|
|
310
|
+
*
|
|
311
|
+
* Groups related options within a <select> dropdown.
|
|
312
|
+
* Must be a child of <select>, contains <option> elements.
|
|
313
|
+
*
|
|
314
|
+
* @param {string} label - The visible label for the group
|
|
315
|
+
* @param {Object} [attributes={}] - HTML attributes (disabled, etc.)
|
|
316
|
+
* @returns {HTMLOptGroupElement} The created optgroup element
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* // Option group
|
|
320
|
+
* const northAmerica = createOptgroup('North America');
|
|
321
|
+
* northAmerica.appendChild(createOption('us', 'United States'));
|
|
322
|
+
* northAmerica.appendChild(createOption('ca', 'Canada'));
|
|
323
|
+
*
|
|
324
|
+
* const select = createSelect({ id: 'country' });
|
|
325
|
+
* select.appendChild(northAmerica);
|
|
326
|
+
*/
|
|
327
|
+
export function createOptgroup(label, attributes = {}) {
|
|
328
|
+
const optgroup = createElement('optgroup');
|
|
329
|
+
|
|
330
|
+
// Set label
|
|
331
|
+
optgroup.label = label;
|
|
332
|
+
|
|
333
|
+
if (Object.keys(attributes).length > 0) {
|
|
334
|
+
setAttributes(optgroup, attributes);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return optgroup;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
//
|
|
341
|
+
// Label Element (Accessibility)
|
|
342
|
+
//
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Create a <label> element
|
|
346
|
+
*
|
|
347
|
+
* Associates descriptive text with form inputs (critical for accessibility).
|
|
348
|
+
* Two association methods:
|
|
349
|
+
* 1. "for" attribute pointing to input id (recommended)
|
|
350
|
+
* 2. Wrapping the input element (implicit association)
|
|
351
|
+
*
|
|
352
|
+
* Best Practice:
|
|
353
|
+
* - ALWAYS provide labels for inputs (except hidden)
|
|
354
|
+
* - Use "for" attribute for explicit association
|
|
355
|
+
* - Clicking label focuses/toggles associated input
|
|
356
|
+
*
|
|
357
|
+
* @param {string} [forId=''] - ID of the associated input element
|
|
358
|
+
* @param {Object} [attributes={}] - HTML attributes (id, class, etc.)
|
|
359
|
+
* @returns {HTMLLabelElement} The created label element
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* // Explicit association (recommended)
|
|
363
|
+
* const label = createLabel('email', { class: 'form-label' });
|
|
364
|
+
* label.textContent = 'Email Address:';
|
|
365
|
+
* const input = createInput('email', { id: 'email' });
|
|
366
|
+
*
|
|
367
|
+
* // Implicit association (wrapping)
|
|
368
|
+
* const wrapperLabel = createLabel();
|
|
369
|
+
* wrapperLabel.textContent = 'Username: ';
|
|
370
|
+
* wrapperLabel.appendChild(createInput('text', { name: 'username' }));
|
|
371
|
+
*/
|
|
372
|
+
export function createLabel(forId = '', attributes = {}) {
|
|
373
|
+
const label = createElement('label');
|
|
374
|
+
|
|
375
|
+
if (forId) {
|
|
376
|
+
setAttributes(label, { for: forId, ...attributes });
|
|
377
|
+
} else if (Object.keys(attributes).length > 0) {
|
|
378
|
+
setAttributes(label, attributes);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return label;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
//
|
|
385
|
+
// Form Element (Container)
|
|
386
|
+
//
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Create a <form> element
|
|
390
|
+
*
|
|
391
|
+
* Container for form inputs and submission handling.
|
|
392
|
+
* Provides native validation and submit events.
|
|
393
|
+
*
|
|
394
|
+
* Common Attributes:
|
|
395
|
+
* - action: URL to submit form data (default: current page)
|
|
396
|
+
* - method: HTTP method (GET or POST, default: GET)
|
|
397
|
+
* - enctype: Encoding type (for file uploads: multipart/form-data)
|
|
398
|
+
* - novalidate: Disable native HTML5 validation
|
|
399
|
+
* - autocomplete: Enable/disable autocomplete (on/off)
|
|
400
|
+
*
|
|
401
|
+
* Best Practice:
|
|
402
|
+
* - Use method="POST" for data modification
|
|
403
|
+
* - Use method="GET" for searches (idempotent)
|
|
404
|
+
* - Add enctype="multipart/form-data" for file uploads
|
|
405
|
+
* - Listen to 'submit' event, call preventDefault() for AJAX
|
|
406
|
+
*
|
|
407
|
+
* @param {Object} [attributes={}] - HTML attributes (action, method, enctype, etc.)
|
|
408
|
+
* @returns {HTMLFormElement} The created form element
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* // Basic POST form
|
|
412
|
+
* const loginForm = createForm({
|
|
413
|
+
* id: 'login',
|
|
414
|
+
* method: 'POST',
|
|
415
|
+
* action: '/api/login'
|
|
416
|
+
* });
|
|
417
|
+
*
|
|
418
|
+
* // Search form (GET)
|
|
419
|
+
* const searchForm = createForm({
|
|
420
|
+
* method: 'GET',
|
|
421
|
+
* action: '/search'
|
|
422
|
+
* });
|
|
423
|
+
*
|
|
424
|
+
* // File upload form
|
|
425
|
+
* const uploadForm = createForm({
|
|
426
|
+
* method: 'POST',
|
|
427
|
+
* enctype: 'multipart/form-data'
|
|
428
|
+
* });
|
|
429
|
+
*
|
|
430
|
+
* // AJAX form (prevent default submission)
|
|
431
|
+
* const ajaxForm = createForm({ id: 'contact' });
|
|
432
|
+
* ajaxForm.addEventListener('submit', (e) => {
|
|
433
|
+
* e.preventDefault();
|
|
434
|
+
* // Handle with fetch/AJAX
|
|
435
|
+
* });
|
|
436
|
+
*/
|
|
437
|
+
export function createForm(attributes = {}) {
|
|
438
|
+
const form = createElement('form');
|
|
439
|
+
|
|
440
|
+
if (Object.keys(attributes).length > 0) {
|
|
441
|
+
setAttributes(form, attributes);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return form;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
//
|
|
448
|
+
// Fieldset + Legend (Form Grouping)
|
|
449
|
+
//
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Create a <fieldset> element
|
|
453
|
+
*
|
|
454
|
+
* Groups related form controls with a visual border.
|
|
455
|
+
* Used for semantic grouping and accessibility.
|
|
456
|
+
* First child is typically <legend> (the group's caption).
|
|
457
|
+
*
|
|
458
|
+
* Best Practice:
|
|
459
|
+
* - Group related inputs (e.g., address fields, payment info)
|
|
460
|
+
* - Use <legend> as first child for the group title
|
|
461
|
+
* - Can be disabled (disables all contained inputs)
|
|
462
|
+
*
|
|
463
|
+
* @param {Object} [attributes={}] - HTML attributes (id, disabled, etc.)
|
|
464
|
+
* @returns {HTMLFieldSetElement} The created fieldset element
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* // Address fieldset
|
|
468
|
+
* const addressFields = createFieldset({ id: 'address' });
|
|
469
|
+
* const legend = createLegend();
|
|
470
|
+
* legend.textContent = 'Shipping Address';
|
|
471
|
+
* addressFields.appendChild(legend);
|
|
472
|
+
*
|
|
473
|
+
* // Disabled fieldset (disables all inputs inside)
|
|
474
|
+
* const disabledFields = createFieldset({ disabled: true });
|
|
475
|
+
*/
|
|
476
|
+
export function createFieldset(attributes = {}) {
|
|
477
|
+
const fieldset = createElement('fieldset');
|
|
478
|
+
|
|
479
|
+
if (Object.keys(attributes).length > 0) {
|
|
480
|
+
setAttributes(fieldset, attributes);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return fieldset;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Create a <legend> element
|
|
488
|
+
*
|
|
489
|
+
* Caption/title for a <fieldset> group.
|
|
490
|
+
* Must be the first child of <fieldset>.
|
|
491
|
+
*
|
|
492
|
+
* @param {Object} [attributes={}] - HTML attributes (id, class, etc.)
|
|
493
|
+
* @returns {HTMLLegendElement} The created legend element
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* // Fieldset with legend
|
|
497
|
+
* const fieldset = createFieldset();
|
|
498
|
+
* const legend = createLegend({ class: 'fieldset-title' });
|
|
499
|
+
* legend.textContent = 'Personal Information';
|
|
500
|
+
* fieldset.appendChild(legend);
|
|
501
|
+
*/
|
|
502
|
+
export function createLegend(attributes = {}) {
|
|
503
|
+
const legend = createElement('legend');
|
|
504
|
+
|
|
505
|
+
if (Object.keys(attributes).length > 0) {
|
|
506
|
+
setAttributes(legend, attributes);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return legend;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
//
|
|
513
|
+
// Default Export (for convenience)
|
|
514
|
+
//
|
|
515
|
+
export default {
|
|
516
|
+
createInput,
|
|
517
|
+
createTextarea,
|
|
518
|
+
createSelect,
|
|
519
|
+
createOption,
|
|
520
|
+
createOptgroup,
|
|
521
|
+
createLabel,
|
|
522
|
+
createForm,
|
|
523
|
+
createFieldset,
|
|
524
|
+
createLegend
|
|
525
|
+
};
|
|
526
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Generic Containers - RAWEST HTML Primitives (Block + Inline)
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* The most atomic building blocks: <div> and <span>.
|
|
7
|
+
* These are semantic-free containers used purely for layout and grouping.
|
|
8
|
+
*
|
|
9
|
+
* @module rendering/generic_containers
|
|
10
|
+
* @layer 0.0 (RAWEST - true bottom)
|
|
11
|
+
* @pattern Pure Factory Functions
|
|
12
|
+
*
|
|
13
|
+
* Philosophy:
|
|
14
|
+
* - Block-level (<div>) for layout/structure
|
|
15
|
+
* - Inline (<span>) for phrasing content
|
|
16
|
+
* - NO styling, NO classes (dress up later)
|
|
17
|
+
* - Accept raw HTML attributes only
|
|
18
|
+
*
|
|
19
|
+
* Dependencies:
|
|
20
|
+
* - utils/dom_utils.js (createElement, setAttributes)
|
|
21
|
+
*
|
|
22
|
+
* Exports:
|
|
23
|
+
* - createDiv(attributes) → HTMLDivElement
|
|
24
|
+
* - createSpan(attributes) → HTMLSpanElement
|
|
25
|
+
*
|
|
26
|
+
* Example:
|
|
27
|
+
* ```javascript
|
|
28
|
+
* import { createDiv, createSpan } from './generic_containers.js';
|
|
29
|
+
*
|
|
30
|
+
* // Raw containers
|
|
31
|
+
* const container = createDiv({ id: 'main' });
|
|
32
|
+
* const badge = createSpan({ class: 'badge', 'data-count': '5' });
|
|
33
|
+
*
|
|
34
|
+
* // Dress up later with utilities
|
|
35
|
+
* container.classList.add('zContainer', 'zP-3');
|
|
36
|
+
* badge.classList.add('zBadge', 'zBadge-primary');
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────
|
|
41
|
+
// Imports
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
// Layer 2: Utilities
|
|
45
|
+
import { createElement, setAttributes, createDiv } from '../../../zSys/dom/dom_utils.js';
|
|
46
|
+
|
|
47
|
+
//
|
|
48
|
+
// Re-exports from utils/dom_utils.js
|
|
49
|
+
//
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Re-export createDiv from dom_utils.js for convenience
|
|
53
|
+
*
|
|
54
|
+
* NOTE: createDiv is now defined in Layer 2 (utils/dom_utils.js) to maintain
|
|
55
|
+
* proper architectural layering. This file (Layer 3 - Renderers) re-exports
|
|
56
|
+
* it for backward compatibility with existing renderer code.
|
|
57
|
+
*
|
|
58
|
+
* See: utils/dom_utils.js for the actual implementation
|
|
59
|
+
*/
|
|
60
|
+
export { createDiv };
|
|
61
|
+
|
|
62
|
+
//
|
|
63
|
+
// Inline Container
|
|
64
|
+
//
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a <span> element (inline container)
|
|
68
|
+
*
|
|
69
|
+
* The most generic inline container, used for:
|
|
70
|
+
* - Wrapping phrasing content (text, icons)
|
|
71
|
+
* - Badges, labels, tags
|
|
72
|
+
* - Inline styling/scripting hooks
|
|
73
|
+
* - Semantic-free inline containers (when <strong>, <em>, etc. don't apply)
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} [attributes={}] - HTML attributes (id, class, data-*, aria-*, etc.)
|
|
76
|
+
* @returns {HTMLSpanElement} The created span element
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Basic span (no attributes)
|
|
80
|
+
* const highlight = createSpan();
|
|
81
|
+
* highlight.textContent = 'Important';
|
|
82
|
+
*
|
|
83
|
+
* // Span with class (for styling hook)
|
|
84
|
+
* const badge = createSpan({ class: 'badge' });
|
|
85
|
+
*
|
|
86
|
+
* // Span with data attributes
|
|
87
|
+
* const tag = createSpan({ 'data-tag-id': '5', 'data-color': 'blue' });
|
|
88
|
+
*
|
|
89
|
+
* // Span for icon wrapper
|
|
90
|
+
* const iconWrapper = createSpan({ class: 'icon', 'aria-hidden': 'true' });
|
|
91
|
+
*/
|
|
92
|
+
export function createSpan(attributes = {}) {
|
|
93
|
+
const span = createElement('span');
|
|
94
|
+
|
|
95
|
+
if (Object.keys(attributes).length > 0) {
|
|
96
|
+
setAttributes(span, attributes);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return span;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//
|
|
103
|
+
// Default Export (for convenience)
|
|
104
|
+
//
|
|
105
|
+
export default {
|
|
106
|
+
createDiv,
|
|
107
|
+
createSpan
|
|
108
|
+
};
|
|
109
|
+
|