native-document 1.0.13 → 1.0.15
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/dist/native-document.dev.js +1297 -804
- package/dist/native-document.min.js +1 -1
- package/docs/anchor.md +216 -53
- package/docs/conditional-rendering.md +25 -24
- package/docs/core-concepts.md +20 -19
- package/docs/elements.md +21 -20
- package/docs/getting-started.md +28 -27
- package/docs/lifecycle-events.md +2 -2
- package/docs/list-rendering.md +607 -0
- package/docs/memory-management.md +1 -1
- package/docs/observables.md +15 -14
- package/docs/routing.md +22 -22
- package/docs/state-management.md +8 -8
- package/docs/validation.md +0 -2
- package/index.js +6 -1
- package/package.json +1 -1
- package/readme.md +5 -4
- package/src/data/MemoryManager.js +8 -20
- package/src/data/Observable.js +2 -180
- package/src/data/ObservableChecker.js +26 -21
- package/src/data/ObservableItem.js +158 -79
- package/src/data/observable-helpers/array.js +74 -0
- package/src/data/observable-helpers/batch.js +22 -0
- package/src/data/observable-helpers/computed.js +28 -0
- package/src/data/observable-helpers/object.js +111 -0
- package/src/elements/anchor.js +54 -9
- package/src/elements/control/for-each-array.js +280 -0
- package/src/elements/control/for-each.js +100 -56
- package/src/elements/index.js +1 -0
- package/src/elements/list.js +4 -0
- package/src/utils/helpers.js +44 -21
- package/src/wrappers/AttributesWrapper.js +5 -18
- package/src/wrappers/DocumentObserver.js +58 -29
- package/src/wrappers/ElementCreator.js +114 -0
- package/src/wrappers/HtmlElementEventsWrapper.js +52 -65
- package/src/wrappers/HtmlElementWrapper.js +11 -167
- package/src/wrappers/NdPrototype.js +109 -0
package/docs/routing.md
CHANGED
|
@@ -172,20 +172,20 @@ Add new entries to browser history. **Specify router name** when using multiple
|
|
|
172
172
|
|
|
173
173
|
```javascript
|
|
174
174
|
const NavigationExample = Div([
|
|
175
|
-
Button('Go to About').nd.
|
|
175
|
+
Button('Go to About').nd.onClick(() => {
|
|
176
176
|
Router.push('/about'); // Uses default router
|
|
177
177
|
}),
|
|
178
178
|
|
|
179
|
-
Button('Go to About (Main Router)').nd.
|
|
179
|
+
Button('Go to About (Main Router)').nd.onClick(() => {
|
|
180
180
|
Router.push('/about', 'main'); // Uses named router
|
|
181
181
|
}),
|
|
182
182
|
|
|
183
|
-
Button('View User 123').nd.
|
|
183
|
+
Button('View User 123').nd.onClick(() => {
|
|
184
184
|
// Navigate in specific router
|
|
185
185
|
Router.push('/users/123', 'app');
|
|
186
186
|
}),
|
|
187
187
|
|
|
188
|
-
Button('Search Products').nd.
|
|
188
|
+
Button('Search Products').nd.onClick(() => {
|
|
189
189
|
Router.push('/search?term=laptop&category=electronics', 'main');
|
|
190
190
|
})
|
|
191
191
|
]);
|
|
@@ -213,12 +213,12 @@ Navigate through browser history:
|
|
|
213
213
|
|
|
214
214
|
```javascript
|
|
215
215
|
const HistoryControls = Div([
|
|
216
|
-
Button('Go Back').nd.
|
|
217
|
-
Button('Go Forward').nd.
|
|
216
|
+
Button('Go Back').nd.onClick(() => Router.back()), // Default router
|
|
217
|
+
Button('Go Forward').nd.onClick(() => Router.forward()), // Default router
|
|
218
218
|
|
|
219
219
|
// Navigate specific router's history
|
|
220
|
-
Button('Back in Main').nd.
|
|
221
|
-
Button('Forward in Admin').nd.
|
|
220
|
+
Button('Back in Main').nd.onClick(() => Router.back('main')),
|
|
221
|
+
Button('Forward in Admin').nd.onClick(() => Router.forward('admin'))
|
|
222
222
|
]);
|
|
223
223
|
```
|
|
224
224
|
|
|
@@ -245,18 +245,18 @@ const blogUrl = router.generateUrl('blog.post', { category: 'javascript', slug:
|
|
|
245
245
|
|
|
246
246
|
```javascript
|
|
247
247
|
const navigation = Div([
|
|
248
|
-
Button('Home').nd.
|
|
248
|
+
Button('Home').nd.onClick(() =>
|
|
249
249
|
Router.push({ name: 'home' }) // Uses router containing this route
|
|
250
250
|
),
|
|
251
251
|
|
|
252
|
-
Button('My Profile').nd.
|
|
252
|
+
Button('My Profile').nd.onClick(() =>
|
|
253
253
|
Router.push({
|
|
254
254
|
name: 'user.profile',
|
|
255
255
|
params: { id: currentUser.id }
|
|
256
256
|
}, 'main') // Specify router if needed
|
|
257
257
|
),
|
|
258
258
|
|
|
259
|
-
Button('Latest Post').nd.
|
|
259
|
+
Button('Latest Post').nd.onClick(() =>
|
|
260
260
|
Router.push({
|
|
261
261
|
name: 'blog.post',
|
|
262
262
|
params: { category: 'news', slug: 'latest-update' },
|
|
@@ -467,11 +467,11 @@ const mainRouter = Router.routers.main;
|
|
|
467
467
|
const adminRouter = Router.routers.admin;
|
|
468
468
|
|
|
469
469
|
// Cross-router navigation
|
|
470
|
-
Button('Go to Admin').nd.
|
|
470
|
+
Button('Go to Admin').nd.onClick(() => {
|
|
471
471
|
Router.push('/admin/users', 'admin'); // Specify router name
|
|
472
472
|
});
|
|
473
473
|
|
|
474
|
-
Button('Back to Main').nd.
|
|
474
|
+
Button('Back to Main').nd.onClick(() => {
|
|
475
475
|
Router.push('/', 'main'); // Navigate in main router
|
|
476
476
|
});
|
|
477
477
|
```
|
|
@@ -707,7 +707,7 @@ const ProductDetail = ({ params, query }) => {
|
|
|
707
707
|
P(p.description),
|
|
708
708
|
|
|
709
709
|
// Add to cart with redirect to login if needed
|
|
710
|
-
Button('Add to Cart').nd.
|
|
710
|
+
Button('Add to Cart').nd.onClick(() => {
|
|
711
711
|
if (!isAuthenticated()) {
|
|
712
712
|
Router.push({
|
|
713
713
|
name: 'login',
|
|
@@ -773,22 +773,22 @@ const FormNavigation = (currentStep, formData) => {
|
|
|
773
773
|
// Navigation buttons
|
|
774
774
|
Div({ class: 'nav-buttons' }, [
|
|
775
775
|
ShowIf(Observable(currentIndex > 0),
|
|
776
|
-
Button('Previous').nd.
|
|
776
|
+
Button('Previous').nd.onClick(() => {
|
|
777
777
|
const prevStep = steps[currentIndex - 1];
|
|
778
778
|
Router.push({ name: `form.${prevStep}` });
|
|
779
779
|
})
|
|
780
780
|
),
|
|
781
781
|
|
|
782
782
|
ShowIf(Observable(currentIndex < steps.length - 1),
|
|
783
|
-
Button('Next').nd.
|
|
783
|
+
Button('Next').nd.onClick(() => {
|
|
784
784
|
const nextStep = steps[currentIndex + 1];
|
|
785
785
|
Router.push({ name: `form.${nextStep}` });
|
|
786
786
|
})
|
|
787
787
|
),
|
|
788
788
|
|
|
789
789
|
ShowIf(Observable(currentIndex === steps.length - 1),
|
|
790
|
-
Button('Submit').nd.
|
|
791
|
-
submitForm(formData.$
|
|
790
|
+
Button('Submit').nd.onClick(() => {
|
|
791
|
+
submitForm(formData.$value);
|
|
792
792
|
})
|
|
793
793
|
)
|
|
794
794
|
])
|
|
@@ -811,7 +811,7 @@ const FormNavigation = (currentStep, formData) => {
|
|
|
811
811
|
|
|
812
812
|
Explore these related topics to build complete applications:
|
|
813
813
|
|
|
814
|
-
- **[State Management](
|
|
815
|
-
- **[Lifecycle Events](
|
|
816
|
-
- **[Memory Management](
|
|
817
|
-
- **[Anchor](
|
|
814
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
815
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
816
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
817
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/state-management.md
CHANGED
|
@@ -95,7 +95,7 @@ const UserMenu = () => {
|
|
|
95
95
|
return Nav([
|
|
96
96
|
ShowIf(user.check(u => u.isLoggedIn), [
|
|
97
97
|
Link({ to: '/profile' }, 'My Profile'),
|
|
98
|
-
Button('Logout').nd.
|
|
98
|
+
Button('Logout').nd.onClick(() => {
|
|
99
99
|
user.set({ ...user.$value, isLoggedIn: false });
|
|
100
100
|
})
|
|
101
101
|
])
|
|
@@ -126,7 +126,7 @@ Modify store state using the returned observable's methods:
|
|
|
126
126
|
const ThemeToggle = () => {
|
|
127
127
|
const theme = Store.use('theme');
|
|
128
128
|
|
|
129
|
-
return Button('Toggle Theme').nd.
|
|
129
|
+
return Button('Toggle Theme').nd.onClick(() => {
|
|
130
130
|
const current = theme.$value;
|
|
131
131
|
theme.set(current === 'light' ? 'dark' : 'light');
|
|
132
132
|
});
|
|
@@ -154,7 +154,7 @@ const LoginForm = () => {
|
|
|
154
154
|
return Form([
|
|
155
155
|
Input({ type: 'email', value: email, placeholder: 'Email' }),
|
|
156
156
|
Input({ type: 'password', value: password, placeholder: 'Password' }),
|
|
157
|
-
Button('Login').nd.
|
|
157
|
+
Button('Login').nd.onClick(handleLogin)
|
|
158
158
|
]);
|
|
159
159
|
};
|
|
160
160
|
```
|
|
@@ -187,9 +187,9 @@ const UserSettings = () => {
|
|
|
187
187
|
Input({
|
|
188
188
|
value: user.check(u => u.name),
|
|
189
189
|
placeholder: 'Name'
|
|
190
|
-
}).nd.
|
|
190
|
+
}).nd.onInput(e => updateName(e.target.value)),
|
|
191
191
|
|
|
192
|
-
Button('Dark Mode').nd.
|
|
192
|
+
Button('Dark Mode').nd.onClick(() =>
|
|
193
193
|
updatePreferences({ theme: 'dark' })
|
|
194
194
|
)
|
|
195
195
|
]);
|
|
@@ -418,6 +418,6 @@ const createAppState = () => {
|
|
|
418
418
|
|
|
419
419
|
Now that you understand state management, explore these related topics:
|
|
420
420
|
|
|
421
|
-
- **[Lifecycle Events](
|
|
422
|
-
- **[Memory Management](
|
|
423
|
-
- **[Anchor](
|
|
421
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
422
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
423
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/validation.md
CHANGED
|
@@ -187,7 +187,5 @@ registerUser.args(
|
|
|
187
187
|
|
|
188
188
|
## Next Steps
|
|
189
189
|
|
|
190
|
-
- **[Advanced Features](advanced-features.md)** - Error boundaries and debugging tools
|
|
191
|
-
- **[API Reference](api-reference.md)** - Complete ArgTypes and validation API
|
|
192
190
|
- **[Memory Management](memory-management.md)** - Debugging memory issues with validation
|
|
193
191
|
- **[Lifecycle Events](lifecycle-events.md)** - Validate lifecycle callback arguments
|
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { default as HtmlElementWrapper
|
|
1
|
+
export { default as HtmlElementWrapper } from './src/wrappers/HtmlElementWrapper'
|
|
2
|
+
export { ElementCreator } from './src/wrappers/ElementCreator'
|
|
2
3
|
|
|
3
4
|
import './src/utils/prototypes.js';
|
|
4
5
|
|
|
@@ -6,6 +7,10 @@ export * from './src/utils/plugins-manager';
|
|
|
6
7
|
export * from './src/utils/args-types';
|
|
7
8
|
export * from './src/utils/validator'
|
|
8
9
|
export * from './src/data/Observable';
|
|
10
|
+
export * from './src/data/observable-helpers/array';
|
|
11
|
+
export * from './src/data/observable-helpers/batch';
|
|
12
|
+
export * from './src/data/observable-helpers/object';
|
|
13
|
+
export * from './src/data/observable-helpers/computed';
|
|
9
14
|
export * from './src/data/Store';
|
|
10
15
|
import * as elements from './elements';
|
|
11
16
|
import * as router from './router';
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -30,7 +30,7 @@ const count = Observable(0);
|
|
|
30
30
|
const App = Div({ class: 'app' }, [
|
|
31
31
|
Div([ 'Count ', count]),
|
|
32
32
|
// OR Div(`Count ${count}`),
|
|
33
|
-
Button('Increment').nd.
|
|
33
|
+
Button('Increment').nd.onClick(() => count.set(count.val() + 1))
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
36
|
document.body.appendChild(App);
|
|
@@ -91,7 +91,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
91
91
|
Input({ placeholder: 'Add new task...', value: newTodo }),
|
|
92
92
|
|
|
93
93
|
// Add button
|
|
94
|
-
Button('Add Todo').nd.
|
|
94
|
+
Button('Add Todo').nd.onClick(() => {
|
|
95
95
|
if (newTodo.val().trim()) {
|
|
96
96
|
todos.push({ id: Date.now(), text: newTodo.val(), done: false })
|
|
97
97
|
newTodo.set('')
|
|
@@ -103,7 +103,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
103
103
|
Div({ class: 'todo-item' }, [
|
|
104
104
|
Input({ type: 'checkbox', checked: todo.done }),
|
|
105
105
|
`${todo.text}`,
|
|
106
|
-
Button('Delete').nd.
|
|
106
|
+
Button('Delete').nd.onClick(() => todos.splice(index.val(), 1))
|
|
107
107
|
]), /*item key (string | callback) */(item) => item),
|
|
108
108
|
|
|
109
109
|
// Empty state
|
|
@@ -158,7 +158,7 @@ const App = function() {
|
|
|
158
158
|
class: { 'hidden': isVisible.check(v => !v) },
|
|
159
159
|
style: { opacity: isVisible.check(v => v ? 1 : 0.2) }
|
|
160
160
|
}, 'Content'),
|
|
161
|
-
Button('Toggle').nd.
|
|
161
|
+
Button('Toggle').nd.onClick(() => isVisible.set(v => !v)),
|
|
162
162
|
]);
|
|
163
163
|
};
|
|
164
164
|
|
|
@@ -191,6 +191,7 @@ When(condition)
|
|
|
191
191
|
- **[Observables](docs/observables.md)** - Reactive state management
|
|
192
192
|
- **[Elements](docs/elements.md)** - Creating and composing UI
|
|
193
193
|
- **[Conditional Rendering](docs/conditional-rendering.md)** - Dynamic content
|
|
194
|
+
- **[List Rendering](docs/list-rendering.md)** - List Rendering
|
|
194
195
|
- **[Routing](docs/routing.md)** - Navigation and URL management
|
|
195
196
|
- **[State Management](docs/state-management.md)** - Global state patterns
|
|
196
197
|
- **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
|
|
@@ -1,40 +1,28 @@
|
|
|
1
1
|
import DebugManager from "../utils/debug-manager";
|
|
2
|
+
import Validator from "../utils/validator";
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
const MemoryManager = (function() {
|
|
5
6
|
|
|
6
|
-
let $
|
|
7
|
+
let $nextObserverId = 0;
|
|
7
8
|
const $observables = new Map();
|
|
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
|
-
}
|
|
17
9
|
|
|
18
10
|
return {
|
|
19
11
|
/**
|
|
20
12
|
* Register an observable and return an id.
|
|
21
13
|
*
|
|
22
14
|
* @param {ObservableItem} observable
|
|
23
|
-
* @param {Function
|
|
15
|
+
* @param {Function} getListeners
|
|
24
16
|
* @returns {number}
|
|
25
17
|
*/
|
|
26
|
-
register(observable
|
|
27
|
-
const id = ++$
|
|
28
|
-
const heldValue = {
|
|
29
|
-
id: id,
|
|
30
|
-
listeners
|
|
31
|
-
};
|
|
32
|
-
if($registry) {
|
|
33
|
-
$registry.register(observable, heldValue);
|
|
34
|
-
}
|
|
18
|
+
register(observable) {
|
|
19
|
+
const id = ++$nextObserverId;
|
|
35
20
|
$observables.set(id, new WeakRef(observable));
|
|
36
21
|
return id;
|
|
37
22
|
},
|
|
23
|
+
unregister(id) {
|
|
24
|
+
$observables.delete(id);
|
|
25
|
+
},
|
|
38
26
|
getObservableById(id) {
|
|
39
27
|
return $observables.get(id)?.deref();
|
|
40
28
|
},
|
package/src/data/Observable.js
CHANGED
|
@@ -2,7 +2,7 @@ import ObservableItem from './ObservableItem';
|
|
|
2
2
|
import Validator from "../utils/validator";
|
|
3
3
|
import MemoryManager from "./MemoryManager";
|
|
4
4
|
import NativeDocumentError from "../errors/NativeDocumentError";
|
|
5
|
-
import {
|
|
5
|
+
import {debounce} from "../utils/helpers.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
*
|
|
@@ -14,45 +14,6 @@ export function Observable(value) {
|
|
|
14
14
|
return new ObservableItem(value);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* @param {Function} callback
|
|
20
|
-
* @param {Array|Function} dependencies
|
|
21
|
-
* @returns {ObservableItem}
|
|
22
|
-
*/
|
|
23
|
-
Observable.computed = function(callback, dependencies = []) {
|
|
24
|
-
const initialValue = callback();
|
|
25
|
-
const observable = new ObservableItem(initialValue);
|
|
26
|
-
const updatedValue = () => observable.set(callback());
|
|
27
|
-
|
|
28
|
-
if(Validator.isFunction(dependencies)) {
|
|
29
|
-
if(!Validator.isObservable(dependencies.$observer)) {
|
|
30
|
-
throw new NativeDocumentError('Observable.computed : dependencies must be valid batch function');
|
|
31
|
-
}
|
|
32
|
-
dependencies.$observer.subscribe(updatedValue);
|
|
33
|
-
return observable;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
dependencies.forEach(dependency => dependency.subscribe(updatedValue));
|
|
37
|
-
|
|
38
|
-
return observable;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
Observable.batch = function(callback) {
|
|
42
|
-
const $observer = Observable(0);
|
|
43
|
-
const batch = function() {
|
|
44
|
-
if(Validator.isAsyncFunction(callback)) {
|
|
45
|
-
return (callback(...arguments)).then(() => {
|
|
46
|
-
$observer.trigger();
|
|
47
|
-
}).catch(error => { throw error; });
|
|
48
|
-
}
|
|
49
|
-
callback(...arguments);
|
|
50
|
-
$observer.trigger();
|
|
51
|
-
};
|
|
52
|
-
batch.$observer = $observer;
|
|
53
|
-
return batch;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
17
|
/**
|
|
57
18
|
*
|
|
58
19
|
* @param id
|
|
@@ -66,7 +27,6 @@ Observable.getById = function(id) {
|
|
|
66
27
|
return item;
|
|
67
28
|
};
|
|
68
29
|
|
|
69
|
-
|
|
70
30
|
/**
|
|
71
31
|
*
|
|
72
32
|
* @param {ObservableItem} observable
|
|
@@ -75,144 +35,6 @@ Observable.cleanup = function(observable) {
|
|
|
75
35
|
observable.cleanup();
|
|
76
36
|
};
|
|
77
37
|
|
|
78
|
-
/**
|
|
79
|
-
* Get the value of an observable or an object of observables.
|
|
80
|
-
* @param {ObservableItem|Object<ObservableItem>} object
|
|
81
|
-
* @returns {{}|*|null}
|
|
82
|
-
*/
|
|
83
|
-
Observable.value = function(data) {
|
|
84
|
-
if(Validator.isObservable(data)) {
|
|
85
|
-
return data.val();
|
|
86
|
-
}
|
|
87
|
-
if(Validator.isProxy(data)) {
|
|
88
|
-
return data.$val();
|
|
89
|
-
}
|
|
90
|
-
if(Validator.isArray(data)) {
|
|
91
|
-
const result = [];
|
|
92
|
-
data.forEach(item => {
|
|
93
|
-
result.push(Observable.value(item));
|
|
94
|
-
});
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
return data;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
*
|
|
102
|
-
* @param {Object} value
|
|
103
|
-
* @returns {Proxy}
|
|
104
|
-
*/
|
|
105
|
-
Observable.init = function(value) {
|
|
106
|
-
const data = {};
|
|
107
|
-
for(const key in value) {
|
|
108
|
-
const itemValue = value[key];
|
|
109
|
-
if(Validator.isJson(itemValue)) {
|
|
110
|
-
data[key] = Observable.init(itemValue);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
else if(Validator.isArray(itemValue)) {
|
|
114
|
-
data[key] = Observable.array(itemValue);
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
data[key] = Observable(itemValue);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const $val = function() {
|
|
121
|
-
const result = {};
|
|
122
|
-
for(const key in data) {
|
|
123
|
-
const dataItem = data[key];
|
|
124
|
-
if(Validator.isObservable(dataItem)) {
|
|
125
|
-
result[key] = dataItem.val();
|
|
126
|
-
} else if(Validator.isProxy(dataItem)) {
|
|
127
|
-
result[key] = dataItem.$val();
|
|
128
|
-
} else {
|
|
129
|
-
result[key] = dataItem;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return result;
|
|
133
|
-
};
|
|
134
|
-
const $clone = function() {
|
|
135
|
-
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
return new Proxy(data, {
|
|
139
|
-
get(target, property) {
|
|
140
|
-
if(property === '__isProxy__') {
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
if(property === '$val') {
|
|
144
|
-
return $val;
|
|
145
|
-
}
|
|
146
|
-
if(property === '$clone') {
|
|
147
|
-
return $clone;
|
|
148
|
-
}
|
|
149
|
-
if(target[property] !== undefined) {
|
|
150
|
-
return target[property];
|
|
151
|
-
}
|
|
152
|
-
return undefined;
|
|
153
|
-
},
|
|
154
|
-
set(target, prop, newValue) {
|
|
155
|
-
if(target[prop] !== undefined) {
|
|
156
|
-
target[prop].set(newValue);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
Observable.object = Observable.init;
|
|
163
|
-
Observable.json = Observable.init;
|
|
164
|
-
Observable.update = function($target, data) {
|
|
165
|
-
for(const key in data) {
|
|
166
|
-
const targetItem = $target[key];
|
|
167
|
-
const newValue = data[key];
|
|
168
|
-
|
|
169
|
-
if(Validator.isObservable(targetItem)) {
|
|
170
|
-
if(Validator.isArray(newValue)) {
|
|
171
|
-
Observable.update(targetItem, newValue);
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
targetItem.set(newValue);
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
if(Validator.isProxy(targetItem)) {
|
|
178
|
-
Observable.update(targetItem, newValue);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
$target[key] = newValue;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
/**
|
|
185
|
-
*
|
|
186
|
-
* @param {Array} target
|
|
187
|
-
* @returns {ObservableItem}
|
|
188
|
-
*/
|
|
189
|
-
Observable.array = function(target) {
|
|
190
|
-
if(!Array.isArray(target)) {
|
|
191
|
-
throw new NativeDocumentError('Observable.array : target must be an array');
|
|
192
|
-
}
|
|
193
|
-
const observer = Observable(target);
|
|
194
|
-
|
|
195
|
-
const methods = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'];
|
|
196
|
-
|
|
197
|
-
methods.forEach((method) => {
|
|
198
|
-
observer[method] = function(...values) {
|
|
199
|
-
const target = observer.val();
|
|
200
|
-
const result = target[method].apply(target, arguments);
|
|
201
|
-
observer.trigger();
|
|
202
|
-
return result;
|
|
203
|
-
};
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const overrideMethods = ['map', 'filter', 'reduce', 'some', 'every', 'find'];
|
|
207
|
-
overrideMethods.forEach((method) => {
|
|
208
|
-
observer[method] = function(callback) {
|
|
209
|
-
return observer.val()[method](callback);
|
|
210
|
-
};
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
return observer;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
38
|
/**
|
|
217
39
|
* Enable auto cleanup of observables.
|
|
218
40
|
* @param {Boolean} enable
|
|
@@ -229,5 +51,5 @@ Observable.autoCleanup = function(enable = false, options = {}) {
|
|
|
229
51
|
});
|
|
230
52
|
|
|
231
53
|
setInterval(() => MemoryManager.cleanObservables(threshold), interval);
|
|
232
|
-
}
|
|
54
|
+
};
|
|
233
55
|
|
|
@@ -7,28 +7,33 @@
|
|
|
7
7
|
export default function ObservableChecker($observable, $checker) {
|
|
8
8
|
this.observable = $observable;
|
|
9
9
|
this.checker = $checker;
|
|
10
|
+
this.unSubscriptions = [];
|
|
11
|
+
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
ObservableChecker.prototype.subscribe = function(callback) {
|
|
14
|
+
const unSubscribe = this.observable.subscribe((value) => {
|
|
15
|
+
callback && callback(this.checker(value));
|
|
16
|
+
});
|
|
17
|
+
this.unSubscriptions.push(unSubscribe);
|
|
18
|
+
return unSubscribe;
|
|
19
|
+
};
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.check = function(callback) {
|
|
21
|
-
return $observable.check(() => callback(this.val()));
|
|
22
|
-
};
|
|
21
|
+
ObservableChecker.prototype.check = function(callback) {
|
|
22
|
+
return this.observable.check(() => callback(this.val()));
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.trigger = function() {
|
|
28
|
-
return $observable.trigger();
|
|
29
|
-
}
|
|
25
|
+
ObservableChecker.prototype.val = function() {
|
|
26
|
+
return this.checker && this.checker(this.observable.val());
|
|
27
|
+
}
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
ObservableChecker.prototype.set = function(value) {
|
|
30
|
+
return this.observable.set(value);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
ObservableChecker.prototype.trigger = function() {
|
|
34
|
+
return this.observable.trigger();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ObservableChecker.prototype.cleanup = function() {
|
|
38
|
+
return this.observable.cleanup();
|
|
39
|
+
};
|