@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,564 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwiperRenderer - Render interactive content carousels/slideshows
|
|
3
|
+
*
|
|
4
|
+
* Terminal-first implementation matching backend zDisplay.swiper()
|
|
5
|
+
*
|
|
6
|
+
* Backend Events (from display_event_timebased.py):
|
|
7
|
+
* - swiper_init: Initialize swiper with slides
|
|
8
|
+
* - swiper_update: Update current slide
|
|
9
|
+
* - swiper_complete: Finish swiper
|
|
10
|
+
*
|
|
11
|
+
* Terminal Paradigm:
|
|
12
|
+
* - Box-drawing UI: for beautiful bordered display
|
|
13
|
+
* - Arrow keys: for navigation (via termios + select)
|
|
14
|
+
* - Number keys: 1-9 for direct jump to slide
|
|
15
|
+
* - Pause toggle: 'p' key
|
|
16
|
+
* - Quit: 'q' key
|
|
17
|
+
* - Auto-advance: Background thread cycles through slides every N seconds
|
|
18
|
+
* - Loop mode: Optional wrap around to start
|
|
19
|
+
*
|
|
20
|
+
* Bifrost Paradigm:
|
|
21
|
+
* - WebSocket events trigger initialization and updates
|
|
22
|
+
* - Touch gestures: Swipe left/right for navigation
|
|
23
|
+
* - Auto-advance: CSS animations + JavaScript intervals
|
|
24
|
+
* - Indicators: Dots showing position (1/N, 2/N, etc.)
|
|
25
|
+
* - zTheme carousel: Full CSS transitions and responsive design
|
|
26
|
+
*
|
|
27
|
+
* Features:
|
|
28
|
+
* - Slide/fade/vertical transitions (zTheme variants)
|
|
29
|
+
* - Auto-advance with configurable delay
|
|
30
|
+
* - Loop mode
|
|
31
|
+
* - Prev/next controls with keyboard shortcuts
|
|
32
|
+
* - Indicators (dots or progress bar)
|
|
33
|
+
* - Pause on hover
|
|
34
|
+
* - Caption support
|
|
35
|
+
* - Multiple concurrent swipers
|
|
36
|
+
*
|
|
37
|
+
* @see https://github.com/ZoloAi/zTheme/blob/main/Manual/ztheme-carousel.html
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────
|
|
41
|
+
// Imports
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
// Layer 0: Constants
|
|
45
|
+
import { TIMEOUTS } from '../../../L1_Foundation/constants/bifrost_constants.js';
|
|
46
|
+
|
|
47
|
+
// Layer 0: Primitives
|
|
48
|
+
import { createDiv } from '../primitives/generic_containers.js';
|
|
49
|
+
import { createButton } from '../primitives/interactive_primitives.js';
|
|
50
|
+
import { createSpan } from '../primitives/generic_containers.js';
|
|
51
|
+
import { createList } from '../primitives/lists_primitives.js';
|
|
52
|
+
|
|
53
|
+
export default class SwiperRenderer {
|
|
54
|
+
/**
|
|
55
|
+
* @param {Object} logger - Logger instance
|
|
56
|
+
*/
|
|
57
|
+
constructor(logger) {
|
|
58
|
+
this.logger = logger;
|
|
59
|
+
this._activeSwipers = new Map(); // Track active swipers by swiperId
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Initialize a swiper (swiper_init event)
|
|
64
|
+
* @param {Object} event - Swiper init event
|
|
65
|
+
* @param {string} event.swiperId - Unique swiper ID
|
|
66
|
+
* @param {string} event.label - Swiper label/title
|
|
67
|
+
* @param {Array<string>} event.slides - Array of slide content
|
|
68
|
+
* @param {number} [event.currentSlide=0] - Initial slide index
|
|
69
|
+
* @param {number} event.totalSlides - Total number of slides
|
|
70
|
+
* @param {boolean} [event.autoAdvance=true] - Enable auto-advance
|
|
71
|
+
* @param {number} [event.delay=3] - Delay between slides (seconds)
|
|
72
|
+
* @param {boolean} [event.loop=false] - Loop back to first slide
|
|
73
|
+
* @param {string} [event.container='#app'] - Target container selector
|
|
74
|
+
* @param {string} [event.variant='slide'] - Transition variant (slide, fade, vertical)
|
|
75
|
+
* @param {boolean} [event.showIndicators=true] - Show dot indicators
|
|
76
|
+
* @param {boolean} [event.showControls=true] - Show prev/next controls
|
|
77
|
+
* @returns {HTMLElement} Swiper container element
|
|
78
|
+
*/
|
|
79
|
+
init(event) {
|
|
80
|
+
const {
|
|
81
|
+
swiperId,
|
|
82
|
+
label = 'Slides',
|
|
83
|
+
slides = [],
|
|
84
|
+
currentSlide = 0,
|
|
85
|
+
_totalSlides,
|
|
86
|
+
autoAdvance = true,
|
|
87
|
+
delay = 3,
|
|
88
|
+
loop = false,
|
|
89
|
+
container = '#app',
|
|
90
|
+
variant = 'slide',
|
|
91
|
+
showIndicators = true,
|
|
92
|
+
showControls = true
|
|
93
|
+
} = event;
|
|
94
|
+
|
|
95
|
+
this.logger.log('[SwiperRenderer] Initializing swiper:', {
|
|
96
|
+
swiperId,
|
|
97
|
+
label,
|
|
98
|
+
slidesCount: slides.length
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!slides || slides.length === 0) {
|
|
102
|
+
this.logger.warn('[SwiperRenderer] No slides provided');
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Create swiper structure using primitives and zTheme classes
|
|
107
|
+
const swiperContainer = this._createSwiperContainer(
|
|
108
|
+
swiperId,
|
|
109
|
+
label,
|
|
110
|
+
slides,
|
|
111
|
+
currentSlide,
|
|
112
|
+
variant,
|
|
113
|
+
showIndicators,
|
|
114
|
+
showControls
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Find target container
|
|
118
|
+
const targetElement = document.querySelector(container) || document.body;
|
|
119
|
+
targetElement.appendChild(swiperContainer);
|
|
120
|
+
|
|
121
|
+
// Track active swiper
|
|
122
|
+
const swiperData = {
|
|
123
|
+
element: swiperContainer,
|
|
124
|
+
label,
|
|
125
|
+
slides,
|
|
126
|
+
currentSlide,
|
|
127
|
+
totalSlides: slides.length,
|
|
128
|
+
autoAdvance,
|
|
129
|
+
delay,
|
|
130
|
+
loop,
|
|
131
|
+
variant,
|
|
132
|
+
intervalId: null
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
this._activeSwipers.set(swiperId, swiperData);
|
|
136
|
+
|
|
137
|
+
// Start auto-advance if enabled
|
|
138
|
+
if (autoAdvance && delay > 0) {
|
|
139
|
+
this._startAutoAdvance(swiperId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Add keyboard navigation
|
|
143
|
+
this._addKeyboardNav(swiperId);
|
|
144
|
+
|
|
145
|
+
this.logger.log('[SwiperRenderer] Swiper initialized successfully');
|
|
146
|
+
return swiperContainer;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Update swiper to show specific slide (swiper_update event)
|
|
151
|
+
* @param {Object} event - Swiper update event
|
|
152
|
+
* @param {string} event.swiperId - Unique swiper ID
|
|
153
|
+
* @param {number} event.currentSlide - Target slide index
|
|
154
|
+
*/
|
|
155
|
+
update(event) {
|
|
156
|
+
const { swiperId, currentSlide } = event;
|
|
157
|
+
|
|
158
|
+
this.logger.log('[SwiperRenderer] Updating swiper:', { swiperId, currentSlide });
|
|
159
|
+
|
|
160
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
161
|
+
if (!swiperData) {
|
|
162
|
+
this.logger.warn('[SwiperRenderer] Swiper not found:', swiperId);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this._goToSlide(swiperId, currentSlide);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Complete and remove swiper (swiper_complete event)
|
|
171
|
+
* @param {Object} event - Swiper complete event
|
|
172
|
+
* @param {string} event.swiperId - Unique swiper ID
|
|
173
|
+
*/
|
|
174
|
+
complete(event) {
|
|
175
|
+
const { swiperId } = event;
|
|
176
|
+
|
|
177
|
+
this.logger.log('[SwiperRenderer] Completing swiper:', swiperId);
|
|
178
|
+
|
|
179
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
180
|
+
if (!swiperData) {
|
|
181
|
+
this.logger.warn('[SwiperRenderer] Swiper not found:', swiperId);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Stop auto-advance
|
|
186
|
+
if (swiperData.intervalId) {
|
|
187
|
+
clearInterval(swiperData.intervalId);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Fade out and remove
|
|
191
|
+
const { element } = swiperData;
|
|
192
|
+
element.style.transition = 'opacity 0.3s';
|
|
193
|
+
element.style.opacity = '0';
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
if (element.parentNode) {
|
|
196
|
+
element.parentNode.removeChild(element);
|
|
197
|
+
}
|
|
198
|
+
}, TIMEOUTS.FADE_TRANSITION);
|
|
199
|
+
|
|
200
|
+
// Remove from active swipers
|
|
201
|
+
this._activeSwipers.delete(swiperId);
|
|
202
|
+
|
|
203
|
+
this.logger.log('[SwiperRenderer] Swiper completed successfully');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Create swiper container using primitives and zTheme classes
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
_createSwiperContainer(swiperId, label, slides, currentSlide, variant, showIndicators, showControls) {
|
|
211
|
+
// Main container with wrapper for label
|
|
212
|
+
const wrapper = createDiv({
|
|
213
|
+
id: `${swiperId}-wrapper`,
|
|
214
|
+
class: 'zMy-3'
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Label/title (using primitive)
|
|
218
|
+
if (label) {
|
|
219
|
+
const labelEl = createDiv({
|
|
220
|
+
class: 'zH4 zMb-2'
|
|
221
|
+
});
|
|
222
|
+
labelEl.textContent = label;
|
|
223
|
+
wrapper.appendChild(labelEl);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Carousel container (using primitive + zTheme classes)
|
|
227
|
+
const carousel = createDiv({
|
|
228
|
+
id: swiperId,
|
|
229
|
+
class: `zCarousel ${variant === 'fade' ? 'zCarousel-fade' : ''} ${variant === 'vertical' ? 'zCarousel-vertical' : ''}`,
|
|
230
|
+
'data-ride': 'false', // We control it manually
|
|
231
|
+
'data-swiper-id': swiperId
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Carousel inner (slide container)
|
|
235
|
+
const carouselInner = createDiv({
|
|
236
|
+
class: 'zCarousel-inner'
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Create slides
|
|
240
|
+
slides.forEach((slideContent, index) => {
|
|
241
|
+
const slideItem = createDiv({
|
|
242
|
+
class: `zCarousel-item ${index === currentSlide ? 'zActive' : ''}`,
|
|
243
|
+
'data-slide-index': index
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Slide content wrapper
|
|
247
|
+
const slideContentDiv = createDiv({
|
|
248
|
+
class: 'zP-5 zBg-light zText-center',
|
|
249
|
+
style: 'min-height: 200px; display: flex; align-items: center; justify-content: center;'
|
|
250
|
+
});
|
|
251
|
+
slideContentDiv.innerHTML = slideContent; // Allow HTML in slides
|
|
252
|
+
|
|
253
|
+
slideItem.appendChild(slideContentDiv);
|
|
254
|
+
carouselInner.appendChild(slideItem);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
carousel.appendChild(carouselInner);
|
|
258
|
+
|
|
259
|
+
// Add controls (prev/next buttons)
|
|
260
|
+
if (showControls) {
|
|
261
|
+
const prevControl = this._createControl('prev', swiperId);
|
|
262
|
+
const nextControl = this._createControl('next', swiperId);
|
|
263
|
+
carousel.appendChild(prevControl);
|
|
264
|
+
carousel.appendChild(nextControl);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Add indicators (dots)
|
|
268
|
+
if (showIndicators) {
|
|
269
|
+
const indicators = this._createIndicators(swiperId, slides.length, currentSlide);
|
|
270
|
+
carousel.appendChild(indicators);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
wrapper.appendChild(carousel);
|
|
274
|
+
return wrapper;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create prev/next control button
|
|
279
|
+
* @private
|
|
280
|
+
*/
|
|
281
|
+
_createControl(direction, swiperId) {
|
|
282
|
+
const control = createButton('button', {
|
|
283
|
+
class: `zCarousel-control-${direction}`,
|
|
284
|
+
type: 'button',
|
|
285
|
+
'data-swiper-id': swiperId,
|
|
286
|
+
'data-direction': direction
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Icon span
|
|
290
|
+
const icon = createSpan({
|
|
291
|
+
class: `zCarousel-control-${direction}-icon`,
|
|
292
|
+
'aria-hidden': 'true'
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Screen reader text
|
|
296
|
+
const srText = createSpan({
|
|
297
|
+
class: 'zVisually-hidden'
|
|
298
|
+
});
|
|
299
|
+
srText.textContent = direction === 'prev' ? 'Previous' : 'Next';
|
|
300
|
+
|
|
301
|
+
control.appendChild(icon);
|
|
302
|
+
control.appendChild(srText);
|
|
303
|
+
|
|
304
|
+
// Add click handler
|
|
305
|
+
control.addEventListener('click', () => {
|
|
306
|
+
if (direction === 'prev') {
|
|
307
|
+
this._prevSlide(swiperId);
|
|
308
|
+
} else {
|
|
309
|
+
this._nextSlide(swiperId);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return control;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Create indicators (dots)
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
_createIndicators(swiperId, totalSlides, currentSlide) {
|
|
321
|
+
const indicatorsList = createList(false, {
|
|
322
|
+
class: 'zCarousel-indicators'
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
for (let i = 0; i < totalSlides; i++) {
|
|
326
|
+
const indicator = createButton('button', {
|
|
327
|
+
type: 'button',
|
|
328
|
+
class: i === currentSlide ? 'zActive' : '',
|
|
329
|
+
'data-swiper-id': swiperId,
|
|
330
|
+
'data-slide-to': i,
|
|
331
|
+
'aria-label': `Slide ${i + 1}`
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
indicator.addEventListener('click', () => {
|
|
335
|
+
this._goToSlide(swiperId, i);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
indicatorsList.appendChild(indicator);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return indicatorsList;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Go to specific slide
|
|
346
|
+
* @private
|
|
347
|
+
*/
|
|
348
|
+
_goToSlide(swiperId, targetIndex) {
|
|
349
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
350
|
+
if (!swiperData) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const { element, currentSlide, totalSlides } = swiperData;
|
|
355
|
+
|
|
356
|
+
// Validate index
|
|
357
|
+
if (targetIndex < 0 || targetIndex >= totalSlides) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (targetIndex === currentSlide) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Find carousel element
|
|
365
|
+
const carousel = element.querySelector(`[data-swiper-id="${swiperId}"]`);
|
|
366
|
+
if (!carousel) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Get all slides
|
|
371
|
+
const slides = carousel.querySelectorAll('.zCarousel-item');
|
|
372
|
+
const currentSlideEl = slides[currentSlide];
|
|
373
|
+
const targetSlideEl = slides[targetIndex];
|
|
374
|
+
|
|
375
|
+
if (!currentSlideEl || !targetSlideEl) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Apply transition classes (zTheme CSS handles animations)
|
|
380
|
+
currentSlideEl.classList.remove('zActive');
|
|
381
|
+
targetSlideEl.classList.add('zActive');
|
|
382
|
+
|
|
383
|
+
// Update indicators
|
|
384
|
+
const indicators = carousel.querySelectorAll('.zCarousel-indicators button');
|
|
385
|
+
if (indicators.length > 0) {
|
|
386
|
+
indicators[currentSlide]?.classList.remove('zActive');
|
|
387
|
+
indicators[targetIndex]?.classList.add('zActive');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Update swiper data
|
|
391
|
+
swiperData.currentSlide = targetIndex;
|
|
392
|
+
|
|
393
|
+
this.logger.log(`[SwiperRenderer] Moved to slide ${targetIndex + 1}/${totalSlides}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Go to previous slide
|
|
398
|
+
* @private
|
|
399
|
+
*/
|
|
400
|
+
_prevSlide(swiperId) {
|
|
401
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
402
|
+
if (!swiperData) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const { currentSlide, totalSlides, loop } = swiperData;
|
|
407
|
+
let targetIndex = currentSlide - 1;
|
|
408
|
+
|
|
409
|
+
if (targetIndex < 0) {
|
|
410
|
+
if (loop) {
|
|
411
|
+
targetIndex = totalSlides - 1; // Wrap to last slide
|
|
412
|
+
} else {
|
|
413
|
+
return; // Can't go before first slide
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
this._goToSlide(swiperId, targetIndex);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Go to next slide
|
|
422
|
+
* @private
|
|
423
|
+
*/
|
|
424
|
+
_nextSlide(swiperId) {
|
|
425
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
426
|
+
if (!swiperData) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const { currentSlide, totalSlides, loop } = swiperData;
|
|
431
|
+
let targetIndex = currentSlide + 1;
|
|
432
|
+
|
|
433
|
+
if (targetIndex >= totalSlides) {
|
|
434
|
+
if (loop) {
|
|
435
|
+
targetIndex = 0; // Wrap to first slide
|
|
436
|
+
} else {
|
|
437
|
+
return; // Can't go past last slide
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
this._goToSlide(swiperId, targetIndex);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Start auto-advance interval
|
|
446
|
+
* @private
|
|
447
|
+
*/
|
|
448
|
+
_startAutoAdvance(swiperId) {
|
|
449
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
450
|
+
if (!swiperData) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const { delay } = swiperData;
|
|
455
|
+
|
|
456
|
+
// Clear any existing interval
|
|
457
|
+
if (swiperData.intervalId) {
|
|
458
|
+
clearInterval(swiperData.intervalId);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Start new interval
|
|
462
|
+
swiperData.intervalId = setInterval(() => {
|
|
463
|
+
this._nextSlide(swiperId);
|
|
464
|
+
}, delay * 1000); // delay is in seconds, convert to ms
|
|
465
|
+
|
|
466
|
+
this.logger.log(`[SwiperRenderer] Auto-advance started (${delay}s delay)`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Stop auto-advance interval
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
_stopAutoAdvance(swiperId) {
|
|
474
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
475
|
+
if (!swiperData) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (swiperData.intervalId) {
|
|
480
|
+
clearInterval(swiperData.intervalId);
|
|
481
|
+
swiperData.intervalId = null;
|
|
482
|
+
this.logger.log('[SwiperRenderer] Auto-advance stopped');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Add keyboard navigation (arrow keys, number keys)
|
|
488
|
+
* @private
|
|
489
|
+
*/
|
|
490
|
+
_addKeyboardNav(swiperId) {
|
|
491
|
+
const swiperData = this._activeSwipers.get(swiperId);
|
|
492
|
+
if (!swiperData) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const keyHandler = (e) => {
|
|
497
|
+
// Only handle if this swiper is still active
|
|
498
|
+
if (!this._activeSwipers.has(swiperId)) {
|
|
499
|
+
document.removeEventListener('keydown', keyHandler);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
switch (e.key) {
|
|
504
|
+
case 'ArrowLeft':
|
|
505
|
+
e.preventDefault();
|
|
506
|
+
this._prevSlide(swiperId);
|
|
507
|
+
break;
|
|
508
|
+
case 'ArrowRight':
|
|
509
|
+
e.preventDefault();
|
|
510
|
+
this._nextSlide(swiperId);
|
|
511
|
+
break;
|
|
512
|
+
case '1':
|
|
513
|
+
case '2':
|
|
514
|
+
case '3':
|
|
515
|
+
case '4':
|
|
516
|
+
case '5':
|
|
517
|
+
case '6':
|
|
518
|
+
case '7':
|
|
519
|
+
case '8':
|
|
520
|
+
case '9': {
|
|
521
|
+
const targetIndex = parseInt(e.key, 10) - 1;
|
|
522
|
+
if (targetIndex < swiperData.totalSlides) {
|
|
523
|
+
this._goToSlide(swiperId, targetIndex);
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case 'p':
|
|
528
|
+
case 'P':
|
|
529
|
+
// Toggle pause
|
|
530
|
+
if (swiperData.intervalId) {
|
|
531
|
+
this._stopAutoAdvance(swiperId);
|
|
532
|
+
} else if (swiperData.autoAdvance) {
|
|
533
|
+
this._startAutoAdvance(swiperId);
|
|
534
|
+
}
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
document.addEventListener('keydown', keyHandler);
|
|
540
|
+
|
|
541
|
+
// Store handler for cleanup
|
|
542
|
+
swiperData.keyHandler = keyHandler;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Get active swiper count
|
|
547
|
+
* @returns {number} Number of active swipers
|
|
548
|
+
*/
|
|
549
|
+
getActiveCount() {
|
|
550
|
+
return this._activeSwipers.size;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Stop all active swipers (cleanup utility)
|
|
555
|
+
*/
|
|
556
|
+
stopAll() {
|
|
557
|
+
this.logger.log('[SwiperRenderer] Stopping all swipers');
|
|
558
|
+
|
|
559
|
+
for (const [swiperId, _data] of this._activeSwipers) {
|
|
560
|
+
this.complete({ swiperId });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|