lilact 0.31.11 → 0.31.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lilact",
3
- "version": "0.31.11",
3
+ "version": "0.31.12",
4
4
  "description": "A Little React/JSX Runtime Implementation for Browser",
5
5
  "repository": "github:arashkazemi/lilact",
6
6
  "homepage": "https://arashkazemi.github.io/lilact/",
package/src/events.jsx CHANGED
@@ -29,269 +29,91 @@
29
29
  */
30
30
 
31
31
 
32
- // Minimal shims (small and explicit)
33
- if (typeof Element !== 'undefined' && !Element.prototype.matches) {
34
- Element.prototype.matches = Element.prototype.msMatchesSelector ||
35
- Element.prototype.webkitMatchesSelector ||
36
- function(s) {
37
- var matches = (this.document || this.ownerDocument).querySelectorAll(s);
38
- var i = matches.length;
39
- while (--i >= 0 && matches.item(i) !== this) {}
40
- return i > -1;
41
- };
42
- }
43
-
44
- if (typeof Event !== 'undefined' && !Event.prototype.composedPath) {
45
- Event.prototype.composedPath = function() {
46
- var path = [];
47
- var el = this.target;
48
- while (el) {
49
- path.push(el);
50
- el = el.parentElement;
51
- }
52
- path.push(window);
53
- return path;
54
- };
55
- }
56
-
57
- // Event pool for reuse
58
- const _pool = [];
59
- const MAX_POOL_SIZE = 10;
60
- const POINTER_TYPES = ["mouse", "pen", "touch"];
61
-
62
- export function createSyntheticEvent(nativeEvent, currentTarget)
63
- {
64
- const e = _pool.length ? _pool.pop() : {};
65
-
66
- e.nativeEvent = nativeEvent;
67
- e.type = nativeEvent.type;
68
- e.target = nativeEvent.target || nativeEvent.srcElement || null;
69
- e.currentTarget = currentTarget || nativeEvent.currentTarget || null;
70
- e.timeStamp = nativeEvent.timeStamp || Date.now();
71
-
72
- // Standard flags
73
- e.defaultPrevented = !!nativeEvent.defaultPrevented;
74
- e.isPropagationStopped = false;
75
- e.isPersistent = false;
76
-
77
- // Common DOM meta when present
78
- e.bubbles = !!nativeEvent.bubbles;
79
- e.cancelable = !!nativeEvent.cancelable;
80
- e.composed = !!nativeEvent.composed;
81
- e.detail = nativeEvent.detail;
82
-
83
- e.relatedTarget =
84
- nativeEvent.relatedTarget ||
85
- (nativeEvent.fromElement ? nativeEvent.fromElement : null) ||
86
- (nativeEvent.toElement ? nativeEvent.toElement : null) ||
87
- null;
88
-
89
- // Modifier keys
90
- e.altKey = !!nativeEvent.altKey;
91
- e.ctrlKey = !!nativeEvent.ctrlKey;
92
- e.metaKey = !!nativeEvent.metaKey;
93
- e.shiftKey = !!nativeEvent.shiftKey;
94
-
95
- // preventDefault / stopPropagation
96
- e.isDefaultPrevented = () => e.defaultPrevented;
97
- e.preventDefault = () => {
98
- if (nativeEvent.preventDefault) nativeEvent.preventDefault();
99
- e.defaultPrevented = true;
32
+ export function createSyntheticEvent(nativeEvent, currentTarget) {
33
+ let propagationStopped = false;
34
+
35
+ const event = {
36
+ nativeEvent,
37
+ type: nativeEvent.type,
38
+ target: nativeEvent.target || nativeEvent.srcElement || null,
39
+ currentTarget: currentTarget || nativeEvent.currentTarget || null,
40
+ timeStamp: nativeEvent.timeStamp ?? Date.now(),
41
+ defaultPrevented: !!nativeEvent.defaultPrevented,
42
+
43
+ isDefaultPrevented() {
44
+ return event.defaultPrevented;
45
+ },
46
+
47
+ isPropagationStopped() {
48
+ return propagationStopped;
49
+ },
50
+
51
+ preventDefault() {
52
+ nativeEvent.preventDefault?.();
53
+ event.defaultPrevented = true;
54
+ },
55
+
56
+ stopPropagation() {
57
+ nativeEvent.stopPropagation?.();
58
+ propagationStopped = true;
59
+ },
60
+
61
+ // Like React 17+ web SyntheticEvents: no pooling, persist is a no-op.
62
+ persist() {},
63
+
64
+ isPersistent() {
65
+ return true;
66
+ },
100
67
  };
101
68
 
102
- e.stopPropagation = () => {
103
- if (nativeEvent.stopPropagation) nativeEvent.stopPropagation();
104
- e.isPropagationStopped = true;
105
- };
106
-
107
- e.persist = () => { e.isPersistent = true; };
108
-
109
- // Key/mouse/pointer related
110
- e.key = nativeEvent.key || null;
111
- e.code = nativeEvent.code || null;
112
- e.which = nativeEvent.which ?? nativeEvent.keyCode ?? null;
113
-
114
- // Mouse / Pointer button state normalization
115
- // - "button" often is 0/1/2 for mouse; for pointer events it's also present.
116
- // - "buttons" is a bitmask of which buttons are down (often important for drag).
117
- e.button = nativeEvent.button ?? null;
118
- e.buttons = nativeEvent.buttons ?? null;
119
-
120
- // Pointer identity + type (critical for multi-touch / pointer capture)
121
- e.pointerId = nativeEvent.pointerId ?? null;
122
- e.pointerType = nativeEvent.pointerType ?? null;
123
- e.isPrimary = nativeEvent.isPrimary ?? null;
124
-
125
- // Coordinates: React/SyntheticEvent uses these directly (React provides them too)
126
- e.clientX = nativeEvent.clientX ?? 0;
127
- e.clientY = nativeEvent.clientY ?? 0;
128
- e.screenX = nativeEvent.screenX ?? 0;
129
- e.screenY = nativeEvent.screenY ?? 0;
130
-
131
- // pageX/pageY might be derived; keep if present
132
- // (Using pageX/pageY only if you need it; most drag uses clientX/Y.)
133
- e.pageX = nativeEvent.pageX ?? null;
134
- e.pageY = nativeEvent.pageY ?? null;
135
-
136
- // movementX/movementY exist on some mouse events; harmless if absent
137
- e.movementX = nativeEvent.movementX ?? 0;
138
- e.movementY = nativeEvent.movementY ?? 0;
139
-
140
- // Pressure/tilt (Pointer Events)
141
- e.pressure = nativeEvent.pressure ?? null;
142
- e.tiltX = nativeEvent.tiltX ?? null;
143
- e.tiltY = nativeEvent.tiltY ?? null;
144
- e.width = nativeEvent.width ?? null;
145
- e.height = nativeEvent.height ?? null;
146
-
147
- // If the native event has a pointer capture API, you can forward bind/capture calls.
148
- // (These are methods on the EventTarget, not on the event, but keeping fields is enough.)
149
- e.pointerEventsSupported = POINTER_TYPES.includes(e.pointerType);
150
-
151
- // For input-like events normalize value and checked
152
- try {
153
- const tgt = e.target;
154
- e.value = tgt && ("value" in tgt) ? tgt.value : undefined;
155
- e.checked = tgt && ("checked" in tgt) ? tgt.checked : undefined;
156
-
157
- // Some input events have selectionStart/selectionEnd etc.
158
- e.selectionStart = tgt && ("selectionStart" in tgt) ? tgt.selectionStart : undefined;
159
- e.selectionEnd = tgt && ("selectionEnd" in tgt) ? tgt.selectionEnd : undefined;
160
- } catch (err) {
161
- e.value = undefined;
162
- e.checked = undefined;
163
- e.selectionStart = undefined;
164
- e.selectionEnd = undefined;
165
- }
166
-
167
- // Touch lists (useful for touch events; sometimes pointerType === "touch" uses pointer events instead)
168
- // Keep references only if they exist.
169
- e.touches = nativeEvent.touches || null;
170
- e.targetTouches = nativeEvent.targetTouches || null;
171
- e.changedTouches = nativeEvent.changedTouches || null;
172
-
173
- // composedPath helper
174
- e.path = typeof nativeEvent.composedPath === "function"
175
- ? nativeEvent.composedPath()
176
- : [e.target];
177
-
178
- // Additional keyboard extras when present
179
- e.repeat = nativeEvent.repeat ?? false;
180
- e.location = nativeEvent.location ?? 0;
181
-
182
- return e;
69
+ // Forward event-specific properties (such as WheelEvent.deltaX/deltaY)
70
+ // and native methods that are not explicitly defined above.
71
+ return new Proxy(event, {
72
+ get(target, property, receiver) {
73
+ if (Reflect.has(target, property)) {
74
+ return Reflect.get(target, property, receiver);
75
+ }
76
+
77
+ const value = Reflect.get(nativeEvent, property, nativeEvent);
78
+ return typeof value === "function" ? value.bind(nativeEvent) : value;
79
+ },
80
+
81
+ has(target, property) {
82
+ return Reflect.has(target, property) || property in nativeEvent;
83
+ },
84
+ });
183
85
  }
184
86
 
185
- export function releaseSyntheticEvent(e)
186
- {
187
- if (e && !e.isPersistent) {
188
- e.nativeEvent = null;
189
- e.type = null;
190
- e.target = null;
191
- e.currentTarget = null;
192
- e.timeStamp = 0;
193
-
194
- e.defaultPrevented = false;
195
- e.isPropagationStopped = false;
196
-
197
- e.isPersistent = false;
198
-
199
- e.isDefaultPrevented = null;
200
- e.preventDefault = null;
201
- e.stopPropagation = null;
202
- e.persist = null;
203
-
204
- e.bubbles = false;
205
- e.cancelable = false;
206
- e.composed = false;
207
- e.detail = undefined;
208
- e.relatedTarget = null;
209
-
210
- // modifier keys
211
- e.altKey = false;
212
- e.ctrlKey = false;
213
- e.metaKey = false;
214
- e.shiftKey = false;
215
-
216
- // pointer/mouse fields
217
- e.key = null;
218
- e.code = null;
219
- e.which = null;
220
-
221
- e.button = null;
222
- e.buttons = null;
223
- e.pointerId = null;
224
- e.pointerType = null;
225
- e.isPrimary = null;
226
-
227
- e.clientX = 0;
228
- e.clientY = 0;
229
- e.screenX = 0;
230
- e.screenY = 0;
231
- e.pageX = null;
232
- e.pageY = null;
233
-
234
- e.movementX = 0;
235
- e.movementY = 0;
236
-
237
- e.pressure = null;
238
- e.tiltX = null;
239
- e.tiltY = null;
240
- e.width = null;
241
- e.height = null;
242
-
243
- // input-like
244
- e.value = undefined;
245
- e.checked = undefined;
246
- e.selectionStart = undefined;
247
- e.selectionEnd = undefined;
248
-
249
- // touch lists
250
- e.touches = null;
251
- e.targetTouches = null;
252
- e.changedTouches = null;
253
-
254
- e.path = null;
255
- e.repeat = false;
256
- e.location = 0;
257
-
258
- if (_pool.length < MAX_POOL_SIZE) _pool.push(e);
259
- }
87
+ export function releaseSyntheticEvent(event) {
88
+ // Clear currentTarget after dispatch, but don't invalidate the event.
89
+ if (event) event.currentTarget = null;
260
90
  }
261
91
 
92
+ export function wrapListener(fn, opts = {}) {
93
+ const { stopPropagationOnTrueReturn = false } = opts;
262
94
 
263
- // Main wrapper factory
264
- // fn: function(syntheticEvent) { ... }
265
- // opts: { capture: bool, passive: bool, once: bool, stopPropagationOnTrueReturn: bool }
266
- export function wrapListener(fn, opts = {})
267
- {
268
- const { stopPropagationOnTrueReturn = false } = opts;
269
-
270
- return function handler(nativeEvent) {
271
-
272
- const currentTarget = this || nativeEvent.currentTarget || null;
273
- const sEvent = createSyntheticEvent(nativeEvent, currentTarget);
95
+ return function handler(nativeEvent) {
96
+ const event = createSyntheticEvent(
97
+ nativeEvent,
98
+ this || nativeEvent.currentTarget || null
99
+ );
274
100
 
275
- try {
276
- const result = fn(sEvent);
101
+ try {
102
+ const result = fn(event);
277
103
 
278
- // If user returns true and opted in, stop propagation
279
- if (stopPropagationOnTrueReturn && result === true) {
280
- sEvent.stopPropagation();
281
- }
282
- } finally {
283
- // Release back to pool unless persisted
284
- releaseSyntheticEvent(sEvent);
285
- }
286
- };
104
+ if (stopPropagationOnTrueReturn && result === true) {
105
+ event.stopPropagation();
106
+ }
107
+ } finally {
108
+ releaseSyntheticEvent(event);
109
+ }
110
+ };
287
111
  }
288
112
 
289
- // Convenience: add/remove wrapper-managed listener
290
- export function addWrappedEventListener(target, type, fn, options = {})
291
- {
292
- const handler = wrapListener(fn, options);
113
+ export function addWrappedEventListener(target, type, fn, options = {}) {
114
+ const handler = wrapListener(fn, options);
115
+
116
+ target.addEventListener(type, handler, options);
293
117
 
294
- target.addEventListener(type, handler, options);
295
- // Return a remover
296
- return () => target.removeEventListener(type, handler, options);
118
+ return () => target.removeEventListener(type, handler, options);
297
119
  }