@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,432 @@
1
+ /**
2
+ * FluidTypography Test Suite
3
+ * Comprehensive tests for fluid typography system
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
+ import { FluidTypography, FluidConfig, ContainerBasedConfig } from '../FluidTypography';
8
+
9
+ // Mock ResizeObserver
10
+ global.ResizeObserver = vi.fn().mockImplementation((callback) => ({
11
+ observe: vi.fn(),
12
+ unobserve: vi.fn(),
13
+ disconnect: vi.fn(),
14
+ }));
15
+
16
+ describe('FluidTypography', () => {
17
+ let fluidTypography: FluidTypography;
18
+ let testElement: HTMLElement;
19
+
20
+ beforeEach(() => {
21
+ // Clean up DOM
22
+ document.body.innerHTML = '';
23
+
24
+ // Create test element
25
+ testElement = document.createElement('p');
26
+ testElement.textContent = 'Test typography content';
27
+ testElement.style.fontSize = '16px';
28
+ document.body.appendChild(testElement);
29
+
30
+ fluidTypography = new FluidTypography();
31
+
32
+ // Mock getComputedStyle
33
+ vi.spyOn(window, 'getComputedStyle').mockReturnValue({
34
+ fontSize: '16px',
35
+ fontFamily: 'Arial, sans-serif',
36
+ fontWeight: 'normal',
37
+ fontStyle: 'normal'
38
+ } as CSSStyleDeclaration);
39
+ });
40
+
41
+ afterEach(() => {
42
+ fluidTypography.destroy();
43
+ document.body.innerHTML = '';
44
+ vi.restoreAllMocks();
45
+ });
46
+
47
+ describe('Fluid Scaling', () => {
48
+ it('should apply fluid scaling with clamp()', () => {
49
+ const config: FluidConfig = {
50
+ minSize: 14,
51
+ maxSize: 24,
52
+ minViewport: 320,
53
+ maxViewport: 1200
54
+ };
55
+
56
+ fluidTypography.applyFluidScaling(testElement, config);
57
+
58
+ expect(testElement.style.fontSize).toContain('clamp(');
59
+ expect(testElement.getAttribute('data-proteus-fluid')).toBe('true');
60
+ expect(testElement.getAttribute('data-proteus-min-size')).toBe('14');
61
+ expect(testElement.getAttribute('data-proteus-max-size')).toBe('24');
62
+ });
63
+
64
+ it('should enforce accessibility constraints', () => {
65
+ const config: FluidConfig = {
66
+ minSize: 10, // Below WCAG AA minimum
67
+ maxSize: 20,
68
+ accessibility: 'AA',
69
+ enforceAccessibility: true
70
+ };
71
+
72
+ fluidTypography.applyFluidScaling(testElement, config);
73
+
74
+ // Should be adjusted to meet WCAG AA minimum (14px)
75
+ expect(testElement.getAttribute('data-proteus-min-size')).toBe('14');
76
+ });
77
+
78
+ it('should respect user preferences', () => {
79
+ // Mock root font size to simulate user preference
80
+ vi.spyOn(window, 'getComputedStyle').mockImplementation((element) => {
81
+ if (element === document.documentElement) {
82
+ return { fontSize: '20px' } as CSSStyleDeclaration; // User increased font size
83
+ }
84
+ return {
85
+ fontSize: '16px',
86
+ fontFamily: 'Arial, sans-serif',
87
+ fontWeight: 'normal',
88
+ fontStyle: 'normal'
89
+ } as CSSStyleDeclaration;
90
+ });
91
+
92
+ const config: FluidConfig = {
93
+ minSize: 16,
94
+ maxSize: 24,
95
+ respectUserPreferences: true
96
+ };
97
+
98
+ fluidTypography.applyFluidScaling(testElement, config);
99
+
100
+ // Font sizes should be scaled by user preference (20/16 = 1.25)
101
+ expect(testElement.getAttribute('data-proteus-min-size')).toBe('20'); // 16 * 1.25
102
+ expect(testElement.getAttribute('data-proteus-max-size')).toBe('30'); // 24 * 1.25
103
+ });
104
+
105
+ it('should generate linear clamp values correctly', () => {
106
+ const config: FluidConfig = {
107
+ minSize: 16,
108
+ maxSize: 24,
109
+ minViewport: 320,
110
+ maxViewport: 1200,
111
+ scalingFunction: 'linear'
112
+ };
113
+
114
+ fluidTypography.applyFluidScaling(testElement, config);
115
+
116
+ const fontSize = testElement.style.fontSize;
117
+ expect(fontSize).toMatch(/clamp\(16px,.*,24px\)/);
118
+ });
119
+
120
+ it('should handle exponential scaling', () => {
121
+ const config: FluidConfig = {
122
+ minSize: 16,
123
+ maxSize: 32,
124
+ minViewport: 320,
125
+ maxViewport: 1200,
126
+ scalingFunction: 'exponential'
127
+ };
128
+
129
+ fluidTypography.applyFluidScaling(testElement, config);
130
+
131
+ const fontSize = testElement.style.fontSize;
132
+ expect(fontSize).toContain('clamp(');
133
+ expect(fontSize).toContain('16px');
134
+ expect(fontSize).toContain('32px');
135
+ });
136
+ });
137
+
138
+ describe('Container-Based Scaling', () => {
139
+ let containerElement: HTMLElement;
140
+
141
+ beforeEach(() => {
142
+ containerElement = document.createElement('div');
143
+ containerElement.style.width = '600px';
144
+ containerElement.style.height = '400px';
145
+ containerElement.appendChild(testElement);
146
+ document.body.appendChild(containerElement);
147
+
148
+ // Mock getBoundingClientRect for container
149
+ vi.spyOn(containerElement, 'getBoundingClientRect').mockReturnValue({
150
+ width: 600,
151
+ height: 400,
152
+ top: 0,
153
+ left: 0,
154
+ bottom: 400,
155
+ right: 600,
156
+ x: 0,
157
+ y: 0,
158
+ toJSON: () => ({})
159
+ } as DOMRect);
160
+ });
161
+
162
+ it('should apply container-based scaling', () => {
163
+ const config: ContainerBasedConfig = {
164
+ minSize: 14,
165
+ maxSize: 24,
166
+ containerElement,
167
+ minContainerWidth: 300,
168
+ maxContainerWidth: 800
169
+ };
170
+
171
+ fluidTypography.applyContainerBasedScaling(testElement, config);
172
+
173
+ // Container width is 600px, should scale between min and max
174
+ const fontSize = parseFloat(testElement.style.fontSize);
175
+ expect(fontSize).toBeGreaterThan(14);
176
+ expect(fontSize).toBeLessThan(24);
177
+ });
178
+
179
+ it('should find nearest container automatically', () => {
180
+ const config: ContainerBasedConfig = {
181
+ minSize: 14,
182
+ maxSize: 24,
183
+ minContainerWidth: 300,
184
+ maxContainerWidth: 800
185
+ };
186
+
187
+ fluidTypography.applyContainerBasedScaling(testElement, config);
188
+
189
+ // Should find the container element automatically
190
+ expect(testElement.style.fontSize).toBeTruthy();
191
+ });
192
+
193
+ it('should update on container resize', () => {
194
+ const config: ContainerBasedConfig = {
195
+ minSize: 14,
196
+ maxSize: 24,
197
+ containerElement,
198
+ minContainerWidth: 300,
199
+ maxContainerWidth: 800
200
+ };
201
+
202
+ fluidTypography.applyContainerBasedScaling(testElement, config);
203
+
204
+ const initialFontSize = testElement.style.fontSize;
205
+
206
+ // Simulate container resize
207
+ vi.spyOn(containerElement, 'getBoundingClientRect').mockReturnValue({
208
+ width: 400,
209
+ height: 400,
210
+ top: 0,
211
+ left: 0,
212
+ bottom: 400,
213
+ right: 400,
214
+ x: 0,
215
+ y: 0,
216
+ toJSON: () => ({})
217
+ } as DOMRect);
218
+
219
+ // Trigger resize update
220
+ fluidTypography['handleContainerResize'](containerElement);
221
+
222
+ expect(testElement.style.fontSize).not.toBe(initialFontSize);
223
+ });
224
+ });
225
+
226
+ describe('Text Fitting', () => {
227
+ beforeEach(() => {
228
+ // Mock getBoundingClientRect for text measurement
229
+ const mockGetBoundingClientRect = vi.fn();
230
+ Element.prototype.getBoundingClientRect = mockGetBoundingClientRect;
231
+
232
+ // Return different widths based on font size
233
+ mockGetBoundingClientRect.mockImplementation(function(this: Element) {
234
+ const fontSize = parseFloat(this.style.fontSize || '16');
235
+ const textLength = this.textContent?.length || 0;
236
+ return {
237
+ width: fontSize * textLength * 0.6, // Approximate character width
238
+ height: fontSize * 1.2,
239
+ top: 0,
240
+ left: 0,
241
+ bottom: fontSize * 1.2,
242
+ right: fontSize * textLength * 0.6,
243
+ x: 0,
244
+ y: 0,
245
+ toJSON: () => ({})
246
+ } as DOMRect;
247
+ });
248
+ });
249
+
250
+ it('should fit text to container width', () => {
251
+ const config = {
252
+ maxWidth: 200,
253
+ minSize: 12,
254
+ maxSize: 24
255
+ };
256
+
257
+ fluidTypography.fitTextToContainer(testElement, config);
258
+
259
+ const fontSize = parseFloat(testElement.style.fontSize);
260
+ expect(fontSize).toBeGreaterThanOrEqual(12);
261
+ expect(fontSize).toBeLessThanOrEqual(24);
262
+ });
263
+
264
+ it('should handle overflow settings', () => {
265
+ const config = {
266
+ maxWidth: 100,
267
+ minSize: 12,
268
+ maxSize: 24,
269
+ allowOverflow: false,
270
+ wordBreak: 'break-all' as const
271
+ };
272
+
273
+ fluidTypography.fitTextToContainer(testElement, config);
274
+
275
+ expect(testElement.style.overflow).toBe('hidden');
276
+ expect(testElement.style.textOverflow).toBe('ellipsis');
277
+ expect(testElement.style.wordBreak).toBe('break-all');
278
+ });
279
+
280
+ it('should calculate optimal text size correctly', () => {
281
+ testElement.textContent = 'Short text';
282
+
283
+ const config = {
284
+ maxWidth: 300,
285
+ minSize: 12,
286
+ maxSize: 48
287
+ };
288
+
289
+ fluidTypography.fitTextToContainer(testElement, config);
290
+
291
+ // Should find a size that fits within the width
292
+ const fontSize = parseFloat(testElement.style.fontSize);
293
+ expect(fontSize).toBeGreaterThan(12);
294
+ });
295
+ });
296
+
297
+ describe('Element Management', () => {
298
+ it('should remove fluid scaling', () => {
299
+ const config: FluidConfig = {
300
+ minSize: 14,
301
+ maxSize: 24
302
+ };
303
+
304
+ fluidTypography.applyFluidScaling(testElement, config);
305
+
306
+ expect(testElement.style.fontSize).toBeTruthy();
307
+ expect(testElement.getAttribute('data-proteus-fluid')).toBe('true');
308
+
309
+ fluidTypography.removeFluidScaling(testElement);
310
+
311
+ expect(testElement.style.fontSize).toBeFalsy();
312
+ expect(testElement.getAttribute('data-proteus-fluid')).toBeNull();
313
+ });
314
+
315
+ it('should handle elements without applied scaling', () => {
316
+ expect(() => {
317
+ fluidTypography.removeFluidScaling(testElement);
318
+ }).not.toThrow();
319
+ });
320
+
321
+ it('should clean up resources on destroy', () => {
322
+ const config: FluidConfig = {
323
+ minSize: 14,
324
+ maxSize: 24
325
+ };
326
+
327
+ fluidTypography.applyFluidScaling(testElement, config);
328
+
329
+ expect(() => {
330
+ fluidTypography.destroy();
331
+ }).not.toThrow();
332
+ });
333
+ });
334
+
335
+ describe('Error Handling', () => {
336
+ it('should handle missing container gracefully', () => {
337
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
338
+
339
+ // Remove element from DOM to simulate missing container
340
+ testElement.remove();
341
+
342
+ const config: ContainerBasedConfig = {
343
+ minSize: 14,
344
+ maxSize: 24
345
+ };
346
+
347
+ expect(() => {
348
+ fluidTypography.applyContainerBasedScaling(testElement, config);
349
+ }).not.toThrow();
350
+
351
+ expect(consoleSpy).toHaveBeenCalledWith(
352
+ expect.stringContaining('No container found')
353
+ );
354
+
355
+ consoleSpy.mockRestore();
356
+ });
357
+
358
+ it('should handle errors in scaling gracefully', () => {
359
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
360
+
361
+ // Force an error by providing invalid config
362
+ const config = null as any;
363
+
364
+ expect(() => {
365
+ fluidTypography.applyFluidScaling(testElement, config);
366
+ }).not.toThrow();
367
+
368
+ expect(consoleSpy).toHaveBeenCalled();
369
+ consoleSpy.mockRestore();
370
+ });
371
+
372
+ it('should handle empty text content', () => {
373
+ testElement.textContent = '';
374
+
375
+ const config = {
376
+ maxWidth: 200,
377
+ minSize: 12,
378
+ maxSize: 24
379
+ };
380
+
381
+ expect(() => {
382
+ fluidTypography.fitTextToContainer(testElement, config);
383
+ }).not.toThrow();
384
+
385
+ expect(parseFloat(testElement.style.fontSize)).toBe(12); // Should use minimum
386
+ });
387
+ });
388
+
389
+ describe('Accessibility Compliance', () => {
390
+ it('should enforce WCAG AA compliance', () => {
391
+ const config: FluidConfig = {
392
+ minSize: 10, // Below WCAG AA minimum
393
+ maxSize: 20,
394
+ accessibility: 'AA',
395
+ enforceAccessibility: true
396
+ };
397
+
398
+ fluidTypography.applyFluidScaling(testElement, config);
399
+
400
+ const minSize = parseFloat(testElement.getAttribute('data-proteus-min-size') || '0');
401
+ expect(minSize).toBeGreaterThanOrEqual(14); // WCAG AA minimum
402
+ });
403
+
404
+ it('should enforce WCAG AAA compliance', () => {
405
+ const config: FluidConfig = {
406
+ minSize: 12, // Below WCAG AAA minimum
407
+ maxSize: 20,
408
+ accessibility: 'AAA',
409
+ enforceAccessibility: true
410
+ };
411
+
412
+ fluidTypography.applyFluidScaling(testElement, config);
413
+
414
+ const minSize = parseFloat(testElement.getAttribute('data-proteus-min-size') || '0');
415
+ expect(minSize).toBeGreaterThanOrEqual(16); // WCAG AAA minimum
416
+ });
417
+
418
+ it('should allow bypassing accessibility constraints', () => {
419
+ const config: FluidConfig = {
420
+ minSize: 10,
421
+ maxSize: 20,
422
+ accessibility: 'AA',
423
+ enforceAccessibility: false
424
+ };
425
+
426
+ fluidTypography.applyFluidScaling(testElement, config);
427
+
428
+ const minSize = parseFloat(testElement.getAttribute('data-proteus-min-size') || '0');
429
+ expect(minSize).toBe(10); // Should not be enforced
430
+ });
431
+ });
432
+ });