@design.estate/wcctools 5.1.0
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/.smartconfig.json +65 -0
- package/changelog.md +414 -0
- package/dist_bundle/bundle.js +92419 -0
- package/dist_bundle/bundle.js.map +1 -0
- package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/elements/wcc-contextmenu.d.ts +41 -0
- package/dist_ts_web/elements/wcc-contextmenu.js +329 -0
- package/dist_ts_web/elements/wcc-dashboard.d.ts +119 -0
- package/dist_ts_web/elements/wcc-dashboard.js +700 -0
- package/dist_ts_web/elements/wcc-frame.d.ts +22 -0
- package/dist_ts_web/elements/wcc-frame.js +256 -0
- package/dist_ts_web/elements/wcc-properties.d.ts +43 -0
- package/dist_ts_web/elements/wcc-properties.js +1147 -0
- package/dist_ts_web/elements/wcc-record-button.d.ts +12 -0
- package/dist_ts_web/elements/wcc-record-button.js +165 -0
- package/dist_ts_web/elements/wcc-recording-panel.d.ts +44 -0
- package/dist_ts_web/elements/wcc-recording-panel.js +1103 -0
- package/dist_ts_web/elements/wcc-sidebar.d.ts +91 -0
- package/dist_ts_web/elements/wcc-sidebar.js +1497 -0
- package/dist_ts_web/elements/wcctools.helpers.d.ts +46 -0
- package/dist_ts_web/elements/wcctools.helpers.js +106 -0
- package/dist_ts_web/index.d.ts +18 -0
- package/dist_ts_web/index.js +30 -0
- package/dist_ts_web/pages/index.d.ts +1 -0
- package/dist_ts_web/pages/index.js +2 -0
- package/dist_ts_web/services/recorder.service.d.ts +55 -0
- package/dist_ts_web/services/recorder.service.js +353 -0
- package/dist_ts_web/wcctools.interfaces.d.ts +29 -0
- package/dist_ts_web/wcctools.interfaces.js +2 -0
- package/dist_ts_web/wcctools.plugins.d.ts +2 -0
- package/dist_ts_web/wcctools.plugins.js +3 -0
- package/license.md +19 -0
- package/package.json +64 -0
- package/readme.md +568 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/elements/wcc-contextmenu.ts +291 -0
- package/ts_web/elements/wcc-dashboard.ts +672 -0
- package/ts_web/elements/wcc-frame.ts +189 -0
- package/ts_web/elements/wcc-properties.ts +1089 -0
- package/ts_web/elements/wcc-record-button.ts +108 -0
- package/ts_web/elements/wcc-recording-panel.ts +1010 -0
- package/ts_web/elements/wcc-sidebar.ts +1470 -0
- package/ts_web/elements/wcctools.helpers.ts +153 -0
- package/ts_web/index.ts +38 -0
- package/ts_web/pages/index.ts +1 -0
- package/ts_web/readme.md +177 -0
- package/ts_web/services/recorder.service.ts +451 -0
- package/ts_web/tspublish.json +3 -0
- package/ts_web/types/dom-mediacapture-stub/index.d.ts +12 -0
- package/ts_web/types/dom-mediacapture-stub/package.json +6 -0
- package/ts_web/types/dom-webcodecs-stub/index.d.ts +2 -0
- package/ts_web/types/dom-webcodecs-stub/package.json +6 -0
- package/ts_web/wcctools.interfaces.ts +31 -0
- package/ts_web/wcctools.plugins.ts +5 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
import { DeesElement, property, html, customElement, type TemplateResult, state } from '@design.estate/dees-element';
|
|
2
|
+
import { WccDashboard } from './wcc-dashboard.js';
|
|
3
|
+
import { determinePropertyType, findElementInstance, type TPropertyType, type TTemplateFactory } from './wcctools.helpers.js';
|
|
4
|
+
import './wcc-record-button.js';
|
|
5
|
+
import './wcc-recording-panel.js';
|
|
6
|
+
|
|
7
|
+
export type { TPropertyType } from './wcctools.helpers.js';
|
|
8
|
+
|
|
9
|
+
export type TEnvironment = 'native' | 'desktop' | 'tablet' | 'phablet' | 'phone';
|
|
10
|
+
export type TTheme = 'bright' | 'dark';
|
|
11
|
+
|
|
12
|
+
let environment: TEnvironment = 'native';
|
|
13
|
+
|
|
14
|
+
export const setEnvironment = (envArg) => {
|
|
15
|
+
environment = envArg;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
@customElement('wcc-properties')
|
|
19
|
+
export class WccProperties extends DeesElement {
|
|
20
|
+
@property({
|
|
21
|
+
type: WccDashboard
|
|
22
|
+
})
|
|
23
|
+
accessor dashboardRef: WccDashboard;
|
|
24
|
+
|
|
25
|
+
@property()
|
|
26
|
+
accessor selectedItem: TTemplateFactory | DeesElement;
|
|
27
|
+
|
|
28
|
+
@property()
|
|
29
|
+
accessor selectedViewport: TEnvironment = 'native';
|
|
30
|
+
|
|
31
|
+
@property()
|
|
32
|
+
accessor selectedTheme: TTheme = 'dark';
|
|
33
|
+
|
|
34
|
+
@property()
|
|
35
|
+
accessor warning: string = null;
|
|
36
|
+
|
|
37
|
+
@property()
|
|
38
|
+
accessor isNative: boolean = false;
|
|
39
|
+
|
|
40
|
+
@property({ type: Number })
|
|
41
|
+
accessor sidebarWidth: number = 200;
|
|
42
|
+
|
|
43
|
+
@state()
|
|
44
|
+
accessor propertyContent: TemplateResult[] = [];
|
|
45
|
+
|
|
46
|
+
@state()
|
|
47
|
+
accessor editingProperties: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
value: any;
|
|
51
|
+
element: HTMLElement;
|
|
52
|
+
editorValue: string;
|
|
53
|
+
editorError: string;
|
|
54
|
+
}> = [];
|
|
55
|
+
|
|
56
|
+
// Recording coordination state
|
|
57
|
+
@state()
|
|
58
|
+
accessor showRecordingPanel: boolean = false;
|
|
59
|
+
|
|
60
|
+
@state()
|
|
61
|
+
accessor isRecording: boolean = false;
|
|
62
|
+
|
|
63
|
+
@state()
|
|
64
|
+
accessor recordingDuration: number = 0;
|
|
65
|
+
|
|
66
|
+
// Delayed hide for native mode transition
|
|
67
|
+
@state()
|
|
68
|
+
accessor isHidden: boolean = false;
|
|
69
|
+
|
|
70
|
+
public editorHeight: number = 300;
|
|
71
|
+
|
|
72
|
+
public render(): TemplateResult {
|
|
73
|
+
return html`
|
|
74
|
+
<style>
|
|
75
|
+
:host {
|
|
76
|
+
/* CSS Variables - Always dark theme */
|
|
77
|
+
--background: #0a0a0a;
|
|
78
|
+
--foreground: #e5e5e5;
|
|
79
|
+
--card: #0f0f0f;
|
|
80
|
+
--card-foreground: #f0f0f0;
|
|
81
|
+
--muted: #1a1a1a;
|
|
82
|
+
--muted-foreground: #666;
|
|
83
|
+
--accent: #222;
|
|
84
|
+
--accent-foreground: #fff;
|
|
85
|
+
--border: rgba(255, 255, 255, 0.06);
|
|
86
|
+
--input: #141414;
|
|
87
|
+
--primary: #3b82f6;
|
|
88
|
+
--primary-foreground: #fff;
|
|
89
|
+
--ring: #3b82f6;
|
|
90
|
+
--radius: 4px;
|
|
91
|
+
--radius-sm: 2px;
|
|
92
|
+
--radius-md: 4px;
|
|
93
|
+
--radius-lg: 6px;
|
|
94
|
+
|
|
95
|
+
/* Base styles */
|
|
96
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
97
|
+
box-sizing: border-box;
|
|
98
|
+
position: absolute;
|
|
99
|
+
left: ${this.sidebarWidth}px;
|
|
100
|
+
height: ${this.editingProperties.length > 0 ? 100 + this.editorHeight : 100}px;
|
|
101
|
+
bottom: 0px;
|
|
102
|
+
right: 0px;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
background: var(--background);
|
|
105
|
+
color: var(--foreground);
|
|
106
|
+
display: ${this.isHidden ? 'none' : 'block'};
|
|
107
|
+
}
|
|
108
|
+
.grid {
|
|
109
|
+
display: grid;
|
|
110
|
+
grid-template-columns: 1fr 150px 350px 100px;
|
|
111
|
+
height: 100%;
|
|
112
|
+
}
|
|
113
|
+
.properties {
|
|
114
|
+
background: transparent;
|
|
115
|
+
overflow-y: auto;
|
|
116
|
+
display: grid;
|
|
117
|
+
grid-template-columns: repeat(3, 1fr);
|
|
118
|
+
border-right: 1px solid var(--border);
|
|
119
|
+
align-content: start;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.material-symbols-outlined {
|
|
123
|
+
font-family: 'Material Symbols Outlined';
|
|
124
|
+
font-weight: normal;
|
|
125
|
+
font-style: normal;
|
|
126
|
+
font-size: 24px; /* Preferred icon size */
|
|
127
|
+
display: inline-block;
|
|
128
|
+
line-height: 1;
|
|
129
|
+
text-transform: none;
|
|
130
|
+
letter-spacing: normal;
|
|
131
|
+
word-wrap: normal;
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
direction: ltr;
|
|
134
|
+
font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 48;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.properties .property {
|
|
138
|
+
padding: 0.4rem;
|
|
139
|
+
background: transparent;
|
|
140
|
+
border-right: 1px solid var(--border);
|
|
141
|
+
border-bottom: 1px solid var(--border);
|
|
142
|
+
transition: all 0.15s ease;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.properties .property:hover {
|
|
146
|
+
background: rgba(255, 255, 255, 0.02);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.properties .property-label {
|
|
150
|
+
font-size: 0.65rem;
|
|
151
|
+
font-weight: 400;
|
|
152
|
+
color: #888;
|
|
153
|
+
margin-bottom: 0.2rem;
|
|
154
|
+
text-transform: capitalize;
|
|
155
|
+
white-space: nowrap;
|
|
156
|
+
overflow: hidden;
|
|
157
|
+
text-overflow: ellipsis;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.properties input[type="text"],
|
|
161
|
+
.properties input[type="number"],
|
|
162
|
+
.properties select {
|
|
163
|
+
display: block;
|
|
164
|
+
width: 100%;
|
|
165
|
+
padding: 0.25rem 0.4rem;
|
|
166
|
+
background: var(--input);
|
|
167
|
+
border: 1px solid transparent;
|
|
168
|
+
color: var(--foreground);
|
|
169
|
+
border-radius: var(--radius-sm);
|
|
170
|
+
font-size: 0.7rem;
|
|
171
|
+
transition: all 0.15s ease;
|
|
172
|
+
outline: none;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.properties input[type="text"]:focus,
|
|
176
|
+
.properties input[type="number"]:focus,
|
|
177
|
+
.properties select:focus {
|
|
178
|
+
border-color: var(--primary);
|
|
179
|
+
background: rgba(59, 130, 246, 0.1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.properties input[type="checkbox"] {
|
|
183
|
+
width: 1rem;
|
|
184
|
+
height: 1rem;
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
accent-color: var(--primary);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.properties .editor-button {
|
|
190
|
+
padding: 0.25rem 0.5rem;
|
|
191
|
+
background: var(--input);
|
|
192
|
+
border: 1px solid transparent;
|
|
193
|
+
border-radius: var(--radius-sm);
|
|
194
|
+
color: var(--foreground);
|
|
195
|
+
font-size: 0.7rem;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: all 0.15s ease;
|
|
198
|
+
text-align: center;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.properties .editor-button:hover {
|
|
202
|
+
border-color: var(--primary);
|
|
203
|
+
background: rgba(59, 130, 246, 0.1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.viewportSelector,
|
|
207
|
+
.themeSelector,
|
|
208
|
+
.shareSelector {
|
|
209
|
+
user-select: none;
|
|
210
|
+
background: transparent;
|
|
211
|
+
display: flex;
|
|
212
|
+
flex-direction: column;
|
|
213
|
+
border-right: 1px solid var(--border);
|
|
214
|
+
}
|
|
215
|
+
.selectorButtons2 {
|
|
216
|
+
display: grid;
|
|
217
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
218
|
+
flex: 1;
|
|
219
|
+
}
|
|
220
|
+
.selectorButtons4 {
|
|
221
|
+
display: grid;
|
|
222
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
223
|
+
flex: 1;
|
|
224
|
+
}
|
|
225
|
+
.selectorButtons5 {
|
|
226
|
+
display: grid;
|
|
227
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
228
|
+
flex: 1;
|
|
229
|
+
}
|
|
230
|
+
.selectorButtons1 {
|
|
231
|
+
display: grid;
|
|
232
|
+
grid-template-columns: minmax(0, 1fr);
|
|
233
|
+
flex: 1;
|
|
234
|
+
}
|
|
235
|
+
.button {
|
|
236
|
+
appearance: none;
|
|
237
|
+
margin: 0;
|
|
238
|
+
font-family: inherit;
|
|
239
|
+
line-height: 1.2;
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
align-items: center;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
padding: 0.5rem 0.25rem;
|
|
245
|
+
text-align: center;
|
|
246
|
+
background: transparent;
|
|
247
|
+
border: 1px solid var(--border);
|
|
248
|
+
transition: all 0.15s ease;
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
font-size: 0.65rem;
|
|
251
|
+
gap: 0.2rem;
|
|
252
|
+
color: #999;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.button:hover {
|
|
256
|
+
background: rgba(59, 130, 246, 0.05);
|
|
257
|
+
color: #bbb;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.button.selected {
|
|
261
|
+
background: rgba(59, 130, 246, 0.15);
|
|
262
|
+
color: var(--primary);
|
|
263
|
+
border-color: rgba(59, 130, 246, 0.3);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.button.selected:hover {
|
|
267
|
+
background: rgba(59, 130, 246, 0.2);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.button:focus-visible,
|
|
271
|
+
.properties .editor-button:focus-visible,
|
|
272
|
+
.editor-close-all:focus-visible {
|
|
273
|
+
outline: 2px solid var(--ring);
|
|
274
|
+
outline-offset: -2px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.properties input:focus-visible,
|
|
278
|
+
.properties select:focus-visible {
|
|
279
|
+
outline: 2px solid var(--ring);
|
|
280
|
+
outline-offset: 1px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.button .material-symbols-outlined {
|
|
284
|
+
font-size: 18px;
|
|
285
|
+
font-variation-settings: 'FILL' 0, 'wght' 300;
|
|
286
|
+
min-width: 18px;
|
|
287
|
+
min-height: 18px;
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.button.selected .material-symbols-outlined {
|
|
294
|
+
font-variation-settings: 'FILL' 1, 'wght' 400;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.panelheading {
|
|
298
|
+
padding: 0.3rem 0.5rem;
|
|
299
|
+
font-weight: 500;
|
|
300
|
+
font-size: 0.65rem;
|
|
301
|
+
background: rgba(59, 130, 246, 0.03);
|
|
302
|
+
border-bottom: 1px solid var(--border);
|
|
303
|
+
color: #888;
|
|
304
|
+
text-transform: uppercase;
|
|
305
|
+
letter-spacing: 0.08em;
|
|
306
|
+
}
|
|
307
|
+
.docs {
|
|
308
|
+
display: flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
justify-content: center;
|
|
311
|
+
text-transform: uppercase;
|
|
312
|
+
background: transparent;
|
|
313
|
+
cursor: pointer;
|
|
314
|
+
font-size: 0.65rem;
|
|
315
|
+
font-weight: 500;
|
|
316
|
+
letter-spacing: 0.08em;
|
|
317
|
+
transition: all 0.15s ease;
|
|
318
|
+
color: #666;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.docs:hover {
|
|
322
|
+
background: rgba(59, 130, 246, 0.05);
|
|
323
|
+
color: #999;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.warning {
|
|
327
|
+
position: absolute;
|
|
328
|
+
background: rgba(20, 20, 20, 0.8);
|
|
329
|
+
color: #888;
|
|
330
|
+
top: 0.5rem;
|
|
331
|
+
bottom: 0.5rem;
|
|
332
|
+
left: 0.5rem;
|
|
333
|
+
right: calc(600px + 0.5rem);
|
|
334
|
+
display: flex;
|
|
335
|
+
align-items: center;
|
|
336
|
+
justify-content: center;
|
|
337
|
+
padding: 1.5rem;
|
|
338
|
+
font-size: 0.85rem;
|
|
339
|
+
border-radius: var(--radius-md);
|
|
340
|
+
border: 1px solid var(--border);
|
|
341
|
+
backdrop-filter: blur(8px);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.advanced-editor-container {
|
|
345
|
+
position: absolute;
|
|
346
|
+
left: 0;
|
|
347
|
+
right: 0;
|
|
348
|
+
top: 0;
|
|
349
|
+
height: ${this.editorHeight}px;
|
|
350
|
+
background: #050505;
|
|
351
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
352
|
+
display: flex;
|
|
353
|
+
flex-direction: column;
|
|
354
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
355
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.editor-header-bar {
|
|
359
|
+
padding: 0.5rem 0.75rem;
|
|
360
|
+
background: transparent;
|
|
361
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
362
|
+
display: flex;
|
|
363
|
+
justify-content: space-between;
|
|
364
|
+
align-items: center;
|
|
365
|
+
height: 36px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.editor-header-title {
|
|
369
|
+
font-size: 0.7rem;
|
|
370
|
+
font-weight: 500;
|
|
371
|
+
color: #666;
|
|
372
|
+
text-transform: uppercase;
|
|
373
|
+
letter-spacing: 0.08em;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.editor-close-all {
|
|
377
|
+
padding: 0.25rem 0.5rem;
|
|
378
|
+
background: transparent;
|
|
379
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
380
|
+
border-radius: var(--radius-sm);
|
|
381
|
+
color: #999;
|
|
382
|
+
font-size: 0.65rem;
|
|
383
|
+
font-weight: 500;
|
|
384
|
+
cursor: pointer;
|
|
385
|
+
transition: all 0.15s ease;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.editor-close-all:hover {
|
|
389
|
+
background: rgba(255, 255, 255, 0.05);
|
|
390
|
+
border-color: rgba(255, 255, 255, 0.12);
|
|
391
|
+
color: #f87171;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.editors-container {
|
|
395
|
+
flex: 1;
|
|
396
|
+
display: flex;
|
|
397
|
+
overflow-x: auto;
|
|
398
|
+
overflow-y: hidden;
|
|
399
|
+
gap: 0;
|
|
400
|
+
background: rgba(255, 255, 255, 0.02);
|
|
401
|
+
padding: 0.75rem;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.editors-container::-webkit-scrollbar {
|
|
405
|
+
height: 8px;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.editors-container::-webkit-scrollbar-track {
|
|
409
|
+
background: rgba(255, 255, 255, 0.02);
|
|
410
|
+
border-radius: 4px;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.editors-container::-webkit-scrollbar-thumb {
|
|
414
|
+
background: rgba(255, 255, 255, 0.08);
|
|
415
|
+
border-radius: 4px;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.editors-container::-webkit-scrollbar-thumb:hover {
|
|
419
|
+
background: rgba(255, 255, 255, 0.12);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.editor-instance {
|
|
423
|
+
min-width: 320px;
|
|
424
|
+
flex: 1;
|
|
425
|
+
max-width: 480px;
|
|
426
|
+
background: rgba(10, 10, 10, 0.6);
|
|
427
|
+
display: flex;
|
|
428
|
+
flex-direction: column;
|
|
429
|
+
border-radius: var(--radius);
|
|
430
|
+
overflow: hidden;
|
|
431
|
+
margin-right: 0.75rem;
|
|
432
|
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
433
|
+
transition: all 0.2s ease;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.editor-instance:hover {
|
|
437
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.editor-instance:last-child {
|
|
441
|
+
margin-right: 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.editor-header {
|
|
445
|
+
padding: 0.5rem 0.75rem;
|
|
446
|
+
background: rgba(255, 255, 255, 0.02);
|
|
447
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
448
|
+
display: flex;
|
|
449
|
+
justify-content: space-between;
|
|
450
|
+
align-items: center;
|
|
451
|
+
height: 36px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.editor-title {
|
|
455
|
+
font-size: 0.75rem;
|
|
456
|
+
font-weight: 500;
|
|
457
|
+
color: #999;
|
|
458
|
+
overflow: hidden;
|
|
459
|
+
text-overflow: ellipsis;
|
|
460
|
+
white-space: nowrap;
|
|
461
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.editor-actions {
|
|
465
|
+
display: flex;
|
|
466
|
+
gap: 0.25rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.editor-button {
|
|
470
|
+
width: 24px;
|
|
471
|
+
height: 24px;
|
|
472
|
+
padding: 0;
|
|
473
|
+
background: transparent;
|
|
474
|
+
border: none;
|
|
475
|
+
color: #666;
|
|
476
|
+
font-size: 1rem;
|
|
477
|
+
cursor: pointer;
|
|
478
|
+
transition: all 0.15s ease;
|
|
479
|
+
border-radius: var(--radius-sm);
|
|
480
|
+
display: flex;
|
|
481
|
+
align-items: center;
|
|
482
|
+
justify-content: center;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.editor-button:hover {
|
|
486
|
+
background: rgba(255, 255, 255, 0.05);
|
|
487
|
+
color: #999;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.editor-button.primary {
|
|
491
|
+
color: #4ade80;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.editor-button.primary:hover {
|
|
495
|
+
background: rgba(74, 222, 128, 0.1);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.editor-content {
|
|
499
|
+
flex: 1;
|
|
500
|
+
display: flex;
|
|
501
|
+
flex-direction: column;
|
|
502
|
+
overflow: hidden;
|
|
503
|
+
min-height: 0;
|
|
504
|
+
position: relative;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.editor-textarea {
|
|
508
|
+
width: 100%;
|
|
509
|
+
height: 100%;
|
|
510
|
+
background: transparent;
|
|
511
|
+
border: none;
|
|
512
|
+
color: #d0d0d0;
|
|
513
|
+
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
514
|
+
font-size: 0.8125rem;
|
|
515
|
+
line-height: 1.6;
|
|
516
|
+
padding: 0.75rem;
|
|
517
|
+
resize: none;
|
|
518
|
+
outline: none;
|
|
519
|
+
transition: all 0.15s ease;
|
|
520
|
+
overflow: auto;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.editor-textarea:focus {
|
|
524
|
+
background: rgba(255, 255, 255, 0.01);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.editor-textarea::selection {
|
|
528
|
+
background: rgba(59, 130, 246, 0.3);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.editor-error {
|
|
532
|
+
position: absolute;
|
|
533
|
+
bottom: 0;
|
|
534
|
+
left: 0;
|
|
535
|
+
right: 0;
|
|
536
|
+
padding: 0.5rem 0.75rem;
|
|
537
|
+
background: rgba(239, 68, 68, 0.9);
|
|
538
|
+
backdrop-filter: blur(4px);
|
|
539
|
+
color: #fff;
|
|
540
|
+
font-size: 0.7rem;
|
|
541
|
+
font-weight: 500;
|
|
542
|
+
display: flex;
|
|
543
|
+
align-items: center;
|
|
544
|
+
gap: 0.375rem;
|
|
545
|
+
border-top: 1px solid rgba(239, 68, 68, 0.5);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.editor-error::before {
|
|
549
|
+
content: '!';
|
|
550
|
+
display: inline-flex;
|
|
551
|
+
width: 16px;
|
|
552
|
+
height: 16px;
|
|
553
|
+
align-items: center;
|
|
554
|
+
justify-content: center;
|
|
555
|
+
background: rgba(255, 255, 255, 0.2);
|
|
556
|
+
border-radius: 50%;
|
|
557
|
+
font-size: 0.65rem;
|
|
558
|
+
font-weight: bold;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.main-content {
|
|
562
|
+
position: absolute;
|
|
563
|
+
left: 0;
|
|
564
|
+
right: 0;
|
|
565
|
+
bottom: 0;
|
|
566
|
+
height: 100px;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
@container wccDashboard (max-width: 600px) {
|
|
570
|
+
:host {
|
|
571
|
+
left: 0;
|
|
572
|
+
height: ${this.editingProperties.length > 0 ? 160 + this.editorHeight : 160}px;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.main-content {
|
|
576
|
+
height: 160px;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.grid {
|
|
580
|
+
grid-template-columns: minmax(90px, 2fr) minmax(0, 5fr) minmax(52px, 1fr);
|
|
581
|
+
grid-template-rows: 60px 100px;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.properties {
|
|
585
|
+
grid-column: 1 / -1;
|
|
586
|
+
grid-row: 1;
|
|
587
|
+
display: flex;
|
|
588
|
+
overflow-x: auto;
|
|
589
|
+
overflow-y: hidden;
|
|
590
|
+
border-right: 0;
|
|
591
|
+
border-bottom: 1px solid var(--border);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.properties .property {
|
|
595
|
+
flex: 0 0 120px;
|
|
596
|
+
min-width: 0;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.themeSelector {
|
|
600
|
+
grid-column: 1;
|
|
601
|
+
grid-row: 2;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.viewportSelector {
|
|
605
|
+
grid-column: 2;
|
|
606
|
+
grid-row: 2;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.shareSelector {
|
|
610
|
+
grid-column: 3;
|
|
611
|
+
grid-row: 2;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.themeSelector,
|
|
615
|
+
.viewportSelector,
|
|
616
|
+
.shareSelector {
|
|
617
|
+
min-width: 0;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.button {
|
|
621
|
+
min-width: 0;
|
|
622
|
+
overflow: hidden;
|
|
623
|
+
padding: 0.25rem 0.1rem;
|
|
624
|
+
font-size: 0.55rem;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.panelheading {
|
|
628
|
+
overflow: hidden;
|
|
629
|
+
padding: 0.3rem 0.25rem;
|
|
630
|
+
text-overflow: ellipsis;
|
|
631
|
+
white-space: nowrap;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.warning {
|
|
635
|
+
top: 0.25rem;
|
|
636
|
+
right: 0.25rem;
|
|
637
|
+
bottom: calc(100px + 0.25rem);
|
|
638
|
+
left: 0.25rem;
|
|
639
|
+
padding: 0.5rem;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
</style>
|
|
643
|
+
${this.editingProperties.length > 0 ? html`
|
|
644
|
+
<div class="advanced-editor-container">
|
|
645
|
+
<div class="editor-header-bar">
|
|
646
|
+
<div class="editor-header-title">Property Editors</div>
|
|
647
|
+
<button class="editor-close-all" @click=${this.closeAllEditors}>
|
|
648
|
+
Close All
|
|
649
|
+
</button>
|
|
650
|
+
</div>
|
|
651
|
+
<div class="editors-container">
|
|
652
|
+
${this.editingProperties.length === 0 ? html`
|
|
653
|
+
<div style="
|
|
654
|
+
flex: 1;
|
|
655
|
+
display: flex;
|
|
656
|
+
align-items: center;
|
|
657
|
+
justify-content: center;
|
|
658
|
+
color: #666;
|
|
659
|
+
font-size: 0.875rem;
|
|
660
|
+
text-align: center;
|
|
661
|
+
padding: 2rem;
|
|
662
|
+
">
|
|
663
|
+
<div>
|
|
664
|
+
<div style="margin-bottom: 0.5rem; font-size: 1.5rem; opacity: 0.5;">{ }</div>
|
|
665
|
+
<div>No properties being edited</div>
|
|
666
|
+
<div style="font-size: 0.75rem; margin-top: 0.25rem; opacity: 0.7;">Click "Edit Object/Array" buttons to start editing</div>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
` : null}
|
|
670
|
+
${this.editingProperties.map(prop => html`
|
|
671
|
+
<div class="editor-instance">
|
|
672
|
+
<div class="editor-header">
|
|
673
|
+
<div class="editor-title">${prop.name}</div>
|
|
674
|
+
<div class="editor-actions">
|
|
675
|
+
<button class="editor-button" @click=${() => this.handleEditorCancel(prop.id)}>✕</button>
|
|
676
|
+
<button class="editor-button primary" @click=${() => this.handleEditorSave(prop.id)}>✓</button>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
<div class="editor-content">
|
|
680
|
+
<textarea
|
|
681
|
+
class="editor-textarea"
|
|
682
|
+
.value=${prop.editorValue}
|
|
683
|
+
@input=${(e: InputEvent) => {
|
|
684
|
+
const editor = this.editingProperties.find(p => p.id === prop.id);
|
|
685
|
+
if (editor) {
|
|
686
|
+
editor.editorValue = (e.target as HTMLTextAreaElement).value;
|
|
687
|
+
editor.editorError = '';
|
|
688
|
+
this.requestUpdate();
|
|
689
|
+
}
|
|
690
|
+
}}
|
|
691
|
+
@keydown=${(e: KeyboardEvent) => {
|
|
692
|
+
if (e.key === 'Tab') {
|
|
693
|
+
e.preventDefault();
|
|
694
|
+
const target = e.target as HTMLTextAreaElement;
|
|
695
|
+
const start = target.selectionStart;
|
|
696
|
+
const end = target.selectionEnd;
|
|
697
|
+
const value = target.value;
|
|
698
|
+
target.value = value.substring(0, start) + ' ' + value.substring(end);
|
|
699
|
+
target.selectionStart = target.selectionEnd = start + 2;
|
|
700
|
+
}
|
|
701
|
+
}}
|
|
702
|
+
></textarea>
|
|
703
|
+
${prop.editorError ? html`
|
|
704
|
+
<div class="editor-error">${prop.editorError}</div>
|
|
705
|
+
` : null}
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
`)}
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
` : null}
|
|
712
|
+
<div class="main-content">
|
|
713
|
+
<div class="grid">
|
|
714
|
+
<div class="properties" role="group" aria-label="Properties">
|
|
715
|
+
${this.propertyContent}
|
|
716
|
+
</div>
|
|
717
|
+
<div class="themeSelector" role="group" aria-labelledby="wcc-theme-heading">
|
|
718
|
+
<div class="panelheading" id="wcc-theme-heading">Theme</div>
|
|
719
|
+
<div class="selectorButtons2">
|
|
720
|
+
${([
|
|
721
|
+
['dark', 'Dark', 'brightness_3'],
|
|
722
|
+
['bright', 'Bright', 'flare'],
|
|
723
|
+
] as Array<[TTheme, string, string]>).map(([theme, label, icon]) => html`
|
|
724
|
+
<button
|
|
725
|
+
type="button"
|
|
726
|
+
class="button ${this.selectedTheme === theme ? 'selected' : ''}"
|
|
727
|
+
aria-pressed=${this.selectedTheme === theme ? 'true' : 'false'}
|
|
728
|
+
@click=${() => this.selectTheme(theme)}
|
|
729
|
+
>
|
|
730
|
+
${label}<i class="material-symbols-outlined" aria-hidden="true">${icon}</i>
|
|
731
|
+
</button>
|
|
732
|
+
`)}
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
<div class="viewportSelector" role="group" aria-labelledby="wcc-viewport-heading">
|
|
736
|
+
<div class="panelheading" id="wcc-viewport-heading">Viewport</div>
|
|
737
|
+
<div class="selectorButtons5">
|
|
738
|
+
${([
|
|
739
|
+
['phone', 'Phone', 'smartphone'],
|
|
740
|
+
['phablet', 'Phablet', 'smartphone'],
|
|
741
|
+
['tablet', 'Tablet', 'tablet'],
|
|
742
|
+
['desktop', 'Desktop', 'desktop_windows'],
|
|
743
|
+
['native', 'Native', 'fullscreen'],
|
|
744
|
+
] as Array<[TEnvironment, string, string]>).map(([viewport, label, icon]) => html`
|
|
745
|
+
<button
|
|
746
|
+
type="button"
|
|
747
|
+
class="button ${this.selectedViewport === viewport ? 'selected' : ''}"
|
|
748
|
+
aria-pressed=${this.selectedViewport === viewport ? 'true' : 'false'}
|
|
749
|
+
@click=${() => this.selectViewport(viewport)}
|
|
750
|
+
>
|
|
751
|
+
${label}<i class="material-symbols-outlined" aria-hidden="true">${icon}</i>
|
|
752
|
+
</button>
|
|
753
|
+
`)}
|
|
754
|
+
</div>
|
|
755
|
+
</div>
|
|
756
|
+
<div class="shareSelector" role="group" aria-labelledby="wcc-share-heading">
|
|
757
|
+
<div class="panelheading" id="wcc-share-heading">Share</div>
|
|
758
|
+
<div class="selectorButtons1">
|
|
759
|
+
<button
|
|
760
|
+
type="button"
|
|
761
|
+
class="button ${this.isRecording ? 'selected' : ''}"
|
|
762
|
+
aria-expanded=${this.showRecordingPanel ? 'true' : 'false'}
|
|
763
|
+
@click=${() => this.handleRecordButtonClick()}
|
|
764
|
+
>
|
|
765
|
+
${this.isRecording ? 'Stop' : 'Record'}<i class="material-symbols-outlined" aria-hidden="true">${this.isRecording ? 'stop_circle' : 'videocam'}</i>
|
|
766
|
+
</button>
|
|
767
|
+
</div>
|
|
768
|
+
</div>
|
|
769
|
+
</div>
|
|
770
|
+
${this.warning ? html`<div class="warning">${this.warning}</div>` : null}
|
|
771
|
+
</div>
|
|
772
|
+
|
|
773
|
+
<!-- Recording Panel (options + preview) -->
|
|
774
|
+
${this.showRecordingPanel ? html`
|
|
775
|
+
<wcc-recording-panel
|
|
776
|
+
.dashboardRef=${this.dashboardRef}
|
|
777
|
+
@recording-start=${() => { this.isRecording = true; }}
|
|
778
|
+
@recording-stop=${() => { this.isRecording = false; }}
|
|
779
|
+
@duration-update=${(e: CustomEvent) => { this.recordingDuration = e.detail.duration; }}
|
|
780
|
+
@close=${() => { this.showRecordingPanel = false; this.isRecording = false; this.recordingDuration = 0; }}
|
|
781
|
+
></wcc-recording-panel>
|
|
782
|
+
` : null}
|
|
783
|
+
`;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
public async createProperties() {
|
|
787
|
+
// Clear any previous warnings
|
|
788
|
+
this.warning = null;
|
|
789
|
+
if (!this.selectedItem) {
|
|
790
|
+
this.propertyContent = [];
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const getEnumValues = (propertyArg): any[] => {
|
|
794
|
+
const enumValues: any[] = [];
|
|
795
|
+
Object.keys(propertyArg.type).forEach((valueArg: string) => {
|
|
796
|
+
enumValues.push(valueArg);
|
|
797
|
+
});
|
|
798
|
+
return enumValues;
|
|
799
|
+
};
|
|
800
|
+
if (this.selectedItem && (this.selectedItem as any).demo) {
|
|
801
|
+
const anonItem: any = this.selectedItem;
|
|
802
|
+
if (!anonItem) {
|
|
803
|
+
this.warning = 'no element selected';
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const wccFrame = await this.dashboardRef.wccFrame;
|
|
807
|
+
|
|
808
|
+
// Wait for render to complete and any demo wrappers to run
|
|
809
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
810
|
+
|
|
811
|
+
// Try to find the element with recursive search
|
|
812
|
+
const viewport = await wccFrame.getViewportElement();
|
|
813
|
+
let firstFoundInstantiatedElement: HTMLElement = findElementInstance(
|
|
814
|
+
viewport,
|
|
815
|
+
this.selectedItem as any
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
// Retry logic if element not found
|
|
819
|
+
let retries = 0;
|
|
820
|
+
while (!firstFoundInstantiatedElement && retries < 5) {
|
|
821
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
822
|
+
try {
|
|
823
|
+
firstFoundInstantiatedElement = findElementInstance(
|
|
824
|
+
viewport,
|
|
825
|
+
this.selectedItem as any
|
|
826
|
+
);
|
|
827
|
+
} catch (error) {
|
|
828
|
+
console.error('Error during element search retry:', error);
|
|
829
|
+
}
|
|
830
|
+
retries++;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
if (!firstFoundInstantiatedElement) {
|
|
834
|
+
this.warning = `no first instantiated element found for >>${anonItem.name}<< after ${retries} retries`;
|
|
835
|
+
this.propertyContent = [];
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const classProperties: Map<string, any> = anonItem.elementProperties;
|
|
839
|
+
if (!classProperties) {
|
|
840
|
+
this.warning = `selected element >>${anonItem.name}<< does not expose element properties`;
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
this.warning = null;
|
|
844
|
+
const propertyArray: TemplateResult[] = [];
|
|
845
|
+
for (const [key, property] of classProperties) {
|
|
846
|
+
if (typeof key !== 'string' || key.startsWith('#') || key === 'goBright' || key === 'domtools') {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const propertyTypeString = determinePropertyType(property);
|
|
850
|
+
if (!propertyTypeString) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
propertyArray.push(
|
|
854
|
+
html`
|
|
855
|
+
<div class="property">
|
|
856
|
+
<div class="property-label">${key} (${propertyTypeString})</div>
|
|
857
|
+
${(() => {
|
|
858
|
+
switch (propertyTypeString) {
|
|
859
|
+
case 'Boolean':
|
|
860
|
+
return html`<input
|
|
861
|
+
aria-label=${key}
|
|
862
|
+
type="checkbox"
|
|
863
|
+
?checked=${firstFoundInstantiatedElement[key]}
|
|
864
|
+
@input="${(eventArg: any) => {
|
|
865
|
+
firstFoundInstantiatedElement[key] = eventArg.target.checked;
|
|
866
|
+
}}"
|
|
867
|
+
/>`;
|
|
868
|
+
case 'String':
|
|
869
|
+
return html`<input
|
|
870
|
+
aria-label=${key}
|
|
871
|
+
type="text"
|
|
872
|
+
.value=${firstFoundInstantiatedElement[key] || ''}
|
|
873
|
+
@input="${(eventArg: any) => {
|
|
874
|
+
firstFoundInstantiatedElement[key] = eventArg.target.value;
|
|
875
|
+
}}"
|
|
876
|
+
/>`;
|
|
877
|
+
case 'Number':
|
|
878
|
+
return html`<input
|
|
879
|
+
aria-label=${key}
|
|
880
|
+
type="number"
|
|
881
|
+
.value=${firstFoundInstantiatedElement[key] ?? ''}
|
|
882
|
+
@input="${(eventArg: any) => {
|
|
883
|
+
firstFoundInstantiatedElement[key] = parseFloat(eventArg.target.value) || 0;
|
|
884
|
+
}}"
|
|
885
|
+
/>`;
|
|
886
|
+
case 'Enum':
|
|
887
|
+
const enumValues: any[] = getEnumValues(property);
|
|
888
|
+
return html`<select
|
|
889
|
+
aria-label=${key}
|
|
890
|
+
.value=${firstFoundInstantiatedElement[key] || ''}
|
|
891
|
+
@change="${(eventArg: any) => {
|
|
892
|
+
firstFoundInstantiatedElement[key] = eventArg.target.value;
|
|
893
|
+
}}"
|
|
894
|
+
>
|
|
895
|
+
${enumValues.map((valueArg) => {
|
|
896
|
+
return html`
|
|
897
|
+
<option
|
|
898
|
+
value="${valueArg}"
|
|
899
|
+
>
|
|
900
|
+
${valueArg}
|
|
901
|
+
</option>
|
|
902
|
+
`;
|
|
903
|
+
})}
|
|
904
|
+
</select>`;
|
|
905
|
+
case 'Object':
|
|
906
|
+
case 'Array':
|
|
907
|
+
return html`<button
|
|
908
|
+
type="button"
|
|
909
|
+
aria-label=${`Edit ${key}`}
|
|
910
|
+
class="editor-button"
|
|
911
|
+
style="width: 100%; margin-top: 0.25rem;"
|
|
912
|
+
@click="${() => this.openAdvancedEditor(key, firstFoundInstantiatedElement[key], firstFoundInstantiatedElement)}"
|
|
913
|
+
>
|
|
914
|
+
Edit ${propertyTypeString}
|
|
915
|
+
</button>`;
|
|
916
|
+
default:
|
|
917
|
+
return html`<div style="color: #666; font-size: 0.7rem;">Unsupported type</div>`;
|
|
918
|
+
}
|
|
919
|
+
})()}
|
|
920
|
+
</div>
|
|
921
|
+
`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
this.propertyContent = propertyArray;
|
|
925
|
+
} else {
|
|
926
|
+
this.propertyContent = [];
|
|
927
|
+
this.warning = `You selected a page.`;
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
public selectTheme(themeArg: TTheme) {
|
|
933
|
+
this.selectedTheme = themeArg;
|
|
934
|
+
this.dispatchEvent(
|
|
935
|
+
new CustomEvent('selectedTheme', {
|
|
936
|
+
detail: themeArg,
|
|
937
|
+
})
|
|
938
|
+
);
|
|
939
|
+
this.dashboardRef.buildUrl();
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
protected updated(changedProperties: Map<string, unknown>) {
|
|
943
|
+
super.updated(changedProperties);
|
|
944
|
+
|
|
945
|
+
// Handle delayed hide for native mode transition
|
|
946
|
+
if (changedProperties.has('isNative')) {
|
|
947
|
+
if (this.isNative) {
|
|
948
|
+
// Delay hiding until frame animation completes
|
|
949
|
+
setTimeout(() => {
|
|
950
|
+
this.isHidden = true;
|
|
951
|
+
}, 300);
|
|
952
|
+
} else {
|
|
953
|
+
// Show immediately when exiting native mode
|
|
954
|
+
this.isHidden = false;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Only recreate properties when selectedItem changes
|
|
959
|
+
if (changedProperties.has('selectedItem')) {
|
|
960
|
+
this.createProperties().catch(error => {
|
|
961
|
+
console.error('Error creating properties:', error);
|
|
962
|
+
this.propertyContent = [];
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
public selectViewport(viewport: TEnvironment) {
|
|
968
|
+
this.selectedViewport = viewport;
|
|
969
|
+
setEnvironment(viewport);
|
|
970
|
+
this.dispatchEvent(
|
|
971
|
+
new CustomEvent('selectedViewport', {
|
|
972
|
+
detail: viewport,
|
|
973
|
+
})
|
|
974
|
+
);
|
|
975
|
+
this.dashboardRef.buildUrl();
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
private openAdvancedEditor(propertyName: string, value: any, element: HTMLElement) {
|
|
979
|
+
// Check if this property is already being edited
|
|
980
|
+
const existingEditor = this.editingProperties.find(p => p.name === propertyName && p.element === element);
|
|
981
|
+
if (existingEditor) {
|
|
982
|
+
return; // Property is already open for editing
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const newEditor = {
|
|
986
|
+
id: `${propertyName}-${Date.now()}`,
|
|
987
|
+
name: propertyName,
|
|
988
|
+
value: value,
|
|
989
|
+
element: element,
|
|
990
|
+
editorValue: JSON.stringify(value, null, 2),
|
|
991
|
+
editorError: ''
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
this.editingProperties = [...this.editingProperties, newEditor];
|
|
995
|
+
|
|
996
|
+
// Notify parent to resize frame if this is the first editor
|
|
997
|
+
if (this.editingProperties.length === 1) {
|
|
998
|
+
this.dispatchEvent(
|
|
999
|
+
new CustomEvent('editorStateChanged', {
|
|
1000
|
+
detail: { isOpen: true },
|
|
1001
|
+
bubbles: true
|
|
1002
|
+
})
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
private handleEditorSave(editorId: string) {
|
|
1008
|
+
const editor = this.editingProperties.find(p => p.id === editorId);
|
|
1009
|
+
if (!editor) return;
|
|
1010
|
+
|
|
1011
|
+
try {
|
|
1012
|
+
const parsedValue = JSON.parse(editor.editorValue);
|
|
1013
|
+
editor.element[editor.name] = parsedValue;
|
|
1014
|
+
|
|
1015
|
+
// Remove this editor from the list
|
|
1016
|
+
this.editingProperties = this.editingProperties.filter(p => p.id !== editorId);
|
|
1017
|
+
|
|
1018
|
+
// If no more editors, notify parent to resize frame
|
|
1019
|
+
if (this.editingProperties.length === 0) {
|
|
1020
|
+
this.dispatchEvent(
|
|
1021
|
+
new CustomEvent('editorStateChanged', {
|
|
1022
|
+
detail: { isOpen: false },
|
|
1023
|
+
bubbles: true
|
|
1024
|
+
})
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Refresh properties display
|
|
1029
|
+
this.createProperties();
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
// Update error for this specific editor
|
|
1032
|
+
const editorIndex = this.editingProperties.findIndex(p => p.id === editorId);
|
|
1033
|
+
if (editorIndex !== -1) {
|
|
1034
|
+
this.editingProperties[editorIndex].editorError = `Invalid JSON: ${error.message}`;
|
|
1035
|
+
this.requestUpdate();
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
private handleEditorCancel(editorId: string) {
|
|
1041
|
+
// Remove this editor from the list
|
|
1042
|
+
this.editingProperties = this.editingProperties.filter(p => p.id !== editorId);
|
|
1043
|
+
|
|
1044
|
+
// If no more editors, notify parent to resize frame
|
|
1045
|
+
if (this.editingProperties.length === 0) {
|
|
1046
|
+
this.dispatchEvent(
|
|
1047
|
+
new CustomEvent('editorStateChanged', {
|
|
1048
|
+
detail: { isOpen: false },
|
|
1049
|
+
bubbles: true
|
|
1050
|
+
})
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
private closeAllEditors() {
|
|
1056
|
+
this.editingProperties = [];
|
|
1057
|
+
|
|
1058
|
+
// Notify parent to resize frame back
|
|
1059
|
+
this.dispatchEvent(
|
|
1060
|
+
new CustomEvent('editorStateChanged', {
|
|
1061
|
+
detail: { isOpen: false },
|
|
1062
|
+
bubbles: true
|
|
1063
|
+
})
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
private toggleNative() {
|
|
1068
|
+
this.dispatchEvent(
|
|
1069
|
+
new CustomEvent('toggleNative', {
|
|
1070
|
+
bubbles: true
|
|
1071
|
+
})
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// ==================== Recording Methods ====================
|
|
1076
|
+
|
|
1077
|
+
private handleRecordButtonClick() {
|
|
1078
|
+
if (this.isRecording) {
|
|
1079
|
+
// Stop recording by calling the panel's stopRecording method
|
|
1080
|
+
const panel = this.shadowRoot?.querySelector('wcc-recording-panel') as any;
|
|
1081
|
+
if (panel && panel.stopRecording) {
|
|
1082
|
+
panel.stopRecording();
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
// Toggle the recording panel
|
|
1086
|
+
this.showRecordingPanel = !this.showRecordingPanel;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|