@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,370 @@
1
+ /**
2
+ * Container Manager for ProteusJS
3
+ * Manages multiple SmartContainer instances and provides the main API
4
+ */
5
+
6
+ import type { ContainerConfig, BreakpointConfig } from '../types';
7
+ import { SmartContainer, ContainerOptions, ContainerState } from './SmartContainer';
8
+ import { ObserverManager } from '../observers/ObserverManager';
9
+ import { MemoryManager } from '../core/MemoryManager';
10
+ import { EventSystem } from '../core/EventSystem';
11
+ import { performanceTracker } from '../utils/performance';
12
+
13
+ export class ContainerManager {
14
+ private containers: Map<Element, SmartContainer> = new Map();
15
+ private observerManager: ObserverManager;
16
+ private memoryManager: MemoryManager;
17
+ private eventSystem: EventSystem;
18
+ private config: Required<ContainerConfig>;
19
+ private autoDetectionEnabled: boolean = false;
20
+
21
+ constructor(
22
+ config: ContainerConfig,
23
+ observerManager: ObserverManager,
24
+ memoryManager: MemoryManager,
25
+ eventSystem: EventSystem
26
+ ) {
27
+ this.observerManager = observerManager;
28
+ this.memoryManager = memoryManager;
29
+ this.eventSystem = eventSystem;
30
+
31
+ this.config = {
32
+ autoDetect: true,
33
+ breakpoints: {
34
+ sm: '300px',
35
+ md: '500px',
36
+ lg: '800px',
37
+ xl: '1200px'
38
+ },
39
+ units: true,
40
+ isolation: true,
41
+ polyfill: true,
42
+ ...config
43
+ };
44
+
45
+ if (this.config.autoDetect) {
46
+ this.enableAutoDetection();
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Create and manage a container
52
+ */
53
+ public container(
54
+ selector: string | Element | Element[],
55
+ options: ContainerOptions = {}
56
+ ): SmartContainer | SmartContainer[] {
57
+ performanceTracker.mark('container-create');
58
+
59
+ const elements = this.normalizeSelector(selector);
60
+ const containers: SmartContainer[] = [];
61
+
62
+ elements.forEach(element => {
63
+ let container = this.containers.get(element);
64
+
65
+ if (container) {
66
+ // Update existing container
67
+ if (options.breakpoints) {
68
+ container.updateBreakpoints(options.breakpoints);
69
+ }
70
+ } else {
71
+ // Create new container
72
+ const mergedOptions: ContainerOptions = {
73
+ breakpoints: this.config.breakpoints,
74
+ ...options
75
+ };
76
+
77
+ container = new SmartContainer(
78
+ element,
79
+ mergedOptions,
80
+ this.observerManager,
81
+ this.memoryManager
82
+ );
83
+
84
+ this.containers.set(element, container);
85
+ container.activate();
86
+
87
+ // Emit container created event
88
+ this.eventSystem.emit('containerCreated', {
89
+ element,
90
+ container,
91
+ options: mergedOptions
92
+ });
93
+ }
94
+
95
+ containers.push(container);
96
+ });
97
+
98
+ performanceTracker.measure('container-create');
99
+
100
+ return elements.length === 1 ? containers[0]! : containers;
101
+ }
102
+
103
+ /**
104
+ * Remove container management from element(s)
105
+ */
106
+ public removeContainer(selector: string | Element | Element[]): boolean {
107
+ const elements = this.normalizeSelector(selector);
108
+ let removed = false;
109
+
110
+ elements.forEach(element => {
111
+ const container = this.containers.get(element);
112
+ if (container) {
113
+ container.deactivate();
114
+ this.containers.delete(element);
115
+ removed = true;
116
+
117
+ // Emit container removed event
118
+ this.eventSystem.emit('containerRemoved', {
119
+ element,
120
+ container
121
+ });
122
+ }
123
+ });
124
+
125
+ return removed;
126
+ }
127
+
128
+ /**
129
+ * Get container instance for element
130
+ */
131
+ public getContainer(element: Element): SmartContainer | undefined {
132
+ return this.containers.get(element);
133
+ }
134
+
135
+ /**
136
+ * Get all managed containers
137
+ */
138
+ public getAllContainers(): SmartContainer[] {
139
+ return Array.from(this.containers.values());
140
+ }
141
+
142
+ /**
143
+ * Get containers by breakpoint
144
+ */
145
+ public getContainersByBreakpoint(breakpoint: string): SmartContainer[] {
146
+ return this.getAllContainers().filter(container =>
147
+ container.isBreakpointActive(breakpoint)
148
+ );
149
+ }
150
+
151
+ /**
152
+ * Update global breakpoints
153
+ */
154
+ public updateGlobalBreakpoints(breakpoints: BreakpointConfig): void {
155
+ const stringBreakpoints = Object.fromEntries(
156
+ Object.entries(breakpoints).map(([key, value]) => [key, String(value)])
157
+ );
158
+ this.config.breakpoints = { ...this.config.breakpoints, ...stringBreakpoints };
159
+
160
+ // Update all existing containers
161
+ this.containers.forEach(container => {
162
+ container.updateBreakpoints(this.config.breakpoints);
163
+ });
164
+
165
+ // Emit breakpoints updated event
166
+ this.eventSystem.emit('breakpointsUpdated', {
167
+ breakpoints: this.config.breakpoints
168
+ });
169
+ }
170
+
171
+ /**
172
+ * Enable automatic container detection
173
+ */
174
+ public enableAutoDetection(): void {
175
+ if (this.autoDetectionEnabled) return;
176
+
177
+ this.autoDetectionEnabled = true;
178
+ this.scanForContainers();
179
+
180
+ // Set up mutation observer for new elements
181
+ this.setupMutationObserver();
182
+ }
183
+
184
+ /**
185
+ * Disable automatic container detection
186
+ */
187
+ public disableAutoDetection(): void {
188
+ this.autoDetectionEnabled = false;
189
+ // Note: We don't remove existing containers, just stop auto-detection
190
+ }
191
+
192
+ /**
193
+ * Manually scan for containers
194
+ */
195
+ public scanForContainers(): void {
196
+ if (!this.autoDetectionEnabled) return;
197
+
198
+ performanceTracker.mark('container-scan');
199
+
200
+ // Look for elements with container-related attributes or classes
201
+ const candidates = this.findContainerCandidates();
202
+
203
+ candidates.forEach(element => {
204
+ if (!this.containers.has(element)) {
205
+ const options = this.extractOptionsFromElement(element);
206
+ this.container(element, options);
207
+ }
208
+ });
209
+
210
+ performanceTracker.measure('container-scan');
211
+ }
212
+
213
+ /**
214
+ * Get container statistics
215
+ */
216
+ public getStats(): object {
217
+ const containers = this.getAllContainers();
218
+ const stats = {
219
+ totalContainers: containers.length,
220
+ activeContainers: containers.filter(c => c.getState().lastUpdate > 0).length,
221
+ autoDetectionEnabled: this.autoDetectionEnabled,
222
+ breakpoints: Object.keys(this.config.breakpoints),
223
+ containersByBreakpoint: {} as Record<string, number>
224
+ };
225
+
226
+ // Count containers by active breakpoints
227
+ Object.keys(this.config.breakpoints).forEach(bp => {
228
+ stats.containersByBreakpoint[bp] = this.getContainersByBreakpoint(bp).length;
229
+ });
230
+
231
+ return stats;
232
+ }
233
+
234
+ /**
235
+ * Destroy all containers and clean up
236
+ */
237
+ public destroy(): void {
238
+ // Deactivate all containers
239
+ this.containers.forEach(container => {
240
+ container.deactivate();
241
+ });
242
+
243
+ this.containers.clear();
244
+ this.autoDetectionEnabled = false;
245
+ }
246
+
247
+ /**
248
+ * Normalize selector to array of elements
249
+ */
250
+ private normalizeSelector(selector: string | Element | Element[]): Element[] {
251
+ if (typeof selector === 'string') {
252
+ return Array.from(document.querySelectorAll(selector));
253
+ } else if (selector instanceof Element) {
254
+ return [selector];
255
+ } else if (Array.isArray(selector)) {
256
+ return selector;
257
+ }
258
+ return [];
259
+ }
260
+
261
+ /**
262
+ * Find potential container elements
263
+ */
264
+ private findContainerCandidates(): Element[] {
265
+ const candidates: Element[] = [];
266
+
267
+ // Elements with data-container attribute
268
+ candidates.push(...Array.from(document.querySelectorAll('[data-container]')));
269
+
270
+ // Elements with container-related classes
271
+ candidates.push(...Array.from(document.querySelectorAll('.container, .card, .widget, .component')));
272
+
273
+ // Elements with CSS container-type
274
+ if (typeof CSS !== 'undefined' && CSS.supports && CSS.supports('container-type', 'inline-size')) {
275
+ const allElements = document.querySelectorAll('*');
276
+ allElements.forEach(element => {
277
+ const style = getComputedStyle(element);
278
+ if (style.containerType && style.containerType !== 'normal') {
279
+ candidates.push(element);
280
+ }
281
+ });
282
+ }
283
+
284
+ // Remove duplicates
285
+ return Array.from(new Set(candidates));
286
+ }
287
+
288
+ /**
289
+ * Extract container options from element attributes
290
+ */
291
+ private extractOptionsFromElement(element: Element): ContainerOptions {
292
+ const options: ContainerOptions = {};
293
+
294
+ // Extract from data attributes
295
+ const containerData = element.getAttribute('data-container');
296
+ if (containerData) {
297
+ try {
298
+ const parsed = JSON.parse(containerData);
299
+ Object.assign(options, parsed);
300
+ } catch (error) {
301
+ // Ignore invalid JSON
302
+ }
303
+ }
304
+
305
+ // Extract breakpoints from data-breakpoints
306
+ const breakpointsData = element.getAttribute('data-breakpoints');
307
+ if (breakpointsData) {
308
+ try {
309
+ options.breakpoints = JSON.parse(breakpointsData);
310
+ } catch (error) {
311
+ // Ignore invalid JSON
312
+ }
313
+ }
314
+
315
+ // Extract container type from CSS or data attribute
316
+ const containerType = element.getAttribute('data-container-type');
317
+ if (containerType) {
318
+ options.containerType = containerType as any;
319
+ }
320
+
321
+ return options;
322
+ }
323
+
324
+ /**
325
+ * Set up mutation observer for auto-detection
326
+ */
327
+ private setupMutationObserver(): void {
328
+ if (typeof MutationObserver === 'undefined') return;
329
+
330
+ const observer = new MutationObserver((mutations) => {
331
+ let shouldScan = false;
332
+
333
+ mutations.forEach(mutation => {
334
+ if (mutation.type === 'childList') {
335
+ mutation.addedNodes.forEach(node => {
336
+ if (node.nodeType === Node.ELEMENT_NODE) {
337
+ shouldScan = true;
338
+ }
339
+ });
340
+ } else if (mutation.type === 'attributes') {
341
+ if (
342
+ mutation.attributeName?.startsWith('data-container') ||
343
+ mutation.attributeName === 'class'
344
+ ) {
345
+ shouldScan = true;
346
+ }
347
+ }
348
+ });
349
+
350
+ if (shouldScan) {
351
+ // Debounce scanning to avoid excessive calls
352
+ setTimeout(() => this.scanForContainers(), 100);
353
+ }
354
+ });
355
+
356
+ observer.observe(document.body, {
357
+ childList: true,
358
+ subtree: true,
359
+ attributes: true,
360
+ attributeFilter: ['data-container', 'data-breakpoints', 'data-container-type', 'class']
361
+ });
362
+
363
+ // Register cleanup
364
+ this.memoryManager.register({
365
+ id: 'container-mutation-observer',
366
+ type: 'observer',
367
+ cleanup: () => observer.disconnect()
368
+ });
369
+ }
370
+ }
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Container Units System for ProteusJS
3
+ * Implements CSS-like container units (cw, ch, cmin, cmax, cqi, cqb)
4
+ */
5
+
6
+ import type { ContainerUnit } from '../types';
7
+
8
+ export interface ContainerDimensions {
9
+ width: number;
10
+ height: number;
11
+ inlineSize: number;
12
+ blockSize: number;
13
+ }
14
+
15
+ export interface UnitCalculation {
16
+ value: number;
17
+ unit: ContainerUnit;
18
+ pixelValue: number;
19
+ containerDimensions: ContainerDimensions;
20
+ }
21
+
22
+ export class ContainerUnits {
23
+ private containerDimensions: Map<Element, ContainerDimensions> = new Map();
24
+ private unitProperties: Map<Element, Set<string>> = new Map();
25
+
26
+ /**
27
+ * Update container dimensions for an element
28
+ */
29
+ public updateDimensions(element: Element, dimensions: ContainerDimensions): void {
30
+ this.containerDimensions.set(element, dimensions);
31
+ this.updateUnitProperties(element);
32
+ }
33
+
34
+ /**
35
+ * Calculate container unit value in pixels
36
+ */
37
+ public calculateUnit(
38
+ element: Element,
39
+ value: number,
40
+ unit: ContainerUnit
41
+ ): number {
42
+ const dimensions = this.containerDimensions.get(element);
43
+ if (!dimensions) {
44
+ console.warn('ProteusJS: No container dimensions found for element');
45
+ return 0;
46
+ }
47
+
48
+ switch (unit) {
49
+ case 'cw':
50
+ return (value / 100) * dimensions.width;
51
+ case 'ch':
52
+ return (value / 100) * dimensions.height;
53
+ case 'cmin':
54
+ return (value / 100) * Math.min(dimensions.width, dimensions.height);
55
+ case 'cmax':
56
+ return (value / 100) * Math.max(dimensions.width, dimensions.height);
57
+ case 'cqi':
58
+ return (value / 100) * dimensions.inlineSize;
59
+ case 'cqb':
60
+ return (value / 100) * dimensions.blockSize;
61
+ default:
62
+ return 0;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Parse container unit string (e.g., "50cw", "25ch")
68
+ */
69
+ public parseUnit(unitString: string): { value: number; unit: ContainerUnit } | null {
70
+ const match = unitString.match(/^(\d*\.?\d+)(cw|ch|cmin|cmax|cqi|cqb)$/);
71
+ if (!match) return null;
72
+
73
+ return {
74
+ value: parseFloat(match[1]!),
75
+ unit: match[2]! as ContainerUnit
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Convert container unit to pixels for specific element
81
+ */
82
+ public toPixels(element: Element, unitString: string): number {
83
+ const parsed = this.parseUnit(unitString);
84
+ if (!parsed) return 0;
85
+
86
+ return this.calculateUnit(element, parsed.value, parsed.unit);
87
+ }
88
+
89
+ /**
90
+ * Apply container units to element styles
91
+ */
92
+ public applyUnits(element: Element, styles: Record<string, string>): void {
93
+ const htmlElement = element as HTMLElement;
94
+ const appliedProperties = new Set<string>();
95
+
96
+ Object.entries(styles).forEach(([property, value]) => {
97
+ const convertedValue = this.convertUnitsInValue(element, value);
98
+ if (convertedValue !== value) {
99
+ htmlElement.style.setProperty(property, convertedValue);
100
+ appliedProperties.add(property);
101
+ }
102
+ });
103
+
104
+ // Track applied properties for cleanup
105
+ if (appliedProperties.size > 0) {
106
+ if (!this.unitProperties.has(element)) {
107
+ this.unitProperties.set(element, new Set());
108
+ }
109
+ appliedProperties.forEach(prop => {
110
+ this.unitProperties.get(element)!.add(prop);
111
+ });
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Remove container unit styles from element
117
+ */
118
+ public removeUnits(element: Element): void {
119
+ const htmlElement = element as HTMLElement;
120
+ const properties = this.unitProperties.get(element);
121
+
122
+ if (properties) {
123
+ properties.forEach(property => {
124
+ htmlElement.style.removeProperty(property);
125
+ });
126
+ this.unitProperties.delete(element);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Generate CSS custom properties for container units
132
+ */
133
+ public generateCustomProperties(element: Element): Record<string, string> {
134
+ const dimensions = this.containerDimensions.get(element);
135
+ if (!dimensions) return {};
136
+
137
+ return {
138
+ '--cw': `${dimensions.width / 100}px`,
139
+ '--ch': `${dimensions.height / 100}px`,
140
+ '--cmin': `${Math.min(dimensions.width, dimensions.height) / 100}px`,
141
+ '--cmax': `${Math.max(dimensions.width, dimensions.height) / 100}px`,
142
+ '--cqi': `${dimensions.inlineSize / 100}px`,
143
+ '--cqb': `${dimensions.blockSize / 100}px`
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Apply custom properties to element
149
+ */
150
+ public applyCustomProperties(element: Element): void {
151
+ const htmlElement = element as HTMLElement;
152
+ const properties = this.generateCustomProperties(element);
153
+
154
+ Object.entries(properties).forEach(([property, value]) => {
155
+ htmlElement.style.setProperty(property, value);
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Create responsive value using container units
161
+ */
162
+ public createResponsiveValue(
163
+ baseValue: number,
164
+ unit: ContainerUnit,
165
+ options: {
166
+ min?: number;
167
+ max?: number;
168
+ scale?: number;
169
+ } = {}
170
+ ): string {
171
+ const { min, max, scale = 1 } = options;
172
+ const scaledValue = baseValue * scale;
173
+
174
+ if (min !== undefined && max !== undefined) {
175
+ return `clamp(${min}px, ${scaledValue}${unit}, ${max}px)`;
176
+ } else if (min !== undefined) {
177
+ return `max(${min}px, ${scaledValue}${unit})`;
178
+ } else if (max !== undefined) {
179
+ return `min(${scaledValue}${unit}, ${max}px)`;
180
+ }
181
+
182
+ return `${scaledValue}${unit}`;
183
+ }
184
+
185
+ /**
186
+ * Get container unit statistics
187
+ */
188
+ public getStats(): object {
189
+ return {
190
+ trackedContainers: this.containerDimensions.size,
191
+ elementsWithUnits: this.unitProperties.size,
192
+ totalUnitProperties: Array.from(this.unitProperties.values())
193
+ .reduce((sum, props) => sum + props.size, 0)
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Clean up container data
199
+ */
200
+ public cleanup(element: Element): void {
201
+ this.removeUnits(element);
202
+ this.containerDimensions.delete(element);
203
+ }
204
+
205
+ /**
206
+ * Clear all data
207
+ */
208
+ public clear(): void {
209
+ // Remove all unit styles
210
+ this.unitProperties.forEach((_properties, element) => {
211
+ this.removeUnits(element);
212
+ });
213
+
214
+ this.containerDimensions.clear();
215
+ this.unitProperties.clear();
216
+ }
217
+
218
+ /**
219
+ * Convert container units in a CSS value string
220
+ */
221
+ private convertUnitsInValue(element: Element, value: string): string {
222
+ // Match container unit patterns in the value
223
+ const unitRegex = /(\d*\.?\d+)(cw|ch|cmin|cmax|cqi|cqb)/g;
224
+
225
+ return value.replace(unitRegex, (_match, numStr, unit) => {
226
+ const num = parseFloat(numStr);
227
+ const pixels = this.calculateUnit(element, num, unit as ContainerUnit);
228
+ return `${pixels}px`;
229
+ });
230
+ }
231
+
232
+ /**
233
+ * Update unit properties when dimensions change
234
+ */
235
+ private updateUnitProperties(element: Element): void {
236
+ const properties = this.unitProperties.get(element);
237
+ if (!properties) return;
238
+
239
+ const htmlElement = element as HTMLElement;
240
+ // const computedStyle = getComputedStyle(htmlElement); // Currently unused
241
+
242
+ // Re-apply container unit styles with new dimensions
243
+ properties.forEach(property => {
244
+ const currentValue = htmlElement.style.getPropertyValue(property);
245
+ if (currentValue && this.hasContainerUnits(currentValue)) {
246
+ const convertedValue = this.convertUnitsInValue(element, currentValue);
247
+ htmlElement.style.setProperty(property, convertedValue);
248
+ }
249
+ });
250
+
251
+ // Update custom properties
252
+ this.applyCustomProperties(element);
253
+ }
254
+
255
+ /**
256
+ * Check if value contains container units
257
+ */
258
+ private hasContainerUnits(value: string): boolean {
259
+ return /\d+(cw|ch|cmin|cmax|cqi|cqb)/.test(value);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Utility functions for container units
265
+ */
266
+ export const containerUnitUtils = {
267
+ /**
268
+ * Create fluid typography using container units
269
+ */
270
+ createFluidType(
271
+ minSize: number,
272
+ maxSize: number,
273
+ minContainer: number,
274
+ maxContainer: number,
275
+ unit: ContainerUnit = 'cw'
276
+ ): string {
277
+ const slope = (maxSize - minSize) / (maxContainer - minContainer);
278
+ const yIntercept = minSize - slope * minContainer;
279
+
280
+ return `clamp(${minSize}px, ${yIntercept}px + ${slope * 100}${unit}, ${maxSize}px)`;
281
+ },
282
+
283
+ /**
284
+ * Create responsive spacing using container units
285
+ */
286
+ createFluidSpacing(
287
+ baseSpacing: number,
288
+ scale: number = 1,
289
+ unit: ContainerUnit = 'cw'
290
+ ): string {
291
+ const scaledValue = baseSpacing * scale;
292
+ return `${scaledValue}${unit}`;
293
+ },
294
+
295
+ /**
296
+ * Convert viewport units to container units
297
+ */
298
+ convertViewportToContainer(
299
+ value: string,
300
+ containerWidth: number,
301
+ containerHeight: number,
302
+ viewportWidth: number = window.innerWidth,
303
+ viewportHeight: number = window.innerHeight
304
+ ): string {
305
+ return value.replace(/(\d*\.?\d+)(vw|vh|vmin|vmax)/g, (match, numStr, unit) => {
306
+ const num = parseFloat(numStr);
307
+ let containerValue: number;
308
+ let containerUnit: ContainerUnit;
309
+
310
+ switch (unit) {
311
+ case 'vw':
312
+ containerValue = (num * viewportWidth) / containerWidth * 100;
313
+ containerUnit = 'cw';
314
+ break;
315
+ case 'vh':
316
+ containerValue = (num * viewportHeight) / containerHeight * 100;
317
+ containerUnit = 'ch';
318
+ break;
319
+ case 'vmin':
320
+ const vminPixels = (num / 100) * Math.min(viewportWidth, viewportHeight);
321
+ containerValue = (vminPixels / Math.min(containerWidth, containerHeight)) * 100;
322
+ containerUnit = 'cmin';
323
+ break;
324
+ case 'vmax':
325
+ const vmaxPixels = (num / 100) * Math.max(viewportWidth, viewportHeight);
326
+ containerValue = (vmaxPixels / Math.max(containerWidth, containerHeight)) * 100;
327
+ containerUnit = 'cmax';
328
+ break;
329
+ default:
330
+ return match;
331
+ }
332
+
333
+ return `${containerValue.toFixed(2)}${containerUnit}`;
334
+ });
335
+ }
336
+ };