@hypen-space/web 0.2.12 → 0.3.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/dist/src/dom/applicators/effects.js +38 -2
- package/dist/src/dom/applicators/effects.js.map +3 -3
- package/dist/src/dom/applicators/events.js +280 -397
- package/dist/src/dom/applicators/events.js.map +5 -4
- package/dist/src/dom/applicators/font.js +94 -5
- package/dist/src/dom/applicators/font.js.map +3 -3
- package/dist/src/dom/applicators/index.js +590 -425
- package/dist/src/dom/applicators/index.js.map +10 -9
- package/dist/src/dom/applicators/layout.js +33 -5
- package/dist/src/dom/applicators/layout.js.map +3 -3
- package/dist/src/dom/applicators/size.js +81 -16
- package/dist/src/dom/applicators/size.js.map +3 -3
- package/dist/src/dom/components/hypenapp.js +296 -0
- package/dist/src/dom/components/hypenapp.js.map +10 -0
- package/dist/src/dom/components/index.js +263 -1
- package/dist/src/dom/components/index.js.map +5 -4
- package/dist/src/dom/element-data.js +140 -0
- package/dist/src/dom/element-data.js.map +10 -0
- package/dist/src/dom/index.js +857 -430
- package/dist/src/dom/index.js.map +13 -11
- package/dist/src/dom/renderer.js +857 -430
- package/dist/src/dom/renderer.js.map +13 -11
- package/dist/src/hypen.js +857 -430
- package/dist/src/hypen.js.map +13 -11
- package/dist/src/index.js +862 -430
- package/dist/src/index.js.map +15 -12
- package/package.json +3 -3
- package/src/canvas/QUICKSTART.md +2 -4
- package/src/dom/applicators/effects.ts +45 -1
- package/src/dom/applicators/events.ts +348 -537
- package/src/dom/applicators/font.ts +127 -2
- package/src/dom/applicators/index.ts +117 -7
- package/src/dom/applicators/layout.ts +40 -4
- package/src/dom/applicators/size.ts +101 -16
- package/src/dom/components/hypenapp.ts +348 -0
- package/src/dom/components/index.ts +2 -0
- package/src/dom/element-data.ts +234 -0
- package/src/dom/renderer.ts +8 -5
- package/src/index.ts +3 -0
|
@@ -1,9 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Font Applicators
|
|
2
|
+
* Font Applicators with Google Fonts support
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ApplicatorHandler } from "./index.js";
|
|
6
6
|
|
|
7
|
+
// Track loaded Google Fonts to avoid duplicate link tags
|
|
8
|
+
const loadedGoogleFonts = new Set<string>();
|
|
9
|
+
|
|
10
|
+
// System font keywords that shouldn't be loaded from Google Fonts
|
|
11
|
+
const systemFontKeywords = new Set([
|
|
12
|
+
"default", "system", "system-ui", "inherit", "initial", "unset",
|
|
13
|
+
"serif", "sans-serif", "monospace", "cursive", "fantasy",
|
|
14
|
+
"-apple-system", "BlinkMacSystemFont", "Segoe UI", "Arial", "Helvetica",
|
|
15
|
+
"Times New Roman", "Georgia", "Courier New", "Verdana", "Tahoma",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a font name is a system font or generic CSS font.
|
|
20
|
+
*/
|
|
21
|
+
function isSystemFont(fontName: string): boolean {
|
|
22
|
+
const normalized = fontName.toLowerCase().trim();
|
|
23
|
+
return systemFontKeywords.has(normalized) ||
|
|
24
|
+
normalized.startsWith("-") ||
|
|
25
|
+
normalized.startsWith("ui-");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load a Google Font by injecting a link tag.
|
|
30
|
+
* @param fontName - The Google Font name (e.g., "Roboto", "Open Sans")
|
|
31
|
+
*/
|
|
32
|
+
function loadGoogleFont(fontName: string): void {
|
|
33
|
+
const normalized = fontName.trim();
|
|
34
|
+
|
|
35
|
+
// Skip if already loaded or is a system font
|
|
36
|
+
if (loadedGoogleFonts.has(normalized) || isSystemFont(normalized)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Mark as loading to avoid duplicates
|
|
41
|
+
loadedGoogleFonts.add(normalized);
|
|
42
|
+
|
|
43
|
+
// Create link element for Google Fonts
|
|
44
|
+
const link = document.createElement("link");
|
|
45
|
+
link.rel = "stylesheet";
|
|
46
|
+
link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(normalized)}:wght@100;200;300;400;500;600;700;800;900&display=swap`;
|
|
47
|
+
|
|
48
|
+
// Add to document head
|
|
49
|
+
document.head.appendChild(link);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse a fontFamily value and load any Google Fonts.
|
|
54
|
+
* Returns a CSS-safe font-family string.
|
|
55
|
+
*/
|
|
56
|
+
function processFontFamily(value: string): string {
|
|
57
|
+
// Split by comma to handle font stacks
|
|
58
|
+
const fonts = value.split(",").map(f => f.trim().replace(/["']/g, ""));
|
|
59
|
+
|
|
60
|
+
for (const font of fonts) {
|
|
61
|
+
if (!isSystemFont(font)) {
|
|
62
|
+
loadGoogleFont(font);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Return original value with proper quoting for CSS
|
|
67
|
+
return fonts.map(f => {
|
|
68
|
+
// Quote font names that contain spaces or special characters
|
|
69
|
+
if (f.includes(" ") && !f.startsWith('"') && !f.startsWith("'")) {
|
|
70
|
+
return `"${f}"`;
|
|
71
|
+
}
|
|
72
|
+
return f;
|
|
73
|
+
}).join(", ");
|
|
74
|
+
}
|
|
75
|
+
|
|
7
76
|
export const fontHandlers: Record<string, ApplicatorHandler> = {
|
|
8
77
|
fontSize: (el, value) => {
|
|
9
78
|
el.style.fontSize = typeof value === "number" ? `${value}px` : String(value);
|
|
@@ -14,7 +83,9 @@ export const fontHandlers: Record<string, ApplicatorHandler> = {
|
|
|
14
83
|
},
|
|
15
84
|
|
|
16
85
|
fontFamily: (el, value) => {
|
|
17
|
-
|
|
86
|
+
const fontValue = String(value);
|
|
87
|
+
// Process font family and load Google Fonts as needed
|
|
88
|
+
el.style.fontFamily = processFontFamily(fontValue);
|
|
18
89
|
},
|
|
19
90
|
|
|
20
91
|
textAlign: (el, value) => {
|
|
@@ -24,4 +95,58 @@ export const fontHandlers: Record<string, ApplicatorHandler> = {
|
|
|
24
95
|
lineHeight: (el, value) => {
|
|
25
96
|
el.style.lineHeight = String(value);
|
|
26
97
|
},
|
|
98
|
+
|
|
99
|
+
fontStyle: (el, value) => {
|
|
100
|
+
// Support: normal, italic, oblique
|
|
101
|
+
el.style.fontStyle = String(value);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
textTransform: (el, value) => {
|
|
105
|
+
// Support: none, capitalize, uppercase, lowercase
|
|
106
|
+
el.style.textTransform = String(value);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Export the font loading utility for manual use
|
|
111
|
+
export const GoogleFonts = {
|
|
112
|
+
/**
|
|
113
|
+
* Preload a Google Font before it's used.
|
|
114
|
+
*/
|
|
115
|
+
preload: loadGoogleFont,
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a font has been loaded.
|
|
119
|
+
*/
|
|
120
|
+
isLoaded: (fontName: string) => loadedGoogleFonts.has(fontName.trim()),
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get list of loaded fonts.
|
|
124
|
+
*/
|
|
125
|
+
getLoadedFonts: () => Array.from(loadedGoogleFonts),
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Popular Google Fonts for reference.
|
|
129
|
+
*/
|
|
130
|
+
popular: [
|
|
131
|
+
"Roboto",
|
|
132
|
+
"Open Sans",
|
|
133
|
+
"Lato",
|
|
134
|
+
"Montserrat",
|
|
135
|
+
"Poppins",
|
|
136
|
+
"Inter",
|
|
137
|
+
"Nunito",
|
|
138
|
+
"Playfair Display",
|
|
139
|
+
"Merriweather",
|
|
140
|
+
"Source Code Pro",
|
|
141
|
+
"Fira Code",
|
|
142
|
+
"JetBrains Mono",
|
|
143
|
+
"Raleway",
|
|
144
|
+
"Ubuntu",
|
|
145
|
+
"Oswald",
|
|
146
|
+
"Quicksand",
|
|
147
|
+
"Work Sans",
|
|
148
|
+
"Rubik",
|
|
149
|
+
"Karla",
|
|
150
|
+
"DM Sans",
|
|
151
|
+
],
|
|
27
152
|
};
|
|
@@ -6,6 +6,47 @@
|
|
|
6
6
|
|
|
7
7
|
export type ApplicatorHandler = (element: HTMLElement, value: any) => void;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Tailwind breakpoint values for responsive variants
|
|
11
|
+
*/
|
|
12
|
+
const BREAKPOINTS: Record<string, string> = {
|
|
13
|
+
sm: '640px',
|
|
14
|
+
md: '768px',
|
|
15
|
+
lg: '1024px',
|
|
16
|
+
xl: '1280px',
|
|
17
|
+
'2xl': '1536px',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Singleton stylesheet for variant CSS rules
|
|
22
|
+
*/
|
|
23
|
+
let variantStyleSheet: CSSStyleSheet | null = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Track inserted rules to avoid duplicates
|
|
27
|
+
*/
|
|
28
|
+
const insertedRules = new Set<string>();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get or create the variant stylesheet
|
|
32
|
+
*/
|
|
33
|
+
function getVariantStyleSheet(): CSSStyleSheet {
|
|
34
|
+
if (!variantStyleSheet) {
|
|
35
|
+
const style = document.createElement('style');
|
|
36
|
+
style.id = 'hypen-variants';
|
|
37
|
+
document.head.appendChild(style);
|
|
38
|
+
variantStyleSheet = style.sheet as CSSStyleSheet;
|
|
39
|
+
}
|
|
40
|
+
return variantStyleSheet;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate a simple hash for deduplication
|
|
45
|
+
*/
|
|
46
|
+
function hashValue(value: any): string {
|
|
47
|
+
return String(value).replace(/[^a-zA-Z0-9]/g, '').slice(0, 8);
|
|
48
|
+
}
|
|
49
|
+
|
|
9
50
|
export class ApplicatorRegistry {
|
|
10
51
|
private handlers: Map<string, ApplicatorHandler> = new Map();
|
|
11
52
|
private elementState: WeakMap<HTMLElement, Map<string, any>> = new WeakMap();
|
|
@@ -245,18 +286,87 @@ export class ApplicatorRegistry {
|
|
|
245
286
|
}
|
|
246
287
|
|
|
247
288
|
/**
|
|
248
|
-
* Set a CSS property with automatic unit handling
|
|
289
|
+
* Set a CSS property with automatic unit handling and variant support
|
|
249
290
|
*/
|
|
250
291
|
private setStyleProperty(element: HTMLElement, name: string, value: any): void {
|
|
251
|
-
|
|
252
|
-
const
|
|
292
|
+
const atIndex = name.indexOf('@');
|
|
293
|
+
const colonIndex = name.indexOf(':');
|
|
294
|
+
|
|
295
|
+
// Responsive variant: padding@md, width@lg, etc.
|
|
296
|
+
if (atIndex !== -1) {
|
|
297
|
+
const prop = name.slice(0, atIndex);
|
|
298
|
+
const breakpoint = name.slice(atIndex + 1);
|
|
299
|
+
const minWidth = BREAKPOINTS[breakpoint];
|
|
300
|
+
|
|
301
|
+
if (minWidth) {
|
|
302
|
+
const cssName = this.toKebabCase(prop);
|
|
303
|
+
const cssValue = this.formatCssValue(cssName, value);
|
|
304
|
+
const className = `hypen-${cssName.replace(/[^a-zA-Z0-9-]/g, '')}-${breakpoint}-${hashValue(value)}`;
|
|
305
|
+
const ruleKey = `${className}:${cssValue}`;
|
|
306
|
+
|
|
307
|
+
// Avoid duplicate rule insertion
|
|
308
|
+
if (!insertedRules.has(ruleKey)) {
|
|
309
|
+
const sheet = getVariantStyleSheet();
|
|
310
|
+
sheet.insertRule(
|
|
311
|
+
`@media (min-width: ${minWidth}) { .${className} { ${cssName}: ${cssValue}; } }`,
|
|
312
|
+
sheet.cssRules.length
|
|
313
|
+
);
|
|
314
|
+
insertedRules.add(ruleKey);
|
|
315
|
+
}
|
|
253
316
|
|
|
254
|
-
|
|
317
|
+
element.classList.add(className);
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// State variant: background-color:hover, border-color:focus, etc.
|
|
323
|
+
if (colonIndex !== -1) {
|
|
324
|
+
const prop = name.slice(0, colonIndex);
|
|
325
|
+
const state = name.slice(colonIndex + 1);
|
|
326
|
+
|
|
327
|
+
// Only handle known CSS pseudo-states
|
|
328
|
+
const validStates = ['hover', 'focus', 'active', 'disabled', 'focus-visible', 'focus-within'];
|
|
329
|
+
if (validStates.includes(state)) {
|
|
330
|
+
const cssName = this.toKebabCase(prop);
|
|
331
|
+
const cssValue = this.formatCssValue(cssName, value);
|
|
332
|
+
const className = `hypen-${cssName.replace(/[^a-zA-Z0-9-]/g, '')}-${state}-${hashValue(value)}`;
|
|
333
|
+
const ruleKey = `${className}:${cssValue}`;
|
|
334
|
+
|
|
335
|
+
// Avoid duplicate rule insertion
|
|
336
|
+
if (!insertedRules.has(ruleKey)) {
|
|
337
|
+
const sheet = getVariantStyleSheet();
|
|
338
|
+
sheet.insertRule(
|
|
339
|
+
`.${className}:${state} { ${cssName}: ${cssValue}; }`,
|
|
340
|
+
sheet.cssRules.length
|
|
341
|
+
);
|
|
342
|
+
insertedRules.add(ruleKey);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
element.classList.add(className);
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Normal property: convert camelCase to kebab-case and apply
|
|
351
|
+
const cssName = this.toKebabCase(name);
|
|
352
|
+
element.style.setProperty(cssName, this.formatCssValue(cssName, value));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Convert camelCase to kebab-case
|
|
357
|
+
*/
|
|
358
|
+
private toKebabCase(name: string): string {
|
|
359
|
+
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Format a CSS value with automatic unit handling
|
|
364
|
+
*/
|
|
365
|
+
private formatCssValue(cssName: string, value: any): string {
|
|
255
366
|
if (typeof value === "number" && this.needsUnit(cssName)) {
|
|
256
|
-
|
|
257
|
-
} else {
|
|
258
|
-
element.style.setProperty(cssName, String(value));
|
|
367
|
+
return `${value}px`;
|
|
259
368
|
}
|
|
369
|
+
return String(value);
|
|
260
370
|
}
|
|
261
371
|
|
|
262
372
|
/**
|
|
@@ -4,10 +4,46 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ApplicatorHandler } from "./index.js";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Maps Hypen alignment values to CSS flexbox values.
|
|
9
|
+
* Ensures consistent cross-platform API (Android/iOS/Web).
|
|
10
|
+
*/
|
|
11
|
+
function mapAlignmentValue(value: string): string {
|
|
12
|
+
const v = String(value).toLowerCase();
|
|
13
|
+
switch (v) {
|
|
14
|
+
// Positional values -> CSS equivalents
|
|
15
|
+
case "top":
|
|
16
|
+
case "start":
|
|
17
|
+
case "leading":
|
|
18
|
+
case "left":
|
|
19
|
+
return "flex-start";
|
|
20
|
+
case "bottom":
|
|
21
|
+
case "end":
|
|
22
|
+
case "trailing":
|
|
23
|
+
case "right":
|
|
24
|
+
return "flex-end";
|
|
25
|
+
case "center":
|
|
26
|
+
return "center";
|
|
27
|
+
// Spacing values -> CSS equivalents
|
|
28
|
+
case "spacebetween":
|
|
29
|
+
case "space-between":
|
|
30
|
+
return "space-between";
|
|
31
|
+
case "spacearound":
|
|
32
|
+
case "space-around":
|
|
33
|
+
return "space-around";
|
|
34
|
+
case "spaceevenly":
|
|
35
|
+
case "space-evenly":
|
|
36
|
+
return "space-evenly";
|
|
37
|
+
// Pass through CSS values as-is
|
|
38
|
+
default:
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
7
43
|
export const layoutHandlers: Record<string, ApplicatorHandler> = {
|
|
8
44
|
// Unified alignment API - works for both Column and Row
|
|
9
45
|
verticalAlignment: (el, value) => {
|
|
10
|
-
const val = String(value);
|
|
46
|
+
const val = mapAlignmentValue(String(value));
|
|
11
47
|
// Check flex-direction to determine which CSS property to set
|
|
12
48
|
const flexDirection = getComputedStyle(el).flexDirection;
|
|
13
49
|
if (flexDirection === "column" || flexDirection === "column-reverse") {
|
|
@@ -20,7 +56,7 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
|
|
|
20
56
|
},
|
|
21
57
|
|
|
22
58
|
horizontalAlignment: (el, value) => {
|
|
23
|
-
const val = String(value);
|
|
59
|
+
const val = mapAlignmentValue(String(value));
|
|
24
60
|
// Check flex-direction to determine which CSS property to set
|
|
25
61
|
const flexDirection = getComputedStyle(el).flexDirection;
|
|
26
62
|
if (flexDirection === "column" || flexDirection === "column-reverse") {
|
|
@@ -34,11 +70,11 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
|
|
|
34
70
|
|
|
35
71
|
// Legacy aliases (kept for backward compatibility)
|
|
36
72
|
horizontalAlign: (el, value) => {
|
|
37
|
-
el.style.justifyContent = String(value);
|
|
73
|
+
el.style.justifyContent = mapAlignmentValue(String(value));
|
|
38
74
|
},
|
|
39
75
|
|
|
40
76
|
verticalAlign: (el, value) => {
|
|
41
|
-
el.style.alignItems = String(value);
|
|
77
|
+
el.style.alignItems = mapAlignmentValue(String(value));
|
|
42
78
|
},
|
|
43
79
|
|
|
44
80
|
gap: (el, value) => {
|
|
@@ -1,52 +1,137 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Size Applicators
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform sizing value support:
|
|
5
|
+
* - Numbers: treated as px (platform default)
|
|
6
|
+
* - "100px": absolute pixels (1px = 1px everywhere)
|
|
7
|
+
* - "100dp" / "100pt": density-independent (1dp ≈ 1pt, scaled by device)
|
|
8
|
+
* - "50%": percentage of parent
|
|
9
|
+
* - "50vw" / "50vh": viewport width/height
|
|
10
|
+
* - "fill" / "100%": fill available space
|
|
11
|
+
* - "wrap" / "auto": fit content
|
|
3
12
|
*/
|
|
4
13
|
|
|
5
14
|
import type { ApplicatorHandler } from "./index.js";
|
|
6
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Parse a size value and return CSS-compatible string.
|
|
18
|
+
* Ensures cross-platform compatibility with Android/iOS.
|
|
19
|
+
*/
|
|
20
|
+
function parseSizeValue(value: any): string | null {
|
|
21
|
+
if (value === null || value === undefined) return null;
|
|
22
|
+
|
|
23
|
+
// Numbers default to px
|
|
24
|
+
if (typeof value === "number") {
|
|
25
|
+
return `${value}px`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const str = String(value).trim().toLowerCase();
|
|
29
|
+
|
|
30
|
+
// Keywords
|
|
31
|
+
switch (str) {
|
|
32
|
+
case "fill":
|
|
33
|
+
case "match_parent":
|
|
34
|
+
return "100%";
|
|
35
|
+
case "wrap":
|
|
36
|
+
case "wrap_content":
|
|
37
|
+
case "auto":
|
|
38
|
+
return "auto";
|
|
39
|
+
case "infinity":
|
|
40
|
+
case "inf":
|
|
41
|
+
case "max":
|
|
42
|
+
return "100%";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Parse value with unit
|
|
46
|
+
const match = str.match(/^(-?[\d.]+)\s*(px|dp|pt|%|vw|vh|vmin|vmax|em|rem)?$/);
|
|
47
|
+
if (!match) {
|
|
48
|
+
// Pass through other CSS values as-is (e.g., "calc(...)", "fit-content")
|
|
49
|
+
return str;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const num = parseFloat(match[1]);
|
|
53
|
+
const unit = match[2] || "px";
|
|
54
|
+
|
|
55
|
+
switch (unit) {
|
|
56
|
+
case "px":
|
|
57
|
+
// Absolute pixels - use as-is
|
|
58
|
+
return `${num}px`;
|
|
59
|
+
case "dp":
|
|
60
|
+
case "pt":
|
|
61
|
+
// Density-independent points
|
|
62
|
+
// On web, 1dp/1pt ≈ 1px at standard density (96dpi)
|
|
63
|
+
// CSS already handles this via px, so we just use px
|
|
64
|
+
// For true density independence, we'd need to query devicePixelRatio
|
|
65
|
+
// but CSS px is already defined as 1/96th of an inch
|
|
66
|
+
return `${num}px`;
|
|
67
|
+
case "%":
|
|
68
|
+
return `${num}%`;
|
|
69
|
+
case "vw":
|
|
70
|
+
return `${num}vw`;
|
|
71
|
+
case "vh":
|
|
72
|
+
return `${num}vh`;
|
|
73
|
+
case "vmin":
|
|
74
|
+
return `${num}vmin`;
|
|
75
|
+
case "vmax":
|
|
76
|
+
return `${num}vmax`;
|
|
77
|
+
case "em":
|
|
78
|
+
return `${num}em`;
|
|
79
|
+
case "rem":
|
|
80
|
+
return `${num}rem`;
|
|
81
|
+
default:
|
|
82
|
+
return `${num}px`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
7
86
|
export const sizeHandlers: Record<string, ApplicatorHandler> = {
|
|
8
87
|
width: (el, value) => {
|
|
9
|
-
|
|
88
|
+
const size = parseSizeValue(value);
|
|
89
|
+
if (size) el.style.width = size;
|
|
10
90
|
},
|
|
11
91
|
|
|
12
92
|
height: (el, value) => {
|
|
13
|
-
|
|
93
|
+
const size = parseSizeValue(value);
|
|
94
|
+
if (size) el.style.height = size;
|
|
14
95
|
},
|
|
15
96
|
|
|
16
97
|
minWidth: (el, value) => {
|
|
17
|
-
|
|
98
|
+
const size = parseSizeValue(value);
|
|
99
|
+
if (size) el.style.minWidth = size;
|
|
18
100
|
},
|
|
19
101
|
|
|
20
102
|
minHeight: (el, value) => {
|
|
21
|
-
|
|
103
|
+
const size = parseSizeValue(value);
|
|
104
|
+
if (size) el.style.minHeight = size;
|
|
22
105
|
},
|
|
23
106
|
|
|
24
107
|
maxWidth: (el, value) => {
|
|
25
|
-
|
|
108
|
+
const size = parseSizeValue(value);
|
|
109
|
+
if (size) el.style.maxWidth = size;
|
|
26
110
|
},
|
|
27
111
|
|
|
28
112
|
maxHeight: (el, value) => {
|
|
29
|
-
|
|
113
|
+
const size = parseSizeValue(value);
|
|
114
|
+
if (size) el.style.maxHeight = size;
|
|
30
115
|
},
|
|
31
116
|
|
|
32
117
|
// Combined size applicator - sets both width and height
|
|
33
118
|
size: (el, value) => {
|
|
34
|
-
if (typeof value === "
|
|
35
|
-
el.style.width = `${value}px`;
|
|
36
|
-
el.style.height = `${value}px`;
|
|
37
|
-
} else if (typeof value === "object" && value !== null) {
|
|
119
|
+
if (typeof value === "object" && value !== null) {
|
|
38
120
|
const obj = value as Record<string, any>;
|
|
39
121
|
if (obj.width !== undefined) {
|
|
40
|
-
|
|
122
|
+
const w = parseSizeValue(obj.width);
|
|
123
|
+
if (w) el.style.width = w;
|
|
41
124
|
}
|
|
42
125
|
if (obj.height !== undefined) {
|
|
43
|
-
|
|
126
|
+
const h = parseSizeValue(obj.height);
|
|
127
|
+
if (h) el.style.height = h;
|
|
44
128
|
}
|
|
45
129
|
} else {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
130
|
+
const size = parseSizeValue(value);
|
|
131
|
+
if (size) {
|
|
132
|
+
el.style.width = size;
|
|
133
|
+
el.style.height = size;
|
|
134
|
+
}
|
|
50
135
|
}
|
|
51
136
|
},
|
|
52
137
|
|