native-document 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/router.js CHANGED
@@ -1,9 +1,11 @@
1
1
 
2
2
  import Router from "./src/router/Router.js";
3
+ import {RouteParamPatterns} from "./src/router/Route.js";
3
4
  import { Link } from './src/router/link.js';
4
5
 
5
6
 
6
7
  export {
8
+ RouteParamPatterns,
7
9
  Router,
8
10
  Link
9
11
  }
@@ -5,10 +5,15 @@ const MemoryManager = (function() {
5
5
 
6
6
  let $nexObserverId = 0;
7
7
  const $observables = new Map();
8
- const $registry = new FinalizationRegistry((heldValue) => {
9
- DebugManager.log('MemoryManager', '🧹 Auto-cleanup observable:', heldValue);
10
- heldValue.listeners.splice(0);
11
- })
8
+ let $registry = null;
9
+ try {
10
+ $registry = new FinalizationRegistry((heldValue) => {
11
+ DebugManager.log('MemoryManager', '🧹 Auto-cleanup observable:', heldValue);
12
+ heldValue.listeners.splice(0);
13
+ });
14
+ } catch (e) {
15
+ DebugManager.warn('MemoryManager', 'FinalizationRegistry not supported, observables will not be cleaned automatically');
16
+ }
12
17
 
13
18
  return {
14
19
  /**
@@ -24,10 +29,15 @@ const MemoryManager = (function() {
24
29
  id: id,
25
30
  listeners
26
31
  };
27
- $registry.register(observable, heldValue);
32
+ if($registry) {
33
+ $registry.register(observable, heldValue);
34
+ }
28
35
  $observables.set(id, new WeakRef(observable));
29
36
  return id;
30
37
  },
38
+ getObservableById(id) {
39
+ return $observables.get(id)?.deref();
40
+ },
31
41
  cleanup() {
32
42
  for (const [_, weakObservableRef] of $observables) {
33
43
  const observable = weakObservableRef.deref();
@@ -14,18 +14,31 @@ export function Observable(value) {
14
14
  return new ObservableItem(value);
15
15
  }
16
16
 
17
-
18
17
  Observable.computed = function(callback, dependencies = []) {
19
18
  const initialValue = callback();
20
19
  const observable = new ObservableItem(initialValue);
21
20
 
22
- const updatedValue = throttle(() => observable.set(callback()), 10, { debounce: true });
21
+ const updatedValue = () => observable.set(callback());
23
22
 
24
23
  dependencies.forEach(dependency => dependency.subscribe(updatedValue));
25
24
 
26
25
  return observable;
27
26
  };
28
27
 
28
+ /**
29
+ *
30
+ * @param id
31
+ * @returns {ObservableItem|null}
32
+ */
33
+ Observable.getById = function(id) {
34
+ const item = MemoryManager.getObservableById(parseInt(id));
35
+ if(!item) {
36
+ throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
37
+ }
38
+ return item;
39
+ };
40
+
41
+
29
42
  /**
30
43
  *
31
44
  * @param {ObservableItem} observable
@@ -17,6 +17,9 @@ export default function ObservableChecker($observable, $checker) {
17
17
  this.val = function() {
18
18
  return $checker && $checker($observable.val());
19
19
  };
20
+ this.check = function(callback) {
21
+ return $observable.check(() => callback(this.val()));
22
+ };
20
23
 
21
24
  this.set = function(value) {
22
25
  return $observable.set(value);
@@ -109,4 +109,8 @@ export default function ObservableItem(value) {
109
109
  }
110
110
  })
111
111
 
112
+ this.toString = function() {
113
+ return '{{#ObItem::(' +$memoryId+ ')}}';
114
+ };
115
+
112
116
  }
package/src/data/Store.js CHANGED
@@ -11,7 +11,7 @@ export const Store = (function() {
11
11
  * @returns {ObservableItem}
12
12
  */
13
13
  use(name) {
14
- const {observer: originalObserver, followers } = $stores.get(name);
14
+ const {observer: originalObserver, subscribers } = $stores.get(name);
15
15
  const observerFollower = Observable(originalObserver.val());
16
16
  const unSubscriber = originalObserver.subscribe(value => observerFollower.set(value));
17
17
  const updaterUnsubscriber = observerFollower.subscribe(value => originalObserver.set(value));
@@ -20,7 +20,7 @@ export const Store = (function() {
20
20
  updaterUnsubscriber();
21
21
  observerFollower.cleanup();
22
22
  };
23
- followers.add(observerFollower);
23
+ subscribers.add(observerFollower);
24
24
 
25
25
  return observerFollower;
26
26
  },
@@ -39,7 +39,7 @@ export const Store = (function() {
39
39
  */
40
40
  create(name, value) {
41
41
  const observer = Observable(value);
42
- $stores.set(name, { observer, followers: new Set()});
42
+ $stores.set(name, { observer, subscribers: new Set()});
43
43
  return observer;
44
44
  },
45
45
  /**
@@ -54,9 +54,9 @@ export const Store = (function() {
54
54
  /**
55
55
  *
56
56
  * @param {string} name
57
- * @returns {{observer: ObservableItem, followers: Set}}
57
+ * @returns {{observer: ObservableItem, subscribers: Set}}
58
58
  */
59
- getWithFollowers(name) {
59
+ getWithSubscribers(name) {
60
60
  return $stores.get(name);
61
61
  },
62
62
  /**
@@ -67,7 +67,7 @@ export const Store = (function() {
67
67
  const item = $stores.get(name);
68
68
  if(!item) return;
69
69
  item.observer.cleanup();
70
- item.followers.forEach(follower => follower.destroy());
70
+ item.subscribers.forEach(follower => follower.destroy());
71
71
  item.observer.clear();
72
72
  }
73
73
  };
@@ -10,7 +10,7 @@ const getChildAsNode = (child) => {
10
10
  if(Validator.isElement(child)) {
11
11
  return child;
12
12
  }
13
- return createTextNode(String(child))
13
+ return createTextNode(child)
14
14
  }
15
15
 
16
16
  export default function Anchor(name) {
@@ -9,7 +9,7 @@ import MemoryRouter from "./modes/MemoryRouter.js";
9
9
  import DebugManager from "../utils/debug-manager.js";
10
10
  import {RouterComponent} from "./RouterComponent.js";
11
11
 
12
- const DEFAULT_ROUTER_NAME = 'default';
12
+ export const DEFAULT_ROUTER_NAME = 'default';
13
13
 
14
14
  /**
15
15
  *
@@ -209,21 +209,21 @@ Router.create = function(options, callback) {
209
209
 
210
210
  router.init(options.entry);
211
211
 
212
- return {
213
- mount: (container) => {
214
- if(Validator.isString(container)) {
215
- const mountContainer = document.querySelector(container);
216
- if(!mountContainer) {
217
- throw new RouterError(`Container not found for selector: ${container}`);
218
- }
219
- container = mountContainer;
220
- } else if(!Validator.isElement(container)) {
221
- throw new RouterError('Container must be a string or an Element');
212
+ router.mount = function(container) {
213
+ if(Validator.isString(container)) {
214
+ const mountContainer = document.querySelector(container);
215
+ if(!mountContainer) {
216
+ throw new RouterError(`Container not found for selector: ${container}`);
222
217
  }
223
-
224
- RouterComponent(router, container);
218
+ container = mountContainer;
219
+ } else if(!Validator.isElement(container)) {
220
+ throw new RouterError('Container must be a string or an Element');
225
221
  }
222
+
223
+ return RouterComponent(router, container);
226
224
  };
225
+
226
+ return router;
227
227
  };
228
228
 
229
229
  Router.get = function(name) {
@@ -1,20 +1,23 @@
1
1
  import Validator from "../utils/validator.js";
2
2
  import {Link as NativeLink} from "../../elements.js";
3
- import Router from "./Router.js";
3
+ import Router, {DEFAULT_ROUTER_NAME} from "./Router.js";
4
4
  import RouterError from "../errors/RouterError.js";
5
5
 
6
6
 
7
- export function Link(attributes, children){
8
- const target = attributes.to || attributes.href;
7
+ export function Link(options, children){
8
+ const { to, href, ...attributes } = options;
9
+ const target = to || href;
9
10
  if(Validator.isString(target)) {
10
11
  const router = Router.get();
11
12
  return NativeLink({ ...attributes, href: target}, children).nd.on.prevent.click(() => {
12
13
  router.push(target);
13
14
  });
14
15
  }
15
- const router = Router.get(target.router);
16
+ const routerName = target.router || DEFAULT_ROUTER_NAME;
17
+ const router = Router.get(routerName);
18
+ console.log(routerName)
16
19
  if(!router) {
17
- throw new RouterError('Router not found "'+target.router+'" for link "'+target.name+'"');
20
+ throw new RouterError('Router not found "'+routerName+'" for link "'+target.name+'"');
18
21
  }
19
22
  const url = router.generateUrl(target.name, target.params, target.query);
20
23
  return NativeLink({ ...attributes, href: url }, children).nd.on.prevent.click(() => {
@@ -29,4 +29,17 @@ String.prototype.use = function(args) {
29
29
  return data;
30
30
  });
31
31
  }, Object.values(args));
32
+ };
33
+
34
+ String.prototype.resolveObservableTemplate = function() {
35
+ if(!Validator.containsObservableReference(this)) {
36
+ return this;
37
+ }
38
+ return this.split(/(\{\{#ObItem::\([0-9]+\)\}\})/g).filter(Boolean).map((value) => {
39
+ if(!Validator.containsObservableReference(value)) {
40
+ return value;
41
+ }
42
+ const [_, id] = value.match(/\{\{#ObItem::\(([0-9]+)\)\}\}/);
43
+ return Observable.getById(id);
44
+ });
32
45
  }
@@ -70,6 +70,11 @@ const Validator = {
70
70
 
71
71
  return children;
72
72
  },
73
+ /**
74
+ * Check if the data contains observables.
75
+ * @param {Array|Object} data
76
+ * @returns {boolean}
77
+ */
73
78
  containsObservables(data) {
74
79
  if(!data) {
75
80
  return false;
@@ -77,6 +82,17 @@ const Validator = {
77
82
  return Validator.isObject(data)
78
83
  && Object.values(data).some(value => Validator.isObservable(value));
79
84
  },
85
+ /**
86
+ * Check if the data contains an observable reference.
87
+ * @param {string} data
88
+ * @returns {boolean}
89
+ */
90
+ containsObservableReference(data) {
91
+ if(!data || typeof data !== 'string') {
92
+ return false;
93
+ }
94
+ return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
95
+ },
80
96
  validateAttributes(attributes) {
81
97
  if (!attributes || typeof attributes !== 'object') {
82
98
  return attributes;
@@ -1,6 +1,7 @@
1
1
  import Validator from "../utils/validator";
2
2
  import NativeDocumentError from "../errors/NativeDocumentError";
3
3
  import {BOOLEAN_ATTRIBUTES} from "./constants.js";
4
+ import {Observable} from "../data/Observable";
4
5
 
5
6
  /**
6
7
  *
@@ -124,7 +125,16 @@ export default function AttributesWrapper(element, attributes) {
124
125
 
125
126
  for(let key in attributes) {
126
127
  const attributeName = key.toLowerCase();
127
- const value = attributes[attributeName];
128
+ let value = attributes[attributeName];
129
+ if(Validator.isString(value) && Validator.isFunction(value.resolveObservableTemplate)) {
130
+ value = value.resolveObservableTemplate();
131
+ if(Validator.isArray(value)) {
132
+ const observables = value.filter(item => Validator.isObservable(item));
133
+ value = Observable.computed(() => {
134
+ return value.map(item => Validator.isObservable(item) ? item.val() : item).join(' ') || ' ';
135
+ }, observables);
136
+ }
137
+ }
128
138
  if(BOOLEAN_ATTRIBUTES.includes(attributeName)) {
129
139
  bindBooleanAttribute(element, attributeName, value);
130
140
  continue;
@@ -117,6 +117,9 @@ export const ElementCreator = {
117
117
  const childrenArray = Array.isArray(children) ? children : [children];
118
118
  childrenArray.forEach(child => {
119
119
  if (child === null) return;
120
+ if(Validator.isString(child) && Validator.isFunction(child.resolveObservableTemplate)) {
121
+ child = child.resolveObservableTemplate();
122
+ }
120
123
  if(Validator.isFunction(child)) {
121
124
  this.processChildren(child(), parent);
122
125
  return;
@@ -195,7 +198,7 @@ export default function HtmlElementWrapper(name, customWrapper) {
195
198
  } catch (error) {
196
199
  DebugManager.error('ElementCreation', `Error creating ${$tagName}`, error);
197
200
  }
198
- }
201
+ };
199
202
 
200
203
  builder.hold = (children, attributes) => (() => builder(children, attributes));
201
204