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.
@@ -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
- protected templateFromString(html: string, shady?: boolean): any;
171
- protected updateStyle(): void;
172
- protected createProjector(
173
- element: Element,
174
- render: () => VNode
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 function PropertyRenderOnSet(target: object, propertyKey: string): void;
186
-
187
- declare function RenderOnSet(target: object, propertyKey: string): void;
187
+ declare const PropertyRenderOnSet: (target: object, propertyKey: string) => void;
188
188
 
189
- declare function ReflectToAttribute(target: object, propertyKey: string): void;
189
+ declare const RenderOnSet: (target: object, propertyKey: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "p-elements-core",
3
- "version": "1.2.2",
3
+ "version": "1.2.3-rc1",
4
4
  "description": "P Elements Core V1",
5
5
  "main": "dist/p-elements-core.js",
6
6
  "types": "p-elements-core.d.ts",
@@ -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 = (target, key, descriptor) => {
14
- let fn = descriptor.value;
15
- console.warn("@Bind decorator is deprecated, use arrow function expression");
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 = (this.shadowRoot as any).adoptedStyleSheets && CSSStyleSheet;
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 = new CSSStyleSheet();
163
- // adopt stylesheet with css processed by ShadyCSS or the css match from template
164
- (styleSheet as any).replaceSync(cssMap.has(this.tagName) === true ? cssMap.get(this.tagName) : styleMatch[1]);
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([ cssMap.get(this.tagName) ? cssMap.get(this.tagName) : styleMatch[1]], { type: "text/css" });
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.append(element, render);
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
- Bind,
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
- private connectedCallback() {
202
+ constructor(args) {
203
+ super(args);
203
204
  const template = this.templateFromString(
204
- `<style>:host{color: red}</style><div class="root"></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 {