pawajs 1.5.7 → 2.0.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.
- package/index.d.ts +5 -1
- package/index.js +43 -15
- package/normal/component.js +19 -8
- package/package.json +1 -1
- package/pawaElement.js +6 -6
- package/power.js +34 -2
package/index.d.ts
CHANGED
|
@@ -163,7 +163,11 @@ export function pluginsMap(): {
|
|
|
163
163
|
pawaAttributes: Set<string>;
|
|
164
164
|
allowAsProp: Set<string>;
|
|
165
165
|
};
|
|
166
|
-
|
|
166
|
+
export function PawaCustomEvent(eventType:string,handler:(el:PawaElement,modifiers:Set,options:{
|
|
167
|
+
capture: Boolean,
|
|
168
|
+
once: Boolean,
|
|
169
|
+
passive: Boolean
|
|
170
|
+
},execute:(e:EventListener)=>void)=>{})
|
|
167
171
|
export const escapePawaAttribute: Set<string>;
|
|
168
172
|
export const dependentPawaAttribute: Set<string>;
|
|
169
173
|
|
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()
|
|
@@ -517,7 +527,10 @@ export const setContext = () => {
|
|
|
517
527
|
}
|
|
518
528
|
|
|
519
529
|
}
|
|
520
|
-
|
|
530
|
+
export const PawaCustomEvent=(eventName,handler)=>{
|
|
531
|
+
if (customEventMap.has(eventName) && !__pawaDev.tool)return
|
|
532
|
+
customEventMap.set(eventName, handler)
|
|
533
|
+
}
|
|
521
534
|
/**
|
|
522
535
|
* Get parent Context
|
|
523
536
|
* @param {object} context
|
|
@@ -975,17 +988,26 @@ const component = (el, resume = false, attr, notRender, stopResume) => {
|
|
|
975
988
|
const mainAttribute = (el, exp) => {
|
|
976
989
|
const attrMap = new Map();
|
|
977
990
|
if (el._running) return
|
|
978
|
-
|
|
991
|
+
|
|
992
|
+
// Check if attribute starts with @ (shorthand for reactive attributes)
|
|
993
|
+
const isAtAttr = exp.name.startsWith('@');
|
|
994
|
+
const targetName = isAtAttr ? exp.name.slice(1) : exp.name;
|
|
995
|
+
|
|
979
996
|
if (el._hasForOrIf()) {
|
|
980
997
|
return
|
|
981
998
|
}
|
|
982
999
|
if (el._componentName) {
|
|
983
1000
|
return
|
|
984
1001
|
}
|
|
1002
|
+
|
|
1003
|
+
if (isAtAttr) {
|
|
1004
|
+
el.removeAttribute(exp.name);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
985
1007
|
attrMap.set(exp.name, exp.value);
|
|
986
|
-
el._preRenderAvoid.push(
|
|
1008
|
+
el._preRenderAvoid.push(targetName)
|
|
987
1009
|
const booleanAttributes = new Set(['checked', 'selected', 'disabled', 'readonly', 'required', 'multiple']);
|
|
988
|
-
el._mainAttribute[
|
|
1010
|
+
el._mainAttribute[targetName] = exp.value
|
|
989
1011
|
el._checkStatic()
|
|
990
1012
|
let enter=false
|
|
991
1013
|
const context=el._context
|
|
@@ -1014,30 +1036,32 @@ const mainAttribute = (el, exp) => {
|
|
|
1014
1036
|
}
|
|
1015
1037
|
});
|
|
1016
1038
|
|
|
1017
|
-
if (booleanAttributes.has(
|
|
1039
|
+
if (booleanAttributes.has(targetName)) {
|
|
1018
1040
|
const boolValue = hasExpression ? !!isBoolean : value.toLowerCase() !== 'false';
|
|
1019
|
-
const propName =
|
|
1041
|
+
const propName = targetName === 'readonly' ? 'readOnly' : targetName;
|
|
1020
1042
|
|
|
1021
1043
|
if (propName in el) {
|
|
1022
1044
|
el[propName] = boolValue;
|
|
1023
1045
|
}
|
|
1024
1046
|
|
|
1025
1047
|
if (boolValue) {
|
|
1026
|
-
el.setAttribute(
|
|
1048
|
+
el.setAttribute(targetName, value);
|
|
1027
1049
|
} else {
|
|
1028
|
-
el.removeAttribute(
|
|
1050
|
+
el.removeAttribute(targetName);
|
|
1029
1051
|
}
|
|
1030
|
-
} else if (
|
|
1052
|
+
} else if (targetName === 'value' && 'value' in el) {
|
|
1031
1053
|
el.value = value;
|
|
1032
|
-
el.setAttribute(
|
|
1054
|
+
el.setAttribute(targetName, value);
|
|
1033
1055
|
} else {
|
|
1034
|
-
if ((
|
|
1056
|
+
if ((targetName === 'class' || targetName === 'style') && enter) {
|
|
1035
1057
|
requestAnimationFrame(()=>{
|
|
1036
|
-
|
|
1058
|
+
el.setAttribute(targetName, value);
|
|
1037
1059
|
})
|
|
1038
1060
|
enter=true
|
|
1039
1061
|
}else{
|
|
1040
|
-
el.setAttribute(
|
|
1062
|
+
el.setAttribute(targetName, value);
|
|
1063
|
+
// Toggle enter to true after initial set so subsequent reactive updates use requestAnimationFrame
|
|
1064
|
+
if (targetName === 'class' || targetName === 'style') enter = true;
|
|
1041
1065
|
}
|
|
1042
1066
|
}
|
|
1043
1067
|
} catch (error) {
|
|
@@ -1196,7 +1220,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
|
|
|
1196
1220
|
directives[attr.name](el, attr, stateContext)
|
|
1197
1221
|
} else if (attr.name.startsWith('on-') && !isAcomponent) {
|
|
1198
1222
|
event(el, attr, stateContext)
|
|
1199
|
-
} else if (attr.value.includes('@{') && !attr.name.startsWith('c-at-')) {
|
|
1223
|
+
} else if ((attr.value.includes('@{') || attr.name.startsWith('@')) && !attr.name.startsWith('c-at-')) {
|
|
1200
1224
|
mainAttribute(el, attr, isName)
|
|
1201
1225
|
} else if (attr.name.startsWith('state-')) {
|
|
1202
1226
|
States(el, attr, getCurrentContext())
|
|
@@ -1230,6 +1254,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
|
|
|
1230
1254
|
directives['switch'](el, attr, stateContext, true, notRender, stopResume)// switch continuity
|
|
1231
1255
|
} else if (fullNamePlugin.has(attr.name)) {
|
|
1232
1256
|
if (externalPlugin[attr.name]) {
|
|
1257
|
+
if (el._componentName && !attr.name.startsWith('c-'))return
|
|
1233
1258
|
const plugin = externalPlugin[attr.name]
|
|
1234
1259
|
try {
|
|
1235
1260
|
if (typeof plugin !== 'function') {
|
|
@@ -1244,6 +1269,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
|
|
|
1244
1269
|
} else if (startAttribute) {
|
|
1245
1270
|
const name = startObject[attr.name]
|
|
1246
1271
|
if (externalPlugin[name]) {
|
|
1272
|
+
if (el._componentName && !attr.name.startsWith('c-'))return
|
|
1247
1273
|
const plugin = externalPlugin[name]
|
|
1248
1274
|
try {
|
|
1249
1275
|
if (typeof plugin !== 'function') {
|
|
@@ -1361,10 +1387,12 @@ const Pawa = {
|
|
|
1361
1387
|
$state,
|
|
1362
1388
|
pawaStartApp,
|
|
1363
1389
|
useAsync,
|
|
1390
|
+
forwardProps,
|
|
1364
1391
|
useInnerContext,
|
|
1365
1392
|
RegisterComponent,
|
|
1366
1393
|
runEffect,
|
|
1367
|
-
html
|
|
1394
|
+
html,
|
|
1395
|
+
PawaCustomEvent
|
|
1368
1396
|
}
|
|
1369
1397
|
|
|
1370
1398
|
export default Pawa
|
package/normal/component.js
CHANGED
|
@@ -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
|
|
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
|
|
177
|
-
if (
|
|
178
|
-
const
|
|
179
|
-
|
|
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
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
|
|
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
|
-
|
|
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(!
|
|
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) {
|
|
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
|
-
|
|
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(() => {
|