@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,394 @@
|
|
1
|
+
/**
|
2
|
+
* Context Isolation for ProteusJS
|
3
|
+
* Prevents interference between nested containers and manages cascade resolution
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface ContainerContext {
|
7
|
+
element: Element;
|
8
|
+
level: number;
|
9
|
+
parent: ContainerContext | null;
|
10
|
+
children: Set<ContainerContext>;
|
11
|
+
isolated: boolean;
|
12
|
+
namespace: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface IsolationConfig {
|
16
|
+
autoIsolate: boolean;
|
17
|
+
maxNestingLevel: number;
|
18
|
+
conflictResolution: 'parent-wins' | 'child-wins' | 'explicit';
|
19
|
+
namespacePrefix: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
export class ContextIsolation {
|
23
|
+
private contexts: Map<Element, ContainerContext> = new Map();
|
24
|
+
private namespaceCounter: number = 0;
|
25
|
+
private config: IsolationConfig;
|
26
|
+
|
27
|
+
constructor(config: Partial<IsolationConfig> = {}) {
|
28
|
+
this.config = {
|
29
|
+
autoIsolate: true,
|
30
|
+
maxNestingLevel: 10,
|
31
|
+
conflictResolution: 'child-wins',
|
32
|
+
namespacePrefix: 'proteus-ctx',
|
33
|
+
...config
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Create isolated context for container
|
39
|
+
*/
|
40
|
+
public createContext(element: Element, isolated: boolean = true): ContainerContext {
|
41
|
+
// Check if context already exists
|
42
|
+
let context = this.contexts.get(element);
|
43
|
+
if (context) {
|
44
|
+
context.isolated = isolated;
|
45
|
+
return context;
|
46
|
+
}
|
47
|
+
|
48
|
+
// Find parent context
|
49
|
+
const parent = this.findParentContext(element);
|
50
|
+
const level = parent ? parent.level + 1 : 0;
|
51
|
+
|
52
|
+
// Check nesting level
|
53
|
+
if (level > this.config.maxNestingLevel) {
|
54
|
+
console.warn(
|
55
|
+
`ProteusJS: Maximum nesting level (${this.config.maxNestingLevel}) exceeded for container`
|
56
|
+
);
|
57
|
+
}
|
58
|
+
|
59
|
+
// Create new context
|
60
|
+
context = {
|
61
|
+
element,
|
62
|
+
level,
|
63
|
+
parent,
|
64
|
+
children: new Set(),
|
65
|
+
isolated: isolated || this.config.autoIsolate,
|
66
|
+
namespace: this.generateNamespace()
|
67
|
+
};
|
68
|
+
|
69
|
+
this.contexts.set(element, context);
|
70
|
+
|
71
|
+
// Update parent-child relationships
|
72
|
+
if (parent) {
|
73
|
+
parent.children.add(context);
|
74
|
+
}
|
75
|
+
|
76
|
+
// Apply isolation if needed
|
77
|
+
if (context.isolated) {
|
78
|
+
this.applyIsolation(context);
|
79
|
+
}
|
80
|
+
|
81
|
+
return context;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Remove context and clean up
|
86
|
+
*/
|
87
|
+
public removeContext(element: Element): boolean {
|
88
|
+
const context = this.contexts.get(element);
|
89
|
+
if (!context) return false;
|
90
|
+
|
91
|
+
// Remove from parent's children
|
92
|
+
if (context.parent) {
|
93
|
+
context.parent.children.delete(context);
|
94
|
+
}
|
95
|
+
|
96
|
+
// Reparent children to this context's parent
|
97
|
+
context.children.forEach(child => {
|
98
|
+
child.parent = context.parent;
|
99
|
+
if (context.parent) {
|
100
|
+
context.parent.children.add(child);
|
101
|
+
}
|
102
|
+
});
|
103
|
+
|
104
|
+
// Remove isolation
|
105
|
+
this.removeIsolation(context);
|
106
|
+
|
107
|
+
this.contexts.delete(element);
|
108
|
+
return true;
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Get context for element
|
113
|
+
*/
|
114
|
+
public getContext(element: Element): ContainerContext | undefined {
|
115
|
+
return this.contexts.get(element);
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Check if element has isolated context
|
120
|
+
*/
|
121
|
+
public isIsolated(element: Element): boolean {
|
122
|
+
const context = this.contexts.get(element);
|
123
|
+
return context?.isolated || false;
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Get all contexts at specific level
|
128
|
+
*/
|
129
|
+
public getContextsAtLevel(level: number): ContainerContext[] {
|
130
|
+
return Array.from(this.contexts.values()).filter(ctx => ctx.level === level);
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Get root contexts (level 0)
|
135
|
+
*/
|
136
|
+
public getRootContexts(): ContainerContext[] {
|
137
|
+
return this.getContextsAtLevel(0);
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Get context hierarchy for element
|
142
|
+
*/
|
143
|
+
public getHierarchy(element: Element): ContainerContext[] {
|
144
|
+
const context = this.contexts.get(element);
|
145
|
+
if (!context) return [];
|
146
|
+
|
147
|
+
const hierarchy: ContainerContext[] = [];
|
148
|
+
let current: ContainerContext | null = context;
|
149
|
+
|
150
|
+
while (current) {
|
151
|
+
hierarchy.unshift(current);
|
152
|
+
current = current.parent;
|
153
|
+
}
|
154
|
+
|
155
|
+
return hierarchy;
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Resolve conflicts between nested containers
|
160
|
+
*/
|
161
|
+
public resolveConflicts(
|
162
|
+
element: Element,
|
163
|
+
property: string,
|
164
|
+
values: Map<ContainerContext, any>
|
165
|
+
): any {
|
166
|
+
const context = this.contexts.get(element);
|
167
|
+
if (!context || values.size === 0) return undefined;
|
168
|
+
|
169
|
+
switch (this.config.conflictResolution) {
|
170
|
+
case 'parent-wins':
|
171
|
+
return this.resolveParentWins(context, values);
|
172
|
+
case 'child-wins':
|
173
|
+
return this.resolveChildWins(context, values);
|
174
|
+
case 'explicit':
|
175
|
+
return this.resolveExplicit(context, values);
|
176
|
+
default:
|
177
|
+
return values.values().next().value;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Generate scoped CSS selector for context
|
183
|
+
*/
|
184
|
+
public getScopedSelector(context: ContainerContext, selector: string): string {
|
185
|
+
if (!context.isolated) return selector;
|
186
|
+
|
187
|
+
const namespace = `[data-${context.namespace}]`;
|
188
|
+
|
189
|
+
// If selector is a simple class or ID, scope it
|
190
|
+
if (selector.startsWith('.') || selector.startsWith('#')) {
|
191
|
+
return `${namespace} ${selector}`;
|
192
|
+
}
|
193
|
+
|
194
|
+
// For complex selectors, prepend namespace
|
195
|
+
return `${namespace} ${selector}`;
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Generate scoped CSS rules for context
|
200
|
+
*/
|
201
|
+
public generateScopedCSS(context: ContainerContext, css: string): string {
|
202
|
+
if (!context.isolated) return css;
|
203
|
+
|
204
|
+
const namespace = `[data-${context.namespace}]`;
|
205
|
+
|
206
|
+
// Simple CSS scoping - prepend namespace to selectors
|
207
|
+
return css.replace(/([^{}]+)\s*{/g, (match, selector) => {
|
208
|
+
const trimmedSelector = selector.trim();
|
209
|
+
if (trimmedSelector.startsWith('@')) {
|
210
|
+
// Don't scope at-rules
|
211
|
+
return match;
|
212
|
+
}
|
213
|
+
return `${namespace} ${trimmedSelector} {`;
|
214
|
+
});
|
215
|
+
}
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Check for potential conflicts
|
219
|
+
*/
|
220
|
+
public detectConflicts(): Array<{
|
221
|
+
elements: Element[];
|
222
|
+
property: string;
|
223
|
+
contexts: ContainerContext[];
|
224
|
+
}> {
|
225
|
+
const conflicts: Array<{
|
226
|
+
elements: Element[];
|
227
|
+
property: string;
|
228
|
+
contexts: ContainerContext[];
|
229
|
+
}> = [];
|
230
|
+
|
231
|
+
// This would need to be integrated with the actual style system
|
232
|
+
// For now, return empty array as placeholder
|
233
|
+
return conflicts;
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Get isolation statistics
|
238
|
+
*/
|
239
|
+
public getStats(): object {
|
240
|
+
const contexts = Array.from(this.contexts.values());
|
241
|
+
const isolated = contexts.filter(ctx => ctx.isolated);
|
242
|
+
const levels = new Map<number, number>();
|
243
|
+
|
244
|
+
contexts.forEach(ctx => {
|
245
|
+
levels.set(ctx.level, (levels.get(ctx.level) || 0) + 1);
|
246
|
+
});
|
247
|
+
|
248
|
+
return {
|
249
|
+
totalContexts: contexts.length,
|
250
|
+
isolatedContexts: isolated.length,
|
251
|
+
maxLevel: Math.max(...contexts.map(ctx => ctx.level), 0),
|
252
|
+
contextsByLevel: Object.fromEntries(levels),
|
253
|
+
averageChildren: contexts.length > 0
|
254
|
+
? contexts.reduce((sum, ctx) => sum + ctx.children.size, 0) / contexts.length
|
255
|
+
: 0
|
256
|
+
};
|
257
|
+
}
|
258
|
+
|
259
|
+
/**
|
260
|
+
* Clear all contexts
|
261
|
+
*/
|
262
|
+
public clear(): void {
|
263
|
+
// Remove isolation from all contexts
|
264
|
+
this.contexts.forEach(context => {
|
265
|
+
this.removeIsolation(context);
|
266
|
+
});
|
267
|
+
|
268
|
+
this.contexts.clear();
|
269
|
+
this.namespaceCounter = 0;
|
270
|
+
}
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Find parent context by traversing DOM
|
274
|
+
*/
|
275
|
+
private findParentContext(element: Element): ContainerContext | null {
|
276
|
+
let parent = element.parentElement;
|
277
|
+
|
278
|
+
while (parent) {
|
279
|
+
const context = this.contexts.get(parent);
|
280
|
+
if (context) return context;
|
281
|
+
parent = parent.parentElement;
|
282
|
+
}
|
283
|
+
|
284
|
+
return null;
|
285
|
+
}
|
286
|
+
|
287
|
+
/**
|
288
|
+
* Generate unique namespace
|
289
|
+
*/
|
290
|
+
private generateNamespace(): string {
|
291
|
+
return `${this.config.namespacePrefix}-${++this.namespaceCounter}`;
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Apply isolation to context
|
296
|
+
*/
|
297
|
+
private applyIsolation(context: ContainerContext): void {
|
298
|
+
const element = context.element as HTMLElement;
|
299
|
+
|
300
|
+
// Add namespace data attribute
|
301
|
+
element.setAttribute(`data-${context.namespace}`, '');
|
302
|
+
|
303
|
+
// Add CSS containment if supported
|
304
|
+
if (typeof CSS !== 'undefined' && CSS.supports && CSS.supports('contain', 'layout style')) {
|
305
|
+
element.style.contain = 'layout style';
|
306
|
+
}
|
307
|
+
|
308
|
+
// Create isolated stacking context
|
309
|
+
if (getComputedStyle(element).position === 'static') {
|
310
|
+
element.style.position = 'relative';
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
* Remove isolation from context
|
316
|
+
*/
|
317
|
+
private removeIsolation(context: ContainerContext): void {
|
318
|
+
const element = context.element as HTMLElement;
|
319
|
+
|
320
|
+
// Remove namespace data attribute
|
321
|
+
element.removeAttribute(`data-${context.namespace}`);
|
322
|
+
|
323
|
+
// Remove CSS containment
|
324
|
+
element.style.removeProperty('contain');
|
325
|
+
|
326
|
+
// Note: We don't remove position as it might have been set by user
|
327
|
+
}
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Resolve conflicts with parent-wins strategy
|
331
|
+
*/
|
332
|
+
private resolveParentWins(
|
333
|
+
context: ContainerContext,
|
334
|
+
values: Map<ContainerContext, any>
|
335
|
+
): any {
|
336
|
+
// Find the highest level (closest to root) context with a value
|
337
|
+
let winner: ContainerContext | null = null;
|
338
|
+
let minLevel = Infinity;
|
339
|
+
|
340
|
+
values.forEach((value, ctx) => {
|
341
|
+
if (ctx.level < minLevel) {
|
342
|
+
minLevel = ctx.level;
|
343
|
+
winner = ctx;
|
344
|
+
}
|
345
|
+
});
|
346
|
+
|
347
|
+
return winner ? values.get(winner) : undefined;
|
348
|
+
}
|
349
|
+
|
350
|
+
/**
|
351
|
+
* Resolve conflicts with child-wins strategy
|
352
|
+
*/
|
353
|
+
private resolveChildWins(
|
354
|
+
context: ContainerContext,
|
355
|
+
values: Map<ContainerContext, any>
|
356
|
+
): any {
|
357
|
+
// Find the lowest level (furthest from root) context with a value
|
358
|
+
let winner: ContainerContext | null = null;
|
359
|
+
let maxLevel = -1;
|
360
|
+
|
361
|
+
values.forEach((value, ctx) => {
|
362
|
+
if (ctx.level > maxLevel) {
|
363
|
+
maxLevel = ctx.level;
|
364
|
+
winner = ctx;
|
365
|
+
}
|
366
|
+
});
|
367
|
+
|
368
|
+
return winner ? values.get(winner) : undefined;
|
369
|
+
}
|
370
|
+
|
371
|
+
/**
|
372
|
+
* Resolve conflicts with explicit strategy
|
373
|
+
*/
|
374
|
+
private resolveExplicit(
|
375
|
+
context: ContainerContext,
|
376
|
+
values: Map<ContainerContext, any>
|
377
|
+
): any {
|
378
|
+
// Only use values from isolated contexts
|
379
|
+
const isolatedValues = new Map<ContainerContext, any>();
|
380
|
+
|
381
|
+
values.forEach((value, ctx) => {
|
382
|
+
if (ctx.isolated) {
|
383
|
+
isolatedValues.set(ctx, value);
|
384
|
+
}
|
385
|
+
});
|
386
|
+
|
387
|
+
if (isolatedValues.size === 0) {
|
388
|
+
return values.values().next().value;
|
389
|
+
}
|
390
|
+
|
391
|
+
// Use child-wins for isolated contexts
|
392
|
+
return this.resolveChildWins(context, isolatedValues);
|
393
|
+
}
|
394
|
+
}
|