cradova 2.2.3 → 2.3.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.
Files changed (3) hide show
  1. package/README.md +276 -110
  2. package/dist/index.js +239 -222
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,11 +49,12 @@ Cradova follows the [VJS specification](https://github.com/fridaycandour/cradova
49
49
 
50
50
  Cradova is aimed to be fast and simple with and fewer abstractions and yet easily composable.
51
51
 
52
- Cradova does't rely on visual DOM or diff algorithms to manage the DOM, instead, State management is done more elegantly with a simple predictive model, manually and easily with all the speed.
52
+ Cradova is not built on visual DOM or diff algorithms.
53
+ Instead, State management is done more elegantly with a simple predictive model, simple and easy with all the speed.
53
54
 
54
- ### is this a big benefit?
55
+ ## Is this a big benefit?
55
56
 
56
- Undoubtedly, this provides a significant advantage. You can experience it firsthand and decide for yourself.
57
+ Undoubtedly, this provides a significant advantage. You can experience it firsthand and decide.
57
58
 
58
59
  Cradova has already been utilized in multiple production projects, and we will continuously update this page to showcase our advancements as we keep improving.
59
60
 
@@ -83,12 +84,10 @@ npm i cradova
83
84
 
84
85
  Many aspects of Cradova are not reflected in the following example. More functionality will be entailed in future docs.
85
86
 
86
- Here's an example of create a basic component in Cradova:
87
+ ## A basic component in Cradova:
87
88
 
88
89
  ```js
89
- // cradova v2.0.0 comes with all html tags prebuilt and fully typed
90
- // this gives your app more performance gain.
91
- import _, { h1 } from "cradova";
90
+ import { div, h1 } from "cradova";
92
91
 
93
92
  function Hello(name) {
94
93
  return h1("Hello " + name, {
@@ -99,88 +98,56 @@ function Hello(name) {
99
98
  });
100
99
  }
101
100
 
102
- // document fragment empty cradova call _()
103
-
104
- const html = _(Hello("peter"), Hello("joe"));
101
+ const html = div(Hello("peter"), Hello("joe"));
105
102
 
106
103
  document.body.append(html);
107
104
  ```
108
105
 
109
- ```js
110
- // regular example
111
- import _ from "cradova";
112
-
113
- function Hello(name) {
114
- return _("h1", "Hello " + name);
115
- }
116
-
117
- const html = _(Hello("peter"), Hello("joe"));
118
-
119
- document.body.append(html);
120
- ```
106
+ ## working with state:
121
107
 
122
- ## Using Screen
108
+ this a collection of basic examples
109
+ you can choose any that best suite what problem you want to solve
123
110
 
124
111
  ```js
125
- import _, { Screen, Router } from "cradova";
112
+ import _, {
113
+ button,
114
+ createSignal,
115
+ Ref,
116
+ reference,
117
+ h1,
118
+ br,
119
+ div,
120
+ } from "../dist/index.js";
126
121
 
127
- function HelloMessage(name) {
128
- // an effect run once after screen renders
129
- this.effect(() => {
130
- const name = new Promise((res) => {
131
- res("friday");
132
- });
133
- this.updateState(await name)
122
+ function Hello(name) {
123
+ return h1("Hello " + name, {
124
+ className: "title",
125
+ style: {
126
+ color: "grey",
127
+ },
134
128
  });
135
- // effects can be used to make api calls needed for the page
136
- return _("div", "Hello " + name);
137
129
  }
138
130
 
131
+ const html = div(Hello("peter"), Hello("joe"));
132
+
133
+ // reference (not state)
134
+
135
+ function typingExample() {
136
+ const re = new reference();
137
+ return _(
138
+ "div",
139
+ input({
140
+ oninput() {
141
+ re.text.innerText = this.value;
142
+ },
143
+ placeholder: "typing simulation",
144
+ }),
145
+ p(" no thing typed yet!", { reference: re.bindAs("text") })
146
+ );
147
+ }
139
148
 
140
- /*
141
-
142
- when using router and screens
143
-
144
- cradova will create a div with data-cra-id=cradova-app-wrapper
145
-
146
- if it already exist cradova will use it instead
147
-
148
- so if you want to use your own mount point then create a div with data-cra-id="cradova-app-wrapper"
149
-
150
- */
151
-
152
- const home = new Screen({
153
- name: "hello page", // page title
154
- template: HelloMessage,
155
- ...
156
- });
157
-
158
- Router.route("/", home);
159
-
160
- // navigates to that page
161
- // Router.navigate("/home", data, force);
162
- // get the page ready in the background
163
- // Router.packageScreen("/home");
164
- // get route params for this page
165
- // Router.getParams();
166
-
167
- ```
168
-
169
- ## State management
170
-
171
- ```js
172
-
173
-
174
- // element can have this.updateState when the shouldUpdate props is true
175
-
176
- // Ref components
177
-
178
- // state can be managed from a store when using createSignal or simpleStores
179
- // this method is not yet documented
180
-
181
- import _, { Ref } from "cradova";
182
-
183
- // simple count
149
+ // setting shouldUpdate to true
150
+ // gives you this.updateState binding
184
151
 
185
152
  function counter() {
186
153
  let num = 0;
@@ -193,32 +160,42 @@ function counter() {
193
160
  });
194
161
  }
195
162
 
163
+ // Another example with data- attribute
164
+
196
165
  function dataCounter() {
197
166
  return _("h1| 0", {
198
167
  shouldUpdate: true,
199
168
  "data-num": "0",
200
169
  onclick() {
201
- const num = Number(this.getAttribute("data-num")) + 1;
202
- this.updateState({ text: num, $num: num });
170
+ const num = this.getAttribute("data-num") * 1 + 1;
171
+ this.updateState({ text: num, "data-num": num });
203
172
  },
204
173
  });
205
174
  }
206
175
 
207
- function HelloMessage(name = "no name") {
208
- return _("div.foo#bar", {
176
+ // hello message
177
+
178
+ function HelloMessage() {
179
+ return div({
209
180
  shouldUpdate: true,
210
- text: "hello " + name,
181
+ text: "Click to get a greeting",
211
182
  onclick() {
212
183
  const name = prompt("what are your names");
213
- this.updateState({ text: "hello " + name });
184
+ this.updateState({
185
+ text: name ? "hello " + name : "Click to get a greeting",
186
+ });
214
187
  },
215
188
  });
216
189
  }
217
190
 
218
- const nameRef = new Ref(function ( name ) {
191
+ // using cradova Ref
192
+
193
+ const nameRef = new Ref(function (name) {
219
194
  const self = this;
220
195
  return _("div.foo#bar", {
221
- text: "hello" + (name || "no name"),
196
+ text: name
197
+ ? "hello " + (name || " user 2")
198
+ : "Click to get a second greeting",
222
199
  onclick() {
223
200
  const name = prompt();
224
201
  self.updateState(name);
@@ -226,62 +203,251 @@ const nameRef = new Ref(function ( name ) {
226
203
  });
227
204
  });
228
205
 
229
- /*
230
- cradova Ref are component objects
231
- with methods for rendering, pre-rendering, and updating a it dom elements.
206
+ function App() {
207
+ return div(counter, dataCounter, HelloMessage, br, nameRef);
208
+ }
232
209
 
233
- Ref also has the feature to stash input values need by the components
210
+ // add your app to the DOM
234
211
 
235
- */
212
+ document.body.append(App());
213
+ ```
214
+
215
+ ## Simple Todo list
216
+
217
+ Let's see a simple TodoList example
218
+
219
+ ```js
220
+ import _, {
221
+ button,
222
+ createSignal,
223
+ css,
224
+ div,
225
+ input,
226
+ main,
227
+ p,
228
+ Ref,
229
+ reference,
230
+ } from "../dist/index.js";
231
+
232
+ function TodoList() {
233
+ // can be used to hold multiple references
234
+ const referenceSet = new reference();
235
+
236
+ // creating a store
237
+ const todoStore = new createSignal([
238
+ "take bath",
239
+ "code code code",
240
+ "take a break",
241
+ ]);
242
+
243
+ // create actions
244
+ todoStore.createAction("add-todo", function (todo) {
245
+ this.set([...this.value, todo]);
246
+ });
236
247
 
248
+ todoStore.createAction("remove-todo", function (todo) {
249
+ const ind = this.value.indexOf(todo);
250
+ this.value.splice(ind, 1);
251
+ this.set(this.value);
252
+ });
237
253
 
238
- function Home() {
239
- return _("div.foo#bar",
240
- counter,
241
- dataCounter,
242
- HelloMessage,
243
- nameRef.render( "no name" )
254
+ // bind Ref to Signal
255
+ todoStore.bindRef(todoList);
256
+
257
+ // markup
258
+ return main(
259
+ _`|Todo List`,
260
+ div(
261
+ input({
262
+ placeholder: "type in todo",
263
+ reference: referenceSet.bindAs("todoInput"),
264
+ }),
265
+ button("Add todo", {
266
+ onclick() {
267
+ todoStore.fireAction("add-todo", referenceSet.todoInput.value);
268
+ referenceSet.todoInput.value = "";
269
+ },
270
+ })
271
+ ),
272
+ todoList.render
244
273
  );
245
274
  }
246
275
 
276
+ const todoList = new Ref(function () {
277
+ const self = this;
278
+ return div(
279
+ self.Signal.value.map((item) =>
280
+ p(item, {
281
+ title: "click to remove",
282
+ onclick() {
283
+ self.Signal.fireAction("remove-todo", item);
284
+ },
285
+ })
286
+ )
287
+ );
288
+ });
289
+
290
+ document.body.appendChild(TodoList());
291
+
292
+ css`
293
+ body {
294
+ box-sizing: border-box;
295
+ display: flex;
296
+ }
297
+ main {
298
+ margin: auto;
299
+ }
300
+ main > p {
301
+ font-size: 2rem;
302
+ }
303
+ `;
304
+ ```
305
+
306
+ ## working with screen and Router:
307
+
308
+ unlike just appending stuff to the DOM,
309
+ a better to build apps is to use a routing system.
310
+
311
+ Cradova Router is a module that allows you do the following:
312
+
313
+ Create specified routes in you application
314
+ help you orchestrate navigation
315
+ render a screen on a route
316
+ pre-render a screen in the background if you want to.
317
+ listen to Navigation changes
318
+ create error boundary at screen level.
319
+ persist rendered screens by default
320
+ allow parallel screen rendering for every unique route scheme
321
+
322
+ let's try an example.
323
+
324
+ ```js
325
+ import _, { Screen, Router } from "cradova";
326
+
327
+ // Ref can be used as screens
328
+
329
+ const template = new Ref(function (name) {
330
+ // an effect run once after screen renders
331
+ const self = this;
332
+ self.effect(() => {
333
+ const name = new Promise((res) => {
334
+ res("john doe");
335
+ });
336
+ setTimeout(async () => {
337
+ self.updateState(await name);
338
+ }, 1000);
339
+ });
340
+ // effects can be used to make api calls needed for the page
341
+ return _("div", name ? ">>>>>>>> Hello " + name : " loading...");
342
+ });
343
+
247
344
  const home = new Screen({
248
345
  name: "home page", // page title
249
- template: Home,
250
- ...
346
+ template,
347
+ });
348
+
349
+ // in your routes.ts file
350
+ Router.BrowserRoutes({
351
+ "/home": home,
352
+ "/lazy-loaded-home": async () => await import("./home"),
251
353
  });
354
+ // creates these routes
252
355
 
253
- /*
356
+ Router.packageScreen("/home", data);
357
+ // get the page ready in the background
358
+
359
+ Router.navigate("/home", data);
360
+ // navigates to that page
254
361
 
255
- Nice things about cradova screens
362
+ Router.getParams();
363
+ // get route params for this current page
364
+
365
+ Router.onPageEvent((lastRoute, newRoute) => {
366
+ console.log(lastRoute, newRoute);
367
+ });
368
+ // listen for navigation changes
369
+ ```
370
+
371
+ ### More info
372
+
373
+ ---
374
+
375
+ More info on cradova Router
376
+
377
+ ---
378
+
379
+ Every cradova app mounts on a div with attribute data-wrapper="app"
380
+
381
+ if it already exist cradova will use it instead.
382
+
383
+ cradova will create a div with data-wrapper="app" if it doesn't exists already.
384
+
385
+ so if you want to use your own mount point then create a div with data-wrapper="app".
386
+
387
+ ---
388
+
389
+ More info on cradova screens
390
+
391
+ ---
256
392
 
257
393
  screens are rendered once by default to hack
258
394
  responsiveness making your app work fast as user navigates.
259
395
 
260
396
  this behavior can be override
261
397
  by passing
262
- prerender: false
398
+ persist: false
263
399
  in the constructor
264
400
 
265
-
266
401
  Cradova screens has
267
402
  onActivate() and
268
- onDeactivate() methods
403
+ onDeactivate() methods which is also available in the
404
+ component function on the this variable bound to it.
269
405
 
270
- these allow you manage rendering
271
- circle for each in your app
406
+ this allow you manage rendering
407
+ circle for each screen in your app
272
408
 
273
- */
409
+ ---
274
410
 
275
- Router.route("/", home);
276
- ```
411
+ More info on cradova Ref
412
+
413
+ ---
414
+
415
+ Refs are dynamic components, they have simple abstractions like:
416
+
417
+ - Effects
418
+ - stash
419
+ - preRender
420
+ - updateState
421
+
422
+ these behaviors allow you manage rendering
423
+ circle for refs in your app
424
+
425
+ ---
426
+
427
+ More info on cradova createSignal
428
+
429
+ ---
430
+
431
+ Cradova Signals allows you to create powerful data stores.
432
+
433
+ with ability to:
434
+
435
+ - create store
436
+ - create actions and fire them
437
+ - bind a Ref
438
+ - listen to changes
439
+ - persist changes to localStorage
440
+ - update a cradova Ref and bindings automatically
441
+
442
+ With these simple and easy abstractions, you can use datastores with powerful convenience.
277
443
 
278
444
  ## Documentation
279
445
 
280
- At the moment, we're in the process of creating documentation for Cradova, and we have limited resources. If you're interested in lending a hand, we invite you to join our community, gain firsthand experience, and contribute to the advancement of Cradova.
446
+ At the moment, we're in the process of creating a documentation website for Cradova, and we have limited resources. If you're interested in lending a hand, we invite you to join our community, gain firsthand experience, and contribute to the advancement of Cradova.
281
447
 
282
448
  ## Getting Help
283
449
 
284
- To get further insights and help on Cradova, visit our new [Telegram Community Chat](https://t.me/cradovaframework).
450
+ To get further insights and help on Cradova, visit our [Discord](https://discord.gg/b7fvMg38) and [Telegram](https://t.me/cradovaframework) Community Chats.
285
451
 
286
452
  ## Contributing
287
453
 
package/dist/index.js CHANGED
@@ -1,135 +1,56 @@
1
1
  // lib/utils/init.ts
2
2
  var Init = function() {
3
- if (!document.querySelector("[data-cra-id=cradova-app-wrapper]")) {
3
+ if (!document.querySelector("[data-wrapper=app]")) {
4
4
  const Wrapper = document.createElement("div");
5
- Wrapper.setAttribute("data-cra-id", "cradova-app-wrapper");
5
+ Wrapper.setAttribute("data-wrapper", "app");
6
6
  document.body.appendChild(Wrapper);
7
7
  }
8
8
  };
9
9
 
10
- // lib/utils/track.ts
11
- function cradovaDispatchTrack(nodes, state) {
12
- for (let i2 = 0; i2 < nodes.length; i2++) {
13
- const element = nodes[i2];
14
- if (!element) {
15
- continue;
10
+ // lib/utils/fns.ts
11
+ var isNode = (node) => typeof node === "object" && typeof node.nodeType === "number";
12
+ var cradovaEvent = class {
13
+ constructor() {
14
+ this.listeners = {};
15
+ }
16
+ addEventListener(eventName, callback) {
17
+ if (!this.listeners[eventName]) {
18
+ this.listeners[eventName] = [];
16
19
  }
17
- if (typeof state === "object" && !Array.isArray(state)) {
18
- for (const key in state) {
19
- if (key === "style") {
20
- for (const [k, v] of Object.entries(state[key])) {
21
- if (typeof element.style[k] !== "undefined" && k !== "src") {
22
- element.style[k] = v;
23
- } else {
24
- throw new Error(
25
- "\u2718 Cradova err : " + k + " is not a valid css style property"
26
- );
27
- }
28
- }
29
- continue;
30
- }
31
- if (typeof element[key] === "function") {
32
- if (key.startsWith("on")) {
33
- element[key] = state[key];
34
- } else {
35
- element[key].apply(element);
36
- }
37
- continue;
38
- }
39
- if (key === "text") {
40
- element.innerText = state[key];
41
- continue;
42
- }
43
- if (key === "remove") {
44
- if (element.parentElement) {
45
- element.parentElement?.removeChild(element);
46
- } else {
47
- element.remove();
48
- }
49
- continue;
50
- }
51
- if (key.includes("$")) {
52
- element.setAttribute("data-" + key.split("$")[1], state[key]);
53
- continue;
54
- }
55
- if (key === "tree") {
56
- element.innerHTML = "";
57
- element.appendChild(frag(state[key]));
58
- continue;
59
- }
60
- try {
61
- if (typeof element[key] !== "undefined") {
62
- element[key] = state[key];
63
- } else {
64
- element[key] = state[key];
65
- if (key !== "for" && key !== "text" && key !== "class" && key !== "tabindex" && key !== "disabled" && !key.includes("aria")) {
66
- console.error(" \u2718 Cradova err: invalid html attribute " + key);
67
- }
68
- }
69
- } catch (error) {
70
- console.error(" \u2718 Cradova err: Cradova got ", state);
71
- console.error(" \u2718 Cradova err: ", error);
72
- }
20
+ this.listeners[eventName].push(callback);
21
+ }
22
+ removeEventListener(eventName, callback) {
23
+ if (this.listeners[eventName]) {
24
+ const index = this.listeners[eventName].indexOf(callback);
25
+ if (index !== -1) {
26
+ this.listeners[eventName].splice(index, 1);
73
27
  }
74
- } else {
75
- throw new TypeError(" \u2718 Cradova err: invalid state object" + state);
76
28
  }
77
29
  }
78
- }
79
- function dispatch(stateID, state) {
80
- let ele;
81
- if (stateID instanceof HTMLElement) {
82
- ele = stateID;
83
- }
84
- let updated = void 0;
85
- if (typeof state === "undefined" && typeof stateID === "object" && !ele) {
86
- for (const [id, eachState] of Object.entries(stateID)) {
87
- const elements = document.querySelectorAll(
88
- "[data-cra-id=" + id + "]"
89
- );
90
- updated = elements.length === 1 ? elements[0] : elements;
91
- cradovaDispatchTrack(elements, eachState);
92
- }
93
- } else {
94
- if (typeof stateID === "string") {
95
- const elements = document.querySelectorAll(
96
- "[data-cra-id=" + stateID + "]"
97
- );
98
- if (elements.length) {
99
- updated = elements.length === 1 ? elements[0] : elements;
100
- if (!state?.cradovaDispatchTrackBreak) {
101
- cradovaDispatchTrack(elements, state);
102
- }
30
+ dispatchEvent(eventName, eventArgs) {
31
+ const eventListeners = this.listeners[eventName];
32
+ if (eventListeners) {
33
+ for (let i2 = 0; i2 < eventListeners.length; i2++) {
34
+ eventListeners[i2](eventArgs);
103
35
  }
104
36
  }
105
- if (ele) {
106
- cradovaDispatchTrack([ele], state);
107
- }
108
37
  }
109
- return updated;
110
- }
111
-
112
- // lib/utils/fns.ts
113
- var isNode = (node) => typeof node === "object" && typeof node.nodeType === "number";
114
- var cradovaAftermountEvent = new CustomEvent("cradova-aftermount");
115
- function uuid() {
116
- let t = Date.now ? +Date.now() : +new Date();
117
- return "cradova-id-xxxxxx".replace(/[x]/g, function(e) {
118
- const r = (t + 16 * Math.random()) % 16 | 0;
119
- return ("x" === e ? r : 7 & r | 8).toString(16);
120
- });
121
- }
38
+ };
39
+ var CradovaEvent = new cradovaEvent();
122
40
  function Rhoda(l) {
123
41
  const fg = new DocumentFragment();
124
42
  for (let ch of l) {
125
43
  if (Array.isArray(ch)) {
126
44
  fg.appendChild(Rhoda(ch));
127
45
  } else {
128
- if (typeof ch === "function") {
129
- ch = ch();
46
+ if (ch?.render) {
47
+ ch = ch.render();
130
48
  }
131
49
  if (typeof ch === "function") {
132
50
  ch = ch();
51
+ if (typeof ch === "function") {
52
+ ch = ch();
53
+ }
133
54
  }
134
55
  if (typeof ch === "string" || typeof ch === "number") {
135
56
  fg.appendChild(document.createTextNode(ch));
@@ -148,45 +69,20 @@ function Rhoda(l) {
148
69
  }
149
70
  return fg;
150
71
  }
151
- function css(identifier, properties) {
152
- if (typeof identifier === "string" && typeof properties === "undefined") {
72
+ function css(identifier) {
73
+ if (Array.isArray(identifier)) {
74
+ identifier = identifier[0];
75
+ }
76
+ if (typeof identifier === "string") {
153
77
  let styTag = document.querySelector("style");
154
78
  if (styTag !== null) {
155
- identifier += styTag.textContent;
156
- styTag.textContent = identifier;
79
+ styTag.textContent = identifier + styTag.textContent;
157
80
  return;
158
81
  }
159
82
  styTag = document.createElement("style");
160
83
  styTag.textContent = identifier;
161
84
  document.head.appendChild(styTag);
162
- return;
163
- }
164
- if (!properties) {
165
- return;
166
- }
167
- const styS = "" + identifier + `{
168
- `;
169
- const styE = `}
170
- `;
171
- let style2 = "", totalStyle = "";
172
- for (const [k, v] of Object.entries(properties)) {
173
- style2 += "" + k + ": " + v + `;
174
- `;
175
- }
176
- let styleTag = document.querySelector("style");
177
- if (styleTag !== null) {
178
- totalStyle += styleTag.innerHTML;
179
- totalStyle += styS + style2 + styE;
180
- styleTag.innerHTML = totalStyle;
181
- return;
182
85
  }
183
- styleTag = document.createElement("style");
184
- totalStyle += styleTag.innerHTML + `
185
-
186
- `;
187
- totalStyle += styS + style2 + styE;
188
- styleTag.innerHTML = totalStyle;
189
- document.head.appendChild(styleTag);
190
86
  }
191
87
  function assert(condition, ...elements) {
192
88
  if (condition) {
@@ -210,28 +106,28 @@ function assertOr(condition, ifTrue, ifFalse) {
210
106
  }
211
107
  var Ref = class {
212
108
  constructor(component) {
213
- this.stateID = uuid();
214
109
  this.effects = [];
215
110
  this.effectuate = null;
216
111
  this.rendered = false;
217
112
  this.published = false;
218
113
  this.hasFirstStateUpdateRun = false;
219
114
  this.preRendered = null;
115
+ this.reference = new reference();
220
116
  this.component = component.bind(this);
221
117
  }
222
118
  preRender(data2) {
223
119
  const chtml = this.component(data2);
224
120
  if (chtml instanceof HTMLElement) {
225
- chtml.setAttribute("data-cra-id", this.stateID);
226
121
  this.preRendered = chtml;
227
122
  } else {
228
- this.preRendered = chtml({ stateID: this.stateID });
123
+ this.preRendered = chtml();
229
124
  }
230
- if (typeof this.preRendered === "undefined") {
125
+ if (!this.preRendered) {
231
126
  throw new Error(
232
- " \u2718 Cradova err : Invalid component type for cradova Ref, got - " + this.preRendered
127
+ " \u2718 Cradova err : Invalid component type for cradova Ref got - " + this.preRendered
233
128
  );
234
129
  }
130
+ this.reference._appendDomForce("reference", this.preRendered);
235
131
  }
236
132
  destroyPreRendered() {
237
133
  this.preRendered = null;
@@ -244,12 +140,11 @@ var Ref = class {
244
140
  if (!this.preRendered) {
245
141
  const chtml = this.component(data2);
246
142
  if (chtml instanceof HTMLElement) {
247
- chtml.setAttribute("data-cra-id", this.stateID);
248
143
  element = chtml;
249
144
  } else {
250
- element = chtml({ stateID: this.stateID });
145
+ element = chtml();
251
146
  }
252
- if (typeof element === "undefined") {
147
+ if (!element) {
253
148
  throw new Error(
254
149
  " \u2718 Cradova err : Invalid component type for cradova Ref, got - " + element
255
150
  );
@@ -260,21 +155,23 @@ var Ref = class {
260
155
  }
261
156
  const av = async () => {
262
157
  await this.effector.apply(this);
263
- window.removeEventListener("cradova-aftermount", av);
158
+ CradovaEvent.removeEventListener("cradovaAftermountEvent", av);
264
159
  };
265
- window.addEventListener("cradova-aftermount", av);
160
+ CradovaEvent.addEventListener("cradovaAftermountEvent", av);
266
161
  this.effector();
267
162
  this.published = true;
268
163
  this.rendered = true;
269
164
  if (!element) {
270
165
  element = this.preRendered;
271
166
  }
167
+ this.reference._appendDomForce("reference", element);
272
168
  return element;
273
169
  }
274
170
  instance() {
275
- return dispatch(this.stateID, {
276
- cradovaDispatchTrackBreak: true
277
- });
171
+ return this.reference.reference;
172
+ }
173
+ _setExtra(Extra) {
174
+ this.Signal = Extra;
278
175
  }
279
176
  effect(fn) {
280
177
  if (!this.rendered) {
@@ -320,34 +217,38 @@ var Ref = class {
320
217
  return;
321
218
  }
322
219
  this.published = false;
323
- const guy = dispatch(this.stateID, {
324
- cradovaDispatchTrackBreak: true
325
- });
326
- if (!guy) {
220
+ if (!this.reference.reference) {
327
221
  return;
328
222
  }
329
223
  const chtml = this.component(data2);
330
224
  let element;
331
225
  if (chtml instanceof HTMLElement) {
332
- chtml.setAttribute("data-cra-id", this.stateID);
333
226
  element = chtml;
334
227
  } else {
335
- element = chtml({ stateID: this.stateID });
228
+ element = chtml();
229
+ }
230
+ if (!element) {
231
+ throw new Error(
232
+ " \u2718 Cradova err : Invalid component type for cradova Ref, got - " + element
233
+ );
336
234
  }
337
235
  try {
338
- guy.insertAdjacentElement("beforebegin", element);
339
- if (guy.parentElement) {
340
- guy.parentElement.removeChild(guy);
236
+ this.reference.reference.insertAdjacentElement("beforebegin", element);
237
+ if (this.reference.reference.parentElement) {
238
+ this.reference.reference.parentElement.removeChild(
239
+ this.reference.reference
240
+ );
341
241
  } else {
342
- guy.remove();
242
+ this.reference.reference.remove();
343
243
  }
244
+ this.reference._appendDomForce("reference", element);
344
245
  } catch (e0) {
345
246
  console.error(e0);
346
247
  }
347
248
  this.published = true;
348
249
  }
349
250
  remove() {
350
- dispatch(this.stateID, { remove: true });
251
+ this.reference.reference.remove();
351
252
  }
352
253
  };
353
254
  var frag = function(children) {
@@ -368,10 +269,8 @@ var frag = function(children) {
368
269
  par.appendChild(document.createTextNode(a2));
369
270
  continue;
370
271
  }
371
- {
372
- console.error(" \u2718 Cradova err: wrong element type" + a2);
373
- throw new TypeError(" \u2718 Cradova err: invalid element");
374
- }
272
+ console.error(" \u2718 Cradova err: wrong element type" + a2);
273
+ throw new TypeError(" \u2718 Cradova err: invalid element");
375
274
  }
376
275
  return par;
377
276
  };
@@ -410,6 +309,121 @@ var lazy = class {
410
309
  this.content = this.content.default;
411
310
  }
412
311
  };
312
+ var reference = class {
313
+ bindAs(name) {
314
+ return [this, name];
315
+ }
316
+ _appendDom(name, Element) {
317
+ if (!Object.hasOwnProperty.call(this, name)) {
318
+ this[name] = Element;
319
+ } else {
320
+ if (Array.isArray(this[name])) {
321
+ this[name].push(Element);
322
+ } else {
323
+ this[name] = [this[name], Element];
324
+ }
325
+ }
326
+ }
327
+ _appendDomForce(name, Element) {
328
+ this[name] = Element;
329
+ }
330
+ };
331
+
332
+ // lib/utils/track.ts
333
+ function cradovaDispatchTrack(nodes, state) {
334
+ for (let i2 = 0; i2 < nodes.length; i2++) {
335
+ const element = nodes[i2];
336
+ if (!element) {
337
+ continue;
338
+ }
339
+ if (typeof state === "object" && !Array.isArray(state)) {
340
+ for (const key in state) {
341
+ if (key === "style") {
342
+ for (const [k, v] of Object.entries(state[key])) {
343
+ if (typeof element.style[k] !== "undefined" && k !== "src") {
344
+ element.style[k] = v;
345
+ } else {
346
+ throw new Error(
347
+ "\u2718 Cradova err : " + k + " is not a valid css style property"
348
+ );
349
+ }
350
+ }
351
+ continue;
352
+ }
353
+ if (typeof element[key] === "function") {
354
+ if (key.startsWith("on")) {
355
+ element[key] = state[key];
356
+ } else {
357
+ element[key].apply(element);
358
+ }
359
+ continue;
360
+ }
361
+ if (key === "text") {
362
+ element.innerText = state[key];
363
+ continue;
364
+ }
365
+ if (key === "tree") {
366
+ element.innerHTML = "";
367
+ element.appendChild(frag(state[key]));
368
+ continue;
369
+ }
370
+ try {
371
+ if (typeof element[key] !== "undefined") {
372
+ element[key] = state[key];
373
+ } else {
374
+ if (key.includes("data-")) {
375
+ element.setAttribute(key, state[key]);
376
+ continue;
377
+ } else {
378
+ element[key] = state[key];
379
+ }
380
+ if (key !== "for" && key !== "text" && key !== "class" && key !== "tabindex" && key !== "disabled" && !key.includes("aria")) {
381
+ console.error(" \u2718 Cradova err: invalid html attribute " + key);
382
+ }
383
+ }
384
+ } catch (error) {
385
+ console.error(" \u2718 Cradova err: Cradova got ", state);
386
+ console.error(" \u2718 Cradova err: ", error);
387
+ }
388
+ }
389
+ } else {
390
+ console.log(nodes, state);
391
+ throw new TypeError(" \u2718 Cradova err: invalid state object" + state);
392
+ }
393
+ }
394
+ }
395
+ function dispatch(stateID, state) {
396
+ if (stateID instanceof HTMLElement) {
397
+ cradovaDispatchTrack([stateID], state);
398
+ }
399
+ let ele;
400
+ let updated = void 0;
401
+ if (typeof state === "undefined" && typeof stateID === "object" && !ele) {
402
+ for (const [id, eachState] of Object.entries(stateID)) {
403
+ const elements = document.querySelectorAll(
404
+ "[data-cra-id=" + id + "]"
405
+ );
406
+ updated = elements.length === 1 ? elements[0] : elements;
407
+ cradovaDispatchTrack(elements, eachState);
408
+ }
409
+ } else {
410
+ if (typeof stateID === "string") {
411
+ const elements = document.querySelectorAll(
412
+ "[data-cra-id=" + stateID + "]"
413
+ );
414
+ if (elements.length) {
415
+ updated = elements.length === 1 ? elements[0] : elements;
416
+ if (!state?.cradovaDispatchTrackBreak) {
417
+ cradovaDispatchTrack(elements, state);
418
+ }
419
+ }
420
+ }
421
+ if (ele) {
422
+ cradovaDispatchTrack([ele], state);
423
+ }
424
+ }
425
+ return updated;
426
+ }
413
427
 
414
428
  // lib/utils/createSignal.ts
415
429
  var createSignal = class {
@@ -474,9 +488,9 @@ var createSignal = class {
474
488
  this.actions[key] = action;
475
489
  } else {
476
490
  if (typeof key === "object" && !action) {
477
- for (const [nam, action2] of Object.entries(key)) {
478
- if (typeof nam === "string" && typeof action2 === "function") {
479
- this.actions[nam] = action2;
491
+ for (const [nam, act] of Object.entries(key)) {
492
+ if (typeof nam === "string" && typeof action === "function") {
493
+ this.actions[nam] = act;
480
494
  } else {
481
495
  throw new Error(`\u2718 Cradova err : can't create action ${nam}`);
482
496
  }
@@ -487,14 +501,13 @@ var createSignal = class {
487
501
  }
488
502
  }
489
503
  fireAction(key, data2) {
490
- if (this.actions[key].updateState) {
491
- this.actions[key].updateState(this, data2);
492
- this._updateState(key, data2);
504
+ this._updateState(key, data2);
505
+ if (this.actions[key] && this.actions[key].updateState) {
506
+ this.actions[key].updateState(data2);
493
507
  return;
494
508
  } else {
495
509
  if (typeof this.actions[key] === "function") {
496
- this.actions[key](this, data2);
497
- this._updateState(key, data2);
510
+ this.actions[key].bind(this)(data2);
498
511
  return;
499
512
  }
500
513
  }
@@ -550,13 +563,20 @@ var createSignal = class {
550
563
  ent.ref.updateState(this.value[ent._signalProperty]);
551
564
  continue;
552
565
  }
566
+ if (!ent._element_property && !ent._signalProperty) {
567
+ ent.ref.updateState(this.value);
568
+ continue;
569
+ }
553
570
  }
554
571
  }
555
572
  }
556
- bindRef(ref, binding) {
573
+ bindRef(ref, binding = { signalProperty: "", _element_property: "" }) {
557
574
  if (ref.render) {
558
575
  ref.render = ref.render.bind(ref, this.value);
559
576
  }
577
+ if (ref._setExtra) {
578
+ ref._setExtra(this);
579
+ }
560
580
  if (ref && ref.updateState) {
561
581
  this.ref.push({
562
582
  ref,
@@ -591,6 +611,12 @@ RouterBox["pageHide"] = null;
591
611
  RouterBox["errorHandler"] = null;
592
612
  RouterBox["params"] = {};
593
613
  RouterBox["routes"] = {};
614
+ RouterBox["pageevents"] = [];
615
+ RouterBox["start_pageevents"] = function(lastR, newR) {
616
+ for (let ci = 0; ci < RouterBox["pageevents"].length; ci++) {
617
+ RouterBox["pageevents"][ci](lastR, newR);
618
+ }
619
+ };
594
620
  var checker = (url) => {
595
621
  if (RouterBox.routes[url]) {
596
622
  return [RouterBox.routes[url], { path: url }];
@@ -686,11 +712,12 @@ Router.navigate = function(href, data2 = null, force = false) {
686
712
  force = true;
687
713
  data2 = null;
688
714
  }
689
- let route = null, params, link2 = null;
715
+ let route = null, params;
690
716
  if (href.includes("://")) {
691
717
  window.location.href = href;
692
718
  } else {
693
- if (href === window.location.pathname) {
719
+ const lastR = window.location.pathname;
720
+ if (href === lastR) {
694
721
  return;
695
722
  }
696
723
  [route, params] = checker(href);
@@ -699,11 +726,10 @@ Router.navigate = function(href, data2 = null, force = false) {
699
726
  RouterBox.params.params = params;
700
727
  route._paramData = params;
701
728
  RouterBox.params.data = data2 || null;
702
- link2 = href;
703
- RouterBox["pageHide"] && RouterBox["pageHide"](href + " :navigated");
704
- window.history.pushState({}, "", link2);
729
+ window.history.pushState({}, "", href);
705
730
  }
706
731
  RouterBox.router(null, force);
732
+ RouterBox["start_pageevents"](lastR, href);
707
733
  }
708
734
  };
709
735
  RouterBox.router = async function(e, force = false) {
@@ -755,7 +781,6 @@ RouterBox.router = async function(e, force = false) {
755
781
  RouterBox["lastNavigatedRouteController"] && RouterBox["lastNavigatedRouteController"]._deActivate();
756
782
  RouterBox["lastNavigatedRoute"] = url;
757
783
  RouterBox["lastNavigatedRouteController"] = route;
758
- RouterBox["pageShow"] && RouterBox["pageShow"](url);
759
784
  } catch (error) {
760
785
  let errorHandler;
761
786
  if (RouterBox.routes[RouterBox.params.params._path]) {
@@ -768,33 +793,18 @@ RouterBox.router = async function(e, force = false) {
768
793
  } else {
769
794
  if (RouterBox.routes["/404"]) {
770
795
  RouterBox.routes["/404"].controller();
771
- } else {
772
- if (Object.keys(RouterBox.routes).length > 0) {
773
- console.error(
774
- " \u2718 Cradova err: route '" + url + "' does not exist and no '/404' route given!"
775
- );
776
- }
777
796
  }
778
797
  }
779
798
  };
780
- Router["onPageShow"] = function(callback) {
799
+ Router["onPageEvent"] = function(callback) {
781
800
  if (typeof callback === "function") {
782
- RouterBox["pageShow"] = callback;
801
+ RouterBox["pageevents"].push(callback);
783
802
  } else {
784
803
  throw new Error(
785
804
  " \u2718 Cradova err: callback for pageShow event is not a function"
786
805
  );
787
806
  }
788
807
  };
789
- Router["onPageHide"] = function(callback) {
790
- if (typeof callback === "function") {
791
- RouterBox["pageHide"] = callback;
792
- } else {
793
- throw new Error(
794
- " \u2718 Cradova err: callback for pageHide event is not a function"
795
- );
796
- }
797
- };
798
808
  Router.packageScreen = async function(path, data2 = {}) {
799
809
  if (!RouterBox.routes[path]) {
800
810
  console.error(" \u2718 Cradova err: no screen with path " + path);
@@ -827,13 +837,16 @@ Router.mount = () => {
827
837
  });
828
838
  };
829
839
 
830
- // lib/utils/tags.ts
840
+ // lib/utils/elements.ts
831
841
  var makeElement = (element, ...ElementChildrenAndPropertyList) => {
832
842
  let beforeMount = null;
833
843
  let props = null, text = null;
834
844
  if (ElementChildrenAndPropertyList.length) {
835
845
  for (let i2 = 0; i2 < ElementChildrenAndPropertyList.length; i2++) {
836
846
  let child = ElementChildrenAndPropertyList[i2];
847
+ if (child?.render) {
848
+ child = child.render();
849
+ }
837
850
  if (typeof child === "function") {
838
851
  child = child();
839
852
  if (typeof child === "function") {
@@ -917,16 +930,21 @@ var makeElement = (element, ...ElementChildrenAndPropertyList) => {
917
930
  });
918
931
  continue;
919
932
  }
933
+ if (prop == "reference" && Array.isArray(props[prop]) && props[prop][0] instanceof reference) {
934
+ element.updateState = dispatch.bind(null, element);
935
+ props[prop][0]._appendDom(props[prop][1], element);
936
+ continue;
937
+ }
920
938
  if (prop === "shouldUpdate" && props[prop] === true) {
921
939
  element.updateState = dispatch.bind(void 0, element);
922
940
  continue;
923
941
  }
924
942
  if (prop === "afterMount" && typeof props["afterMount"] === "function") {
925
- const av = () => {
943
+ const avi = () => {
926
944
  props["afterMount"].apply(element);
927
- window.removeEventListener("cradova-aftermount", av);
945
+ CradovaEvent.removeEventListener("cradovaAftermountEvent", avi);
928
946
  };
929
- window.addEventListener("cradova-aftermount", av);
947
+ CradovaEvent.addEventListener("cradovaAftermountEvent", avi);
930
948
  continue;
931
949
  }
932
950
  try {
@@ -958,7 +976,7 @@ var makeElement = (element, ...ElementChildrenAndPropertyList) => {
958
976
  if (typeof beforeMount === "function") {
959
977
  beforeMount.apply(element);
960
978
  }
961
- if (element.tagName === "A") {
979
+ if (element.tagName === "A" && window) {
962
980
  if (element.href.includes(window.location.origin)) {
963
981
  element.addEventListener("click", (e) => {
964
982
  e.preventDefault();
@@ -969,10 +987,10 @@ var makeElement = (element, ...ElementChildrenAndPropertyList) => {
969
987
  return element;
970
988
  };
971
989
  var cra = (element_initials) => {
972
- return (...bo) => {
990
+ return (...initials) => {
973
991
  return makeElement(
974
992
  document.createElement(element_initials),
975
- ...bo
993
+ ...initials
976
994
  );
977
995
  };
978
996
  };
@@ -1160,12 +1178,6 @@ var Screen = class {
1160
1178
  this._params = null;
1161
1179
  this._delegatedRoutesCount = -1;
1162
1180
  const { template: template2, name, persist, renderInParallel, transition } = cradova_screen_initials;
1163
- if (typeof template2 !== "function") {
1164
- console.error(" \u2718 Cradova err: expected a screen but got ", template2);
1165
- throw new Error(
1166
- " \u2718 Cradova err: only functions that returns a cradova element is valid as screen"
1167
- );
1168
- }
1169
1181
  this._html = template2;
1170
1182
  this._name = name;
1171
1183
  this._transition = transition || "";
@@ -1201,6 +1213,10 @@ var Screen = class {
1201
1213
  this.errorHandler = errorHandler;
1202
1214
  }
1203
1215
  async _package() {
1216
+ if (this._html.render) {
1217
+ this._template.innerHTML = "";
1218
+ this._template.appendChild(this._html.render(this._data));
1219
+ }
1204
1220
  if (typeof this._html === "function") {
1205
1221
  let fuc = await this._html.apply(this, this._data);
1206
1222
  if (typeof fuc === "function") {
@@ -1227,8 +1243,9 @@ var Screen = class {
1227
1243
  }
1228
1244
  }
1229
1245
  if (!this._template.firstChild) {
1246
+ console.error(" \u2718 Cradova err: expected a screen but got ", this._html);
1230
1247
  throw new Error(
1231
- " \u2718 Cradova err: no screen is rendered, may have been past wrongly."
1248
+ " \u2718 Cradova err: only functions that returns a cradova element is valid as screen"
1232
1249
  );
1233
1250
  }
1234
1251
  if (this._secondaryChildren.length) {
@@ -1265,10 +1282,10 @@ var Screen = class {
1265
1282
  await this._package();
1266
1283
  }
1267
1284
  }
1268
- const doc = document.querySelector("[data-cra-id=cradova-app-wrapper]");
1285
+ const doc = document.querySelector("[data-wrapper=app]");
1269
1286
  if (!doc) {
1270
1287
  throw new Error(
1271
- " \u2718 Cradova err: Unable to render, cannot find cradova root <div data-cra-id='cradova-app-wrapper'> ... </div>"
1288
+ " \u2718 Cradova err: Unable to render, cannot find cradova root <div data-wrapper='app'> ... </div>"
1272
1289
  );
1273
1290
  }
1274
1291
  if (this._transition) {
@@ -1280,8 +1297,10 @@ var Screen = class {
1280
1297
  if (this._callBack) {
1281
1298
  await this._callBack();
1282
1299
  }
1283
- window.dispatchEvent(cradovaAftermountEvent);
1284
- window.scrollTo(0, 0);
1300
+ CradovaEvent.dispatchEvent("cradovaAftermountEvent");
1301
+ if (window) {
1302
+ window.scrollTo(0, 0);
1303
+ }
1285
1304
  }
1286
1305
  };
1287
1306
 
@@ -1344,9 +1363,6 @@ var make = function(txx) {
1344
1363
  return [tag || "DIV", IDs[0], classes.join(" "), innerValue];
1345
1364
  };
1346
1365
  var _2 = (...element_initials) => {
1347
- if (typeof element_initials[0] !== "string") {
1348
- return frag(element_initials);
1349
- }
1350
1366
  const initials = make(element_initials.shift());
1351
1367
  let props = void 0;
1352
1368
  let element;
@@ -1382,6 +1398,7 @@ Init();
1382
1398
  var lib_default = _2;
1383
1399
  export {
1384
1400
  Ajax,
1401
+ CradovaEvent,
1385
1402
  Ref,
1386
1403
  Router,
1387
1404
  Screen,
@@ -1408,7 +1425,6 @@ export {
1408
1425
  code,
1409
1426
  col,
1410
1427
  colgroup,
1411
- cradovaAftermountEvent,
1412
1428
  createSignal,
1413
1429
  css,
1414
1430
  data,
@@ -1472,6 +1488,7 @@ export {
1472
1488
  pre,
1473
1489
  progress,
1474
1490
  q,
1491
+ reference,
1475
1492
  rp,
1476
1493
  rt,
1477
1494
  ruby,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cradova",
3
- "version": "2.2.3",
3
+ "version": "2.3.0",
4
4
  "description": "Web framework for building web apps and PWAs",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",