p-elements-core 1.2.27 → 1.2.28

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 (46) hide show
  1. package/.gitlab-ci.yml +1 -0
  2. package/demo/sample.js +1 -1
  3. package/dist/p-elements-core-modern.js +1 -1
  4. package/dist/p-elements-core.js +1 -1
  5. package/docs/.eleventy.js +4 -1
  6. package/docs/package-lock.json +27 -4
  7. package/docs/package.json +2 -1
  8. package/docs/src/_data/demos/hello-world/hello-world.tsx +1 -1
  9. package/docs/src/_data/demos/timer/demo-timer.tsx +120 -0
  10. package/docs/src/_data/demos/timer/icons.tsx +62 -0
  11. package/docs/src/_data/demos/timer/index.html +5 -3
  12. package/docs/src/_data/demos/timer/project.json +3 -3
  13. package/docs/src/_includes/layouts/base.njk +22 -17
  14. package/docs/src/_includes/layouts/playground.njk +11 -6
  15. package/docs/src/_includes/partials/{page-header.njk → app-header.njk} +3 -2
  16. package/docs/src/_includes/partials/head.njk +4 -2
  17. package/docs/src/_includes/partials/nav.njk +15 -7
  18. package/docs/src/_includes/partials/top-nav.njk +52 -14
  19. package/docs/src/assets/favicon.png +0 -0
  20. package/docs/src/documentation/custom-element.md +221 -0
  21. package/docs/src/documentation/decorators/bind.md +71 -0
  22. package/docs/src/documentation/decorators/custom-element-config.md +63 -0
  23. package/docs/src/documentation/decorators/property.md +83 -0
  24. package/docs/src/documentation/decorators/query.md +66 -0
  25. package/docs/src/documentation/decorators/render-property-on-set.md +60 -0
  26. package/docs/src/documentation/decorators.md +9 -0
  27. package/docs/src/documentation/getting-started.md +49 -6
  28. package/docs/src/documentation/index.md +19 -17
  29. package/docs/src/documentation/reactive-properties.md +54 -0
  30. package/docs/src/index.d.ts +5 -3
  31. package/docs/src/playground/index.njk +10 -0
  32. package/docs/src/playground/timer.njk +10 -0
  33. package/docs/src/scripts/components/app-mobile-menu/app-mobile-menu.css +48 -0
  34. package/docs/src/scripts/components/app-mobile-menu/app-mobile-menu.tsx +112 -0
  35. package/docs/src/scripts/components/app-mode-switch/app-mode-switch.css +78 -0
  36. package/docs/src/scripts/components/app-mode-switch/app-mode-switch.tsx +166 -0
  37. package/docs/src/scripts/components/app-playground/app-playground.tsx +77 -54
  38. package/docs/src/scripts/components/app-split-panel/app-split-panel.css +33 -0
  39. package/docs/src/scripts/components/app-split-panel/app-split-panel.tsx +73 -0
  40. package/docs/src/scripts/components/app-split-panel/resize-bar.tsx +155 -0
  41. package/docs/src/scripts/index.ts +3 -0
  42. package/docs/src/styles/main.css +565 -85
  43. package/p-elements-core.d.ts +11 -2
  44. package/package.json +3 -2
  45. package/docs/src/_data/demos/timer/timer.tsx +0 -22
  46. package/docs/src/playground/index.md +0 -47
@@ -0,0 +1,112 @@
1
+ import css from "./app-mobile-menu.css";
2
+ @CustomElementConfig({
3
+ tagName: "app-mobile-menu",
4
+ })
5
+ export class AppMobileMenuElement extends CustomElement {
6
+ static readonly style = css;
7
+
8
+ @Property({ type: "boolean", attribute: "open", reflect: true })
9
+ open: boolean = false;
10
+
11
+ @Property({ type: "number", attribute: "width", reflect: true })
12
+ width: number = 300;
13
+
14
+ updated(propertyName: string, oldValue: any, newValue: any): void {
15
+ if (propertyName === "open") {
16
+ this.positionMenu(newValue === true);
17
+ } else if (propertyName === "width") {
18
+ requestAnimationFrame(() => {
19
+ this.#dialog?.style.setProperty(
20
+ "--app-mobile-menu-width",
21
+ newValue ? `${newValue}px` : "300px"
22
+ );
23
+ });
24
+ }
25
+ }
26
+
27
+ positionMenu(open: boolean) {
28
+ requestAnimationFrame(() => {
29
+ if (!this.#dialog) {
30
+ this.#dialog = Maquette.dom.create(
31
+ <dialog
32
+ on={{
33
+ close: (e: Event) => {
34
+ const dlg = e.target as HTMLDialogElement;
35
+ dlg.classList.remove("open");
36
+ this.open = false;
37
+ },
38
+ }}
39
+ >
40
+ <div class="dialog-content">
41
+ <button onclick={() => this.#dialog.close()} class="close-button">
42
+ <svg
43
+ width="2rem"
44
+ height="2rem"
45
+ viewBox="0 0 24 24"
46
+ fill="none"
47
+ xmlns="http://www.w3.org/2000/svg"
48
+ >
49
+ <path d="M18 6L6 18" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round"/>
50
+ <path d="M6 6L18 18" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round"/>
51
+ </svg>
52
+ <slot name="close"></slot>
53
+ </button>
54
+ <slot></slot>
55
+ </div>
56
+ </dialog>
57
+ ).domNode as HTMLDialogElement;
58
+ this.shadowRoot.appendChild(this.#dialog);
59
+ }
60
+ if (open) {
61
+ this.#dialog?.showModal();
62
+ requestAnimationFrame(() => {
63
+ this.#dialog.classList.add("open");
64
+ });
65
+ }
66
+ });
67
+ }
68
+
69
+ render(): VNode {
70
+ return (
71
+ <div class="mobile-menu">
72
+ <button onclick={() => (this.open = !this.open)}>
73
+ <svg
74
+ width="1.4rem"
75
+ height="1.4rem"
76
+ viewBox="0 0 24 24"
77
+ fill="none"
78
+ xmlns="http://www.w3.org/2000/svg"
79
+ >
80
+ <path
81
+ d="M4 18L20 18"
82
+ stroke="currentColor"
83
+ stroke-width="2"
84
+ stroke-linecap="round"
85
+ />
86
+ <path
87
+ d="M4 12L20 12"
88
+ stroke="currentColor"
89
+ stroke-width="2"
90
+ stroke-linecap="round"
91
+ />
92
+ <path
93
+ d="M4 6L20 6"
94
+ stroke="currentColor"
95
+ stroke-width="2"
96
+ stroke-linecap="round"
97
+ />
98
+ </svg>
99
+ </button>
100
+ <div>
101
+ <slot name="open"></slot>
102
+ </div>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ #dialog: HTMLDialogElement | null = null;
108
+
109
+ connectedCallback() {
110
+ super.connectedCallback();
111
+ }
112
+ }
@@ -0,0 +1,78 @@
1
+ :host {
2
+ background: none;
3
+ border: none;
4
+ padding: 0;
5
+ cursor: pointer;
6
+ touch-action: manipulation;
7
+ -webkit-tap-highlight-color: transparent;
8
+ display: inline-block;
9
+
10
+ }
11
+
12
+ :host > div{
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ gap: .5rem;
17
+ }
18
+
19
+
20
+ .sun-and-moon > :is(.moon, .sun, .sun-beams) {
21
+ transform-origin: center center;
22
+ }
23
+
24
+
25
+ svg {
26
+ margin: auto;
27
+ stroke-linecap: round;
28
+ }
29
+
30
+ svg + div {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ }
35
+
36
+ :host([mode="light"]) .sun-and-moon > .sun {
37
+ transform: scale(1.75);
38
+ }
39
+
40
+ :host([mode="light"]) .sun-and-moon > .sun-beams {
41
+ opacity: 0;
42
+ }
43
+
44
+ :host([mode="light"]) .sun-and-moon > .moon > circle {
45
+ transform: translate(-7px);
46
+ }
47
+
48
+ /* Animations */
49
+
50
+ :host([mode="dark"]) .sun-and-moon > .sun {
51
+ transition: transform 0.5s cubic-bezier(0.5, 1.25, 0.75, 1.25);
52
+ }
53
+
54
+ :host([mode="dark"]) .sun-and-moon > .sun-beams {
55
+ transition:
56
+ transform 0.5s cubic-bezier(0.5, 1.5, 0.75, 1.25),
57
+ opacity 0.5s cubic-bezier(0.25, 0, 0.3, 1);
58
+ }
59
+
60
+ :host([mode="darkt"]) .sun-and-moon .moon > circle {
61
+ transition: transform 0.25s cubic-bezier(0, 0, 0, 1);
62
+ }
63
+
64
+ :host([mode="light"]) .sun-and-moon > .sun {
65
+ transform: scale(1.75);
66
+ transition-timing-function: cubic-bezier(0.25, 0, 0.3, 1);
67
+ transition-duration: 0.25s;
68
+ }
69
+
70
+ :host([mode="light"]) .sun-and-moon > .sun-beams {
71
+ transform: rotate(-25deg);
72
+ transition-duration: 0.15s;
73
+ }
74
+
75
+ :host([mode="light"]) .sun-and-moon > .moon > circle {
76
+ transition-delay: 0.25s;
77
+ transition-duration: 0.5s;
78
+ }
@@ -0,0 +1,166 @@
1
+ import css from "./app-mode-switch.css";
2
+
3
+ export enum ThemeMode {
4
+ Light = "light",
5
+ Dark = "dark",
6
+ }
7
+
8
+ interface AppModeSwitchEventMap {
9
+ modeChange: CustomEvent<{ mode: ThemeMode }>;
10
+ click: MouseEvent;
11
+ }
12
+
13
+ @CustomElementConfig({
14
+ tagName: "app-mode-switch",
15
+ })
16
+ export class AppModeSwitchElement extends CustomElement {
17
+ static readonly style = css;
18
+
19
+ @Property({ type: "string", attribute: "mode", reflect: true })
20
+ mode: ThemeMode = ThemeMode.Light;
21
+
22
+ render(): VNode {
23
+ return (
24
+ <div>
25
+ <svg
26
+ class="sun-and-moon"
27
+ aria-hidden="true"
28
+ width="24"
29
+ height="24"
30
+ viewBox="0 0 24 24"
31
+ >
32
+ <mask class="moon" id="moon-mask">
33
+ <rect x="0" y="0" width="100%" height="100%" fill="white"></rect>
34
+ <circle cx="24" cy="10" r="6" fill="black"></circle>
35
+ </mask>
36
+ <circle
37
+ class="sun"
38
+ cx="12"
39
+ cy="12"
40
+ r="6"
41
+ mask="url(#moon-mask)"
42
+ fill="currentColor"
43
+ ></circle>
44
+ <g class="sun-beams" stroke="currentColor">
45
+ <line x1="12" y1="1" x2="12" y2="3"></line>
46
+ <line x1="12" y1="21" x2="12" y2="23"></line>
47
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
48
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
49
+ <line x1="1" y1="12" x2="3" y2="12"></line>
50
+ <line x1="21" y1="12" x2="23" y2="12"></line>
51
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
52
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
53
+ </g>
54
+ </svg>
55
+ <div>
56
+ <slot name={this.mode === ThemeMode.Dark ? ThemeMode.Light : ThemeMode.Dark }></slot>
57
+ </div>
58
+ </div>
59
+ );
60
+ }
61
+
62
+ addEventListener<T extends keyof AppModeSwitchEventMap>(
63
+ type: T,
64
+ listener: (this: AppModeSwitchElement, ev: AppModeSwitchEventMap[T]) => any,
65
+ options?: boolean | AddEventListenerOptions
66
+ ): void;
67
+
68
+ addEventListener(
69
+ type: string,
70
+ listener: (this: AppModeSwitchElement, ev: Event) => any,
71
+ options?: boolean | AddEventListenerOptions
72
+ ): void {
73
+ super.addEventListener(type, listener, options);
74
+ }
75
+
76
+ private onClick = () => {
77
+ this.mode =
78
+ this.mode === ThemeMode.Light ? ThemeMode.Dark : ThemeMode.Light;
79
+ };
80
+
81
+ updated(propertyName: string, oldValue: any, newValue: any): void {
82
+ if (propertyName === "mode") {
83
+ this.dispatchEvent(
84
+ new CustomEvent("modeChange", { detail: { mode: newValue } })
85
+ );
86
+ }
87
+ }
88
+
89
+ connectedCallback() {
90
+ super.connectedCallback();
91
+ if (!this.hasAttribute("tabindex")) {
92
+ this.setAttribute("tabindex", "0");
93
+ }
94
+ this.setAttribute("role", "button");
95
+ this.addEventListener("click", this.onClick);
96
+ }
97
+
98
+ disconnectedCallback() {
99
+ super.disconnectedCallback();
100
+ this.removeEventListener("click", this.onClick);
101
+ }
102
+ }
103
+
104
+ // init theme
105
+ let currentTheme: ThemeMode;
106
+
107
+ const darkSchemeMediaQueryList = window.matchMedia(
108
+ "(prefers-color-scheme: dark)"
109
+ );
110
+
111
+ function setTheme(theme: ThemeMode) {
112
+ if (currentTheme === theme) return;
113
+ currentTheme = theme;
114
+ document.documentElement.setAttribute("theme", theme);
115
+ if (systemTheme.startsWith(theme)) {
116
+ localStorage.removeItem("theme");
117
+ } else {
118
+ localStorage.setItem("theme", theme);
119
+ }
120
+ }
121
+
122
+ let systemTheme = darkSchemeMediaQueryList.matches
123
+ ? `${ThemeMode.Dark}-mode`
124
+ : `${ThemeMode.Light}-mode`;
125
+
126
+ darkSchemeMediaQueryList.addEventListener("change", ({ matches }) => {
127
+ const modeSwitch =
128
+ document.querySelector<AppModeSwitchElement>("app-mode-switch");
129
+ if (matches) {
130
+ systemTheme = ThemeMode.Dark;
131
+ setTheme(ThemeMode.Dark);
132
+ if (modeSwitch){
133
+ modeSwitch.mode = ThemeMode.Dark;
134
+ }
135
+ } else {
136
+ systemTheme = ThemeMode.Light;
137
+ setTheme(ThemeMode.Light);
138
+ if (modeSwitch){
139
+ modeSwitch.mode = ThemeMode.Light;
140
+ }
141
+ }
142
+ });
143
+
144
+ const savedTheme = localStorage.getItem("theme") as ThemeMode;
145
+ if (savedTheme) {
146
+ setTheme(savedTheme);
147
+ } else {
148
+ setTheme(systemTheme.split("-")[0] as ThemeMode);
149
+ }
150
+
151
+ document.addEventListener("DOMContentLoaded", () => {
152
+ const modeSwitch =
153
+ document.querySelector<AppModeSwitchElement>("app-mode-switch");
154
+ if (!modeSwitch) return;
155
+ modeSwitch.mode = savedTheme
156
+ ? savedTheme
157
+ : (systemTheme.split("-")[0] as ThemeMode);
158
+ modeSwitch.addEventListener("modeChange", (e: CustomEventInit) => {
159
+ if (e.detail.mode === ThemeMode.Dark) {
160
+ setTheme(ThemeMode.Dark);
161
+ } else if (e.detail.mode === ThemeMode.Light) {
162
+ setTheme(ThemeMode.Light);
163
+ }
164
+ });
165
+ });
166
+
@@ -1,12 +1,36 @@
1
- import { forEach } from "underscore";
2
-
3
1
  @CustomElementConfig({
4
2
  tagName: "app-playground",
5
3
  })
6
4
  export class AppPlaygroundElement extends CustomElement {
5
+
6
+ static style = `
7
+ :host{
8
+ display: block;
9
+ height: 100%;
10
+ }
11
+ .app-playground{
12
+ height: 100%;
13
+ }
14
+ .app-playground-split-panel{
15
+ height: 100%;
16
+ }
17
+ playground-preview{
18
+ height: 100%;
19
+ position: unset;
20
+ }
21
+ .app-playground-editor-container{
22
+ height: 100%;
23
+ display: flex;
24
+ flex-direction: column;
25
+ }
26
+ .app-playground-editor{
27
+ height: 100%;
28
+ flex: 1;
29
+ }
30
+ `;
31
+
7
32
  constructor() {
8
33
  super();
9
- this.#initDone = false;
10
34
  if (!this.id) {
11
35
  this.id = "app-playground-" + Math.floor(Math.random() * 1000);
12
36
  }
@@ -16,12 +40,6 @@ export class AppPlaygroundElement extends CustomElement {
16
40
 
17
41
  static readonly projectorMode = "replace";
18
42
 
19
- #initDom() {
20
- const div = document.createElement("div");
21
- this.appendChild(div);
22
- this.createProjector(div, this.render);
23
- }
24
-
25
43
  #_playgroundLoaded = false;
26
44
 
27
45
  get #playgroundLoaded() {
@@ -42,6 +60,9 @@ export class AppPlaygroundElement extends CustomElement {
42
60
  @Property({ type: "object" })
43
61
  files: any = null;
44
62
 
63
+ @Property({ type: "string", attribute: "cdn-base-url", reflect: true })
64
+ cdnBaseUrl: string = null;
65
+
45
66
  updated(propertyName: string, oldValue: any, newValue: any): void {
46
67
  if (propertyName === "projectSrc" && newValue) {
47
68
  this.#loadProject(newValue);
@@ -116,52 +137,54 @@ export class AppPlaygroundElement extends CustomElement {
116
137
 
117
138
  render = () => {
118
139
  return (
119
- <div afterCreate={this.#afterCreate}>
120
- {this.#playgroundLoaded && (
121
- <playground-project
122
- cdn-base-url="https://cdn.jsdelivr.net/npm"
123
- id={"project" + this.id}
124
- >
125
- {this.files?.map((file: any) => {
126
- return (
127
- <script
128
- key={file.name}
129
- type={this.#getType(file.name)}
130
- filename={file.name}
131
- innerHTML={file.content}
132
- ></script>
133
- );
134
- })}
135
- </playground-project>
136
- )}
137
- {this.#playgroundLoaded && (
138
- <div>
139
- <playground-tab-bar
140
- id={"tab-bar" + this.id}
140
+ <div afterCreate={this.#afterCreate} class="app-playground">
141
+
142
+ <app-split-panel class="app-playground-split-panel">
143
+ {this.#playgroundLoaded && (
144
+ [
145
+ <playground-project
146
+ class="app-playground-project"
147
+ cdn-base-url={
148
+ this.cdnBaseUrl ? this.cdnBaseUrl : "https://cdn.jsdelivr.net/npm"
149
+ }
150
+ id={"project" + this.id}
151
+ >
152
+ {this.files?.map((file: any) => {
153
+ return (
154
+ <script
155
+ key={file.name}
156
+ type={this.#getType(file.name)}
157
+ filename={file.name}
158
+ innerHTML={file.content}
159
+ ></script>
160
+ );
161
+ })}
162
+ </playground-project>,<playground-preview
163
+ slot="primary"
164
+ class="app-playground-preview"
141
165
  project={"project" + this.id}
142
- editor={"editor" + this.id}
143
- ></playground-tab-bar>
144
- </div>
145
- )}
146
- {this.#playgroundLoaded && (
147
- <playground-file-editor
148
- id={"editor" + this.id}
149
- project={"project" + this.id}
150
- ></playground-file-editor>
151
- )}
152
- {this.#playgroundLoaded && (
153
- <playground-preview
154
- project={"project" + this.id}
155
- ></playground-preview>
156
- )}
166
+ ></playground-preview>]
167
+ )}
168
+ <div slot="secondary" class="app-playground-editor-container">
169
+ {this.#playgroundLoaded && (
170
+ <playground-tab-bar
171
+ class="app-playground-tab-bar"
172
+ id={"tab-bar" + this.id}
173
+ project={"project" + this.id}
174
+ editor={"editor" + this.id}
175
+ ></playground-tab-bar>
176
+ )}
177
+ {this.#playgroundLoaded && (
178
+ <playground-file-editor
179
+ class="app-playground-editor"
180
+ id={"editor" + this.id}
181
+ project={"project" + this.id}
182
+ ></playground-file-editor>
183
+ )}
184
+ </div>
185
+
186
+ </app-split-panel>
157
187
  </div>
158
188
  );
159
189
  };
160
-
161
- connectedCallback(): void {
162
- if (!this.#initDone) {
163
- this.#initDom();
164
- this.#initDone = true;
165
- }
166
- }
167
- }
190
+ }
@@ -0,0 +1,33 @@
1
+ .split-panel {
2
+ display: flex;
3
+ flex-direction: row;
4
+ width: 100%;
5
+ height: 100%;
6
+ gap: 3px;
7
+ }
8
+
9
+ resize-bar {
10
+ background-color: var(--app-split-panel-resize-bar-color, #ccc);
11
+ z-index: 1;
12
+ flex: 0;
13
+ width: 4px;
14
+ }
15
+
16
+ #PrimaryPanel {
17
+ border-right: 1px solid var(--app-split-panel-border-color, #ccc);
18
+ /* padding-right: 1em; */
19
+ }
20
+ #SecondaryPanel {
21
+ border-left: 1px solid var(--app-split-panel-border-color, #ccc);
22
+ /* padding-left: 1em; */
23
+ overflow-x: auto;
24
+ flex: 1;
25
+ }
26
+ :host{
27
+ --resize-bar-touch-size: 6px;
28
+ --resize-bar-background-color: var(--app-split-panel-resize-bar-color, transparent);
29
+ }
30
+
31
+ .split-panel--horizontal #PrimaryPanel{
32
+ width: var(--primary-size);
33
+ }
@@ -0,0 +1,73 @@
1
+ import css from "./app-split-panel.css";
2
+
3
+ import "./resize-bar";
4
+
5
+ @CustomElementConfig({
6
+ tagName: "app-split-panel",
7
+ })
8
+ export class AppSplitPanelElement extends CustomElement {
9
+ static readonly style = css;
10
+
11
+
12
+ @Property({ type: "number", attribute: "min-width" })
13
+ minWidth = 300;
14
+
15
+ @Property({ type: "number", attribute: "primary-size" })
16
+ primarySize = 200;
17
+
18
+ @Property({ type: "number", attribute: "primary-min-size" })
19
+ primaryMinSize = 200;
20
+
21
+ updated(propertyName: string, oldValue: any, newValue: any): void {
22
+ if (propertyName === "primarySize") {
23
+ requestAnimationFrame(() => {
24
+ this.style.setProperty("--primary-size", `${newValue}px`);
25
+ });
26
+ } else if (propertyName === "primaryMinSize") {
27
+ requestAnimationFrame(() => {
28
+ this.shadowRoot.querySelector("resize-bar")?.setAttribute("min", `${newValue}`);
29
+ });
30
+ }
31
+ }
32
+
33
+ render(): VNode {
34
+ return (
35
+ <div classes={{"split-panel": true, "split-panel--horizontal": true}}>
36
+ <div id="PrimaryPanel">
37
+ <slot name="primary"></slot>
38
+ </div>
39
+ <resize-bar
40
+ property="width"
41
+ dimension="width"
42
+ target="PrimaryPanel" afterCreate={
43
+ (el: HTMLElement) => {
44
+ el.setAttribute("min", `${this.primaryMinSize}`);
45
+ }
46
+ }
47
+ ></resize-bar>
48
+ <div id="SecondaryPanel">
49
+ <slot name="secondary"></slot>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ // connectedCallback(): void {
56
+ // super.connectedCallback();
57
+
58
+ // const waitForPrimaryPanel = () => {
59
+ // if (this.shadowRoot?.querySelector("#PrimaryPanel")) {
60
+ // // PrimaryPanel exists, you can continue with any initialization here if needed
61
+ // const resizeObserver = new ResizeObserver((entries) => {
62
+
63
+ // });
64
+ // resizeObserver.observe(this.shadowRoot.querySelector("#PrimaryPanel"));
65
+ // } else {
66
+ // setTimeout(waitForPrimaryPanel, 50);
67
+ // }
68
+ // };
69
+ // waitForPrimaryPanel();
70
+
71
+ // }
72
+
73
+ }