pawajs 1.5.7 → 2.0.1

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/index.d.ts CHANGED
@@ -78,6 +78,52 @@ export interface PawaElement extends HTMLElement {
78
78
  safeEval(context: any, expr: string, error?: string, element?: boolean): any;
79
79
  }
80
80
 
81
+ export interface PawaDev {
82
+ tool: boolean;
83
+ errors: any[];
84
+ totalEffect: number;
85
+ errorState: any;
86
+ components: Set<any>;
87
+ renderCount: number;
88
+ performance: {
89
+ renderTime: number[];
90
+ effectTime: number[];
91
+ componentTime: number[];
92
+ start: number;
93
+ end: number;
94
+ };
95
+ _originalStyles: Map<any, any>;
96
+ listeners: Set<Function>;
97
+ highlightElement(el: HTMLElement): void;
98
+ unhighlightElement(el: HTMLElement): void;
99
+ subscribe(cb: (event: { type: string; data: any }) => void): () => void;
100
+ emit(type: string, data: any): void;
101
+ setError(options?: {
102
+ el?: HTMLElement;
103
+ msg?: string;
104
+ directives?: string;
105
+ stack?: string;
106
+ template?: string;
107
+ warn?: boolean;
108
+ }): void;
109
+ clearErrors(): void;
110
+ getSnapshot(): {
111
+ renderCount: number;
112
+ totalEffect: number;
113
+ performance: any;
114
+ errors: any[];
115
+ componentCount: number;
116
+ };
117
+ logRender(c: any, t: any): void;
118
+ logEffect(e: any, t: any): void;
119
+ logComponent(n: any, t: any): void;
120
+ }
121
+
122
+ declare global {
123
+ var __pawaDev: PawaDev;
124
+ var __pawaStream: (element: HTMLElement, context: any, statecontext?: any) => void;
125
+ }
126
+
81
127
  export interface PawaComment extends Comment {
82
128
  _index: number | null;
83
129
  _el: Comment;
@@ -163,7 +209,11 @@ export function pluginsMap(): {
163
209
  pawaAttributes: Set<string>;
164
210
  allowAsProp: Set<string>;
165
211
  };
166
-
212
+ export function PawaCustomEvent(eventType:string,handler:(el:PawaElement,modifiers:Set<string>,options:{
213
+ capture: Boolean,
214
+ once: Boolean,
215
+ passive: Boolean
216
+ },execute:(e:EventListener)=>void)=>void):void;
167
217
  export const escapePawaAttribute: Set<string>;
168
218
  export const dependentPawaAttribute: Set<string>;
169
219
 
package/index.js CHANGED
@@ -49,6 +49,15 @@ const createPawaDev = () => {
49
49
  dev.errors = [];
50
50
  dev.emit('clear', null);
51
51
  },
52
+ getSnapshot() {
53
+ return {
54
+ renderCount: dev.renderCount,
55
+ totalEffect: dev.totalEffect,
56
+ performance: dev.performance,
57
+ errors: dev.errors,
58
+ componentCount: dev.components.size
59
+ };
60
+ },
52
61
  logRender(c, t) { dev.renderCount++; /* preserve logging */ },
53
62
  logEffect(e, t) { dev.totalEffect++; /* preserve */ },
54
63
  logComponent(n, t) { /* preserve */ }
@@ -66,6 +75,7 @@ if (typeof globalThis !== 'undefined') {
66
75
  global.__pawaDev = pawaDevInstance;
67
76
  }
68
77
 
78
+ export const customEventMap = new Map()
69
79
  const compoBeforeCall = new Set()
70
80
  const compoAfterCall = new Set()
71
81
  const renderBeforePawa = new Set()
@@ -188,7 +198,8 @@ export const lazyComponents=new Map()
188
198
  /**
189
199
  * @type {PawaComponent}
190
200
  */
191
- let stateContext = {
201
+
202
+ let stateContext = {
192
203
  _hasRun: false,
193
204
  _formerContext: null,
194
205
  _insert: {},
@@ -517,7 +528,10 @@ export const setContext = () => {
517
528
  }
518
529
 
519
530
  }
520
-
531
+ export const PawaCustomEvent=(eventName,handler)=>{
532
+ if (customEventMap.has(eventName) && !__pawaDev.tool)return
533
+ customEventMap.set(eventName, handler)
534
+ }
521
535
  /**
522
536
  * Get parent Context
523
537
  * @param {object} context
@@ -975,17 +989,26 @@ const component = (el, resume = false, attr, notRender, stopResume) => {
975
989
  const mainAttribute = (el, exp) => {
976
990
  const attrMap = new Map();
977
991
  if (el._running) return
978
- // Store original attribute value
992
+
993
+ // Check if attribute starts with @ (shorthand for reactive attributes)
994
+ const isAtAttr = exp.name.startsWith('@');
995
+ const targetName = isAtAttr ? exp.name.slice(1) : exp.name;
996
+
979
997
  if (el._hasForOrIf()) {
980
998
  return
981
999
  }
982
1000
  if (el._componentName) {
983
1001
  return
984
1002
  }
1003
+
1004
+ if (isAtAttr) {
1005
+ el.removeAttribute(exp.name);
1006
+ }
1007
+
985
1008
  attrMap.set(exp.name, exp.value);
986
- el._preRenderAvoid.push(exp.name)
1009
+ el._preRenderAvoid.push(targetName)
987
1010
  const booleanAttributes = new Set(['checked', 'selected', 'disabled', 'readonly', 'required', 'multiple']);
988
- el._mainAttribute[exp.name] = exp.value
1011
+ el._mainAttribute[targetName] = exp.value
989
1012
  el._checkStatic()
990
1013
  let enter=false
991
1014
  const context=el._context
@@ -1014,30 +1037,32 @@ const mainAttribute = (el, exp) => {
1014
1037
  }
1015
1038
  });
1016
1039
 
1017
- if (booleanAttributes.has(exp.name)) {
1040
+ if (booleanAttributes.has(targetName)) {
1018
1041
  const boolValue = hasExpression ? !!isBoolean : value.toLowerCase() !== 'false';
1019
- const propName = exp.name === 'readonly' ? 'readOnly' : exp.name;
1042
+ const propName = targetName === 'readonly' ? 'readOnly' : targetName;
1020
1043
 
1021
1044
  if (propName in el) {
1022
1045
  el[propName] = boolValue;
1023
1046
  }
1024
1047
 
1025
1048
  if (boolValue) {
1026
- el.setAttribute(exp.name, value);
1049
+ el.setAttribute(targetName, value);
1027
1050
  } else {
1028
- el.removeAttribute(exp.name);
1051
+ el.removeAttribute(targetName);
1029
1052
  }
1030
- } else if (exp.name === 'value' && 'value' in el) {
1053
+ } else if (targetName === 'value' && 'value' in el) {
1031
1054
  el.value = value;
1032
- el.setAttribute(exp.name, value);
1055
+ el.setAttribute(targetName, value);
1033
1056
  } else {
1034
- if ((exp.name === 'class' || exp.name === 'style') && enter) {
1057
+ if ((targetName === 'class' || targetName === 'style') && enter) {
1035
1058
  requestAnimationFrame(()=>{
1036
- el.setAttribute(exp.name, value);
1059
+ el.setAttribute(targetName, value);
1037
1060
  })
1038
1061
  enter=true
1039
1062
  }else{
1040
- el.setAttribute(exp.name, value);
1063
+ el.setAttribute(targetName, value);
1064
+ // Toggle enter to true after initial set so subsequent reactive updates use requestAnimationFrame
1065
+ if (targetName === 'class' || targetName === 'style') enter = true;
1041
1066
  }
1042
1067
  }
1043
1068
  } catch (error) {
@@ -1196,7 +1221,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
1196
1221
  directives[attr.name](el, attr, stateContext)
1197
1222
  } else if (attr.name.startsWith('on-') && !isAcomponent) {
1198
1223
  event(el, attr, stateContext)
1199
- } else if (attr.value.includes('@{') && !attr.name.startsWith('c-at-')) {
1224
+ } else if ((attr.value.includes('@{') || attr.name.startsWith('@')) && !attr.name.startsWith('c-at-')) {
1200
1225
  mainAttribute(el, attr, isName)
1201
1226
  } else if (attr.name.startsWith('state-')) {
1202
1227
  States(el, attr, getCurrentContext())
@@ -1230,6 +1255,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
1230
1255
  directives['switch'](el, attr, stateContext, true, notRender, stopResume)// switch continuity
1231
1256
  } else if (fullNamePlugin.has(attr.name)) {
1232
1257
  if (externalPlugin[attr.name]) {
1258
+ if (el._componentName && !attr.name.startsWith('c-'))return
1233
1259
  const plugin = externalPlugin[attr.name]
1234
1260
  try {
1235
1261
  if (typeof plugin !== 'function') {
@@ -1244,6 +1270,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
1244
1270
  } else if (startAttribute) {
1245
1271
  const name = startObject[attr.name]
1246
1272
  if (externalPlugin[name]) {
1273
+ if (el._componentName && !attr.name.startsWith('c-'))return
1247
1274
  const plugin = externalPlugin[name]
1248
1275
  try {
1249
1276
  if (typeof plugin !== 'function') {
@@ -1361,10 +1388,12 @@ const Pawa = {
1361
1388
  $state,
1362
1389
  pawaStartApp,
1363
1390
  useAsync,
1391
+ forwardProps,
1364
1392
  useInnerContext,
1365
1393
  RegisterComponent,
1366
1394
  runEffect,
1367
- html
1395
+ html,
1396
+ PawaCustomEvent
1368
1397
  }
1369
1398
 
1370
1399
  export default Pawa
@@ -3,6 +3,10 @@ import {PawaElement,PawaComment} from '../pawaElement.js';
3
3
  import {keepContext,render, HmrComponentMap } from '../index.js'
4
4
  import {createEffect} from '../reactive.js'
5
5
  export const normal_component=(el,stateContext,setStateContext,mapsPlugin,formerStateContext,pawaContext,stateWatch)=>{
6
+ // Checks if the content is an SVG fragment (graphic elements) without a root <svg> tag.
7
+ // We exclude 'svg' from the match because a root <svg> tag can be parsed safely inside a <div>.
8
+ const isSvgFragment = (str) => /^\s*<(path|circle|rect|line|polyline|polygon|ellipse|g|defs|symbol|use|image|text|animate|mask|pattern|clipPath|linearGradient|radialGradient|filter)/i.test(str);
9
+
6
10
  const compoBeforeCall=mapsPlugin.compoBeforeCall
7
11
  const compoAfterCall=mapsPlugin.compoAfterCall
8
12
  const endComment = document.createComment(`end ${el.tagName}`)
@@ -61,7 +65,7 @@ export const normal_component=(el,stateContext,setStateContext,mapsPlugin,former
61
65
  console.error(error.message)
62
66
  }
63
67
  }
64
- let div =el._compoToSvg?document.createElementNS('http://www.w3.org/2000/svg', 'svg'): document.createElement('div')
68
+ let div
65
69
  el._componentTerminate=() => {
66
70
  comment._terminateByComponent(endComment)
67
71
  }
@@ -147,6 +151,10 @@ export const normal_component=(el,stateContext,setStateContext,mapsPlugin,former
147
151
  }
148
152
 
149
153
  // stateContext._hasRun=true
154
+ div = isSvgFragment(compo)
155
+ ? document.createElementNS('http://www.w3.org/2000/svg', 'svg')
156
+ : document.createElement('div');
157
+
150
158
  div.innerHTML = compo;
151
159
  if (component?._insert) {
152
160
  Object.assign(el._context,component._insert)
@@ -173,10 +181,15 @@ export const normal_component=(el,stateContext,setStateContext,mapsPlugin,former
173
181
  }
174
182
  const context=el._context
175
183
  const getAsChild=()=>{
176
- const asChild=div.firstElementChild
177
- if (splitAndAdd(asChild?.tagName|| '') === 'ASCHILD') {
178
- const getChildren=asChild.firstElementChild
179
- Array.from(asChild.attributes).forEach(attr=>{
184
+ const asChild=div
185
+ if (el._asChild) {
186
+ const divFirst=div.firstElementChild
187
+ if (isSvgFragment(children)) {
188
+ div=document.createElementNS('http://www.w3.org/2000/svg', 'svg')
189
+ }
190
+ div.innerHTML=children
191
+ const getChildren=div.firstElementChild
192
+ Array.from(divFirst.attributes).forEach(attr=>{
180
193
  if (getChildren.hasAttribute(attr.name)) {
181
194
  let attrName=getChildren.getAttribute(attr.name)
182
195
  attrName=attr.value +' '+attrName
@@ -184,9 +197,7 @@ export const normal_component=(el,stateContext,setStateContext,mapsPlugin,former
184
197
  }else{
185
198
  getChildren.setAttribute(attr.name, attr.value)
186
199
  }
187
- })
188
- asChild.remove()
189
- div.appendChild(getChildren)
200
+ })
190
201
  }
191
202
  }
192
203
  const propsSetter=()=>{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pawajs",
3
- "version": "1.5.7",
3
+ "version": "2.0.1",
4
4
  "type":"module",
5
5
  "description": "pawajs library (reactive web runtime) for easily building web ui, enhancing html element, micro frontend etc ",
6
6
  "main": "index.js",
package/pawaElement.js CHANGED
@@ -252,7 +252,7 @@ export class PawaElement {
252
252
  }
253
253
  const tag = this._el.tagName
254
254
  try {
255
- if (components.has(splitAndAdd(tag)) || this._lazy) {
255
+ if (components.has(splitAndAdd(tag)) || this._lazy && !this._el.hasAttribute('data-prevent:c')) {
256
256
  this._elementType='component'
257
257
  this._componentOrTemplate=true
258
258
  this._deCompositionElement=true
@@ -304,14 +304,14 @@ export class PawaElement {
304
304
  const {allowAsProp}=pluginsMap()
305
305
  const dependAttribute=getDependentAttribute()
306
306
  this._attributes.forEach((attr) => {
307
- if (attr.name === 'svg') {
308
- this._compoToSvg = true
307
+ if (attr.name.startsWith('state-')) {
309
308
  return
310
- }else if(attr.name === 'aschild' || attr.name === 'as-child'){
309
+ }else if(attr.name === 'aschild' || attr.name === 'as-child' || attr.name === ':as-child'){
311
310
  this._asChild = true
312
311
  return
313
312
  }
314
- if (!attr.name.startsWith(':') && (!pawaAttribute.has(attr.name) && !dependAttribute.has(attr.name) )) {
313
+ const primaryAttribute=['if','else','else-if','key','for-each','case','switch','s-default']
314
+ if (!attr.name.startsWith(':') && !primaryAttribute.includes(attr.name)) {
315
315
  let name=''
316
316
  if (attr.name.startsWith('-')) {
317
317
  name=attr.name.slice(1)
@@ -353,7 +353,7 @@ export class PawaElement {
353
353
  }
354
354
 
355
355
  this._restProps[attr.name]={name:attr.name,value:attr.value}
356
- }else if(!pawaAttribute.has(attr.name) && attr.name.startsWith(':')){
356
+ }else if(!primaryAttribute.includes(attr.name) && attr.name.startsWith(':')){
357
357
 
358
358
  const propsName=attr.name.slice(1)
359
359
  if(attr.value === '') attr.value="true";
package/power.js CHANGED
@@ -7,6 +7,7 @@ import {normal_If} from './normal/If.js'
7
7
  import {normal_For} from './normal/For.js'
8
8
  import {resumer} from './resumer.js'
9
9
  import { normal_key } from './normal/Key.js';
10
+ import { customEventMap } from './index.js';
10
11
 
11
12
  // Shared helper to check common event modifiers
12
13
  const checkCommonModifiers = (e, modifiers) => {
@@ -102,11 +103,14 @@ const checkKeyModifiers = (e, modifiers, eventType) => {
102
103
  const processEventExecution = (el, attrName, modifiers, callback, eventType, directiveName,context) => {
103
104
  const execute = () => {
104
105
  try { callback(); }
105
- catch (error) { setPawaDevError({ message: `Error from ${directiveName}-${eventType} directive ${error.message}`, error, template: el._template }); }
106
+ catch (error) {
107
+ console.log(error,'at event')
108
+ setPawaDevError({ message: `Error from ${directiveName}-${eventType} directive ${error.message}`, error, template: el._template }); }
106
109
  };
107
110
  if (!el._eventTimers) el._eventTimers = {};
108
111
  const delay = parseInt([...modifiers].find(m => /^\d+$/.test(m)) || '300');
109
112
  if (modifiers.has('debounce')) {
113
+
110
114
  const timerKey = `${attrName}_debounce`;
111
115
  clearTimeout(el._eventTimers[timerKey]);
112
116
  el._eventTimers[timerKey] = setTimeout(execute, delay);
@@ -308,7 +312,35 @@ export const event = (el, attr, stateContext) => {
308
312
  };
309
313
 
310
314
  const target = modifiers.has('window') ? window : el;
311
- target.addEventListener(eventType, handler, options);
315
+
316
+ // Wrapper to ensure custom events respect standard action modifiers
317
+ const wrappedExecute = (e) => {
318
+ // Apply common modifiers first
319
+ if (!checkCommonModifiers(e, modifiers)) return;
320
+ if (!checkKeyModifiers(e, modifiers, eventType)) return;
321
+
322
+ // Apply target modifiers
323
+ if (modifiers.has('self') && e.target !== el) return;
324
+ if (modifiers.has('not-self') && e.target === el) return;
325
+
326
+ // Apply form modifiers (if applicable, though less common for custom events)
327
+ if (modifiers.has('dirty') && e.target && !e.target.value) return;
328
+ if (modifiers.has('pristine') && e.target && e.target.value) return;
329
+ if (modifiers.has('valid') && e.target && !e.target.checkValidity?.()) return;
330
+ if (modifiers.has('invalid') && e.target && e.target.checkValidity?.()) return;
331
+
332
+ // Apply action modifiers
333
+ if (modifiers.has('prevent')) e.preventDefault?.();
334
+ if (modifiers.has('stop')) e.stopPropagation?.();
335
+ processEventExecution(el, attr.name, modifiers, () => executeEvent(e), eventType, 'on');
336
+ };
337
+
338
+ if (customEventMap.has(eventType)) {
339
+ const custom=customEventMap.get(eventType)
340
+ custom(el, modifiers, options, wrappedExecute)
341
+ return
342
+ }
343
+
312
344
 
313
345
  el._MountFunctions.push(() => target.addEventListener(eventType, handler, options));
314
346
  el._setUnMount(() => {
@@ -515,6 +547,32 @@ export const documentEvent = (el, attr) => {
515
547
  };
516
548
 
517
549
  const target = modifiers.has('window') ? window : document;
550
+
551
+ // Wrapper to ensure custom events respect standard action and "out" modifiers
552
+ const wrappedExecute = (e) => {
553
+ // Apply common modifiers first
554
+ if (!checkCommonModifiers(e, modifiers)) return;
555
+ if (!checkKeyModifiers(e, modifiers, eventType)) return;
556
+
557
+ // ========== TARGET MODIFIERS (OUTSIDE LOGIC) ==========
558
+ // For out- events, 'self' means we ignore the event if it happens inside the element or on the element itself.
559
+ if (modifiers.has('self') && (e.target === el || el.contains(e.target))) return;
560
+ if (modifiers.has('not-self') && e.target === el) return;
561
+
562
+ // ========== ACTION MODIFIERS ==========
563
+ if (modifiers.has('prevent')) e.preventDefault?.();
564
+ if (modifiers.has('stop')) e.stopPropagation?.();
565
+
566
+ // ========== EXECUTION ==========
567
+ processEventExecution(el, attr.name, modifiers, () => executeOutEvent(e), eventType, 'out');
568
+ };
569
+
570
+ if (customEventMap.has(eventType)) {
571
+ const custom = customEventMap.get(eventType);
572
+ custom(el, modifiers, options, wrappedExecute);
573
+ return;
574
+ }
575
+
518
576
  el._MountFunctions.push(() => target.addEventListener(eventType, handler, options))
519
577
  const unMount = () => target.removeEventListener(eventType, handler, options);
520
578
  el._setUnMount(unMount)
package/server.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export const isServer: () => boolean;
2
+ export const getServerInstance: () => {
3
+ useInsert: null;
4
+ useInnerContext: null;
5
+ setContext: null;
6
+ useContext: null;
7
+ $state: null;
8
+ accessChild: null;
9
+ useServer: null;
10
+ useAsync: null;
11
+ forwardPops: null;
12
+ };
13
+ export const setServer: (obj?: {}) => void;