lilact 0.31.10 → 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.10",
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/",
@@ -453,7 +453,7 @@ class ComponentCore
453
453
 
454
454
  const prev_state = this.state, prev_props=this.props;
455
455
 
456
- if(this.element) {
456
+ if( this.element && ! (this?.component instanceof RootComponent) ) {
457
457
  this.updateElementProps(next_props);
458
458
  }
459
459
  this.props = next_props;
@@ -642,14 +642,16 @@ class ComponentCore
642
642
  for(let a in this.props) {
643
643
  const al = a.toLowerCase();
644
644
 
645
- if( !patch.hasOwnProperty(a) ) {
646
-
645
+ if( !patch.hasOwnProperty(a) || ( !a.startsWith('data-') && !a.startsWith('aria-') &&
646
+ (patch[a]===undefined || patch[a]===null || patch[a]==='' || patch[a]===false ) ) )
647
+ {
647
648
  if( events_set.has(al) ) {
648
649
  this.event_detachers[al]();
649
650
  }
650
651
  else {
651
652
  this.element.removeAttribute(a);
652
653
  }
654
+
653
655
  }
654
656
  }
655
657
 
@@ -731,29 +733,27 @@ class ComponentCore
731
733
 
732
734
  updateElementClass(patch=this.props)
733
735
  {
734
- let cn = patch?.className;
735
- cn ??= patch?.class ? patch.class : '';
736
+ let cn = patch?.className ?? patch?.class ?? '';
736
737
 
737
738
  if(this?.parent?.[CHILD_CLASS_ADDENDUM]) {
738
- cn += ' ' + this?.parent?.[CHILD_CLASS_ADDENDUM];
739
+ cn += ' ' + this.parent[CHILD_CLASS_ADDENDUM];
739
740
  }
740
-
741
+ cn = cn.trim();
742
+
743
+ for(const n of Array.from(this.element.classList)) {
744
+ if(cn.indexOf(n)===-1) {
745
+ this.element.classList.remove(n);
746
+ }
747
+ }
748
+
741
749
  if(cn.length>0) {
742
750
  cn = cn.split(/\s+/g);
743
- for(const n of Array.from(this.element.classList)) {
744
- if(cn.indexOf(n)===-1) {
745
- this.element.classList.remove(n);
746
- }
747
- }
748
751
  for(const n of cn) {
749
752
  if(n.length>0) {
750
753
  this.element.classList.add(n);
751
754
  }
752
755
  }
753
756
  }
754
- else {
755
- delete this.element.className;
756
- }
757
757
  }
758
758
 
759
759
 
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
  }