@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.
- package/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,356 @@
|
|
1
|
+
/**
|
2
|
+
* Clamp-Based Scaling System for ProteusJS
|
3
|
+
* Implements intelligent font scaling using CSS clamp() with JavaScript fallbacks
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface ScalingConfig {
|
7
|
+
minSize: number;
|
8
|
+
maxSize: number;
|
9
|
+
minContainer: number;
|
10
|
+
maxContainer: number;
|
11
|
+
unit: 'px' | 'rem' | 'em';
|
12
|
+
containerUnit: 'px' | 'cw' | 'ch' | 'cmin' | 'cmax';
|
13
|
+
curve: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'custom';
|
14
|
+
customCurve?: (progress: number) => number;
|
15
|
+
}
|
16
|
+
|
17
|
+
export interface ScalingResult {
|
18
|
+
clampValue: string;
|
19
|
+
fallbackValue: string;
|
20
|
+
currentSize: number;
|
21
|
+
progress: number;
|
22
|
+
isNative: boolean;
|
23
|
+
}
|
24
|
+
|
25
|
+
export class ClampScaling {
|
26
|
+
private supportsClamp: boolean;
|
27
|
+
private supportsContainerUnits: boolean;
|
28
|
+
|
29
|
+
constructor() {
|
30
|
+
this.supportsClamp = this.checkClampSupport();
|
31
|
+
this.supportsContainerUnits = this.checkContainerUnitSupport();
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Create clamp-based scaling value
|
36
|
+
*/
|
37
|
+
public createScaling(config: ScalingConfig): ScalingResult {
|
38
|
+
const {
|
39
|
+
minSize,
|
40
|
+
maxSize,
|
41
|
+
minContainer,
|
42
|
+
maxContainer,
|
43
|
+
unit,
|
44
|
+
containerUnit,
|
45
|
+
curve
|
46
|
+
} = config;
|
47
|
+
|
48
|
+
// Validate configuration to prevent NaN
|
49
|
+
const containerRange = maxContainer - minContainer;
|
50
|
+
const sizeRange = maxSize - minSize;
|
51
|
+
|
52
|
+
if (containerRange <= 0) {
|
53
|
+
console.warn('Invalid container range, using fallback scaling');
|
54
|
+
return {
|
55
|
+
clampValue: `${minSize}${unit}`,
|
56
|
+
fallbackValue: `${minSize}${unit}`,
|
57
|
+
currentSize: minSize,
|
58
|
+
progress: 0,
|
59
|
+
isNative: false
|
60
|
+
};
|
61
|
+
}
|
62
|
+
|
63
|
+
// Calculate slope and y-intercept for linear scaling
|
64
|
+
const slope = sizeRange / containerRange;
|
65
|
+
const yIntercept = minSize - slope * minContainer;
|
66
|
+
|
67
|
+
let clampValue: string;
|
68
|
+
let fallbackValue: string;
|
69
|
+
|
70
|
+
if (this.supportsClamp && (this.supportsContainerUnits || containerUnit === 'px')) {
|
71
|
+
// Use native CSS clamp
|
72
|
+
const preferredValue = this.createPreferredValue(
|
73
|
+
yIntercept,
|
74
|
+
slope,
|
75
|
+
unit,
|
76
|
+
containerUnit,
|
77
|
+
curve,
|
78
|
+
config
|
79
|
+
);
|
80
|
+
|
81
|
+
clampValue = `clamp(${minSize}${unit}, ${preferredValue}, ${maxSize}${unit})`;
|
82
|
+
fallbackValue = `${minSize}${unit}`;
|
83
|
+
} else {
|
84
|
+
// Use JavaScript fallback
|
85
|
+
clampValue = `var(--proteus-font-size, ${minSize}${unit})`;
|
86
|
+
fallbackValue = `${minSize}${unit}`;
|
87
|
+
}
|
88
|
+
|
89
|
+
return {
|
90
|
+
clampValue,
|
91
|
+
fallbackValue,
|
92
|
+
currentSize: minSize,
|
93
|
+
progress: 0,
|
94
|
+
isNative: this.supportsClamp
|
95
|
+
};
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Calculate current font size for given container size
|
100
|
+
*/
|
101
|
+
public calculateSize(
|
102
|
+
containerSize: number,
|
103
|
+
config: ScalingConfig
|
104
|
+
): number {
|
105
|
+
const {
|
106
|
+
minSize,
|
107
|
+
maxSize,
|
108
|
+
minContainer,
|
109
|
+
maxContainer,
|
110
|
+
curve,
|
111
|
+
customCurve
|
112
|
+
} = config;
|
113
|
+
|
114
|
+
// Clamp container size to valid range
|
115
|
+
const clampedSize = Math.max(minContainer, Math.min(maxContainer, containerSize));
|
116
|
+
|
117
|
+
// Calculate progress (0-1)
|
118
|
+
const progress = (clampedSize - minContainer) / (maxContainer - minContainer);
|
119
|
+
|
120
|
+
// Apply easing curve
|
121
|
+
const easedProgress = this.applyCurve(progress, curve, customCurve);
|
122
|
+
|
123
|
+
// Calculate final size
|
124
|
+
const size = minSize + (maxSize - minSize) * easedProgress;
|
125
|
+
|
126
|
+
return Math.max(minSize, Math.min(maxSize, size));
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Apply scaling to element
|
131
|
+
*/
|
132
|
+
public applyScaling(
|
133
|
+
element: Element,
|
134
|
+
containerSize: number,
|
135
|
+
config: ScalingConfig
|
136
|
+
): void {
|
137
|
+
const htmlElement = element as HTMLElement;
|
138
|
+
const result = this.createScaling(config);
|
139
|
+
|
140
|
+
if (result.isNative) {
|
141
|
+
// Use CSS clamp
|
142
|
+
htmlElement.style.fontSize = result.clampValue;
|
143
|
+
} else {
|
144
|
+
// Use JavaScript calculation
|
145
|
+
const calculatedSize = this.calculateSize(containerSize, config);
|
146
|
+
htmlElement.style.setProperty('--proteus-font-size', `${calculatedSize}${config.unit}`);
|
147
|
+
htmlElement.style.fontSize = result.clampValue;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Create responsive scaling for multiple breakpoints
|
153
|
+
*/
|
154
|
+
public createMultiBreakpointScaling(
|
155
|
+
breakpoints: Array<{
|
156
|
+
container: number;
|
157
|
+
size: number;
|
158
|
+
}>,
|
159
|
+
unit: 'px' | 'rem' | 'em' = 'rem',
|
160
|
+
containerUnit: 'px' | 'cw' | 'ch' | 'cmin' | 'cmax' = 'cw'
|
161
|
+
): string {
|
162
|
+
if (breakpoints.length < 2) {
|
163
|
+
throw new Error('At least 2 breakpoints required for multi-breakpoint scaling');
|
164
|
+
}
|
165
|
+
|
166
|
+
// Sort breakpoints by container size
|
167
|
+
const sorted = [...breakpoints].sort((a, b) => a.container - b.container);
|
168
|
+
|
169
|
+
// Create clamp values for each segment
|
170
|
+
const clampValues: string[] = [];
|
171
|
+
|
172
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
173
|
+
const current = sorted[i]!;
|
174
|
+
const next = sorted[i + 1]!;
|
175
|
+
|
176
|
+
const config: ScalingConfig = {
|
177
|
+
minSize: current.size,
|
178
|
+
maxSize: next.size,
|
179
|
+
minContainer: current.container,
|
180
|
+
maxContainer: next.container,
|
181
|
+
unit,
|
182
|
+
containerUnit,
|
183
|
+
curve: 'linear'
|
184
|
+
};
|
185
|
+
|
186
|
+
const result = this.createScaling(config);
|
187
|
+
clampValues.push(result.clampValue);
|
188
|
+
}
|
189
|
+
|
190
|
+
// Combine with CSS max() for proper breakpoint handling
|
191
|
+
return clampValues.length === 1
|
192
|
+
? clampValues[0]!
|
193
|
+
: `max(${clampValues.join(', ')})`;
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Generate optimal scaling configuration
|
198
|
+
*/
|
199
|
+
public generateOptimalConfig(
|
200
|
+
_element: Element,
|
201
|
+
targetSizes: { small: number; large: number },
|
202
|
+
containerSizes: { small: number; large: number },
|
203
|
+
options: {
|
204
|
+
unit?: 'px' | 'rem' | 'em';
|
205
|
+
accessibility?: boolean;
|
206
|
+
readability?: boolean;
|
207
|
+
} = {}
|
208
|
+
): ScalingConfig {
|
209
|
+
const { unit = 'rem', accessibility = true, readability = true } = options;
|
210
|
+
|
211
|
+
let { small: minSize, large: maxSize } = targetSizes;
|
212
|
+
const { small: minContainer, large: maxContainer } = containerSizes;
|
213
|
+
|
214
|
+
// Apply accessibility constraints
|
215
|
+
if (accessibility) {
|
216
|
+
// Ensure minimum readable size (WCAG guidelines)
|
217
|
+
const minReadableSize = unit === 'px' ? 16 : 1; // 16px or 1rem
|
218
|
+
minSize = Math.max(minSize, minReadableSize);
|
219
|
+
|
220
|
+
// Limit maximum size to prevent overwhelming text
|
221
|
+
const maxReadableSize = unit === 'px' ? 72 : 4.5; // 72px or 4.5rem
|
222
|
+
maxSize = Math.min(maxSize, maxReadableSize);
|
223
|
+
}
|
224
|
+
|
225
|
+
// Apply readability constraints
|
226
|
+
if (readability) {
|
227
|
+
// Ensure reasonable scaling ratio (not too aggressive)
|
228
|
+
const maxRatio = 2.5;
|
229
|
+
if (maxSize / minSize > maxRatio) {
|
230
|
+
maxSize = minSize * maxRatio;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
return {
|
235
|
+
minSize,
|
236
|
+
maxSize,
|
237
|
+
minContainer,
|
238
|
+
maxContainer,
|
239
|
+
unit,
|
240
|
+
containerUnit: 'cw',
|
241
|
+
curve: 'ease-out' // Slightly slower scaling at larger sizes
|
242
|
+
};
|
243
|
+
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* Validate scaling configuration
|
247
|
+
*/
|
248
|
+
public validateConfig(config: ScalingConfig): { valid: boolean; errors: string[] } {
|
249
|
+
const errors: string[] = [];
|
250
|
+
|
251
|
+
if (config.minSize >= config.maxSize) {
|
252
|
+
errors.push('minSize must be less than maxSize');
|
253
|
+
}
|
254
|
+
|
255
|
+
if (config.minContainer >= config.maxContainer) {
|
256
|
+
errors.push('minContainer must be less than maxContainer');
|
257
|
+
}
|
258
|
+
|
259
|
+
if (config.minSize <= 0) {
|
260
|
+
errors.push('minSize must be positive');
|
261
|
+
}
|
262
|
+
|
263
|
+
if (config.minContainer <= 0) {
|
264
|
+
errors.push('minContainer must be positive');
|
265
|
+
}
|
266
|
+
|
267
|
+
// Check for reasonable scaling ratios
|
268
|
+
const sizeRatio = config.maxSize / config.minSize;
|
269
|
+
if (sizeRatio > 5) {
|
270
|
+
errors.push('Size ratio is very large (>5x), consider reducing for better readability');
|
271
|
+
}
|
272
|
+
|
273
|
+
return {
|
274
|
+
valid: errors.length === 0,
|
275
|
+
errors
|
276
|
+
};
|
277
|
+
}
|
278
|
+
|
279
|
+
/**
|
280
|
+
* Create preferred value for clamp function
|
281
|
+
*/
|
282
|
+
private createPreferredValue(
|
283
|
+
yIntercept: number,
|
284
|
+
slope: number,
|
285
|
+
unit: string,
|
286
|
+
containerUnit: string,
|
287
|
+
curve: ScalingConfig['curve'],
|
288
|
+
_config: ScalingConfig
|
289
|
+
): string {
|
290
|
+
// Validate inputs to prevent NaN
|
291
|
+
if (!isFinite(yIntercept) || !isFinite(slope)) {
|
292
|
+
console.warn('Invalid scaling values detected, using fallback');
|
293
|
+
return `1${unit}`; // Fallback value
|
294
|
+
}
|
295
|
+
|
296
|
+
if (curve === 'linear') {
|
297
|
+
// Simple linear scaling
|
298
|
+
const slopeValue = slope * (containerUnit === 'px' ? 1 : 100);
|
299
|
+
|
300
|
+
// Format numbers to prevent very long decimals
|
301
|
+
const formattedIntercept = Number(yIntercept.toFixed(3));
|
302
|
+
const formattedSlope = Number(slopeValue.toFixed(3));
|
303
|
+
|
304
|
+
return `${formattedIntercept}${unit} + ${formattedSlope}${containerUnit}`;
|
305
|
+
} else {
|
306
|
+
// For non-linear curves, we need to use CSS calc with custom properties
|
307
|
+
// This is a simplified approach - full implementation would need more complex CSS
|
308
|
+
const formattedIntercept = Number(yIntercept.toFixed(3));
|
309
|
+
const formattedSlope = Number((slope * 100).toFixed(3));
|
310
|
+
|
311
|
+
return `${formattedIntercept}${unit} + ${formattedSlope}${containerUnit}`;
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Apply easing curve to progress value
|
317
|
+
*/
|
318
|
+
private applyCurve(
|
319
|
+
progress: number,
|
320
|
+
curve: ScalingConfig['curve'],
|
321
|
+
customCurve?: (progress: number) => number
|
322
|
+
): number {
|
323
|
+
switch (curve) {
|
324
|
+
case 'linear':
|
325
|
+
return progress;
|
326
|
+
case 'ease-in':
|
327
|
+
return progress * progress;
|
328
|
+
case 'ease-out':
|
329
|
+
return 1 - Math.pow(1 - progress, 2);
|
330
|
+
case 'ease-in-out':
|
331
|
+
return progress < 0.5
|
332
|
+
? 2 * progress * progress
|
333
|
+
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
334
|
+
case 'custom':
|
335
|
+
return customCurve ? customCurve(progress) : progress;
|
336
|
+
default:
|
337
|
+
return progress;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
|
341
|
+
/**
|
342
|
+
* Check if CSS clamp() is supported
|
343
|
+
*/
|
344
|
+
private checkClampSupport(): boolean {
|
345
|
+
if (typeof CSS === 'undefined' || !CSS.supports) return false;
|
346
|
+
return CSS.supports('font-size', 'clamp(1rem, 2vw, 2rem)');
|
347
|
+
}
|
348
|
+
|
349
|
+
/**
|
350
|
+
* Check if container units are supported
|
351
|
+
*/
|
352
|
+
private checkContainerUnitSupport(): boolean {
|
353
|
+
if (typeof CSS === 'undefined' || !CSS.supports) return false;
|
354
|
+
return CSS.supports('font-size', '1cw');
|
355
|
+
}
|
356
|
+
}
|