@sc4rfurryx/proteusjs 1.0.0

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.
Files changed (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,545 @@
1
+ /**
2
+ * Flow Layout Engine for ProteusJS
3
+ * Natural content flow with reading pattern optimization
4
+ */
5
+
6
+ export interface FlowConfig {
7
+ pattern: 'z-pattern' | 'f-pattern' | 'gutenberg' | 'natural' | 'custom' | 'auto';
8
+ direction: 'ltr' | 'rtl' | 'auto';
9
+ language: string;
10
+ accessibility: boolean;
11
+ focusManagement: boolean;
12
+ skipLinks: boolean;
13
+ landmarks: boolean;
14
+ readingOrder: 'visual' | 'logical' | 'auto';
15
+ customFlow?: FlowStep[];
16
+ }
17
+
18
+ export interface FlowStep {
19
+ selector: string;
20
+ priority: number;
21
+ region: 'header' | 'main' | 'content' | 'footer' | 'navigation' | 'sidebar';
22
+ tabIndex?: number;
23
+ ariaLabel?: string;
24
+ landmark?: string;
25
+ }
26
+
27
+ export interface FlowState {
28
+ currentPattern: string;
29
+ direction: 'ltr' | 'rtl';
30
+ focusableElements: Element[];
31
+ tabOrder: Element[];
32
+ landmarks: Map<string, Element>;
33
+ skipTargets: Element[];
34
+ }
35
+
36
+ export class FlowLayout {
37
+ private element: Element;
38
+ private config: Required<FlowConfig>;
39
+ private state: FlowState;
40
+ private mutationObserver: MutationObserver | null = null;
41
+
42
+ private static readonly READING_PATTERNS = {
43
+ 'z-pattern': [
44
+ { selector: 'header, [role="banner"]', region: 'header' as const, priority: 1 },
45
+ { selector: 'nav, [role="navigation"]', region: 'navigation' as const, priority: 2 },
46
+ { selector: 'main, [role="main"]', region: 'main' as const, priority: 3 },
47
+ { selector: 'aside, [role="complementary"]', region: 'sidebar' as const, priority: 4 },
48
+ { selector: 'footer, [role="contentinfo"]', region: 'footer' as const, priority: 5 }
49
+ ],
50
+ 'f-pattern': [
51
+ { selector: 'header, [role="banner"]', region: 'header' as const, priority: 1 },
52
+ { selector: 'main, [role="main"]', region: 'main' as const, priority: 2 },
53
+ { selector: 'aside, [role="complementary"]', region: 'sidebar' as const, priority: 3 },
54
+ { selector: 'nav, [role="navigation"]', region: 'navigation' as const, priority: 4 },
55
+ { selector: 'footer, [role="contentinfo"]', region: 'footer' as const, priority: 5 }
56
+ ],
57
+ 'gutenberg': [
58
+ { selector: 'nav, [role="navigation"]', region: 'navigation' as const, priority: 1 },
59
+ { selector: 'header, [role="banner"]', region: 'header' as const, priority: 2 },
60
+ { selector: 'main, [role="main"]', region: 'main' as const, priority: 3 },
61
+ { selector: 'aside, [role="complementary"]', region: 'sidebar' as const, priority: 4 },
62
+ { selector: 'footer, [role="contentinfo"]', region: 'footer' as const, priority: 5 }
63
+ ],
64
+ 'natural': [
65
+ { selector: 'header, [role="banner"]', region: 'header' as const, priority: 1 },
66
+ { selector: 'main, [role="main"]', region: 'main' as const, priority: 2 },
67
+ { selector: 'nav, [role="navigation"]', region: 'navigation' as const, priority: 3 },
68
+ { selector: 'aside, [role="complementary"]', region: 'sidebar' as const, priority: 4 },
69
+ { selector: 'footer, [role="contentinfo"]', region: 'footer' as const, priority: 5 }
70
+ ]
71
+ };
72
+
73
+ private static readonly RTL_LANGUAGES = [
74
+ 'ar', 'he', 'fa', 'ur', 'yi', 'ji', 'iw', 'ku', 'ps', 'sd'
75
+ ];
76
+
77
+ constructor(element: Element, config: Partial<FlowConfig> = {}) {
78
+ this.element = element;
79
+ this.config = {
80
+ pattern: 'natural',
81
+ direction: 'auto',
82
+ language: 'en',
83
+ accessibility: true,
84
+ focusManagement: true,
85
+ skipLinks: true,
86
+ landmarks: true,
87
+ readingOrder: 'auto',
88
+ customFlow: [],
89
+ ...config
90
+ };
91
+
92
+ this.state = this.createInitialState();
93
+ this.activate();
94
+ }
95
+
96
+ /**
97
+ * Activate the flow layout
98
+ */
99
+ public activate(): void {
100
+ this.analyzeContent();
101
+ this.applyFlowPattern();
102
+ this.setupAccessibility();
103
+ this.setupObservers();
104
+ }
105
+
106
+ /**
107
+ * Deactivate and clean up
108
+ */
109
+ public deactivate(): void {
110
+ this.cleanupObservers();
111
+ this.removeFlowStyles();
112
+ this.removeAccessibilityFeatures();
113
+ }
114
+
115
+ /**
116
+ * Update flow configuration
117
+ */
118
+ public updateConfig(newConfig: Partial<FlowConfig>): void {
119
+ this.config = { ...this.config, ...newConfig };
120
+ this.activate();
121
+ }
122
+
123
+ /**
124
+ * Get current flow state
125
+ */
126
+ public getState(): FlowState {
127
+ return { ...this.state };
128
+ }
129
+
130
+ /**
131
+ * Manually set tab order
132
+ */
133
+ public setTabOrder(elements: Element[]): void {
134
+ elements.forEach((element, index) => {
135
+ (element as HTMLElement).tabIndex = index + 1;
136
+ });
137
+ this.state.tabOrder = elements;
138
+ }
139
+
140
+ /**
141
+ * Add skip link
142
+ */
143
+ public addSkipLink(target: Element, label: string): void {
144
+ if (!this.config.skipLinks) return;
145
+
146
+ const skipLink = document.createElement('a');
147
+ skipLink.href = `#${this.ensureId(target)}`;
148
+ skipLink.textContent = label;
149
+ skipLink.className = 'proteus-skip-link';
150
+ skipLink.style.cssText = `
151
+ position: absolute;
152
+ top: -40px;
153
+ left: 6px;
154
+ background: #000;
155
+ color: #fff;
156
+ padding: 8px;
157
+ text-decoration: none;
158
+ z-index: 1000;
159
+ transition: top 0.3s;
160
+ `;
161
+
162
+ // Show on focus
163
+ skipLink.addEventListener('focus', () => {
164
+ skipLink.style.top = '6px';
165
+ });
166
+ skipLink.addEventListener('blur', () => {
167
+ skipLink.style.top = '-40px';
168
+ });
169
+
170
+ document.body.insertBefore(skipLink, document.body.firstChild);
171
+ this.state.skipTargets.push(target);
172
+ }
173
+
174
+ /**
175
+ * Analyze content structure
176
+ */
177
+ private analyzeContent(): void {
178
+ // Detect reading direction
179
+ this.state.direction = this.detectReadingDirection();
180
+
181
+ // Find focusable elements
182
+ this.state.focusableElements = this.findFocusableElements();
183
+
184
+ // Identify landmarks
185
+ this.state.landmarks = this.identifyLandmarks();
186
+
187
+ // Determine optimal pattern
188
+ this.state.currentPattern = this.determineOptimalPattern();
189
+ }
190
+
191
+ /**
192
+ * Apply flow pattern
193
+ */
194
+ private applyFlowPattern(): void {
195
+ let pattern;
196
+ if (this.config.pattern === 'custom') {
197
+ pattern = this.config.customFlow;
198
+ } else if (this.config.pattern === 'auto') {
199
+ const optimalPattern = this.determineOptimalPattern();
200
+ pattern = FlowLayout.READING_PATTERNS[optimalPattern as keyof typeof FlowLayout.READING_PATTERNS];
201
+ } else {
202
+ pattern = FlowLayout.READING_PATTERNS[this.config.pattern];
203
+ }
204
+
205
+ if (!pattern) return;
206
+
207
+ // Sort elements by pattern priority
208
+ const sortedElements = this.sortElementsByPattern(pattern);
209
+
210
+ // Apply visual flow
211
+ this.applyVisualFlow(sortedElements);
212
+
213
+ // Set logical tab order
214
+ if (this.config.focusManagement) {
215
+ this.setLogicalTabOrder(sortedElements);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Setup accessibility features
221
+ */
222
+ private setupAccessibility(): void {
223
+ if (!this.config.accessibility) return;
224
+
225
+ // Add landmarks
226
+ if (this.config.landmarks) {
227
+ this.addLandmarks();
228
+ }
229
+
230
+ // Add skip links
231
+ if (this.config.skipLinks) {
232
+ this.addDefaultSkipLinks();
233
+ }
234
+
235
+ // Set reading order
236
+ this.setReadingOrder();
237
+ }
238
+
239
+ /**
240
+ * Detect reading direction
241
+ */
242
+ private detectReadingDirection(): 'ltr' | 'rtl' {
243
+ if (this.config.direction !== 'auto') {
244
+ return this.config.direction;
245
+ }
246
+
247
+ const langCode = this.config.language?.split('-')[0]?.toLowerCase();
248
+ return langCode && FlowLayout.RTL_LANGUAGES.includes(langCode) ? 'rtl' : 'ltr';
249
+ }
250
+
251
+ /**
252
+ * Find all focusable elements
253
+ */
254
+ private findFocusableElements(): Element[] {
255
+ const focusableSelectors = [
256
+ 'a[href]',
257
+ 'button:not([disabled])',
258
+ 'input:not([disabled])',
259
+ 'select:not([disabled])',
260
+ 'textarea:not([disabled])',
261
+ '[tabindex]:not([tabindex="-1"])',
262
+ '[contenteditable="true"]'
263
+ ].join(', ');
264
+
265
+ return Array.from(this.element.querySelectorAll(focusableSelectors));
266
+ }
267
+
268
+ /**
269
+ * Identify semantic landmarks
270
+ */
271
+ private identifyLandmarks(): Map<string, Element> {
272
+ const landmarks = new Map<string, Element>();
273
+
274
+ const landmarkSelectors = {
275
+ 'banner': 'header, [role="banner"]',
276
+ 'main': 'main, [role="main"]',
277
+ 'navigation': 'nav, [role="navigation"]',
278
+ 'complementary': 'aside, [role="complementary"]',
279
+ 'contentinfo': 'footer, [role="contentinfo"]',
280
+ 'search': '[role="search"]',
281
+ 'form': 'form, [role="form"]'
282
+ };
283
+
284
+ Object.entries(landmarkSelectors).forEach(([role, selector]) => {
285
+ const element = this.element.querySelector(selector);
286
+ if (element) {
287
+ landmarks.set(role, element);
288
+ }
289
+ });
290
+
291
+ return landmarks;
292
+ }
293
+
294
+ /**
295
+ * Determine optimal reading pattern
296
+ */
297
+ private determineOptimalPattern(): string {
298
+ if (this.config.pattern !== 'auto') {
299
+ return this.config.pattern;
300
+ }
301
+
302
+ // Analyze layout structure to suggest pattern
303
+ const hasHeader = this.state.landmarks.has('banner');
304
+ const hasSidebar = this.state.landmarks.has('complementary');
305
+ const hasNav = this.state.landmarks.has('navigation');
306
+
307
+ if (hasHeader && hasSidebar && hasNav) {
308
+ return 'z-pattern';
309
+ } else if (hasHeader && hasSidebar) {
310
+ return 'f-pattern';
311
+ } else {
312
+ return 'natural';
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Sort elements by pattern priority
318
+ */
319
+ private sortElementsByPattern(pattern: FlowStep[]): Element[] {
320
+ const elements: Array<{ element: Element; priority: number }> = [];
321
+
322
+ pattern.forEach(step => {
323
+ const stepElements = Array.from(this.element.querySelectorAll(step.selector));
324
+ stepElements.forEach(element => {
325
+ elements.push({ element, priority: step.priority });
326
+ });
327
+ });
328
+
329
+ // Sort by priority, then by DOM order
330
+ return elements
331
+ .sort((a, b) => {
332
+ if (a.priority !== b.priority) {
333
+ return a.priority - b.priority;
334
+ }
335
+ // Use DOM order as tiebreaker
336
+ return a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
337
+ })
338
+ .map(item => item.element);
339
+ }
340
+
341
+ /**
342
+ * Apply visual flow styles
343
+ */
344
+ private applyVisualFlow(elements: Element[]): void {
345
+ elements.forEach((element, index) => {
346
+ const htmlElement = element as HTMLElement;
347
+ htmlElement.style.setProperty('--flow-order', index.toString());
348
+ htmlElement.classList.add('proteus-flow-item');
349
+ });
350
+
351
+ // Add CSS for visual flow
352
+ this.addFlowCSS();
353
+ }
354
+
355
+ /**
356
+ * Set logical tab order
357
+ */
358
+ private setLogicalTabOrder(elements: Element[]): void {
359
+ const focusableInOrder = elements.filter(el =>
360
+ this.state.focusableElements.includes(el)
361
+ );
362
+
363
+ focusableInOrder.forEach((element, index) => {
364
+ (element as HTMLElement).tabIndex = index + 1;
365
+ });
366
+
367
+ this.state.tabOrder = focusableInOrder;
368
+ }
369
+
370
+ /**
371
+ * Add landmark roles and labels
372
+ */
373
+ private addLandmarks(): void {
374
+ this.state.landmarks.forEach((element, role) => {
375
+ const htmlElement = element as HTMLElement;
376
+
377
+ if (!htmlElement.getAttribute('role')) {
378
+ htmlElement.setAttribute('role', role);
379
+ }
380
+
381
+ if (!htmlElement.getAttribute('aria-label') && !htmlElement.getAttribute('aria-labelledby')) {
382
+ const label = this.generateLandmarkLabel(role);
383
+ htmlElement.setAttribute('aria-label', label);
384
+ }
385
+ });
386
+ }
387
+
388
+ /**
389
+ * Add default skip links
390
+ */
391
+ private addDefaultSkipLinks(): void {
392
+ const mainContent = this.state.landmarks.get('main');
393
+ if (mainContent) {
394
+ this.addSkipLink(mainContent, 'Skip to main content');
395
+ }
396
+
397
+ const navigation = this.state.landmarks.get('navigation');
398
+ if (navigation) {
399
+ this.addSkipLink(navigation, 'Skip to navigation');
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Set reading order for screen readers
405
+ */
406
+ private setReadingOrder(): void {
407
+ if (this.config.readingOrder === 'visual') {
408
+ // Use CSS order for screen readers
409
+ (this.element as HTMLElement).style.setProperty('--reading-order', 'visual');
410
+ } else {
411
+ // Maintain logical DOM order
412
+ (this.element as HTMLElement).style.setProperty('--reading-order', 'logical');
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Generate landmark label
418
+ */
419
+ private generateLandmarkLabel(role: string): string {
420
+ const labels = {
421
+ 'banner': 'Site header',
422
+ 'main': 'Main content',
423
+ 'navigation': 'Site navigation',
424
+ 'complementary': 'Sidebar',
425
+ 'contentinfo': 'Site footer',
426
+ 'search': 'Search',
427
+ 'form': 'Form'
428
+ };
429
+
430
+ return labels[role as keyof typeof labels] || role;
431
+ }
432
+
433
+ /**
434
+ * Add CSS for flow layout
435
+ */
436
+ private addFlowCSS(): void {
437
+ const styleId = 'proteus-flow-styles';
438
+ if (document.getElementById(styleId)) return;
439
+
440
+ const style = document.createElement('style');
441
+ style.id = styleId;
442
+ style.textContent = `
443
+ .proteus-flow-item {
444
+ order: var(--flow-order, 0);
445
+ }
446
+
447
+ .proteus-skip-link:focus {
448
+ clip: auto !important;
449
+ height: auto !important;
450
+ margin: 0 !important;
451
+ overflow: visible !important;
452
+ position: absolute !important;
453
+ width: auto !important;
454
+ }
455
+
456
+ [dir="rtl"] .proteus-flow-item {
457
+ direction: rtl;
458
+ }
459
+ `;
460
+
461
+ document.head.appendChild(style);
462
+ }
463
+
464
+ /**
465
+ * Ensure element has an ID
466
+ */
467
+ private ensureId(element: Element): string {
468
+ if (!element.id) {
469
+ element.id = `proteus-flow-${Math.random().toString(36).substring(2, 11)}`;
470
+ }
471
+ return element.id;
472
+ }
473
+
474
+ /**
475
+ * Setup observers
476
+ */
477
+ private setupObservers(): void {
478
+ this.mutationObserver = new MutationObserver(() => {
479
+ this.analyzeContent();
480
+ this.applyFlowPattern();
481
+ });
482
+
483
+ this.mutationObserver.observe(this.element, {
484
+ childList: true,
485
+ subtree: true,
486
+ attributes: true,
487
+ attributeFilter: ['role', 'tabindex']
488
+ });
489
+ }
490
+
491
+ /**
492
+ * Clean up observers
493
+ */
494
+ private cleanupObservers(): void {
495
+ if (this.mutationObserver) {
496
+ this.mutationObserver.disconnect();
497
+ this.mutationObserver = null;
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Remove flow styles
503
+ */
504
+ private removeFlowStyles(): void {
505
+ const flowItems = this.element.querySelectorAll('.proteus-flow-item');
506
+ flowItems.forEach(item => {
507
+ const htmlItem = item as HTMLElement;
508
+ htmlItem.classList.remove('proteus-flow-item');
509
+ htmlItem.style.removeProperty('--flow-order');
510
+ });
511
+
512
+ const styleElement = document.getElementById('proteus-flow-styles');
513
+ if (styleElement) {
514
+ styleElement.remove();
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Remove accessibility features
520
+ */
521
+ private removeAccessibilityFeatures(): void {
522
+ // Remove skip links
523
+ const skipLinks = document.querySelectorAll('.proteus-skip-link');
524
+ skipLinks.forEach(link => link.remove());
525
+
526
+ // Reset tab indices
527
+ this.state.tabOrder.forEach(element => {
528
+ (element as HTMLElement).removeAttribute('tabindex');
529
+ });
530
+ }
531
+
532
+ /**
533
+ * Create initial state
534
+ */
535
+ private createInitialState(): FlowState {
536
+ return {
537
+ currentPattern: 'natural',
538
+ direction: 'ltr',
539
+ focusableElements: [],
540
+ tabOrder: [],
541
+ landmarks: new Map(),
542
+ skipTargets: []
543
+ };
544
+ }
545
+ }