apprun 4.0.0 → 6.0.0-rc.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.
- package/CHANGELOG.md +235 -12
- package/LICENSE +1 -1
- package/README.md +225 -110
- package/apprun.d.ts +240 -34
- package/cli/app.js +29 -0
- package/cli/colors.js +69 -0
- package/cli/index.html +13 -0
- package/cli/index.js +96 -0
- package/dist/apprun-code.js +2 -0
- package/dist/apprun-code.js.map +1 -0
- package/dist/apprun-dev-tools.js +1 -1
- package/dist/apprun-dev-tools.js.map +1 -1
- package/dist/apprun-html.esm.js +34 -0
- package/dist/apprun-html.esm.js.map +1 -0
- package/dist/apprun-html.js +2 -1
- package/dist/apprun-html.js.LICENSE.txt +11 -0
- package/dist/apprun-html.js.map +1 -1
- package/dist/apprun-play.js +2 -0
- package/dist/apprun-play.js.map +1 -0
- package/dist/apprun.esm.js +2 -0
- package/dist/apprun.esm.js.map +1 -0
- package/dist/apprun.js +1 -1
- package/dist/apprun.js.map +1 -1
- package/dist/createState.js +2 -0
- package/dist/createState.js.map +1 -0
- package/esm/add-components.js +90 -0
- package/esm/add-components.js.map +1 -0
- package/esm/app.js +209 -0
- package/esm/app.js.map +1 -0
- package/esm/apprun-code.js +170 -0
- package/esm/apprun-code.js.map +1 -0
- package/esm/apprun-dev-tools-tests.js +95 -0
- package/esm/apprun-dev-tools-tests.js.map +1 -0
- package/esm/apprun-dev-tools.js +281 -0
- package/esm/apprun-dev-tools.js.map +1 -0
- package/esm/apprun-html.js +24 -0
- package/esm/apprun-html.js.map +1 -0
- package/esm/apprun-play.js +248 -0
- package/esm/apprun-play.js.map +1 -0
- package/esm/apprun.js +220 -0
- package/esm/apprun.js.map +1 -0
- package/esm/component.js +413 -0
- package/esm/component.js.map +1 -0
- package/esm/createState.js +9 -0
- package/esm/createState.js.map +1 -0
- package/esm/decorator.js +88 -0
- package/esm/decorator.js.map +1 -0
- package/esm/directive.js +306 -0
- package/esm/directive.js.map +1 -0
- package/esm/router.js +345 -0
- package/esm/router.js.map +1 -0
- package/esm/type-utils.js +90 -0
- package/esm/type-utils.js.map +1 -0
- package/esm/types.js +58 -0
- package/esm/types.js.map +1 -0
- package/esm/vdom-lit-html.js +65 -0
- package/esm/vdom-lit-html.js.map +1 -0
- package/esm/vdom-my-prop-attr.js +227 -0
- package/esm/vdom-my-prop-attr.js.map +1 -0
- package/esm/vdom-my.js +293 -0
- package/esm/vdom-my.js.map +1 -0
- package/esm/vdom-to-html.js +54 -0
- package/esm/vdom-to-html.js.map +1 -0
- package/esm/vdom.js +28 -0
- package/esm/vdom.js.map +1 -0
- package/esm/version.js +15 -0
- package/esm/version.js.map +1 -0
- package/esm/web-component.js +155 -0
- package/esm/web-component.js.map +1 -0
- package/jsx-runtime.js +2 -0
- package/jsx-runtime.js.map +1 -0
- package/package.json +90 -41
- package/.travis.yml +0 -10
- package/.vscode/launch.json +0 -19
- package/BACKERS.md +0 -3
- package/apprun-cli.js +0 -177
- package/cli-templates/_gitignore +0 -6
- package/cli-templates/component.ts_ +0 -16
- package/cli-templates/index.html +0 -11
- package/cli-templates/karma.conf.js +0 -24
- package/cli-templates/main.ts_ +0 -13
- package/cli-templates/readme.md +0 -7
- package/cli-templates/spa_index.html +0 -14
- package/cli-templates/spa_main.ts_ +0 -47
- package/cli-templates/spec.ts_ +0 -11
- package/cli-templates/tsconfig.json +0 -12
- package/cli-templates/webpack.config.js +0 -23
- package/demo-html/app.js +0 -2
- package/demo-html/app.js.map +0 -1
- package/demo-html/counter-c.html +0 -29
- package/demo-html/counter-wc-event.html +0 -29
- package/demo-html/counter-wc.html +0 -29
- package/demo-html/counter.html +0 -26
- package/demo-html/index.html +0 -14
- package/demo-html/main.tsx +0 -31
- package/docs/README.md +0 -326
- package/docs/index.html +0 -23
- package/index.html +0 -85
- package/logo.png +0 -0
- package/src/app.ts +0 -69
- package/src/apprun-dev-tools.tsx +0 -67
- package/src/apprun-html.ts +0 -13
- package/src/apprun.ts +0 -53
- package/src/component.ts +0 -189
- package/src/createComponent.tsx +0 -38
- package/src/decorator.ts +0 -36
- package/src/router.ts +0 -19
- package/src/types.ts +0 -9
- package/src/vdom-html.ts +0 -17
- package/src/vdom-my.ts +0 -191
- package/src/vdom-to-html.tsx +0 -45
- package/src/vdom.ts +0 -7
- package/src/web-component.ts +0 -29
- package/tests/app-rx.spec_ +0 -41
- package/tests/app.spec.ts +0 -122
- package/tests/application.spec.tsx +0 -23
- package/tests/component.spec.tsx +0 -296
- package/tests/custom-element.spec.tsx +0 -19
- package/tests/data-attr.spec.tsx +0 -42
- package/tests/decorator.spec.tsx +0 -104
- package/tests/fragment.spec.tsx +0 -79
- package/tests/hyperscript.spec.ts +0 -43
- package/tests/node-test.js +0 -7
- package/tests/router.spec.ts +0 -69
- package/tests/stateful-component.spec.tsx +0 -248
- package/tests/typed.spec.tsx +0 -63
- package/tests/vdom-html.spec.ts +0 -35
- package/tests/vdom-jsx.spec.tsx +0 -131
- package/tests/vdom-my.spec.tsx +0 -195
- package/tests/view-engine.spec.js +0 -59
- package/tsconfig.json +0 -11
- package/tslint.json +0 -124
- package/typescriptreact.json +0 -87
- package/viewEngine.js +0 -54
- package/webpack.config.js +0 -30
package/src/component.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import app, { App } from './app';
|
|
3
|
-
import { Reflect } from './decorator'
|
|
4
|
-
import { VNode, View, Update } from './types';
|
|
5
|
-
|
|
6
|
-
const componentCache = {};
|
|
7
|
-
app.on('get-components', o => o.components = componentCache);
|
|
8
|
-
|
|
9
|
-
export class Component<T=any> {
|
|
10
|
-
static __isAppRunComponent = true;
|
|
11
|
-
private _app = new App();
|
|
12
|
-
private _actions = [];
|
|
13
|
-
element;
|
|
14
|
-
private _history = [];
|
|
15
|
-
private _history_idx = -1;
|
|
16
|
-
private enable_history;
|
|
17
|
-
private global_event;
|
|
18
|
-
public rendered;
|
|
19
|
-
public mounted;
|
|
20
|
-
|
|
21
|
-
private renderState(state: T) {
|
|
22
|
-
if (!this.view) return;
|
|
23
|
-
const html = this.view(state);
|
|
24
|
-
|
|
25
|
-
app.run('debug', {
|
|
26
|
-
component: this,
|
|
27
|
-
state,
|
|
28
|
-
vdom: html || '[vdom is null - no render]',
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
if (typeof document !== 'object') return;
|
|
32
|
-
|
|
33
|
-
const el = (typeof this.element === 'string') ?
|
|
34
|
-
document.getElementById(this.element) : this.element;
|
|
35
|
-
if (el) el['_component'] = this;
|
|
36
|
-
app.render(el, html, this);
|
|
37
|
-
if (this.rendered) (this.rendered(this.state));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public setState(state: T, options: { render: boolean, history: boolean, callback?}
|
|
41
|
-
= { render: true, history: false}) {
|
|
42
|
-
if (state instanceof Promise) {
|
|
43
|
-
// Promise will not be saved or rendered
|
|
44
|
-
// state will be saved and rendered when promise is resolved
|
|
45
|
-
state.then(s => {
|
|
46
|
-
this.setState(s, options)
|
|
47
|
-
}).catch(err => {
|
|
48
|
-
console.error(err);
|
|
49
|
-
throw err;
|
|
50
|
-
});
|
|
51
|
-
this.state = state;
|
|
52
|
-
} else {
|
|
53
|
-
if (state == null) return;
|
|
54
|
-
this.state = state;
|
|
55
|
-
if (options.render !== false) this.renderState(state);
|
|
56
|
-
if (options.history !== false && this.enable_history) {
|
|
57
|
-
this._history = [...this._history, state];
|
|
58
|
-
this._history_idx = this._history.length - 1;
|
|
59
|
-
}
|
|
60
|
-
if (typeof options.callback === 'function') options.callback(this.state);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
constructor(
|
|
65
|
-
protected state?: T,
|
|
66
|
-
protected view?: View<T>,
|
|
67
|
-
protected update?: Update<T>,
|
|
68
|
-
protected options?) {
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
start = (element = null,
|
|
72
|
-
options: { render?: boolean, history?, global_event?: boolean } = { render: true }): Component<T> => {
|
|
73
|
-
return this.mount(element, { ...options, render: true });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public mount(element = null, options?: { render?: boolean, history?, global_event?: boolean}) {
|
|
77
|
-
|
|
78
|
-
console.assert(!this.element, 'Component already mounted.')
|
|
79
|
-
this.options = options = Object.assign(this.options || {}, options);
|
|
80
|
-
this.element = element;
|
|
81
|
-
this.global_event = options.global_event;
|
|
82
|
-
this.enable_history = !!options.history;
|
|
83
|
-
|
|
84
|
-
if (this.enable_history) {
|
|
85
|
-
const prev = () => {
|
|
86
|
-
this._history_idx --;
|
|
87
|
-
if (this._history_idx >=0) {
|
|
88
|
-
this.setState(this._history[this._history_idx], { render: true, history: false });
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
this._history_idx = 0;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const next = () => {
|
|
96
|
-
this._history_idx ++;
|
|
97
|
-
if (this._history_idx < this._history.length) {
|
|
98
|
-
this.setState(this._history[this._history_idx], { render: true, history: false });
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
this._history_idx = this._history.length - 1;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
this.on(options.history.prev || 'history-prev', prev)
|
|
105
|
-
this.on(options.history.next || 'history-next', next)
|
|
106
|
-
}
|
|
107
|
-
this.add_actions();
|
|
108
|
-
if (this.state === undefined) this.state = this['model'] || {};
|
|
109
|
-
if (options.render) {
|
|
110
|
-
this.setState(this.state, { render: true, history: true });
|
|
111
|
-
} else {
|
|
112
|
-
this.setState(this.state, { render: false, history: true });
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
componentCache[element] = componentCache[element] || [];
|
|
116
|
-
componentCache[element].push(this);
|
|
117
|
-
return this;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
is_global_event(name: string): boolean {
|
|
121
|
-
return name && (name.startsWith('#') || name.startsWith('/'));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
add_action(name: string, action, options: any = {}) {
|
|
125
|
-
if (!action || typeof action !== 'function') return;
|
|
126
|
-
this.on(name, (...p) => {
|
|
127
|
-
const newState = action(this.state, ...p);
|
|
128
|
-
|
|
129
|
-
app.run('debug', {
|
|
130
|
-
component: this,
|
|
131
|
-
'event': name,
|
|
132
|
-
e: p,
|
|
133
|
-
state: this.state,
|
|
134
|
-
newState,
|
|
135
|
-
options
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
this.setState(newState, options)
|
|
139
|
-
}, options);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
add_actions() {
|
|
143
|
-
const actions = this.update || {};
|
|
144
|
-
Reflect.getMetadataKeys(this).forEach(key => {
|
|
145
|
-
if (key.startsWith('apprun-update:')) {
|
|
146
|
-
const meta = Reflect.getMetadata(key, this)
|
|
147
|
-
actions[meta.name] = [this[meta.key].bind(this), meta.options];
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
const all = {};
|
|
151
|
-
Object.keys(actions).forEach(name => {
|
|
152
|
-
const action = actions[name];
|
|
153
|
-
if (typeof action === 'function' || Array.isArray(action)) {
|
|
154
|
-
name.split(',').forEach(n => all[n.trim()] = action)
|
|
155
|
-
}
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
Object.keys(all).forEach(name => {
|
|
159
|
-
const action = all[name];
|
|
160
|
-
if (typeof action === 'function') {
|
|
161
|
-
this.add_action(name, action);
|
|
162
|
-
} else if (Array.isArray(action)) {
|
|
163
|
-
this.add_action(name, action[0], action[1]);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public run(name: string, ...args) {
|
|
169
|
-
return this.global_event || this.is_global_event(name) ?
|
|
170
|
-
app.run(name, ...args) :
|
|
171
|
-
this._app.run(name, ...args);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
public on(name: string, fn: (...args) => void, options?: any) {
|
|
175
|
-
this._actions.push({ name, fn });
|
|
176
|
-
return this.global_event || this.is_global_event(name) ?
|
|
177
|
-
app.on(name, fn, options) :
|
|
178
|
-
this._app.on(name, fn, options);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
public unmount() {
|
|
182
|
-
this._actions.forEach(action => {
|
|
183
|
-
const { name, fn } = action;
|
|
184
|
-
this.global_event || this.is_global_event(name) ?
|
|
185
|
-
app.off(name, fn) :
|
|
186
|
-
this._app.off(name, fn);
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/createComponent.tsx
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import app from './app';
|
|
2
|
-
|
|
3
|
-
function render(node, parent, idx) {
|
|
4
|
-
const { tag, props, children } = node;
|
|
5
|
-
|
|
6
|
-
let id = props && props['id'];
|
|
7
|
-
let key = `_${tag.name}_${idx}`
|
|
8
|
-
if (!id) {
|
|
9
|
-
id = `_${tag.name}_${idx}`;
|
|
10
|
-
} else {
|
|
11
|
-
key = `_${tag.name}_${id}`;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (!parent.__componentCache) parent.__componentCache = {};
|
|
15
|
-
let component = parent.__componentCache[key];
|
|
16
|
-
if (!component) {
|
|
17
|
-
component = parent.__componentCache[key] = new tag({ ...props, children }).mount(id);
|
|
18
|
-
}
|
|
19
|
-
component.mounted && component.mounted(props, children);
|
|
20
|
-
const state = component.state;
|
|
21
|
-
let vdom = '';
|
|
22
|
-
if (!(state instanceof Promise) && component.view) {
|
|
23
|
-
vdom = component.view(state);
|
|
24
|
-
component.rendered && component.rendered(state);
|
|
25
|
-
}
|
|
26
|
-
return <div id={id}>{vdom}</div>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function createComponent(node, parent, idx = 0) {
|
|
30
|
-
if (typeof node === 'string') return node;
|
|
31
|
-
if (Array.isArray(node)) return node.map(child => createComponent(child, parent, idx++));
|
|
32
|
-
let vdom = node;
|
|
33
|
-
if (node && node.tag && Object.getPrototypeOf(node.tag).__isAppRunComponent) vdom = render(node, parent, idx++);
|
|
34
|
-
if (vdom && vdom.children) vdom.children = vdom.children.map(child => createComponent(child, parent, idx++));
|
|
35
|
-
return vdom;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default createComponent;
|
package/src/decorator.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export const Reflect = {
|
|
2
|
-
|
|
3
|
-
meta: new WeakMap(),
|
|
4
|
-
|
|
5
|
-
defineMetadata(metadataKey, metadataValue, target) {
|
|
6
|
-
if (!this.meta.has(target)) this.meta.set(target, {});
|
|
7
|
-
this.meta.get(target)[metadataKey] = metadataValue;
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
getMetadataKeys(target) {
|
|
11
|
-
target = Object.getPrototypeOf(target);
|
|
12
|
-
return this.meta.get(target) ? Object.keys(this.meta.get(target)) : [];
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
getMetadata(metadataKey, target) {
|
|
16
|
-
target = Object.getPrototypeOf(target);
|
|
17
|
-
return this.meta.get(target) ? this.meta.get(target)[metadataKey] : null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function update(name?: string, options: any = {}) {
|
|
22
|
-
return (target: any, key: string, descriptor: any) => {
|
|
23
|
-
name = name || key;
|
|
24
|
-
Reflect.defineMetadata(`apprun-update:${name}`,
|
|
25
|
-
{ name, key, options }, target);
|
|
26
|
-
return descriptor;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function on(name?: string, options: any = {}) {
|
|
31
|
-
return function (target: any, key: string) {
|
|
32
|
-
name = name || key;
|
|
33
|
-
Reflect.defineMetadata(`apprun-update:${name}`,
|
|
34
|
-
{ name, key, options }, target)
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/router.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import app from './app';
|
|
2
|
-
|
|
3
|
-
const ROUTER_EVENT = '//';
|
|
4
|
-
|
|
5
|
-
export default function route(url: string) {
|
|
6
|
-
if (!url) url = '#';
|
|
7
|
-
if (url.startsWith('#')) {
|
|
8
|
-
const [name, ...rest] = url.split('/');
|
|
9
|
-
app.run(name, ...rest);
|
|
10
|
-
app.run(ROUTER_EVENT, name, ...rest);
|
|
11
|
-
} else if (url.startsWith('/')) {
|
|
12
|
-
const [_, name, ...rest] = url.split('/');
|
|
13
|
-
app.run('/' + name, ...rest);
|
|
14
|
-
app.run(ROUTER_EVENT, '/' + name, ...rest);
|
|
15
|
-
} else {
|
|
16
|
-
app.run(url);
|
|
17
|
-
app.run(ROUTER_EVENT, url);
|
|
18
|
-
}
|
|
19
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export type VNode = {
|
|
2
|
-
tag: string | Function,
|
|
3
|
-
props: {},
|
|
4
|
-
children: Array<VNode|string>
|
|
5
|
-
}
|
|
6
|
-
export type VDOM = string | VNode | Array<VNode | string>;
|
|
7
|
-
export type View<T> = (state: T) => VDOM | void;
|
|
8
|
-
export type Action<T> = (state: T, ...p: any[]) => T | Promise<T>;
|
|
9
|
-
export type Update<T> = { [name: string]: Action<T> | {}[] | void; };
|
package/src/vdom-html.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { createElement, updateElement, Fragment } from './vdom-my';
|
|
2
|
-
import * as morphdom from 'morphdom';
|
|
3
|
-
|
|
4
|
-
export function render(element, html, parent?) {
|
|
5
|
-
if (typeof html === 'string') {
|
|
6
|
-
html = html.trim();
|
|
7
|
-
if (element.firstChild) {
|
|
8
|
-
morphdom(element.firstChild, html);
|
|
9
|
-
} else {
|
|
10
|
-
element.innerHTML = html;
|
|
11
|
-
}
|
|
12
|
-
} else {
|
|
13
|
-
updateElement(element, html, parent);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export { createElement, Fragment };
|
|
17
|
-
|
package/src/vdom-my.ts
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import createComponent from './createComponent';
|
|
2
|
-
import { VNode } from './types';
|
|
3
|
-
export type Element = any; //HTMLElement | SVGSVGElement | SVGElement;
|
|
4
|
-
|
|
5
|
-
const ATTR_PROPS = '_props';
|
|
6
|
-
|
|
7
|
-
function collect(children) {
|
|
8
|
-
const ch = [];
|
|
9
|
-
const push = (c) => {
|
|
10
|
-
if (c !== null && c !== undefined && c !== '' && c !== false) {
|
|
11
|
-
ch.push((typeof c === 'function' || typeof c === 'object') ? c : `${c}`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
children && children.forEach(c => {
|
|
15
|
-
if (Array.isArray(c)) {
|
|
16
|
-
c.forEach(i => push(i));
|
|
17
|
-
} else {
|
|
18
|
-
push(c);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
return ch;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function createElement (tag: string | Function, props: {}, ...children) {
|
|
25
|
-
const ch = collect(children);
|
|
26
|
-
if (typeof tag === 'string') return { tag, props, children: ch };
|
|
27
|
-
else if (tag === undefined && children) return ch; // JSX fragments
|
|
28
|
-
else if (Object.getPrototypeOf(tag).__isAppRunComponent) {
|
|
29
|
-
return { tag, props, children:ch } // createComponent(tag, { ...props, children });
|
|
30
|
-
}
|
|
31
|
-
else
|
|
32
|
-
return tag(props, ch);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const keyCache = {};
|
|
36
|
-
|
|
37
|
-
export const updateElement = render;
|
|
38
|
-
|
|
39
|
-
export function render(element: Element, nodes: VNode | VNode[], parent = {}) {
|
|
40
|
-
// console.log('render', element, node);
|
|
41
|
-
if (nodes == null) return;
|
|
42
|
-
|
|
43
|
-
nodes = createComponent(nodes, parent);
|
|
44
|
-
|
|
45
|
-
if (!element) return;
|
|
46
|
-
if (Array.isArray(nodes)) {
|
|
47
|
-
updateChildren(element, nodes);
|
|
48
|
-
} else {
|
|
49
|
-
const node = nodes;
|
|
50
|
-
if (!element.firstChild) {
|
|
51
|
-
element.appendChild(create(node));
|
|
52
|
-
} else {
|
|
53
|
-
update(element.firstChild, node);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function same(el: Element, node: VNode) {
|
|
59
|
-
// if (!el || !node) return false;
|
|
60
|
-
const key1 = el.nodeName;
|
|
61
|
-
const key2 = `${node.tag || ''}`;
|
|
62
|
-
return key1 === key2.toUpperCase();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function update(element: Element, node: VNode) {
|
|
66
|
-
console.assert(!!element);
|
|
67
|
-
//console.log('update', element, node);
|
|
68
|
-
if (!same(element, node)) {
|
|
69
|
-
element.parentNode.replaceChild(create(node), element);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
updateChildren(element, node.children);
|
|
73
|
-
updateProps(element, node.props);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function updateChildren(element, children) {
|
|
77
|
-
const len = Math.min(element.childNodes.length, children.length);
|
|
78
|
-
for (let i = 0; i < len; i++) {
|
|
79
|
-
const child = children[i];
|
|
80
|
-
const el = element.childNodes[i];
|
|
81
|
-
if (typeof child === 'string') {
|
|
82
|
-
if (el.textContent !== child) {
|
|
83
|
-
if (el.nodeType === 3) {
|
|
84
|
-
el.textContent = child
|
|
85
|
-
} else {
|
|
86
|
-
element.replaceChild(createText(child), el);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
const key = child.props && child.props['key'];
|
|
91
|
-
if (key) {
|
|
92
|
-
if (el.key === key) {
|
|
93
|
-
update(element.childNodes[i], child);
|
|
94
|
-
} else {
|
|
95
|
-
const old = key && keyCache[key];
|
|
96
|
-
if (old) {
|
|
97
|
-
element.replaceChild(old, el);
|
|
98
|
-
element.appendChild(el);
|
|
99
|
-
update(element.childNodes[i], child);
|
|
100
|
-
} else {
|
|
101
|
-
element.appendChild(create(child), el);
|
|
102
|
-
update(element.childNodes[i], child);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
update(element.childNodes[i], child);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
let n = element.childNodes.length;
|
|
112
|
-
while (n > len) {
|
|
113
|
-
element.removeChild(element.lastChild);
|
|
114
|
-
n--;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (children.length > len) {
|
|
118
|
-
const d = document.createDocumentFragment();
|
|
119
|
-
for (let i = len; i < children.length; i++) {
|
|
120
|
-
d.appendChild(create(children[i]));
|
|
121
|
-
}
|
|
122
|
-
element.appendChild(d);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function createText(node) {
|
|
127
|
-
if (node.indexOf('_html:') === 0) { // ?
|
|
128
|
-
const div = document.createElement('div');
|
|
129
|
-
div.insertAdjacentHTML('afterbegin', node.substring(6))
|
|
130
|
-
return div;
|
|
131
|
-
} else {
|
|
132
|
-
return document.createTextNode(node);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function create(node: VNode | string): Element {
|
|
137
|
-
console.assert(node !== null && node !== undefined);
|
|
138
|
-
// console.log('create', node, typeof node);
|
|
139
|
-
|
|
140
|
-
if (typeof node === "string") return createText(node);
|
|
141
|
-
if (!node.tag || (typeof node.tag == 'function')) return createText(JSON.stringify(node));
|
|
142
|
-
|
|
143
|
-
const element = (node.tag === "svg")
|
|
144
|
-
? document.createElementNS("http://www.w3.org/2000/svg", node.tag)
|
|
145
|
-
: document.createElement(node.tag);
|
|
146
|
-
|
|
147
|
-
updateProps(element, node.props);
|
|
148
|
-
|
|
149
|
-
if (node.children) node.children.forEach(child => element.appendChild(create(child)));
|
|
150
|
-
|
|
151
|
-
return element
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function mergeProps(a: {}, b: {}): {} {
|
|
155
|
-
const props = {};
|
|
156
|
-
if (a) Object.keys(a).forEach(p => props[p] = '');
|
|
157
|
-
if (b) Object.keys(b).forEach(p => props[p] = b[p]);
|
|
158
|
-
return props;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function updateProps(element: Element, props: {}) {
|
|
162
|
-
console.assert(!!element);
|
|
163
|
-
// console.log('updateProps', element, props);
|
|
164
|
-
|
|
165
|
-
const cached = element[ATTR_PROPS] || {};
|
|
166
|
-
props = mergeProps(cached, props);
|
|
167
|
-
element[ATTR_PROPS] = props;
|
|
168
|
-
for (let name in props) {
|
|
169
|
-
const value = props[name];
|
|
170
|
-
// if (cached[name] === value) continue;
|
|
171
|
-
// console.log('updateProps', name, value);
|
|
172
|
-
if (name === 'style') {
|
|
173
|
-
if (element.style.cssText) element.style.cssText = '';
|
|
174
|
-
for (let s in value) {
|
|
175
|
-
if (element.style[s] !== value[s]) element.style[s] = value[s];
|
|
176
|
-
}
|
|
177
|
-
} else if (name.startsWith('data-')) {
|
|
178
|
-
const dname = name.substring(5);
|
|
179
|
-
if (element.dataset[dname] !== value) element.dataset[dname] = value;
|
|
180
|
-
} else if (name.startsWith("role") || name.startsWith("aria-")) {
|
|
181
|
-
if (element.getAttribute(name) !== value) element.setAttribute(name, value)
|
|
182
|
-
} else {
|
|
183
|
-
if (element[name] !== value) element[name] = value;
|
|
184
|
-
if (name === 'key' && value) keyCache[value] = element;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function Fragment(props, ...children): any[] {
|
|
190
|
-
return collect(children);
|
|
191
|
-
}
|
package/src/vdom-to-html.tsx
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { VDOM } from './types';
|
|
3
|
-
|
|
4
|
-
function getProp(prop) {
|
|
5
|
-
if (typeof prop === 'object') {
|
|
6
|
-
return Object.keys(prop).map(name => `${name}:${prop[name]}`).join(';');
|
|
7
|
-
}
|
|
8
|
-
else return prop.toString();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function toProps(props) {
|
|
12
|
-
return Object.keys(props)
|
|
13
|
-
.map(name => ` ${name === 'className' ? 'class' : name}="${getProp(props[name])}"`)
|
|
14
|
-
.join('');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function toHTMLArray(nodes) {
|
|
18
|
-
return nodes.map(node => toHTML(node)).join('');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function clean(obj) {
|
|
22
|
-
for (var i in obj) {
|
|
23
|
-
if (obj[i] == null) {
|
|
24
|
-
delete obj[i];
|
|
25
|
-
} else if (typeof obj[i] === 'object') {
|
|
26
|
-
clean(obj[i]);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function toHTML (vdom: VDOM) {
|
|
32
|
-
if (!vdom) return '';
|
|
33
|
-
clean(vdom);
|
|
34
|
-
if (Array.isArray(vdom)) return toHTMLArray(vdom);
|
|
35
|
-
if (typeof vdom === 'string') {
|
|
36
|
-
return vdom.startsWith('_html:') ? vdom.substring(6) : vdom;
|
|
37
|
-
} else if (vdom.tag) {
|
|
38
|
-
const props = vdom.props ? toProps(vdom.props) : '';
|
|
39
|
-
const children = vdom.children ? toHTMLArray(vdom.children) : '';
|
|
40
|
-
return `<${vdom.tag}${props}>${children}</${vdom.tag}>`;
|
|
41
|
-
}
|
|
42
|
-
if (typeof vdom === 'object') return JSON.stringify(vdom);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export default toHTML;
|
package/src/vdom.ts
DELETED
package/src/web-component.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Component } from './component';
|
|
2
|
-
declare var customElements;
|
|
3
|
-
|
|
4
|
-
export const customElement = (componentClass, options={}) => class extends HTMLElement {
|
|
5
|
-
private _shadowRoot;
|
|
6
|
-
private _component;
|
|
7
|
-
public on;
|
|
8
|
-
public run;
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
const opts = { render: true, shadow: false, ...options };
|
|
12
|
-
this._shadowRoot = opts.shadow ?
|
|
13
|
-
this.attachShadow({ mode: 'open' }) : this;
|
|
14
|
-
const props = {}
|
|
15
|
-
Array.from(this.attributes).forEach(item => props[item.name] = item.value);
|
|
16
|
-
if (this.children) {
|
|
17
|
-
props['children'] = Array.from(this.children);
|
|
18
|
-
props['children'].forEach(el => el.parentElement.removeChild(el));
|
|
19
|
-
}
|
|
20
|
-
this._component = new componentClass(props).mount(this._shadowRoot, opts);
|
|
21
|
-
this.on = this._component.on.bind(this._component);
|
|
22
|
-
this.run = this._component.run.bind(this._component);
|
|
23
|
-
}
|
|
24
|
-
get state() { return this._component.state; }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default (name: string, componentClass, options?) => {
|
|
28
|
-
customElements && customElements.define(name, customElement(componentClass, options))
|
|
29
|
-
}
|
package/tests/app-rx.spec_
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import {App} from '../src/app';
|
|
2
|
-
|
|
3
|
-
describe('app', () => {
|
|
4
|
-
|
|
5
|
-
let app: App;
|
|
6
|
-
beforeEach(() => app = new App());
|
|
7
|
-
|
|
8
|
-
it ('should be able to cancel subscription', () => {
|
|
9
|
-
let called = false;
|
|
10
|
-
const fn = () => (called = true);
|
|
11
|
-
const subscription: any = app.on('test', fn);
|
|
12
|
-
subscription.unsubscribe();
|
|
13
|
-
app.run('test');
|
|
14
|
-
expect(called).toBeFalsy();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it ('should be able to return Observable<{}> from app.on', (done) => {
|
|
18
|
-
let called = false;
|
|
19
|
-
const subject:any = app.on('test');
|
|
20
|
-
subject.debounceTime(200).subscribe(()=> called = true);
|
|
21
|
-
app.run('test');
|
|
22
|
-
expect(called).toBeFalsy();
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
expect(called).toBeTruthy();
|
|
25
|
-
done();
|
|
26
|
-
}, 250);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it ('should be able to return Subject<{}> from app.run', () => {
|
|
30
|
-
let i=0, j=0
|
|
31
|
-
const fn = () => {i++};
|
|
32
|
-
const subscription: any = app.on('test', fn)
|
|
33
|
-
const subject = app.run('test');
|
|
34
|
-
subscription.unsubscribe();
|
|
35
|
-
subject.subscribe(() => j++);
|
|
36
|
-
subject.next();
|
|
37
|
-
expect(i).toEqual(1);
|
|
38
|
-
expect(j).toEqual(1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
});
|