@hypen-space/web 0.2.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 (195) hide show
  1. package/dist/chunk-2s02mkzs.js +32 -0
  2. package/dist/chunk-2s02mkzs.js.map +9 -0
  3. package/dist/src/canvas/accessibility.js +152 -0
  4. package/dist/src/canvas/accessibility.js.map +10 -0
  5. package/dist/src/canvas/events.js +198 -0
  6. package/dist/src/canvas/events.js.map +10 -0
  7. package/dist/src/canvas/index.js +28 -0
  8. package/dist/src/canvas/index.js.map +9 -0
  9. package/dist/src/canvas/input.js +132 -0
  10. package/dist/src/canvas/input.js.map +10 -0
  11. package/dist/src/canvas/layout.js +309 -0
  12. package/dist/src/canvas/layout.js.map +10 -0
  13. package/dist/src/canvas/paint.js +878 -0
  14. package/dist/src/canvas/paint.js.map +10 -0
  15. package/dist/src/canvas/renderer.js +276 -0
  16. package/dist/src/canvas/renderer.js.map +10 -0
  17. package/dist/src/canvas/text.js +118 -0
  18. package/dist/src/canvas/text.js.map +10 -0
  19. package/dist/src/canvas/types.js +2 -0
  20. package/dist/src/canvas/types.js.map +9 -0
  21. package/dist/src/canvas/utils.js +139 -0
  22. package/dist/src/canvas/utils.js.map +10 -0
  23. package/dist/src/dom/applicators/advanced-layout.js +111 -0
  24. package/dist/src/dom/applicators/advanced-layout.js.map +10 -0
  25. package/dist/src/dom/applicators/background.js +54 -0
  26. package/dist/src/dom/applicators/background.js.map +10 -0
  27. package/dist/src/dom/applicators/border.js +33 -0
  28. package/dist/src/dom/applicators/border.js.map +10 -0
  29. package/dist/src/dom/applicators/color.js +36 -0
  30. package/dist/src/dom/applicators/color.js.map +10 -0
  31. package/dist/src/dom/applicators/display.js +57 -0
  32. package/dist/src/dom/applicators/display.js.map +10 -0
  33. package/dist/src/dom/applicators/effects.js +89 -0
  34. package/dist/src/dom/applicators/effects.js.map +10 -0
  35. package/dist/src/dom/applicators/events.js +518 -0
  36. package/dist/src/dom/applicators/events.js.map +10 -0
  37. package/dist/src/dom/applicators/font.js +39 -0
  38. package/dist/src/dom/applicators/font.js.map +10 -0
  39. package/dist/src/dom/applicators/index.js +296 -0
  40. package/dist/src/dom/applicators/index.js.map +10 -0
  41. package/dist/src/dom/applicators/layout.js +86 -0
  42. package/dist/src/dom/applicators/layout.js.map +10 -0
  43. package/dist/src/dom/applicators/margin.js +32 -0
  44. package/dist/src/dom/applicators/margin.js.map +10 -0
  45. package/dist/src/dom/applicators/padding.js +35 -0
  46. package/dist/src/dom/applicators/padding.js.map +10 -0
  47. package/dist/src/dom/applicators/size.js +42 -0
  48. package/dist/src/dom/applicators/size.js.map +10 -0
  49. package/dist/src/dom/applicators/transform.js +92 -0
  50. package/dist/src/dom/applicators/transform.js.map +10 -0
  51. package/dist/src/dom/applicators/transition.js +66 -0
  52. package/dist/src/dom/applicators/transition.js.map +10 -0
  53. package/dist/src/dom/applicators/typography.js +87 -0
  54. package/dist/src/dom/applicators/typography.js.map +10 -0
  55. package/dist/src/dom/canvas/index.js +50 -0
  56. package/dist/src/dom/canvas/index.js.map +10 -0
  57. package/dist/src/dom/components/audio.js +48 -0
  58. package/dist/src/dom/components/audio.js.map +10 -0
  59. package/dist/src/dom/components/avatar.js +58 -0
  60. package/dist/src/dom/components/avatar.js.map +10 -0
  61. package/dist/src/dom/components/badge.js +55 -0
  62. package/dist/src/dom/components/badge.js.map +10 -0
  63. package/dist/src/dom/components/button.js +29 -0
  64. package/dist/src/dom/components/button.js.map +10 -0
  65. package/dist/src/dom/components/card.js +33 -0
  66. package/dist/src/dom/components/card.js.map +10 -0
  67. package/dist/src/dom/components/center.js +32 -0
  68. package/dist/src/dom/components/center.js.map +10 -0
  69. package/dist/src/dom/components/checkbox.js +54 -0
  70. package/dist/src/dom/components/checkbox.js.map +10 -0
  71. package/dist/src/dom/components/column.js +31 -0
  72. package/dist/src/dom/components/column.js.map +10 -0
  73. package/dist/src/dom/components/container.js +29 -0
  74. package/dist/src/dom/components/container.js.map +10 -0
  75. package/dist/src/dom/components/divider.js +45 -0
  76. package/dist/src/dom/components/divider.js.map +10 -0
  77. package/dist/src/dom/components/grid.js +44 -0
  78. package/dist/src/dom/components/grid.js.map +10 -0
  79. package/dist/src/dom/components/heading.js +47 -0
  80. package/dist/src/dom/components/heading.js.map +10 -0
  81. package/dist/src/dom/components/image.js +39 -0
  82. package/dist/src/dom/components/image.js.map +10 -0
  83. package/dist/src/dom/components/index.js +217 -0
  84. package/dist/src/dom/components/index.js.map +10 -0
  85. package/dist/src/dom/components/input.js +41 -0
  86. package/dist/src/dom/components/input.js.map +10 -0
  87. package/dist/src/dom/components/link.js +42 -0
  88. package/dist/src/dom/components/link.js.map +10 -0
  89. package/dist/src/dom/components/list.js +42 -0
  90. package/dist/src/dom/components/list.js.map +10 -0
  91. package/dist/src/dom/components/paragraph.js +35 -0
  92. package/dist/src/dom/components/paragraph.js.map +10 -0
  93. package/dist/src/dom/components/progressbar.js +57 -0
  94. package/dist/src/dom/components/progressbar.js.map +10 -0
  95. package/dist/src/dom/components/route.js +44 -0
  96. package/dist/src/dom/components/route.js.map +10 -0
  97. package/dist/src/dom/components/router.js +33 -0
  98. package/dist/src/dom/components/router.js.map +10 -0
  99. package/dist/src/dom/components/row.js +31 -0
  100. package/dist/src/dom/components/row.js.map +10 -0
  101. package/dist/src/dom/components/select.js +57 -0
  102. package/dist/src/dom/components/select.js.map +10 -0
  103. package/dist/src/dom/components/slider.js +48 -0
  104. package/dist/src/dom/components/slider.js.map +10 -0
  105. package/dist/src/dom/components/spacer.js +30 -0
  106. package/dist/src/dom/components/spacer.js.map +10 -0
  107. package/dist/src/dom/components/spinner.js +65 -0
  108. package/dist/src/dom/components/spinner.js.map +10 -0
  109. package/dist/src/dom/components/stack.js +45 -0
  110. package/dist/src/dom/components/stack.js.map +10 -0
  111. package/dist/src/dom/components/switch.js +83 -0
  112. package/dist/src/dom/components/switch.js.map +10 -0
  113. package/dist/src/dom/components/text.js +37 -0
  114. package/dist/src/dom/components/text.js.map +10 -0
  115. package/dist/src/dom/components/textarea.js +51 -0
  116. package/dist/src/dom/components/textarea.js.map +10 -0
  117. package/dist/src/dom/components/video.js +51 -0
  118. package/dist/src/dom/components/video.js.map +10 -0
  119. package/dist/src/dom/debug.js +170 -0
  120. package/dist/src/dom/debug.js.map +10 -0
  121. package/dist/src/dom/events.js +112 -0
  122. package/dist/src/dom/events.js.map +10 -0
  123. package/dist/src/dom/index.js +73 -0
  124. package/dist/src/dom/index.js.map +9 -0
  125. package/dist/src/dom/renderer.js +277 -0
  126. package/dist/src/dom/renderer.js.map +10 -0
  127. package/dist/src/index.js +89 -0
  128. package/dist/src/index.js.map +9 -0
  129. package/package.json +84 -0
  130. package/src/canvas/QUICKSTART.md +421 -0
  131. package/src/canvas/README.md +376 -0
  132. package/src/canvas/accessibility.ts +218 -0
  133. package/src/canvas/events.ts +307 -0
  134. package/src/canvas/index.ts +35 -0
  135. package/src/canvas/input.ts +210 -0
  136. package/src/canvas/layout.ts +401 -0
  137. package/src/canvas/paint.ts +1321 -0
  138. package/src/canvas/renderer.ts +422 -0
  139. package/src/canvas/text.ts +182 -0
  140. package/src/canvas/types.ts +137 -0
  141. package/src/canvas/utils.ts +218 -0
  142. package/src/dom/README.md +265 -0
  143. package/src/dom/applicators/advanced-layout.ts +128 -0
  144. package/src/dom/applicators/background.ts +50 -0
  145. package/src/dom/applicators/border.ts +19 -0
  146. package/src/dom/applicators/color.ts +23 -0
  147. package/src/dom/applicators/display.ts +54 -0
  148. package/src/dom/applicators/effects.ts +97 -0
  149. package/src/dom/applicators/events.ts +689 -0
  150. package/src/dom/applicators/font.ts +27 -0
  151. package/src/dom/applicators/index.ts +354 -0
  152. package/src/dom/applicators/layout.ts +92 -0
  153. package/src/dom/applicators/margin.ts +18 -0
  154. package/src/dom/applicators/padding.ts +18 -0
  155. package/src/dom/applicators/size.ts +31 -0
  156. package/src/dom/applicators/transform.ts +93 -0
  157. package/src/dom/applicators/transition.ts +65 -0
  158. package/src/dom/applicators/typography.ts +91 -0
  159. package/src/dom/canvas/index.ts +60 -0
  160. package/src/dom/components/audio.ts +45 -0
  161. package/src/dom/components/avatar.ts +49 -0
  162. package/src/dom/components/badge.ts +45 -0
  163. package/src/dom/components/button.ts +13 -0
  164. package/src/dom/components/card.ts +19 -0
  165. package/src/dom/components/center.ts +16 -0
  166. package/src/dom/components/checkbox.ts +54 -0
  167. package/src/dom/components/column.ts +15 -0
  168. package/src/dom/components/container.ts +13 -0
  169. package/src/dom/components/divider.ts +37 -0
  170. package/src/dom/components/grid.ts +40 -0
  171. package/src/dom/components/heading.ts +41 -0
  172. package/src/dom/components/image.ts +27 -0
  173. package/src/dom/components/index.ts +115 -0
  174. package/src/dom/components/input.ts +29 -0
  175. package/src/dom/components/link.ts +35 -0
  176. package/src/dom/components/list.ts +30 -0
  177. package/src/dom/components/paragraph.ts +23 -0
  178. package/src/dom/components/progressbar.ts +51 -0
  179. package/src/dom/components/route.ts +37 -0
  180. package/src/dom/components/router.ts +22 -0
  181. package/src/dom/components/row.ts +15 -0
  182. package/src/dom/components/select.ts +56 -0
  183. package/src/dom/components/slider.ts +45 -0
  184. package/src/dom/components/spacer.ts +16 -0
  185. package/src/dom/components/spinner.ts +60 -0
  186. package/src/dom/components/stack.ts +34 -0
  187. package/src/dom/components/switch.ts +86 -0
  188. package/src/dom/components/text.ts +24 -0
  189. package/src/dom/components/textarea.ts +50 -0
  190. package/src/dom/components/video.ts +50 -0
  191. package/src/dom/debug.ts +247 -0
  192. package/src/dom/events.ts +168 -0
  193. package/src/dom/index.ts +11 -0
  194. package/src/dom/renderer.ts +327 -0
  195. package/src/index.ts +56 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Input Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const inputHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("input");
10
+ el.dataset.hypenType = "input";
11
+ return el as any as HTMLElement;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ const input = el as HTMLInputElement;
16
+
17
+ if (props.type !== undefined) {
18
+ input.type = String(props.type);
19
+ }
20
+
21
+ if (props.placeholder !== undefined) {
22
+ input.placeholder = String(props.placeholder);
23
+ }
24
+
25
+ if (props.value !== undefined) {
26
+ input.value = String(props.value);
27
+ }
28
+ },
29
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Link Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const linkHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("a");
10
+ el.dataset.hypenType = "link";
11
+ return el;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ const anchor = el as HTMLAnchorElement;
16
+
17
+ // Support href or first positional argument
18
+ const href = props["0"] || props.href;
19
+ if (href !== undefined) {
20
+ anchor.href = String(href);
21
+ }
22
+
23
+ // Target property
24
+ if (props.target !== undefined) {
25
+ anchor.target = String(props.target);
26
+ }
27
+
28
+ // Rel property
29
+ if (props.rel !== undefined) {
30
+ anchor.rel = String(props.rel);
31
+ }
32
+ },
33
+ };
34
+
35
+
@@ -0,0 +1,30 @@
1
+ /**
2
+ * List Component - Scrollable stack
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const listHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("div");
10
+ el.style.display = "flex";
11
+ el.style.overflow = "auto";
12
+ el.dataset.hypenType = "list";
13
+ return el;
14
+ },
15
+
16
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
17
+ // Direction: vertical or horizontal
18
+ const direction = props.direction || props["1"] || "vertical";
19
+ if (direction === "vertical") {
20
+ el.style.flexDirection = "column";
21
+ } else {
22
+ el.style.flexDirection = "row";
23
+ }
24
+
25
+ // Gap between items
26
+ if (props.gap !== undefined) {
27
+ el.style.gap = typeof props.gap === "number" ? `${props.gap}px` : String(props.gap);
28
+ }
29
+ },
30
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Paragraph Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const paragraphHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("p");
10
+ el.dataset.hypenType = "paragraph";
11
+ return el;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ // Text content
16
+ const text = props["0"] || props.text;
17
+ if (text !== undefined) {
18
+ el.textContent = String(text);
19
+ }
20
+ },
21
+ };
22
+
23
+
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ProgressBar Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const progressBarHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const wrapper = document.createElement("div");
10
+ wrapper.dataset.hypenType = "progressbar";
11
+ wrapper.style.width = "100%";
12
+ wrapper.style.height = "8px";
13
+ wrapper.style.backgroundColor = "#e0e0e0";
14
+ wrapper.style.borderRadius = "4px";
15
+ wrapper.style.overflow = "hidden";
16
+
17
+ const bar = document.createElement("div");
18
+ bar.dataset.hypenBar = "true";
19
+ bar.style.height = "100%";
20
+ bar.style.backgroundColor = "#2196F3";
21
+ bar.style.transition = "width 0.3s ease";
22
+ bar.style.width = "0%";
23
+
24
+ wrapper.appendChild(bar);
25
+ return wrapper;
26
+ },
27
+
28
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
29
+ const bar = el.querySelector('[data-hypen-bar="true"]') as HTMLElement;
30
+ if (!bar) return;
31
+
32
+ // Value and max
33
+ const value = Number(props.value || 0);
34
+ const max = Number(props.max || 100);
35
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
36
+ bar.style.width = `${percentage}%`;
37
+
38
+ // Color
39
+ if (props.color !== undefined) {
40
+ bar.style.backgroundColor = String(props.color);
41
+ }
42
+
43
+ // Height
44
+ if (props.height !== undefined) {
45
+ const height = typeof props.height === "number" ? `${props.height}px` : String(props.height);
46
+ el.style.height = height;
47
+ }
48
+ },
49
+ };
50
+
51
+
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Route Component - Lazy rendering container for route content
3
+ * Stores component name as metadata, Router handles actual rendering
4
+ */
5
+
6
+ import type { ComponentHandler } from "./index.js";
7
+
8
+ export const routeHandler: ComponentHandler = {
9
+ create(): HTMLElement {
10
+ const el = document.createElement("div");
11
+ el.style.display = "flex";
12
+ el.style.flexDirection = "column";
13
+ el.style.width = "100%";
14
+ el.dataset.hypenType = "route";
15
+ el.dataset.routeRendered = "false"; // Track if component has been rendered
16
+ return el;
17
+ },
18
+
19
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
20
+ // Extract route path from props
21
+ const path = props.path || props["0"] || "/";
22
+ el.dataset.routePath = String(path);
23
+
24
+ // Check if this is a lazy route (has __lazy flag)
25
+ const isLazy = props.__lazy === true;
26
+ el.dataset.routeLazy = String(isLazy);
27
+
28
+ // Store component name - either from explicit prop or from lazy child
29
+ const componentName = props.component || props.__lazy_child;
30
+ if (componentName) {
31
+ el.dataset.routeComponent = String(componentName);
32
+ }
33
+
34
+ console.log(`🛣️ Route created: path="${path}", lazy=${isLazy}, component="${el.dataset.routeComponent || 'none'}"`);
35
+ },
36
+ };
37
+
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Router Component - Container for routes
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const routerHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("div");
10
+ el.style.display = "flex";
11
+ el.style.flexDirection = "column";
12
+ el.style.width = "100%";
13
+ el.dataset.hypenType = "router";
14
+ return el;
15
+ },
16
+
17
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
18
+ // Router doesn't need special prop handling
19
+ // The routing logic is handled by the Router module
20
+ },
21
+ };
22
+
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Row Component - Horizontal Stack
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const rowHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("div");
10
+ el.style.display = "flex";
11
+ el.style.flexDirection = "row";
12
+ el.dataset.hypenType = "row";
13
+ return el;
14
+ },
15
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Select Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const selectHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("select");
10
+ el.dataset.hypenType = "select";
11
+ return el as any as HTMLElement;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ const select = el as HTMLSelectElement;
16
+
17
+ // Value property
18
+ if (props.value !== undefined) {
19
+ select.value = String(props.value);
20
+ }
21
+
22
+ // Disabled
23
+ if (props.disabled !== undefined) {
24
+ select.disabled = Boolean(props.disabled);
25
+ }
26
+
27
+ // Multiple
28
+ if (props.multiple !== undefined) {
29
+ select.multiple = Boolean(props.multiple);
30
+ }
31
+
32
+ // Options array
33
+ if (props.options && Array.isArray(props.options)) {
34
+ // Clear existing options
35
+ select.innerHTML = "";
36
+
37
+ // Add new options
38
+ props.options.forEach((opt: any) => {
39
+ const option = document.createElement("option");
40
+
41
+ if (typeof opt === "string") {
42
+ option.value = opt;
43
+ option.textContent = opt;
44
+ } else if (typeof opt === "object") {
45
+ option.value = String(opt.value ?? opt.label ?? "");
46
+ option.textContent = String(opt.label ?? opt.value ?? "");
47
+ if (opt.disabled) option.disabled = true;
48
+ }
49
+
50
+ select.appendChild(option);
51
+ });
52
+ }
53
+ },
54
+ };
55
+
56
+
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Slider Component (Range Input)
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const sliderHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("input");
10
+ el.type = "range";
11
+ el.dataset.hypenType = "slider";
12
+ return el as any as HTMLElement;
13
+ },
14
+
15
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
16
+ const input = el as HTMLInputElement;
17
+
18
+ // Value
19
+ if (props.value !== undefined) {
20
+ input.value = String(props.value);
21
+ }
22
+
23
+ // Min
24
+ if (props.min !== undefined) {
25
+ input.min = String(props.min);
26
+ }
27
+
28
+ // Max
29
+ if (props.max !== undefined) {
30
+ input.max = String(props.max);
31
+ }
32
+
33
+ // Step
34
+ if (props.step !== undefined) {
35
+ input.step = String(props.step);
36
+ }
37
+
38
+ // Disabled
39
+ if (props.disabled !== undefined) {
40
+ input.disabled = Boolean(props.disabled);
41
+ }
42
+ },
43
+ };
44
+
45
+
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Spacer Component - Flexible space in flex layouts
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const spacerHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("div");
10
+ el.style.flex = "1";
11
+ el.dataset.hypenType = "spacer";
12
+ return el;
13
+ },
14
+ };
15
+
16
+
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Spinner Component (Loading Indicator)
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const spinnerHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const wrapper = document.createElement("div");
10
+ wrapper.dataset.hypenType = "spinner";
11
+ wrapper.style.display = "inline-block";
12
+
13
+ const spinner = document.createElement("div");
14
+ spinner.style.width = "40px";
15
+ spinner.style.height = "40px";
16
+ spinner.style.border = "4px solid #f3f3f3";
17
+ spinner.style.borderTop = "4px solid #3498db";
18
+ spinner.style.borderRadius = "50%";
19
+ spinner.style.animation = "spin 1s linear infinite";
20
+
21
+ // Add keyframe animation
22
+ const style = document.createElement("style");
23
+ style.textContent = `
24
+ @keyframes spin {
25
+ 0% { transform: rotate(0deg); }
26
+ 100% { transform: rotate(360deg); }
27
+ }
28
+ `;
29
+
30
+ wrapper.appendChild(style);
31
+ wrapper.appendChild(spinner);
32
+
33
+ return wrapper;
34
+ },
35
+
36
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
37
+ const spinner = el.querySelector("div:not(style)") as HTMLElement;
38
+ if (!spinner) return;
39
+
40
+ // Size
41
+ if (props.size !== undefined) {
42
+ const size = String(props.size);
43
+ const sizeMap: Record<string, string> = {
44
+ small: "24px",
45
+ medium: "40px",
46
+ large: "60px",
47
+ };
48
+ const actualSize = sizeMap[size] || size;
49
+ spinner.style.width = actualSize;
50
+ spinner.style.height = actualSize;
51
+ }
52
+
53
+ // Color
54
+ if (props.color !== undefined) {
55
+ spinner.style.borderTopColor = String(props.color);
56
+ }
57
+ },
58
+ };
59
+
60
+
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Stack Component - Overlaying elements with absolute positioning
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const stackHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("div");
10
+ el.style.position = "relative";
11
+ el.style.display = "flex";
12
+ 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
+
30
+ return el;
31
+ },
32
+ };
33
+
34
+
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Switch Component (Toggle)
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const switchHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const wrapper = document.createElement("label");
10
+ wrapper.dataset.hypenType = "switch";
11
+ wrapper.style.display = "inline-flex";
12
+ wrapper.style.alignItems = "center";
13
+ wrapper.style.gap = "8px";
14
+ wrapper.style.cursor = "pointer";
15
+
16
+ const input = document.createElement("input");
17
+ input.type = "checkbox";
18
+ input.dataset.hypenSwitch = "true";
19
+
20
+ // Style the switch
21
+ input.style.appearance = "none";
22
+ input.style.width = "44px";
23
+ input.style.height = "24px";
24
+ input.style.backgroundColor = "#ccc";
25
+ input.style.borderRadius = "12px";
26
+ input.style.position = "relative";
27
+ input.style.cursor = "pointer";
28
+ input.style.transition = "background-color 0.2s";
29
+
30
+ // Add pseudo-element styling via CSS
31
+ const style = document.createElement("style");
32
+ style.textContent = `
33
+ input[data-hypen-switch="true"]::before {
34
+ content: "";
35
+ position: absolute;
36
+ width: 20px;
37
+ height: 20px;
38
+ background-color: white;
39
+ border-radius: 50%;
40
+ top: 2px;
41
+ left: 2px;
42
+ transition: transform 0.2s;
43
+ }
44
+ input[data-hypen-switch="true"]:checked {
45
+ background-color: #4CAF50;
46
+ }
47
+ input[data-hypen-switch="true"]:checked::before {
48
+ transform: translateX(20px);
49
+ }
50
+ `;
51
+ wrapper.appendChild(style);
52
+ wrapper.appendChild(input);
53
+
54
+ return wrapper;
55
+ },
56
+
57
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
58
+ const input = el.querySelector('input[type="checkbox"]') as HTMLInputElement;
59
+ if (!input) return;
60
+
61
+ // On state (checked)
62
+ if (props.on !== undefined) {
63
+ input.checked = Boolean(props.on);
64
+ }
65
+
66
+ // Disabled
67
+ if (props.disabled !== undefined) {
68
+ input.disabled = Boolean(props.disabled);
69
+ }
70
+
71
+ // Label text
72
+ const label = props["0"] || props.label;
73
+ if (label !== undefined) {
74
+ // Remove existing text node if any
75
+ const textNodes = Array.from(el.childNodes).filter(
76
+ node => node.nodeType === Node.TEXT_NODE
77
+ );
78
+ textNodes.forEach(node => node.remove());
79
+
80
+ // Add new label text
81
+ el.appendChild(document.createTextNode(String(label)));
82
+ }
83
+ },
84
+ };
85
+
86
+
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Text Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const textHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("span");
10
+ el.style.display = "inline-block";
11
+ el.dataset.hypenType = "text";
12
+ return el;
13
+ },
14
+
15
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
16
+ // Text content from first positional arg or "text" prop
17
+ const text = props["0"] || props.text;
18
+ if (text !== undefined) {
19
+ // Store the original text template for state interpolation
20
+ el.dataset.textTemplate = String(text);
21
+ el.textContent = String(text);
22
+ }
23
+ },
24
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Textarea Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const textareaHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("textarea");
10
+ el.dataset.hypenType = "textarea";
11
+ return el as any as HTMLElement;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ const textarea = el as HTMLTextAreaElement;
16
+
17
+ // Value property
18
+ const value = props["0"] || props.value;
19
+ if (value !== undefined) {
20
+ textarea.value = String(value);
21
+ }
22
+
23
+ // Placeholder
24
+ if (props.placeholder !== undefined) {
25
+ textarea.placeholder = String(props.placeholder);
26
+ }
27
+
28
+ // Rows
29
+ if (props.rows !== undefined) {
30
+ textarea.rows = Number(props.rows);
31
+ }
32
+
33
+ // Cols
34
+ if (props.cols !== undefined) {
35
+ textarea.cols = Number(props.cols);
36
+ }
37
+
38
+ // Disabled
39
+ if (props.disabled !== undefined) {
40
+ textarea.disabled = Boolean(props.disabled);
41
+ }
42
+
43
+ // Readonly
44
+ if (props.readonly !== undefined) {
45
+ textarea.readOnly = Boolean(props.readonly);
46
+ }
47
+ },
48
+ };
49
+
50
+
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Video Component
3
+ */
4
+
5
+ import type { ComponentHandler } from "./index.js";
6
+
7
+ export const videoHandler: ComponentHandler = {
8
+ create(): HTMLElement {
9
+ const el = document.createElement("video");
10
+ el.dataset.hypenType = "video";
11
+ return el as any as HTMLElement;
12
+ },
13
+
14
+ applyProps(el: HTMLElement, props: Record<string, any>): void {
15
+ const video = el as HTMLVideoElement;
16
+
17
+ // Source
18
+ const src = props["0"] || props.src;
19
+ if (src !== undefined) {
20
+ video.src = String(src);
21
+ }
22
+
23
+ // Controls
24
+ if (props.controls !== undefined) {
25
+ video.controls = Boolean(props.controls);
26
+ }
27
+
28
+ // Autoplay
29
+ if (props.autoplay !== undefined) {
30
+ video.autoplay = Boolean(props.autoplay);
31
+ }
32
+
33
+ // Loop
34
+ if (props.loop !== undefined) {
35
+ video.loop = Boolean(props.loop);
36
+ }
37
+
38
+ // Muted
39
+ if (props.muted !== undefined) {
40
+ video.muted = Boolean(props.muted);
41
+ }
42
+
43
+ // Poster
44
+ if (props.poster !== undefined) {
45
+ video.poster = String(props.poster);
46
+ }
47
+ },
48
+ };
49
+
50
+