@hypen-space/web 0.4.36 → 0.4.37

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 (139) hide show
  1. package/dist/canvas/accessibility.js +174 -174
  2. package/dist/canvas/accessibility.js.map +1 -10
  3. package/dist/canvas/events.js +264 -360
  4. package/dist/canvas/events.js.map +1 -11
  5. package/dist/canvas/index.js +11 -47
  6. package/dist/canvas/index.js.map +1 -9
  7. package/dist/canvas/input.js +178 -289
  8. package/dist/canvas/input.js.map +1 -11
  9. package/dist/canvas/layout.js +349 -547
  10. package/dist/canvas/layout.js.map +1 -12
  11. package/dist/canvas/paint.js +1122 -1146
  12. package/dist/canvas/paint.js.map +1 -12
  13. package/dist/canvas/renderer.js +352 -2259
  14. package/dist/canvas/renderer.js.map +1 -17
  15. package/dist/canvas/text.js +129 -264
  16. package/dist/canvas/text.js.map +1 -11
  17. package/dist/canvas/types.js +7 -2
  18. package/dist/canvas/types.js.map +1 -9
  19. package/dist/canvas/utils.js +158 -161
  20. package/dist/canvas/utils.js.map +1 -10
  21. package/dist/client.js +19 -45
  22. package/dist/client.js.map +1 -9
  23. package/dist/dom/applicators/advanced-layout.js +105 -138
  24. package/dist/dom/applicators/advanced-layout.js.map +1 -10
  25. package/dist/dom/applicators/background.js +36 -76
  26. package/dist/dom/applicators/background.js.map +1 -10
  27. package/dist/dom/applicators/border.js +82 -106
  28. package/dist/dom/applicators/border.js.map +1 -10
  29. package/dist/dom/applicators/color.js +17 -58
  30. package/dist/dom/applicators/color.js.map +1 -10
  31. package/dist/dom/applicators/display.js +39 -79
  32. package/dist/dom/applicators/display.js.map +1 -10
  33. package/dist/dom/applicators/effects.js +121 -147
  34. package/dist/dom/applicators/effects.js.map +1 -10
  35. package/dist/dom/applicators/events.js +413 -450
  36. package/dist/dom/applicators/events.js.map +1 -11
  37. package/dist/dom/applicators/font.js +119 -140
  38. package/dist/dom/applicators/font.js.map +1 -10
  39. package/dist/dom/applicators/index.js +386 -1770
  40. package/dist/dom/applicators/index.js.map +1 -26
  41. package/dist/dom/applicators/layout.js +143 -149
  42. package/dist/dom/applicators/layout.js.map +1 -10
  43. package/dist/dom/applicators/margin.js +64 -105
  44. package/dist/dom/applicators/margin.js.map +1 -10
  45. package/dist/dom/applicators/padding.js +64 -105
  46. package/dist/dom/applicators/padding.js.map +1 -10
  47. package/dist/dom/applicators/size.js +169 -172
  48. package/dist/dom/applicators/size.js.map +1 -10
  49. package/dist/dom/applicators/transform.js +73 -114
  50. package/dist/dom/applicators/transform.js.map +1 -10
  51. package/dist/dom/applicators/transition.js +47 -88
  52. package/dist/dom/applicators/transition.js.map +1 -10
  53. package/dist/dom/applicators/types.js +7 -2
  54. package/dist/dom/applicators/types.js.map +1 -9
  55. package/dist/dom/applicators/typography.js +68 -109
  56. package/dist/dom/applicators/typography.js.map +1 -10
  57. package/dist/dom/canvas/index.js +49 -83
  58. package/dist/dom/canvas/index.js.map +1 -10
  59. package/dist/dom/components/app.js +16 -63
  60. package/dist/dom/components/app.js.map +1 -10
  61. package/dist/dom/components/audio.js +31 -76
  62. package/dist/dom/components/audio.js.map +1 -10
  63. package/dist/dom/components/avatar.js +41 -87
  64. package/dist/dom/components/avatar.js.map +1 -10
  65. package/dist/dom/components/badge.js +35 -83
  66. package/dist/dom/components/badge.js.map +1 -10
  67. package/dist/dom/components/button.js +26 -69
  68. package/dist/dom/components/button.js.map +1 -10
  69. package/dist/dom/components/card.js +13 -63
  70. package/dist/dom/components/card.js.map +1 -10
  71. package/dist/dom/components/center.js +21 -65
  72. package/dist/dom/components/center.js.map +1 -10
  73. package/dist/dom/components/checkbox.js +37 -82
  74. package/dist/dom/components/checkbox.js.map +1 -10
  75. package/dist/dom/components/column.js +16 -62
  76. package/dist/dom/components/column.js.map +1 -10
  77. package/dist/dom/components/container.js +11 -59
  78. package/dist/dom/components/container.js.map +1 -10
  79. package/dist/dom/components/divider.js +27 -73
  80. package/dist/dom/components/divider.js.map +1 -10
  81. package/dist/dom/components/grid.js +41 -80
  82. package/dist/dom/components/grid.js.map +1 -10
  83. package/dist/dom/components/heading.js +28 -74
  84. package/dist/dom/components/heading.js.map +1 -10
  85. package/dist/dom/components/hypenapp.js +291 -288
  86. package/dist/dom/components/hypenapp.js.map +1 -10
  87. package/dist/dom/components/image.js +18 -67
  88. package/dist/dom/components/image.js.map +1 -10
  89. package/dist/dom/components/index.js +109 -1427
  90. package/dist/dom/components/index.js.map +1 -42
  91. package/dist/dom/components/input.js +19 -69
  92. package/dist/dom/components/input.js.map +1 -10
  93. package/dist/dom/components/link.js +23 -70
  94. package/dist/dom/components/link.js.map +1 -10
  95. package/dist/dom/components/list.js +26 -72
  96. package/dist/dom/components/list.js.map +1 -10
  97. package/dist/dom/components/paragraph.js +14 -63
  98. package/dist/dom/components/paragraph.js.map +1 -10
  99. package/dist/dom/components/progressbar.js +38 -85
  100. package/dist/dom/components/progressbar.js.map +1 -10
  101. package/dist/dom/components/route.js +27 -73
  102. package/dist/dom/components/route.js.map +1 -10
  103. package/dist/dom/components/router.js +15 -62
  104. package/dist/dom/components/router.js.map +1 -10
  105. package/dist/dom/components/row.js +25 -70
  106. package/dist/dom/components/row.js.map +1 -10
  107. package/dist/dom/components/select.js +42 -85
  108. package/dist/dom/components/select.js.map +1 -10
  109. package/dist/dom/components/slider.js +31 -76
  110. package/dist/dom/components/slider.js.map +1 -10
  111. package/dist/dom/components/spacer.js +10 -60
  112. package/dist/dom/components/spacer.js.map +1 -10
  113. package/dist/dom/components/spinner.js +41 -88
  114. package/dist/dom/components/spinner.js.map +1 -10
  115. package/dist/dom/components/stack.js +19 -67
  116. package/dist/dom/components/stack.js.map +1 -10
  117. package/dist/dom/components/switch.js +50 -93
  118. package/dist/dom/components/switch.js.map +1 -10
  119. package/dist/dom/components/text.js +24 -69
  120. package/dist/dom/components/text.js.map +1 -10
  121. package/dist/dom/components/textarea.js +35 -79
  122. package/dist/dom/components/textarea.js.map +1 -10
  123. package/dist/dom/components/video.js +35 -79
  124. package/dist/dom/components/video.js.map +1 -10
  125. package/dist/dom/debug.js +159 -160
  126. package/dist/dom/debug.js.map +1 -10
  127. package/dist/dom/element-data.js +181 -143
  128. package/dist/dom/element-data.js.map +1 -10
  129. package/dist/dom/events.js +133 -142
  130. package/dist/dom/events.js.map +1 -10
  131. package/dist/dom/index.js +11 -48
  132. package/dist/dom/index.js.map +1 -9
  133. package/dist/dom/renderer.js +311 -3596
  134. package/dist/dom/renderer.js.map +1 -62
  135. package/dist/hypen.js +385 -3880
  136. package/dist/hypen.js.map +1 -63
  137. package/dist/index.js +36 -53
  138. package/dist/index.js.map +1 -9
  139. package/package.json +2 -2
@@ -1,484 +1,447 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- function __accessProp(key) {
6
- return this[key];
7
- }
8
- var __toCommonJS = (from) => {
9
- var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
- if (entry)
11
- return entry;
12
- entry = __defProp({}, "__esModule", { value: true });
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (var key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(entry, key))
16
- __defProp(entry, key, {
17
- get: __accessProp.bind(from, key),
18
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
- });
20
- }
21
- __moduleCache.set(from, entry);
22
- return entry;
23
- };
24
- var __moduleCache;
25
- var __returnValue = (v) => v;
26
- function __exportSetter(name, newValue) {
27
- this[name] = __returnValue.bind(null, newValue);
28
- }
29
- var __export = (target, all) => {
30
- for (var name in all)
31
- __defProp(target, name, {
32
- get: all[name],
33
- enumerable: true,
34
- configurable: true,
35
- set: __exportSetter.bind(all, name)
36
- });
37
- };
38
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
39
-
40
- // src/dom/element-data.ts
41
- import { getElementDisposables } from "@hypen-space/core/disposable";
42
- var elementDataMap = new WeakMap;
43
- function getHypenData(element) {
44
- let data = elementDataMap.get(element);
45
- if (!data) {
46
- data = {};
47
- elementDataMap.set(element, data);
48
- }
49
- return data;
50
- }
51
- function hasHypenData(element) {
52
- return elementDataMap.has(element);
53
- }
54
- function clearHypenData(element) {
55
- elementDataMap.delete(element);
56
- }
57
- function getEngine(element) {
58
- return getHypenData(element).engine;
59
- }
60
- function setEngine(element, engine) {
61
- getHypenData(element).engine = engine;
62
- }
63
- function findEngine(element) {
64
- let current = element;
65
- while (current) {
66
- const engine = getEngine(current);
67
- if (engine)
68
- return engine;
69
- current = current.parentElement;
70
- }
71
- return;
72
- }
73
- function getRegisteredEvents(element) {
74
- const data = getHypenData(element);
75
- if (!data.registeredEvents) {
76
- data.registeredEvents = new Set;
77
- }
78
- return data.registeredEvents;
79
- }
80
- function isEventRegistered(element, eventKey) {
81
- return getRegisteredEvents(element).has(eventKey);
82
- }
83
- function registerEvent(element, eventKey) {
84
- getRegisteredEvents(element).add(eventKey);
85
- }
86
- function unregisterEvent(element, eventKey) {
87
- getRegisteredEvents(element).delete(eventKey);
88
- }
89
- function getKeyTarget(element) {
90
- return getHypenData(element).keyTarget;
91
- }
92
- function setKeyTarget(element, key) {
93
- getHypenData(element).keyTarget = key;
94
- }
95
- function getMeta(element, key) {
96
- return getHypenData(element).meta?.[key];
97
- }
98
- function setMeta(element, key, value) {
99
- const data = getHypenData(element);
100
- if (!data.meta) {
101
- data.meta = {};
102
- }
103
- data.meta[key] = value;
104
- }
105
- function disposeHypenElement(element) {
106
- try {
107
- const disposables = getElementDisposables(element);
108
- disposables.dispose();
109
- } catch {}
110
- clearHypenData(element);
111
- }
112
- var HYPEN_ENGINE_SYMBOL = Symbol.for("hypen.engine");
113
- var REGISTERED_EVENTS_SYMBOL = Symbol.for("hypen.registeredEvents");
114
- var KEY_TARGET_SYMBOL = Symbol.for("hypen.keyTarget");
115
- function getLegacyEngine(element) {
116
- const engine = getEngine(element);
117
- if (engine)
118
- return engine;
119
- return element[HYPEN_ENGINE_SYMBOL] ?? element.__hypenEngine;
120
- }
121
- function setLegacyEngine(element, engine) {
122
- setEngine(element, engine);
123
- element.__hypenEngine = engine;
124
- }
125
-
126
- // src/dom/applicators/events.ts
127
- import {
128
- getElementDisposables as getElementDisposables2,
129
- disposableListener,
130
- disposableTimeout
131
- } from "@hypen-space/core/disposable";
1
+ /**
2
+ * Event Applicators
3
+ *
4
+ * Handles event applicators like onClick, onPress, etc.
5
+ * Uses a factory pattern to reduce boilerplate and ensure consistency.
6
+ */
7
+ import { getElementDisposables, disposableListener, disposableTimeout, } from "@hypen-space/core/disposable";
132
8
  import { frameworkLoggers } from "@hypen-space/core/logger";
133
- var log = frameworkLoggers.events;
9
+ import { getEngine, getRegisteredEvents, registerEvent, unregisterEvent, getKeyTarget, setKeyTarget, } from "../element-data.js";
10
+ const log = frameworkLoggers.events;
11
+ // ============================================================================
12
+ // Utility Functions
13
+ // ============================================================================
14
+ /**
15
+ * Convert Map or nested objects to plain objects
16
+ */
134
17
  function toPlainObject(value) {
135
- if (value instanceof Map) {
136
- const obj = {};
137
- for (const [key, val] of value.entries()) {
138
- obj[key] = toPlainObject(val);
18
+ if (value instanceof Map) {
19
+ const obj = {};
20
+ for (const [key, val] of value.entries()) {
21
+ obj[key] = toPlainObject(val);
22
+ }
23
+ return obj;
139
24
  }
140
- return obj;
141
- }
142
- if (Array.isArray(value)) {
143
- return value.map((item) => toPlainObject(item));
144
- }
145
- if (value && typeof value === "object") {
146
- const obj = {};
147
- for (const [key, val] of Object.entries(value)) {
148
- obj[key] = toPlainObject(val);
25
+ if (Array.isArray(value)) {
26
+ return value.map((item) => toPlainObject(item));
149
27
  }
150
- return obj;
151
- }
152
- return value;
28
+ if (value && typeof value === "object") {
29
+ const obj = {};
30
+ for (const [key, val] of Object.entries(value)) {
31
+ obj[key] = toPlainObject(val);
32
+ }
33
+ return obj;
34
+ }
35
+ return value;
153
36
  }
37
+ /**
38
+ * Extract action name and custom payload from an applicator value
39
+ */
154
40
  function extractActionDetails(value) {
155
- if (typeof value === "string") {
156
- if (!value.startsWith("@")) {
157
- return { actionName: null, payload: {} };
158
- }
159
- let actionName = value.substring(1);
160
- if (actionName.startsWith("actions.")) {
161
- actionName = actionName.substring(8);
162
- }
163
- return { actionName, payload: {} };
164
- }
165
- if (value && typeof value === "object") {
166
- const plain = toPlainObject(value);
167
- const payload = {};
168
- let actionName = null;
169
- if (plain && typeof plain === "object") {
170
- const actionValue = plain["0"];
171
- if (typeof actionValue === "string" && actionValue.startsWith("@")) {
172
- actionName = actionValue.substring(1);
41
+ // String format: "@actions.doSomething" or "@doSomething"
42
+ if (typeof value === "string") {
43
+ if (!value.startsWith("@")) {
44
+ return { actionName: null, payload: {} };
45
+ }
46
+ let actionName = value.substring(1);
173
47
  if (actionName.startsWith("actions.")) {
174
- actionName = actionName.substring(8);
48
+ actionName = actionName.substring(8);
175
49
  }
176
- }
177
- for (const [key, val] of Object.entries(plain)) {
178
- if (key !== "0") {
179
- if (/^\d+$/.test(key) && val && typeof val === "object" && !Array.isArray(val)) {
180
- for (const [innerKey, innerVal] of Object.entries(val)) {
181
- payload[innerKey] = innerVal;
50
+ return { actionName, payload: {} };
51
+ }
52
+ // Object format: { "0": "@actions.doSomething", "customKey": "value" }
53
+ if (value && typeof value === "object") {
54
+ const plain = toPlainObject(value);
55
+ const payload = {};
56
+ let actionName = null;
57
+ if (plain && typeof plain === "object") {
58
+ const actionValue = plain["0"];
59
+ if (typeof actionValue === "string" && actionValue.startsWith("@")) {
60
+ actionName = actionValue.substring(1);
61
+ if (actionName.startsWith("actions.")) {
62
+ actionName = actionName.substring(8);
63
+ }
64
+ }
65
+ for (const [key, val] of Object.entries(plain)) {
66
+ if (key !== "0") {
67
+ // If the key is numeric (like "1", "2") and the value is an object,
68
+ // merge the object's keys into the payload directly.
69
+ // This handles: .onClick("@actions.foo", { id: "123" })
70
+ // where the second positional arg becomes "1": { id: "123" }
71
+ if (/^\d+$/.test(key) && val && typeof val === "object" && !Array.isArray(val)) {
72
+ for (const [innerKey, innerVal] of Object.entries(val)) {
73
+ payload[innerKey] = innerVal;
74
+ }
75
+ }
76
+ else {
77
+ payload[key] = val;
78
+ }
79
+ }
182
80
  }
183
- } else {
184
- payload[key] = val;
185
- }
186
81
  }
187
- }
82
+ return { actionName, payload };
188
83
  }
189
- return { actionName, payload };
190
- }
191
- return { actionName: null, payload: {} };
84
+ return { actionName: null, payload: {} };
192
85
  }
86
+ /**
87
+ * Extract relevant data from a DOM event
88
+ */
193
89
  function extractEventData(event, element) {
194
- const data = {
195
- type: event.type,
196
- timestamp: Date.now()
197
- };
198
- if (event instanceof MouseEvent) {
199
- data.clientX = event.clientX;
200
- data.clientY = event.clientY;
201
- data.button = event.button;
202
- }
203
- if (event instanceof KeyboardEvent) {
204
- data.key = event.key;
205
- data.code = event.code;
206
- data.ctrlKey = event.ctrlKey;
207
- data.shiftKey = event.shiftKey;
208
- data.altKey = event.altKey;
209
- data.metaKey = event.metaKey;
210
- }
211
- if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
212
- data.value = element.value;
213
- }
214
- if (element instanceof HTMLSelectElement) {
215
- data.value = element.value;
216
- data.selectedIndex = element.selectedIndex;
217
- }
218
- if (event.type === "submit" && element instanceof HTMLFormElement) {
219
- data.formData = new FormData(element);
220
- }
221
- return data;
222
- }
223
- function createEventHandler(eventType, options = {}) {
224
- return (element, value) => {
225
- const { actionName, payload: customPayload } = extractActionDetails(value);
226
- if (!actionName) {
227
- log.warn(`${eventType} requires an action reference starting with @, got:`, value);
228
- return;
90
+ const data = {
91
+ type: event.type,
92
+ timestamp: Date.now(),
93
+ };
94
+ // Mouse events
95
+ if (event instanceof MouseEvent) {
96
+ data.clientX = event.clientX;
97
+ data.clientY = event.clientY;
98
+ data.button = event.button;
99
+ }
100
+ // Keyboard events
101
+ if (event instanceof KeyboardEvent) {
102
+ data.key = event.key;
103
+ data.code = event.code;
104
+ data.ctrlKey = event.ctrlKey;
105
+ data.shiftKey = event.shiftKey;
106
+ data.altKey = event.altKey;
107
+ data.metaKey = event.metaKey;
229
108
  }
230
- const disposables = getElementDisposables2(element);
231
- const eventKey = `${eventType}:${actionName}`;
232
- if (getRegisteredEvents(element).has(eventKey)) {
233
- return;
109
+ // Input element values
110
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
111
+ data.value = element.value;
234
112
  }
235
- registerEvent(element, eventKey);
236
- let throttleTimer = null;
237
- const listener = (event) => {
238
- if (options.throttleMs && throttleTimer) {
239
- return;
240
- }
241
- if (options.throttleMs) {
242
- throttleTimer = disposableTimeout(() => {
243
- throttleTimer = null;
244
- }, options.throttleMs);
245
- }
246
- if (options.preventDefault) {
247
- event.preventDefault();
248
- }
249
- const payload = Object.keys(customPayload).length > 0 ? { ...customPayload } : options.extractPayload ? options.extractPayload(event, element) : extractEventData(event, element);
250
- const engine = getEngine(element);
251
- if (engine) {
252
- try {
253
- engine.dispatchAction(actionName, payload);
254
- } catch (err) {
255
- log.error(`Error dispatching action "${actionName}":`, err);
113
+ // Select element values
114
+ if (element instanceof HTMLSelectElement) {
115
+ data.value = element.value;
116
+ data.selectedIndex = element.selectedIndex;
117
+ }
118
+ // Form data
119
+ if (event.type === "submit" && element instanceof HTMLFormElement) {
120
+ data.formData = new FormData(element);
121
+ }
122
+ return data;
123
+ }
124
+ /**
125
+ * Capitalize first letter of a string
126
+ */
127
+ function capitalize(str) {
128
+ return str.charAt(0).toUpperCase() + str.slice(1);
129
+ }
130
+ // ============================================================================
131
+ // Event Handler Factory
132
+ // ============================================================================
133
+ /**
134
+ * Create an event handler applicator with common boilerplate
135
+ */
136
+ function createEventHandler(eventType, options = {}) {
137
+ return (element, value) => {
138
+ const { actionName, payload: customPayload } = extractActionDetails(value);
139
+ if (!actionName) {
140
+ log.warn(`${eventType} requires an action reference starting with @, got:`, value);
141
+ return;
142
+ }
143
+ const disposables = getElementDisposables(element);
144
+ // Track that we've registered this event type
145
+ // The disposable stack handles cleanup automatically
146
+ const eventKey = `${eventType}:${actionName}`;
147
+ if (getRegisteredEvents(element).has(eventKey)) {
148
+ // Already registered - skip to avoid duplicates
149
+ // This can happen during re-renders
150
+ return;
256
151
  }
257
- }
152
+ registerEvent(element, eventKey);
153
+ // Create throttle state if needed
154
+ let throttleTimer = null;
155
+ // Create the event listener
156
+ const listener = (event) => {
157
+ // Handle throttling
158
+ if (options.throttleMs && throttleTimer) {
159
+ return;
160
+ }
161
+ if (options.throttleMs) {
162
+ throttleTimer = disposableTimeout(() => {
163
+ throttleTimer = null;
164
+ }, options.throttleMs);
165
+ }
166
+ // Handle preventDefault
167
+ if (options.preventDefault) {
168
+ event.preventDefault();
169
+ }
170
+ // Build payload
171
+ const payload = Object.keys(customPayload).length > 0
172
+ ? { ...customPayload }
173
+ : options.extractPayload
174
+ ? options.extractPayload(event, element)
175
+ : extractEventData(event, element);
176
+ // Dispatch to engine, catching any async rejections
177
+ const engine = getEngine(element);
178
+ if (engine) {
179
+ try {
180
+ engine.dispatchAction(actionName, payload);
181
+ }
182
+ catch (err) {
183
+ log.error(`Error dispatching action "${actionName}":`, err);
184
+ }
185
+ }
186
+ };
187
+ // Register the listener using disposable pattern
188
+ disposables.add(disposableListener(element, eventType, listener, {
189
+ passive: options.passive,
190
+ }));
191
+ // Clean up registered events tracking on dispose
192
+ disposables.addCallback(() => {
193
+ unregisterEvent(element, eventKey);
194
+ if (throttleTimer) {
195
+ throttleTimer.dispose();
196
+ }
197
+ });
258
198
  };
259
- disposables.add(disposableListener(element, eventType, listener, {
260
- passive: options.passive
261
- }));
262
- disposables.addCallback(() => {
263
- unregisterEvent(element, eventKey);
264
- if (throttleTimer) {
265
- throttleTimer.dispose();
266
- }
267
- });
268
- };
269
199
  }
200
+ /**
201
+ * Create a keyboard event handler that filters by key
202
+ */
270
203
  function createKeyHandler(defaultKey = "Enter") {
271
- return (element, value) => {
272
- const { actionName, payload: customPayload } = extractActionDetails(value);
273
- if (!actionName) {
274
- log.warn(`onKey requires an action reference starting with @, got:`, value);
275
- return;
276
- }
277
- const disposables = getElementDisposables2(element);
278
- const eventKey = `keydown:${actionName}:${defaultKey}`;
279
- if (getRegisteredEvents(element).has(eventKey)) {
280
- return;
281
- }
282
- registerEvent(element, eventKey);
283
- const targetKey = getKeyTarget(element) || defaultKey;
284
- const keyToMatch = targetKey.toLowerCase() === "return" ? "Enter" : targetKey;
285
- const listener = (event) => {
286
- const keyEvent = event;
287
- if (keyEvent.key !== keyToMatch) {
288
- return;
289
- }
290
- event.preventDefault();
291
- const target = event.target;
292
- const payload = Object.keys(customPayload).length > 0 ? { ...customPayload } : {
293
- type: event.type,
294
- timestamp: Date.now(),
295
- key: keyEvent.key,
296
- code: keyEvent.code,
297
- value: target.value,
298
- input: target.value,
299
- ctrlKey: keyEvent.ctrlKey,
300
- shiftKey: keyEvent.shiftKey,
301
- altKey: keyEvent.altKey,
302
- metaKey: keyEvent.metaKey
303
- };
304
- const engine = getEngine(element);
305
- if (engine) {
306
- engine.dispatchAction(actionName, payload);
307
- }
204
+ return (element, value) => {
205
+ const { actionName, payload: customPayload } = extractActionDetails(value);
206
+ if (!actionName) {
207
+ log.warn(`onKey requires an action reference starting with @, got:`, value);
208
+ return;
209
+ }
210
+ const disposables = getElementDisposables(element);
211
+ const eventKey = `keydown:${actionName}:${defaultKey}`;
212
+ if (getRegisteredEvents(element).has(eventKey)) {
213
+ return;
214
+ }
215
+ registerEvent(element, eventKey);
216
+ // Get target key from element data or use default
217
+ const targetKey = getKeyTarget(element) || defaultKey;
218
+ const keyToMatch = targetKey.toLowerCase() === "return" ? "Enter" : targetKey;
219
+ const listener = (event) => {
220
+ const keyEvent = event;
221
+ if (keyEvent.key !== keyToMatch) {
222
+ return;
223
+ }
224
+ event.preventDefault();
225
+ const target = event.target;
226
+ const payload = Object.keys(customPayload).length > 0
227
+ ? { ...customPayload }
228
+ : {
229
+ type: event.type,
230
+ timestamp: Date.now(),
231
+ key: keyEvent.key,
232
+ code: keyEvent.code,
233
+ value: target.value,
234
+ input: target.value,
235
+ ctrlKey: keyEvent.ctrlKey,
236
+ shiftKey: keyEvent.shiftKey,
237
+ altKey: keyEvent.altKey,
238
+ metaKey: keyEvent.metaKey,
239
+ };
240
+ const engine = getEngine(element);
241
+ if (engine) {
242
+ engine.dispatchAction(actionName, payload);
243
+ }
244
+ };
245
+ disposables.add(disposableListener(element, "keydown", listener));
246
+ disposables.addCallback(() => {
247
+ unregisterEvent(element, eventKey);
248
+ });
308
249
  };
309
- disposables.add(disposableListener(element, "keydown", listener));
310
- disposables.addCallback(() => {
311
- unregisterEvent(element, eventKey);
312
- });
313
- };
314
250
  }
251
+ /**
252
+ * Create a long-click/long-press handler
253
+ */
315
254
  function createLongClickHandler(thresholdMs = 500) {
316
- return (element, value) => {
317
- const { actionName, payload: customPayload } = extractActionDetails(value);
318
- if (!actionName) {
319
- log.warn(`onLongClick requires an action reference starting with @, got:`, value);
320
- return;
321
- }
322
- const disposables = getElementDisposables2(element);
323
- const eventKey = `longclick:${actionName}`;
324
- if (getRegisteredEvents(element).has(eventKey)) {
325
- return;
326
- }
327
- registerEvent(element, eventKey);
328
- let longClickTimer = null;
329
- const downListener = (event) => {
330
- const pointerEvent = event;
331
- longClickTimer = disposableTimeout(() => {
332
- const payload = Object.keys(customPayload).length > 0 ? { ...customPayload } : {
333
- type: "longclick",
334
- timestamp: Date.now(),
335
- clientX: pointerEvent.clientX,
336
- clientY: pointerEvent.clientY
337
- };
338
- const engine = getEngine(element);
339
- if (engine) {
340
- engine.dispatchAction(actionName, payload);
255
+ return (element, value) => {
256
+ const { actionName, payload: customPayload } = extractActionDetails(value);
257
+ if (!actionName) {
258
+ log.warn(`onLongClick requires an action reference starting with @, got:`, value);
259
+ return;
341
260
  }
342
- longClickTimer = null;
343
- }, thresholdMs);
344
- };
345
- const cancelListener = () => {
346
- if (longClickTimer) {
347
- longClickTimer.dispose();
348
- longClickTimer = null;
349
- }
261
+ const disposables = getElementDisposables(element);
262
+ const eventKey = `longclick:${actionName}`;
263
+ if (getRegisteredEvents(element).has(eventKey)) {
264
+ return;
265
+ }
266
+ registerEvent(element, eventKey);
267
+ let longClickTimer = null;
268
+ const downListener = (event) => {
269
+ const pointerEvent = event;
270
+ longClickTimer = disposableTimeout(() => {
271
+ const payload = Object.keys(customPayload).length > 0
272
+ ? { ...customPayload }
273
+ : {
274
+ type: "longclick",
275
+ timestamp: Date.now(),
276
+ clientX: pointerEvent.clientX,
277
+ clientY: pointerEvent.clientY,
278
+ };
279
+ const engine = getEngine(element);
280
+ if (engine) {
281
+ engine.dispatchAction(actionName, payload);
282
+ }
283
+ longClickTimer = null;
284
+ }, thresholdMs);
285
+ };
286
+ const cancelListener = () => {
287
+ if (longClickTimer) {
288
+ longClickTimer.dispose();
289
+ longClickTimer = null;
290
+ }
291
+ };
292
+ disposables.add(disposableListener(element, "pointerdown", downListener));
293
+ disposables.add(disposableListener(element, "pointerup", cancelListener));
294
+ disposables.add(disposableListener(element, "pointerleave", cancelListener));
295
+ disposables.addCallback(() => {
296
+ unregisterEvent(element, eventKey);
297
+ cancelListener();
298
+ });
350
299
  };
351
- disposables.add(disposableListener(element, "pointerdown", downListener));
352
- disposables.add(disposableListener(element, "pointerup", cancelListener));
353
- disposables.add(disposableListener(element, "pointerleave", cancelListener));
354
- disposables.addCallback(() => {
355
- unregisterEvent(element, eventKey);
356
- cancelListener();
357
- });
358
- };
359
300
  }
360
- var inputPayload = (event, element) => {
361
- const target = element;
362
- return {
363
- type: event.type,
364
- timestamp: Date.now(),
365
- value: target.value,
366
- input: target.value
367
- };
301
+ // ============================================================================
302
+ // Payload Extractors
303
+ // ============================================================================
304
+ const inputPayload = (event, element) => {
305
+ const target = element;
306
+ return {
307
+ type: event.type,
308
+ timestamp: Date.now(),
309
+ value: target.value,
310
+ input: target.value,
311
+ };
368
312
  };
369
- var scrollPayload = (_event, element) => {
370
- const scrollTop = element.scrollTop;
371
- const scrollHeight = element.scrollHeight;
372
- const clientHeight = element.clientHeight;
373
- const scrollPercentage = scrollHeight - clientHeight > 0 ? scrollTop / (scrollHeight - clientHeight) * 100 : 0;
374
- const nearBottom = scrollHeight - scrollTop - clientHeight < 100 || scrollPercentage > 90;
375
- return {
376
- type: "scroll",
377
- timestamp: Date.now(),
378
- scrollTop,
379
- scrollLeft: element.scrollLeft,
380
- scrollHeight,
381
- scrollWidth: element.scrollWidth,
382
- clientHeight,
383
- clientWidth: element.clientWidth,
384
- scrollPercentage: Math.round(scrollPercentage),
385
- nearBottom,
386
- atBottom: scrollHeight - scrollTop === clientHeight,
387
- atTop: scrollTop === 0
388
- };
313
+ const scrollPayload = (_event, element) => {
314
+ const scrollTop = element.scrollTop;
315
+ const scrollHeight = element.scrollHeight;
316
+ const clientHeight = element.clientHeight;
317
+ const scrollPercentage = scrollHeight - clientHeight > 0
318
+ ? (scrollTop / (scrollHeight - clientHeight)) * 100
319
+ : 0;
320
+ const nearBottom = scrollHeight - scrollTop - clientHeight < 100 || scrollPercentage > 90;
321
+ return {
322
+ type: "scroll",
323
+ timestamp: Date.now(),
324
+ scrollTop,
325
+ scrollLeft: element.scrollLeft,
326
+ scrollHeight,
327
+ scrollWidth: element.scrollWidth,
328
+ clientHeight,
329
+ clientWidth: element.clientWidth,
330
+ scrollPercentage: Math.round(scrollPercentage),
331
+ nearBottom,
332
+ atBottom: scrollHeight - scrollTop === clientHeight,
333
+ atTop: scrollTop === 0,
334
+ };
389
335
  };
390
- var focusPayload = (event, element) => ({
391
- type: event.type,
392
- timestamp: Date.now(),
393
- value: element.value ?? undefined
394
- });
395
- var mousePayload = (event, _element) => {
396
- const mouseEvent = event;
397
- return {
336
+ const focusPayload = (event, element) => ({
398
337
  type: event.type,
399
338
  timestamp: Date.now(),
400
- clientX: mouseEvent.clientX,
401
- clientY: mouseEvent.clientY
402
- };
339
+ value: element.value ?? undefined,
340
+ });
341
+ const mousePayload = (event, _element) => {
342
+ const mouseEvent = event;
343
+ return {
344
+ type: event.type,
345
+ timestamp: Date.now(),
346
+ clientX: mouseEvent.clientX,
347
+ clientY: mouseEvent.clientY,
348
+ };
403
349
  };
404
- var eventHandlers = {
405
- onClick: createEventHandler("click"),
406
- onPress: createEventHandler("click"),
407
- onChange: createEventHandler("change"),
408
- onSubmit: createEventHandler("submit", { preventDefault: true }),
409
- onInput: createEventHandler("input", { extractPayload: inputPayload }),
410
- onKey: createKeyHandler("Enter"),
411
- "onKey.key": (element, value) => {
412
- setKeyTarget(element, String(value));
413
- },
414
- "onKey.action": createKeyHandler("Enter"),
415
- onScroll: createEventHandler("scroll", {
416
- throttleMs: 100,
417
- passive: true,
418
- extractPayload: scrollPayload
419
- }),
420
- onLongClick: createLongClickHandler(500),
421
- onLongPress: createLongClickHandler(500),
422
- onFocus: createEventHandler("focus", { extractPayload: focusPayload }),
423
- onBlur: createEventHandler("blur", { extractPayload: focusPayload }),
424
- onMouseEnter: createEventHandler("mouseenter", { extractPayload: mousePayload }),
425
- onMouseLeave: createEventHandler("mouseleave", { extractPayload: mousePayload }),
426
- bind: (element, value) => {
427
- const bindPath = typeof value === "string" ? value : null;
428
- if (!bindPath)
429
- return;
430
- const disposables = getElementDisposables2(element);
431
- const eventKey = `bind:${bindPath}`;
432
- if (getRegisteredEvents(element).has(eventKey))
433
- return;
434
- registerEvent(element, eventKey);
435
- const hypenType = element.dataset?.hypenType;
436
- if (hypenType === "checkbox" || hypenType === "switch") {
437
- const input = element.querySelector('input[type="checkbox"]');
438
- if (!input)
439
- return;
440
- const listener = () => {
441
- const engine = getEngine(element);
442
- if (engine) {
443
- engine.dispatchAction("__hypen_bind", {
444
- path: bindPath,
445
- value: input.checked
446
- });
350
+ // ============================================================================
351
+ // Event Handlers Export
352
+ // ============================================================================
353
+ export const eventHandlers = {
354
+ // Basic click/press
355
+ onClick: createEventHandler("click"),
356
+ onPress: createEventHandler("click"), // Alias for mobile-style naming
357
+ // Form events
358
+ onChange: createEventHandler("change"),
359
+ onSubmit: createEventHandler("submit", { preventDefault: true }),
360
+ onInput: createEventHandler("input", { extractPayload: inputPayload }),
361
+ // Keyboard events
362
+ onKey: createKeyHandler("Enter"),
363
+ "onKey.key": (element, value) => {
364
+ // Store the target key for the action handler to use
365
+ setKeyTarget(element, String(value));
366
+ },
367
+ "onKey.action": createKeyHandler("Enter"),
368
+ // Scroll (throttled)
369
+ onScroll: createEventHandler("scroll", {
370
+ throttleMs: 100,
371
+ passive: true,
372
+ extractPayload: scrollPayload,
373
+ }),
374
+ // Long click/press
375
+ onLongClick: createLongClickHandler(500),
376
+ onLongPress: createLongClickHandler(500), // Alias for mobile-style naming
377
+ // Focus events
378
+ onFocus: createEventHandler("focus", { extractPayload: focusPayload }),
379
+ onBlur: createEventHandler("blur", { extractPayload: focusPayload }),
380
+ // Mouse hover events
381
+ onMouseEnter: createEventHandler("mouseenter", { extractPayload: mousePayload }),
382
+ onMouseLeave: createEventHandler("mouseleave", { extractPayload: mousePayload }),
383
+ // Two-way binding for .bind(@state.x)
384
+ bind: ((element, value) => {
385
+ const bindPath = typeof value === "string" ? value : null;
386
+ if (!bindPath)
387
+ return;
388
+ const disposables = getElementDisposables(element);
389
+ const eventKey = `bind:${bindPath}`;
390
+ if (getRegisteredEvents(element).has(eventKey))
391
+ return;
392
+ registerEvent(element, eventKey);
393
+ // Determine the target element, event type, and value extractor based on component type
394
+ const hypenType = element.dataset?.hypenType;
395
+ if (hypenType === "checkbox" || hypenType === "switch") {
396
+ // Checkbox/Switch: wrapper <label> containing <input type="checkbox">
397
+ const input = element.querySelector('input[type="checkbox"]');
398
+ if (!input)
399
+ return;
400
+ const listener = () => {
401
+ const engine = getEngine(element);
402
+ if (engine) {
403
+ engine.dispatchAction("__hypen_bind", {
404
+ path: bindPath,
405
+ value: input.checked,
406
+ });
407
+ }
408
+ };
409
+ disposables.add(disposableListener(input, "change", listener, { passive: true }));
447
410
  }
448
- };
449
- disposables.add(disposableListener(input, "change", listener, { passive: true }));
450
- } else if (element instanceof HTMLSelectElement) {
451
- const listener = () => {
452
- const engine = getEngine(element);
453
- if (engine) {
454
- engine.dispatchAction("__hypen_bind", {
455
- path: bindPath,
456
- value: element.value
457
- });
411
+ else if (element instanceof HTMLSelectElement) {
412
+ // Select: listen to change event, read .value
413
+ const listener = () => {
414
+ const engine = getEngine(element);
415
+ if (engine) {
416
+ engine.dispatchAction("__hypen_bind", {
417
+ path: bindPath,
418
+ value: element.value,
419
+ });
420
+ }
421
+ };
422
+ disposables.add(disposableListener(element, "change", listener, { passive: true }));
458
423
  }
459
- };
460
- disposables.add(disposableListener(element, "change", listener, { passive: true }));
461
- } else if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
462
- const listener = () => {
463
- const engine = getEngine(element);
464
- if (engine) {
465
- engine.dispatchAction("__hypen_bind", {
466
- path: bindPath,
467
- value: element.value
468
- });
424
+ else if (element instanceof HTMLInputElement ||
425
+ element instanceof HTMLTextAreaElement) {
426
+ // Input/Textarea: listen to input event, read .value
427
+ const listener = () => {
428
+ const engine = getEngine(element);
429
+ if (engine) {
430
+ engine.dispatchAction("__hypen_bind", {
431
+ path: bindPath,
432
+ value: element.value,
433
+ });
434
+ }
435
+ };
436
+ disposables.add(disposableListener(element, "input", listener, { passive: true }));
469
437
  }
470
- };
471
- disposables.add(disposableListener(element, "input", listener, { passive: true }));
472
- } else {
473
- log.warn(`.bind() is not supported on element type "${element.dataset?.hypenType || element.tagName}". ` + `Supported types: input, textarea, checkbox, switch, select.`);
474
- unregisterEvent(element, eventKey);
475
- return;
476
- }
477
- disposables.addCallback(() => unregisterEvent(element, eventKey));
478
- }
479
- };
480
- export {
481
- eventHandlers
438
+ else {
439
+ log.warn(`.bind() is not supported on element type "${element.dataset?.hypenType || element.tagName}". ` +
440
+ `Supported types: input, textarea, checkbox, switch, select.`);
441
+ unregisterEvent(element, eventKey);
442
+ return;
443
+ }
444
+ disposables.addCallback(() => unregisterEvent(element, eventKey));
445
+ }),
482
446
  };
483
-
484
- //# debugId=7925A87B5F9F454264756E2164756E21
447
+ //# sourceMappingURL=events.js.map