pawajs 1.3.7 → 1.3.8

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/README.md CHANGED
@@ -11,11 +11,11 @@ PawaJS is a JavaScript library designed for building dynamic user interfaces. It
11
11
 
12
12
  ## Features
13
13
 
14
- - **Declarative Rendering:** Use a clean, HTML-based template syntax with powerful directives (`if`, `for`, `on-event`, etc.) to describe your UI.
14
+ - **Declarative Rendering:** Use a clean, HTML-based template syntax with powerful directives (`if`, `for-each`, `on-event`, etc.) to describe your UI.
15
15
  - **Reactive State Management:** Effortlessly create reactive state that automatically updates the DOM when it changes using the `$state` utility.
16
16
  - **Component-Based Architecture:** Build encapsulated components that manage their own state, making your code more reusable and maintainable.
17
17
  - **Async Components:** First-class support for asynchronous components, allowing you to fetch data directly within your component definition.
18
- - **Efficient List Rendering:** Render lists of data with the `for` directive, including support for keyed updates for optimal performance.
18
+ - **Efficient List Rendering:** Render lists of data with the `for-each` directive, including support for keyed updates for optimal performance.
19
19
  - **Lifecycle Hooks:** Tap into a component's lifecycle with `mount` and `unmount` directives, or the `runEffect` hook for more complex side effects.
20
20
  - **Context API:** Pass data through the component tree without having to pass props down manually at every level.
21
21
  - **Server-Side Rendering (SSR) Isomorphic architecture:** PawaJS is built with SSR in mind, featuring a "resuming" mechanism to efficiently continue server-rendered HTML on the client.
@@ -34,10 +34,10 @@ CDN
34
34
 
35
35
  ```html
36
36
  <head>
37
- <script type="module" href="https://cdn.jsdelivr.net/npm/pawajs@1.2.0/cdn/index.js"></script>
38
- </head>
37
+ </head>
39
38
  <body>
40
39
  <div id="app"></div>
40
+ <script type="module" src="https://cdn.jsdelivr.net/npm/pawajs@1.2.0/cdn/index.js"></script>
41
41
  <script type="module">
42
42
  window.$pawa.pawaStartApp(document.getElementById('app'));
43
43
  </script>
@@ -263,12 +263,12 @@ makes pawajs engine to wait before removing the element until the animation/tran
263
263
  <input value="@{user.value.name}" on-input="user.value.value = e.target.value">
264
264
  </div>
265
265
  ```
266
- #### `for`
266
+ #### `for-each`
267
267
  For rendering lists from an array. Use `for-key` to give each element a unique identity, which helps PawaJS optimize rendering.
268
268
 
269
269
  ```html
270
270
  <ul>
271
- <li for="todo, i in todos.value" for-key="{{todo.id}}">
271
+ <li for-each="todo, i in todos.value" for-key="{{todo.id}}">
272
272
  <span>@{i + 1}. @{todo.text}</span>
273
273
  </li>
274
274
  </ul>
@@ -342,6 +342,7 @@ useValidateComponent(TodoList, {
342
342
  - `useContext(contextObject)` & `setContext()`: A mechanism for providing and consuming data throughout a component tree.
343
343
  - `useRef()`: Creates a reference object that can be attached to a DOM element using the `ref` directive.
344
344
  - `useValidateComponent(Component, rules)`: Defines validation rules for a component's props.
345
+ - `useServer()`: Returns `{ setServerData, getServerData }` for handling server-side data serialization and client-side retrieval in SSR applications.
345
346
 
346
347
  ---
347
348
 
package/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  export interface PawaElement extends HTMLElement {
2
3
  _running: boolean;
3
4
  _context: any;
@@ -75,6 +76,42 @@ export interface PawaElement extends HTMLElement {
75
76
  setProps(): void;
76
77
  }
77
78
 
79
+ export interface PawaComment extends Comment {
80
+ _index: number | null;
81
+ _el: Comment;
82
+ _setCoveringElement: (el: any) => void;
83
+ _data: Record<string, any>;
84
+ _terminateEffects: Set<Function>;
85
+ _run: any;
86
+ _coveringElement: any;
87
+ _setData: (obj: any) => void;
88
+ _removeSiblings: (endComment: any) => void;
89
+ _controlComponent: boolean;
90
+ _componentTerminate: Function | null;
91
+ _componentElement: any;
92
+ _setComponentOut: any;
93
+ _deleteEffects: () => void;
94
+ _remove: () => void;
95
+ _terminateByComponent: (endComment: any) => void;
96
+ _forKey: string | null;
97
+ _forIndex: any;
98
+ _setKey: (arg: any) => void;
99
+ _endComment: Comment | null;
100
+ _keyRemover: (callback?: Function, firstElement?: boolean) => Promise<void>;
101
+ _resetForKeyElement: () => DocumentFragment;
102
+ _deletKey: () => void;
103
+ forKeyResetElement(): DocumentFragment;
104
+ keyRemoveElement(callback?: Function, firstElement?: boolean): Promise<void>;
105
+ deleteKey(): void;
106
+ setForKey(arg: any): void;
107
+ terminateEffects(): void;
108
+ setCoveringElement(el: any): void;
109
+ setData(obj: any): void;
110
+ terminate(endComment: any): void;
111
+ remove(): void;
112
+ removeSiblings(endComment: any): void;
113
+ }
114
+
78
115
  export interface AttriPlugin {
79
116
  startsWith?: string;
80
117
  fullName?: string;
@@ -149,6 +186,8 @@ export function getDependentAttribute(): Set<string>;
149
186
 
150
187
  export function getPawaAttributes(): Set<string>;
151
188
 
189
+ export function getPrimaryDirectives(): Set<string>;
190
+
152
191
  export function setError(params: { error: any }): void;
153
192
 
154
193
  /**
@@ -159,10 +198,15 @@ export function RegisterComponent(...args: (string | Function)[]): void;
159
198
 
160
199
  /**
161
200
  * Runs a side effect or lifecycle hook.
162
- * @param {() => void | (() => void)} callback - Effect function, optionally returning cleanup.
201
+ * @param {(comment:PawaComment) => void | (() => void)} callback - Effect function,comment for component hacking and optionally returning cleanup .
163
202
  * @param {any[] | object | number | null} [deps] - Dependencies or hook type (null=mount).
203
+ * # number - beforemount
204
+ * # null - onMount
205
+ * # array[...deps] - dependency reactive
206
+ * # object<{component:true}|any element from ref> - read alone effect tied to either the component or element
164
207
  */
165
- export function runEffect(callback: () => void | (() => void), deps?: any[] | object | number | null): void;
208
+ export function runEffect(callback: (comment:PawaComment) => void | (() => void), deps?: any[] | object | number | null): void;
209
+
166
210
 
167
211
  export interface PropValidation {
168
212
  strict?: boolean;
@@ -203,16 +247,18 @@ export function useInnerContext<T=any>(): T;
203
247
  /**
204
248
  * Tells pawa-ssr to serialize the children prop.
205
249
  * Then pawajs adds it into the component unpon re-execution
206
- * @returns {()=>void}
250
+ * @returns {void}
207
251
  */
208
- export function accessChild(): (()=>void);
252
+ export function accessChild(): void;
209
253
  /**
210
- * Tells pawa-ssr to serialized data from useInsert.
211
- * Then pawajs continuity model de-serializes it and adds to the rendering context
212
- * @returns {void}
213
- * Note: meant for server-only component
254
+ * Tells pawa-ssr to serialized data.
255
+ * Then pawajs continuity model de-serializes it and adds to the rendering context or getServerData hook
256
+ * @returns {{setServerData:(data:object)=>void,getServerData:()=>any}}
214
257
  */
215
- export function useServer(): (() => void) | undefined;
258
+ export function useServer<T = Record<string, any>>(): {
259
+ setServerData: (data: T) => void;
260
+ getServerData: () => T;
261
+ };
216
262
 
217
263
  /**
218
264
  * Stores the component instance into the returned $async hook.
package/index.js CHANGED
@@ -271,6 +271,7 @@ let stateContext = {
271
271
  _transportContext: {},
272
272
  _reactiveProps: {},
273
273
  _template: '',
274
+ _serializedData:{},
274
275
  _static: [],
275
276
  }
276
277
 
@@ -298,8 +299,8 @@ const setPrimaryAttibute = (...name) => {
298
299
 
299
300
  export const getPrimaryDirectives=()=>primaryDirective
300
301
 
301
- setPrimaryAttibute('if', 'else-if', 'for', 'else','switch','case','default','case','key')
302
- setPawaAttributes('if', 'else-if', 'for', 'else', 'mount',
302
+ setPrimaryAttibute('if', 'else-if', 'for-each', 'else','switch','case','default','case','key')
303
+ setPawaAttributes('if', 'else-if', 'for-each', 'else', 'mount',
303
304
  'unmount', 'forKey', 'state-', 'on-', 'out-','key','switch','case','default')
304
305
  export const getDependentAttribute = () => dependentPawaAttribute
305
306
  export const getPawaAttributes = () => {
@@ -494,10 +495,26 @@ export const accessChild = () => {
494
495
  return serverInstance.accessChild?.()
495
496
  }
496
497
  }
497
- /**@returns {()=>void} - let's component serialized useInsert data on server*/
498
+ /**@returns {()=>{getServerData:()=>any,setServerData:(data:object)=>void}} - let's component serialized data on server*/
498
499
  export const useServer = () => {
499
500
  if (client) {
500
- return
501
+ if (stateContext && stateContext._hasRun === false) {
502
+ const component=stateContext._template
503
+ const data=stateContext._serializedData
504
+ /**
505
+ * Meant to ne used on server rendering
506
+ */
507
+ const setServerData=(data={})=>{
508
+ console.warn( `meant to be used on server-only at ${component}`)
509
+ }
510
+ /**
511
+ * Get serialized data from server during pawajs continuity
512
+ */
513
+ const getServerData=()=>data
514
+ return {setServerData,getServerData}
515
+ }else{
516
+ return {data:null}
517
+ }
501
518
  } else {
502
519
  return serverInstance.useServer?.()
503
520
  }
@@ -583,6 +600,7 @@ export const setStateContext = (context) => {
583
600
  }
584
601
  stateContext._transportContext = {}
585
602
  stateContext._static = []
603
+ stateContext._serializedData={}
586
604
  stateContext._formerContext = formerStateContext
587
605
  stateContext._reactiveProps = {}
588
606
  stateContext._template = ''
@@ -843,8 +861,7 @@ const mainAttribute = (el, exp) => {
843
861
  }
844
862
  attrMap.set(exp.name, exp.value);
845
863
  el._preRenderAvoid.push(exp.name)
846
- const removeAttribute = new Set()
847
- removeAttribute.add('disabled')
864
+ const booleanAttributes = new Set(['checked', 'selected', 'disabled', 'readonly', 'required', 'multiple']);
848
865
  el._mainAttribute[exp.name] = exp.value
849
866
  el._checkStatic()
850
867
  let enter=false
@@ -861,26 +878,40 @@ const mainAttribute = (el, exp) => {
861
878
  };
862
879
  const values = keys.map((key) => resolvePath(key, el._context));
863
880
 
881
+ const hasExpression = regex.test(value);
882
+ regex.lastIndex = 0;
883
+
864
884
  value = value.replace(regex, (match, expression) => {
865
885
  if (checkKeywordsExistence(el._staticContext, expression)) {
866
886
  return ''
867
887
  } else {
868
888
  const func = new Function(...keys, `return ${expression}`);
869
- isBoolean = func(...values)
870
- if (typeof isBoolean !== 'boolean') {
871
- return isBoolean
889
+ const result = func(...values)
890
+ isBoolean = result
891
+ if (typeof result !== 'boolean') {
892
+ return result
872
893
  }else{
873
894
  return ''
874
895
  }
875
896
  }
876
897
  });
877
898
 
878
- if (removeAttribute.has(exp.name)) {
879
- if (isBoolean) {
899
+ if (booleanAttributes.has(exp.name)) {
900
+ const boolValue = hasExpression ? !!isBoolean : value.toLowerCase() !== 'false';
901
+ const propName = exp.name === 'readonly' ? 'readOnly' : exp.name;
902
+
903
+ if (propName in el) {
904
+ el[propName] = boolValue;
905
+ }
906
+
907
+ if (boolValue) {
880
908
  el.setAttribute(exp.name, '');
881
909
  } else {
882
- el.removeAttribute(exp.name)
910
+ el.removeAttribute(exp.name);
883
911
  }
912
+ } else if (exp.name === 'value' && 'value' in el) {
913
+ el.value = value;
914
+ el.setAttribute(exp.name, value);
884
915
  } else {
885
916
  if (exp.name === 'class' && !enter) {
886
917
  requestAnimationFrame(()=>{
@@ -943,7 +974,11 @@ const textContentHandler = (el, isName) => {
943
974
  return String(func(...values));
944
975
  }
945
976
  });
946
- textNode.nodeValue = value;
977
+ if(el.tagName === 'TEXTAREA'){
978
+ el.value=value
979
+ }else{
980
+ textNode.nodeValue = value;
981
+ }
947
982
 
948
983
  });
949
984
  } catch (error) {
@@ -971,7 +1006,7 @@ const template = (el,notRender, attr) => {
971
1006
 
972
1007
  const directives = {
973
1008
  if: If,
974
- for: For,
1009
+ 'for-each': For,
975
1010
  else:(el)=>{el._running =true},
976
1011
  case:(el)=>{el._running =true},
977
1012
  default:(el)=>{el._running =true},
@@ -1060,25 +1095,22 @@ export const render = (el, contexts = {}, notRender, isName) => {
1060
1095
  }
1061
1096
  else if (attr.name.startsWith('c-c-')) {
1062
1097
  stopResume.stop = true
1063
-
1064
- component(el, true, attr, notRender, stopResume)
1098
+ component(el, true, attr, notRender, stopResume)// component continuity
1065
1099
  } else if (attr.name.startsWith('c-at-')) {
1066
- resumer.resume_attribute?.(el, attr, notRender)
1100
+ resumer.resume_attribute?.(el, attr, notRender) //attribute continuity
1067
1101
  } else if (attr.name.startsWith('c-$-')) {
1068
- resumer.resume_state?.(el, attr, notRender)
1069
- } else if (attr.name.startsWith('c-t')) {
1102
+ resumer.resume_state?.(el, attr, notRender) //state continuity
1103
+ } else if (attr.name.startsWith('c-t')) {//text continuity
1070
1104
  resumer.resume_text(el, attr, isName)
1071
1105
  } else if (attr.name.startsWith('c-if-')) {
1072
- directives['if'](el, attr, stateContext, true, notRender, stopResume)
1106
+ directives['if'](el, attr, stateContext, true, notRender, stopResume) // condition continuity
1073
1107
  } else if (attr.name === 'c-for') {
1074
- directives['for'](el, attr, stateContext, true, notRender, stopResume)
1108
+ directives['for-each'](el, attr, stateContext, true, notRender, stopResume) // for-each continuity
1075
1109
  }else if (attr.name.startsWith('c-key-')) {
1076
- directives['key'](el, attr, stateContext, true, notRender, stopResume)
1110
+ directives['key'](el, attr, stateContext, true, notRender, stopResume)// key continuity
1077
1111
  }
1078
1112
  else if (attr.name.startsWith('c-sw-')) {
1079
- directives['switch'](el, attr, stateContext, true, notRender, stopResume)
1080
- } else if (attr.name === 'c-for') {
1081
- directives['for'](el, attr, stateContext, true, notRender, stopResume)
1113
+ directives['switch'](el, attr, stateContext, true, notRender, stopResume)// switch continuity
1082
1114
  } else if (fullNamePlugin.has(attr.name)) {
1083
1115
  if (externalPlugin[attr.name]) {
1084
1116
  const plugin = externalPlugin[attr.name]
@@ -1137,6 +1169,7 @@ export const render = (el, contexts = {}, notRender, isName) => {
1137
1169
  const number = { notRender: null, index: null }
1138
1170
  Array.from(el.children).forEach((child, index) => {
1139
1171
  number.index = index
1172
+ if (number.notRender !== null && index <= number.notRender) return
1140
1173
  render(child, context, number, isName)
1141
1174
  })
1142
1175
  el._callMount()
package/merger/for.js CHANGED
@@ -40,7 +40,7 @@ export const merger_for = (el, stateContext, attr, arrayName, arrayItem, indexes
40
40
  [indexes]: index,
41
41
  ...context
42
42
  }
43
- const newElement = el._attrElement('for')
43
+ const newElement = el._attrElement('for-each')
44
44
  newElement.setAttribute('data-for-index', index)
45
45
  processNode(newElement, itemContext)
46
46
  div.appendChild(newElement)
@@ -167,7 +167,7 @@ export const merger_for = (el, stateContext, attr, arrayName, arrayItem, indexes
167
167
  [indexes]: index,
168
168
  ...context
169
169
  }
170
- const newElement = el._attrElement('for')
170
+ const newElement = el._attrElement('for-each')
171
171
  const keyComment = document.createComment(`key=${newElement.getAttribute('for-key') || index}`)
172
172
  const endKeyComment = document.createComment('end key')
173
173
  PawaComment.Element(keyComment)
@@ -197,6 +197,7 @@ export const merger_for = (el, stateContext, attr, arrayName, arrayItem, indexes
197
197
  } else {
198
198
  if(!resume)return
199
199
  if(once)return
200
+ const number={notRender:null,index:null}
200
201
  elementArray.forEach((keyComment) => {
201
202
  if(keyComment.nextElementSibling === null) return
202
203
  const context = el._context
package/merger/if.js CHANGED
@@ -102,10 +102,11 @@ export const merger_if=(el,attr,stateContext,resume=false,{comment,endComment,ch
102
102
  stateContext._hasRun = false
103
103
  keepContext(stateContext)
104
104
  }
105
- const number={notRender:null,index:null}
105
+
106
+ const number = { notRender: null, index: null }
106
107
  children.forEach((value, index) => {
107
- number.index=index
108
- if(number.notRender && index >= number.notRender) return
108
+ number.index = index
109
+ if (number.notRender !== null && index <= number.notRender) return
109
110
  render(value,el._context,number)
110
111
  })
111
112
  stateContext._hasRun=true
package/merger/key.js CHANGED
@@ -51,7 +51,7 @@ export const merger_key=(el,attr,stateContext,resume=false,{comment,endComment,c
51
51
  const number={notRender:null,index:null}
52
52
  children.forEach((value, index) => {
53
53
  number.index=index
54
- if(number.notRender && index >= number.notRender) return
54
+ if (number.notRender !== null && index <= number.notRender) return
55
55
  render(value,el._context,number)
56
56
  })
57
57
  stateContext._hasRun=true
package/merger/switch.js CHANGED
@@ -106,8 +106,8 @@ export const merger_switch=(el,attr,stateContext,resume=false,{comment,endCommen
106
106
  }
107
107
  const number={notRender:null,index:null}
108
108
  children.forEach((value, index) => {
109
- number.index=index
110
- if(number.notRender && index >= number.notRender) return
109
+ number.index = index
110
+ if (number.notRender !== null && index <= number.notRender) return
111
111
  render(value,el._context,number)
112
112
  })
113
113
  stateContext._hasRun=true
@@ -1,6 +1,7 @@
1
1
  import {propsValidator, setPawaDevError, pawaWayRemover, checkKeywordsExistence, sanitizeTemplate } from '../utils.js';
2
2
  import {PawaElement,PawaComment} from '../pawaElement.js';
3
3
  import {keepContext,render} from '../index.js'
4
+ import {createEffect} from '../reactive.js'
4
5
  export const normal_component=(el,stateContext,setStateContext,mapsPlugin,formerStateContext,pawaContext,stateWatch)=>{
5
6
  const compoBeforeCall=mapsPlugin.compoBeforeCall
6
7
  const compoAfterCall=mapsPlugin.compoAfterCall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pawajs",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "type":"module",
5
5
  "description": "pawajs library (html runtime) for easily building web ui, enhancing html element, micro frontend etc ",
6
6
  "main": "index.js",
package/pawaComponent.js CHANGED
@@ -41,6 +41,10 @@ class PawaComponent {
41
41
  isMount: [],
42
42
  isUnMount: [],
43
43
  };
44
+ /**
45
+ * Data serialized from pawa-ssr
46
+ */
47
+ this._serializedData={}
44
48
  /**
45
49
  * Map for non-reactive internal state (currently unused).
46
50
  * @type {Map}
package/power.js CHANGED
@@ -244,7 +244,7 @@ export const For = (el, attr, stateContext,resume=false,notRender,stopResume) =>
244
244
  getEndComment(comment,setEndComment,id,children,'isFor')
245
245
  const numberIfChildren=notRender.index + children.length - 1
246
246
  notRender.notRender=numberIfChildren
247
- dataElement=comment.nextSibling
247
+ dataElement=document.querySelector(`[p\\:store-for="${id}"]`);
248
248
  el.removeAttribute(attr.name)
249
249
  dataElement.remove()
250
250
  resumer.resume_for?.(el,attr,stateContext,{comment,endComment,id,children,dataElement})
package/resumer.js CHANGED
@@ -14,8 +14,6 @@ export const resumer={
14
14
  */
15
15
  export const setResumer=(resume)=>{
16
16
  for(const [key,value] of Object.entries(resume)){
17
- if(resumer[key] === null && resumer[key] !== undefined){
18
17
  resumer[key]=value
19
- }
20
18
  }
21
19
  }
package/server.js CHANGED
@@ -13,8 +13,6 @@ const serverInstance={
13
13
  export const getServerInstance=()=>serverInstance
14
14
  export const setServer=(obj={})=>{
15
15
  for (const [key,value] of Object.entries(obj)) {
16
- if (serverInstance[key] === null) {
17
16
  serverInstance[key]=value
18
- }
19
17
  }
20
18
  }