@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,512 @@
1
+ /**
2
+ * Responsive Spacing System for ProteusJS
3
+ * Fluid spacing with proportional scaling and accessibility compliance
4
+ */
5
+
6
+ export interface SpacingConfig {
7
+ baseSize: number;
8
+ scale: 'minor-second' | 'major-second' | 'minor-third' | 'major-third' | 'perfect-fourth' | 'golden-ratio' | number;
9
+ density: 'compact' | 'comfortable' | 'spacious';
10
+ containerAware: boolean;
11
+ accessibility: boolean;
12
+ touchTargets: boolean;
13
+ minTouchSize: number;
14
+ maxSpacing: number;
15
+ responsive: boolean;
16
+ breakpoints?: Record<string, Partial<SpacingConfig>>;
17
+ }
18
+
19
+ export interface SpacingScale {
20
+ xs: number;
21
+ sm: number;
22
+ md: number;
23
+ lg: number;
24
+ xl: number;
25
+ xxl: number;
26
+ }
27
+
28
+ export interface TouchTargetConfig {
29
+ minSize: number;
30
+ preferredSize: number;
31
+ spacing: number;
32
+ interactive: boolean;
33
+ }
34
+
35
+ export interface SpacingState {
36
+ currentScale: SpacingScale;
37
+ containerSize: number;
38
+ scaleFactor: number;
39
+ touchCompliant: boolean;
40
+ appliedSpacing: Map<Element, string>;
41
+ }
42
+
43
+ export class SpacingSystem {
44
+ private element: Element;
45
+ private config: Required<SpacingConfig>;
46
+ private state: SpacingState;
47
+ private resizeObserver: ResizeObserver | null = null;
48
+
49
+ private static readonly SCALE_RATIOS = {
50
+ 'minor-second': 1.067,
51
+ 'major-second': 1.125,
52
+ 'minor-third': 1.2,
53
+ 'major-third': 1.25,
54
+ 'perfect-fourth': 1.333,
55
+ 'golden-ratio': 1.618
56
+ };
57
+
58
+ private static readonly DENSITY_MULTIPLIERS = {
59
+ 'compact': 0.8,
60
+ 'comfortable': 1.0,
61
+ 'spacious': 1.25
62
+ };
63
+
64
+ private static readonly WCAG_MIN_TOUCH_SIZE = 44; // pixels
65
+
66
+ constructor(element: Element, config: Partial<SpacingConfig> = {}) {
67
+ this.element = element;
68
+ this.config = {
69
+ baseSize: 16,
70
+ scale: 'minor-third',
71
+ density: 'comfortable',
72
+ containerAware: true,
73
+ accessibility: true,
74
+ touchTargets: true,
75
+ minTouchSize: SpacingSystem.WCAG_MIN_TOUCH_SIZE,
76
+ maxSpacing: 128,
77
+ responsive: true,
78
+ breakpoints: {},
79
+ ...config
80
+ };
81
+
82
+ this.state = this.createInitialState();
83
+ this.setupSpacing();
84
+ }
85
+
86
+ /**
87
+ * Activate the spacing system
88
+ */
89
+ public activate(): void {
90
+ this.calculateSpacing();
91
+ this.applySpacing();
92
+ this.setupObservers();
93
+ }
94
+
95
+ /**
96
+ * Deactivate and clean up
97
+ */
98
+ public deactivate(): void {
99
+ this.cleanupObservers();
100
+ this.removeSpacing();
101
+ }
102
+
103
+ /**
104
+ * Update spacing configuration
105
+ */
106
+ public updateConfig(newConfig: Partial<SpacingConfig>): void {
107
+ this.config = { ...this.config, ...newConfig };
108
+ this.activate();
109
+ }
110
+
111
+ /**
112
+ * Get current spacing state
113
+ */
114
+ public getState(): SpacingState {
115
+ return { ...this.state };
116
+ }
117
+
118
+ /**
119
+ * Apply spacing to specific element
120
+ */
121
+ public applyToElement(element: Element, spacingType: keyof SpacingScale): void {
122
+ const spacing = this.state.currentScale[spacingType];
123
+ const htmlElement = element as HTMLElement;
124
+
125
+ htmlElement.style.setProperty('--spacing', `${spacing}px`);
126
+ this.state.appliedSpacing.set(element, spacingType);
127
+ }
128
+
129
+ /**
130
+ * Apply margin spacing
131
+ */
132
+ public applyMargin(element: Element, spacing: keyof SpacingScale | number): void {
133
+ const value = typeof spacing === 'number' ? spacing : this.state.currentScale[spacing];
134
+ const htmlElement = element as HTMLElement;
135
+
136
+ htmlElement.style.margin = `${value}px`;
137
+ }
138
+
139
+ /**
140
+ * Apply padding spacing
141
+ */
142
+ public applyPadding(element: Element, spacing: keyof SpacingScale | number): void {
143
+ const value = typeof spacing === 'number' ? spacing : this.state.currentScale[spacing];
144
+ const htmlElement = element as HTMLElement;
145
+
146
+ htmlElement.style.padding = `${value}px`;
147
+ }
148
+
149
+ /**
150
+ * Apply gap spacing (for flex/grid)
151
+ */
152
+ public applyGap(element: Element, spacing: keyof SpacingScale | number): void {
153
+ const value = typeof spacing === 'number' ? spacing : this.state.currentScale[spacing];
154
+ const htmlElement = element as HTMLElement;
155
+
156
+ htmlElement.style.gap = `${value}px`;
157
+ }
158
+
159
+ /**
160
+ * Ensure touch target compliance
161
+ */
162
+ public ensureTouchTargets(): void {
163
+ if (!this.config.touchTargets) return;
164
+
165
+ const interactiveElements = this.findInteractiveElements();
166
+
167
+ interactiveElements.forEach(element => {
168
+ this.makeTouchCompliant(element);
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Generate spacing scale
174
+ */
175
+ public generateScale(): SpacingScale {
176
+ const ratio = typeof this.config.scale === 'number'
177
+ ? this.config.scale
178
+ : SpacingSystem.SCALE_RATIOS[this.config.scale];
179
+
180
+ const densityMultiplier = SpacingSystem.DENSITY_MULTIPLIERS[this.config.density];
181
+ const containerMultiplier = this.calculateContainerMultiplier();
182
+
183
+ const baseSize = this.config.baseSize * densityMultiplier * containerMultiplier;
184
+
185
+ return {
186
+ xs: Math.round(baseSize / (ratio * ratio)), // base / ratio²
187
+ sm: Math.round(baseSize / ratio), // base / ratio
188
+ md: Math.round(baseSize), // base
189
+ lg: Math.round(baseSize * ratio), // base * ratio
190
+ xl: Math.round(baseSize * ratio * ratio), // base * ratio²
191
+ xxl: Math.min(Math.round(baseSize * ratio * ratio * ratio), this.config.maxSpacing) // base * ratio³
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Calculate optimal spacing for content
197
+ */
198
+ public calculateOptimalSpacing(contentType: 'text' | 'interactive' | 'layout' | 'component'): keyof SpacingScale {
199
+ switch (contentType) {
200
+ case 'text':
201
+ return this.config.density === 'compact' ? 'sm' : 'md';
202
+ case 'interactive':
203
+ return this.config.accessibility ? 'lg' : 'md';
204
+ case 'layout':
205
+ return this.config.density === 'spacious' ? 'xl' : 'lg';
206
+ case 'component':
207
+ return 'md';
208
+ default:
209
+ return 'md';
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Validate accessibility compliance
215
+ */
216
+ public validateAccessibility(): { compliant: boolean; issues: string[] } {
217
+ const issues: string[] = [];
218
+
219
+ if (this.config.touchTargets) {
220
+ const interactiveElements = this.findInteractiveElements();
221
+
222
+ interactiveElements.forEach(element => {
223
+ const rect = element.getBoundingClientRect();
224
+ const minSize = this.config.minTouchSize;
225
+
226
+ if (rect.width < minSize || rect.height < minSize) {
227
+ issues.push(`Touch target too small: ${rect.width}x${rect.height} (minimum: ${minSize}x${minSize})`);
228
+ }
229
+ });
230
+ }
231
+
232
+ // Check spacing ratios
233
+ const scale = this.state.currentScale;
234
+ const ratios = [
235
+ scale.sm / scale.xs,
236
+ scale.md / scale.sm,
237
+ scale.lg / scale.md,
238
+ scale.xl / scale.lg
239
+ ];
240
+
241
+ const inconsistentRatios = ratios.length > 0 && ratios.some(ratio => Math.abs(ratio - ratios[0]!) > 0.1);
242
+ if (inconsistentRatios) {
243
+ issues.push('Inconsistent spacing ratios detected');
244
+ }
245
+
246
+ return {
247
+ compliant: issues.length === 0,
248
+ issues
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Setup initial spacing
254
+ */
255
+ private setupSpacing(): void {
256
+ this.calculateSpacing();
257
+ }
258
+
259
+ /**
260
+ * Calculate spacing values
261
+ */
262
+ private calculateSpacing(): void {
263
+ const containerRect = this.element.getBoundingClientRect();
264
+ const activeConfig = this.getActiveConfig(containerRect.width);
265
+
266
+ // Update state
267
+ this.state = {
268
+ currentScale: this.generateScale(),
269
+ containerSize: containerRect.width,
270
+ scaleFactor: this.calculateContainerMultiplier(),
271
+ touchCompliant: this.validateTouchCompliance(),
272
+ appliedSpacing: new Map()
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Apply spacing to elements
278
+ */
279
+ private applySpacing(): void {
280
+ // Apply CSS custom properties
281
+ const htmlElement = this.element as HTMLElement;
282
+ const scale = this.state.currentScale;
283
+
284
+ Object.entries(scale).forEach(([key, value]) => {
285
+ htmlElement.style.setProperty(`--spacing-${key}`, `${value}px`);
286
+ });
287
+
288
+ // Apply spacing classes
289
+ this.addSpacingCSS();
290
+
291
+ // Ensure touch targets if enabled
292
+ if (this.config.touchTargets) {
293
+ this.ensureTouchTargets();
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Calculate container-based multiplier
299
+ */
300
+ private calculateContainerMultiplier(): number {
301
+ if (!this.config.containerAware) return 1;
302
+
303
+ const containerWidth = this.element.getBoundingClientRect().width;
304
+
305
+ // Scale spacing based on container size
306
+ // Smaller containers get smaller spacing, larger containers get larger spacing
307
+ const baseWidth = 800; // Reference width
308
+ const minMultiplier = 0.75;
309
+ const maxMultiplier = 1.5;
310
+
311
+ const ratio = containerWidth / baseWidth;
312
+ return Math.max(minMultiplier, Math.min(maxMultiplier, ratio));
313
+ }
314
+
315
+ /**
316
+ * Get active configuration based on container width
317
+ */
318
+ private getActiveConfig(containerWidth: number): Required<SpacingConfig> {
319
+ let activeConfig = { ...this.config };
320
+
321
+ if (this.config.breakpoints) {
322
+ const sortedBreakpoints = Object.entries(this.config.breakpoints)
323
+ .map(([name, config]) => ({ name, width: parseInt(name), config }))
324
+ .sort((a, b) => a.width - b.width);
325
+
326
+ for (const breakpoint of sortedBreakpoints) {
327
+ if (containerWidth >= breakpoint.width) {
328
+ activeConfig = { ...activeConfig, ...breakpoint.config };
329
+ }
330
+ }
331
+ }
332
+
333
+ return activeConfig;
334
+ }
335
+
336
+ /**
337
+ * Find interactive elements
338
+ */
339
+ private findInteractiveElements(): Element[] {
340
+ const interactiveSelectors = [
341
+ 'button',
342
+ 'a[href]',
343
+ 'input',
344
+ 'select',
345
+ 'textarea',
346
+ '[tabindex]:not([tabindex="-1"])',
347
+ '[role="button"]',
348
+ '[role="link"]',
349
+ '[onclick]'
350
+ ].join(', ');
351
+
352
+ return Array.from(this.element.querySelectorAll(interactiveSelectors));
353
+ }
354
+
355
+ /**
356
+ * Make element touch compliant
357
+ */
358
+ private makeTouchCompliant(element: Element): void {
359
+ const htmlElement = element as HTMLElement;
360
+ const rect = element.getBoundingClientRect();
361
+ const minSize = this.config.minTouchSize;
362
+
363
+ if (rect.width < minSize || rect.height < minSize) {
364
+ htmlElement.style.minWidth = `${minSize}px`;
365
+ htmlElement.style.minHeight = `${minSize}px`;
366
+ htmlElement.style.display = htmlElement.style.display || 'inline-block';
367
+ }
368
+
369
+ // Ensure adequate spacing between touch targets
370
+ const spacing = this.state.currentScale.sm;
371
+ htmlElement.style.margin = `${spacing / 2}px`;
372
+ }
373
+
374
+ /**
375
+ * Validate touch compliance
376
+ */
377
+ private validateTouchCompliance(): boolean {
378
+ if (!this.config.touchTargets) return true;
379
+
380
+ const interactiveElements = this.findInteractiveElements();
381
+
382
+ return interactiveElements.every(element => {
383
+ const rect = element.getBoundingClientRect();
384
+ return rect.width >= this.config.minTouchSize && rect.height >= this.config.minTouchSize;
385
+ });
386
+ }
387
+
388
+ /**
389
+ * Add spacing CSS utilities
390
+ */
391
+ private addSpacingCSS(): void {
392
+ const styleId = 'proteus-spacing-styles';
393
+ if (document.getElementById(styleId)) return;
394
+
395
+ const style = document.createElement('style');
396
+ style.id = styleId;
397
+
398
+ const scale = this.state.currentScale;
399
+ let css = ':root {\n';
400
+
401
+ Object.entries(scale).forEach(([key, value]) => {
402
+ css += ` --spacing-${key}: ${value}px;\n`;
403
+ });
404
+
405
+ css += '}\n\n';
406
+
407
+ // Utility classes
408
+ Object.entries(scale).forEach(([key, value]) => {
409
+ css += `.m-${key} { margin: ${value}px; }\n`;
410
+ css += `.mt-${key} { margin-top: ${value}px; }\n`;
411
+ css += `.mr-${key} { margin-right: ${value}px; }\n`;
412
+ css += `.mb-${key} { margin-bottom: ${value}px; }\n`;
413
+ css += `.ml-${key} { margin-left: ${value}px; }\n`;
414
+ css += `.mx-${key} { margin-left: ${value}px; margin-right: ${value}px; }\n`;
415
+ css += `.my-${key} { margin-top: ${value}px; margin-bottom: ${value}px; }\n`;
416
+
417
+ css += `.p-${key} { padding: ${value}px; }\n`;
418
+ css += `.pt-${key} { padding-top: ${value}px; }\n`;
419
+ css += `.pr-${key} { padding-right: ${value}px; }\n`;
420
+ css += `.pb-${key} { padding-bottom: ${value}px; }\n`;
421
+ css += `.pl-${key} { padding-left: ${value}px; }\n`;
422
+ css += `.px-${key} { padding-left: ${value}px; padding-right: ${value}px; }\n`;
423
+ css += `.py-${key} { padding-top: ${value}px; padding-bottom: ${value}px; }\n`;
424
+
425
+ css += `.gap-${key} { gap: ${value}px; }\n`;
426
+ });
427
+
428
+ // Touch target utilities
429
+ css += `
430
+ .touch-target {
431
+ min-width: ${this.config.minTouchSize}px;
432
+ min-height: ${this.config.minTouchSize}px;
433
+ display: inline-block;
434
+ }
435
+
436
+ .touch-spacing {
437
+ margin: ${this.state.currentScale.sm / 2}px;
438
+ }
439
+ `;
440
+
441
+ style.textContent = css;
442
+ document.head.appendChild(style);
443
+ }
444
+
445
+ /**
446
+ * Setup observers
447
+ */
448
+ private setupObservers(): void {
449
+ if (!this.config.responsive) return;
450
+
451
+ this.resizeObserver = new ResizeObserver(() => {
452
+ this.calculateSpacing();
453
+ this.applySpacing();
454
+ });
455
+
456
+ this.resizeObserver.observe(this.element);
457
+ }
458
+
459
+ /**
460
+ * Clean up observers
461
+ */
462
+ private cleanupObservers(): void {
463
+ if (this.resizeObserver) {
464
+ this.resizeObserver.disconnect();
465
+ this.resizeObserver = null;
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Remove spacing
471
+ */
472
+ private removeSpacing(): void {
473
+ const htmlElement = this.element as HTMLElement;
474
+ const scale = this.state.currentScale;
475
+
476
+ // Remove CSS custom properties
477
+ Object.keys(scale).forEach(key => {
478
+ htmlElement.style.removeProperty(`--spacing-${key}`);
479
+ });
480
+
481
+ // Remove applied spacing
482
+ this.state.appliedSpacing.forEach((spacingType, element) => {
483
+ (element as HTMLElement).style.removeProperty('--spacing');
484
+ });
485
+
486
+ // Remove style element
487
+ const styleElement = document.getElementById('proteus-spacing-styles');
488
+ if (styleElement) {
489
+ styleElement.remove();
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Create initial state
495
+ */
496
+ private createInitialState(): SpacingState {
497
+ return {
498
+ currentScale: {
499
+ xs: 4,
500
+ sm: 8,
501
+ md: 16,
502
+ lg: 24,
503
+ xl: 32,
504
+ xxl: 48
505
+ },
506
+ containerSize: 0,
507
+ scaleFactor: 1,
508
+ touchCompliant: true,
509
+ appliedSpacing: new Map()
510
+ };
511
+ }
512
+ }