native-document 1.0.53 → 1.0.54

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.
@@ -1,8 +1,5 @@
1
1
  import Validator from "../../utils/validator";
2
2
  import {Observable} from "../Observable";
3
- import ObservableItem from "../ObservableItem";
4
-
5
-
6
3
 
7
4
  const ObservableObjectValue = function(data) {
8
5
  const result = {};
@@ -45,40 +42,47 @@ const ObservableGet = function(target, property) {
45
42
  /**
46
43
  *
47
44
  * @param {Object} initialValue
48
- * @param {{propagation: boolean, deep: boolean}} configs
45
+ * @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
49
46
  * @returns {Proxy}
50
47
  */
51
- Observable.init = function(initialValue, { propagation= false, deep = true } = {}) {
48
+ Observable.init = function(initialValue, configs = null) {
52
49
  const data = {};
53
50
  for(const key in initialValue) {
54
51
  const itemValue = initialValue[key];
55
52
  if(Array.isArray(itemValue)) {
56
- if(deep) {
53
+ if(configs?.deep !== false) {
57
54
  const mappedItemValue = itemValue.map(item => {
58
55
  if(Validator.isJson(item)) {
59
- return Observable.json(item, { propagation, deep });
56
+ return Observable.json(item, configs);
60
57
  }
61
58
  if(Validator.isArray(item)) {
62
- return Observable.array(item, { propagation, deep });
59
+ return Observable.array(item, configs);
63
60
  }
64
- return Observable(item);
61
+ return Observable(item, configs);
65
62
  });
66
- data[key] = Observable.array(mappedItemValue, { propagation });
63
+ data[key] = Observable.array(mappedItemValue, configs);
67
64
  continue;
68
65
  }
69
- data[key] = Observable.array(itemValue, { propagation });
66
+ data[key] = Observable.array(itemValue, configs);
70
67
  continue;
71
68
  }
72
69
  if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
73
70
  data[key] = itemValue;
74
71
  continue;
75
72
  }
76
- data[key] = Observable(itemValue);
73
+ data[key] = Observable(itemValue, configs);
74
+ }
75
+
76
+ const $reset = () => {
77
+ for(const key in data) {
78
+ const item = data[key];
79
+ item.reset();
80
+ }
77
81
  }
78
82
 
79
83
  const $val = () => ObservableObjectValue(data);
80
84
 
81
- const $clone = () => Observable.init($val(), { propagation, deep });
85
+ const $clone = () => Observable.init($val(), configs);
82
86
 
83
87
  const $updateWith = (values) => {
84
88
  Observable.update(proxy, values);
@@ -88,34 +92,17 @@ Observable.init = function(initialValue, { propagation= false, deep = true } = {
88
92
 
89
93
  const proxy = new Proxy(data, {
90
94
  get(target, property) {
91
- if(property === '__isProxy__') {
92
- return true;
93
- }
94
- if(property === '$value') {
95
- return $val();
96
- }
97
- if(property === '$clone') {
98
- return $clone;
99
- }
100
- if(property === '$keys') {
101
- return Object.keys(initialValue);
102
- }
103
- if(property === '$observables') {
104
- return Object.values(target);
105
- }
106
- if(property === '$set' || property === '$updateWith') {
107
- return $updateWith;
108
- }
109
- if(property === '$get') {
110
- return $get;
111
- }
112
- if(property === '$val') {
113
- return $val;
114
- }
115
- if(target[property] !== undefined) {
116
- return target[property];
117
- }
118
- return undefined;
95
+ if(property === '__isProxy__') { return true; }
96
+ if(property === '$value') { return $val() }
97
+ if(property === 'get' || property === '$get') { return $get; }
98
+ if(property === 'val' || property === '$val') { return $val; }
99
+ if(property === 'set' || property === '$set' || property === '$updateWith') { return $updateWith; }
100
+ if(property === 'observables' || property === '$observables') { return Object.values(target); }
101
+ if(property === 'keys'|| property === '$keys') { return Object.keys(initialValue); }
102
+ if(property === 'clone' || property === '$clone') { return $clone; }
103
+ if(property === 'reset') { return $reset; }
104
+ if(property === 'configs') { return configs; }
105
+ return target[property];
119
106
  },
120
107
  set(target, prop, newValue) {
121
108
  if(target[prop] !== undefined) {
@@ -166,6 +153,7 @@ Observable.value = function(data) {
166
153
 
167
154
  Observable.update = function($target, newData) {
168
155
  const data = Validator.isProxy(newData) ? newData.$value : newData;
156
+ const configs = $target.configs;
169
157
 
170
158
  for(const key in data) {
171
159
  const targetItem = $target[key];
@@ -178,9 +166,9 @@ Observable.update = function($target, newData) {
178
166
  if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
179
167
  const newValues = newValue.map(item => {
180
168
  if(Validator.isProxy(firstElementFromOriginalValue)) {
181
- return Observable.init(item);
169
+ return Observable.init(item, configs);
182
170
  }
183
- return Observable(item);
171
+ return Observable(item, configs);
184
172
  });
185
173
  targetItem.set(newValues);
186
174
  continue;
@@ -49,9 +49,20 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
49
49
  if(content) {
50
50
  anchor.appendChild(content);
51
51
  }
52
- })
52
+ });
53
53
 
54
- return anchor;
54
+ return anchor.nd.with({
55
+ add(key, view, shouldFocusOn = false) {
56
+ values[key] = view;
57
+ if(shouldFocusOn) {
58
+ $condition.set(key);
59
+ }
60
+ },
61
+ remove(key) {
62
+ shouldKeepInCache && cache.delete(key);
63
+ delete values[key];
64
+ }
65
+ });
55
66
  }
56
67
 
57
68
 
@@ -57,4 +57,36 @@ export const getKey = (item, defaultKey, key) => {
57
57
 
58
58
  export const trim = function(str, char) {
59
59
  return str.replace(new RegExp(`^[${char}]+|[${char}]+$`, 'g'), '');
60
- }
60
+ }
61
+
62
+ export const deepClone = (value, onObservableFound) => {
63
+ // Primitives
64
+ if (value === null || typeof value !== 'object') {
65
+ return value;
66
+ }
67
+
68
+ // Dates
69
+ if (value instanceof Date) {
70
+ return new Date(value.getTime());
71
+ }
72
+
73
+ // Arrays
74
+ if (Array.isArray(value)) {
75
+ return value.map(item => deepClone(item));
76
+ }
77
+
78
+ // Observables - keep the référence
79
+ if (Validator.isObservable(value)) {
80
+ onObservableFound && onObservableFound(value);
81
+ return value;
82
+ }
83
+
84
+ // Objects
85
+ const cloned = {};
86
+ for (const key in value) {
87
+ if (value.hasOwnProperty(key)) {
88
+ cloned[key] = deepClone(value[key]);
89
+ }
90
+ }
91
+ return cloned;
92
+ };
@@ -50,7 +50,7 @@ const Validator = {
50
50
  return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
51
51
  },
52
52
  isObject(value) {
53
- return typeof value === 'object';
53
+ return typeof value === 'object' && value !== null;
54
54
  },
55
55
  isJson(value) {
56
56
  return typeof value === 'object' && value !== null && !Array.isArray(value) && value.constructor.name === 'Object';
@@ -1,6 +1,9 @@
1
1
  import DocumentObserver from "./DocumentObserver";
2
2
  import PluginsManager from "../utils/plugins-manager";
3
3
  import Validator from "../utils/validator";
4
+ import NativeDocumentError from "../errors/NativeDocumentError.js";
5
+ import DebugManager from "../utils/debug-manager.js";
6
+
4
7
  import {EVENTS} from "../utils/events";
5
8
 
6
9
  export function NDElement(element) {
@@ -19,6 +22,11 @@ NDElement.prototype.ref = function(target, name) {
19
22
  return this;
20
23
  };
21
24
 
25
+ NDElement.prototype.refSelf = function(target, name) {
26
+ target[name] = this;
27
+ return this;
28
+ };
29
+
22
30
  NDElement.prototype.unmountChildren = function() {
23
31
  let element = this.$element;
24
32
  for(let i = 0, length = element.children.length; i < length; i++) {
@@ -209,4 +217,78 @@ for(const event of EVENTS) {
209
217
  captureEventWrapper(this.$element, eventName, directHandler);
210
218
  return this;
211
219
  };
212
- }
220
+ }
221
+
222
+ NDElement.prototype.with = function(methods) {
223
+ if (!methods || typeof methods !== 'object') {
224
+ throw new NativeDocumentError('extend() requires an object of methods');
225
+ }
226
+ if(process.env.NODE_ENV === 'development') {
227
+ if (!this.$localExtensions) {
228
+ this.$localExtensions = new Map();
229
+ }
230
+ }
231
+
232
+ for (const name in methods) {
233
+ const method = methods[name];
234
+
235
+ if (typeof method !== 'function') {
236
+ console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
237
+ continue;
238
+ }
239
+ if(process.env.NODE_ENV === 'development') {
240
+ if (this[name] && !this.$localExtensions.has(name)) {
241
+ DebugManager.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
242
+ }
243
+ this.$localExtensions.set(name, method);
244
+ }
245
+
246
+ this[name] = method.bind(this);
247
+ }
248
+
249
+ return this;
250
+ }
251
+
252
+ NDElement.extend = function(methods) {
253
+ if (!methods || typeof methods !== 'object') {
254
+ throw new NativeDocumentError('NDElement.extend() requires an object of methods');
255
+ }
256
+
257
+ if (Array.isArray(methods)) {
258
+ throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
259
+ }
260
+
261
+ const protectedMethods = new Set([
262
+ 'constructor', 'valueOf', '$element', '$observer',
263
+ 'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
264
+ 'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
265
+ ]);
266
+
267
+ for (const name in methods) {
268
+ if (!methods.hasOwnProperty(name)) {
269
+ continue;
270
+ }
271
+
272
+ const method = methods[name];
273
+
274
+ if (typeof method !== 'function') {
275
+ DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
276
+ continue;
277
+ }
278
+
279
+ if (protectedMethods.has(name)) {
280
+ DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
281
+ throw new NativeDocumentError(`Cannot override protected method "${name}"`);
282
+ }
283
+
284
+ if (NDElement.prototype[name]) {
285
+ DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
286
+ }
287
+
288
+ NDElement.prototype[name] = method;
289
+ }
290
+
291
+ PluginsManager.emit('NDElementExtended', methods);
292
+
293
+ return NDElement;
294
+ };
@@ -1,9 +1,19 @@
1
1
  import { NDElement } from "./NDElement";
2
2
 
3
- Object.defineProperty(HTMLElement.prototype, 'nd', {
3
+ const property = {
4
4
  configurable: true,
5
5
  get() {
6
- return new NDElement(this);
6
+ return new NDElement(this);
7
+ }
8
+ };
9
+
10
+ Object.defineProperty(HTMLElement.prototype, 'nd', property);
11
+
12
+ Object.defineProperty(DocumentFragment.prototype, 'nd', property);
13
+ Object.defineProperty(NDElement.prototype, 'nd', {
14
+ configurable: true,
15
+ get: function() {
16
+ return this;
7
17
  }
8
18
  });
9
19
 
@@ -1,16 +1,26 @@
1
1
 
2
+ type OnceProxy<T> = {
3
+ [K in keyof T]: T[K];
4
+ };
5
+
6
+ type MemoizeProxy<T> = {
7
+ [key: string]: T;
8
+ [key: symbol]: T;
9
+ };
10
+
2
11
  export function once<T extends (...args: any[]) => any>(
3
12
  fn: T
4
13
  ): (...args: Parameters<T>) => ReturnType<T>;
5
14
 
6
- export function autoOnce<T extends object>(
7
- fn: () => T
8
- ): T;
15
+
16
+ export function autoOnce<T extends object>(fn: () => T): OnceProxy<T>;
9
17
 
10
18
  export function memoize<T extends (...args: any[]) => any>(
11
19
  fn: T
12
20
  ): (key: any, ...args: Parameters<T>) => ReturnType<T>;
13
21
 
14
- export function autoMemoize<T extends (...args: any[]) => any>(
15
- fn: T
16
- ): Record<PropertyKey, Parameters<T>['length'] extends 0 ? ReturnType<T> : T>;
22
+ export function autoMemoize<T>(fn: () => T): MemoizeProxy<T>;
23
+
24
+ export function autoMemoize<T, Args extends any[]>(
25
+ fn: (...args: Args) => T
26
+ ): MemoizeProxy<(...args: Args) => T>;