@panyam/tsappkit 0.0.3
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/README.md +98 -0
- package/dist/index.d.mts +866 -0
- package/dist/index.d.ts +866 -0
- package/dist/index.js +1736 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1718 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -0
- package/src/BasePage.ts +270 -0
- package/src/Component.ts +178 -0
- package/src/DOMUtils.ts +72 -0
- package/src/EventBus.ts +272 -0
- package/src/KeyboardShortcutManager.ts +435 -0
- package/src/LCMComponent.ts +140 -0
- package/src/LifecycleController.ts +213 -0
- package/src/MobileBottomDrawer.ts +236 -0
- package/src/Modal.ts +233 -0
- package/src/SplashScreen.ts +111 -0
- package/src/TemplateLoader.ts +101 -0
- package/src/ThemeManager.ts +100 -0
- package/src/ToastManager.ts +161 -0
- package/src/events.ts +5 -0
- package/src/index.ts +39 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple event system for component communication
|
|
3
|
+
* Provides error isolation and idempotent subscriptions
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Simple event handler function type
|
|
7
|
+
*/
|
|
8
|
+
type EventHandler = (data?: any) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Interface for components that want to receive events via the EventBus
|
|
11
|
+
*/
|
|
12
|
+
interface EventSubscriber {
|
|
13
|
+
/**
|
|
14
|
+
* Handle incoming events from the EventBus
|
|
15
|
+
* @param eventType - The type of event being handled
|
|
16
|
+
* @param data - The event data payload
|
|
17
|
+
* @param subject - The subject/subject entity (what the event is about)
|
|
18
|
+
* @param emitter - The entity that emitted the event
|
|
19
|
+
*/
|
|
20
|
+
handleBusEvent(eventType: string, data: any, subject: any, emitter: any): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Synchronous event bus for component communication
|
|
24
|
+
* Features:
|
|
25
|
+
* - Type-safe event names and payloads
|
|
26
|
+
* - Error isolation (one handler failure doesn't stop others)
|
|
27
|
+
* - Source exclusion (events not sent back to source)
|
|
28
|
+
* - Debug logging for troubleshooting
|
|
29
|
+
*/
|
|
30
|
+
declare class EventBus {
|
|
31
|
+
private subscribers;
|
|
32
|
+
private functionSubscribers;
|
|
33
|
+
private onceHandlers;
|
|
34
|
+
private debugMode;
|
|
35
|
+
constructor(debugMode?: boolean);
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to an event with a simple handler function
|
|
38
|
+
*/
|
|
39
|
+
on(eventType: string, handler: EventHandler): void;
|
|
40
|
+
/**
|
|
41
|
+
* Unsubscribe a handler function from an event
|
|
42
|
+
*/
|
|
43
|
+
off(eventType: string, handler: EventHandler): void;
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to an event for one-time execution
|
|
46
|
+
*/
|
|
47
|
+
once(eventType: string, handler: EventHandler): void;
|
|
48
|
+
/**
|
|
49
|
+
* Add a subscription using the EventSubscriber pattern
|
|
50
|
+
* Provides automatic idempotency - same subscriber object won't be added twice
|
|
51
|
+
*/
|
|
52
|
+
addSubscription(eventType: string, subject: any, subscriber: EventSubscriber): void;
|
|
53
|
+
/**
|
|
54
|
+
* Remove a subscription using the EventSubscriber pattern
|
|
55
|
+
*/
|
|
56
|
+
removeSubscription(eventType: string, subject: any, subscriber: EventSubscriber): void;
|
|
57
|
+
/**
|
|
58
|
+
* Emit an event to all subscribers
|
|
59
|
+
* @param eventType - The event type to emit
|
|
60
|
+
* @param data - The event data payload
|
|
61
|
+
* @param subject - The subject/subject entity that this event relates to (optional for simple API)
|
|
62
|
+
* @param emitter - The entity that emitted the event (optional for simple API)
|
|
63
|
+
*/
|
|
64
|
+
emit<T = any>(eventType: string, data?: T, subject?: any, emitter?: any): void;
|
|
65
|
+
/**
|
|
66
|
+
* Get all event types that have subscribers
|
|
67
|
+
*/
|
|
68
|
+
getEventTypes(): string[];
|
|
69
|
+
/**
|
|
70
|
+
* Get subscriber count for an event type
|
|
71
|
+
*/
|
|
72
|
+
getSubscriberCount(eventType: string): number;
|
|
73
|
+
/**
|
|
74
|
+
* Clear all subscriptions (useful for cleanup)
|
|
75
|
+
*/
|
|
76
|
+
clear(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Enable or disable debug logging
|
|
79
|
+
*/
|
|
80
|
+
setDebugMode(enabled: boolean): void;
|
|
81
|
+
}
|
|
82
|
+
declare const ComponentEventTypes: {
|
|
83
|
+
readonly COMPONENT_INITIALIZED: "component-initialized";
|
|
84
|
+
readonly COMPONENT_HYDRATED: "component-hydrated";
|
|
85
|
+
readonly COMPONENT_ERROR: "component-error";
|
|
86
|
+
};
|
|
87
|
+
declare const LifecycleEventTypes: {
|
|
88
|
+
readonly LOCAL_INIT_STARTED: "lifecycle-local-init-started";
|
|
89
|
+
readonly LOCAL_INIT_FINISHED: "lifecycle-local-init-finished";
|
|
90
|
+
readonly DEPENDENCIES_INJECTED: "lifecycle-dependencies-injected";
|
|
91
|
+
readonly ACTIVATION_STARTED: "lifecycle-activation-started";
|
|
92
|
+
readonly ACTIVATION_FINISHED: "lifecycle-activation-finished";
|
|
93
|
+
readonly DEACTIVATION_STARTED: "lifecycle-deactivation-started";
|
|
94
|
+
readonly DEACTIVATION_FINISHED: "lifecycle-deactivation-finished";
|
|
95
|
+
};
|
|
96
|
+
type LifecycleEventType = typeof LifecycleEventTypes[keyof typeof LifecycleEventTypes];
|
|
97
|
+
type ComponentEventType = typeof ComponentEventTypes[keyof typeof ComponentEventTypes];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* LCMComponent Interface - Defines multi-phase component initialization
|
|
101
|
+
*
|
|
102
|
+
* This interface implements a breadth-first lifecycle pattern that eliminates
|
|
103
|
+
* initialization order dependencies and race conditions through synchronization barriers.
|
|
104
|
+
*
|
|
105
|
+
* Lifecycle Phases:
|
|
106
|
+
* 1. Construction - Components are created but not initialized
|
|
107
|
+
* 2. bindToDOM() - Basic DOM setup, discover child components
|
|
108
|
+
* 3. injectDependencies() - Receive references to other components
|
|
109
|
+
* 4. activate() - Final setup when all dependencies are ready
|
|
110
|
+
*
|
|
111
|
+
* Key Benefits:
|
|
112
|
+
* - Order Independence: Components can be created in any sequence
|
|
113
|
+
* - Async Safety: Each phase waits for all components before proceeding
|
|
114
|
+
* - Clear Dependencies: Explicit injection points prevent race conditions
|
|
115
|
+
* - Error Isolation: Component failures don't cascade to others
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* LCM Component - Short for LifeCycle Managed Component
|
|
119
|
+
* enable components to be declared first and their loading be managed by a
|
|
120
|
+
* LifecycleController so that we have layered creation, dependency injection and setup
|
|
121
|
+
* in a breadth first way.
|
|
122
|
+
*
|
|
123
|
+
* A key constraint on LCMComponents are that they should not perform any initialization
|
|
124
|
+
* in the constructor. This is because a LifecycleController should be used to load/setup
|
|
125
|
+
* these components and they will follow a layered approach. Performing these actions in
|
|
126
|
+
* the constructor could violate the idempotency guarantees.
|
|
127
|
+
*/
|
|
128
|
+
interface LCMComponent {
|
|
129
|
+
/**
|
|
130
|
+
* Phase 1: The "local" initialization of the component.
|
|
131
|
+
*
|
|
132
|
+
* In this phase the component initializes itself and returns any children it might
|
|
133
|
+
* want initialized as part of the lifecycled loading.
|
|
134
|
+
*
|
|
135
|
+
* This phase should:
|
|
136
|
+
* - Set up basic DOM elements and event listeners
|
|
137
|
+
* - Create child components (but don't initialize them) and return them
|
|
138
|
+
* - Return array of child components for lifecycle controller discovery
|
|
139
|
+
*
|
|
140
|
+
* This phase must be synchronous and should not:
|
|
141
|
+
* - Access other components or external dependencies
|
|
142
|
+
* - Perform async operations
|
|
143
|
+
* - Emit events or notifications
|
|
144
|
+
*
|
|
145
|
+
* @returns Array of child components to be managed by lifecycle controller
|
|
146
|
+
*/
|
|
147
|
+
performLocalInit(): Promise<LCMComponent[]> | LCMComponent[];
|
|
148
|
+
/**
|
|
149
|
+
* Phase 2: Inject dependencies from parent/siblings
|
|
150
|
+
*
|
|
151
|
+
* This phase should:
|
|
152
|
+
* - Receive and store references to required dependencies
|
|
153
|
+
* - Validate that required dependencies are provided
|
|
154
|
+
* - Set up internal state based on dependencies
|
|
155
|
+
*
|
|
156
|
+
* This phase can be async and may:
|
|
157
|
+
* - Load external data or resources
|
|
158
|
+
* - Perform validation or setup operations
|
|
159
|
+
* - Initialize internal components that depend on injected references
|
|
160
|
+
*
|
|
161
|
+
* @param deps Record of dependency name to dependency instance
|
|
162
|
+
* @returns Promise<void> or void - can be async
|
|
163
|
+
*/
|
|
164
|
+
setupDependencies(): Promise<void> | void;
|
|
165
|
+
/**
|
|
166
|
+
* Phase 3: Activate component when all dependencies are ready
|
|
167
|
+
*
|
|
168
|
+
* This phase should:
|
|
169
|
+
* - Complete final initialization
|
|
170
|
+
* - Enable component functionality
|
|
171
|
+
* - Start listening for external events
|
|
172
|
+
* - Begin normal operation
|
|
173
|
+
*
|
|
174
|
+
* This phase can be async and may:
|
|
175
|
+
* - Connect to external services
|
|
176
|
+
* - Load initial data
|
|
177
|
+
* - Emit ready notifications
|
|
178
|
+
*
|
|
179
|
+
* @returns Promise<void> or void - can be async
|
|
180
|
+
*/
|
|
181
|
+
activate(): Promise<void> | void;
|
|
182
|
+
/**
|
|
183
|
+
* Cleanup phase: Deactivate component and clean up resources
|
|
184
|
+
*
|
|
185
|
+
* This should:
|
|
186
|
+
* - Stop all ongoing operations
|
|
187
|
+
* - Remove event listeners
|
|
188
|
+
* - Clean up external connections
|
|
189
|
+
* - Dispose of child components
|
|
190
|
+
*
|
|
191
|
+
* @returns Promise<void> or void - can be async
|
|
192
|
+
*/
|
|
193
|
+
deactivate(): Promise<void> | void;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Configuration for component lifecycle behavior
|
|
197
|
+
*/
|
|
198
|
+
interface LCMComponentConfig {
|
|
199
|
+
/**
|
|
200
|
+
* Maximum time to wait for a lifecycle phase to complete (ms)
|
|
201
|
+
* Default: 10000 (10 seconds)
|
|
202
|
+
*/
|
|
203
|
+
phaseTimeoutMs?: number;
|
|
204
|
+
/**
|
|
205
|
+
* Whether to continue if individual components fail during a phase
|
|
206
|
+
* Default: false (fail fast)
|
|
207
|
+
*/
|
|
208
|
+
continueOnError?: boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Whether to validate dependencies against declared requirements
|
|
211
|
+
* Default: true
|
|
212
|
+
*/
|
|
213
|
+
validateDependencies?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Enable debug logging for lifecycle phases
|
|
216
|
+
* Default: false
|
|
217
|
+
*/
|
|
218
|
+
enableDebugLogging?: boolean;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Event emitted during component lifecycle transitions
|
|
222
|
+
*/
|
|
223
|
+
interface LCMComponentEvent {
|
|
224
|
+
type: 'phase-start' | 'phase-complete' | 'phase-error' | 'component-ready';
|
|
225
|
+
componentName: string;
|
|
226
|
+
timestamp: number;
|
|
227
|
+
error?: Error;
|
|
228
|
+
metadata?: Record<string, any>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Base interface for all UI components
|
|
233
|
+
* Enforces separation of concerns and standard lifecycle
|
|
234
|
+
*/
|
|
235
|
+
interface Component {
|
|
236
|
+
/**
|
|
237
|
+
* Unique identifier for this component instance
|
|
238
|
+
*/
|
|
239
|
+
readonly componentId: string;
|
|
240
|
+
/**
|
|
241
|
+
* Root DOM element that this component owns and manages
|
|
242
|
+
*/
|
|
243
|
+
readonly rootElement: HTMLElement;
|
|
244
|
+
/**
|
|
245
|
+
* Handle dynamic content updates (e.g., from HTMX or server responses)
|
|
246
|
+
* @param newHTML - New HTML content to replace current content
|
|
247
|
+
*/
|
|
248
|
+
contentUpdated(newHTML: string): void;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Abstract base class implementing common component functionality
|
|
252
|
+
* Provides standard lifecycle management and event bus integration
|
|
253
|
+
*
|
|
254
|
+
* All components auto-initialize in constructor AND implement LCMComponent
|
|
255
|
+
* for coordination with other components when needed.
|
|
256
|
+
*/
|
|
257
|
+
declare abstract class BaseComponent implements Component, LCMComponent, EventSubscriber {
|
|
258
|
+
readonly componentId: string;
|
|
259
|
+
readonly rootElement: HTMLElement;
|
|
260
|
+
readonly debugMode: boolean;
|
|
261
|
+
protected _eventBus: EventBus;
|
|
262
|
+
constructor(componentId: string, rootElement: HTMLElement, eventBus?: EventBus | null, debugMode?: boolean);
|
|
263
|
+
get eventBus(): EventBus;
|
|
264
|
+
contentUpdated(newHTML: string): void;
|
|
265
|
+
/**
|
|
266
|
+
* Subscribe to an event using the new EventSubscriber pattern
|
|
267
|
+
*/
|
|
268
|
+
protected addSubscription(eventType: string, target?: any): void;
|
|
269
|
+
/**
|
|
270
|
+
* Unsubscribe from an event using the new EventSubscriber pattern
|
|
271
|
+
*/
|
|
272
|
+
protected removeSubscription(eventType: string, target?: any): void;
|
|
273
|
+
/**
|
|
274
|
+
* Emit an event from this component
|
|
275
|
+
*/
|
|
276
|
+
protected emit<T = any>(eventType: string, data: T, target: any, emitter?: any): void;
|
|
277
|
+
/**
|
|
278
|
+
* Default implementation of EventSubscriber interface
|
|
279
|
+
* Components can override this to handle events
|
|
280
|
+
*/
|
|
281
|
+
handleBusEvent(eventType: string, data: any, target: any, emitter: any): void;
|
|
282
|
+
/**
|
|
283
|
+
* Find elements within this component's root element only
|
|
284
|
+
* Enforces separation of concerns - no cross-component DOM access
|
|
285
|
+
*/
|
|
286
|
+
protected findElement<T extends HTMLElement = HTMLElement>(selector: string): T | null;
|
|
287
|
+
/**
|
|
288
|
+
* Find multiple elements within this component's root element only
|
|
289
|
+
*/
|
|
290
|
+
protected findElements<T extends HTMLElement = HTMLElement>(selector: string): T[];
|
|
291
|
+
/**
|
|
292
|
+
* Log messages with component identification
|
|
293
|
+
*/
|
|
294
|
+
protected log(message: string, data?: any): void;
|
|
295
|
+
/**
|
|
296
|
+
* Default lifecycle method: discover and return child components
|
|
297
|
+
* Override this if your component creates child components that need lifecycle management
|
|
298
|
+
*/
|
|
299
|
+
performLocalInit(): Promise<LCMComponent[]> | LCMComponent[];
|
|
300
|
+
/**
|
|
301
|
+
* Default lifecycle method: inject dependencies
|
|
302
|
+
* Override this if your component needs dependencies from other components
|
|
303
|
+
*/
|
|
304
|
+
setupDependencies(): void | Promise<void>;
|
|
305
|
+
/**
|
|
306
|
+
* Default lifecycle method: activate component for coordination
|
|
307
|
+
* Override this if your component needs to coordinate with other components after initialization
|
|
308
|
+
*/
|
|
309
|
+
activate(): void | Promise<void>;
|
|
310
|
+
/**
|
|
311
|
+
* Default lifecycle method: deactivate component
|
|
312
|
+
* Override this if your component needs cleanup during lifecycle management
|
|
313
|
+
*/
|
|
314
|
+
deactivate(): void | Promise<void>;
|
|
315
|
+
set innerHTML(innerHTML: string);
|
|
316
|
+
shouldUpdateHtml(html: string): [string, boolean];
|
|
317
|
+
htmlUpdated(html: string): void;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* LifecycleController - Orchestrates breadth-first component initialization
|
|
322
|
+
*
|
|
323
|
+
* This controller implements a breadth-first traversal of the component tree
|
|
324
|
+
* followed by phase-wise initialization with synchronization barriers.
|
|
325
|
+
*
|
|
326
|
+
* Process:
|
|
327
|
+
* 1. Discovery Phase: Traverse component tree to find all components
|
|
328
|
+
* 2. DOM Binding Phase: All components bind to DOM simultaneously
|
|
329
|
+
* 3. Dependency Injection Phase: All components receive dependencies
|
|
330
|
+
* 4. Activation Phase: All components complete initialization
|
|
331
|
+
*
|
|
332
|
+
* Each phase acts as a synchronization barrier - no component can proceed
|
|
333
|
+
* to the next phase until ALL components have completed the current phase.
|
|
334
|
+
*/
|
|
335
|
+
declare class LifecycleController {
|
|
336
|
+
protected eventBus: EventBus;
|
|
337
|
+
private allComponents;
|
|
338
|
+
private componentsByLevel;
|
|
339
|
+
private config;
|
|
340
|
+
static DefaultConfig: LCMComponentConfig;
|
|
341
|
+
constructor(eventBus: EventBus, config?: LCMComponentConfig);
|
|
342
|
+
/**
|
|
343
|
+
* Initialize component tree starting from root component
|
|
344
|
+
*
|
|
345
|
+
* @param rootComponent The root component to start initialization from
|
|
346
|
+
* @param rootName Optional name for the root component (for debugging)
|
|
347
|
+
* @returns Promise that resolves when all components are fully initialized
|
|
348
|
+
*/
|
|
349
|
+
initializeFromRoot(rootComponent: LCMComponent): Promise<void>;
|
|
350
|
+
/**
|
|
351
|
+
* Phase 0: Discover all components via breadth-first traversal
|
|
352
|
+
*/
|
|
353
|
+
private performLocalInit;
|
|
354
|
+
/**
|
|
355
|
+
* Phase 1: Dependency injection.
|
|
356
|
+
* Here all our components are already discovered so we can call directly
|
|
357
|
+
*/
|
|
358
|
+
protected injectDependencies(): void;
|
|
359
|
+
/**
|
|
360
|
+
* Phase 2: Activate all components
|
|
361
|
+
*
|
|
362
|
+
* Here all our components are already discovered and their dependencies setup
|
|
363
|
+
* so we can call directly
|
|
364
|
+
*/
|
|
365
|
+
protected activate(): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Deactivate all components in reverse order. This is usually called when a page quits or a component is
|
|
368
|
+
* deactivated
|
|
369
|
+
*/
|
|
370
|
+
deactivateAll(): Promise<void>;
|
|
371
|
+
/**
|
|
372
|
+
* Emit lifecycle event to registered callbacks
|
|
373
|
+
*/
|
|
374
|
+
private emitEvent;
|
|
375
|
+
/**
|
|
376
|
+
* Log message if debug logging is enabled
|
|
377
|
+
*/
|
|
378
|
+
private log;
|
|
379
|
+
/**
|
|
380
|
+
* Log error message
|
|
381
|
+
*/
|
|
382
|
+
private logError;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
interface ComponentLifecycleEvent {
|
|
386
|
+
success: boolean;
|
|
387
|
+
error: Error | null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Theme management for applications
|
|
392
|
+
*/
|
|
393
|
+
declare class ThemeManager {
|
|
394
|
+
static LIGHT: string;
|
|
395
|
+
static DARK: string;
|
|
396
|
+
static SYSTEM: string;
|
|
397
|
+
static readonly LIGHT_ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-full h-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z\" /></svg>";
|
|
398
|
+
static readonly DARK_ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-full h-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z\" /></svg>";
|
|
399
|
+
static readonly SYSTEM_ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-full h-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25m18 0A2.25 2.25 0 0018.75 3H5.25A2.25 2.25 0 003 5.25m18 0V12a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 12V5.25\" /></svg>";
|
|
400
|
+
/**
|
|
401
|
+
* Initialize theme based on saved preference or system default
|
|
402
|
+
*/
|
|
403
|
+
static initialize(): void;
|
|
404
|
+
/**
|
|
405
|
+
* Set theme and save preference
|
|
406
|
+
*/
|
|
407
|
+
static setTheme(theme: string): void;
|
|
408
|
+
/**
|
|
409
|
+
* Get current theme setting (light, dark, or system)
|
|
410
|
+
*/
|
|
411
|
+
static getCurrentThemeSetting(): string;
|
|
412
|
+
/**
|
|
413
|
+
* Gets the *next* theme in the cycle: Light -> Dark -> System -> Light ...
|
|
414
|
+
*/
|
|
415
|
+
static getNextTheme(currentSetting: string): string;
|
|
416
|
+
/**
|
|
417
|
+
* Gets the appropriate SVG icon string for a given theme setting.
|
|
418
|
+
*/
|
|
419
|
+
static getIconSVG(themeSetting: string): string;
|
|
420
|
+
/**
|
|
421
|
+
* Gets a user-friendly label for the theme setting.
|
|
422
|
+
*/
|
|
423
|
+
static getThemeLabel(themeSetting: string): string;
|
|
424
|
+
/**
|
|
425
|
+
* Initialize the ThemeManager (no instance needed for static methods)
|
|
426
|
+
*/
|
|
427
|
+
static init(): void;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Modal manager for the application
|
|
432
|
+
* Handles showing and hiding modals with different content
|
|
433
|
+
*/
|
|
434
|
+
declare class Modal {
|
|
435
|
+
private static instance;
|
|
436
|
+
private modalContainer;
|
|
437
|
+
private modalBackdrop;
|
|
438
|
+
private modalPanel;
|
|
439
|
+
private modalContent;
|
|
440
|
+
private closeButton;
|
|
441
|
+
private templateLoader;
|
|
442
|
+
private currentTemplateId;
|
|
443
|
+
private currentData;
|
|
444
|
+
private onSubmitCallback;
|
|
445
|
+
private onApplyCallback;
|
|
446
|
+
/**
|
|
447
|
+
* Private constructor for singleton pattern
|
|
448
|
+
*/
|
|
449
|
+
private constructor();
|
|
450
|
+
/**
|
|
451
|
+
* Get the Modal instance (singleton)
|
|
452
|
+
*/
|
|
453
|
+
static getInstance(): Modal;
|
|
454
|
+
/**
|
|
455
|
+
* Bind event listeners for modal interactions
|
|
456
|
+
*/
|
|
457
|
+
private bindEvents;
|
|
458
|
+
/**
|
|
459
|
+
* Check if the modal is currently visible
|
|
460
|
+
*/
|
|
461
|
+
isVisible(): boolean;
|
|
462
|
+
/**
|
|
463
|
+
* Show a modal with content from the specified template ID.
|
|
464
|
+
* Uses TemplateLoader to get the content element.
|
|
465
|
+
* @param templateId ID used in `data-template-id` attribute in TemplateRegistry.html
|
|
466
|
+
* @param data Optional data to pass to the modal. Can include callbacks like `onSubmit`.
|
|
467
|
+
* @returns The root HTMLElement of the loaded content, or null if failed.
|
|
468
|
+
*/
|
|
469
|
+
show(templateId: string, data?: any): HTMLElement | null;
|
|
470
|
+
/**
|
|
471
|
+
* Hide the modal
|
|
472
|
+
*/
|
|
473
|
+
hide(): Promise<void>;
|
|
474
|
+
/**
|
|
475
|
+
* Get the current modal content element
|
|
476
|
+
*/
|
|
477
|
+
getContentElement(): HTMLElement | null;
|
|
478
|
+
/**
|
|
479
|
+
* Get the current template ID
|
|
480
|
+
*/
|
|
481
|
+
getCurrentTemplate(): string | null;
|
|
482
|
+
/**
|
|
483
|
+
* Get the current modal data
|
|
484
|
+
*/
|
|
485
|
+
getCurrentData(): any;
|
|
486
|
+
/**
|
|
487
|
+
* Update modal data (excluding callbacks for now)
|
|
488
|
+
*/
|
|
489
|
+
updateData(newData: any): void;
|
|
490
|
+
/**
|
|
491
|
+
* Initialize the modal component
|
|
492
|
+
*/
|
|
493
|
+
static init(): Modal;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Toast types for styling
|
|
498
|
+
*/
|
|
499
|
+
type ToastType = 'success' | 'error' | 'info' | 'warning';
|
|
500
|
+
/**
|
|
501
|
+
* Manages toast notifications
|
|
502
|
+
*/
|
|
503
|
+
declare class ToastManager {
|
|
504
|
+
private static instance;
|
|
505
|
+
private container;
|
|
506
|
+
private template;
|
|
507
|
+
private toasts;
|
|
508
|
+
private counter;
|
|
509
|
+
/**
|
|
510
|
+
* Private constructor for singleton pattern
|
|
511
|
+
*/
|
|
512
|
+
private constructor();
|
|
513
|
+
/**
|
|
514
|
+
* Get the ToastManager instance (singleton)
|
|
515
|
+
*/
|
|
516
|
+
static getInstance(): ToastManager;
|
|
517
|
+
/**
|
|
518
|
+
* Show a toast notification
|
|
519
|
+
* @param title Toast title
|
|
520
|
+
* @param message Toast message
|
|
521
|
+
* @param type Toast type for styling
|
|
522
|
+
* @param duration Duration in ms (default: 4000)
|
|
523
|
+
*/
|
|
524
|
+
showToast(title: string, message: string, type?: ToastType, duration?: number): string;
|
|
525
|
+
/**
|
|
526
|
+
* Hide a toast notification
|
|
527
|
+
* @param id Toast ID
|
|
528
|
+
*/
|
|
529
|
+
hideToast(id: string): void;
|
|
530
|
+
/**
|
|
531
|
+
* Hide all toast notifications
|
|
532
|
+
*/
|
|
533
|
+
hideAllToasts(): void;
|
|
534
|
+
/**
|
|
535
|
+
* Initialize the component
|
|
536
|
+
*/
|
|
537
|
+
static init(): ToastManager;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Base class for all pages that provides common UI components and functionality
|
|
542
|
+
* Implements proper LCMComponent lifecycle management for pages
|
|
543
|
+
*/
|
|
544
|
+
declare abstract class BasePage extends BaseComponent {
|
|
545
|
+
readonly componentId: string;
|
|
546
|
+
readonly debugMode: boolean;
|
|
547
|
+
protected themeManager: typeof ThemeManager;
|
|
548
|
+
protected modal: Modal;
|
|
549
|
+
protected toastManager: ToastManager;
|
|
550
|
+
protected themeToggleButton: HTMLButtonElement;
|
|
551
|
+
protected themeToggleIcon: HTMLElement;
|
|
552
|
+
constructor(componentId: string, eventBus?: EventBus | null, debugMode?: boolean);
|
|
553
|
+
performLocalInit(): Promise<LCMComponent[]> | LCMComponent[];
|
|
554
|
+
activate(): void;
|
|
555
|
+
/**
|
|
556
|
+
* Initialize common UI components that all pages need
|
|
557
|
+
*/
|
|
558
|
+
protected initializeBaseComponents(): void;
|
|
559
|
+
/**
|
|
560
|
+
* Bind common event handlers that all pages need
|
|
561
|
+
*/
|
|
562
|
+
protected bindBaseEvents(): void;
|
|
563
|
+
/**
|
|
564
|
+
* Handle theme toggle button clicks
|
|
565
|
+
*/
|
|
566
|
+
protected handleThemeToggleClick(): void;
|
|
567
|
+
/**
|
|
568
|
+
* Update the theme toggle button state and appearance
|
|
569
|
+
*/
|
|
570
|
+
protected updateThemeButtonState(currentTheme?: string): void;
|
|
571
|
+
/**
|
|
572
|
+
* Show a toast notification
|
|
573
|
+
*/
|
|
574
|
+
protected showToast(title: string, message: string, type?: 'success' | 'error' | 'info' | 'warning', duration?: number): void;
|
|
575
|
+
/**
|
|
576
|
+
* Show a modal dialog
|
|
577
|
+
*/
|
|
578
|
+
protected showModal(templateId: string, data?: any): void;
|
|
579
|
+
/**
|
|
580
|
+
* Hide the modal dialog
|
|
581
|
+
*/
|
|
582
|
+
protected hideModal(): void;
|
|
583
|
+
/**
|
|
584
|
+
* Get the current theme setting
|
|
585
|
+
*/
|
|
586
|
+
protected getCurrentTheme(): string;
|
|
587
|
+
/**
|
|
588
|
+
* Check if the current theme is dark mode
|
|
589
|
+
*/
|
|
590
|
+
protected isDarkMode(): boolean;
|
|
591
|
+
/**
|
|
592
|
+
* Initialize responsive header actions drawer
|
|
593
|
+
* On desktop: drawer is always visible, positioned inline with header
|
|
594
|
+
* On mobile: drawer slides down from top when menu button is clicked
|
|
595
|
+
*/
|
|
596
|
+
protected initializeHeaderActionsDropdown(): void;
|
|
597
|
+
/**
|
|
598
|
+
* Abstract method that subclasses must implement to initialize their specific components
|
|
599
|
+
* Should return any child components that need lifecycle management
|
|
600
|
+
*/
|
|
601
|
+
protected initializeSpecificComponents(): LCMComponent[];
|
|
602
|
+
/**
|
|
603
|
+
* Abstract method that subclasses must implement to bind their specific events
|
|
604
|
+
*/
|
|
605
|
+
protected bindSpecificEvents(): void;
|
|
606
|
+
/**
|
|
607
|
+
* Component-specific cleanup logic (required by BaseComponent)
|
|
608
|
+
*/
|
|
609
|
+
protected destroyComponent(): void;
|
|
610
|
+
/**
|
|
611
|
+
* Ensure an element exists, create if missing
|
|
612
|
+
* This is acceptable for page-level orchestration to find component root elements
|
|
613
|
+
*/
|
|
614
|
+
protected ensureElement(selector: string, fallbackId: string): HTMLElement;
|
|
615
|
+
protected dismissSplashScreen(): void;
|
|
616
|
+
static loadAfterPageLoaded<T>(pageName: string, PageClass: any, PageClassName: string): void;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
declare class TemplateLoader {
|
|
620
|
+
registryName: string;
|
|
621
|
+
constructor(registryName?: string);
|
|
622
|
+
/**
|
|
623
|
+
* Finds the specified template wrapper element in the registry.
|
|
624
|
+
* @param templateId The data-template-id of the wrapper element.
|
|
625
|
+
* @returns The wrapper HTMLElement or null if not found.
|
|
626
|
+
*/
|
|
627
|
+
private _findTemplateWrapper;
|
|
628
|
+
/**
|
|
629
|
+
* Loads and returns the inner HTML of a template definition.
|
|
630
|
+
* @param templateId The data-template-id of the wrapper element.
|
|
631
|
+
* @returns The innerHTML content as string if it exists otherwise null.
|
|
632
|
+
*/
|
|
633
|
+
loadHtml(templateId: string): string | null;
|
|
634
|
+
/**
|
|
635
|
+
* Loads and clones the child elements of a template definition.
|
|
636
|
+
* @param templateId The data-template-id of the wrapper element.
|
|
637
|
+
* @returns An array of cloned HTMLElement children, or an empty array if not found or has no children.
|
|
638
|
+
*/
|
|
639
|
+
load(templateId: string): HTMLElement[];
|
|
640
|
+
/**
|
|
641
|
+
* Loads a template's content, clears the target element, and appends the cloned content into it.
|
|
642
|
+
* @param templateId The data-template-id of the wrapper element to load.
|
|
643
|
+
* @param targetElement The HTMLElement where the cloned content should be placed.
|
|
644
|
+
* @returns True if the operation was successful (template found and content appended, even if content was empty), false otherwise.
|
|
645
|
+
*/
|
|
646
|
+
loadInto(templateId: string, targetElement: HTMLElement | null): boolean;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Utility for managing the splash screen that loads before JavaScript
|
|
651
|
+
*/
|
|
652
|
+
declare class SplashScreen {
|
|
653
|
+
private static readonly SPLASH_ID;
|
|
654
|
+
private static dismissed;
|
|
655
|
+
/**
|
|
656
|
+
* Dismiss the splash screen with a fade-out animation
|
|
657
|
+
* Safe to call multiple times - only dismisses once
|
|
658
|
+
*/
|
|
659
|
+
static dismiss(): void;
|
|
660
|
+
/**
|
|
661
|
+
* Update the splash screen message (if it hasn't been dismissed yet)
|
|
662
|
+
*/
|
|
663
|
+
static updateMessage(title?: string, message?: string): void;
|
|
664
|
+
/**
|
|
665
|
+
* Update the splash screen progress bar
|
|
666
|
+
* @param percent - Progress percentage (0-100)
|
|
667
|
+
*/
|
|
668
|
+
static updateProgress(percent: number): void;
|
|
669
|
+
/**
|
|
670
|
+
* Update both message and progress at once
|
|
671
|
+
*/
|
|
672
|
+
static update(options: {
|
|
673
|
+
title?: string;
|
|
674
|
+
message?: string;
|
|
675
|
+
progress?: number;
|
|
676
|
+
}): void;
|
|
677
|
+
/**
|
|
678
|
+
* Check if splash screen is still visible
|
|
679
|
+
*/
|
|
680
|
+
static isVisible(): boolean;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* MobileBottomDrawer - Reusable bottom drawer component for mobile layouts
|
|
685
|
+
*
|
|
686
|
+
* Features:
|
|
687
|
+
* - Slides up from bottom covering 60-70% of viewport
|
|
688
|
+
* - Backdrop overlay that dims the content behind
|
|
689
|
+
* - Auto-closes when backdrop is tapped
|
|
690
|
+
* - Swipe down to close gesture
|
|
691
|
+
* - Smooth slide-up/down animations
|
|
692
|
+
* - Holds any panel content
|
|
693
|
+
*/
|
|
694
|
+
declare class MobileBottomDrawer extends BaseComponent implements LCMComponent {
|
|
695
|
+
private backdropElement;
|
|
696
|
+
private drawerElement;
|
|
697
|
+
private contentElement;
|
|
698
|
+
private closeButton;
|
|
699
|
+
private isOpen;
|
|
700
|
+
private onCloseCallback?;
|
|
701
|
+
private touchStartY;
|
|
702
|
+
private touchCurrentY;
|
|
703
|
+
private isDragging;
|
|
704
|
+
/**
|
|
705
|
+
* Create a MobileBottomDrawer
|
|
706
|
+
* @param rootElement - The root container element for the drawer
|
|
707
|
+
* @param eventBus - Event bus for component communication
|
|
708
|
+
* @param debugMode - Enable debug logging
|
|
709
|
+
*/
|
|
710
|
+
constructor(rootElement: HTMLElement, eventBus: EventBus, debugMode?: boolean);
|
|
711
|
+
/**
|
|
712
|
+
* Phase 1: Initialize DOM and discover child components
|
|
713
|
+
*/
|
|
714
|
+
performLocalInit(): Promise<LCMComponent[]>;
|
|
715
|
+
/**
|
|
716
|
+
* Bind event listeners for drawer interactions
|
|
717
|
+
*/
|
|
718
|
+
private bindEvents;
|
|
719
|
+
/**
|
|
720
|
+
* Handle touch start for swipe gesture
|
|
721
|
+
*/
|
|
722
|
+
private handleTouchStart;
|
|
723
|
+
/**
|
|
724
|
+
* Handle touch move for swipe gesture
|
|
725
|
+
*/
|
|
726
|
+
private handleTouchMove;
|
|
727
|
+
/**
|
|
728
|
+
* Handle touch end for swipe gesture
|
|
729
|
+
*/
|
|
730
|
+
private handleTouchEnd;
|
|
731
|
+
/**
|
|
732
|
+
* Open the drawer with slide-up animation
|
|
733
|
+
*/
|
|
734
|
+
open(): void;
|
|
735
|
+
/**
|
|
736
|
+
* Close the drawer with slide-down animation
|
|
737
|
+
*/
|
|
738
|
+
close(): void;
|
|
739
|
+
/**
|
|
740
|
+
* Toggle drawer open/closed
|
|
741
|
+
*/
|
|
742
|
+
toggle(): void;
|
|
743
|
+
/**
|
|
744
|
+
* Check if drawer is currently open
|
|
745
|
+
*/
|
|
746
|
+
getIsOpen(): boolean;
|
|
747
|
+
/**
|
|
748
|
+
* Set the content element for the drawer
|
|
749
|
+
* @param element - The element to insert into the drawer content area
|
|
750
|
+
*/
|
|
751
|
+
setContent(element: HTMLElement): void;
|
|
752
|
+
/**
|
|
753
|
+
* Set a callback to be called when drawer closes
|
|
754
|
+
* @param callback - Function to call on close
|
|
755
|
+
*/
|
|
756
|
+
setOnClose(callback: () => void): void;
|
|
757
|
+
/**
|
|
758
|
+
* Get the content container element
|
|
759
|
+
*/
|
|
760
|
+
getContentContainer(): HTMLElement;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Common DOM utility functions for consistent behavior across components
|
|
765
|
+
*/
|
|
766
|
+
/**
|
|
767
|
+
* Checks if the user is currently typing in an input field, textarea, or other editable element.
|
|
768
|
+
* This is used to prevent keyboard shortcuts from interfering with user input.
|
|
769
|
+
*
|
|
770
|
+
* @param element - The target element from a keyboard event
|
|
771
|
+
* @returns true if the user is in an input context, false otherwise
|
|
772
|
+
*/
|
|
773
|
+
declare function isInInputContext(element: HTMLElement | null): boolean;
|
|
774
|
+
/**
|
|
775
|
+
* Checks if modifier keys are pressed (Ctrl, Alt, Cmd, Shift).
|
|
776
|
+
* This is commonly used to filter keyboard shortcuts.
|
|
777
|
+
*
|
|
778
|
+
* @param event - The keyboard event
|
|
779
|
+
* @returns true if any modifier keys are pressed, false otherwise
|
|
780
|
+
*/
|
|
781
|
+
declare function hasModifierKeys(event: KeyboardEvent): boolean;
|
|
782
|
+
/**
|
|
783
|
+
* Combined check for whether keyboard shortcuts should be ignored.
|
|
784
|
+
* This checks both modifier keys and input context.
|
|
785
|
+
*
|
|
786
|
+
* @param event - The keyboard event
|
|
787
|
+
* @returns true if shortcuts should be ignored, false if they can be processed
|
|
788
|
+
*/
|
|
789
|
+
declare function shouldIgnoreShortcut(event: KeyboardEvent): boolean;
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Generic keyboard shortcut manager for handling multi-key commands
|
|
793
|
+
* across all application pages
|
|
794
|
+
*
|
|
795
|
+
* Current Behavior (default):
|
|
796
|
+
* - User types 'c3' → visual indicator → Enter/timeout → execute
|
|
797
|
+
* - Requires explicit confirmation for execution
|
|
798
|
+
*
|
|
799
|
+
* Future Immediate Mode (when enabled):
|
|
800
|
+
* - User types 'c3' → immediate preview → Escape to cancel
|
|
801
|
+
* - Provides instant feedback with option to cancel
|
|
802
|
+
*/
|
|
803
|
+
interface ShortcutConfig {
|
|
804
|
+
key: string;
|
|
805
|
+
handler: (args?: string) => void;
|
|
806
|
+
description: string;
|
|
807
|
+
category?: string;
|
|
808
|
+
requiresArgs?: boolean;
|
|
809
|
+
argType?: 'number' | 'string';
|
|
810
|
+
contextFilter?: (event: KeyboardEvent) => boolean;
|
|
811
|
+
previewHandler?: (args?: string) => void;
|
|
812
|
+
cancelHandler?: () => void;
|
|
813
|
+
executeImmediately?: boolean;
|
|
814
|
+
}
|
|
815
|
+
interface ShortcutManagerConfig {
|
|
816
|
+
shortcuts: ShortcutConfig[];
|
|
817
|
+
helpContainer?: string;
|
|
818
|
+
timeout?: number;
|
|
819
|
+
immediateExecution?: boolean;
|
|
820
|
+
previewDelay?: number;
|
|
821
|
+
onStateChange?: (state: KeyboardState, command?: string) => void;
|
|
822
|
+
}
|
|
823
|
+
declare enum KeyboardState {
|
|
824
|
+
NORMAL = "normal",
|
|
825
|
+
AWAITING_ARGS = "awaiting_args"
|
|
826
|
+
}
|
|
827
|
+
declare class KeyboardShortcutManager {
|
|
828
|
+
private shortcuts;
|
|
829
|
+
private state;
|
|
830
|
+
private currentCommand;
|
|
831
|
+
private currentArgs;
|
|
832
|
+
private helpContainer;
|
|
833
|
+
private timeout;
|
|
834
|
+
private timeoutId;
|
|
835
|
+
private helpOverlay;
|
|
836
|
+
private immediateExecution;
|
|
837
|
+
private previewDelay;
|
|
838
|
+
private previewTimeoutId;
|
|
839
|
+
private isPreviewActive;
|
|
840
|
+
private onStateChange;
|
|
841
|
+
constructor(config: ShortcutManagerConfig);
|
|
842
|
+
private initialize;
|
|
843
|
+
private handleKeydown;
|
|
844
|
+
private handleNormalState;
|
|
845
|
+
private handleAwaitingArgsState;
|
|
846
|
+
private executeCurrentCommand;
|
|
847
|
+
private executeShortcut;
|
|
848
|
+
private schedulePreviewExecution;
|
|
849
|
+
private cancelPreviewExecution;
|
|
850
|
+
private executePreview;
|
|
851
|
+
private cancelCurrentPreview;
|
|
852
|
+
private resetState;
|
|
853
|
+
private startTimeout;
|
|
854
|
+
private resetTimeout;
|
|
855
|
+
private clearTimeout;
|
|
856
|
+
private updateStateIndicator;
|
|
857
|
+
private showHelp;
|
|
858
|
+
private hideHelp;
|
|
859
|
+
private generateHelpContent;
|
|
860
|
+
destroy(): void;
|
|
861
|
+
getState(): KeyboardState;
|
|
862
|
+
getCurrentCommand(): string;
|
|
863
|
+
getCurrentArgs(): string;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export { BaseComponent, BasePage, type Component, type ComponentEventType, ComponentEventTypes, type ComponentLifecycleEvent, EventBus, type EventHandler, type EventSubscriber, KeyboardShortcutManager, KeyboardState, type LCMComponent, type LCMComponentConfig, type LCMComponentEvent, LifecycleController, type LifecycleEventType, LifecycleEventTypes, MobileBottomDrawer, Modal, type ShortcutConfig, type ShortcutManagerConfig, SplashScreen, TemplateLoader, ThemeManager, ToastManager, hasModifierKeys, isInInputContext, shouldIgnoreShortcut };
|