p-elements-core 1.2.32-rc7 → 1.2.32-rc9

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.
Files changed (64) hide show
  1. package/.editorconfig +17 -17
  2. package/.gitlab-ci.yml +18 -18
  3. package/CHANGELOG.md +201 -201
  4. package/demo/sample.js +1 -1
  5. package/demo/screen.css +16 -16
  6. package/dist/p-elements-core-modern.js +1 -1
  7. package/dist/p-elements-core.js +1 -1
  8. package/docs/package-lock.json +6897 -6897
  9. package/docs/package.json +27 -27
  10. package/docs/src/404.md +8 -8
  11. package/docs/src/_data/demos/hello-world/hello-world.tsx +35 -35
  12. package/docs/src/_data/demos/hello-world/index.html +10 -10
  13. package/docs/src/_data/demos/hello-world/project.json +7 -7
  14. package/docs/src/_data/demos/timer/demo-timer.tsx +120 -120
  15. package/docs/src/_data/demos/timer/icons.tsx +62 -62
  16. package/docs/src/_data/demos/timer/index.html +12 -12
  17. package/docs/src/_data/demos/timer/project.json +8 -8
  18. package/docs/src/_data/global.js +13 -13
  19. package/docs/src/_data/helpers.js +19 -19
  20. package/docs/src/_includes/layouts/base.njk +30 -30
  21. package/docs/src/_includes/layouts/playground.njk +40 -40
  22. package/docs/src/_includes/partials/app-header.njk +8 -8
  23. package/docs/src/_includes/partials/head.njk +14 -14
  24. package/docs/src/_includes/partials/nav.njk +19 -19
  25. package/docs/src/_includes/partials/top-nav.njk +51 -51
  26. package/docs/src/documentation/custom-element.md +221 -221
  27. package/docs/src/documentation/decorators/bind.md +71 -71
  28. package/docs/src/documentation/decorators/custom-element-config.md +63 -63
  29. package/docs/src/documentation/decorators/property.md +83 -83
  30. package/docs/src/documentation/decorators/query.md +66 -66
  31. package/docs/src/documentation/decorators/render-property-on-set.md +60 -60
  32. package/docs/src/documentation/decorators.md +9 -9
  33. package/docs/src/documentation/reactive-properties.md +53 -53
  34. package/docs/src/index.d.ts +25 -25
  35. package/docs/src/index.md +3 -3
  36. package/docs/src/scripts/components/app-mode-switch/app-mode-switch.css +78 -78
  37. package/docs/src/scripts/components/app-mode-switch/app-mode-switch.tsx +166 -166
  38. package/docs/src/scripts/components/app-playground/app-playground.tsx +189 -189
  39. package/docs/tsconfig.json +22 -22
  40. package/index.html +10 -2
  41. package/package.json +1 -1
  42. package/readme.md +206 -206
  43. package/src/custom-element-controller.ts +31 -31
  44. package/src/custom-element.test.ts +906 -906
  45. package/src/custom-element.ts +28 -21
  46. package/src/decorators/bind.test.ts +163 -163
  47. package/src/decorators/bind.ts +46 -46
  48. package/src/decorators/custom-element-config.ts +17 -17
  49. package/src/decorators/property.test.ts +279 -279
  50. package/src/decorators/query.test.ts +146 -146
  51. package/src/decorators/query.ts +12 -12
  52. package/src/decorators/render-property-on-set.ts +3 -3
  53. package/src/helpers/css.ts +71 -71
  54. package/src/maquette/cache.ts +35 -35
  55. package/src/maquette/dom.ts +115 -115
  56. package/src/maquette/h.ts +100 -100
  57. package/src/maquette/index.ts +12 -12
  58. package/src/maquette/interfaces.ts +536 -536
  59. package/src/maquette/jsx.ts +61 -61
  60. package/src/maquette/mapping.ts +56 -56
  61. package/src/maquette/projection.ts +666 -666
  62. package/src/maquette/projector.ts +205 -200
  63. package/src/sample/mixin/highlight.tsx +33 -33
  64. package/src/sample/sample.tsx +98 -0
@@ -71,7 +71,7 @@ export abstract class CustomElement extends HTMLElement {
71
71
 
72
72
  #internalsObjectUntilAttached: object | null;
73
73
 
74
- #controllers: ICustomElementController[] = [];
74
+ readonly #controllers: ICustomElementController[] = [];
75
75
 
76
76
  /** Promise that resolves when the current update is complete */
77
77
  #updatePromise: Promise<void> | null = null;
@@ -131,7 +131,7 @@ export abstract class CustomElement extends HTMLElement {
131
131
  *
132
132
  * @returns {readonly PropertyOptionsWithName[]} Array of property metadata
133
133
  */
134
- get properties() : readonly PropertyOptionsWithName[] {
134
+ get properties(): readonly PropertyOptionsWithName[] {
135
135
  const ctor = this.constructor as ComponentConstructor;
136
136
  if (!ctor._propertyInfo) {
137
137
  return [];
@@ -231,7 +231,7 @@ export abstract class CustomElement extends HTMLElement {
231
231
  * @public
232
232
  */
233
233
  renderNow(): void {
234
- if (!this.shadowRoot) {
234
+ if (this.#useShadowRoot && !this.shadowRoot) {
235
235
  return;
236
236
  }
237
237
 
@@ -372,11 +372,10 @@ export abstract class CustomElement extends HTMLElement {
372
372
  this.#initStylesheet(styleElement.textContent);
373
373
  styleElement.remove();
374
374
  }
375
- window.addEventListener("updatecssapply", () => {
375
+ window.addEventListener("updatecssapply", () => {
376
376
  this.#polyfillCssApply();
377
377
  });
378
378
 
379
-
380
379
  return fragment;
381
380
  }
382
381
 
@@ -391,9 +390,13 @@ export abstract class CustomElement extends HTMLElement {
391
390
  element: Element,
392
391
  render: () => VNode,
393
392
  ): Promise<Projector> {
393
+ await new Promise((resolve) =>
394
+ requestAnimationFrame(() => requestAnimationFrame(resolve)),
395
+ );
396
+ return await this.#createProjector(element, render);
397
+ }
394
398
 
395
- await new Promise((resolve) => requestAnimationFrame(resolve));
396
-
399
+ #createProjector(element: Element, render: () => VNode): Promise<Projector> {
397
400
  return new Promise<Projector>((resolve, reject) => {
398
401
  let projector: Projector;
399
402
  const mode = this.#projectorMode ? this.#projectorMode : "append";
@@ -403,13 +406,11 @@ export abstract class CustomElement extends HTMLElement {
403
406
  if (eventName === "renderStart" || eventName === "renderDone") {
404
407
  this.#invokeRenderLifecycleFn(eventName);
405
408
  }
406
- }
409
+ },
407
410
  });
408
411
  projector[mode](element, render.bind(this));
409
412
  this.#projector = projector;
410
- requestAnimationFrame(() => {
411
- projector.renderNow();
412
- });
413
+ projector.renderNow();
413
414
  resolve(projector);
414
415
  this.dispatchEvent(new CustomEvent("firstRender", {}));
415
416
  });
@@ -428,13 +429,21 @@ export abstract class CustomElement extends HTMLElement {
428
429
  }
429
430
 
430
431
  #invokeRenderLifecycleFn(eventName: string) {
431
- if (this[eventName]){
432
- this[eventName](eventName === "renderStart" ? this.#isFirstRenderStart : this.#isFirstRenderDone);
432
+ if (this[eventName]) {
433
+ this[eventName](
434
+ eventName === "renderStart"
435
+ ? this.#isFirstRenderStart
436
+ : this.#isFirstRenderDone,
437
+ );
433
438
  }
434
439
  const controllerEventName = `host${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
435
440
  this.#controllers.forEach((controller) => {
436
441
  if (controller[controllerEventName]) {
437
- controller[controllerEventName](eventName === "renderStart" ? this.#isFirstRenderStart : this.#isFirstRenderDone);
442
+ controller[controllerEventName](
443
+ eventName === "renderStart"
444
+ ? this.#isFirstRenderStart
445
+ : this.#isFirstRenderDone,
446
+ );
438
447
  }
439
448
  });
440
449
  if (eventName === "renderStart") {
@@ -491,18 +500,17 @@ export abstract class CustomElement extends HTMLElement {
491
500
  const div = document.createElement("div");
492
501
  this.shadowRoot.appendChild(div);
493
502
  requestAnimationFrame(() => {
494
- this.createProjector(div, (this as any).render).then(() => {
503
+ this.#createProjector(div, (this as any).render).then(() => {
495
504
  this.#upgradeProperties();
496
- })
505
+ });
497
506
  });
498
- window.addEventListener("updatecssapply", () => {
507
+ window.addEventListener("updatecssapply", () => {
499
508
  this.#polyfillCssApply();
500
509
  });
501
-
502
510
  }
503
511
  }
504
512
 
505
- #polyfillCssApply(): string {
513
+ #polyfillCssApply(): string {
506
514
  let style = replaceApplyToCssVars(this.#cssText);
507
515
  if (this.#cssText !== style) {
508
516
  this.#cssText = style;
@@ -513,14 +521,13 @@ export abstract class CustomElement extends HTMLElement {
513
521
  URL.revokeObjectURL(this.#linkElement.href);
514
522
  }
515
523
  this.#linkElement.href = URL.createObjectURL(
516
- new Blob([style], { type: "text/css" })
524
+ new Blob([style], { type: "text/css" }),
517
525
  );
518
526
  }
519
527
  }
520
528
  return style;
521
529
  }
522
530
 
523
-
524
531
  #initStylesheet(style: string) {
525
532
  this.#cssText = style;
526
533
  if (this.#useShadowRoot && this.shadowRoot) {
@@ -1,163 +1,163 @@
1
- /**
2
- * Tests for @bind decorator
3
- * Covers method binding to preserve 'this' context
4
- */
5
-
6
- import { describe, it, expect } from 'vitest';
7
- import '../test-setup.js';
8
- import { bind } from './bind.js';
9
- import { CustomElement } from '../custom-element.js';
10
- import { customElementConfig } from './custom-element-config.js';
11
- import { generateUniqueTagName } from '../test-setup.js';
12
- import { waitForRender } from '../test-utils.js';
13
-
14
- describe('@bind decorator', () => {
15
- it('should bind method to instance', async () => {
16
- const tagName = generateUniqueTagName('bind-test');
17
-
18
- @customElementConfig({ tagName })
19
- class BindTest extends CustomElement {
20
- static style = ':host { display: block; }';
21
- value = 'bound';
22
-
23
- @bind
24
- getValue() {
25
- return this.value;
26
- }
27
-
28
- render() {
29
- return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
30
- }
31
- }
32
-
33
- const el = document.createElement(tagName) as BindTest;
34
- document.body.appendChild(el);
35
- await waitForRender(el);
36
-
37
- const method = el.getValue;
38
- expect(method()).toBe('bound');
39
-
40
- document.body.removeChild(el);
41
- });
42
-
43
- it('should preserve this context when method is extracted', async () => {
44
- const tagName = generateUniqueTagName('bind-test');
45
-
46
- @customElementConfig({ tagName })
47
- class BindTest extends CustomElement {
48
- static style = ':host { display: block; }';
49
- name = 'test-element';
50
-
51
- @bind
52
- getName() {
53
- return this.name;
54
- }
55
-
56
- render() {
57
- return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
58
- }
59
- }
60
-
61
- const el = document.createElement(tagName) as BindTest;
62
- document.body.appendChild(el);
63
- await waitForRender(el);
64
-
65
- const { getName } = el;
66
- expect(getName()).toBe('test-element');
67
-
68
- document.body.removeChild(el);
69
- });
70
-
71
- it('should work with callbacks', async () => {
72
- const tagName = generateUniqueTagName('bind-test');
73
- let result: string;
74
-
75
- @customElementConfig({ tagName })
76
- class BindTest extends CustomElement {
77
- static style = ':host { display: block; }';
78
- message = 'callback test';
79
-
80
- @bind
81
- handleCallback() {
82
- return this.message;
83
- }
84
-
85
- render() {
86
- return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
87
- }
88
- }
89
-
90
- const el = document.createElement(tagName) as BindTest;
91
- document.body.appendChild(el);
92
- await waitForRender(el);
93
-
94
- const callback = el.handleCallback;
95
- result = callback();
96
-
97
- expect(result).toBe('callback test');
98
-
99
- document.body.removeChild(el);
100
- });
101
-
102
- it('should handle methods with arguments', async () => {
103
- const tagName = generateUniqueTagName('bind-test');
104
-
105
- @customElementConfig({ tagName })
106
- class BindTest extends CustomElement {
107
- static style = ':host { display: block; }';
108
-
109
- prefixValue = 'Hello';
110
-
111
- @bind
112
- greet(name: string) {
113
- return `${this.prefixValue}, ${name}!`;
114
- }
115
-
116
- render() {
117
- return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
118
- }
119
- }
120
-
121
- const el = document.createElement(tagName) as BindTest;
122
- document.body.appendChild(el);
123
- await waitForRender(el);
124
-
125
- const extracted = el.greet;
126
- expect(extracted('World')).toBe('Hello, World!');
127
-
128
- document.body.removeChild(el);
129
- });
130
-
131
- it('should preserve bound method reference', async () => {
132
- const tagName = generateUniqueTagName('bind-test');
133
-
134
- @customElementConfig({ tagName })
135
- class BindTest extends CustomElement {
136
- static style = ':host { display: block; }';
137
-
138
- valueNum = 42;
139
-
140
- @bind
141
- getValue() {
142
- return this.valueNum;
143
- }
144
-
145
- render() {
146
- return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
147
- }
148
- }
149
-
150
- const el = document.createElement(tagName) as BindTest;
151
- document.body.appendChild(el);
152
- await waitForRender(el);
153
-
154
- const bound1 = el.getValue;
155
- const bound2 = el.getValue;
156
-
157
- // Should return the same bound function
158
- expect(bound1).toBe(bound2);
159
- expect(bound1()).toBe(42);
160
-
161
- document.body.removeChild(el);
162
- });
163
- });
1
+ /**
2
+ * Tests for @bind decorator
3
+ * Covers method binding to preserve 'this' context
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import '../test-setup.js';
8
+ import { bind } from './bind.js';
9
+ import { CustomElement } from '../custom-element.js';
10
+ import { customElementConfig } from './custom-element-config.js';
11
+ import { generateUniqueTagName } from '../test-setup.js';
12
+ import { waitForRender } from '../test-utils.js';
13
+
14
+ describe('@bind decorator', () => {
15
+ it('should bind method to instance', async () => {
16
+ const tagName = generateUniqueTagName('bind-test');
17
+
18
+ @customElementConfig({ tagName })
19
+ class BindTest extends CustomElement {
20
+ static style = ':host { display: block; }';
21
+ value = 'bound';
22
+
23
+ @bind
24
+ getValue() {
25
+ return this.value;
26
+ }
27
+
28
+ render() {
29
+ return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
30
+ }
31
+ }
32
+
33
+ const el = document.createElement(tagName) as BindTest;
34
+ document.body.appendChild(el);
35
+ await waitForRender(el);
36
+
37
+ const method = el.getValue;
38
+ expect(method()).toBe('bound');
39
+
40
+ document.body.removeChild(el);
41
+ });
42
+
43
+ it('should preserve this context when method is extracted', async () => {
44
+ const tagName = generateUniqueTagName('bind-test');
45
+
46
+ @customElementConfig({ tagName })
47
+ class BindTest extends CustomElement {
48
+ static style = ':host { display: block; }';
49
+ name = 'test-element';
50
+
51
+ @bind
52
+ getName() {
53
+ return this.name;
54
+ }
55
+
56
+ render() {
57
+ return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
58
+ }
59
+ }
60
+
61
+ const el = document.createElement(tagName) as BindTest;
62
+ document.body.appendChild(el);
63
+ await waitForRender(el);
64
+
65
+ const { getName } = el;
66
+ expect(getName()).toBe('test-element');
67
+
68
+ document.body.removeChild(el);
69
+ });
70
+
71
+ it('should work with callbacks', async () => {
72
+ const tagName = generateUniqueTagName('bind-test');
73
+ let result: string;
74
+
75
+ @customElementConfig({ tagName })
76
+ class BindTest extends CustomElement {
77
+ static style = ':host { display: block; }';
78
+ message = 'callback test';
79
+
80
+ @bind
81
+ handleCallback() {
82
+ return this.message;
83
+ }
84
+
85
+ render() {
86
+ return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
87
+ }
88
+ }
89
+
90
+ const el = document.createElement(tagName) as BindTest;
91
+ document.body.appendChild(el);
92
+ await waitForRender(el);
93
+
94
+ const callback = el.handleCallback;
95
+ result = callback();
96
+
97
+ expect(result).toBe('callback test');
98
+
99
+ document.body.removeChild(el);
100
+ });
101
+
102
+ it('should handle methods with arguments', async () => {
103
+ const tagName = generateUniqueTagName('bind-test');
104
+
105
+ @customElementConfig({ tagName })
106
+ class BindTest extends CustomElement {
107
+ static style = ':host { display: block; }';
108
+
109
+ prefixValue = 'Hello';
110
+
111
+ @bind
112
+ greet(name: string) {
113
+ return `${this.prefixValue}, ${name}!`;
114
+ }
115
+
116
+ render() {
117
+ return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
118
+ }
119
+ }
120
+
121
+ const el = document.createElement(tagName) as BindTest;
122
+ document.body.appendChild(el);
123
+ await waitForRender(el);
124
+
125
+ const extracted = el.greet;
126
+ expect(extracted('World')).toBe('Hello, World!');
127
+
128
+ document.body.removeChild(el);
129
+ });
130
+
131
+ it('should preserve bound method reference', async () => {
132
+ const tagName = generateUniqueTagName('bind-test');
133
+
134
+ @customElementConfig({ tagName })
135
+ class BindTest extends CustomElement {
136
+ static style = ':host { display: block; }';
137
+
138
+ valueNum = 42;
139
+
140
+ @bind
141
+ getValue() {
142
+ return this.valueNum;
143
+ }
144
+
145
+ render() {
146
+ return { vnodeSelector: 'div', properties: {}, children: [], text: undefined, domNode: null };
147
+ }
148
+ }
149
+
150
+ const el = document.createElement(tagName) as BindTest;
151
+ document.body.appendChild(el);
152
+ await waitForRender(el);
153
+
154
+ const bound1 = el.getValue;
155
+ const bound2 = el.getValue;
156
+
157
+ // Should return the same bound function
158
+ expect(bound1).toBe(bound2);
159
+ expect(bound1()).toBe(42);
160
+
161
+ document.body.removeChild(el);
162
+ });
163
+ });
@@ -1,46 +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
- };
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
+ };
@@ -1,17 +1,17 @@
1
- export interface IElementConfig {
2
- tagName: string;
3
- options?: {
4
- extends: string;
5
- };
6
- }
7
- export const customElementConfig = (config: IElementConfig) => {
8
- return (Element) => {
9
- if (customElements.get(config.tagName)) {
10
- console.warn(
11
- `Custom element with tag name ${config.tagName} already exists.`
12
- );
13
- return;
14
- }
15
- customElements.define(config.tagName, Element, config.options);
16
- };
17
- };
1
+ export interface IElementConfig {
2
+ tagName: string;
3
+ options?: {
4
+ extends: string;
5
+ };
6
+ }
7
+ export const customElementConfig = (config: IElementConfig) => {
8
+ return (Element) => {
9
+ if (customElements.get(config.tagName)) {
10
+ console.warn(
11
+ `Custom element with tag name ${config.tagName} already exists.`
12
+ );
13
+ return;
14
+ }
15
+ customElements.define(config.tagName, Element, config.options);
16
+ };
17
+ };