p-elements-core 1.2.2 → 1.2.3-rc1
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/demo/sample.js +2 -2
- package/dist/p-elements-core-modern.js +3 -3
- package/dist/p-elements-core.js +3 -3
- package/p-elements-core.d.ts +10 -10
- package/package.json +1 -1
- package/src/custom-element.ts +76 -123
- package/src/decorators/bind.ts +46 -0
- package/src/decorators/custom-element-config.ts +10 -0
- package/src/decorators/render-property-on-set.ts +46 -0
- package/src/sample.tsx +12 -7
package/p-elements-core.d.ts
CHANGED
|
@@ -166,13 +166,15 @@ declare const CustomElementConfig: (
|
|
|
166
166
|
config: IElementConfig
|
|
167
167
|
) => (Element: any) => void;
|
|
168
168
|
|
|
169
|
+
declare type ElementProjectorMode = "append" | "merge" | "replace";
|
|
170
|
+
|
|
169
171
|
declare abstract class CustomElement extends HTMLElement {
|
|
170
|
-
|
|
171
|
-
protected
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
): Promise<Projector>;
|
|
172
|
+
private _projector;
|
|
173
|
+
protected templateFromString(html: string): any;
|
|
174
|
+
private hashCode;
|
|
175
|
+
protected adoptStyle(root: Document | ShadowRoot, css: string): string | void;
|
|
176
|
+
projectorMode?: ElementProjectorMode;
|
|
177
|
+
protected createProjector(element: Element, render: () => VNode): Promise<Projector>;
|
|
176
178
|
protected renderNow(): void;
|
|
177
179
|
}
|
|
178
180
|
|
|
@@ -182,8 +184,6 @@ declare const Bind: (
|
|
|
182
184
|
descriptor: any
|
|
183
185
|
) => void;
|
|
184
186
|
|
|
185
|
-
declare
|
|
186
|
-
|
|
187
|
-
declare function RenderOnSet(target: object, propertyKey: string): void;
|
|
187
|
+
declare const PropertyRenderOnSet: (target: object, propertyKey: string) => void;
|
|
188
188
|
|
|
189
|
-
declare
|
|
189
|
+
declare const RenderOnSet: (target: object, propertyKey: string) => void;
|
package/package.json
CHANGED
package/src/custom-element.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { bind } from "./decorators/bind";
|
|
2
|
+
import { customElementConfig } from "./decorators/custom-element-config";
|
|
3
|
+
import { propertyRenderOnSet } from "./decorators/render-property-on-set";
|
|
4
|
+
|
|
5
|
+
export type ElementProjectorMode = "append" | "merge" | "replace";
|
|
6
|
+
|
|
1
7
|
declare var HTMLElement: {
|
|
2
8
|
prototype: HTMLElement;
|
|
3
9
|
new (param?): HTMLElement;
|
|
@@ -10,112 +16,9 @@ export interface IElementConfig {
|
|
|
10
16
|
};
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
export const Bind =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (typeof fn !== "function") {
|
|
17
|
-
throw new Error(
|
|
18
|
-
`@Bind decorator can only be applied to methods not: ${typeof fn}`
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// In IE11 calling Object.defineProperty has a side-effect of evaluating the
|
|
23
|
-
// getter for the property which is being replaced. This causes infinite
|
|
24
|
-
// recursion and an "Out of stack space" error.
|
|
25
|
-
let definingProperty = false;
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
configurable: true,
|
|
29
|
-
get() {
|
|
30
|
-
if (
|
|
31
|
-
definingProperty ||
|
|
32
|
-
this === target.prototype ||
|
|
33
|
-
this.hasOwnProperty(key) ||
|
|
34
|
-
typeof fn !== "function"
|
|
35
|
-
) {
|
|
36
|
-
return fn;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let boundFn = fn.bind(this);
|
|
40
|
-
definingProperty = true;
|
|
41
|
-
Object.defineProperty(this, key, {
|
|
42
|
-
configurable: true,
|
|
43
|
-
get() {
|
|
44
|
-
return boundFn;
|
|
45
|
-
},
|
|
46
|
-
set(value) {
|
|
47
|
-
fn = value;
|
|
48
|
-
delete this[key];
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
definingProperty = false;
|
|
52
|
-
return boundFn;
|
|
53
|
-
},
|
|
54
|
-
set(value) {
|
|
55
|
-
fn = value;
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const CustomElementConfig = (config: IElementConfig) => {
|
|
61
|
-
return (Element) => {
|
|
62
|
-
setTimeout(() => {
|
|
63
|
-
customElements.define(config.tagName, Element, config.options);
|
|
64
|
-
}, 10);
|
|
65
|
-
const camelCased = config.tagName.replace(/-([a-z])/g, function (g) {
|
|
66
|
-
return g[1].toUpperCase();
|
|
67
|
-
});
|
|
68
|
-
(<any>window)[camelCased.charAt(0).toUpperCase() + camelCased.slice(1)] =
|
|
69
|
-
Element;
|
|
70
|
-
};
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export function PropertyRenderOnSet(target, key) {
|
|
74
|
-
if (!Reflect.get(target, "_dp_" + key)) {
|
|
75
|
-
Reflect.defineProperty(target, "_dp_" + key, {
|
|
76
|
-
configurable: true,
|
|
77
|
-
enumerable: true,
|
|
78
|
-
get: function () {
|
|
79
|
-
return this["_" + key + "_value"];
|
|
80
|
-
},
|
|
81
|
-
set: function (value) {
|
|
82
|
-
this["_" + key + "_value"] = value;
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!target["_dp_setters_" + key]) {
|
|
88
|
-
target["_dp_setters_" + key] = [];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const propertyRenderOnSetFunction = (o, v) => {
|
|
92
|
-
if (o && o.renderNow) {
|
|
93
|
-
o.renderNow();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
target["_dp_setters_" + key].push(propertyRenderOnSetFunction);
|
|
98
|
-
|
|
99
|
-
const getter = function () {
|
|
100
|
-
return this["_" + key + "_value"];
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const setter = function (newVal) {
|
|
104
|
-
this["_dp_" + key] = newVal;
|
|
105
|
-
if (target["_dp_setters_" + key]) {
|
|
106
|
-
target["_dp_setters_" + key].forEach((fn) => {
|
|
107
|
-
fn(this, newVal);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
Object.defineProperty(target, key, {
|
|
113
|
-
configurable: true,
|
|
114
|
-
enumerable: true,
|
|
115
|
-
get: getter,
|
|
116
|
-
set: setter,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
19
|
+
export const Bind = bind;
|
|
20
|
+
export const CustomElementConfig = customElementConfig;
|
|
21
|
+
export const PropertyRenderOnSet = propertyRenderOnSet;
|
|
119
22
|
|
|
120
23
|
declare var HTMLElement: {
|
|
121
24
|
prototype: HTMLElement;
|
|
@@ -133,6 +36,9 @@ if (!isNative((document as any).registerElement)) {
|
|
|
133
36
|
// cssMap contain css string processed by ShadyCSS
|
|
134
37
|
const cssMap = new Map();
|
|
135
38
|
|
|
39
|
+
// documentAdoptedStyleSheets contain hash of css string
|
|
40
|
+
const documentAdoptedStyleSheets: number[] = [];
|
|
41
|
+
|
|
136
42
|
export abstract class CustomElement extends HTMLElement {
|
|
137
43
|
constructor(self) {
|
|
138
44
|
self = super(self);
|
|
@@ -150,22 +56,30 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
150
56
|
this.attachShadow({ mode: "open" });
|
|
151
57
|
}
|
|
152
58
|
this.shadowRoot.innerHTML = "";
|
|
153
|
-
const supportReplaceCssSync =
|
|
59
|
+
const supportReplaceCssSync =
|
|
60
|
+
(this.shadowRoot as any).adoptedStyleSheets && CSSStyleSheet;
|
|
154
61
|
|
|
155
|
-
// find css style in template
|
|
62
|
+
// find css style in template
|
|
156
63
|
let style = "<style></style>";
|
|
157
64
|
const styleMatch = html.match(/<style[^>]*>([\s\S]*?)<\/style>/);
|
|
158
65
|
if (styleMatch) {
|
|
159
66
|
style = styleMatch[0];
|
|
160
67
|
if (supportReplaceCssSync) {
|
|
161
68
|
html = html.replace(styleMatch[0], "");
|
|
162
|
-
const styleSheet =
|
|
163
|
-
// adopt stylesheet with css processed by ShadyCSS or the css match from template
|
|
164
|
-
(styleSheet as any).replaceSync(
|
|
69
|
+
const styleSheet = new CSSStyleSheet();
|
|
70
|
+
// adopt stylesheet with css processed by ShadyCSS or the css match from template
|
|
71
|
+
(styleSheet as any).replaceSync(
|
|
72
|
+
cssMap.has(this.tagName) === true
|
|
73
|
+
? cssMap.get(this.tagName)
|
|
74
|
+
: styleMatch[1]
|
|
75
|
+
);
|
|
165
76
|
(this.shadowRoot as any).adoptedStyleSheets = [styleSheet];
|
|
166
77
|
} else {
|
|
167
78
|
// add link to stylesheet with object uri from css processed by ShadyCSS or the css match from template
|
|
168
|
-
const blob = new Blob(
|
|
79
|
+
const blob = new Blob(
|
|
80
|
+
[cssMap.get(this.tagName) ? cssMap.get(this.tagName) : styleMatch[1]],
|
|
81
|
+
{ type: "text/css" }
|
|
82
|
+
);
|
|
169
83
|
html = html.replace(
|
|
170
84
|
styleMatch[0],
|
|
171
85
|
`<link rel="stylesheet" href="${URL.createObjectURL(
|
|
@@ -180,7 +94,7 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
180
94
|
myElementTemplate.innerHTML = html;
|
|
181
95
|
let template = document.importNode(myElementTemplate.content, true);
|
|
182
96
|
|
|
183
|
-
// process css with ShadyCSS function
|
|
97
|
+
// process css with ShadyCSS function
|
|
184
98
|
const textContentForShadyStyle = () => {
|
|
185
99
|
if (cssMap.has(this.tagName)) {
|
|
186
100
|
return cssMap.get(this.tagName);
|
|
@@ -195,7 +109,7 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
195
109
|
return s;
|
|
196
110
|
};
|
|
197
111
|
|
|
198
|
-
// create object uri from css processed by ShadyCSS function
|
|
112
|
+
// create object uri from css processed by ShadyCSS function
|
|
199
113
|
const creatObjectUrlForShadyStyle = () => {
|
|
200
114
|
if (cssMap.has(this.tagName)) {
|
|
201
115
|
return cssMap.get(this.tagName);
|
|
@@ -219,8 +133,7 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
219
133
|
const css = textContentForShadyStyle();
|
|
220
134
|
(styleSheet as any).replaceSync(css);
|
|
221
135
|
(this.shadowRoot as any).adoptedStyleSheets = [styleSheet];
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
136
|
+
} else {
|
|
224
137
|
const link = this.shadowRoot.querySelector("link");
|
|
225
138
|
if (link) {
|
|
226
139
|
if (link.hasAttribute("data-tmp")) {
|
|
@@ -230,37 +143,78 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
230
143
|
link.href = creatObjectUrlForShadyStyle();
|
|
231
144
|
}
|
|
232
145
|
}
|
|
233
|
-
|
|
234
146
|
}, 300);
|
|
235
147
|
});
|
|
236
148
|
|
|
237
149
|
setTimeout(() => {
|
|
238
|
-
|
|
239
150
|
const link = this.shadowRoot.querySelector("link");
|
|
240
151
|
if (link) {
|
|
241
|
-
|
|
242
152
|
if (link.hasAttribute("data-tmp")) {
|
|
243
153
|
URL.revokeObjectURL(link.href);
|
|
244
154
|
link.removeAttribute("data-tmp");
|
|
245
155
|
}
|
|
246
156
|
link.href = creatObjectUrlForShadyStyle();
|
|
247
157
|
}
|
|
248
|
-
|
|
249
158
|
}, 100);
|
|
250
159
|
|
|
251
160
|
return template;
|
|
252
161
|
}
|
|
253
162
|
|
|
163
|
+
private hashCode(s: string) {
|
|
164
|
+
for (var i = 0, h = 0; i < s.length; i++)
|
|
165
|
+
h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
|
|
166
|
+
return h;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
protected adoptStyle(root: Document | ShadowRoot, css: string) : string | void {
|
|
170
|
+
let hash = 0;
|
|
171
|
+
if (root instanceof Document) {
|
|
172
|
+
hash = this.hashCode(css);
|
|
173
|
+
if (documentAdoptedStyleSheets.indexOf(hash) !== -1) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const hasAdoptedStyleSheetsSupport =
|
|
178
|
+
(root as any).adoptedStyleSheets && CSSStyleSheet;
|
|
179
|
+
|
|
180
|
+
if (hasAdoptedStyleSheetsSupport) {
|
|
181
|
+
const sheet = new CSSStyleSheet();
|
|
182
|
+
(sheet as any).replaceSync(css);
|
|
183
|
+
(root as any).adoptedStyleSheets = [
|
|
184
|
+
...(root as any).adoptedStyleSheets,
|
|
185
|
+
sheet,
|
|
186
|
+
];
|
|
187
|
+
} else {
|
|
188
|
+
const blob = new Blob([css], { type: "text/css" });
|
|
189
|
+
const url = URL.createObjectURL(blob);
|
|
190
|
+
|
|
191
|
+
const link = document.createElement("link");
|
|
192
|
+
link.setAttribute("rel", "stylesheet");
|
|
193
|
+
link.setAttribute("href", url);
|
|
194
|
+
if (root instanceof Document) {
|
|
195
|
+
root.head.appendChild(link)
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
root.appendChild(link);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (hash !== 0) {
|
|
202
|
+
documentAdoptedStyleSheets.push(hash);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
projectorMode?: ElementProjectorMode;
|
|
207
|
+
|
|
254
208
|
protected createProjector(
|
|
255
209
|
element: Element,
|
|
256
210
|
render: () => VNode
|
|
257
211
|
): Promise<Projector> {
|
|
258
212
|
return new Promise<Projector>((resolve, reject) => {
|
|
259
213
|
let projector: Projector;
|
|
260
|
-
|
|
214
|
+
const mode = this.projectorMode ? this.projectorMode : "append";
|
|
261
215
|
setTimeout(() => {
|
|
262
216
|
projector = (window as any).Maquette.createProjector();
|
|
263
|
-
projector
|
|
217
|
+
projector[mode](element, render);
|
|
264
218
|
this._projector = projector;
|
|
265
219
|
resolve(projector);
|
|
266
220
|
this.dispatchEvent(new CustomEvent("firstRender", {}));
|
|
@@ -273,5 +227,4 @@ export abstract class CustomElement extends HTMLElement {
|
|
|
273
227
|
this._projector.renderNow();
|
|
274
228
|
}
|
|
275
229
|
}
|
|
276
|
-
|
|
277
230
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const bind = (target, key, descriptor) => {
|
|
2
|
+
let fn = descriptor.value;
|
|
3
|
+
console.warn("@Bind decorator is deprecated, use arrow function expression");
|
|
4
|
+
if (typeof fn !== "function") {
|
|
5
|
+
throw new Error(
|
|
6
|
+
`@Bind decorator can only be applied to methods not: ${typeof fn}`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// In IE11 calling Object.defineProperty has a side-effect of evaluating the
|
|
11
|
+
// getter for the property which is being replaced. This causes infinite
|
|
12
|
+
// recursion and an "Out of stack space" error.
|
|
13
|
+
let definingProperty = false;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
configurable: true,
|
|
17
|
+
get() {
|
|
18
|
+
if (
|
|
19
|
+
definingProperty ||
|
|
20
|
+
this === target.prototype ||
|
|
21
|
+
this.hasOwnProperty(key) ||
|
|
22
|
+
typeof fn !== "function"
|
|
23
|
+
) {
|
|
24
|
+
return fn;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let boundFn = fn.bind(this);
|
|
28
|
+
definingProperty = true;
|
|
29
|
+
Object.defineProperty(this, key, {
|
|
30
|
+
configurable: true,
|
|
31
|
+
get() {
|
|
32
|
+
return boundFn;
|
|
33
|
+
},
|
|
34
|
+
set(value) {
|
|
35
|
+
fn = value;
|
|
36
|
+
delete this[key];
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
definingProperty = false;
|
|
40
|
+
return boundFn;
|
|
41
|
+
},
|
|
42
|
+
set(value) {
|
|
43
|
+
fn = value;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const customElementConfig = (config: IElementConfig) => {
|
|
2
|
+
return (Element) => {
|
|
3
|
+
customElements.define(config.tagName, Element, config.options);
|
|
4
|
+
const camelCased = config.tagName.replace(/-([a-z])/g, function (g) {
|
|
5
|
+
return g[1].toUpperCase();
|
|
6
|
+
});
|
|
7
|
+
(<any>window)[camelCased.charAt(0).toUpperCase() + camelCased.slice(1)] =
|
|
8
|
+
Element;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const propertyRenderOnSet = (target, key) => {
|
|
2
|
+
if (!Reflect.get(target, "_dp_" + key)) {
|
|
3
|
+
Reflect.defineProperty(target, "_dp_" + key, {
|
|
4
|
+
configurable: true,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
get: function () {
|
|
7
|
+
return this["_" + key + "_value"];
|
|
8
|
+
},
|
|
9
|
+
set: function (value) {
|
|
10
|
+
this["_" + key + "_value"] = value;
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!target["_dp_setters_" + key]) {
|
|
16
|
+
target["_dp_setters_" + key] = [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const propertyRenderOnSetFunction = (o, v) => {
|
|
20
|
+
if (o && o.renderNow) {
|
|
21
|
+
o.renderNow();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
target["_dp_setters_" + key].push(propertyRenderOnSetFunction);
|
|
26
|
+
|
|
27
|
+
const getter = function () {
|
|
28
|
+
return this["_" + key + "_value"];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const setter = function (newVal) {
|
|
32
|
+
this["_dp_" + key] = newVal;
|
|
33
|
+
if (target["_dp_setters_" + key]) {
|
|
34
|
+
target["_dp_setters_" + key].forEach((fn) => {
|
|
35
|
+
fn(this, newVal);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
Object.defineProperty(target, key, {
|
|
41
|
+
configurable: true,
|
|
42
|
+
enumerable: true,
|
|
43
|
+
get: getter,
|
|
44
|
+
set: setter,
|
|
45
|
+
});
|
|
46
|
+
}
|
package/src/sample.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
PropertyRenderOnSet,
|
|
3
3
|
CustomElement,
|
|
4
4
|
CustomElementConfig,
|
|
5
|
-
|
|
5
|
+
ElementProjectorMode,
|
|
6
6
|
} from "./custom-element";
|
|
7
7
|
import * as anime from "animejs";
|
|
8
8
|
|
|
@@ -199,16 +199,21 @@ class MyGreetings extends CustomElement {
|
|
|
199
199
|
tagName: "p-foo",
|
|
200
200
|
})
|
|
201
201
|
class PFoo extends CustomElement {
|
|
202
|
-
|
|
202
|
+
constructor(args) {
|
|
203
|
+
super(args);
|
|
203
204
|
const template = this.templateFromString(
|
|
204
|
-
`<style>:host{color: red}</style><div
|
|
205
|
+
`<style>:host{color: red}</style><div></div>`
|
|
205
206
|
);
|
|
206
207
|
this.shadowRoot.appendChild(template);
|
|
207
|
-
|
|
208
|
-
this.shadowRoot.querySelector(".root").innerHTML = `
|
|
209
|
-
<div>P-FOO</div>
|
|
210
|
-
`;
|
|
208
|
+
this.createProjector(this.shadowRoot.querySelector("div"), this.render);
|
|
211
209
|
}
|
|
210
|
+
|
|
211
|
+
projectorMode: ElementProjectorMode = "replace";
|
|
212
|
+
|
|
213
|
+
private render = () => {
|
|
214
|
+
return <div>Foo</div>;
|
|
215
|
+
};
|
|
216
|
+
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
class SuperAnchorElement extends HTMLAnchorElement {
|