@hypen-space/web 0.3.7 → 0.3.9

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 (87) hide show
  1. package/dist/src/dom/applicators/advanced-layout.js +195 -110
  2. package/dist/src/dom/applicators/advanced-layout.js.map +5 -4
  3. package/dist/src/dom/applicators/background.js +33 -42
  4. package/dist/src/dom/applicators/background.js.map +3 -3
  5. package/dist/src/dom/applicators/border.js +58 -67
  6. package/dist/src/dom/applicators/border.js.map +3 -3
  7. package/dist/src/dom/applicators/color.js +15 -24
  8. package/dist/src/dom/applicators/color.js.map +3 -3
  9. package/dist/src/dom/applicators/display.js +36 -45
  10. package/dist/src/dom/applicators/display.js.map +3 -3
  11. package/dist/src/dom/applicators/effects.js +103 -112
  12. package/dist/src/dom/applicators/effects.js.map +3 -3
  13. package/dist/src/dom/applicators/events.js +36 -45
  14. package/dist/src/dom/applicators/events.js.map +4 -4
  15. package/dist/src/dom/applicators/font.js +76 -86
  16. package/dist/src/dom/applicators/font.js.map +3 -3
  17. package/dist/src/dom/applicators/index.js +774 -888
  18. package/dist/src/dom/applicators/index.js.map +19 -19
  19. package/dist/src/dom/applicators/layout.js +64 -72
  20. package/dist/src/dom/applicators/layout.js.map +3 -3
  21. package/dist/src/dom/applicators/margin.js +1 -5
  22. package/dist/src/dom/applicators/margin.js.map +3 -3
  23. package/dist/src/dom/applicators/padding.js +1 -5
  24. package/dist/src/dom/applicators/padding.js.map +3 -3
  25. package/dist/src/dom/applicators/size.js +69 -77
  26. package/dist/src/dom/applicators/size.js.map +3 -3
  27. package/dist/src/dom/applicators/transform.js +71 -80
  28. package/dist/src/dom/applicators/transform.js.map +3 -3
  29. package/dist/src/dom/applicators/transition.js +45 -54
  30. package/dist/src/dom/applicators/transition.js.map +3 -3
  31. package/dist/src/dom/applicators/types.js +2 -0
  32. package/dist/src/dom/applicators/types.js.map +9 -0
  33. package/dist/src/dom/applicators/typography.js +65 -74
  34. package/dist/src/dom/applicators/typography.js.map +3 -3
  35. package/dist/src/dom/components/avatar.js +4 -3
  36. package/dist/src/dom/components/avatar.js.map +3 -3
  37. package/dist/src/dom/components/button.js +11 -1
  38. package/dist/src/dom/components/button.js.map +3 -3
  39. package/dist/src/dom/components/column.js +2 -1
  40. package/dist/src/dom/components/column.js.map +3 -3
  41. package/dist/src/dom/components/container.js +4 -1
  42. package/dist/src/dom/components/container.js.map +3 -3
  43. package/dist/src/dom/components/index.js +44 -20
  44. package/dist/src/dom/components/index.js.map +9 -9
  45. package/dist/src/dom/components/list.js +3 -1
  46. package/dist/src/dom/components/list.js.map +3 -3
  47. package/dist/src/dom/components/row.js +2 -1
  48. package/dist/src/dom/components/row.js.map +3 -3
  49. package/dist/src/dom/components/stack.js +24 -18
  50. package/dist/src/dom/components/stack.js.map +3 -3
  51. package/dist/src/dom/element-data.js +6 -11
  52. package/dist/src/dom/element-data.js.map +3 -3
  53. package/dist/src/dom/index.js +906 -998
  54. package/dist/src/dom/index.js.map +27 -27
  55. package/dist/src/dom/renderer.js +906 -998
  56. package/dist/src/dom/renderer.js.map +27 -27
  57. package/dist/src/hypen.js +906 -998
  58. package/dist/src/hypen.js.map +27 -27
  59. package/dist/src/index.js +906 -998
  60. package/dist/src/index.js.map +27 -27
  61. package/package.json +2 -2
  62. package/src/dom/applicators/advanced-layout.ts +3 -2
  63. package/src/dom/applicators/background.ts +1 -1
  64. package/src/dom/applicators/border.ts +1 -1
  65. package/src/dom/applicators/color.ts +1 -1
  66. package/src/dom/applicators/display.ts +1 -1
  67. package/src/dom/applicators/effects.ts +1 -1
  68. package/src/dom/applicators/events.ts +2 -2
  69. package/src/dom/applicators/font.ts +1 -1
  70. package/src/dom/applicators/index.ts +49 -17
  71. package/src/dom/applicators/layout.ts +28 -9
  72. package/src/dom/applicators/margin.ts +56 -6
  73. package/src/dom/applicators/padding.ts +56 -6
  74. package/src/dom/applicators/size.ts +14 -4
  75. package/src/dom/applicators/transform.ts +1 -1
  76. package/src/dom/applicators/transition.ts +1 -1
  77. package/src/dom/applicators/types.ts +7 -0
  78. package/src/dom/applicators/typography.ts +1 -1
  79. package/src/dom/components/avatar.ts +4 -3
  80. package/src/dom/components/button.ts +17 -0
  81. package/src/dom/components/column.ts +9 -0
  82. package/src/dom/components/container.ts +2 -0
  83. package/src/dom/components/list.ts +3 -0
  84. package/src/dom/components/row.ts +4 -0
  85. package/src/dom/components/stack.ts +31 -19
  86. package/src/dom/components/text.ts +7 -0
  87. package/src/dom/element-data.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypen-space/web",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Hypen web renderers - DOM and Canvas rendering for browsers",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -48,7 +48,7 @@
48
48
  "clean": "rm -rf dist"
49
49
  },
50
50
  "dependencies": {
51
- "@hypen-space/core": "^0.3.5"
51
+ "@hypen-space/core": "^0.3.9"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/bun": "latest",
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { ApplicatorHandler } from "./index.js";
6
+ import { mapAlignmentValue } from "./layout.js";
6
7
 
7
8
  export const advancedLayoutHandlers: Record<string, ApplicatorHandler> = {
8
9
  // Flexbox properties
@@ -19,11 +20,11 @@ export const advancedLayoutHandlers: Record<string, ApplicatorHandler> = {
19
20
  },
20
21
 
21
22
  justifyContent: (el, value) => {
22
- el.style.justifyContent = String(value);
23
+ el.style.justifyContent = mapAlignmentValue(String(value));
23
24
  },
24
25
 
25
26
  alignItems: (el, value) => {
26
- el.style.alignItems = String(value);
27
+ el.style.alignItems = mapAlignmentValue(String(value));
27
28
  },
28
29
 
29
30
  alignContent: (el, value) => {
@@ -2,7 +2,7 @@
2
2
  * Background Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const backgroundHandlers: Record<string, ApplicatorHandler> = {
8
8
  backgroundImage: (el, value) => {
@@ -2,7 +2,7 @@
2
2
  * Border Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const borderHandlers: Record<string, ApplicatorHandler> = {
8
8
  // Compound border applicator - can take width, color, style, radius
@@ -2,7 +2,7 @@
2
2
  * Color Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const colorHandlers: Record<string, ApplicatorHandler> = {
8
8
  color: (el, value) => {
@@ -2,7 +2,7 @@
2
2
  * Display and Visibility Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const displayHandlers: Record<string, ApplicatorHandler> = {
8
8
  display: (el, value) => {
@@ -2,7 +2,7 @@
2
2
  * Visual Effects Applicators (Shadows, Filters, Blend Modes)
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const effectsHandlers: Record<string, ApplicatorHandler> = {
8
8
  // Shadow effects
@@ -5,13 +5,13 @@
5
5
  * Uses a factory pattern to reduce boilerplate and ensure consistency.
6
6
  */
7
7
 
8
- import type { ApplicatorHandler } from "./index.js";
8
+ import type { ApplicatorHandler } from "./types.js";
9
9
  import {
10
10
  getElementDisposables,
11
11
  disposableListener,
12
12
  disposableTimeout,
13
13
  type Disposable,
14
- } from "@hypen-space/core";
14
+ } from "@hypen-space/core/disposable";
15
15
  import {
16
16
  type IEngine,
17
17
  getEngine,
@@ -2,7 +2,7 @@
2
2
  * Font Applicators with Google Fonts support
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  // Track loaded Google Fonts to avoid duplicate link tags
8
8
  const loadedGoogleFonts = new Set<string>();
@@ -4,7 +4,42 @@
4
4
  * Handles style applicators (modifiers) for Hypen components
5
5
  */
6
6
 
7
- export type ApplicatorHandler = (element: HTMLElement, value: any) => void;
7
+ // Re-export the type from types.ts to maintain API compatibility
8
+ export type { ApplicatorHandler } from "./types.js";
9
+ import type { ApplicatorHandler } from "./types.js";
10
+
11
+ // Static imports for all applicator handlers (avoids circular dependency)
12
+ import {
13
+ paddingHandler,
14
+ paddingTopHandler,
15
+ paddingBottomHandler,
16
+ paddingLeftHandler,
17
+ paddingRightHandler,
18
+ paddingHorizontalHandler,
19
+ paddingVerticalHandler,
20
+ } from "./padding.js";
21
+ import {
22
+ marginHandler,
23
+ marginTopHandler,
24
+ marginBottomHandler,
25
+ marginLeftHandler,
26
+ marginRightHandler,
27
+ marginHorizontalHandler,
28
+ marginVerticalHandler,
29
+ } from "./margin.js";
30
+ import { colorHandlers } from "./color.js";
31
+ import { borderHandlers } from "./border.js";
32
+ import { sizeHandlers } from "./size.js";
33
+ import { fontHandlers } from "./font.js";
34
+ import { layoutHandlers } from "./layout.js";
35
+ import { eventHandlers } from "./events.js";
36
+ import { typographyHandlers } from "./typography.js";
37
+ import { transformHandlers } from "./transform.js";
38
+ import { effectsHandlers } from "./effects.js";
39
+ import { advancedLayoutHandlers } from "./advanced-layout.js";
40
+ import { backgroundHandlers } from "./background.js";
41
+ import { displayHandlers } from "./display.js";
42
+ import { transitionHandlers } from "./transition.js";
8
43
 
9
44
  /**
10
45
  * Tailwind breakpoint values for responsive variants
@@ -388,26 +423,23 @@ export class ApplicatorRegistry {
388
423
 
389
424
  /**
390
425
  * Register default applicator handlers
426
+ * Uses statically imported handlers (defined at top of file) to avoid circular dependencies
391
427
  */
392
428
  private registerDefaults(): void {
393
- const { paddingHandler } = require("./padding.js");
394
- const { marginHandler } = require("./margin.js");
395
- const { colorHandlers } = require("./color.js");
396
- const { borderHandlers } = require("./border.js");
397
- const { sizeHandlers } = require("./size.js");
398
- const { fontHandlers } = require("./font.js");
399
- const { layoutHandlers } = require("./layout.js");
400
- const { eventHandlers } = require("./events.js");
401
- const { typographyHandlers } = require("./typography.js");
402
- const { transformHandlers } = require("./transform.js");
403
- const { effectsHandlers } = require("./effects.js");
404
- const { advancedLayoutHandlers } = require("./advanced-layout.js");
405
- const { backgroundHandlers } = require("./background.js");
406
- const { displayHandlers } = require("./display.js");
407
- const { transitionHandlers } = require("./transition.js");
408
-
409
429
  this.register("padding", paddingHandler);
430
+ this.register("paddingTop", paddingTopHandler);
431
+ this.register("paddingBottom", paddingBottomHandler);
432
+ this.register("paddingLeft", paddingLeftHandler);
433
+ this.register("paddingRight", paddingRightHandler);
434
+ this.register("paddingHorizontal", paddingHorizontalHandler);
435
+ this.register("paddingVertical", paddingVerticalHandler);
410
436
  this.register("margin", marginHandler);
437
+ this.register("marginTop", marginTopHandler);
438
+ this.register("marginBottom", marginBottomHandler);
439
+ this.register("marginLeft", marginLeftHandler);
440
+ this.register("marginRight", marginRightHandler);
441
+ this.register("marginHorizontal", marginHorizontalHandler);
442
+ this.register("marginVertical", marginVerticalHandler);
411
443
 
412
444
  for (const [name, handler] of Object.entries(colorHandlers)) {
413
445
  this.register(name, handler as ApplicatorHandler);
@@ -2,13 +2,13 @@
2
2
  * Layout Applicators (Flexbox/Grid)
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  /**
8
8
  * Maps Hypen alignment values to CSS flexbox values.
9
9
  * Ensures consistent cross-platform API (Android/iOS/Web).
10
10
  */
11
- function mapAlignmentValue(value: string): string {
11
+ export function mapAlignmentValue(value: string): string {
12
12
  const v = String(value).toLowerCase();
13
13
  switch (v) {
14
14
  // Positional values -> CSS equivalents
@@ -44,9 +44,14 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
44
44
  // Unified alignment API - works for both Column and Row
45
45
  verticalAlignment: (el, value) => {
46
46
  const val = mapAlignmentValue(String(value));
47
- // Check flex-direction to determine which CSS property to set
48
- const flexDirection = getComputedStyle(el).flexDirection;
49
- if (flexDirection === "column" || flexDirection === "column-reverse") {
47
+ // Check display and flex-direction to determine which CSS property to set
48
+ const display = el.style.display || getComputedStyle(el).display;
49
+ const flexDirection = el.style.flexDirection || getComputedStyle(el).flexDirection;
50
+
51
+ if (display === "grid") {
52
+ // For Grid (Stack): use align-items to align children vertically
53
+ el.style.alignItems = val;
54
+ } else if (flexDirection === "column" || flexDirection === "column-reverse") {
50
55
  // For column: vertical is the main axis (justify-content)
51
56
  el.style.justifyContent = val;
52
57
  } else {
@@ -57,14 +62,28 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
57
62
 
58
63
  horizontalAlignment: (el, value) => {
59
64
  const val = mapAlignmentValue(String(value));
60
- // Check flex-direction to determine which CSS property to set
61
- const flexDirection = getComputedStyle(el).flexDirection;
62
- if (flexDirection === "column" || flexDirection === "column-reverse") {
65
+ // Check display and flex-direction to determine which CSS property to set
66
+ const display = el.style.display || getComputedStyle(el).display;
67
+ const flexDirection = el.style.flexDirection || getComputedStyle(el).flexDirection;
68
+
69
+ if (display === "grid") {
70
+ // For Grid (Stack): use justify-items to align children horizontally
71
+ el.style.justifyItems = val;
72
+ } else if (flexDirection === "column" || flexDirection === "column-reverse") {
63
73
  // For column: horizontal is the cross axis (align-items)
64
74
  el.style.alignItems = val;
65
- } else {
75
+ } else if (flexDirection === "row" || flexDirection === "row-reverse") {
66
76
  // For row: horizontal is the main axis (justify-content)
67
77
  el.style.justifyContent = val;
78
+ // For arrangement to have visible effect, Row needs to fill available width
79
+ // (matching iOS/Android behavior where non-start alignment auto-expands)
80
+ // Only auto-expand if not scrollable
81
+ if (val !== "flex-start" && el.style.overflow !== "auto" && el.style.overflowX !== "auto") {
82
+ el.style.width = "100%";
83
+ }
84
+ } else {
85
+ // Fallback for other display types
86
+ el.style.justifyContent = val;
68
87
  }
69
88
  },
70
89
 
@@ -1,18 +1,68 @@
1
1
  /**
2
- * Margin Applicator
2
+ * Margin Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
+
7
+ // Helper to extract numeric value from applicator value
8
+ const getNumericValue = (value: any): number | null => {
9
+ if (typeof value === "number") return value;
10
+ if (typeof value === "object" && value["0"] !== undefined) return Number(value["0"]);
11
+ if (typeof value === "string") return parseFloat(value);
12
+ return null;
13
+ };
6
14
 
7
15
  export const marginHandler: ApplicatorHandler = (el, value) => {
8
16
  if (typeof value === "number") {
9
17
  el.style.margin = `${value}px`;
10
18
  } else if (typeof value === "object") {
11
- if (value.left !== undefined) el.style.marginLeft = `${value.left}px`;
12
- if (value.right !== undefined) el.style.marginRight = `${value.right}px`;
13
- if (value.top !== undefined) el.style.marginTop = `${value.top}px`;
14
- if (value.bottom !== undefined) el.style.marginBottom = `${value.bottom}px`;
19
+ // Handle {0: value} format from .margin(value)
20
+ if (value["0"] !== undefined && Object.keys(value).length === 1) {
21
+ el.style.margin = `${value["0"]}px`;
22
+ } else {
23
+ if (value.left !== undefined) el.style.marginLeft = `${value.left}px`;
24
+ if (value.right !== undefined) el.style.marginRight = `${value.right}px`;
25
+ if (value.top !== undefined) el.style.marginTop = `${value.top}px`;
26
+ if (value.bottom !== undefined) el.style.marginBottom = `${value.bottom}px`;
27
+ }
15
28
  } else {
16
29
  el.style.margin = String(value);
17
30
  }
18
31
  };
32
+
33
+ // Directional margin handlers for .marginTop(8), .marginBottom(8), etc.
34
+ export const marginTopHandler: ApplicatorHandler = (el, value) => {
35
+ const v = getNumericValue(value);
36
+ if (v !== null) el.style.marginTop = `${v}px`;
37
+ };
38
+
39
+ export const marginBottomHandler: ApplicatorHandler = (el, value) => {
40
+ const v = getNumericValue(value);
41
+ if (v !== null) el.style.marginBottom = `${v}px`;
42
+ };
43
+
44
+ export const marginLeftHandler: ApplicatorHandler = (el, value) => {
45
+ const v = getNumericValue(value);
46
+ if (v !== null) el.style.marginLeft = `${v}px`;
47
+ };
48
+
49
+ export const marginRightHandler: ApplicatorHandler = (el, value) => {
50
+ const v = getNumericValue(value);
51
+ if (v !== null) el.style.marginRight = `${v}px`;
52
+ };
53
+
54
+ export const marginHorizontalHandler: ApplicatorHandler = (el, value) => {
55
+ const v = getNumericValue(value);
56
+ if (v !== null) {
57
+ el.style.marginLeft = `${v}px`;
58
+ el.style.marginRight = `${v}px`;
59
+ }
60
+ };
61
+
62
+ export const marginVerticalHandler: ApplicatorHandler = (el, value) => {
63
+ const v = getNumericValue(value);
64
+ if (v !== null) {
65
+ el.style.marginTop = `${v}px`;
66
+ el.style.marginBottom = `${v}px`;
67
+ }
68
+ };
@@ -1,18 +1,68 @@
1
1
  /**
2
- * Padding Applicator
2
+ * Padding Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
+
7
+ // Helper to extract numeric value from applicator value
8
+ const getNumericValue = (value: any): number | null => {
9
+ if (typeof value === "number") return value;
10
+ if (typeof value === "object" && value["0"] !== undefined) return Number(value["0"]);
11
+ if (typeof value === "string") return parseFloat(value);
12
+ return null;
13
+ };
6
14
 
7
15
  export const paddingHandler: ApplicatorHandler = (el, value) => {
8
16
  if (typeof value === "number") {
9
17
  el.style.padding = `${value}px`;
10
18
  } else if (typeof value === "object") {
11
- if (value.left !== undefined) el.style.paddingLeft = `${value.left}px`;
12
- if (value.right !== undefined) el.style.paddingRight = `${value.right}px`;
13
- if (value.top !== undefined) el.style.paddingTop = `${value.top}px`;
14
- if (value.bottom !== undefined) el.style.paddingBottom = `${value.bottom}px`;
19
+ // Handle {0: value} format from .padding(value)
20
+ if (value["0"] !== undefined && Object.keys(value).length === 1) {
21
+ el.style.padding = `${value["0"]}px`;
22
+ } else {
23
+ if (value.left !== undefined) el.style.paddingLeft = `${value.left}px`;
24
+ if (value.right !== undefined) el.style.paddingRight = `${value.right}px`;
25
+ if (value.top !== undefined) el.style.paddingTop = `${value.top}px`;
26
+ if (value.bottom !== undefined) el.style.paddingBottom = `${value.bottom}px`;
27
+ }
15
28
  } else {
16
29
  el.style.padding = String(value);
17
30
  }
18
31
  };
32
+
33
+ // Directional padding handlers for .paddingTop(8), .paddingBottom(8), etc.
34
+ export const paddingTopHandler: ApplicatorHandler = (el, value) => {
35
+ const v = getNumericValue(value);
36
+ if (v !== null) el.style.paddingTop = `${v}px`;
37
+ };
38
+
39
+ export const paddingBottomHandler: ApplicatorHandler = (el, value) => {
40
+ const v = getNumericValue(value);
41
+ if (v !== null) el.style.paddingBottom = `${v}px`;
42
+ };
43
+
44
+ export const paddingLeftHandler: ApplicatorHandler = (el, value) => {
45
+ const v = getNumericValue(value);
46
+ if (v !== null) el.style.paddingLeft = `${v}px`;
47
+ };
48
+
49
+ export const paddingRightHandler: ApplicatorHandler = (el, value) => {
50
+ const v = getNumericValue(value);
51
+ if (v !== null) el.style.paddingRight = `${v}px`;
52
+ };
53
+
54
+ export const paddingHorizontalHandler: ApplicatorHandler = (el, value) => {
55
+ const v = getNumericValue(value);
56
+ if (v !== null) {
57
+ el.style.paddingLeft = `${v}px`;
58
+ el.style.paddingRight = `${v}px`;
59
+ }
60
+ };
61
+
62
+ export const paddingVerticalHandler: ApplicatorHandler = (el, value) => {
63
+ const v = getNumericValue(value);
64
+ if (v !== null) {
65
+ el.style.paddingTop = `${v}px`;
66
+ el.style.paddingBottom = `${v}px`;
67
+ }
68
+ };
@@ -11,7 +11,7 @@
11
11
  * - "wrap" / "auto": fit content
12
12
  */
13
13
 
14
- import type { ApplicatorHandler } from "./index.js";
14
+ import type { ApplicatorHandler } from "./types.js";
15
15
 
16
16
  /**
17
17
  * Parse a size value and return CSS-compatible string.
@@ -135,12 +135,22 @@ export const sizeHandlers: Record<string, ApplicatorHandler> = {
135
135
  }
136
136
  },
137
137
 
138
- // Fill max width - shorthand for width: 100%
138
+ // Fill max width - stretch to fill parent width
139
139
  fillMaxWidth: (el, value) => {
140
140
  if (value === false) return;
141
- // Value can be a fraction (0-1) or boolean
142
141
  const fraction = typeof value === "number" ? value : 1;
143
- el.style.width = `${fraction * 100}%`;
142
+ if (fraction === 1) {
143
+ // For 100% width: use align-self stretch in Column (cross-axis)
144
+ // and width 100% as fallback for Row and other contexts
145
+ el.style.alignSelf = "stretch";
146
+ el.style.width = "100%";
147
+ el.style.minWidth = "0"; // Prevent flex item from overflowing
148
+ } else {
149
+ // For fractional width, use percentage
150
+ el.style.width = `${fraction * 100}%`;
151
+ }
152
+ // For grid containers (Stack)
153
+ el.style.justifySelf = "stretch";
144
154
  },
145
155
 
146
156
  // Fill max height - shorthand for height: 100%
@@ -2,7 +2,7 @@
2
2
  * Transform Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const transformHandlers: Record<string, ApplicatorHandler> = {
8
8
  transform: (el, value) => {
@@ -2,7 +2,7 @@
2
2
  * Transition and Animation Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const transitionHandlers: Record<string, ApplicatorHandler> = {
8
8
  transition: (el, value) => {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Applicator Handler Type
3
+ *
4
+ * Separated to avoid circular dependencies between index.ts and individual handlers.
5
+ */
6
+
7
+ export type ApplicatorHandler = (element: HTMLElement, value: any) => void;
@@ -2,7 +2,7 @@
2
2
  * Typography Applicators
3
3
  */
4
4
 
5
- import type { ApplicatorHandler } from "./index.js";
5
+ import type { ApplicatorHandler } from "./types.js";
6
6
 
7
7
  export const typographyHandlers: Record<string, ApplicatorHandler> = {
8
8
  textAlign: (el, value) => {
@@ -23,10 +23,11 @@ export const avatarHandler: ComponentHandler = {
23
23
  },
24
24
 
25
25
  applyProps(el: HTMLElement, props: Record<string, any>): void {
26
- // Image source
27
- if (props.src !== undefined) {
26
+ // Image source - support named arg with .0 suffix, positional arg, or plain name
27
+ const src = props["src.0"] || props["0"] || props.src || props.source;
28
+ if (src !== undefined) {
28
29
  const img = document.createElement("img");
29
- img.src = String(props.src);
30
+ img.src = String(src);
30
31
  img.style.width = "100%";
31
32
  img.style.height = "100%";
32
33
  img.style.objectFit = "cover";
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * Button Component
3
+ *
4
+ * Renders as a flex container. Children align to start by default (matching iOS).
5
+ * Use .horizontalAlignment("center") to center content.
6
+ * Reset default HTML button styles for cross-platform consistency.
3
7
  */
4
8
 
5
9
  import type { ComponentHandler } from "./index.js";
@@ -7,6 +11,19 @@ import type { ComponentHandler } from "./index.js";
7
11
  export const buttonHandler: ComponentHandler = {
8
12
  create(): HTMLElement {
9
13
  const el = document.createElement("button");
14
+ // Reset default button styles for cross-platform consistency
15
+ el.style.border = "none";
16
+ el.style.background = "none";
17
+ el.style.padding = "0";
18
+ el.style.margin = "0";
19
+ el.style.font = "inherit";
20
+ el.style.color = "inherit";
21
+ el.style.cursor = "pointer";
22
+ // Make it a flex container - align to start by default (matching iOS)
23
+ // Use .horizontalAlignment("center") to center content
24
+ el.style.display = "flex";
25
+ el.style.flexDirection = "column";
26
+ el.style.alignItems = "flex-start";
10
27
  el.dataset.hypenType = "button";
11
28
  return el;
12
29
  },
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * Column Component - Vertical Stack
3
+ *
4
+ * Children wrap to content by default (matching Android/iOS behavior).
5
+ * Column stretches to fill parent's width so children with fillMaxWidth work.
6
+ * Use .fillMaxWidth() on children or .horizontalAlignment("stretch") to stretch.
3
7
  */
4
8
 
5
9
  import type { ComponentHandler } from "./index.js";
@@ -9,6 +13,11 @@ export const columnHandler: ComponentHandler = {
9
13
  const el = document.createElement("div");
10
14
  el.style.display = "flex";
11
15
  el.style.flexDirection = "column";
16
+ // Wrap children to content by default (match iOS/Android behavior)
17
+ el.style.alignItems = "flex-start";
18
+ // Stretch to fill parent's width so children with fillMaxWidth can reference it
19
+ // This matches iOS/Android where Column expands to accommodate children that want to stretch
20
+ el.style.alignSelf = "stretch";
12
21
  el.dataset.hypenType = "column";
13
22
  return el;
14
23
  },
@@ -7,6 +7,8 @@ import type { ComponentHandler } from "./index.js";
7
7
  export const containerHandler: ComponentHandler = {
8
8
  create(): HTMLElement {
9
9
  const el = document.createElement("div");
10
+ // Simple block container - wraps to content by default
11
+ // Use .fillMaxWidth(true) to stretch
10
12
  el.dataset.hypenType = "container";
11
13
  return el;
12
14
  },
@@ -8,6 +8,9 @@ export const listHandler: ComponentHandler = {
8
8
  create(): HTMLElement {
9
9
  const el = document.createElement("div");
10
10
  el.style.display = "flex";
11
+ el.style.flexDirection = "column"; // Default to vertical (like Android)
12
+ // Default to flex-start to match Android/iOS behavior
13
+ el.style.alignItems = "flex-start";
11
14
  el.style.overflow = "auto";
12
15
  el.dataset.hypenType = "list";
13
16
  return el;
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Row Component - Horizontal Stack
3
+ * Children keep their intrinsic size by default (matching iOS/Android behavior).
4
+ * Use .weight(1) on children to make them expand equally.
3
5
  */
4
6
 
5
7
  import type { ComponentHandler } from "./index.js";
@@ -9,6 +11,8 @@ export const rowHandler: ComponentHandler = {
9
11
  const el = document.createElement("div");
10
12
  el.style.display = "flex";
11
13
  el.style.flexDirection = "row";
14
+ // Wrap to content by default (match iOS/Android behavior)
15
+ el.style.alignItems = "flex-start";
12
16
  el.dataset.hypenType = "row";
13
17
  return el;
14
18
  },
@@ -4,29 +4,41 @@
4
4
 
5
5
  import type { ComponentHandler } from "./index.js";
6
6
 
7
+ // Inject global styles once
8
+ let stackStylesInjected = false;
9
+ function ensureStackStyles(): void {
10
+ if (stackStylesInjected) return;
11
+ stackStylesInjected = true;
12
+
13
+ const style = document.createElement("style");
14
+ style.id = "hypen-stack-styles";
15
+ style.textContent = `
16
+ [data-hypen-type="stack"] {
17
+ position: relative;
18
+ display: grid;
19
+ grid-template-areas: "stack";
20
+ /* Default alignment: top-left (matching iOS/Android ZStack default) */
21
+ justify-items: start;
22
+ align-items: start;
23
+ /* Ensure Stack participates properly in flex layouts (Row/Column) */
24
+ min-width: 0;
25
+ min-height: 0;
26
+ }
27
+ [data-hypen-type="stack"] > * {
28
+ grid-area: stack;
29
+ /* Don't set justify-self/align-self here - let parent's justify-items/align-items control */
30
+ }
31
+ `;
32
+ document.head.appendChild(style);
33
+ }
34
+
7
35
  export const stackHandler: ComponentHandler = {
8
36
  create(): HTMLElement {
37
+ ensureStackStyles();
38
+
9
39
  const el = document.createElement("div");
10
- el.style.position = "relative";
11
- el.style.display = "flex";
12
40
  el.dataset.hypenType = "stack";
13
-
14
- // Children will be absolutely positioned
15
- const style = document.createElement("style");
16
- style.textContent = `
17
- [data-hypen-type="stack"] > * {
18
- position: absolute;
19
- top: 0;
20
- left: 0;
21
- width: 100%;
22
- height: 100%;
23
- }
24
- [data-hypen-type="stack"] > *:first-child {
25
- position: relative;
26
- }
27
- `;
28
- el.appendChild(style);
29
-
41
+
30
42
  return el;
31
43
  },
32
44
  };