-
-
Notifications
You must be signed in to change notification settings - Fork 111
Expand file tree
/
Copy pathComponent.js
More file actions
111 lines (108 loc) · 4 KB
/
Component.js
File metadata and controls
111 lines (108 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
'use strict';
const { WeakMap } = require('../shared/poorlyfills.js');
// hyperHTML.Component is a very basic class
// able to create Custom Elements like components
// including the ability to listen to connect/disconnect
// events via onconnect/ondisconnect attributes
// Components can be created imperatively or declaratively.
// The main difference is that declared components
// will not automatically render on setState(...)
// to simplify state handling on render.
function Component() {}
Object.defineProperty(exports, '__esModule', {value: true}).default = Component
// Component is lazily setup because it needs
// wire mechanism as lazy content
function setup(content) {
// there are various weakly referenced variables in here
// and mostly are to use Component.for(...) static method.
const children = new WeakMap;
const create = Object.create;
const createEntry = (wm, id, component) => {
wm.set(id, component);
return component;
};
const get = (Class, info, context, id) => {
switch (typeof id) {
case 'object':
case 'function':
const wm = info.w || (info.w = new WeakMap);
return wm.get(id) || createEntry(wm, id, new Class(context));
default:
const sm = info.p || (info.p = create(null));
return sm[id] || (sm[id] = new Class(context));
}
};
const set = context => {
const info = {w: null, p: null};
children.set(context, info);
return info;
};
// The Component Class
Object.defineProperties(
Component,
{
// Component.for(context[, id]) is a convenient way
// to automatically relate data/context to children components
// If not created yet, the new Component(context) is weakly stored
// and after that same instance would always be returned.
for: {
configurable: true,
value(context, id) {
const info = children.get(context) || set(context);
return get(this, info, context, id == null ? 'default' : id);
}
}
}
);
Object.defineProperties(
Component.prototype,
{
// all events are handled with the component as context
handleEvent: {value(e) {
const ct = e.currentTarget;
this[
('getAttribute' in ct && ct.getAttribute('data-call')) ||
('on' + e.type)
](e);
}},
// components will lazily define html or svg properties
// as soon as these are invoked within the .render() method
// Such render() method is not provided by the base class
// but it must be available through the Component extend.
// Declared components could implement a
// render(props) method too and use props as needed.
html: lazyGetter('html', content),
svg: lazyGetter('svg', content),
// the state is a very basic/simple mechanism inspired by Preact
state: lazyGetter('state', function () { return this.defaultState; }),
// it is possible to define a default state that'd be always an object otherwise
defaultState: {get() { return {}; }},
// setting some property state through a new object
// or a callback, triggers also automatically a render
// unless explicitly specified to not do so (render === false)
setState: {value(state, render) {
const target = this.state;
const source = typeof state === 'function' ? state.call(this, target) : state;
for (const key in source) target[key] = source[key];
if (render !== false) this.render();
return this;
}}
}
);
}
exports.setup = setup
// instead of a secret key I could've used a WeakMap
// However, attaching a property directly will result
// into better performance with thousands of components
// hanging around, and less memory pressure caused by the WeakMap
const lazyGetter = (type, fn) => {
const secret = '_' + type + '$';
return {
get() {
return this[secret] || (this[type] = fn.call(this, type));
},
set(value) {
Object.defineProperty(this, secret, {configurable: true, value});
}
};
};