native-document 1.0.40 → 1.0.42
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 +160 -34
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.devtools.min.js +1 -1
- package/dist/native-document.min.js +1 -1
- package/elements.js +1 -0
- package/index.d.ts +1 -0
- package/index.js +4 -2
- package/package.json +1 -1
- package/src/data/ObservableItem.js +14 -0
- package/src/data/observable-helpers/array.js +26 -2
- package/src/data/observable-helpers/object.js +4 -0
- package/src/elements/anchor.js +28 -12
- package/src/elements/control/show-if.js +8 -9
- package/src/elements/control/switch.js +1 -2
- package/src/elements/img.js +5 -3
- package/src/router/Route.js +3 -1
- package/src/router/RouteGroupHelper.js +8 -0
- package/src/router/Router.js +2 -1
- package/src/router/RouterComponent.js +8 -3
- package/src/utils/fetch/NativeFetch.js +79 -0
- package/src/utils/validator.js +5 -1
- package/src/wrappers/NDElement.js +0 -2
- package/src/wrappers/SingletonView.js +48 -0
- package/src/wrappers/TemplateCloner.js +2 -0
- package/types/elements.d.ts +17 -1
- package/types/observable.d.ts +3 -0
- package/types/router.d.ts +3 -2
- package/types/singleton.d.ts +19 -0
- package/utils.d.ts +0 -0
- package/utils.js +6 -0
|
@@ -9,7 +9,7 @@ import {ElementCreator} from "../../wrappers/ElementCreator";
|
|
|
9
9
|
*
|
|
10
10
|
* @param {ObservableItem|ObservableChecker} condition
|
|
11
11
|
* @param {*} child
|
|
12
|
-
* @param {{comment?: string|null, shouldKeepInCache?: Boolean}}
|
|
12
|
+
* @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
|
|
13
13
|
* @returns {DocumentFragment}
|
|
14
14
|
*/
|
|
15
15
|
export const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
|
|
@@ -25,7 +25,7 @@ export const ShowIf = function(condition, child, { comment = null, shouldKeepInC
|
|
|
25
25
|
}
|
|
26
26
|
childElement = ElementCreator.getChild(child);
|
|
27
27
|
if(Validator.isFragment(childElement)) {
|
|
28
|
-
childElement = Array.from(childElement.
|
|
28
|
+
childElement = Array.from(childElement.childNodes);
|
|
29
29
|
}
|
|
30
30
|
return childElement;
|
|
31
31
|
};
|
|
@@ -50,15 +50,14 @@ export const ShowIf = function(condition, child, { comment = null, shouldKeepInC
|
|
|
50
50
|
* Hide the element if the condition is true
|
|
51
51
|
* @param {ObservableItem|ObservableChecker} condition
|
|
52
52
|
* @param child
|
|
53
|
-
* @param comment
|
|
53
|
+
* @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
|
|
54
54
|
* @returns {DocumentFragment}
|
|
55
55
|
*/
|
|
56
|
-
export const HideIf = function(condition, child,
|
|
57
|
-
|
|
56
|
+
export const HideIf = function(condition, child, configs) {
|
|
58
57
|
const hideCondition = Observable(!condition.val());
|
|
59
58
|
condition.subscribe(value => hideCondition.set(!value));
|
|
60
59
|
|
|
61
|
-
return ShowIf(hideCondition, child,
|
|
60
|
+
return ShowIf(hideCondition, child, configs);
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
/**
|
|
@@ -66,9 +65,9 @@ export const HideIf = function(condition, child, comment) {
|
|
|
66
65
|
*
|
|
67
66
|
* @param {ObservableItem|ObservableChecker} condition
|
|
68
67
|
* @param {*} child
|
|
69
|
-
* @param {string|null}
|
|
68
|
+
* @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
|
|
70
69
|
* @returns {DocumentFragment}
|
|
71
70
|
*/
|
|
72
|
-
export const HideIfNot = function(condition, child,
|
|
73
|
-
return ShowIf(condition, child,
|
|
71
|
+
export const HideIfNot = function(condition, child, configs) {
|
|
72
|
+
return ShowIf(condition, child, configs);
|
|
74
73
|
}
|
|
@@ -18,7 +18,7 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
18
18
|
throw new NativeDocumentError("Toggle : condition must be an Observable");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const anchor = new Anchor();
|
|
21
|
+
const anchor = new Anchor('Match');
|
|
22
22
|
const cache = new Map();
|
|
23
23
|
|
|
24
24
|
const getItem = function(key) {
|
|
@@ -63,7 +63,6 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
63
63
|
* @returns {DocumentFragment}
|
|
64
64
|
*/
|
|
65
65
|
export const Switch = function ($condition, onTrue, onFalse) {
|
|
66
|
-
|
|
67
66
|
if(!Validator.isObservable($condition)) {
|
|
68
67
|
throw new NativeDocumentError("Toggle : condition must be an Observable");
|
|
69
68
|
}
|
package/src/elements/img.js
CHANGED
|
@@ -16,11 +16,13 @@ export const Img = function(src, attributes) {
|
|
|
16
16
|
* @returns {Image}
|
|
17
17
|
*/
|
|
18
18
|
export const AsyncImg = function(src, defaultImage, attributes, callback) {
|
|
19
|
-
const
|
|
19
|
+
const defaultSrc = Validator.isObservable(src) ? src.val() : src;
|
|
20
|
+
const image = Img(defaultImage || defaultSrc, attributes);
|
|
20
21
|
const img = new Image();
|
|
22
|
+
|
|
21
23
|
img.onload = () => {
|
|
22
24
|
Validator.isFunction(callback) && callback(null, image);
|
|
23
|
-
image.src = src;
|
|
25
|
+
image.src = Validator.isObservable(src) ? src.val() : src;
|
|
24
26
|
};
|
|
25
27
|
img.onerror = () => {
|
|
26
28
|
Validator.isFunction(callback) && callback(new NativeDocumentError('Image not found'));
|
|
@@ -30,7 +32,7 @@ export const AsyncImg = function(src, defaultImage, attributes, callback) {
|
|
|
30
32
|
img.src = newSrc;
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
|
-
img.src =
|
|
35
|
+
img.src = defaultSrc;
|
|
34
36
|
return image;
|
|
35
37
|
};
|
|
36
38
|
|
package/src/router/Route.js
CHANGED
|
@@ -13,7 +13,7 @@ export const RouteParamPatterns = {
|
|
|
13
13
|
*/
|
|
14
14
|
export function Route($path, $component, $options = {}) {
|
|
15
15
|
|
|
16
|
-
$path = '/'+trim($path, '/');
|
|
16
|
+
$path = '/'+trim($path, '/').replace(/\/+/, '/');
|
|
17
17
|
|
|
18
18
|
let $pattern = null;
|
|
19
19
|
let $name = $options.name || null;
|
|
@@ -21,6 +21,7 @@ export function Route($path, $component, $options = {}) {
|
|
|
21
21
|
const $middlewares = $options.middlewares || [];
|
|
22
22
|
const $shouldRebuild = $options.shouldRebuild || false;
|
|
23
23
|
const $paramsValidators = $options.with || {};
|
|
24
|
+
const $layout = $options.layout || null;
|
|
24
25
|
|
|
25
26
|
const $params = {};
|
|
26
27
|
const $paramsNames = [];
|
|
@@ -65,6 +66,7 @@ export function Route($path, $component, $options = {}) {
|
|
|
65
66
|
this.middlewares = () => $middlewares;
|
|
66
67
|
this.shouldRebuild = () => $shouldRebuild;
|
|
67
68
|
this.path = () => $path;
|
|
69
|
+
this.layout = () => $layout;
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
*
|
|
@@ -48,5 +48,13 @@ export const RouteGroupHelper = {
|
|
|
48
48
|
});
|
|
49
49
|
name && fullName.push(name);
|
|
50
50
|
return fullName.join('.');
|
|
51
|
+
},
|
|
52
|
+
layout: ($groupTree) => {
|
|
53
|
+
for(let i = $groupTree.length - 1; i >= 0; i--) {
|
|
54
|
+
if($groupTree[i]?.options?.layout) {
|
|
55
|
+
return $groupTree[i].options.layout;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
51
59
|
}
|
|
52
60
|
};
|
package/src/router/Router.js
CHANGED
|
@@ -54,7 +54,7 @@ export default function Router($options = {}) {
|
|
|
54
54
|
*
|
|
55
55
|
* @param {string} path
|
|
56
56
|
* @param {Function} component
|
|
57
|
-
* @param {{name:?string, middlewares:Function[], shouldRebuild:Boolean, with: Object }} options
|
|
57
|
+
* @param {{name:?string, middlewares:Function[], shouldRebuild:Boolean, with: Object, layout: Function }} options
|
|
58
58
|
* @returns {this}
|
|
59
59
|
*/
|
|
60
60
|
this.add = function(path, component, options) {
|
|
@@ -62,6 +62,7 @@ export default function Router($options = {}) {
|
|
|
62
62
|
...options,
|
|
63
63
|
middlewares: RouteGroupHelper.fullMiddlewares($groupTree, options?.middlewares || []),
|
|
64
64
|
name: options?.name ? RouteGroupHelper.fullName($groupTree, options.name) : null,
|
|
65
|
+
layout: options?.layout || RouteGroupHelper.layout($groupTree)
|
|
65
66
|
});
|
|
66
67
|
$routes.push(route);
|
|
67
68
|
if(route.name()) {
|
|
@@ -7,8 +7,13 @@ export function RouterComponent(router, container) {
|
|
|
7
7
|
|
|
8
8
|
const $cache = new Map();
|
|
9
9
|
|
|
10
|
-
const updateContainer = function(node) {
|
|
10
|
+
const updateContainer = function(node, route) {
|
|
11
11
|
container.innerHTML = '';
|
|
12
|
+
const layout = route.layout();
|
|
13
|
+
if(layout) {
|
|
14
|
+
container.appendChild(layout(node));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
12
17
|
container.appendChild(node);
|
|
13
18
|
};
|
|
14
19
|
|
|
@@ -19,13 +24,13 @@ export function RouterComponent(router, container) {
|
|
|
19
24
|
const { route, params, query, path } = state;
|
|
20
25
|
if($cache.has(path)) {
|
|
21
26
|
const cacheNode = $cache.get(path);
|
|
22
|
-
updateContainer(cacheNode);
|
|
27
|
+
updateContainer(cacheNode, route);
|
|
23
28
|
return;
|
|
24
29
|
}
|
|
25
30
|
const Component = route.component();
|
|
26
31
|
const node = Component({ params, query });
|
|
27
32
|
$cache.set(path, node);
|
|
28
|
-
updateContainer(node);
|
|
33
|
+
updateContainer(node, route);
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
router.subscribe(handleCurrentRouterState);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export default function NativeFetch($baseUrl) {
|
|
2
|
+
|
|
3
|
+
const $interceptors = {
|
|
4
|
+
request: [],
|
|
5
|
+
response: []
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
this.interceptors = {
|
|
9
|
+
response: (callback) => {
|
|
10
|
+
$interceptors.response.push(callback);
|
|
11
|
+
},
|
|
12
|
+
request: (callback) => {
|
|
13
|
+
$interceptors.request.push(callback);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
this.fetch = async function(method, endpoint, params = {}, options = {}) {
|
|
18
|
+
if(!endpoint.startsWith('http')) {
|
|
19
|
+
endpoint = ($baseUrl.endsWith('/') ? $baseUrl : $baseUrl+'/') + endpoint;
|
|
20
|
+
}
|
|
21
|
+
let configs = {
|
|
22
|
+
method,
|
|
23
|
+
headers: {
|
|
24
|
+
...(options.headers || {})
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
if(params) {
|
|
28
|
+
if(params instanceof FormData) {
|
|
29
|
+
configs.body = params;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
configs.headers['Content-Type'] = 'application/json';
|
|
33
|
+
if(method !== 'GET') {
|
|
34
|
+
configs.body = JSON.stringify(params);
|
|
35
|
+
} else {
|
|
36
|
+
configs.params = params;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for(const interceptor of $interceptors.request) {
|
|
42
|
+
configs = (await interceptor(configs, endpoint)) || configs;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let response = await fetch(endpoint, configs);
|
|
46
|
+
|
|
47
|
+
for(const interceptor of $interceptors.response) {
|
|
48
|
+
response = (await interceptor(response, endpoint)) || response;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const contentType = response.headers.get('content-type') || '';
|
|
52
|
+
const data = contentType.includes('application/json')
|
|
53
|
+
? await response.json()
|
|
54
|
+
: await response.text();
|
|
55
|
+
|
|
56
|
+
if(!response.ok) {
|
|
57
|
+
const error = new Error(data?.message || response.statusText);
|
|
58
|
+
error.status = response.status;
|
|
59
|
+
error.data = data;
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return data;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
this.post = function (endpoint, params = {}, options = {}) {
|
|
68
|
+
return this.fetch('POST', endpoint, params, options);
|
|
69
|
+
};
|
|
70
|
+
this.put = function (endpoint, params = {}, options = {}) {
|
|
71
|
+
return this.fetch('PUT', endpoint, params, options);
|
|
72
|
+
};
|
|
73
|
+
this.delete = function (endpoint, params = {}, options = {}) {
|
|
74
|
+
return this.fetch('DELETE', endpoint, params, options);
|
|
75
|
+
};
|
|
76
|
+
this.get = function (endpoint, params = {}, options = {}) {
|
|
77
|
+
return this.fetch('GET', endpoint, params, options);
|
|
78
|
+
};
|
|
79
|
+
};
|
package/src/utils/validator.js
CHANGED
|
@@ -19,6 +19,9 @@ const Validator = {
|
|
|
19
19
|
isProxy(value) {
|
|
20
20
|
return value?.__isProxy__
|
|
21
21
|
},
|
|
22
|
+
isAnchor(value) {
|
|
23
|
+
return value?.__Anchor__
|
|
24
|
+
},
|
|
22
25
|
isObservableChecker(value) {
|
|
23
26
|
return value?.__$isObservableChecker || value instanceof ObservableChecker;
|
|
24
27
|
},
|
|
@@ -50,7 +53,8 @@ const Validator = {
|
|
|
50
53
|
return value && (
|
|
51
54
|
value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
|
|
52
55
|
value.nodeType === COMMON_NODE_TYPES.TEXT ||
|
|
53
|
-
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT
|
|
56
|
+
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
|
|
57
|
+
value.nodeType === COMMON_NODE_TYPES.COMMENT
|
|
54
58
|
);
|
|
55
59
|
},
|
|
56
60
|
isFragment(value) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Anchor from "../elements/anchor";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function SingletonView($viewCreator) {
|
|
5
|
+
let $cacheNode = null;
|
|
6
|
+
let $components = null;
|
|
7
|
+
|
|
8
|
+
this.render = (data) => {
|
|
9
|
+
if(!$cacheNode) {
|
|
10
|
+
$cacheNode = $viewCreator(this);
|
|
11
|
+
}
|
|
12
|
+
if(!$components) {
|
|
13
|
+
return $cacheNode;
|
|
14
|
+
}
|
|
15
|
+
for(const index in $components) {
|
|
16
|
+
const updater = $components[index];
|
|
17
|
+
updater(...data);
|
|
18
|
+
}
|
|
19
|
+
return $cacheNode;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.createSection = (name, fn) => {
|
|
23
|
+
$components = $components || {};
|
|
24
|
+
const anchor = new Anchor('Component '+name);
|
|
25
|
+
|
|
26
|
+
$components[name] = function(...args) {
|
|
27
|
+
anchor.removeChildren();
|
|
28
|
+
if(!fn) {
|
|
29
|
+
anchor.append(args);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
anchor.appendChild(fn(...args));
|
|
33
|
+
};
|
|
34
|
+
return anchor;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
export function useSingleton(fn) {
|
|
40
|
+
let $cache = null;
|
|
41
|
+
|
|
42
|
+
return function(...args) {
|
|
43
|
+
if(!$cache) {
|
|
44
|
+
$cache = new SingletonView(fn);
|
|
45
|
+
}
|
|
46
|
+
return $cache.render(args);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {ElementCreator} from "./ElementCreator";
|
|
2
2
|
import {createTextNode} from "./HtmlElementWrapper";
|
|
3
|
+
import Anchor from "../elements/anchor";
|
|
3
4
|
|
|
4
5
|
const cloneBindingsDataCache = new WeakMap();
|
|
5
6
|
|
|
@@ -142,6 +143,7 @@ export function TemplateCloner($fn) {
|
|
|
142
143
|
this.attach = (fn) => {
|
|
143
144
|
return createBinding(fn, 'attach');
|
|
144
145
|
};
|
|
146
|
+
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
export function useCache(fn) {
|
package/types/elements.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type ValidChild =
|
|
|
18
18
|
| ValidChild[]
|
|
19
19
|
| ((...args: any[]) => ValidChild);
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
export type Attributes = Record<string, any> & {
|
|
22
23
|
class?: string | Record<string, boolean | ObservableItem<boolean>>;
|
|
23
24
|
style?: string | Record<string, string | ObservableItem<string>>;
|
|
@@ -122,5 +123,20 @@ export declare const TBodyCell: ElementFunction;
|
|
|
122
123
|
export declare const Fragment: ElementFunction;
|
|
123
124
|
export declare const NativeDocumentFragment: typeof Anchor;
|
|
124
125
|
|
|
126
|
+
|
|
127
|
+
export declare type AnchorDocumentFragment = DocumentFragment & {
|
|
128
|
+
detach: () => void;
|
|
129
|
+
restore: () => void;
|
|
130
|
+
clear: () => void;
|
|
131
|
+
remove: () => void;
|
|
132
|
+
removeChildren: () => void;
|
|
133
|
+
insertBefore: (child: ValidChild, before: HTMLElement|Comment) => void;
|
|
134
|
+
replaceContent: (child: ValidChild) => void;
|
|
135
|
+
appendElement: (child: ValidChild, before: HTMLElement) => void;
|
|
136
|
+
getByIndex: (index: number) => HTMLElement;
|
|
137
|
+
endElement: () => Comment;
|
|
138
|
+
startElement: () => Comment;
|
|
139
|
+
};
|
|
140
|
+
|
|
125
141
|
// Anchor
|
|
126
|
-
export declare function Anchor(name?: string, isUniqueChild?: boolean):
|
|
142
|
+
export declare function Anchor(name?: string, isUniqueChild?: boolean): AnchorDocumentFragment;
|
package/types/observable.d.ts
CHANGED
|
@@ -50,11 +50,14 @@ export interface ObservableArray<T> extends ObservableItem<T[]> {
|
|
|
50
50
|
sort(compareFn?: (a: T, b: T) => number): T[];
|
|
51
51
|
splice(start: number, deleteCount?: number, ...items: T[]): T[];
|
|
52
52
|
|
|
53
|
+
isEmpty(): boolean;
|
|
53
54
|
clear(): boolean;
|
|
54
55
|
merge(values: T[]): void;
|
|
56
|
+
removeItem(item: T): T[];
|
|
55
57
|
remove(index: number): T[];
|
|
56
58
|
swap(indexA: number, indexB: number): boolean;
|
|
57
59
|
length(): number;
|
|
60
|
+
count(condition: (item:T, index?:number) => boolean): number;
|
|
58
61
|
populateAndRender(iteration: number, callback: (index: number) => T): void;
|
|
59
62
|
|
|
60
63
|
map<U>(callback: (value: T, index: number, array: T[]) => U): U[];
|
package/types/router.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Router system type definitions
|
|
2
|
-
import { ValidChild
|
|
2
|
+
import { ValidChild } from './elements';
|
|
3
|
+
import { NDElement } from './nd-element';
|
|
3
4
|
|
|
4
5
|
export interface RouteParams {
|
|
5
6
|
[key: string]: string;
|
|
@@ -42,7 +43,7 @@ export interface Router {
|
|
|
42
43
|
with?: Record<string, string>;
|
|
43
44
|
}): this;
|
|
44
45
|
|
|
45
|
-
group(suffix: string, options: { middlewares?: Function[]; name?: string }, callback: () => void): this;
|
|
46
|
+
group(suffix: string, options: { middlewares?: Function[]; name?: string, layout?: Function }, callback: () => void): this;
|
|
46
47
|
|
|
47
48
|
generateUrl(name: string, params?: RouteParams, query?: QueryParams): string;
|
|
48
49
|
resolve(target: string | { name: string; params?: RouteParams; query?: QueryParams }): {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AnchorDocumentFragment } from "./elements";
|
|
2
|
+
|
|
3
|
+
export type ViewCreator = (instance: SingletonView) => Node;
|
|
4
|
+
|
|
5
|
+
export type SectionFunction = (...args: any[]) => Node | Node[];
|
|
6
|
+
|
|
7
|
+
export type ComponentUpdater = (...args: any[]) => void;
|
|
8
|
+
|
|
9
|
+
export type ComponentsMap = Record<string, ComponentUpdater>;
|
|
10
|
+
|
|
11
|
+
export declare class SingletonView {
|
|
12
|
+
constructor(viewCreator: ViewCreator);
|
|
13
|
+
|
|
14
|
+
render(data: any[]): Node;
|
|
15
|
+
|
|
16
|
+
createSection(name: string, fn?: SectionFunction): AnchorDocumentFragment;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export declare function useSingleton(fn: ViewCreator): (...args: any[]) => Node;
|
package/utils.d.ts
ADDED
|
File without changes
|